Compare commits

...

68 Commits

Author SHA1 Message Date
71d8fb0d93 Bump up version to 0.0.20 2016-03-18 19:40:11 +02:00
2f293da7a3 Merge pull request #391 from mailpoet/scheduled_sending
Scheduled sending
2016-03-18 18:49:47 +02:00
17b56f0160 - Fixes scheduling issues
- Fixes unit test
- Updates as per code review comments
2016-03-18 12:11:38 -04:00
bb9fce7f82 - Implements post notification scheduling 2016-03-18 11:15:31 -04:00
ad4f1f8326 Merge pull request #390 from mailpoet/newsletter_footer_links
Newsletter footer links
2016-03-18 16:10:17 +02:00
8ece62c9a6 fix tests 2016-03-18 14:58:33 +01:00
a9b9e9c631 Updated tests in order to fix WP related issues 2016-03-18 14:30:59 +01:00
6e289b6a8f - Implements welcome e-mail scheduling 2016-03-17 11:22:29 -04:00
20ced8b099 replace mailpoet_title 'hack' by checking the post type 2016-03-17 15:48:41 +01:00
f38b632707 properly handle custom subscriptions pages 2016-03-17 15:48:41 +01:00
8ce0595342 turned static values into constants 2016-03-17 15:48:06 +01:00
f11de2f1ad Updated shortcodes for unsubscribe/manage/browser links
- fixed all issues in #387 except the custom mailpoet pages
2016-03-17 15:48:06 +01:00
e28451d410 removed edit page in settings + manage subscription update 2016-03-17 15:45:05 +01:00
72882aaf2b fixed shortcodes replacement for "global:" in newsletters
- extracted "get subscription pages urls" from models\Subscriber
- added unit tests for subscription\url class (not working because of WP/Codeception issue)
2016-03-17 15:45:05 +01:00
fdf9dd0fa3 Merge pull request #386 from mailpoet/editor_refactor
Editor refactor
2016-03-17 15:19:04 +01:00
97dd0abea2 fixed makepot task
- added proper grunt module (grunt-cli)
- updated translations extract code to remove warnings when no translations are found
2016-03-17 15:08:38 +01:00
a099174226 Revert "Extract text labels from React code for translation in twig views"
This reverts commit 9aef6850c2.

Conflicts:
	views/newsletters.html
2016-03-17 15:24:07 +02:00
a054acc6e6 Merge pull request #389 from mailpoet/import_xss_update
Updates import to santize user input
2016-03-17 12:02:58 +02:00
74254d7e2a - Updates import to santize user input 2016-03-15 13:06:21 -04:00
1795964c69 - Updates composer dependencies 2016-03-15 12:10:09 -04:00
0118b2472a Fix loading of makepot task for pot translation file generation 2016-03-10 19:24:35 +02:00
5fe03f0dee Add event selection explanation to welcome email type 2016-03-10 17:49:06 +02:00
9aef6850c2 Extract text labels from React code for translation in twig views 2016-03-10 17:08:40 +02:00
f688a69f8b Fix translations to be injected at config time 2016-03-07 17:52:14 +02:00
b2682fa0b7 Remove console.log statements from newsletter editor 2016-03-07 16:57:20 +02:00
18b15c5440 Restructure JS loading to not duplicate JS asset loading 2016-03-07 16:15:26 +02:00
c3416977bb Replace editor translations with Twig localize function 2016-03-07 15:42:12 +02:00
88a00bc38c Rename editor form.html to editor.html 2016-03-07 14:42:12 +02:00
a1441dfde6 Bump up version to 0.0.19 2016-03-04 18:43:22 +02:00
8e7336d352 Merge pull request #373 from mailpoet/editor_rendering
Editor rendering (Part 2)
2016-03-04 11:11:42 -05:00
e0e2933cdf Merge pull request #379 from mailpoet/scheduled_sending
Fix mistyped method name
2016-03-04 11:06:40 -05:00
c93ec629ea Fix mistyped method name 2016-03-04 18:03:02 +02:00
d613df6558 Merge pull request #378 from mailpoet/scheduled_sending
Prepares codebase for future scheduled sending
2016-03-04 17:58:59 +02:00
01c9096543 - Rewrites hooks to not use closures 2016-03-04 10:47:55 -05:00
f120e839dd Merge pull request #375 from mailpoet/export_large_dataset_fix
Export update
2016-03-04 14:12:20 +02:00
f0ab592c04 Merge pull request #376 from mailpoet/rendering_serverside_update
Removes padding from the last column element
2016-03-04 13:30:31 +02:00
1a269d28b3 Merge pull request #374 from mailpoet/edit_subscription
edit subscription form + save
2016-03-04 12:54:56 +02:00
8d4a666bf0 added logged out handling of subscriber save request 2016-03-04 11:52:06 +01:00
4a96e483a6 edit profile rendering (missing segments list) + fallback for Url::redirectBack() 2016-03-04 11:20:17 +01:00
263a66407f - Removes mailpoet_padded class from the last element in a column 2016-03-03 19:13:50 -05:00
c4b728f4e1 - Updates Daemon to execute workers via WP's action hook
- Bootstraps Scheduler class
2016-03-03 15:42:10 -05:00
9970ad7fb6 Merge pull request #369 from mailpoet/manage_subscription
Manage subscription (part 2)
2016-03-03 15:33:56 -05:00
eb380499d9 - Rewrites export to support large datasets
- Updates unit tests
- Fixes an issue with export UI not displaying subscribers without segment
2016-03-03 14:33:10 -05:00
4b528549f5 edit subscription form + save 2016-03-03 15:57:42 +01:00
a903017bc2 Update "Please include unsubscribe link" message 2016-03-03 15:48:59 +02:00
afd25e9174 Remove old and unused editor template 2016-03-03 15:39:46 +02:00
b1bbf1b3bc Add default email for newsletter preview, disallow empty email 2016-03-03 12:59:49 +02:00
9e84e8df93 Show loading icon when loading newsletter preview 2016-03-02 16:57:04 +02:00
df775b5a07 Set font-style for paragraph and headings 2016-03-02 16:57:04 +02:00
c9c22b9b52 Switch 'Comic Sans' to 'Comic Sans MS' 2016-03-02 16:57:04 +02:00
93d20688ea Prevent heading line height and font weight from being overridden 2016-03-02 16:57:04 +02:00
9ebcddfa2a Update button defaults 2016-03-02 16:57:04 +02:00
85995bc8a6 Merge pull request #369 from mailpoet/manage_subscription
Manage subscription (part 2)
2016-03-02 16:42:12 +02:00
a8f2959bc6 added Subscriber::generateToken() 2016-03-02 15:07:37 +01:00
36242bd580 Fixed sending confirmation email to new subscribers (first time)
- fixed newsletters listing issue with queue
2016-03-02 13:11:06 +01:00
1e7dbc8449 refactor pages 2016-03-01 18:27:31 +01:00
12c159c627 bugfix + refactoring 2016-03-01 16:23:15 +01:00
82ed7e51c5 Replaced "contains" by "indexOf" (chrome issue)
- added public ajax routing (not checking permissions)
- exception handling in form subscription
2016-03-01 13:18:36 +01:00
0b3aa0d12a Merge pull request #370 from mailpoet/import_batch_update
Import batch size increase
2016-03-01 12:10:46 +02:00
4d788f69aa - Updates batch size to 2000 2016-02-29 11:41:17 -05:00
c721843c12 extracted subscription pages code from router 2016-02-29 13:34:17 +01:00
d2be407ccb rendering of edit subscription form 2016-02-29 13:34:17 +01:00
e6337216cf improved demo view of subscription pages 2016-02-29 13:34:17 +01:00
f1c396f0b0 basic implementation of confirm/edit/unsubscribe pages 2016-02-29 13:34:17 +01:00
3622bc9fcb fixed sending issue of confirmation email 2016-02-29 13:34:17 +01:00
14fe333678 Send confirmation email + page 2016-02-29 13:34:17 +01:00
cf6466197a Fixed Subscribers' bulk actions when filtering by a segment
- filter by segment is now affected by the selected group (all, trash,...)
- updated relationship methods between subscribers & segments (to account for subsegment status)
2016-02-29 13:34:17 +01:00
f56bee76f2 MailPoet.Date to handle localized dates and times 2016-02-29 13:34:17 +01:00
133 changed files with 6028 additions and 4015 deletions

View File

