Compare commits
215 Commits
3.0.0-beta
...
3.0.0-beta
Author | SHA1 | Date | |
---|---|---|---|
95e66f1f29 | |||
ac0460ab04 | |||
2d059debb7 | |||
df0ad2df37 | |||
0f7725e6af | |||
eda346c582 | |||
94060a6443 | |||
1cd7c5e876 | |||
b369cadde0 | |||
5321a136e7 | |||
d4c04f29bf | |||
20798d8957 | |||
3cde437628 | |||
8db7af48cd | |||
d05d033727 | |||
ccba1925b1 | |||
b590586d4c | |||
44c742402c | |||
3a9db95c37 | |||
5d88938d94 | |||
67e0f1776d | |||
5b68febb05 | |||
9bf65ca798 | |||
d95aa40502 | |||
a59bf76fb4 | |||
51fdc7f1df | |||
aa51b751d0 | |||
aff522c5cd | |||
66d039ace3 | |||
ed5e6cdd8c | |||
47f5e1e7b4 | |||
626d6c0fa9 | |||
2b45d64695 | |||
c27446666e | |||
7f0195378c | |||
6caa3a069b | |||
baaf73b584 | |||
b2a92feb50 | |||
c11b9677d5 | |||
0e5a26ce1f | |||
40ec5569d0 | |||
d14ecc982b | |||
9c27384ba3 | |||
2268f0ffa6 | |||
bfc04bfa87 | |||
37ac31cdac | |||
15096d483f | |||
16724affad | |||
384d59abe0 | |||
027414b7a2 | |||
a1cd56c419 | |||
6ee1c23f9a | |||
44a223eba1 | |||
7c66754541 | |||
bb80fc0860 | |||
6c7cc5de0d | |||
063cc9edc3 | |||
76c283502e | |||
554add0882 | |||
689c340081 | |||
01e4ed7efc | |||
59f408846a | |||
64606e69cf | |||
e8e0c1e0b3 | |||
953c124ef0 | |||
2e1be55bf9 | |||
33125f2ce7 | |||
e99d2b380c | |||
bcf89f0dfe | |||
3d64a42126 | |||
97eb772ab8 | |||
bc40bbb44b | |||
ed117e53d9 | |||
b74f40c7a8 | |||
bf4023c0ad | |||
c98cdb3e57 | |||
a01b094f9f | |||
e75bb7ee59 | |||
563c62855a | |||
8a15424a62 | |||
8eb04534bc | |||
b3abf46604 | |||
6f19a1593e | |||
883711698e | |||
bdcfd77d42 | |||
2cd503e0e0 | |||
c9519f0b3d | |||
0daa3057e7 | |||
0ac5129e0e | |||
a3aa347fdf | |||
a40d1122a5 | |||
9955b8fda1 | |||
c994438fa8 | |||
3085ae575a | |||
793b8ce29e | |||
5d447cdec3 | |||
e7698b0131 | |||
f86121b656 | |||
45c223c14b | |||
66990d62c2 | |||
4439111a44 | |||
440b7e4e6c | |||
b90d7894ca | |||
a0b37eb08c | |||
44b83436bc | |||
0d75ee0e12 | |||
50d77f2aff | |||
cb813171ce | |||
3f188e3690 | |||
e74938df90 | |||
93f7739f46 | |||
a918091977 | |||
b539eae7f9 | |||
228a671749 | |||
70fe253db3 | |||
bdb97261fd | |||
8507c77699 | |||
771ff134a8 | |||
9c1cbba163 | |||
f51122b58f | |||
e8fd992235 | |||
e126278e32 | |||
9d2651083d | |||
1e1ae4c3cf | |||
385f5ab535 | |||
dc371d76ca | |||
be0c9b71d8 | |||
6e250d9317 | |||
afeaa00fc7 | |||
6575d1579d | |||
282199d362 | |||
3e5c46e8f3 | |||
71515f3ff0 | |||
934a8d5bf8 | |||
6be2464c86 | |||
ace8a52262 | |||
7d37d279cd | |||
5525a959a8 | |||
a421dbd674 | |||
bedfc4f80c | |||
af2a6b2559 | |||
cb8c32e171 | |||
8abec208fc | |||
5264cb1cf4 | |||
47e0e1a836 | |||
e602612cd6 | |||
9f7ae122e3 | |||
22caba31e3 | |||
82ab4acb8d | |||
9466be4793 | |||
a2e2090cbb | |||
d100d61403 | |||
d781ef6d01 | |||
1dafc4da04 | |||
063c271e40 | |||
a53007e30b | |||
c616b3299a | |||
86eab0d8f8 | |||
bfd35b1cdc | |||
461203279b | |||
b9c45b46ba | |||
1a42ae4cca | |||
f2ad7ee34c | |||
37017ef69d | |||
157725c300 | |||
98d6f55a6e | |||
425d45a862 | |||
438b4fb1ec | |||
1f91d40def | |||
c5e1def2f9 | |||
65ba834742 | |||
19dc048858 | |||
938279bf8f | |||
dd2df429ef | |||
c4e05912ff | |||
bb34e8477f | |||
32f7d7771f | |||
e77717c4c2 | |||
d27d5ae5dd | |||
168263f0ea | |||
f1ced11809 | |||
c2546e8aed | |||
b7ef191641 | |||
2220a13399 | |||
31ec7475c8 | |||
bfdc13a8d1 | |||
9a3c4ff7de | |||
25410eb09c | |||
122f88668a | |||
9c35eb9723 | |||
fa528ed1ff | |||
1a7623bc4a | |||
3a4a37e1af | |||
888a566dda | |||
3567779faf | |||
cb5b0bd753 | |||
88d0511adb | |||
a4a654cfd5 | |||
4d3c90ce0d | |||
f51aba4dbd | |||
f651c06cb9 | |||
940328c608 | |||
ce85600753 | |||
5666116645 | |||
815461a851 | |||
1102467e39 | |||
a5ee865271 | |||
59bda6cf6c | |||
a4d9d55b09 | |||
8cf918013d | |||
7789a10026 | |||
ce0ad33c32 | |||
63d1fe17a9 | |||
da92795635 | |||
915f8b5865 |
@ -16,3 +16,4 @@ WP_TEST_MAILER_SMTP_LOGIN=""
|
||||
WP_TEST_MAILER_SMTP_PASSWORD=""
|
||||
WP_SVN_USERNAME=""
|
||||
WP_SVN_PASSWORD=""
|
||||
WP_TRANSIFEX_API_TOKEN=""
|
9
.tx/config
Normal file
9
.tx/config
Normal file
@ -0,0 +1,9 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[mp3.mailpoet]
|
||||
source_file = lang/mailpoet.pot
|
||||
file_filter = lang/mailpoet-<lang>.po
|
||||
source_lang = en_US
|
||||
type = PO
|
||||
minimum_perc = 100
|
13
README.md
13
README.md
@ -129,6 +129,19 @@ _n()
|
||||
|
||||
You can use Twig i18n functions in Handlebars, just load your template from a Twig view.
|
||||
|
||||
# Build
|
||||
|
||||
To build a plugin , run `./build.sh`.
|
||||
|
||||
Some build process steps are described below (their dependencies etc.).
|
||||
|
||||
## packtranslations step
|
||||
|
||||
This step imports translations from Transifex and generates MO files. It requires:
|
||||
* `tx` client: https://docs.transifex.com/client/installing-the-client
|
||||
* `msgfmt` command (from Gettext package)
|
||||
Finally , a `WP_TRANSIFEX_API_TOKEN` environment variable should be initialized with a valid key.
|
||||
|
||||
# Publish
|
||||
|
||||
Before you run a publishing command, you need to:
|
||||
|
63
RoboFile.php
63
RoboFile.php
@ -68,9 +68,9 @@ class RoboFile extends \Robo\Tasks {
|
||||
}
|
||||
|
||||
function compileAll() {
|
||||
$collection = $this->collection();
|
||||
$collection->add(array($this, 'compileJs'));
|
||||
$collection->add(array($this, 'compileCss'));
|
||||
$collection = $this->collectionBuilder();
|
||||
$collection->addCode(array($this, 'compileJs'));
|
||||
$collection->addCode(array($this, 'compileCss'));
|
||||
return $collection->run();
|
||||
}
|
||||
|
||||
@ -104,6 +104,12 @@ class RoboFile extends \Robo\Tasks {
|
||||
);
|
||||
}
|
||||
|
||||
function packtranslations() {
|
||||
// Define WP_TRANSIFEX_API_TOKEN env. variable
|
||||
$this->loadEnv();
|
||||
return $this->_exec('./tasks/pack_translations.sh');
|
||||
}
|
||||
|
||||
function testUnit($opts=['file' => null, 'xml' => false]) {
|
||||
$this->loadEnv();
|
||||
$this->_exec('vendor/bin/codecept build');
|
||||
@ -164,9 +170,9 @@ class RoboFile extends \Robo\Tasks {
|
||||
}
|
||||
|
||||
function qa() {
|
||||
$collection = $this->collection();
|
||||
$collection->add(array($this, 'qaLint'));
|
||||
$collection->add(function() {
|
||||
$collection = $this->collectionBuilder();
|
||||
$collection->addCode(array($this, 'qaLint'));
|
||||
$collection->addCode(function() {
|
||||
return $this->qaCodeSniffer('all');
|
||||
});
|
||||
return $collection->run();
|
||||
@ -186,7 +192,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
'./vendor/bin/phpcs '.
|
||||
'--standard=./tasks/code_sniffer/MailPoet '.
|
||||
'--ignore=./lib/Util/Sudzy/*,./lib/Util/CSS.php,./lib/Util/XLSXWriter.php,'.
|
||||
'./lib/Config/PopulatorData/Templates/* '.
|
||||
'./lib/Util/pQuery/*,./lib/Config/PopulatorData/Templates/* '.
|
||||
'lib/ '.
|
||||
$severityFlag
|
||||
);
|
||||
@ -219,42 +225,54 @@ class RoboFile extends \Robo\Tasks {
|
||||
return;
|
||||
}
|
||||
|
||||
$collection = $this->collection();
|
||||
$collection = $this->collectionBuilder();
|
||||
|
||||
// Clean up tmp dirs if the previous run was halted
|
||||
if(file_exists("$svn_dir/trunk_new") || file_exists("$svn_dir/trunk_old")) {
|
||||
$this->taskFileSystemStack()
|
||||
$collection->taskFileSystemStack()
|
||||
->stopOnFail()
|
||||
->remove(array("$svn_dir/trunk_new", "$svn_dir/trunk_old"))
|
||||
->addToCollection($collection);
|
||||
->remove(array("$svn_dir/trunk_new", "$svn_dir/trunk_old"));
|
||||
}
|
||||
|
||||
// Extract the distributable zip to tmp trunk dir
|
||||
$this->taskExtract($plugin_dist_file)
|
||||
$collection->taskExtract($plugin_dist_file)
|
||||
->to("$svn_dir/trunk_new")
|
||||
->preserveTopDirectory(false)
|
||||
->addToCollection($collection);
|
||||
->preserveTopDirectory(false);
|
||||
|
||||
// Rename current trunk
|
||||
if(file_exists("$svn_dir/trunk")) {
|
||||
$this->taskFileSystemStack()
|
||||
->rename("$svn_dir/trunk", "$svn_dir/trunk_old")
|
||||
->addToCollection($collection);
|
||||
$collection->taskFileSystemStack()
|
||||
->rename("$svn_dir/trunk", "$svn_dir/trunk_old");
|
||||
}
|
||||
|
||||
// Replace old trunk with a new one
|
||||
$this->taskFileSystemStack()
|
||||
$collection->taskFileSystemStack()
|
||||
->stopOnFail()
|
||||
->rename("$svn_dir/trunk_new", "$svn_dir/trunk")
|
||||
->remove("$svn_dir/trunk_old")
|
||||
->addToCollection($collection);
|
||||
->remove("$svn_dir/trunk_old");
|
||||
|
||||
// Add new repository assets
|
||||
$collection->taskFileSystemStack()
|
||||
->mirror('./plugin_repository/assets', "$svn_dir/assets_new");
|
||||
|
||||
// Rename current assets folder
|
||||
if(file_exists("$svn_dir/assets")) {
|
||||
$collection->taskFileSystemStack()
|
||||
->rename("$svn_dir/assets", "$svn_dir/assets_old");
|
||||
}
|
||||
|
||||
// Replace old assets with new ones
|
||||
$collection->taskFileSystemStack()
|
||||
->stopOnFail()
|
||||
->rename("$svn_dir/assets_new", "$svn_dir/assets")
|
||||
->remove("$svn_dir/assets_old");
|
||||
|
||||
// Windows compatibility
|
||||
$awkCmd = '{print " --force \""$2"\""}';
|
||||
// Mac OS X compatibility
|
||||
$xargsFlag = (stripos(PHP_OS, 'Darwin') !== false) ? '' : '-r';
|
||||
|
||||
$this->taskExecStack()
|
||||
$collection->taskExecStack()
|
||||
->stopOnFail()
|
||||
// Set SVN repo as working directory
|
||||
->dir($svn_dir)
|
||||
@ -263,8 +281,7 @@ class RoboFile extends \Robo\Tasks {
|
||||
// Recursively add files to SVN that haven't been added yet
|
||||
->exec("svn add --force * --auto-props --parents --depth infinity -q")
|
||||
// Tag the release
|
||||
->exec("svn cp trunk tags/$plugin_version")
|
||||
->addToCollection($collection);
|
||||
->exec("svn cp trunk tags/$plugin_version");
|
||||
|
||||
$result = $collection->run();
|
||||
|
||||
|
@ -131,6 +131,7 @@ body
|
||||
display: none
|
||||
|
||||
.wrap > .mailpoet_notice,
|
||||
.notice
|
||||
.update-nag
|
||||
margin-left: 2px + 15px !important
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
padding 15px 15px 0 15px
|
||||
margin 0 25px 25px 0
|
||||
width 300px
|
||||
height 250px
|
||||
height 300px
|
||||
border 1px solid #dedede
|
||||
background-color #fff
|
||||
h3
|
||||
|
@ -1024,7 +1024,7 @@ WysijaForm.Widget = Class.create(WysijaForm.Block, {
|
||||
},
|
||||
editSettings: function() {
|
||||
MailPoet.Modal.popup({
|
||||
title: 'Edit field settings', // TODO: translate!
|
||||
title: MailPoet.I18n.t('editFieldSettings'),
|
||||
template: jQuery('#form_template_field_settings').html(),
|
||||
data: this.getData(),
|
||||
onSuccess: function() {
|
||||
@ -1061,4 +1061,4 @@ function info(value) {
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
module.exports = WysijaForm;
|
||||
module.exports = WysijaForm;
|
||||
|
@ -77,7 +77,7 @@ const messages = {
|
||||
const bulk_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('trash'),
|
||||
label: MailPoet.I18n.t('moveToTrash'),
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
|
@ -82,7 +82,7 @@ const ListingItem = React.createClass({
|
||||
null,
|
||||
this.props.item.id
|
||||
) }>
|
||||
{MailPoet.I18n.t('trash')}
|
||||
{MailPoet.I18n.t('moveToTrash')}
|
||||
</a>
|
||||
</span>
|
||||
);
|
||||
@ -873,4 +873,4 @@ const Listing = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = Listing;
|
||||
module.exports = Listing;
|
||||
|
@ -164,11 +164,16 @@ define([
|
||||
{ 'one-page': (this.props.count <= this.props.limit) }
|
||||
);
|
||||
|
||||
var numberOfItemsLabel;
|
||||
if (this.props.count == 1) {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsSingular');
|
||||
} else {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsMultiple')
|
||||
.replace('%$1d', this.props.count.toLocaleString());
|
||||
}
|
||||
return (
|
||||
<div className={ classes }>
|
||||
<span className="displaying-num">{
|
||||
MailPoet.I18n.t('numberOfItems').replace('%$1d', this.props.count.toLocaleString())
|
||||
}</span>
|
||||
<span className="displaying-num">{ numberOfItemsLabel }</span>
|
||||
{ pagination }
|
||||
</div>
|
||||
);
|
||||
|
@ -28,6 +28,9 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
opened: false,
|
||||
locked: false,
|
||||
|
||||
// previously focused element
|
||||
prevFocus: null,
|
||||
|
||||
// sub panels
|
||||
subpanels: [],
|
||||
|
||||
@ -59,6 +62,9 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
// display overlay
|
||||
overlay: false,
|
||||
|
||||
// focus upon displaying
|
||||
focus: true,
|
||||
|
||||
// highlighted elements
|
||||
highlight: null,
|
||||
|
||||
@ -71,7 +77,7 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
options: {},
|
||||
templates: {
|
||||
overlay: '<div id="mailpoet_modal_overlay" style="display:none;"></div>',
|
||||
popup: '<div id="mailpoet_popup">'+
|
||||
popup: '<div id="mailpoet_popup" tabindex="-1">'+
|
||||
'<div class="mailpoet_popup_wrapper">'+
|
||||
'<a href="javascript:;" id="mailpoet_modal_close"></a>'+
|
||||
'<div id="mailpoet_popup_title"><h2></h2></div>'+
|
||||
@ -85,11 +91,11 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
'</div>',
|
||||
panel: '<div id="mailpoet_panel">'+
|
||||
'<a href="javascript:;" id="mailpoet_modal_close"></a>'+
|
||||
'<div class="mailpoet_panel_wrapper">'+
|
||||
'<div class="mailpoet_panel_wrapper" tabindex="-1">'+
|
||||
'<div class="mailpoet_panel_body clearfix"></div>'+
|
||||
'</div>'+
|
||||
'</div>',
|
||||
subpanel: '<div class="mailpoet_panel_wrapper">'+
|
||||
subpanel: '<div class="mailpoet_panel_wrapper" tabindex="-1">'+
|
||||
'<div class="mailpoet_panel_body clearfix"></div>'+
|
||||
'</div>'
|
||||
},
|
||||
@ -251,6 +257,11 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
// add sub panel content
|
||||
jQuery('.mailpoet_'+this.options.type+'_body').last()
|
||||
.html(this.subpanels[(this.subpanels.length - 1)].element);
|
||||
|
||||
// focus on sub panel
|
||||
if(this.options.focus) {
|
||||
this.focus();
|
||||
}
|
||||
} else if (this.options.element) {
|
||||
jQuery('.mailpoet_'+this.options.type+'_body').empty();
|
||||
jQuery('.mailpoet_'+this.options.type+'_body')
|
||||
@ -369,6 +380,9 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
// set modal dimensions
|
||||
this.setDimensions();
|
||||
|
||||
// remember the previously focused element
|
||||
this.prevFocus = jQuery(':focus');
|
||||
|
||||
// add a flag on the body so that we can prevent scrolling
|
||||
jQuery('body').addClass('mailpoet_modal_opened');
|
||||
|
||||
@ -388,6 +402,10 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
}
|
||||
}
|
||||
|
||||
if(this.options.focus) {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
// set popup as opened
|
||||
this.opened = true;
|
||||
|
||||
@ -398,6 +416,16 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
|
||||
return this;
|
||||
},
|
||||
focus: function() {
|
||||
if(this.options.type == 'popup') {
|
||||
jQuery('#mailpoet_'+this.options.type).focus();
|
||||
} else {
|
||||
// panel and subpanel
|
||||
jQuery('#mailpoet_'+this.options.type+' .mailpoet_panel_wrapper')
|
||||
.filter(':visible').focus();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
highlightOn: function(element) {
|
||||
jQuery(element).addClass('mailpoet_modal_highlight');
|
||||
return this;
|
||||
@ -579,6 +607,11 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
// remove last subpanels
|
||||
this.subpanels.pop();
|
||||
|
||||
// focus on previous panel
|
||||
if(this.options.focus) {
|
||||
this.focus();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -591,6 +624,11 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
// destroy modal element
|
||||
this.destroy();
|
||||
|
||||
// restore the previously focused element
|
||||
if(this.prevFocus !== undefined){
|
||||
this.prevFocus.focus();
|
||||
}
|
||||
|
||||
// reset options
|
||||
this.options = {
|
||||
onSuccess: null,
|
||||
|
@ -6,8 +6,9 @@
|
||||
define([
|
||||
'backbone.marionette',
|
||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||
'mailpoet',
|
||||
'spectrum'
|
||||
], function(Marionette, BehaviorsLookup, Spectrum) {
|
||||
], function(Marionette, BehaviorsLookup, MailPoet, Spectrum) {
|
||||
|
||||
BehaviorsLookup.ColorPickerBehavior = Marionette.Behavior.extend({
|
||||
onRender: function() {
|
||||
@ -17,6 +18,8 @@ define([
|
||||
showInitial: true,
|
||||
preferredFormat: "hex6",
|
||||
allowEmpty: true,
|
||||
chooseText: MailPoet.I18n.t('selectColor'),
|
||||
cancelText: MailPoet.I18n.t('cancelColorSelection')
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -1,8 +1,9 @@
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'backbone.supermodel',
|
||||
'underscore'
|
||||
], function(App, SuperModel, _) {
|
||||
'underscore',
|
||||
'mailpoet'
|
||||
], function(App, SuperModel, _, MailPoet) {
|
||||
"use strict";
|
||||
|
||||
var Module = {};
|
||||
@ -81,7 +82,16 @@ define([
|
||||
|
||||
App.on('start', function(options) {
|
||||
var body = options.newsletter.body;
|
||||
App._contentContainer = new (App.getBlockTypeModel('container'))(body.content, {parse: true});
|
||||
var content = (_.has(body, 'content')) ? body.content : {};
|
||||
|
||||
if (!_.has(options.newsletter, 'body') || !_.isObject(options.newsletter.body)) {
|
||||
MailPoet.Notice.error(
|
||||
MailPoet.I18n.t('newsletterBodyIsCorrupted'),
|
||||
{ static: true }
|
||||
);
|
||||
}
|
||||
|
||||
App._contentContainer = new (App.getBlockTypeModel('container'))(content, {parse: true});
|
||||
App._contentContainerView = new (App.getBlockTypeView('container'))({
|
||||
model: App._contentContainer,
|
||||
renderOptions: { depth: 0 },
|
||||
|
@ -46,9 +46,8 @@ define([
|
||||
//MailPoet.Notice.success("<?php _e('Newsletter has been saved.'); ?>");
|
||||
} else if(response.error !== undefined) {
|
||||
if(response.error.length === 0) {
|
||||
// TODO: Handle translations
|
||||
MailPoet.Notice.error(
|
||||
"An unknown error occurred, please check your settings.",
|
||||
MailPoet.I18n.t('templateSaveFailed'),
|
||||
{
|
||||
scroll: true,
|
||||
}
|
||||
@ -169,7 +168,7 @@ define([
|
||||
},
|
||||
beforeSave: function() {
|
||||
// TODO: Add a loading animation instead
|
||||
this.$('.mailpoet_autosaved_at').text('Saving...');
|
||||
this.$('.mailpoet_autosaved_at').text(MailPoet.I18n.t('saving'));
|
||||
},
|
||||
afterSave: function(json, response) {
|
||||
this.validateNewsletter(json);
|
||||
@ -330,8 +329,7 @@ define([
|
||||
|
||||
Module.beforeExitWithUnsavedChanges = function(e) {
|
||||
if (saveTimeout) {
|
||||
// TODO: Translate this message
|
||||
var message = "There are unsaved changes which will be lost if you leave this page.";
|
||||
var message = MailPoet.I18n.t('unsavedChangesWillBeLost');
|
||||
e = e || window.event;
|
||||
|
||||
if (e) {
|
||||
|
@ -158,6 +158,9 @@ define([
|
||||
*/
|
||||
Module.SidebarStylesView = Marionette.LayoutView.extend({
|
||||
getTemplate: function() { return templates.sidebarStyles; },
|
||||
behaviors: {
|
||||
ColorPickerBehavior: {},
|
||||
},
|
||||
events: function() {
|
||||
return {
|
||||
"change #mailpoet_text_font_color": _.partial(this.changeColorField, 'text.fontColor'),
|
||||
@ -205,15 +208,6 @@ define([
|
||||
initialize: function(options) {
|
||||
this.availableStyles = options.availableStyles;
|
||||
},
|
||||
onRender: function() {
|
||||
this.$('.mailpoet_color').spectrum({
|
||||
clickoutFiresChange: true,
|
||||
showInput: true,
|
||||
showInitial: true,
|
||||
preferredFormat: "hex6",
|
||||
allowEmpty: true,
|
||||
});
|
||||
},
|
||||
changeField: function(field, event) {
|
||||
this.model.set(field, jQuery(event.target).val());
|
||||
},
|
||||
|
@ -69,11 +69,11 @@ define([
|
||||
// Expose style methods to global application
|
||||
App.getGlobalStyles = Module.getGlobalStyles;
|
||||
App.setGlobalStyles = Module.setGlobalStyles;
|
||||
|
||||
App.getAvailableStyles = Module.getAvailableStyles;
|
||||
|
||||
var body = options.newsletter.body;
|
||||
this.setGlobalStyles(body.globalStyles);
|
||||
var globalStyles = (_.has(body, 'globalStyles')) ? body.globalStyles : {};
|
||||
this.setGlobalStyles(globalStyles);
|
||||
});
|
||||
|
||||
App.on('start', function(options) {
|
||||
|
@ -98,7 +98,7 @@ const columns = [
|
||||
const bulk_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('trash'),
|
||||
label: MailPoet.I18n.t('moveToTrash'),
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
@ -340,4 +340,4 @@ const NewsletterListNotification = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = NewsletterListNotification;
|
||||
module.exports = NewsletterListNotification;
|
||||
|
@ -93,7 +93,7 @@ const columns = [
|
||||
const bulk_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('trash'),
|
||||
label: MailPoet.I18n.t('moveToTrash'),
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
@ -223,4 +223,4 @@ const NewsletterListStandard = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = NewsletterListStandard;
|
||||
module.exports = NewsletterListStandard;
|
||||
|
@ -95,7 +95,7 @@ const columns = [
|
||||
const bulk_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('trash'),
|
||||
label: MailPoet.I18n.t('moveToTrash'),
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
@ -369,4 +369,4 @@ const NewsletterListWelcome = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = NewsletterListWelcome;
|
||||
module.exports = NewsletterListWelcome;
|
||||
|
@ -173,15 +173,20 @@ define(
|
||||
var data = this.state.item;
|
||||
this.setState({ loading: true });
|
||||
|
||||
// Ensure that body is JSON encoded
|
||||
if (!_.isUndefined(data.body)) {
|
||||
data.body = JSON.stringify(data.body);
|
||||
}
|
||||
// Store only properties that can be changed on this page
|
||||
const IGNORED_NEWSLETTER_PROPERTIES = [
|
||||
'preheader', 'body', 'created_at', 'deleted_at', 'hash',
|
||||
'status', 'updated_at', 'type'
|
||||
];
|
||||
const newsletterData = _.omit(
|
||||
data,
|
||||
IGNORED_NEWSLETTER_PROPERTIES
|
||||
);
|
||||
|
||||
return MailPoet.Ajax.post({
|
||||
endpoint: 'newsletters',
|
||||
action: 'save',
|
||||
data: data,
|
||||
data: newsletterData,
|
||||
}).always(() => {
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
|
@ -90,7 +90,7 @@ const messages = {
|
||||
const bulk_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('trash'),
|
||||
label: MailPoet.I18n.t('moveToTrash'),
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
|
@ -200,7 +200,7 @@ const bulk_actions = [
|
||||
},
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('trash'),
|
||||
label: MailPoet.I18n.t('moveToTrash'),
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
|
4
build.sh
4
build.sh
@ -3,6 +3,7 @@
|
||||
# Translations (npm install & composer install need to be run before)
|
||||
echo '[BUILD] Generating translations'
|
||||
./do makepot
|
||||
./do packtranslations
|
||||
|
||||
plugin_name='mailpoet'
|
||||
|
||||
@ -50,17 +51,20 @@ rm -rf $plugin_name/vendor/soundasleep/html2text/tests
|
||||
rm -rf $plugin_name/vendor/mtdowling/cron-expression/tests
|
||||
rm -rf $plugin_name/vendor/swiftmailer/swiftmailer/tests
|
||||
rm -rf $plugin_name/vendor/cerdic/css-tidy/testing
|
||||
rm -rf $plugin_name/vendor/sabberworm/php-css-parser/tests
|
||||
|
||||
# Remove risky files from 3rd party extensions
|
||||
echo '[BUILD] Removing risky and demo files from vendor libraries'
|
||||
rm -f $plugin_name/vendor/j4mie/idiorm/demo.php
|
||||
rm -f $plugin_name/vendor/cerdic/css-tidy/css_optimiser.php
|
||||
rm -f $plugin_name/assets/js/lib/tinymce/package.json
|
||||
|
||||
# Copy release files.
|
||||
echo '[BUILD] Copying release files'
|
||||
cp license.txt $plugin_name
|
||||
cp index.php $plugin_name
|
||||
cp $plugin_name.php $plugin_name
|
||||
cp mailpoet_initializer.php $plugin_name
|
||||
cp readme.txt $plugin_name
|
||||
cp uninstall.php $plugin_name
|
||||
|
||||
|
@ -8,24 +8,25 @@
|
||||
"require": {
|
||||
"php": ">=5.3.3",
|
||||
"twig/twig": "1.*",
|
||||
"cerdic/css-tidy": "*",
|
||||
"tburry/pquery": "*",
|
||||
"cerdic/css-tidy": "^1.5.5",
|
||||
"tburry/pquery": "^1.1.1",
|
||||
"j4mie/paris": "1.5.4",
|
||||
"swiftmailer/swiftmailer": "^5.4",
|
||||
"mtdowling/cron-expression": "^1.1",
|
||||
"nesbot/carbon": "^1.21",
|
||||
"soundasleep/html2text": "dev-master",
|
||||
"soundasleep/html2text": "^0.3.4",
|
||||
"sabberworm/php-css-parser": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "*",
|
||||
"codeception/verify": "*",
|
||||
"codegyre/robo": "*",
|
||||
"vlucas/phpdotenv": "*",
|
||||
"codeception/codeception": "^2.2.9",
|
||||
"codeception/verify": "^0.3.3",
|
||||
"consolidation/robo": "^1.0.5",
|
||||
"henrikbjorn/lurker": "^1.2",
|
||||
"vlucas/phpdotenv": "^2.4.0",
|
||||
"umpirsky/twig-gettext-extractor": "1.1.*",
|
||||
"raveren/kint": "^1.0",
|
||||
"squizlabs/php_codesniffer": "*",
|
||||
"wimg/php-compatibility": "*",
|
||||
"squizlabs/php_codesniffer": "^2.8.1",
|
||||
"wimg/php-compatibility": "^7.1.2",
|
||||
"simplyadmire/composer-plugins" : "@dev"
|
||||
},
|
||||
"autoload": {
|
||||
|
1049
composer.lock
generated
1049
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace MailPoet\API;
|
||||
use \MailPoet\Util\Security;
|
||||
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\Util\Security;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -9,9 +11,14 @@ class API {
|
||||
private $_method;
|
||||
private $_token;
|
||||
|
||||
private $_endpoint_namespaces = array();
|
||||
private $_endpoint_class;
|
||||
private $_data = array();
|
||||
|
||||
function __construct() {
|
||||
$this->addEndpointNamespace(__NAMESPACE__ . "\\Endpoints");
|
||||
}
|
||||
|
||||
function init() {
|
||||
// Admin Security token
|
||||
add_action(
|
||||
@ -33,12 +40,14 @@ class API {
|
||||
}
|
||||
|
||||
function setupAjax() {
|
||||
$this->getRequestData();
|
||||
do_action('mailpoet_api_setup', array($this));
|
||||
|
||||
$this->getRequestData($_POST);
|
||||
|
||||
if($this->checkToken() === false) {
|
||||
$error_response = new ErrorResponse(
|
||||
array(
|
||||
Error::UNAUTHORIZED => __('Invalid request.', 'mailpoet')
|
||||
Error::UNAUTHORIZED => __('Invalid request', 'mailpoet')
|
||||
),
|
||||
array(),
|
||||
Response::STATUS_UNAUTHORIZED
|
||||
@ -46,37 +55,41 @@ class API {
|
||||
$error_response->send();
|
||||
}
|
||||
|
||||
$this->processRoute();
|
||||
$response = $this->processRoute();
|
||||
$response->send();
|
||||
}
|
||||
|
||||
function getRequestData() {
|
||||
$this->_endpoint = isset($_POST['endpoint'])
|
||||
? trim($_POST['endpoint'])
|
||||
function getRequestData($data) {
|
||||
$this->_endpoint = isset($data['endpoint'])
|
||||
? Helpers::underscoreToCamelCase(trim($data['endpoint']))
|
||||
: null;
|
||||
$this->_method = isset($_POST['method'])
|
||||
? trim($_POST['method'])
|
||||
$this->_method = isset($data['method'])
|
||||
? Helpers::underscoreToCamelCase(trim($data['method']))
|
||||
: null;
|
||||
$this->_token = isset($_POST['token'])
|
||||
? trim($_POST['token'])
|
||||
$this->_token = isset($data['token'])
|
||||
? trim($data['token'])
|
||||
: null;
|
||||
|
||||
if(!$this->_endpoint || !$this->_method) {
|
||||
// throw exception bad request
|
||||
$error_response = new ErrorResponse(
|
||||
array(
|
||||
Error::BAD_REQUEST => __('Invalid request.', 'mailpoet')
|
||||
Error::BAD_REQUEST => __('Invalid request', 'mailpoet')
|
||||
),
|
||||
array(),
|
||||
Response::STATUS_BAD_REQUEST
|
||||
);
|
||||
$error_response->send();
|
||||
} else {
|
||||
$this->_endpoint_class = (
|
||||
__NAMESPACE__."\\Endpoints\\".ucfirst($this->_endpoint)
|
||||
);
|
||||
foreach($this->_endpoint_namespaces as $namespace) {
|
||||
$class_name = $namespace . "\\" . ucfirst($this->_endpoint);
|
||||
if(class_exists($class_name)) {
|
||||
$this->_endpoint_class = $class_name;
|
||||
}
|
||||
}
|
||||
|
||||
$this->_data = isset($_POST['data'])
|
||||
? stripslashes_deep($_POST['data'])
|
||||
$this->_data = isset($data['data'])
|
||||
? stripslashes_deep($data['data'])
|
||||
: array();
|
||||
|
||||
// remove reserved keywords from data
|
||||
@ -98,6 +111,10 @@ class API {
|
||||
|
||||
function processRoute() {
|
||||
try {
|
||||
if(empty($this->_endpoint_class)) {
|
||||
throw new \Exception('Invalid endpoint');
|
||||
}
|
||||
|
||||
$endpoint = new $this->_endpoint_class();
|
||||
|
||||
// check the accessibility of the requested endpoint's action
|
||||
@ -119,17 +136,17 @@ class API {
|
||||
array(),
|
||||
Response::STATUS_FORBIDDEN
|
||||
);
|
||||
$error_response->send();
|
||||
return $error_response;
|
||||
}
|
||||
}
|
||||
|
||||
$response = $endpoint->{$this->_method}($this->_data);
|
||||
$response->send();
|
||||
return $response;
|
||||
} catch(\Exception $e) {
|
||||
$error_response = new ErrorResponse(
|
||||
array($e->getCode() => $e->getMessage())
|
||||
);
|
||||
$error_response->send();
|
||||
return $error_response;
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,4 +166,12 @@ class API {
|
||||
$global .= '</script>';
|
||||
echo $global;
|
||||
}
|
||||
}
|
||||
|
||||
function addEndpointNamespace($namespace) {
|
||||
$this->_endpoint_namespaces[] = $namespace;
|
||||
}
|
||||
|
||||
function getEndpointNamespaces() {
|
||||
return $this->_endpoint_namespaces;
|
||||
}
|
||||
}
|
||||
|
@ -27,9 +27,9 @@ abstract class Endpoint {
|
||||
function badRequest($errors = array(), $meta = array()) {
|
||||
if(empty($errors)) {
|
||||
$errors = array(
|
||||
Error::BAD_REQUEST => __('Invalid request parameters.', 'mailpoet')
|
||||
Error::BAD_REQUEST => __('Invalid request parameters', 'mailpoet')
|
||||
);
|
||||
}
|
||||
return new ErrorResponse($errors, $meta, Response::STATUS_BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
<?php
|
||||
namespace MailPoet\API\Endpoints;
|
||||
use \MailPoet\API\Endpoint as APIEndpoint;
|
||||
use \MailPoet\API\Error as APIError;
|
||||
use MailPoet\API\Endpoint as APIEndpoint;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?php
|
||||
namespace MailPoet\API\Endpoints;
|
||||
use \MailPoet\API\Endpoint as APIEndpoint;
|
||||
use \MailPoet\API\Error as APIError;
|
||||
use \MailPoet\Models\CustomField;
|
||||
use MailPoet\API\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\Error as APIError;
|
||||
use MailPoet\Models\CustomField;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -3,11 +3,11 @@ namespace MailPoet\API\Endpoints;
|
||||
use MailPoet\API\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\Error as APIError;
|
||||
|
||||
use \MailPoet\Models\Form;
|
||||
use \MailPoet\Models\StatisticsForms;
|
||||
use \MailPoet\Form\Renderer as FormRenderer;
|
||||
use \MailPoet\Listing;
|
||||
use \MailPoet\Form\Util;
|
||||
use MailPoet\Models\Form;
|
||||
use MailPoet\Models\StatisticsForms;
|
||||
use MailPoet\Form\Renderer as FormRenderer;
|
||||
use MailPoet\Listing;
|
||||
use MailPoet\Form\Util;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -1,10 +1,8 @@
|
||||
<?php
|
||||
namespace MailPoet\API\Endpoints;
|
||||
use \MailPoet\API\Endpoint as APIEndpoint;
|
||||
use \MailPoet\API\Error as APIError;
|
||||
use MailPoet\API\Endpoint as APIEndpoint;
|
||||
|
||||
use MailPoet\Subscribers\ImportExport\Import\MailChimp;
|
||||
use MailPoet\Models\CustomField;
|
||||
use MailPoet\Models\Segment;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
@ -14,7 +14,10 @@ class Mailer extends APIEndpoint {
|
||||
(isset($data['sender'])) ? $data['sender'] : false,
|
||||
(isset($data['reply_to'])) ? $data['reply_to'] : false
|
||||
);
|
||||
$result = $mailer->send($data['newsletter'], $data['subscriber']);
|
||||
$extra_params = array(
|
||||
'test_email' => true
|
||||
);
|
||||
$result = $mailer->send($data['newsletter'], $data['subscriber'], $extra_params);
|
||||
} catch(\Exception $e) {
|
||||
return $this->errorResponse(array(
|
||||
$e->getCode() => $e->getMessage()
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\API\Endpoints;
|
||||
use \MailPoet\API\Endpoint as APIEndpoint;
|
||||
use \MailPoet\API\Error as APIError;
|
||||
use MailPoet\API\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\Error as APIError;
|
||||
|
||||
use MailPoet\Models\NewsletterTemplate;
|
||||
|
||||
|
@ -4,6 +4,7 @@ namespace MailPoet\API\Endpoints;
|
||||
use MailPoet\API\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\Error as APIError;
|
||||
use MailPoet\Listing;
|
||||
use MailPoet\Models\SendingQueue;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\NewsletterTemplate;
|
||||
@ -70,30 +71,40 @@ class Newsletters extends APIEndpoint {
|
||||
}
|
||||
|
||||
if(!empty($options)) {
|
||||
NewsletterOption::where('newsletter_id', $newsletter->id)
|
||||
->deleteMany();
|
||||
|
||||
$option_fields = NewsletterOptionField::where(
|
||||
'newsletter_type',
|
||||
$data['type']
|
||||
)->findArray();
|
||||
$newsletter->type
|
||||
)->findMany();
|
||||
|
||||
// update newsletter options
|
||||
foreach($option_fields as $option_field) {
|
||||
if(isset($options[$option_field['name']])) {
|
||||
$relation = NewsletterOption::create();
|
||||
$relation->newsletter_id = $newsletter->id;
|
||||
$relation->option_field_id = $option_field['id'];
|
||||
$relation->value = $options[$option_field['name']];
|
||||
$relation->save();
|
||||
if(isset($options[$option_field->name])) {
|
||||
$newsletter_option = NewsletterOption::createOrUpdate(
|
||||
array(
|
||||
'newsletter_id' => $newsletter->id,
|
||||
'option_field_id' => $option_field->id,
|
||||
'value' => $options[$option_field->name]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$newsletter = Newsletter::filter('filterWithOptions')->findOne($newsletter->id);
|
||||
// reload newsletter with updated options
|
||||
$newsletter = Newsletter::filter('filterWithOptions')
|
||||
->findOne($newsletter->id);
|
||||
|
||||
// if this is a post notification, process options and update its schedule
|
||||
if($newsletter->type === Newsletter::TYPE_NOTIFICATION) {
|
||||
Scheduler::processPostNotificationSchedule($newsletter);
|
||||
// if this is a post notification, process newsletter options and update its schedule
|
||||
if($newsletter->type === Newsletter::TYPE_NOTIFICATION) {
|
||||
// generate the new schedule from options and get the new "next run" date
|
||||
$newsletter->schedule = Scheduler::processPostNotificationSchedule($newsletter);
|
||||
$next_run_date = Scheduler::getNextRunDate($newsletter->schedule);
|
||||
// find previously scheduled jobs and reschedule them using the new "next run" date
|
||||
SendingQueue::where('newsletter_id', $newsletter->id)
|
||||
->where('status', SendingQueue::STATUS_SCHEDULED)
|
||||
->findResultSet()
|
||||
->set('scheduled_at', $next_run_date)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->successResponse($newsletter->asArray());
|
||||
@ -340,11 +351,10 @@ class Newsletters extends APIEndpoint {
|
||||
}
|
||||
|
||||
// get preview url
|
||||
$subscriber = Subscriber::getCurrentWPUser();
|
||||
$newsletter->preview_url = NewsletterUrl::getViewInBrowserUrl(
|
||||
NewsletterUrl::TYPE_LISTING_EDITOR,
|
||||
$newsletter,
|
||||
$subscriber,
|
||||
$subscriber = null,
|
||||
$queue
|
||||
);
|
||||
|
||||
@ -435,4 +445,4 @@ class Newsletters extends APIEndpoint {
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,11 @@
|
||||
<?php
|
||||
namespace MailPoet\API\Endpoints;
|
||||
use \MailPoet\API\Endpoint as APIEndpoint;
|
||||
use \MailPoet\API\Error as APIError;
|
||||
use MailPoet\API\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\Error as APIError;
|
||||
|
||||
use \MailPoet\Models\Segment;
|
||||
use \MailPoet\Models\SubscriberSegment;
|
||||
use \MailPoet\Models\SegmentFilter;
|
||||
use \MailPoet\Listing;
|
||||
use \MailPoet\Segments\WP;
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Listing;
|
||||
use MailPoet\Segments\WP;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -5,11 +5,7 @@ use MailPoet\API\Error as APIError;
|
||||
|
||||
use MailPoet\Mailer\Mailer;
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\NewsletterOption;
|
||||
use MailPoet\Models\NewsletterOptionField;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Models\SubscriberSegment;
|
||||
use MailPoet\Newsletter\Scheduler\Scheduler;
|
||||
use MailPoet\Models\SendingQueue as SendingQueueModel;
|
||||
use MailPoet\Util\Helpers;
|
||||
@ -52,7 +48,6 @@ class SendingQueue extends APIEndpoint {
|
||||
APIError::NOT_FOUND => __('This newsletter is already being sent.', 'mailpoet')
|
||||
));
|
||||
}
|
||||
|
||||
$queue = SendingQueueModel::where('newsletter_id', $newsletter->id)
|
||||
->where('status', SendingQueueModel::STATUS_SCHEDULED)
|
||||
->findOne();
|
||||
@ -77,10 +72,8 @@ class SendingQueue extends APIEndpoint {
|
||||
$segment_ids = array_map(function($segment) {
|
||||
return $segment['id'];
|
||||
}, $segments);
|
||||
$subscribers = Subscriber::getSubscribedInSegments($segment_ids)
|
||||
->findArray();
|
||||
$subscribers = Helpers::arrayColumn($subscribers, 'subscriber_id');
|
||||
$subscribers = array_unique($subscribers);
|
||||
$subscribers = Subscriber::getSubscribedInSegments($segment_ids)->findArray();
|
||||
$subscribers = Helpers::flattenArray($subscribers);
|
||||
if(!count($subscribers)) {
|
||||
return $this->errorResponse(array(
|
||||
APIError::UNKNOWN => __('There are no subscribers in that list!', 'mailpoet')
|
||||
|
67
lib/API/Endpoints/Services.php
Normal file
67
lib/API/Endpoints/Services.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
namespace MailPoet\API\Endpoints;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use MailPoet\API\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\Error as APIError;
|
||||
use MailPoet\Services\Bridge;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Services extends APIEndpoint {
|
||||
public $bridge;
|
||||
|
||||
function __construct() {
|
||||
$this->bridge = new Bridge();
|
||||
}
|
||||
|
||||
function verifyMailPoetKey($data = array()) {
|
||||
$key = isset($data['key']) ? trim($data['key']) : null;
|
||||
|
||||
if(!$key) {
|
||||
return $this->badRequest(array(
|
||||
APIError::BAD_REQUEST => __('Please specify a key.', 'mailpoet')
|
||||
));
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->bridge->checkKey($key);
|
||||
} catch(\Exception $e) {
|
||||
return $this->errorResponse(array(
|
||||
$e->getCode() => $e->getMessage()
|
||||
));
|
||||
}
|
||||
|
||||
$state = !empty($result['state']) ? $result['state'] : null;
|
||||
|
||||
$success_message = null;
|
||||
if($state == Bridge::MAILPOET_KEY_VALID) {
|
||||
$success_message = __('Your MailPoet API key is valid!', 'mailpoet');
|
||||
} elseif($state == Bridge::MAILPOET_KEY_EXPIRING) {
|
||||
$success_message = sprintf(
|
||||
__('Your MailPoet key expires on %s!', 'mailpoet'),
|
||||
Carbon::createFromTimestamp(strtotime($result['data']['expire_at']))
|
||||
->format('Y-m-d')
|
||||
);
|
||||
}
|
||||
|
||||
if($success_message) {
|
||||
return $this->successResponse(array('message' => $success_message));
|
||||
}
|
||||
|
||||
switch($state) {
|
||||
case Bridge::MAILPOET_KEY_INVALID:
|
||||
$error = __('Your MailPoet key is invalid!', 'mailpoet');
|
||||
break;
|
||||
default:
|
||||
$code = !empty($result['code']) ? $result['code'] : Bridge::CHECK_ERROR_UNKNOWN;
|
||||
$error = sprintf(
|
||||
__('Error validating API key, please try again later (code: %s)', 'mailpoet'),
|
||||
$code
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->errorResponse(array(APIError::BAD_REQUEST => $error));
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
<?php
|
||||
namespace MailPoet\API\Endpoints;
|
||||
use \MailPoet\API\Endpoint as APIEndpoint;
|
||||
use \MailPoet\API\Error as APIError;
|
||||
use \MailPoet\Models\Setting;
|
||||
use MailPoet\API\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\Error as APIError;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Services\Bridge;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -21,6 +22,13 @@ class Settings extends APIEndpoint {
|
||||
foreach($settings as $name => $value) {
|
||||
Setting::setValue($name, $value);
|
||||
}
|
||||
if(!empty($settings['mta']['mailpoet_api_key'])
|
||||
&& Bridge::isMPSendingServiceEnabled()
|
||||
) {
|
||||
$bridge = new Bridge();
|
||||
$result = $bridge->checkKey($settings['mta']['mailpoet_api_key']);
|
||||
$bridge->updateSubscriberCount($result);
|
||||
}
|
||||
return $this->successResponse(Setting::getAll());
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\API\Endpoints;
|
||||
use \MailPoet\API\Endpoint as APIEndpoint;
|
||||
use \MailPoet\Config\Activator;
|
||||
use MailPoet\API\Endpoint as APIEndpoint;
|
||||
use MailPoet\Config\Activator;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -1,15 +1,11 @@
|
||||
<?php
|
||||
namespace MailPoet\API\Endpoints;
|
||||
use \MailPoet\API\Endpoint as APIEndpoint;
|
||||
use \MailPoet\API\Error as APIError;
|
||||
use \MailPoet\API\Access as APIAccess;
|
||||
use MailPoet\API\Endpoint as APIEndpoint;
|
||||
use MailPoet\API\Error as APIError;
|
||||
use MailPoet\API\Access as APIAccess;
|
||||
|
||||
use MailPoet\Listing;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Models\SubscriberSegment;
|
||||
use MailPoet\Models\SubscriberCustomField;
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Models\Form;
|
||||
use MailPoet\Models\StatisticsForms;
|
||||
|
||||
@ -65,10 +61,17 @@ class Subscribers extends APIEndpoint {
|
||||
$form = Form::findOne($form_id);
|
||||
unset($data['form_id']);
|
||||
|
||||
if(!$form) {
|
||||
return $this->badRequest(array(
|
||||
APIError::BAD_REQUEST => __('Please specify a valid form ID.', 'mailpoet')
|
||||
));
|
||||
}
|
||||
|
||||
$segment_ids = (!empty($data['segments'])
|
||||
? (array)$data['segments']
|
||||
: array()
|
||||
);
|
||||
$segment_ids = $form->filterSegments($segment_ids);
|
||||
unset($data['segments']);
|
||||
|
||||
if(empty($segment_ids)) {
|
||||
@ -77,6 +80,10 @@ class Subscribers extends APIEndpoint {
|
||||
));
|
||||
}
|
||||
|
||||
// only accept fields defined in the form
|
||||
$form_fields = $form->getFieldList();
|
||||
$data = array_intersect_key($data, array_flip($form_fields));
|
||||
|
||||
$subscriber = Subscriber::subscribe($data, $segment_ids);
|
||||
$errors = $subscriber->getErrors();
|
||||
|
||||
@ -91,11 +98,13 @@ class Subscribers extends APIEndpoint {
|
||||
|
||||
$form = $form->asArray();
|
||||
|
||||
if($form['settings']['on_success'] === 'page') {
|
||||
// redirect to a page on a success, pass the page url in the meta
|
||||
$meta['redirect_url'] = get_permalink($form['settings']['success_page']);
|
||||
} else if($form['settings']['on_success'] === 'url') {
|
||||
$meta['redirect_url'] = $form['settings']['success_url'];
|
||||
if(!empty($form['settings']['on_success'])) {
|
||||
if($form['settings']['on_success'] === 'page') {
|
||||
// redirect to a page on a success, pass the page url in the meta
|
||||
$meta['redirect_url'] = get_permalink($form['settings']['success_page']);
|
||||
} else if($form['settings']['on_success'] === 'url') {
|
||||
$meta['redirect_url'] = $form['settings']['success_url'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,6 +116,9 @@ class Subscribers extends APIEndpoint {
|
||||
}
|
||||
|
||||
function save($data = array()) {
|
||||
if(empty($data['segments'])) {
|
||||
$data['segments'] = array();
|
||||
}
|
||||
$subscriber = Subscriber::createOrUpdate($data);
|
||||
$errors = $subscriber->getErrors();
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
use \MailPoet\Analytics\Reporter;
|
||||
use \MailPoet\Models\Setting;
|
||||
use MailPoet\Analytics\Reporter;
|
||||
use MailPoet\Models\Setting;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
use \MailPoet\Models\Setting;
|
||||
use \MailPoet\Util\Url;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Util\Url;
|
||||
|
||||
class Changelog {
|
||||
function __construct() {
|
||||
|
@ -1,8 +1,6 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
use MailPoet\Cron\Workers\Scheduler;
|
||||
use MailPoet\Cron\Workers\SendingQueue;
|
||||
use \MailPoet\Models\Setting;
|
||||
use MailPoet\Models\Setting;
|
||||
|
||||
class Hooks {
|
||||
function __construct() {
|
||||
|
@ -27,6 +27,7 @@ class Menu {
|
||||
$this->assets_url = $assets_url;
|
||||
$subscribers_feature = new SubscribersFeature();
|
||||
$this->subscribers_over_limit = $subscribers_feature->check();
|
||||
$this->checkMailPoetAPIKey();
|
||||
}
|
||||
|
||||
function init() {
|
||||
@ -74,7 +75,8 @@ class Menu {
|
||||
add_screen_option('per_page', array(
|
||||
'label' => _x(
|
||||
'Number of newsletters per page',
|
||||
'newsletters per page (screen options)'
|
||||
'newsletters per page (screen options)',
|
||||
'mailpoet'
|
||||
),
|
||||
'option' => 'mailpoet_newsletters_per_page'
|
||||
));
|
||||
@ -96,7 +98,8 @@ class Menu {
|
||||
add_screen_option('per_page', array(
|
||||
'label' => _x(
|
||||
'Number of forms per page',
|
||||
'forms per page (screen options)'
|
||||
'forms per page (screen options)',
|
||||
'mailpoet'
|
||||
),
|
||||
'option' => 'mailpoet_forms_per_page'
|
||||
));
|
||||
@ -118,7 +121,8 @@ class Menu {
|
||||
add_screen_option('per_page', array(
|
||||
'label' => _x(
|
||||
'Number of subscribers per page',
|
||||
'subscribers per page (screen options)'
|
||||
'subscribers per page (screen options)',
|
||||
'mailpoet'
|
||||
),
|
||||
'option' => 'mailpoet_subscribers_per_page'
|
||||
));
|
||||
@ -141,7 +145,8 @@ class Menu {
|
||||
add_screen_option('per_page', array(
|
||||
'label' => _x(
|
||||
'Number of segments per page',
|
||||
'segments per page (screen options)'
|
||||
'segments per page (screen options)',
|
||||
'mailpoet'
|
||||
),
|
||||
'option' => 'mailpoet_segments_per_page'
|
||||
));
|
||||
@ -302,6 +307,7 @@ class Menu {
|
||||
'settings' => $settings,
|
||||
'segments' => Segment::getSegmentsWithSubscriberCount(),
|
||||
'cron_trigger' => CronTrigger::getAvailableMethods(),
|
||||
'total_subscribers' => Subscriber::getTotalSubscribers(),
|
||||
'pages' => Pages::getAll(),
|
||||
'flags' => $flags,
|
||||
'current_user' => wp_get_current_user(),
|
||||
@ -387,6 +393,9 @@ class Menu {
|
||||
|
||||
function newsletters() {
|
||||
if($this->subscribers_over_limit) return $this->displaySubscriberLimitExceededTemplate();
|
||||
if(isset($this->mp_api_key_valid) && $this->mp_api_key_valid === false) {
|
||||
return $this->displayMailPoetAPIKeyInvalidTemplate();
|
||||
}
|
||||
|
||||
global $wp_roles;
|
||||
|
||||
@ -483,6 +492,39 @@ class Menu {
|
||||
exit;
|
||||
}
|
||||
|
||||
function displayMailPoetAPIKeyInvalidTemplate() {
|
||||
$this->displayPage('invalidkey.html', array(
|
||||
'subscriber_count' => Subscriber::getTotalSubscribers()
|
||||
));
|
||||
exit;
|
||||
}
|
||||
|
||||
static function isOnMailPoetAdminPage(array $exclude = null, $screen_id = null) {
|
||||
if(is_null($screen_id)) {
|
||||
if(empty($_REQUEST['page'])) {
|
||||
return false;
|
||||
}
|
||||
$screen_id = $_REQUEST['page'];
|
||||
}
|
||||
if(!empty($exclude)) {
|
||||
foreach($exclude as $slug) {
|
||||
if(stripos($screen_id, $slug) !== false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return (stripos($screen_id, 'mailpoet-') !== false);
|
||||
}
|
||||
|
||||
function checkMailPoetAPIKey(ServicesChecker $checker = null) {
|
||||
if(self::isOnMailPoetAdminPage()) {
|
||||
$show_notices = isset($_REQUEST['page'])
|
||||
&& stripos($_REQUEST['page'], 'mailpoet-newsletters') === false;
|
||||
$checker = $checker ?: new ServicesChecker();
|
||||
$this->mp_api_key_valid = $checker->checkMailPoetAPIKeyValid($show_notices);
|
||||
}
|
||||
}
|
||||
|
||||
private function getLimitPerPage($model = null) {
|
||||
if($model === null) {
|
||||
return Listing\Handler::DEFAULT_LIMIT_PER_PAGE;
|
||||
@ -504,8 +546,4 @@ class Menu {
|
||||
$notice->displayWPNotice();
|
||||
}
|
||||
}
|
||||
|
||||
static function isOnMailPoetAdminPage() {
|
||||
return (!empty($_REQUEST['page']) && stripos($_REQUEST['page'], 'mailpoet-') !== false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ class Migrator {
|
||||
function sendingQueues() {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'type varchar(12) NULL DEFAULT NULL,',
|
||||
'type varchar(90) NULL DEFAULT NULL,',
|
||||
'newsletter_id mediumint(9) NOT NULL,',
|
||||
'newsletter_rendered_body longtext,',
|
||||
'newsletter_rendered_subject varchar(250) NULL DEFAULT NULL,',
|
||||
|
@ -3,11 +3,11 @@ namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Cron\CronTrigger;
|
||||
use MailPoet\Mailer\MailerLog;
|
||||
use \MailPoet\Models\Segment;
|
||||
use \MailPoet\Segments\WP;
|
||||
use \MailPoet\Models\Setting;
|
||||
use \MailPoet\Settings\Pages;
|
||||
use \MailPoet\Util\Helpers;
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Segments\WP;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Settings\Pages;
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -138,14 +138,14 @@ class Populator {
|
||||
$default_segment->hydrate(array(
|
||||
'name' => __('My First List', 'mailpoet'),
|
||||
'description' =>
|
||||
__('This list is automatically created when you install MailPoet', 'mailpoet')
|
||||
__('This list is automatically created when you install MailPoet.', 'mailpoet')
|
||||
));
|
||||
$default_segment->save();
|
||||
}
|
||||
}
|
||||
|
||||
private function newsletterOptionFields() {
|
||||
return array(
|
||||
$option_fields = array(
|
||||
array(
|
||||
'name' => 'isScheduled',
|
||||
'newsletter_type' => 'standard',
|
||||
@ -200,6 +200,14 @@ class Populator {
|
||||
'newsletter_type' => 'notification',
|
||||
)
|
||||
);
|
||||
|
||||
return array(
|
||||
'rows' => $option_fields,
|
||||
'identification_columns' => array(
|
||||
'name',
|
||||
'newsletter_type'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function newsletterTemplates() {
|
||||
@ -209,17 +217,40 @@ class Populator {
|
||||
$template = new $template(Env::$assets_url);
|
||||
$templates[] = $template->get();
|
||||
}
|
||||
return $templates;
|
||||
return array(
|
||||
'rows' => $templates,
|
||||
'identification_columns' => array(
|
||||
'name'
|
||||
),
|
||||
'remove_duplicates' => true
|
||||
);
|
||||
}
|
||||
|
||||
private function populate($model) {
|
||||
$modelMethod = Helpers::underscoreToCamelCase($model);
|
||||
$rows = $this->$modelMethod();
|
||||
$table = $this->prefix . $model;
|
||||
$data_descriptor = $this->$modelMethod();
|
||||
$rows = $data_descriptor['rows'];
|
||||
$identification_columns = array_fill_keys(
|
||||
$data_descriptor['identification_columns'],
|
||||
''
|
||||
);
|
||||
$remove_duplicates =
|
||||
isset($data_descriptor['remove_duplicates']) && $data_descriptor['remove_duplicates'];
|
||||
|
||||
foreach($rows as $row) {
|
||||
if(!$this->rowExists($table, $row)) {
|
||||
$existence_comparison_fields = array_intersect_key(
|
||||
$row,
|
||||
$identification_columns
|
||||
);
|
||||
|
||||
if(!$this->rowExists($table, $existence_comparison_fields)) {
|
||||
$this->insertRow($table, $row);
|
||||
} else {
|
||||
if($remove_duplicates) {
|
||||
$this->removeDuplicates($table, $row, $existence_comparison_fields);
|
||||
}
|
||||
$this->updateRow($table, $row, $existence_comparison_fields);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -245,4 +276,36 @@ class Populator {
|
||||
$row
|
||||
);
|
||||
}
|
||||
|
||||
private function updateRow($table, $row, $where) {
|
||||
global $wpdb;
|
||||
|
||||
return $wpdb->update(
|
||||
$table,
|
||||
$row,
|
||||
$where
|
||||
);
|
||||
}
|
||||
|
||||
private function removeDuplicates($table, $row, $where) {
|
||||
global $wpdb;
|
||||
|
||||
$conditions = array('1=1');
|
||||
$values = array();
|
||||
foreach($where as $field => $value) {
|
||||
$conditions[] = "`t1`.`$field` = `t2`.`$field`";
|
||||
$conditions[] = "`t1`.`$field` = %s";
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
$conditions = implode(' AND ', $conditions);
|
||||
|
||||
$sql = "DELETE FROM `$table` WHERE $conditions";
|
||||
return $wpdb->query(
|
||||
$wpdb->prepare(
|
||||
"DELETE t1 FROM $table t1, $table t2 WHERE t1.id < t2.id AND $conditions",
|
||||
$values
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,9 +1,9 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
use \Twig_Loader_Filesystem as TwigFileSystem;
|
||||
use \Twig_Environment as TwigEnv;
|
||||
use \Twig_Lexer as TwigLexer;
|
||||
use \MailPoet\Twig;
|
||||
use Twig_Loader_Filesystem as TwigFileSystem;
|
||||
use Twig_Environment as TwigEnv;
|
||||
use Twig_Lexer as TwigLexer;
|
||||
use MailPoet\Twig;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -1,14 +1,12 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Config\Env;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\WP\Notice as WPNotice;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class RequirementsChecker {
|
||||
const TEST_PHP_VERSION = 'PHPVersion';
|
||||
const TEST_FOLDER_PERMISSIONS = 'TempAndCacheFolderCreation';
|
||||
const TEST_PDO_EXTENSION = 'PDOExtension';
|
||||
const TEST_MBSTRING_EXTENSION = 'MbstringExtension';
|
||||
@ -16,25 +14,26 @@ class RequirementsChecker {
|
||||
|
||||
public $display_error_notice;
|
||||
public $vendor_classes = array(
|
||||
'\ORM',
|
||||
'\Model',
|
||||
'\Twig_Environment',
|
||||
'\Twig_Loader_Filesystem',
|
||||
'\Twig_Lexer',
|
||||
'\Twig_Extension',
|
||||
'\Twig_Extension_GlobalsInterface',
|
||||
'\Twig_SimpleFunction',
|
||||
'\Swift_Mailer',
|
||||
'\Swift_SmtpTransport',
|
||||
'\Swift_Message',
|
||||
'\Carbon\Carbon',
|
||||
'\Sudzy\ValidModel',
|
||||
'\Sudzy\ValidationException',
|
||||
'\Sudzy\Engine',
|
||||
'\pQuery',
|
||||
'\Cron\CronExpression',
|
||||
'\Html2Text\Html2Text',
|
||||
'\csstidy'
|
||||
'\ORM',
|
||||
'\Model',
|
||||
'\Twig_Environment',
|
||||
'\Twig_Loader_Filesystem',
|
||||
'\Twig_Lexer',
|
||||
'\Twig_Extension',
|
||||
'\Twig_Extension_GlobalsInterface',
|
||||
'\Twig_SimpleFunction',
|
||||
'\Swift_Mailer',
|
||||
'\Swift_SmtpTransport',
|
||||
'\Swift_Message',
|
||||
'\Carbon\Carbon',
|
||||
'\Sudzy\ValidModel',
|
||||
'\Sudzy\ValidationException',
|
||||
'\Sudzy\Engine',
|
||||
'\pQuery',
|
||||
'\Cron\CronExpression',
|
||||
'\Html2Text\Html2Text',
|
||||
'\csstidy',
|
||||
'\Sabberworm\CSS\Parser'
|
||||
);
|
||||
|
||||
function __construct($display_error_notice = true) {
|
||||
@ -44,7 +43,6 @@ class RequirementsChecker {
|
||||
function checkAllRequirements() {
|
||||
$available_tests = array(
|
||||
self::TEST_PDO_EXTENSION,
|
||||
self::TEST_PHP_VERSION,
|
||||
self::TEST_FOLDER_PERMISSIONS,
|
||||
self::TEST_MBSTRING_EXTENSION,
|
||||
self::TEST_VENDOR_SOURCE
|
||||
@ -56,17 +54,6 @@ class RequirementsChecker {
|
||||
return $results;
|
||||
}
|
||||
|
||||
function checkPHPVersion() {
|
||||
if(version_compare(phpversion(), '5.3.0', '<')) {
|
||||
$error = Helpers::replaceLinkTags(
|
||||
__('This plugin requires PHP version 5.3 or newer. Please read our [link]instructions[/link] on how to resolve this issue.', 'mailpoet'),
|
||||
'//docs.mailpoet.com/article/152-minimum-requirements-for-mailpoet-3#php_version'
|
||||
);
|
||||
return $this->processError($error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function checkTempAndCacheFolderCreation() {
|
||||
$paths = array(
|
||||
'temp_path' => Env::$temp_path,
|
||||
@ -75,7 +62,7 @@ class RequirementsChecker {
|
||||
if(!is_dir($paths['cache_path']) && !wp_mkdir_p($paths['cache_path'])) {
|
||||
$error = Helpers::replaceLinkTags(
|
||||
__('This plugin requires write permissions inside the /wp-content/uploads folder. Please read our [link]instructions[/link] on how to resolve this issue.', 'mailpoet'),
|
||||
'//docs.mailpoet.com/article/152-minimum-requirements-for-mailpoet-3#folder_permissions'
|
||||
'//beta.docs.mailpoet.com/article/152-minimum-requirements-for-mailpoet-3#folder_permissions'
|
||||
);
|
||||
return $this->processError($error);
|
||||
}
|
||||
@ -92,14 +79,12 @@ class RequirementsChecker {
|
||||
}
|
||||
|
||||
function checkPDOExtension() {
|
||||
if(!extension_loaded('pdo') && !extension_loaded('pdo_mysql')) {
|
||||
$error = Helpers::replaceLinkTags(
|
||||
__('This plugin requires PDO_MYSQL PHP extension. Please read our [link]instructions[/link] on how to resolve this issue.', 'mailpoet'),
|
||||
'//docs.mailpoet.com/article/152-minimum-requirements-for-mailpoet-3#php_extension'
|
||||
);
|
||||
return $this->processError($error);
|
||||
}
|
||||
return true;
|
||||
if(extension_loaded('pdo') && extension_loaded('pdo_mysql')) return true;
|
||||
$error = Helpers::replaceLinkTags(
|
||||
__('This plugin requires the PDO_MYSQL PHP extension. Please read our [link]instructions[/link] on how to resolve this issue.', 'mailpoet'),
|
||||
'//beta.docs.mailpoet.com/article/152-minimum-requirements-for-mailpoet-3#php_extension'
|
||||
);
|
||||
return $this->processError($error);
|
||||
}
|
||||
|
||||
function checkMbstringExtension() {
|
||||
|
49
lib/Config/ServicesChecker.php
Normal file
49
lib/Config/ServicesChecker.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Services\Bridge;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\WP\Notice as WPNotice;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class ServicesChecker {
|
||||
function checkMailPoetAPIKeyValid($display_error_notice = true) {
|
||||
if(!Bridge::isMPSendingServiceEnabled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$result = Setting::getValue(Bridge::API_KEY_STATE_SETTING_NAME);
|
||||
if(empty($result['state']) || $result['state'] == Bridge::MAILPOET_KEY_VALID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if($result['state'] == Bridge::MAILPOET_KEY_INVALID) {
|
||||
$error = Helpers::replaceLinkTags(
|
||||
__('All sending is currently paused! Your key to send with MailPoet is invalid. [link]Visit MailPoet.com to purchase a key[/link]', 'mailpoet'),
|
||||
'https://account.mailpoet.com?s=' . Subscriber::getTotalSubscribers()
|
||||
);
|
||||
if($display_error_notice) {
|
||||
WPNotice::displayError($error);
|
||||
}
|
||||
return false;
|
||||
} elseif($result['state'] == Bridge::MAILPOET_KEY_EXPIRING
|
||||
&& !empty($result['data']['expire_at'])
|
||||
) {
|
||||
$date = date('Y-m-d', strtotime($result['data']['expire_at']));
|
||||
$error = Helpers::replaceLinkTags(
|
||||
__('Your newsletters are awesome! Don\'t forget to [link]upgrade your MailPoet email plan[/link] by %s to keep sending them to your subscribers.', 'mailpoet'),
|
||||
'https://account.mailpoet.com?s=' . Subscriber::getTotalSubscribers()
|
||||
);
|
||||
$error = sprintf($error, $date);
|
||||
if($display_error_notice) {
|
||||
WPNotice::displayWarning($error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
use \MailPoet\Models\Newsletter;
|
||||
use \MailPoet\Models\Subscriber;
|
||||
use \MailPoet\Models\SubscriberSegment;
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Models\SubscriberSegment;
|
||||
use MailPoet\Newsletter\Url as NewsletterUrl;
|
||||
|
||||
class Shortcodes {
|
||||
|
@ -1,7 +1,6 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
use \MailPoet\Util\Security;
|
||||
use \MailPoet\Models\Form;
|
||||
use MailPoet\Models\Form;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -3,6 +3,7 @@ namespace MailPoet\Cron;
|
||||
use MailPoet\Cron\Workers\Scheduler as SchedulerWorker;
|
||||
use MailPoet\Cron\Workers\SendingQueue\SendingQueue as SendingQueueWorker;
|
||||
use MailPoet\Cron\Workers\Bounce as BounceWorker;
|
||||
use MailPoet\Cron\Workers\SendingServiceKeyCheck as SendingServiceKeyCheckWorker;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
require_once(ABSPATH . 'wp-includes/pluggable.php');
|
||||
@ -48,6 +49,7 @@ class Daemon {
|
||||
try {
|
||||
$this->executeScheduleWorker();
|
||||
$this->executeQueueWorker();
|
||||
$this->executeSendingServiceKeyCheckWorker();
|
||||
$this->executeBounceWorker();
|
||||
} catch(\Exception $e) {
|
||||
// continue processing, no need to handle errors
|
||||
@ -80,6 +82,11 @@ class Daemon {
|
||||
return $queue->process();
|
||||
}
|
||||
|
||||
function executeSendingServiceKeyCheckWorker() {
|
||||
$worker = new SendingServiceKeyCheckWorker($this->timer);
|
||||
return $worker->process();
|
||||
}
|
||||
|
||||
function executeBounceWorker() {
|
||||
$bounce = new BounceWorker($this->timer);
|
||||
return $bounce->process();
|
||||
|
@ -5,7 +5,9 @@ use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Cron\Workers\Scheduler as SchedulerWorker;
|
||||
use MailPoet\Cron\Workers\SendingQueue\SendingQueue as SendingQueueWorker;
|
||||
use MailPoet\Cron\Workers\Bounce as BounceWorker;
|
||||
use MailPoet\Cron\Workers\SendingServiceKeyCheck as SendingServiceKeyCheckWorker;
|
||||
use MailPoet\Mailer\MailerLog;
|
||||
use MailPoet\Services\Bridge;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -17,14 +19,25 @@ class WordPress {
|
||||
}
|
||||
|
||||
static function checkExecutionRequirements() {
|
||||
// sending queue
|
||||
$scheduled_queues = SchedulerWorker::getScheduledQueues();
|
||||
$running_queues = SendingQueueWorker::getRunningQueues();
|
||||
$sending_limit_reached = MailerLog::isSendingLimitReached();
|
||||
$bounce_sync_available = BounceWorker::checkBounceSyncAvailable();
|
||||
$sending_is_paused = MailerLog::isSendingPaused();
|
||||
// sending service
|
||||
$mp_sending_enabled = Bridge::isMPSendingServiceEnabled();
|
||||
// bounce sync
|
||||
$bounce_due_queues = BounceWorker::getAllDueQueues();
|
||||
$bounce_future_queues = BounceWorker::getFutureQueues();
|
||||
return (($scheduled_queues || $running_queues) && !$sending_limit_reached)
|
||||
|| ($bounce_sync_available && ($bounce_due_queues || !$bounce_future_queues));
|
||||
// sending service key check
|
||||
$sskeycheck_due_queues = SendingServiceKeyCheckWorker::getAllDueQueues();
|
||||
$sskeycheck_future_queues = SendingServiceKeyCheckWorker::getFutureQueues();
|
||||
// check requirements for each worker
|
||||
$sending_queue_active = (($scheduled_queues || $running_queues) && !$sending_limit_reached && !$sending_is_paused);
|
||||
$bounce_sync_active = ($mp_sending_enabled && ($bounce_due_queues || !$bounce_future_queues));
|
||||
$sending_service_key_check_active = ($mp_sending_enabled && ($sskeycheck_due_queues || !$sskeycheck_future_queues));
|
||||
|
||||
return ($sending_queue_active || $bounce_sync_active || $sending_service_key_check_active);
|
||||
}
|
||||
|
||||
static function cleanup() {
|
||||
|
@ -6,15 +6,19 @@ use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Mailer\Mailer;
|
||||
use MailPoet\Models\SendingQueue;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Services\Bridge;
|
||||
use MailPoet\Services\Bridge\API;
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Bounce {
|
||||
const TASK_TYPE = 'bounce';
|
||||
const BATCH_SIZE = 100;
|
||||
|
||||
const BOUNCED_HARD = 'hard';
|
||||
const BOUNCED_SOFT = 'soft';
|
||||
const NOT_BOUNCED = null;
|
||||
const BATCH_SIZE = 100;
|
||||
|
||||
public $timer;
|
||||
public $api;
|
||||
@ -25,21 +29,15 @@ class Bounce {
|
||||
CronHelper::enforceExecutionLimit($this->timer);
|
||||
}
|
||||
|
||||
static function checkBounceSyncAvailable() {
|
||||
$mailer_config = Mailer::getMailerConfig();
|
||||
return !empty($mailer_config['method'])
|
||||
&& $mailer_config['method'] === Mailer::METHOD_MAILPOET;
|
||||
}
|
||||
|
||||
function initApi() {
|
||||
if(!$this->api) {
|
||||
$mailer_config = Mailer::getMailerConfig();
|
||||
$this->api = new Bounce\API($mailer_config['mailpoet_api_key']);
|
||||
$this->api = new API($mailer_config['mailpoet_api_key']);
|
||||
}
|
||||
}
|
||||
|
||||
function process() {
|
||||
if(!self::checkBounceSyncAvailable()) {
|
||||
if(!Bridge::isMPSendingServiceEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -64,7 +62,7 @@ class Bounce {
|
||||
}
|
||||
|
||||
static function scheduleBounceSync() {
|
||||
$already_scheduled = SendingQueue::where('type', 'bounce')
|
||||
$already_scheduled = SendingQueue::where('type', self::TASK_TYPE)
|
||||
->whereNull('deleted_at')
|
||||
->where('status', SendingQueue::STATUS_SCHEDULED)
|
||||
->findMany();
|
||||
@ -72,7 +70,7 @@ class Bounce {
|
||||
return false;
|
||||
}
|
||||
$queue = SendingQueue::create();
|
||||
$queue->type = 'bounce';
|
||||
$queue->type = self::TASK_TYPE;
|
||||
$queue->status = SendingQueue::STATUS_SCHEDULED;
|
||||
$queue->priority = SendingQueue::PRIORITY_LOW;
|
||||
$queue->scheduled_at = self::getNextRunDate();
|
||||
@ -143,7 +141,7 @@ class Bounce {
|
||||
}
|
||||
|
||||
function processEmails(array $subscriber_emails) {
|
||||
$checked_emails = $this->api->check($subscriber_emails);
|
||||
$checked_emails = $this->api->checkBounces($subscriber_emails);
|
||||
$this->processApiResponse((array)$checked_emails);
|
||||
}
|
||||
|
||||
@ -170,7 +168,7 @@ class Bounce {
|
||||
|
||||
static function getScheduledQueues($future = false) {
|
||||
$dateWhere = ($future) ? 'whereGt' : 'whereLte';
|
||||
return SendingQueue::where('type', 'bounce')
|
||||
return SendingQueue::where('type', self::TASK_TYPE)
|
||||
->$dateWhere('scheduled_at', Carbon::createFromTimestamp(current_time('timestamp')))
|
||||
->whereNull('deleted_at')
|
||||
->where('status', SendingQueue::STATUS_SCHEDULED)
|
||||
@ -178,7 +176,7 @@ class Bounce {
|
||||
}
|
||||
|
||||
static function getRunningQueues() {
|
||||
return SendingQueue::where('type', 'bounce')
|
||||
return SendingQueue::where('type', self::TASK_TYPE)
|
||||
->whereLte('scheduled_at', Carbon::createFromTimestamp(current_time('timestamp')))
|
||||
->whereNull('deleted_at')
|
||||
->whereNull('status')
|
||||
|
@ -1,41 +0,0 @@
|
||||
<?php
|
||||
namespace MailPoet\Cron\Workers\Bounce;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class API {
|
||||
public $url = 'https://bridge.mailpoet.com/api/v0/bounces/search';
|
||||
public $api_key;
|
||||
|
||||
function __construct($api_key) {
|
||||
$this->api_key = $api_key;
|
||||
}
|
||||
|
||||
function check(array $emails) {
|
||||
$result = wp_remote_post(
|
||||
$this->url,
|
||||
$this->request($emails)
|
||||
);
|
||||
if(wp_remote_retrieve_response_code($result) === 200) {
|
||||
return json_decode(wp_remote_retrieve_body($result), true);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private function auth() {
|
||||
return 'Basic ' . base64_encode('api:' . $this->api_key);
|
||||
}
|
||||
|
||||
private function request($body) {
|
||||
return array(
|
||||
'timeout' => 10,
|
||||
'httpversion' => '1.0',
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => $this->auth()
|
||||
),
|
||||
'body' => json_encode($body)
|
||||
);
|
||||
}
|
||||
}
|
@ -76,10 +76,8 @@ class Scheduler {
|
||||
}, $segments);
|
||||
|
||||
// ensure that subscribers are in segments
|
||||
$subscribers = Subscriber::getSubscribedInSegments($segment_ids)
|
||||
->findArray();
|
||||
$subscribers = Helpers::arrayColumn($subscribers, 'subscriber_id');
|
||||
$subscribers = array_unique($subscribers);
|
||||
$subscribers = Subscriber::getSubscribedInSegments($segment_ids)->findArray();
|
||||
$subscribers = Helpers::flattenArray($subscribers);
|
||||
|
||||
if(empty($subscribers)) {
|
||||
return $this->deleteQueueOrUpdateNextRunDate($queue, $newsletter);
|
||||
@ -107,11 +105,8 @@ class Scheduler {
|
||||
$segment_ids = array_map(function($segment) {
|
||||
return $segment['id'];
|
||||
}, $segments);
|
||||
$subscribers = Subscriber::getSubscribedInSegments($segment_ids)
|
||||
->findArray();
|
||||
$subscribers = Helpers::arrayColumn($subscribers, 'subscriber_id');
|
||||
$subscribers = array_unique($subscribers);
|
||||
|
||||
$subscribers = Subscriber::getSubscribedInSegments($segment_ids)->findArray();
|
||||
$subscribers = Helpers::flattenArray($subscribers);
|
||||
// update current queue
|
||||
$queue->subscribers = serialize(
|
||||
array(
|
||||
@ -169,8 +164,13 @@ class Scheduler {
|
||||
function deleteQueueOrUpdateNextRunDate($queue, $newsletter) {
|
||||
if($newsletter->intervalType === NewsletterScheduler::INTERVAL_IMMEDIATELY) {
|
||||
$queue->delete();
|
||||
return;
|
||||
} else {
|
||||
$next_run_date = NewsletterScheduler::getNextRunDate($newsletter->schedule);
|
||||
if(!$next_run_date) {
|
||||
$queue->delete();
|
||||
return;
|
||||
}
|
||||
$queue->scheduled_at = $next_run_date;
|
||||
$queue->save();
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ use MailPoet\Cron\Workers\SendingQueue\Tasks\Mailer as MailerTask;
|
||||
use MailPoet\Cron\Workers\SendingQueue\Tasks\Newsletter as NewsletterTask;
|
||||
use MailPoet\Mailer\MailerLog;
|
||||
use MailPoet\Models\SendingQueue as SendingQueueModel;
|
||||
use MailPoet\Models\StatisticsNewsletters as StatisticsNewslettersModel;
|
||||
use MailPoet\Models\Subscriber as SubscriberModel;
|
||||
use MailPoet\Models\StatisticsNewsletters as StatisticsNewslettersModel;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -15,7 +15,7 @@ class SendingQueue {
|
||||
public $mailer_task;
|
||||
public $newsletter_task;
|
||||
public $timer;
|
||||
const BATCH_SIZE = 50;
|
||||
const BATCH_SIZE = 20;
|
||||
|
||||
function __construct($timer = false, $mailer_task = false, $newsletter_task = false) {
|
||||
$this->mailer_task = ($mailer_task) ? $mailer_task : new MailerTask();
|
||||
@ -34,6 +34,8 @@ class SendingQueue {
|
||||
}
|
||||
// configure mailer
|
||||
$this->mailer_task->configureMailer($newsletter);
|
||||
// get newsletter segments
|
||||
$newsletter_segments_ids = $this->newsletter_task->getSegments($newsletter);
|
||||
// get subscribers
|
||||
$queue->subscribers = $queue->getSubscribers();
|
||||
$subscriber_batches = array_chunk(
|
||||
@ -41,12 +43,10 @@ class SendingQueue {
|
||||
self::BATCH_SIZE
|
||||
);
|
||||
foreach($subscriber_batches as $subscribers_to_process_ids) {
|
||||
$found_subscribers = SubscriberModel::whereIn('id', $subscribers_to_process_ids)
|
||||
->whereNull('deleted_at')
|
||||
->findMany();
|
||||
$found_subscribers_ids = array_map(function($subscriber) {
|
||||
return $subscriber->id;
|
||||
}, $found_subscribers);
|
||||
$found_subscribers = SubscriberModel::findSubscribersInSegments(
|
||||
$subscribers_to_process_ids, $newsletter_segments_ids
|
||||
)->findMany();
|
||||
$found_subscribers_ids = SubscriberModel::extractSubscribersIds($found_subscribers);
|
||||
// if some subscribers weren't found, remove them from the processing list
|
||||
if(count($found_subscribers_ids) !== count($subscribers_to_process_ids)) {
|
||||
$subscibers_to_remove = array_diff(
|
||||
|
@ -5,6 +5,7 @@ use MailPoet\Cron\Workers\SendingQueue\Tasks\Links as LinksTask;
|
||||
use MailPoet\Cron\Workers\SendingQueue\Tasks\Posts as PostsTask;
|
||||
use MailPoet\Cron\Workers\SendingQueue\Tasks\Shortcodes as ShortcodesTask;
|
||||
use MailPoet\Models\Newsletter as NewsletterModel;
|
||||
use MailPoet\Models\NewsletterSegment as NewsletterSegmentModel;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Newsletter\Links\Links as NewsletterLinks;
|
||||
use MailPoet\Newsletter\Renderer\PostProcess\OpenTracking;
|
||||
@ -100,4 +101,11 @@ class Newsletter {
|
||||
$newsletter->setStatus(NewsletterModel::STATUS_SENT);
|
||||
}
|
||||
}
|
||||
|
||||
function getSegments($newsletter) {
|
||||
$segments = NewsletterSegmentModel::where('newsletter_id', $newsletter->id)
|
||||
->select('segment_id')
|
||||
->findArray();
|
||||
return Helpers::flattenArray($segments);
|
||||
}
|
||||
}
|
147
lib/Cron/Workers/SendingServiceKeyCheck.php
Normal file
147
lib/Cron/Workers/SendingServiceKeyCheck.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
namespace MailPoet\Cron\Workers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Mailer\Mailer;
|
||||
use MailPoet\Models\SendingQueue;
|
||||
use MailPoet\Services\Bridge;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class SendingServiceKeyCheck {
|
||||
const TASK_TYPE = 'sending_service_key_check';
|
||||
const UNAVAILABLE_SERVICE_RESCHEDULE_TIMEOUT = 60;
|
||||
|
||||
public $timer;
|
||||
public $bridge;
|
||||
|
||||
function __construct($timer = false) {
|
||||
$this->timer = ($timer) ? $timer : microtime(true);
|
||||
// abort if execution limit is reached
|
||||
CronHelper::enforceExecutionLimit($this->timer);
|
||||
}
|
||||
|
||||
function initApi() {
|
||||
if(!$this->bridge) {
|
||||
$this->bridge = new Bridge();
|
||||
}
|
||||
}
|
||||
|
||||
function process() {
|
||||
if(!Bridge::isMPSendingServiceEnabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->initApi();
|
||||
|
||||
$scheduled_queues = self::getScheduledQueues();
|
||||
$running_queues = self::getRunningQueues();
|
||||
|
||||
if(!$scheduled_queues && !$running_queues) {
|
||||
self::schedule();
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach($scheduled_queues as $i => $queue) {
|
||||
$this->prepareQueue($queue);
|
||||
}
|
||||
foreach($running_queues as $i => $queue) {
|
||||
$this->processQueue($queue);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static function schedule() {
|
||||
$already_scheduled = SendingQueue::where('type', self::TASK_TYPE)
|
||||
->whereNull('deleted_at')
|
||||
->where('status', SendingQueue::STATUS_SCHEDULED)
|
||||
->findMany();
|
||||
if($already_scheduled) {
|
||||
return false;
|
||||
}
|
||||
$queue = SendingQueue::create();
|
||||
$queue->type = self::TASK_TYPE;
|
||||
$queue->status = SendingQueue::STATUS_SCHEDULED;
|
||||
$queue->priority = SendingQueue::PRIORITY_LOW;
|
||||
$queue->scheduled_at = self::getNextRunDate();
|
||||
$queue->newsletter_id = 0;
|
||||
$queue->save();
|
||||
return $queue;
|
||||
}
|
||||
|
||||
function prepareQueue(SendingQueue $queue) {
|
||||
$queue->status = null;
|
||||
$queue->save();
|
||||
|
||||
// abort if execution limit is reached
|
||||
CronHelper::enforceExecutionLimit($this->timer);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function processQueue(SendingQueue $queue) {
|
||||
// abort if execution limit is reached
|
||||
CronHelper::enforceExecutionLimit($this->timer);
|
||||
|
||||
try {
|
||||
$mailer_config = Mailer::getMailerConfig();
|
||||
$result = $this->bridge->checkKey($mailer_config['mailpoet_api_key']);
|
||||
$this->bridge->updateSubscriberCount($result);
|
||||
} catch (\Exception $e) {
|
||||
$result = false;
|
||||
}
|
||||
|
||||
if(empty($result['code']) || $result['code'] == Bridge::CHECK_ERROR_UNAVAILABLE) {
|
||||
// reschedule the check
|
||||
$scheduled_at = Carbon::createFromTimestamp(current_time('timestamp'));
|
||||
$queue->scheduled_at = $scheduled_at->addMinutes(
|
||||
self::UNAVAILABLE_SERVICE_RESCHEDULE_TIMEOUT
|
||||
);
|
||||
$queue->save();
|
||||
return false;
|
||||
}
|
||||
|
||||
$queue->processed_at = current_time('mysql');
|
||||
$queue->status = SendingQueue::STATUS_COMPLETED;
|
||||
$queue->save();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static function getNextRunDate() {
|
||||
$date = Carbon::createFromTimestamp(current_time('timestamp'));
|
||||
// Random day of the next week
|
||||
$date->setISODate($date->format('o'), $date->format('W') + 1, mt_rand(1, 7));
|
||||
$date->startOfDay();
|
||||
return $date;
|
||||
}
|
||||
|
||||
static function getScheduledQueues($future = false) {
|
||||
$dateWhere = ($future) ? 'whereGt' : 'whereLte';
|
||||
return SendingQueue::where('type', self::TASK_TYPE)
|
||||
->$dateWhere('scheduled_at', Carbon::createFromTimestamp(current_time('timestamp')))
|
||||
->whereNull('deleted_at')
|
||||
->where('status', SendingQueue::STATUS_SCHEDULED)
|
||||
->findMany();
|
||||
}
|
||||
|
||||
static function getRunningQueues() {
|
||||
return SendingQueue::where('type', self::TASK_TYPE)
|
||||
->whereLte('scheduled_at', Carbon::createFromTimestamp(current_time('timestamp')))
|
||||
->whereNull('deleted_at')
|
||||
->whereNull('status')
|
||||
->findMany();
|
||||
}
|
||||
|
||||
static function getAllDueQueues() {
|
||||
$scheduled_queues = self::getScheduledQueues();
|
||||
$running_queues = self::getRunningQueues();
|
||||
return array_merge((array)$scheduled_queues, (array)$running_queues);
|
||||
}
|
||||
|
||||
static function getFutureQueues() {
|
||||
return self::getScheduledQueues(true);
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ abstract class Base {
|
||||
|
||||
if($block['id'] === 'email') {
|
||||
$rules['required'] = true;
|
||||
$rules['error-message'] = __('Please specify a valid email address', 'mailpoet');
|
||||
$rules['error-message'] = __('Please specify a valid email address.', 'mailpoet');
|
||||
}
|
||||
|
||||
if($block['id'] === 'segments') {
|
||||
@ -132,4 +132,4 @@ abstract class Base {
|
||||
}
|
||||
return join(' ', $modifiers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
<?php
|
||||
namespace MailPoet\Form;
|
||||
use MailPoet\Form\Block;
|
||||
use MailPoet\Form\Util;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -1,13 +1,9 @@
|
||||
<?php
|
||||
namespace MailPoet\Form;
|
||||
use \MailPoet\Config\Renderer;
|
||||
use \MailPoet\Models\Form;
|
||||
use \MailPoet\Models\Segment;
|
||||
use \MailPoet\Models\Setting;
|
||||
use \MailPoet\Models\Subscriber;
|
||||
use \MailPoet\Form\Renderer as FormRenderer;
|
||||
use \MailPoet\Form\Util;
|
||||
use \MailPoet\Util\Security;
|
||||
use MailPoet\Config\Renderer;
|
||||
use MailPoet\Models\Form;
|
||||
use MailPoet\Form\Renderer as FormRenderer;
|
||||
use MailPoet\Util\Security;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -83,9 +79,9 @@ class Widget extends \WP_Widget {
|
||||
endpoint: 'forms',
|
||||
action: 'create'
|
||||
}).done(function(response) {
|
||||
if(response.result && response.form_id) {
|
||||
if(response.data && response.data.id) {
|
||||
window.location =
|
||||
"<?php echo $form_edit_url; ?>" + response.form_id;
|
||||
"<?php echo $form_edit_url; ?>" + response.data.id;
|
||||
}
|
||||
});
|
||||
return false;
|
||||
|
@ -28,9 +28,9 @@ class Mailer {
|
||||
$this->mailer_instance = $this->buildMailer();
|
||||
}
|
||||
|
||||
function send($newsletter, $subscriber) {
|
||||
function send($newsletter, $subscriber, $extra_params = array()) {
|
||||
$subscriber = $this->formatSubscriberNameAndEmailAddress($subscriber);
|
||||
return $this->mailer_instance->send($newsletter, $subscriber);
|
||||
return $this->mailer_instance->send($newsletter, $subscriber, $extra_params);
|
||||
}
|
||||
|
||||
function buildMailer() {
|
||||
@ -80,7 +80,7 @@ class Mailer {
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new \Exception(__('Mailing method does not exist', 'mailpoet'));
|
||||
throw new \Exception(__('Mailing method does not exist.', 'mailpoet'));
|
||||
}
|
||||
return $mailer_instance;
|
||||
}
|
||||
@ -88,7 +88,7 @@ class Mailer {
|
||||
static function getMailerConfig($mailer = false) {
|
||||
if(!$mailer) {
|
||||
$mailer = Setting::getValue(self::MAILER_CONFIG_SETTING_NAME);
|
||||
if(!$mailer || !isset($mailer['method'])) throw new \Exception(__('Mailer is not configured', 'mailpoet'));
|
||||
if(!$mailer || !isset($mailer['method'])) throw new \Exception(__('Mailer is not configured.', 'mailpoet'));
|
||||
}
|
||||
if(empty($mailer['frequency'])) {
|
||||
$default_settings = Setting::getDefaults();
|
||||
@ -105,7 +105,7 @@ class Mailer {
|
||||
function getSenderNameAndAddress($sender = false) {
|
||||
if(empty($sender)) {
|
||||
$sender = Setting::getValue('sender', array());
|
||||
if(empty($sender['address'])) throw new \Exception(__('Sender name and email are not configured', 'mailpoet'));
|
||||
if(empty($sender['address'])) throw new \Exception(__('Sender name and email are not configured.', 'mailpoet'));
|
||||
}
|
||||
$from_name = $this->encodeAddressNamePart($sender['name']);
|
||||
return array(
|
||||
@ -187,4 +187,4 @@ class Mailer {
|
||||
'response' => true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ class MailerLog {
|
||||
if($mailer_log['retry_attempt'] === self::RETRY_ATTEMPTS_LIMIT) {
|
||||
$mailer_log = self::pauseSending($mailer_log);
|
||||
}
|
||||
if($mailer_log['status'] === self::STATUS_PAUSED) {
|
||||
if(self::isSendingPaused($mailer_log)) {
|
||||
throw new \Exception(__('Sending has been paused.', 'mailpoet'));
|
||||
}
|
||||
if(!is_null($mailer_log['retry_at'])) {
|
||||
@ -117,4 +117,9 @@ class MailerLog {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static function isSendingPaused($mailer_log = false) {
|
||||
$mailer_log = self::getMailerLog($mailer_log);
|
||||
return $mailer_log['status'] === self::STATUS_PAUSED;
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ class AmazonSES {
|
||||
public $sender;
|
||||
public $reply_to;
|
||||
public $return_path;
|
||||
public $message;
|
||||
public $date;
|
||||
public $date_without_time;
|
||||
private $available_regions = array(
|
||||
@ -31,7 +32,7 @@ class AmazonSES {
|
||||
$this->aws_secret_key = $secret_key;
|
||||
$this->aws_region = (in_array($region, $this->available_regions)) ? $region : false;
|
||||
if(!$this->aws_region) {
|
||||
throw new \Exception(__('Unsupported Amazon SES region.', 'mailpoet'));
|
||||
throw new \Exception(__('Unsupported Amazon SES region', 'mailpoet'));
|
||||
}
|
||||
$this->aws_endpoint = sprintf('email.%s.amazonaws.com', $this->aws_region);
|
||||
$this->aws_signing_algorithm = 'AWS4-HMAC-SHA256';
|
||||
@ -48,10 +49,10 @@ class AmazonSES {
|
||||
$this->date_without_time = gmdate('Ymd');
|
||||
}
|
||||
|
||||
function send($newsletter, $subscriber) {
|
||||
function send($newsletter, $subscriber, $extra_params = array()) {
|
||||
$result = wp_remote_post(
|
||||
$this->url,
|
||||
$this->request($newsletter, $subscriber)
|
||||
$this->request($newsletter, $subscriber, $extra_params)
|
||||
);
|
||||
if(is_wp_error($result)) {
|
||||
return Mailer::formatMailerConnectionErrorResult($result->get_error_message());
|
||||
@ -61,32 +62,69 @@ class AmazonSES {
|
||||
$response = ($response) ?
|
||||
$response->Error->Message->__toString() :
|
||||
sprintf(__('%s has returned an unknown error.', 'mailpoet'), Mailer::METHOD_AMAZONSES);
|
||||
if(empty($extra_params['test_email'])) {
|
||||
$response .= sprintf(' %s: %s', __('Unprocessed subscriber', 'mailpoet'), $subscriber);
|
||||
}
|
||||
return Mailer::formatMailerSendErrorResult($response);
|
||||
}
|
||||
return Mailer::formatMailerSendSuccessResult();
|
||||
}
|
||||
|
||||
function getBody($newsletter, $subscriber) {
|
||||
function getBody($newsletter, $subscriber, $extra_params = array()) {
|
||||
$this->message = $this->createMessage($newsletter, $subscriber, $extra_params);
|
||||
$body = array(
|
||||
'Action' => 'SendEmail',
|
||||
'Action' => 'SendRawEmail',
|
||||
'Version' => '2010-12-01',
|
||||
'Destination.ToAddresses.member.1' => $subscriber,
|
||||
'Source' => $this->sender['from_name_email'],
|
||||
'ReplyToAddresses.member.1' => $this->reply_to['reply_to_name_email'],
|
||||
'Message.Subject.Data' => $newsletter['subject'],
|
||||
'ReturnPath' => $this->return_path
|
||||
'RawMessage.Data' => $this->encodeMessage($this->message)
|
||||
);
|
||||
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 = array_map('urlencode', $this->getBody($newsletter, $subscriber));
|
||||
function createMessage($newsletter, $subscriber, $extra_params = array()) {
|
||||
$message = \Swift_Message::newInstance()
|
||||
->setTo($this->processSubscriber($subscriber))
|
||||
->setFrom(array(
|
||||
$this->sender['from_email'] => $this->sender['from_name']
|
||||
))
|
||||
->setSender($this->sender['from_email'])
|
||||
->setReplyTo(array(
|
||||
$this->reply_to['reply_to_email'] => $this->reply_to['reply_to_name']
|
||||
))
|
||||
->setReturnPath($this->return_path)
|
||||
->setSubject($newsletter['subject']);
|
||||
if(!empty($extra_params['unsubscribe_url'])) {
|
||||
$headers = $message->getHeaders();
|
||||
$headers->addTextHeader('List-Unsubscribe', '<' . $extra_params['unsubscribe_url'] . '>');
|
||||
}
|
||||
if(!empty($newsletter['body']['html'])) {
|
||||
$message = $message->setBody($newsletter['body']['html'], 'text/html');
|
||||
}
|
||||
if(!empty($newsletter['body']['text'])) {
|
||||
$message = $message->addPart($newsletter['body']['text'], 'text/plain');
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
|
||||
function encodeMessage(\Swift_Message $message) {
|
||||
return base64_encode($message->toString());
|
||||
}
|
||||
|
||||
function processSubscriber($subscriber) {
|
||||
preg_match('!(?P<name>.*?)\s<(?P<email>.*?)>!', $subscriber, $subscriber_data);
|
||||
if(!isset($subscriber_data['email'])) {
|
||||
$subscriber_data = array(
|
||||
'email' => $subscriber,
|
||||
);
|
||||
}
|
||||
return array(
|
||||
$subscriber_data['email'] =>
|
||||
(isset($subscriber_data['name'])) ? $subscriber_data['name'] : ''
|
||||
);
|
||||
}
|
||||
|
||||
function request($newsletter, $subscriber, $extra_params = array()) {
|
||||
$body = array_map('urlencode', $this->getBody($newsletter, $subscriber, $extra_params));
|
||||
return array(
|
||||
'timeout' => 10,
|
||||
'httpversion' => '1.1',
|
||||
@ -96,7 +134,7 @@ class AmazonSES {
|
||||
'Authorization' => $this->signRequest($body),
|
||||
'X-Amz-Date' => $this->date
|
||||
),
|
||||
'body' => urldecode(http_build_query($body))
|
||||
'body' => urldecode(http_build_query($body, null, '&'))
|
||||
);
|
||||
}
|
||||
|
||||
@ -137,7 +175,7 @@ class AmazonSES {
|
||||
'x-amz-date:' . $this->date,
|
||||
'',
|
||||
'host;x-amz-date',
|
||||
hash($this->hash_algorithm, urldecode(http_build_query($body)))
|
||||
hash($this->hash_algorithm, urldecode(http_build_query($body, null, '&')))
|
||||
));
|
||||
}
|
||||
|
||||
@ -176,4 +214,4 @@ class AmazonSES {
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,37 +2,46 @@
|
||||
namespace MailPoet\Mailer\Methods;
|
||||
|
||||
use MailPoet\Mailer\Mailer;
|
||||
use MailPoet\Config\ServicesChecker;
|
||||
use MailPoet\Services\Bridge;
|
||||
use MailPoet\Services\Bridge\API;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class MailPoet {
|
||||
public $url = 'https://bridge.mailpoet.com/api/messages';
|
||||
public $api_key;
|
||||
public $api;
|
||||
public $sender;
|
||||
public $reply_to;
|
||||
public $services_checker;
|
||||
|
||||
function __construct($api_key, $sender, $reply_to) {
|
||||
$this->api_key = $api_key;
|
||||
$this->api = new API($api_key);
|
||||
$this->sender = $sender;
|
||||
$this->reply_to = $reply_to;
|
||||
$this->services_checker = new ServicesChecker(false);
|
||||
}
|
||||
|
||||
function send($newsletter, $subscriber) {
|
||||
$message_body = $this->getBody($newsletter, $subscriber);
|
||||
$result = wp_remote_post(
|
||||
$this->url,
|
||||
$this->request($message_body)
|
||||
);
|
||||
if(is_wp_error($result)) {
|
||||
return Mailer::formatMailerConnectionErrorResult($result->get_error_message());
|
||||
}
|
||||
if(wp_remote_retrieve_response_code($result) !== 201) {
|
||||
$response = (wp_remote_retrieve_body($result)) ?
|
||||
wp_remote_retrieve_body($result) :
|
||||
wp_remote_retrieve_response_message($result);
|
||||
function send($newsletter, $subscriber, $extra_params = array()) {
|
||||
if($this->services_checker->checkMailPoetAPIKeyValid() === false) {
|
||||
$response = __('MailPoet API key is invalid!', 'mailpoet');
|
||||
return Mailer::formatMailerSendErrorResult($response);
|
||||
}
|
||||
return Mailer::formatMailerSendSuccessResult();
|
||||
|
||||
$message_body = $this->getBody($newsletter, $subscriber);
|
||||
$result = $this->api->sendMessages($message_body);
|
||||
|
||||
switch($result['status']) {
|
||||
case API::SENDING_STATUS_CONNECTION_ERROR:
|
||||
return Mailer::formatMailerConnectionErrorResult($result['message']);
|
||||
case API::SENDING_STATUS_SEND_ERROR:
|
||||
if(!empty($result['code']) && $result['code'] === API::RESPONSE_CODE_KEY_INVALID) {
|
||||
Bridge::invalidateKey();
|
||||
}
|
||||
return Mailer::formatMailerSendErrorResult($result['message']);
|
||||
case API::SENDING_STATUS_OK:
|
||||
default:
|
||||
return Mailer::formatMailerSendSuccessResult();
|
||||
}
|
||||
}
|
||||
|
||||
function processSubscriber($subscriber) {
|
||||
@ -49,19 +58,20 @@ class MailPoet {
|
||||
}
|
||||
|
||||
function getBody($newsletter, $subscriber) {
|
||||
$composeBody = function($newsletter, $subscriber) {
|
||||
$_this = $this;
|
||||
$composeBody = function($newsletter, $subscriber) use($_this) {
|
||||
$body = array(
|
||||
'to' => (array(
|
||||
'address' => $subscriber['email'],
|
||||
'name' => $subscriber['name']
|
||||
)),
|
||||
'from' => (array(
|
||||
'address' => $this->sender['from_email'],
|
||||
'name' => $this->sender['from_name']
|
||||
'address' => $_this->sender['from_email'],
|
||||
'name' => $_this->sender['from_name']
|
||||
)),
|
||||
'reply_to' => (array(
|
||||
'address' => $this->reply_to['reply_to_email'],
|
||||
'name' => $this->reply_to['reply_to_name']
|
||||
'address' => $_this->reply_to['reply_to_email'],
|
||||
'name' => $_this->reply_to['reply_to_name']
|
||||
)),
|
||||
'subject' => $newsletter['subject']
|
||||
);
|
||||
@ -86,21 +96,4 @@ class MailPoet {
|
||||
}
|
||||
return $body;
|
||||
}
|
||||
|
||||
function auth() {
|
||||
return 'Basic ' . base64_encode('api:' . $this->api_key);
|
||||
}
|
||||
|
||||
function request($body) {
|
||||
return array(
|
||||
'timeout' => 10,
|
||||
'httpversion' => '1.0',
|
||||
'method' => 'POST',
|
||||
'headers' => array(
|
||||
'Content-Type' => 'application/json',
|
||||
'Authorization' => $this->auth()
|
||||
),
|
||||
'body' => json_encode($body)
|
||||
);
|
||||
}
|
||||
}
|
@ -20,18 +20,22 @@ class PHPMail {
|
||||
$this->mailer = $this->buildMailer();
|
||||
}
|
||||
|
||||
function send($newsletter, $subscriber) {
|
||||
function send($newsletter, $subscriber, $extra_params = array()) {
|
||||
try {
|
||||
$message = $this->createMessage($newsletter, $subscriber);
|
||||
$message = $this->createMessage($newsletter, $subscriber, $extra_params);
|
||||
$result = $this->mailer->send($message);
|
||||
} catch(\Exception $e) {
|
||||
return Mailer::formatMailerSendErrorResult($e->getMessage());
|
||||
}
|
||||
return ($result === 1) ?
|
||||
Mailer::formatMailerSendSuccessResult() :
|
||||
Mailer::formatMailerSendErrorResult(
|
||||
sprintf(__('%s has returned an unknown error.', 'mailpoet'), Mailer::METHOD_PHPMAIL)
|
||||
);
|
||||
if($result === 1) {
|
||||
return Mailer::formatMailerSendSuccessResult();
|
||||
} else {
|
||||
$result = sprintf(__('%s has returned an unknown error.', 'mailpoet'), Mailer::METHOD_PHPMAIL);
|
||||
if(empty($extra_params['test_email'])) {
|
||||
$result .= sprintf(' %s: %s', __('Unprocessed subscriber', 'mailpoet'), $subscriber);
|
||||
}
|
||||
return Mailer::formatMailerSendErrorResult($result);
|
||||
}
|
||||
}
|
||||
|
||||
function buildMailer() {
|
||||
@ -39,7 +43,7 @@ class PHPMail {
|
||||
return \Swift_Mailer::newInstance($transport);
|
||||
}
|
||||
|
||||
function createMessage($newsletter, $subscriber) {
|
||||
function createMessage($newsletter, $subscriber, $extra_params = array()) {
|
||||
$message = \Swift_Message::newInstance()
|
||||
->setTo($this->processSubscriber($subscriber))
|
||||
->setFrom(array(
|
||||
@ -51,6 +55,10 @@ class PHPMail {
|
||||
))
|
||||
->setReturnPath($this->return_path)
|
||||
->setSubject($newsletter['subject']);
|
||||
if(!empty($extra_params['unsubscribe_url'])) {
|
||||
$headers = $message->getHeaders();
|
||||
$headers->addTextHeader('List-Unsubscribe', '<' . $extra_params['unsubscribe_url'] . '>');
|
||||
}
|
||||
if(!empty($newsletter['body']['html'])) {
|
||||
$message = $message->setBody($newsletter['body']['html'], 'text/html');
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ class SMTP {
|
||||
public $reply_to;
|
||||
public $return_path;
|
||||
public $mailer;
|
||||
const SMTP_CONNECTION_TIMEOUT = 10; // seconds
|
||||
|
||||
function __construct(
|
||||
$host, $port, $authentication, $login = null, $password = null, $encryption,
|
||||
@ -32,26 +33,28 @@ class SMTP {
|
||||
$return_path :
|
||||
$this->sender['from_email'];
|
||||
$this->mailer = $this->buildMailer();
|
||||
$this->mailer_logger = new \Swift_Plugins_Loggers_ArrayLogger();
|
||||
$this->mailer->registerPlugin(new \Swift_Plugins_LoggerPlugin($this->mailer_logger));
|
||||
}
|
||||
|
||||
function send($newsletter, $subscriber) {
|
||||
function send($newsletter, $subscriber, $extra_params = array()) {
|
||||
try {
|
||||
$message = $this->createMessage($newsletter, $subscriber);
|
||||
$message = $this->createMessage($newsletter, $subscriber, $extra_params);
|
||||
$result = $this->mailer->send($message);
|
||||
} catch(\Exception $e) {
|
||||
return Mailer::formatMailerSendErrorResult($e->getMessage());
|
||||
return Mailer::formatMailerSendErrorResult(
|
||||
$this->processExceptionMessage($e->getMessage())
|
||||
);
|
||||
}
|
||||
return ($result === 1) ?
|
||||
Mailer::formatMailerSendSuccessResult() :
|
||||
Mailer::formatMailerSendErrorResult(
|
||||
sprintf(__('%s has returned an unknown error.', 'mailpoet'), Mailer::METHOD_SMTP)
|
||||
);
|
||||
Mailer::formatMailerSendErrorResult($this->processLogMessage($subscriber, $extra_params));
|
||||
}
|
||||
|
||||
function buildMailer() {
|
||||
$transport = \Swift_SmtpTransport::newInstance(
|
||||
$this->host, $this->port, $this->encryption);
|
||||
$transport->setTimeout(10);
|
||||
$transport->setTimeout(self::SMTP_CONNECTION_TIMEOUT);
|
||||
if($this->authentication) {
|
||||
$transport
|
||||
->setUsername($this->login)
|
||||
@ -60,18 +63,26 @@ class SMTP {
|
||||
return \Swift_Mailer::newInstance($transport);
|
||||
}
|
||||
|
||||
function createMessage($newsletter, $subscriber) {
|
||||
function createMessage($newsletter, $subscriber, $extra_params = array()) {
|
||||
$message = \Swift_Message::newInstance()
|
||||
->setTo($this->processSubscriber($subscriber))
|
||||
->setFrom(array(
|
||||
->setFrom(
|
||||
array(
|
||||
$this->sender['from_email'] => $this->sender['from_name']
|
||||
))
|
||||
)
|
||||
)
|
||||
->setSender($this->sender['from_email'])
|
||||
->setReplyTo(array(
|
||||
$this->reply_to['reply_to_email'] => $this->reply_to['reply_to_name']
|
||||
))
|
||||
->setReplyTo(
|
||||
array(
|
||||
$this->reply_to['reply_to_email'] => $this->reply_to['reply_to_name']
|
||||
)
|
||||
)
|
||||
->setReturnPath($this->return_path)
|
||||
->setSubject($newsletter['subject']);
|
||||
if(!empty($extra_params['unsubscribe_url'])) {
|
||||
$headers = $message->getHeaders();
|
||||
$headers->addTextHeader('List-Unsubscribe', '<' . $extra_params['unsubscribe_url'] . '>');
|
||||
}
|
||||
if(!empty($newsletter['body']['html'])) {
|
||||
$message = $message->setBody($newsletter['body']['html'], 'text/html');
|
||||
}
|
||||
@ -93,4 +104,27 @@ class SMTP {
|
||||
(isset($subscriber_data['name'])) ? $subscriber_data['name'] : ''
|
||||
);
|
||||
}
|
||||
|
||||
function processLogMessage($subscriber, $extra_params = array(), $log = false) {
|
||||
$log = ($log) ? $log : $this->mailer_logger->dump();
|
||||
// extract error message from log
|
||||
preg_match('/!! (.*?)>>/ism', $log, $message);
|
||||
if(!empty($message[1])) {
|
||||
$message = $message[1];
|
||||
// remove line breaks from the message due to how logger's dump() method works
|
||||
$message = preg_replace('/\r|\n/', '', $message);
|
||||
} else {
|
||||
$message = sprintf(__('%s has returned an unknown error.', 'mailpoet'), Mailer::METHOD_SMTP);
|
||||
}
|
||||
if(empty($extra_params['test_email'])) {
|
||||
$message .= sprintf(' %s: %s', __('Unprocessed subscriber', 'mailpoet'), $subscriber);
|
||||
}
|
||||
return $message;
|
||||
}
|
||||
|
||||
function processExceptionMessage($message) {
|
||||
// remove redundant information appended by Swift logger to exception messages
|
||||
$message = explode(PHP_EOL, $message);
|
||||
return $message[0];
|
||||
}
|
||||
}
|
@ -17,25 +17,28 @@ class SendGrid {
|
||||
$this->reply_to = $reply_to;
|
||||
}
|
||||
|
||||
function send($newsletter, $subscriber) {
|
||||
function send($newsletter, $subscriber, $extra_params = array()) {
|
||||
$result = wp_remote_post(
|
||||
$this->url,
|
||||
$this->request($newsletter, $subscriber)
|
||||
$this->request($newsletter, $subscriber, $extra_params)
|
||||
);
|
||||
if(is_wp_error($result)) {
|
||||
return Mailer::formatMailerConnectionErrorResult($result->get_error_message());
|
||||
}
|
||||
if(wp_remote_retrieve_response_code($result) !== 200) {
|
||||
$response = json_decode($result['body'], true);
|
||||
$response = (!empty($response['errors'])) ?
|
||||
$response['errors'] :
|
||||
$response = (!empty($response['errors'][0])) ?
|
||||
$response['errors'][0] :
|
||||
sprintf(__('%s has returned an unknown error.', 'mailpoet'), Mailer::METHOD_SENDGRID);
|
||||
if(empty($extra_params['test_email'])) {
|
||||
$response .= sprintf(' %s: %s', __('Unprocessed subscriber', 'mailpoet'), $subscriber);
|
||||
}
|
||||
return Mailer::formatMailerSendErrorResult($response);
|
||||
}
|
||||
return Mailer::formatMailerSendSuccessResult();
|
||||
}
|
||||
|
||||
function getBody($newsletter, $subscriber) {
|
||||
function getBody($newsletter, $subscriber, $extra_params = array()) {
|
||||
$body = array(
|
||||
'to' => $subscriber,
|
||||
'from' => $this->sender['from_email'],
|
||||
@ -43,6 +46,13 @@ class SendGrid {
|
||||
'replyto' => $this->reply_to['reply_to_email'],
|
||||
'subject' => $newsletter['subject']
|
||||
);
|
||||
$headers = array();
|
||||
if(!empty($extra_params['unsubscribe_url'])) {
|
||||
$headers['List-Unsubscribe'] = '<' . $extra_params['unsubscribe_url'] . '>';
|
||||
}
|
||||
if($headers) {
|
||||
$body['headers'] = json_encode($headers);
|
||||
}
|
||||
if(!empty($newsletter['body']['html'])) {
|
||||
$body['html'] = $newsletter['body']['html'];
|
||||
}
|
||||
@ -56,8 +66,8 @@ class SendGrid {
|
||||
return 'Bearer ' . $this->api_key;
|
||||
}
|
||||
|
||||
function request($newsletter, $subscriber) {
|
||||
$body = $this->getBody($newsletter, $subscriber);
|
||||
function request($newsletter, $subscriber, $extra_params = array()) {
|
||||
$body = $this->getBody($newsletter, $subscriber, $extra_params);
|
||||
return array(
|
||||
'timeout' => 10,
|
||||
'httpversion' => '1.1',
|
||||
@ -65,7 +75,7 @@ class SendGrid {
|
||||
'headers' => array(
|
||||
'Authorization' => $this->auth()
|
||||
),
|
||||
'body' => http_build_query($body)
|
||||
'body' => http_build_query($body, null, '&')
|
||||
);
|
||||
}
|
||||
}
|
@ -11,10 +11,10 @@ class CustomField extends Model {
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
$this->addValidations('name', array(
|
||||
'required' => __('Please specify a name', 'mailpoet')
|
||||
'required' => __('Please specify a name.', 'mailpoet')
|
||||
));
|
||||
$this->addValidations('type', array(
|
||||
'required' => __('Please specify a type', 'mailpoet')
|
||||
'required' => __('Please specify a type.', 'mailpoet')
|
||||
));
|
||||
}
|
||||
|
||||
@ -142,4 +142,4 @@ class CustomField extends Model {
|
||||
|
||||
return $custom_field->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ class Form extends Model {
|
||||
parent::__construct();
|
||||
|
||||
$this->addValidations('name', array(
|
||||
'required' => __('Please specify a name', 'mailpoet')
|
||||
'required' => __('Please specify a name.', 'mailpoet')
|
||||
));
|
||||
}
|
||||
|
||||
@ -39,6 +39,49 @@ class Form extends Model {
|
||||
return parent::save();
|
||||
}
|
||||
|
||||
function getFieldList() {
|
||||
$form = $this->asArray();
|
||||
if(empty($form['body'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$skipped_types = array('html', 'divider', 'submit');
|
||||
$fields = array();
|
||||
|
||||
foreach((array)$form['body'] as $field) {
|
||||
if(empty($field['id'])
|
||||
|| empty($field['type'])
|
||||
|| in_array($field['type'], $skipped_types)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if($field['id'] > 0) {
|
||||
$fields[] = 'cf_' . $field['id'];
|
||||
} else {
|
||||
$fields[] = $field['id'];
|
||||
}
|
||||
}
|
||||
|
||||
return $fields ?: false;
|
||||
}
|
||||
|
||||
function filterSegments(array $segment_ids = array()) {
|
||||
$form = $this->asArray();
|
||||
if(empty($form['settings']['segments'])) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if(!empty($form['settings']['segments_selected_by'])
|
||||
&& $form['settings']['segments_selected_by'] == 'user'
|
||||
) {
|
||||
$segment_ids = array_intersect($segment_ids, $form['settings']['segments']);
|
||||
} else {
|
||||
$segment_ids = $form['settings']['segments'];
|
||||
}
|
||||
|
||||
return $segment_ids;
|
||||
}
|
||||
|
||||
static function search($orm, $search = '') {
|
||||
return $orm->whereLike('name', '%'.$search.'%');
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ class Model extends \Sudzy\ValidModel {
|
||||
static function bulkTrash($orm) {
|
||||
$model = get_called_class();
|
||||
$count = self::bulkAction($orm, function($ids) use($model) {
|
||||
self::rawExecute(join(' ', array(
|
||||
$model::rawExecute(join(' ', array(
|
||||
'UPDATE `'.$model::$_table.'`',
|
||||
'SET `deleted_at` = NOW()',
|
||||
'WHERE `id` IN ('.rtrim(str_repeat('?,', count($ids)), ',').')'
|
||||
@ -106,7 +106,7 @@ class Model extends \Sudzy\ValidModel {
|
||||
static function bulkRestore($orm) {
|
||||
$model = get_called_class();
|
||||
$count = self::bulkAction($orm, function($ids) use($model) {
|
||||
self::rawExecute(join(' ', array(
|
||||
$model::rawExecute(join(' ', array(
|
||||
'UPDATE `'.$model::$_table.'`',
|
||||
'SET `deleted_at` = NULL',
|
||||
'WHERE `id` IN ('.rtrim(str_repeat('?,', count($ids)), ',').')'
|
||||
|
@ -24,7 +24,7 @@ class Newsletter extends Model {
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
$this->addValidations('type', array(
|
||||
'required' => __('Please specify a type', 'mailpoet')
|
||||
'required' => __('Please specify a type.', 'mailpoet')
|
||||
));
|
||||
}
|
||||
|
||||
@ -593,6 +593,7 @@ class Newsletter extends Model {
|
||||
return self::select(array(
|
||||
'id',
|
||||
'subject',
|
||||
'hash',
|
||||
'type',
|
||||
'status',
|
||||
'updated_at',
|
||||
@ -714,4 +715,23 @@ class Newsletter extends Model {
|
||||
self::NEWSLETTER_HASH_LENGTH
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function restore() {
|
||||
if($this->status == self::STATUS_SENDING) {
|
||||
$this->set('status', self::STATUS_DRAFT);
|
||||
$this->save();
|
||||
}
|
||||
return parent::restore();
|
||||
}
|
||||
|
||||
static function bulkRestore($orm) {
|
||||
parent::bulkAction($orm, function($ids) {
|
||||
Newsletter::whereIn('id', $ids)
|
||||
->where('status', Newsletter::STATUS_SENDING)
|
||||
->findResultSet()
|
||||
->set('status', Newsletter::STATUS_DRAFT)
|
||||
->save();
|
||||
});
|
||||
return parent::bulkRestore($orm);
|
||||
}
|
||||
}
|
||||
|
@ -5,4 +5,20 @@ if(!defined('ABSPATH')) exit;
|
||||
|
||||
class NewsletterOption extends Model {
|
||||
public static $_table = MP_NEWSLETTER_OPTION_TABLE;
|
||||
|
||||
static function createOrUpdate($data = array()) {
|
||||
if(!is_array($data) || empty($data['newsletter_id']) || empty($data['option_field_id'])) return;
|
||||
|
||||
$newsletter_option = self::where('option_field_id', $data['option_field_id'])
|
||||
->where('newsletter_id', $data['newsletter_id'])
|
||||
->findOne();
|
||||
|
||||
if(empty($newsletter_option)) $newsletter_option = self::create();
|
||||
|
||||
$newsletter_option->newsletter_id = $data['newsletter_id'];
|
||||
$newsletter_option->option_field_id = $data['option_field_id'];
|
||||
$newsletter_option->value = $data['value'];
|
||||
$newsletter_option->save();
|
||||
return $newsletter_option;
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,10 @@ class NewsletterOptionField extends Model {
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
$this->addValidations('name', array(
|
||||
'required' => __('Please specify a name', 'mailpoet')
|
||||
'required' => __('Please specify a name.', 'mailpoet')
|
||||
));
|
||||
$this->addValidations('newsletter_type', array(
|
||||
'required' => __('Please specify a newsletter type', 'mailpoet')
|
||||
'required' => __('Please specify a newsletter type.', 'mailpoet')
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -10,10 +10,10 @@ class NewsletterTemplate extends Model {
|
||||
parent::__construct();
|
||||
|
||||
$this->addValidations('name', array(
|
||||
'required' => __('Please specify a name', 'mailpoet')
|
||||
'required' => __('Please specify a name.', 'mailpoet')
|
||||
));
|
||||
$this->addValidations('body', array(
|
||||
'required' => __('The template body cannot be empty', 'mailpoet')
|
||||
'required' => __('The template body cannot be empty.', 'mailpoet')
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ class Segment extends Model {
|
||||
parent::__construct();
|
||||
|
||||
$this->addValidations('name', array(
|
||||
'required' => __('Please specify a name', 'mailpoet')
|
||||
'required' => __('Please specify a name.', 'mailpoet')
|
||||
));
|
||||
}
|
||||
|
||||
@ -76,21 +76,26 @@ class Segment extends Model {
|
||||
'subscribers'
|
||||
)
|
||||
->select_expr(
|
||||
'SUM(CASE subscribers.status WHEN "' . Subscriber::STATUS_SUBSCRIBED . '" THEN 1 ELSE 0 END)',
|
||||
'SUM(CASE WHEN subscribers.status = "' . Subscriber::STATUS_SUBSCRIBED . '"
|
||||
AND relation.status = "' . Subscriber::STATUS_SUBSCRIBED . '" THEN 1 ELSE 0 END)',
|
||||
Subscriber::STATUS_SUBSCRIBED
|
||||
)
|
||||
->select_expr(
|
||||
'SUM(CASE subscribers.status WHEN "' . Subscriber::STATUS_UNSUBSCRIBED . '" THEN 1 ELSE 0 END)',
|
||||
'SUM(CASE WHEN subscribers.status = "' . Subscriber::STATUS_UNSUBSCRIBED . '"
|
||||
OR relation.status = "' . Subscriber::STATUS_UNSUBSCRIBED . '" THEN 1 ELSE 0 END)',
|
||||
Subscriber::STATUS_UNSUBSCRIBED
|
||||
)
|
||||
->select_expr(
|
||||
'SUM(CASE subscribers.status WHEN "' . Subscriber::STATUS_UNCONFIRMED . '" THEN 1 ELSE 0 END)',
|
||||
'SUM(CASE WHEN subscribers.status = "' . Subscriber::STATUS_UNCONFIRMED . '"
|
||||
AND relation.status != "' . Subscriber::STATUS_UNSUBSCRIBED . '" THEN 1 ELSE 0 END)',
|
||||
Subscriber::STATUS_UNCONFIRMED
|
||||
)
|
||||
->select_expr(
|
||||
'SUM(CASE subscribers.status WHEN "' . Subscriber::STATUS_BOUNCED . '" THEN 1 ELSE 0 END)',
|
||||
'SUM(CASE WHEN subscribers.status = "' . Subscriber::STATUS_BOUNCED . '"
|
||||
AND relation.status != "' . Subscriber::STATUS_UNSUBSCRIBED . '" THEN 1 ELSE 0 END)',
|
||||
Subscriber::STATUS_BOUNCED
|
||||
)
|
||||
->whereNull('subscribers.deleted_at')
|
||||
->findOne()
|
||||
->asArray();
|
||||
|
||||
@ -106,7 +111,7 @@ class Segment extends Model {
|
||||
$wp_segment->hydrate(array(
|
||||
'name' => __('WordPress Users', 'mailpoet'),
|
||||
'description' =>
|
||||
__('This lists containts all of your WordPress users', 'mailpoet'),
|
||||
__('This list contains all of your WordPress users.', 'mailpoet'),
|
||||
'type' => 'wp_users'
|
||||
));
|
||||
$wp_segment->save();
|
||||
@ -227,8 +232,8 @@ class Segment extends Model {
|
||||
|
||||
static function bulkTrash($orm) {
|
||||
$count = parent::bulkAction($orm, function($ids) {
|
||||
parent::rawExecute(join(' ', array(
|
||||
'UPDATE `'.self::$_table.'`',
|
||||
Segment::rawExecute(join(' ', array(
|
||||
'UPDATE `' . Segment::$_table . '`',
|
||||
'SET `deleted_at` = NOW()',
|
||||
'WHERE `id` IN ('.rtrim(str_repeat('?,', count($ids)), ',').')',
|
||||
'AND `type` = "default"'
|
||||
|
@ -19,7 +19,7 @@ class Setting extends Model {
|
||||
parent::__construct();
|
||||
|
||||
$this->addValidations('name', array(
|
||||
'required' => __('Please specify a name', 'mailpoet')
|
||||
'required' => __('Please specify a name.', 'mailpoet')
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -280,7 +280,9 @@ class Subscriber extends Model {
|
||||
'value' => ''
|
||||
);
|
||||
|
||||
$subscribers_without_segment = self::filter('withoutSegments')->count();
|
||||
$subscribers_without_segment = self::filter('withoutSegments')
|
||||
->whereNull('deleted_at')
|
||||
->count();
|
||||
$subscribers_without_segment_label = sprintf(
|
||||
__('Subscribers without a list (%s)', 'mailpoet'),
|
||||
number_format($subscribers_without_segment)
|
||||
@ -446,8 +448,10 @@ class Subscriber extends Model {
|
||||
'subscribers.id = relation.subscriber_id',
|
||||
'subscribers'
|
||||
)
|
||||
->select('subscribers.id')
|
||||
->whereNull('subscribers.deleted_at')
|
||||
->where('subscribers.status', 'subscribed');
|
||||
->where('subscribers.status', 'subscribed')
|
||||
->distinct();
|
||||
return $subscribers;
|
||||
}
|
||||
|
||||
@ -485,6 +489,17 @@ class Subscriber extends Model {
|
||||
unset($data['segments']);
|
||||
}
|
||||
|
||||
// fields that must exist
|
||||
$not_null_fields = array(
|
||||
'first_name' => '',
|
||||
'last_name' => ''
|
||||
);
|
||||
foreach($not_null_fields as $field => $value) {
|
||||
if(!isset($data[$field])) {
|
||||
$data[$field] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// custom fields
|
||||
$custom_fields = array();
|
||||
foreach($data as $key => $value) {
|
||||
@ -716,8 +731,8 @@ class Subscriber extends Model {
|
||||
|
||||
static function bulkTrash($orm) {
|
||||
$count = parent::bulkAction($orm, function($subscriber_ids) {
|
||||
self::rawExecute(join(' ', array(
|
||||
'UPDATE `'.self::$_table.'`',
|
||||
Subscriber::rawExecute(join(' ', array(
|
||||
'UPDATE `' . Subscriber::$_table . '`',
|
||||
'SET `deleted_at` = NOW()',
|
||||
'WHERE `id` IN ('.
|
||||
rtrim(str_repeat('?,', count($subscriber_ids)), ',')
|
||||
@ -735,7 +750,8 @@ class Subscriber extends Model {
|
||||
$count = parent::bulkAction($orm, function($subscriber_ids) {
|
||||
// delete all subscriber/segment relationships
|
||||
SubscriberSegment::deleteManySubscriptions($subscriber_ids);
|
||||
|
||||
// delete all subscriber/custom field relationships
|
||||
SubscriberCustomField::deleteManySubscriberRelations($subscriber_ids);
|
||||
// delete subscribers (except WP Users)
|
||||
Subscriber::whereIn('id', $subscriber_ids)
|
||||
->whereNull('wp_user_id')
|
||||
@ -771,13 +787,18 @@ class Subscriber extends Model {
|
||||
|
||||
static function withoutSegments($orm) {
|
||||
return $orm->select(MP_SUBSCRIBERS_TABLE.'.*')
|
||||
->leftOuterJoin(
|
||||
MP_SUBSCRIBER_SEGMENT_TABLE,
|
||||
->rawJoin(
|
||||
'LEFT OUTER JOIN (
|
||||
SELECT `subscriber_id`
|
||||
FROM '.MP_SUBSCRIBER_SEGMENT_TABLE.'
|
||||
WHERE `status` = "'.self::STATUS_SUBSCRIBED.'"
|
||||
)',
|
||||
array(
|
||||
MP_SUBSCRIBERS_TABLE.'.id',
|
||||
'=',
|
||||
MP_SUBSCRIBER_SEGMENT_TABLE.'.subscriber_id'
|
||||
)
|
||||
),
|
||||
MP_SUBSCRIBER_SEGMENT_TABLE
|
||||
)
|
||||
->whereNull(MP_SUBSCRIBER_SEGMENT_TABLE.'.subscriber_id');
|
||||
}
|
||||
@ -847,4 +868,18 @@ class Subscriber extends Model {
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static function findSubscribersInSegments(array $subscribers_ids, array $segments_ids) {
|
||||
return self::getSubscribedInSegments($segments_ids)
|
||||
->whereIn('subscribers.id', $subscribers_ids)
|
||||
->select('subscribers.*');
|
||||
}
|
||||
|
||||
static function extractSubscribersIds(array $subscribers) {
|
||||
return array_filter(
|
||||
array_map(function($subscriber) {
|
||||
return (!empty($subscriber->id)) ? $subscriber->id : false;
|
||||
}, $subscribers)
|
||||
);
|
||||
}
|
||||
}
|
@ -89,4 +89,10 @@ class SubscriberCustomField extends Model {
|
||||
$relations = self::where('subscriber_id', $subscriber->id);
|
||||
return $relations->deleteMany();
|
||||
}
|
||||
|
||||
static function deleteManySubscriberRelations(array $subscriber_ids) {
|
||||
if(empty($subscriber_ids)) return false;
|
||||
$relations = self::whereIn('subscriber_id', $subscriber_ids);
|
||||
return $relations->deleteMany();
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
<?php
|
||||
namespace MailPoet\Models;
|
||||
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -54,6 +53,15 @@ class SubscriberSegment extends Model {
|
||||
return true;
|
||||
}
|
||||
|
||||
static function resubscribeToAllSegments($subscriber) {
|
||||
if($subscriber === false) return false;
|
||||
// (re)subscribe to all segments linked to the subscriber
|
||||
return self::where('subscriber_id', $subscriber->id)
|
||||
->findResultSet()
|
||||
->set('status', Subscriber::STATUS_SUBSCRIBED)
|
||||
->save();
|
||||
}
|
||||
|
||||
static function subscribeToSegments($subscriber, $segment_ids = array()) {
|
||||
if($subscriber === false) return false;
|
||||
if(!empty($segment_ids)) {
|
||||
@ -68,12 +76,6 @@ class SubscriberSegment extends Model {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
// (re)subscribe to all segments linked to the subscriber
|
||||
return self::where('subscriber_id', $subscriber->id)
|
||||
->findResultSet()
|
||||
->set('status', Subscriber::STATUS_SUBSCRIBED)
|
||||
->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
namespace MailPoet\Newsletter;
|
||||
|
||||
use MailPoet\Newsletter\Editor\Transformer;
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -43,7 +43,7 @@ class MetaInformationManager {
|
||||
// Get categories
|
||||
$categories = wp_get_post_terms(
|
||||
$post_id,
|
||||
get_object_taxonomies($post_type),
|
||||
array('post_tag', 'category'),
|
||||
array('fields' => 'names')
|
||||
);
|
||||
if(!empty($categories)) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Editor;
|
||||
|
||||
use \MailPoet\Newsletter\Editor\PostTransformer;
|
||||
use MailPoet\Newsletter\Editor\PostTransformer;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Editor;
|
||||
|
||||
use \MailPoet\Newsletter\Editor\PostContentManager;
|
||||
use \MailPoet\Newsletter\Editor\MetaInformationManager;
|
||||
use \MailPoet\Newsletter\Editor\StructureTransformer;
|
||||
use MailPoet\Newsletter\Editor\PostContentManager;
|
||||
use MailPoet\Newsletter\Editor\MetaInformationManager;
|
||||
use MailPoet\Newsletter\Editor\StructureTransformer;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Editor;
|
||||
|
||||
use \pQuery;
|
||||
use pQuery;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Editor;
|
||||
|
||||
use \MailPoet\Newsletter\Editor\TitleListTransformer;
|
||||
use \MailPoet\Newsletter\Editor\PostListTransformer;
|
||||
use MailPoet\Newsletter\Editor\TitleListTransformer;
|
||||
use MailPoet\Newsletter\Editor\PostListTransformer;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
|
@ -19,7 +19,7 @@ class Button {
|
||||
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) . '%"
|
||||
arcsize="' . round((int)$element['styles']['block']['borderRadius'] / (int)$element['styles']['block']['lineHeight'] * 100) . '%"
|
||||
strokeweight="' . $element['styles']['block']['borderWidth'] . '"
|
||||
strokecolor="' . $element['styles']['block']['borderColor'] . '"
|
||||
fillcolor="' . $element['styles']['block']['backgroundColor'] . '">
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user