1
0
mirror of https://github.com/ascribe/onion.git synced 2024-06-30 13:41:57 +02:00

Merge remote-tracking branch 'remotes/origin/AD-456-ikonotv-branded-page-for-registra' into AD-943-add-custom-additional-fields

This commit is contained in:
diminator 2015-09-16 15:59:07 +02:00
commit b24d4608dd
26 changed files with 550 additions and 289 deletions

View File

@ -30,7 +30,7 @@ class ContractListActions {
changeContract(contract){ changeContract(contract){
return Q.Promise((resolve, reject) => { return Q.Promise((resolve, reject) => {
OwnershipFetcher.makeContractPublic(contract) OwnershipFetcher.changeContract(contract)
.then((res) => { .then((res) => {
resolve(res); resolve(res);
}) })

View File

@ -38,9 +38,9 @@ let FurtherDetails = React.createClass({
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
}, },
submitKey(key){ submitFile(file){
this.setState({ this.setState({
otherDataKey: key otherDataKey: file.key
}); });
}, },
@ -78,7 +78,7 @@ let FurtherDetails = React.createClass({
extraData={this.props.extraData} /> extraData={this.props.extraData} />
<Form> <Form>
<FurtherDetailsFileuploader <FurtherDetailsFileuploader
submitKey={this.submitKey} submitFile={this.submitFile}
setIsUploadReady={this.setIsUploadReady} setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
editable={this.props.editable} editable={this.props.editable}

View File

@ -17,7 +17,7 @@ let FurtherDetailsFileuploader = React.createClass({
pieceId: React.PropTypes.number, pieceId: React.PropTypes.number,
otherData: React.PropTypes.arrayOf(React.PropTypes.object), otherData: React.PropTypes.arrayOf(React.PropTypes.object),
setIsUploadReady: React.PropTypes.func, setIsUploadReady: React.PropTypes.func,
submitKey: React.PropTypes.func, submitFile: React.PropTypes.func,
isReadyForFormSubmission: React.PropTypes.func, isReadyForFormSubmission: React.PropTypes.func,
editable: React.PropTypes.bool, editable: React.PropTypes.bool,
multiple: React.PropTypes.bool multiple: React.PropTypes.bool
@ -55,11 +55,8 @@ let FurtherDetailsFileuploader = React.createClass({
url: ApiUrls.blob_otherdatas, url: ApiUrls.blob_otherdatas,
pieceId: this.props.pieceId pieceId: this.props.pieceId
}} }}
validation={{ validation={AppConstants.fineUploader.validation.additionalData}
itemLimit: 100000, submitFile={this.props.submitFile}
sizeLimit: '50000000'
}}
submitKey={this.props.submitKey}
setIsUploadReady={this.props.setIsUploadReady} setIsUploadReady={this.props.setIsUploadReady}
isReadyForFormSubmission={this.props.isReadyForFormSubmission} isReadyForFormSubmission={this.props.isReadyForFormSubmission}
session={{ session={{

View File

@ -4,45 +4,40 @@ import React from 'react';
import Form from '../ascribe_forms/form'; import Form from '../ascribe_forms/form';
import Property from '../ascribe_forms/property'; import Property from '../ascribe_forms/property';
import InputCheckbox from '../ascribe_forms/input_checkbox';
import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions'; import GlobalNotificationActions from '../../actions/global_notification_actions';
import ContractListActions from '../../actions/contract_list_actions'; import ContractListActions from '../../actions/contract_list_actions';
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
import InputFineUploader from './input_fineuploader';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
import { getCookie } from '../../utils/fetch_api_utils';
import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils'; import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils';
let CreateContractForm = React.createClass({ let CreateContractForm = React.createClass({
propTypes: {
isPublic: React.PropTypes.bool,
// A class of a file the user has to upload
// Needs to be defined both in singular as well as in plural
fileClassToUpload: React.PropTypes.shape({
singular: React.PropTypes.string,
plural: React.PropTypes.string
})
},
getInitialState() { getInitialState() {
return { return {
contractKey: null, isUploadReady: false,
isUploadReady: false contractName: ''
}; };
}, },
getFormData(){
return {
blob: this.state.contractKey
};
},
submitKey(key) {
this.setState({
contractKey: key
});
},
setIsUploadReady(isReady) { setIsUploadReady(isReady) {
this.setState({ this.setState({
isUploadReady: isReady isUploadReady: isReady
@ -56,31 +51,25 @@ let CreateContractForm = React.createClass({
this.refs.form.reset(); this.refs.form.reset();
}, },
submitFileName(fileName) {
this.setState({
contractName: fileName
});
this.refs.form.submit();
},
render() { render() {
return ( return (
<Form <Form
ref='form' ref='form'
url={ApiUrls.ownership_contract_list} url={ApiUrls.ownership_contract_list}
getFormData={this.getFormData} handleSuccess={this.handleCreateSuccess}>
handleSuccess={this.handleCreateSuccess}
buttons={
<button
type="submit"
className="btn ascribe-btn ascribe-btn-login"
disabled={!this.state.isUploadReady}>
{getLangText('Create new contract')}
</button>
}
spinner={
<span className="btn ascribe-btn ascribe-btn-login ascribe-btn-login-spinner">
<img src="https://s3-us-west-2.amazonaws.com/ascribe0/media/thumbnails/ascribe_animated_medium.gif" />
</span>
}>
<Property <Property
label="Contract file"> name="blob"
<ReactS3FineUploader label={getLangText('Contract file (*.pdf only, max. 50MB per contract)')}>
ref='uploader' <InputFineUploader
submitFileName={this.submitFileName}
keyRoutine={{ keyRoutine={{
url: AppConstants.serverUrl + 's3/key/', url: AppConstants.serverUrl + 's3/key/',
fileClass: 'contract' fileClass: 'contract'
@ -89,46 +78,30 @@ let CreateContractForm = React.createClass({
url: ApiUrls.blob_contracts url: ApiUrls.blob_contracts
}} }}
validation={{ validation={{
itemLimit: 100000, itemLimit: AppConstants.fineUploader.validation.additionalData.itemLimit,
sizeLimit: '50000000' sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit,
}} allowedExtensions: ['pdf']
signature={{
endpoint: AppConstants.serverUrl + 's3/signature/',
customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken)
}
}}
deleteFile={{
enabled: true,
method: 'DELETE',
endpoint: AppConstants.serverUrl + 's3/delete',
customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken)
}
}} }}
areAssetsDownloadable={true} areAssetsDownloadable={true}
areAssetsEditable={true} areAssetsEditable={true}
submitKey={this.submitKey}
setIsUploadReady={this.setIsUploadReady} setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}/> isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
fileClassToUpload={this.props.fileClassToUpload}/>
</Property> </Property>
<Property <Property
name='name' name='name'
label={getLangText('Contract name')}> label={getLangText('Contract name')}
hidden={true}>
<input <input
type="text" type="text"
placeholder="(e.g. Contract - Loan agreement #1)" value={this.state.contractName}/>
required/>
</Property> </Property>
<Property <Property
name="is_public" name="is_public"
className="ascribe-settings-property-collapsible-toggle" hidden={true}>
style={{paddingBottom: 0}}> <input
<InputCheckbox> type="checkbox"
<span> value={this.props.isPublic} />
Make contract public (this will replace the current public contract)
</span>
</InputCheckbox>
</Property> </Property>
</Form> </Form>
); );

View File

@ -10,6 +10,7 @@ import Property from './property';
import InputFineUploader from './input_fineuploader'; import InputFineUploader from './input_fineuploader';
import ApiUrls from '../../constants/api_urls'; import ApiUrls from '../../constants/api_urls';
import AppConstants from '../../constants/application_constants';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
import { mergeOptions } from '../../utils/general_utils'; import { mergeOptions } from '../../utils/general_utils';
@ -99,6 +100,14 @@ let RegisterPieceForm = React.createClass({
name="digital_work_key" name="digital_work_key"
ignoreFocus={true}> ignoreFocus={true}>
<InputFineUploader <InputFineUploader
keyRoutine={{
url: AppConstants.serverUrl + 's3/key/',
fileClass: 'digitalwork'
}}
createBlobRoutine={{
url: ApiUrls.blob_digitalworks
}}
validation={AppConstants.fineUploader.validation.registerWork}
setIsUploadReady={this.setIsUploadReady} setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile} isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
isFineUploaderActive={this.props.isFineUploaderActive} isFineUploaderActive={this.props.isFineUploaderActive}

View File

@ -5,7 +5,6 @@ import React from 'react';
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader'; import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
import ApiUrls from '../../constants/api_urls';
import { getCookie } from '../../utils/fetch_api_utils'; import { getCookie } from '../../utils/fetch_api_utils';
@ -13,7 +12,21 @@ let InputFileUploader = React.createClass({
propTypes: { propTypes: {
setIsUploadReady: React.PropTypes.func, setIsUploadReady: React.PropTypes.func,
isReadyForFormSubmission: React.PropTypes.func, isReadyForFormSubmission: React.PropTypes.func,
submitFileName: React.PropTypes.func,
onClick: React.PropTypes.func, onClick: React.PropTypes.func,
keyRoutine: React.PropTypes.shape({
url: React.PropTypes.string,
fileClass: React.PropTypes.string
}),
createBlobRoutine: React.PropTypes.shape({
url: React.PropTypes.string
}),
validation: React.PropTypes.shape({
itemLimit: React.PropTypes.number,
sizeLimit: React.PropTypes.string,
allowedExtensions: React.PropTypes.arrayOf(React.PropTypes.string)
}),
// isFineUploaderActive is used to lock react fine uploader in case // isFineUploaderActive is used to lock react fine uploader in case
// a user is actually not logged in already to prevent him from droping files // a user is actually not logged in already to prevent him from droping files
@ -24,7 +37,14 @@ let InputFileUploader = React.createClass({
enableLocalHashing: React.PropTypes.bool, enableLocalHashing: React.PropTypes.bool,
// provided by Property // provided by Property
disabled: React.PropTypes.bool disabled: React.PropTypes.bool,
// A class of a file the user has to upload
// Needs to be defined both in singular as well as in plural
fileClassToUpload: React.PropTypes.shape({
singular: React.PropTypes.string,
plural: React.PropTypes.string
})
}, },
getInitialState() { getInitialState() {
@ -33,10 +53,14 @@ let InputFileUploader = React.createClass({
}; };
}, },
submitKey(key){ submitFile(file){
this.setState({ this.setState({
value: key value: file.key
}); });
if(typeof this.props.submitFileName === 'function') {
this.props.submitFileName(file.originalName);
}
}, },
reset() { reset() {
@ -56,18 +80,10 @@ let InputFileUploader = React.createClass({
<ReactS3FineUploader <ReactS3FineUploader
ref="fineuploader" ref="fineuploader"
onClick={this.props.onClick} onClick={this.props.onClick}
keyRoutine={{ keyRoutine={this.props.keyRoutine}
url: AppConstants.serverUrl + 's3/key/', createBlobRoutine={this.props.createBlobRoutine}
fileClass: 'digitalwork' validation={this.props.validation}
}} submitFile={this.submitFile}
createBlobRoutine={{
url: ApiUrls.blob_digitalworks
}}
submitKey={this.submitKey}
validation={{
itemLimit: 100000,
sizeLimit: '25000000000'
}}
setIsUploadReady={this.props.setIsUploadReady} setIsUploadReady={this.props.setIsUploadReady}
isReadyForFormSubmission={this.props.isReadyForFormSubmission} isReadyForFormSubmission={this.props.isReadyForFormSubmission}
areAssetsDownloadable={false} areAssetsDownloadable={false}
@ -87,7 +103,8 @@ let InputFileUploader = React.createClass({
} }
}} }}
onInactive={this.props.onLoggedOut} onInactive={this.props.onLoggedOut}
enableLocalHashing={this.props.enableLocalHashing} /> enableLocalHashing={this.props.enableLocalHashing}
fileClassToUpload={this.props.fileClassToUpload}/>
); );
} }
}); });

View File

@ -70,7 +70,7 @@ let Property = React.createClass({
// In order to set this.state.value from another component // In order to set this.state.value from another component
// the state of value should only be set if its not undefined and // the state of value should only be set if its not undefined and
// actually references something // actually references something
if(typeof childInput.getDOMNode().value !== 'undefined') { if(childInput && typeof childInput.getDOMNode().value !== 'undefined') {
this.setState({ this.setState({
value: childInput.getDOMNode().value value: childInput.getDOMNode().value
}); });

View File

@ -9,12 +9,14 @@ import ContractListStore from '../../stores/contract_list_store';
import ContractListActions from '../../actions/contract_list_actions'; import ContractListActions from '../../actions/contract_list_actions';
import ActionPanel from '../ascribe_panel/action_panel'; import ActionPanel from '../ascribe_panel/action_panel';
import ContractSettingsUpdateButton from './contract_settings_update_button';
import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions'; import GlobalNotificationActions from '../../actions/global_notification_actions';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let ContractSettings = React.createClass({ let ContractSettings = React.createClass({
propTypes: { propTypes: {
defaultExpanded: React.PropTypes.bool defaultExpanded: React.PropTypes.bool
@ -37,23 +39,6 @@ let ContractSettings = React.createClass({
this.setState(state); this.setState(state);
}, },
makeContractPublic(contract) {
return () => {
contract.is_public = true;
ContractListActions.changeContract(contract)
.then(() => {
ContractListActions.fetchContractList(true);
let notification = getLangText('Contract %s is now public', contract.name);
notification = new GlobalNotificationModel(notification, 'success', 4000);
GlobalNotificationActions.appendGlobalNotification(notification);
})
.catch((err) => {
let notification = new GlobalNotificationModel(err, 'danger', 10000);
GlobalNotificationActions.appendGlobalNotification(notification);
});
};
},
removeContract(contract) { removeContract(contract) {
return () => { return () => {
ContractListActions.removeContract(contract.id) ContractListActions.removeContract(contract.id)
@ -80,81 +65,92 @@ let ContractSettings = React.createClass({
render() { render() {
let publicContracts = this.getPublicContracts(); let publicContracts = this.getPublicContracts();
let privateContracts = this.getPrivateContracts(); let privateContracts = this.getPrivateContracts();
let createPublicContractForm = null;
if(publicContracts.length === 0) {
createPublicContractForm = (
<CreateContractForm
isPublic={true}
fileClassToUpload={{
singular: 'new public contract',
plural: 'new public contracts'
}}/>
);
}
return ( return (
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Contract Settings')} title={getLangText('Contracts')}
show={true} show={true}
defaultExpanded={true}> defaultExpanded={true}>
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('List Contracts')} title={getLangText('Public Contracts')}
show={true} show={true}
defaultExpanded={true}> defaultExpanded={true}>
<CollapsibleParagraph {createPublicContractForm}
title={getLangText('Public Contracts')} {publicContracts.map((contract, i) => {
show={true} return (
defaultExpanded={true}> <ActionPanel
{publicContracts.map((contract, i) => { key={i}
return ( title={contract.name}
<ActionPanel content={contract.name}
key={i} buttons={
title={contract.name} <div className="pull-right">
content={contract.name} <ContractSettingsUpdateButton contract={contract}/>
buttons={ <a
<div className="pull-right"> className="btn btn-default btn-sm margin-left-2px"
<button className="btn btn-default btn-sm margin-left-2px"> href={contract.blob.url_safe}
UPDATE target="_blank">
</button> {getLangText('PREVIEW')}
<button </a>
className="btn btn-default btn-sm margin-left-2px" <button
onClick={this.removeContract(contract)}> className="btn btn-default btn-sm margin-left-2px"
REMOVE onClick={this.removeContract(contract)}>
</button> {getLangText('REMOVE')}
</div> </button>
} </div>
leftColumnWidth="40%" }
rightColumnWidth="60%"/> leftColumnWidth="40%"
); rightColumnWidth="60%"/>
})} );
</CollapsibleParagraph> })}
<CollapsibleParagraph
title={getLangText('Private Contracts')}
show={true}
defaultExpanded={true}>
{privateContracts.map((contract, i) => {
return (
<ActionPanel
key={i}
title={contract.name}
content={contract.name}
buttons={
<div className="pull-right">
<button className="btn btn-default btn-sm margin-left-2px">
UPDATE
</button>
<button
className="btn btn-default btn-sm margin-left-2px"
onClick={this.removeContract(contract)}>
REMOVE
</button>
<button
className="btn btn-default btn-sm margin-left-2px"
onClick={this.makeContractPublic(contract)}>
MAKE PUBLIC
</button>
</div>
}
leftColumnWidth="40%"
rightColumnWidth="60%"/>
);
})}
</CollapsibleParagraph>
</CollapsibleParagraph> </CollapsibleParagraph>
<CollapsibleParagraph <CollapsibleParagraph
title={getLangText('Create Contract')} title={getLangText('Private Contracts')}
show={true} show={true}
defaultExpanded={true}> defaultExpanded={true}>
<CreateContractForm /> <CreateContractForm
isPublic={false}
fileClassToUpload={{
singular: getLangText('new private contract'),
plural: getLangText('new private contracts')
}}/>
{privateContracts.map((contract, i) => {
return (
<ActionPanel
key={i}
title={contract.name}
content={contract.name}
buttons={
<div className="pull-right">
<ContractSettingsUpdateButton contract={contract} />
<a
className="btn btn-default btn-sm margin-left-2px"
href={contract.blob.url_safe}
target="_blank">
{getLangText('PREVIEW')}
</a>
<button
className="btn btn-default btn-sm margin-left-2px"
onClick={this.removeContract(contract)}>
{getLangText('REMOVE')}
</button>
</div>
}
leftColumnWidth="40%"
rightColumnWidth="60%"/>
);
})}
</CollapsibleParagraph> </CollapsibleParagraph>
</CollapsibleParagraph> </CollapsibleParagraph>
); );

View File

@ -0,0 +1,98 @@
'use strict';
import React from 'react';
import ReactS3FineUploader from '../ascribe_uploader/react_s3_fine_uploader';
import UploadButton from '../ascribe_uploader/ascribe_upload_button/upload_button';
import AppConstants from '../../constants/application_constants';
import ApiUrls from '../../constants/api_urls';
import ContractListActions from '../../actions/contract_list_actions';
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
import { formSubmissionValidation } from '../ascribe_uploader/react_s3_fine_uploader_utils';
import { getCookie } from '../../utils/fetch_api_utils';
import { getLangText } from '../../utils/lang_utils';
let ContractSettingsUpdateButton = React.createClass({
propTypes: {
contract: React.PropTypes.object
},
submitFile(file) {
let contract = this.props.contract;
// override the blob with the key's value
contract.blob = file.key;
// send it to the server
ContractListActions
.changeContract(contract)
.then((res) => {
// Display feedback to the user
let notification = new GlobalNotificationModel(getLangText('Contract %s successfully updated', res.name), 'success', 5000);
GlobalNotificationActions.appendGlobalNotification(notification);
// and refresh the contract list to get the updated contracs
return ContractListActions.fetchContractList(true);
})
.then(() => {
// Also, reset the fineuploader component so that the user can again 'update' his contract
this.refs.fineuploader.reset();
})
.catch((err) => {
console.logGlobal(err);
let notification = new GlobalNotificationModel(getLangText('Contract could not be updated'), 'success', 5000);
GlobalNotificationActions.appendGlobalNotification(notification);
});
},
render() {
return (
<ReactS3FineUploader
ref="fineuploader"
fileInputElement={UploadButton}
keyRoutine={{
url: AppConstants.serverUrl + 's3/key/',
fileClass: 'contract'
}}
createBlobRoutine={{
url: ApiUrls.blob_contracts
}}
validation={{
itemLimit: AppConstants.fineUploader.validation.registerWork.itemLimit,
sizeLimit: AppConstants.fineUploader.validation.additionalData.sizeLimit,
allowedExtensions: ['pdf']
}}
setIsUploadReady={() =>{/* So that ReactS3FineUploader is not complaining */}}
signature={{
endpoint: AppConstants.serverUrl + 's3/signature/',
customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken)
}
}}
deleteFile={{
enabled: true,
method: 'DELETE',
endpoint: AppConstants.serverUrl + 's3/delete',
customHeaders: {
'X-CSRFToken': getCookie(AppConstants.csrftoken)
}
}}
fileClassToUpload={{
singular: getLangText('UPDATE'),
plural: getLangText('UPDATE')
}}
isReadyForFormSubmission={formSubmissionValidation.atLeastOneUploadedFile}
submitFile={this.submitFile}
/>
);
}
});
export default ContractSettingsUpdateButton;

View File

@ -26,8 +26,6 @@ let SettingsContainer = React.createClass({
<APISettings /> <APISettings />
<BitcoinWalletSettings /> <BitcoinWalletSettings />
<ContractSettings /> <ContractSettings />
<br />
<br />
</div> </div>
); );
} }

View File

@ -6,20 +6,14 @@ import ProgressBar from 'react-bootstrap/lib/ProgressBar';
import FileDragAndDropDialog from './file_drag_and_drop_dialog'; import FileDragAndDropDialog from './file_drag_and_drop_dialog';
import FileDragAndDropPreviewIterator from './file_drag_and_drop_preview_iterator'; import FileDragAndDropPreviewIterator from './file_drag_and_drop_preview_iterator';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../../utils/lang_utils';
// Taken from: https://github.com/fedosejev/react-file-drag-and-drop // Taken from: https://github.com/fedosejev/react-file-drag-and-drop
let FileDragAndDrop = React.createClass({ let FileDragAndDrop = React.createClass({
propTypes: { propTypes: {
className: React.PropTypes.string,
onDragStart: React.PropTypes.func,
onDrop: React.PropTypes.func.isRequired, onDrop: React.PropTypes.func.isRequired,
onDrag: React.PropTypes.func,
onDragEnter: React.PropTypes.func,
onLeave: React.PropTypes.func,
onDragLeave: React.PropTypes.func,
onDragOver: React.PropTypes.func, onDragOver: React.PropTypes.func,
onDragEnd: React.PropTypes.func,
onInactive: React.PropTypes.func, onInactive: React.PropTypes.func,
filesToUpload: React.PropTypes.array, filesToUpload: React.PropTypes.array,
handleDeleteFile: React.PropTypes.func, handleDeleteFile: React.PropTypes.func,
@ -37,37 +31,16 @@ let FileDragAndDrop = React.createClass({
hashingProgress: React.PropTypes.number, hashingProgress: React.PropTypes.number,
// sets the value of this.state.hashingProgress in reactfineuploader // sets the value of this.state.hashingProgress in reactfineuploader
// to -1 which is code for: aborted // to -1 which is code for: aborted
handleCancelHashing: React.PropTypes.func handleCancelHashing: React.PropTypes.func,
},
handleDragStart(event) { // A class of a file the user has to upload
if (typeof this.props.onDragStart === 'function') { // Needs to be defined both in singular as well as in plural
this.props.onDragStart(event); fileClassToUpload: React.PropTypes.shape({
} singular: React.PropTypes.string,
}, plural: React.PropTypes.string
}),
handleDrag(event) { allowedExtensions: React.PropTypes.string
if (typeof this.props.onDrag === 'function') {
this.props.onDrag(event);
}
},
handleDragEnd(event) {
if (typeof this.props.onDragEnd === 'function') {
this.props.onDragEnd(event);
}
},
handleDragEnter(event) {
if (typeof this.props.onDragEnter === 'function') {
this.props.onDragEnter(event);
}
},
handleDragLeave(event) {
if (typeof this.props.onDragLeave === 'function') {
this.props.onDragLeave(event);
}
}, },
handleDragOver(event) { handleDragOver(event) {
@ -159,14 +132,27 @@ let FileDragAndDrop = React.createClass({
}, },
render: function () { render: function () {
let { filesToUpload,
dropzoneInactive,
className,
hashingProgress,
handleCancelHashing,
multiple,
enableLocalHashing,
fileClassToUpload,
areAssetsDownloadable,
areAssetsEditable,
allowedExtensions
} = this.props;
// has files only is true if there are files that do not have the status deleted or canceled // has files only is true if there are files that do not have the status deleted or canceled
let hasFiles = this.props.filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1).length > 0; let hasFiles = filesToUpload.filter((file) => file.status !== 'deleted' && file.status !== 'canceled' && file.size !== -1).length > 0;
let className = hasFiles ? 'has-files ' : ''; let updatedClassName = hasFiles ? 'has-files ' : '';
className += this.props.dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone'; updatedClassName += dropzoneInactive ? 'inactive-dropzone' : 'active-dropzone';
className += this.props.className ? ' ' + this.props.className : ''; updatedClassName += ' file-drag-and-drop';
// if !== -2: triggers a FileDragAndDrop-global spinner // if !== -2: triggers a FileDragAndDrop-global spinner
if(this.props.hashingProgress !== -2) { if(hashingProgress !== -2) {
return ( return (
<div className={className}> <div className={className}>
<div className="file-drag-and-drop-hashing-dialog"> <div className="file-drag-and-drop-hashing-dialog">
@ -184,29 +170,26 @@ let FileDragAndDrop = React.createClass({
} else { } else {
return ( return (
<div <div
className={className} className={updatedClassName}
onDragStart={this.handleDragStart}
onDrag={this.handleDrop} onDrag={this.handleDrop}
onDragEnter={this.handleDragEnter}
onDragLeave={this.handleDragLeave}
onDragOver={this.handleDragOver} onDragOver={this.handleDragOver}
onDrop={this.handleDrop} onDrop={this.handleDrop}>
onDragEnd={this.handleDragEnd}>
<FileDragAndDropDialog <FileDragAndDropDialog
multipleFiles={this.props.multiple} multipleFiles={multiple}
hasFiles={hasFiles} hasFiles={hasFiles}
onClick={this.handleOnClick} onClick={this.handleOnClick}
enableLocalHashing={this.props.enableLocalHashing}/> enableLocalHashing={enableLocalHashing}
fileClassToUpload={fileClassToUpload}/>
<FileDragAndDropPreviewIterator <FileDragAndDropPreviewIterator
files={this.props.filesToUpload} files={filesToUpload}
handleDeleteFile={this.handleDeleteFile} handleDeleteFile={this.handleDeleteFile}
handleCancelFile={this.handleCancelFile} handleCancelFile={this.handleCancelFile}
handlePauseFile={this.handlePauseFile} handlePauseFile={this.handlePauseFile}
handleResumeFile={this.handleResumeFile} handleResumeFile={this.handleResumeFile}
areAssetsDownloadable={this.props.areAssetsDownloadable} areAssetsDownloadable={areAssetsDownloadable}
areAssetsEditable={this.props.areAssetsEditable}/> areAssetsEditable={areAssetsEditable}/>
<input <input
multiple={this.props.multiple} multiple={multiple}
ref="fileinput" ref="fileinput"
type="file" type="file"
style={{ style={{
@ -214,7 +197,8 @@ let FileDragAndDrop = React.createClass({
height: 0, height: 0,
width: 0 width: 0
}} }}
onChange={this.handleDrop} /> onChange={this.handleDrop}
accept={allowedExtensions}/>
</div> </div>
); );
} }

View File

@ -3,7 +3,7 @@
import React from 'react'; import React from 'react';
import Router from 'react-router'; import Router from 'react-router';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../../utils/lang_utils';
let Link = Router.Link; let Link = Router.Link;
@ -12,7 +12,14 @@ let FileDragAndDropDialog = React.createClass({
hasFiles: React.PropTypes.bool, hasFiles: React.PropTypes.bool,
multipleFiles: React.PropTypes.bool, multipleFiles: React.PropTypes.bool,
onClick: React.PropTypes.func, onClick: React.PropTypes.func,
enableLocalHashing: React.PropTypes.bool enableLocalHashing: React.PropTypes.bool,
// A class of a file the user has to upload
// Needs to be defined both in singular as well as in plural
fileClassToUpload: React.PropTypes.shape({
singular: React.PropTypes.string,
plural: React.PropTypes.string
})
}, },
mixins: [Router.State], mixins: [Router.State],
@ -56,29 +63,29 @@ let FileDragAndDropDialog = React.createClass({
} else { } else {
if(this.props.multipleFiles) { if(this.props.multipleFiles) {
return ( return (
<div className="file-drag-and-drop-dialog"> <span className="file-drag-and-drop-dialog">
<p>{getLangText('Drag files here')}</p> <p>{getLangText('Drag %s here', this.props.fileClassToUpload.plural)}</p>
<p>{getLangText('or')}</p> <p>{getLangText('or')}</p>
<span <span
className="btn btn-default" className="btn btn-default"
onClick={this.props.onClick}> onClick={this.props.onClick}>
{getLangText('choose files to upload')} {getLangText('choose %s to upload', this.props.fileClassToUpload.plural)}
</span> </span>
</div> </span>
); );
} else { } else {
let dialog = queryParams.method === 'hash' ? getLangText('choose a file to hash') : getLangText('choose a file to upload'); let dialog = queryParams.method === 'hash' ? getLangText('choose a %s to hash', this.props.fileClassToUpload.singular) : getLangText('choose a %s to upload', this.props.fileClassToUpload.singular);
return ( return (
<div className="file-drag-and-drop-dialog"> <span className="file-drag-and-drop-dialog">
<p>{getLangText('Drag a file here')}</p> <p>{getLangText('Drag a %s here', this.props.fileClassToUpload.singular)}</p>
<p>{getLangText('or')}</p> <p>{getLangText('or')}</p>
<span <span
className="btn btn-default" className="btn btn-default"
onClick={this.props.onClick}> onClick={this.props.onClick}>
{dialog} {dialog}
</span> </span>
</div> </span>
); );
} }
} }

View File

@ -4,7 +4,9 @@ import React from 'react';
import FileDragAndDropPreviewImage from './file_drag_and_drop_preview_image'; import FileDragAndDropPreviewImage from './file_drag_and_drop_preview_image';
import FileDragAndDropPreviewOther from './file_drag_and_drop_preview_other'; import FileDragAndDropPreviewOther from './file_drag_and_drop_preview_other';
import { getLangText } from '../../utils/lang_utils.js';
import { getLangText } from '../../../utils/lang_utils';
let FileDragAndDropPreview = React.createClass({ let FileDragAndDropPreview = React.createClass({
@ -43,6 +45,7 @@ let FileDragAndDropPreview = React.createClass({
handleDownloadFile() { handleDownloadFile() {
if(this.props.file.s3Url) { if(this.props.file.s3Url) {
// This simply opens a new browser tab with the url provided
open(this.props.file.s3Url); open(this.props.file.s3Url);
} }
}, },

View File

@ -3,8 +3,8 @@
import React from 'react'; import React from 'react';
import ProgressBar from 'react-bootstrap/lib/ProgressBar'; import ProgressBar from 'react-bootstrap/lib/ProgressBar';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../../constants/application_constants';
import { getLangText } from '../../utils/lang_utils.js'; import { getLangText } from '../../../utils/lang_utils';
let FileDragAndDropPreviewImage = React.createClass({ let FileDragAndDropPreviewImage = React.createClass({
propTypes: { propTypes: {

View File

@ -5,7 +5,7 @@ import React from 'react';
import FileDragAndDropPreview from './file_drag_and_drop_preview'; import FileDragAndDropPreview from './file_drag_and_drop_preview';
import FileDragAndDropPreviewProgress from './file_drag_and_drop_preview_progress'; import FileDragAndDropPreviewProgress from './file_drag_and_drop_preview_progress';
import { displayValidFilesFilter } from './react_s3_fine_uploader_utils'; import { displayValidFilesFilter } from '../react_s3_fine_uploader_utils';
let FileDragAndDropPreviewIterator = React.createClass({ let FileDragAndDropPreviewIterator = React.createClass({

View File

@ -3,8 +3,8 @@
import React from 'react'; import React from 'react';
import ProgressBar from 'react-bootstrap/lib/ProgressBar'; import ProgressBar from 'react-bootstrap/lib/ProgressBar';
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../../constants/application_constants';
import { getLangText } from '../../utils/lang_utils.js'; import { getLangText } from '../../../utils/lang_utils';
let FileDragAndDropPreviewOther = React.createClass({ let FileDragAndDropPreviewOther = React.createClass({
propTypes: { propTypes: {
@ -61,7 +61,7 @@ let FileDragAndDropPreviewOther = React.createClass({
<div className="file-drag-and-drop-preview-table-wrapper"> <div className="file-drag-and-drop-preview-table-wrapper">
<div className="file-drag-and-drop-preview-other"> <div className="file-drag-and-drop-preview-other">
{actionSymbol} {actionSymbol}
<span>{'.' + this.props.type}</span> <p>{'.' + this.props.type}</p>
</div> </div>
</div> </div>
</div> </div>

View File

@ -4,7 +4,8 @@ import React from 'react';
import ProgressBar from 'react-bootstrap/lib/ProgressBar'; import ProgressBar from 'react-bootstrap/lib/ProgressBar';
import { displayValidProgressFilesFilter } from './react_s3_fine_uploader_utils'; import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils';
import { getLangText } from '../../../utils/lang_utils';
let FileDragAndDropPreviewProgress = React.createClass({ let FileDragAndDropPreviewProgress = React.createClass({
@ -54,7 +55,7 @@ let FileDragAndDropPreviewProgress = React.createClass({
return ( return (
<ProgressBar <ProgressBar
now={Math.ceil(overallProgress)} now={Math.ceil(overallProgress)}
label="Overall progress: %(percent)s%" label={getLangText('Overall progress%s', ': %(percent)s%')}
className="ascribe-progress-bar" className="ascribe-progress-bar"
style={style} /> style={style} />
); );

View File

@ -0,0 +1,103 @@
'use strict';
import React from 'react';
import { displayValidProgressFilesFilter } from '../react_s3_fine_uploader_utils';
import { getLangText } from '../../../utils/lang_utils';
let UploadButton = React.createClass({
propTypes: {
onDrop: React.PropTypes.func.isRequired,
filesToUpload: React.PropTypes.array,
multiple: React.PropTypes.bool,
// For simplification purposes we're just going to use this prop as a
// label for the upload button
fileClassToUpload: React.PropTypes.shape({
singular: React.PropTypes.string,
plural: React.PropTypes.string
}),
allowedExtensions: React.PropTypes.string
},
handleDrop(event) {
event.preventDefault();
event.stopPropagation();
let files = event.target.files;
if(typeof this.props.onDrop === 'function' && files) {
this.props.onDrop(files);
}
},
getUploadingFiles() {
return this.props.filesToUpload.filter((file) => file.status === 'uploading');
},
handleOnClick() {
let uploadingFiles = this.getUploadingFiles();
// We only want the button to be clickable if there are no files currently uploading
if(uploadingFiles.length === 0) {
// Firefox only recognizes the simulated mouse click if bubbles is set to true,
// but since Google Chrome propagates the event much further than needed, we
// need to stop propagation as soon as the event is created
var evt = new MouseEvent('click', {
view: window,
bubbles: true,
cancelable: true
});
evt.stopPropagation();
this.refs.fileinput.getDOMNode().dispatchEvent(evt);
}
},
getButtonLabel() {
let { filesToUpload, fileClassToUpload } = this.props;
// filter invalid files that might have been deleted or canceled...
filesToUpload = filesToUpload.filter(displayValidProgressFilesFilter);
// Depending on wether there is an upload going on or not we
// display the progress
if(filesToUpload.length > 0) {
return getLangText('Upload progress') + ': ' + Math.ceil(filesToUpload[0].progress) + '%';
} else {
return fileClassToUpload.singular;
}
},
render() {
let {
multiple,
fileClassToUpload,
allowedExtensions
} = this.props;
return (
<button
onClick={this.handleOnClick}
className="btn btn-default btn-sm margin-left-2px"
disabled={this.getUploadingFiles().length !== 0}>
{this.getButtonLabel()}
<input
multiple={multiple}
ref="fileinput"
type="file"
style={{
display: 'none',
height: 0,
width: 0
}}
onChange={this.handleDrop}
accept={allowedExtensions}/>
</button>
);
}
});
export default UploadButton;

View File

@ -1,13 +1,13 @@
'use strict'; 'use strict';
import React from 'react/addons'; import React from 'react/addons';
import fineUploader from 'fineUploader';
import Router from 'react-router'; import Router from 'react-router';
import Q from 'q'; import Q from 'q';
import S3Fetcher from '../../fetchers/s3_fetcher'; import S3Fetcher from '../../fetchers/s3_fetcher';
import fineUploader from 'fineUploader'; import FileDragAndDrop from './ascribe_file_drag_and_drop/file_drag_and_drop';
import FileDragAndDrop from './file_drag_and_drop';
import GlobalNotificationModel from '../../models/global_notification_model'; import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions'; import GlobalNotificationActions from '../../actions/global_notification_actions';
@ -15,10 +15,11 @@ import GlobalNotificationActions from '../../actions/global_notification_actions
import AppConstants from '../../constants/application_constants'; import AppConstants from '../../constants/application_constants';
import { computeHashOfFile } from '../../utils/file_utils'; import { computeHashOfFile } from '../../utils/file_utils';
import { displayValidFilesFilter } from './react_s3_fine_uploader_utils'; import { displayValidFilesFilter, transformAllowedExtensionsToInputAcceptProp } from './react_s3_fine_uploader_utils';
import { getCookie } from '../../utils/fetch_api_utils'; import { getCookie } from '../../utils/fetch_api_utils';
import { getLangText } from '../../utils/lang_utils'; import { getLangText } from '../../utils/lang_utils';
let ReactS3FineUploader = React.createClass({ let ReactS3FineUploader = React.createClass({
propTypes: { propTypes: {
keyRoutine: React.PropTypes.shape({ keyRoutine: React.PropTypes.shape({
@ -36,7 +37,7 @@ let ReactS3FineUploader = React.createClass({
React.PropTypes.number React.PropTypes.number
]) ])
}), }),
submitKey: React.PropTypes.func, submitFile: React.PropTypes.func,
autoUpload: React.PropTypes.bool, autoUpload: React.PropTypes.bool,
debug: React.PropTypes.bool, debug: React.PropTypes.bool,
objectProperties: React.PropTypes.shape({ objectProperties: React.PropTypes.shape({
@ -83,7 +84,8 @@ let ReactS3FineUploader = React.createClass({
}), }),
validation: React.PropTypes.shape({ validation: React.PropTypes.shape({
itemLimit: React.PropTypes.number, itemLimit: React.PropTypes.number,
sizeLimit: React.PropTypes.string sizeLimit: React.PropTypes.string,
allowedExtensions: React.PropTypes.arrayOf(React.PropTypes.string)
}), }),
messages: React.PropTypes.shape({ messages: React.PropTypes.shape({
unsupportedBrowser: React.PropTypes.string unsupportedBrowser: React.PropTypes.string
@ -110,7 +112,22 @@ let ReactS3FineUploader = React.createClass({
enableLocalHashing: React.PropTypes.bool, enableLocalHashing: React.PropTypes.bool,
// automatically injected by React-Router // automatically injected by React-Router
query: React.PropTypes.object query: React.PropTypes.object,
// A class of a file the user has to upload
// Needs to be defined both in singular as well as in plural
fileClassToUpload: React.PropTypes.shape({
singular: React.PropTypes.string,
plural: React.PropTypes.string
}),
// Uploading functionality of react fineuploader is disconnected from its UI
// layer, which means that literally every (properly adjusted) react element
// can handle the UI handling.
fileInputElement: React.PropTypes.oneOfType([
React.PropTypes.func,
React.PropTypes.element
])
}, },
mixins: [Router.State], mixins: [Router.State],
@ -162,7 +179,12 @@ let ReactS3FineUploader = React.createClass({
return name; return name;
}, },
multiple: false, multiple: false,
defaultErrorMessage: getLangText('Unexpected error. Please contact us if this happens repeatedly.') defaultErrorMessage: getLangText('Unexpected error. Please contact us if this happens repeatedly.'),
fileClassToUpload: {
singular: getLangText('file'),
plural: getLangText('files')
},
fileInputElement: FileDragAndDrop
}; };
}, },
@ -386,12 +408,12 @@ let ReactS3FineUploader = React.createClass({
// Only after the blob has been created server-side, we can make the form submittable. // Only after the blob has been created server-side, we can make the form submittable.
this.createBlob(files[id]) this.createBlob(files[id])
.then(() => { .then(() => {
// since the form validation props isReadyForFormSubmission, setIsUploadReady and submitKey // since the form validation props isReadyForFormSubmission, setIsUploadReady and submitFile
// are optional, we'll only trigger them when they're actually defined // are optional, we'll only trigger them when they're actually defined
if(this.props.submitKey) { if(this.props.submitFile) {
this.props.submitKey(files[id].key); this.props.submitFile(files[id]);
} else { } else {
console.warn('You didn\'t define submitKey in as a prop in react-s3-fine-uploader'); console.warn('You didn\'t define submitFile in as a prop in react-s3-fine-uploader');
} }
// for explanation, check comment of if statement above // for explanation, check comment of if statement above
@ -426,7 +448,7 @@ let ReactS3FineUploader = React.createClass({
}); });
this.state.uploader.cancelAll(); this.state.uploader.cancelAll();
let notification = new GlobalNotificationModel(this.props.defaultErrorMessage, 'danger', 5000); let notification = new GlobalNotificationModel(errorReason || this.props.defaultErrorMessage, 'danger', 5000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
}, },
@ -451,7 +473,7 @@ let ReactS3FineUploader = React.createClass({
let notification = new GlobalNotificationModel(getLangText('File upload canceled'), 'success', 5000); let notification = new GlobalNotificationModel(getLangText('File upload canceled'), 'success', 5000);
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
// since the form validation props isReadyForFormSubmission, setIsUploadReady and submitKey // since the form validation props isReadyForFormSubmission, setIsUploadReady and submitFile
// are optional, we'll only trigger them when they're actually defined // are optional, we'll only trigger them when they're actually defined
if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) { if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) {
if(this.props.isReadyForFormSubmission(this.state.filesToUpload)) { if(this.props.isReadyForFormSubmission(this.state.filesToUpload)) {
@ -518,7 +540,7 @@ let ReactS3FineUploader = React.createClass({
GlobalNotificationActions.appendGlobalNotification(notification); GlobalNotificationActions.appendGlobalNotification(notification);
} }
// since the form validation props isReadyForFormSubmission, setIsUploadReady and submitKey // since the form validation props isReadyForFormSubmission, setIsUploadReady and submitFile
// are optional, we'll only trigger them when they're actually defined // are optional, we'll only trigger them when they're actually defined
if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) { if(this.props.isReadyForFormSubmission && this.props.setIsUploadReady) {
// also, lets check if after the completion of this upload, // also, lets check if after the completion of this upload,
@ -818,27 +840,48 @@ let ReactS3FineUploader = React.createClass({
}, },
getAllowedExtensions() {
let { validation } = this.props;
if(validation && validation.allowedExtensions && validation.allowedExtensions.length > 0) {
return transformAllowedExtensionsToInputAcceptProp(validation.allowedExtensions);
} else {
return null;
}
},
render() { render() {
return ( let {
<div> multiple,
<FileDragAndDrop areAssetsDownloadable,
className="file-drag-and-drop" areAssetsEditable,
onDrop={this.handleUploadFile} onInactive,
filesToUpload={this.state.filesToUpload} enableLocalHashing,
handleDeleteFile={this.handleDeleteFile} fileClassToUpload,
handleCancelFile={this.handleCancelFile} validation,
handlePauseFile={this.handlePauseFile} fileInputElement
handleResumeFile={this.handleResumeFile} } = this.props;
handleCancelHashing={this.handleCancelHashing}
multiple={this.props.multiple} // Here we initialize the template that has been either provided from the outside
areAssetsDownloadable={this.props.areAssetsDownloadable} // or the default input that is FileDragAndDrop.
areAssetsEditable={this.props.areAssetsEditable} return React.createElement(fileInputElement, {
onInactive={this.props.onInactive} onDrop: this.handleUploadFile,
dropzoneInactive={this.isDropzoneInactive()} filesToUpload: this.state.filesToUpload,
hashingProgress={this.state.hashingProgress} handleDeleteFile: this.handleDeleteFile,
enableLocalHashing={this.props.enableLocalHashing} /> handleCancelFile: this.handleCancelFile,
</div> handlePauseFile: this.handlePauseFile,
); handleResumeFile: this.handleResumeFile,
handleCancelHashing: this.handleCancelHashing,
multiple: multiple,
areAssetsDownloadable: areAssetsDownloadable,
areAssetsEditable: areAssetsEditable,
onInactive: onInactive,
dropzoneInactive: this.isDropzoneInactive(),
hashingProgress: this.state.hashingProgress,
enableLocalHashing: enableLocalHashing,
fileClassToUpload: fileClassToUpload,
allowedExtensions: this.getAllowedExtensions()
});
} }
}); });

View File

@ -52,3 +52,22 @@ export function displayValidProgressFilesFilter(file) {
return file.status !== 'deleted' && file.status !== 'canceled' && file.status !== 'online'; return file.status !== 'deleted' && file.status !== 'canceled' && file.status !== 'online';
} }
/**
* Fineuploader allows to specify the file extensions that are allowed to upload.
* For our self defined input, we can reuse those declarations to restrict which files
* the user can pick from his hard drive.
*
* Takes an array of file extensions (['pdf', 'png', ...]) and transforms them into a string
* that can be passed into an html5 input via its 'accept' prop.
* @param {array} allowedExtensions Array of strings without a dot prefixed
* @return {string} Joined string (comma-separated) of the passed-in array
*/
export function transformAllowedExtensionsToInputAcceptProp(allowedExtensions) {
// add a dot in front of the extension
let prefixedAllowedExtensions = allowedExtensions.map((ext) => '.' + ext);
// generate a comma separated list to add them to the DOM element
// See: http://stackoverflow.com/questions/4328947/limit-file-format-when-using-input-type-file
return prefixedAllowedExtensions.join(', ');
}

View File

@ -112,7 +112,7 @@ let CylandAdditionalDataForm = React.createClass({
</Property> </Property>
<FurtherDetailsFileuploader <FurtherDetailsFileuploader
uploadStarted={this.uploadStarted} uploadStarted={this.uploadStarted}
submitKey={this.submitKey} submitFile={this.submitFile}
setIsUploadReady={this.setIsUploadReady} setIsUploadReady={this.setIsUploadReady}
isReadyForFormSubmission={formSubmissionValidation.fileOptional} isReadyForFormSubmission={formSubmissionValidation.fileOptional}
editable={!this.props.disabled} editable={!this.props.disabled}

View File

@ -57,6 +57,19 @@ let constants = {
// Source: http://www.w3schools.com/tags/att_input_type.asp // Source: http://www.w3schools.com/tags/att_input_type.asp
'possibleInputTypes': ['button', 'checkbox', 'color', 'date', 'datetime', 'datetime-local', 'email', 'file', 'hidden', 'image', 'month', 'number', 'password', 'radio', 'range', 'reset', 'search', 'submit', 'tel', 'text', 'time', 'url', 'week'], 'possibleInputTypes': ['button', 'checkbox', 'color', 'date', 'datetime', 'datetime-local', 'email', 'file', 'hidden', 'image', 'month', 'number', 'password', 'radio', 'range', 'reset', 'search', 'submit', 'tel', 'text', 'time', 'url', 'week'],
'fineUploader': {
'validation': {
'additionalData': {
'itemLimit': 100,
'sizeLimit': '50000000'
},
'registerWork': {
'itemLimit': 1,
'sizeLimit': '25000000000'
}
}
},
// in case of whitelabel customization, we store stuff here // in case of whitelabel customization, we store stuff here
'whitelabel': {}, 'whitelabel': {},
'raven': { 'raven': {

View File

@ -48,7 +48,7 @@ let OwnershipFetcher = {
return requests.get(ApiUrls.ownership_loans_pieces_request); return requests.get(ApiUrls.ownership_loans_pieces_request);
}, },
makeContractPublic(contractObj){ changeContract(contractObj){
return requests.put(ApiUrls.ownership_contract, { body: contractObj, contract_id: contractObj.id }); return requests.put(ApiUrls.ownership_contract, { body: contractObj, contract_id: contractObj.id });
}, },

View File

@ -24,9 +24,8 @@
background: none; background: none;
border: 0; border: 0;
width: 100%; width: 100%;
/* Shrink the size of the headline for a nested element */ .ascribe-collapsible-wrapper {
.ascribe-collapsible-wrapper > .ascribe-collapsible-content {
padding-left: 1em; padding-left: 1em;
font-size: 95%; font-size: 95%;
} }

View File

@ -9,6 +9,7 @@
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
cursor: default !important; cursor: default !important;
padding: 1.5em 0 1.5em 0;
.file-drag-and-drop-dialog > p:first-child { .file-drag-and-drop-dialog > p:first-child {
font-size: 1.5em !important; font-size: 1.5em !important;

View File

@ -486,4 +486,4 @@ hr {
.ascribe-progress-bar-xs { .ascribe-progress-bar-xs {
height: 12px; height: 12px;
} }