@ -89,14 +89,14 @@ class RoboFile extends \Robo\Tasks {
}
function makepot() {
$this->_exec('grunt makepot'.
$this->_exec('./node_modules/.bin/grunt makepot'.
' --gruntfile '.__DIR__.'/tasks/makepot/makepot.js'.
' --base_path '.__DIR__
);
}
function pushpot() {
$this->_exec('grunt pushpot'.
$this->_exec('./node_modules/.bin/grunt pushpot'.
' --gruntfile '.__DIR__.'/tasks/makepot/makepot.js'.
' --base_path '.__DIR__
);
@ -123,7 +123,7 @@ class RoboFile extends \Robo\Tasks {
$this->compileJs();
$this->_exec(join(' ', array(
'./node_modules/mocha/bin/mocha',
'./node_modules/.bin/mocha',
'-r tests/javascript/mochaTestHelper.js',
'tests/javascript/testBundles/**/*.js'
)));

View File

@ -32,5 +32,6 @@ $block-hover-highlight-color = $primary-active-color
position: relative
line-height: 1.61803398875
p
p, h1, h2, h3, h4, h5, h6
line-height: 1.61803398875
font-style: normal

View File

@ -12,9 +12,11 @@
h1, h2, h3, h4, h5, h6
padding: 0
margin: 0
font-weight: normal
p
margin-top: 0
font-weight: normal
blockquote
margin: 1em

View File

@ -2,3 +2,13 @@
@require 'parsley'
@require 'form_validation'
/* labels */
.mailpoet_text_label
.mailpoet_textarea_label
.mailpoet_select_label
.mailpoet_radio_label
.mailpoet_checkbox_label
.mailpoet_list_label
.mailpoet_date_label
display:block

136
assets/js/src/date.js Normal file
View File

@ -0,0 +1,136 @@
define('date',
[
'mailpoet',
'jquery',
'moment'
], function(
MailPoet,
jQuery,
Moment
) {
'use strict';
MailPoet.Date = {
version: 0.1,
options: {},
defaults: {
offset: 0,
format: 'F, d Y H:i:s'
},
init: function(options) {
options = options || {};
// set UTC offset
if (
options.offset === undefined
&& window.mailpoet_date_offset !== undefined
) {
options.offset = window.mailpoet_date_offset;
}
// set date format
if (
options.format === undefined
&& window.mailpoet_date_format !== undefined
) {
options.format = window.mailpoet_date_format;
}
// merge options
this.options = jQuery.extend({}, this.defaults, options);
return this;
},
format: function(date, options) {
this.init(options);
return Moment.utc(date)
.local()
.format(this.convertFormat(this.options.format));
},
short: function(date) {
return this.format(date, {
format: 'F, j Y'
});
},
full: function(date) {
return this.format(date, {
format: 'F, j Y H:i:s'
});
},
time: function(date) {
return this.format(date, {
format: 'H:i:s'
});
},
convertFormat: function(format) {
const format_mappings = {
date: {
D: 'ddd',
l: 'dddd',
d: 'DD',
j: 'D',
z: 'DDDD',
N: 'E',
S: '',
M: 'MMM',
F: 'MMMM',
m: 'MM',
n: '',
t: '',
y: 'YY',
Y: 'YYYY',
H: 'HH',
h: 'hh',
g: 'h',
A: 'A',
i: 'mm',
s: 'ss',
T: 'z',
O: 'ZZ',
w: 'd',
W: 'WW'
},
strftime: {
a: 'ddd',
A: 'dddd',
b: 'MMM',
B: 'MMMM',
d: 'DD',
e: 'D',
F: 'YYYY-MM-DD',
H: 'HH',
I: 'hh',
j: 'DDDD',
k: 'H',
l: 'h',
m: 'MM',
M: 'mm',
p: 'A',
S: 'ss',
u: 'E',
w: 'd',
W: 'WW',
y: 'YY',
Y: 'YYYY',
z: 'ZZ',
Z: 'z'
}
};
const replacements = format_mappings['date'];
let outputFormat = '';
Object.keys(replacements).forEach(function(key) {
if (format.indexOf(key) !== -1) {
format = format.replace(key, '%'+key);
}
});
outputFormat = format;
Object.keys(replacements).forEach(function(key) {
if (outputFormat.indexOf('%'+key) !== -1) {
outputFormat = outputFormat.replace('%'+key, replacements[key]);
}
});
return outputFormat;
}
};
});

View File

@ -146,7 +146,7 @@ const FormList = React.createClass({
{ segments }
</td>
<td className="column-date" data-colname="Created on">
<abbr>{ form.created_at }</abbr>
<abbr>{ MailPoet.Date.full(form.created_at) }</abbr>
</td>
</div>
);

View File

@ -12,9 +12,19 @@ define([
'newsletter_editor/blocks/button',
'newsletter_editor/blocks/divider',
'newsletter_editor/components/communication',
'mailpoet',
'underscore',
'jquery'
], function(App, BaseBlock, ButtonBlock, DividerBlock, CommunicationComponent, _, jQuery) {
], function(
App,
BaseBlock,
ButtonBlock,
DividerBlock,
CommunicationComponent,
MailPoet,
_,
jQuery
) {
"use strict";
@ -73,7 +83,7 @@ define([
that.get('_container').get('blocks').reset(content, {parse: true});
that.trigger('postsChanged');
}).fail(function(error) {
console.log('ALC fetchPosts error', arguments);
MailPoet.Notice.error(App.getConfig().get('translations.failedToFetchRenderedPosts'));
});
},
/**

View File

@ -16,7 +16,7 @@ define([
defaults: function() {
return this._getDefaults({
type: 'footer',
text: '<a href="[unsubscribeUrl]">Unsubscribe</a> | <a href="[manageSubscriptionUrl]">Manage subscription</a><br /><b>Add your postal address here!</b>',
text: '<a href="[subscription:unsubscribe_url]">Unsubscribe</a> | <a href="[subscription:manage_url]">Manage subscription</a><br /><b>Add your postal address here!</b>',
styles: {
block: {
backgroundColor: 'transparent',

View File

@ -16,7 +16,7 @@ define([
defaults: function() {
return this._getDefaults({
type: 'header',
text: 'Display problems? <a href="[viewInBrowserUrl]">View it in your browser</a>',
text: 'Display problems? <a href="[newsletter:view_in_browser_url]">View it in your browser</a>',
styles: {
block: {
backgroundColor: 'transparent',

View File

@ -101,7 +101,7 @@ define([
that.get('_selectedPosts').reset(); // Empty out the collection
that.trigger('change:_availablePosts');
}).fail(function() {
console.log('Posts fetchPosts error', arguments);
MailPoet.Notice.error(App.getConfig().get('translations.failedToFetchAvailablePosts'));
});
},
_refreshTransformedPosts: function() {
@ -118,7 +118,7 @@ define([
CommunicationComponent.getTransformedPosts(data).done(function(posts) {
that.get('_transformedPosts').get('blocks').reset(posts, {parse: true});
}).fail(function() {
console.log('Posts _refreshTransformedPosts error', arguments);
MailPoet.Notice.error(App.getConfig().get('translations.failedToFetchRenderedPosts'));
});
},
_insertSelectedPosts: function() {
@ -134,7 +134,7 @@ define([
CommunicationComponent.getTransformedPosts(data).done(function(posts) {
collection.add(posts, { at: index });
}).fail(function() {
console.log('Posts fetchPosts error', arguments);
MailPoet.Notice.error(App.getConfig().get('translations.failedToFetchRenderedPosts'));
});
},
});

View File

@ -10,7 +10,6 @@ define([
availableStyles: {},
socialIcons: {},
blockDefaults: {},
translations: {},
sidepanelWidth: '331px',
validation: {},
urls: {},

View File

@ -206,12 +206,10 @@ define([
}
);
} else {
console.log('Saving template with ', templateName, templateDescription);
Module.saveTemplate({
name: templateName,
description: templateDescription,
}).done(function() {
console.log('Template saved', arguments);
MailPoet.Notice.success(
App.getConfig().get('translations.templateSaved'),
{
@ -220,7 +218,6 @@ define([
}
);
}).fail(function() {
console.log('Template save failed', arguments);
MailPoet.Notice.error(
App.getConfig().get('translations.templateSaveFailed'),
{
@ -262,7 +259,6 @@ define([
}
);
} else {
console.log('Exporting template with ', templateName, templateDescription);
Module.exportTemplate({
name: templateName,
description: templateDescription,
@ -278,7 +274,6 @@ define([
next: function() {
this.hideOptionContents();
if(!this.$('.mailpoet_save_next').hasClass('button-disabled')) {
console.log('Next');
window.location.href = App.getConfig().get('urls.send');
}
},
@ -289,7 +284,7 @@ define([
}
if (App.getConfig().get('validation.validateUnsubscribeLinkPresent') &&
JSON.stringify(jsonObject).indexOf("[unsubscribeUrl]") < 0) {
JSON.stringify(jsonObject).indexOf("[subscription:unsubscribe_url]") < 0) {
this.showValidationError(App.getConfig().get('translations.unsubscribeLinkMissing'));
return;
}

View File

@ -1,13 +1,24 @@
define([
'newsletter_editor/App',
'newsletter_editor/components/communication',
'mailpoet',
'backbone',
'backbone.marionette',
'backbone.supermodel',
'underscore',
'jquery',
'sticky-kit'
], function(App, CommunicationComponent, Backbone, Marionette, SuperModel, _, jQuery, StickyKit) {
], function(
App,
CommunicationComponent,
MailPoet,
Backbone,
Marionette,
SuperModel,
_,
jQuery,
StickyKit
) {
"use strict";
@ -230,27 +241,39 @@ define([
json.body = JSON.stringify(json.body);
}
MailPoet.Modal.loading(true);
MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'render',
data: json,
}).done(function(response){
console.log('Should open a new window');
MailPoet.Modal.loading(false);
window.open('data:text/html;charset=utf-8,' + encodeURIComponent(response.rendered_body), '_blank');
}).fail(function(error) {
console.log('Preview error', json);
MailPoet.Modal.loading(false);
alert('Something went wrong, check console');
});
},
sendPreview: function() {
// testing sending method
console.log('trying to send a preview');
// get form data
var $emailField = this.$('#mailpoet_preview_to_email');
var data = {
subscriber: this.$('#mailpoet_preview_to_email').val(),
subscriber: $emailField.val(),
id: App.getNewsletter().get('id'),
};
if (data.subscriber.length <= 0) {
MailPoet.Notice.error(
App.getConfig().get('translations.newsletterPreviewEmailMissing'),
{
positionAfter: $emailField,
scroll: true,
}
);
return false;
}
// send test email
MailPoet.Modal.loading(true);

View File

@ -135,7 +135,7 @@ define(
});
},
renderStatus: function(item) {
if(item.queue === null) {
if(!item.queue) {
return (
<span>Not sent yet.</span>
);
@ -208,10 +208,8 @@ define(
'has-row-actions'
);
var segments = mailpoet_segments.filter(function(segment) {
return (jQuery.inArray(segment.id, newsletter.segments) !== -1);
}).map(function(segment) {
return segment.name;
var segments = newsletter.segments.map(function(segment) {
return segment.name
}).join(', ');
return (
@ -229,10 +227,10 @@ define(
{ segments }
</td>
<td className="column-date" data-colname="Subscribed on">
<abbr>{ newsletter.created_at }</abbr>
<abbr>{ MailPoet.Date.full(newsletter.created_at) }</abbr>
</td>
<td className="column-date" data-colname="Last modified on">
<abbr>{ newsletter.updated_at }</abbr>
<abbr>{ MailPoet.Date.full(newsletter.updated_at) }</abbr>
</td>
</div>
);

View File

@ -53,13 +53,13 @@ define(
var weekDayField = {
name: 'weekDay',
values: {
0: 'Monday',
1: 'Tuesday',
2: 'Wednesday',
3: 'Thursday',
4: 'Friday',
5: 'Saturday',
6: 'Sunday',
0: 'Sunday',
1: 'Monday',
2: 'Tuesday',
3: 'Wednesday',
4: 'Thursday',
5: 'Friday',
6: 'Saturday'
},
};
@ -84,10 +84,10 @@ define(
var nthWeekDayField = {
name: 'nthWeekDay',
values: {
'0': '1st',
'1': '2nd',
'2': '3rd',
'3': 'last',
'1': '1st',
'2': '2nd',
'3': '3rd',
'L': 'last',
},
};
@ -99,9 +99,9 @@ define(
return {
intervalType: 'immediate', // 'immediate'|'daily'|'weekly'|'monthly'
timeOfDay: 0,
weekDay: 0,
weekDay: 1,
monthDay: 0,
nthWeekDay: 0,
nthWeekDay: 1,
};
},
handleIntervalChange: function(event) {

View File

@ -155,6 +155,8 @@ define(
<h1>Welcome email</h1>
<Breadcrumb step="type" />
<h3>{MailPoetI18n.selectEventToSendWelcomeEmail}</h3>
<Select
field={events}
item={this.state}

View File

@ -190,7 +190,7 @@ const SegmentList = React.createClass({
<abbr>{ segment.subscribers_count.unsubscribed || 0 }</abbr>
</td>
<td className="column-date" data-colname="Created on">
<abbr>{ segment.created_at }</abbr>
<abbr>{ MailPoet.Date.full(segment.created_at) }</abbr>
</td>
</div>
);

View File

@ -3,15 +3,13 @@ define(
'react',
'react-router',
'mailpoet',
'form/form.jsx',
'moment'
'form/form.jsx'
],
function(
React,
Router,
MailPoet,
Form,
Moment
Form
) {
var fields = [
{
@ -69,9 +67,8 @@ define(
label = segment.name;
if (subscription.status === 'unsubscribed') {
const unsubscribed_at = Moment(subscription.updated_at)
.utcOffset(parseInt(mailpoet_date_offset))
.format('ddd, D MMM YYYY HH:mm:ss');
const unsubscribed_at = MailPoet.Date
.format(subscription.updated_at);
label += ' (Unsubscribed on '+unsubscribed_at+')';
}
}

View File

@ -7,7 +7,8 @@ define(
'handlebars',
'papaparse',
'select2',
'asyncqueue'
'asyncqueue',
'xss'
],
function (
Backbone,
@ -16,7 +17,8 @@ define(
MailPoet,
Handlebars,
Papa,
AsyncQueue
AsyncQueue,
xss
) {
if (!jQuery('#mailpoet_subscribers_import').length) {
return;
@ -355,7 +357,7 @@ define(
complete: function (CSV) {
for (var rowCount in CSV.data) {
var rowData = CSV.data[rowCount].map(function (el) {
return el.trim();
return filterXSS(el.trim());
}),
rowColumnCount = rowData.length;
// set the number of row elements based on the first non-empty row
@ -1056,7 +1058,7 @@ define(
var columns = {},
queue = new jQuery.AsyncQueue(),
batchNumber = 0,
batchSize = 500,
batchSize = 2000,
timestamp = Date.now() / 1000,
subscribers = [],
importResults = {

View File

@ -208,6 +208,16 @@ const bulk_actions = [
);
}
},
{
name: 'sendConfirmationEmail',
label: 'Resend confirmation email',
onSuccess: function(response) {
MailPoet.Notice.success(
'%$1d confirmation emails have been sent.'
.replace('%$1d', ~~response)
);
}
},
{
name: 'trash',
label: 'Trash',
@ -272,13 +282,13 @@ const SubscriberList = React.createClass({
subscriber.subscriptions.map((subscription) => {
const segment = this.getSegmentFromId(subscription.segment_id);
if(segment === false) return;
if (subscription.status === 'subscribed') {
subscribed_segments.push(segment.name);
} else {
} else if (subscription.status === 'unsubscribed') {
unsubscribed_segments.push(segment.name);
}
});
segments = (
<span>
<span className="mailpoet_segments_subscribed">
@ -331,10 +341,10 @@ const SubscriberList = React.createClass({
{ segments }
</td>
<td className="column-date" data-colname="Subscribed on">
<abbr>{ subscriber.created_at }</abbr>
<abbr>{ MailPoet.Date.full(subscriber.created_at) }</abbr>
</td>
<td className="column-date" data-colname="Last modified on">
<abbr>{ subscriber.updated_at }</abbr>
<abbr>{ MailPoet.Date.full(subscriber.updated_at) }</abbr>
</td>
</div>
);

View File

@ -9,6 +9,7 @@ settings:
bootstrap: _bootstrap.php
colors: true
memory_limit: 1024M
log: true
extensions:
enabled:
- Codeception\Extension\RunFailed

View File

@ -9,7 +9,7 @@
"j4mie/paris": "1.5.4",
"swiftmailer/swiftmailer": "^5.4",
"phpseclib/phpseclib": "*",
"mtdowling/cron-expression": "^1.0",
"mtdowling/cron-expression": "^1.1",
"nesbot/carbon": "^1.21",
"soundasleep/html2text": "^0.3.0"
},

10
composer.lock generated
View File

@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
"hash": "db2edea5fb720fcb8013ac470e924c19",
"content-hash": "85119d1ccd5193b6b08fda305a2427e7",
"hash": "2bed8395d84740d7c0ae644a6c6216fd",
"content-hash": "7b66e221814f3d5839ed4faabd2f50ad",
"packages": [
{
"name": "cerdic/css-tidy",
@ -2294,7 +2294,7 @@
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/config/zipball/41ee6c70758f40fa1dbf90d019ae0a66c4a09e74",
"url": "https://api.github.com/repos/symfony/config/zipball/ee4cdda66aff834c8125e8f2c15932461667c521",
"reference": "41ee6c70758f40fa1dbf90d019ae0a66c4a09e74",
"shasum": ""
},
@ -2671,7 +2671,7 @@
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/form/zipball/7fd5e4034cb8e215887136f5e176430bbf5ef085",
"url": "https://api.github.com/repos/symfony/form/zipball/b629051c77a4f37c625651d03002760385df4575",
"reference": "7fd5e4034cb8e215887136f5e176430bbf5ef085",
"shasum": ""
},
@ -2929,7 +2929,7 @@
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/6f1979c3b0f4c22c77a8a8971afaa7dd07f082ac",
"url": "https://api.github.com/repos/symfony/process/zipball/d9d21cfcc3e202ee34777d6da38897695d4d208d",
"reference": "6f1979c3b0f4c22c77a8a8971afaa7dd07f082ac",
"shasum": ""
},

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,12 @@
<?php
namespace MailPoet\Config;
use \MailPoet\Models\Setting;
use \MailPoet\Util\Url;
class Changelog {
function __construct() {
}
function init() {
$doing_ajax = (bool)(defined('DOING_AJAX') && DOING_AJAX);
@ -42,20 +46,7 @@ class Changelog {
// save version number
Setting::setValue('version', Env::$version);
global $wp;
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
if($redirect_url !== $current_url) {
wp_safe_redirect(
add_query_arg(
array(
'mailpoet_redirect' => urlencode($current_url)
),
$redirect_url
)
);
exit;
}
Url::redirectWithReferer($redirect_url);
}
}
}

View File

@ -1,5 +1,7 @@
<?php
namespace MailPoet\Config;
use MailPoet\Cron\Workers\Scheduler;
use MailPoet\Cron\Workers\SendingQueue;
use \MailPoet\Models\Setting;
class Hooks {
@ -11,6 +13,7 @@ class Hooks {
$this->setupWPUsers();
$this->setupImageSize();
$this->setupListing();
$this->setupCronWorkers();
}
function setupSubscribe() {
@ -144,4 +147,19 @@ class Hooks {
return $status;
}
}
function setupCronWorkers() {
add_action('mailpoet_cron_worker', array($this, 'runSchedulerWorker'), 10, 1);
add_action('mailpoet_cron_worker', array($this, 'runSendingQueueWorker'), 10, 1);
}
function runSchedulerWorker($timer) {
$scheduler = new Scheduler($timer);
$scheduler->process();
}
function runSendingQueueWorker($timer) {
$sending_queue = new SendingQueue($timer);
$sending_queue->process();
}
}

View File

@ -4,8 +4,6 @@ namespace MailPoet\Config;
use MailPoet\Models;
use MailPoet\Cron\Supervisor;
use MailPoet\Router;
use MailPoet\Models\Setting;
use MailPoet\Settings\Pages;
if(!defined('ABSPATH')) exit;
@ -26,6 +24,7 @@ class Initializer {
register_activation_hook(Env::$file, array($this, 'runPopulator'));
add_action('plugins_loaded', array($this, 'setup'));
add_action('init', array($this, 'onInit'));
add_action('widgets_init', array($this, 'setupWidget'));
}
@ -34,16 +33,14 @@ class Initializer {
$this->setupRenderer();
$this->setupLocalizer();
$this->setupMenu();
$this->setupRouter();
$this->setupPermissions();
$this->setupPublicAPI();
$this->setupAnalytics();
$this->setupChangelog();
$this->runQueueSupervisor();
$this->setupShortcodes();
$this->setupHooks();
$this->setupPages();
$this->setupImages();
$this->setupPublicAPI();
$this->runQueueSupervisor();
} catch(\Exception $e) {
// if anything goes wrong during init
// automatically deactivate the plugin
@ -51,11 +48,20 @@ class Initializer {
}
}
function onInit() {
$this->setupRouter();
$this->setupPages();
$this->runQueueSupervisor();
}
function setupDB() {
\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('logger', function($query, $time) {
// error_log("\n".$query."\n");
});
\ORM::configure('driver_options', array(
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
@ -144,8 +150,11 @@ class Initializer {
}
function setupPages() {
$pages = new Pages();
$pages = new \MailPoet\Settings\Pages();
$pages->init();
$subscription_pages = new \MailPoet\Subscription\Pages();
$subscription_pages->init();
}
function setupShortcodes() {

View File

@ -380,11 +380,12 @@ class Menu {
$data = array(
'customFields' => $custom_fields,
'settings' => Setting::getAll(),
);
wp_enqueue_media();
wp_enqueue_script('tinymce-wplink', includes_url('js/tinymce/plugins/wplink/plugin.js'));
wp_enqueue_style('editor', includes_url('css/editor.css'));
echo $this->renderer->render('newsletter/form.html', $data);
echo $this->renderer->render('newsletter/editor.html', $data);
}
function import() {

View File

@ -221,6 +221,7 @@ class Migrator {
'count_processed mediumint(9) NOT NULL DEFAULT 0,',
'count_to_process mediumint(9) NOT NULL DEFAULT 0,',
'count_failed mediumint(9) NOT NULL DEFAULT 0,',
'scheduled_at TIMESTAMP NOT NULL DEFAULT 0,',
'processed_at TIMESTAMP NOT NULL DEFAULT 0,',
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',

View File

@ -70,8 +70,13 @@ class Populator {
if($page === null) {
$mailpoet_page_id = Pages::createMailPoetPage();
Setting::setValue('subscription.page', $mailpoet_page_id);
} else {
$mailpoet_page_id = (int)$page->ID;
}
Setting::setValue('subscription.unsubscribe_page', $mailpoet_page_id);
Setting::setValue('subscription.manage_page', $mailpoet_page_id);
Setting::setValue('subscription.confirmation_page', $mailpoet_page_id);
}
private function createDefaultSettings() {
@ -175,6 +180,14 @@ class Populator {
'name' => 'nthWeekDay',
'newsletter_type' => 'notification',
),
array(
'name' => 'schedule',
'newsletter_type' => 'notification',
),
array(
'name' => 'lastSentData',
'newsletter_type' => 'notification',
),
);
}

View File

@ -142,7 +142,7 @@ class BlankTemplate {
"blocks" => array(
array(
"type" => "footer",
"text" => "<a href=\"[unsubscribeUrl]\">Unsubscribe</a> | <a href=\"[manageSubscriptionUrl]\">Manage subscription</a><br /><b>Add your postal address here!</b>",
"text" => "<a href=\"[subscription:unsubscribe_url]\">Unsubscribe</a> | <a href=\"[subscription:manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>",
"styles" => array(
"block" => array(
"backgroundColor" => "transparent"

View File

@ -46,7 +46,7 @@ class FranksRoastHouseTemplate {
"blocks" => array(
array(
"type" => "header",
"text" => __("Display problems?&nbsp;<a href=\"[viewInBrowserUrl]\">View it in your browser</a>"),
"text" => __("Display problems?&nbsp;<a href=\"[newsletter:view_in_browser_url]\">View it in your browser</a>"),
"styles" => array(
"block" => array(
"backgroundColor" => "#ccc6c6"
@ -280,7 +280,7 @@ class FranksRoastHouseTemplate {
"blocks" => array(
array(
"type" => "footer",
"text" => __("<p><a href=\"[unsubscribeUrl]\">Unsubscribe</a> | <a href=\"[manageSubscriptionUrl]\">Manage subscription</a><br />12345 MailPoet Drive, EmailVille, 76543</p>"),
"text" => __("<p><a href=\"[subscription:unsubscribe_url]\">Unsubscribe</a> | <a href=\"[subscription:manage_url]\">Manage subscription</a><br />12345 MailPoet Drive, EmailVille, 76543</p>"),
"styles" => array(
"block" => array(
"backgroundColor" => "#a9a7a7"

View File

@ -242,7 +242,7 @@ class PostNotificationsBlankTemplate {
"blocks" => array(
array(
"type" => "footer",
"text" => __("<a href=\"[unsubscribeUrl]\">Unsubscribe</a> | <a href=\"[manageSubscriptionUrl]\">Manage subscription</a><br /><b>Add your postal address here!</b>"),
"text" => __("<a href=\"[subscription:unsubscribe_url]\">Unsubscribe</a> | <a href=\"[subscription:manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>"),
"styles" => array(
"block" => array(
"backgroundColor" => "transparent"

View File

@ -46,7 +46,7 @@ class WelcomeTemplate {
"blocks" => array(
array(
"type" => "header",
"text" => __("Display problems?&nbsp;<a href=\"[viewInBrowserUrl]\">View it in your browser</a>"),
"text" => __("Display problems?&nbsp;<a href=\"[newsletter:view_in_browser_url]\">View it in your browser</a>"),
"styles" => array(
"block" => array(
"backgroundColor" => "transparent"
@ -224,7 +224,7 @@ class WelcomeTemplate {
"blocks" => array(
array(
"type" => "footer",
"text" => __("<a href=\"[unsubscribeUrl]\">Unsubscribe</a> | <a href=\"[manageSubscriptionUrl]\">Manage subscription</a><br /><b>Add your postal address here!</b>"),
"text" => __("<a href=\"[subscription:unsubscribe_url]\">Unsubscribe</a> | <a href=\"[subscription:manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>"),
"styles" => array(
"block" => array(
"backgroundColor" => "transparent"

View File

@ -68,4 +68,11 @@ class CronHelper {
// throw an error if all connection attempts failed
throw new \Exception(__('Site URL is unreachable.'));
}
static function checkExecutionTimer($timer) {
$elapsed_time = microtime(true) - $timer;
if($elapsed_time >= self::daemon_execution_limit) {
throw new \Exception(__('Maximum execution time reached.'));
}
}
}

View File

@ -1,7 +1,9 @@
<?php
namespace MailPoet\Cron;
use MailPoet\Cron\Workers\Scheduler;
use MailPoet\Cron\Workers\SendingQueue;
use MailPoet\Models\Newsletter;
require_once(ABSPATH . 'wp-includes/pluggable.php');
@ -35,8 +37,7 @@ class Daemon {
}
$this->abortIfStopped($daemon);
try {
$sending_queue = new SendingQueue($this->timer);
$sending_queue->process();
do_action('mailpoet_cron_worker', $this->timer);
} catch(\Exception $e) {
}
$elapsed_time = microtime(true) - $this->timer;

View File

@ -0,0 +1,27 @@
<?php
namespace MailPoet\Cron\Workers;
use MailPoet\Cron\CronHelper;
use MailPoet\Models\Setting;
use MailPoet\Util\Security;
if(!defined('ABSPATH')) exit;
class Scheduler {
public $timer;
function __construct($timer = false) {
$this->timer = ($timer) ? $timer : microtime(true);
CronHelper::checkExecutionTimer($this->timer);
}
function process() {
}
function checkExecutionTimer() {
$elapsed_time = microtime(true) - $this->timer;
if($elapsed_time >= CronHelper::daemon_execution_limit) {
throw new \Exception(__('Maximum execution time reached.'));
}
}
}

View File

@ -10,6 +10,7 @@ use MailPoet\Models\Subscriber;
use MailPoet\Newsletter\Renderer\Renderer;
use MailPoet\Newsletter\Shortcodes\Shortcodes;
use MailPoet\Util\Helpers;
use MailPoet\Util\Security;
if(!defined('ABSPATH')) exit;
@ -27,6 +28,7 @@ class SendingQueue {
'processBulkSubscribers' :
'processIndividualSubscriber';
$this->timer = ($timer) ? $timer : microtime(true);
CronHelper::checkExecutionTimer($this->timer);
}
function process() {
@ -102,7 +104,7 @@ class SendingQueue {
}
$this->updateQueue($queue);
$this->checkSendingLimit();
$this->checkExecutionTimer();
CronHelper::checkExecutionTimer($this->timer);
return $queue->subscribers;
}
@ -129,7 +131,7 @@ class SendingQueue {
$this->updateNewsletterStatistics($newsletter_statistics);
}
$this->updateQueue($queue);
$this->checkExecutionTimer();
CronHelper::checkExecutionTimer($this->timer);
}
return $queue->subscribers;
}
@ -201,7 +203,9 @@ class SendingQueue {
$queue->subscribers->failed
)
);
$queue->subscribers->to_process = array_values($queue->subscribers->to_process);
$queue->subscribers->to_process = array_values(
$queue->subscribers->to_process
);
$queue->count_processed =
count($queue->subscribers->processed) + count($queue->subscribers->failed);
$queue->count_to_process = count($queue->subscribers->to_process);
@ -259,11 +263,4 @@ class SendingQueue {
}
return;
}
function checkExecutionTimer() {
$elapsed_time = microtime(true) - $this->timer;
if($elapsed_time >= CronHelper::daemon_execution_limit) {
throw new \Exception(__('Maximum execution time reached.'));
}
}
}

View File

@ -112,4 +112,17 @@ abstract class Base {
&& strlen(trim($block['params']['value'])) > 0)
? esc_attr(trim($block['params']['value'])) : '';
}
protected static function getInputModifiers($block = array()) {
$modifiers = array();
if(isset($block['params']['readonly'])) {
$modifiers[] = 'readonly';
}
if(isset($block['params']['disabled'])) {
$modifiers[] = 'disabled';
}
return join(' ', $modifiers);
}
}

View File

@ -20,18 +20,22 @@ class Checkbox extends Base {
foreach($options as $option) {
$html .= '<label class="mailpoet_checkbox_label">';
$html .= '<input type="hidden" name="'.$field_name.'" value="" />';
$html .= '<input type="checkbox" class="mailpoet_checkbox" ';
$html .= 'name="'.$field_name.'" ';
$html .= 'value="1" ';
$html .= (isset($option['is_checked']) && $option['is_checked'])
? 'checked="checked"' : '';
$html .= (
(isset($option['is_checked']) && $option['is_checked'])
||
(self::getFieldValue($block))
) ? 'checked="checked"' : '';
$html .= $field_validation;
$html .= ' />'.$option['value'];
$html .= ' /> '.esc_attr($option['value']);
$html .= '</label>';
}

View File

@ -33,18 +33,37 @@ class Date extends Base {
// generate an array of selectors based on date format
$date_selectors = explode('/', $date_format);
// format value if present
$value = self::getFieldValue($block);
$day = null;
$month = null;
$year = null;
if($value) {
$day = (int)strftime('%d', $value);
$month = (int)strftime('%m', $value);
$year = (int)strftime('%Y', $value);
} else if(!empty($block['params']['is_default_today'])) {
$day = (int)strftime('%d');
$month = (int)strftime('%m');
$year = (int)strftime('%Y');
}
foreach($date_selectors as $date_selector) {
if($date_selector === 'dd') {
$block['selected'] = $day;
$html .= '<select class="mailpoet_date_day" ';
$html .= 'name="'.$field_name.'[day]" placeholder="'.__('Day').'">';
$html .= static::getDays($block);
$html .= '</select>';
} else if($date_selector === 'mm') {
$block['selected'] = $month;
$html .= '<select class="mailpoet_date_month" ';
$html .= 'name="'.$field_name.'[month]" placeholder="'.__('Month').'">';
$html .= static::getMonths($block);
$html .= '</select>';
} else if($date_selector === 'yyyy') {
$block['selected'] = $year;
$html .= '<select class="mailpoet_date_year" ';
$html .= 'name="'.$field_name.'[year]" placeholder="'.__('Year').'">';
$html .= static::getYears($block);
@ -84,11 +103,6 @@ class Date extends Base {
'selected' => null
);
// is default today
if(!empty($block['params']['is_default_today'])) {
$defaults['selected'] = (int)strftime('%m');
}
// merge block with defaults
$block = array_merge($defaults, $block);

View File

@ -25,14 +25,24 @@ class Radio extends Base {
$html .= 'name="'.$field_name.'" ';
$html .= 'value="'.esc_attr($option['value']).'" ';
if(is_array($option['value'])) {
$value = key($option['value']);
$label = reset($option['value']);
} else {
$value = $option['value'];
$label = $option['value'];
}
$html .= 'value="'.esc_attr($value).'" ';
$html .= (
(isset($option['is_checked']) && $option['is_checked'])
||
(self::getFieldValue($block) === $value)
) ? 'checked="checked"' : '';
$html .= (isset($option['is_checked']) && $option['is_checked'])
? 'checked="checked"' : '';
$html .= $field_validation;
$html .= ' />&nbsp;'.esc_attr($option['value']);
$html .= ' /> '.esc_attr($label);
$html .= '</label>';
}

View File

@ -28,7 +28,7 @@ class Segment extends Base {
$html .= 'name="'.$field_name.'[]" ';
$html .= 'value="'.$option['id'].'" '.$is_checked.' ';
$html .= $field_validation;
$html .= ' />'.$option['name'];
$html .= ' /> '.esc_attr($option['name']);
$html .= '</label>';
}

View File

@ -10,9 +10,7 @@ class Select extends Base {
$field_validation = static::getInputValidation($block);
$html .= '<p class="mailpoet_paragraph">';
$html .= static::renderLabel($block);
$html .= '<select class="mailpoet_select" name="'.$field_name.'">';
if(isset($block['params']['label_within'])
@ -20,11 +18,28 @@ class Select extends Base {
$html .= '<option value="">'.static::getFieldLabel($block).'</option>';
}
foreach($block['params']['values'] as $option) {
$is_selected = (isset($option['is_checked']) && $option['is_checked'])
? 'selected="selected"' : '';
$html .= '<option value="'.$option['value'].'" '.$is_selected.'>';
$html .= $option['value'];
$options = (!empty($block['params']['values'])
? $block['params']['values']
: array()
);
foreach($options as $option) {
$is_selected = (
(isset($option['is_checked']) && $option['is_checked'])
||
(self::getFieldValue($block) === $option['value'])
) ? 'selected="selected"' : '';
if(is_array($option['value'])) {
$value = key($option['value']);
$label = reset($option['value']);
} else {
$value = $option['value'];
$label = $option['value'];
}
$html .= '<option value="'.$value.'" '.$is_selected.'>';
$html .= esc_attr($label);
$html .= '</option>';
}
$html .= '</select>';

View File

@ -6,7 +6,7 @@ class Text extends Base {
static function render($block) {
$type = 'text';
if($block['id'] === 'email') {
$type = 'email';
$type = 'email';
}
$html = '';
@ -27,6 +27,8 @@ class Text extends Base {
$html .= static::getInputValidation($block);
$html .= static::getInputModifiers($block);
$html .= '/>';
$html .= '</p>';

View File

@ -19,6 +19,8 @@ class Textarea extends Base {
$html .= static::getInputValidation($block);
$html .= static::getInputModifiers($block);
$html .= '></textarea>';
$html .= '</p>';

View File

@ -39,8 +39,7 @@ class Renderer {
}
}
// private: rendering methods
private static function renderBlocks($blocks = array()) {
static function renderBlocks($blocks = array()) {
$html = '';
foreach ($blocks as $key => $block) {
$html .= static::renderBlock($block)."\n";
@ -49,7 +48,7 @@ class Renderer {
return $html;
}
private static function renderBlock($block = array()) {
static function renderBlock($block = array()) {
$html = '';
switch($block['type']) {
case 'html':

View File

@ -17,7 +17,7 @@ class Styles {
}
/* labels */
.mailpoet_input_label,
.mailpoet_text_label,
.mailpoet_textarea_label,
.mailpoet_select_label,
.mailpoet_radio_label,
@ -28,7 +28,7 @@ class Styles {
}
/* inputs */
.mailpoet_input,
.mailpoet_text,
.mailpoet_textarea,
.mailpoet_select,
.mailpoet_date {
@ -36,9 +36,7 @@ class Styles {
}
.mailpoet_checkbox {
display:inline;
margin-right: 5px;
vertical-align:middle;
}
.mailpoet_validate_success {

View File

@ -93,7 +93,7 @@ class Handler {
return array(
'count' => $count,
'filters' => $this->model->filter('filters'),
'filters' => $this->model->filter('filters', $this->data['group']),
'groups' => $this->model->filter('groups'),
'items' => $items
);

View File

@ -110,7 +110,7 @@ class Model extends \Sudzy\ValidModel {
$total = $orm->count();
if($total > 0) {
$models = $orm->select('id')
$models = $orm->select(static::$_table.'.id')
->offset(null)
->limit(null)
->findArray();

View File

@ -73,16 +73,20 @@ class Newsletter extends Model {
}
function withSendingQueue() {
$this->queue = $this->getQueue();
$queue = $this->getQueue();
if($queue === false) {
$this->queue = false;
} else {
$this->queue = $queue->asArray();
}
return $this;
}
static function search($orm, $search = '') {
return $orm->where_like('subject', '%' . $search . '%');
}
static function filters() {
static function filters($orm, $group = 'all') {
$segments = Segment::orderByAsc('name')->findMany();
$segment_list = array();
$segment_list[] = array(
@ -91,7 +95,9 @@ class Newsletter extends Model {
);
foreach($segments as $segment) {
$newsletters_count = $segment->newsletters()->count();
$newsletters_count = $segment->newsletters()
->filter('groupBy', $group)
->count();
if($newsletters_count > 0) {
$segment_list[] = array(
'label' => sprintf('%s (%d)', $segment->name, $newsletters_count),

View File

@ -35,7 +35,7 @@ class Segment extends Model {
__NAMESPACE__.'\SubscriberSegment',
'segment_id',
'subscriber_id'
);
)->where(MP_SUBSCRIBER_SEGMENT_TABLE.'.status', 'subscribed');
}
function duplicate($data = array()) {
@ -186,6 +186,6 @@ class Segment extends Model {
}
static function getPublic() {
return self::getPublished()->where('type', 'default');
return self::getPublished()->where('type', 'default')->orderByAsc('name');
}
}

View File

@ -6,6 +6,13 @@ if (!defined('ABSPATH')) exit;
class Setting extends Model {
public static $_table = MP_SETTINGS_TABLE;
public static $defaults = null;
const DEFAULT_SENDING_METHOD_GROUP = 'website';
const DEFAULT_SENDING_METHOD = 'PHPMail';
const DEFAULT_SENDING_FREQUENCY_EMAILS = 25;
const DEFAULT_SENDING_FREQUENCY_INTERVAL = 15; // in minutes
function __construct() {
parent::__construct();
@ -14,8 +21,34 @@ class Setting extends Model {
));
}
public static function getDefaults() {
if(self::$defaults === null) {
self::loadDefaults();
}
return self::$defaults;
}
public static function loadDefaults() {
self::$defaults = array(
'mta_group' => self::DEFAULT_SENDING_METHOD_GROUP,
'mta' => array(
'method' => self::DEFAULT_SENDING_METHOD,
'frequency' => array(
'emails' => self::DEFAULT_SENDING_FREQUENCY_EMAILS,
'interval' => self::DEFAULT_SENDING_FREQUENCY_INTERVAL
)
),
'signup_confirmation' => array(
'enabled' => true,
'subject' => sprintf(__('Confirm your subscription to %1$s'), get_option('blogname')),
'body' => __("Hello!\n\nHurray! You've subscribed to our site.\nWe need you to activate your subscription to the list(s): [lists_to_confirm] by clicking the link below: \n\n[activation_link]Click here to confirm your subscription.[/activation_link]\n\nThank you,\n\nThe team!")
)
);
}
public static function getValue($key, $default = null) {
$keys = explode('.', $key);
$defaults = self::getDefaults();
if(count($keys) === 1) {
$setting = Setting::where('name', $key)->findOne();
@ -23,9 +56,14 @@ class Setting extends Model {
return $default;
} else {
if(is_serialized($setting->value)) {
return unserialize($setting->value);
$value = unserialize($setting->value);
} else {
return $setting->value;
$value = $setting->value;
}
if(is_array($value) && array_key_exists($key, $defaults)) {
return array_replace_recursive($defaults[$key], $value);
} else {
return $value;
}
}
} else {
@ -93,7 +131,7 @@ class Setting extends Model {
$settings[$setting->name] = $value;
}
}
return $settings;
return array_replace_recursive(self::getDefaults(), $settings);
}
public static function createOrUpdate($data = array()) {

View File

@ -1,12 +1,19 @@
<?php
namespace MailPoet\Models;
use MailPoet\Mailer\Mailer;
use MailPoet\Newsletter\Scheduler\Scheduler;
use MailPoet\Util\Helpers;
use MailPoet\Subscription;
if(!defined('ABSPATH')) exit;
class Subscriber extends Model {
public static $_table = MP_SUBSCRIBERS_TABLE;
const STATUS_SUBSCRIBED = 'subscribed';
const STATUS_UNSUBSCRIBED = 'unsubscribed';
const STATUS_UNCONFIRMED = 'unconfirmed';
function __construct() {
parent::__construct();
@ -16,13 +23,22 @@ class Subscriber extends Model {
));
}
static function findOne($id = null) {
if(is_int($id) || (string)(int)$id === $id) {
return parent::findOne($id);
} else {
return parent::where('email', $id)->findOne();
}
}
function segments() {
return $this->has_many_through(
__NAMESPACE__.'\Segment',
__NAMESPACE__.'\SubscriberSegment',
'subscriber_id',
'segment_id'
);
)
->where(MP_SUBSCRIBER_SEGMENT_TABLE.'.status', self::STATUS_SUBSCRIBED);
}
function delete() {
@ -57,9 +73,79 @@ class Subscriber extends Model {
}
function sendConfirmationEmail() {
$this->set('status', 'unconfirmed');
if($this->status === self::STATUS_UNCONFIRMED) {
$signup_confirmation = Setting::getValue('signup_confirmation');
// TODO
$segments = $this->segments()->findMany();
$segment_names = array_map(function($segment) {
return $segment->name;
}, $segments);
$body = nl2br($signup_confirmation['body']);
// replace list of segments shortcode
$body = str_replace(
'[lists_to_confirm]',
'<strong>'.join(', ', $segment_names).'</strong>',
$body
);
// replace activation link
$body = str_replace(
array(
'[activation_link]',
'[/activation_link]'
),
array(
'<a href="'.esc_attr(Subscription\Url::getConfirmationUrl($this)).'">',
'</a>'
),
$body
);
// build email data
$email = array(
'subject' => $signup_confirmation['subject'],
'body' => array(
'html' => $body,
'text' => $body
)
);
// convert subscriber to array
$subscriber = $this->asArray();
// set from
$from = (
!empty($signup_confirmation['from'])
&& !empty($signup_confirmation['from']['email'])
) ? $signup_confirmation['from']
: false;
// set reply to
$reply_to = (
!empty($signup_confirmation['reply_to'])
&& !empty($signup_confirmation['reply_to']['email'])
) ? $signup_confirmation['reply_to']
: false;
// send email
try {
$mailer = new Mailer(false, $from, $reply_to);
return $mailer->send($email, $subscriber);
} catch(\Exception $e) {
$this->setError($e->getMessage());
return false;
}
}
return false;
}
static function generateToken($email = null) {
if($email !== null) {
return md5(AUTH_KEY.$email);
}
return false;
}
static function subscribe($subscriber_data = array(), $segment_ids = array()) {
@ -68,23 +154,27 @@ class Subscriber extends Model {
}
$subscriber = self::createOrUpdate($subscriber_data);
$errors = $subscriber->getErrors();
if($errors === false && $subscriber->id > 0) {
$subscriber = self::findOne($subscriber->id);
if($subscriber !== false && $subscriber->id() > 0) {
// restore deleted subscriber
if($subscriber->deleted_at !== NULL) {
$subscriber->setExpr('deleted_at', 'NULL');
}
if((bool)Setting::getValue('signup_confirmation.enabled')) {
if($subscriber->status !== 'subscribed') {
if($subscriber->status !== self::STATUS_SUBSCRIBED) {
$subscriber->sendConfirmationEmail();
}
} else {
$subscriber->set('status', 'subscribed');
$subscriber->set('status', self::STATUS_SUBSCRIBED);
}
if($subscriber->save()) {
$subscriber->addToSegments($segment_ids);
Scheduler::welcomeForSegmentSubscription($subscriber->id, $segment_ids);
}
}
@ -102,7 +192,7 @@ class Subscriber extends Model {
);
}
static function filters() {
static function filters($orm, $group = 'all') {
$segments = Segment::orderByAsc('name')->findMany();
$segment_list = array();
$segment_list[] = array(
@ -119,8 +209,9 @@ class Subscriber extends Model {
foreach($segments as $segment) {
$subscribers_count = $segment->subscribers()
->whereNull('deleted_at')
->filter('groupBy', $group)
->count();
$segment_list[] = array(
'label' => sprintf('%s (%d)', $segment->name, $subscribers_count),
'value' => $segment->id()
@ -161,19 +252,19 @@ class Subscriber extends Model {
'count' => self::getPublished()->count()
),
array(
'name' => 'subscribed',
'name' => self::STATUS_SUBSCRIBED,
'label' => __('Subscribed'),
'count' => self::filter('subscribed')->count()
'count' => self::filter(self::STATUS_SUBSCRIBED)->count()
),
array(
'name' => 'unconfirmed',
'name' => self::STATUS_UNCONFIRMED,
'label' => __('Unconfirmed'),
'count' => self::filter('unconfirmed')->count()
'count' => self::filter(self::STATUS_UNCONFIRMED)->count()
),
array(
'name' => 'unsubscribed',
'name' => self::STATUS_UNSUBSCRIBED,
'label' => __('Unsubscribed'),
'count' => self::filter('unsubscribed')->count()
'count' => self::filter(self::STATUS_UNSUBSCRIBED)->count()
),
array(
'name' => 'trash',
@ -247,6 +338,9 @@ class Subscriber extends Model {
static function createOrUpdate($data = array()) {
$subscriber = false;
if(is_array($data) && !empty($data)) {
$data = stripslashes_deep($data);
}
if(isset($data['id']) && (int)$data['id'] > 0) {
$subscriber = self::findOne((int)$data['id']);
@ -272,16 +366,25 @@ class Subscriber extends Model {
foreach($data as $key => $value) {
if(strpos($key, 'cf_') === 0) {
if(is_array($value)) {
$value = array_filter($value);
$value = reset($value);
}
$custom_fields[(int)substr($key, 3)] = $value;
unset($data[$key]);
}
}
$old_status = false;
$new_status = false;
if($subscriber === false) {
$subscriber = self::create();
$subscriber->hydrate($data);
} else {
$old_status = $subscriber->status;
$subscriber->set($data);
$new_status = $subscriber->status;
}
if($subscriber->save()) {
@ -290,8 +393,19 @@ class Subscriber extends Model {
$subscriber->setCustomField($custom_field_id, $value);
}
}
if($segment_ids !== false) {
SubscriberSegment::setSubscriptions($subscriber, $segment_ids);
// check for status change
if(
($old_status === self::STATUS_SUBSCRIBED)
&&
($new_status === self::STATUS_UNSUBSCRIBED)
) {
// make sure we unsubscribe the user from all lists
SubscriberSegment::setSubscriptions($subscriber, array());
} else {
if($segment_ids !== false) {
SubscriberSegment::setSubscriptions($subscriber, $segment_ids);
}
}
}
return $subscriber;
@ -412,21 +526,23 @@ class Subscriber extends Model {
static function bulkConfirmUnconfirmed($orm) {
$subscribers = $orm->findResultSet();
$subscribers->set('status', 'subscribed')->save();
$subscribers->set('status', self::STATUS_SUBSCRIBED)->save();
return $subscribers->count();
}
static function bulkResendConfirmationEmail($orm) {
static function bulkSendConfirmationEmail($orm) {
$subscribers = $orm
->where('status', 'unconfirmed')
->findResultSet();
->where('status', self::STATUS_UNCONFIRMED)
->findMany();
$emails_sent = 0;
if(!empty($subscribers)) {
foreach($subscribers as $subscriber) {
$subscriber->sendConfirmationEmail();
if($subscriber->sendConfirmationEmail()) {
$emails_sent++;
}
}
return $subscribers->count();
return $emails_sent;
}
return false;
}
@ -467,19 +583,19 @@ class Subscriber extends Model {
static function subscribed($orm) {
return $orm
->whereNull('deleted_at')
->where('status', 'subscribed');
->where('status', self::STATUS_SUBSCRIBED);
}
static function unsubscribed($orm) {
return $orm
->whereNull('deleted_at')
->where('status', 'unsubscribed');
->where('status', self::STATUS_UNSUBSCRIBED);
}
static function unconfirmed($orm) {
return $orm
->whereNull('deleted_at')
->where('status', 'unconfirmed');
->where('status', self::STATUS_UNCONFIRMED);
}
static function withoutSegments($orm) {

View File

@ -16,47 +16,27 @@ class SubscriberSegment extends Model {
if($subscriber->id > 0) {
// unsubscribe from current subscriptions
SubscriberSegment::where('subscriber_id', $subscriber->id)
->whereNotIn('segment_id', $segment_ids)
->findResultSet()
->set('status', 'unsubscribed')
->set('status', Subscriber::STATUS_UNSUBSCRIBED)
->save();
// subscribe to segments
foreach($segment_ids as $segment_id) {
self::createOrUpdate(array(
'subscriber_id' => $subscriber->id,
'segment_id' => $segment_id,
'status' => 'subscribed'
));
if((int)$segment_id > 0) {
self::createOrUpdate(array(
'subscriber_id' => $subscriber->id,
'segment_id' => $segment_id,
'status' => Subscriber::STATUS_SUBSCRIBED
));
}
}
}
return $subscriber;
}
static function filterWithCustomFields($orm) {
$orm = $orm->select(MP_SUBSCRIBERS_TABLE.'.*');
$customFields = CustomField::findArray();
foreach ($customFields as $customField) {
$orm = $orm->select_expr(
'CASE WHEN ' .
MP_CUSTOM_FIELDS_TABLE . '.id=' . $customField['id'] . ' THEN ' .
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END as "' . $customField['name'].'"');
}
$orm = $orm
->left_outer_join(
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE,
array(MP_SUBSCRIBERS_TABLE.'.id', '=',
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.subscriber_id'))
->left_outer_join(
MP_CUSTOM_FIELDS_TABLE,
array(MP_CUSTOM_FIELDS_TABLE.'.id','=',
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.custom_field_id'));
return $orm;
}
static function subscribed($orm) {
return $orm->where('status', 'subscribed');
return $orm->where('status', Subscriber::STATUS_SUBSCRIBED);
}
static function createOrUpdate($data = array()) {

View File

@ -19,7 +19,7 @@ class Footer {
}
$template = '
<tr>
<td class="mailpoet_padded_header_footer mailpoet_footer" bgcolor="' . $element['styles']['block']['backgroundColor'] . '"
<td class="mailpoet_header_footer_padded mailpoet_footer" bgcolor="' . $element['styles']['block']['backgroundColor'] . '"
style="' . StylesHelper::getBlockStyles($element) . StylesHelper::getStyles($element['styles'], 'text') . '">
' . $DOM->html() . '
</td>

View File

@ -19,7 +19,7 @@ class Header {
}
$template = '
<tr>
<td class="mailpoet_padded_header_footer mailpoet_header" bgcolor="' . $element['styles']['block']['backgroundColor'] . '"
<td class="mailpoet_header_footer_padded mailpoet_header" bgcolor="' . $element['styles']['block']['backgroundColor'] . '"
style="' . StylesHelper::getBlockStyles($element) . StylesHelper::getStyles($element['styles'], 'text') . '">
' . $DOM->html() . '
</td>

View File

@ -10,7 +10,8 @@ class Renderer {
$template = ($columns_count === 1) ?
$this->getOneColumnTemplate($styles, $class) :
$this->getMultipleColumnsTemplate($styles, $width, $alignment, $class);
$result = array_map(function ($content) use ($template) {
$result = array_map(function($content) use ($template) {
$content = self::removePaddingFromLastElement($content);
return $template['content_start'] . $content . $template['content_end'];
}, $columns_data);
$result = implode('', $result);
@ -75,4 +76,8 @@ class Renderer {
</tr>';
return $template;
}
function removePaddingFromLastElement($element) {
return preg_replace('/mailpoet_padded(?!.*mailpoet_padded)/ism', '', $element);
}
}

View File

@ -43,9 +43,12 @@ class Renderer {
}
function renderBody($content) {
$content = array_map(function ($content_block) {
$content = array_map(function($content_block) {
$column_count = count($content_block['blocks']);
$column_data = $this->blocks_renderer->render($content_block, $column_count);
$column_data = $this->blocks_renderer->render(
$content_block,
$column_count
);
return $this->columns_renderer->render(
$content_block['styles'],
$column_count,

View File

@ -40,7 +40,7 @@
padding-right: 20px;
padding-bottom: 20px;
}
.mailpoet_padded_header_footer {
.mailpoet_header_footer_padded {
padding: 10px 20px;
}
@media screen and (max-width: 599px) and (-webkit-min-device-pixel-ratio: 1) {

View File

@ -0,0 +1,114 @@
<?php
namespace MailPoet\Newsletter\Scheduler;
use Carbon\Carbon;
use MailPoet\Models\Newsletter;
use MailPoet\Models\NewsletterOption;
use MailPoet\Models\NewsletterOptionField;
use MailPoet\Models\SendingQueue;
class Scheduler {
const seconds_in_hour = 3600;
const last_weekday_format = 'L';
static function postNotification($newsletter_id) {
$newsletter = Newsletter::filter('filterWithOptions')
->findOne($newsletter_id)
->asArray();
$interval_type = $newsletter['intervalType'];
$hour = (int) $newsletter['timeOfDay'] / self::seconds_in_hour;
$week_day = $newsletter['weekDay'];
$month_day = $newsletter['monthDay'];
$nth_week_day = ($newsletter['nthWeekDay'] === self::last_weekday_format) ?
$newsletter['nthWeekDay'] :
'#' . $newsletter['nthWeekDay'];
switch($interval_type) {
case 'immediately':
$cron = '* * * * *';
break;
case 'immediate': //daily
$cron = sprintf('0 %s * * *', $hour);
break;
case 'weekly':
$cron = sprintf('0 %s * * %s', $hour, $week_day);
break;
case 'monthly':
$cron = sprintf('0 %s %s * *', $hour, $month_day);
break;
case 'nthWeekDay':
$cron = sprintf('0 %s ? * %s%s', $hour, $week_day, $nth_week_day);
break;
}
$option_field = NewsletterOptionField::where('name', 'schedule')
->findOne()
->asArray();
$relation = NewsletterOption::create();
$relation->newsletter_id = $newsletter['id'];
$relation->option_field_id = $option_field['id'];
$relation->value = $cron;
$relation->save();
}
static function welcomeForSegmentSubscription($subscriber_id, array $segments) {
$newsletters = self::getWelcomeNewsletters();
if(!count($newsletters)) return;
foreach($newsletters as $newsletter) {
if($newsletter['event'] === 'segment' &&
in_array($newsletter['segment'], $segments)
) {
self::createSendingQueueEntry($newsletter, $subscriber_id);
}
}
}
static function welcomeForNewWPUser($subscriber_id, array $wp_user) {
$newsletters = self::getWelcomeNewsletters();
if(!count($newsletters)) return;
foreach($newsletters as $newsletter) {
if($newsletter['event'] === 'user' &&
in_array($newsletter['role'], $wp_user['roles'])
) {
self::createSendingQueueEntry($newsletter, $subscriber_id);
}
}
}
private static function getWelcomeNewsletters() {
return Newsletter::where('type', 'welcome')
->filter('filterWithOptions')
->findArray();
}
private static function createSendingQueueEntry($newsletter, $subscriber_id) {
$queue = SendingQueue::create();
$queue->newsletter_id = $newsletter['id'];
$queue->subscribers = serialize(
array(
'to_process' => array($subscriber_id)
)
);
$queue->count_total = $queue->count_to_process = 1;
$after_time_type = $newsletter['afterTimeType'];
$after_time_number = $newsletter['afterTimeNumber'];
$scheduled_at = null;
switch($after_time_type) {
case 'hours':
$scheduled_at = Carbon::now()
->addHours($after_time_number);
break;
case 'days':
$scheduled_at = Carbon::now()
->addDays($after_time_number);
break;
case 'weeks':
$scheduled_at = Carbon::now()
->addWeeks($after_time_number);
break;
}
if($scheduled_at) {
$queue->status = 'scheduled';
$queue->scheduled_at = $scheduled_at;
}
$queue->save();
}
}

View File

@ -1,30 +0,0 @@
<?php
namespace MailPoet\Newsletter\Shortcodes\Categories;
require_once(ABSPATH . 'wp-includes/pluggable.php');
class Link {
/*
{
text: '<%= __('Unsubscribe link') %>',
shortcode: 'global:unsubscribe',
},
{
text: '<%= __('Edit subscription page link') %>',
shortcode: 'global:manage',
},
{
text: '<%= __('View in browser link') %>',
shortcode: 'global:browser',
}
*/
static function process($action) {
// TODO: implement
$actions = array(
'unsubscribe' => '',
'manage' => '',
'browser' => ''
);
return (isset($actions[$action])) ? $actions[$action] : false;
}
}

View File

@ -18,6 +18,14 @@ class Newsletter {
{
text: '<%= __('Issue number') %>',
shortcode: 'newsletter:number',
},
{
text: '<%= __('Issue number') %>',
shortcode: 'newsletter:number',
},
{
text: '<%= __('View in browser link') %>',
shortcode: 'newsletter:view_in_browser',
}
*/
static function process($action, $default_value = false, $newsletter) {
@ -27,17 +35,34 @@ class Newsletter {
switch($action) {
case 'subject':
return ($newsletter) ? $newsletter['subject'] : false;
break;
case 'total':
$posts = wp_count_posts();
return $posts->publish;
break;
case 'post_title':
$post = wp_get_recent_posts(array('numberposts' => 1));
return (isset($post[0])) ? $post[0]['post_title'] : false;
break;
case 'number':
// TODO: implement
return;
return 1;
break;
case 'view_in_browser':
return '<a href="#TODO">'.__('View in your browser').'</a>';
break;
case 'view_in_browser_url':
return '#TODO';
break;
default:
return false;
break;
}
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace MailPoet\Newsletter\Shortcodes\Categories;
use MailPoet\Subscription\Url as SubscriptionUrl;
class Subscription {
/*
{
text: '<%= __('Unsubscribe') %>',-
shortcode: 'subscription:unsubscribe',
},
{
text: '<%= __('Manage subscriptions') %>',
shortcode: 'subscription:manage',
},
*/
static function process(
$action,
$default_value = false,
$newsletter = false,
$subscriber = false
) {
switch($action) {
case 'unsubscribe':
return '<a target="_blank" href="'.
esc_attr(SubscriptionUrl::getUnsubscribeUrl($subscriber))
.'">'.__('Unsubscribe').'</a>';
break;
case 'unsubscribe_url':
return SubscriptionUrl::getUnsubscribeUrl($subscriber);
break;
case 'manage':
return '<a target="_blank" href="'.
esc_attr(SubscriptionUrl::getManageUrl($subscriber))
.'">'.__('Manage subscription').'</a>';
break;
case 'manage_url':
return SubscriptionUrl::getManageUrl($subscriber);
break;
default:
return false;
break;
}
}
}

View File

@ -35,20 +35,31 @@ class User {
switch($action) {
case 'firstname':
return ($subscriber) ? $subscriber['first_name'] : $default_value;
break;
case 'lastname':
return ($subscriber) ? $subscriber['last_name'] : $default_value;
break;
case 'email':
return ($subscriber) ? $subscriber['email'] : false;
break;
case 'displayname':
if($subscriber && $subscriber['wp_user_id']) {
$wp_user = get_userdata($subscriber['wp_user_id']);
return $wp_user->user_login;
};
}
return $default_value;
break;
case 'count':
return Subscriber::count();
return Subscriber::filter('subscribed')->count();
break;
default:
return false;
break;
}
}
}

View File

@ -9,7 +9,8 @@ class Shortcodes {
function __construct(
$rendered_newsletter,
$newsletter = false,
$subscriber = false) {
$subscriber = false
) {
$this->rendered_newsletter = $rendered_newsletter;
$this->newsletter = $newsletter;
$this->subscriber = $subscriber;
@ -23,13 +24,12 @@ class Shortcodes {
function process($shortcodes) {
$processed_shortcodes = array_map(
function ($shortcode) {
// TODO: discuss renaming "global". It is a reserved name in PHP.
if($shortcode === 'global') $shortcode = 'link';
preg_match(
'/\[(?P<type>\w+):(?P<action>\w+)(?:.*?default:(?P<default>.*?))?\]/',
$shortcode,
$shortcode_details
);
$shortcode_class =
__NAMESPACE__ . '\\Categories\\' . ucfirst($shortcode_details['type']);
if(!class_exists($shortcode_class)) return false;
@ -41,7 +41,7 @@ class Shortcodes {
$this->subscriber
);
}, $shortcodes);
return array_filter($processed_shortcodes);
return $processed_shortcodes;
}
function replace() {

View File

@ -14,6 +14,7 @@ use MailPoet\Models\NewsletterOptionField;
use MailPoet\Models\NewsletterOption;
use MailPoet\Newsletter\Renderer\Renderer;
use MailPoet\Models\SendingQueue;
use MailPoet\Newsletter\Scheduler\Scheduler;
if(!defined('ABSPATH')) exit;
@ -265,6 +266,12 @@ class Newsletters {
}
}
}
if(!isset($data['id']) &&
isset($data['type']) &&
$data['type'] === 'notification'
) {
Scheduler::postNotification($newsletter->id);
}
return array(
'result' => true,
'newsletter' => $newsletter->asArray()

View File

@ -15,12 +15,26 @@ class Router {
);
add_action(
'wp_ajax_mailpoet',
array($this, 'setup')
array($this, 'setupAdmin')
);
add_action(
'wp_ajax_nopriv_mailpoet',
array($this, 'setupPublic')
);
}
function setup() {
$this->securityCheck();
function setupAdmin() {
$this->verifyToken();
$this->checkPermissions();
return $this->processRoute();
}
function setupPublic() {
$this->verifyToken();
return $this->processRoute();
}
function processRoute() {
$class = ucfirst($_POST['endpoint']);
$endpoint = __NAMESPACE__ . "\\" . $class;
$method = $_POST['method'];
@ -43,8 +57,11 @@ class Router {
echo $global;
}
function securityCheck() {
function checkPermissions() {
if(!current_user_can('manage_options')) { die(); }
}
function verifyToken() {
if(!wp_verify_nonce($_POST['token'], 'mailpoet_token')) { die(); }
}
}

View File

@ -15,7 +15,7 @@ class Subscribers {
function __construct() {
}
function get($id = false) {
function get($id = null) {
$subscriber = Subscriber::findOne($id);
if($subscriber !== false) {
$subscriber = $subscriber
@ -88,18 +88,10 @@ class Subscribers {
}
$subscriber = Subscriber::subscribe($data, $segment_ids);
$result = false;
if($subscriber === false || !$subscriber->id()) {
$errors = array_merge($errors, $subscriber->getValidationErrors());
} else {
$result = true;
}
if(!empty($errors)) {
if($subscriber->getErrors() !== false) {
return array(
'result' => false,
'errors' => $errors
'errors' => $subscriber->getErrors()
);
}

View File

@ -2,17 +2,15 @@
namespace MailPoet\Segments;
use \MailPoet\Models\Subscriber;
use \MailPoet\Models\Segment;
use MailPoet\Newsletter\Scheduler\Scheduler;
class WP {
static function synchronizeUser($wp_user_id) {
$wpUser = \get_userdata($wp_user_id);
$wp_user = \get_userdata($wp_user_id);
$segment = Segment::getWPUsers();
if($wpUser === false or $segment === false) return;
$subscriber = Subscriber::where('wp_user_id', $wpUser->ID)
if($wp_user === false or $segment === false) return;
$subscriber = Subscriber::where('wp_user_id', $wp_user->ID)
->findOne();
switch(current_filter()) {
case 'delete_user':
case 'deleted_user':
@ -20,23 +18,22 @@ class WP {
if($subscriber !== false && $subscriber->id()) {
$subscriber->delete();
}
break;
break;
case 'user_register':
$new_user = (!$subscriber) ? true : false;
case 'added_existing_user':
case 'profile_update':
default:
// get first name & last name
$first_name = $wpUser->first_name;
$last_name = $wpUser->last_name;
if(empty($wpUser->first_name) && empty($wpUser->last_name)) {
$first_name = $wpUser->display_name;
$first_name = $wp_user->first_name;
$last_name = $wp_user->last_name;
if(empty($wp_user->first_name) && empty($wp_user->last_name)) {
$first_name = $wp_user->display_name;
}
// subscriber data
$data = array(
'wp_user_id'=> $wpUser->ID,
'email' => $wpUser->user_email,
'wp_user_id' => $wp_user->ID,
'email' => $wp_user->user_email,
'first_name' => $first_name,
'last_name' => $last_name,
'status' => 'subscribed'
@ -46,13 +43,18 @@ class WP {
$data['id'] = $subscriber->id();
}
$subscriber = Subscriber::createOrUpdate($data);
if($subscriber->getErrors() === false && $subscriber->id > 0) {
if($segment !== false) {
$segment->addSubscriber($subscriber->id);
}
if(isset($new_user) && $new_user === true) {
Scheduler::welcomeForNewWPUser(
$subscriber->id,
(array) $wp_user
);
}
}
break;
break;
}
}

View File

@ -13,13 +13,13 @@ class Pages {
),
'public' => true,
'has_archive' => false,
'show_ui' => true,
'show_in_menu' => false,
'show_ui' => WP_DEBUG,
'show_in_menu' => WP_DEBUG,
'rewrite' => false,
'show_in_nav_menus'=>false,
'can_export'=>false,
'publicly_queryable'=>true,
'exclude_from_search'=>true
'show_in_nav_menus' => false,
'can_export' => false,
'publicly_queryable' => true,
'exclude_from_search' => true
));
}
@ -65,8 +65,9 @@ class Pages {
return array(
'id' => $page->ID,
'title' => $page->post_title,
'preview_url' => get_permalink($page->ID),
'edit_url' => get_edit_post_link($page->ID)
'preview_url' => add_query_arg(array(
'mailpoet_preview' => 1
), get_permalink($page->ID))
);
}
}

View File

@ -17,6 +17,8 @@ class BootStrapMenu {
Segment::getSegmentsWithSubscriberCount() :
Segment::getSegmentsForExport($with_confirmed_subscribers);
return array_map(function($segment) {
if (!$segment['name']) $segment['name'] = __('Not In Segment');
if (!$segment['id']) $segment['id'] = 0;
return array(
'id' => $segment['id'],
'name' => $segment['name'],
@ -31,7 +33,7 @@ class BootStrapMenu {
'first_name' => __('First name'),
'last_name' => __('Last name'),
'status' => __('Status')
// TODO: add additional fiels from MP2
// TODO: add additional fields from MP2
/*
'confirmed_ip' => __('IP address')
'confirmed_at' => __('Subscription date')

View File

@ -17,22 +17,30 @@ class Export {
public $segments;
public $subscribers_without_segment;
public $subscriber_fields;
public $subscriber_custom_fields;
public $formatted_subscriber_fields;
public $export_path;
public $export_file;
public $export_file_URL;
public $profiler_start;
public $subscriber_batch_size;
public function __construct($data) {
set_time_limit(0);
$this->export_confirmed_option = $data['export_confirmed_option'];
$this->export_format_option = $data['export_format_option'];
$this->group_by_segment_option = $data['group_by_segment_option'];
$this->segments = $data['segments'];
$this->subscribers_without_segment = array_search(0, $this->segments);
$this->subscriber_fields = $data['subscriber_fields'];
$this->subscriber_custom_fields = $this->getSubscriberCustomFields();
$this->formatted_subscriber_fields = $this->formatSubscriberFields(
$this->subscriber_fields,
$this->subscriber_custom_fields
);
$this->export_path = Env::$temp_path;
$this->export_file = $this->getExportFile($this->export_format_option);
$this->export_file_URL = $this->getExportFileURL($this->export_file);
$this->profiler_start = microtime(true);
$this->subscriber_batch_size = 15000;
}
function process() {
@ -40,75 +48,12 @@ class Export {
if(is_writable($this->export_path) === false) {
throw new \Exception(__("Couldn't save export file on the server."));
}
$subscribers = $this->getSubscribers();
$subscriber_custom_fields = $this->getSubscriberCustomFields();
$formatted_subscriber_fields = $this->formatSubscriberFields(
$this->subscriber_fields,
$subscriber_custom_fields
$processed_subscribers = call_user_func(
array(
$this,
'generate' . strtoupper($this->export_format_option)
)
);
if($this->export_format_option === 'csv') {
$CSV_file = fopen($this->export_file, 'w');
$format_CSV = function($row) {
return '"' . str_replace('"', '\"', $row) . '"';
};
// add UTF-8 BOM (3 bytes, hex EF BB BF) at the start of the file for
// Excel to automatically recognize the encoding
fwrite($CSV_file, chr(0xEF) . chr(0xBB) . chr(0xBF));
if($this->group_by_segment_option) {
$formatted_subscriber_fields[] = __('Segment');
}
fwrite(
$CSV_file,
implode(
',',
array_map(
$format_CSV,
$formatted_subscriber_fields
)
) . "\n"
);
foreach($subscribers as $subscriber) {
$row = $this->formatSubscriberData($subscriber);
if($this->group_by_segment_option) {
$row[] = ucwords($subscriber['segment_name']);
}
fwrite($CSV_file, implode(',', array_map($format_CSV, $row)) . "\n");
}
fclose($CSV_file);
} else {
$writer = new XLSXWriter();
$writer->setAuthor('MailPoet (www.mailpoet.com)');
$header_row = array($formatted_subscriber_fields);
$last_segment = false;
$rows = array();
foreach($subscribers as $subscriber) {
if($last_segment && $last_segment !== $subscriber['segment_name'] &&
$this->group_by_segment_option
) {
$writer->writeSheet(
array_merge($header_row, $rows), ucwords($last_segment)
);
$rows = array();
}
// detect RTL language and set Excel to properly display the sheet
$RTL_regex = '/\p{Arabic}|\p{Hebrew}/u';
if(!$writer->rtl && (
preg_grep($RTL_regex, $subscriber) ||
preg_grep($RTL_regex, $formatted_subscriber_fields))
) {
$writer->rtl = true;
}
$rows[] = $this->formatSubscriberData($subscriber);
$last_segment = $subscriber['segment_name'];
}
$writer->writeSheet(
array_merge($header_row, $rows),
($this->group_by_segment_option) ?
ucwords($subscriber['segment_name']) :
__('All Segments')
);
$writer->writeToFile($this->export_file);
}
} catch(\Exception $e) {
return array(
'result' => false,
@ -118,14 +63,118 @@ class Export {
return array(
'result' => true,
'data' => array(
'totalExported' => count($subscribers),
'totalExported' => $processed_subscribers,
'exportFileURL' => $this->export_file_URL
),
'profiler' => $this->timeExecution()
)
);
}
function getSubscribers() {
function generateCSV() {
$processed_subscribers = 0;
$offset = 0;
$formatted_subscriber_fields = $this->formatted_subscriber_fields;
$CSV_file = fopen($this->export_file, 'w');
$format_CSV = function($row) {
return '"' . str_replace('"', '\"', $row) . '"';
};
// add UTF-8 BOM (3 bytes, hex EF BB BF) at the start of the file for
// Excel to automatically recognize the encoding
fwrite($CSV_file, chr(0xEF) . chr(0xBB) . chr(0xBF));
if($this->group_by_segment_option) {
$formatted_subscriber_fields[] = __('Segment');
}
fwrite(
$CSV_file,
implode(
',',
array_map(
$format_CSV,
$formatted_subscriber_fields
)
) . PHP_EOL
);
do {
$subscribers = $this->getSubscribers($offset, $this->subscriber_batch_size);
$processed_subscribers += count($subscribers);
foreach($subscribers as $subscriber) {
$row = $this->formatSubscriberData($subscriber);
if($this->group_by_segment_option) {
$row[] = ucwords($subscriber['segment_name']);
}
fwrite($CSV_file, implode(',', array_map($format_CSV, $row)) . "\n");
}
$offset += $this->subscriber_batch_size;
} while(count($subscribers) === $this->subscriber_batch_size);
fclose($CSV_file);
return $processed_subscribers;
}
function generateXLSX() {
$processed_subscribers = 0;
$offset = 0;
$XLSX_writer = new XLSXWriter();
$XLSX_writer->setAuthor('MailPoet (www.mailpoet.com)');
$last_segment = false;
$processed_segments = array();
do {
$subscribers = $this->getSubscribers($offset, $this->subscriber_batch_size);
$processed_subscribers += count($subscribers);
foreach($subscribers as $i => $subscriber) {
$current_segment = ucwords($subscriber['segment_name']);
// Sheet header (1st row) will be written only if:
// * This is the first time we're processing a segment
// * "Group by subscriber option" is turned AND the previous subscriber's
// segment is different from the current subscriber's segment
// Header will NOT be written if:
// * We have already processed the segment. Because SQL results are not
// sorted by segment name (due to slow queries when using ORDER BY and LIMIT),
// we need to keep track of processed segments so that we do not create header
// multiple times when switching from one segment to another and back.
if((!count($processed_segments) ||
($last_segment !== $current_segment && $this->group_by_segment_option)
) &&
(!in_array($last_segment, $processed_segments) ||
!in_array($current_segment, $processed_segments)
)
) {
$this->writeXLSX(
$XLSX_writer,
$subscriber['segment_name'],
$this->formatted_subscriber_fields
);
$processed_segments[] = $current_segment;
}
$last_segment = ucwords($subscriber['segment_name']);
// detect RTL language and set Excel to properly display the sheet
$RTL_regex = '/\p{Arabic}|\p{Hebrew}/u';
if(!$XLSX_writer->rtl && (
preg_grep($RTL_regex, $subscriber) ||
preg_grep($RTL_regex, $this->formatted_subscriber_fields))
) {
$XLSX_writer->rtl = true;
}
$this->writeXLSX(
$XLSX_writer,
$last_segment,
$this->formatSubscriberData($subscriber)
);
}
$offset += $this->subscriber_batch_size;
} while(count($subscribers) === $this->subscriber_batch_size);
$XLSX_writer->writeToFile($this->export_file);
return $processed_subscribers;
}
function writeXLSX($XLSX_writer, $segment, $data) {
return $XLSX_writer->writeSheetRow(
($this->group_by_segment_option) ?
ucwords($segment) :
__('All Segments'),
$data
);
}
function getSubscribers($offset, $limit) {
$subscribers = Subscriber::
left_outer_join(
SubscriberSegment::$_table,
@ -141,7 +190,6 @@ class Export {
'=',
SubscriberSegment::$_table . '.segment_id'
))
->orderByAsc('segment_name')
->filter('filterWithCustomFieldsForExport');
if($this->subscribers_without_segment !== false) {
$subscribers = $subscribers
@ -168,9 +216,11 @@ class Export {
$subscribers =
$subscribers->where(Subscriber::$_table . '.status', 'subscribed');
}
$subscribers = $subscribers->whereNull(Subscriber::$_table . '.deleted_at');
return $subscribers->findArray();
$subscribers = $subscribers
->whereNull(Subscriber::$_table . '.deleted_at')
->limit(sprintf('%d, %d', $offset, $limit))
->findArray();
return $subscribers;
}
function getExportFileURL($file) {
@ -216,9 +266,4 @@ class Export {
return $subscriber[$field];
}, $this->subscriber_fields);
}
function timeExecution() {
$profiler_end = microtime(true);
return ($profiler_end - $this->profiler_start) / 60;
}
}

345
lib/Subscription/Pages.php Normal file
View File

@ -0,0 +1,345 @@
<?php
namespace MailPoet\Subscription;
use \MailPoet\Router\Subscribers;
use \MailPoet\Models\Subscriber;
use \MailPoet\Models\CustomField;
use \MailPoet\Models\Setting;
use \MailPoet\Models\Segment;
use \MailPoet\Util\Helpers;
use \MailPoet\Util\Url;
use \MailPoet\Subscription;
class Pages {
const DEMO_EMAIL = 'demo@mailpoet.com';
function __construct() {
}
function init() {
$action = $this->getAction();
if($action !== null) {
add_filter('document_title_parts', array($this,'setWindowTitle'), 10, 1);
add_filter('the_title', array($this,'setPageTitle'), 10, 1);
add_filter('the_content', array($this,'setPageContent'), 10, 1);
}
add_action(
'admin_post_mailpoet_subscriber_save',
array($this, 'subscriberSave')
);
add_action(
'admin_post_nopriv_mailpoet_subscriber_save',
array($this, 'subscriberSave')
);
}
function subscriberSave() {
$action = (isset($_POST['action']) ? $_POST['action'] : null);
if($action !== 'mailpoet_subscriber_save') {
Url::redirectBack();
}
$reserved_keywords = array('action', 'mailpoet_redirect');
$subscriber_data = array_diff_key(
$_POST,
array_flip($reserved_keywords)
);
if(isset($subscriber_data['email'])) {
if($subscriber_data['email'] !== self::DEMO_EMAIL) {
$subscriber = Subscriber::createOrUpdate($subscriber_data);
$errors = $subscriber->getErrors();
}
}
// TBD: success/error messages (not present in MP2)
Url::redirectBack();
}
function isPreview() {
return (array_key_exists('mailpoet_preview', $_GET));
}
function setWindowTitle($meta = array()) {
$meta['title'] = $this->setPageTitle($meta['title']);
return $meta;
}
function setPageTitle($page_title = '') {
global $post;
if($post->post_type === 'mailpoet_page') {
$subscriber = $this->getSubscriber();
switch($this->getAction()) {
case 'confirm':
return $this->getConfirmTitle($subscriber);
break;
case 'manage':
return $this->getManageTitle($subscriber);
break;
case 'unsubscribe':
if($subscriber !== false) {
if($subscriber->status !== Subscriber::STATUS_UNSUBSCRIBED) {
$subscriber->status = Subscriber::STATUS_UNSUBSCRIBED;
$subscriber->save();
}
}
return $this->getUnsubscribeTitle($subscriber);
break;
}
}
return $page_title;
}
function setPageContent($page_content = '[mailpoet_page]') {
$content = '';
$subscriber = $this->getSubscriber();
switch($this->getAction()) {
case 'confirm':
$content = $this->getConfirmContent($subscriber);
break;
case 'manage':
$content = $this->getManageContent($subscriber);
break;
case 'unsubscribe':
$content = $this->getUnsubscribeContent($subscriber);
break;
}
return str_replace('[mailpoet_page]', $content, $page_content);
}
private function getConfirmTitle($subscriber) {
if($this->isPreview()) {
$title = sprintf(
__("You've subscribed to: %s"),
'demo 1, demo 2'
);
} else if($subscriber === false) {
$title = __('Your confirmation link expired, please subscribe again.');
} else {
if($subscriber->status !== Subscriber::STATUS_SUBSCRIBED) {
$subscriber->status = Subscriber::STATUS_SUBSCRIBED;
$subscriber->save();
}
$segment_names = array_map(function($segment) {
return $segment->name;
}, $subscriber->segments()->findMany());
if(empty($segment_names)) {
$title = __("You've subscribed!");
} else {
$title = sprintf(
__("You've subscribed to: %s"),
join(', ', $segment_names)
);
}
}
return $title;
}
private function getManageTitle($subscriber) {
if($this->isPreview()) {
return sprintf(
__('Edit your subscriber profile: %s'),
self::DEMO_EMAIL
);
} else if($subscriber !== false) {
return sprintf(
__('Edit your subscriber profile: %s'),
$subscriber->email
);
}
}
private function getUnsubscribeTitle($subscriber) {
if($this->isPreview() || $subscriber !== false) {
return __("You've unsubscribed!");
}
}
private function getConfirmContent($subscriber) {
if($this->isPreview() || $subscriber !== false) {
return __("Yup, we've added you to our list. You'll hear from us shortly.");
}
}
private function getManageContent($subscriber) {
if($this->isPreview()) {
$subscriber = Subscriber::create();
$subscriber->hydrate(array(
'email' => self::DEMO_EMAIL
));
} else if($subscriber !== false) {
$subscriber = $subscriber
->withCustomFields()
->withSubscriptions();
} else {
return;
}
$custom_fields = array_map(function($custom_field) use($subscriber) {
$custom_field->id = 'cf_'.$custom_field->id;
$custom_field = $custom_field->asArray();
$custom_field['params']['value'] = $subscriber->{$custom_field['id']};
return $custom_field;
}, CustomField::findMany());
$segment_ids = Setting::getValue('subscription.segments', array());
if(!empty($segment_ids)) {
$segments = Segment::getPublic()
->whereIn('id', $segment_ids)
->findMany();
} else {
$segments = Segment::getPublic()
->findMany();
}
$subscribed_segment_ids = array();
if(!empty($subscriber->subscriptions)) {
foreach ($subscriber->subscriptions as $subscription) {
if($subscription['status'] === Subscriber::STATUS_SUBSCRIBED) {
$subscribed_segment_ids[] = $subscription['segment_id'];
}
}
}
$segments = array_map(function($segment) use($subscribed_segment_ids) {
return array(
'id' => $segment->id,
'name' => $segment->name,
'is_checked' => in_array($segment->id, $subscribed_segment_ids)
);
}, $segments);
$fields = array(
array(
'id' => 'email',
'type' => 'text',
'params' => array(
'label' => __('Email'),
'required' => true,
'value' => $subscriber->email,
'readonly' => true
)
),
array(
'id' => 'first_name',
'type' => 'text',
'params' => array(
'label' => __('First name'),
'value' => $subscriber->first_name
)
),
array(
'id' => 'last_name',
'type' => 'text',
'params' => array(
'label' => __('Last name'),
'value' => $subscriber->last_name
)
),
array(
'id' => 'status',
'type' => 'select',
'params' => array(
'label' => __('Status'),
'values' => array(
array(
'value' => array(
Subscriber::STATUS_SUBSCRIBED => __('Subscribed')
),
'is_checked' => (
$subscriber->status === Subscriber::STATUS_SUBSCRIBED
)
),
array(
'value' => array(
Subscriber::STATUS_UNSUBSCRIBED => __('Unsubscribed')
),
'is_checked' => (
$subscriber->status === Subscriber::STATUS_UNSUBSCRIBED
)
)
)
)
)
);
$form = array_merge(
$fields,
$custom_fields,
array(
array(
'id' => 'segments',
'type' => 'segment',
'params' => array(
'label' => __('Your lists'),
'values' => $segments
)
),
array(
'id' => 'submit',
'type' => 'submit',
'params' => array(
'label' => __('Save')
)
)
)
);
$form_html = '<form method="POST" '.
'action="'.admin_url('admin-post.php').'" '.
'novalidate>';
$form_html .= '<input type="hidden" name="action" '.
'value="mailpoet_subscriber_save" />';
$form_html .= '<input type="hidden" name="segments" value="" />';
$form_html .= '<input type="hidden" name="mailpoet_redirect" '.
'value="'.Url::getCurrentUrl().'" />';
$form_html .= \MailPoet\Form\Renderer::renderBlocks($form);
$form_html .= '</form>';
return $form_html;
}
private function getUnsubscribeContent($subscriber) {
$content = '';
if($this->isPreview() || $subscriber !== false) {
$content = '<p>'.__("Great, you'll never hear from us again!").'</p>';
if($subscriber !== false) {
$content .= '<p><strong>'.
str_replace(
array('[link]', '[/link]'),
array('<a href="'.Subscription\Url::getConfirmationUrl($subscriber).'">', '</a>'),
__('You made a mistake? [link]Undo unsubscribe.[/link]')
).
'</strong></p>';
}
}
return $content;
}
private function getSubscriber() {
$token = (isset($_GET['mailpoet_token']))
? $_GET['mailpoet_token']
: null;
$email = (isset($_GET['mailpoet_email']))
? $_GET['mailpoet_email']
: null;
if(Subscriber::generateToken($email) === $token) {
$subscriber = Subscriber::findOne($email);
if($subscriber !== false) {
return $subscriber;
}
}
return false;
}
private function getAction() {
return (isset($_GET['mailpoet_action']))
? $_GET['mailpoet_action']
: null;
}
}

57
lib/Subscription/Url.php Normal file
View File

@ -0,0 +1,57 @@
<?php
namespace MailPoet\Subscription;
use \MailPoet\Models\Subscriber;
use \MailPoet\Models\Setting;
class Url {
static function getConfirmationUrl($subscriber = false) {
$post = get_post(Setting::getValue('subscription.confirmation_page'));
return self::getSubscriptionUrl($post, 'confirm', $subscriber);
}
static function getManageUrl($subscriber = false) {
$post = get_post(Setting::getValue('subscription.manage_page'));
return self::getSubscriptionUrl($post, 'manage', $subscriber);
}
static function getUnsubscribeUrl($subscriber = false) {
$post = get_post(Setting::getValue('subscription.unsubscribe_page'));
return self::getSubscriptionUrl($post, 'unsubscribe', $subscriber);
}
private static function getSubscriptionUrl(
$post = null, $action = null, $subscriber = false
) {
if($post === null || $action === null) return;
$url = get_permalink($post);
if($subscriber !== false) {
if(is_object($subscriber)) {
$subscriber = $subscriber->asArray();
}
$params = array(
'mailpoet_action='.$action,
'mailpoet_token='.Subscriber::generateToken($subscriber['email']),
'mailpoet_email='.$subscriber['email']
);
} else {
$params = array(
'mailpoet_action='.$action,
'mailpoet_preview=1'
);
}
// add parameters
$url .= (parse_url($url, PHP_URL_QUERY) ? '&' : '?').join('&', $params);
$url_params = parse_url($url);
if(empty($url_params['scheme'])) {
$url = get_bloginfo('url').$url;
}
return $url;
}
}

62
lib/Util/Url.php Normal file
View File

@ -0,0 +1,62 @@
<?php
namespace MailPoet\Util;
class Url {
function __construct() {
}
static function getCurrentUrl() {
global $wp;
return home_url(
add_query_arg(
$wp->query_string,
$wp->request
)
);
}
static function redirectTo($url = null) {
wp_safe_redirect($url);
exit();
}
static function redirectBack() {
// check mailpoet_redirect parameter
$referer = (isset($_POST['mailpoet_redirect'])
? $_POST['mailpoet_redirect']
: null
);
// fallback: http referer
if($referer === null) {
if(!empty($_SERVER['HTTP_REFERER'])) {
$referer = $_SERVER['HTTP_REFERER'];
}
}
// fallback: home_url
if($referer === null) {
$referer = home_url();
}
if($referer !== null) {
self::redirectTo($referer);
}
exit();
}
static function redirectWithReferer($url = null) {
$current_url = self::getCurrentUrl();
$url = add_query_arg(
array(
'mailpoet_redirect' => urlencode($current_url)
),
$url
);
if($url !== $current_url) {
self::redirectTo($url);
}
exit();
}
}

View File

@ -4,7 +4,7 @@ if(!defined('ABSPATH')) exit;
use \MailPoet\Config\Initializer;
/*
* Plugin Name: MailPoet
* Version: 0.0.18
* Version: 0.0.20
* 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.18');
define('MAILPOET_VERSION', '0.0.20');
$initializer = new Initializer(array(
'file' => __FILE__,

View File

@ -34,7 +34,8 @@
"spectrum-colorpicker": "^1.6.2",
"tinymce": "4.1.10",
"underscore": "1.8.3",
"velocity-animate": "1.2.3"
"velocity-animate": "1.2.3",
"xss": "^0.2.10"
},
"devDependencies": {
"expose-loader": "latest",
@ -45,7 +46,7 @@
"amd-inject-loader": "latest",
"chai": "2.2.0",
"chai-jq": "0.0.8",
"grunt": "^0.4.5",
"grunt-cli": "latest",
"jquery": "2.1.4",
"jsdom": "3.1.2",
"mocha": "2.2.1",

View File

@ -60,6 +60,9 @@ module.exports = function (grunt) {
}
});
grunt.loadNpmTasks('grunt-shell');
grunt.loadNpmTasks( 'grunt-wp-i18n' );
// set base
grunt.file.setBase(base_path);
@ -77,4 +80,4 @@ module.exports = function (grunt) {
grunt.registerTask('pushpot', ['shell:txpush']);
grunt.registerTask('update', ['makepot', 'shell:txpush']);
}
};
};

View File

@ -178,9 +178,10 @@ class StringExtractor {
* - line - line number
*/
public function find_function_calls( $function_names, $code, $extension = '.php') {
$function_calls = array();
if($extension === 'php') {
$tokens = token_get_all( $code );
$function_calls = array();
$latest_comment = false;
$in_func = false;
foreach( $tokens as $token ) {

View File

@ -13,6 +13,8 @@ $models = array(
'Newsletter',
'NewsletterSegment',
'NewsletterTemplate',
'NewsletterOption',
'NewsletterOptionField',
'Segment',
'SendingQueue',
'Setting',
@ -29,3 +31,60 @@ $destroy = function ($model) {
$db->commit();
};
array_map($destroy, $models);
abstract class MailPoetTest extends \Codeception\TestCase\Test {
protected $backupGlobals = true;
protected $backupGlobalsBlacklist = array(
'app',
'post',
'authordata',
'currentday',
'currentmonth',
'page',
'pages',
'multipage',
'more',
'numpages',
'is_iphone',
'is_chrome',
'is_safari',
'is_NS4',
'is_opera',
'is_macIE',
'is_winIE',
'is_gecko',
'is_lynx',
'is_IE',
'is_apache',
'is_IIS',
'is_iis7',
'wp_version',
'wp_db_version',
'tinymce_version',
'manifest_version',
'required_php_version',
'required_mysql_version',
'super_admins',
'wp_query',
'wp_rewrite',
'wp',
'wpdb',
'wp_locale',
'wp_admin_bar',
'wp_roles',
'wp_meta_boxes',
'wp_registered_sidebars',
'wp_registered_widgets',
'wp_registered_widget_controls',
'wp_registered_widget_updates',
'pagenow',
'post_type',
'allowedposttags',
'allowedtags',
'menu'
);
protected $backupStaticAttributes = false;
protected $runTestInSeparateProcess = false;
protected $preserveGlobalState = false;
protected $inIsolation = false;
}

View File

@ -1,34 +1,34 @@
<?php
use MailPoet\Config\Env;
class EnvCest {
class EnvTest extends MailPoetTest {
function _before() {
Env::init('file', '1.0.0');
}
function itCanReturnPluginPrefix() {
function testItCanReturnPluginPrefix() {
expect(Env::$plugin_prefix)->equals('mailpoet_');
}
function itCanReturnDbPrefix() {
function testItCanReturnDbPrefix() {
global $wpdb;
$db_prefix = $wpdb->prefix . 'mailpoet_';
expect(Env::$db_prefix)->equals($db_prefix);
}
function itCanReturnDbHost() {
function testItCanReturnDbHost() {
if(preg_match('/(?=:\d+$)/', DB_HOST)) {
expect(Env::$db_host)->equals(explode(':', DB_HOST)[0]);
} else expect(Env::$db_host)->equals(DB_HOST);
}
function itCanReturnDbPort() {
function testItCanReturnDbPort() {
if(preg_match('/(?=:\d+$)/', DB_HOST)) {
expect(Env::$db_port)->equals(explode(':', DB_HOST)[1]);
} else expect(Env::$db_port)->equals(3306);
}
function itCanReturnSocket() {
function testItCanReturnSocket() {
if(!preg_match('/(?=:\d+$)/', DB_HOST)
&& preg_match('/:/', DB_HOST)
) {
@ -36,25 +36,25 @@ class EnvCest {
} else expect(Env::$db_socket)->false();
}
function itCanReturnDbName() {
function testItCanReturnDbName() {
expect(Env::$db_name)->equals(DB_NAME);
}
function itCanReturnDbUser() {
function testItCanReturnDbUser() {
expect(Env::$db_username)->equals(DB_USER);
}
function itCanReturnDbPassword() {
function testItCanReturnDbPassword() {
expect(Env::$db_password)->equals(DB_PASSWORD);
}
function itCanReturnDbCharset() {
function testItCanReturnDbCharset() {
global $wpdb;
$charset = $wpdb->get_charset_collate();
expect(Env::$db_charset)->equals($charset);
}
function itCanGenerateDbSourceName() {
function testItCanGenerateDbSourceName() {
$source_name = ((!ENV::$db_socket) ? 'mysql:host=' : 'mysql:unix_socket=') .
ENV::$db_host . ';port=' . ENV::$db_port . ';dbname=' . DB_NAME;
expect(Env::$db_source_name)->equals($source_name);

View File

@ -1,12 +1,12 @@
<?php
use \MailPoet\Config\Migrator;
class MigratorCest {
class MigratorTest extends MailPoetTest {
function _before() {
$this->migrator = new Migrator();
}
function itCanGenerateTheSubscribersSql() {
function testItCanGenerateTheSubscribersSql() {
$subscriber_sql = $this->migrator->subscribers();
$expected_table = $this->migrator->prefix . 'subscribers';
expect($subscriber_sql)->contains($expected_table);

View File

@ -1,12 +1,12 @@
<?php
use \MailPoet\Config\Renderer;
class RendererCest {
class RendererTest extends MailPoetTest {
function _before() {
$this->renderer = new Renderer();
}
function itWillNotEnableCacheWhenWpDebugIsOn() {
function testItWillNotEnableCacheWhenWpDebugIsOn() {
$result = $this->renderer->detectCache();
expect($result)->equals(false);
}

View File

@ -1,58 +0,0 @@
<?php
use MailPoet\Listing;
use MailPoet\Models\Subscriber;
class ListingCest {
function _before() {
}
function itShouldReturnListingData() {
$listing = new Listing\Handler(
'\MailPoet\Models\Subscriber',
array()
);
$result = $listing->get();
expect(array_key_exists('items', $result))->equals(true);
expect(array_key_exists('count', $result))->equals(true);
expect(array_key_exists('filters', $result))->equals(true);
expect(array_key_exists('groups', $result))->equals(true);
}
function itShouldGroup(UnitTester $I) {
$I->generateSubscribers(1);
$I->generateSubscribers(2, array('status' => 'unsubscribed'));
$I->generateSubscribers(3, array('status' => 'subscribed'));
$listing = new Listing\Handler(
'\MailPoet\Models\Subscriber',
array('group' => 'subscribed')
);
$result = $listing->get();
expect($result['groups'])->notEmpty();
expect($result['count'])->equals(3);
}
function itShouldSearch(UnitTester $I) {
$I->generateSubscriber(array(
'email' => 'j.d@mailpoet.com'
));
$listing = new Listing\Handler(
'\MailPoet\Models\Subscriber',
array(
'search' => 'j.d'
)
);
$result = $listing->get();
expect($result['count'])->equals(1);
}
function _after() {
Subscriber::deleteMany();
}
}

View File

@ -1,7 +1,7 @@
<?php
use MailPoet\Mailer\Mailer;
class MailerCest {
class MailerTest extends MailPoetTest {
function _before() {
$this->sender = array(
'name' => 'Sender',
@ -25,7 +25,7 @@ class MailerCest {
);
}
function itRequiresMailerMethod() {
function testItRequiresMailerMethod() {
try {
$mailer = new Mailer();
} catch (Exception $e) {
@ -33,7 +33,7 @@ class MailerCest {
}
}
function itRequiresSender() {
function testItRequiresSender() {
try {
$mailer = new Mailer($mailer = $this->mailer);
} catch (Exception $e) {
@ -41,7 +41,7 @@ class MailerCest {
}
}
function itCanConstruct() {
function testItCanConstruct() {
$mailer = new Mailer($this->mailer, $this->sender, $this->reply_to);
expect($mailer->sender['from_name'])->equals($this->sender['name']);
expect($mailer->sender['from_email'])->equals($this->sender['address']);
@ -49,13 +49,13 @@ class MailerCest {
expect($mailer->reply_to['reply_to_email'])->equals($this->reply_to['address']);
}
function itCanBuildMailerInstance() {
function testItCanBuildMailerInstance() {
$mailer = new Mailer($this->mailer, $this->sender);
expect(get_class($mailer->mailer_instance))
->equals('MailPoet\Mailer\Methods\MailPoet');
}
function itCanAbortWhenMethodDoesNotExist() {
function testItCanAbortWhenMethodDoesNotExist() {
try {
$mailer = new Mailer(array('method' => 'test'), $this->sender);
} catch (Exception $e) {
@ -63,7 +63,7 @@ class MailerCest {
}
}
function itCanTransformSubscriber() {
function testItCanTransformSubscriber() {
$mailer = new Mailer($this->mailer, $this->sender, $this->reply_to);
expect($mailer->transformSubscriber('test@email.com'))
->equals('test@email.com');
@ -93,7 +93,7 @@ class MailerCest {
)->equals('First Last <test@email.com>');
}
function itCanSend() {
function testItCanSend() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$mailer = new Mailer($this->mailer, $this->sender, $this->reply_to);
expect($mailer->send($this->newsletter, $this->subscriber))->true();

View File

@ -2,7 +2,7 @@
use MailPoet\Mailer\Methods\AmazonSES;
class AmazonSESCest {
class AmazonSESTest extends MailPoetTest {
function _before() {
$this->settings = array(
'method' => 'AmazonSES',
@ -43,7 +43,7 @@ class AmazonSESCest {
);
}
function itsConstructorWorks() {
function testItsConstructorWorks() {
expect($this->mailer->aws_endpoint)
->equals(
sprintf('email.%s.amazonaws.com', $this->settings['region'])
@ -56,7 +56,7 @@ class AmazonSESCest {
expect(preg_match('!^\d{8}$!', $this->mailer->date_without_time))->equals(1);
}
function itCanGenerateBody() {
function testItCanGenerateBody() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($body['Action'])->equals('SendEmail');
expect($body['Version'])->equals('2010-12-01');
@ -74,7 +74,7 @@ class AmazonSESCest {
expect($body['ReturnPath'])->equals($this->sender['from_name_email']);
}
function itCanCreateRequest() {
function testItCanCreateRequest() {
$request = $this->mailer->request($this->newsletter, $this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($request['timeout'])->equals(10);
@ -87,7 +87,7 @@ class AmazonSESCest {
expect($request['body'])->equals(urldecode(http_build_query($body)));
}
function itCanCreateCanonicalRequest() {
function testItCanCreateCanonicalRequest() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$canonicalRequest = explode(
"\n",
@ -110,7 +110,7 @@ class AmazonSESCest {
);
}
function itCanCreateCredentialScope() {
function testItCanCreateCredentialScope() {
$credentialScope = $this->mailer->getCredentialScope();
expect($credentialScope)
->equals(
@ -121,7 +121,7 @@ class AmazonSESCest {
);
}
function itCanCreateStringToSign() {
function testItCanCreateStringToSign() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$credentialScope = $this->mailer->getCredentialScope();
$canonicalRequest = $this->mailer->getCanonicalRequest($body);
@ -141,7 +141,7 @@ class AmazonSESCest {
);
}
function itCanSignRequest() {
function testItCanSignRequest() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$signedRequest = $this->mailer->signRequest($body);
expect($signedRequest)
@ -155,7 +155,7 @@ class AmazonSESCest {
->equals(1);
}
function itCannotSendWithoutProperAccessKey() {
function testItCannotSendWithoutProperAccessKey() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$this->mailer->aws_access_key = 'somekey';
$result = $this->mailer->send(
@ -165,7 +165,7 @@ class AmazonSESCest {
expect($result)->false();
}
function itCanSend() {
function testItCanSend() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$result = $this->mailer->send(
$this->newsletter,

View File

@ -2,7 +2,7 @@
use MailPoet\Mailer\Methods\ElasticEmail;
class ElasticEmailCest {
class ElasticEmailTest extends MailPoetTest {
function _before() {
$this->settings = array(
'method' => 'ElasticEmail',
@ -35,7 +35,7 @@ class ElasticEmailCest {
);
}
function itCanGenerateBody() {
function testItCanGenerateBody() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($body['api_key'])->equals($this->settings['api_key']);
expect($body['from'])->equals($this->sender['from_email']);
@ -48,7 +48,7 @@ class ElasticEmailCest {
expect($body['body_text'])->equals($this->newsletter['body']['text']);
}
function itCanCreateRequest() {
function testItCanCreateRequest() {
$request = $this->mailer->request($this->newsletter, $this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($request['timeout'])->equals(10);
@ -57,7 +57,7 @@ class ElasticEmailCest {
expect($request['body'])->equals(urldecode(http_build_query($body)));
}
function itCannotSendWithoutProperApiKey() {
function testItCannotSendWithoutProperApiKey() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$this->mailer->api_key = 'someapi';
$result = $this->mailer->send(
@ -67,7 +67,7 @@ class ElasticEmailCest {
expect($result)->false();
}
function itCanSend() {
function testItCanSend() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$result = $this->mailer->send(
$this->newsletter,

View File

@ -2,7 +2,7 @@
use MailPoet\Mailer\Methods\MailGun;
class MailGunCest {
class MailGunTest extends MailPoetTest {
function _before() {
$this->settings = array(
'method' => 'MailGun',
@ -39,7 +39,7 @@ class MailGunCest {
);
}
function itCanGenerateBody() {
function testItCanGenerateBody() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($body['from'])->equals($this->sender['from_name_email']);
expect($body['h:Reply-To'])->equals($this->reply_to['reply_to_name_email']);
@ -49,12 +49,12 @@ class MailGunCest {
expect($body['text'])->equals($this->newsletter['body']['text']);
}
function itCanDoBasicAuth() {
function testItCanDoBasicAuth() {
expect($this->mailer->auth())
->equals('Basic ' . base64_encode('api:' . $this->settings['api_key']));
}
function itCanCreateRequest() {
function testItCanCreateRequest() {
$request = $this->mailer->request($this->newsletter, $this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($request['timeout'])->equals(10);
@ -67,7 +67,7 @@ class MailGunCest {
expect($request['body'])->equals(urldecode(http_build_query($body)));
}
function itCannotSendWithoutProperApiKey() {
function testItCannotSendWithoutProperApiKey() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$this->mailer->api_key = 'someapi';
$result = $this->mailer->send(
@ -77,7 +77,7 @@ class MailGunCest {
expect($result)->false();
}
function itCannotSendWithoutProperDomain() {
function testItCannotSendWithoutProperDomain() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$this->mailer->url =
str_replace($this->settings['domain'], 'somedomain', $this->mailer->url);
@ -88,7 +88,7 @@ class MailGunCest {
expect($result)->false();
}
function itCanSend() {
function testItCanSend() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$result = $this->mailer->send(
$this->newsletter,

View File

@ -2,7 +2,7 @@
use MailPoet\Mailer\Methods\MailPoet;
class MailPoetCest {
class MailPoetAPITest extends MailPoetTest {
function _before() {
$this->settings = array(
'method' => 'MailPoet',
@ -35,7 +35,7 @@ class MailPoetCest {
);
}
function itCanGenerateBodyForSingleMessage() {
function testItCanGenerateBodyForSingleMessage() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$subscriber = $this->mailer->processSubscriber($this->subscriber);
expect($body[0]['to']['address'])->equals($subscriber['email']);
@ -49,7 +49,7 @@ class MailPoetCest {
expect($body[0]['text'])->equals($this->newsletter['body']['text']);
}
function itCanGenerateBodyForMultipleMessages() {
function testItCanGenerateBodyForMultipleMessages() {
$newsletters = array_fill(0, 10, $this->newsletter);
$subscribers = array_fill(0, 10, $this->subscriber);
$body = $this->mailer->getBody($newsletters, $subscribers);
@ -66,7 +66,7 @@ class MailPoetCest {
expect($body[0]['text'])->equals($this->newsletter['body']['text']);
}
function itCanCreateRequest() {
function testItCanCreateRequest() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$request = $this->mailer->request($body);
expect($request['timeout'])->equals(10);
@ -77,7 +77,7 @@ class MailPoetCest {
expect($request['body'])->equals(json_encode($body));
}
function itCanProcessSubscriber() {
function testItCanProcessSubscriber() {
expect($this->mailer->processSubscriber('test@test.com'))
->equals(
array(
@ -98,12 +98,12 @@ class MailPoetCest {
));
}
function itCanDoBasicAuth() {
function testItCanDoBasicAuth() {
expect($this->mailer->auth())
->equals('Basic ' . base64_encode('api:' . $this->settings['api_key']));
}
function itCannotSendWithoutProperApiKey() {
function testItCannotSendWithoutProperApiKey() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$this->mailer->api_key = 'someapi';
$result = $this->mailer->send(
@ -113,7 +113,7 @@ class MailPoetCest {
expect($result)->false();
}
function itCanSend() {
function testItCanSend() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$result = $this->mailer->send(
$this->newsletter,

View File

@ -2,7 +2,7 @@
use MailPoet\Mailer\Methods\PHPMail;
class PHPMailCest {
class PHPMailTest extends MailPoetTest {
function _before() {
$this->sender = array(
'from_name' => 'Sender',
@ -28,12 +28,12 @@ class PHPMailCest {
);
}
function itCanBuildMailer() {
function testItCanBuildMailer() {
$mailer = $this->mailer->buildMailer();
expect($mailer->getTransport() instanceof \Swift_MailTransport)->true();
}
function itCanCreateMessage() {
function testItCanCreateMessage() {
$message = $this->mailer->createMessage($this->newsletter, $this->subscriber);
expect($message->getTo())
->equals(array('mailpoet-phoenix-test@mailinator.com' => 'Recipient'));
@ -49,7 +49,7 @@ class PHPMailCest {
->equals('text/plain');
}
function itCanProcessSubscriber() {
function testItCanProcessSubscriber() {
expect($this->mailer->processSubscriber('test@test.com'))
->equals(array('test@test.com' => ''));
expect($this->mailer->processSubscriber('First <test@test.com>'))
@ -58,7 +58,7 @@ class PHPMailCest {
->equals(array('test@test.com' => 'First Last'));
}
function itCanSend() {
function testItCanSend() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$result = $this->mailer->send(
$this->newsletter,

View File

@ -2,7 +2,7 @@
use MailPoet\Mailer\Methods\SMTP;
class SMTPCest {
class SMTPTest extends MailPoetTest {
function _before() {
$this->settings = array(
'method' => 'SMTP',
@ -49,7 +49,7 @@ class SMTPCest {
);
}
function itCanBuildMailer() {
function testItCanBuildMailer() {
$mailer = $this->mailer->buildMailer();
expect($mailer->getTransport()->getHost())
->equals($this->settings['host']);
@ -63,7 +63,7 @@ class SMTPCest {
->equals($this->settings['encryption']);
}
function itCanCreateMessage() {
function testItCanCreateMessage() {
$message = $this->mailer->createMessage($this->newsletter, $this->subscriber);
expect($message->getTo())
->equals(array('mailpoet-phoenix-test@mailinator.com' => 'Recipient'));
@ -79,7 +79,7 @@ class SMTPCest {
->equals('text/plain');
}
function itCanProcessSubscriber() {
function testItCanProcessSubscriber() {
expect($this->mailer->processSubscriber('test@test.com'))
->equals(array('test@test.com' => ''));
expect($this->mailer->processSubscriber('First <test@test.com>'))
@ -88,7 +88,7 @@ class SMTPCest {
->equals(array('test@test.com' => 'First Last'));
}
function itCantSentWithoutProperAuthentication() {
function testItCantSentWithoutProperAuthentication() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$this->mailer->login = 'someone';
$this->mailer->mailer = $this->mailer->buildMailer();
@ -99,7 +99,7 @@ class SMTPCest {
expect($result)->false();
}
function itCanSend() {
function testItCanSend() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$result = $this->mailer->send(
$this->newsletter,

View File

@ -2,7 +2,7 @@
use MailPoet\Mailer\Methods\SendGrid;
class SendGridCest {
class SendGridTest extends MailPoetTest {
function _before() {
$this->settings = array(
'method' => 'SendGrid',
@ -35,7 +35,7 @@ class SendGridCest {
);
}
function itCanGenerateBody() {
function testItCanGenerateBody() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($body['to'])->contains($this->subscriber);
expect($body['from'])->equals($this->sender['from_email']);
@ -46,7 +46,7 @@ class SendGridCest {
expect($body['text'])->equals($this->newsletter['body']['text']);
}
function itCanCreateRequest() {
function testItCanCreateRequest() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$request = $this->mailer->request($this->newsletter, $this->subscriber);
expect($request['timeout'])->equals(10);
@ -57,12 +57,12 @@ class SendGridCest {
expect($request['body'])->equals(http_build_query($body));
}
function itCanDoBasicAuth() {
function testItCanDoBasicAuth() {
expect($this->mailer->auth())
->equals('Bearer ' . $this->settings['api_key']);
}
function itCannotSendWithoutProperApiKey() {
function testItCannotSendWithoutProperApiKey() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$this->mailer->api_key = 'someapi';
$result = $this->mailer->send(
@ -72,7 +72,7 @@ class SendGridCest {
expect($result)->false();
}
function itCanSend() {
function testItCanSend() {
if(getenv('WP_TEST_MAILER_ENABLE_SENDING') !== 'true') return;
$result = $this->mailer->send(
$this->newsletter,

View File

@ -4,7 +4,7 @@ use MailPoet\Models\CustomField;
use MailPoet\Models\Subscriber;
use MailPoet\Models\SubscriberCustomField;
class CustomFieldCest {
class CustomFieldTest extends MailPoetTest {
function _before() {
$this->data = array(
'name' => 'City',
@ -29,30 +29,30 @@ class CustomFieldCest {
);
}
function itCanBeCreated() {
function testItCanBeCreated() {
expect($this->custom_field->id() > 0)->true();
expect($this->custom_field->getErrors())->false();
}
function itHasAName() {
function testItHasAName() {
expect($this->custom_field->name)->equals($this->data['name']);
}
function itHasAType() {
function testItHasAType() {
expect($this->custom_field->type)->equals($this->data['type']);
}
function itHasSerializedParams() {
function testItHasSerializedParams() {
$params = unserialize($this->custom_field->params);
expect($params)->equals($this->data['params']);
}
function itCanDecodeParams() {
function testItCanDecodeParams() {
$custom_field = $this->custom_field->asArray();
expect($custom_field['params'])->equals($this->data['params']);
}
function itHasToBeValid() {
function testItHasToBeValid() {
$invalid_custom_field = CustomField::create();
$result = $invalid_custom_field->save();
@ -63,19 +63,19 @@ class CustomFieldCest {
expect($errors[1])->equals('You need to specify a type.');
}
function itHasACreatedAtOnCreation() {
function testItHasACreatedAtOnCreation() {
$custom_field = CustomField::findOne($this->custom_field->id);
expect($custom_field->created_at)->notNull();
expect($custom_field->created_at)->notEquals('0000-00-00 00:00:00');
}
function itHasAnUpdatedAtOnCreation() {
function testItHasAnUpdatedAtOnCreation() {
$custom_field = CustomField::findOne($this->custom_field->id);
expect($custom_field->updated_at)
->equals($custom_field->created_at);
}
function itUpdatesTheUpdatedAtOnUpdate() {
function testItUpdatesTheUpdatedAtOnUpdate() {
$custom_field = CustomField::findOne($this->custom_field->id);
$created_at = $custom_field->created_at;
@ -92,7 +92,7 @@ class CustomFieldCest {
expect($is_time_updated)->true();
}
function itCanHaveManySubscribers() {
function testItCanHaveManySubscribers() {
foreach($this->subscribers as $subscriber) {
$subscriber = Subscriber::createOrUpdate($subscriber);
@ -106,7 +106,7 @@ class CustomFieldCest {
expect(count($subscribers))->equals(2);
}
function itCanHaveAValue() {
function testItCanHaveAValue() {
$subscriber = Subscriber::createOrUpdate($this->subscribers[0]);
$association = SubscriberCustomField::create();

View File

@ -1,19 +1,19 @@
<?php
use MailPoet\Models\Form;
class FormCest {
class FormTest extends MailPoetTest {
function _before() {
$this->form = Form::createOrUpdate(array(
'name' => 'My Form'
));
}
function itCanBeCreated() {
function testItCanBeCreated() {
expect($this->form->id() > 0)->true();
expect($this->form->getErrors())->false();
}
function itHasToBeValid() {
function testItHasToBeValid() {
$invalid_form = Form::create();
$result = $invalid_form->save();
$errors = $result->getErrors();
@ -22,7 +22,7 @@ class FormCest {
expect($errors[0])->equals('You need to specify a name.');
}
function itCanBeGrouped() {
function testItCanBeGrouped() {
$forms = Form::filter('groupBy', 'all')->findArray();
expect($forms)->count(1);
@ -41,24 +41,24 @@ class FormCest {
expect($forms)->count(1);
}
function itCanBeSearched() {
function testItCanBeSearched() {
$form = Form::filter('search', 'my F')->findOne();
expect($form->name)->equals('My Form');
}
function itHasACreatedAtOnCreation() {
function testItHasACreatedAtOnCreation() {
$form = Form::findOne($this->form->id);
expect($form->created_at)->notNull();
expect($form->created_at)->notEquals('0000-00-00 00:00:00');
}
function itHasAnUpdatedAtOnCreation() {
function testItHasAnUpdatedAtOnCreation() {
$form = Form::findOne($this->form->id);
expect($form->updated_at)
->equals($form->created_at);
}
function itUpdatesTheUpdatedAtOnUpdate() {
function testItUpdatesTheUpdatedAtOnUpdate() {
$form = Form::findOne($this->form->id);
$created_at = $form->created_at;
@ -75,7 +75,7 @@ class FormCest {
expect($is_time_updated)->true();
}
function itCanCreateOrUpdate() {
function testItCanCreateOrUpdate() {
$created_form = Form::createOrUpdate(array(
'name' => 'Created Form'
));

View File

@ -3,16 +3,19 @@
use MailPoet\Models\Newsletter;
use MailPoet\Models\NewsletterOption;
use MailPoet\Models\NewsletterOptionField;
use MailPoet\Config\Populator;
class NewsletterOptionFieldCest {
class NewsletterOptionFieldTest extends MailPoetTest {
function _before() {
$this->data = array(
'name' => 'Event',
'name' => 'event',
'newsletter_type' => 'welcome'
);
$option = NewsletterOptionField::create();
$option->hydrate($this->data);
$this->option_field = $option->save();
$option->save();
$this->option_field = NewsletterOptionField::findOne($option->id);
$this->newsletter_data = array(
array(
@ -30,21 +33,21 @@ class NewsletterOptionFieldCest {
);
}
function itCanBeCreated() {
expect($this->option_field->id() > 0)->true();
function testItCanBeCreated() {
expect($this->option_field->id() > 0)->equals(true);
expect($this->option_field->getErrors())->false();
}
function itHasName() {
function testItHasName() {
expect($this->option_field->name)->equals($this->data['name']);
}
function itHasNewsletterType() {
function testItHasNewsletterType() {
expect($this->option_field->newsletter_type)
->equals($this->data['newsletter_type']);
}
function itHasToBeValid() {
function testItHasToBeValid() {
$invalid_newsletter_option = NewsletterOptionField::create();
$result = $invalid_newsletter_option->save();
$errors = $result->getErrors();
@ -54,19 +57,18 @@ class NewsletterOptionFieldCest {
expect($errors[1])->equals('You need to specify a newsletter type.');
}
function itHasACreatedAtOnCreation() {
$option_field = NewsletterOptionField::findOne($this->option_field->id);
expect($option_field->created_at)->notNull();
expect($option_field->created_at)->notEquals('0000-00-00 00:00:00');
function testItHasACreatedAtOnCreation() {
expect($this->option_field->created_at)->notNull();
expect($this->option_field->created_at)->notEquals('0000-00-00 00:00:00');
}
function itHasAnUpdatedAtOnCreation() {
function testItHasAnUpdatedAtOnCreation() {
$option_field = NewsletterOptionField::findOne($this->option_field->id);
expect($option_field->updated_at)
->equals($option_field->created_at);
}
function itUpdatesTheUpdatedAtOnUpdate() {
function testItUpdatesTheUpdatedAtOnUpdate() {
$option_field = NewsletterOptionField::findOne($this->option_field->id);
$created_at = $option_field->created_at;
@ -82,7 +84,7 @@ class NewsletterOptionFieldCest {
expect($is_time_updated)->true();
}
function itCanHaveManyNewsletters() {
function testItCanHaveManyNewsletters() {
foreach($this->newsletter_data as $data) {
$newsletter = Newsletter::create();
$newsletter->hydrate($data);
@ -98,7 +100,7 @@ class NewsletterOptionFieldCest {
expect(count($newsletters))->equals(2);
}
function itCanStoreOptionValue() {
function testItCanStoreOptionValue() {
$newsletter = Newsletter::create();
$newsletter->hydrate($this->newsletter_data[0]);
$newsletter->save();

View File

@ -2,7 +2,7 @@
use MailPoet\Models\NewsletterTemplate;
class NewsletterTemplateCest {
class NewsletterTemplateTest extends MailPoetTest {
function _before() {
$this->data = array(
'name' => 'Some template',
@ -15,12 +15,12 @@ class NewsletterTemplateCest {
$this->saved = $template->save();
}
function itCanBeCreated() {
function testItCanBeCreated() {
expect($this->saved->id() > 0)->true();
expect($this->saved->getErrors())->false();
}
function itHasToBeValid() {
function testItHasToBeValid() {
$invalid_newsletter_template = NewsletterTemplate::create();
$result = $invalid_newsletter_template->save();
$errors = $result->getErrors();
@ -30,25 +30,25 @@ class NewsletterTemplateCest {
expect($errors[1])->equals('Template body cannot be empty.');
}
function itHasName() {
function testItHasName() {
$template = NewsletterTemplate::where('name', $this->data['name'])
->findOne();
expect($template->name)->equals($this->data['name']);
}
function itHasDescription() {
function testItHasDescription() {
$template = NewsletterTemplate::where('description', $this->data['description'])
->findOne();
expect($template->description)->equals($this->data['description']);
}
function itHasBody() {
function testItHasBody() {
$template = NewsletterTemplate::where('body', $this->data['body'])
->findOne();
expect($template->body)->equals($this->data['body']);
}
function itCanCreateOrUpdate() {
function testItCanCreateOrUpdate() {
$created_template = NewsletterTemplate::createOrUpdate(
array(
'name' => 'Another template',

View File

@ -7,7 +7,7 @@ use MailPoet\Models\NewsletterSegment;
use MailPoet\Models\NewsletterOptionField;
use MailPoet\Models\NewsletterOption;
class NewsletterCest {
class NewsletterTest extends MailPoetTest {
function _before() {
$this->newsletter = Newsletter::createOrUpdate(array(
'subject' => 'My Standard Newsletter',
@ -32,44 +32,44 @@ class NewsletterCest {
$association->save();
}
function itCanBeCreated() {
function testItCanBeCreated() {
expect($this->newsletter->id() > 0)->true();
expect($this->newsletter->getErrors())->false();
}
function itHasASubject() {
function testItHasASubject() {
$newsletter = Newsletter::findOne($this->newsletter->id);
expect($newsletter->subject)->equals($this->newsletter->subject);
}
function itHasAType() {
function testItHasAType() {
$newsletter = Newsletter::findOne($this->newsletter->id);
expect($newsletter->type)->equals($this->newsletter->type);
}
function itHasABody() {
function testItHasABody() {
$newsletter = Newsletter::findOne($this->newsletter->id);
expect($newsletter->body)->equals($this->newsletter->body);
}
function itHasPreheader() {
function testItHasPreheader() {
$newsletter = Newsletter::findOne($this->newsletter->id);
expect($newsletter->preheader)->equals($this->newsletter->preheader);
}
function itHasACreatedAtOnCreation() {
function testItHasACreatedAtOnCreation() {
$newsletter = Newsletter::findOne($this->newsletter->id);
expect($newsletter->created_at)->notNull();
expect($newsletter->created_at)->notEquals('0000-00-00 00:00:00');
}
function itHasAnUpdatedAtOnCreation() {
function testItHasAnUpdatedAtOnCreation() {
$newsletter = Newsletter::findOne($this->newsletter->id);
expect($newsletter->updated_at)
->equals($newsletter->created_at);
}
function itUpdatesTheUpdatedAtOnUpdate() {
function testItUpdatesTheUpdatedAtOnUpdate() {
$newsletter = Newsletter::findOne($this->newsletter->id);
$created_at = $newsletter->created_at;
@ -86,7 +86,7 @@ class NewsletterCest {
expect($is_time_updated)->true();
}
function itCanBeQueued() {
function testItCanBeQueued() {
$queue = $this->newsletter->getQueue();
expect($queue)->false();
@ -98,7 +98,7 @@ class NewsletterCest {
expect($queue->id() > 0)->true();
}
function itCanHaveSegments() {
function testItCanHaveSegments() {
$newsletter_segments = $this->newsletter->segments()->findArray();
expect($newsletter_segments)->count(2);
expect($newsletter_segments[0]['id'])->equals($this->segment_1->id);
@ -107,7 +107,7 @@ class NewsletterCest {
expect($newsletter_segments[1]['name'])->equals('Segment 2');
}
function itCanCreateOrUpdate() {
function testItCanCreateOrUpdate() {
$is_created = Newsletter::createOrUpdate(
array(
'subject' => 'new newsletter',
@ -130,13 +130,13 @@ class NewsletterCest {
expect($newsletter->subject)->equals('updated newsletter');
}
function itCannotSetAnEmptyDeletedAt() {
function testItCannotSetAnEmptyDeletedAt() {
$this->newsletter->deleted_at = '';
$newsletter = $this->newsletter->save();
expect($newsletter->deleted_at)->equals('NULL');
}
function itCanBeFilteredBySegment() {
function testItCanBeFilteredBySegment() {
// no filter
$newsletters = Newsletter::filter('filterBy')->findArray();
expect($newsletters)->count(1);
@ -159,7 +159,7 @@ class NewsletterCest {
expect($newsletters)->isEmpty();
}
function itCanBeGrouped() {
function testItCanBeGrouped() {
$newsletters = Newsletter::filter('groupBy', 'all')->findArray();
expect($newsletters)->count(1);
@ -178,7 +178,7 @@ class NewsletterCest {
expect($newsletters)->count(1);
}
function itHasSearchFilter() {
function testItHasSearchFilter() {
Newsletter::createOrUpdate(
array(
'subject' => 'search for "pineapple"',
@ -190,7 +190,7 @@ class NewsletterCest {
expect($newsletter->subject)->contains('pineapple');
}
function itCanHaveOptions() {
function testItCanHaveOptions() {
$newsletter_options = array(
'name' => 'Event',
'newsletter_type' => 'welcome',
@ -208,7 +208,7 @@ class NewsletterCest {
expect($newsletter->Event)->equals($association->value);
}
function itCanFilterOptions() {
function testItCanFilterOptions() {
$newsletter_options = array(
array(
'name' => 'Event',

View File

@ -6,7 +6,7 @@ use MailPoet\Models\Segment;
use MailPoet\Models\Subscriber;
use MailPoet\Models\SubscriberSegment;
class SegmentCest {
class SegmentTest extends MailPoetTest {
function _before() {
$this->data = array(
'name' => 'some name',
@ -40,12 +40,12 @@ class SegmentCest {
);
}
function itCanBeCreated() {
function testItCanBeCreated() {
expect($this->segment->id() > 0)->true();
expect($this->segment->getErrors())->false();
}
function itCanHaveName() {
function testItCanHaveName() {
expect($this->segment->name)->equals($this->data['name']);
}
@ -61,11 +61,11 @@ class SegmentCest {
);
}
function itCanHaveDescription() {
function testItCanHaveDescription() {
expect($this->segment->description)->equals($this->data['description']);
}
function itHasToBeValid() {
function testItHasToBeValid() {
$invalid_segment = Segment::create();
$result = $invalid_segment->save();
@ -75,19 +75,19 @@ class SegmentCest {
expect($errors[0])->equals('You need to specify a name.');
}
function itHasACreatedAtOnCreation() {
function testItHasACreatedAtOnCreation() {
$segment = Segment::findOne($this->segment->id);
expect($segment->created_at)->notNull();
expect($segment->created_at)->notEquals('0000-00-00 00:00:00');
}
function itHasAnUpdatedAtOnCreation() {
function testItHasAnUpdatedAtOnCreation() {
$segment = Segment::findOne($this->segment->id);
expect($segment->updated_at)
->equals($segment->created_at);
}
function itUpdatesTheUpdatedAtOnUpdate() {
function testItUpdatesTheUpdatedAtOnUpdate() {
$segment = Segment::findOne($this->segment->id);
$created_at = $segment->created_at;
@ -104,7 +104,7 @@ class SegmentCest {
expect($is_time_updated)->true();
}
function itCanCreateOrUpdate() {
function testItCanCreateOrUpdate() {
$is_created = Segment::createOrUpdate(array(
'name' => 'new list'
));
@ -125,7 +125,7 @@ class SegmentCest {
expect($segment->name)->equals('updated list');
}
function itCanHaveManySubscribers() {
function testItCanHaveManySubscribers() {
foreach($this->subscribers_data as $subscriber_data) {
$subscriber = Subscriber::create();
$subscriber->hydrate($subscriber_data);
@ -142,7 +142,7 @@ class SegmentCest {
expect(count($subscribers))->equals(2);
}
function itCanHaveManyNewsletters() {
function testItCanHaveManyNewsletters() {
foreach($this->newsletters_data as $newsletter_data) {
$newsletter = Newsletter::create();
$newsletter->hydrate($newsletter_data);
@ -159,7 +159,7 @@ class SegmentCest {
expect(count($newsletters))->equals(2);
}
function itCanGetSegmentsWithSubscriberCount() {
function testItCanGetSegmentsWithSubscriberCount() {
foreach($this->subscribers_data as $subscriber_data) {
$subscriber = Subscriber::create();
$subscriber->hydrate($subscriber_data);
@ -173,7 +173,7 @@ class SegmentCest {
expect($segment[0]['subscribers'])->equals(2);
}
function itCanGetSegmentsForExport() {
function testItCanGetSegmentsForExport() {
foreach($this->subscribers_data as $index => $subscriber_data) {
$subscriber = Subscriber::create();
$subscriber->hydrate($subscriber_data);

View File

@ -1,8 +1,8 @@
<?php
use MailPoet\Models\Setting;
class SettingCest {
function itCanBeCreated() {
class SettingTest extends MailPoetTest {
function testItCanBeCreated() {
$setting = Setting::createOrUpdate(array(
'name' => 'key',
'value' => 'val'
@ -11,7 +11,7 @@ class SettingCest {
expect($setting->getErrors())->false();
}
function itHasToBeValid() {
function testItHasToBeValid() {
$invalid_setting = Setting::createOrUpdate();
$errors = $invalid_setting->getErrors();
@ -19,7 +19,13 @@ class SettingCest {
expect($errors[0])->equals('You need to specify a name.');
}
function itCanGetAllSettings() {
function testItHasDefaultSettings() {
$default_settings = Setting::getDefaults();
expect($default_settings)->notEmpty();
expect($default_settings['signup_confirmation']['enabled'])->true();
}
function testItCanGetAllSettingsIncludingDefaults() {
Setting::setValue('key_1', 'value_1');
Setting::setValue('key_2', 'value_2');
Setting::setValue('key_3', array(
@ -28,16 +34,20 @@ class SettingCest {
));
$settings = Setting::getAll();
expect(array_keys($settings))->count(3);
expect($settings['key_1'])->equals('value_1');
expect($settings['key_2'])->equals('value_2');
expect($settings['key_3'])->equals(array(
'subkey_1' => 'subvalue_1',
'subkey_2' => 'subvalue_2'
));
// default settings
$default_settings = Setting::getDefaults();
expect($settings['signup_confirmation'])
->equals($default_settings['signup_confirmation']);
}
function itReturnsDefaultValueIfNotSet() {
function testItReturnsDefaultValueIfNotSet() {
// try to get an "unknown" key
$setting = Setting::getValue('unknown_key', 'default_value');
expect($setting)->equals('default_value');
@ -59,7 +69,7 @@ class SettingCest {
expect($setting)->equals('default_value');
}
function itCanCreateOrUpdate() {
function testItCanCreateOrUpdate() {
$data = array(
'name' => 'new',
'value' => 'data'
@ -81,12 +91,12 @@ class SettingCest {
expect($setting->value)->equals('new data');
}
function itCanGetAndSetValue() {
function testItCanGetAndSetValue() {
expect(Setting::setValue('test', '123'))->true();
expect(Setting::getValue('test'))->equals('123');
}
function itCanGetAndSetNestedValue() {
function testItCanGetAndSetNestedValue() {
expect(Setting::setValue('test.key', '123'))->true();
expect(Setting::getValue('test.key'))->equals('123');
@ -98,7 +108,7 @@ class SettingCest {
expect(Setting::getValue('test.key.subkey2'))->equals('456');
}
function itCanSetValueToNull() {
function testItCanSetValueToNull() {
expect(Setting::setValue('test.key', true))->true();
expect(Setting::getValue('test.key'))->equals(true);

Some files were not shown because too many files have changed in this diff Show More