Compare commits

...

40 Commits

Author SHA1 Message Date
ffc1d0a61c Set MailPoet version to 0.0.10 for release 2016-01-08 19:38:57 +02:00
d1b160def7 Merge pull request #286 from mailpoet/cron_update
Cron update
2016-01-08 19:23:50 +02:00
493fd01754 Merge pull request #285 from mailpoet/import_export_fix
Import export fix
2016-01-08 19:21:56 +02:00
9ced4b1757 Fix export test after temp URL changed 2016-01-08 19:21:32 +02:00
17010e5ba9 Merge pull request #281 from mailpoet/rendering_engine_update
Rendering engine update
2016-01-08 19:00:12 +02:00
42ad7584d4 - Refactors ColumnsHelper class 2016-01-08 11:35:30 -05:00
dbc0f9b238 - Removes header padding 2016-01-08 11:23:02 -05:00
e62e9a5892 - Fixes issue with temp folder
- Updates formatting
2016-01-08 10:55:09 -05:00
bc25fa61b4 - Updates render method
- Removes unused/commented out code
2016-01-08 10:38:46 -05:00
2590967183 - Formats new line identation
- Formats identations in general
2016-01-08 09:00:09 -05:00
86eafd3c17 - Removes tabs 2016-01-08 08:47:41 -05:00
90a6f160c2 - Removes space after function 2016-01-08 07:53:26 -05:00
c774aec6a2 Revert "- Fixes minor issue when daemon has not yet been created"
This reverts commit 8f2fd1d76e.
2016-01-08 07:49:15 -05:00
8f2fd1d76e - Fixes minor issue when daemon has not yet been created 2016-01-08 07:40:32 -05:00
4df11163a1 automatically update 'updated_at' when saving daemon 2016-01-08 12:23:15 +01:00
82a736ffbb Cron update + removing console.log
- use Setting::getValue for getDaemon method
- added "updated_at" property within cron_daemon value (instead of using the setting's column)
- converted line endings to Unix in notice.js and removed console.log
2016-01-08 12:02:11 +01:00
87052986e8 - Rebases master
- Updates template
2016-01-07 23:53:15 -05:00
0c73c0fadc - Resolves issues identified by @rafaehlers during testing 2016-01-07 22:47:59 -05:00
5c7e11076d - WIP on updating import 2016-01-07 18:11:59 -05:00
d1df94c759 - Addresses issues identified during code review 2016-01-07 17:24:46 -05:00
53cc39c6f5 - Removes spacer from social icon elements 2016-01-07 17:24:45 -05:00
4955c72ee1 - Removes transparent background from divider element 2016-01-07 17:24:44 -05:00
16661af8c3 - Updates text alignemnt for buttons 2016-01-07 17:24:44 -05:00
bc80f69e41 - Updates the template 2016-01-07 17:24:43 -05:00
0192934e65 - Removes debug leftovers 2016-01-07 17:24:42 -05:00
2793e74858 - Rewrites the rendering engine
- Updates tests
Closes #280
2016-01-07 17:24:32 -05:00
5996696cc9 Merge pull request #284 from mailpoet/animations
Animations
2016-01-07 17:31:17 +01:00
7f6cf5bbf3 Remove obsolete clear divs 2016-01-07 18:28:37 +02:00
c0ef2254cd Merge pull request #283 from mailpoet/queue_refactor
Queue refactoring
2016-01-07 13:11:47 +01:00
0dbe04c3f8 - Addresses issues identified during code review 2016-01-06 19:19:06 -05:00
ef1805d9b5 Change obsolete "Arial Black" fonts to "Arial", add Velocity to tests 2016-01-06 16:43:39 +02:00
514f539e83 Remove obsolete Velocity dependence due to it being injected elsewhere 2016-01-06 15:58:14 +02:00
50f072705e Fix double animations when ALC block changes, remove console.log lines 2016-01-06 12:48:45 +02:00
f8f7bc3d3d Handle sidebar animations with Velocity, fix delete button transitions 2016-01-06 12:29:32 +02:00
f1bf2bb097 - Refactors Mailer class
- Refactors SendingQueue worker class
- Adds Maier router with a send() method + ability to specify sending method
- Updates tests
- Introduces 'stopping' and 'starting' cron states
- Improves cron control mechanism
Closes #276
2016-01-05 10:34:57 -05:00
bbe2f69a7f Clean up unused and speed up animations, fix sidebar transitions 2016-01-05 17:32:59 +02:00
c844488b0b Switch to VelocityJS for view transitions, slow down some transitions 2016-01-05 15:01:30 +02:00
112fe0cd6e Merge pull request #282 from mailpoet/sticky_kit
Fix sticky-kit dependency usage
2016-01-04 16:25:59 +01:00
c9e6dce785 Rename vendor_static/ to vendor/ 2016-01-04 17:02:46 +02:00
d1c09c015a Remove remote sticky-kit dep, use static local patched package instead 2016-01-04 13:19:40 +02:00
85 changed files with 4631 additions and 4487 deletions

2
.gitignore vendored
View File

@ -1,7 +1,7 @@
.DS_Store
TODO
composer.phar
vendor
/vendor
tests/_output/*
tests/acceptance.suite.yml
tests/_support/_generated/*

View File

@ -43,7 +43,7 @@
bottom: 0
background-color: rgba(255, 255, 255, 0.0)
opacity: 0
transition: all 200ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
transition: all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
&:hover
background-color: rgba(255, 255, 255, 0.7)

View File

@ -90,7 +90,7 @@ body.mailpoet_modal_opened
padding: 0
margin: 0
width: 100%
transition: margin 0.3s ease-out
transition: margin 250ms ease-out
.mailpoet_panel_wrapper
background-color: #f1f1f1
@ -200,4 +200,4 @@ body.mailpoet_modal_opened
0%
50%
background-color: #064E6D
100%
100%

View File

@ -85,7 +85,6 @@ $layer-selector-width = 30px
max-width: 100%
display: inline-block
opacity: 1
animation-fade-in-and-scale-horizontally()
.mailpoet_delete_block_confirm,
.mailpoet_delete_block_cancel
@ -113,6 +112,9 @@ $layer-selector-width = 30px
max-width: 100%
opacity: 1
.mailpoet_delete_block_cancel
margin-left: 3px
.mailpoet_delete_block_confirm
color: $warning-text-color

View File

@ -52,7 +52,7 @@ $draggable-widget-z-index = 2
padding: 0
margin: 0
z-index: $draggable-widget-z-index
animation-fade-in-and-scale-up()
animation-fade-in()
.mailpoet_widget_icon
padding: 0

View File

@ -26,13 +26,9 @@ $widget-icon-width = 30px
border-right: 0
&.closed .mailpoet_region_content
max-height: 0px
overflow: hidden
margin-top: 0
display: none
.mailpoet_region_content
max-height: 2000px
transition: max-height 300ms ease
padding: 0 20px
margin-top: 12px

View File

@ -30,8 +30,3 @@ $block-hover-highlight-color = $primary-active-color
.mailpoet_content
position: relative
.mailpoet_block_transition_in
animation-fade-in-and-scale-up()
.mailpoet_block_transition_out
animation-fade-out-and-scale-down()

View File

@ -1,5 +1,5 @@
animation-slide-open-downwards()
transition: all 300ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
transition: all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
max-height: 2000px
opacity: 1
@ -9,45 +9,22 @@ animation-slide-open-downwards()
overflow-y: hidden
animation-background-color()
transition: background 300ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
transition: background 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
animation-fade-in-and-scale-up()
animation-name: fadeInAndScaleUp
animation-duration: 500ms
animation-fill-mode: forwards
animation-fade-out-and-scale-down()
animation-name: fadeOutAndScaleDown
animation-duration: 500ms
animation-fade-in()
animation-name: fadeIn
animation-duration: 250ms
animation-fill-mode: forwards
animation-fade-in-and-scale-horizontally()
transition: all 300ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
transition: all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
@keyframes fadeInAndScaleUp {
@keyframes fadeIn {
0% {
opacity: 0.3
max-height: 0
overflow: hidden
}
100% {
opacity: 1
max-height: 5000px
overflow: hidden
}
}
@keyframes fadeOutAndScaleDown {
0% {
opacity: 1
max-height: 5000px
overflow: hidden
}
100% {
opacity: 0.3
max-height: 0
overflow: hidden
}
}

View File

@ -15,10 +15,10 @@ define(
status: 'loading'
};
},
getDaemonData: function() {
getCronData: function() {
MailPoet.Ajax.post({
endpoint: 'cron',
action: 'getDaemonStatus'
action: 'getStatus'
})
.done(function(response) {
jQuery('.button-primary')
@ -32,25 +32,23 @@ define(
},
componentDidMount: function() {
if(this.isMounted()) {
this.getDaemonData();
setInterval(this.getDaemonData, 5000);
this.getCronData();
setInterval(this.getCronData, 5000);
}
},
controlDaemon: function(action) {
controlCron: function(action) {
if(jQuery('.button-primary').hasClass('disabled')) {
return;
}
jQuery('.button-primary')
.addClass('disabled');
MailPoet.Ajax.post({
endpoint: 'cron',
action: 'controlDaemon',
data: {
'action': action
}
action: action,
})
.done(function(response) {
if(!response.result) {
//this.replaceState();
} else {
//this.setState(response);
MailPoet.Notice.error(MailPoetI18n.daemonControlError);
}
}.bind(this));
},
@ -71,19 +69,25 @@ define(
<strong> {this.state.counter} </strong> times (once every 30 seconds, unless it was interrupted and restarted).
<br />
<br />
<a href="#" className="button-primary" onClick={this.controlDaemon.bind(null, 'stop')}>Stop</a>&nbsp;&nbsp;
<a href="#" className="button-primary" onClick={this.controlDaemon.bind(null, 'pause')}>Pause</a>
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'stop')}>Stop</a>
</div>
);
break;
case 'starting':
case 'stopping':
return(
<div>
Daemon is {this.state.status}
</div>
);
break;
case 'paused':
case 'stopped':
return(
<div>
Daemon is {this.state.status}
<br />
<br />
<a href="#" className="button-primary" onClick={this.controlDaemon.bind(null, 'start')}>Start</a>
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'start')}>Start</a>
</div>
);
break;

View File

@ -70,8 +70,8 @@ define([
fetchPosts: function() {
var that = this;
CommunicationComponent.getTransformedPosts(this.toJSON()).done(function(content) {
console.log('ALC fetched', arguments);
that.get('_container').get('blocks').reset(content, {parse: true});
that.trigger('postsChanged');
}).fail(function(error) {
console.log('ALC fetchPosts error', arguments);
});
@ -100,6 +100,11 @@ define([
toolsRegion: '.mailpoet_tools',
postsRegion: '.mailpoet_automated_latest_content_block_posts',
},
modelEvents: _.extend(
_.omit(base.BlockView.prototype.modelEvents, 'change'),
{
'postsChanged': 'render',
}),
events: _.extend(base.BlockView.prototype.events, {
'click .mailpoet_automated_latest_content_block_overlay': 'showSettings',
}),

View File

@ -128,24 +128,37 @@ define([
}
},
deleteBlock: function() {
this.transitionOut().done(function() {
this.transitionOut().then(function() {
this.model.destroy();
}.bind(this));
},
transitionIn: function() {
return this._transition('mailpoet_block_transition_in');
return this._transition('slideDown', 'fadeIn', 'easeIn');
},
transitionOut: function() {
return this._transition('mailpoet_block_transition_out');
return this._transition('slideUp', 'fadeOut', 'easeOut');
},
_transition: function(className) {
_transition: function(slideDirection, fadeDirection, easing) {
var promise = jQuery.Deferred();
this.$el.addClass(className);
this.$el.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd animationend', function() {
this.$el.removeClass(className);
promise.resolve();
}.bind(this));
this.$el.velocity(
slideDirection,
{
duration: 250,
easing: easing,
complete: function() {
promise.resolve();
}.bind(this),
}
).velocity(
fadeDirection,
{
duration: 250,
easing: easing,
queue: false, // Do not enqueue, trigger animation in parallel
}
);
return promise;
},
});

View File

@ -248,20 +248,32 @@ define([
}.bind(this));
},
transitionIn: function() {
return this._transition('mailpoet_block_transition_in');
return this._transition('slideDown', 'fadeIn', 'easeIn');
},
transitionOut: function() {
return this._transition('mailpoet_block_transition_out');
return this._transition('slideUp', 'fadeOut', 'easeOut');
},
_transition: function(className) {
var that = this,
promise = jQuery.Deferred();
_transition: function(slideDirection, fadeDirection, easing) {
var promise = jQuery.Deferred();
this.$el.velocity(
slideDirection,
{
duration: 250,
easing: easing,
complete: function() {
promise.resolve();
}.bind(this),
}
).velocity(
fadeDirection,
{
duration: 250,
easing: easing,
queue: false, // Do not enqueue, trigger animation in parallel
}
);
this.$el.addClass(className);
this.$el.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd animationend', function() {
that.$el.removeClass(className);
promise.resolve();
});
return promise;
},
});

View File

@ -97,7 +97,6 @@ define([
fetchAvailablePosts: function() {
var that = this;
CommunicationComponent.getPosts(this.toJSON()).done(function(posts) {
console.log('Posts fetched', arguments);
that.get('_availablePosts').reset(posts);
that.get('_selectedPosts').reset(); // Empty out the collection
that.trigger('change:_availablePosts');
@ -117,7 +116,6 @@ define([
}
CommunicationComponent.getTransformedPosts(data).done(function(posts) {
console.log('Transformed posts fetched', arguments);
that.get('_transformedPosts').get('blocks').reset(posts, {parse: true});
}).fail(function() {
console.log('Posts _refreshTransformedPosts error', arguments);
@ -134,7 +132,6 @@ define([
if (data.posts.length === 0) return;
CommunicationComponent.getTransformedPosts(data).done(function(posts) {
console.log('Available posts fetched', arguments);
collection.add(posts, { at: index });
}).fail(function() {
console.log('Posts fetchPosts error', arguments);

View File

@ -208,20 +208,32 @@ define([
}.bind(this));
},
transitionIn: function() {
return this._transition('mailpoet_block_transition_in');
return this._transition('slideDown', 'fadeIn', 'easeIn');
},
transitionOut: function() {
return this._transition('mailpoet_block_transition_out');
return this._transition('slideUp', 'fadeOut', 'easeOut');
},
_transition: function(className) {
var that = this,
promise = jQuery.Deferred();
_transition: function(slideDirection, fadeDirection, easing) {
var promise = jQuery.Deferred();
this.$el.velocity(
slideDirection,
{
duration: 250,
easing: easing,
complete: function() {
promise.resolve();
}.bind(this),
}
).velocity(
fadeDirection,
{
duration: 250,
easing: easing,
queue: false, // Do not enqueue, trigger animation in parallel
}
);
this.$el.addClass(className);
this.$el.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd animationend', function() {
that.$el.removeClass(className);
promise.resolve();
});
return promise;
},
});

View File

@ -51,8 +51,33 @@ define([
},
events: {
'click .mailpoet_sidebar_region h3, .mailpoet_sidebar_region .handlediv': function(event) {
this.$el.find('.mailpoet_sidebar_region').addClass('closed');
this.$el.find(event.target).parent().parent().removeClass('closed');
var $openRegion = this.$el.find('.mailpoet_sidebar_region:not(.closed)'),
$targetRegion = this.$el.find(event.target).closest('.mailpoet_sidebar_region');
if ($openRegion.get(0) === $targetRegion.get(0)) {
return;
}
$openRegion.find('.mailpoet_region_content').velocity(
'slideUp',
{
duration: 250,
easing: "easeOut",
complete: function() {
$openRegion.addClass('closed');
}.bind(this)
}
);
$targetRegion.find('.mailpoet_region_content').velocity(
'slideDown',
{
duration: 250,
easing: "easeIn",
complete: function() {
$targetRegion.removeClass('closed');
},
}
);
},
},
initialize: function(options) {

View File

@ -17,7 +17,7 @@ define([
},
h1: {
fontColor: '#111111',
fontFamily: 'Arial Black',
fontFamily: 'Arial',
fontSize: '40px'
},
h2: {

View File

@ -16,7 +16,7 @@ define(
Breadcrumb
) {
var settings = window.mailpoet_settings || {};
var settings = window.mailpoet_settings || {};
var fields = [
{
@ -24,14 +24,17 @@ define(
label: 'Subject line',
tip: "Be creative! It's the first thing your subscribers see."+
"Tempt them to open your email.",
type: 'text'
type: 'text',
validation: {
'data-parsley-required': true
}
},
{
name: 'segments',
label: 'Lists',
tip: "The subscriber list that will be used for this campaign.",
label: 'Segments',
tip: "The subscriber segment that will be used for this campaign.",
type: 'selection',
placeholder: "Select a list",
placeholder: "Select a segment",
id: "mailpoet_segments",
endpoint: "segments",
multiple: true,
@ -111,12 +114,19 @@ define(
action: 'add',
data: {
newsletter_id: this.props.params.id,
segments: jQuery('#mailpoet_segments').val()
segments: jQuery('#mailpoet_segments').val(),
sender: {
'name': jQuery('#mailpoet_newsletter [name="sender_name"]').val(),
'address': jQuery('#mailpoet_newsletter [name="sender_address"]').val()
},
reply_to: {
'name': jQuery('#mailpoet_newsletter [name="reply_to_name"]').val(),
'address': jQuery('#mailpoet_newsletter [name="reply_to_address"]').val()
}
}
}).done(function(response) {
if(response.result === true) {
this.history.pushState(null, '/');
MailPoet.Notice.success(
'The newsletter is being sent...'
);

View File

@ -1,75 +1,75 @@
define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
"use strict";
/*==================================================================================================
MailPoet Notice:
description: Handles notices
version: 0.2
author: Jonathan Labreuille
company: Wysija
dependencies: jQuery
Usage:
// success message (static: false)
MailPoet.Notice.success('Yatta!');
// error message (static: false)
MailPoet.Notice.error('Boo!');
// system message (static: true)
MailPoet.Notice.system('You need to updated ASAP!');
Examples:
MailPoet.Notice.success('- success #1 -');
setTimeout(function() {
MailPoet.Notice.success('- success #2 -');
setTimeout(function() {
MailPoet.Notice.error('- error -');
setTimeout(function() {
MailPoet.Notice.system('- system -');
setTimeout(function() {
MailPoet.Notice.hide();
}, 2500);
}, 300);
}, 400);
}, 500);
==================================================================================================*/
MailPoet.Notice = {
version: 0.2,
// default options
defaults: {
type: 'success',
message: '',
static: false,
hideClose: false,
id: null,
define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
"use strict";
/*==================================================================================================
MailPoet Notice:
description: Handles notices
version: 0.2
author: Jonathan Labreuille
company: Wysija
dependencies: jQuery
Usage:
// success message (static: false)
MailPoet.Notice.success('Yatta!');
// error message (static: false)
MailPoet.Notice.error('Boo!');
// system message (static: true)
MailPoet.Notice.system('You need to updated ASAP!');
Examples:
MailPoet.Notice.success('- success #1 -');
setTimeout(function() {
MailPoet.Notice.success('- success #2 -');
setTimeout(function() {
MailPoet.Notice.error('- error -');
setTimeout(function() {
MailPoet.Notice.system('- system -');
setTimeout(function() {
MailPoet.Notice.hide();
}, 2500);
}, 300);
}, 400);
}, 500);
==================================================================================================*/
MailPoet.Notice = {
version: 0.2,
// default options
defaults: {
type: 'success',
message: '',
static: false,
hideClose: false,
id: null,
positionAfter: false,
scroll: false,
timeout: 2000,
onOpen: null,
onClose: null
},
options: {},
init: function(options) {
// set options
this.options = jQuery.extend({}, this.defaults, options);
// clone element
this.element = jQuery('#mailpoet_notice_'+this.options.type).clone();
// add data-id to the element
if (this.options.id) this.element.attr('data-id', 'notice_' + this.options.id);
// remove id from clone
this.element.removeAttr('id');
// insert notice after its parent
scroll: false,
timeout: 2000,
onOpen: null,
onClose: null
},
options: {},
init: function(options) {
// set options
this.options = jQuery.extend({}, this.defaults, options);
// clone element
this.element = jQuery('#mailpoet_notice_'+this.options.type).clone();
// add data-id to the element
if (this.options.id) this.element.attr('data-id', 'notice_' + this.options.id);
// remove id from clone
this.element.removeAttr('id');
// insert notice after its parent
var positionAfter;
if (typeof this.options.positionAfter === 'object') {
positionAfter = this.options.positionAfter;
@ -78,136 +78,135 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
} else {
positionAfter = jQuery('#mailpoet_notice_'+this.options.type);
}
console.log('positionAfter', typeof this.options.positionAfter);
positionAfter.after(this.element);
// setup onClose callback
var onClose = null;
if(this.options.onClose !== null) {
onClose = this.options.onClose;
}
// listen to remove event
jQuery(this.element).on('close', function() {
jQuery(this).fadeOut(200, function() {
// on close callback
if(onClose !== null) {
onClose();
}
// remove notice
jQuery(this).remove();
});
}.bind(this.element));
// listen to message event
jQuery(this.element).on('message', function(e, message) {
MailPoet.Notice.setMessage(message);
}.bind(this.element));
return this;
},
isHTML: function(str) {
var a = document.createElement('div');
a.innerHTML = str;
for(var c = a.childNodes, i = c.length; i--;) {
if(c[i].nodeType == 1) return true;
}
return false;
},
setMessage: function(message) {
// if it's not an html message, let's sugar coat the message with a fancy <p>
if(this.isHTML(message) === false) {
message = '<p>'+message+'</p>';
}
// set message
return this.element.html(message);
},
show: function(options) {
// initialize
this.init(options);
// show notice
this.showNotice();
// return this;
},
showNotice: function() {
// set message
this.setMessage(this.options.message);
// position notice
this.element.insertAfter(jQuery('h2.title'));
// set class name
switch(this.options.type) {
case 'success':
this.element.addClass('updated');
break;
case 'system':
this.element.addClass('update-nag');
break;
case 'error':
this.element.addClass('error');
break;
}
// make the notice appear
this.element.fadeIn(200);
// if scroll option is enabled, scroll to the notice
if(this.options.scroll === true) {
this.element.get(0).scrollIntoView(false);
}
// if the notice is not static, it has to disappear after a timeout
if(this.options.static === false) {
this.element.delay(this.options.timeout).trigger('close');
} else if (this.options.hideClose === false) {
this.element.append('<a href="javascript:;" class="mailpoet_notice_close"><span class="dashicons dashicons-dismiss"></span></a>');
this.element.find('.mailpoet_notice_close').on('click', function() {
jQuery(this).trigger('close');
});
}
// call onOpen callback
if(this.options.onOpen !== null) {
this.options.onOpen(this.element);
}
},
hide: function(all) {
if(all !== undefined && all === true) {
jQuery('.mailpoet_notice:not([id])').trigger('close');
} else if (all !== undefined && jQuery.isArray(all)) {
for (var id in all) {
jQuery('[data-id="notice_' + all[id] + '"]')
.trigger('close');
}
} if (all !== undefined) {
jQuery('[data-id="notice_' + all + '"]')
.trigger('close');
} else {
jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])')
.trigger('close');
}
},
error: function(message, options) {
this.show(jQuery.extend({}, {
type: 'error',
message: '<p>'+message+'</p>'
}, options));
},
success: function(message, options) {
this.show(jQuery.extend({}, {
type: 'success',
message: '<p>'+message+'</p>'
}, options));
},
system: function(message, options) {
this.show(jQuery.extend({}, {
type: 'system',
static: true,
message: message
}, options));
}
};
positionAfter.after(this.element);
// setup onClose callback
var onClose = null;
if(this.options.onClose !== null) {
onClose = this.options.onClose;
}
// listen to remove event
jQuery(this.element).on('close', function() {
jQuery(this).fadeOut(200, function() {
// on close callback
if(onClose !== null) {
onClose();
}
// remove notice
jQuery(this).remove();
});
}.bind(this.element));
// listen to message event
jQuery(this.element).on('message', function(e, message) {
MailPoet.Notice.setMessage(message);
}.bind(this.element));
return this;
},
isHTML: function(str) {
var a = document.createElement('div');
a.innerHTML = str;
for(var c = a.childNodes, i = c.length; i--;) {
if(c[i].nodeType == 1) return true;
}
return false;
},
setMessage: function(message) {
// if it's not an html message, let's sugar coat the message with a fancy <p>
if(this.isHTML(message) === false) {
message = '<p>'+message+'</p>';
}
// set message
return this.element.html(message);
},
show: function(options) {
// initialize
this.init(options);
// show notice
this.showNotice();
// return this;
},
showNotice: function() {
// set message
this.setMessage(this.options.message);
// position notice
this.element.insertAfter(jQuery('h2.title'));
// set class name
switch(this.options.type) {
case 'success':
this.element.addClass('updated');
break;
case 'system':
this.element.addClass('update-nag');
break;
case 'error':
this.element.addClass('error');
break;
}
// make the notice appear
this.element.fadeIn(200);
// if scroll option is enabled, scroll to the notice
if(this.options.scroll === true) {
this.element.get(0).scrollIntoView(false);
}
// if the notice is not static, it has to disappear after a timeout
if(this.options.static === false) {
this.element.delay(this.options.timeout).trigger('close');
} else if (this.options.hideClose === false) {
this.element.append('<a href="javascript:;" class="mailpoet_notice_close"><span class="dashicons dashicons-dismiss"></span></a>');
this.element.find('.mailpoet_notice_close').on('click', function() {
jQuery(this).trigger('close');
});
}
// call onOpen callback
if(this.options.onOpen !== null) {
this.options.onOpen(this.element);
}
},
hide: function(all) {
if(all !== undefined && all === true) {
jQuery('.mailpoet_notice:not([id])').trigger('close');
} else if (all !== undefined && jQuery.isArray(all)) {
for (var id in all) {
jQuery('[data-id="notice_' + all[id] + '"]')
.trigger('close');
}
} if (all !== undefined) {
jQuery('[data-id="notice_' + all + '"]')
.trigger('close');
} else {
jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])')
.trigger('close');
}
},
error: function(message, options) {
this.show(jQuery.extend({}, {
type: 'error',
message: '<p>'+message+'</p>'
}, options));
},
success: function(message, options) {
this.show(jQuery.extend({}, {
type: 'success',
message: '<p>'+message+'</p>'
}, options));
},
system: function(message, options) {
this.show(jQuery.extend({}, {
type: 'system',
static: true,
message: message
}, options));
}
};
});

View File

