1
0
mirror of https://github.com/ascribe/onion.git synced 2024-12-22 09:23:13 +01:00

piece + share

acl first cut
This commit is contained in:
diminator 2015-07-13 14:10:46 +02:00
parent 88ff4b55d6
commit a07bc9e0e2
16 changed files with 131 additions and 370 deletions

View File

@ -14,18 +14,21 @@ import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
import { getLangText } from '../../utils/lang_utils.js';
import apiUrls from '../../constants/api_urls';
let AclButton = React.createClass({
propTypes: {
action: React.PropTypes.oneOf(AppConstants.aclList).isRequired,
availableAcls: React.PropTypes.array.isRequired,
pieceOrEditions: React.PropTypes.array.isRequired,
pieceOrEditions: React.PropTypes.object.isRequired,
currentUser: React.PropTypes.object,
handleSuccess: React.PropTypes.func.isRequired,
className: React.PropTypes.string
},
isPiece(){
return !(this.props.pieceOrEditions.constructor === Array);
},
actionProperties(){
if (this.props.action === 'consign'){
return {
@ -62,16 +65,46 @@ let AclButton = React.createClass({
return {
title: getLangText('Share artwork'),
tooltip: getLangText('Share the artwork'),
form: <ShareForm currentUser={ this.props.currentUser } editions={ this.props.pieceOrEditions }/>,
form: (
<ShareForm
message={this.getShareMessage()}
id={this.getFormDataId()}
url={this.isPiece() ? apiUrls.ownership_shares_pieces : apiUrls.ownership_shares_editions }/>
),
handleSuccess: this.showNotification
};
}
},
showNotification(response){
this.props.handleSuccess();
let notification = new GlobalNotificationModel(response.notification, 'success');
GlobalNotificationActions.appendGlobalNotification(notification);
},
getTitlesString(){
if (this.isPiece()){
return '\"' + this.props.pieceOrEditions.title + '\"';
}
else {
return this.props.pieceOrEditions.map(function(edition) {
return '- \"' + edition.title + ', ' + getLangText('edition') + ' ' + edition.edition_number + '\"\n';
}).join('');
}
},
getFormDataId(){
if (this.isPiece()) {
return {piece_id: this.props.pieceOrEditions.id};
}
else {
return {bitcoin_id: this.props.pieceOrEditions.map(function(edition){
return edition.bitcoin_id;
}).join()};
}
},
render() {
let shouldDisplay = this.props.availableAcls.indexOf(this.props.action) > -1;
let aclProps = this.actionProperties();
@ -88,6 +121,18 @@ let AclButton = React.createClass({
{ aclProps.form }
</ModalWrapper>
);
},
getShareMessage(){
return (
`${getLangText('Hi')},
${getLangText('I am sharing')} :
${this.getTitlesString()}${getLangText('with you')}.
${getLangText('Truly yours')},
${this.props.currentUser.username}`
);
}
});

View File

@ -7,11 +7,12 @@ import UserStore from '../../stores/user_store';
import AclButton from '../ascribe_buttons/acl_button';
import DeleteButton from '../ascribe_buttons/delete_button';
import CreateEditionButton from '../ascribe_buttons/create_editions_button';
let AclButtonList = React.createClass({
propTypes: {
className: React.PropTypes.string,
editions: React.PropTypes.array,
editions: React.PropTypes.object,
availableAcls: React.PropTypes.array,
handleSuccess: React.PropTypes.func
},
@ -66,7 +67,10 @@ let AclButtonList = React.createClass({
pieceOrEditions={this.props.editions}
currentUser={this.state.currentUser}
handleSuccess={this.props.handleSuccess} />
<DeleteButton editions={this.props.editions}/>
<DeleteButton
editions={this.props.editions}/>
<CreateEditionButton
piece={this.props.editions}/>
</div>
);
}

View File

@ -0,0 +1,44 @@
'use strict';
import React from 'react';
import Router from 'react-router';
import Button from 'react-bootstrap/lib/Button';
import EditionDeleteForm from '../ascribe_forms/form_delete_edition';
import EditionRemoveFromCollectionForm from '../ascribe_forms/form_remove_editions_from_collection';
import ModalWrapper from '../ascribe_modal/modal_wrapper';
import AccordionListItemCreateEditions from './../ascribe_accordion_list/accordion_list_item_create_editions';
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
import { getAvailableAcls } from '../../utils/acl_utils';
import { getLangText } from '../../utils/lang_utils.js'
import EditionListActions from '../../actions/edition_list_actions';
let CreateEditionsButton = React.createClass({
propTypes: {
piece: React.PropTypes.object.isRequired
},
mixins: [Router.Navigation],
render: function () {
if (this.props.piece.constructor === Array){
return null;
}
let availableAcls = getAvailableAcls([this.props.piece]);
if (availableAcls.indexOf('editions') === -1){
return null;
}
return (
<button className="btn btn-default btn-sm">
CREATE EDITIONS
</button>
);
}
});
export default CreateEditionsButton;

View File

@ -218,7 +218,6 @@ let EditionSummary = React.createClass({
<EditionDetailProperty label={getLangText('ID')} value={ this.props.edition.bitcoin_id } />
<EditionDetailProperty label={getLangText('OWNER')} value={ this.props.edition.owner } />
{this.getStatus()}
<br/>
{this.getActions()}
<hr/>
</div>

View File

@ -70,7 +70,8 @@ let Piece = React.createClass({
content={this.props.piece}/>
<PieceSummary
currentUser={this.state.currentUser}
piece={this.props.piece} />
piece={this.props.piece}
handleSuccess={this.props.loadPiece}/>
<CollapsibleParagraph
title="Further Details"
show={this.props.piece.acl.indexOf('edit') > -1
@ -95,26 +96,24 @@ let PieceSummary = React.createClass({
propTypes: {
piece: React.PropTypes.object
},
getActions(){
//let actions = (
// <Row>
// <Col md={12}>
// <AclButtonList
// className="text-center ascribe-button-list"
// availableAcls={this.props.piece.acl}
// editions={[this.props.piece]}
// handleSuccess={this.handleSuccess} />
// </Col>
// </Row>);
//return actions;
return null;
let actions = (
<Row>
<Col md={12}>
<AclButtonList
className="text-center ascribe-button-list"
availableAcls={this.props.piece.acl}
editions={this.props.piece}
handleSuccess={this.props.handleSuccess} />
</Col>
</Row>);
return actions;
//return null;
},
render() {
return (
<div className="ascribe-detail-header">
<DetailProperty label={getLangText('REGISTREE')} value={ this.props.piece.user_registered } />
<br/>
{this.getActions()}
<hr/>
</div>

View File

@ -1,64 +0,0 @@
'use strict';
import React from 'react';
import apiUrls from '../../constants/api_urls';
import FormMixin from '../../mixins/form_mixin';
import InputText from './input_text';
import ButtonSubmitOrClose from '../ascribe_buttons/button_submit_close';
import SignupModal from '../ascribe_modal/modal_signup';
import PasswordResetRequestModal from '../ascribe_modal/modal_password_request_reset';
import { getLangText } from '../../utils/lang_utils.js';
let LoginForm = React.createClass({
mixins: [FormMixin],
url() {
return apiUrls.users_login;
},
getFormData() {
return {
email: this.refs.email.state.value,
password: this.refs.password.state.value
};
},
renderForm() {
return (
<form id="login_modal_content" role="form" onSubmit={this.submit}>
<input className="invisible" type="email" name="fake_consignee"/>
<input className="invisible" type="password" name="fake_password"/>
<InputText
ref="email"
placeHolder={getLangText('Email')}
required="required"
type="email"
submitted={this.state.submitted}/>
<InputText
ref="password"
placeHolder={getLangText('Password')}
required="required"
type="password"
submitted={this.state.submitted}/>
<div>
{getLangText('Forgot your password')}&#63;
<PasswordResetRequestModal
button={<a className="button" href="#"> {getLangText('Reset password')}</a>}/>
</div>
<div>
{getLangText('Not a member yet')}&#63;
<SignupModal
button={<a className="button" href="#"> {getLangText('Sign up')}</a>}/>
</div>
<ButtonSubmitOrClose
text={getLangText('LOGIN')}
onClose={this.props.onRequestHide}
submitted={this.state.submitted} />
</form>
);
}
});
export default LoginForm;

View File

@ -1,43 +0,0 @@
'use strict';
import React from 'react';
import apiUrls from '../../constants/api_urls';
import FormMixin from '../../mixins/form_mixin';
import InputTextAreaToggable from './input_textarea_toggable';
let EditionNoteForm = React.createClass({
mixins: [FormMixin],
url() {
return apiUrls.note_edition;
},
getFormData() {
return {
bitcoin_id: this.getBitcoinIds().join(),
note: this.refs.personalNote.state.value
};
},
renderForm() {
return (
<form id="personal_note_content" role="form" key="personal_note_content">
<InputTextAreaToggable
ref="personalNote"
className="form-control"
defaultValue={this.props.editions[0].public_note}
rows={3}
editable={this.props.editable}
required=""
onSubmit={this.submit}
/>
</form>
);
}
});
export default EditionNoteForm;

View File

@ -1,43 +0,0 @@
'use strict';
import React from 'react';
import apiUrls from '../../constants/api_urls';
import FormMixin from '../../mixins/form_mixin';
import InputTextAreaToggable from './input_textarea_toggable';
let PersonalNoteForm = React.createClass({
mixins: [FormMixin],
url() {
return apiUrls.note_notes;
},
getFormData() {
return {
bitcoin_id: this.getBitcoinIds().join(),
note: this.refs.personalNote.state.value
};
},
renderForm() {
return (
<form id="personal_note_content" role="form" key="personal_note_content">
<InputTextAreaToggable
ref="personalNote"
className="form-control"
defaultValue={this.props.editions[0].note_from_user}
rows={3}
editable={this.props.editable}
required=""
onSubmit={this.submit}
/>
</form>
);
}
});
export default PersonalNoteForm;

View File

@ -1,51 +0,0 @@
'use strict';
import React from 'react';
import apiUrls from '../../constants/api_urls';
import FormMixin from '../../mixins/form_mixin';
import InputText from './input_text';
import ButtonSubmit from '../ascribe_buttons/button_submit';
import { getLangText } from '../../utils/lang_utils.js'
let PasswordResetForm = React.createClass({
mixins: [FormMixin],
url() {
return apiUrls.users_password_reset;
},
getFormData() {
return {
email: this.props.email,
token: this.props.token,
password: this.refs.password.state.value,
password_confirm: this.refs.password_confirm.state.value
};
},
renderForm() {
return (
<form id="reset_password_modal_content" role="form" onSubmit={this.submit}>
<div>Reset the password for {this.props.email}:</div>
<InputText
ref="password"
placeHolder={getLangText('Choose a password')}
required="required"
type="password"
submitted={this.state.submitted}/>
<InputText
ref="password_confirm"
placeHolder={getLangText('Confirm password')}
required="required"
type="password"
submitted={this.state.submitted}/>
<ButtonSubmit
text={getLangText('RESET PASSWORD')}
submitted={this.state.submitted} />
</form>
);
}
});
export default PasswordResetForm;

View File

@ -1,42 +0,0 @@
'use strict';
import React from 'react';
import apiUrls from '../../constants/api_urls';
import FormMixin from '../../mixins/form_mixin';
import InputText from './input_text';
import ButtonSubmitOrClose from '../ascribe_buttons/button_submit_close';
import { getLangText } from '../../utils/lang_utils.js'
let PasswordResetRequestForm = React.createClass({
mixins: [FormMixin],
url() {
return apiUrls.users_password_reset_request;
},
getFormData() {
return {
email: this.refs.email.state.value
};
},
renderForm() {
return (
<form id="request_reset_password_modal_content" role="form" onSubmit={this.submit}>
<InputText
ref="email"
placeHolder={getLangText('Email')}
required="required"
type="email"
submitted={this.state.submitted}/>
<ButtonSubmitOrClose
text={getLangText('RESET PASSWORD')}
onClose={this.props.onRequestHide}
submitted={this.state.submitted} />
</form>
);
}
});
export default PasswordResetRequestForm;

View File

@ -24,8 +24,9 @@ let EditionRemoveFromCollectionForm = React.createClass({
<p>{getLangText('Are you sure you would like to remove these editions from your collection')}&#63;</p>
<p>{getLangText('This is an irrevocable action%s', '.')}</p>
<div className="modal-footer">
<button type="submit" className="btn btn-ascribe-inv" onClick={this.submit}>{getLangText('YES, REMOVE')}</button>
<button className="btn btn-ascribe" onClick={this.props.onRequestHide}>{getLangText('CLOSE')}</button>
<button type="submit" className="btn btn-danger btn-delete btn-sm ascribe-margin-1px" onClick={this.submit}>{getLangText('YES, REMOVE')}</button>
<button className="btn btn-default btn-sm ascribe-margin-1px" style={{marginLeft: '0'}}
onClick={this.props.onRequestHide}>{getLangText('CLOSE')}</button>
</div>
</div>
);

View File

@ -2,7 +2,7 @@
import React from 'react';
import ApiUrls from '../../constants/api_urls';
import Form from './form';
import Property from './property';
@ -15,49 +15,27 @@ import { getLangText } from '../../utils/lang_utils.js';
let ShareForm = React.createClass({
propTypes: {
url: React.PropTypes.string,
id: React.PropTypes.string,
message: React.PropTypes.string,
editions: React.PropTypes.array,
currentUser: React.PropTypes.object
currentUser: React.PropTypes.object,
onRequestHide: React.PropTypes.func,
handleSuccess: React.PropTypes.func
},
getFormData() {
return {
bitcoin_id: this.getBitcoinIds().join()
};
},
handleSuccess(response){
if ('handleSuccess' in this.props){
this.props.handleSuccess(response);
}
},
getBitcoinIds(){
return this.props.editions.map(function(edition){
return edition.bitcoin_id;
});
},
getTitlesString(){
return this.props.editions.map(function(edition){
return '- \"' + edition.title + ', ' + getLangText('edition') + ' ' + edition.edition_number + '\"\n';
});
getFormData(){
return this.props.id;
},
render() {
let title = this.getTitlesString().join('');
let username = this.props.currentUser.username;
let message =
`${getLangText('Hi')},
${getLangText('I am sharing')} :
${title}${getLangText('with you')}.
${getLangText('Truly yours')},
${username}`;
return (
<Form
ref='form'
url={ApiUrls.ownership_shares_editions}
url={this.props.url}
getFormData={this.getFormData}
handleSuccess={this.handleSuccess}
handleSuccess={this.props.handleSuccess}
buttons={
<div className="modal-footer">
<p className="pull-right">
@ -73,9 +51,7 @@ ${username}`;
spinner={
<div className="modal-footer">
<img src={AppConstants.baseUrl + 'static/img/ascribe_animated_small.gif'} />
</div>
}
>
</div>}>
<Property
name='share_emails'
label={getLangText('Emails')}>
@ -91,7 +67,7 @@ ${username}`;
<InputTextAreaToggable
rows={1}
editable={true}
defaultValue={message}
defaultValue={this.props.message}
placeholder={getLangText('Enter a message...')}
required="required"/>
</Property>

View File

@ -1,33 +0,0 @@
'use strict';
import React from 'react';
import ModalWrapper from './modal_wrapper';
import LoginForm from '../ascribe_forms/form_login';
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
import { getLangText } from '../../utils/lang_utils.js'
let LoginModal = React.createClass({
handleLoginSuccess(){
this.props.handleSuccess();
let notificationText = getLangText('Login successful');
let notification = new GlobalNotificationModel(notificationText, 'success');
GlobalNotificationActions.appendGlobalNotification(notification);
},
render() {
return (
<ModalWrapper
button={this.props.button}
title={getLangText('Log in to ascribe')}
handleSuccess={this.handleLoginSuccess}
tooltip={getLangText('Log in to ascribe')}>
<LoginForm />
</ModalWrapper>
);
}
});
export default LoginModal;

View File

@ -1,32 +0,0 @@
'use strict';
import React from 'react';
import ModalWrapper from './modal_wrapper';
import SignupForm from '../ascribe_forms/form_signup';
import GlobalNotificationModel from '../../models/global_notification_model';
import GlobalNotificationActions from '../../actions/global_notification_actions';
import { getLangText } from '../../utils/lang_utils.js'
let SignupModal = React.createClass({
handleSignupSuccess(response){
let notificationText = getLangText('We sent an email to your address') + ' ' + response.user.email + ', ' + getLangText('please confirm') + '.';
let notification = new GlobalNotificationModel(notificationText, 'success', 50000);
GlobalNotificationActions.appendGlobalNotification(notification);
},
render() {
return (
<ModalWrapper
button={this.props.button}
title={getLangText('Create an account')}
handleSuccess={this.handleSignupSuccess}
tooltip={getLangText('Sign up to ascribe')}>
<SignupForm />
</ModalWrapper>
);
}
});
export default SignupModal;

View File

@ -12,7 +12,7 @@ let apiUrls = {
'coa_verify': AppConstants.apiEndpoint + 'coa/verify_coa/',
'edition': AppConstants.apiEndpoint + 'editions/${bitcoin_id}/',
'edition_delete': AppConstants.apiEndpoint + 'editions/${edition_id}/',
'edition_remove_from_collection': AppConstants.apiEndpoint + 'ownership/shares/${edition_id}/',
'edition_remove_from_collection': AppConstants.apiEndpoint + 'ownership/shares/editions/${edition_id}/',
'editions': AppConstants.apiEndpoint + 'editions/', // this should be moved to the one below
'editions_list': AppConstants.apiEndpoint + 'pieces/${piece_id}/editions/',
'licenses': AppConstants.apiEndpoint + 'ownership/licenses/',
@ -26,6 +26,7 @@ let apiUrls = {
'ownership_loans_deny': AppConstants.apiEndpoint + 'ownership/loans/deny/',
'ownership_loans_contract': AppConstants.apiEndpoint + 'ownership/loans/contract/',
'ownership_shares_editions': AppConstants.apiEndpoint + 'ownership/shares/editions/',
'ownership_shares_pieces': AppConstants.apiEndpoint + 'ownership/shares/pieces/',
'ownership_transfers': AppConstants.apiEndpoint + 'ownership/transfers/',
'ownership_transfers_withdraw': AppConstants.apiEndpoint + 'ownership/transfers/withdraw/',
'ownership_unconsigns': AppConstants.apiEndpoint + 'ownership/unconsigns/',

View File

@ -93,7 +93,7 @@ $ascribe-accordion-list-font: 'Source Sans Pro';
}
}
border-left: 3px solid rgba(0,0,0,0);
border-top: 1px solid rgba(0,0,0,.1);
//border-top: 1px solid rgba(0,0,0,.1);
border-bottom: 1px solid rgba(0,0,0,.05);
}
tbody {