Compare commits
42 Commits
Author | SHA1 | Date | |
---|---|---|---|
6aadd1fdc4 | |||
13589a4660 | |||
5f124659d0 | |||
d3ebc9706c | |||
e83d01ff28 | |||
9600e4f220 | |||
3e746d1545 | |||
3cc43aa302 | |||
c610d87e85 | |||
362ee49ce4 | |||
515515ba9f | |||
ed7da1a8fe | |||
12c036dbef | |||
1dd4ade04d | |||
0706450f9a | |||
b837a153d1 | |||
6d22a85fd7 | |||
3d706414b7 | |||
ef0cbb3e9f | |||
f5552847a3 | |||
101ef0cff4 | |||
9e70ba5e6e | |||
8f1a7ed3de | |||
6aa976ba1f | |||
db85604f18 | |||
7605fc71ac | |||
2c98270084 | |||
a5300624c2 | |||
2714c7fa9a | |||
49b65729db | |||
e053b62a70 | |||
88113cf22e | |||
5bf352e9fc | |||
5a7d5ac3f0 | |||
05848ce7aa | |||
b58d996ac7 | |||
16fa4491a5 | |||
456deede14 | |||
4ef50ca551 | |||
5ff7d98b00 | |||
3018dff1ff | |||
9386fe8328 |
@ -1,4 +1,4 @@
|
||||
define('ajax', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
||||
define('ajax', ['mailpoet', 'jquery', 'underscore'], function(MailPoet, jQuery, _) {
|
||||
'use strict';
|
||||
MailPoet.Ajax = {
|
||||
version: 0.5,
|
||||
@ -52,6 +52,13 @@ define('ajax', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
||||
var params = this.getParams();
|
||||
var jqXHR;
|
||||
|
||||
// remove null values from the data object
|
||||
if (_.isObject(params.data)) {
|
||||
params.data = _.pick(params.data, function(value) {
|
||||
return (value !== null)
|
||||
})
|
||||
}
|
||||
|
||||
// make ajax request depending on method
|
||||
if(method === 'get') {
|
||||
jqXHR = jQuery.get(
|
||||
|
@ -15,6 +15,10 @@ const columns = [
|
||||
name: 'segments',
|
||||
label: MailPoet.I18n.t('segments')
|
||||
},
|
||||
{
|
||||
name: 'signups',
|
||||
label: MailPoet.I18n.t('signups')
|
||||
},
|
||||
{
|
||||
name: 'created_at',
|
||||
label: MailPoet.I18n.t('createdOn'),
|
||||
@ -146,9 +150,12 @@ const FormList = React.createClass({
|
||||
</strong>
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column-format" data-colname={MailPoet.I18n.t('segments')}>
|
||||
<td className="column" data-colname={MailPoet.I18n.t('segments')}>
|
||||
{ segments }
|
||||
</td>
|
||||
<td className="column" data-colname={MailPoet.I18n.t('signups')}>
|
||||
{ form.signups }
|
||||
</td>
|
||||
<td className="column-date" data-colname={MailPoet.I18n.t('createdOn')}>
|
||||
<abbr>{ MailPoet.Date.format(form.created_at) }</abbr>
|
||||
</td>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import MailPoet from 'mailpoet'
|
||||
import jQuery from 'jquery'
|
||||
import React from 'react'
|
||||
import _ from 'underscore'
|
||||
import { Router, Link } from 'react-router'
|
||||
import classNames from 'classnames'
|
||||
import ListingBulkActions from 'listing/bulk_actions.jsx'
|
||||
@ -13,7 +14,7 @@ import ListingFilters from 'listing/filters.jsx'
|
||||
const ListingItem = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
toggled: true
|
||||
expanded: false
|
||||
};
|
||||
},
|
||||
handleSelectItem: function(e) {
|
||||
@ -34,7 +35,7 @@ const ListingItem = React.createClass({
|
||||
this.props.onDeleteItem(id);
|
||||
},
|
||||
handleToggleItem: function(id) {
|
||||
this.setState({ toggled: !this.state.toggled });
|
||||
this.setState({ expanded: !this.state.expanded });
|
||||
},
|
||||
render: function() {
|
||||
var checkbox = false;
|
||||
@ -182,7 +183,7 @@ const ListingItem = React.createClass({
|
||||
);
|
||||
}
|
||||
|
||||
const row_classes = classNames({ 'is-expanded': !this.state.toggled });
|
||||
const row_classes = classNames({ 'is-expanded': this.state.expanded });
|
||||
|
||||
return (
|
||||
<tr className={ row_classes }>
|
||||
@ -303,13 +304,12 @@ const Listing = React.createClass({
|
||||
getParam: function(param) {
|
||||
const regex = /(.*)\[(.*)\]/;
|
||||
const matches = regex.exec(param);
|
||||
return [matches[1], matches[2]]
|
||||
return [matches[1], matches[2]];
|
||||
},
|
||||
initWithParams: function(params) {
|
||||
let state = this.getInitialState();
|
||||
|
||||
// check for url params
|
||||
if (params.splat !== undefined) {
|
||||
if (params.splat) {
|
||||
params.splat.split('/').map(param => {
|
||||
let [key, value] = this.getParam(param);
|
||||
switch(key) {
|
||||
@ -348,6 +348,17 @@ const Listing = React.createClass({
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
},
|
||||
getParams: function() {
|
||||
// get all route parameters (without the "splat")
|
||||
let params = _.omit(this.props.params, 'splat');
|
||||
// TODO:
|
||||
// find a way to set the "type" in the routes definition
|
||||
// so that it appears in `this.props.params`
|
||||
if (this.props.type) {
|
||||
params.type = this.props.type;
|
||||
}
|
||||
return params;
|
||||
},
|
||||
setParams: function() {
|
||||
if (this.props.location) {
|
||||
let params = Object.keys(this.state)
|
||||
@ -378,18 +389,38 @@ const Listing = React.createClass({
|
||||
.filter(key => { return (key !== undefined) })
|
||||
.join('/');
|
||||
|
||||
// prepend url with "tab" if specified
|
||||
if (this.props.tab !== undefined) {
|
||||
params = `/${ this.props.tab }/${ params }`;
|
||||
} else {
|
||||
params = `/${ params }`;
|
||||
}
|
||||
// set url
|
||||
let url = this.getUrlWithParams(params);
|
||||
|
||||
if (this.props.location.pathname !== params) {
|
||||
this.context.router.push(`${params}`);
|
||||
if (this.props.location.pathname !== url) {
|
||||
this.context.router.push(`${url}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
getUrlWithParams: function(params) {
|
||||
let base_url = (this.props.base_url !== undefined)
|
||||
? this.props.base_url
|
||||
: null;
|
||||
|
||||
if (base_url !== null) {
|
||||
base_url = this.setBaseUrlParams(base_url);
|
||||
return `/${ base_url }/${ params }`;
|
||||
} else {
|
||||
return `/${ params }`;
|
||||
}
|
||||
},
|
||||
setBaseUrlParams: function(base_url) {
|
||||
if (base_url.indexOf(':') !== -1) {
|
||||
const params = this.getParams();
|
||||
Object.keys(params).map((key) => {
|
||||
if (base_url.indexOf(':'+key) !== -1) {
|
||||
base_url = base_url.replace(':'+key, params[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return base_url;
|
||||
},
|
||||
componentDidMount: function() {
|
||||
if (this.isMounted()) {
|
||||
const params = this.props.params || {};
|
||||
@ -416,7 +447,7 @@ const Listing = React.createClass({
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'listing',
|
||||
data: {
|
||||
tab: (this.props.tab) ? this.props.tab : '',
|
||||
params: this.getParams(),
|
||||
offset: (this.state.page - 1) * this.state.limit,
|
||||
limit: this.state.limit,
|
||||
group: this.state.group,
|
||||
@ -531,7 +562,7 @@ const Listing = React.createClass({
|
||||
|
||||
var data = params || {};
|
||||
data.listing = {
|
||||
tab: (this.props.tab) ? this.props.tab : '',
|
||||
params: this.getParams(),
|
||||
offset: 0,
|
||||
limit: 0,
|
||||
filter: this.state.filter,
|
||||
|
152
assets/js/src/newsletters/listings/mixins.jsx
Normal file
152
assets/js/src/newsletters/listings/mixins.jsx
Normal file
@ -0,0 +1,152 @@
|
||||
import React from 'react'
|
||||
import MailPoet from 'mailpoet'
|
||||
import classNames from 'classnames'
|
||||
import jQuery from 'jquery'
|
||||
|
||||
const _QueueMixin = {
|
||||
pauseSending: function(newsletter) {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'sendingQueue',
|
||||
action: 'pause',
|
||||
data: newsletter.id
|
||||
}).done(function() {
|
||||
jQuery('#resume_'+newsletter.id).show();
|
||||
jQuery('#pause_'+newsletter.id).hide();
|
||||
});
|
||||
},
|
||||
resumeSending: function(newsletter) {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'sendingQueue',
|
||||
action: 'resume',
|
||||
data: newsletter.id
|
||||
}).done(function() {
|
||||
jQuery('#pause_'+newsletter.id).show();
|
||||
jQuery('#resume_'+newsletter.id).hide();
|
||||
});
|
||||
},
|
||||
renderQueueStatus: function(newsletter) {
|
||||
if (!newsletter.queue) {
|
||||
return (
|
||||
<span>{MailPoet.I18n.t('notSentYet')}</span>
|
||||
);
|
||||
} else {
|
||||
if (newsletter.queue.status === 'scheduled') {
|
||||
return (
|
||||
<span>
|
||||
{ MailPoet.I18n.t('scheduledFor') } { MailPoet.Date.format(newsletter.queue.scheduled_at) }
|
||||
</span>
|
||||
)
|
||||
}
|
||||
const progressClasses = classNames(
|
||||
'mailpoet_progress',
|
||||
{ 'mailpoet_progress_complete': newsletter.queue.status === 'completed'}
|
||||
);
|
||||
|
||||
// calculate percentage done
|
||||
const percentage = Math.round(
|
||||
(newsletter.queue.count_processed * 100) / (newsletter.queue.count_total)
|
||||
);
|
||||
|
||||
let label;
|
||||
|
||||
if (newsletter.queue.status === 'completed') {
|
||||
label = (
|
||||
<span>
|
||||
{
|
||||
MailPoet.I18n.t('newsletterQueueCompleted')
|
||||
.replace(
|
||||
"%$1d",
|
||||
newsletter.queue.count_processed - newsletter.queue.count_failed
|
||||
)
|
||||
.replace(
|
||||
"%$2d",
|
||||
newsletter.queue.count_total
|
||||
)
|
||||
}
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
label = (
|
||||
<span>
|
||||
{ newsletter.queue.count_processed } / { newsletter.queue.count_total }
|
||||
|
||||
<a
|
||||
id={ 'resume_'+newsletter.id }
|
||||
className="button"
|
||||
style={{ display: (newsletter.queue.status === 'paused')
|
||||
? 'inline-block': 'none' }}
|
||||
href="javascript:;"
|
||||
onClick={ this.resumeSending.bind(null, newsletter) }
|
||||
>{MailPoet.I18n.t('resume')}</a>
|
||||
<a
|
||||
id={ 'pause_'+newsletter.id }
|
||||
className="button mailpoet_pause"
|
||||
style={{ display: (newsletter.queue.status === null)
|
||||
? 'inline-block': 'none' }}
|
||||
href="javascript:;"
|
||||
onClick={ this.pauseSending.bind(null, newsletter) }
|
||||
>{MailPoet.I18n.t('pause')}</a>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={ progressClasses }>
|
||||
<span
|
||||
className="mailpoet_progress_bar"
|
||||
style={ { width: percentage + "%"} }
|
||||
></span>
|
||||
<span className="mailpoet_progress_label">
|
||||
{ percentage + "%" }
|
||||
</span>
|
||||
</div>
|
||||
<p style={{ textAlign:'center' }}>
|
||||
{ label }
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const _StatisticsMixin = {
|
||||
renderStatistics: function(newsletter) {
|
||||
if (
|
||||
newsletter.statistics
|
||||
&& newsletter.queue
|
||||
&& newsletter.queue.status !== 'scheduled'
|
||||
) {
|
||||
const total_sent = ~~(newsletter.queue.count_processed);
|
||||
|
||||
let percentage_clicked = 0;
|
||||
let percentage_opened = 0;
|
||||
let percentage_unsubscribed = 0;
|
||||
|
||||
if (total_sent > 0) {
|
||||
percentage_clicked = Math.round(
|
||||
(~~(newsletter.statistics.clicked) * 100) / total_sent
|
||||
);
|
||||
percentage_opened = Math.round(
|
||||
(~~(newsletter.statistics.opened) * 100) / total_sent
|
||||
);
|
||||
percentage_unsubscribed = Math.round(
|
||||
(~~(newsletter.statistics.unsubscribed) * 100) / total_sent
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
{ percentage_opened }%, { percentage_clicked }%, { percentage_unsubscribed }%
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<span>{MailPoet.I18n.t('notSentYet')}</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { _QueueMixin as QueueMixin };
|
||||
export { _StatisticsMixin as StatisticsMixin };
|
@ -252,6 +252,20 @@ const NewsletterListNotification = React.createClass({
|
||||
</span>
|
||||
);
|
||||
},
|
||||
renderHistoryLink: function(newsletter) {
|
||||
const childrenCount = ~~(newsletter.children_count);
|
||||
if (childrenCount === 0) {
|
||||
return (
|
||||
MailPoet.I18n.t('notSentYet')
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Link
|
||||
to={ `/notification/history/${ newsletter.id }` }
|
||||
>{ MailPoet.I18n.t('viewHistory') }</Link>
|
||||
);
|
||||
}
|
||||
},
|
||||
renderItem: function(newsletter, actions) {
|
||||
const rowClasses = classNames(
|
||||
'manage-column',
|
||||
@ -277,7 +291,7 @@ const NewsletterListNotification = React.createClass({
|
||||
{ this.renderSettings(newsletter) }
|
||||
</td>
|
||||
<td className="column" data-colname={ MailPoet.I18n.t('history') }>
|
||||
<a href="#TODO">{ MailPoet.I18n.t('viewHistory') }</a>
|
||||
{ this.renderHistoryLink(newsletter) }
|
||||
</td>
|
||||
<td className="column-date" data-colname={ MailPoet.I18n.t('lastModifiedOn') }>
|
||||
<abbr>{ MailPoet.Date.format(newsletter.updated_at) }</abbr>
|
||||
@ -299,7 +313,8 @@ const NewsletterListNotification = React.createClass({
|
||||
location={ this.props.location }
|
||||
params={ this.props.params }
|
||||
endpoint="newsletters"
|
||||
tab="notification"
|
||||
type="notification"
|
||||
base_url="notification"
|
||||
onRenderItem={ this.renderItem }
|
||||
columns={ columns }
|
||||
bulk_actions={ bulk_actions }
|
||||
|
125
assets/js/src/newsletters/listings/notification_history.jsx
Normal file
125
assets/js/src/newsletters/listings/notification_history.jsx
Normal file
@ -0,0 +1,125 @@
|
||||
import React from 'react'
|
||||
import { Router, Link } from 'react-router'
|
||||
import classNames from 'classnames'
|
||||
import jQuery from 'jquery'
|
||||
import MailPoet from 'mailpoet'
|
||||
|
||||
import Listing from 'listing/listing.jsx'
|
||||
import ListingTabs from 'newsletters/listings/tabs.jsx'
|
||||
|
||||
import { QueueMixin, StatisticsMixin } from 'newsletters/listings/mixins.jsx'
|
||||
|
||||
const mailpoet_tracking_enabled = (!!(window['mailpoet_tracking_enabled']));
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'subject',
|
||||
label: MailPoet.I18n.t('subject'),
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
label: MailPoet.I18n.t('status')
|
||||
},
|
||||
{
|
||||
name: 'segments',
|
||||
label: MailPoet.I18n.t('lists')
|
||||
},
|
||||
{
|
||||
name: 'statistics',
|
||||
label: MailPoet.I18n.t('statistics'),
|
||||
display: mailpoet_tracking_enabled
|
||||
},
|
||||
{
|
||||
name: 'processed_at',
|
||||
label: MailPoet.I18n.t('sentOn'),
|
||||
}
|
||||
];
|
||||
|
||||
const newsletter_actions = [
|
||||
{
|
||||
name: 'view',
|
||||
link: function(newsletter) {
|
||||
return (
|
||||
<a href={ newsletter.preview_url } target="_blank">
|
||||
{MailPoet.I18n.t('preview')}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const NewsletterListNotificationHistory = React.createClass({
|
||||
mixins: [QueueMixin, StatisticsMixin],
|
||||
renderItem: function(newsletter, actions) {
|
||||
const rowClasses = classNames(
|
||||
'manage-column',
|
||||
'column-primary',
|
||||
'has-row-actions'
|
||||
);
|
||||
|
||||
const segments = newsletter.segments.map(function(segment) {
|
||||
return segment.name
|
||||
}).join(', ');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<td className={ rowClasses }>
|
||||
<strong>
|
||||
<a
|
||||
href={ newsletter.preview_url }
|
||||
target="_blank"
|
||||
>{ newsletter.subject }</a>
|
||||
</strong>
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column" data-colname={ MailPoet.I18n.t('status') }>
|
||||
{ this.renderQueueStatus(newsletter) }
|
||||
</td>
|
||||
<td className="column" data-colname={ MailPoet.I18n.t('lists') }>
|
||||
{ segments }
|
||||
</td>
|
||||
{ (mailpoet_tracking_enabled === true) ? (
|
||||
<td className="column" data-colname={ MailPoet.I18n.t('statistics') }>
|
||||
{ this.renderStatistics(newsletter) }
|
||||
</td>
|
||||
) : null }
|
||||
<td className="column-date" data-colname={ MailPoet.I18n.t('lastModifiedOn') }>
|
||||
<abbr>{ MailPoet.Date.format(newsletter.updated_at) }</abbr>
|
||||
</td>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="title">
|
||||
{MailPoet.I18n.t('pageTitle')} <Link className="page-title-action" to="/new">{MailPoet.I18n.t('new')}</Link>
|
||||
</h1>
|
||||
|
||||
<ListingTabs tab="notification" />
|
||||
|
||||
<Link
|
||||
className="page-title-action"
|
||||
to="/notification"
|
||||
>{MailPoet.I18n.t('backToPostNotifications')}</Link>
|
||||
|
||||
<Listing
|
||||
limit={ mailpoet_listing_per_page }
|
||||
location={ this.props.location }
|
||||
params={ this.props.params }
|
||||
endpoint="newsletters"
|
||||
type="notification_history"
|
||||
base_url="notification/history/:parent_id"
|
||||
onRenderItem={ this.renderItem }
|
||||
columns={columns}
|
||||
item_actions={ newsletter_actions }
|
||||
auto_refresh={ true }
|
||||
sort_by="updated_at"
|
||||
sort_order="desc"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = NewsletterListNotificationHistory;
|
@ -7,6 +7,8 @@ import MailPoet from 'mailpoet'
|
||||
import Listing from 'listing/listing.jsx'
|
||||
import ListingTabs from 'newsletters/listings/tabs.jsx'
|
||||
|
||||
import { QueueMixin, StatisticsMixin } from 'newsletters/listings/mixins.jsx'
|
||||
|
||||
const mailpoet_tracking_enabled = (!!(window['mailpoet_tracking_enabled']));
|
||||
|
||||
const messages = {
|
||||
@ -139,135 +141,7 @@ const newsletter_actions = [
|
||||
];
|
||||
|
||||
const NewsletterListStandard = React.createClass({
|
||||
pauseSending: function(newsletter) {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'sendingQueue',
|
||||
action: 'pause',
|
||||
data: newsletter.id
|
||||
}).done(function() {
|
||||
jQuery('#resume_'+newsletter.id).show();
|
||||
jQuery('#pause_'+newsletter.id).hide();
|
||||
});
|
||||
},
|
||||
resumeSending: function(newsletter) {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'sendingQueue',
|
||||
action: 'resume',
|
||||
data: newsletter.id
|
||||
}).done(function() {
|
||||
jQuery('#pause_'+newsletter.id).show();
|
||||
jQuery('#resume_'+newsletter.id).hide();
|
||||
});
|
||||
},
|
||||
renderStatus: function(newsletter) {
|
||||
if (!newsletter.queue) {
|
||||
return (
|
||||
<span>{MailPoet.I18n.t('notSentYet')}</span>
|
||||
);
|
||||
} else {
|
||||
if (newsletter.queue.status === 'scheduled') {
|
||||
return (
|
||||
<span>{MailPoet.I18n.t('scheduledFor')} { MailPoet.Date.format(newsletter.queue.scheduled_at) } </span>
|
||||
)
|
||||
}
|
||||
const progressClasses = classNames(
|
||||
'mailpoet_progress',
|
||||
{ 'mailpoet_progress_complete': newsletter.queue.status === 'completed'}
|
||||
);
|
||||
|
||||
// calculate percentage done
|
||||
const percentage = Math.round(
|
||||
(newsletter.queue.count_processed * 100) / (newsletter.queue.count_total)
|
||||
);
|
||||
|
||||
let label;
|
||||
|
||||
if (newsletter.queue.status === 'completed') {
|
||||
label = (
|
||||
<span>
|
||||
{
|
||||
MailPoet.I18n.t('newsletterQueueCompleted')
|
||||
.replace("%$1d", newsletter.queue.count_processed - newsletter.queue.count_failed)
|
||||
.replace("%$2d", newsletter.queue.count_total)
|
||||
}
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
label = (
|
||||
<span>
|
||||
{ newsletter.queue.count_processed } / { newsletter.queue.count_total }
|
||||
|
||||
<a
|
||||
id={ 'resume_'+newsletter.id }
|
||||
className="button"
|
||||
style={{ display: (newsletter.queue.status === 'paused') ? 'inline-block': 'none' }}
|
||||
href="javascript:;"
|
||||
onClick={ this.resumeSending.bind(null, newsletter) }
|
||||
>{MailPoet.I18n.t('resume')}</a>
|
||||
<a
|
||||
id={ 'pause_'+newsletter.id }
|
||||
className="button mailpoet_pause"
|
||||
style={{ display: (newsletter.queue.status === null) ? 'inline-block': 'none' }}
|
||||
href="javascript:;"
|
||||
onClick={ this.pauseSending.bind(null, newsletter) }
|
||||
>{MailPoet.I18n.t('pause')}</a>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={ progressClasses }>
|
||||
<span
|
||||
className="mailpoet_progress_bar"
|
||||
style={ { width: percentage + "%"} }
|
||||
></span>
|
||||
<span className="mailpoet_progress_label">
|
||||
{ percentage + "%" }
|
||||
</span>
|
||||
</div>
|
||||
<p style={{ textAlign:'center' }}>
|
||||
{ label }
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
renderStatistics: function(newsletter) {
|
||||
if (mailpoet_tracking_enabled === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newsletter.statistics && newsletter.queue && newsletter.queue.status !== 'scheduled') {
|
||||
const total_sent = ~~(newsletter.queue.count_processed);
|
||||
|
||||
let percentage_clicked = 0;
|
||||
let percentage_opened = 0;
|
||||
let percentage_unsubscribed = 0;
|
||||
|
||||
if (total_sent > 0) {
|
||||
percentage_clicked = Math.round(
|
||||
(~~(newsletter.statistics.clicked) * 100) / total_sent
|
||||
);
|
||||
percentage_opened = Math.round(
|
||||
(~~(newsletter.statistics.opened) * 100) / total_sent
|
||||
);
|
||||
percentage_unsubscribed = Math.round(
|
||||
(~~(newsletter.statistics.unsubscribed) * 100) / total_sent
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
{ percentage_opened }%, { percentage_clicked }%, { percentage_unsubscribed }%
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<span>{MailPoet.I18n.t('notSentYet')}</span>
|
||||
);
|
||||
}
|
||||
},
|
||||
mixins: [QueueMixin, StatisticsMixin],
|
||||
renderItem: function(newsletter, actions) {
|
||||
const rowClasses = classNames(
|
||||
'manage-column',
|
||||
@ -291,7 +165,7 @@ const NewsletterListStandard = React.createClass({
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column" data-colname={ MailPoet.I18n.t('status') }>
|
||||
{ this.renderStatus(newsletter) }
|
||||
{ this.renderQueueStatus(newsletter) }
|
||||
</td>
|
||||
<td className="column" data-colname={ MailPoet.I18n.t('lists') }>
|
||||
{ segments }
|
||||
@ -321,7 +195,8 @@ const NewsletterListStandard = React.createClass({
|
||||
location={ this.props.location }
|
||||
params={ this.props.params }
|
||||
endpoint="newsletters"
|
||||
tab="standard"
|
||||
type="standard"
|
||||
base_url="standard"
|
||||
onRenderItem={this.renderItem}
|
||||
columns={columns}
|
||||
bulk_actions={ bulk_actions }
|
||||
|
@ -343,7 +343,8 @@ const NewsletterListWelcome = React.createClass({
|
||||
location={ this.props.location }
|
||||
params={ this.props.params }
|
||||
endpoint="newsletters"
|
||||
tab="welcome"
|
||||
type="welcome"
|
||||
base_url="welcome"
|
||||
onRenderItem={ this.renderItem }
|
||||
columns={ columns }
|
||||
bulk_actions={ bulk_actions }
|
||||
|
@ -14,12 +14,13 @@ import NewsletterTypeNotification from 'newsletters/types/notification/notificat
|
||||
import NewsletterListStandard from 'newsletters/listings/standard.jsx'
|
||||
import NewsletterListWelcome from 'newsletters/listings/welcome.jsx'
|
||||
import NewsletterListNotification from 'newsletters/listings/notification.jsx'
|
||||
import NewsletterListNotificationHistory from 'newsletters/listings/notification_history.jsx'
|
||||
|
||||
const history = useRouterHistory(createHashHistory)({ queryKey: false });
|
||||
|
||||
const App = React.createClass({
|
||||
render() {
|
||||
return this.props.children
|
||||
return this.props.children;
|
||||
}
|
||||
});
|
||||
|
||||
@ -31,18 +32,16 @@ if(container) {
|
||||
<Route path="/" component={ App }>
|
||||
<IndexRedirect to="standard" />
|
||||
{/* Listings */}
|
||||
<Route name="listing/standard" path="standard" component={ NewsletterListStandard } />
|
||||
<Route name="listing/welcome" path="welcome" component={ NewsletterListWelcome } />
|
||||
<Route name="listing/notification" path="notification" component={ NewsletterListNotification } />
|
||||
<Route path="standard/*" component={ NewsletterListStandard } />
|
||||
<Route path="welcome/*" component={ NewsletterListWelcome } />
|
||||
<Route path="notification/*" component={ NewsletterListNotification } />
|
||||
<Route path="standard(/)**" params={{ tab: 'standard' }} component={ NewsletterListStandard } />
|
||||
<Route path="welcome(/)**" component={ NewsletterListWelcome } />
|
||||
<Route path="notification/history/:parent_id(/)**" component={ NewsletterListNotificationHistory } />
|
||||
<Route path="notification(/)**" component={ NewsletterListNotification } />
|
||||
{/* Newsletter: type selection */}
|
||||
<Route path="new" component={ NewsletterTypes } />
|
||||
{/* New newsletter: types */}
|
||||
<Route name="new/standard" path="new/standard" component={ NewsletterTypeStandard } />
|
||||
<Route name="new/welcome" path="new/welcome" component={ NewsletterTypeWelcome } />
|
||||
<Route name="new/notification" path="new/notification" component={ NewsletterTypeNotification } />
|
||||
<Route path="new/standard" component={ NewsletterTypeStandard } />
|
||||
<Route path="new/welcome" component={ NewsletterTypeWelcome } />
|
||||
<Route path="new/notification" component={ NewsletterTypeNotification } />
|
||||
{/* Template selection */}
|
||||
<Route name="template" path="template/:id" component={ NewsletterTemplates } />
|
||||
{/* Sending options */}
|
||||
|
@ -32,7 +32,7 @@ class API {
|
||||
$endpoint = self::ENDPOINT_NAMESPACE . ucfirst($this->endpoint);
|
||||
if(!$this->api_request) return;
|
||||
if(!$this->endpoint || !class_exists($endpoint)) {
|
||||
$this->terminateRequest(self::RESPONSE_ERROR, __('Invalid API endpoint.'));
|
||||
self::terminateRequest(self::RESPONSE_ERROR, __('Invalid API endpoint.'));
|
||||
}
|
||||
$this->callEndpoint(
|
||||
$endpoint,
|
||||
@ -43,7 +43,7 @@ class API {
|
||||
|
||||
function callEndpoint($endpoint, $action, $data) {
|
||||
if(!method_exists($endpoint, $action)) {
|
||||
$this->terminateRequest(self::RESPONSE_ERROR, __('Invalid API action.'));
|
||||
self::terminateRequest(self::RESPONSE_ERROR, __('Invalid API action.'));
|
||||
}
|
||||
call_user_func(
|
||||
array(
|
||||
@ -56,9 +56,16 @@ class API {
|
||||
|
||||
static function decodeRequestData($data) {
|
||||
$data = base64_decode($data);
|
||||
return (is_serialized($data)) ?
|
||||
unserialize($data) :
|
||||
self::terminateRequest(self::RESPONSE_ERROR, __('Invalid API data format.'));
|
||||
|
||||
if(is_serialized($data)) {
|
||||
$data = unserialize($data);
|
||||
}
|
||||
|
||||
if(!is_array($data)) {
|
||||
$data = array();
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
static function encodeRequestData($data) {
|
||||
@ -76,7 +83,7 @@ class API {
|
||||
return add_query_arg($params, home_url());
|
||||
}
|
||||
|
||||
function terminateRequest($code, $message) {
|
||||
static function terminateRequest($code, $message) {
|
||||
status_header($code, $message);
|
||||
exit;
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ class Subscription {
|
||||
|
||||
static function confirm($data) {
|
||||
$subscription = new UserSubscription\Pages('confirm', $data);
|
||||
$subscription->confirm();
|
||||
}
|
||||
|
||||
static function manage($data) {
|
||||
@ -18,5 +19,6 @@ class Subscription {
|
||||
|
||||
static function unsubscribe($data) {
|
||||
$subscription = new UserSubscription\Pages('unsubscribe', $data);
|
||||
$subscription->unsubscribe();
|
||||
}
|
||||
}
|
@ -56,6 +56,7 @@ class Initializer {
|
||||
\ORM::configure(Env::$db_source_name);
|
||||
\ORM::configure('username', Env::$db_username);
|
||||
\ORM::configure('password', Env::$db_password);
|
||||
\ORM::configure('logging', WP_DEBUG);
|
||||
\ORM::configure('driver_options', array(
|
||||
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
|
||||
\PDO::MYSQL_ATTR_INIT_COMMAND =>
|
||||
|
@ -173,6 +173,7 @@ class Migrator {
|
||||
function newsletters() {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'parent_id mediumint(9) NULL,',
|
||||
'subject varchar(250) NOT NULL DEFAULT "",',
|
||||
'type varchar(20) NOT NULL DEFAULT "standard",',
|
||||
'sender_address varchar(150) NOT NULL DEFAULT "",',
|
||||
|
@ -69,6 +69,13 @@ class Populator {
|
||||
private function createDefaultSettings() {
|
||||
$current_user = wp_get_current_user();
|
||||
|
||||
if(!Setting::getValue('task_scheduler')) {
|
||||
// disable task scheduler (cron) be default
|
||||
Setting::setValue('task_scheduler', array(
|
||||
'method' => 'WordPress'
|
||||
));
|
||||
}
|
||||
|
||||
// default sender info based on current user
|
||||
$sender = array(
|
||||
'name' => $current_user->display_name,
|
||||
@ -106,7 +113,7 @@ class Populator {
|
||||
$default_segment->hydrate(array(
|
||||
'name' => __('My First List'),
|
||||
'description' =>
|
||||
__('The list is automatically created when you install MailPoet')
|
||||
__('This list is automatically created when you install MailPoet')
|
||||
));
|
||||
$default_segment->save();
|
||||
}
|
||||
|
@ -254,7 +254,7 @@ class FranksRoastHouseTemplate {
|
||||
),
|
||||
array(
|
||||
"type" => "text",
|
||||
"text" => __("<h2>New and Improved Hours!</h2>\n<p></p>\n<p>Frank's is now open even later, so you can get your caffeine fix all day long! Here's our new opening hours:</p>\n<p></p>\n<ul>\n<li>Monday - Thursday: 6am - 12am</li>\n<li>Friday - Saturday: 6am - 1:30am</li>\n<li>Sunday: 7:30am - 11pm</li>\n</ul>")
|
||||
"text" => __("<h2>New and Improved Hours!</h2>\n<p></p>\n<p>Frank's is now open even later, so you can get your caffeine fix all day (and night) long! Here's our new opening hours:</p>\n<p></p>\n<ul>\n<li>Monday - Thursday: 6am - 12am</li>\n<li>Friday - Saturday: 6am - 1:30am</li>\n<li>Sunday: 7:30am - 11pm</li>\n</ul>")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -13,8 +13,8 @@ class PostNotificationsBlankTemplate {
|
||||
|
||||
function get() {
|
||||
return array(
|
||||
'name' => __("Post Notifications Blank Template"),
|
||||
'description' => __("A simple and plain post notifications template for you to create your own email from."),
|
||||
'name' => __("Blank Post Notifications Template"),
|
||||
'description' => __("A simple post notifications template. Customize it and make it your own!"),
|
||||
'readonly' => 0,
|
||||
'thumbnail' => $this->getThumbnail(),
|
||||
'body' => json_encode($this->getBody()),
|
||||
@ -72,7 +72,7 @@ class PostNotificationsBlankTemplate {
|
||||
"blocks" => array(
|
||||
array(
|
||||
"type" => "text",
|
||||
"text" => __("<p style=\"text-align: right;\">Your posts below are generated using our <strong>Automated Latest Content widget</strong> in the right sidebar.</p>")
|
||||
"text" => __("<p style=\"text-align: right;\">Your posts below are generated using our <strong>Automated Latest Content widget</strong> on the right sidebar.</p>")
|
||||
)
|
||||
)
|
||||
),
|
||||
|
@ -14,7 +14,7 @@ class WelcomeTemplate {
|
||||
function get() {
|
||||
return array(
|
||||
'name' => __("Welcome Email Example"),
|
||||
'description' => __("A really simple welcome email idea to help you say hello your new subscribers."),
|
||||
'description' => __("A simple welcome email idea to help welcome your new subscribers."),
|
||||
'readonly' => 0,
|
||||
'thumbnail' => $this->getThumbnail(),
|
||||
'body' => json_encode($this->getBody()),
|
||||
@ -101,7 +101,7 @@ class WelcomeTemplate {
|
||||
"blocks" => array(
|
||||
array(
|
||||
"type" => "text",
|
||||
"text" => __("<h1 style=\"text-align: center;\"><strong>What's in a Welcome Email? </strong></h1>\n<p>How about thanking the person and telling them what they can expect from signing up as a subscriber, for example, how frequently they might receive newsletters from you and what type of content. If you also write a blog, why not share a few of your most recent posts using Automated Latest Content?</p>\n<p>Get help with your welcome email in our blog post: <a href=\"http://www.mailpoet.com/improve-your-signup-welcome-email/\">http://www.mailpoet.com/improve-your-signup-welcome-email/</a></p>\n<p></p>")
|
||||
"text" => __("<h1 style=\"text-align: center;\"><strong>What's should you put in a Welcome Email? </strong></h1>\n<p>We recommend thanking the person (for subscribing!) and telling them what they can expect in the future. For example, how the type of content you'll be sending (and how often you'll be sending it.) If you have a blog, why not share a few of your recent posts using the Automated Latest Content widget?</p>\n<p>Get some more tips on your Welcome Email in our blog post: <a href=\"http://www.mailpoet.com/improve-your-signup-welcome-email/\">http://www.mailpoet.com/improve-your-signup-welcome-email/</a></p>\n<p></p>")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -4,6 +4,7 @@ use \MailPoet\Models\Newsletter;
|
||||
use \MailPoet\Models\Subscriber;
|
||||
use \MailPoet\Models\SubscriberSegment;
|
||||
use \MailPoet\Subscription;
|
||||
use MailPoet\Newsletter\Url as NewsletterUrl;
|
||||
|
||||
class Shortcodes {
|
||||
function __construct() {
|
||||
@ -113,10 +114,12 @@ class Shortcodes {
|
||||
}
|
||||
|
||||
function renderArchiveSubject($newsletter) {
|
||||
return '<a href="TODO" target="_blank" title="'
|
||||
$preview_url = NewsletterUrl::getViewInBrowserUrl($newsletter);
|
||||
|
||||
return '<a href="'.esc_attr($preview_url).'" target="_blank" title="'
|
||||
.esc_attr(__('Preview in a new tab')).'">'
|
||||
.esc_attr($newsletter->subject).
|
||||
'</a>';
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -67,29 +67,33 @@ class Scheduler {
|
||||
}
|
||||
|
||||
function processPostNotificationNewsletter($newsletter, $queue) {
|
||||
// ensure that segments exist
|
||||
$segments = $newsletter->segments()->findArray();
|
||||
if(empty($segments)) {
|
||||
$this->deleteQueueOrUpdateNextRunDate($queue, $newsletter);
|
||||
return;
|
||||
}
|
||||
$segment_ids = array_map(function($segment) {
|
||||
return $segment['id'];
|
||||
return (int)$segment['id'];
|
||||
}, $segments);
|
||||
|
||||
// ensure that subscribers are in segments
|
||||
$subscribers = Subscriber::getSubscribedInSegments($segment_ids)
|
||||
->findArray();
|
||||
$subscribers = Helpers::arrayColumn($subscribers, 'subscriber_id');
|
||||
$subscribers = array_unique($subscribers);
|
||||
|
||||
if(empty($subscribers)) {
|
||||
$this->deleteQueueOrUpdateNextRunDate($queue, $newsletter);
|
||||
return;
|
||||
}
|
||||
// schedule new queue if the post notification is not destined for immediate delivery
|
||||
if($newsletter->intervalType !== NewsletterScheduler::INTERVAL_IMMEDIATELY) {
|
||||
$new_queue = SendingQueue::create();
|
||||
$new_queue->newsletter_id = $newsletter->id;
|
||||
$new_queue->status = NewsletterScheduler::STATUS_SCHEDULED;
|
||||
self::deleteQueueOrUpdateNextRunDate($new_queue, $newsletter);
|
||||
}
|
||||
|
||||
// create a duplicate newsletter that acts as a history record
|
||||
$notification_history = $this->createNotificationHistory($newsletter->id);
|
||||
if(!$notification_history) return;
|
||||
|
||||
// queue newsletter for delivery
|
||||
$queue->newsletter_id = $notification_history->id;
|
||||
$queue->subscribers = serialize(
|
||||
array(
|
||||
'to_process' => $subscribers
|
||||
@ -164,7 +168,7 @@ class Scheduler {
|
||||
return true;
|
||||
}
|
||||
|
||||
private function deleteQueueOrUpdateNextRunDate($queue, $newsletter) {
|
||||
function deleteQueueOrUpdateNextRunDate($queue, $newsletter) {
|
||||
if($newsletter->intervalType === NewsletterScheduler::INTERVAL_IMMEDIATELY) {
|
||||
$queue->delete();
|
||||
} else {
|
||||
@ -173,4 +177,12 @@ class Scheduler {
|
||||
$queue->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createNotificationHistory($newsletter_id) {
|
||||
$newsletter = Newsletter::findOne($newsletter_id);
|
||||
$notification_history = $newsletter->createNotificationHistory();
|
||||
return ($notification_history->getErrors() === false) ?
|
||||
$notification_history :
|
||||
false;
|
||||
}
|
||||
}
|
@ -178,13 +178,14 @@ class SendingQueue {
|
||||
if(!$queue->count_to_process) {
|
||||
$queue->processed_at = current_time('mysql');
|
||||
$queue->status = SendingQueueModel::STATUS_COMPLETED;
|
||||
// set newsletter status to sent
|
||||
// if it's a standard or post notificaiton newsletter, update its status to sent
|
||||
$newsletter = NewsletterModel::findOne($queue->newsletter_id);
|
||||
// if it's a standard newsletter, update its status
|
||||
if($newsletter->type === NewsletterModel::TYPE_STANDARD) {
|
||||
if($newsletter->type === NewsletterModel::TYPE_STANDARD ||
|
||||
$newsletter->type === NewsletterModel::TYPE_NOTIFICATION_HISTORY
|
||||
) {
|
||||
$newsletter->setStatus(NewsletterModel::STATUS_SENT);
|
||||
}
|
||||
}
|
||||
return $queue->save();
|
||||
}
|
||||
}
|
||||
}
|
@ -43,7 +43,7 @@ class Mailer {
|
||||
function getMailerConfig() {
|
||||
$mta_config = Setting::getValue('mta');
|
||||
if(!$mta_config) {
|
||||
throw new \Exception(__('Mailer is not configured.'));
|
||||
throw new \Exception(__('Mailer is not configured'));
|
||||
}
|
||||
return $mta_config;
|
||||
}
|
||||
@ -90,7 +90,7 @@ class Mailer {
|
||||
if($this->mta_log['sent'] === $frequency_limit &&
|
||||
$elapsed_time <= $frequency_interval
|
||||
) {
|
||||
throw new \Exception(__('Sending frequency limit has been reached.'));
|
||||
throw new \Exception(__('Sending frequency limit has been reached'));
|
||||
}
|
||||
if($elapsed_time > $frequency_interval) {
|
||||
$this->mta_log = array(
|
||||
|
@ -57,8 +57,8 @@ class Newsletter {
|
||||
if($newsletter['type'] === 'notification' && !$newsletter_contains_posts) {
|
||||
return false;
|
||||
}
|
||||
// save all posts
|
||||
$newsletter = PostsTask::extractAndSave($newsletter);
|
||||
// extract and save newsletter posts
|
||||
PostsTask::extractAndSave($newsletter);
|
||||
return $newsletter;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\Cron\Workers\SendingQueue\Tasks;
|
||||
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\NewsletterPost;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
@ -18,9 +19,12 @@ class Posts {
|
||||
if(!count($matched_posts_ids)) {
|
||||
return $newsletter;
|
||||
}
|
||||
$newsletter_id = ($newsletter['type'] === Newsletter::TYPE_NOTIFICATION_HISTORY) ?
|
||||
$newsletter['parent_id'] :
|
||||
$newsletter['id'];
|
||||
foreach($matched_posts_ids as $post_id) {
|
||||
$newletter_post = NewsletterPost::create();
|
||||
$newletter_post->newsletter_id = $newsletter['id'];
|
||||
$newletter_post->newsletter_id = $newsletter_id;
|
||||
$newletter_post->post_id = $post_id;
|
||||
$newletter_post->save();
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ abstract class Base {
|
||||
if(in_array($block['type'], array('radio', 'checkbox'))) {
|
||||
$rules['group'] = 'custom_field_'.$block['id'];
|
||||
$rules['errors-container'] = '.mailpoet_error_'.$block['id'];
|
||||
$rules['required-message'] = __('Please select at least one option.');
|
||||
$rules['required-message'] = __('Please select at least one option');
|
||||
}
|
||||
|
||||
$validation = array();
|
||||
|
@ -17,7 +17,7 @@ class Widget extends \WP_Widget {
|
||||
'mailpoet_form',
|
||||
__('MailPoet Form'),
|
||||
array(
|
||||
'description' => __('Add a newsletter subscription form.')
|
||||
'description' => __('Add a newsletter subscription form')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -16,8 +16,8 @@ class Handler {
|
||||
$this->model = \Model::factory($this->model_class);
|
||||
|
||||
$this->data = array(
|
||||
// tabs
|
||||
'tab' => (isset($data['tab']) ? $data['tab'] : false),
|
||||
// extra parameters
|
||||
'params' => (isset($data['params']) ? $data['params'] : array()),
|
||||
// pagination
|
||||
'offset' => (isset($data['offset']) ? (int)$data['offset'] : 0),
|
||||
'limit' => (isset($data['limit'])
|
||||
@ -121,7 +121,6 @@ class Handler {
|
||||
$this->table_name.'.'.$this->data['sort_by']
|
||||
)
|
||||
->findMany();
|
||||
|
||||
} else {
|
||||
$this->setFilter();
|
||||
$this->setGroup();
|
||||
|
@ -84,7 +84,7 @@ class Mailer {
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new \Exception(__('Mailing method does not exist.'));
|
||||
throw new \Exception(__('Mailing method does not exist'));
|
||||
}
|
||||
return $mailer_instance;
|
||||
}
|
||||
@ -92,7 +92,7 @@ class Mailer {
|
||||
function getMailer($mailer = false) {
|
||||
if(!$mailer) {
|
||||
$mailer = Setting::getValue('mta');
|
||||
if(!$mailer || !isset($mailer['method'])) throw new \Exception(__('Mailer is not configured.'));
|
||||
if(!$mailer || !isset($mailer['method'])) throw new \Exception(__('Mailer is not configured'));
|
||||
}
|
||||
$mailer['class'] = 'MailPoet\\Mailer\\Methods\\' . $mailer['method'];
|
||||
return $mailer;
|
||||
@ -101,7 +101,7 @@ class Mailer {
|
||||
function getSender($sender = false) {
|
||||
if(empty($sender)) {
|
||||
$sender = Setting::getValue('sender', array());
|
||||
if(empty($sender['address'])) throw new \Exception(__('Sender name and email are not configured.'));
|
||||
if(empty($sender['address'])) throw new \Exception(__('Sender name and email are not configured'));
|
||||
}
|
||||
return array(
|
||||
'from_name' => $sender['name'],
|
||||
|
@ -9,10 +9,10 @@ class CustomField extends Model {
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
$this->addValidations('name', array(
|
||||
'required' => __('Please specify a name.')
|
||||
'required' => __('Please specify a name')
|
||||
));
|
||||
$this->addValidations('type', array(
|
||||
'required' => __('Please specify a type.')
|
||||
'required' => __('Please specify a type')
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ class Form extends Model {
|
||||
parent::__construct();
|
||||
|
||||
$this->addValidations('name', array(
|
||||
'required' => __('Please specify a name.')
|
||||
'required' => __('Please specify a name')
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,7 @@ class Newsletter extends Model {
|
||||
const TYPE_STANDARD = 'standard';
|
||||
const TYPE_WELCOME = 'welcome';
|
||||
const TYPE_NOTIFICATION = 'notification';
|
||||
const TYPE_NOTIFICATION_HISTORY = 'notification_history';
|
||||
|
||||
// standard newsletters
|
||||
const STATUS_DRAFT = 'draft';
|
||||
@ -19,12 +20,11 @@ class Newsletter extends Model {
|
||||
// automatic newsletters status
|
||||
const STATUS_ACTIVE = 'active';
|
||||
|
||||
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->addValidations('type', array(
|
||||
'required' => __('Please specify a type.')
|
||||
'required' => __('Please specify a type')
|
||||
));
|
||||
}
|
||||
|
||||
@ -56,7 +56,6 @@ class Newsletter extends Model {
|
||||
}
|
||||
|
||||
function duplicate($data = array()) {
|
||||
// get current newsletter's data as an array
|
||||
$newsletter_data = $this->asArray();
|
||||
|
||||
// remove id so that it creates a new record
|
||||
@ -109,6 +108,43 @@ class Newsletter extends Model {
|
||||
return $duplicate;
|
||||
}
|
||||
|
||||
function createNotificationHistory() {
|
||||
$newsletter_data = $this->asArray();
|
||||
|
||||
// remove id so that it creates a new record
|
||||
unset($newsletter_data['id']);
|
||||
|
||||
$data = array_merge(
|
||||
$newsletter_data,
|
||||
array(
|
||||
'parent_id' => $this->id,
|
||||
'type' => self::TYPE_NOTIFICATION_HISTORY,
|
||||
'status' => self::STATUS_SENDING
|
||||
)
|
||||
);
|
||||
|
||||
$notification_history = self::create();
|
||||
$notification_history->hydrate($data);
|
||||
|
||||
$notification_history->save();
|
||||
|
||||
if($notification_history->getErrors() === false) {
|
||||
// create relationships between notification history and segments
|
||||
$segments = $this->segments()->findArray();
|
||||
|
||||
if(!empty($segments)) {
|
||||
foreach($segments as $segment) {
|
||||
$relation = NewsletterSegment::create();
|
||||
$relation->segment_id = $segment['id'];
|
||||
$relation->newsletter_id = $notification_history->id;
|
||||
$relation->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $notification_history;
|
||||
}
|
||||
|
||||
function asArray() {
|
||||
$model = parent::asArray();
|
||||
|
||||
@ -125,6 +161,14 @@ class Newsletter extends Model {
|
||||
return parent::delete();
|
||||
}
|
||||
|
||||
function children() {
|
||||
return $this->has_many(
|
||||
__NAMESPACE__.'\Newsletter',
|
||||
'parent_id',
|
||||
'id'
|
||||
);
|
||||
}
|
||||
|
||||
function segments() {
|
||||
return $this->has_many_through(
|
||||
__NAMESPACE__.'\Segment',
|
||||
@ -139,6 +183,11 @@ class Newsletter extends Model {
|
||||
return $this;
|
||||
}
|
||||
|
||||
function withChildrenCount() {
|
||||
$this->children_count = $this->children()->count();
|
||||
return $this;
|
||||
}
|
||||
|
||||
function options() {
|
||||
return $this->has_many_through(
|
||||
__NAMESPACE__.'\NewsletterOptionField',
|
||||
@ -189,15 +238,11 @@ class Newsletter extends Model {
|
||||
} else {
|
||||
$this->statistics = $statistics->asArray();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
function getStatistics() {
|
||||
if($this->queue === false) {
|
||||
return false;
|
||||
}
|
||||
return SendingQueue::tableAlias('queues')
|
||||
$statistics_query = SendingQueue::tableAlias('queues')
|
||||
->selectExpr(
|
||||
'COUNT(DISTINCT(clicks.subscriber_id)) as clicked, ' .
|
||||
'COUNT(DISTINCT(opens.subscriber_id)) as opened, ' .
|
||||
@ -217,9 +262,22 @@ class Newsletter extends Model {
|
||||
MP_STATISTICS_UNSUBSCRIBES_TABLE,
|
||||
'queues.id = unsubscribes.queue_id',
|
||||
'unsubscribes'
|
||||
)
|
||||
->where('queues.id', $this->queue['id'])
|
||||
->findOne();
|
||||
);
|
||||
|
||||
if($this->type === self::TYPE_WELCOME) {
|
||||
return $statistics_query
|
||||
->where('queues.newsletter_id', $this->id)
|
||||
->where('queues.status', SendingQueue::STATUS_COMPLETED)
|
||||
->findOne();
|
||||
} else {
|
||||
if($this->queue === false) {
|
||||
return false;
|
||||
} else {
|
||||
return $statistics_query
|
||||
->where('queues.id', $this->queue['id'])
|
||||
->findOne();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static function search($orm, $search = '') {
|
||||
@ -230,6 +288,15 @@ class Newsletter extends Model {
|
||||
}
|
||||
|
||||
static function filters($data = array()) {
|
||||
$type = isset($data['params']['type']) ? $data['params']['type'] : null;
|
||||
|
||||
// newsletter types without filters
|
||||
if(in_array($type, array(
|
||||
self::TYPE_NOTIFICATION_HISTORY
|
||||
))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$segments = Segment::orderByAsc('name')->findMany();
|
||||
$segment_list = array();
|
||||
$segment_list[] = array(
|
||||
@ -239,7 +306,7 @@ class Newsletter extends Model {
|
||||
|
||||
foreach($segments as $segment) {
|
||||
$newsletters = $segment->newsletters()
|
||||
->filter('filterType', $data['tab'])
|
||||
->filter('filterType', $type)
|
||||
->filter('groupBy', $data);
|
||||
|
||||
$newsletters_count = $newsletters->count();
|
||||
@ -260,18 +327,32 @@ class Newsletter extends Model {
|
||||
}
|
||||
|
||||
static function filterBy($orm, $data = array()) {
|
||||
$type = isset($data['tab']) ? $data['tab'] : null;
|
||||
|
||||
// apply filters
|
||||
if(!empty($data['filter'])) {
|
||||
foreach($data['filter'] as $key => $value) {
|
||||
if($key === 'segment') {
|
||||
$segment = Segment::findOne($value);
|
||||
if($segment !== false) {
|
||||
$orm = $segment->newsletters()->filter('filterType', $type);
|
||||
$orm = $segment->newsletters();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// filter by type
|
||||
$type = isset($data['params']['type']) ? $data['params']['type'] : null;
|
||||
if($type !== null) {
|
||||
$orm->filter('filterType', $type);
|
||||
}
|
||||
|
||||
// filter by parent id
|
||||
$parent_id = isset($data['params']['parent_id'])
|
||||
? (int)$data['params']['parent_id']
|
||||
: null;
|
||||
if($parent_id !== null) {
|
||||
$orm->where('parent_id', $parent_id);
|
||||
}
|
||||
|
||||
return $orm;
|
||||
}
|
||||
|
||||
@ -306,7 +387,14 @@ class Newsletter extends Model {
|
||||
}
|
||||
|
||||
static function groups($data = array()) {
|
||||
$type = isset($data['tab']) ? $data['tab'] : null;
|
||||
$type = isset($data['params']['type']) ? $data['params']['type'] : null;
|
||||
|
||||
// newsletter types without groups
|
||||
if(in_array($type, array(
|
||||
self::TYPE_NOTIFICATION_HISTORY
|
||||
))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$groups = array(
|
||||
array(
|
||||
@ -431,7 +519,8 @@ class Newsletter extends Model {
|
||||
if(in_array($type, array(
|
||||
self::TYPE_STANDARD,
|
||||
self::TYPE_WELCOME,
|
||||
self::TYPE_NOTIFICATION
|
||||
self::TYPE_NOTIFICATION,
|
||||
self::TYPE_NOTIFICATION_HISTORY
|
||||
))) {
|
||||
$orm->where('type', $type);
|
||||
}
|
||||
@ -447,7 +536,6 @@ class Newsletter extends Model {
|
||||
'updated_at',
|
||||
'deleted_at'
|
||||
))
|
||||
->filter('filterType', $data['tab'])
|
||||
->filter('filterBy', $data)
|
||||
->filter('groupBy', $data)
|
||||
->filter('search', $data['search']);
|
||||
@ -523,4 +611,4 @@ class Newsletter extends Model {
|
||||
->whereIn('options.value', $segments)
|
||||
->findMany();
|
||||
}
|
||||
}
|
||||
}
|
@ -9,10 +9,10 @@ class NewsletterOptionField extends Model {
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
$this->addValidations('name', array(
|
||||
'required' => __('Please specify a name.')
|
||||
'required' => __('Please specify a name')
|
||||
));
|
||||
$this->addValidations('newsletter_type', array(
|
||||
'required' => __('Please specify a newsletter type.')
|
||||
'required' => __('Please specify a newsletter type')
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -10,10 +10,10 @@ class NewsletterTemplate extends Model {
|
||||
parent::__construct();
|
||||
|
||||
$this->addValidations('name', array(
|
||||
'required' => __('Please specify a name.')
|
||||
'required' => __('Please specify a name')
|
||||
));
|
||||
$this->addValidations('body', array(
|
||||
'required' => __('The template body cannot be empty.')
|
||||
'required' => __('The template body cannot be empty')
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ class Segment extends Model {
|
||||
parent::__construct();
|
||||
|
||||
$this->addValidations('name', array(
|
||||
'required' => __('Please specify a name.')
|
||||
'required' => __('Please specify a name')
|
||||
));
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ class Segment extends Model {
|
||||
$wp_segment->hydrate(array(
|
||||
'name' => __('WordPress Users'),
|
||||
'description' =>
|
||||
__('This lists containts all of your WordPress users.'),
|
||||
__('This lists containts all of your WordPress users'),
|
||||
'type' => 'wp_users'
|
||||
));
|
||||
$wp_segment->save();
|
||||
|
@ -17,7 +17,7 @@ class Setting extends Model {
|
||||
parent::__construct();
|
||||
|
||||
$this->addValidations('name', array(
|
||||
'required' => __('Please specify a name.')
|
||||
'required' => __('Please specify a name')
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,10 @@ if(!defined('ABSPATH')) exit;
|
||||
class StatisticsForms extends Model {
|
||||
public static $_table = MP_STATISTICS_FORMS_TABLE;
|
||||
|
||||
public static function getTotalSignups($form_id = false) {
|
||||
return self::where('form_id', $form_id)->count();
|
||||
}
|
||||
|
||||
public static function record($form_id, $subscriber_id) {
|
||||
if($form_id > 0 && $subscriber_id > 0) {
|
||||
// check if we already have a record for today
|
||||
|
@ -18,8 +18,8 @@ class Subscriber extends Model {
|
||||
parent::__construct();
|
||||
|
||||
$this->addValidations('email', array(
|
||||
'required' => __('Please enter your email address.'),
|
||||
'isEmail' => __('Your email address is invalid.')
|
||||
'required' => __('Please enter your email address'),
|
||||
'isEmail' => __('Your email address is invalid!')
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Newsletter\Renderer\StylesHelper;
|
||||
|
||||
class Renderer {
|
||||
@ -10,7 +11,10 @@ class Renderer {
|
||||
function __construct(array $newsletter, $posts = false) {
|
||||
$this->newsletter = $newsletter;
|
||||
$this->posts = array();
|
||||
$this->ALC = new \MailPoet\Newsletter\AutomatedLatestContent($this->newsletter['id']);
|
||||
$newsletter_id = ($newsletter['type'] === Newsletter::TYPE_NOTIFICATION_HISTORY) ?
|
||||
$newsletter['parent_id'] :
|
||||
$newsletter['id'];
|
||||
$this->ALC = new \MailPoet\Newsletter\AutomatedLatestContent($newsletter_id);
|
||||
}
|
||||
|
||||
function render($data, $column_count) {
|
||||
@ -45,12 +49,12 @@ class Renderer {
|
||||
|
||||
function processAutomatedLatestContent($args, $column_count) {
|
||||
$posts_to_exclude = $this->getPosts();
|
||||
$ALCPosts = $this->ALC->getPosts($args, $posts_to_exclude);
|
||||
foreach($ALCPosts as $post) {
|
||||
$ALC_posts = $this->ALC->getPosts($args, $posts_to_exclude);
|
||||
foreach($ALC_posts as $post) {
|
||||
$posts_to_exclude[] = $post->ID;
|
||||
}
|
||||
$transformed_posts = array(
|
||||
'blocks' => $this->ALC->transformPosts($args, $ALCPosts)
|
||||
'blocks' => $this->ALC->transformPosts($args, $ALC_posts)
|
||||
);
|
||||
$this->setPosts($posts_to_exclude);
|
||||
$transformed_posts = StylesHelper::applyTextAlignment($transformed_posts);
|
||||
|
@ -23,8 +23,19 @@ class Renderer {
|
||||
|
||||
function render() {
|
||||
$newsletter = $this->newsletter;
|
||||
$rendered_body = $this->renderBody($newsletter['body']['content']);
|
||||
$rendered_styles = $this->renderStyles($newsletter['body']['globalStyles']);
|
||||
$body = (is_array($newsletter['body']))
|
||||
? $newsletter['body']
|
||||
: array();
|
||||
$content = (array_key_exists('content', $body))
|
||||
? $body['content']
|
||||
: array();
|
||||
$styles = (array_key_exists('globalStyles', $body))
|
||||
? $body['globalStyles']
|
||||
: array();
|
||||
|
||||
$rendered_body = $this->renderBody($content);
|
||||
$rendered_styles = $this->renderStyles($styles);
|
||||
|
||||
$template = $this->injectContentIntoTemplate($this->template, array(
|
||||
$newsletter['subject'],
|
||||
$rendered_styles,
|
||||
@ -33,6 +44,7 @@ class Renderer {
|
||||
));
|
||||
$template = $this->inlineCSSStyles($template);
|
||||
$template = $this->postProcessTemplate($template);
|
||||
|
||||
return array(
|
||||
'html' => $template,
|
||||
'text' => $this->renderTextVersion($template)
|
||||
@ -40,6 +52,10 @@ class Renderer {
|
||||
}
|
||||
|
||||
function renderBody($content) {
|
||||
$blocks = (array_key_exists('blocks', $content))
|
||||
? $content['blocks']
|
||||
: array();
|
||||
|
||||
$rendered_content = array_map(function($content_block) {
|
||||
$column_count = count($content_block['blocks']);
|
||||
$column_data = $this->blocks_renderer->render(
|
||||
@ -51,7 +67,7 @@ class Renderer {
|
||||
$column_count,
|
||||
$column_data
|
||||
);
|
||||
}, $content['blocks']);
|
||||
}, $blocks);
|
||||
return implode('', $rendered_content);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\Router;
|
||||
use \MailPoet\Models\Form;
|
||||
use \MailPoet\Models\StatisticsForms;
|
||||
use \MailPoet\Form\Renderer as FormRenderer;
|
||||
use \MailPoet\Listing;
|
||||
use \MailPoet\Form\Util;
|
||||
@ -30,6 +31,9 @@ class Forms {
|
||||
// fetch segments relations for each returned item
|
||||
foreach($listing_data['items'] as $key => $form) {
|
||||
$form = $form->asArray();
|
||||
|
||||
$form['signups'] = StatisticsForms::getTotalSignups($form['id']);
|
||||
|
||||
$form['segments'] = (
|
||||
!empty($form['settings']['segments'])
|
||||
? $form['settings']['segments']
|
||||
|
@ -165,7 +165,7 @@ class Newsletters {
|
||||
if(!isset($data['body'])) {
|
||||
return array(
|
||||
'result' => false,
|
||||
'errors' => array(__('Newsletter data is missing.'))
|
||||
'errors' => array(__('Newsletter data is missing'))
|
||||
);
|
||||
}
|
||||
$newsletter_id = (isset($data['id'])) ? (int)$data['id'] : null;
|
||||
@ -173,7 +173,7 @@ class Newsletters {
|
||||
if(!$newsletter) {
|
||||
return array(
|
||||
'result' => false,
|
||||
'errors' => array(__('Newsletter could not be read.'))
|
||||
'errors' => array(__('Newsletter could not be read'))
|
||||
);
|
||||
}
|
||||
$newsletter->body = $data['body'];
|
||||
@ -245,6 +245,7 @@ class Newsletters {
|
||||
}
|
||||
|
||||
function listing($data = array()) {
|
||||
|
||||
$listing = new Listing\Handler(
|
||||
'\MailPoet\Models\Newsletter',
|
||||
$data
|
||||
@ -267,6 +268,11 @@ class Newsletters {
|
||||
$newsletter
|
||||
->withOptions()
|
||||
->withSegments()
|
||||
->withChildrenCount();
|
||||
} else if($newsletter->type === Newsletter::TYPE_NOTIFICATION_HISTORY) {
|
||||
$newsletter
|
||||
->withSegments()
|
||||
->withSendingQueue()
|
||||
->withStatistics();
|
||||
}
|
||||
|
||||
|
@ -33,12 +33,14 @@ class SendingQueue {
|
||||
if($newsletter === false) {
|
||||
return array(
|
||||
'result' => false,
|
||||
'errors' => array(__('This newsletter does not exist.'))
|
||||
'errors' => array(__('This newsletter does not exist'))
|
||||
);
|
||||
}
|
||||
|
||||
if($newsletter->type === Newsletter::TYPE_WELCOME) {
|
||||
// set welcome email active
|
||||
if($newsletter->type === Newsletter::TYPE_WELCOME ||
|
||||
$newsletter->type === Newsletter::TYPE_NOTIFICATION
|
||||
) {
|
||||
// set newsletter status to active
|
||||
$result = $newsletter->setStatus(Newsletter::STATUS_ACTIVE);
|
||||
$errors = $result->getErrors();
|
||||
|
||||
@ -48,20 +50,16 @@ class SendingQueue {
|
||||
'errors' => $errors
|
||||
);
|
||||
} else {
|
||||
$message = ($newsletter->type === Newsletter::TYPE_WELCOME) ?
|
||||
__('Your welcome email has been activated') :
|
||||
__('Your post notification has been activated');
|
||||
return array(
|
||||
'result' => true,
|
||||
'data' => array(
|
||||
'message' => __('Your welcome email has been activated.')
|
||||
'message' => $message
|
||||
)
|
||||
);
|
||||
}
|
||||
} else if($newsletter->type === Newsletter::TYPE_NOTIFICATION) {
|
||||
// Post Notifications
|
||||
$newsletter = Scheduler::processPostNotificationSchedule($newsletter->id);
|
||||
Scheduler::createPostNotificationQueue($newsletter);
|
||||
|
||||
// set post notification active
|
||||
$newsletter->setStatus(Newsletter::STATUS_ACTIVE);
|
||||
}
|
||||
|
||||
$queue = SendingQueueModel::whereNull('status')
|
||||
@ -71,7 +69,7 @@ class SendingQueue {
|
||||
if(!empty($queue)) {
|
||||
return array(
|
||||
'result' => false,
|
||||
'errors' => array(__('This newsletter is already being sent.'))
|
||||
'errors' => array(__('This newsletter is already being sent'))
|
||||
);
|
||||
}
|
||||
|
||||
@ -83,18 +81,6 @@ class SendingQueue {
|
||||
$queue->newsletter_id = $newsletter->id;
|
||||
}
|
||||
|
||||
if($newsletter->type === Newsletter::TYPE_NOTIFICATION) {
|
||||
$queue->scheduled_at = Scheduler::getNextRunDate($newsletter->schedule);
|
||||
$queue->status = SendingQueueModel::STATUS_SCHEDULED;
|
||||
$queue->save();
|
||||
return array(
|
||||
'result' => true,
|
||||
'data' => array(
|
||||
'message' => __('Your post notification has been activated.')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if((bool)$newsletter->isScheduled) {
|
||||
// set newsletter status
|
||||
$newsletter->setStatus(Newsletter::STATUS_SCHEDULED);
|
||||
@ -107,7 +93,7 @@ class SendingQueue {
|
||||
$queue->subscribers = null;
|
||||
$queue->count_total = $queue->count_to_process = 0;
|
||||
|
||||
$message = __('The newsletter has been scheduled.');
|
||||
$message = __('The newsletter has been scheduled');
|
||||
} else {
|
||||
$segments = $newsletter->segments()->findArray();
|
||||
$segment_ids = array_map(function($segment) {
|
||||
@ -120,7 +106,7 @@ class SendingQueue {
|
||||
if(!count($subscribers)) {
|
||||
return array(
|
||||
'result' => false,
|
||||
'errors' => array(__('There are no subscribers.'))
|
||||
'errors' => array(__('There are no subscribers'))
|
||||
);
|
||||
}
|
||||
$queue->status = null;
|
||||
@ -188,4 +174,4 @@ class SendingQueue {
|
||||
'result' => $result
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -69,7 +69,7 @@ class Subscribers {
|
||||
$form = Form::findOne($data['form_id']);
|
||||
unset($data['form_id']);
|
||||
if($form === false || !$form->id()) {
|
||||
$errors[] = __('This form does not exist.');
|
||||
$errors[] = __('This form does not exist');
|
||||
}
|
||||
|
||||
$segment_ids = (!empty($data['segments'])
|
||||
|
@ -8,9 +8,9 @@ use MailPoet\Newsletter\Scheduler\Scheduler;
|
||||
class WP {
|
||||
static function synchronizeUser($wp_user_id, $old_wp_user_data = false) {
|
||||
$wp_user = \get_userdata($wp_user_id);
|
||||
$wp_users_segment = Segment::getWPSegment();
|
||||
$wp_segment = Segment::getWPSegment();
|
||||
|
||||
if($wp_user === false or $wp_users_segment === false) return;
|
||||
if($wp_user === false or $wp_segment === false) return;
|
||||
|
||||
$subscriber = Subscriber::where('wp_user_id', $wp_user->ID)
|
||||
->findOne();
|
||||
@ -20,8 +20,14 @@ class WP {
|
||||
case 'delete_user':
|
||||
case 'deleted_user':
|
||||
case 'remove_user_from_blog':
|
||||
if($subscriber !== false && $subscriber->id()) {
|
||||
$subscriber->delete();
|
||||
if($subscriber !== false) {
|
||||
// unlink subscriber to wp user
|
||||
$subscriber->setExpr('wp_user_id', 'NULL')->save();
|
||||
|
||||
// delete subscription to wp segment
|
||||
SubscriberSegment::where('subscriber_id', $subscriber->id)
|
||||
->where('segment_id', $wp_segment->id)
|
||||
->deleteMany();
|
||||
}
|
||||
break;
|
||||
case 'profile_update':
|
||||
@ -55,7 +61,7 @@ class WP {
|
||||
// add subscriber to the WP Users segment
|
||||
SubscriberSegment::subscribeToSegments(
|
||||
$subscriber,
|
||||
array($wp_users_segment->id)
|
||||
array($wp_segment->id)
|
||||
);
|
||||
|
||||
// welcome email
|
||||
@ -73,7 +79,7 @@ class WP {
|
||||
|
||||
static function synchronizeUsers() {
|
||||
// get wordpress users list
|
||||
$wp_users_segment = Segment::getWPSegment();
|
||||
$wp_segment = Segment::getWPSegment();
|
||||
|
||||
// fetch all wp users id
|
||||
$wp_users = \get_users(array(
|
||||
|
@ -46,10 +46,10 @@ class Export {
|
||||
function process() {
|
||||
try {
|
||||
if(is_writable($this->export_path) === false) {
|
||||
throw new \Exception(__("Couldn't save export file on the server."));
|
||||
throw new \Exception(__("Couldn't save export file on the server"));
|
||||
}
|
||||
if(!extension_loaded('zip')) {
|
||||
throw new \Exception(__('Export requires a ZIP extension to be installed on the host.'));
|
||||
throw new \Exception(__('Export requires a ZIP extension to be installed on the host'));
|
||||
}
|
||||
$processed_subscribers = call_user_func(
|
||||
array(
|
||||
|
@ -135,13 +135,13 @@ class MailChimp {
|
||||
$errorMessage = __('Invalid API Key.');
|
||||
break;
|
||||
case 'connection':
|
||||
$errorMessage = __('Could not connect to your MailChimp account.');
|
||||
$errorMessage = __('Could not connect to your MailChimp account');
|
||||
break;
|
||||
case 'headers':
|
||||
$errorMessage = __('The selected lists do not have matching columns (headers).');
|
||||
$errorMessage = __('The selected lists do not have matching columns (headers)');
|
||||
break;
|
||||
case 'size':
|
||||
$errorMessage = __('The information received from MailChimp is too large for processing. Please limit the number of lists.');
|
||||
$errorMessage = __('The information received from MailChimp is too large for processing. Please limit the number of lists!');
|
||||
break;
|
||||
case 'subscribers':
|
||||
$errorMessage = __('Did not find any active subscribers.');
|
||||
|
@ -20,7 +20,7 @@ class Comment {
|
||||
static function getSubscriptionField() {
|
||||
$label = Setting::getValue(
|
||||
'subscribe.on_comment.label',
|
||||
__('Yes, please add me to your mailing list.')
|
||||
__('Yes, please add me to your mailing list')
|
||||
);
|
||||
|
||||
return '<p class="comment-form-mailpoet">
|
||||
|
@ -20,7 +20,7 @@ class Pages {
|
||||
private $data;
|
||||
private $subscriber;
|
||||
|
||||
function __construct($action, $data) {
|
||||
function __construct($action, $data = array()) {
|
||||
$this->action = $action;
|
||||
$this->data = $data;
|
||||
$this->subscriber = $this->getSubscriber();
|
||||
@ -59,10 +59,8 @@ class Pages {
|
||||
|
||||
function confirm() {
|
||||
if($this->subscriber !== false) {
|
||||
if($this->subscriber->status !== Subscriber::STATUS_SUBSCRIBED) {
|
||||
$this->subscriber->status = Subscriber::STATUS_SUBSCRIBED;
|
||||
$this->subscriber->save();
|
||||
}
|
||||
$this->subscriber->status = Subscriber::STATUS_SUBSCRIBED;
|
||||
$this->subscriber->save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -78,14 +76,19 @@ class Pages {
|
||||
function setPageTitle($page_title = '') {
|
||||
global $post;
|
||||
|
||||
if($post->post_title !== __('MailPoet Page')) return $page_title;
|
||||
if($this->isPreview() === false && $this->subscriber === false) {
|
||||
return __("Hmmm... we don't have a record of you");
|
||||
}
|
||||
|
||||
if(
|
||||
($this->isMailPoetPage($post->ID) === false)
|
||||
($post->post_title !== __('MailPoet Page'))
|
||||
||
|
||||
($page_title !== single_post_title('', false))
|
||||
) {
|
||||
// when it's a custom page, just return the original page title
|
||||
return $page_title;
|
||||
} else {
|
||||
// when it's our own page, generate page title based on requested action
|
||||
switch($this->action) {
|
||||
case 'confirm':
|
||||
return $this->getConfirmTitle();
|
||||
@ -97,36 +100,31 @@ class Pages {
|
||||
return $this->getUnsubscribeTitle();
|
||||
}
|
||||
}
|
||||
return $page_title;
|
||||
}
|
||||
|
||||
function setPageContent($page_content = '[mailpoet_page]') {
|
||||
global $post;
|
||||
|
||||
if(
|
||||
($this->isPreview() === false)
|
||||
&&
|
||||
($this->isMailPoetPage($post->ID) === false)
|
||||
) {
|
||||
return $page_content;
|
||||
}
|
||||
|
||||
$content = '';
|
||||
|
||||
switch($this->action) {
|
||||
case 'confirm':
|
||||
$content = $this->getConfirmContent();
|
||||
break;
|
||||
case 'manage':
|
||||
$content = $this->getManageContent();
|
||||
break;
|
||||
case 'unsubscribe':
|
||||
$content = $this->getUnsubscribeContent();
|
||||
break;
|
||||
// if we're not in preview mode and the subscriber does not exist
|
||||
if($this->isPreview() === false && $this->subscriber === false) {
|
||||
return __("Your email address doesn't appear in our lists anymore. Sign up again or contact us if this appears to be a mistake.");
|
||||
}
|
||||
|
||||
if(strpos($page_content, '[mailpoet_page]') !== false) {
|
||||
return str_replace('[mailpoet_page]', $content, $page_content);
|
||||
$content = '';
|
||||
|
||||
switch($this->action) {
|
||||
case 'confirm':
|
||||
$content = $this->getConfirmContent();
|
||||
break;
|
||||
case 'manage':
|
||||
$content = $this->getManageContent();
|
||||
break;
|
||||
case 'unsubscribe':
|
||||
$content = $this->getUnsubscribeContent();
|
||||
break;
|
||||
}
|
||||
return str_replace('[mailpoet_page]', trim($content), $page_content);
|
||||
} else {
|
||||
return $page_content;
|
||||
}
|
||||
@ -150,32 +148,22 @@ class Pages {
|
||||
return $meta;
|
||||
}
|
||||
|
||||
function isMailPoetPage($page_id = null) {
|
||||
$mailpoet_page_ids = array_unique(array_values(
|
||||
Setting::getValue('subscription.pages', array())
|
||||
));
|
||||
|
||||
return (in_array($page_id, $mailpoet_page_ids));
|
||||
}
|
||||
|
||||
private function getConfirmTitle() {
|
||||
if($this->isPreview()) {
|
||||
$title = sprintf(
|
||||
__("You've subscribed to: %s"),
|
||||
__("You have subscribed to: %s"),
|
||||
'demo 1, demo 2'
|
||||
);
|
||||
} else if($this->subscriber === false) {
|
||||
$title = __('Your confirmation link expired, please subscribe again.');
|
||||
} else {
|
||||
$segment_names = array_map(function($segment) {
|
||||
return $segment->name;
|
||||
}, $this->subscriber->segments()->findMany());
|
||||
|
||||
if(empty($segment_names)) {
|
||||
$title = __("You're now subscribed!");
|
||||
$title = __("You are now subscribed!");
|
||||
} else {
|
||||
$title = sprintf(
|
||||
__("You've subscribed to: %s"),
|
||||
__("You have subscribed to: %s"),
|
||||
join(', ', $segment_names)
|
||||
);
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ if(!defined('ABSPATH')) exit;
|
||||
use \MailPoet\Config\Initializer;
|
||||
/*
|
||||
* Plugin Name: MailPoet
|
||||
* Version: 0.0.35
|
||||
* Version: 0.0.37
|
||||
* Plugin URI: http://www.mailpoet.com
|
||||
* Description: MailPoet Newsletters.
|
||||
* Author: MailPoet
|
||||
@ -22,7 +22,7 @@ use \MailPoet\Config\Initializer;
|
||||
|
||||
require 'vendor/autoload.php';
|
||||
|
||||
define('MAILPOET_VERSION', '0.0.35');
|
||||
define('MAILPOET_VERSION', '0.0.37');
|
||||
|
||||
$initializer = new Initializer(array(
|
||||
'file' => __FILE__,
|
||||
|
@ -73,7 +73,7 @@ class MailerTest extends MailPoetTest {
|
||||
$mailer = new Mailer();
|
||||
$this->fail('Mailer did not throw an exception');
|
||||
} catch(Exception $e) {
|
||||
expect($e->getMessage())->equals('Mailer is not configured.');
|
||||
expect($e->getMessage())->equals('Mailer is not configured');
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,7 +82,7 @@ class MailerTest extends MailPoetTest {
|
||||
$mailer = new Mailer($mailer = $this->mailer);
|
||||
$this->fail('Mailer did not throw an exception');
|
||||
} catch(Exception $e) {
|
||||
expect($e->getMessage())->equals('Sender name and email are not configured.');
|
||||
expect($e->getMessage())->equals('Sender name and email are not configured');
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ class MailerTest extends MailPoetTest {
|
||||
$mailer = new Mailer(array('method' => 'Unknown'), $this->sender);
|
||||
$this->fail('Mailer did not throw an exception');
|
||||
} catch(Exception $e) {
|
||||
expect($e->getMessage())->equals('Mailing method does not exist.');
|
||||
expect($e->getMessage())->equals('Mailing method does not exist');
|
||||
}
|
||||
}
|
||||
|
||||
@ -155,4 +155,4 @@ class MailerTest extends MailPoetTest {
|
||||
$mailer = new Mailer($this->mailer, $this->sender, $this->reply_to);
|
||||
expect($mailer->send($this->newsletter, $this->subscriber))->true();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,8 +72,8 @@ class CustomFieldTest extends MailPoetTest {
|
||||
$errors = $result->getErrors();
|
||||
|
||||
expect(is_array($errors))->true();
|
||||
expect($errors[0])->equals('Please specify a name.');
|
||||
expect($errors[1])->equals('Please specify a type.');
|
||||
expect($errors[0])->equals('Please specify a name');
|
||||
expect($errors[1])->equals('Please specify a type');
|
||||
}
|
||||
|
||||
function testItHasACreatedAtOnCreation() {
|
||||
|
@ -19,7 +19,7 @@ class FormTest extends MailPoetTest {
|
||||
$errors = $result->getErrors();
|
||||
|
||||
expect(is_array($errors))->true();
|
||||
expect($errors[0])->equals('Please specify a name.');
|
||||
expect($errors[0])->equals('Please specify a name');
|
||||
}
|
||||
|
||||
function testItCanBeGrouped() {
|
||||
|
@ -53,8 +53,8 @@ class NewsletterOptionFieldTest extends MailPoetTest {
|
||||
$errors = $result->getErrors();
|
||||
|
||||
expect(is_array($errors))->true();
|
||||
expect($errors[0])->equals('Please specify a name.');
|
||||
expect($errors[1])->equals('Please specify a newsletter type.');
|
||||
expect($errors[0])->equals('Please specify a name');
|
||||
expect($errors[1])->equals('Please specify a newsletter type');
|
||||
}
|
||||
|
||||
function testItHasACreatedAtOnCreation() {
|
||||
|
@ -26,8 +26,8 @@ class NewsletterTemplateTest extends MailPoetTest {
|
||||
$errors = $result->getErrors();
|
||||
|
||||
expect(is_array($errors))->true();
|
||||
expect($errors[0])->equals('Please specify a name.');
|
||||
expect($errors[1])->equals('The template body cannot be empty.');
|
||||
expect($errors[0])->equals('Please specify a name');
|
||||
expect($errors[1])->equals('The template body cannot be empty');
|
||||
}
|
||||
|
||||
function testItHasName() {
|
||||
|
@ -72,7 +72,7 @@ class SegmentTest extends MailPoetTest {
|
||||
$errors = $result->getErrors();
|
||||
|
||||
expect(is_array($errors))->true();
|
||||
expect($errors[0])->equals('Please specify a name.');
|
||||
expect($errors[0])->equals('Please specify a name');
|
||||
}
|
||||
|
||||
function testItHasACreatedAtOnCreation() {
|
||||
|
@ -16,7 +16,7 @@ class SettingTest extends MailPoetTest {
|
||||
$errors = $invalid_setting->getErrors();
|
||||
|
||||
expect($errors)->notEmpty();
|
||||
expect($errors[0])->equals('Please specify a name.');
|
||||
expect($errors[0])->equals('Please specify a name');
|
||||
}
|
||||
|
||||
function testItHasDefaultSettings() {
|
||||
|
@ -36,6 +36,20 @@ class StatisticsFormsTest extends MailPoetTest {
|
||||
expect($record)->false();
|
||||
}
|
||||
|
||||
function testItCanReturnTheTotalSignupsOfAForm() {
|
||||
// simulate 2 signups for form #1
|
||||
StatisticsForms::record($form_id = 1, $subscriber_id = 2);
|
||||
StatisticsForms::record($form_id = 1, $subscriber_id = 1);
|
||||
// simulate 1 signup for form #2
|
||||
StatisticsForms::record($form_id = 2, $subscriber_id = 2);
|
||||
|
||||
$form_1_signups = StatisticsForms::getTotalSignups($form_id = 1);
|
||||
expect($form_1_signups)->equals(2);
|
||||
|
||||
$form_2_signups = StatisticsForms::getTotalSignups($form_id = 2);
|
||||
expect($form_2_signups)->equals(1);
|
||||
}
|
||||
|
||||
function _after() {
|
||||
StatisticsForms::deleteMany();
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ class SubscriberTest extends MailPoetTest {
|
||||
));
|
||||
$subscriber->save();
|
||||
$errors = $subscriber->getErrors();
|
||||
expect($errors)->contains("Your email address is invalid.");
|
||||
expect($errors)->contains("Your email address is invalid!");
|
||||
|
||||
// pdo error
|
||||
$subscriber = Subscriber::create();
|
||||
@ -443,4 +443,4 @@ class SubscriberTest extends MailPoetTest {
|
||||
ORM::raw_execute('TRUNCATE ' . CustomField::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . SubscriberCustomField::$_table);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,9 @@ class NewsletterRendererTest extends MailPoetTest {
|
||||
),
|
||||
'id' => 1,
|
||||
'subject' => 'Some subject',
|
||||
'preheader' => 'Some preheader'
|
||||
'preheader' => 'Some preheader',
|
||||
'type' => 'standard',
|
||||
'status' => 'active'
|
||||
);
|
||||
$this->renderer = new Renderer($this->newsletter);
|
||||
$this->column_renderer = new ColumnRenderer();
|
||||
|
@ -95,18 +95,18 @@ class CustomFieldsTest extends MailPoetTest {
|
||||
// missing type
|
||||
$response = $router->save(array('name' => 'New custom field'));
|
||||
expect($response['result'])->false();
|
||||
expect($response['errors'][0])->equals('Please specify a type.');
|
||||
expect($response['errors'][0])->equals('Please specify a type');
|
||||
|
||||
// missing name
|
||||
$response = $router->save(array('type' => 'text'));
|
||||
expect($response['result'])->false();
|
||||
expect($response['errors'][0])->equals('Please specify a name.');
|
||||
expect($response['errors'][0])->equals('Please specify a name');
|
||||
|
||||
// missing data
|
||||
$response = $router->save();
|
||||
expect($response['result'])->false();
|
||||
expect($response['errors'][0])->equals('Please specify a name.');
|
||||
expect($response['errors'][1])->equals('Please specify a type.');
|
||||
expect($response['errors'][0])->equals('Please specify a name');
|
||||
expect($response['errors'][1])->equals('Please specify a type');
|
||||
}
|
||||
|
||||
function testItCanGetACustomField() {
|
||||
|
@ -59,7 +59,7 @@ class FormsTest extends MailPoetTest {
|
||||
$router = new Forms();
|
||||
$response = $router->save(/* missing data */);
|
||||
expect($response['result'])->false();
|
||||
expect($response['errors'][0])->equals('Please specify a name.');
|
||||
expect($response['errors'][0])->equals('Please specify a name');
|
||||
|
||||
$response = $router->save($form_data);
|
||||
expect($response['result'])->true();
|
||||
|
@ -48,7 +48,7 @@ class NewslettersTest extends MailPoetTest {
|
||||
|
||||
$response = $router->save($invalid_data);
|
||||
expect($response['result'])->false();
|
||||
expect($response['errors'][0])->equals('Please specify a type.');
|
||||
expect($response['errors'][0])->equals('Please specify a type');
|
||||
}
|
||||
|
||||
function testItCanSaveAnExistingNewsletter() {
|
||||
@ -123,7 +123,7 @@ class NewslettersTest extends MailPoetTest {
|
||||
|
||||
$response = $router->create();
|
||||
expect($response['result'])->false();
|
||||
expect($response['errors'][0])->equals('Please specify a type.');
|
||||
expect($response['errors'][0])->equals('Please specify a type');
|
||||
}
|
||||
|
||||
function testItCanGetListingData() {
|
||||
|
@ -44,7 +44,7 @@ class SegmentsTest extends MailPoetTest {
|
||||
$router = new Segments();
|
||||
$response = $router->save(/* missing data */);
|
||||
expect($response['result'])->false();
|
||||
expect($response['errors'][0])->equals('Please specify a name.');
|
||||
expect($response['errors'][0])->equals('Please specify a name');
|
||||
|
||||
$response = $router->save($segment_data);
|
||||
expect($response['result'])->true();
|
||||
|
@ -67,7 +67,7 @@ class SubscribersTest extends MailPoetTest {
|
||||
|
||||
$response = $router->save(/* missing data */);
|
||||
expect($response['result'])->false();
|
||||
expect($response['errors'][0])->equals('Please enter your email address.');
|
||||
expect($response['errors'][0])->equals('Please enter your email address');
|
||||
|
||||
$invalid_data = array(
|
||||
'email' => 'john.doe@invalid'
|
||||
@ -75,7 +75,7 @@ class SubscribersTest extends MailPoetTest {
|
||||
|
||||
$response = $router->save($invalid_data);
|
||||
expect($response['result'])->false();
|
||||
expect($response['errors'][0])->equals('Your email address is invalid.');
|
||||
expect($response['errors'][0])->equals('Your email address is invalid!');
|
||||
}
|
||||
|
||||
function testItCanSaveAnExistingSubscriber() {
|
||||
|
@ -225,7 +225,7 @@ class ExportTest extends MailPoetTest {
|
||||
$this->export->export_path = '/fake_folder';
|
||||
$result = $this->export->process();
|
||||
expect($result['errors'][0])
|
||||
->equals("Couldn't save export file on the server.");
|
||||
->equals("Couldn't save export file on the server");
|
||||
}
|
||||
|
||||
function testItCanProcess() {
|
||||
|
@ -8,7 +8,7 @@
|
||||
<h2>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="<%= __('Click here to change the name!') %>"
|
||||
placeholder="<%= __('Click here to change the name') %>"
|
||||
id="mailpoet_form_name"
|
||||
value="<%= form.name %>"
|
||||
/>
|
||||
@ -52,7 +52,7 @@
|
||||
<select
|
||||
id="mailpoet_form_segments"
|
||||
name="segments"
|
||||
data-placeholder="<%= __('Please choose a list') %>"
|
||||
data-placeholder="<%= __('Please select a list') %>"
|
||||
multiple
|
||||
data-parsley-required-message="<%= __('Please select a list.') %>"
|
||||
required
|
||||
@ -598,7 +598,7 @@
|
||||
var name = $(this).siblings('.mailpoet_form_field').attr('wysija_name');
|
||||
|
||||
if(window.confirm(
|
||||
"<%= __('Delete this field for all your subscribers?') %>"
|
||||
"<%= __('This field will be deleted for all your subscribers. Are you sure?') %>"
|
||||
)) {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'customFields',
|
||||
|
@ -1,5 +1,5 @@
|
||||
{{#if params.label}}<p>{{ params.label }}</p>{{/if}}
|
||||
{{#unless params.values}}<p class="mailpoet_error"><%= __('Please select at least 1 list') %></p>{{/unless}}
|
||||
{{#unless params.values}}<p class="mailpoet_error"><%= __('Please select at least one list') %></p>{{/unless}}
|
||||
{{#each params.values}}
|
||||
<p>
|
||||
<input class="mailpoet_checkbox" type="checkbox" {{#if is_checked}}checked="checked"{{/if}} disabled="disabled" />{{ name }}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<ul id="mailpoet_segment_selection" class="clearfix"></ul>
|
||||
|
||||
<div id="mailpoet_segment_available_container">
|
||||
<h3><%= __('Select the segment you want to add:') %></h3>
|
||||
<h3><%= __('Select the segment that you want to add:') %></h3>
|
||||
|
||||
<select class="mailpoet_segment_available"></select>
|
||||
|
||||
|
@ -16,12 +16,12 @@
|
||||
'pageTitle': __('Forms'),
|
||||
'searchLabel': __('Search'),
|
||||
'loadingItems': __('Loading forms...'),
|
||||
'noItemsFound': __('No forms found.'),
|
||||
'selectAllLabel': __('All forms on this page are selected.'),
|
||||
'selectedAllLabel': __('All %d forms are selected.'),
|
||||
'selectAllLink': __('Select all forms on all pages.'),
|
||||
'clearSelection': __('Clear selection.'),
|
||||
'permanentlyDeleted': __('%d forms permanently deleted.'),
|
||||
'noItemsFound': __('No forms found'),
|
||||
'selectAllLabel': __('All forms on this page are selected'),
|
||||
'selectedAllLabel': __('All %d forms are selected'),
|
||||
'selectAllLink': __('Select all forms on all pages'),
|
||||
'clearSelection': __('Clear selection'),
|
||||
'permanentlyDeleted': __('%d forms permanently deleted'),
|
||||
'selectBulkAction': __('Select bulk action'),
|
||||
'bulkActions': __('Bulk Actions'),
|
||||
'apply': __('Apply'),
|
||||
@ -41,16 +41,17 @@
|
||||
|
||||
'formName': __('Name'),
|
||||
'segments': __('Lists'),
|
||||
'signups': __('Signups'),
|
||||
'createdOn': __('Created on'),
|
||||
'oneFormTrashed': __('1 form was moved to the trash.'),
|
||||
'multipleFormsTrashed': __('%$1d forms were moved to the trash.'),
|
||||
'oneFormDeleted': __('1 form was permanently deleted.'),
|
||||
'multipleFormsDeleted': __('%$1d forms were permanently deleted.'),
|
||||
'oneFormRestored': __('1 form has been restored from the trash.'),
|
||||
'multipleFormsRestored': __('%$1d forms have been restored from the trash.'),
|
||||
'oneFormTrashed': __('1 form was moved to the trash'),
|
||||
'multipleFormsTrashed': __('%$1d forms were moved to the trash'),
|
||||
'oneFormDeleted': __('1 form was permanently deleted'),
|
||||
'multipleFormsDeleted': __('%$1d forms were permanently deleted'),
|
||||
'oneFormRestored': __('1 form has been restored from the trash'),
|
||||
'multipleFormsRestored': __('%$1d forms have been restored from the trash'),
|
||||
'edit': __('Edit'),
|
||||
'duplicate': __('Duplicate'),
|
||||
'formDuplicated': __('Form "%$1s" has been duplicated.'),
|
||||
'formDuplicated': __('Form "%$1s" has been duplicated'),
|
||||
'trash': __('Trash'),
|
||||
'new': __('New'),
|
||||
}) %>
|
||||
|
@ -318,15 +318,15 @@
|
||||
'failedToFetchAvailablePosts': __('Failed to fetch available posts'),
|
||||
'failedToFetchRenderedPosts': __('Failed to fetch rendered posts'),
|
||||
'shortcodesWindowTitle': __('Select a shortcode'),
|
||||
'unsubscribeLinkMissing': __('All newsletters must include an "unsubscribe" link. Add a footer widget to your newsletter to continue.'),
|
||||
'newsletterPreviewEmailMissing': __('Enter an email address to send the preview newsletter to.'),
|
||||
'newsletterPreviewFailed': __('Preview failed. Pleae check console log.'),
|
||||
'unsubscribeLinkMissing': __('All newsletters must include an "unsubscribe" link. Add a footer widget to your newsletter to continue'),
|
||||
'newsletterPreviewEmailMissing': __('Enter an email address to send the preview newsletter to'),
|
||||
'newsletterPreviewFailed': __('Preview failed. Pleae check console log'),
|
||||
'newsletterPreviewSent': __('Newsletter preview email has been successfully sent!'),
|
||||
'newsletterPreviewFailedToSend': __('Attempt to send a newsletter preview email failed. Please verify that your sending method is configured correctly and try again.'),
|
||||
'newsletterPreviewFailedToSend': __('Attempt to send a newsletter preview email failed. Please verify that your sending method is configured correctly and try again'),
|
||||
'templateNameMissing': __('Please add a template name'),
|
||||
'templateDescriptionMissing': __('Please add a template description'),
|
||||
'templateSaved': __('Template has been saved.'),
|
||||
'templateSaveFailed': __('Template has not been saved, please try again.'),
|
||||
'templateSaveFailed': __('Template has not been saved, please try again'),
|
||||
'categoriesAndTags': __('Categories & tags'),
|
||||
'noPostsToDisplay': __('There is no content to display'),
|
||||
}) %>
|
||||
@ -1152,7 +1152,7 @@
|
||||
},
|
||||
header: {
|
||||
text: '<%= __('Display problems?') %> ' +
|
||||
'<a href="[link:newsletter_view_in_browser_url]"><%= __('Open this email in your web browser.') %></a>',
|
||||
'<a href="[link:newsletter_view_in_browser_url]"><%= __('Open this email in your web browser') %></a>',
|
||||
styles: {
|
||||
block: {
|
||||
backgroundColor: 'transparent',
|
||||
|
@ -10,6 +10,6 @@
|
||||
<input type="text"
|
||||
class="mailpoet_input mailpoet_input_preheader"
|
||||
value="{{ model.preheader }}"
|
||||
placeholder="<%= __('Write your preheader here for Outlook users...') %>"
|
||||
placeholder="<%= __('Write your preheader here (for subscribers that use Outlook)') %>"
|
||||
/>
|
||||
</div>
|
||||
|
@ -28,12 +28,12 @@
|
||||
|
||||
'searchLabel': __('Search'),
|
||||
'loadingItems': __('Loading newsletters...'),
|
||||
'noItemsFound': __('No newsletters found.'),
|
||||
'selectAllLabel': __('All newsletters on this page are selected.'),
|
||||
'selectedAllLabel': __('All %d newsletters are selected.'),
|
||||
'selectAllLink': __('Select all newsletters on all pages.'),
|
||||
'clearSelection': __('Clear selection.'),
|
||||
'permanentlyDeleted': __('%d newsletters were permanently deleted.'),
|
||||
'noItemsFound': __('No newsletters found'),
|
||||
'selectAllLabel': __('All newsletters on this page are selected'),
|
||||
'selectedAllLabel': __('All %d newsletters are selected'),
|
||||
'selectAllLink': __('Select all newsletters on all pages'),
|
||||
'clearSelection': __('Clear selection'),
|
||||
'permanentlyDeleted': __('%d newsletters were permanently deleted'),
|
||||
'selectBulkAction': __('Select bulk action'),
|
||||
'bulkActions': __('Bulk Actions'),
|
||||
'apply': __('Apply'),
|
||||
@ -66,23 +66,23 @@
|
||||
'viewHistory': __('View history'),
|
||||
'createdOn': __('Created on'),
|
||||
'lastModifiedOn': __('Last modified on'),
|
||||
'oneNewsletterTrashed': __('1 newsletter was moved to the trash.'),
|
||||
'multipleNewslettersTrashed': __('%$1d newsletters were moved to the trash.'),
|
||||
'oneNewsletterDeleted': __('1 newsletter was permanently deleted.'),
|
||||
'multipleNewslettersDeleted': __('%$1d newsletters were permanently deleted.'),
|
||||
'oneNewsletterRestored': __('1 newsletter has been recovered from the trash.'),
|
||||
'multipleNewslettersRestored': __('%$1d newsletters have been recovered from the trash.'),
|
||||
'oneNewsletterTrashed': __('1 newsletter was moved to the trash'),
|
||||
'multipleNewslettersTrashed': __('%$1d newsletters were moved to the trash'),
|
||||
'oneNewsletterDeleted': __('1 newsletter was permanently deleted'),
|
||||
'multipleNewslettersDeleted': __('%$1d newsletters were permanently deleted'),
|
||||
'oneNewsletterRestored': __('1 newsletter has been recovered from the trash'),
|
||||
'multipleNewslettersRestored': __('%$1d newsletters have been recovered from the trash'),
|
||||
'trash': __('Trash'),
|
||||
'edit': __('Edit'),
|
||||
'duplicate': __('Duplicate'),
|
||||
'newsletterDuplicated': __('Newsletter "%$1s" has been duplicated.'),
|
||||
'notSentYet': __('Not sent yet.'),
|
||||
'newsletterDuplicated': __('Newsletter "%$1s" has been duplicated'),
|
||||
'notSentYet': __('Not sent yet'),
|
||||
'scheduledFor': __('Scheduled for'),
|
||||
'scheduleIt': __('Schedule it'),
|
||||
'active': __('Active'),
|
||||
'inactive': __('Not Active'),
|
||||
'newsletterQueueCompleted': __('Sent to %$1d of %$2d.'),
|
||||
'sentToXSubscribers': __('Sent to %$1d subscribers.'),
|
||||
'newsletterQueueCompleted': __('Sent to %$1d of %$2d'),
|
||||
'sentToXSubscribers': __('Sent to %$1d subscribers'),
|
||||
'resume': __('Resume'),
|
||||
'pause': __('Pause'),
|
||||
'new': __('New'),
|
||||
@ -92,7 +92,7 @@
|
||||
'selectJsonFileToUpload': __('Select a .json file to upload'),
|
||||
'upload': __('Upload'),
|
||||
'mailpoetGuideTemplateTitle': __("MailPoet's Guide"),
|
||||
'mailpoetGuideTemplateDescription': __("This is the standard template that comes with MailPoet."),
|
||||
'mailpoetGuideTemplateDescription': __("This is the standard template that comes with MailPoet"),
|
||||
'confirmTemplateDeletion': __('You are about to delete the template named "%$1s"'),
|
||||
'delete': __('Delete'),
|
||||
'select': __('Select'),
|
||||
@ -105,14 +105,14 @@
|
||||
'regularNewsletterTypeDescription': __('Send a newsletter with images, buttons, dividers, and social bookmarks. Or, just send a basic text email.'),
|
||||
'create': __('Create'),
|
||||
'welcomeNewsletterTypeTitle': __('Welcome email'),
|
||||
'welcomeNewsletterTypeDescription': __('Send an email for new users.'),
|
||||
'welcomeNewsletterTypeDescription': __('Send an email to new users'),
|
||||
'setUp': __('Set up'),
|
||||
'postNotificationNewsletterTypeTitle': __('Post notifications'),
|
||||
'postNotificationsNewsletterTypeDescription': __('Automatically send posts immediately, daily, weekly or monthly. Filter by categories, if you like.'),
|
||||
'selectFrequency': __('Select a frequency'),
|
||||
'postNotificationSubjectLineTip': __("Insert [newsletter:total] to show number of posts, [newsletter:post_title] to show the latest post's title & [newsletter:number] to display the issue number."),
|
||||
'activate': __('Activate'),
|
||||
'sendWelcomeEmailWhen': __('Send welcome email when...'),
|
||||
'sendWelcomeEmailWhen': __('Send this welcome email when...'),
|
||||
|
||||
'daily': __('Once a day at...'),
|
||||
'weekly': __('Weekly on...'),
|
||||
@ -144,22 +144,22 @@
|
||||
|
||||
'subjectLine': __('Subject line'),
|
||||
'subjectLineTip': __("Be creative! It's the first thing that your subscribers see. Tempt them to open your email."),
|
||||
'emptySubjectLineError': __('Please specify a subject.'),
|
||||
'emptySubjectLineError': __('Please specify a subject'),
|
||||
'segments': __('Segments'),
|
||||
'segmentsTip': __('This subscriber segment will be used for this campaign.'),
|
||||
'segmentsTip': __('This subscriber segment will be used for this campaign'),
|
||||
'selectSegmentPlaceholder': __('Select a segment'),
|
||||
'noSegmentsSelectedError': __('Please select a segment.'),
|
||||
'noSegmentsSelectedError': __('Please select a segment'),
|
||||
'sender': __('Sender'),
|
||||
'senderTip': __('Your name and email.'),
|
||||
'senderNamePlaceholder': __('John Doe'),
|
||||
'senderAddressPlaceholder': __('john.doe@email.com'),
|
||||
'replyTo': __('Reply-to'),
|
||||
'replyToTip': __('When your subscribers reply to your newsletter, their emails will go to this address.'),
|
||||
'replyToTip': __('When your subscribers reply to your newsletter, their emails will go to this address'),
|
||||
'replyToNamePlaceholder': __('John Doe'),
|
||||
'replyToAddressPlaceholder': __('john.doe@email.com'),
|
||||
'newsletterUpdated': __('Newsletter was updated successfully!'),
|
||||
'newsletterAdded': __('Newsletter was added successfully!'),
|
||||
'newsletterSendingError': __('An error occurred while trying to send. <a href="%$1s">Please check your settings.</a>'),
|
||||
'newsletterSendingError': __('An error occurred while trying to send. <a href="%$1s">Please check your settings</a>'),
|
||||
'finalNewsletterStep': __('Final step: last details'),
|
||||
'saveDraftAndClose': __('Save as draft and close'),
|
||||
'orSimply': __('or simply'),
|
||||
@ -211,10 +211,10 @@
|
||||
'next': __('Next'),
|
||||
'previous': __('Previous'),
|
||||
|
||||
'welcomeEmailActivated': __('Your welcome email is now active.'),
|
||||
'welcomeEmailActivationFailed': __('Your welcome email could not be activated, check its settings.'),
|
||||
'postNotificationActivated': __('Your post notification is now active.'),
|
||||
'postNotificationActivationFailed': __('Your post notification could not be activated, check its settings.'),
|
||||
'welcomeEmailActivated': __('Your welcome email is now active!'),
|
||||
'welcomeEmailActivationFailed': __('Your welcome email could not be activated, please check the settings'),
|
||||
'postNotificationActivated': __('Your post notification is now active!'),
|
||||
'postNotificationActivationFailed': __('Your post notification could not be activated, check the settings'),
|
||||
|
||||
'welcomeEventSegment': __('This newsletter is sent when someone subscribes to the list "%$1s"'),
|
||||
'welcomeEventWPUserAnyRole': __('This newsletter is sent when a new WordPress user is added to your site'),
|
||||
@ -229,6 +229,9 @@
|
||||
'sendNthWeekDay': __('Send every %$1s %$2s of the month at %$3s'),
|
||||
'sendImmediately': __('Send immediately'),
|
||||
'ifNewContentToSegments': __("if there's new content to %$1s."),
|
||||
'sendingToSegmentsNotSpecified': __('You need to select a segment to send to.')
|
||||
'sendingToSegmentsNotSpecified': __('You need to select a segment to send to.'),
|
||||
|
||||
'backToPostNotifications': __('Back to Post notifications'),
|
||||
'sentOn': __('Sent on')
|
||||
}) %>
|
||||
<% endblock %>
|
||||
|
@ -13,12 +13,12 @@
|
||||
'pageTitle': __('Segments'),
|
||||
'searchLabel': __('Search'),
|
||||
'loadingItems': __('Loading segments...'),
|
||||
'noItemsFound': __('No segments found.'),
|
||||
'selectAllLabel': __('All segments on this page are selected.'),
|
||||
'selectedAllLabel': __('All %d segments are selected.'),
|
||||
'selectAllLink': __('Select all segments on all pages.'),
|
||||
'clearSelection': __('Clear selection.'),
|
||||
'permanentlyDeleted': __('%d segments were permanently deleted.'),
|
||||
'noItemsFound': __('No segments found'),
|
||||
'selectAllLabel': __('All segments on this page are selected'),
|
||||
'selectedAllLabel': __('All %d segments are selected'),
|
||||
'selectAllLink': __('Select all segments on all pages'),
|
||||
'clearSelection': __('Clear selection'),
|
||||
'permanentlyDeleted': __('%d segments were permanently deleted'),
|
||||
'selectBulkAction': __('Select bulk action'),
|
||||
'bulkActions': __('Bulk Actions'),
|
||||
'apply': __('Apply'),
|
||||
@ -31,18 +31,18 @@
|
||||
'unconfirmed': __('Unconfirmed'),
|
||||
'unsubscribed': __('Unsubscribed'),
|
||||
'createdOn': __('Created on'),
|
||||
'oneSegmentTrashed': __('1 segment was moved to the trash.'),
|
||||
'multipleSegmentsTrashed': __('%$1d segments were moved to the trash.'),
|
||||
'oneSegmentDeleted': __('1 segment was permanently deleted.'),
|
||||
'multipleSegmentsDeleted': __('%$1d segments were permanently deleted.'),
|
||||
'oneSegmentRestored': __('1 segment has been restored from the trash.'),
|
||||
'multipleSegmentsRestored': __('%$1d segments have been restored from the trash.'),
|
||||
'oneSegmentTrashed': __('1 segment was moved to the trash'),
|
||||
'multipleSegmentsTrashed': __('%$1d segments were moved to the trash'),
|
||||
'oneSegmentDeleted': __('1 segment was permanently deleted'),
|
||||
'multipleSegmentsDeleted': __('%$1d segments were permanently deleted'),
|
||||
'oneSegmentRestored': __('1 segment has been restored from the trash'),
|
||||
'multipleSegmentsRestored': __('%$1d segments have been restored from the trash'),
|
||||
'duplicate': __('Duplicate'),
|
||||
'listDuplicated': __('List "%$1s" has been duplicated.'),
|
||||
'listDuplicated': __('List "%$1s" has been duplicated'),
|
||||
'update': __('Update'),
|
||||
'forceSync': __('Force Sync'),
|
||||
'readMore': __('Read More'),
|
||||
'listSynchronized': __('List "%$1s" has been synchronized.'),
|
||||
'listSynchronized': __('List "%$1s" has been synchronized'),
|
||||
'viewSubscribers': __('View Subscribers'),
|
||||
'new': __('New'),
|
||||
'edit': __('Edit'),
|
||||
@ -61,7 +61,7 @@
|
||||
'pageOutOf': __('of'),
|
||||
'numberOfItems': __('%$1d items'),
|
||||
|
||||
'segmentDescriptionTip': __('This text box is for your own use and is never shown to your subscribers.'),
|
||||
'segmentDescriptionTip': __('This text box is for your own use and is never shown to your subscribers'),
|
||||
'backToList': __('Back to list')
|
||||
}) %>
|
||||
<% endblock %>
|
||||
|
@ -80,19 +80,19 @@
|
||||
}).done(function(response) {
|
||||
if(response === true) {
|
||||
MailPoet.Notice.success(
|
||||
"<%= __('Settings saved.') %>",
|
||||
"<%= __('Settings saved') %>",
|
||||
{ scroll: true }
|
||||
);
|
||||
} else {
|
||||
MailPoet.Notice.error(
|
||||
"<%= __('Settings could not be saved.') %>",
|
||||
"<%= __('Settings could not be saved') %>",
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
MailPoet.Modal.loading(false);
|
||||
}).error(function(errors) {
|
||||
MailPoet.Notice.error(
|
||||
"<%= __('An error occurred. Please check your settings.') %>",
|
||||
"<%= __('An error occurred. Please check the settings.') %>",
|
||||
{ scroll: true }
|
||||
);
|
||||
MailPoet.Modal.loading(false);
|
||||
|
@ -7,7 +7,7 @@
|
||||
<%= __('Bounce email') %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('Your bounced emails will be sent to this address.') %>
|
||||
<%= __('Your bounced emails will be sent to this address') %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
@ -21,6 +21,52 @@
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- task scheduler -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label>
|
||||
<%= __('Newsletter task scheduler') %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('Select what will activate your newsletter queue.') %>
|
||||
<a href="#TODO"
|
||||
target="_blank"
|
||||
><%= __('Read more.') %></a>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
<p>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
name="task_scheduler[method]"
|
||||
value="WordPress"
|
||||
<% if (settings.task_scheduler.method == 'WordPress') %>
|
||||
checked="checked"
|
||||
<% endif %>
|
||||
/><%= __('Visitors to your website (recommended)') %>
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
name="task_scheduler[method]"
|
||||
value="MailPoet"
|
||||
<% if (settings.task_scheduler.method == 'MailPoet') %>
|
||||
checked="checked"
|
||||
<% endif %>
|
||||
/><%= __("MailPoet's own script. Doesn't work with [link]these hosts[/link].")
|
||||
|replace({
|
||||
'[link]': '<a target="_blank" href="#TODO">',
|
||||
'[/link]': '</a>'
|
||||
})
|
||||
|raw
|
||||
%>
|
||||
</label>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- link tracking -->
|
||||
<tr>
|
||||
<th scope="row">
|
||||
@ -28,30 +74,30 @@
|
||||
<%= __('Open and click tracking') %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('Some users prefer to not track their subscribers.') %>
|
||||
<%= __('Some users prefer to not track their subscribers') %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
<p>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
name="tracking[enabled]"
|
||||
value="1"
|
||||
<% if(settings.tracking.enabled) %>
|
||||
checked="checked"
|
||||
<% endif %>
|
||||
type="radio"
|
||||
name="tracking[enabled]"
|
||||
value="1"
|
||||
<% if(settings.tracking.enabled) %>
|
||||
checked="checked"
|
||||
<% endif %>
|
||||
/><%= __('Yes') %>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
name="tracking[enabled]"
|
||||
value=""
|
||||
<% if not(settings.tracking.enabled) %>
|
||||
checked="checked"
|
||||
<% endif %>
|
||||
type="radio"
|
||||
name="tracking[enabled]"
|
||||
value=""
|
||||
<% if not(settings.tracking.enabled) %>
|
||||
checked="checked"
|
||||
<% endif %>
|
||||
/><%= __('No') %>
|
||||
</label>
|
||||
</p>
|
||||
@ -64,7 +110,7 @@
|
||||
<%= __('Share anonymous data') %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('Share anonymous data and help us improve the plugin! We appreciate your help.') %>
|
||||
<%= __('Share anonymous data and help us improve the plugin. We appreciate your help!') %>
|
||||
<a
|
||||
href="http://support.mailpoet.com/knowledgebase/share-your-data/?utm_source=wpadmin&utm_campaign=advanced_settings"
|
||||
target="_blank"
|
||||
@ -102,7 +148,7 @@
|
||||
<th scope="row">
|
||||
<label><%= __('Reinstall from scratch') %></label>
|
||||
<p class="description">
|
||||
<%= __('Want to start from the beginning? This will completely wipe out MailPoet and reinstall from scratch.') %>
|
||||
<%= __('Want to start over? This will completely wipe out MailPoet and reinstall from scratch.') %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
@ -122,7 +168,7 @@
|
||||
$(function() {
|
||||
$('#mailpoet_reinstall').on('click', function() {
|
||||
if(confirm(
|
||||
"<%= __('If you continue, all of your current MailPoet data will be permanently erased (newsletters, statistics, subscribers, etc...)') %>"
|
||||
"<%= __('Are you sure? All of your MailPoet data will be permanently erased (newsletters, statistics, subscribers, etc.)') %>"
|
||||
)) {
|
||||
MailPoet.Modal.loading(true);
|
||||
MailPoet.Ajax.post({
|
||||
|
@ -6,7 +6,7 @@
|
||||
<%= __("Default sender") %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('These email addresses will be the defaults for each new email created.') %>
|
||||
<%= __('These email addresses will be selected by default for each new email') %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
@ -47,7 +47,7 @@
|
||||
<%= __("Email notifications") %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('These email addresses will receive notifications (separate each address with a comma).') %>
|
||||
<%= __('These email addresses will receive notifications (separate each address with a comma)') %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
@ -88,7 +88,7 @@
|
||||
<%= __('Subscribe in comments') %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('Visitors that comment on a post can click on a checkbox to subscribe to your list.') %>
|
||||
<%= __('Visitors that comment on a post can subscribe to your list via a checkbox') %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
@ -112,7 +112,7 @@
|
||||
<% if(settings.subscribe.on_comment.label) %>
|
||||
value="<%= settings.subscribe.on_comment.label %>"
|
||||
<% else %>
|
||||
value="<%= __('Yes, add me to your mailing list.') %>"
|
||||
value="<%= __('Yes, add me to your mailing list') %>"
|
||||
<% endif %>
|
||||
/>
|
||||
</p>
|
||||
@ -148,7 +148,7 @@
|
||||
<%= __('Subscribe in registration form') %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('Allow users who register on your site to subscribe to a list.') %>
|
||||
<%= __('Allow users who register on your site to subscribe to a list') %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
@ -176,7 +176,7 @@
|
||||
<% if(settings.subscribe.on_register.label) %>
|
||||
value="<%= settings.subscribe.on_register.label %>"
|
||||
<% else %>
|
||||
value="<%= __('Yes, add me to your mailing list.') %>"
|
||||
value="<%= __('Yes, add me to your mailing list') %>"
|
||||
<% endif %>
|
||||
/>
|
||||
</p>
|
||||
@ -203,7 +203,7 @@
|
||||
</div>
|
||||
<% else %>
|
||||
<p>
|
||||
<em><%= __('Registration is disabled on this site.') %></em>
|
||||
<em><%= __('Registration is disabled on this site') %></em>
|
||||
</p>
|
||||
<% endif %>
|
||||
</td>
|
||||
@ -215,7 +215,7 @@
|
||||
<%= __('Manage Subscription page') %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('Your subscribers will be directed to this page when they click the "Manage your subscription" link in your email.') %>
|
||||
<%= __('When your subscribers click the "Manage your subscription" link, they will be directed to this page') %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
@ -270,9 +270,9 @@
|
||||
<%= __('Unsubscribe page') %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('Your subscribers will be directed to this page when they click the "Unsubscribe" link in your email.') %>
|
||||
<%= __('When your subscribers click the "Unsubscribe" link, they will be directed to this page') %>
|
||||
<br />
|
||||
<%= __('Use this shortcode on your website\'s WordPress pages: [mailpoet_manage text="Manage your subscription"].') %>
|
||||
<%= __('Use this shortcode on your website\'s WordPress pages: [mailpoet_manage text="Manage your subscription"]') %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
@ -307,7 +307,7 @@
|
||||
<%= __('Archive page shortcode') %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('Paste this shortcode on a page to display a list of past newsletters.') %>
|
||||
<%= __('Paste this shortcode on a page to display a list of past newsletters') %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
@ -343,7 +343,7 @@
|
||||
<%= __('This shortcode displays the total number of subscribers') %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('Paste this shortcode on a post or page to display the total number of confirmed subscribers.') %>
|
||||
<%= __('Paste this shortcode on a post or page to display the total number of confirmed subscribers') %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
|
@ -263,7 +263,7 @@
|
||||
id="mailpoet_bounce_test"
|
||||
class="button-secondary"
|
||||
href="javascript:;"
|
||||
><%= __('Is it working? Try to connect.') %></a>
|
||||
><%= __('Is it working? Try to connect') %></a>
|
||||
</label>
|
||||
</th>
|
||||
<td id="mailpoet_bounce_test_result"></td>
|
||||
|
@ -55,7 +55,7 @@
|
||||
</h3>
|
||||
|
||||
<p class="mailpoet_description">
|
||||
<strong><%= __('Currently in closed beta.') %></strong>
|
||||
<strong><%= __('Currently in Closed Beta') %></strong>
|
||||
<br />
|
||||
<%= __('[link]Sign up to our newsletter[/link] to get our latest news on our sending service plus other useful tips and tricks.')
|
||||
| replace({
|
||||
@ -83,9 +83,9 @@
|
||||
<h3><%= __('Your web host / web server') %></h3>
|
||||
|
||||
<p class="mailpoet_description">
|
||||
<strong><%= __('Free, but not recommended.') %></strong>
|
||||
<strong><%= __('Free, but not recommended') %></strong>
|
||||
<br />
|
||||
<%= __('Web hosts generally have a bad reputation as a sender. Your newsletter will probably be considered spam.') %>
|
||||
<%= __('Web hosts generally have a bad reputation as a sender. Your newsletter will probably be considered spam') %>
|
||||
</p>
|
||||
|
||||
<div class="mailpoet_status">
|
||||
@ -105,9 +105,9 @@
|
||||
<h3><%= __('Third-party') %></h3>
|
||||
|
||||
<p class="mailpoet_description">
|
||||
<strong><%= __('Currently the best solution.') %></strong>
|
||||
<strong><%= __('Currently the best solution') %></strong>
|
||||
<br />
|
||||
<%= __('Send with an external email provider. This is usually not free.') %>
|
||||
<%= __('Send with an external email provider. This is usually not free') %>
|
||||
</p>
|
||||
|
||||
<div class="mailpoet_status">
|
||||
@ -245,9 +245,9 @@
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
<%= __('<strong>Warning!</strong> Sending more than the recommended amount of emails? You may break the terms of your web host or provider.') %>'
|
||||
<%= __('<strong>Warning!</strong> Sending more than the recommended amount of emails? You may break the terms of your web host or provider!') %>'
|
||||
<br />
|
||||
<%= __('Please ask your host for the maximum number of emails you are allowed to send per day.') %>
|
||||
<%= __('Please ask your host for the maximum number of emails you are allowed to send per day') %>
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
@ -546,7 +546,7 @@
|
||||
<%= __('Authentication') %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('Leave this option set to Yes. Only a tiny portion of SMTP services prefer Authentication to be turned off.') %>
|
||||
<%= __('Leave this option set to Yes. Only a tiny portion of SMTP services prefer Authentication to be turned off') %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
@ -588,12 +588,12 @@
|
||||
<%= __('SPF Signature (Highly recommended!)') %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __("Improves your delivery rate by verifying that you're allowed to send emails from your domain.") %>
|
||||
<%= __("Improves your delivery rate by verifying that you're allowed to send emails from your domain") %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
<p>
|
||||
<%= __("SPF is set up in your DNS. Read your host's support documentation for more information.") %>
|
||||
<%= __("SPF is set up in your DNS. Read your host's support documentation for more information") %>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
@ -631,7 +631,7 @@
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="mailpoet_mta_setup_cancel"
|
||||
><%= __('or cancel.') %></a>
|
||||
><%= __('or Cancel') %></a>
|
||||
</p>
|
||||
</th>
|
||||
<td></td>
|
||||
@ -688,9 +688,9 @@
|
||||
data: {
|
||||
mailer: mailer,
|
||||
newsletter: {
|
||||
subject: "<%= __('This is a sending method test.') %>",
|
||||
subject: "<%= __('This is a Sending Method Test') %>",
|
||||
body: {
|
||||
text: "<%= __('Yup, it works. You can start blasting away emails to the moon.') %>"
|
||||
text: "<%= __('Yup, it works! You can start blasting away emails to the moon.') %>"
|
||||
}
|
||||
},
|
||||
subscriber: {
|
||||
@ -709,7 +709,7 @@
|
||||
} else {
|
||||
if (response.errors) {
|
||||
MailPoet.Notice.error(
|
||||
"<%= __('The email could not be sent.') %> " + response.errors,
|
||||
"<%= __('The email could not be sent: ') %> " + response.errors,
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
|
@ -151,7 +151,7 @@
|
||||
<%= __('Confirmation page') %>
|
||||
</label>
|
||||
<p class="description">
|
||||
<%= __('When subscribers click on the activation link, they are redirected to this page.') %>
|
||||
<%= __('When subscribers click on the activation link, they are redirected to this page') %>
|
||||
</p>
|
||||
</th>
|
||||
<td>
|
||||
@ -192,9 +192,9 @@
|
||||
var result = false;
|
||||
|
||||
if(~~($(this).val()) === 1) {
|
||||
result = confirm("<%= __('Subscribers will need to activate their subscription via email in order to receive your newsletters. This is highly recommended.') %>");
|
||||
result = confirm("<%= __('Subscribers will need to activate their subscription via email in order to receive your newsletters. This is highly recommended!') %>");
|
||||
} else {
|
||||
result = confirm("<%= __('Unconfirmed subscribers will receive your newsletters from without needing to activate their subscriptions.') %>");
|
||||
result = confirm("<%= __('Unconfirmed subscribers will receive your newsletters from without needing to activate their subscriptions. This is not recommended!') %>");
|
||||
}
|
||||
// if the user confirmed changing the signup confirmation (yes/no)
|
||||
if(result === true) {
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
<!-- number of emails per day -->
|
||||
<%=
|
||||
__("That's <strong>%1$s emails</strong> per day.")
|
||||
__("That's <strong>%1$s emails</strong> per day")
|
||||
| format('{{ daily_emails }}')
|
||||
| raw
|
||||
%>
|
||||
@ -17,7 +17,7 @@
|
||||
<br /><br />
|
||||
<span style="color: #d54e21;">
|
||||
<%=
|
||||
__("That's %1$s emails per second. <strong>We highly recommend to send 1 email per second at most.</strong> This is the time MailPoet needs to process and send a single email from most hosts. <strong>Alternatively, send with MailPoet which can be 50 times faster.</strong>")
|
||||
__("That's %1$s emails per second. <strong>We highly recommend to send 1 email per second, at the absolute maximum.</strong> MailPoet needs at least one second to process and send a single email (on most hosts.) <strong>Alternatively, send with MailPoet, which can be up to 50 times faster.</strong>")
|
||||
| format('{{ emails_per_second }}')
|
||||
| raw
|
||||
%>
|
||||
|
@ -8,7 +8,7 @@
|
||||
</h1>
|
||||
<% if segments is empty %>
|
||||
<div class="error">
|
||||
<p><%= __("Yikes! Couldn't find any subscribers.") %></p>
|
||||
<p><%= __("Yikes! Couldn't find any subscribers") %></p>
|
||||
</div>
|
||||
<% endif %>
|
||||
<div class="inside">
|
||||
|
@ -32,26 +32,26 @@
|
||||
|
||||
<% block translations %>
|
||||
<%= localize({
|
||||
'noMailChimpLists': __('No active lists found.'),
|
||||
'noMailChimpLists': __('No active lists found'),
|
||||
'serverError': __('Server error:'),
|
||||
'select': __('Select'),
|
||||
'csvKBLink': csvKBLink,
|
||||
'wrongFileFormat': __('Only comma-separated (CSV) file formats are supported.'),
|
||||
'wrongFileFormat': __('Only comma-separated (CSV) file formats are supported'),
|
||||
'maxPostSizeNotice': __('Your CSV is over %s and is too big to process. Please split the file into two or more sections.')|replace({'%s': maxPostSize}),
|
||||
'dataProcessingError': __("Your data could not be processed. Please make sure it is in the correct format."),
|
||||
'noValidRecords': __('No valid records were found. This file needs to be formatted in a CSV style (comma-separated.) Look at some [link]examples on our support site[/link]'),
|
||||
'importNoticeSkipped': __('%1$s records had issues and were skipped.'),
|
||||
'importNoticeInvalid': __('%1$s emails are not valid : %2$s.'),
|
||||
'importNoticeDuplicate': __('%1$s emails appear more than once in your file : %2$s.'),
|
||||
'hideDetails': __('Hide details.'),
|
||||
'showDetails': __('Show more details.'),
|
||||
'segmentSelectionRequired': __('Please select at least one list.'),
|
||||
'importNoticeSkipped': __('%1$s records had issues and were skipped'),
|
||||
'importNoticeInvalid': __('%1$s emails are not valid : %2$s'),
|
||||
'importNoticeDuplicate': __('%1$s emails appear more than once in your file : %2$s'),
|
||||
'hideDetails': __('Hide details'),
|
||||
'showDetails': __('Show more details'),
|
||||
'segmentSelectionRequired': __('Please select at least one list'),
|
||||
'addNewList': __('Add new list'),
|
||||
'addNewColumuserColumnsn': __('Add new list'),
|
||||
'userColumns': __('User columns'),
|
||||
'selectedValueAlreadyMatched': __('The selected value is already matched to another column.'),
|
||||
'confirmCorrespondingColumn': __('Confirm that this column corresponds to the selected field.'),
|
||||
'columnContainInvalidElement': __('One of the columns contains an invalid email. Please fix it before continuing.'),
|
||||
'selectedValueAlreadyMatched': __('The selected value is already matched to another column'),
|
||||
'confirmCorrespondingColumn': __('Confirm that this column corresponds to the selected field'),
|
||||
'columnContainInvalidElement': __('One of the columns contains an invalid email. Please fix it before continuing'),
|
||||
'january': __('January'),
|
||||
'february': __('February'),
|
||||
'march': __('March'),
|
||||
@ -64,17 +64,17 @@
|
||||
'october': __('October'),
|
||||
'november': __('November'),
|
||||
'december': __('December'),
|
||||
'noDateFieldMatch': __("Do not match as a 'date field' if most of the rows for that column return the same error."),
|
||||
'noDateFieldMatch': __("Do not match as a 'date field' if most of the rows for that column return the same error"),
|
||||
'emptyDate': __('Date cannot be empty'),
|
||||
'verifyDateMatch': __('Verify that the date in blue matches the original date'),
|
||||
'pm': __('PM'),
|
||||
'am': __('AM'),
|
||||
'dateMatchError': __('Error matching date.'),
|
||||
'columnContainsInvalidDate': __('One of the columns contains an invalid date. Please fix it before continuing.'),
|
||||
'dateMatchError': __('Error matching date'),
|
||||
'columnContainsInvalidDate': __('One of the columns contains an invalid date. Please fix it before continuing'),
|
||||
'listCreateError': __('Error adding a new segment:'),
|
||||
'columnContainsInvalidElement': __('One of the columns contains an invalid email. Please fix before continuing.'),
|
||||
'customFieldCreateError': __('Custom field could not be created.'),
|
||||
'columnContainsInvalidElement': __('One of the columns contains an invalid email. Please fix before continuing'),
|
||||
'customFieldCreateError': __('Custom field could not be created'),
|
||||
'subscribersCreated': __('%1$s subscribers added to %2$s.'),
|
||||
'subscribersUpdated': __('%1$s existing subscribers were updated and added to %2$s.')
|
||||
'subscribersUpdated': __('%1$s existing subscribers were updated and added to %2$s')
|
||||
}) %>
|
||||
<% endblock %>
|
||||
|
@ -113,7 +113,7 @@
|
||||
<%= ('If the answer is "no", consider yourself a spammer.') %>
|
||||
<br/>
|
||||
<%=
|
||||
__('[link]Read more on support.mailpoet.com[/link].')
|
||||
__('[link]Read more on support.mailpoet.com[/link]')
|
||||
|replace({
|
||||
'[link]': '<a target="_blank" href="http://support.mailpoet.com/knowledgebase/dont-import-subscribers-who-didnt-sign-up/#utm_source=wpadmin&utm_campaign=importwarning">',
|
||||
'[/link]': '</a>'
|
||||
|
@ -34,12 +34,12 @@
|
||||
<th scope="row">
|
||||
<label>
|
||||
<%= __('Pick one or many segments') %>
|
||||
<p class="description"><%= __('Pick the segments you want to import these subscribers to.') %>
|
||||
<p class="description"><%= __('Pick the segments that you want to import these subscribers to.') %>
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<select id="mailpoet_segments_select" data-placeholder="<%= __('Select') %>" multiple="multiple"></select>
|
||||
<a href="javascript:;" class="mailpoet_create_segment"><%= __('Create new list') %></a>
|
||||
<a href="javascript:;" class="mailpoet_create_segment"><%= __('Create a new list') %></a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="mailpoet_no_segments mailpoet_hidden">
|
||||
@ -134,7 +134,7 @@
|
||||
<input id="new_segment_name" type="text" name="name"/>
|
||||
</p>
|
||||
<p class="mailpoet_validation_error" data-error="segment_name_required">
|
||||
<%= __('Please specify a name.') %>
|
||||
<%= __('Please specify a name') %>
|
||||
</p>
|
||||
<p class="mailpoet_validation_error" data-error="segment_name_not_unique">
|
||||
<%= __('Another record already exists. Please specify a different "%1$s".')|format('name') %>
|
||||
@ -192,10 +192,10 @@
|
||||
<input id="new_column_name" type="text" name="name" value="{{ name }}"/>
|
||||
</p>
|
||||
<p class="mailpoet_validation_error" data-error="name_required">
|
||||
<%= __('Please to specify a name.') %>
|
||||
<%= __('Please specify a name') %>
|
||||
</p>
|
||||
<p class="mailpoet_validation_error" data-error="name_not_unique">
|
||||
<%= __('This name is already taken.') %>
|
||||
<%= __('This name is already taken') %>
|
||||
</p>
|
||||
|
||||
<hr/>
|
||||
|
@ -25,10 +25,10 @@
|
||||
<p>{{{updated}}}</p>
|
||||
{{/if}}
|
||||
{{#if no_action}}
|
||||
<p><%= __('No new subscribers were found/added.') %></p>
|
||||
<p><%= __('No new subscribers were found/added') %></p>
|
||||
{{/if}}
|
||||
{{#if added_to_segment_with_welcome_notification}}
|
||||
<p><%= __('Note: Imported subscribers will not receive any welcome emails.') %></p>
|
||||
<p><%= __('Note: Imported subscribers will not receive any welcome emails') %></p>
|
||||
{{/if}}
|
||||
</script>
|
||||
</div>
|
@ -17,12 +17,12 @@
|
||||
'pageTitle': __('Subscribers'),
|
||||
'searchLabel': __('Search'),
|
||||
'loadingItems': __('Loading subscribers...'),
|
||||
'noItemsFound': __('No subscribers were found.'),
|
||||
'selectAllLabel': __('All subscribers on this page are selected.'),
|
||||
'selectedAllLabel': __('All %d subscribers are selected.'),
|
||||
'selectAllLink': __('Select all subscribers on all pages.'),
|
||||
'clearSelection': __('Clear selection.'),
|
||||
'permanentlyDeleted': __('%d subscribers were permanently deleted.'),
|
||||
'noItemsFound': __('No subscribers were found'),
|
||||
'selectAllLabel': __('All subscribers on this page are selected'),
|
||||
'selectedAllLabel': __('All %d subscribers are selected'),
|
||||
'selectAllLink': __('Select all subscribers on all pages'),
|
||||
'clearSelection': __('Clear selection'),
|
||||
'permanentlyDeleted': __('%d subscribers were permanently deleted'),
|
||||
'selectBulkAction': __('Select bulk action'),
|
||||
'bulkActions': __('Bulk Actions'),
|
||||
'apply': __('Apply'),
|
||||
@ -60,23 +60,23 @@
|
||||
'lists': __('Lists'),
|
||||
'subscribedOn': __('Subscribed on'),
|
||||
'lastModifiedOn': __('Last modified on'),
|
||||
'oneSubscriberTrashed': __('1 subscriber was moved to the trash.'),
|
||||
'multipleSubscribersTrashed': __('%$1d subscribers were moved to the trash.'),
|
||||
'oneSubscriberDeleted': __('1 subscriber was permanently deleted.'),
|
||||
'multipleSubscribersDeleted': __('%$1d subscribers were permanently deleted.'),
|
||||
'oneSubscriberRestored': __('1 subscriber has been restored from the trash.'),
|
||||
'multipleSubscribersRestored': __('%$1d subscribers have been restored from the trash.'),
|
||||
'oneSubscriberTrashed': __('1 subscriber was moved to the trash'),
|
||||
'multipleSubscribersTrashed': __('%$1d subscribers were moved to the trash'),
|
||||
'oneSubscriberDeleted': __('1 subscriber was permanently deleted'),
|
||||
'multipleSubscribersDeleted': __('%$1d subscribers were permanently deleted'),
|
||||
'oneSubscriberRestored': __('1 subscriber has been restored from the trash'),
|
||||
'multipleSubscribersRestored': __('%$1d subscribers have been restored from the trash'),
|
||||
'moveToList': __('Move to list...'),
|
||||
'multipleSubscribersMovedToList': __('%$1d subscribers were moved to list <strong>%$2s</strong>.'),
|
||||
'multipleSubscribersMovedToList': __('%$1d subscribers were moved to list <strong>%$2s</strong>'),
|
||||
'addToList': __('Add to list...'),
|
||||
'multipleSubscribersAddedToList': __('%$1d subscribers were added to list <strong>%$2s</strong>.'),
|
||||
'multipleSubscribersAddedToList': __('%$1d subscribers were added to list <strong>%$2s</strong>'),
|
||||
'removeFromList': __('Remove from list...'),
|
||||
'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'),
|
||||
'multipleSubscribersRemovedFromAllLists': __('%$1d subscribers were removed from all lists.'),
|
||||
'multipleSubscribersRemovedFromAllLists': __('%$1d subscribers were removed from all lists'),
|
||||
'resendConfirmationEmail': __('Resend confirmation email'),
|
||||
'multipleConfirmationEmailsSent': __('%$1d confirmation emails have been sent.'),
|
||||
'listsToWhichSubscriberWasSubscribed': __('Lists to which the subscriber was subscribed.'),
|
||||
'multipleConfirmationEmailsSent': __('%$1d confirmation emails have been sent'),
|
||||
'listsToWhichSubscriberWasSubscribed': __('Lists to which the subscriber was subscribed'),
|
||||
'WPUsersSegment': __('WordPress Users'),
|
||||
'WPUserEditNotice': __('This subscriber is a registered WordPress user. [link]Edit his profile[/link] to change his/her email.'),
|
||||
'tip': __('Tip:'),
|
||||
|
@ -79,8 +79,8 @@
|
||||
<h2>
|
||||
<%= __('Welcome to MailPoet') %> <%= settings.version %> <%=__('BETA') %>
|
||||
</h2>
|
||||
<p>You’re one our extra special testers! We salute you.</p>
|
||||
<h3 class="changes">Big changes...watch the video!</h3>
|
||||
<p>You’re one of our extra special testers! We salute you.</p>
|
||||
<h3 class="changes">Big changes are coming. Check out the video for more details!</h3>
|
||||
<iframe src="https://player.vimeo.com/video/130151897?title=0&byline=0&portr
|
||||
ait=0" width="710" height="399" frameborder="0" webkitallowfullscreen
|
||||
mozallowfullscreen allowfullscreen></iframe>
|
||||
|
Reference in New Issue
Block a user