@ -62,7 +62,7 @@ define(
if (_.contains(fieldsToExclude, selectedOptionId)) {
selectEvent.preventDefault();
if (selectedOptionId === 'deselect') {
jQuery(selectElement).select2('val', '');
jQuery(selectElement).val('').trigger('change');
} else {
var allOptions = [];
_.each(container.find('option'), function (field) {
@ -70,7 +70,7 @@ define(
allOptions.push(field.value);
}
});
jQuery(selectElement).select2('val', allOptions);
jQuery(selectElement).val(allOptions).trigger('change');
}
jQuery(selectElement).select2('close');
}
@ -138,11 +138,11 @@ define(
endpoint: 'ImportExport',
action: 'processExport',
data: JSON.stringify({
'exportConfirmedOption': exportData.exportConfirmedOption,
'exportFormatOption': jQuery(':radio[name="option_format"]:checked').val(),
'groupBySegmentOption': (groupBySegmentOptionElement.is(":visible")) ? groupBySegmentOptionElement.prop('checked') : false,
'export_confirmed_option': exportData.exportConfirmedOption,
'export_format_option': jQuery(':radio[name="option_format"]:checked').val(),
'group_by_segment_option': (groupBySegmentOptionElement.is(":visible")) ? groupBySegmentOptionElement.prop('checked') : false,
'segments': (exportData.segments) ? segmentsContainerElement.val() : false,
'subscriberFields': subscriberFieldsContainerElement.val()
'subscriber_fields': subscriberFieldsContainerElement.val()
})
})
.done(function (response) {

File diff suppressed because it is too large Load Diff

View File

@ -6,15 +6,14 @@ if(!defined('ABSPATH')) exit;
class Env {
static $version;
static $plugin_name;
static $plugin_url;
static $plugin_path;
static $file;
static $path;
static $views_path;
static $assets_path;
static $assets_url;
static $temp_name;
static $temp_path;
static $temp_URL;
static $languages_path;
static $lib_path;
static $plugin_prefix;
@ -34,12 +33,11 @@ class Env {
self::$file = $file;
self::$path = dirname(self::$file);
self::$plugin_name = 'mailpoet';
self::$plugin_url = plugin_dir_url(__FILE__);
self::$views_path = self::$path . '/views';
self::$assets_path = self::$path . '/assets';
self::$assets_url = plugins_url('/assets', $file);
self::$temp_name = 'temp';
self::$temp_path = self::$path . '/' . self::$temp_name;
self::$temp_path = wp_upload_dir()['path'];
self::$temp_URL = wp_upload_dir()['url'];
self::$languages_path = self::$path . '/lang';
self::$lib_path = self::$path . '/lib';
self::$plugin_prefix = 'mailpoet_';

View File

@ -75,7 +75,7 @@ class Initializer {
define('MP_SUBSCRIBER_CUSTOM_FIELD_TABLE', $subscriber_custom_field);
define('MP_NEWSLETTER_OPTION_FIELDS_TABLE', $newsletter_option_fields);
define('MP_NEWSLETTER_OPTION_TABLE', $newsletter_option);
define('MP_SENDING_QUEUE_TABLE', $sending_queues);
define('MP_SENDING_QUEUES_TABLE', $sending_queues);
define('MP_NEWSLETTER_STATISTICS_TABLE', $newsletter_statistics);
}
@ -113,7 +113,6 @@ class Initializer {
}
function setupAnalytics() {
$widget = new Analytics();
$widget->init();
}
@ -143,13 +142,15 @@ class Initializer {
}
function runQueueSupervisor() {
if(php_sapi_name() === 'cli') return;
try {
$supervisor = new Supervisor();
$supervisor->checkDaemon();
} catch (\Exception $e) {}
} catch (\Exception $e) {
}
}
function setupImages() {
add_image_size('mailpoet_newsletter_max', 1320);
}
}
}

View File

@ -179,7 +179,7 @@ class BlankTemplate {
),
"h1" => array(
"fontColor" => "#111111",
"fontFamily" => "Arial Black",
"fontFamily" => "Arial",
"fontSize" => "24px"
),
"h2" => array(

View File

@ -7,15 +7,20 @@ use MailPoet\Util\Helpers;
if(!defined('ABSPATH')) exit;
class PublicAPI {
public $api;
public $section;
public $action;
public $request_payload;
function __construct() {
# http://example.com/?mailpoet-api&section=&action=&payload=
# http://example.com/?mailpoet-api&section=&action=&request_payload=
$this->api = isset($_GET['mailpoet-api']) ? true : false;
$this->section = isset($_GET['section']) ? $_GET['section'] : false;
$this->action = isset($_GET['action']) ?
Helpers::underscoreToCamelCase($_GET['action']) :
false;
$this->payload = isset($_GET['payload']) ?
json_decode(urldecode($_GET['payload']), true) :
$this->request_payload = isset($_GET['request_payload']) ?
json_decode(urldecode($_GET['request_payload']), true) :
false;
}
@ -26,10 +31,9 @@ class PublicAPI {
function queue() {
try {
$queue = new Daemon($this->payload);
$queue = new Daemon($this->request_payload);
$this->_checkAndCallMethod($queue, $this->action);
} catch(\Exception $e) {
// mailer configuration error
}
}

View File

@ -10,86 +10,99 @@ require_once(ABSPATH . 'wp-includes/pluggable.php');
if(!defined('ABSPATH')) exit;
class Daemon {
function __construct($payload = array()) {
public $daemon;
public $request_payload;
public $refreshed_token;
public $timer;
function __construct($request_payload = array()) {
set_time_limit(0);
ignore_user_abort();
list ($this->daemon, $this->daemonData) = $this->getDaemon();
$this->refreshedToken = $this->refreshToken();
$this->payload = $payload;
$this->daemon = $this->getDaemon();
$this->refreshed_token = $this->refreshToken();
$this->request_payload = $request_payload;
$this->timer = microtime(true);
}
function start() {
if(!isset($this->payload['session'])) {
$this->abortWithError('missing session ID');
if(!isset($this->request_payload['session'])) {
$this->abortWithError(__('Missing session ID.'));
}
$this->manageSession('start');
$daemon = $this->daemon;
$daemonData = $this->daemonData;
if(!$daemon) {
$daemon = Setting::create();
$daemon->name = 'cron_daemon';
$daemonData = array(
'status' => null,
'counter' => 0
$this->saveDaemon(
array(
'status' => 'starting',
'counter' => 0
)
);
$daemon->value = json_encode($daemonData);
$daemon->save();
}
if($daemonData['status'] !== 'started') {
$_SESSION['cron_daemon'] = 'started';
$daemonData['status'] = 'started';
$daemonData['token'] = $this->refreshedToken;
$_SESSION['cron_daemon'] = array('result' => true);
$this->manageSession('end');
$daemon->value = json_encode($daemonData);
$daemon->save();
$this->callSelf();
} else {
if($daemon['status'] === 'started') {
$_SESSION['cron_daemon'] = array(
'result' => false,
'error' => 'already started'
'errors' => array(__('Daemon already running.'))
);
}
if($daemon['status'] === 'starting') {
$_SESSION['cron_daemon'] = 'started';
$_SESSION['cron_daemon'] = array('result' => true);
$this->manageSession('end');
$daemon['status'] = 'started';
$daemon['token'] = $this->refreshed_token;
$this->saveDaemon($daemon);
$this->callSelf();
}
$this->manageSession('end');
}
function run() {
if(!$this->daemon || $this->daemonData['status'] !== 'started') {
$this->abortWithError('not running');
$allowed_statuses = array(
'stopping',
'starting',
'started'
);
if(!$this->daemon || !in_array($this->daemon['status'], $allowed_statuses)) {
$this->abortWithError(__('Invalid daemon status.'));
}
if(!isset($this->payload['token']) ||
$this->payload['token'] !== $this->daemonData['token']
if(!isset($this->request_payload['token']) ||
$this->request_payload['token'] !== $this->daemon['token']
) {
$this->abortWithError('invalid token');
$this->abortWithError('Invalid token.');
}
try {
$sendingQueue = new SendingQueue($this->timer);
$sendingQueue->process();
$sending_queue = new SendingQueue($this->timer);
$sending_queue->process();
} catch(Exception $e) {
}
$elapsedTime = microtime(true) - $this->timer;
if($elapsedTime < 30) {
sleep(30 - $elapsedTime);
$elapsed_time = microtime(true) - $this->timer;
if($elapsed_time < 30) {
sleep(30 - $elapsed_time);
}
// after each execution, read daemon in case its status was modified
$daemon = $this->getDaemon();
// after each execution, read daemon in case it's status was modified
list($daemon, $daemonData) = $this->getDaemon();
$daemonData['counter']++;
$daemonData['token'] = $this->refreshedToken;
$daemon->value = json_encode($daemonData);
$daemon->save();
if($daemonData['status'] === 'strated') $this->callSelf();
if($daemon['status'] === 'stopping') $daemon['status'] = 'stopped';
if($daemon['status'] === 'starting') $daemon['status'] = 'started';
$daemon['token'] = $this->refreshed_token;
$daemon['counter']++;
$this->saveDaemon($daemon);
if($daemon['status'] === 'started') $this->callSelf();
}
function getDaemon() {
$daemon = Setting::where('name', 'cron_daemon')
->findOne();
return array(
($daemon) ? $daemon : null,
($daemon) ? json_decode($daemon->value, true) : null
return Setting::getValue('cron_daemon');
}
function saveDaemon($daemon_data) {
$daemon_data['updated_at'] = time();
return Setting::setValue(
'cron_daemon',
$daemon_data
);
}
@ -99,24 +112,23 @@ class Daemon {
function manageSession($action) {
switch($action) {
case 'start':
if(session_id()) {
session_write_close();
}
session_id($this->payload['session']);
session_start();
break;
case 'end':
case 'start':
if(session_id()) {
session_write_close();
break;
}
session_id($this->request_payload['session']);
session_start();
break;
case 'end':
session_write_close();
break;
}
}
function callSelf() {
$payload = json_encode(array('token' => $this->refreshedToken));
Supervisor::getRemoteUrl(
'/?mailpoet-api&section=queue&action=run&payload=' . urlencode($payload)
$payload = json_encode(array('token' => $this->refreshed_token));
Supervisor::accessRemoteUrl(
'/?mailpoet-api&section=queue&action=run&request_payload=' . urlencode($payload)
);
exit;
}
@ -125,7 +137,7 @@ class Daemon {
wp_send_json(
array(
'result' => false,
'error' => $error
'errors' => array($error)
));
exit;
}

View File

@ -1,38 +1,47 @@
<?php
namespace MailPoet\Cron;
use Carbon\Carbon;
use MailPoet\Config\Env;
use MailPoet\Models\Setting;
if(!defined('ABSPATH')) exit;
class Supervisor {
function __construct($forceStart = false) {
$this->forceStart = $forceStart;
public $daemon;
function __construct($force_start = false) {
$this->force_start = $force_start;
if(!Env::isPluginActivated()) {
throw new \Exception('Database has not been configured.');
throw new \Exception(__('MailPoet is not activated.'));
}
list ($this->daemon, $this->daemonData) = $this->getDaemon();
$this->daemon = $this->getDaemon();
}
function checkDaemon() {
if(!$this->daemon) {
return $this->startDaemon();
}
if(!$this->forceStart && $this->daemonData['status'] === 'stopped') {
return;
if(
!$this->force_start &&
in_array($this->daemon['status'], array('stopped', 'stopping'))
) {
return $this->daemon['status'];
}
$currentTime = Carbon::now('UTC');
$lastUpdateTime = Carbon::createFromFormat(
'Y-m-d H:i:s',
$this->daemon->updated_at, 'UTC'
);
$timeSinceLastStart = $currentTime->diffInSeconds($lastUpdateTime);
if($timeSinceLastStart < 40) return;
$this->daemonData['status'] = null;
$this->daemon->value = json_encode($this->daemonData);
$this->daemon->save();
$elapsed_time = time() - (int)$this->daemon['updated_at'];
if($elapsed_time < 40) {
if(!$this->force_start) {
return;
}
if($this->daemon['status'] === 'stopping' ||
$this->daemon['status'] === 'starting'
) {
return $this->daemon['status'];
}
}
$this->daemon['status'] = 'starting';
$this->saveDaemon($this->daemon);
return $this->startDaemon();
}
@ -41,9 +50,10 @@ class Supervisor {
$sessionId = session_id();
session_write_close();
$_SESSION['cron_daemon'] = null;
$payload = json_encode(array('session' => $sessionId));
self::getRemoteUrl(
'/?mailpoet-api&section=queue&action=start&payload=' . urlencode($payload)
$requestPayload = json_encode(array('session' => $sessionId));
self::accessRemoteUrl(
'/?mailpoet-api&section=queue&action=start&request_payload=' .
urlencode($requestPayload)
);
session_start();
$daemonStatus = $_SESSION['cron_daemon'];
@ -53,19 +63,20 @@ class Supervisor {
}
function getDaemon() {
$daemon = Setting::where('name', 'cron_daemon')
->findOne();
$daemonData = ($daemon) ? json_decode($daemon->value, true) : false;
return array(
$daemon,
$daemonData
return Setting::getValue('cron_daemon');
}
function saveDaemon($daemon_data) {
return Setting::setValue(
'cron_daemon',
$daemon_data
);
}
static function getRemoteUrl($url) {
static function accessRemoteUrl($url) {
$args = array(
'timeout' => 1,
'user-agent' => 'MailPoet (www.mailpoet.com)'
'user-agent' => 'MailPoet (www.mailpoet.com) Cron'
);
wp_remote_get(
self::getSiteUrl() . $url,
@ -74,11 +85,19 @@ class Supervisor {
}
static function getSiteUrl() {
if(preg_match('!:\d+/!', site_url())) return site_url();
preg_match('!http://(?P<host>.*?):(?P<port>\d+)!', site_url(), $server);
// additional check for some sites running on a virtual machine or behind
// proxy where there could be different ports (e.g., host:8080 => guest:80)
// if the site URL does not contain a port, return the URL
if(!preg_match('!^https?://.*?:\d+!', site_url())) return site_url();
preg_match('!://(?P<host>.*?):(?P<port>\d+)!', site_url(), $server);
// connect to the URL with port
$fp = @fsockopen($server['host'], $server['port'], $errno, $errstr, 1);
return ($fp) ?
site_url() :
preg_replace('/(?=:\d+):\d+/', '$1', site_url());
if($fp) return site_url();
// connect to the URL without port
$fp = @fsockopen($server['host'], $server['port'], $errno, $errstr, 1);
if($fp) return preg_replace('!(?=:\d+):\d+!', '$1', site_url());
// throw an error if all connections fail
throw new \Exception(__('Site URL is unreachable.'));
}
}

View File

@ -1,114 +1,134 @@
<?php
namespace MailPoet\Cron\Workers;
use MailPoet\Mailer\Mailer;
use MailPoet\Models\Newsletter;
use MailPoet\Models\NewsletterStatistics;
use MailPoet\Models\Subscriber;
use MailPoet\Newsletter\Renderer\Renderer;
use MailPoet\Router\Mailer;
if(!defined('ABSPATH')) exit;
class SendingQueue {
public $timer;
function __construct($timer = false) {
$this->timer = ($timer) ? $timer : microtime(true);
}
function process() {
$queues =
\MailPoet\Models\SendingQueue::orderByDesc('priority')
->whereNull('deleted_at')
->whereNull('status')
->findResultSet();
foreach($queues as $queue) {
$newsletter = Newsletter::findOne($queue->newsletter_id);
// TODO: implement mailer sending frequency limits
foreach($this->getQueues() as $queue) {
$newsletter = Newsletter::findOne($queue->newsletter_id)
->asArray();
if(!$newsletter) {
continue;
};
$newsletter = $newsletter->asArray();
$mailer = new Mailer($httpRequest = false);
if(!empty($newsletter['sender_address']) &&
!empty($newsletter['sender_name'])
) {
$mailer->fromName = $newsletter['sender_name'];
$mailer->fromEmail = $newsletter['sender_address'];
$mailer->fromNameEmail = sprintf(
'%s <%s>',
$mailer->fromName,
$mailer->fromEmail
);
}
if(!empty($newsletter['reply_to_address']) &&
!empty($newsletter['reply_to_name'])
) {
$mailer->replyToName = $newsletter['reply_to_name'];
$mailer->replyToEmail = $newsletter['reply_to_address'];
$mailer->replyToNameEmail = sprintf(
'%s <%s>',
$mailer->replyToName,
$mailer->replyToEmail
);
}
$mailer->mailer = $mailer->buildMailer();
$renderer = new Renderer(json_decode($newsletter['body'], true));
$newsletter = array(
'subject' => $newsletter['subject'],
'id' => $newsletter['id'],
'body' => array(
'html' => $renderer->renderAll(),
'text' => ''
// TODO: add text body
)
);
$mailer = $this->configureMailerForNewsletter($newsletter);
$newsletter = $this->renderNewsletter($newsletter);
$subscribers = json_decode($queue->subscribers, true);
$subscribersToProcess = $subscribers['to_process'];
if(!isset($subscribers['failed'])) $subscribers['failed'] = array();
$subscribers_to_process = $subscribers['to_process'];
if(!isset($subscribers['processed'])) $subscribers['processed'] = array();
foreach(array_chunk($subscribersToProcess, 200) as $subscriberIds) {
$dbSubscribers = Subscriber::whereIn('id', $subscriberIds)
if(!isset($subscribers['failed'])) $subscribers['failed'] = array();
foreach(array_chunk($subscribers_to_process, 200) as $subscriber_ids) {
$db_subscribers = Subscriber::whereIn('id', $subscriber_ids)
->findArray();
foreach($dbSubscribers as $i => $dbSubscriber) {
foreach($db_subscribers as $db_subscriber) {
$this->checkExecutionTimer();
// TODO: replace shortcodes in the newsletter
$result = $mailer->mailer->send(
$newsletter,
$mailer->transformSubscriber($dbSubscriber)
);
$newsletterStatistics = NewsletterStatistics::create();
$newsletterStatistics->subscriber_id = $dbSubscriber['id'];
$newsletterStatistics->newsletter_id = $newsletter['id'];
$newsletterStatistics->queue_id = $queue->id;
$newsletterStatistics->save();
$result = $this->sendNewsletter(
$mailer,
$this->processNewsletter($newsletter),
$db_subscriber);
if($result) {
$subscribers['processed'][] = $dbSubscriber['id'];
$this->updateStatistics($newsletter['id'], $db_subscriber['id'], $queue->id);
$subscribers['processed'][] = $db_subscriber['id'];
} else {
$subscribers['failed'][] = $dbSubscriber['id'];
$subscribers['failed'][] = $db_subscriber['id'];
}
$subscribers['to_process'] = array_values(
array_diff(
$subscribers['to_process'],
array_merge($subscribers['processed'], $subscribers['failed'])
)
);
$queue->count_processed =
count($subscribers['processed']) + count($subscribers['failed']);
$queue->count_to_process = count($subscribers['to_process']);
$queue->count_failed = count($subscribers['failed']);
$queue->count_total =
$queue->count_processed + $queue->count_to_process;
if(!$queue->count_to_process) {
$queue->processed_at = date('Y-m-d H:i:s');
$queue->status = 'completed';
}
$queue->subscribers = json_encode($subscribers);
$queue->save();
$this->updateQueue($queue, $subscribers);
}
}
}
}
function processNewsletter($newsletter) {
// TODO: replace shortcodes, etc..
return $newsletter;
}
function sendNewsletter($mailer, $newsletter, $subscriber) {
return $mailer->mailer_instance->send(
$newsletter,
$mailer->transformSubscriber($subscriber)
);
}
function updateStatistics($newsletter_id, $subscriber_id, $queue_id) {
$newsletter_statistic = NewsletterStatistics::create();
$newsletter_statistic->subscriber_id = $newsletter_id;
$newsletter_statistic->newsletter_id = $subscriber_id;
$newsletter_statistic->queue_id = $queue_id;
$newsletter_statistic->save();
}
function updateQueue($queue, $subscribers) {
$subscribers['to_process'] = array_values(
array_diff(
$subscribers['to_process'],
array_merge($subscribers['processed'], $subscribers['failed'])
)
);
$queue->count_processed =
count($subscribers['processed']) + count($subscribers['failed']);
$queue->count_to_process = count($subscribers['to_process']);
$queue->count_failed = count($subscribers['failed']);
$queue->count_total =
$queue->count_processed + $queue->count_to_process;
if(!$queue->count_to_process) {
$queue->processed_at = date('Y-m-d H:i:s');
$queue->status = 'completed';
}
$queue->subscribers = json_encode($subscribers);
$queue->save();
}
function configureMailerForNewsletter($newsletter) {
if(!empty($newsletter['sender_address']) && !empty($newsletter['sender_name'])) {
$sender = array(
'name' => $newsletter['sender_name'],
'address' => $newsletter['sender_address']
);
} else {
$sender = false;
}
if(!empty($newsletter['reply_to_address']) && !empty($newsletter['reply_to_name'])) {
$reply_to = array(
'name' => $newsletter['reply_to_name'],
'address' => $newsletter['reply_to_address']
);
} else {
$reply_to = false;
}
$mailer = new Mailer($method = false, $sender, $reply_to);
return $mailer;
}
function checkExecutionTimer() {
$elapsedTime = microtime(true) - $this->timer;
if($elapsedTime >= 28) throw new \Exception('Maximum execution time reached.');
$elapsed_time = microtime(true) - $this->timer;
if($elapsed_time >= 30) throw new \Exception('Maximum execution time reached.');
}
function getQueues() {
return \MailPoet\Models\SendingQueue::orderByDesc('priority')
->whereNull('deleted_at')
->whereNull('status')
->findResultSet();
}
function renderNewsletter($newsletter) {
$renderer = new Renderer(json_decode($newsletter['body'], true));
// TODO: update once text rendering is implemented/enderer returns an array
$newsletter['body'] = array('html' => $renderer->render(), 'text' => '');
return $newsletter;
}
}

View File

@ -1,113 +0,0 @@
<?php
namespace MailPoet\Mailer\API;
if(!defined('ABSPATH')) exit;
class AmazonSES {
function __construct($region, $accessKey, $secretKey, $from) {
$this->awsAccessKey = $accessKey;
$this->awsSecret_key = $secretKey;
$this->awsRegion = $region;
$this->awsEndpoint = sprintf('email.%s.amazonaws.com', $region);
$this->awsSigningAlgorithm = 'AWS4-HMAC-SHA256';
$this->awsService = 'ses';
$this->awsTerminationString = 'aws4_request';
$this->hashAlgorithm = 'sha256';
$this->url = 'https://' . $this->awsEndpoint;
$this->from = $from;
$this->date = gmdate('Ymd\THis\Z');
$this->dateWithoutTime = gmdate('Ymd');
}
function send($newsletter, $subscriber) {
$result = wp_remote_post(
$this->url,
$this->request($newsletter, $subscriber)
);
return (
!is_wp_error($result) === true &&
wp_remote_retrieve_response_code($result) === 200
);
}
function getBody($newsletter, $subscriber) {
$body = array(
'Action' => 'SendEmail',
'Version' => '2010-12-01',
'Source' => $this->from,
'Destination.ToAddresses.member.1' => $subscriber,
'Message.Subject.Data' => $newsletter['subject'],
'ReturnPath' => $this->from
);
if(!empty($newsletter['body']['html'])) {
$body['Message.Body.Html.Data'] = $newsletter['body']['html'];
}
if(!empty($newsletter['body']['text'])) {
$body['Message.Body.Text.Data'] = $newsletter['body']['text'];
}
return $body;
}
function request($newsletter, $subscriber) {
$body = $this->getBody($newsletter, $subscriber);
return array(
'timeout' => 10,
'httpversion' => '1.1',
'method' => 'POST',
'headers' => array(
'Host' => $this->awsEndpoint,
'Authorization' => $this->signRequest($body),
'X-Amz-Date' => $this->date
),
'body' => urldecode(http_build_query($body))
);
}
function signRequest($body) {
$stringToSign = $this->createStringToSign(
$this->getCredentialScope(),
$this->getCanonicalRequest($body)
);
$signature = hash_hmac($this->hashAlgorithm, $stringToSign, $this->getSigningKey());
return sprintf(
'%s Credential=%s/%s, SignedHeaders=host;x-amz-date, Signature=%s',
$this->awsSigningAlgorithm,
$this->awsAccessKey,
$this->getCredentialScope(),
$signature);
}
function getCredentialScope() {
return sprintf('%s/%s/%s/%s', $this->dateWithoutTime, $this->awsRegion, $this->awsService, $this->awsTerminationString);
}
function getCanonicalRequest($body) {
return implode("\n", array(
'POST',
'/',
'',
'host:' . $this->awsEndpoint,
'x-amz-date:' . $this->date,
'',
'host;x-amz-date',
hash($this->hashAlgorithm, urldecode(http_build_query($body)))
));
}
function createStringToSign($credentialScope, $canonicalRequest) {
return implode("\n", array(
$this->awsSigningAlgorithm,
$this->date,
$credentialScope,
hash($this->hashAlgorithm, $canonicalRequest)
));
}
function getSigningKey() {
$dateKey = hash_hmac($this->hashAlgorithm, $this->dateWithoutTime, 'AWS4' . $this->awsSecret_key, true);
$regionKey = hash_hmac($this->hashAlgorithm, $this->awsRegion, $dateKey, true);
$serviceKey = hash_hmac($this->hashAlgorithm, $this->awsService, $regionKey, true);
return hash_hmac($this->hashAlgorithm, $this->awsTerminationString, $serviceKey, true);
}
}

146
lib/Mailer/Mailer.php Normal file
View File

@ -0,0 +1,146 @@
<?php
namespace MailPoet\Mailer;
use MailPoet\Models\Setting;
require_once(ABSPATH . 'wp-includes/pluggable.php');
if(!defined('ABSPATH')) exit;
class Mailer {
public $mailer;
public $sender;
public $reply_to;
public $mailer_instance;
function __construct($mailer = false, $sender = false, $reply_to = false) {
$this->mailer = $this->getMailer($mailer);
$this->sender = $this->getSender($sender);
$this->reply_to = $this->getReplyTo($reply_to);
$this->mailer_instance = $this->buildMailer();
}
function send($newsletter, $subscriber) {
$subscriber = $this->transformSubscriber($subscriber);
return $this->mailer_instance->send($newsletter, $subscriber);
}
function buildMailer() {
switch($this->mailer['method']) {
case 'AmazonSES':
$mailer_instance = new $this->mailer['class'](
$this->mailer['region'],
$this->mailer['access_key'],
$this->mailer['secret_key'],
$this->sender['from_name_email']
);
break;
case 'ElasticEmail':
$mailer_instance = new $this->mailer['class'](
$this->mailer['api_key'],
$this->sender['from_email'],
$this->sender['from_name']
);
break;
case 'MailGun':
$mailer_instance = new $this->mailer['class'](
$this->mailer['domain'],
$this->mailer['api_key'],
$this->sender['from_name_email']
);
break;
case 'MailPoet':
$mailer_instance = new $this->mailer['class'](
$this->mailer['mailpoet_api_key'],
$this->sender['from_email'],
$this->sender['from_name']
);
break;
case 'Mandrill':
$mailer_instance = new $this->mailer['class'](
$this->mailer['api_key'],
$this->sender['from_email'],
$this->sender['from_name']
);
break;
case 'SendGrid':
$mailer_instance = new $this->mailer['class'](
$this->mailer['api_key'],
$this->sender['from_email'],
$this->sender['from_name']
);
break;
case 'WPMail':
$mailer_instance = new $this->mailer['class'](
$this->sender['from_email'],
$this->sender['from_name']
);
break;
case 'SMTP':
$mailer_instance = new $this->mailer['class'](
$this->mailer['host'],
$this->mailer['port'],
$this->mailer['authentication'],
$this->mailer['login'],
$this->mailer['password'],
$this->mailer['encryption'],
$this->sender['from_email'],
$this->sender['from_name']
);
break;
default:
throw new \Exception(__('Mailing method does not exist.'));
break;
}
return $mailer_instance;
}
function getMailer($mailer = false) {
if(!$mailer) {
$mailer = Setting::getValue('mta', null);
if(!$mailer || !isset($mailer['method'])) throw new \Exception(__('Mailer is not configured.'));
}
$mailer['class'] = 'MailPoet\\Mailer\\Methods\\' . $mailer['method'];
return $mailer;
}
function getSender($sender = false) {
if(!$sender) {
$sender = Setting::getValue('sender', null);
if(!$sender) throw new \Exception(__('Sender name and email are not configured.'));
}
return array(
'from_name' => $sender['name'],
'from_email' => $sender['address'],
'from_name_email' => sprintf('%s <%s>', $sender['name'], $sender['address'])
);
}
function getReplyTo($reply_to = false) {
if(!$reply_to) {
$reply_to = Setting::getValue('reply_to', null);
if(!$reply_to) {
$reply_to = array(
'name' => $this->sender['from_name'],
'address' => $this->sender['from_email']
);
}
}
return array(
'reply_to_name' => $reply_to['name'],
'reply_to_email' => $reply_to['address'],
'reply_to_name_email' => sprintf('%s <%s>', $reply_to['name'], $reply_to['address'])
);
}
function transformSubscriber($subscriber) {
if(!is_array($subscriber)) return $subscriber;
if(isset($subscriber['address'])) $subscriber['email'] = $subscriber['address'];
$first_name = (isset($subscriber['first_name'])) ? $subscriber['first_name'] : '';
$last_name = (isset($subscriber['last_name'])) ? $subscriber['last_name'] : '';
if(!$first_name && !$last_name) return $subscriber['email'];
$subscriber = sprintf('%s %s <%s>', $first_name, $last_name, $subscriber['email']);
$subscriber = trim(preg_replace('!\s\s+!', ' ', $subscriber));
return $subscriber;
}
}

View File

@ -0,0 +1,155 @@
<?php
namespace MailPoet\Mailer\Methods;
if(!defined('ABSPATH')) exit;
class AmazonSES {
public $aws_access_key;
public $aws_secret_key;
public $aws_region;
public $aws_endpoint;
public $aws_signing_algorithm;
public $aws_service;
public $aws_termination_string;
public $hash_algorithm;
public $url;
public $from;
public $date;
public $date_without_time;
function __construct($region, $access_key, $secret_key, $from) {
$this->aws_access_key = $access_key;
$this->aws_secret_key = $secret_key;
$this->aws_region = $region;
$this->aws_endpoint = sprintf('email.%s.amazonaws.com', $region);
$this->aws_signing_algorithm = 'AWS4-HMAC-SHA256';
$this->aws_service = 'ses';
$this->aws_termination_string = 'aws4_request';
$this->hash_algorithm = 'sha256';
$this->url = 'https://' . $this->aws_endpoint;
$this->from = $from;
$this->date = gmdate('Ymd\THis\Z');
$this->date_without_time = gmdate('Ymd');
}
function send($newsletter, $subscriber) {
$result = wp_remote_post(
$this->url,
$this->request($newsletter, $subscriber)
);
return (
!is_wp_error($result) === true &&
wp_remote_retrieve_response_code($result) === 200
);
}
function getBody($newsletter, $subscriber) {
$body = array(
'Action' => 'SendEmail',
'Version' => '2010-12-01',
'Source' => $this->from,
'Destination.ToAddresses.member.1' => $subscriber,
'Message.Subject.Data' => $newsletter['subject'],
'ReturnPath' => $this->from
);
if(!empty($newsletter['body']['html'])) {
$body['Message.Body.Html.Data'] = $newsletter['body']['html'];
}
if(!empty($newsletter['body']['text'])) {
$body['Message.Body.Text.Data'] = $newsletter['body']['text'];
}
return $body;
}
function request($newsletter, $subscriber) {
$body = $this->getBody($newsletter, $subscriber);
return array(
'timeout' => 10,
'httpversion' => '1.1',
'method' => 'POST',
'headers' => array(
'Host' => $this->aws_endpoint,
'Authorization' => $this->signRequest($body),
'X-Amz-Date' => $this->date
),
'body' => urldecode(http_build_query($body))
);
}
function signRequest($body) {
$string_to_sign = $this->createStringToSign(
$this->getCredentialScope(),
$this->getCanonicalRequest($body)
);
$signature = hash_hmac(
$this->hash_algorithm,
$string_to_sign,
$this->getSigningKey()
);
return sprintf(
'%s Credential=%s/%s, SignedHeaders=host;x-amz-date, Signature=%s',
$this->aws_signing_algorithm,
$this->aws_access_key,
$this->getCredentialScope(),
$signature);
}
function getCredentialScope() {
return sprintf(
'%s/%s/%s/%s',
$this->date_without_time,
$this->aws_region,
$this->aws_service,
$this->aws_termination_string);
}
function getCanonicalRequest($body) {
return implode("\n", array(
'POST',
'/',
'',
'host:' . $this->aws_endpoint,
'x-amz-date:' . $this->date,
'',
'host;x-amz-date',
hash($this->hash_algorithm, urldecode(http_build_query($body)))
));
}
function createStringToSign($credential_scope, $canonical_request) {
return implode("\n", array(
$this->aws_signing_algorithm,
$this->date,
$credential_scope,
hash($this->hash_algorithm, $canonical_request)
));
}
function getSigningKey() {
$date_key = hash_hmac(
$this->hash_algorithm,
$this->date_without_time,
'AWS4' . $this->aws_secret_key,
true
);
$region_key = hash_hmac(
$this->hash_algorithm,
$this->aws_region,
$date_key,
true
);
$service_key = hash_hmac(
$this->hash_algorithm,
$this->aws_service,
$region_key,
true
);
return hash_hmac(
$this->hash_algorithm,
$this->aws_termination_string,
$service_key,
true
);
}
}

View File

@ -1,14 +1,18 @@
<?php
namespace MailPoet\Mailer\API;
namespace MailPoet\Mailer\Methods;
if(!defined('ABSPATH')) exit;
class ElasticEmail {
function __construct($apiKey, $fromEmail, $fromName) {
$this->url = 'https://api.elasticemail.com/mailer/send';
$this->apiKey = $apiKey;
$this->fromEmail = $fromEmail;
$this->fromName = $fromName;
public $url = 'https://api.elasticemail.com/mailer/send';
public $api_key;
public $from_email;
public $from_name;
function __construct($api_key, $from_email, $from_name) {
$this->api_key = $api_key;
$this->from_email = $from_email;
$this->from_name = $from_name;
}
function send($newsletter, $subscriber) {
@ -23,9 +27,9 @@ class ElasticEmail {
function getBody($newsletter, $subscriber) {
$body = array(
'api_key' => $this->apiKey,
'from' => $this->fromEmail,
'from_name' => $this->fromName,
'api_key' => $this->api_key,
'from' => $this->from_email,
'from_name' => $this->from_name,
'to' => $subscriber,
'subject' => $newsletter['subject']
);

View File

@ -1,12 +1,16 @@
<?php
namespace MailPoet\Mailer\API;
namespace MailPoet\Mailer\Methods;
if(!defined('ABSPATH')) exit;
class MailGun {
function __construct($domain, $apiKey, $from) {
public $url;
public $api_key;
public $from;
function __construct($domain, $api_key, $from) {
$this->url = sprintf('https://api.mailgun.net/v3/%s/messages', $domain);
$this->apiKey = $apiKey;
$this->api_key = $api_key;
$this->from = $from;
}
@ -37,7 +41,7 @@ class MailGun {
}
function auth() {
return 'Basic ' . base64_encode('api:' . $this->apiKey);
return 'Basic ' . base64_encode('api:' . $this->api_key);
}
function request($newsletter, $subscriber) {

View File

@ -1,16 +1,20 @@
<?php
namespace MailPoet\Mailer;
namespace MailPoet\Mailer\Methods;
if(!defined('ABSPATH')) exit;
class MailPoet {
function __construct($apiKey, $fromEmail, $fromName) {
$this->url = 'https://bridge.mailpoet.com/api/messages';
$this->apiKey = $apiKey;
$this->fromEmail = $fromEmail;
$this->fromName = $fromName;
public $url = 'https://bridge.mailpoet.com/api/messages';
public $api_key;
public $from_email;
public $from_name;
function __construct($api_key, $from_email, $from_name) {
$this->api_key = $api_key;
$this->from_email = $from_email;
$this->from_name = $from_name;
}
function send($newsletter, $subscriber) {
$result = wp_remote_post(
$this->url,
@ -21,20 +25,20 @@ class MailPoet {
wp_remote_retrieve_response_code($result) === 201
);
}
function processSubscriber($subscriber) {
preg_match('!(?P<name>.*?)\s<(?P<email>.*?)>!', $subscriber, $subscriberData);
if(!isset($subscriberData['email'])) {
$subscriberData = array(
preg_match('!(?P<name>.*?)\s<(?P<email>.*?)>!', $subscriber, $subscriber_data);
if(!isset($subscriber_data['email'])) {
$subscriber_data = array(
'email' => $subscriber,
);
}
return array(
'email' => $subscriberData['email'],
'name' => (isset($subscriberData['name'])) ? $subscriberData['name'] : ''
'email' => $subscriber_data['email'],
'name' => (isset($subscriber_data['name'])) ? $subscriber_data['name'] : ''
);
}
function getBody($newsletter, $subscriber) {
$body = array(
'to' => (array(
@ -42,8 +46,8 @@ class MailPoet {
'name' => $subscriber['name']
)),
'from' => (array(
'address' => $this->fromEmail,
'name' => $this->fromName
'address' => $this->from_email,
'name' => $this->from_name
)),
'subject' => $newsletter['subject']
);
@ -55,11 +59,11 @@ class MailPoet {
}
return $body;
}
function auth() {
return 'Basic ' . base64_encode('api:' . $this->apiKey);
return 'Basic ' . base64_encode('api:' . $this->api_key);
}
function request($newsletter, $subscriber) {
$body = array($this->getBody($newsletter, $subscriber));
return array(

View File

@ -1,14 +1,18 @@
<?php
namespace MailPoet\Mailer\API;
namespace MailPoet\Mailer\Methods;
if(!defined('ABSPATH')) exit;
class Mandrill {
function __construct($apiKey, $fromEmail, $fromName) {
$this->url = 'https://mandrillapp.com/api/1.0/messages/send.json';
$this->apiKey = $apiKey;
$this->fromName = $fromName;
$this->fromEmail = $fromEmail;
public $url = 'https://mandrillapp.com/api/1.0/messages/send.json';
public $api_key;
public $from_email;
public $from_name;
function __construct($api_key, $from_email, $from_name) {
$this->api_key = $api_key;
$this->from_name = $from_name;
$this->from_email = $from_email;
}
function send($newsletter, $subscriber) {
@ -24,24 +28,24 @@ class Mandrill {
}
function processSubscriber($subscriber) {
preg_match('!(?P<name>.*?)\s<(?P<email>.*?)>!', $subscriber, $subscriberData);
if(!isset($subscriberData['email'])) {
$subscriberData = array(
preg_match('!(?P<name>.*?)\s<(?P<email>.*?)>!', $subscriber, $subscriber_data);
if(!isset($subscriber_data['email'])) {
$subscriber_data = array(
'email' => $subscriber,
);
}
return array(
'email' => $subscriberData['email'],
'name' => (isset($subscriberData['name'])) ? $subscriberData['name'] : ''
'email' => $subscriber_data['email'],
'name' => (isset($subscriber_data['name'])) ? $subscriber_data['name'] : ''
);
}
function getBody($newsletter, $subscriber) {
$body = array(
'key' => $this->apiKey,
'key' => $this->api_key,
'message' => array(
'from_email' => $this->fromEmail,
'from_name' => $this->fromName,
'from_email' => $this->from_email,
'from_name' => $this->from_name,
'to' => array($subscriber),
'subject' => $newsletter['subject']
),

View File

@ -1,19 +1,30 @@
<?php
namespace MailPoet\Mailer;
namespace MailPoet\Mailer\Methods;
if(!defined('ABSPATH')) exit;
class SMTP {
function __construct($host, $port, $authentication, $login = null, $password = null, $encryption,
$fromEmail, $fromName) {
public $host;
public $port;
public $authentication;
public $login;
public $password;
public $encryption;
public $from_name;
public $from_email;
public $mailer;
function __construct(
$host, $port, $authentication, $login = null, $password = null, $encryption,
$from_email, $from_name) {
$this->host = $host;
$this->port = $port;
$this->authentication = $authentication;
$this->login = $login;
$this->password = $password;
$this->encryption = $encryption;
$this->fromName = $fromName;
$this->fromEmail = $fromEmail;
$this->from_name = $from_name;
$this->from_email = $from_email;
$this->mailer = $this->buildMailer();
}
@ -42,7 +53,7 @@ class SMTP {
function createMessage($newsletter, $subscriber) {
$message = \Swift_Message::newInstance()
->setFrom(array($this->fromEmail => $this->fromName))
->setFrom(array($this->from_email => $this->from_name))
->setTo($this->processSubscriber($subscriber))
->setSubject($newsletter['subject']);
if(!empty($newsletter['body']['html'])) {
@ -55,15 +66,15 @@ class SMTP {
}
function processSubscriber($subscriber) {
preg_match('!(?P<name>.*?)\s<(?P<email>.*?)>!', $subscriber, $subscriberData);
if(!isset($subscriberData['email'])) {
$subscriberData = array(
preg_match('!(?P<name>.*?)\s<(?P<email>.*?)>!', $subscriber, $subscriber_data);
if(!isset($subscriber_data['email'])) {
$subscriber_data = array(
'email' => $subscriber,
);
}
return array(
$subscriberData['email'] =>
(isset($subscriberData['name'])) ? $subscriberData['name'] : ''
$subscriber_data['email'] =>
(isset($subscriber_data['name'])) ? $subscriber_data['name'] : ''
);
}
}

View File

@ -1,14 +1,18 @@
<?php
namespace MailPoet\Mailer\API;
namespace MailPoet\Mailer\Methods;
if(!defined('ABSPATH')) exit;
class SendGrid {
function __construct($apiKey, $fromEmail, $fromName) {
$this->url = 'https://api.sendgrid.com/api/mail.send.json';
$this->apiKey = $apiKey;
$this->fromEmail = $fromEmail;
$this->fromName = $fromName;
public $url = 'https://api.sendgrid.com/api/mail.send.json';
public $api_key;
public $from_email;
public $from_name;
function __construct($api_key, $from_email, $from_name) {
$this->api_key = $api_key;
$this->from_email = $from_email;
$this->from_name = $from_name;
}
function send($newsletter, $subscriber) {
@ -27,8 +31,8 @@ class SendGrid {
function getBody($newsletter, $subscriber) {
$body = array(
'to' => $subscriber,
'from' => $this->fromEmail,
'fromname' => $this->fromName,
'from' => $this->from_email,
'from_name' => $this->from_name,
'subject' => $newsletter['subject']
);
if(!empty($newsletter['body']['html'])) {
@ -41,7 +45,7 @@ class SendGrid {
}
function auth() {
return 'Bearer ' . $this->apiKey;
return 'Bearer ' . $this->api_key;
}
function request($newsletter, $subscriber) {

View File

@ -1,22 +1,22 @@
<?php
namespace MailPoet\Mailer;
namespace MailPoet\Mailer\Methods;
require_once(ABSPATH . 'wp-includes/pluggable.php');
if(!defined('ABSPATH')) exit;
class WPMail {
function __construct($fromEmail, $fromName) {
$this->fromEmail = $fromEmail;
$this->fromName = $fromName;
add_filter('wp_mail_from', array(
$this,
'setFromEmail'
));
$this->filters = array(
'wp_mail_from' => 'setFromEmail',
'wp_mail_from_name' => 'setFromName',
'wp_mail_content_type' => 'setContentType'
);
public $from_email;
public $from_name;
public $filters = array(
'wp_mail_from' => 'setFromEmail',
'wp_mail_from_name' => 'setFromName',
'wp_mail_content_type' => 'setContentType'
);
function __construct($from_email, $from_name) {
$this->from_email = $from_email;
$this->from_name = $from_name;
}
function addFilters() {
@ -38,11 +38,11 @@ class WPMail {
}
function setFromEmail() {
return $this->fromEmail;
return $this->from_email;
}
function setFromName() {
return $this->fromName;
return $this->from_name;
}
function setContentType() {
@ -53,7 +53,9 @@ class WPMail {
$this->addFilters();
$result = wp_mail(
$subscriber, $newsletter['subject'],
(!empty($newsletter['body']['html'])) ? $newsletter['body']['html'] : $newsletter['body']['text']
(!empty($newsletter['body']['html'])) ?
$newsletter['body']['html'] :
$newsletter['body']['text']
);
$this->removeFilters();
return ($result === true);

View File

@ -4,7 +4,7 @@ namespace MailPoet\Models;
if(!defined('ABSPATH')) exit;
class SendingQueue extends Model {
public static $_table = MP_SENDING_QUEUE_TABLE;
public static $_table = MP_SENDING_QUEUES_TABLE;
function __construct() {
parent::__construct();

View File

@ -154,7 +154,7 @@ class Subscriber extends Model {
$orm = $orm->selectExpr(
'CASE WHEN ' .
MP_CUSTOM_FIELDS_TABLE . '.id=' . $customField['id'] . ' THEN ' .
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END as "' . $customField['name'].'"');
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END as "' . $customField['id'].'"');
}
$orm = $orm
->leftOuterJoin(

View File

@ -1,47 +1,40 @@
<?php namespace MailPoet\Newsletter\Renderer\Blocks;
<?php
namespace MailPoet\Newsletter\Renderer\Blocks;
use MailPoet\Newsletter\Renderer\StylesHelper;
class Button {
static function render($element) {
$stylesHelper = new StylesHelper();
$template = '
<tr>
<td class="mailpoet_col mailpoet_button mailpoet_padded" valign = "top" >
<div>
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td align="' . $element['styles']['block']['textAlign'] . '">
<!--[if mso]>
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:w="urn:schemas-microsoft-com:office:word"
href="' . $element['url'] . '"
style="height:' . $element['styles']['block']['lineHeight'] . ';
width:' . $element['styles']['block']['width'] . ';
v-text-anchor:middle;"
arcsize="' . round($element['styles']['block']['borderRadius'] / $element['styles']['block']['lineHeight'] * 100) . '%"
strokecolor="' . $element['styles']['block']['borderColor'] . '"
fillcolor="' . $element['styles']['block']['backgroundColor'] . '">
<tr>
<td class="mailpoet_padded" valign="top">
<div>
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0;">
<tr>
<td class="mailpoet_button-container" style="padding:8px 0;text-align:' . $element['styles']['block']['textAlign'] . ';"><!--[if mso]>
<v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word"
href="' . $element['url'] . '"
style="height:' . $element['styles']['block']['lineHeight'] . ';
width:' . $element['styles']['block']['width'] . ';
v-text-anchor:middle;"
arcsize="' . round($element['styles']['block']['borderRadius'] / $element['styles']['block']['lineHeight'] * 100) . '%"
strokeweight="1px"
strokecolor="' . $element['styles']['block']['borderColor'] . '"
fillcolor="' . $element['styles']['block']['backgroundColor'] . '">
<w:anchorlock/>
<center style="color:' . $element['styles']['block']['fontColor'] . ';
font-family:' . $element['styles']['block']['fontFamily'] . ';
font-size:' . $element['styles']['block']['fontSize'] . ';
font-weight:bold;">' . $element['text'] . '
font-family:' . $element['styles']['block']['fontFamily'] . ';
font-size:' . $element['styles']['block']['fontSize'] . ';
font-weight:bold;">' . $element['text'] . '
</center>
</v:roundrect>
<![endif]-->
<a class="mailpoet_button"
href="' . $element['url'] . '"
style="display:inline-block;text-align:center;text-decoration:none;-webkit-text-size-adjust:none;mso-hide:all;' . $stylesHelper->getBlockStyles($element, array('textAlign')) . '"> ' . $element['text'] . '
</a>
</td>
</tr>
</table>
</div>
</td>
</tr>';
<![endif]--><a class="mailpoet_button" href="' . $element['url'] . '" style="display:inline-block;-webkit-text-size-adjust:none;mso-hide:all;text-decoration:none!important;text-align:center;' . StylesHelper::getBlockStyles($element, $exclude = array('textAlign')) . '"> ' . $element['text'] . '
</td>
</tr>
</table>
</div>
</td>
</tr>';
return $template;
}
}

View File

@ -1,23 +1,34 @@
<?php namespace MailPoet\Newsletter\Renderer\Blocks;
<?php
namespace MailPoet\Newsletter\Renderer\Blocks;
use MailPoet\Newsletter\Renderer\StylesHelper;
class Divider {
static function render($element) {
$template = '
<tr>
<td class="mailpoet_col mailpoet_divider mailpoet_padded"
style="background-color: ' . $element['styles']['block']['backgroundColor'] . '; padding: ' . $element['styles']['block']['padding'] . ' 0;"
valign="top">
<table width="100%">
<tr>
<td style="border-top-width: ' . $element['styles']['block']['borderWidth'] . ';
border-top-style: ' . $element['styles']['block']['borderStyle'] . ';
border-top-color: ' . $element['styles']['block']['borderColor'] . ';">
</td>
</tr>
</table>
</td>
</tr>';
<tr>
<td class="mailpoet_divider" valign="top" ' .
(($element['styles']['block']['backgroundColor'] !== 'transparent') ?
'bgColor="' . $element['styles']['block']['backgroundColor'] . '" style="background-color:' . $element['styles']['block']['backgroundColor'] . ';' :
'style="'
) .
sprintf('padding: %s %spx %s %spx;',
$element['styles']['block']['padding'],
StylesHelper::$padding_width,
$element['styles']['block']['padding'],
StylesHelper::$padding_width) . '">
<table width="100%" border="0" cellpadding="0" cellspacing="0"
style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0;">
<tr>
<td class="mailpoet_divider-cell"
style="border-top-width: ' . $element['styles']['block']['borderWidth'] . ';
border-top-style: ' . $element['styles']['block']['borderStyle'] . ';
border-top-color: ' . $element['styles']['block']['borderColor'] . ';">
</td>
</tr>
</table>
</td>
</tr>';
return $template;
}
}

View File

@ -1,30 +1,27 @@
<?php namespace MailPoet\Newsletter\Renderer\Blocks;
<?php
namespace MailPoet\Newsletter\Renderer\Blocks;
use MailPoet\Newsletter\Renderer\StylesHelper;
class Footer {
static function render($element) {
$stylesHelper = new StylesHelper();
// apply link styles
if(isset($element['styles']['link'])) {
$element['text'] = str_replace('<a', '<a style="' . $stylesHelper->getStyles($element['styles'], 'link') . '"', $element['text']);
$element['text'] = str_replace(
'<a',
'<a style="'
. StylesHelper::getStyles($element['styles'], 'link')
. '"', $element['text']
);
}
// apply text styles
if(isset($element['styles']['link'])) {
$element['text'] = str_replace('<p', '<p style="' . $stylesHelper->getStyles($element['styles'], 'text') . '"', $element['text']);
}
$element['text'] = preg_replace('/\n/', '<br /><br />', $element['text']);
$element['text'] = preg_replace('/(<\/?p>)/', '', $element['text']);
$template = '
<tr>
<td class="mailpoet_col mailpoet_footer"
style="' . $stylesHelper->getStyles($element['styles'], 'block') . '"
valign="top">
<div>' . $element['text'] . '</div>
</td>
</tr>';
<tr>
<td class="mailpoet_padded_header_footer mailpoet_footer" bgcolor="' . $element['styles']['block']['backgroundColor'] . '"
style="' . StylesHelper::getBlockStyles($element) . StylesHelper::getStyles($element['styles'], 'text') . '">
' . $element['text'] . '
</td>
</tr>';
return $template;
}
}

View File

@ -1,30 +1,27 @@
<?php namespace MailPoet\Newsletter\Renderer\Blocks;
<?php
namespace MailPoet\Newsletter\Renderer\Blocks;
use MailPoet\Newsletter\Renderer\StylesHelper;
class Header {
static function render($element) {
$stylesHelper = new StylesHelper();
// apply link styles
if(isset($element['styles']['link'])) {
$element['text'] = str_replace('<a', '<a style="' . $stylesHelper->getStyles($element['styles'], 'link') . '"', $element['text']);
$element['text'] = str_replace(
'<a',
'<a style="'
. StylesHelper::getStyles($element['styles'], 'link')
. '"', $element['text']
);
}
// apply text styles
if(isset($element['styles']['link'])) {
$element['text'] = str_replace('<p', '<p style="' . $stylesHelper->getStyles($element['styles'], 'text') . '"', $element['text']);
}
$element['text'] = preg_replace('/\n/', '<br /><br />', $element['text']);
$element['text'] = preg_replace('/(<\/?p>)/', '', $element['text']);
$template = '
<tr>
<td class="mailpoet_col mailpoet_header"
style="' . $stylesHelper->getBlockStyles($element) . '"
valign="top">
<div>' . $element['text'] . '</div>
</td>
</tr>';
<tr>
<td class="mailpoet_padded_header_footer mailpoet_header" bgcolor="' . $element['styles']['block']['backgroundColor'] . '"
style="' . StylesHelper::getBlockStyles($element) . StylesHelper::getStyles($element['styles'], 'text') . '">
' . $element['text'] . '
</td>
</tr>';
return $template;
}
}

View File

@ -1,24 +1,42 @@
<?php namespace MailPoet\Newsletter\Renderer\Blocks;
<?php
namespace MailPoet\Newsletter\Renderer\Blocks;
use MailPoet\Newsletter\Renderer\Columns\ColumnsHelper;
use MailPoet\Newsletter\Renderer\StylesHelper;
class Image {
static function render($element) {
$stylesHelper = new StylesHelper();
$element['width'] = (int) $element['width'];
static function render($element, $columnCount) {
$element = self::getImageDimensions($element, $columnCount);
$template = '
<tr>
<td class="mailpoet_col mailpoet_image ' . (($element['padded'] === true) ? "mailpoet_padded" : "") . '"
style="' . $stylesHelper->getBlockStyles($element) . '"
valign="top">
<img style="top:0; left:0; height: auto; width:100%;"
src="' . $element['src'] . '"
width="' . (($element['padded'] === true) ? $element['width'] - (20 * 2) : $element['width']) . '">
</td>
</tr>';
<tr>
<td class="mailpoet_image ' . $element['paddedClass'] . '" align="center" valign="top">
<img style="max-width:' . $element['width'] . 'px;" src="' . $element['src'] . '"
width="' . $element['width'] . '" height="' . $element['height'] . '" alt="' . $element['alt'] . '"/>
</td>
</tr>';
return $template;
}
static function getImageDimensions($element, $column_count) {
$column_width = ColumnsHelper::columnWidth($column_count);
$padded_width = StylesHelper::$padding_width * 2;
// resize image if it's wider than the column width
if((int) $element['width'] >= $column_width) {
$ratio = (int) $element['width'] / $column_width;
$element['width'] = $column_width;
$element['height'] = ceil((int) $element['height'] / $ratio);
}
if($element['padded'] == "true" && $element['width'] >= $column_width) {
// resize image if the padded option is on
$ratio = (int) $element['width'] / ((int) $element['width'] - $padded_width);
$element['width'] = (int) $element['width'] - $padded_width;
$element['height'] = ceil((int) $element['height'] / $ratio);
$element['paddedClass'] = 'mailpoet_padded';
} else {
$element['width'] = (int) $element['width'];
$element['height'] = (int) $element['height'];
$element['paddedClass'] = '';
}
return $element;
}
}

View File

@ -1,24 +1,24 @@
<?php namespace MailPoet\Newsletter\Renderer\Blocks;
<?php
namespace MailPoet\Newsletter\Renderer\Blocks;
class Renderer {
function render($data) {
array_map(function ($block) use (&$blockContent, &$columns) {
$blockContent .= $this->createElementFromBlockType($block);
function render($data, $column_count) {
$block_content = '';
array_map(function ($block) use (&$block_content, &$columns, $column_count) {
$block_content .= $this->createElementFromBlockType($block, $column_count);
if(isset($block['blocks'])) {
$blockContent = $this->render($block);
$block_content = $this->render($block, $column_count);
}
// vertical orientation denotes column container
if($block['type'] === 'container' && $block['orientation'] === 'vertical') {
$columns[] = $blockContent;
$columns[] = $block_content;
}
}, $data['blocks']);
return (isset($columns)) ? $columns : $blockContent;
return (isset($columns)) ? $columns : $block_content;
}
function createElementFromBlockType($block) {
$blockClass = __NAMESPACE__ . '\\' . ucfirst($block['type']);
return (class_exists($blockClass)) ? $blockClass::render($block) : '';
function createElementFromBlockType($block, $column_count) {
$block_class = __NAMESPACE__ . '\\' . ucfirst($block['type']);
return (class_exists($block_class)) ? $block_class::render($block, $column_count) : '';
}
}
}

View File

@ -1,26 +1,23 @@
<?php namespace MailPoet\Newsletter\Renderer\Blocks;
<?php
namespace MailPoet\Newsletter\Renderer\Blocks;
class Social {
static function render($element) {
$iconsBlock = '';
$icons_block = '';
if(is_array($element['icons'])) {
foreach ($element['icons'] as $icon) {
$iconsBlock .= '
<a href="' . $icon['link'] . '">
<img src="' . $icon['image'] . '" width = "32" height = "32" style="width: 32px; height: 32px;" alt="' . $icon['iconType'] . '">
</a>
<img src="http://mp3.mailpoet.net/spacer.gif" width = "10" height = "1" style=" width: 10px; height: 1px;">';
foreach($element['icons'] as $index => $icon) {
$icons_block .= '
<a href="' . $icon['link'] . '" style="text-decoration:none!important;">
<img src="' . $icon['image'] . '" width="' . (int) $icon['width'] . '" height="' . (int) $icon['height'] . '" style="width:' . $icon['width'] . ';height:' . $icon['width'] . ';-ms-interpolation-mode:bicubic;border:0;display:inline;outline:none;" alt="' . $icon['iconType'] . '">
</a>';
}
$template = '
<tr>
<td class="mailpoet_padded" valign="top" align="center">
' . $icons_block . '
</td>
</tr>';
return $template;
}
$template = '
<tr>
<td class="mailpoet_col mailpoet_social" valign="top">
<div class="mailpoet_social-icon mailpoet_padded">' . $iconsBlock . ' </div>
</td>
</tr>';
return $template;
}
}

View File

@ -1,23 +1,12 @@
<?php namespace MailPoet\Newsletter\Renderer\Blocks;
use MailPoet\Newsletter\Renderer\StylesHelper;
<?php
namespace MailPoet\Newsletter\Renderer\Blocks;
class Spacer {
static function render($element) {
$stylesHelper = new StylesHelper();
// if the parent container (column) has background set and the divider element has a transparent background,
// it will assume the newsletter background, not that of the parent container
if($element['styles']['block']['backgroundColor'] === 'transparent') {
unset($element['styles']['block']['backgroundColor']);
}
$template = '
<tr>
<td class="mailpoet_col mailpoet_spacer" style="' . $stylesHelper->getBlockStyles($element) . '" valign="top"> </td>
</tr>';
<tr>
<td class="mailpoet_spacer" height="' . (int) $element['styles']['block']['height'] . '" valign="top"></td>
</tr>';
return $template;
}
}

View File

@ -1,114 +1,110 @@
<?php namespace MailPoet\Newsletter\Renderer\Blocks;
<?php
namespace MailPoet\Newsletter\Renderer\Blocks;
class Text {
static $typeFace = array(
'Arial' => "Arial, 'Helvetica Neue', Helvetica, sans-serif",
'Comic Sans MS' => "'Comic Sans MS', 'Marker Felt-Thin', Arial, sans-serif",
'Courier New' => "'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace",
'Georgia' => "Georgia, Times, 'Times New Roman', serif",
'Lucida' => "'Lucida Sans Unicode', 'Lucida Grande', sans-serif",
'Tahoma' => "Tahoma, Verdana, Segoe, sans-serif",
'Times New Roman' => "'Times New Roman', Times, Baskerville, Georgia, serif",
'Trebuchet MS' => "'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif",
'Verdana' => "Verdana, Geneva, sans-serif"
);
static function render($element) {
$html = $element['text'];
$html = self::convertBlockquotesToTables($html);
$html = self::addLineBreakAfterTags($html);
$html = self::removeEmptyTags($html);
$html = self::convertEmptyParagraphsToLineBreaks($html);
$html = self::convertParagraphsToTables($html);
$html = self::removeLastBreakLine($html);
$html = self::addLineBreakAfterTags($html);
$html = self::styleLists($html);
$html = self::styleHeadings($html);
$html = self::removeLastElementBreakLine($html);
$template = '
<tr>
<td class="mailpoet_col mailpoet_text mailpoet_padded" valign="top">' . $html . ' </td>
</tr>';
<tr>
<td class="mailpoet_text mailpoet_padded" valign="top" style="word-break:break-word;word-wrap:break-word;">
' . $html . '
</td>
</tr>';
return $template;
}
static function removeLastBreakLine($html) {
return preg_replace('/<br>([^<br>]*)$/s', '', $html);
}
static function convertParagraphsToTables($html) {
$html = preg_replace('/<p>(.*?)<\/p>/', '
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td>
<span class="paragraph">
<table width="100%" cellpadding="0" cellspacing="0" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0">
<tr>
<td class="mailpoet_paragraph" style="word-break:break-word;word-wrap:break-word;">
$1
</span>
</td>
</tr>
</table>', $html);
return preg_replace('/<p(.+style=\".*?\")?>(.*?)<\/p>/', '
<table cellpadding="0" cellspacing="0" border="0">
<tr>
<td $1>
<span class="paragraph">
<br /><br />
</td>
</tr>
</table>'
, $html);
$html = preg_replace('/<p style=\"(.*)\">(.*?)<\/p>/', '
<table width="100%" cellpadding="0" cellspacing="0" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0">
<tr>
<td class="mailpoet_paragraph" style="word-break:break-word;word-wrap:break-word;$1">
$2
</span>
</td>
</tr>
</table>', $html);
<br /><br />
</td>
</tr>
</table>'
, $html);
return $html;
}
static function convertEmptyParagraphsToLineBreaks($html) {
return preg_replace('/<p(?:.+style=\".*?\")?><\/p>/', '<br>', $html);
static function removeLastElementBreakLine($html) {
return preg_replace('/<br\/>([^<br\/>]*)$/s', '', $html);
}
static function addLineBreakAfterTags($html) {
return preg_replace('/(<\/(ul|ol|h\d)>)/', '$1<br>', $html);
return preg_replace('/(<\/(ul|ol|h\d)>)/', '$1<br />', $html);
}
static function convertBlockquotesToTables($html) {
$template = '
<table cellpadding="0" cellspacing="0" border="0" class="mailpoet_blockquote">
<tbody>
<tr>
<td valign="top">$1</td>
</tr>
</tbody>
</table>
<br>';
return preg_replace('/<blockquote>(.*?)<\/blockquote>/s', $template, $html);
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tbody>
<tr>
<td width="2" bgcolor="#565656"></td>
<td width="10"></td>
<td valign="top">
<table style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0">
<tr>
<td class="mailpoet_blockquote">
$1
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<br/>';
preg_match('/<blockquote>.*?<\/blockquote>/s', $html, $blockquotes);
foreach($blockquotes as $index => $blockquote) {
$blockquote = preg_replace('/<\/p>\n<p>/', '<br/><br/>', $blockquote);
$blockquote = preg_replace('/<\/?p>/', '', $blockquote);
$blockquote = preg_replace(
'/<blockquote>(.*?)<\/blockquote>/s',
$template,
$blockquote
);
$html = preg_replace(
'/' . preg_quote($blockquotes[$index], '/') . '/',
$blockquote,
$html
);
}
return $html;
}
static function removeEmptyTags($html) {
$pattern = <<<'EOD'
~
<
(?:
!--[^-]*(?:-(?!->)[^-]*)*-->[^<]*(*SKIP)(*F) # skip comments
|
( # group 1
(span|em|strong) # tag name in group 2
[^"'>]* #'"# all that is not a quote or a closing angle bracket
(?: # quoted attributes
"[^\\"]*(?:\\.[^\\"]*)*+" [^"'>]* #'"# double quote
|
'[^\\']*(?:\\.[^\\']*)*+' [^"'>]* #'"# single quote
)*+
>
\s*
(?:
<!--[^-]*(?:-(?!->)[^-]*)*+--> \s* # html comments
|
<(?1) \s* # recursion with the group 1
)*+
</\2> # closing tag
) # end of the group 1
)
~sxi
EOD;
return preg_replace($pattern, '', $html);
static function styleHeadings($html) {
return preg_replace(
'/<(h[1-6])(?:.+style=\"(.*)?\")?>/',
'<$1 style="margin:0;font-style:normal;font-weight:normal;$2">',
$html
);
}
static function styleLists($html) {
$html = preg_replace(
'/<(ul|ol)>/',
'<$1 class="mailpoet_paragraph" style="padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;">',
$html
);
$html = preg_replace('/<li>/', '<li class="mailpoet_paragraph">', $html);
return $html;
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace MailPoet\Newsletter\Renderer\Columns;
class ColumnsHelper {
static $columns_width = array(
1 => 660,
2 => 330,
3 => 220
);
static $columns_class = array(
1 => 'cols-one',
2 => 'cols-two',
3 => 'cols-three'
);
static $columns_alignment = array(
1 => null,
2 => 'left',
3 => 'right'
);
static function columnWidth($columns_count) {
return self::$columns_width[$columns_count];
}
static function columnClass($columns_count) {
return self::$columns_class[$columns_count];
}
static function columnClasses() {
return self::$columns_class;
}
static function columnAlignment($columns_count) {
return self::$columns_alignment[$columns_count];
}
}

View File

@ -1,76 +1,78 @@
<?php namespace MailPoet\Newsletter\Renderer\Columns;
<?php
namespace MailPoet\Newsletter\Renderer\Columns;
class Renderer {
public $columnWidths = array(
1 => 600,
2 => 300,
3 => 200
);
public $columnClasses = array(
1 => 'mailpoet_col-one',
2 => 'mailpoet_col-two',
3 => 'mailpoet_col-three'
);
function render($columnsCount, $columnsData) {
$columnWidth = $this->columnWidths[$columnsCount];
$columnClass = $this->columnClasses[$columnsCount];
// open column container
$columnContainerTemplate = '
<tr>
<td class="mailpoet_content" align="left" style="border-collapse: collapse;">
<table width="100%" border="0" cellpadding="0" cellspacing="0" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0">
<tbody>
<tr>
<td class="mailpoet_cols-wrapper" style="border-collapse: collapse; padding-left: 0px; padding-right: 0px;">
<!--[if mso]>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td width="' . $columnWidth . '" style="width: ' . $columnWidth . 'px;" valign="top">
<![endif]-->';
$columnOpenTemplate = '
<table width="' . $columnWidth . '"
border="0" cellpadding="0" cellspacing="0" align="left" class="mailpoet_force-row ' . $columnClass . ' mailpoet_col"
style="width: ' . $columnWidth . 'px; border-spacing: 0; mso-table-lspace: 0pt; mso-table-rspace: 0pt;
table-layout: fixed; margin-left: auto; margin-right: auto;" bgcolor="#999999">
<tbody>';
$columnCloseTemplate = '
</tbody>
</table>
<!--[if mso]>
</td>
<td width="' . $columnWidth . '" style="width: ' . $columnWidth . 'px;" valign="top">
<![endif]-->';
foreach ($columnsData as $index => $columnData) {
$index++;
$columnContainerTemplate .= $columnOpenTemplate . $columnData;
if($columnsCount > 1 && $index != $columnsCount) {
$columnContainerTemplate .= $columnCloseTemplate;
}
function render($column_styles, $columns_count, $columns_data) {
$styles = $column_styles['block'];
$width = ColumnsHelper::columnWidth($columns_count);
$class = ColumnsHelper::columnClass($columns_count);
$alignment = ColumnsHelper::columnAlignment($columns_count);
$template = ($columns_count === 1) ?
$this->getOneColumnTemplate($styles, $class) :
$this->getMultipleColumnsTemplate($styles, $width, $alignment, $class);
$result = array_map(function ($content) use ($template) {
return $template['content_start'] . $content . $template['content_end'];
}, $columns_data);
$result = implode('', $result);
if($columns_count !== 1) {
$result = $template['container_start'] . $result . $template['container_end'];
}
return $result;
}
// close column container
$columnContainerTemplate .= '
</tbody>
</table>
<!--[if mso]>
</td>
</tr>
</tbody>
</table>
<![endif]-->
</td>
</tr>
function getOneColumnTemplate($styles, $class) {
$template['content_start'] = '
<tr>
<td class="mailpoet_content" align="center" style="border-collapse:collapse">
<table width="100%" border="0" cellpadding="0" cellspacing="0" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0">
<tbody>
<tr>
<td style="padding-left:0;padding-right:0">
<table width="100%" border="0" cellpadding="0" cellspacing="0" class="mailpoet_' . $class . '" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0;table-layout:fixed;margin-left:auto;margin-right:auto;padding-left:0;padding-right:0;background-color:' . $styles['backgroundColor'] . '!important;" bgcolor="' . $styles['backgroundColor'] . '">
<tbody>';
$template['content_end'] = '
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>';
return $template;
}
function getMultipleColumnsTemplate($styles, $width, $alignment, $class) {
$template['container_start'] = '
<tr>
<td class="mailpoet_content-' . $class . '" align="left" style="border-collapse:collapse;background-color:' . $styles['backgroundColor'] . '!important;" bgcolor="' . $styles['backgroundColor'] . '">
<table width="100%" border="0" cellpadding="0" cellspacing="0" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0">
<tbody>
<tr>
<td align="center" style="font-size:0;"><!--[if mso]>
<table border="0" width="100%" cellpadding="0" cellspacing="0">
<tbody>
<tr>';
$template['content_start'] = '
<td width="' . $width . '" valign="top">
<![endif]--><div style="display:inline-block; max-width:' . $width . 'px; vertical-align:top; width:100%;">
<table width="' . $width . '" class="mailpoet_' . $class . '" border="0" cellpadding="0" cellspacing="0" align="' . $alignment . '" style="width:100%;max-width:' . $width . 'px;border-spacing:0;mso-table-lspace:0;mso-table-rspace:0;table-layout:fixed;margin-left:auto;margin-right:auto;padding-left:0;padding-right:0;">
<tbody>';
$template['content_end'] = '
</tbody>
</table>
</div><!--[if mso]>
</td>';
$template['container_end'] = '
</tr>
</tbody>
</table>
<![endif]--></td>
</tr>
</tbody>
</table>
</td>
</tr>';
return $columnContainerTemplate;
return $template;
}
}
}

View File

@ -1,87 +1,124 @@
<?php
namespace MailPoet\Newsletter\Renderer;
if(!defined('ABSPATH')) exit;
class Renderer {
public $template = 'Template.html';
public $blocks_renderer;
public $columns_renderer;
public $DOM_parser;
public $CSS_inliner;
public $newsletter;
function __construct($newsletterData) {
$this->blocksRenderer = new Blocks\Renderer();
$this->columnsRenderer = new Columns\Renderer();
$this->stylesHelper = new StylesHelper();
$this->DOMQuery = new \pQuery();
$this->CSSInliner = new \MailPoet\Util\CSS();
$this->data = $newsletterData;
function __construct($newsletter) {
$this->blocks_renderer = new Blocks\Renderer();
$this->columns_renderer = new Columns\Renderer();
$this->DOM_parser = new \pQuery();
$this->CSS_inliner = new \MailPoet\Util\CSS();
$this->newsletter = $newsletter;
$this->template = file_get_contents(dirname(__FILE__) . '/' . $this->template);
}
function renderAll() {
$newsletterContent = $this->renderContent($this->data['content']);
$newsletterStyles = $this->renderStyles($this->data['globalStyles']);
$renderedTemplate = $this->renderTemplate($this->template, array(
$newsletterStyles,
$newsletterContent
function render() {
$newsletter_data = (is_array($this->newsletter['body'])) ?
$this->newsletter['body'] :
json_decode($this->newsletter['body'], true);
$newsletter_body = $this->renderBody($newsletter_data['content']);
$newsletter_styles = $this->renderStyles($newsletter_data['globalStyles']);
$newsletter_subject = $this->newsletter['subject'];
$newsletter_preheader = $this->newsletter['preheader'];
$template = $this->injectContentIntoTemplate($this->template, array(
$newsletter_subject,
$newsletter_styles,
$newsletter_preheader,
$newsletter_body
));
$renderedTemplateWithInlinedStyles = $this->inlineCSSStyles($renderedTemplate);
return $this->postProcessRenderedTemplate($renderedTemplateWithInlinedStyles);
$template = $this->inlineCSSStyles($template);
return $this->postProcessTemplate($template);
}
function renderContent($content) {
$newsletterContent = array_map(function ($contentBlock) {
$columnCount = count($contentBlock['blocks']);
$columnData = $this->blocksRenderer->render($contentBlock);
return $this->columnsRenderer->render($columnCount, $columnData);
function renderBody($content) {
$content = array_map(function ($content_block) {
$column_count = count($content_block['blocks']);
$column_data = $this->blocks_renderer->render($content_block, $column_count);
return $this->columns_renderer->render(
$content_block['styles'],
$column_count,
$column_data
);
}, $content['blocks']);
return implode('', $newsletterContent);
return implode('', $content);
}
function renderStyles($styles) {
$newsletterStyles = '';
foreach ($styles as $selector => $style) {
switch ($selector) {
$css = '';
foreach($styles as $selector => $style) {
switch($selector) {
case 'h1':
$selector = 'h1';
break;
case 'h2':
$selector = 'h2';
break;
case 'h3':
$selector = 'h3';
break;
case 'text':
$selector = 'span.paragraph, ul, ol';
$selector = '.mailpoet_paragraph, .mailpoet_blockquote';
break;
case 'body':
$selector = '.mailpoet_content-wrapper';
$selector = 'body, .mailpoet-wrapper';
break;
case 'link':
$selector = '.mailpoet_content-wrapper a';
$selector = '.mailpoet-wrapper a';
break;
case 'wrapper':
$selector = '.mailpoet_container, .mailpoet_col-one, .mailpoet_col-two, .mailpoet_col-three';
$selector = '.mailpoet_content-wrapper';
break;
}
$newsletterStyles .= $selector . '{' . PHP_EOL;
foreach ($style as $attribute => $individualStyle) {
$newsletterStyles .= $this->stylesHelper->translateCSSAttribute($attribute) . ':' . $individualStyle . ';' . PHP_EOL;
if(isset($style['fontSize'])) {
$css .= StylesHelper::setFontAndLineHeight(
(int) $style['fontSize'],
$selector
);
unset($style['fontSize']);
}
$newsletterStyles .= '}' . PHP_EOL;
if(isset($style['fontFamily'])) {
$css .= StylesHelper::setFontFamily(
$style['fontFamily'],
$selector
);
unset($style['fontFamily']);
}
$css .= StylesHelper::setStyle($style, $selector);
}
return $newsletterStyles;
return $css;
}
function renderTemplate($template, $data) {
function injectContentIntoTemplate($template, $data) {
return preg_replace_callback('/{{\w+}}/', function ($matches) use (&$data) {
return array_shift($data);
}, $template);
}
function inlineCSSStyles($template) {
return $this->CSSInliner->inlineCSS(null, $template);
return $this->CSS_inliner->inlineCSS(null, $template);
}
function postProcessRenderedTemplate($template) {
// remove padding from last element inside each column
$DOM = $this->DOMQuery->parseStr($template);
$lastColumnElement = $DOM->query('.mailpoet_col > tbody > tr:last-child > td');
foreach ($lastColumnElement as $element) {
$element->setAttribute('style', str_replace('padding-bottom:20px;', '', $element->attributes['style']));
}
function renderTextVersion($template) {
// TODO: add text rendering
return $template;
}
function postProcessTemplate($template) {
// replace all !important tags except for in the body tag
$DOM = $this->DOM_parser->parseStr($template);
$template = $DOM->query('.mailpoet_template');
$template->html(
str_replace('!important', '', $template->html())
);
// TODO: return array with html and text body
return $DOM->__toString();
}
}

View File

@ -1,7 +1,10 @@
<?php namespace MailPoet\Newsletter\Renderer;
<?php
namespace MailPoet\Newsletter\Renderer;
use MailPoet\Newsletter\Renderer\Columns\ColumnsHelper;
class StylesHelper {
public $cssAtributesTable = array(
static $css_attributes = array(
'backgroundColor' => 'background-color',
'fontColor' => 'color',
'fontFamily' => 'font-family',
@ -14,26 +17,236 @@ class StylesHelper {
'borderRadius' => 'border-radius',
'lineHeight' => 'line-height'
);
static $font = array(
'Arial' => "Arial, 'Helvetica Neue', Helvetica, sans-serif",
'Comic Sans MS' => "'Comic Sans MS', 'Marker Felt-Thin', Arial, sans-serif",
'Courier New' => "'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace",
'Georgia' => "Georgia, Times, 'Times New Roman', serif",
'Lucida' => "'Lucida Sans Unicode', 'Lucida Grande', sans-serif",
'Tahoma' => 'Tahoma, Verdana, Segoe, sans-serif',
'Times New Roman' => "'Times New Roman', Times, Baskerville, Georgia, serif",
'Trebuchet MS' => "'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif",
'Verdana' => 'Verdana, Geneva, sans-serif'
);
static $font_size = array(
// font_size => array(columnCount => lineHeight);
8 => array(
1 => "20",
2 => "15",
3 => "13"
),
9 => array(
1 => "20",
2 => "16",
3 => "14"
),
10 => array(
1 => "20",
2 => "17",
3 => "15"
),
11 => array(
1 => "21",
2 => "18",
3 => "16"
),
12 => array(
1 => "22",
2 => "19",
3 => "17"
),
13 => array(
1 => "23",
2 => "20",
3 => "19"
),
14 => array(
1 => "24",
2 => "21",
3 => "20"
),
15 => array(
1 => "25",
2 => "22",
3 => "21"
),
16 => array(
1 => "26",
2 => "23",
3 => "22"
),
17 => array(
1 => "27",
2 => "24",
3 => "24"
),
18 => array(
1 => "28",
2 => "25",
3 => "25"
),
19 => array(
1 => "29",
2 => "27",
3 => "26"
),
20 => array(
1 => "30",
2 => "28",
3 => "27"
),
21 => array(
1 => "31",
2 => "29",
3 => "29"
),
22 => array(
1 => "32",
2 => "30",
3 => "30"
),
23 => array(
1 => "33",
2 => "32",
3 => "31"
),
24 => array(
1 => "34",
2 => "33",
3 => "32"
),
25 => array(
1 => "36",
2 => "34",
3 => "34"
),
26 => array(
1 => "37",
2 => "35",
3 => "35"
),
27 => array(
1 => "38",
2 => "37",
3 => "36"
),
28 => array(
1 => "39",
2 => "38",
3 => "37"
),
29 => array(
1 => "40",
2 => "39",
3 => "39"
),
30 => array(
1 => "42",
2 => "40",
3 => "40"
),
31 => array(
1 => "43",
2 => "42",
3 => "41"
),
32 => array(
1 => "44",
2 => "43",
3 => "43"
),
33 => array(
1 => "45",
2 => "44",
3 => "44"
),
34 => array(
1 => "47",
2 => "46",
3 => "45"
),
35 => array(
1 => "48",
2 => "47",
3 => "46"
),
36 => array(
1 => "49",
2 => "48",
3 => "48"
),
37 => array(
1 => "50",
2 => "49",
3 => "49"
),
38 => array(
1 => "52",
2 => "51",
3 => "50"
),
39 => array(
1 => "53",
2 => "52",
3 => "52"
),
40 => array(
1 => "54",
2 => "53",
3 => "53"
)
);
static $padding_width = 20;
function getBlockStyles($element, $ignoreSpecificStyles = false) {
static function getBlockStyles($element, $ignore_specific_styles = false) {
if(!isset($element['styles']['block'])) {
return;
}
return $this->getStyles($element['styles'], 'block', $ignoreSpecificStyles);
return self::getStyles($element['styles'], 'block', $ignore_specific_styles);
}
function getStyles($data, $type, $ignoreSpecificStyles = false) {
$styles = array_map(function ($attribute, $style) use($ignoreSpecificStyles) {
if(!$ignoreSpecificStyles || !in_array($attribute, $ignoreSpecificStyles)) {
return $this->translateCSSAttribute($attribute) . ': ' . $style . ' !important;';
static function getStyles($data, $type, $ignore_specific_styles = false) {
$styles = array_map(function ($attribute, $style) use ($ignore_specific_styles) {
if(!$ignore_specific_styles || !in_array($attribute, $ignore_specific_styles)) {
return self::translateCSSAttribute($attribute) . ': ' . $style . ' !important;';
}
}, array_keys($data[$type]), $data[$type]);
return implode('', $styles);
}
function translateCSSAttribute($attribute) {
return (array_key_exists($attribute, $this->cssAtributesTable)) ? $this->cssAtributesTable[$attribute] : $attribute;
static function translateCSSAttribute($attribute) {
return (array_key_exists($attribute, self::$css_attributes)) ?
self::$css_attributes[$attribute] :
$attribute;
}
}
static function setFontFamily($font_family, $selector) {
$font_family = (isset(self::$font[$font_family])) ?
self::$font[$font_family] :
self::$font['Arial'];
$css = $selector . '{' . PHP_EOL;
$css .= 'font-family:' . $font_family . ';' . PHP_EOL;
$css .= '}' . PHP_EOL;
return $css;
}
static function setFontAndLineHeight($font_size, $selector) {
$css = '';
foreach(ColumnsHelper::columnClasses() as $column_count => $column_class) {
$css .= '.mailpoet_content-' . $column_class . ' ' . $selector . '{' . PHP_EOL;
$css .= 'font-size:' . $font_size . 'px;' . PHP_EOL;
$css .= 'line-height:' . StylesHelper::$font_size[$font_size][$column_count] . 'px;' . PHP_EOL;
$css .= '}' . PHP_EOL;
}
return $css;
}
static function setStyle($style, $selector) {
$css = $selector . '{' . PHP_EOL;
foreach($style as $attribute => $individual_style) {
$css .= self::translateCSSAttribute($attribute) . ':' . $individual_style . ';' . PHP_EOL;
}
$css .= '}' . PHP_EOL;
return $css;
}
}

View File

@ -1,266 +1,91 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="format-detection" content="telephone=no">
<title>MailPoet Newsletter Template</title>
<title>{{newsletter_subject}}</title>
<style type="text/css">
html, body {
margin: 0;
padding: 0;
}
.ExternalClass * {
line-height: 130%;
}
.ExternalClass a {
line-height: 140%;
}
.ExternalClass h1, h2, h3, h1, h2, h3 {
Margin: 0;
}
.ExternalClass ol {
margin-top: 0;
margin-bottom: 0;
}
table, td {
border-collapse: collapse;
}
.mailpoet_image img {
height: auto;
width: 100%;
-ms-interpolation-mode: bicubic;
border: 0;
display: block;
outline: none;
text-align: center;
}
.mailpoet_padded {
padding-left: 20px;
padding-right: 20px;
padding-bottom: 20px;
}
.mailpoet_padded_header_footer {
padding: 10px 20px;
}
@media screen and (max-width: 599px) and (-webkit-min-device-pixel-ratio: 1) {
.mailpoet_header {
padding: 10px 20px;
}
.mailpoet_button {
width: 100% !important;
max-width: 100% !important;
padding: 5px 0 !important;
}
div, .mailpoet_cols-two, .mailpoet_cols-three {
max-width: 100% !important;
}
}
{{newsletter_styles}}
</style>
</head>
<body style="-ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; mso-line-height-rule: exactly; font-family: Helvetica, Arial, sans-serif; text-align: left; color: #333333; margin: 0; padding: 0;"
bgcolor="#F0F0F0" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
<style type="text/css">
body {
margin: 0;
padding: 0;
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
mso-line-height-rule: exactly;
text-align: left;
}
h1,
h2,
h3,
p,
ol,
ul,
li {
margin: 0;
}
ol,
ul,
li {
padding-left: 0;
}
.ExternalClass {
width: 100%;
}
.ExternalClass,
.ExternalClass p,
.ExternalClass span,
.ExternalClass font,
.ExternalClass td,
.ExternalClass div {
line-height: 100%;
}
.ReadMsgBody {
width: 100%;
background-color: #ebebeb;
}
table {
border-spacing: 0;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
}
p {
margin: 0;
padding: 0;
margin-bottom: 0;
}
table td {
border-collapse: collapse;
}
img {
-ms-interpolation-mode: bicubic;
margin: 0;
border: 0;
padding: 0;
display: block;
}
td.mailpoet_button, .mailpoet_image, .mailpoet_text, .mailpoet_social {
padding-bottom: 20px;
}
.mailpoet_button div {
padding: 8px 0 8px 0;
}
.yshortcuts a {
border-bottom: none !important;
}
.mailpoet_container {
width: 600px;
max-width: 600px;
}
.mailpoet_col,
.mailpoet_cols-wrapper {
padding-left: 0px;
padding-right: 0px;
}
.mailpoet_col-one,
.mailpoet_col-two,
.mailpoet_col-three {
table-layout: fixed;
margin-left: auto;
margin-right: auto;
}
.mailpoet_col-one,
.mailpoet_col-two,
.mailpoet_col-three,
.mailpoet_header,
.mailpoet_footer {
margin-left: auto;
margin-right: auto;
}
.mailpoet_footer div, .mailpoet_header div {
margin: 5px 0 5px 0;
}
/* .mailpoet_col-one ol,*/
.mailpoet_col-one ul {
margin-left: 18px;
}
/*.mailpoet_col-two ol,*/
.mailpoet_col-two ul {
margin-left: 16px;
}
.mailpoet_col-two li {
padding-left: 5px;
}
/*.mailpoet_col-three ol,*/
.mailpoet_col-three ul {
margin-left: 14px;
}
.mailpoet_col-three li {
padding-left: 6px;
}
.mailpoet_content-wrapper a {
text-decoration: underline;
}
.mailpoet_padded {
padding-left: 20px;
padding-right: 20px;
/*word-break: break-all;*/
word-wrap: break-word;
}
.mailpoet_centered {
margin-left: auto;
margin-right: auto;
}
.mailpoet_center {
text-align: center;
}
.mailpoet_right {
text-align: right;
}
.mailpoet_left {
text-align: left;
}
.mailpoet_justify {
text-align: justify;
}
.mailpoet_blockquote {
border-left: 2px #565656 solid;
padding-left: 10px;
font-style: italic;
}
.mailpoet_blockquote p.mailpoet_signature {
font-style: normal;
font-weight: bold;
}
.mailpoet_social {
text-align: center;
}
.mailpoet_social img {
display: inline;
}
.mailpoet_social div {
padding: 5px 0 5px 0;
}
.ios-footer a {
color: #aaaaaa !important;
text-decoration: underline;
}
@media screen and (max-width: 400px) and (-webkit-min-device-pixel-ratio: 1) {
[class="mailpoet_cols-wrapper"] {
padding-left: 0 !important;
padding-right: 0 !important;
}
}
@media screen and (max-width: 599px) and (-webkit-min-device-pixel-ratio: 1) {
[class~="mailpoet_force-row"],
[class="mailpoet_container"] {
width: 100% !important;
max-width: 100% !important;
}
a[class="mailpoet_button"] {
width: 100% !important;
max-width: 100% !important;
padding: 5px 0 !important;
}
td[class="mailpoet_col"] {
width: 100% !important;
}
[class=mailpoet_wrapper] ol,
[class=mailpoet_wrapper] ul {
margin-left: 18px !important;
}
[class=mailpoet_wrapper] li {
padding-left: 2px !important;
}
.mailpoet_social img {
height: 32px !important;
width: 32px !important;
padding-bottom: 20px !important;
}
}
{{newsletter_editor_styles}}
</style>
<table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0">
<tbody>
<tr>
<td align="center" class="mailpoet_content-wrapper" valign="top" style="border-collapse:collapse;" bgcolor="#333333">
<table border="0" width="600" cellpadding="0" cellspacing="0" class="mailpoet_container" style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0;max-width:600px">
<tbody>
{{newsletter_editor_content}}
</tbody>
</table>
</td>
</tr>
</table>
<body leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
<table class="mailpoet_template" border="0" width="100%" cellpadding="0" cellspacing="0"
style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0">
<tbody>
<tr>
<td class="mailpoet_preheader" style="-webkit-text-size-adjust:none;font-size:1px;line-height:1px;color:#333333;" height="1">
{{newsletter_preheader}}
</td>
</tr>
<tr>
<td align="center" class="mailpoet-wrapper" valign="top"><!--[if mso]>
<table align="center" border="0" cellspacing="0" cellpadding="0"
width="660">
<tr>
<td class="mailpoet_content-wrapper" align="center" valign="top" width="660">
<![endif]--><table class="mailpoet_content-wrapper" border="0" width="660" cellpadding="0" cellspacing="0"
style="border-spacing:0;mso-table-lspace:0;mso-table-rspace:0;max-width:660px;width:100%;">
<tbody>
{{newsletter_body}}
</tbody>
</table><!--[if mso]>
</td>
</tr>
</table>
<![endif]--></td>
</tr>
</tbody>
</table>
</body>
</html>
</html>

View File

@ -1,38 +1,32 @@
<?php
namespace MailPoet\Router;
use MailPoet\Models\Segment;
use MailPoet\Models\SendingQueue;
use MailPoet\Util\Helpers;
use Carbon\Carbon;
use MailPoet\Cron\Daemon;
use MailPoet\Cron\Supervisor;
use MailPoet\Models\Setting;
if(!defined('ABSPATH')) exit;
class Cron {
function controlDaemon($data) {
switch($data['action']) {
case 'start':
$supervisor = new \MailPoet\Cron\Supervisor($forceStart = true);
wp_send_json(
array(
'result' => $supervisor->checkDaemon()
)
);
exit;
break;
case 'stop':
$status = 'stopped';
break;
default:
$status = 'paused';
break;
}
$daemon = new \MailPoet\Cron\Daemon();
if(!$daemon->daemon || $daemon->daemonData['status'] !== 'started') {
function start() {
$supervisor = new Supervisor($forceStart = true);
wp_send_json(
array(
'result' => $supervisor->checkDaemon() ? true : false
)
);
}
function stop() {
$daemon = new Daemon();
if(!$daemon->daemon ||
$daemon->daemon['status'] !== 'started'
) {
$result = false;
} else {
$daemon->daemonData['status'] = $status;
$daemon->daemon->value = json_encode($daemon->daemonData);
$result = $daemon->daemon->save();
$daemon->daemon['status'] = 'stopping';
$result = $daemon->saveDaemon($daemon->daemon);
}
wp_send_json(
array(
@ -41,153 +35,29 @@ class Cron {
);
}
function getDaemonStatus() {
$daemon = new \MailPoet\Cron\BootStrapMenu();
wp_send_json($daemon->bootStrap());
}
function addQueue($data) {
$queue = SendingQueue::where('newsletter_id', $data['newsletter_id'])
->whereNull('status')
->findArray();
!d($queue);
exit;
$queue = SendingQueue::create();
$queue->newsletter_id = $data['newsletter_id'];
$subscriber_ids = array();
$segments = Segment::whereIn('id', $data['segments'])
->findMany();
foreach($segments as $segment) {
$subscriber_ids = array_merge($subscriber_ids, Helpers::arrayColumn(
$segment->subscribers()
->findArray(),
'id'
));
}
$subscriber_ids = array_unique($subscriber_ids);
$queue->subscribers = json_encode(
array(
'to_process' => $subscriber_ids
)
);
$queue->count_total = $queue->count_to_process = count($subscriber_ids);
$queue->save();
function getStatus() {
$daemon = Setting::where('name', 'cron_daemon')
->findOne();
wp_send_json(
!$queue->save() ?
array(
'result' => false,
'error' => 'Queue could not be created.'
($daemon) ?
array_merge(
array(
'timeSinceStart' =>
Carbon::createFromFormat(
'Y-m-d H:i:s',
$daemon->created_at,
'UTC'
)->diffForHumans(),
'timeSinceUpdate' =>
Carbon::createFromFormat(
'Y-m-d H:i:s',
$daemon->updated_at,
'UTC'
)->diffForHumans()
),
unserialize($daemon->value)
) :
array(
'result' => true,
'data' => array($queue->id)
)
);
}
function addQueues($data) {
$result = array_map(function ($queueData) {
$queue = SendingQueue::create();
$queue->newsletter_id = $queueData['newsletter_id'];
$queue->subscribers = json_encode(
array(
'to_process' => $queueData['subscribers']
)
);
$queue->count_total = $queue->count_to_process = count($queueData['subscribers']);
$queue->save();
return array(
'newsletter_id' => $queue->newsletter_id,
'queue_id' => $queue->id
);
}, $data);
$result = Helpers::arrayColumn($result, 'queue_id', 'newsletter_id');
wp_send_json(
count($data) != count($result) ?
array(
'result' => false,
'error' => __('Some queues could not be created.'),
'data' => $result
) :
array(
'result' => true,
'data' => $result
)
);
}
function deleteQueue($data) {
$queue = SendingQueue::whereNull('deleted_at')
->findOne($data['queue_id']);
if(!$queue) {
wp_send_json(
array(
'result' => false,
'error' => __('Queue not found.')
)
);
}
$queue->deleted_at = 'Y-m-d H:i:s';
$queue->save();
wp_send_json(array('result' => true));
}
function deleteQueues($data) {
$queues = SendingQueue::whereNull('deleted_at')
->whereIn('id', $data['queue_ids'])
->findResultSet();
if(!$queues->count()) {
wp_send_json(
array(
'result' => false,
'error' => __('Queues not found.')
)
);
}
foreach($queues as $queue) {
$queue->deleted_at = 'Y-m-d H:i:s';
$queue->save();
}
wp_send_json(array('result' => true));
}
function getQueueStatus($data) {
$queue = SendingQueue::whereNull('deleted_at')
->findOne($data['queue_id'])
->asArray();
wp_send_json(
!$queue ?
array(
'result' => false,
'error' => __('Queue not found.')
) :
array(
'result' => true,
'data' => $queue
)
);
}
function getQueuesStatus($data) {
$queues = SendingQueue::whereNull('deleted_at')
->whereIn('id', $data['queue_ids'])
->findArray();
wp_send_json(
!$queues ?
array(
'result' => false,
'error' => __('Queue not found.')
) :
array(
'result' => true,
'data' => $queues
)
"false"
);
}
}

View File

@ -1,191 +1,20 @@
<?php
namespace MailPoet\Router;
use MailPoet\Models\Setting;
require_once(ABSPATH . 'wp-includes/pluggable.php');
if(!defined('ABSPATH')) exit;
class Mailer {
function __construct($httpRequest = true) {
$this->mailerType = array(
'AmazonSES' => 'API',
'ElasticEmail' => 'API',
'MailGun' => 'API',
'Mandrill' => 'API',
'SendGrid' => 'API',
'MailPoet' => null,
'SMTP' => null,
'WPMail' => null
);
if(!$httpRequest) {
list($this->fromName, $this->fromEmail, $this->fromNameEmail)
= $this->getSetting('sender');
$this->mailer = $this->getSetting('mailer');
}
}
function send($data) {
$subscriber = $this->transformSubscriber($data['subscriber']);
list($fromName, $fromEmail, $fromNameEmail)
= $this->getSetting('sender');
if(!$fromName && !$fromEmail) {
wp_send_json(
array(
'result' => false,
'errors' => array(__('Please configure your name and e-mail address.'))
)
);
}
$data['mailer']['class'] = 'MailPoet\\Mailer\\' .
(($this->mailerType[$data['mailer']['method']]) ?
$this->mailerType[$data['mailer']['method']] . '\\' . $data['mailer']['method'] :
$data['mailer']['method']
);
$mailer = $this->buildMailer(
$data['mailer'],
$fromName,
$fromEmail,
$fromNameEmail
$mailer = new \MailPoet\Mailer\Mailer(
(isset($data['mailer'])) ? $data['mailer'] : false,
(isset($data['sender'])) ? $data['sender'] : false,
(isset($data['reply_to'])) ? $data['reply_to'] : false
);
if(!empty($newsletter['sender_address']) &&
!empty($newsletter['sender_name'])
) {
$mailer->fromName = $newsletter['sender_name'];
$mailer->fromEmail = $newsletter['sender_address'];
$mailer->fromNameEmail = sprintf(
'%s <%s>',
$mailer->fromName,
$mailer->fromEmail
);
}
if(!empty($newsletter['reply_to_address']) &&
!empty($newsletter['reply_to_name'])
) {
$mailer->replyToName = $newsletter['reply_to_name'];
$mailer->replyToEmail = $newsletter['reply_to_address'];
$mailer->replyToNameEmail = sprintf(
'%s <%s>',
$mailer->replyToName,
$mailer->replyToEmail
);
}
$result = $mailer->send($data['newsletter'], $subscriber);
$result = $mailer->send($data['newsletter'], $data['subscriber']);
wp_send_json(
array(
'result' => ($result) ? true : false
)
);
}
function buildMailer($mailer = false, $fromName = false, $fromEmail = false, $fromNameEmail = false) {
if(!$mailer) $mailer = $this->mailer;
if(!$fromName) $fromName = $this->fromName;
if(!$fromEmail) $fromEmail = $this->fromEmail;
if(!$fromNameEmail) $fromNameEmail = $this->fromNameEmail;
switch($mailer['method']) {
case 'AmazonSES':
$mailerInstance = new $mailer['class'](
$mailer['region'],
$mailer['access_key'],
$mailer['secret_key'],
$fromNameEmail
);
break;
case 'ElasticEmail':
$mailerInstance = new $mailer['class'](
$mailer['api_key'],
$fromEmail, $fromName
);
break;
case 'MailGun':
$mailerInstance = new $mailer['class'](
$mailer['domain'],
$mailer['api_key'],
$fromNameEmail
);
break;
case 'MailPoet':
$mailerInstance = new $mailer['class'](
$mailer['mailpoet_api_key'],
$fromEmail,
$fromName
);
break;
case 'Mandrill':
$mailerInstance = new $mailer['class'](
$mailer['api_key'],
$fromEmail, $fromName
);
break;
case 'SendGrid':
$mailerInstance = new $mailer['class'](
$mailer['api_key'],
$fromEmail,
$fromName
);
break;
case 'WPMail':
$mailerInstance = new $mailer['class'](
$fromEmail,
$fromName
);
break;
case 'SMTP':
$mailerInstance = new $mailer['class'](
$mailer['host'],
$mailer['port'],
$mailer['authentication'],
$mailer['login'],
$mailer['password'],
$mailer['encryption'],
$fromEmail,
$fromName
);
break;
default:
throw new \Exception('Mailing method does not exist.');
break;
}
return $mailerInstance;
}
function transformSubscriber($subscriber) {
if(!is_array($subscriber)) return $subscriber;
if(isset($subscriber['address'])) $subscriber['email'] = $subscriber['address'];
$first_name = (isset($subscriber['first_name'])) ? $subscriber['first_name'] : '';
$last_name = (isset($subscriber['last_name'])) ? $subscriber['last_name'] : '';
if(!$first_name && !$last_name) return $subscriber['email'];
$subscriber = sprintf('%s %s <%s>', $first_name, $last_name, $subscriber['email']);
$subscriber = trim(preg_replace('!\s\s+!', ' ', $subscriber));
return $subscriber;
}
function getSetting($setting) {
switch($setting) {
case 'mailer':
$mailer = Setting::getValue('mta', null);
if(!$mailer || !isset($mailer['method'])) throw new \Exception('Mailing method is not configured.');
$mailer['class'] = 'MailPoet\\Mailer\\' .
(($this->mailerType[$mailer['method']]) ?
$this->mailerType[$mailer['method']] . '\\' . $mailer['method'] :
$mailer['method']
);
return $mailer;
break;
case 'sender':
$sender = Setting::getValue($setting, null);
if(!$sender) throw new \Exception('Sender name and email are not configured.');
return array(
$sender['name'],
$sender['address'],
sprintf('%s <%s>', $sender['name'], $sender['address'])
);
break;
default:
return Setting::getValue($setting, null);
break;
}
}
}

View File

@ -188,12 +188,12 @@ class Newsletters {
));
}
// TO REMOVE once we add the columns from/reply_to
// TODO: TO REMOVE once we add the columns from/reply_to
$newsletter = array_merge($newsletter, $data['newsletter']);
// END - TO REMOVE
$renderer = new Renderer(json_decode($newsletter['body'], true));
$newsletter['body']['html'] = $renderer->renderAll();
$newsletter['body']['html'] = $renderer->render();
$newsletter['body']['text'] = '';
$subscribers = Subscriber::find_array();
@ -217,8 +217,8 @@ class Newsletters {
if(!isset($data['body'])) {
wp_send_json(false);
}
$renderer = new Renderer(json_decode($data['body'], true));
wp_send_json(array('rendered_body' => $renderer->renderAll()));
$renderer = new Renderer($data);
wp_send_json(array('rendered_body' => $renderer->render()));
}
function listing($data = array()) {

View File

@ -1,196 +0,0 @@
<?php
namespace MailPoet\Router;
use MailPoet\Models\Segment;
use MailPoet\Models\SendingQueue;
use MailPoet\Util\Helpers;
if(!defined('ABSPATH')) exit;
class Queue {
function controlDaemon($data) {
switch($data['action']) {
case 'start':
$supervisor = new Supervisor($forceStart = true);
wp_send_json(
array(
'result' => $supervisor->checkDaemon() ?
true :
false
)
);
break;
case 'stop':
$status = 'stopped';
break;
default:
$status = 'paused';
break;
}
$daemon = new Daemon();
if(!$daemon->daemon || $daemon->daemonData['status'] !== 'started') {
$result = false;
} else {
$daemon->daemonData['status'] = $status;
$daemon->daemon->value = json_encode($daemon->daemonData);
$result = $daemon->daemon->save();
}
wp_send_json(
array(
'result' => $result
)
);
}
function getDaemonStatus() {
$daemon = new \MailPoet\Cron\BootStrapMenu();
wp_send_json($daemon->bootStrap());
}
function addQueue($data) {
$queue = SendingQueue::where('newsletter_id', $data['newsletter_id'])
->whereNull('status')
->findArray();
!d($queue);
exit;
$queue = SendingQueue::create();
$queue->newsletter_id = $data['newsletter_id'];
$subscriber_ids = array();
$segments = Segment::whereIn('id', $data['segments'])
->findMany();
foreach($segments as $segment) {
$subscriber_ids = array_merge($subscriber_ids, Helpers::arrayColumn(
$segment->subscribers()
->findArray(),
'id'
));
}
$subscriber_ids = array_unique($subscriber_ids);
$queue->subscribers = json_encode(
array(
'to_process' => $subscriber_ids
)
);
$queue->count_total = $queue->count_to_process = count($subscriber_ids);
$queue->save();
wp_send_json(
!$queue->save() ?
array(
'result' => false,
'error' => 'Queue could not be created.'
) :
array(
'result' => true,
'data' => array($queue->id)
)
);
}
function addQueues($data) {
$result = array_map(function ($queueData) {
$queue = SendingQueue::create();
$queue->newsletter_id = $queueData['newsletter_id'];
$queue->subscribers = json_encode(
array(
'to_process' => $queueData['subscribers']
)
);
$queue->count_total = $queue->count_to_process = count($queueData['subscribers']);
$queue->save();
return array(
'newsletter_id' => $queue->newsletter_id,
'queue_id' => $queue->id
);
}, $data);
$result = Helpers::arrayColumn($result, 'queue_id', 'newsletter_id');
wp_send_json(
count($data) != count($result) ?
array(
'result' => false,
'error' => __('Some queues could not be created.'),
'data' => $result
) :
array(
'result' => true,
'data' => $result
)
);
}
function deleteQueue($data) {
$queue = SendingQueue::whereNull('deleted_at')
->findOne($data['queue_id']);
if(!$queue) {
wp_send_json(
array(
'result' => false,
'error' => __('Queue not found.')
)
);
}
$queue->deleted_at = 'Y-m-d H:i:s';
$queue->save();
wp_send_json(array('result' => true));
}
function deleteQueues($data) {
$queues = SendingQueue::whereNull('deleted_at')
->whereIn('id', $data['queue_ids'])
->findResultSet();
if(!$queues->count()) {
wp_send_json(
array(
'result' => false,
'error' => __('Queues not found.')
)
);
}
foreach($queues as $queue) {
$queue->deleted_at = 'Y-m-d H:i:s';
$queue->save();
}
wp_send_json(array('result' => true));
}
function getQueueStatus($data) {
$queue = SendingQueue::whereNull('deleted_at')
->findOne($data['queue_id'])
->asArray();
wp_send_json(
!$queue ?
array(
'result' => false,
'error' => __('Queue not found.')
) :
array(
'result' => true,
'data' => $queue
)
);
}
function getQueuesStatus($data) {
$queues = SendingQueue::whereNull('deleted_at')
->whereIn('id', $data['queue_ids'])
->findArray();
wp_send_json(
!$queues ?
array(
'result' => false,
'error' => __('Queue not found.')
) :
array(
'result' => true,
'data' => $queues
)
);
}
}

View File

@ -19,7 +19,6 @@ class SendingQueue {
'errors' => array($e->getMessage())
)
);
exit;
}
$queue = \MailPoet\Models\SendingQueue::where('newsletter_id', $data['newsletter_id'])

View File

@ -6,15 +6,17 @@ use MailPoet\Models\Segment;
use MailPoet\Util\Helpers;
class BootStrapMenu {
public $action;
function __construct($action = null) {
$this->action = $action;
}
function getSegments($withConfirmedSubscribers = false) {
function getSegments($with_confirmed_subscribers = false) {
$segments = ($this->action === 'import') ?
Segment::getSegmentsWithSubscriberCount() :
Segment::getSegmentsForExport($withConfirmedSubscribers);
return array_map(function ($segment) {
Segment::getSegmentsForExport($with_confirmed_subscribers);
return array_map(function($segment) {
return array(
'id' => $segment['id'],
'name' => $segment['name'],
@ -29,6 +31,7 @@ class BootStrapMenu {
'first_name' => __('First name'),
'last_name' => __('Last name'),
'status' => __('Status')
// TODO: add additional fiels from MP2
/*
'confirmed_ip' => __('IP address')
'confirmed_at' => __('Subscription date')
@ -36,35 +39,35 @@ class BootStrapMenu {
);
}
function formatSubscriberFields($subscriberFields) {
return array_map(function ($fieldId, $fieldName) {
function formatSubscriberFields($subscriber_fields) {
return array_map(function($field_id, $field_name) {
return array(
'id' => $fieldId,
'name' => $fieldName,
'type' => ($fieldId === 'confirmed_at') ? 'date' : null,
'id' => $field_id,
'name' => $field_name,
'type' => ($field_id === 'confirmed_at') ? 'date' : null,
'custom' => false
);
}, array_keys($subscriberFields), $subscriberFields);
}, array_keys($subscriber_fields), $subscriber_fields);
}
function getSubscriberCustomFields() {
return CustomField::findArray();
}
function formatSubscriberCustomFields($subscriberCustomFields) {
return array_map(function ($field) {
function formatSubscriberCustomFields($subscriber_custom_fields) {
return array_map(function($field) {
return array(
'id' => $field['id'],
'name' => $field['name'],
'type' => $field['type'],
'custom' => true
);
}, $subscriberCustomFields);
}, $subscriber_custom_fields);
}
function formatFieldsForSelect2(
$subscriberFields,
$subscriberCustomFields) {
$subscriber_fields,
$subscriber_custom_fields) {
$actions = ($this->action === 'import') ?
array(
array(
@ -93,14 +96,14 @@ class BootStrapMenu {
),
array(
'name' => __('System columns'),
'children' => $this->formatSubscriberFields($subscriberFields)
'children' => $this->formatSubscriberFields($subscriber_fields)
)
);
if($subscriberCustomFields) {
if($subscriber_custom_fields) {
array_push($select2Fields, array(
'name' => __('User columns'),
'children' => $this->formatSubscriberCustomFields(
$subscriberCustomFields
$subscriber_custom_fields
)
));
}
@ -108,28 +111,28 @@ class BootStrapMenu {
}
function bootstrap() {
$subscriberFields = $this->getSubscriberFields();
$subscriberCustomFields = $this->getSubscriberCustomFields();
$subscriber_fields = $this->getSubscriberFields();
$subscriber_custom_fields = $this->getSubscriberCustomFields();
$data['segments'] = json_encode($this->getSegments());
$data['subscriberFieldsSelect2'] = json_encode(
$this->formatFieldsForSelect2(
$subscriberFields,
$subscriberCustomFields
$subscriber_fields,
$subscriber_custom_fields
)
);
if($this->action === 'import') {
$data['subscriberFields'] = json_encode(
array_merge(
$this->formatSubscriberFields($subscriberFields),
$this->formatSubscriberCustomFields($subscriberCustomFields)
$this->formatSubscriberFields($subscriber_fields),
$this->formatSubscriberCustomFields($subscriber_custom_fields)
)
);
$data['maxPostSizeBytes'] = Helpers::getMaxPostSize('bytes');
$data['maxPostSize'] = Helpers::getMaxPostSize();
} else {
$data['segmentsWithConfirmedSubscribers'] =
json_encode($this->getSegments($withConfirmedSubscribers = true));
json_encode($this->getSegments($with_confirmed_subscribers = true));
}
return $data;
}
}
}

View File

@ -11,106 +11,110 @@ use MailPoet\Util\Helpers;
use MailPoet\Util\XLSXWriter;
class Export {
public $export_confirmed_option;
public $export_format_option;
public $group_by_segment_option;
public $segments;
public $subscribers_without_segment;
public $subscriber_fields;
public $export_file;
public $export_file_URL;
public $profiler_start;
public function __construct($data) {
$this->exportConfirmedOption = $data['exportConfirmedOption'];
$this->exportFormatOption = $data['exportFormatOption'];
$this->groupBySegmentOption = $data['groupBySegmentOption'];
$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->subscribersWithoutSegment = array_search(0, $this->segments);
$this->subscriberFields = $data['subscriberFields'];
$this->exportFile = $this->getExportFile($this->exportFormatOption);
$this->exportFileURL = $this->getExportFileURL($this->exportFile);
$this->profilerStart = microtime(true);
$this->subscribers_without_segment = array_search(0, $this->segments);
$this->subscriber_fields = $data['subscriber_fields'];
$this->export_file = $this->getExportFile($this->export_format_option);
$this->export_file_URL = $this->getExportFileURL($this->export_file);
$this->profiler_start = microtime(true);
}
function process() {
$subscribers = $this->getSubscribers();
$subscriberCustomFields = $this->getSubscriberCustomFields();
$formattedSubscriberFields = $this->formatSubscriberFields(
$this->subscriberFields,
$subscriberCustomFields
$subscriber_custom_fields = $this->getSubscriberCustomFields();
$formatted_subscriber_fields = $this->formatSubscriberFields(
$this->subscriber_fields,
$subscriber_custom_fields
);
try {
if($this->exportFormatOption === 'csv') {
$CSVFile = fopen($this->exportFile, 'w');
$formatCSV = function ($row) {
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($CSVFile, chr(0xEF) . chr(0xBB) . chr(0xBF));
if($this->groupBySegmentOption) {
$formattedSubscriberFields[] = __('List');
fwrite($CSV_file, chr(0xEF) . chr(0xBB) . chr(0xBF));
if($this->group_by_segment_option) {
$formatted_subscriber_fields[] = __('Segment');
}
fwrite(
$CSVFile,
$CSV_file,
implode(
',',
array_map(
$formatCSV,
$formattedSubscriberFields
$format_CSV,
$formatted_subscriber_fields
)
) . "\n"
);
foreach ($subscribers as $subscriber) {
$row = $this->formatSubscriberData(
$subscriber,
$formattedSubscriberFields
);
if($this->groupBySegmentOption) {
foreach($subscribers as $subscriber) {
$row = $this->formatSubscriberData($subscriber);
if($this->group_by_segment_option) {
$row[] = ucwords($subscriber['segment_name']);
}
fwrite($CSVFile, implode(',', array_map($formatCSV, $row)) . "\n");
fwrite($CSV_file, implode(',', array_map($format_CSV, $row)) . "\n");
}
fclose($CSVFile);
fclose($CSV_file);
} else {
$writer = new XLSXWriter();
$writer->setAuthor('MailPoet (www.mailpoet.com)');
$headerRow = array($formattedSubscriberFields);
$lastSegment = false;
$header_row = array($formatted_subscriber_fields);
$last_segment = false;
$rows = array();
foreach ($subscribers as $subscriber) {
if($lastSegment && $lastSegment !== $subscriber['segment_name'] &&
$this->groupBySegmentOption
foreach($subscribers as $subscriber) {
if($last_segment && $last_segment !== $subscriber['segment_name'] &&
$this->group_by_segment_option
) {
$writer->writeSheet(
array_merge($headerRow, $rows), ucwords($lastSegment)
array_merge($header_row, $rows), ucwords($last_segment)
);
$rows = array();
}
// detect RTL language and set Excel to properly display the sheet
$RTLRegex = '/\p{Arabic}|\p{Hebrew}/u';
$RTL_regex = '/\p{Arabic}|\p{Hebrew}/u';
if(!$writer->rtl && (
preg_grep($RTLRegex, $subscriber) ||
preg_grep($RTLRegex, $formattedSubscriberFields))
preg_grep($RTL_regex, $subscriber) ||
preg_grep($RTL_regex, $formatted_subscriber_fields))
) {
$writer->rtl = true;
}
$rows[] = $this->formatSubscriberData(
$subscriber,
$formattedSubscriberFields
);
$lastSegment = $subscriber['segment_name'];
$rows[] = $this->formatSubscriberData($subscriber);
$last_segment = $subscriber['segment_name'];
}
$writer->writeSheet(
array_merge($headerRow, $rows),
($this->groupBySegmentOption) ?
array_merge($header_row, $rows),
($this->group_by_segment_option) ?
ucwords($subscriber['segment_name']) :
'MailPoet'
__('All Segments')
);
$writer->writeToFile($this->exportFile);
$writer->writeToFile($this->export_file);
}
} catch (Exception $e) {
} catch(Exception $e) {
return array(
'result' => false,
'error' => $e->getMessage()
'errors' => array($e->getMessage())
);
}
return array(
'result' => true,
'data' => array(
'totalExported' => count($subscribers),
'exportFileURL' => $this->exportFileURL
'exportFileURL' => $this->export_file_URL
),
'profiler' => $this->timeExecution()
);
@ -134,11 +138,11 @@ class Export {
))
->orderByAsc('segment_name')
->filter('filterWithCustomFieldsForExport');
if($this->subscribersWithoutSegment !== false) {
if($this->subscribers_without_segment !== false) {
$subscribers = $subscribers
->selectExpr('CASE WHEN ' . Segment::$_table . '.name IS NOT NULL ' .
'THEN ' . Segment::$_table . '.name ' .
'ELSE "' . __('Not In List') . '" END as segment_name'
'ELSE "' . __('Not In Segment') . '" END as segment_name'
)
->whereRaw(
SubscriberSegment::$_table . '.segment_id IN (' .
@ -151,11 +155,11 @@ class Export {
->select(Segment::$_table . '.name', 'segment_name')
->whereIn(SubscriberSegment::$_table . '.segment_id', $this->segments);
}
if(!$this->groupBySegmentOption) {
if(!$this->group_by_segment_option) {
$subscribers =
$subscribers->groupBy(Subscriber::$_table . '.id');
}
if($this->exportConfirmedOption) {
if($this->export_confirmed_option) {
$subscribers =
$subscribers->where(Subscriber::$_table . '.status', 'subscribed');
}
@ -166,9 +170,8 @@ class Export {
function getExportFileURL($file) {
return sprintf(
'%s/%s/%s',
Env::$plugin_url,
Env::$temp_name,
'%s/%s',
Env::$temp_URL,
basename($file)
);
}
@ -189,32 +192,28 @@ class Export {
);
}
function formatSubscriberFields($subscriberFields, $subscriberCustomFields) {
$bootStrapMenu = new BootStrapMenu();
$translatedFields = $bootStrapMenu->getSubscriberFields();
return array_map(function ($field) use (
$translatedFields, $subscriberCustomFields
function formatSubscriberFields($subscriber_fields, $subscriber_custom_fields) {
$bootstrap_menu = new BootStrapMenu();
$translated_fields = $bootstrap_menu->getSubscriberFields();
return array_map(function($field) use (
$translated_fields, $subscriber_custom_fields
) {
$field = (isset($translatedFields[$field])) ?
ucfirst($translatedFields[$field]) :
$field = (isset($translated_fields[$field])) ?
ucfirst($translated_fields[$field]) :
ucfirst($field);
return (isset($subscriberCustomFields[$field])) ?
$subscriberCustomFields[$field] : $field;
}, $subscriberFields);
return (isset($subscriber_custom_fields[$field])) ?
ucfirst($subscriber_custom_fields[$field]) : $field;
}, $subscriber_fields);
}
function formatSubscriberData($subscriber, $subscriberCustomFields) {
return array_map(function ($field) use (
$subscriber, $subscriberCustomFields
) {
return (isset($subscriberCustomFields[$field])) ?
$subscriberCustomFields[$field] :
$subscriber[$field];
}, $this->subscriberFields);
function formatSubscriberData($subscriber) {
return array_map(function($field) use ($subscriber) {
return $subscriber[$field];
}, $this->subscriber_fields);
}
function timeExecution() {
$profilerEnd = microtime(true);
return ($profilerEnd - $this->profilerStart) / 60;
$profiler_end = microtime(true);
return ($profiler_end - $this->profiler_start) / 60;
}
}

View File

@ -8,192 +8,201 @@ use MailPoet\Subscribers\ImportExport\BootStrapMenu;
use MailPoet\Util\Helpers;
class Import {
public $subscribers_data;
public $segments;
public $update_subscribers;
public $subscriber_fields;
public $subscriber_custom_fields;
public $subscribers_count;
public $import_time;
public $profiler_start;
public function __construct($data) {
$this->subscribersData = $data['subscribers'];
$this->subscribers_data = $data['subscribers'];
$this->segments = $data['segments'];
$this->updateSubscribers = $data['updateSubscribers'];
$this->subscriberFields = $this->getSubscriberFields(
array_keys($this->subscribersData)
$this->update_subscribers = $data['updateSubscribers'];
$this->subscriber_fields = $this->getSubscriberFields(
array_keys($this->subscribers_data)
);
$this->subscriberCustomFields = $this->getCustomSubscriberFields(
array_keys($this->subscribersData)
$this->subscriber_custom_fields = $this->getCustomSubscriberFields(
array_keys($this->subscribers_data)
);
$this->subscribersCount = count(reset($this->subscribersData));
$this->currentTime = date('Y-m-d H:i:s');
$this->profilerStart = microtime(true);
$this->subscribers_count = count(reset($this->subscribers_data));
$this->import_time = date('Y-m-d H:i:s');
$this->profiler_start = microtime(true);
}
function process() {
$subscriberFields = $this->subscriberFields;
$subscriberCustomFields = $this->subscriberCustomFields;
$subscribersData = $this->subscribersData;
list ($subscribersData, $subscriberFields) =
$this->filterSubscriberStatus($subscribersData, $subscriberFields);
$this->deleteExistingTrashedSubscribers($subscribersData);
list($subscribersData, $subscriberFields) = $this->extendSubscribersAndFields(
$subscribersData, $subscriberFields
$subscriber_fields = $this->subscriber_fields;
$subscriber_custom_fields = $this->subscriber_custom_fields;
$subscribers_data = $this->subscribers_data;
list ($subscribers_data, $subscriber_fields) =
$this->filterSubscriberStatus($subscribers_data, $subscriber_fields);
$this->deleteExistingTrashedSubscribers($subscribers_data);
list($subscribers_data, $subscriber_fields) = $this->extendSubscribersAndFields(
$subscribers_data, $subscriber_fields
);
list($existingSubscribers, $newSubscribers) =
$this->filterExistingAndNewSubscribers($subscribersData);
$createdSubscribers = $updatedSubscribers = array();
list($existing_subscribers, $new_subscribers) =
$this->filterExistingAndNewSubscribers($subscribers_data);
$created_subscribers = $updated_subscribers = array();
try {
if($newSubscribers) {
$createdSubscribers =
if($new_subscribers) {
$created_subscribers =
$this->createOrUpdateSubscribers(
'create',
$newSubscribers,
$subscriberFields,
$subscriberCustomFields
$new_subscribers,
$subscriber_fields,
$subscriber_custom_fields
);
}
if($existingSubscribers && $this->updateSubscribers) {
$updatedSubscribers =
if($existing_subscribers && $this->update_subscribers) {
$updated_subscribers =
$this->createOrUpdateSubscribers(
'update',
$existingSubscribers,
$subscriberFields,
$subscriberCustomFields
$existing_subscribers,
$subscriber_fields,
$subscriber_custom_fields
);
if($createdSubscribers) {
if($created_subscribers) {
// subtract added from updated subscribers when DB operation takes <1s
$updatedSubscribers = array_diff_key(
$updatedSubscribers,
$createdSubscribers,
$subscriberCustomFields
$updated_subscribers = array_diff_key(
$updated_subscribers,
$created_subscribers,
$subscriber_custom_fields
);
}
}
} catch(\PDOException $e) {
return array(
'result' => false,
'error' => $e->getMessage()
'errors' => array($e->getMessage())
);
}
$segments = new BootStrapMenu('import');
return array(
'result' => true,
'data' => array(
'created' => count($createdSubscribers),
'updated' => count($updatedSubscribers),
'created' => count($created_subscribers),
'updated' => count($updated_subscribers),
'segments' => $segments->getSegments()
),
'profiler' => $this->timeExecution()
);
}
function filterExistingAndNewSubscribers($subscribersData) {
$existingRecords = array_filter(
array_map(function ($subscriberEmails) {
function filterExistingAndNewSubscribers($subscribers_data) {
$existing_records = array_filter(
array_map(function($subscriber_emails) {
return Subscriber::selectMany(array('email'))
->whereIn('email', $subscriberEmails)
->whereIn('email', $subscriber_emails)
->whereNull('deleted_at')
->findArray();
}, array_chunk($subscribersData['email'], 200))
}, array_chunk($subscribers_data['email'], 200))
);
if(!$existingRecords) {
if(!$existing_records) {
return array(
false,
$subscribersData
$subscribers_data
);
}
$existingRecords = Helpers::flattenArray($existingRecords);
$newRecords = array_keys(
$existing_records = Helpers::flattenArray($existing_records);
$new_records = array_keys(
array_diff(
$subscribersData['email'],
$existingRecords
$subscribers_data['email'],
$existing_records
)
);
if(!$newRecords) {
if(!$new_records) {
return array(
$subscribersData,
$subscribers_data,
false
);
}
$newSubscribers =
$new_subscribers =
array_filter(
array_map(function ($subscriber) use ($newRecords) {
return array_map(function ($index) use ($subscriber) {
array_map(function($subscriber) use ($new_records) {
return array_map(function($index) use ($subscriber) {
return $subscriber[$index];
}, $newRecords);
}, $subscribersData)
}, $new_records);
}, $subscribers_data)
);
$existingSubscribers =
array_map(function ($subscriber) use ($newRecords) {
$existing_subscribers =
array_map(function($subscriber) use ($new_records) {
return array_values( // reindex array
array_filter( // remove NULL entries
array_map(function ($index, $data) use ($newRecords) {
if(!in_array($index, $newRecords)) return $data;
array_map(function($index, $data) use ($new_records) {
if(!in_array($index, $new_records)) return $data;
}, array_keys($subscriber), $subscriber)
)
);
}, $subscribersData);
}, $subscribers_data);
return array(
$existingSubscribers,
$newSubscribers
$existing_subscribers,
$new_subscribers
);
}
function deleteExistingTrashedSubscribers($subscribersData) {
$existingTrashedRecords = array_filter(
array_map(function ($subscriberEmails) {
function deleteExistingTrashedSubscribers($subscribers_data) {
$existing_trashed_records = array_filter(
array_map(function($subscriber_emails) {
return Subscriber::selectMany(array('id'))
->whereIn('email', $subscriberEmails)
->whereIn('email', $subscriber_emails)
->whereNotNull('deleted_at')
->findArray();
}, array_chunk($subscribersData['email'], 200))
}, array_chunk($subscribers_data['email'], 200))
);
if(!$existingTrashedRecords) return;
$existingTrashedRecords = Helpers::flattenArray($existingTrashedRecords);
foreach(array_chunk($existingTrashedRecords, 200) as $subscriberIds) {
Subscriber::whereIn('id', $subscriberIds)
if(!$existing_trashed_records) return;
$existing_trashed_records = Helpers::flattenArray($existing_trashed_records);
foreach(array_chunk($existing_trashed_records, 200) as $subscriber_ids) {
Subscriber::whereIn('id', $subscriber_ids)
->deleteMany();
SubscriberSegment::whereIn('subscriber_id', $subscriberIds)
SubscriberSegment::whereIn('subscriber_id', $subscriber_ids)
->deleteMany();
}
}
function extendSubscribersAndFields($subscribersData, $subscriberFields) {
$subscribersData['created_at'] = $this->filterSubscriberCreatedAtDate();
$subscriberFields[] = 'created_at';
function extendSubscribersAndFields($subscribers_data, $subscriber_fields) {
$subscribers_data['created_at'] = $this->filterSubscriberCreatedAtDate();
$subscriber_fields[] = 'created_at';
return array(
$subscribersData,
$subscriberFields
$subscribers_data,
$subscriber_fields
);
}
function getSubscriberFields($subscriberFields) {
function getSubscriberFields($subscriber_fields) {
return array_values(
array_filter(
array_map(function ($field) {
array_map(function($field) {
if(!is_int($field)) return $field;
}, $subscriberFields)
}, $subscriber_fields)
)
);
}
function getCustomSubscriberFields($subscriberFields) {
function getCustomSubscriberFields($subscriber_fields) {
return array_values(
array_filter(
array_map(function ($field) {
array_map(function($field) {
if(is_int($field)) return $field;
}, $subscriberFields)
}, $subscriber_fields)
)
);
}
function filterSubscriberCreatedAtDate() {
return array_fill(0, $this->subscribersCount, $this->currentTime);
return array_fill(0, $this->subscribers_count, $this->import_time);
}
function filterSubscriberStatus($subscribersData, $subscriberFields) {
if(!in_array('status', $subscriberFields)) {
$subscribersData['status'] =
array_fill(0, count($subscribersData['email']), 'subscribed');
$subscriberFields[] = 'status';
function filterSubscriberStatus($subscribers_data, $subscriber_fields) {
if(!in_array('status', $subscriber_fields)) {
$subscribers_data['status'] =
array_fill(0, count($subscribers_data['email']), 'subscribed');
$subscriber_fields[] = 'status';
return array(
$subscribersData,
$subscriberFields
$subscribers_data,
$subscriber_fields
);
}
$statuses = array(
@ -216,7 +225,7 @@ class Import {
'false'
)
);
$subscribersData['status'] = array_map(function ($state) use ($statuses) {
$subscribers_data['status'] = array_map(function($state) use ($statuses) {
if(in_array(strtolower($state), $statuses['subscribed'])) {
return 'subscribed';
}
@ -227,38 +236,38 @@ class Import {
return 'unconfirmed';
}
return 'subscribed'; // make "subscribed" a default status
}, $subscribersData['status']);
}, $subscribers_data['status']);
return array(
$subscribersData,
$subscriberFields
$subscribers_data,
$subscriber_fields
);
}
function createOrUpdateSubscribers(
$action,
$subscribersData,
$subscriberFields,
$subscriberCustomFields
$subscribers_data,
$subscriber_fields,
$subscriber_custom_fields
) {
$subscribersCount = count(reset($subscribersData)) - 1;
$subscribers = array_map(function ($index) use ($subscribersData, $subscriberFields) {
return array_map(function ($field) use ($index, $subscribersData) {
return $subscribersData[$field][$index];
}, $subscriberFields);
}, range(0, $subscribersCount));
$currentTime = ($action === 'update') ? date('Y-m-d H:i:s') : $this->currentTime;
$subscribers_count = count(reset($subscribers_data)) - 1;
$subscribers = array_map(function($index) use ($subscribers_data, $subscriber_fields) {
return array_map(function($field) use ($index, $subscribers_data) {
return $subscribers_data[$field][$index];
}, $subscriber_fields);
}, range(0, $subscribers_count));
$import_time = ($action === 'update') ? date('Y-m-d H:i:s') : $this->import_time;
foreach(array_chunk($subscribers, 100) as $data) {
if($action == 'create') {
Subscriber::createMultiple(
$subscriberFields,
$subscriber_fields,
$data
);
}
if($action == 'update') {
Subscriber::updateMultiple(
$subscriberFields,
$subscriber_fields,
$data,
$currentTime
$import_time
);
}
}
@ -268,16 +277,16 @@ class Import {
'id',
'email'
))
->where(($action === 'create') ? 'created_at' : 'updated_at', $currentTime)
->where(($action === 'create') ? 'created_at' : 'updated_at', $import_time)
->findArray(),
'email', 'id'
);
if($subscriberCustomFields) {
if($subscriber_custom_fields) {
$this->createOrUpdateCustomFields(
($action === 'create') ? 'create' : 'update',
$result,
$subscribersData,
$subscriberCustomFields
$subscribers_data,
$subscriber_custom_fields
);
}
$this->addSubscribersToSegments(
@ -286,30 +295,30 @@ class Import {
);
return $result;
}
function createOrUpdateCustomFields(
$action,
$dbSubscribers,
$subscribersData,
$subscriberCustomFields
$db_subscribers,
$subscribers_data,
$subscriber_custom_fields
) {
$subscribers = array_map(
function ($column) use ($dbSubscribers, $subscribersData) {
$count = range(0, count($subscribersData[$column]) - 1);
function($column) use ($db_subscribers, $subscribers_data) {
$count = range(0, count($subscribers_data[$column]) - 1);
return array_map(
function ($index, $value)
use ($dbSubscribers, $subscribersData, $column) {
$subscriberId = array_search(
$subscribersData['email'][$index],
$dbSubscribers
function($index, $value)
use ($db_subscribers, $subscribers_data, $column) {
$subscriber_id = array_search(
$subscribers_data['email'][$index],
$db_subscribers
);
return array(
$column,
$subscriberId,
$subscriber_id,
$value
);
}, $count, $subscribersData[$column]);
}, $subscriberCustomFields)[0];
}, $count, $subscribers_data[$column]);
}, $subscriber_custom_fields)[0];
foreach(array_chunk($subscribers, 200) as $data) {
if($action === 'create') {
SubscriberCustomField::createMultiple(
@ -323,15 +332,15 @@ class Import {
}
}
}
function addSubscribersToSegments($subscribers, $segments) {
foreach(array_chunk($subscribers, 200) as $data) {
SubscriberSegment::createMultiple($segments, $data);
}
}
function timeExecution() {
$profilerEnd = microtime(true);
return ($profilerEnd - $this->profilerStart) / 60;
$profiler_end = microtime(true);
return ($profiler_end - $this->profiler_start) / 60;
}
}

View File

@ -365,9 +365,6 @@ class CSS {
// And put the CSS back as a string!
$node->style = self::arrayToStyle($style);
// remove all !important tags (inlined styles take precedent over others anyway)
$node->style = str_replace("!important", "", $node->style);
// I'm leaving this for debug purposes, it has proved useful.
/*
if($rule['selector'] === 'table.table-recap td')

View File

@ -5,7 +5,7 @@ if (!defined('ABSPATH')) exit;
/*
* Plugin Name: MailPoet
* Version: 0.0.8
* Version: 0.0.10
* Plugin URI: http://www.mailpoet.com
* Description: MailPoet Newsletters.
* Author: MailPoet
@ -23,7 +23,7 @@ if (!defined('ABSPATH')) exit;
require 'vendor/autoload.php';
define('MAILPOET_VERSION', '0.0.8');
define('MAILPOET_VERSION', '0.0.10');
$initializer = new Initializer(array(
'file' => __FILE__,

View File

@ -5,8 +5,7 @@
},
"napa": {
"blob": "eligrey/Blob.js.git",
"filesaver": "eligrey/FileSaver.js.git",
"sticky-kit": "leafo/sticky-kit.git"
"filesaver": "eligrey/FileSaver.js.git"
},
"dependencies": {
"backbone": "1.2.3",
@ -35,7 +34,8 @@
"select2": "^4.0.0",
"spectrum-colorpicker": "^1.6.2",
"tinymce": "4.1.10",
"underscore": "1.8.3"
"underscore": "1.8.3",
"velocity-animate": "1.2.3"
},
"devDependencies": {
"expose-loader": "latest",

View File

@ -0,0 +1,100 @@
<?php
use MailPoet\Mailer\Mailer;
class MailerCest {
function _before() {
$this->sender = array(
'name' => 'Sender',
'address' => 'staff@mailinator.com'
);
$this->reply_to = array(
'name' => 'Reply To',
'address' => 'staff@mailinator.com'
);
$this->mailer = array(
'method' => 'MailPoet',
'mailpoet_api_key' => 'dhNSqj1XHkVltIliyQDvMiKzQShOA5rs0m_DdRUVZHU'
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing Mailer',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itRequiresMailerMethod() {
try {
$mailer = new Mailer();
} catch (Exception $e) {
expect($e->getMessage())->equals('Mailer is not configured.');
}
}
function itRequiresSender() {
try {
$mailer = new Mailer($mailer = $this->mailer);
} catch (Exception $e) {
expect($e->getMessage())->equals('Sender name and email are not configured.');
}
}
function itCanConstruct() {
$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']);
expect($mailer->reply_to['reply_to_name'])->equals($this->reply_to['name']);
expect($mailer->reply_to['reply_to_email'])->equals($this->reply_to['address']);
}
function itCanBuildMailerInstance() {
$mailer = new Mailer($this->mailer, $this->sender);
expect(get_class($mailer->mailer_instance))
->equals('MailPoet\Mailer\Methods\MailPoet');
}
function itCanAbortWhenMethodDoesNotExist() {
try {
$mailer = new Mailer(array('method' => 'test'), $this->sender);
} catch (Exception $e) {
expect($e->getMessage())->equals('Mailing method does not exist.');
}
}
function itCanTransformSubscriber() {
$mailer = new Mailer($this->mailer, $this->sender, $this->reply_to);
expect($mailer->transformSubscriber('test@email.com'))
->equals('test@email.com');
expect($mailer->transformSubscriber(
array(
'email' => 'test@email.com'
))
)->equals('test@email.com');
expect($mailer->transformSubscriber(
array(
'first_name' => 'First',
'email' => 'test@email.com'
))
)->equals('First <test@email.com>');
expect($mailer->transformSubscriber(
array(
'last_name' => 'Last',
'email' => 'test@email.com'
))
)->equals('Last <test@email.com>');
expect($mailer->transformSubscriber(
array(
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'test@email.com'
))
)->equals('First Last <test@email.com>');
}
function itCanSend() {
$mailer = new Mailer($this->mailer, $this->sender, $this->reply_to);
expect($mailer->send($this->newsletter, $this->subscriber))->true();
}
}

View File

@ -1,155 +1,155 @@
<?php
use MailPoet\Mailer\API\AmazonSES;
class AmazonSESCest {
function _before() {
$this->settings = array(
'method' => 'AmazonSES',
'type' => 'API',
'access_key' => 'AKIAJM6Y5HMGXBLDNSRA',
'secret_key' => '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',
'region' => 'us-east-1',
);
$this->from = 'Sender <vlad@mailpoet.com>';
$this->mailer = new AmazonSES(
$this->settings['region'],
$this->settings['access_key'],
$this->settings['secret_key'],
$this->from);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing AmazonSES',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itsConstructorWorks() {
expect($this->mailer->awsEndpoint)
->equals(
sprintf('email.%s.amazonaws.com', $this->settings['region'])
);
expect($this->mailer->url) ->equals(
sprintf('https://email.%s.amazonaws.com', $this->settings['region'])
);
expect(preg_match('!^\d{8}T\d{6}Z$!', $this->mailer->date))->equals(1);
expect(preg_match('!^\d{8}$!', $this->mailer->dateWithoutTime))->equals(1);
}
function itCanGenerateBody() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($body['Action'])->equals('SendEmail');
expect($body['Version'])->equals('2010-12-01');
expect($body['Source'])->equals($this->from);
expect($body['Destination.ToAddresses.member.1'])
->contains($this->subscriber);
expect($body['Message.Subject.Data'])
->equals($this->newsletter['subject']);
expect($body['Message.Body.Html.Data'])
->equals($this->newsletter['body']['html']);
expect($body['Message.Body.Text.Data'])
->equals($this->newsletter['body']['text']);
expect($body['ReturnPath'])->equals($this->from);
}
function itCanCreateRequest() {
$request = $this->mailer->request($this->newsletter, $this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($request['timeout'])->equals(10);
expect($request['httpversion'])->equals('1.1');
expect($request['method'])->equals('POST');
expect($request['headers']['Host'])->equals($this->mailer->awsEndpoint);
expect($request['headers']['Authorization'])
->equals($this->mailer->signRequest($body));
expect($request['headers']['X-Amz-Date'])->equals($this->mailer->date);
expect($request['body'])->equals(urldecode(http_build_query($body)));
}
function itCanCreateCanonicalRequest() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$canonicalRequest = explode(
"\n",
$this->mailer->getCanonicalRequest($body)
);
expect($canonicalRequest)
->equals(
array(
'POST',
'/',
'',
'host:' . $this->mailer->awsEndpoint,
'x-amz-date:' . $this->mailer->date,
'',
'host;x-amz-date',
hash($this->mailer->hashAlgorithm,
urldecode(http_build_query($body))
)
)
);
}
function itCanCreateCredentialScope() {
$credentialScope = $this->mailer->getCredentialScope();
expect($credentialScope)
->equals(
$this->mailer->dateWithoutTime . '/' .
$this->mailer->awsRegion . '/' .
$this->mailer->awsService . '/' .
$this->mailer->awsTerminationString
);
}
function itCanCreateStringToSign() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$credentialScope = $this->mailer->getCredentialScope();
$canonicalRequest = $this->mailer->getCanonicalRequest($body);
$stringToSing = $this->mailer->createStringToSign(
$credentialScope,
$canonicalRequest
);
$stringToSing = explode("\n", $stringToSing);
expect($stringToSing)
->equals(
array(
$this->mailer->awsSigningAlgorithm,
$this->mailer->date,
$credentialScope,
hash($this->mailer->hashAlgorithm, $canonicalRequest)
)
);
}
function itCanSignRequest() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$signedRequest = $this->mailer->signRequest($body);
expect($signedRequest)
->contains(
$this->mailer->awsSigningAlgorithm . ' Credential=' .
$this->mailer->awsAccessKey . '/' .
$this->mailer->getCredentialScope() . ', ' .
'SignedHeaders=host;x-amz-date, Signature='
);
expect(preg_match('!Signature=[A-Fa-f0-9]{64}$!', $signedRequest))
->equals(1);
}
function itCannotSendWithoutProperAccessKey() {
$this->mailer->awsAccessKey = 'somekey';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
}
<?php
use MailPoet\Mailer\Methods\AmazonSES;
class AmazonSESCest {
function _before() {
$this->settings = array(
'method' => 'AmazonSES',
'access_key' => 'AKIAJM6Y5HMGXBLDNSRA',
'secret_key' => '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',
'region' => 'us-east-1',
);
$this->from = 'Sender <vlad@mailpoet.com>';
$this->mailer = new AmazonSES(
$this->settings['region'],
$this->settings['access_key'],
$this->settings['secret_key'],
$this->from);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing AmazonSES',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itsConstructorWorks() {
expect($this->mailer->aws_endpoint)
->equals(
sprintf('email.%s.amazonaws.com', $this->settings['region'])
);
expect($this->mailer->url)
->equals(
sprintf('https://email.%s.amazonaws.com', $this->settings['region'])
);
expect(preg_match('!^\d{8}T\d{6}Z$!', $this->mailer->date))->equals(1);
expect(preg_match('!^\d{8}$!', $this->mailer->date_without_time))->equals(1);
}
function itCanGenerateBody() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($body['Action'])->equals('SendEmail');
expect($body['Version'])->equals('2010-12-01');
expect($body['Source'])->equals($this->from);
expect($body['Destination.ToAddresses.member.1'])
->contains($this->subscriber);
expect($body['Message.Subject.Data'])
->equals($this->newsletter['subject']);
expect($body['Message.Body.Html.Data'])
->equals($this->newsletter['body']['html']);
expect($body['Message.Body.Text.Data'])
->equals($this->newsletter['body']['text']);
expect($body['ReturnPath'])->equals($this->from);
}
function itCanCreateRequest() {
$request = $this->mailer->request($this->newsletter, $this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($request['timeout'])->equals(10);
expect($request['httpversion'])->equals('1.1');
expect($request['method'])->equals('POST');
expect($request['headers']['Host'])->equals($this->mailer->aws_endpoint);
expect($request['headers']['Authorization'])
->equals($this->mailer->signRequest($body));
expect($request['headers']['X-Amz-Date'])->equals($this->mailer->date);
expect($request['body'])->equals(urldecode(http_build_query($body)));
}
function itCanCreateCanonicalRequest() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$canonicalRequest = explode(
"\n",
$this->mailer->getCanonicalRequest($body)
);
expect($canonicalRequest)
->equals(
array(
'POST',
'/',
'',
'host:' . $this->mailer->aws_endpoint,
'x-amz-date:' . $this->mailer->date,
'',
'host;x-amz-date',
hash($this->mailer->hash_algorithm,
urldecode(http_build_query($body))
)
)
);
}
function itCanCreateCredentialScope() {
$credentialScope = $this->mailer->getCredentialScope();
expect($credentialScope)
->equals(
$this->mailer->date_without_time . '/' .
$this->mailer->aws_region . '/' .
$this->mailer->aws_service . '/' .
$this->mailer->aws_termination_string
);
}
function itCanCreateStringToSign() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$credentialScope = $this->mailer->getCredentialScope();
$canonicalRequest = $this->mailer->getCanonicalRequest($body);
$stringToSing = $this->mailer->createStringToSign(
$credentialScope,
$canonicalRequest
);
$stringToSing = explode("\n", $stringToSing);
expect($stringToSing)
->equals(
array(
$this->mailer->aws_signing_algorithm,
$this->mailer->date,
$credentialScope,
hash($this->mailer->hash_algorithm, $canonicalRequest)
)
);
}
function itCanSignRequest() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$signedRequest = $this->mailer->signRequest($body);
expect($signedRequest)
->contains(
$this->mailer->aws_signing_algorithm . ' Credential=' .
$this->mailer->aws_access_key . '/' .
$this->mailer->getCredentialScope() . ', ' .
'SignedHeaders=host;x-amz-date, Signature='
);
expect(preg_match('!Signature=[A-Fa-f0-9]{64}$!', $signedRequest))
->equals(1);
}
function itCannotSendWithoutProperAccessKey() {
$this->mailer->aws_access_key = 'somekey';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
}

View File

@ -1,65 +1,64 @@
<?php
use MailPoet\Mailer\API\ElasticEmail;
class ElasticEmailCest {
function _before() {
$this->settings = array(
'method' => 'ElasticEmail',
'type' => 'API',
'api_key' => '997f1f7f-41de-4d7f-a8cb-86c8481370fa'
);
$this->fromEmail = 'staff@mailpoet.com';
$this->fromName = 'Sender';
$this->mailer = new ElasticEmail(
$this->settings['api_key'],
$this->fromEmail,
$this->fromName
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing ElasticEmail',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanGenerateBody() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($body['api_key'])->equals($this->settings['api_key']);
expect($body['from'])->equals($this->fromEmail);
expect($body['from_name'])->equals($this->fromName);
expect($body['to'])->contains($this->subscriber);
expect($body['subject'])->equals($this->newsletter['subject']);
expect($body['body_html'])->equals($this->newsletter['body']['html']);
expect($body['body_text'])->equals($this->newsletter['body']['text']);
}
function itCanCreateRequest() {
$request = $this->mailer->request($this->newsletter, $this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($request['timeout'])->equals(10);
expect($request['httpversion'])->equals('1.0');
expect($request['method'])->equals('POST');
expect($request['body'])->equals(urldecode(http_build_query($body)));
}
function itCannotSendWithoutProperAPIKey() {
$this->mailer->apiKey = 'someapi';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
}
<?php
use MailPoet\Mailer\Methods\ElasticEmail;
class ElasticEmailCest {
function _before() {
$this->settings = array(
'method' => 'ElasticEmail',
'api_key' => '997f1f7f-41de-4d7f-a8cb-86c8481370fa'
);
$this->from_email = 'staff@mailpoet.com';
$this->from_name = 'Sender';
$this->mailer = new ElasticEmail(
$this->settings['api_key'],
$this->from_email,
$this->from_name
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing ElasticEmail',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanGenerateBody() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($body['api_key'])->equals($this->settings['api_key']);
expect($body['from'])->equals($this->from_email);
expect($body['from_name'])->equals($this->from_name);
expect($body['to'])->contains($this->subscriber);
expect($body['subject'])->equals($this->newsletter['subject']);
expect($body['body_html'])->equals($this->newsletter['body']['html']);
expect($body['body_text'])->equals($this->newsletter['body']['text']);
}
function itCanCreateRequest() {
$request = $this->mailer->request($this->newsletter, $this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($request['timeout'])->equals(10);
expect($request['httpversion'])->equals('1.0');
expect($request['method'])->equals('POST');
expect($request['body'])->equals(urldecode(http_build_query($body)));
}
function itCannotSendWithoutProperApiKey() {
$this->mailer->api_key = 'someapi';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
}

View File

@ -1,82 +1,81 @@
<?php
use MailPoet\Mailer\API\MailGun;
class MailGunCest {
function _before() {
$this->settings = array(
'method' => 'MailGun',
'type' => 'API',
'api_key' => 'key-6cf5g5qjzenk-7nodj44gdt8phe6vam2',
'domain' => 'mrcasual.com'
);
$this->from = 'Sender <staff@mailpoet.com>';
$this->mailer = new MailGun(
$this->settings['domain'],
$this->settings['api_key'],
$this->from
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing MailGun',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanGenerateBody() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($body['from'])->equals($this->from);
expect($body['to'])->equals($this->subscriber);
expect($body['subject'])->equals($this->newsletter['subject']);
expect($body['html'])->equals($this->newsletter['body']['html']);
expect($body['text'])->equals($this->newsletter['body']['text']);
}
function itCanDoBasicAuth() {
expect($this->mailer->auth())
->equals('Basic ' . base64_encode('api:' . $this->settings['api_key']));
}
function itCanCreateRequest() {
$request = $this->mailer->request($this->newsletter, $this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($request['timeout'])->equals(10);
expect($request['httpversion'])->equals('1.0');
expect($request['method'])->equals('POST');
expect($request['headers']['Content-Type'])
->equals('application/x-www-form-urlencoded');
expect($request['headers']['Authorization'])
->equals('Basic ' . base64_encode('api:' . $this->settings['api_key']));
expect($request['body'])->equals(urldecode(http_build_query($body)));
}
function itCannotSendWithoutProperAPIKey() {
$this->mailer->apiKey = 'someapi';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCannotSendWithoutProperDomain() {
$this->mailer->url =
str_replace($this->settings['domain'], 'somedomain', $this->mailer->url);
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
<?php
use MailPoet\Mailer\Methods\MailGun;
class MailGunCest {
function _before() {
$this->settings = array(
'method' => 'MailGun',
'api_key' => 'key-6cf5g5qjzenk-7nodj44gdt8phe6vam2',
'domain' => 'mrcasual.com'
);
$this->from = 'Sender <staff@mailpoet.com>';
$this->mailer = new MailGun(
$this->settings['domain'],
$this->settings['api_key'],
$this->from
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing MailGun',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanGenerateBody() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($body['from'])->equals($this->from);
expect($body['to'])->equals($this->subscriber);
expect($body['subject'])->equals($this->newsletter['subject']);
expect($body['html'])->equals($this->newsletter['body']['html']);
expect($body['text'])->equals($this->newsletter['body']['text']);
}
function itCanDoBasicAuth() {
expect($this->mailer->auth())
->equals('Basic ' . base64_encode('api:' . $this->settings['api_key']));
}
function itCanCreateRequest() {
$request = $this->mailer->request($this->newsletter, $this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($request['timeout'])->equals(10);
expect($request['httpversion'])->equals('1.0');
expect($request['method'])->equals('POST');
expect($request['headers']['Content-Type'])
->equals('application/x-www-form-urlencoded');
expect($request['headers']['Authorization'])
->equals('Basic ' . base64_encode('api:' . $this->settings['api_key']));
expect($request['body'])->equals(urldecode(http_build_query($body)));
}
function itCannotSendWithoutProperApiKey() {
$this->mailer->api_key = 'someapi';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCannotSendWithoutProperDomain() {
$this->mailer->url =
str_replace($this->settings['domain'], 'somedomain', $this->mailer->url);
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
}

View File

@ -1,95 +1,95 @@
<?php
use MailPoet\Mailer\MailPoet;
class MailPoetCest {
function _before() {
$this->settings = array(
'method' => 'MailPoet',
'api_key' => 'dhNSqj1XHkVltIliyQDvMiKzQShOA5rs0m_DdRUVZHU'
);
$this->fromEmail = 'staff@mailpoet.com';
$this->fromName = 'Sender';
$this->mailer = new MailPoet(
$this->settings['api_key'],
$this->fromEmail,
$this->fromName
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing MailPoet',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanGenerateBody() {
$subscriber = $this->mailer->processSubscriber($this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $subscriber);
expect($body['to']['address'])->equals($subscriber['email']);
expect($body['to']['name'])->equals($subscriber['name']);
expect($body['from']['address'])->equals($this->fromEmail);
expect($body['subject'])->equals($this->newsletter['subject']);
expect($body['html'])->equals($this->newsletter['body']['html']);
expect($body['text'])->equals($this->newsletter['body']['text']);
}
function itCanCreateRequest() {
$subscriber = $this->mailer->processSubscriber(
'Recipient <mailpoet-phoenix-test@mailinator.com>'
);
$body = array($this->mailer->getBody($this->newsletter, $subscriber));
$request = $this->mailer->request($this->newsletter, $subscriber);
expect($request['timeout'])->equals(10);
expect($request['httpversion'])->equals('1.0');
expect($request['method'])->equals('POST');
expect($request['headers']['Content-Type'])->equals('application/json');
expect($request['headers']['Authorization'])->equals($this->mailer->auth());
expect($request['body'])->equals($body);
}
function itCanProcessSubscriber() {
expect($this->mailer->processSubscriber('test@test.com'))
->equals(
array(
'email' => 'test@test.com',
'name' => ''
));
expect($this->mailer->processSubscriber('First <test@test.com>'))
->equals(
array(
'email' => 'test@test.com',
'name' => 'First'
));
expect($this->mailer->processSubscriber('First Last <test@test.com>'))
->equals(
array(
'email' => 'test@test.com',
'name' => 'First Last'
));
}
function itCanDoBasicAuth() {
expect($this->mailer->auth())
->equals('Basic ' . base64_encode('api:' . $this->settings['api_key']));
}
function itCannotSendWithoutProperAPIKey() {
$this->mailer->apiKey = 'someapi';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
}
<?php
use MailPoet\Mailer\Methods\MailPoet;
class MailPoetCest {
function _before() {
$this->settings = array(
'method' => 'MailPoet',
'api_key' => 'dhNSqj1XHkVltIliyQDvMiKzQShOA5rs0m_DdRUVZHU'
);
$this->from_email = 'staff@mailpoet.com';
$this->from_name = 'Sender';
$this->mailer = new MailPoet(
$this->settings['api_key'],
$this->from_email,
$this->from_name
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing MailPoet',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanGenerateBody() {
$subscriber = $this->mailer->processSubscriber($this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $subscriber);
expect($body['to']['address'])->equals($subscriber['email']);
expect($body['to']['name'])->equals($subscriber['name']);
expect($body['from']['address'])->equals($this->from_email);
expect($body['subject'])->equals($this->newsletter['subject']);
expect($body['html'])->equals($this->newsletter['body']['html']);
expect($body['text'])->equals($this->newsletter['body']['text']);
}
function itCanCreateRequest() {
$subscriber = $this->mailer->processSubscriber(
'Recipient <mailpoet-phoenix-test@mailinator.com>'
);
$body = array($this->mailer->getBody($this->newsletter, $subscriber));
$request = $this->mailer->request($this->newsletter, $subscriber);
expect($request['timeout'])->equals(10);
expect($request['httpversion'])->equals('1.0');
expect($request['method'])->equals('POST');
expect($request['headers']['Content-Type'])->equals('application/json');
expect($request['headers']['Authorization'])->equals($this->mailer->auth());
expect($request['body'])->equals($body);
}
function itCanProcessSubscriber() {
expect($this->mailer->processSubscriber('test@test.com'))
->equals(
array(
'email' => 'test@test.com',
'name' => ''
));
expect($this->mailer->processSubscriber('First <test@test.com>'))
->equals(
array(
'email' => 'test@test.com',
'name' => 'First'
));
expect($this->mailer->processSubscriber('First Last <test@test.com>'))
->equals(
array(
'email' => 'test@test.com',
'name' => 'First Last'
));
}
function itCanDoBasicAuth() {
expect($this->mailer->auth())
->equals('Basic ' . base64_encode('api:' . $this->settings['api_key']));
}
function itCannotSendWithoutProperApiKey() {
$this->mailer->api_key = 'someapi';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
}

View File

@ -1,90 +1,89 @@
<?php
use MailPoet\Mailer\API\Mandrill;
class MandrillCest {
function _before() {
$this->settings = array(
'method' => 'Mandrill',
'type' => 'API',
'api_key' => '692ys1B7REEoZN7R-dYwNA'
);
$this->fromEmail = 'staff@mailpoet.com';
$this->fromName = 'Sender';
$this->mailer = new Mandrill(
$this->settings['api_key'],
$this->fromEmail,
$this->fromName
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing Mandrill',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanGenerateBody() {
$subscriber = $this->mailer->processSubscriber($this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $subscriber);
expect($body['key'])->equals($this->settings['api_key']);
expect($body['message']['from_email'])->equals($this->fromEmail);
expect($body['message']['from_name'])->equals($this->fromName);
expect($body['message']['to'])->equals(array($subscriber));
expect($body['message']['subject'])->equals($this->newsletter['subject']);
expect($body['message']['html'])->equals($this->newsletter['body']['html']);
expect($body['message']['text'])->equals($this->newsletter['body']['text']);
expect($body['async'])->false();
}
function itCanCreateRequest() {
$subscriber = $this->mailer->processSubscriber($this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $subscriber);
$request = $this->mailer->request($this->newsletter, $subscriber);
expect($request['timeout'])->equals(10);
expect($request['httpversion'])->equals('1.0');
expect($request['method'])->equals('POST');
expect($request['headers']['Content-Type'])->equals('application/json');
expect($request['body'])->equals(json_encode($body));
}
function itCanProcessSubscriber() {
expect($this->mailer->processSubscriber('test@test.com'))
->equals(
array(
'email' => 'test@test.com',
'name' => ''
));
expect($this->mailer->processSubscriber('First <test@test.com>'))
->equals(
array(
'email' => 'test@test.com',
'name' => 'First'
));
expect($this->mailer->processSubscriber('First Last <test@test.com>'))
->equals(
array(
'email' => 'test@test.com',
'name' => 'First Last'
));
}
function itCannotSendWithoutProperAPIKey() {
$this->mailer->apiKey = 'someapi';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
<?php
use MailPoet\Mailer\Methods\Mandrill;
class MandrillCest {
function _before() {
$this->settings = array(
'method' => 'Mandrill',
'api_key' => '692ys1B7REEoZN7R-dYwNA'
);
$this->from_email = 'staff@mailpoet.com';
$this->from_name = 'Sender';
$this->mailer = new Mandrill(
$this->settings['api_key'],
$this->from_email,
$this->from_name
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing Mandrill',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanGenerateBody() {
$subscriber = $this->mailer->processSubscriber($this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $subscriber);
expect($body['key'])->equals($this->settings['api_key']);
expect($body['message']['from_email'])->equals($this->from_email);
expect($body['message']['from_name'])->equals($this->from_name);
expect($body['message']['to'])->equals(array($subscriber));
expect($body['message']['subject'])->equals($this->newsletter['subject']);
expect($body['message']['html'])->equals($this->newsletter['body']['html']);
expect($body['message']['text'])->equals($this->newsletter['body']['text']);
expect($body['async'])->false();
}
function itCanCreateRequest() {
$subscriber = $this->mailer->processSubscriber($this->subscriber);
$body = $this->mailer->getBody($this->newsletter, $subscriber);
$request = $this->mailer->request($this->newsletter, $subscriber);
expect($request['timeout'])->equals(10);
expect($request['httpversion'])->equals('1.0');
expect($request['method'])->equals('POST');
expect($request['headers']['Content-Type'])->equals('application/json');
expect($request['body'])->equals(json_encode($body));
}
function itCanProcessSubscriber() {
expect($this->mailer->processSubscriber('test@test.com'))
->equals(
array(
'email' => 'test@test.com',
'name' => ''
));
expect($this->mailer->processSubscriber('First <test@test.com>'))
->equals(
array(
'email' => 'test@test.com',
'name' => 'First'
));
expect($this->mailer->processSubscriber('First Last <test@test.com>'))
->equals(
array(
'email' => 'test@test.com',
'name' => 'First Last'
));
}
function itCannotSendWithoutProperApiKey() {
$this->mailer->api_key = 'someapi';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
}

View File

@ -1,92 +1,92 @@
<?php
use MailPoet\Mailer\SMTP;
class SMTPCest {
function _before() {
$this->settings = array(
'method' => 'SMTP',
'host' => 'email-smtp.us-west-2.amazonaws.com',
'port' => 587,
'login' => 'AKIAIGPBLH6JWG5VCBQQ',
'password' => 'AudVHXHaYkvr54veCzqiqOxDiMMyfQW3/V6F1tYzGXY3',
'authentication' => '1',
'encryption' => 'tls'
);
$this->fromEmail = 'staff@mailpoet.com';
$this->fromName = 'Sender';
$this->mailer = new SMTP(
$this->settings['host'],
$this->settings['port'],
$this->settings['authentication'],
$this->settings['login'],
$this->settings['password'],
$this->settings['encryption'],
$this->fromEmail,
$this->fromName
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing SMTP',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanBuildMailer() {
$mailer = $this->mailer->buildMailer();
expect($mailer->getTransport()->getHost())
->equals($this->settings['host']);
expect($mailer->getTransport()->getPort())
->equals($this->settings['port']);
expect($mailer->getTransport()->getUsername())
->equals($this->settings['login']);
expect($mailer->getTransport()->getPassword())
->equals($this->settings['password']);
expect($mailer->getTransport()->getEncryption())
->equals($this->settings['encryption']);
}
function itCanCreateMessage() {
$message = $this->mailer->createMessage($this->newsletter, $this->subscriber);
expect($message->getTo())
->equals(array('mailpoet-phoenix-test@mailinator.com' => 'Recipient'));
expect($message->getFrom())
->equals(array($this->fromEmail => $this->fromName));
expect($message->getSubject())
->equals($this->newsletter['subject']);
expect($message->getBody())
->equals($this->newsletter['body']['html']);
expect($message->getChildren()[0]->getContentType())
->equals('text/plain');
}
function itCanProcessSubscriber() {
expect($this->mailer->processSubscriber('test@test.com'))
->equals(array('test@test.com' => ''));
expect($this->mailer->processSubscriber('First <test@test.com>'))
->equals(array('test@test.com' => 'First'));
expect($this->mailer->processSubscriber('First Last <test@test.com>'))
->equals(array('test@test.com' => 'First Last'));
}
function itCantSentWithoutProperAuthentication() {
$this->mailer->login = 'someone';
$this->mailer->mailer = $this->mailer->buildMailer();
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
<?php
use MailPoet\Mailer\Methods\SMTP;
class SMTPCest {
function _before() {
$this->settings = array(
'method' => 'SMTP',
'host' => 'email-smtp.us-west-2.amazonaws.com',
'port' => 587,
'login' => 'AKIAIGPBLH6JWG5VCBQQ',
'password' => 'AudVHXHaYkvr54veCzqiqOxDiMMyfQW3/V6F1tYzGXY3',
'authentication' => '1',
'encryption' => 'tls'
);
$this->from_email = 'staff@mailpoet.com';
$this->from_name = 'Sender';
$this->mailer = new SMTP(
$this->settings['host'],
$this->settings['port'],
$this->settings['authentication'],
$this->settings['login'],
$this->settings['password'],
$this->settings['encryption'],
$this->from_email,
$this->from_name
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing SMTP',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanBuildMailer() {
$mailer = $this->mailer->buildMailer();
expect($mailer->getTransport()->getHost())
->equals($this->settings['host']);
expect($mailer->getTransport()->getPort())
->equals($this->settings['port']);
expect($mailer->getTransport()->getUsername())
->equals($this->settings['login']);
expect($mailer->getTransport()->getPassword())
->equals($this->settings['password']);
expect($mailer->getTransport()->getEncryption())
->equals($this->settings['encryption']);
}
function itCanCreateMessage() {
$message = $this->mailer->createMessage($this->newsletter, $this->subscriber);
expect($message->getTo())
->equals(array('mailpoet-phoenix-test@mailinator.com' => 'Recipient'));
expect($message->getFrom())
->equals(array($this->from_email => $this->from_name));
expect($message->getSubject())
->equals($this->newsletter['subject']);
expect($message->getBody())
->equals($this->newsletter['body']['html']);
expect($message->getChildren()[0]->getContentType())
->equals('text/plain');
}
function itCanProcessSubscriber() {
expect($this->mailer->processSubscriber('test@test.com'))
->equals(array('test@test.com' => ''));
expect($this->mailer->processSubscriber('First <test@test.com>'))
->equals(array('test@test.com' => 'First'));
expect($this->mailer->processSubscriber('First Last <test@test.com>'))
->equals(array('test@test.com' => 'First Last'));
}
function itCantSentWithoutProperAuthentication() {
$this->mailer->login = 'someone';
$this->mailer->mailer = $this->mailer->buildMailer();
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
}

View File

@ -1,71 +1,70 @@
<?php
use MailPoet\Mailer\API\SendGrid;
class SendGridCest {
function _before() {
$this->settings = array(
'method' => 'SendGrid',
'type' => 'API',
'api_key' => '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'
);
$this->fromEmail = 'staff@mailpoet.com';
$this->fromName = 'Sender';
$this->mailer = new SendGrid(
$this->settings['api_key'],
$this->fromEmail,
$this->fromName
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing SendGrid',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanGenerateBody() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($body['to'])->contains($this->subscriber);
expect($body['from'])->equals($this->fromEmail);
expect($body['fromname'])->equals($this->fromName);
expect($body['subject'])->equals($this->newsletter['subject']);
expect($body['html'])->equals($this->newsletter['body']['html']);
expect($body['text'])->equals($this->newsletter['body']['text']);
}
function itCanCreateRequest() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$request = $this->mailer->request($this->newsletter, $this->subscriber);
expect($request['timeout'])->equals(10);
expect($request['httpversion'])->equals('1.1');
expect($request['method'])->equals('POST');
expect($request['headers']['Authorization'])
->equals('Bearer ' . $this->settings['api_key']);
expect($request['body'])->equals(urldecode(http_build_query($body)));
}
function itCanDoBasicAuth() {
expect($this->mailer->auth())
->equals('Bearer ' . $this->settings['api_key']);
}
function itCannotSendWithoutProperAPIKey() {
$this->mailer->apiKey = 'someapi';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
}
<?php
use MailPoet\Mailer\Methods\SendGrid;
class SendGridCest {
function _before() {
$this->settings = array(
'method' => 'SendGrid',
'api_key' => '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'
);
$this->from_email = 'staff@mailpoet.com';
$this->from_name = 'Sender';
$this->mailer = new SendGrid(
$this->settings['api_key'],
$this->from_email,
$this->from_name
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing SendGrid',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanGenerateBody() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
expect($body['to'])->contains($this->subscriber);
expect($body['from'])->equals($this->from_email);
expect($body['from_name'])->equals($this->from_name);
expect($body['subject'])->equals($this->newsletter['subject']);
expect($body['html'])->equals($this->newsletter['body']['html']);
expect($body['text'])->equals($this->newsletter['body']['text']);
}
function itCanCreateRequest() {
$body = $this->mailer->getBody($this->newsletter, $this->subscriber);
$request = $this->mailer->request($this->newsletter, $this->subscriber);
expect($request['timeout'])->equals(10);
expect($request['httpversion'])->equals('1.1');
expect($request['method'])->equals('POST');
expect($request['headers']['Authorization'])
->equals('Bearer ' . $this->settings['api_key']);
expect($request['body'])->equals(urldecode(http_build_query($body)));
}
function itCanDoBasicAuth() {
expect($this->mailer->auth())
->equals('Bearer ' . $this->settings['api_key']);
}
function itCannotSendWithoutProperApiKey() {
$this->mailer->api_key = 'someapi';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->false();
}
function itCanSend() {
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
expect($result)->true();
}
}

View File

@ -1,70 +1,70 @@
<?php
use MailPoet\Mailer\WPMail;
class WPMailCest {
function _before() {
$this->settings = array(
'method' => 'WPMail'
);
$this->fromEmail = 'staff@mailpoet.com';
$this->fromName = 'Sender';
$this->mailer = new WPMail(
$this->fromEmail,
$this->fromName
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing SMTP',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanAddFilters() {
$this->mailer->addFilters();
expect(has_filter('wp_mail_from_name', array(
$this->mailer,
'setFromName'
)))->notEmpty();
expect(has_filter('wp_mail_from', array(
$this->mailer,
'setFromEmail'
)))->notEmpty();
expect(has_filter('wp_mail_content_type', array(
$this->mailer,
'setContentType'
)))->notEmpty();
}
function itCanRemoveFilters() {
$this->mailer->addFilters();
$this->mailer->removeFilters();
expect(has_filter('wp_mail_from_name'))->false();
expect(has_filter('wp_mail_from'))->false();
expect(has_filter('wp_mail_content_type'))->false();
}
function itCanSetFromName() {
expect($this->mailer->setFromName())->equals($this->fromName);
}
function itCanSetFromEmail() {
expect($this->mailer->setFromName())->equals($this->fromName);
}
function itCanSetContentType() {
expect($this->mailer->setContentType())->equals('text/html');
}
function itCanSend() {
$_SERVER['SERVER_NAME'] = 'localhost';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
//expect($result)->true();
}
<?php
use MailPoet\Mailer\Methods\WPMail;
class WPMailCest {
function _before() {
$this->settings = array(
'method' => 'WPMail'
);
$this->from_email = 'staff@mailpoet.com';
$this->from_name = 'Sender';
$this->mailer = new WPMail(
$this->from_email,
$this->from_name
);
$this->subscriber = 'Recipient <mailpoet-phoenix-test@mailinator.com>';
$this->newsletter = array(
'subject' => 'testing SMTP',
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
}
function itCanAddFilters() {
$this->mailer->addFilters();
expect(has_filter('wp_mail_from_name', array(
$this->mailer,
'setFromName'
)))->notEmpty();
expect(has_filter('wp_mail_from', array(
$this->mailer,
'setFromEmail'
)))->notEmpty();
expect(has_filter('wp_mail_content_type', array(
$this->mailer,
'setContentType'
)))->notEmpty();
}
function itCanRemoveFilters() {
$this->mailer->addFilters();
$this->mailer->removeFilters();
expect(has_filter('wp_mail_from_name'))->false();
expect(has_filter('wp_mail_from'))->false();
expect(has_filter('wp_mail_content_type'))->false();
}
function itCanSetFromName() {
expect($this->mailer->setFromName())->equals($this->from_name);
}
function itCanSetFromEmail() {
expect($this->mailer->setFromEmail())->equals($this->from_email);
}
function itCanSetContentType() {
expect($this->mailer->setContentType())->equals('text/html');
}
function itCanSend() {
$_SERVER['SERVER_NAME'] = 'localhost';
$result = $this->mailer->send(
$this->newsletter,
$this->subscriber
);
//expect($result)->true();
}
}

View File

@ -13,118 +13,260 @@ use MailPoet\Newsletter\Renderer\Renderer;
class NewsletterRendererCest {
function __construct() {
$this->newsletterData = json_decode(file_get_contents(dirname(__FILE__) . '/RendererTestData.json'), true);
$this->renderer = new Renderer($this->newsletterData);
$this->columnRenderer = new ColumnRenderer();
$this->queryDOM = new \pQuery();
$this->newsletter = array(
'body' => file_get_contents(dirname(__FILE__) . '/RendererTestData.json'),
'subject' => 'Some subject',
'preheader' => 'Some preheader'
);
$this->renderer = new Renderer($this->newsletter);
$this->column_renderer = new ColumnRenderer();
$this->DOM_parser = new \pQuery();
}
function itRendersCompleteNewsletter() {
$template = $this->renderer->renderAll();
$DOM = $this->queryDOM->parseStr($template);
// we expect to have 4 column containers and 7 columns (1x1, 1x2, 1x3, 1x1)
expect(count($DOM('.mailpoet_cols-wrapper')))->equals(4);
expect(count($DOM('.mailpoet_force-row')))->equals(7);
$template = $this->renderer->render();
$DOM = $this->DOM_parser->parseStr($template);
// we expect to have 7 columns:
// 1x column including header
// 2x column
// 3x column
// 1x footer
expect(count($DOM('.mailpoet_cols-one')))->equals(2);
expect(count($DOM('.mailpoet_cols-two')))->equals(2);
expect(count($DOM('.mailpoet_cols-three')))->equals(3);
}
function itRendersColumns() {
$columnContent = array(
function itRendersOneColumn() {
$column_content = array(
'one'
);
$column_styles = array(
'block' => array(
'backgroundColor' => "#999999"
)
);
$DOM = $this->DOM_parser->parseStr(
$this->column_renderer->render(
$column_styles,
count($column_content),
$column_content)
);
foreach($DOM('table.mailpoet_cols-one > tbody') as $column) {
$rendered_column_content[] = trim($column->text());
};
expect($rendered_column_content)->equals($column_content);
}
function itRendersTwoColumns() {
$column_content = array(
'one',
'two'
);
$column_styles = array(
'block' => array(
'backgroundColor' => "#999999"
)
);
$DOM = $this->DOM_parser->parseStr(
$this->column_renderer->render(
$column_styles,
count($column_content),
$column_content)
);
foreach($DOM('table.mailpoet_cols-two > tbody') as $column) {
$rendered_column_content[] = trim($column->text());
};
expect($rendered_column_content)->equals($column_content);
}
function itRendersThreeColumns() {
$column_content = array(
'one',
'two',
'three'
);
$DOM = $this->queryDOM->parseStr($this->columnRenderer->render(count($columnContent), $columnContent));
// rendered object should cocntain three columns
foreach ($DOM('.mailpoet_force-row > tbody') as $column) {
$renderedColumnContent[] = trim($column->text());
$column_styles = array(
'block' => array(
'backgroundColor' => "#999999"
)
);
$DOM = $this->DOM_parser->parseStr(
$this->column_renderer->render(
$column_styles,
count($column_content),
$column_content)
);
foreach($DOM('table.mailpoet_cols-three > tbody') as $column) {
$rendered_column_content[] = trim($column->text());
};
expect(count(array_diff($renderedColumnContent, $columnContent)))->equals(0);
expect($rendered_column_content)->equals($column_content);
}
function itRendersHeader() {
$template = $this->newsletterData['content']['blocks'][0]['blocks'][0]['blocks'][0];
$DOM = $this->queryDOM->parseStr(Header::render($template));
// element should be proplerly nested, and styles should be applied to <td>, <a> and <p> elements
expect(is_object($DOM('tr > td > p', 0)))->true();
expect(is_object($DOM('tr > td > p > a', 0)))->true();
$newsletter = json_decode($this->newsletter['body'], true);
$template = $newsletter['content']['blocks'][0]['blocks'][0]['blocks'][0];
$DOM = $this->DOM_parser->parseStr(Header::render($template));
// element should be proplerly nested, and styles should be applied
expect(!empty($DOM('tr > td.mailpoet_header', 0)->html()))->true();
expect(!empty($DOM('tr > td > a', 0)->html()))->true();
expect($DOM('a', 0)->attr('style'))->notEmpty();
expect($DOM('p', 0)->attr('style'))->notEmpty();
expect($DOM('td', 0)->attr('style'))->notEmpty();
}
function itRendersImage() {
$template = $this->newsletterData['content']['blocks'][0]['blocks'][0]['blocks'][1];
$DOM = $this->queryDOM->parseStr(Image::render($template));
// element should be properly nested, it's width set and style applied to <td>
expect(is_object($DOM('tr > td > img', 0)))->true();
expect($DOM('tr > td > img', 0)->attr('width'))->equals(560);
expect($DOM('tr > td', 0)->attr('style'))->notEmpty();
function itRendersImage() {
$newsletter = json_decode($this->newsletter['body'], true);
$template = $newsletter['content']['blocks'][0]['blocks'][0]['blocks'][1];
$DOM = $this->DOM_parser->parseStr(Image::render($template, $columnCount = 1));
// element should be properly nested, it's width set and style applied
expect($DOM('tr > td > img', 0)->attr('width'))->equals(620);
expect($DOM('tr > td > img', 0)->attr('style'))->notEmpty();
}
function itAdjustImageSizeBasedOnColumnWidth() {
$newsletter = json_decode($this->newsletter['body'], true);
$template = $newsletter['content']['blocks'][0]['blocks'][0]['blocks'][1];
$template['width'] = '800px';
$DOM = $this->DOM_parser->parseStr(Image::render($template, $columnCount = 2));
// 800px resized to 330px (2-column layout) and 40px padding applied
expect($DOM('tr > td > img', 0)->attr('width'))->equals(290);
$template['width'] = '280px';
$DOM = $this->DOM_parser->parseStr(Image::render($template, $columnCount = 2));
// 280px image should not be resized and padding should not be applied
expect($DOM('tr > td > img', 0)->attr('width'))->equals(280);
}
function itRendersText() {
$template = $this->newsletterData['content']['blocks'][0]['blocks'][0]['blocks'][2];
$DOM = $this->queryDOM->parseStr(Text::render($template));
$newsletter = json_decode($this->newsletter['body'], true);
$template = $newsletter['content']['blocks'][0]['blocks'][0]['blocks'][2];
$DOM = $this->DOM_parser->parseStr(Text::render($template));
// blockquotes and paragraphs should be converted to spans and placed inside a table
expect(is_object($DOM('tr > td.mailpoet_text > table > tr > td > span.paragraph', 0)))->true();
expect(is_object($DOM('tr > td.mailpoet_text > table.mailpoet_blockquote > tbody > tr > td > table > tr > td > span.paragraph', 0)))->true();
expect(
!empty($DOM('tr > td > table > tr > td.mailpoet_paragraph', 0)->html())
)->true();
expect(
!empty($DOM('tr > td > table > tr > td.mailpoet_blockquote', 0)->html()
))->true();
// ul/ol/li should have mailpoet_paragraph class added & styles applied
expect(
!empty(
$DOM('tr > td > ul.mailpoet_paragraph > li.mailpoet_paragraph', 0)->html()
)
)->true();
expect(
!empty(
$DOM('tr > td > ol.mailpoet_paragraph > li.mailpoet_paragraph', 0)->html()
)
)->true();
expect($DOM('tr > td.mailpoet_text > ul.mailpoet_paragraph', 0)->attr('style'))
->contains('padding-top:0;padding-bottom:0;margin-top:0;margin-bottom:0;');
// headings should be styled
expect($DOM('tr > td.mailpoet_text > h1', 0)->attr('style'))
->contains('margin:0;font-style:normal;font-weight:normal;');
}
function itRendersDivider() {
$template = $this->newsletterData['content']['blocks'][0]['blocks'][0]['blocks'][3];
$DOM = $this->queryDOM->parseStr(Divider::render($template));
$newsletter = json_decode($this->newsletter['body'], true);
$template = $newsletter['content']['blocks'][0]['blocks'][0]['blocks'][3];
$DOM = $this->DOM_parser->parseStr(Divider::render($template));
// element should be properly nested and its border-top-width set
expect(is_object($DOM('tr > td.mailpoet_divider > table > tr > td', 0)))->true();
expect(preg_match('/border-top-width: 3px/', $DOM('tr > td.mailpoet_divider > table > tr > td', 0)->attr('style')))->equals(1);
expect(
preg_match(
'/border-top-width: 3px/',
$DOM('tr > td.mailpoet_divider > table > tr > td.mailpoet_divider-cell', 0)->attr('style')
))->equals(1);
}
function itRendersSpacer() {
$template = $this->newsletterData['content']['blocks'][0]['blocks'][0]['blocks'][4];
$DOM = $this->queryDOM->parseStr(Spacer::render($template));
$newsletter = json_decode($this->newsletter['body'], true);
$template = $newsletter['content']['blocks'][0]['blocks'][0]['blocks'][4];
$DOM = $this->DOM_parser->parseStr(Spacer::render($template));
// element should be properly nested and its height set
expect(is_object($DOM('tr > td.mailpoet_spacer', 0)))->true();
expect(preg_match('/height: 50px/', $DOM('tr > td.mailpoet_spacer', 0)->attr('style')))->equals(1);
expect($DOM('tr > td.mailpoet_spacer', 0)->attr('height'))->equals(50);
}
function itRendersButton() {
$template = $this->newsletterData['content']['blocks'][0]['blocks'][0]['blocks'][5];
$DOM = $this->queryDOM->parseStr(Button::render($template));
$newsletter = json_decode($this->newsletter['body'], true);
$template = $newsletter['content']['blocks'][0]['blocks'][0]['blocks'][5];
$DOM = $this->DOM_parser->parseStr(Button::render($template));
// element should be properly nested with arcsize/styles/fillcolor set
expect(is_object($DOM('tr > td.mailpoet_button > div > table > tr > td > a.mailpoet_button', 0)))->true();
expect(preg_match('/line-height: 30px/', $DOM('a.mailpoet_button', 0)->attr('style')))->equals(1);
expect(preg_match('/arcsize="' . round(20 / 30 * 100) . '%"/', $DOM('tr > td.mailpoet_button > div > table > tr > td', 0)->text()))->equals(1);
expect(preg_match('/style="height:30px.*?width:100px/', $DOM('tr > td.mailpoet_button > div > table > tr > td', 0)->text()))->equals(1);
expect(preg_match('/style="color:#ffffff.*?font-family:Arial.*?font-size:13px/', $DOM('tr > td.mailpoet_button > div > table > tr > td', 0)->text()))->equals(1);
expect(preg_match('/fillcolor="#666666/', $DOM('tr > td.mailpoet_button > div > table > tr > td', 0)->text()))->equals(1);
expect(
!empty($DOM('tr > td > div > table > tr > td > a.mailpoet_button', 0)->html())
)->true();
expect(
preg_match(
'/line-height: 30px/',
$DOM('a.mailpoet_button', 0)->attr('style'))
)->equals(1);
expect(
preg_match(
'/arcsize="' . round(20 / 30 * 100) . '%"/',
$DOM('tr > td > div > table > tr > td', 0)->text())
)->equals(1);
expect(
preg_match(
'/style="height:30px.*?width:100px/',
$DOM('tr > td > div > table > tr > td', 0)->text())
)->equals(1);
expect(
preg_match(
'/style="color:#ffffff.*?font-family:Arial.*?font-size:14px/',
$DOM('tr > td > div > table > tr > td', 0)->text())
)->equals(1);
expect(
preg_match(
'/fillcolor="#666666/',
$DOM('tr > td > div > table > tr > td', 0)->text())
)->equals(1);
}
function itRendersSocialIcons() {
$template = $this->newsletterData['content']['blocks'][0]['blocks'][0]['blocks'][6];
$DOM = $this->queryDOM->parseStr(Social::render($template));
// element should be properly nested, contain social icons and image source/link href/alt should be properly set
expect(is_object($DOM('tr > td.mailpoet_social > div', 0)))->true();
$newsletter = json_decode($this->newsletter['body'], true);
$template = $newsletter['content']['blocks'][0]['blocks'][0]['blocks'][6];
$DOM = $this->DOM_parser->parseStr(Social::render($template));
// element should be properly nested, contain social icons and
// image source/link href/alt should be properly set
expect(!empty($DOM('tr > td', 0)->html()))->true();
expect($DOM('a', 0)->attr('href'))->equals('http://example.com');
expect($DOM('td > a:nth-of-type(10) > img')->attr('src'))->contains('custom.png');
expect($DOM('td > a:nth-of-type(10) > img')->attr('alt'))->equals('custom');
// there should be 10 icons
expect(count($DOM('a > img')))->equals(10);
expect($DOM('a', 0)->attr('href'))->equals('http://example.org');
expect($DOM('a > img', 0)->attr('src'))->equals('http://mp3.mailpoet.net/various/social-icons/custom.png');
expect($DOM('a > img', 0)->attr('alt'))->equals('custom');
}
function itRendersFooter() {
$template = $this->newsletterData['content']['blocks'][3]['blocks'][0]['blocks'][0];
$DOM = $this->queryDOM->parseStr(Footer::render($template));
// element should be proplerly nested, and styles should be applied to <td>, <a> and <p> elements
expect(is_object($DOM('tr > td.mailpoet_footer > div', 0)))->true();
expect(is_object($DOM('tr > td.mailpoet_footer > div > a > p', 0)))->true();
expect($DOM('tr > td.mailpoet_footer', 0)->attr('style'))->notEmpty();
$newsletter = json_decode($this->newsletter['body'], true);
$template = $newsletter['content']['blocks'][3]['blocks'][0]['blocks'][0];
$DOM = $this->DOM_parser->parseStr(Footer::render($template));
// element should be properly nested, and styles should be applied
expect(!empty($DOM('tr > td.mailpoet_footer', 0)->html()))->true();
expect(!empty($DOM('tr > td > a', 0)->html()))->true();
expect($DOM('a', 0)->attr('style'))->notEmpty();
expect($DOM('p', 0)->attr('style'))->notEmpty();
expect($DOM('td', 0)->attr('style'))->notEmpty();
}
}
function itSetsSubject() {
$template = $this->renderer->render();
$DOM = $this->DOM_parser->parseStr($template);
$subject = trim($DOM('title')->text());
expect($subject)->equals($this->newsletter['subject']);
}
function itSetsPreheader() {
$template = $this->renderer->render();
$DOM = $this->DOM_parser->parseStr($template);
$preheader = trim($DOM('td.mailpoet_preheader')->text());
expect($preheader)->equals($this->newsletter['preheader']);
}
function itPostProcessesTemplate() {
$template = $this->renderer->render();
// !important should be stripped from everywhere except from
// with the <style> tag
expect(preg_match('/<style.*?important/s', $template))
->equals(1);
expect(preg_match('/mailpoet_template.*?important/s', $template))
->equals(0);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,72 +0,0 @@
<?php
use MailPoet\Router\Mailer;
class MailerCest {
function __construct() {
$this->router = new Mailer();
}
function itCanConstruct() {
// TOFIX: "from" property doesn't exist on $this->router
// the sender should be explicitely defined in this unit test.
//expect($this->router->from)->equals('Sender <staff@mailpoet.com>');
}
function itCanTransformSubscriber() {
expect($this->router->transformSubscriber('test@email.com'))
->equals('test@email.com');
expect($this->router->transformSubscriber(
array(
'email' => 'test@email.com'
))
)->equals('test@email.com');
expect($this->router->transformSubscriber(
array(
'first_name' => 'First',
'email' => 'test@email.com'
))
)->equals('First <test@email.com>');
expect($this->router->transformSubscriber(
array(
'last_name' => 'Last',
'email' => 'test@email.com'
))
)->equals('Last <test@email.com>');
expect($this->router->transformSubscriber(
array(
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'test@email.com'
))
)->equals('First Last <test@email.com>');
}
function itCanConfigureMailer() {
// TOFIX: This fails because $this->router->mailer is not set
/*$mailer = $this->router->buildMailer();
$class = 'Mailpoet\\Mailer\\' .
((isset($this->router->mailer['type'])) ?
$this->router->mailer['type'] . '\\' . $this->router->mailer['method'] :
$this->router->mailer['method']
);
expect($mailer instanceof $class)->true();
expect(method_exists($mailer, 'send'))->true();*/
}
function itCanSend() {
// TOFIX: This fails because $this->router->mailer is not set
/*$newsletter = array(
'subject' => 'testing Mailer router with ' . $this->router->mailer['method'],
'body' => array(
'html' => 'HTML body',
'text' => 'TEXT body'
)
);
$subscriber = array(
'first_name' => 'First',
'last_name' => 'Last',
'email' => 'mailpoet-phoenix-test@mailinator.com'
);
expect($this->router->send($newsletter, $subscriber))->true();*/
}
}

View File

@ -10,15 +10,15 @@ use MailPoet\Subscribers\ImportExport\Export\Export;
class ExportCest {
function _before() {
$this->JSONdata = json_decode(file_get_contents(dirname(__FILE__) . '/ExportTestData.json'), true);
$this->subscriberFields = array(
$this->JSON_data = json_decode(file_get_contents(dirname(__FILE__) . '/ExportTestData.json'), true);
$this->subscriber_fields = array(
'first_name' => 'First name',
'last_name' => 'Last name',
'email' => 'Email',
1 => 'Country'
);
$this->subscribersData = array(
$this->subscribers_data = array(
array(
'first_name' => 'Adam',
'last_name' => 'Smith',
@ -42,13 +42,13 @@ class ExportCest {
'email' => 'paul@newman.com'
)
);
$this->customFieldsData = array(
$this->custom_fields_data = array(
array(
'name' => 'Country',
'type' => 'text'
)
);
$this->segmentsData = array(
$this->segments_data = array(
array(
'name' => 'Newspapers'
),
@ -56,7 +56,7 @@ class ExportCest {
'name' => 'Journals'
)
);
foreach ($this->subscribersData as $subscriber) {
foreach($this->subscribers_data as $subscriber) {
if(isset($subscriber[1])) {
unset($subscriber[1]);
}
@ -64,20 +64,20 @@ class ExportCest {
$entity->hydrate($subscriber);
$entity->save();
}
foreach ($this->segmentsData as $customField) {
foreach($this->segments_data as $custom_field) {
$entity = Segment::create();
$entity->hydrate($customField);
$entity->hydrate($custom_field);
$entity->save();
}
foreach ($this->customFieldsData as $customField) {
foreach($this->custom_fields_data as $custom_field) {
$entity = CustomField::create();
$entity->hydrate($customField);
$entity->hydrate($custom_field);
$entity->save();
}
$entity = SubscriberCustomField::create();
$entity->subscriber_id = 2;
$entity->custom_field_id = 1;
$entity->value = $this->subscribersData[1][1];
$entity->value = $this->subscribers_data[1][1];
$entity->save();
$entity = SubscriberSegment::create();
$entity->subscriber_id = 1;
@ -95,15 +95,15 @@ class ExportCest {
$entity->subscriber_id = 3;
$entity->segment_id = 2;
$entity->save();
$this->export = new Export($this->JSONdata);
$this->export = new Export($this->JSON_data);
}
function itCanConstruct() {
expect($this->export->exportConfirmedOption)
expect($this->export->export_confirmed_option)
->equals(false);
expect($this->export->exportFormatOption)
expect($this->export->export_format_option)
->equals('csv');
expect($this->export->groupBySegmentOption)
expect($this->export->group_by_segment_option)
->equals(false);
expect($this->export->segments)
->equals(
@ -112,9 +112,9 @@ class ExportCest {
2
)
);
expect($this->export->subscribersWithoutSegment)
expect($this->export->subscribers_without_segment)
->equals(0);
expect($this->export->subscriberFields)
expect($this->export->subscriber_fields)
->equals(
array(
'email',
@ -126,42 +126,41 @@ class ExportCest {
preg_match(
'|' .
Env::$temp_path . '/MailPoet_export_[a-f0-9]{4}.' .
$this->export->exportFormatOption .
'|', $this->export->exportFile)
$this->export->export_format_option .
'|', $this->export->export_file)
)->equals(1);
expect(
preg_match(
'|' .
Env::$plugin_url . '/' .
Env::$temp_name . '/' .
basename($this->export->exportFile) .
Env::$temp_URL . '/' .
basename($this->export->export_file) .
'|'
, $this->export->exportFileURL)
, $this->export->export_file_URL)
)->equals(1);
}
function itCanGetSubscriberCustomFields() {
$source = CustomField::where('name', $this->customFieldsData[0]['name'])
$source = CustomField::where('name', $this->custom_fields_data[0]['name'])
->findOne();
$target = $this->export->getSubscriberCustomFields();
expect($target)->equals(array($source->id => $source->name));
}
function itCanFormatSubscriberFields() {
$formattedSubscriberFields = $this->export->formatSubscriberFields(
array_keys($this->subscriberFields),
function itCanFormatsubscriber_fields() {
$formatted_subscriber_fields = $this->export->formatSubscriberFields(
array_keys($this->subscriber_fields),
$this->export->getSubscriberCustomFields()
);
expect($formattedSubscriberFields)
->equals(array_values($this->subscriberFields));
expect($formatted_subscriber_fields)
->equals(array_values($this->subscriber_fields));
}
function itProperlyReturnsSubscriberCustomFields() {
$subscribers = $this->export->getSubscribers();
foreach ($subscribers as $subscriber) {
if($subscriber['email'] === $this->subscribersData[1]) {
foreach($subscribers as $subscriber) {
if($subscriber['email'] === $this->subscribers_data[1]) {
expect($subscriber['Country'])
->equals($this->subscribersData[1][1]);
->equals($this->subscribers_data[1][1]);
}
}
}
@ -182,26 +181,26 @@ class ExportCest {
}
function itCanGroupSubscribersBySegments() {
$this->export->groupBySegmentOption = true;
$this->export->subscribersWithoutSegment = true;
$this->export->group_by_segment_option = true;
$this->export->subscribers_without_segment = true;
$subscribers = $this->export->getSubscribers();
expect(count($subscribers))->equals(5);
}
function itCanGetSubscribersOnlyWithoutSegments() {
$this->export->segments = array(0);
$this->export->subscribersWithoutSegment = true;
$this->export->subscribers_without_segment = true;
$subscribers = $this->export->getSubscribers();
expect(count($subscribers))->equals(1);
expect($subscribers[0]['segment_name'])->equals('Not In List');
expect($subscribers[0]['segment_name'])->equals('Not In Segment');
}
function itCanGetOnlyConfirmedSubscribers() {
$this->export->exportConfirmedOption = true;
$this->export->export_confirmed_option = true;
$subscribers = $this->export->getSubscribers();
expect(count($subscribers))->equals(1);
expect($subscribers[0]['email'])
->equals($this->subscribersData[1]['email']);
->equals($this->subscribers_data[1]['email']);
}
function itCanGetSubscribersOnlyInSegments() {
@ -213,18 +212,17 @@ class ExportCest {
}
function itCanProcess() {
$this->export->exportFile = $this->export->getExportFile('csv');
$this->export->exportFormatOption = 'csv';
$this->export->export_file = $this->export->getExportFile('csv');
$this->export->export_format_option = 'csv';
$this->export->process();
$CSVFileSize = filesize($this->export->exportFile);
$this->export->exportFile = $this->export->getExportFile('xls');
$this->export->exportFormatOption = 'xls';
$CSV_file_size = filesize($this->export->export_file);
$this->export->export_file = $this->export->getExportFile('xls');
$this->export->export_format_option = 'xls';
$this->export->process();
$XLSFileSize = filesize($this->export->exportFile);
expect($CSVFileSize)->greaterThan(0);
expect($XLSFileSize)->greaterThan(0);
expect($XLSFileSize)->greaterThan($CSVFileSize);
$XLS_file_size = filesize($this->export->export_file);
expect($CSV_file_size)->greaterThan(0);
expect($XLS_file_size)->greaterThan(0);
expect($XLS_file_size)->greaterThan($CSV_file_size);
}
function _after() {
@ -234,4 +232,4 @@ class ExportCest {
ORM::raw_execute('TRUNCATE ' . CustomField::$_table);
ORM::raw_execute('TRUNCATE ' . SubscriberCustomField::$_table);
}
}
}

View File

@ -1,12 +1,12 @@
{
"exportConfirmedOption": false,
"exportFormatOption": "csv",
"groupBySegmentOption": false,
"export_confirmed_option": false,
"export_format_option": "csv",
"group_by_segment_option": false,
"segments": [
"1",
"2"
],
"subscriberFields": [
"subscriber_fields": [
"email",
"first_name",
"1"

View File

@ -8,8 +8,8 @@ use MailPoet\Util\Helpers;
class ImportCest {
function __construct() {
$this->JSONdata = json_decode(file_get_contents(dirname(__FILE__) . '/ImportTestData.json'), true);
$this->subscribersData = array(
$this->JSON_data = json_decode(file_get_contents(dirname(__FILE__) . '/ImportTestData.json'), true);
$this->subscribers_data = array(
'first_name' => array(
'Adam',
'Mary'
@ -27,28 +27,28 @@ class ImportCest {
'Brazil'
)
);
$this->subscriberFields = array(
$this->subscriber_fields = array(
'first_name',
'last_name',
'email'
);
$this->segments = range(0, 1);
$this->subscriberCustomFields = array(777);
$this->import = new Import($this->JSONdata);
$this->subscriber_custom_fields = array(777);
$this->import = new Import($this->JSON_data);
}
function itCanConstruct() {
expect($this->import->subscribersData)->equals($this->JSONdata['subscribers']);
expect($this->import->segments)->equals($this->JSONdata['segments']);
expect(is_array($this->import->subscriberFields))->true();
expect(is_array($this->import->subscriberCustomFields))->true();
expect($this->import->subscribersCount)->equals(
count($this->JSONdata['subscribers']['email'])
expect($this->import->subscribers_data)->equals($this->JSON_data['subscribers']);
expect($this->import->segments)->equals($this->JSON_data['segments']);
expect(is_array($this->import->subscriber_fields))->true();
expect(is_array($this->import->subscriber_custom_fields))->true();
expect($this->import->subscribers_count)->equals(
count($this->JSON_data['subscribers']['email'])
);
expect(
preg_match(
'/\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}/',
$this->import->currentTime)
$this->import->import_time)
)->equals(1);
}
@ -62,23 +62,23 @@ class ImportCest {
));
$subscriber->save();
list($existing, $new) = $this->import->filterExistingAndNewSubscribers(
$this->subscribersData
$this->subscribers_data
);
expect($existing['email'][0])->equals($this->subscribersData['email'][0]);
expect($new['email'][0])->equals($this->subscribersData['email'][1]);
expect($existing['email'][0])->equals($this->subscribers_data['email'][0]);
expect($new['email'][0])->equals($this->subscribers_data['email'][1]);
}
function itCanExtendSubscribersAndFields() {
expect(in_array('created_at', $this->import->subscriberFields))->false();
expect(isset($this->import->subscriberFields['created_at']))->false();
expect(in_array('created_at', $this->import->subscriber_fields))->false();
expect(isset($this->import->subscriber_fields['created_at']))->false();
list($subscribers, $fields) = $this->import->extendSubscribersAndFields(
$this->import->subscribersData,
$this->import->subscriberFields
$this->import->subscribers_data,
$this->import->subscriber_fields
);
expect(in_array('created_at', $fields))->true();
expect(isset($this->import->subscriberFields['created_at']))->false();
expect(isset($this->import->subscriber_fields['created_at']))->false();
expect(count($subscribers['created_at']))
->equals($this->import->subscribersCount);
->equals($this->import->subscribers_count);
}
function itCanGetSubscriberFields() {
@ -106,17 +106,17 @@ class ImportCest {
}
function itCanFilterSubscriberStatus() {
$subscibersData = $this->subscribersData;
$subscriberFields = $this->subscriberFields;
list($subscibersData, $subsciberFields) =
$this->import->filterSubscriberStatus($subscibersData, $subscriberFields);
$subscibers_data = $this->subscribers_data;
$subscriber_fields = $this->subscriber_fields;
list($subscibers_data, $subsciber_fields) =
$this->import->filterSubscriberStatus($subscibers_data, $subscriber_fields);
// subscribers' status was set to "subscribed" & status column was added
// to subscribers fields
expect(array_pop($subsciberFields))->equals('status');
expect($subscibersData['status'][0])->equals('subscribed');
expect(count($subscibersData['status']))->equals(2);
$subscriberFields[] = 'status';
$subscibersData = array(
expect(array_pop($subsciber_fields))->equals('status');
expect($subscibers_data['status'][0])->equals('subscribed');
expect(count($subscibers_data['status']))->equals(2);
$subscriber_fields[] = 'status';
$subscibers_data = array(
'status' => array(
#subscribed
'subscribed',
@ -135,9 +135,9 @@ class ImportCest {
'false'
),
);
list($subscibersData, $subsciberFields) =
$this->import->filterSubscriberStatus($subscibersData, $subscriberFields);
expect($subscibersData)->equals(
list($subscibers_data, $subsciber_fields) =
$this->import->filterSubscriberStatus($subscibers_data, $subscriber_fields);
expect($subscibers_data)->equals(
array(
'status' => array(
'subscribed',
@ -158,73 +158,73 @@ class ImportCest {
}
function itCanAddOrUpdateSubscribers() {
$subscribersData = $this->subscribersData;
$subscribers_data = $this->subscribers_data;
$this->import->createOrUpdateSubscribers(
'create',
$subscribersData,
$this->subscriberFields,
$subscribers_data,
$this->subscriber_fields,
false
);
$subscribers = Subscriber::findArray();
expect(count($subscribers))->equals(2);
expect($subscribers[0]['email'])
->equals($subscribersData['email'][0]);
->equals($subscribers_data['email'][0]);
$data['first_name'][1] = 'MaryJane';
$this->import->createOrUpdateSubscribers(
'update',
$subscribersData,
$this->subscriberFields,
$subscribers_data,
$this->subscriber_fields,
false
);
$subscribers = Subscriber::findArray();
expect($subscribers[1]['first_name'])
->equals($subscribersData['first_name'][1]);
->equals($subscribers_data['first_name'][1]);
}
function itCanDeleteTrashedSubscribers() {
$subscribersData = $this->subscribersData;
$subscriberFields = $this->subscriberFields;
$subscribersData['deleted_at'] = array(
$subscribers_data = $this->subscribers_data;
$subscriber_fields = $this->subscriber_fields;
$subscribers_data['deleted_at'] = array(
null,
date('Y-m-d H:i:s')
);
$subscriberFields[] = 'deleted_at';
$subscriber_fields[] = 'deleted_at';
$this->import->createOrUpdateSubscribers(
'create',
$subscribersData,
$subscriberFields,
$subscribers_data,
$subscriber_fields,
false
);
$dbSubscribers = Helpers::arrayColumn(
$db_subscribers = Helpers::arrayColumn(
Subscriber::select('id')
->findArray(),
'id'
);
expect(count($dbSubscribers))->equals(2);
expect(count($db_subscribers))->equals(2);
$this->import->addSubscribersToSegments(
$dbSubscribers,
$db_subscribers,
$this->segments
);
$subscribersSegments = SubscriberSegment::findArray();
expect(count($subscribersSegments))->equals(4);
$subscribers_segments = SubscriberSegment::findArray();
expect(count($subscribers_segments))->equals(4);
$this->import->deleteExistingTrashedSubscribers(
$subscribersData
$subscribers_data
);
$subscribersSegments = SubscriberSegment::findArray();
$dbSubscribers = Subscriber::findArray();
expect(count($subscribersSegments))->equals(2);
expect(count($dbSubscribers))->equals(1);
$subscribers_segments = SubscriberSegment::findArray();
$db_subscribers = Subscriber::findArray();
expect(count($subscribers_segments))->equals(2);
expect(count($db_subscribers))->equals(1);
}
function itCanCreateOrUpdateCustomFields() {
$subscribersData = $this->subscribersData;
$subscribers_data = $this->subscribers_data;
$this->import->createOrUpdateSubscribers(
'create',
$subscribersData,
$this->subscriberFields,
$subscribers_data,
$this->subscriber_fields,
false
);
$dbSubscribers = Helpers::arrayColumn(
$db_subscribers = Helpers::arrayColumn(
Subscriber::selectMany(
array(
'id',
@ -235,60 +235,60 @@ class ImportCest {
);
$this->import->createOrUpdateCustomFields(
'create',
$dbSubscribers,
$subscribersData,
$this->subscriberCustomFields
$db_subscribers,
$subscribers_data,
$this->subscriber_custom_fields
);
$subscriberCustomFields = SubscriberCustomField::findArray();
expect(count($subscriberCustomFields))->equals(2);
expect($subscriberCustomFields[0]['value'])
->equals($subscribersData[777][0]);
$subscribersData[777][1] = 'Rio';
$subscriber_custom_fields = SubscriberCustomField::findArray();
expect(count($subscriber_custom_fields))->equals(2);
expect($subscriber_custom_fields[0]['value'])
->equals($subscribers_data[777][0]);
$subscribers_data[777][1] = 'Rio';
$this->import->createOrUpdateCustomFields(
'update',
$dbSubscribers,
$subscribersData,
$this->subscriberCustomFields
$db_subscribers,
$subscribers_data,
$this->subscriber_custom_fields
);
$subscriberCustomFields = SubscriberCustomField::findArray();
expect($subscriberCustomFields[1]['value'])
->equals($subscribersData[777][1]);
$subscriber_custom_fields = SubscriberCustomField::findArray();
expect($subscriber_custom_fields[1]['value'])
->equals($subscribers_data[777][1]);
}
function itCanaddSubscribersToSegments() {
$subscribersData = $this->subscribersData;
$subscribers_data = $this->subscribers_data;
$this->import->createOrUpdateSubscribers(
'create',
$subscribersData,
$this->subscriberFields,
$subscribers_data,
$this->subscriber_fields,
false
);
$dbSubscribers = Helpers::arrayColumn(
$db_subscribers = Helpers::arrayColumn(
Subscriber::select('id')
->findArray(),
'id'
);
$this->import->addSubscribersToSegments(
$dbSubscribers,
$db_subscribers,
$this->segments
);
$subscribersSegments = SubscriberSegment::findArray();
$subscribers_segments = SubscriberSegment::findArray();
// 2 subscribers * 2 segments
expect(count($subscribersSegments))->equals(4);
expect(count($subscribers_segments))->equals(4);
}
function itCanDeleteExistingTrashedSubscribers() {
$subscribersData = $this->subscribersData;
$subscriberFields = $this->subscriberFields;
$subscriberFields[] = 'deleted_at';
$subscribersData['deleted_at'] = array(
$subscribers_data = $this->subscribers_data;
$subscriber_fields = $this->subscriber_fields;
$subscriber_fields[] = 'deleted_at';
$subscribers_data['deleted_at'] = array(
null,
date('Y-m-d H:i:s')
);
$this->import->createOrUpdateSubscribers(
'create',
$subscribersData,
$subscriberFields,
$subscribers_data,
$subscriber_fields,
false
);
}
@ -304,11 +304,12 @@ class ImportCest {
Subscriber::where('email', 'mbanks4@blinklist.com')
->findOne()
->delete();
$import->currentTime = date('Y-m-d 12:i:s');
// TODO: find a more elegant way to test this
$import->import_time = date('Y-m-d 12:i:s');
$result = $import->process();
expect($result['data']['created'])->equals(1);
expect($result['data']['updated'])->equals(996);
$import->updateSubscribers = false;
$import->update_subscribers = false;
$result = $import->process();
expect($result['data']['created'])->equals(0);
expect($result['data']['updated'])->equals(0);

View File

@ -4,4 +4,4 @@
<%= source('newsletter/templates/svg/block-tools/settings.svg') %>
</a>{{/if}}{{#if tools.delete}}<div class="mailpoet_delete_block"><a href="javascript:;" class="mailpoet_tool mailpoet_delete_block_activate" title="<%= __('Delete') %>"><%= source('newsletter/templates/svg/block-tools/trash.svg') %></a><a href="javascript:;" class="mailpoet_delete_block_confirm" title="<%= __('Confirm deletion') %>"><%= __('Delete') %></a><a href="javascript:;" class="mailpoet_delete_block_cancel" title="<%= __('Cancel deletion') %>"><%= __('Cancel') %></a></div>{{/if}}{{#if tools.move}}<a href="javascript:;" class="mailpoet_tool mailpoet_move_block" title="<%= __('Drag to move') %>">
<%= source('newsletter/templates/svg/block-tools/move.svg') %>
</a>{{/if}}<div class="clearfix"></div>
</a>{{/if}}

View File

@ -1,5 +1,4 @@
<div class="handlediv" title="Click to toggle"><br></div>
<h3><%= __('Content') %></h3>
<div class="mailpoet_region_content">
<div class="mailpoet_region_content clearfix">
</div>
<div style="clear:both;"></div>

View File

@ -1,5 +1,4 @@
<div class="handlediv" title="Click to toggle"><br></div>
<h3><%= __('Layout') %></h3>
<div class="mailpoet_region_content">
<div class="mailpoet_region_content clearfix">
</div>
<div style="clear:both;"></div>

View File

@ -79,6 +79,9 @@
</tr>
<tr>
<th>
<a href="javascript:;" id="return_to_step1"
class="button-primary wysija button"><%= __('Previous step') %> </a>
&nbsp;&nbsp;
<a href="javascript:;" id="step2_process"
class="button-primary wysija button-disabled"><%= __('Next step') %> </a>
</th>
@ -95,7 +98,7 @@
{{#show_and_match_columns .}}
{{#.}}
<th>
<select class="mailpoet_subscribers_column_data_match" data-column-id="{{column_id}}" id="column_{{@index}}">
<select class="mailpoet_subscribers_column_data_match" id="{{column_id}}" data-column-id="{{column_id}}" id="column_{{@index}}">
</th>
{{/.}}
{{/show_and_match_columns}}

View File

@ -23,7 +23,7 @@ baseConfig = {
'handlebars': 'handlebars/dist/handlebars.js',
'backbone.marionette': 'backbone.marionette/lib/backbone.marionette',
'backbone.supermodel$': 'backbone.supermodel/build/backbone.supermodel.js',
'sticky-kit': 'vendor_static/jquery.sticky-kit.js',
'sticky-kit': 'vendor/jquery.sticky-kit.js',
'interact$': 'interact.js/interact.js',
'spectrum$': 'spectrum-colorpicker/spectrum.js',
'blob$': 'blob/Blob.js',
@ -77,7 +77,11 @@ baseConfig = {
{
include: /html2canvas.js$/,
loader: 'expose-loader?html2canvas',
}
},
{
include: require.resolve('velocity-animate'),
loader: 'imports-loader?jQuery=jquery',
},
]
}
};
@ -123,6 +127,7 @@ config.push(_.extend({}, baseConfig, {
'sticky-kit',
'blob',
'filesaver',
'velocity-animate',
'newsletter_editor/communicationsFix.js',
'newsletter_editor/App',
@ -193,6 +198,8 @@ config.push(_.extend({}, baseConfig, {
'select2',
'blob',
'filesaver',
'velocity-animate',
'newsletter_editor/communicationsFix.js',
'newsletter_editor/App',
'newsletter_editor/components/config.js',
@ -253,7 +260,7 @@ config.push(_.extend({}, baseConfig, {
'tests/javascript/newsletter_editor'
],
alias: {
'sticky-kit': 'sticky-kit/jquery.sticky-kit',
'sticky-kit': 'vendor/jquery.sticky-kit.js',
'backbone.marionette': 'backbone.marionette/lib/backbone.marionette',
'backbone.supermodel$': 'backbone.supermodel/build/backbone.supermodel.js',
'blob$': 'blob/Blob.js',