2015-06-26 00:38:40 +02:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
import React from 'react';
|
|
|
|
import Router from 'react-router';
|
|
|
|
|
|
|
|
import Row from 'react-bootstrap/lib/Row';
|
|
|
|
import Col from 'react-bootstrap/lib/Col';
|
|
|
|
import Button from 'react-bootstrap/lib/Button';
|
|
|
|
import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
|
|
|
|
|
|
|
import UserActions from '../actions/user_actions';
|
|
|
|
import UserStore from '../stores/user_store';
|
|
|
|
import CoaActions from '../actions/coa_actions';
|
|
|
|
import CoaStore from '../stores/coa_store';
|
|
|
|
|
|
|
|
import MediaPlayer from './ascribe_media/media_player';
|
|
|
|
|
|
|
|
import CollapsibleParagraph from './ascribe_collapsible/collapsible_paragraph';
|
|
|
|
|
|
|
|
import Form from './ascribe_forms/form';
|
|
|
|
import Property from './ascribe_forms/property';
|
|
|
|
import InputTextAreaToggable from './ascribe_forms/input_textarea_toggable';
|
|
|
|
|
|
|
|
import PieceExtraDataForm from './ascribe_forms/form_piece_extradata';
|
|
|
|
import RequestActionForm from './ascribe_forms/form_request_action';
|
|
|
|
|
|
|
|
import EditionActions from '../actions/edition_actions';
|
|
|
|
import AclButtonList from './ascribe_buttons/acl_button_list';
|
|
|
|
|
2015-06-29 10:00:26 +02:00
|
|
|
import ReactS3FineUploader from './ascribe_uploader/react_s3_fine_uploader';
|
|
|
|
|
2015-06-26 00:38:40 +02:00
|
|
|
import GlobalNotificationModel from '../models/global_notification_model';
|
|
|
|
import GlobalNotificationActions from '../actions/global_notification_actions';
|
|
|
|
|
|
|
|
import apiUrls from '../constants/api_urls';
|
|
|
|
import AppConstants from '../constants/application_constants';
|
|
|
|
|
2015-06-29 16:46:12 +02:00
|
|
|
import { getCookie } from '../utils/fetch_api_utils';
|
2015-07-03 19:08:56 +02:00
|
|
|
import { getLangText } from '../utils/lang_utils';
|
2015-06-29 16:46:12 +02:00
|
|
|
|
2015-06-26 00:38:40 +02:00
|
|
|
let Link = Router.Link;
|
|
|
|
/**
|
|
|
|
* This is the component that implements display-specific functionality
|
|
|
|
*/
|
|
|
|
let Edition = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
edition: React.PropTypes.object,
|
|
|
|
loadEdition: React.PropTypes.func
|
|
|
|
},
|
|
|
|
|
|
|
|
getInitialState() {
|
|
|
|
return UserStore.getState();
|
|
|
|
},
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
UserStore.listen(this.onChange);
|
|
|
|
UserActions.fetchCurrentUser();
|
|
|
|
},
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
UserStore.unlisten(this.onChange);
|
|
|
|
},
|
|
|
|
|
|
|
|
onChange(state) {
|
|
|
|
this.setState(state);
|
|
|
|
},
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<Row>
|
|
|
|
<Col md={6}>
|
2015-07-08 10:35:45 +02:00
|
|
|
<MediaContainer edition={this.props.edition}/>
|
2015-06-26 00:38:40 +02:00
|
|
|
</Col>
|
|
|
|
<Col md={6} className="ascribe-edition-details">
|
|
|
|
<EditionHeader edition={this.props.edition}/>
|
|
|
|
<EditionSummary
|
|
|
|
currentUser={this.state.currentUser}
|
|
|
|
edition={this.props.edition} />
|
|
|
|
|
|
|
|
<CollapsibleParagraph
|
|
|
|
title="Notes"
|
|
|
|
show={(this.state.currentUser.username && true || false) ||
|
|
|
|
(this.props.edition.acl.indexOf('edit') > -1 || this.props.edition.public_note)}>
|
|
|
|
<EditionPersonalNote
|
|
|
|
currentUser={this.state.currentUser}
|
|
|
|
handleSuccess={this.props.loadEdition}
|
|
|
|
edition={this.props.edition}/>
|
|
|
|
<EditionPublicEditionNote
|
|
|
|
handleSuccess={this.props.loadEdition}
|
|
|
|
edition={this.props.edition}/>
|
|
|
|
</CollapsibleParagraph>
|
|
|
|
|
|
|
|
<CollapsibleParagraph
|
2015-07-03 19:08:56 +02:00
|
|
|
title={getLangText('Further Details')}
|
2015-06-30 17:12:51 +02:00
|
|
|
show={this.props.edition.acl.indexOf('edit') > -1
|
|
|
|
|| Object.keys(this.props.edition.extra_data).length > 0
|
|
|
|
|| this.props.edition.other_data !== null}>
|
2015-06-26 00:38:40 +02:00
|
|
|
<EditionFurtherDetails
|
|
|
|
handleSuccess={this.props.loadEdition}
|
|
|
|
edition={this.props.edition}/>
|
|
|
|
</CollapsibleParagraph>
|
|
|
|
|
|
|
|
<CollapsibleParagraph
|
2015-07-03 19:08:56 +02:00
|
|
|
title={getLangText('Certificate of Authenticity')}
|
2015-06-26 00:38:40 +02:00
|
|
|
show={this.props.edition.acl.indexOf('coa') > -1}>
|
|
|
|
<CoaDetails
|
|
|
|
edition={this.props.edition}/>
|
|
|
|
</CollapsibleParagraph>
|
|
|
|
|
|
|
|
<CollapsibleParagraph
|
2015-07-03 19:08:56 +02:00
|
|
|
title={getLangText('Provenance/Ownership History')}
|
2015-06-26 00:38:40 +02:00
|
|
|
show={this.props.edition.ownership_history && this.props.edition.ownership_history.length > 0}>
|
|
|
|
<EditionDetailHistoryIterator
|
|
|
|
history={this.props.edition.ownership_history} />
|
|
|
|
</CollapsibleParagraph>
|
|
|
|
|
|
|
|
<CollapsibleParagraph
|
2015-07-03 19:08:56 +02:00
|
|
|
title={getLangText('Consignment History')}
|
2015-06-26 00:38:40 +02:00
|
|
|
show={this.props.edition.consign_history && this.props.edition.consign_history.length > 0}>
|
|
|
|
<EditionDetailHistoryIterator
|
|
|
|
history={this.props.edition.consign_history} />
|
|
|
|
</CollapsibleParagraph>
|
|
|
|
|
|
|
|
<CollapsibleParagraph
|
2015-07-03 19:08:56 +02:00
|
|
|
title={getLangText('Loan History')}
|
2015-06-26 00:38:40 +02:00
|
|
|
show={this.props.edition.loan_history && this.props.edition.loan_history.length > 0}>
|
|
|
|
<EditionDetailHistoryIterator
|
|
|
|
history={this.props.edition.loan_history} />
|
|
|
|
</CollapsibleParagraph>
|
|
|
|
|
|
|
|
<CollapsibleParagraph
|
2015-07-03 19:08:56 +02:00
|
|
|
title={getLangText('SPOOL Details')}>
|
2015-07-03 12:35:45 +02:00
|
|
|
<SpoolDetails
|
|
|
|
edition={this.props.edition} />
|
2015-06-26 00:38:40 +02:00
|
|
|
</CollapsibleParagraph>
|
|
|
|
</Col>
|
|
|
|
</Row>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-07-03 12:35:45 +02:00
|
|
|
let MediaContainer = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
edition: React.PropTypes.object
|
|
|
|
},
|
|
|
|
|
|
|
|
render() {
|
|
|
|
let thumbnail = this.props.edition.thumbnail;
|
|
|
|
let mimetype = this.props.edition.digital_work.mime;
|
|
|
|
let embed = null;
|
|
|
|
let extraData = null;
|
2015-07-08 10:35:45 +02:00
|
|
|
let encodingStatus = this.props.edition.digital_work.isEncoding;
|
2015-07-03 12:35:45 +02:00
|
|
|
|
|
|
|
if (this.props.edition.digital_work.encoding_urls) {
|
|
|
|
extraData = this.props.edition.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; });
|
|
|
|
}
|
|
|
|
|
|
|
|
if (['video', 'audio'].indexOf(mimetype) > -1){
|
|
|
|
embed = (
|
|
|
|
<CollapsibleButton
|
|
|
|
button={
|
|
|
|
<Button bsSize="xsmall" className="ascribe-margin-1px">
|
|
|
|
Embed
|
|
|
|
</Button>
|
|
|
|
}
|
|
|
|
panel={
|
2015-07-03 15:52:42 +02:00
|
|
|
<pre className="">
|
|
|
|
{'<iframe width="560" height="315" src="http://embed.ascribe.io/edition/'
|
|
|
|
+ this.props.edition.bitcoin_id + '" frameborder="0" allowfullscreen></iframe>'
|
|
|
|
}
|
|
|
|
</pre>
|
2015-07-03 12:35:45 +02:00
|
|
|
}/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<MediaPlayer mimetype={mimetype}
|
2015-07-08 10:35:45 +02:00
|
|
|
preview={thumbnail}
|
|
|
|
url={this.props.edition.digital_work.url}
|
|
|
|
extraData={extraData}
|
|
|
|
encodingStatus={encodingStatus} />
|
2015-07-03 12:35:45 +02:00
|
|
|
<p className="text-center">
|
|
|
|
<Button bsSize="xsmall" className="ascribe-margin-1px" href={this.props.edition.digital_work.url} target="_blank">
|
2015-07-03 19:08:56 +02:00
|
|
|
{getLangText('Download')} <Glyphicon glyph="cloud-download"/>
|
2015-07-03 12:35:45 +02:00
|
|
|
</Button>
|
|
|
|
{embed}
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const CollapsibleButton = React.createClass({
|
|
|
|
|
|
|
|
propTypes: {
|
|
|
|
button: React.PropTypes.object,
|
|
|
|
children: React.PropTypes.oneOfType([
|
|
|
|
React.PropTypes.object,
|
|
|
|
React.PropTypes.array
|
|
|
|
])
|
|
|
|
},
|
|
|
|
|
|
|
|
getInitialState() {
|
|
|
|
return {expanded: false};
|
|
|
|
},
|
|
|
|
handleToggle(e){
|
|
|
|
e.preventDefault();
|
|
|
|
this.setState({expanded: !this.state.expanded});
|
|
|
|
},
|
|
|
|
render() {
|
|
|
|
let isVisible = (this.state.expanded) ? '' : 'invisible';
|
|
|
|
return (
|
|
|
|
<span>
|
|
|
|
<span onClick={this.handleToggle}>
|
|
|
|
{this.props.button}
|
|
|
|
</span>
|
|
|
|
<div ref='panel' className={isVisible}>
|
|
|
|
{this.props.panel}
|
|
|
|
</div>
|
|
|
|
</span>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2015-06-26 00:38:40 +02:00
|
|
|
let EditionHeader = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
edition: React.PropTypes.object
|
|
|
|
},
|
|
|
|
|
|
|
|
render() {
|
|
|
|
var titleHtml = <div className="ascribe-detail-title">{this.props.edition.title}</div>;
|
|
|
|
return (
|
|
|
|
<div className="ascribe-detail-header">
|
2015-07-03 19:08:56 +02:00
|
|
|
<EditionDetailProperty label={getLangText('TITLE')} value={titleHtml} />
|
|
|
|
<EditionDetailProperty label={getLangText('BY')} value={this.props.edition.artist_name} />
|
|
|
|
<EditionDetailProperty label={getLangText('DATE')} value={ this.props.edition.date_created.slice(0, 4) } />
|
2015-06-26 00:38:40 +02:00
|
|
|
<hr/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
let EditionSummary = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
edition: React.PropTypes.object
|
|
|
|
},
|
|
|
|
|
2015-06-26 11:44:35 +02:00
|
|
|
getTransferWithdrawData(){
|
|
|
|
return {'bitcoin_id': this.props.edition.bitcoin_id};
|
|
|
|
},
|
2015-06-26 00:38:40 +02:00
|
|
|
handleSuccess(){
|
|
|
|
EditionActions.fetchOne(this.props.edition.id);
|
|
|
|
},
|
|
|
|
showNotification(response){
|
|
|
|
this.handleSuccess();
|
|
|
|
let notification = new GlobalNotificationModel(response.notification, 'success');
|
|
|
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
|
|
|
},
|
|
|
|
render() {
|
|
|
|
let status = null;
|
|
|
|
if (this.props.edition.status.length > 0){
|
|
|
|
let statusStr = this.props.edition.status.join().replace(/_/, ' ');
|
2015-07-03 19:08:56 +02:00
|
|
|
status = <EditionDetailProperty label={getLangText('STATUS')} value={ statusStr }/>;
|
2015-06-26 00:38:40 +02:00
|
|
|
if (this.props.edition.pending_new_owner && this.props.edition.acl.indexOf('withdraw_transfer') > -1){
|
2015-06-26 11:44:35 +02:00
|
|
|
status = (
|
|
|
|
<Form
|
|
|
|
url={apiUrls.ownership_transfers_withdraw}
|
|
|
|
getFormData={this.getTransferWithdrawData}
|
|
|
|
handleSuccess={this.showNotification}>
|
2015-07-03 19:08:56 +02:00
|
|
|
<EditionDetailProperty label={getLangText('STATUS')} value={ statusStr }>
|
2015-06-26 11:44:35 +02:00
|
|
|
<button
|
|
|
|
type="submit"
|
|
|
|
className="pull-right btn btn-default btn-sm">
|
2015-07-03 19:08:56 +02:00
|
|
|
{getLangText('WITHDRAW')}
|
2015-06-26 11:44:35 +02:00
|
|
|
</button>
|
|
|
|
</EditionDetailProperty>
|
|
|
|
</Form>
|
2015-06-26 00:38:40 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let actions = null;
|
|
|
|
if (this.props.edition.request_action && this.props.edition.request_action.length > 0){
|
|
|
|
actions = (
|
|
|
|
<RequestActionForm
|
|
|
|
editions={ [this.props.edition] }
|
|
|
|
handleSuccess={this.showNotification}/>);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
actions = (
|
|
|
|
<Row>
|
|
|
|
<Col md={12}>
|
|
|
|
<AclButtonList
|
2015-06-30 17:12:13 +02:00
|
|
|
className="text-center ascribe-button-list"
|
2015-06-26 00:38:40 +02:00
|
|
|
availableAcls={this.props.edition.acl}
|
|
|
|
editions={[this.props.edition]}
|
|
|
|
handleSuccess={this.handleSuccess} />
|
|
|
|
</Col>
|
|
|
|
</Row>);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div className="ascribe-detail-header">
|
2015-07-03 19:08:56 +02:00
|
|
|
<EditionDetailProperty label={getLangText('EDITION')}
|
|
|
|
value={this.props.edition.edition_number + ' ' + getLangText('of') + ' ' + this.props.edition.num_editions} />
|
|
|
|
<EditionDetailProperty label={getLangText('ID')} value={ this.props.edition.bitcoin_id } />
|
|
|
|
<EditionDetailProperty label={getLangText('OWNER')} value={ this.props.edition.owner } />
|
2015-06-26 00:38:40 +02:00
|
|
|
{status}
|
|
|
|
<br/>
|
|
|
|
{actions}
|
|
|
|
<hr/>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
let EditionDetailProperty = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
label: React.PropTypes.string,
|
|
|
|
value: React.PropTypes.oneOfType([
|
|
|
|
React.PropTypes.string,
|
|
|
|
React.PropTypes.element
|
|
|
|
]),
|
|
|
|
separator: React.PropTypes.string,
|
|
|
|
labelClassName: React.PropTypes.string,
|
|
|
|
valueClassName: React.PropTypes.string
|
|
|
|
},
|
|
|
|
|
|
|
|
getDefaultProps() {
|
|
|
|
return {
|
|
|
|
separator: ':',
|
|
|
|
labelClassName: 'col-xs-5 col-sm-4 col-md-3 col-lg-3',
|
|
|
|
valueClassName: 'col-xs-7 col-sm-8 col-md-9 col-lg-9'
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
|
|
|
render() {
|
|
|
|
let value = this.props.value;
|
|
|
|
if (this.props.children){
|
|
|
|
value = (
|
|
|
|
<div className="row-same-height">
|
|
|
|
<div className="col-xs-6 col-xs-height col-bottom no-padding">
|
|
|
|
{ this.props.value }
|
|
|
|
</div>
|
|
|
|
<div className="col-xs-6 col-xs-height">
|
|
|
|
{ this.props.children }
|
|
|
|
</div>
|
|
|
|
</div>);
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<div className="row ascribe-detail-property">
|
|
|
|
<div className="row-same-height">
|
|
|
|
<div className={this.props.labelClassName + ' col-xs-height col-bottom'}>
|
|
|
|
<div>{ this.props.label + this.props.separator}</div>
|
|
|
|
</div>
|
|
|
|
<div className={this.props.valueClassName + ' col-xs-height col-bottom'}>
|
|
|
|
{value}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let EditionDetailHistoryIterator = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
history: React.PropTypes.array
|
|
|
|
},
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<Form>
|
|
|
|
{this.props.history.map((historicalEvent, i) => {
|
2015-06-26 01:05:20 +02:00
|
|
|
return (
|
2015-06-26 00:38:40 +02:00
|
|
|
<Property
|
|
|
|
name={i}
|
|
|
|
key={i}
|
|
|
|
label={ historicalEvent[0] }
|
|
|
|
editable={false}>
|
|
|
|
<pre className="ascribe-pre">{ historicalEvent[1] }</pre>
|
|
|
|
</Property>
|
2015-06-26 01:05:20 +02:00
|
|
|
);
|
2015-06-26 00:38:40 +02:00
|
|
|
})}
|
|
|
|
<hr />
|
|
|
|
</Form>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let EditionPersonalNote = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
edition: React.PropTypes.object,
|
|
|
|
currentUser: React.PropTypes.object,
|
|
|
|
handleSuccess: React.PropTypes.func
|
|
|
|
},
|
|
|
|
showNotification(){
|
|
|
|
this.props.handleSuccess();
|
2015-07-03 19:08:56 +02:00
|
|
|
let notification = new GlobalNotificationModel(getLangText('Private note saved'), 'success');
|
2015-06-26 00:38:40 +02:00
|
|
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
|
|
|
},
|
|
|
|
|
|
|
|
render() {
|
|
|
|
if (this.props.currentUser.username && true || false) {
|
|
|
|
return (
|
|
|
|
<Form
|
|
|
|
url={apiUrls.note_notes}
|
|
|
|
handleSuccess={this.showNotification}>
|
|
|
|
<Property
|
|
|
|
name='note'
|
2015-07-03 19:08:56 +02:00
|
|
|
label={getLangText('Personal note (private)')}
|
2015-06-26 00:38:40 +02:00
|
|
|
editable={true}>
|
|
|
|
<InputTextAreaToggable
|
|
|
|
rows={3}
|
|
|
|
editable={true}
|
|
|
|
defaultValue={this.props.edition.note_from_user}
|
2015-07-03 19:08:56 +02:00
|
|
|
placeholder={getLangText('Enter a personal note%s', '...')}
|
2015-06-26 00:38:40 +02:00
|
|
|
required/>
|
|
|
|
</Property>
|
|
|
|
<Property hidden={true} name='bitcoin_id'>
|
|
|
|
<input defaultValue={this.props.edition.bitcoin_id}/>
|
|
|
|
</Property>
|
|
|
|
<hr />
|
|
|
|
</Form>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let EditionPublicEditionNote = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
edition: React.PropTypes.object,
|
|
|
|
handleSuccess: React.PropTypes.func
|
|
|
|
},
|
|
|
|
showNotification(){
|
|
|
|
this.props.handleSuccess();
|
2015-07-08 10:15:58 +02:00
|
|
|
let notification = new GlobalNotificationModel(getLangText('Public note saved'), 'success');
|
2015-06-26 00:38:40 +02:00
|
|
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
|
|
|
},
|
|
|
|
render() {
|
|
|
|
let isEditable = this.props.edition.acl.indexOf('edit') > -1;
|
|
|
|
if (this.props.edition.acl.indexOf('edit') > -1 || this.props.edition.public_note){
|
|
|
|
return (
|
|
|
|
<Form
|
|
|
|
url={apiUrls.note_edition}
|
|
|
|
handleSuccess={this.showNotification}>
|
|
|
|
<Property
|
|
|
|
name='note'
|
2015-07-03 19:08:56 +02:00
|
|
|
label={getLangText('Edition note (public)')}
|
2015-06-26 00:38:40 +02:00
|
|
|
editable={isEditable}>
|
|
|
|
<InputTextAreaToggable
|
|
|
|
rows={3}
|
|
|
|
editable={isEditable}
|
|
|
|
defaultValue={this.props.edition.public_note}
|
2015-07-03 19:08:56 +02:00
|
|
|
placeholder={getLangText('Enter a public note for this edition%', '...')}
|
2015-06-26 00:38:40 +02:00
|
|
|
required/>
|
|
|
|
</Property>
|
|
|
|
<Property hidden={true} name='bitcoin_id'>
|
|
|
|
<input defaultValue={this.props.edition.bitcoin_id}/>
|
|
|
|
</Property>
|
|
|
|
<hr />
|
|
|
|
</Form>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
let EditionFurtherDetails = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
edition: React.PropTypes.object,
|
|
|
|
handleSuccess: React.PropTypes.func
|
|
|
|
},
|
2015-06-29 16:00:26 +02:00
|
|
|
|
|
|
|
getInitialState() {
|
|
|
|
return {
|
|
|
|
loading: false
|
|
|
|
};
|
|
|
|
},
|
|
|
|
|
2015-06-26 00:38:40 +02:00
|
|
|
showNotification(){
|
|
|
|
this.props.handleSuccess();
|
2015-07-03 19:08:56 +02:00
|
|
|
let notification = new GlobalNotificationModel(getLangText('Details updated'), 'success');
|
2015-06-26 00:38:40 +02:00
|
|
|
GlobalNotificationActions.appendGlobalNotification(notification);
|
|
|
|
},
|
|
|
|
|
2015-06-29 16:00:26 +02:00
|
|
|
submitKey(key){
|
|
|
|
this.setState({
|
|
|
|
otherDataKey: key
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
2015-06-30 09:20:20 +02:00
|
|
|
setIsUploadReady(isReady) {
|
2015-06-29 16:00:26 +02:00
|
|
|
this.setState({
|
2015-06-30 09:20:20 +02:00
|
|
|
isUploadReady: isReady
|
2015-06-29 16:00:26 +02:00
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
isReadyForFormSubmission(files) {
|
|
|
|
files = files.filter((file) => file.status !== 'deleted' && file.status !== 'canceled');
|
|
|
|
if(files.length > 0 && files[0].status === 'upload successful') {
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2015-06-26 00:38:40 +02:00
|
|
|
render() {
|
|
|
|
let editable = this.props.edition.acl.indexOf('edit') > -1;
|
2015-07-01 12:13:05 +02:00
|
|
|
|
2015-06-26 00:38:40 +02:00
|
|
|
return (
|
|
|
|
<Row>
|
|
|
|
<Col md={12} className="ascribe-edition-personal-note">
|
|
|
|
<PieceExtraDataForm
|
|
|
|
name='artist_contact_info'
|
2015-07-03 19:08:56 +02:00
|
|
|
title={getLangText('Artist Contact Info')}
|
2015-06-26 00:38:40 +02:00
|
|
|
handleSuccess={this.showNotification}
|
|
|
|
editable={editable}
|
|
|
|
edition={this.props.edition} />
|
|
|
|
<PieceExtraDataForm
|
|
|
|
name='display_instructions'
|
2015-07-03 19:08:56 +02:00
|
|
|
title={getLangText('Display Instructions')}
|
2015-06-26 00:38:40 +02:00
|
|
|
handleSuccess={this.showNotification}
|
|
|
|
editable={editable}
|
|
|
|
edition={this.props.edition} />
|
|
|
|
<PieceExtraDataForm
|
|
|
|
name='technology_details'
|
2015-07-03 19:08:56 +02:00
|
|
|
title={getLangText('Technology Details')}
|
2015-06-26 00:38:40 +02:00
|
|
|
handleSuccess={this.showNotification}
|
|
|
|
editable={editable}
|
|
|
|
edition={this.props.edition} />
|
2015-06-29 10:00:26 +02:00
|
|
|
<FileUploader
|
2015-06-29 16:00:26 +02:00
|
|
|
submitKey={this.submitKey}
|
2015-06-30 09:20:20 +02:00
|
|
|
setIsUploadReady={this.setIsUploadReady}
|
2015-06-29 16:00:26 +02:00
|
|
|
isReadyForFormSubmission={this.isReadyForFormSubmission}
|
2015-06-30 17:12:51 +02:00
|
|
|
editable={editable}
|
2015-06-29 16:00:26 +02:00
|
|
|
edition={this.props.edition}/>
|
2015-06-26 00:38:40 +02:00
|
|
|
</Col>
|
|
|
|
</Row>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-06-29 16:00:26 +02:00
|
|
|
let FileUploader = React.createClass({
|
|
|
|
propTypes: {
|
2015-06-30 09:23:26 +02:00
|
|
|
edition: React.PropTypes.object,
|
2015-06-30 09:20:20 +02:00
|
|
|
setIsUploadReady: React.PropTypes.func,
|
2015-06-29 16:00:26 +02:00
|
|
|
submitKey: React.PropTypes.func,
|
2015-07-01 12:13:05 +02:00
|
|
|
isReadyForFormSubmission: React.PropTypes.func,
|
|
|
|
editable: React.PropTypes.bool
|
2015-06-29 10:00:26 +02:00
|
|
|
},
|
2015-06-29 16:00:26 +02:00
|
|
|
|
2015-06-29 10:00:26 +02:00
|
|
|
render() {
|
2015-07-01 12:13:05 +02:00
|
|
|
// Essentially there a three cases important to the fileuploader
|
|
|
|
//
|
|
|
|
// 1. there is no other_data => do not show the fileuploader at all
|
|
|
|
// 2. there is other_data, but user has no edit rights => show fileuploader but without action buttons
|
|
|
|
// 3. both other_data and editable are defined or true => show fileuploade with all action buttons
|
|
|
|
if (!this.props.editable && !this.props.edition.other_data){
|
2015-06-30 17:12:51 +02:00
|
|
|
return null;
|
|
|
|
}
|
2015-06-29 10:00:26 +02:00
|
|
|
return (
|
2015-06-30 17:12:51 +02:00
|
|
|
<Form>
|
|
|
|
<Property
|
2015-07-03 19:08:56 +02:00
|
|
|
label={getLangText('Additional files')}>
|
2015-06-30 17:12:51 +02:00
|
|
|
<ReactS3FineUploader
|
|
|
|
keyRoutine={{
|
|
|
|
url: AppConstants.serverUrl + 's3/key/',
|
|
|
|
fileClass: 'otherdata',
|
|
|
|
bitcoinId: this.props.edition.bitcoin_id
|
|
|
|
}}
|
|
|
|
createBlobRoutine={{
|
|
|
|
url: apiUrls.blob_otherdatas,
|
|
|
|
bitcoinId: this.props.edition.bitcoin_id
|
|
|
|
}}
|
|
|
|
validation={{
|
|
|
|
itemLimit: 100000,
|
|
|
|
sizeLimit: '10000000'
|
|
|
|
}}
|
|
|
|
submitKey={this.props.submitKey}
|
|
|
|
setIsUploadReady={this.props.setIsUploadReady}
|
|
|
|
isReadyForFormSubmission={this.props.isReadyForFormSubmission}
|
|
|
|
session={{
|
|
|
|
endpoint: AppConstants.serverUrl + 'api/blob/otherdatas/fineuploader_session/',
|
|
|
|
customHeaders: {
|
|
|
|
'X-CSRFToken': getCookie('csrftoken')
|
|
|
|
},
|
|
|
|
params: {
|
|
|
|
'pk': this.props.edition.other_data ? this.props.edition.other_data.id : null
|
2015-07-02 11:54:33 +02:00
|
|
|
},
|
|
|
|
cors: {
|
|
|
|
expected: true,
|
|
|
|
sendCredentials: true
|
2015-06-30 17:12:51 +02:00
|
|
|
}
|
|
|
|
}}
|
2015-07-02 16:51:22 +02:00
|
|
|
signature={{
|
|
|
|
endpoint: AppConstants.serverUrl + 's3/signature/',
|
|
|
|
customHeaders: {
|
|
|
|
'X-CSRFToken': getCookie('csrftoken')
|
|
|
|
}
|
|
|
|
}}
|
|
|
|
deleteFile={{
|
|
|
|
enabled: true,
|
|
|
|
method: 'DELETE',
|
|
|
|
endpoint: AppConstants.serverUrl + 's3/delete',
|
|
|
|
customHeaders: {
|
|
|
|
'X-CSRFToken': getCookie('csrftoken')
|
|
|
|
}
|
|
|
|
}}
|
2015-07-01 12:13:05 +02:00
|
|
|
areAssetsDownloadable={true}
|
|
|
|
areAssetsEditable={this.props.editable}/>
|
2015-06-30 17:12:51 +02:00
|
|
|
</Property>
|
|
|
|
<hr />
|
|
|
|
</Form>
|
2015-06-29 10:00:26 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-06-26 00:38:40 +02:00
|
|
|
let CoaDetails = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
edition: React.PropTypes.object
|
|
|
|
},
|
|
|
|
|
|
|
|
getInitialState() {
|
|
|
|
return CoaStore.getState();
|
|
|
|
},
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
CoaStore.listen(this.onChange);
|
|
|
|
if (this.props.edition.coa) {
|
|
|
|
CoaActions.fetchOne(this.props.edition.coa);
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
console.log('create coa');
|
|
|
|
CoaActions.create(this.props.edition);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
CoaStore.unlisten(this.onChange);
|
|
|
|
},
|
|
|
|
|
|
|
|
onChange(state) {
|
|
|
|
this.setState(state);
|
|
|
|
},
|
|
|
|
|
|
|
|
render() {
|
|
|
|
if (this.state.coa.url_safe) {
|
|
|
|
return (
|
|
|
|
<div>
|
2015-06-30 17:12:13 +02:00
|
|
|
<p className="text-center ascribe-button-list">
|
2015-07-02 19:22:32 +02:00
|
|
|
<a href={this.state.coa.url_safe} target="_blank">
|
|
|
|
<button className="btn btn-default btn-xs">
|
2015-07-03 19:08:56 +02:00
|
|
|
{getLangText('Download')} <Glyphicon glyph="cloud-download"/>
|
2015-07-02 19:22:32 +02:00
|
|
|
</button>
|
|
|
|
</a>
|
2015-06-26 00:38:40 +02:00
|
|
|
<Link to="coa_verify">
|
2015-06-30 17:12:13 +02:00
|
|
|
<button className="btn btn-default btn-xs">
|
2015-07-03 19:08:56 +02:00
|
|
|
{getLangText('Verify')} <Glyphicon glyph="check"/>
|
2015-06-30 17:12:13 +02:00
|
|
|
</button>
|
2015-06-26 00:38:40 +02:00
|
|
|
</Link>
|
|
|
|
|
|
|
|
</p>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<div className="text-center">
|
|
|
|
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_medium.gif'} />
|
|
|
|
</div>);
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2015-07-03 12:35:45 +02:00
|
|
|
let SpoolDetails = React.createClass({
|
|
|
|
propTypes: {
|
|
|
|
edition: React.PropTypes.object
|
|
|
|
},
|
|
|
|
|
|
|
|
render() {
|
|
|
|
let bitcoinIdValue = (
|
|
|
|
<a target="_blank" href={'https://www.blocktrail.com/BTC/address/' + this.props.edition.bitcoin_id}>{this.props.edition.bitcoin_id}</a>
|
|
|
|
);
|
|
|
|
|
|
|
|
let hashOfArtwork = (
|
|
|
|
<a target="_blank" href={'https://www.blocktrail.com/BTC/address/' + this.props.edition.hash_as_address}>{this.props.edition.hash_as_address}</a>
|
|
|
|
);
|
|
|
|
|
|
|
|
let ownerAddress = (
|
|
|
|
<a target="_blank" href={'https://www.blocktrail.com/BTC/address/' + this.props.edition.btc_owner_address_noprefix}>{this.props.edition.btc_owner_address_noprefix}</a>
|
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<Form >
|
|
|
|
<Property
|
|
|
|
name='artwork_id'
|
2015-07-03 19:08:56 +02:00
|
|
|
label={getLangText('Artwork ID')}
|
2015-07-03 12:35:45 +02:00
|
|
|
editable={false}>
|
|
|
|
<pre className="ascribe-pre">{bitcoinIdValue}</pre>
|
|
|
|
</Property>
|
|
|
|
<Property
|
|
|
|
name='hash_of_artwork'
|
2015-07-03 19:08:56 +02:00
|
|
|
label={getLangText('Hash of Artwork, title, etc')}
|
2015-07-03 12:35:45 +02:00
|
|
|
editable={false}>
|
|
|
|
<pre className="ascribe-pre">{hashOfArtwork}</pre>
|
|
|
|
</Property>
|
|
|
|
<Property
|
|
|
|
name='owner_address'
|
2015-07-03 19:08:56 +02:00
|
|
|
label={getLangText('Owned by SPOOL address')}
|
2015-07-03 12:35:45 +02:00
|
|
|
editable={false}>
|
|
|
|
<pre className="ascribe-pre">{ownerAddress}</pre>
|
|
|
|
</Property>
|
|
|
|
<hr />
|
|
|
|
</Form>);
|
|
|
|
|
|
|
|
}
|
|
|
|
});
|
2015-06-26 00:38:40 +02:00
|
|
|
export default Edition;
|