fixed react forms (new bug discovered on new forms with default values not saved)

This commit is contained in:
Jonathan Labreuille
2016-05-18 16:06:31 +02:00
parent 0c8a8c6854
commit 046127eeba
17 changed files with 164 additions and 66 deletions

View File

@ -1,2 +1,5 @@
.mailpoet_form
margin: 0 0 20px 0
.mailpoet_form td .mailpoet_form td
vertical-align: top !important vertical-align: top !important

View File

@ -9,6 +9,13 @@ define([
render() { render() {
const yearsRange = 100; const yearsRange = 100;
const years = []; const years = [];
if (this.props.empty_value_label !== undefined) {
years.push((
<option value="" key={ 0 }>{ this.props.empty_value_label }</option>
));
}
const currentYear = Moment().year(); const currentYear = Moment().year();
for (let i = currentYear; i >= currentYear - yearsRange; i--) { for (let i = currentYear; i >= currentYear - yearsRange; i--) {
years.push(( years.push((
@ -33,6 +40,13 @@ define([
class FormFieldDateMonth extends React.Component { class FormFieldDateMonth extends React.Component {
render() { render() {
const months = []; const months = [];
if (this.props.empty_value_label !== undefined) {
months.push((
<option value="" key={ 0 }>{ this.props.empty_value_label }</option>
));
}
for (let i = 1; i <= 12; i++) { for (let i = 1; i <= 12; i++) {
months.push(( months.push((
<option <option
@ -56,6 +70,13 @@ define([
class FormFieldDateDay extends React.Component { class FormFieldDateDay extends React.Component {
render() { render() {
const days = []; const days = [];
if (this.props.empty_value_label !== undefined) {
days.push((
<option value="" key={ 0 }>{ this.props.empty_value_label }</option>
));
}
for (let i = 1; i <= 31; i++) { for (let i = 1; i <= 31; i++) {
days.push(( days.push((
<option <option
@ -81,9 +102,9 @@ define([
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
year: Moment().year(), year: undefined,
month: 1, month: undefined,
day: 1 day: undefined
} }
} }
componentDidMount() { componentDidMount() {
@ -112,7 +133,7 @@ define([
`${this.state.month}/${this.state.day}/${this.state.year}`, `${this.state.month}/${this.state.day}/${this.state.year}`,
'M/D/YYYY' 'M/D/YYYY'
).valueOf(); ).valueOf();
if (!isNaN(newTimeStamp) && parseInt(newTimeStamp, 10) > 0) { if (~~(newTimeStamp) > 0) {
// convert milliseconds to seconds // convert milliseconds to seconds
newTimeStamp /= 1000; newTimeStamp /= 1000;
return this.props.onValueChange({ return this.props.onValueChange({
@ -158,6 +179,7 @@ define([
key={ 'year' } key={ 'year' }
name={ this.props.field.name } name={ this.props.field.name }
year={ this.state.year } year={ this.state.year }
empty_value_label={ this.props.field.empty_year_label }
/>); />);
break; break;
@ -169,6 +191,7 @@ define([
name={ this.props.field.name } name={ this.props.field.name }
month={ this.state.month } month={ this.state.month }
monthNames={ monthNames } monthNames={ monthNames }
empty_value_label={ this.props.field.empty_month_label }
/>); />);
break; break;
@ -179,6 +202,7 @@ define([
key={ 'day' } key={ 'day' }
name={ this.props.field.name } name={ this.props.field.name }
day={ this.state.day } day={ this.state.day }
empty_value_label={ this.props.field.empty_day_label }
/>); />);
break; break;
} }

View File

@ -10,12 +10,27 @@ function(
return false; return false;
} }
var values = (this.props.field.filterValues !== undefined) let values = this.props.field.values;
? this.props.field.filterValues(this.props.item) let filter = false;
: this.props.field.values; let empty_option = false;
if (this.props.field.empty_value_label !== undefined) {
empty_option = (
<option value="">{ this.props.field.empty_value_label }</option>
);
}
if (this.props.field['filter'] !== undefined) {
filter = this.props.field.filter;
}
const options = Object.keys(values).map( const options = Object.keys(values).map(
(value, index) => { (value, index) => {
if (filter !== false && filter(this.props.item, value) === false) {
return;
}
return ( return (
<option <option
key={ 'option-' + index } key={ 'option-' + index }
@ -34,6 +49,7 @@ function(
onChange={ this.props.onValueChange } onChange={ this.props.onValueChange }
{...this.props.field.validation} {...this.props.field.validation}
> >
{empty_option}
{options} {options}
</select> </select>
); );

View File

@ -4,9 +4,9 @@ define([
function( function(
React React
) { ) {
var FormFieldText = React.createClass({ const FormFieldText = React.createClass({
render: function() { render: function() {
var value = this.props.item[this.props.field.name]; let value = this.props.item[this.props.field.name];
if(value === undefined) { if(value === undefined) {
value = this.props.field.defaultValue || ''; value = this.props.field.defaultValue || '';
} }
@ -15,9 +15,9 @@ function(
<input <input
type="text" type="text"
disabled={ disabled={
(this.props.field.disabled !== undefined) (this.props.field['disabled'] !== undefined)
? this.props.field.disabled(this.props.item) ? this.props.field.disabled(this.props.item)
: '' : false
} }
className={ (this.props.field.size) ? '' : 'regular-text' } className={ (this.props.field.size) ? '' : 'regular-text' }
size={ size={

View File

@ -37,9 +37,13 @@ define(
return this.props.errors ? this.props.errors : this.state.errors; return this.props.errors ? this.props.errors : this.state.errors;
}, },
componentDidMount: function() { componentDidMount: function() {
if(this.props.params.id !== undefined) { if(this.isMounted()) {
if(this.isMounted()) { if(this.props.params.id !== undefined) {
this.loadItem(this.props.params.id); this.loadItem(this.props.params.id);
} else {
this.setState({
item: jQuery('.mailpoet_form').serializeObject()
});
} }
} }
}, },
@ -167,12 +171,15 @@ define(
{ 'mailpoet_form_loading': this.state.loading || this.props.loading } { 'mailpoet_form_loading': this.state.loading || this.props.loading }
); );
var beforeFormContent = false;
var afterFormContent = false;
if (this.props.beforeFormContent !== undefined) { if (this.props.beforeFormContent !== undefined) {
var beforeFormContent = this.props.beforeFormContent(this.getValues()) beforeFormContent = this.props.beforeFormContent(this.getValues());
} }
if (this.props.afterFormContent !== undefined) { if (this.props.afterFormContent !== undefined) {
var afterFormContent = this.props.afterFormContent(this.getValues()) afterFormContent = this.props.afterFormContent(this.getValues());
} }
var fields = this.props.fields.map(function(field, i) { var fields = this.props.fields.map(function(field, i) {
@ -200,7 +207,7 @@ define(
return ( return (
<div> <div>
{ beforeFormContent } { beforeFormContent }
<form <form
id={ this.props.id } id={ this.props.id }
ref="form" ref="form"

View File

@ -3,13 +3,15 @@ define(
'react', 'react',
'react-router', 'react-router',
'mailpoet', 'mailpoet',
'form/form.jsx' 'form/form.jsx',
'react-string-replace'
], ],
function( function(
React, React,
Router, Router,
MailPoet, MailPoet,
Form Form,
ReactStringReplace
) { ) {
var fields = [ var fields = [
{ {
@ -17,7 +19,7 @@ define(
label: MailPoet.I18n.t('email'), label: MailPoet.I18n.t('email'),
type: 'text', type: 'text',
disabled: function(subscriber) { disabled: function(subscriber) {
if (subscriber.wp_user_id !== null) return 'disabled'; return ~~(subscriber.wp_user_id > 0);
} }
}, },
{ {
@ -25,7 +27,7 @@ define(
label: MailPoet.I18n.t('firstname'), label: MailPoet.I18n.t('firstname'),
type: 'text', type: 'text',
disabled: function(subscriber) { disabled: function(subscriber) {
if (subscriber.wp_user_id !== null) return 'disabled'; return ~~(subscriber.wp_user_id > 0);
} }
}, },
{ {
@ -33,7 +35,7 @@ define(
label: MailPoet.I18n.t('lastname'), label: MailPoet.I18n.t('lastname'),
type: 'text', type: 'text',
disabled: function(subscriber) { disabled: function(subscriber) {
if (subscriber.wp_user_id !== null) return 'disabled'; return ~~(subscriber.wp_user_id > 0);
} }
}, },
{ {
@ -41,15 +43,15 @@ define(
label: MailPoet.I18n.t('status'), label: MailPoet.I18n.t('status'),
type: 'select', type: 'select',
values: { values: {
'unconfirmed': MailPoet.I18n.t('unconfirmed'),
'subscribed': MailPoet.I18n.t('subscribed'), 'subscribed': MailPoet.I18n.t('subscribed'),
'unconfirmed': MailPoet.I18n.t('unconfirmed'),
'unsubscribed': MailPoet.I18n.t('unsubscribed') 'unsubscribed': MailPoet.I18n.t('unsubscribed')
}, },
filterValues: function(subscriber) { filter: function(subscriber, value) {
if (subscriber.wp_user_id !== null) { if (~~(subscriber.wp_user_id) > 0 && value === 'unconfirmed') {
delete this.values.unconfirmed; return false;
} }
return this.values; return true;
} }
}, },
{ {
@ -115,6 +117,19 @@ define(
field.values = custom_field.params.values; field.values = custom_field.params.values;
} }
// add empty values' label for selects (date, select)
switch(custom_field.type) {
case 'date':
field.empty_year_label = MailPoet.I18n.t('year');
field.empty_month_label = MailPoet.I18n.t('month');
field.empty_day_label = MailPoet.I18n.t('day');
break;
case 'select':
field.empty_value_label = '-';
break;
}
fields.push(field); fields.push(field);
}); });
@ -128,17 +143,35 @@ define(
}; };
var beforeFormContent = function(subscriber) { var beforeFormContent = function(subscriber) {
if (subscriber.wp_user_id !== null) { if (~~(subscriber.wp_user_id) > 0) {
var content = return (
'<p>' + <p className="description">
MailPoet.I18n.t('wordPressUserNotice') { ReactStringReplace(
.replace('[link]', '<a href="user-edit.php?user_id=' + subscriber.wp_user_id + '">') MailPoet.I18n.t('WPUserEditNotice'),
.replace('[/link]', '</a>') + /\[link\](.*?)\[\/link\]/g,
'</p>'; (match, i) => (
return <div dangerouslySetInnerHTML={ {__html: content} } /> <a
key={ i }
href={`user-edit.php?user_id=${ subscriber.wp_user_id }`}
>{ match }</a>
)
)
}
</p>
);
} }
}; };
var afterFormContent = function(subscriber) {
return (
<p className="description">
<strong>
{ MailPoet.I18n.t('tip') }
</strong> { MailPoet.I18n.t('customFieldsTip') }
</p>
);
}
var Link = Router.Link; var Link = Router.Link;
var SubscriberForm = React.createClass({ var SubscriberForm = React.createClass({
@ -156,6 +189,7 @@ define(
params={ this.props.params } params={ this.props.params }
messages={ messages } messages={ messages }
beforeFormContent={ beforeFormContent } beforeFormContent={ beforeFormContent }
afterFormContent={ afterFormContent }
/> />
</div> </div>
); );

View File

@ -213,16 +213,6 @@ const bulk_actions = [
); );
} }
}, },
/* {
name: 'confirmUnconfirmed',
label: MailPoet.I18n.t('confirmUnconfirmed'),
onSuccess: function(response) {
MailPoet.Notice.success(
MailPoet.I18n.t('multipleSubscribersConfirmed')
.replace('%$1d', ~~response)
);
}
},*/
{ {
name: 'sendConfirmationEmail', name: 'sendConfirmationEmail',
label: MailPoet.I18n.t('resendConfirmationEmail'), label: MailPoet.I18n.t('resendConfirmationEmail'),
@ -290,10 +280,15 @@ const SubscriberList = React.createClass({
} }
let segments = false; let segments = false;
let subscribed_segments = [];
// WordPress Users
if (~~(subscriber.wp_user_id) > 0) {
subscribed_segments.push(MailPoet.I18n.t('WPUsersSegment'));
}
// Subscriptions
if (subscriber.subscriptions.length > 0) { if (subscriber.subscriptions.length > 0) {
let subscribed_segments = [];
subscriber.subscriptions.map((subscription) => { subscriber.subscriptions.map((subscription) => {
const segment = this.getSegmentFromId(subscription.segment_id); const segment = this.getSegmentFromId(subscription.segment_id);
if(segment === false) return; if(segment === false) return;
@ -301,15 +296,14 @@ const SubscriberList = React.createClass({
subscribed_segments.push(segment.name); subscribed_segments.push(segment.name);
} }
}); });
segments = (
<span>
<span className="mailpoet_segments_subscribed">
{ subscribed_segments.join(', ') }
</span>
</span>
);
} }
segments = (
<span>
{ subscribed_segments.join(', ') }
</span>
);
let avatar = false; let avatar = false;
if(subscriber.avatar_url) { if(subscriber.avatar_url) {
avatar = ( avatar = (

View File

@ -109,6 +109,10 @@ class Date extends Base {
$month_names = static::getMonthNames(); $month_names = static::getMonthNames();
$html = ''; $html = '';
// empty value label
$html .= '<option value="">'.__('Month').'</option>';
for($i = 1; $i < 13; $i++) { for($i = 1; $i < 13; $i++) {
$is_selected = ($i === $block['selected']) ? 'selected="selected"' : ''; $is_selected = ($i === $block['selected']) ? 'selected="selected"' : '';
$html .= '<option value="'.$i.'" '.$is_selected.'>'; $html .= '<option value="'.$i.'" '.$is_selected.'>';
@ -125,6 +129,7 @@ class Date extends Base {
'from' => (int)strftime('%Y') - 100, 'from' => (int)strftime('%Y') - 100,
'to' => (int)strftime('%Y') 'to' => (int)strftime('%Y')
); );
// is default today // is default today
if(!empty($block['params']['is_default_today'])) { if(!empty($block['params']['is_default_today'])) {
$defaults['selected'] = (int)strftime('%Y'); $defaults['selected'] = (int)strftime('%Y');
@ -135,6 +140,9 @@ class Date extends Base {
$html = ''; $html = '';
// empty value label
$html .= '<option value="">'.__('Year').'</option>';
// return years as an array // return years as an array
for($i = (int)$block['to']; $i > (int)($block['from'] - 1); $i--) { for($i = (int)$block['to']; $i > (int)($block['from'] - 1); $i--) {
$is_selected = ($i === $block['selected']) ? 'selected="selected"' : ''; $is_selected = ($i === $block['selected']) ? 'selected="selected"' : '';
@ -158,6 +166,9 @@ class Date extends Base {
$html = ''; $html = '';
// empty value label
$html .= '<option value="">'.__('Day').'</option>';
// return days as an array // return days as an array
for($i = 1; $i < 32; $i++) { for($i = 1; $i < 32; $i++) {
$is_selected = ($i === $block['selected']) ? 'selected="selected"' : ''; $is_selected = ($i === $block['selected']) ? 'selected="selected"' : '';

View File

@ -13,9 +13,12 @@ class Select extends Base {
$html .= static::renderLabel($block); $html .= static::renderLabel($block);
$html .= '<select class="mailpoet_select" name="'.$field_name.'">'; $html .= '<select class="mailpoet_select" name="'.$field_name.'">';
if(isset($block['params']['label_within']) if(isset($block['params']['label_within']) && $block['params']['label_within']) {
&& $block['params']['label_within']) {
$html .= '<option value="">'.static::getFieldLabel($block).'</option>'; $html .= '<option value="">'.static::getFieldLabel($block).'</option>';
} else {
if(empty($block['params']['required']) || !$block['params']['required']) {
$html .= '<option value="">-</option>';
}
} }
$options = (!empty($block['params']['values']) $options = (!empty($block['params']['values'])

View File

@ -539,12 +539,6 @@ class Subscriber extends Model {
return false; return false;
} }
/* static function bulkConfirmUnconfirmed($orm) {
$subscribers = $orm->findResultSet();
$subscribers->set('status', self::STATUS_SUBSCRIBED)->save();
return $subscribers->count();
}*/
static function bulkSendConfirmationEmail($orm) { static function bulkSendConfirmationEmail($orm) {
$subscribers = $orm $subscribers = $orm
->where('status', self::STATUS_UNCONFIRMED) ->where('status', self::STATUS_UNCONFIRMED)

View File

@ -32,6 +32,7 @@ class Pages {
// manage subscription link shortcode // manage subscription link shortcode
// [mailpoet_manage text="Manage your subscription"] // [mailpoet_manage text="Manage your subscription"]
add_shortcode('mailpoet_manage', array($this, 'getManageLink')); add_shortcode('mailpoet_manage', array($this, 'getManageLink'));
add_shortcode('mailpoet_manage_subscription', array($this, 'getManageContent'));
} }
private function isPreview() { private function isPreview() {
@ -203,7 +204,7 @@ class Pages {
} }
} }
private function getManageContent() { public function getManageContent() {
if($this->isPreview()) { if($this->isPreview()) {
$subscriber = Subscriber::create(); $subscriber = Subscriber::create();
$subscriber->hydrate(array( $subscriber->hydrate(array(
@ -273,6 +274,7 @@ class Pages {
'id' => 'status', 'id' => 'status',
'type' => 'select', 'type' => 'select',
'params' => array( 'params' => array(
'required' => true,
'label' => __('Status'), 'label' => __('Status'),
'values' => array( 'values' => array(
array( array(

View File

@ -29,6 +29,7 @@
"react-infinity": "latest", "react-infinity": "latest",
"react-prefixr": "latest", "react-prefixr": "latest",
"react-router": "latest", "react-router": "latest",
"react-string-replace": "^0.3.2",
"react-waypoint": "latest", "react-waypoint": "latest",
"select2": "^4.0.0", "select2": "^4.0.0",
"spectrum-colorpicker": "^1.6.2", "spectrum-colorpicker": "^1.6.2",

View File

@ -1,5 +1,6 @@
<% set currentDay = 'now' | date('d') | number_format %> <% set currentDay = 'now' | date('d') | number_format %>
<select id="{{ id }}_days"> <select id="{{ id }}_days">
<option value=""><%= __('Day') %></option>
<% for day in 1..31 %> <% for day in 1..31 %>
<option <option
<% if(currentDay == day) %> <% if(currentDay == day) %>

View File

@ -1,5 +1,6 @@
<% set currentMonth = 'now'|date('n') %> <% set currentMonth = 'now'|date('n') %>
<select id="{{ id }}_months"> <select id="{{ id }}_months">
<option value=""><%= __('Month') %></option>
<% for month in 1..12 %> <% for month in 1..12 %>
<option <option
<% if(currentMonth == month) %> <% if(currentMonth == month) %>

View File

@ -2,6 +2,7 @@
<% set minYear = currentYear - 100 %> <% set minYear = currentYear - 100 %>
<select id="{{ id }}_years"> <select id="{{ id }}_years">
<option value=""><%= __('Year') %></option>
<% for year in currentYear..minYear %> <% for year in currentYear..minYear %>
<option <option
<% if(currentYear == year) %> <% if(currentYear == year) %>

View File

@ -8,6 +8,8 @@
<select> <select>
{{#if params.label_within}} {{#if params.label_within}}
<option value="">{{ params.label }}{{#if params.required}} *{{/if}}</option> <option value="">{{ params.label }}{{#if params.required}} *{{/if}}</option>
{{else}}
{{#unless params.required}}<option value="">-</option>{{/unless}}
{{/if}} {{/if}}
{{#each params.values}} {{#each params.values}}
<option {{#if is_checked}}selected="selected"{{/if}}>{{ value }}</option> <option {{#if is_checked}}selected="selected"{{/if}}>{{ value }}</option>

View File

@ -73,12 +73,16 @@
'multipleSubscribersRemovedFromList': __('%$1d subscribers were removed from list <strong>%$2s</strong>.'), 'multipleSubscribersRemovedFromList': __('%$1d subscribers were removed from list <strong>%$2s</strong>.'),
'removeFromAllLists': __('Remove from all lists'), 'removeFromAllLists': __('Remove from all lists'),
'multipleSubscribersRemovedFromAllLists': __('%$1d subscribers were removed from all lists.'), 'multipleSubscribersRemovedFromAllLists': __('%$1d subscribers were removed from all lists.'),
'confirmUnconfirmed': __('Confirm unconfirmed'),
'multipleSubscribersConfirmed': __('%$1d subscribers have been confirmed.'),
'resendConfirmationEmail': __('Resend confirmation email'), 'resendConfirmationEmail': __('Resend confirmation email'),
'multipleConfirmationEmailsSent': __('%$1d confirmation emails have been sent.'), 'multipleConfirmationEmailsSent': __('%$1d confirmation emails have been sent.'),
'listsToWhichSubscriberWasSubscribed': __('Lists to which the subscriber was subscribed.'), 'listsToWhichSubscriberWasSubscribed': __('Lists to which the subscriber was subscribed.'),
'wordPressUserNotice': __('This subscriber is a registered WordPress user. [link]Edit his profile[/link] to change his email.'), 'WPUsersSegment': __('WordPress Users'),
'WPUserEditNotice': __('This subscriber is a registered WordPress user. [link]Edit his profile[/link] to change his email.'),
'tip': __('Tip:'),
'customFieldsTip': __('need to add new fields like telephone number or address? You can add custom fields by editing any form.'),
'year': __('Year'),
'month': __('Month'),
'day': __('Day'),
'new': __('New'), 'new': __('New'),
'import': __('Import'), 'import': __('Import'),
'export': __('Export'), 'export': __('Export'),