Compare commits
106 Commits
Author | SHA1 | Date | |
---|---|---|---|
daff3d5016 | |||
f13a4dd4f3 | |||
04238f3339 | |||
bc62474f3c | |||
98005a2a6f | |||
b7fe8dc6d6 | |||
436faea591 | |||
4208b148b4 | |||
5ce5f0bf8a | |||
18518de397 | |||
68f747211a | |||
ebd26ddd98 | |||
924aa0439f | |||
3124d6a61b | |||
149d031b52 | |||
fa96c4697d | |||
d46c9d5412 | |||
9425ac1593 | |||
4e32479609 | |||
7d95b38dc4 | |||
17c83c5bd4 | |||
23bb2f111f | |||
4655b2c64c | |||
84fede11b8 | |||
f37488fc0b | |||
20e2e03982 | |||
a5d96f1534 | |||
a516eb1a95 | |||
6dd8270bec | |||
981cbd579f | |||
52a0aae10f | |||
ebca4257a6 | |||
c3944095bc | |||
a1445d1b6a | |||
e62c24879b | |||
00f06ea202 | |||
32ca24ce38 | |||
44e3adb422 | |||
a1346ebecc | |||
25b51d0446 | |||
556a170903 | |||
7ac386a8e2 | |||
d91b55ec52 | |||
786fbc36a2 | |||
160e8b7a12 | |||
0b1f85da09 | |||
fbc6f54ddc | |||
a603e97b8c | |||
0875b627b6 | |||
035784ece0 | |||
aa93c7349f | |||
82cf4a28fd | |||
832e5ef342 | |||
e3de3a123a | |||
db91590159 | |||
28c61fca0b | |||
e62a8c2ec5 | |||
fdbd1245e3 | |||
0eef46db57 | |||
080ae88a04 | |||
225be9f3cd | |||
c9a42ebb76 | |||
ae9b3df92d | |||
63a08ebb55 | |||
2ef5096fa9 | |||
95cfe2d8ec | |||
49676b3fc5 | |||
c96ac06423 | |||
6a3166c311 | |||
0017df1c2d | |||
f2a0d4ce96 | |||
cb50517cbc | |||
0fedd1779f | |||
1625e1771b | |||
bde78b607b | |||
f3c58c27be | |||
9fec460295 | |||
162859529e | |||
3bdaaf8368 | |||
f6ab0050b2 | |||
10a20935c3 | |||
8135b677f7 | |||
90382bc86d | |||
47af8939cc | |||
4a0deb2182 | |||
3a206b2c88 | |||
70cfcbe7cc | |||
6342cb17bd | |||
64c35b12c8 | |||
6a14f97419 | |||
dfec34eda9 | |||
893231e8e5 | |||
e9110680ee | |||
7b54285ca6 | |||
33ea16eb0f | |||
b1ae07d38e | |||
3f168d052f | |||
158d26ef86 | |||
ae03ee2c46 | |||
ad0adb48e1 | |||
ff5353c894 | |||
abb2389378 | |||
3cf50810f9 | |||
20a6e6d6de | |||
0b1fc8f6c3 | |||
0a771acb02 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -15,4 +15,5 @@ temp
|
|||||||
wysija-newsletters.zip
|
wysija-newsletters.zip
|
||||||
tests/javascript/testBundles
|
tests/javascript/testBundles
|
||||||
assets/css/*.css
|
assets/css/*.css
|
||||||
assets/js/*.js
|
assets/js/*.js
|
||||||
|
.vagrant
|
||||||
|
11
RoboFile.php
11
RoboFile.php
@ -47,6 +47,10 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
->run();
|
->run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function watchJs() {
|
||||||
|
$this->_exec('./node_modules/webpack/bin/webpack.js --watch');
|
||||||
|
}
|
||||||
|
|
||||||
function compileAll() {
|
function compileAll() {
|
||||||
$this->compileJs();
|
$this->compileJs();
|
||||||
$this->compileCss();
|
$this->compileCss();
|
||||||
@ -61,7 +65,8 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
'assets/css/src/admin.styl',
|
'assets/css/src/admin.styl',
|
||||||
'assets/css/src/newsletter_editor/newsletter_editor.styl',
|
'assets/css/src/newsletter_editor/newsletter_editor.styl',
|
||||||
'assets/css/src/public.styl',
|
'assets/css/src/public.styl',
|
||||||
'assets/css/src/rtl.styl'
|
'assets/css/src/rtl.styl',
|
||||||
|
'assets/css/src/importExport.styl'
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->_exec(join(' ', array(
|
$this->_exec(join(' ', array(
|
||||||
@ -91,7 +96,7 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
function testUnit($file = null) {
|
function testUnit($file = null) {
|
||||||
$this->loadEnv();
|
$this->loadEnv();
|
||||||
$this->_exec('vendor/bin/codecept build');
|
$this->_exec('vendor/bin/codecept build');
|
||||||
$this->_exec('vendor/bin/codecept run unit -f '.(($file) ? $file : ''));
|
$this->_exec('vendor/bin/codecept run unit '.(($file) ? $file : ''));
|
||||||
}
|
}
|
||||||
|
|
||||||
function testJavascript() {
|
function testJavascript() {
|
||||||
@ -120,4 +125,4 @@ class RoboFile extends \Robo\Tasks {
|
|||||||
$dotenv = new Dotenv\Dotenv(__DIR__);
|
$dotenv = new Dotenv\Dotenv(__DIR__);
|
||||||
$dotenv->load();
|
$dotenv->load();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,6 +18,38 @@
|
|||||||
height: 150px
|
height: 150px
|
||||||
margin-right: 15px
|
margin-right: 15px
|
||||||
float: left
|
float: left
|
||||||
|
overflow: hidden
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
img
|
||||||
|
min-width: 150px
|
||||||
|
min-height: 150px
|
||||||
|
height: auto
|
||||||
|
width: 110%
|
||||||
|
position: relative
|
||||||
|
top: 0
|
||||||
|
left: 50%
|
||||||
|
transform: translate(-50%, 0%)
|
||||||
|
|
||||||
|
.mailpoet_overlay
|
||||||
|
position: absolute
|
||||||
|
top: 0
|
||||||
|
left: 0
|
||||||
|
right: 0
|
||||||
|
bottom: 0
|
||||||
|
background-color: rgba(255, 255, 255, 0.0)
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background-color: rgba(255, 255, 255, 0.7)
|
||||||
|
|
||||||
|
&::after
|
||||||
|
content: " "
|
||||||
|
position: absolute
|
||||||
|
top: 0
|
||||||
|
left: 0
|
||||||
|
bottom: 0
|
||||||
|
right: 0
|
||||||
|
background: url(../img/preview_magnifying_glass.svg) no-repeat center center
|
||||||
|
|
||||||
.mailpoet_boxes .mailpoet_description
|
.mailpoet_boxes .mailpoet_description
|
||||||
float:left
|
float:left
|
||||||
|
78
assets/css/src/importExport.styl
Normal file
78
assets/css/src/importExport.styl
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
.mailpoet_hidden, .mailpoet_validation_error
|
||||||
|
display none
|
||||||
|
|
||||||
|
.form-table
|
||||||
|
th
|
||||||
|
width 300px
|
||||||
|
|
||||||
|
#paste_input
|
||||||
|
width 100%
|
||||||
|
|
||||||
|
input[type="radio"]
|
||||||
|
margin-right 0.5em !important
|
||||||
|
& + span
|
||||||
|
margin-right 2.5em
|
||||||
|
|
||||||
|
span
|
||||||
|
&.mailpoet_mailchimp-key-status
|
||||||
|
&.mailpoet_mailchimp-ok
|
||||||
|
&:before
|
||||||
|
content "\2713"
|
||||||
|
color #0e90d2
|
||||||
|
margin-left 15px
|
||||||
|
&.mailpoet_mailchimp-error
|
||||||
|
&:before
|
||||||
|
content "\2717"
|
||||||
|
color #900
|
||||||
|
margin-left 15px
|
||||||
|
|
||||||
|
#subscribers_data
|
||||||
|
overflow auto
|
||||||
|
table
|
||||||
|
width auto
|
||||||
|
td
|
||||||
|
padding 0.5em
|
||||||
|
& > table
|
||||||
|
& > tbody
|
||||||
|
& > td
|
||||||
|
padding 0.5em
|
||||||
|
& > tr
|
||||||
|
&:nth-child(odd)
|
||||||
|
background #f9f9f9
|
||||||
|
.mailpoet_header
|
||||||
|
text-transform uppercase
|
||||||
|
font-weight 600
|
||||||
|
text-decoration underline
|
||||||
|
|
||||||
|
#subscribers_data th:first-child, #subscribers_data td:first-child
|
||||||
|
width 10em !important
|
||||||
|
text-align center !important
|
||||||
|
padding 0 1em 0 1em !important
|
||||||
|
vertical-align inherit !important
|
||||||
|
|
||||||
|
#subscribers_data
|
||||||
|
& > table
|
||||||
|
& > thead
|
||||||
|
& > tr
|
||||||
|
& > th
|
||||||
|
& > span
|
||||||
|
width 15em !important
|
||||||
|
|
||||||
|
.mailpoet_data_match
|
||||||
|
color #0e90d2
|
||||||
|
margin-left 0.25em
|
||||||
|
|
||||||
|
.mailpoet_import_error, .mailpoet_validation_error
|
||||||
|
color #900
|
||||||
|
|
||||||
|
tr
|
||||||
|
&.mailpoet_segments
|
||||||
|
& > td
|
||||||
|
& > a
|
||||||
|
margin-left 15px
|
||||||
|
|
||||||
|
span
|
||||||
|
&.select2-search
|
||||||
|
&.select2-search--dropdown
|
||||||
|
display none !important
|
||||||
|
|
@ -159,23 +159,6 @@ body.mailpoet_modal_opened
|
|||||||
margin: 0
|
margin: 0
|
||||||
text-align: right
|
text-align: right
|
||||||
|
|
||||||
.mailpoet_button
|
|
||||||
padding: 3px 15px
|
|
||||||
border: 1px solid #444
|
|
||||||
font-weight: normal
|
|
||||||
cursor: pointer
|
|
||||||
background-color: #222
|
|
||||||
color: #cfcfcf
|
|
||||||
font-size: 1em
|
|
||||||
|
|
||||||
.mailpoet_button:hover
|
|
||||||
background-color: #00aacc
|
|
||||||
color: #fff
|
|
||||||
|
|
||||||
.mailpoet_button:active
|
|
||||||
background-color: #00ccff
|
|
||||||
color: #fff
|
|
||||||
|
|
||||||
@media screen and (max-width: 782px)
|
@media screen and (max-width: 782px)
|
||||||
#mailpoet_modal_overlay.mailpoet_panel_overlay
|
#mailpoet_modal_overlay.mailpoet_panel_overlay
|
||||||
top: 46px
|
top: 46px
|
||||||
|
@ -19,6 +19,10 @@ $divider-hover-border-color = $primary-active-color
|
|||||||
width: 100%
|
width: 100%
|
||||||
border: 1px solid transparent
|
border: 1px solid transparent
|
||||||
|
|
||||||
|
.mailpoet_active_divider_style
|
||||||
|
border: 1px solid $active-divider-border-color
|
||||||
|
background: $active-divider-background-color
|
||||||
|
|
||||||
.mailpoet_field_divider_style:hover
|
.mailpoet_field_divider_style:hover
|
||||||
border: 1px solid $divider-hover-border-color
|
border: 1px solid $divider-hover-border-color
|
||||||
|
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
.mailpoet_image_block
|
.mailpoet_image_block
|
||||||
|
|
||||||
|
img
|
||||||
|
vertical-align: bottom
|
||||||
|
max-width: 100%
|
||||||
|
width: auto
|
||||||
|
height: auto
|
||||||
|
|
||||||
&.mailpoet_full_image
|
&.mailpoet_full_image
|
||||||
padding-left: 0
|
padding-left: 0
|
||||||
padding-right: 0
|
padding-right: 0
|
||||||
margin-bottom: 0
|
margin-bottom: 0
|
||||||
|
|
||||||
|
img
|
||||||
|
width: 100%
|
||||||
|
|
||||||
.mailpoet_content a:hover
|
.mailpoet_content a:hover
|
||||||
cursor: all-scroll
|
cursor: all-scroll
|
||||||
|
|
||||||
img
|
|
||||||
vertical-align: bottom
|
|
||||||
max-width: $newsletter-width
|
|
||||||
width: 100%
|
|
||||||
height: auto
|
|
||||||
|
@ -1,15 +1,12 @@
|
|||||||
.mailpoet_posts_block
|
.mailpoet_posts_block
|
||||||
box-shadow(none)
|
padding-left: 0
|
||||||
|
padding-right: 0
|
||||||
|
|
||||||
& > .mailpoet_content
|
.mailpoet_posts_block_posts
|
||||||
font-size: 1em
|
overflow: auto
|
||||||
text-align: center
|
|
||||||
background-color: $primary-active-color
|
& > .mailpoet_block
|
||||||
margin: 20px 0
|
width: 100%
|
||||||
padding: 15px
|
|
||||||
box-shadow(inset 1px 2px 1px $primary-inset-shadow-color)
|
|
||||||
color: $white-color
|
|
||||||
border-radius(3px)
|
|
||||||
|
|
||||||
.mailpoet_post_selection_filter_row
|
.mailpoet_post_selection_filter_row
|
||||||
margin-top: 5px
|
margin-top: 5px
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/* Fix select2 z-index to work with MailPoet.Modal */
|
/* Fix select2 z-index to work with MailPoet.Modal */
|
||||||
.select2-dropdown
|
.select2-dropdown
|
||||||
z-index: 101000
|
z-index: 101000 !important
|
||||||
|
|
||||||
/* Remove input field styles from select2 type input */
|
/* Remove input field styles from select2 type input */
|
||||||
.select2-container
|
.select2-container
|
||||||
|
12
assets/img/preview_magnifying_glass.svg
Normal file
12
assets/img/preview_magnifying_glass.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="47.002px" height="38.003px" viewBox="0 0 47.002 38.003" enable-background="new 0 0 47.002 38.003" xml:space="preserve">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" fill="#565656" d="M46.328,36.365c-1.188,1.725-3.553,2.158-5.273,0.962L25.479,26.52
|
||||||
|
c-1.104-0.763-1.674-2.007-1.631-3.257c-2.006,2.157-4.642,3.606-7.594,4.145c-3.626,0.663-7.288-0.13-10.311-2.227
|
||||||
|
c-3.024-2.098-5.054-5.253-5.714-8.887c-0.661-3.636,0.127-7.31,2.221-10.344c4.325-6.264,12.927-7.834,19.177-3.5
|
||||||
|
c5.672,3.938,7.486,11.412,4.537,17.443c1.152-0.486,2.519-0.392,3.627,0.377l15.58,10.808C47.09,32.274,47.52,34.641,46.328,36.365
|
||||||
|
z M23.235,12.09c-0.459-2.534-1.874-4.734-3.982-6.196C14.897,2.87,8.896,3.963,5.878,8.331c-3.014,4.373-1.922,10.388,2.435,13.408
|
||||||
|
c4.356,3.025,10.356,1.932,13.374-2.438C23.146,17.187,23.696,14.625,23.235,12.09z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/img/sample_template/coffee-grain.jpg
Normal file
BIN
assets/img/sample_template/coffee-grain.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 178 KiB |
BIN
assets/img/sample_template/header-v2.jpg
Normal file
BIN
assets/img/sample_template/header-v2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
BIN
assets/img/sample_template/map-v2.jpg
Normal file
BIN
assets/img/sample_template/map-v2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 130 KiB |
BIN
assets/img/sample_template/sandwich.jpg
Normal file
BIN
assets/img/sample_template/sandwich.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
8
assets/js/lib/analytics.js
Normal file
8
assets/js/lib/analytics.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable time_event track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.union people.track_charge people.clear_charges people.delete_user".split(" ");
|
||||||
|
for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"file:"===e.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f)}})(document,window.mixpanel||[]);
|
||||||
|
|
||||||
|
mixpanel.init("f683d388fb25fcf331f1b2b5c4449798");
|
||||||
|
|
||||||
|
if (typeof mailpoet_analytics_data === 'object') {
|
||||||
|
mixpanel.track('Wysija Usage', mailpoet_analytics_data || {});
|
||||||
|
}
|
@ -20,7 +20,17 @@ function(
|
|||||||
this.loadCachedItems();
|
this.loadCachedItems();
|
||||||
this.setupSelect2();
|
this.setupSelect2();
|
||||||
},
|
},
|
||||||
componentDidUpdate: function() {
|
componentDidUpdate: function(prevProps, prevState) {
|
||||||
|
if(
|
||||||
|
(this.props.item !== undefined && prevProps.item !== undefined)
|
||||||
|
&& (this.props.item.id !== prevProps.item.id)
|
||||||
|
) {
|
||||||
|
jQuery('#'+this.refs.select.id).select2(
|
||||||
|
'val',
|
||||||
|
this.props.item[this.props.field.name]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
this.setupSelect2();
|
this.setupSelect2();
|
||||||
},
|
},
|
||||||
setupSelect2: function() {
|
setupSelect2: function() {
|
||||||
@ -54,6 +64,11 @@ function(
|
|||||||
loadCachedItems: function() {
|
loadCachedItems: function() {
|
||||||
if(typeof(window['mailpoet_'+this.props.field.endpoint]) !== 'undefined') {
|
if(typeof(window['mailpoet_'+this.props.field.endpoint]) !== 'undefined') {
|
||||||
var items = window['mailpoet_'+this.props.field.endpoint];
|
var items = window['mailpoet_'+this.props.field.endpoint];
|
||||||
|
|
||||||
|
if(this.props.field['filter'] !== undefined) {
|
||||||
|
items = items.filter(this.props.field.filter);
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
items: items
|
items: items
|
||||||
});
|
});
|
||||||
@ -76,37 +91,33 @@ function(
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
if(this.state.items.length === 0) {
|
var options = this.state.items.map(function(item, index) {
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
var options = this.state.items.map(function(item, index) {
|
|
||||||
return (
|
|
||||||
<option
|
|
||||||
key={ item.id }
|
|
||||||
value={ item.id }
|
|
||||||
>
|
|
||||||
{ item.name }
|
|
||||||
</option>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
var default_value = (
|
|
||||||
(this.props.item !== undefined && this.props.field.name !== undefined)
|
|
||||||
? this.props.item[this.props.field.name]
|
|
||||||
: null
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<select
|
<option
|
||||||
id={ this.props.field.id || this.props.field.name }
|
key={ item.id }
|
||||||
ref="select"
|
value={ item.id }
|
||||||
placeholder={ this.props.field.placeholder }
|
>
|
||||||
multiple={ this.props.field.multiple }
|
{ item.name }
|
||||||
onChange={ this.handleChange }
|
</option>
|
||||||
defaultValue={ default_value }
|
|
||||||
>{ options }</select>
|
|
||||||
);
|
);
|
||||||
}
|
});
|
||||||
|
|
||||||
|
var default_value = (
|
||||||
|
(this.props.item !== undefined && this.props.field.name !== undefined)
|
||||||
|
? this.props.item[this.props.field.name]
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<select
|
||||||
|
id={ this.props.field.id || this.props.field.name }
|
||||||
|
ref="select"
|
||||||
|
placeholder={ this.props.field.placeholder }
|
||||||
|
multiple={ this.props.field.multiple }
|
||||||
|
onChange={ this.handleChange }
|
||||||
|
defaultValue={ default_value }
|
||||||
|
>{ options }</select>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -88,23 +88,23 @@ define(
|
|||||||
}).done(function(response) {
|
}).done(function(response) {
|
||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
|
|
||||||
if(response === true) {
|
if(response.result === true) {
|
||||||
if(this.props.onSuccess !== undefined) {
|
if(this.props.onSuccess !== undefined) {
|
||||||
this.props.onSuccess()
|
this.props.onSuccess();
|
||||||
} else {
|
} else {
|
||||||
this.history.pushState(null, '/')
|
this.history.pushState(null, '/')
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.props.params.id !== undefined) {
|
if(this.props.params.id !== undefined) {
|
||||||
this.props.messages['updated']();
|
this.props.messages.onUpdate();
|
||||||
} else {
|
} else {
|
||||||
this.props.messages['created']();
|
this.props.messages.onCreate();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if(response === false) {
|
if(response.result === false) {
|
||||||
// unknown error occurred
|
if(response.errors.length > 0) {
|
||||||
} else {
|
this.setState({ errors: response.errors });
|
||||||
this.setState({ errors: response });
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
@ -121,13 +121,15 @@ define(
|
|||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
var errors = this.state.errors.map(function(error, index) {
|
if(this.state.errors !== undefined) {
|
||||||
return (
|
var errors = this.state.errors.map(function(error, index) {
|
||||||
<p key={ 'error-'+index } className="mailpoet_error">
|
return (
|
||||||
{ error }
|
<p key={ 'error-'+index } className="mailpoet_error">
|
||||||
</p>
|
{ error }
|
||||||
);
|
</p>
|
||||||
});
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var formClasses = classNames(
|
var formClasses = classNames(
|
||||||
'mailpoet_form',
|
'mailpoet_form',
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import ReactDOM from 'react-dom'
|
|
||||||
import { Router, History } from 'react-router'
|
|
||||||
import MailPoet from 'mailpoet'
|
|
||||||
import Form from 'form/form.jsx'
|
|
||||||
|
|
||||||
const fields = [
|
|
||||||
{
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
type: 'text'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'segments',
|
|
||||||
label: 'Lists',
|
|
||||||
type: 'selection',
|
|
||||||
endpoint: 'segments',
|
|
||||||
multiple: true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const messages = {
|
|
||||||
updated: function() {
|
|
||||||
MailPoet.Notice.success('Form successfully updated!');
|
|
||||||
},
|
|
||||||
created: function() {
|
|
||||||
MailPoet.Notice.success('Form successfully added!');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const FormForm = React.createClass({
|
|
||||||
mixins: [
|
|
||||||
History
|
|
||||||
],
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2 className="title">
|
|
||||||
Form <a
|
|
||||||
href="javascript:;"
|
|
||||||
className="add-new-h2"
|
|
||||||
onClick={ this.history.goBack }
|
|
||||||
>Back to list</a>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<Form
|
|
||||||
endpoint="forms"
|
|
||||||
fields={ fields }
|
|
||||||
params={ this.props.params }
|
|
||||||
messages={ messages }
|
|
||||||
onSuccess={ this.history.goBack } />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = FormForm
|
|
@ -2,7 +2,6 @@ import React from 'react'
|
|||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import { Router, Route, IndexRoute } from 'react-router'
|
import { Router, Route, IndexRoute } from 'react-router'
|
||||||
import FormList from 'forms/list.jsx'
|
import FormList from 'forms/list.jsx'
|
||||||
import FormForm from 'forms/form.jsx'
|
|
||||||
import createHashHistory from 'history/lib/createHashHistory'
|
import createHashHistory from 'history/lib/createHashHistory'
|
||||||
|
|
||||||
let history = createHashHistory({ queryKey: false })
|
let history = createHashHistory({ queryKey: false })
|
||||||
@ -20,8 +19,6 @@ if(container) {
|
|||||||
<Router history={ history }>
|
<Router history={ history }>
|
||||||
<Route path="/" component={ App }>
|
<Route path="/" component={ App }>
|
||||||
<IndexRoute component={ FormList } />
|
<IndexRoute component={ FormList } />
|
||||||
<Route path="new" component={ FormForm } />
|
|
||||||
<Route path="edit/:id" component={ FormForm } />
|
|
||||||
<Route path="*" component={ FormList } />
|
<Route path="*" component={ FormList } />
|
||||||
</Route>
|
</Route>
|
||||||
</Router>
|
</Router>
|
||||||
|
@ -105,6 +105,9 @@ const item_actions = [
|
|||||||
refresh();
|
refresh();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'trash'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
3
assets/js/src/helpscout.js
Normal file
3
assets/js/src/helpscout.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
define([], function() {
|
||||||
|
!function(e,o,n){window.HSCW=o,window.HS=n,n.beacon=n.beacon||{};var t=n.beacon;t.userConfig={},t.readyQueue=[],t.config=function(e){this.userConfig=e},t.ready=function(e){this.readyQueue.push(e)},o.config={docs:{enabled:!1,baseUrl:""},contact:{enabled:!0,formId:"e5c408c7-895e-11e5-9e75-0a7d6919297d"}};var r=e.getElementsByTagName("script")[0],c=e.createElement("script");c.type="text/javascript",c.async=!0,c.src="https://djtflbt20bdde.cloudfront.net/",r.parentNode.insertBefore(c,r)}(document,window.HSCW||{},window.HS||{});
|
||||||
|
});
|
@ -15,16 +15,16 @@ function(
|
|||||||
this.setState({
|
this.setState({
|
||||||
action: e.target.value,
|
action: e.target.value,
|
||||||
extra: false
|
extra: false
|
||||||
});
|
}, function() {
|
||||||
|
var action = this.getSelectedAction();
|
||||||
|
|
||||||
var action = this.getSelectedAction();
|
// action on select callback
|
||||||
|
if(action !== null && action['onSelect'] !== undefined) {
|
||||||
// action on select callback
|
this.setState({
|
||||||
if(action !== null && action['onSelect'] !== undefined) {
|
extra: action.onSelect(e)
|
||||||
this.setState({
|
});
|
||||||
extra: action.onSelect(e)
|
}
|
||||||
});
|
}.bind(this));
|
||||||
}
|
|
||||||
},
|
},
|
||||||
handleApplyAction: function(e) {
|
handleApplyAction: function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -85,7 +85,12 @@ function(
|
|||||||
Select bulk action
|
Select bulk action
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<select ref="action" value={ this.state.action } onChange={this.handleChangeAction}>
|
<select
|
||||||
|
name="bulk_actions"
|
||||||
|
ref="action"
|
||||||
|
value={ this.state.action }
|
||||||
|
onChange={this.handleChangeAction}
|
||||||
|
>
|
||||||
<option value="">Bulk Actions</option>
|
<option value="">Bulk Actions</option>
|
||||||
{ this.props.bulk_actions.map(function(action, index) {
|
{ this.props.bulk_actions.map(function(action, index) {
|
||||||
return (
|
return (
|
||||||
|
@ -11,7 +11,7 @@ define(['react', 'classnames'], function(React, classNames) {
|
|||||||
column.is_primary = (index === 0);
|
column.is_primary = (index === 0);
|
||||||
column.sorted = (this.props.sort_by === column.name)
|
column.sorted = (this.props.sort_by === column.name)
|
||||||
? this.props.sort_order
|
? this.props.sort_order
|
||||||
: 'asc';
|
: 'desc';
|
||||||
return (
|
return (
|
||||||
<ListingColumn
|
<ListingColumn
|
||||||
onSort={this.props.onSort}
|
onSort={this.props.onSort}
|
||||||
@ -32,6 +32,7 @@ define(['react', 'classnames'], function(React, classNames) {
|
|||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
|
name="select_all"
|
||||||
ref="toggle"
|
ref="toggle"
|
||||||
checked={ this.props.selection }
|
checked={ this.props.selection }
|
||||||
onChange={ this.handleSelectItems } />
|
onChange={ this.handleSelectItems } />
|
||||||
|
@ -79,27 +79,48 @@ define(
|
|||||||
|
|
||||||
if(custom_actions.length > 0) {
|
if(custom_actions.length > 0) {
|
||||||
item_actions = custom_actions.map(function(action, index) {
|
item_actions = custom_actions.map(function(action, index) {
|
||||||
if(action.refresh) {
|
if(action.display !== undefined) {
|
||||||
|
if(action.display(this.props.item) === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(action.name === 'trash') {
|
||||||
|
return (
|
||||||
|
<span key={ 'action-'+index } className="trash">
|
||||||
|
{(index > 0) ? ' | ' : ''}
|
||||||
|
<a
|
||||||
|
href="javascript:;"
|
||||||
|
onClick={ this.handleTrashItem.bind(
|
||||||
|
null,
|
||||||
|
this.props.item.id
|
||||||
|
) }>
|
||||||
|
Trash
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
} else if(action.refresh) {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
onClick={ this.props.onRefreshItems }
|
onClick={ this.props.onRefreshItems }
|
||||||
key={ 'action-'+index } className={ action.name }>
|
key={ 'action-'+index } className={ action.name }>
|
||||||
|
{(index > 0) ? ' | ' : ''}
|
||||||
{ action.link(this.props.item) }
|
{ action.link(this.props.item) }
|
||||||
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
|
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
} else if(action.link) {
|
} else if(action.link) {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
key={ 'action-'+index } className={ action.name }>
|
key={ 'action-'+index } className={ action.name }>
|
||||||
|
{(index > 0) ? ' | ' : ''}
|
||||||
{ action.link(this.props.item) }
|
{ action.link(this.props.item) }
|
||||||
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
|
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
key={ 'action-'+index } className={ action.name }>
|
key={ 'action-'+index } className={ action.name }>
|
||||||
|
{(index > 0) ? ' | ' : ''}
|
||||||
<a href="javascript:;" onClick={
|
<a href="javascript:;" onClick={
|
||||||
(action.onClick !== undefined)
|
(action.onClick !== undefined)
|
||||||
? action.onClick.bind(null,
|
? action.onClick.bind(null,
|
||||||
@ -108,7 +129,6 @@ define(
|
|||||||
)
|
)
|
||||||
: false
|
: false
|
||||||
}>{ action.label }</a>
|
}>{ action.label }</a>
|
||||||
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
|
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -158,17 +178,6 @@ define(
|
|||||||
<div>
|
<div>
|
||||||
<div className="row-actions">
|
<div className="row-actions">
|
||||||
{ item_actions }
|
{ item_actions }
|
||||||
{ ' | ' }
|
|
||||||
<span className="trash">
|
|
||||||
<a
|
|
||||||
href="javascript:;"
|
|
||||||
onClick={ this.handleTrashItem.bind(
|
|
||||||
null,
|
|
||||||
this.props.item.id
|
|
||||||
) }>
|
|
||||||
Trash
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
|
onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
|
||||||
@ -419,7 +428,13 @@ define(
|
|||||||
groups: response.groups || [],
|
groups: response.groups || [],
|
||||||
count: response.count || 0,
|
count: response.count || 0,
|
||||||
loading: false
|
loading: false
|
||||||
});
|
}, function() {
|
||||||
|
if(this.props['onGetItems'] !== undefined) {
|
||||||
|
this.props.onGetItems(
|
||||||
|
~~(this.state.groups[0]['count'])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -631,7 +646,7 @@ define(
|
|||||||
// bulk actions
|
// bulk actions
|
||||||
var bulk_actions = this.props.bulk_actions || [];
|
var bulk_actions = this.props.bulk_actions || [];
|
||||||
|
|
||||||
if(this.state.group === 'trash') {
|
if(this.state.group === 'trash' && bulk_actions.length > 0) {
|
||||||
bulk_actions = [
|
bulk_actions = [
|
||||||
{
|
{
|
||||||
name: 'restore',
|
name: 'restore',
|
||||||
|
@ -306,7 +306,7 @@ define([
|
|||||||
_.each(postTypes, function(type) {
|
_.each(postTypes, function(type) {
|
||||||
select.append(jQuery('<option>', {
|
select.append(jQuery('<option>', {
|
||||||
value: type.name,
|
value: type.name,
|
||||||
text: type.labels.singular_name,
|
text: type.label,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
select.val(selectedValue);
|
select.val(selectedValue);
|
||||||
|
@ -203,6 +203,9 @@ define([
|
|||||||
changeBoolField: function(field, event) {
|
changeBoolField: function(field, event) {
|
||||||
this.model.set(field, (jQuery(event.target).val() === 'true') ? true : false);
|
this.model.set(field, (jQuery(event.target).val() === 'true') ? true : false);
|
||||||
},
|
},
|
||||||
|
changeBoolCheckboxField: function(field, event) {
|
||||||
|
this.model.set(field, (!!jQuery(event.target).prop('checked')));
|
||||||
|
},
|
||||||
changeColorField: function(field, event) {
|
changeColorField: function(field, event) {
|
||||||
var value = jQuery(event.target).val();
|
var value = jQuery(event.target).val();
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
|
@ -162,10 +162,10 @@ define([
|
|||||||
this.toolsView = new Module.ContainerBlockToolsView({
|
this.toolsView = new Module.ContainerBlockToolsView({
|
||||||
model: this.model,
|
model: this.model,
|
||||||
tools: {
|
tools: {
|
||||||
settings: this.renderOptions.depth > 1,
|
settings: this.renderOptions.depth === 1,
|
||||||
delete: this.renderOptions.depth === 1,
|
delete: this.renderOptions.depth === 1,
|
||||||
move: this.renderOptions.depth === 1,
|
move: this.renderOptions.depth === 1,
|
||||||
layerSelector: this.renderOptions.depth === 1,
|
layerSelector: false,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.toolsRegion.show(this.toolsView);
|
this.toolsRegion.show(this.toolsView);
|
||||||
@ -265,6 +265,41 @@ define([
|
|||||||
behaviors: {
|
behaviors: {
|
||||||
ColorPickerBehavior: {},
|
ColorPickerBehavior: {},
|
||||||
},
|
},
|
||||||
|
regions: {
|
||||||
|
columnsSettingsRegion: '.mailpoet_container_columns_settings',
|
||||||
|
},
|
||||||
|
initialize: function() {
|
||||||
|
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
|
||||||
|
|
||||||
|
this._columnsSettingsView = new (Module.ContainerBlockColumnsSettingsView)({
|
||||||
|
collection: this.model.get('blocks'),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onRender: function() {
|
||||||
|
this.columnsSettingsRegion.show(this._columnsSettingsView);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Module.ContainerBlockColumnsSettingsView = Marionette.CollectionView.extend({
|
||||||
|
getChildView: function() { return Module.ContainerBlockColumnSettingsView; },
|
||||||
|
childViewOptions: function(model, index) {
|
||||||
|
return {
|
||||||
|
columnIndex: index,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Module.ContainerBlockColumnSettingsView = Marionette.ItemView.extend({
|
||||||
|
getTemplate: function() { return templates.containerBlockColumnSettings; },
|
||||||
|
initialize: function(options) {
|
||||||
|
this.columnNumber = (options.columnIndex || 0) + 1;
|
||||||
|
},
|
||||||
|
templateHelpers: function() {
|
||||||
|
return {
|
||||||
|
model: this.model.toJSON(),
|
||||||
|
columnNumber: this.columnNumber,
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Module.OneColumnContainerWidgetView = base.WidgetView.extend({
|
Module.OneColumnContainerWidgetView = base.WidgetView.extend({
|
||||||
|
@ -66,7 +66,7 @@ define([
|
|||||||
"keyup .mailpoet_field_image_link": _.partial(this.changeField, "link"),
|
"keyup .mailpoet_field_image_link": _.partial(this.changeField, "link"),
|
||||||
"keyup .mailpoet_field_image_address": _.partial(this.changeField, "src"),
|
"keyup .mailpoet_field_image_address": _.partial(this.changeField, "src"),
|
||||||
"keyup .mailpoet_field_image_alt_text": _.partial(this.changeField, "alt"),
|
"keyup .mailpoet_field_image_alt_text": _.partial(this.changeField, "alt"),
|
||||||
"change .mailpoet_field_image_padded": _.partial(this.changeBoolField, "padded"),
|
"change .mailpoet_field_image_padded": _.partial(this.changeBoolCheckboxField, "padded"),
|
||||||
"change .mailpoet_field_image_alignment": _.partial(this.changeField, "styles.block.textAlign"),
|
"change .mailpoet_field_image_alignment": _.partial(this.changeField, "styles.block.textAlign"),
|
||||||
"click .mailpoet_field_image_select_another_image": "showMediaManager",
|
"click .mailpoet_field_image_select_another_image": "showMediaManager",
|
||||||
"click .mailpoet_done_editing": "close",
|
"click .mailpoet_done_editing": "close",
|
||||||
|
@ -31,7 +31,7 @@ define([
|
|||||||
base = BaseBlock;
|
base = BaseBlock;
|
||||||
|
|
||||||
Module.PostsBlockModel = base.BlockModel.extend({
|
Module.PostsBlockModel = base.BlockModel.extend({
|
||||||
stale: ['_selectedPosts', '_availablePosts'],
|
stale: ['_selectedPosts', '_availablePosts', '_transformedPosts'],
|
||||||
defaults: function() {
|
defaults: function() {
|
||||||
return this._getDefaults({
|
return this._getDefaults({
|
||||||
type: 'posts',
|
type: 'posts',
|
||||||
@ -63,6 +63,7 @@ define([
|
|||||||
divider: {},
|
divider: {},
|
||||||
_selectedPosts: [],
|
_selectedPosts: [],
|
||||||
_availablePosts: [],
|
_availablePosts: [],
|
||||||
|
_transformedPosts: new (App.getBlockTypeModel('container'))(),
|
||||||
}, App.getConfig().get('blockDefaults.posts'));
|
}, App.getConfig().get('blockDefaults.posts'));
|
||||||
},
|
},
|
||||||
relations: function() {
|
relations: function() {
|
||||||
@ -71,15 +72,26 @@ define([
|
|||||||
divider: App.getBlockTypeModel('divider'),
|
divider: App.getBlockTypeModel('divider'),
|
||||||
_selectedPosts: Backbone.Collection,
|
_selectedPosts: Backbone.Collection,
|
||||||
_availablePosts: Backbone.Collection,
|
_availablePosts: Backbone.Collection,
|
||||||
|
_transformedPosts: App.getBlockTypeModel('container'),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
var that = this;
|
var that = this,
|
||||||
|
POST_REFRESH_DELAY_MS = 500,
|
||||||
|
refreshAvailablePosts = _.debounce(this.fetchAvailablePosts.bind(this), POST_REFRESH_DELAY_MS),
|
||||||
|
refreshTransformedPosts = _.debounce(this._refreshTransformedPosts.bind(this), POST_REFRESH_DELAY_MS);
|
||||||
|
|
||||||
// Attach Radio.Requests API primarily for highlighting
|
// Attach Radio.Requests API primarily for highlighting
|
||||||
_.extend(this, Radio.Requests);
|
_.extend(this, Radio.Requests);
|
||||||
|
|
||||||
this.fetchAvailablePosts();
|
this.fetchAvailablePosts();
|
||||||
this.on('change:amount change:contentType change:terms change:inclusionType change:postStatus change:search change:sortBy', this._scheduleFetchAvailablePosts, this);
|
this.on('change:amount change:contentType change:terms change:inclusionType change:postStatus change:search change:sortBy', refreshAvailablePosts);
|
||||||
|
|
||||||
|
this.listenTo(this.get('_selectedPosts'), 'add remove reset', refreshTransformedPosts);
|
||||||
|
this.on('change:displayType change:titleFormat change:titlePosition change:titleAlignment change:titleIsLink change:imagePadded change:showAuthor change:authorPrecededBy change:showCategories change:categoriesPrecededBy change:readMoreType change:readMoreText change:showDivider', refreshTransformedPosts);
|
||||||
|
this.listenTo(this.get('readMoreButton'), 'change', refreshTransformedPosts);
|
||||||
|
this.listenTo(this.get('divider'), 'change', refreshTransformedPosts);
|
||||||
|
|
||||||
this.on('insertSelectedPosts', this._insertSelectedPosts, this);
|
this.on('insertSelectedPosts', this._insertSelectedPosts, this);
|
||||||
},
|
},
|
||||||
fetchAvailablePosts: function() {
|
fetchAvailablePosts: function() {
|
||||||
@ -93,20 +105,23 @@ define([
|
|||||||
console.log('Posts fetchPosts error', arguments);
|
console.log('Posts fetchPosts error', arguments);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
_refreshTransformedPosts: function() {
|
||||||
* Batch more changes during a specific time, instead of fetching
|
var that = this,
|
||||||
* ALC posts on each model change
|
data = this.toJSON();
|
||||||
*/
|
|
||||||
_scheduleFetchAvailablePosts: function() {
|
data.posts = this.get('_selectedPosts').pluck('ID');
|
||||||
var timeout = 500,
|
|
||||||
that = this;
|
if (data.posts.length === 0) {
|
||||||
if (this._fetchPostsTimer !== undefined) {
|
this.get('_transformedPosts.blocks').reset();
|
||||||
clearTimeout(this._fetchPostsTimer);
|
return;
|
||||||
}
|
}
|
||||||
this._fetchPostsTimer = setTimeout(function() {
|
|
||||||
that.fetchAvailablePosts();
|
WordpressComponent.getTransformedPosts(data).done(function(posts) {
|
||||||
that._fetchPostsTimer = undefined;
|
console.log('Transformed posts fetched', arguments);
|
||||||
}, timeout);
|
that.get('_transformedPosts').get('blocks').reset(posts, {parse: true});
|
||||||
|
}).fail(function() {
|
||||||
|
console.log('Posts _refreshTransformedPosts error', arguments);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
_insertSelectedPosts: function() {
|
_insertSelectedPosts: function() {
|
||||||
var that = this,
|
var that = this,
|
||||||
@ -131,6 +146,9 @@ define([
|
|||||||
className: "mailpoet_block mailpoet_posts_block mailpoet_droppable_block",
|
className: "mailpoet_block mailpoet_posts_block mailpoet_droppable_block",
|
||||||
getTemplate: function() { return templates.postsBlock; },
|
getTemplate: function() { return templates.postsBlock; },
|
||||||
modelEvents: {},
|
modelEvents: {},
|
||||||
|
regions: _.extend({
|
||||||
|
postsRegion: '.mailpoet_posts_block_posts',
|
||||||
|
}, base.BlockView.prototype.regions),
|
||||||
onDragSubstituteBy: function() { return Module.PostsWidgetView; },
|
onDragSubstituteBy: function() { return Module.PostsWidgetView; },
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||||
@ -142,6 +160,13 @@ define([
|
|||||||
this.toolsRegion.show(this.toolsView);
|
this.toolsRegion.show(this.toolsView);
|
||||||
}
|
}
|
||||||
this.trigger('showSettings');
|
this.trigger('showSettings');
|
||||||
|
|
||||||
|
var ContainerView = App.getBlockTypeView('container'),
|
||||||
|
renderOptions = {
|
||||||
|
disableTextEditor: true,
|
||||||
|
disableDragAndDrop: true,
|
||||||
|
};
|
||||||
|
this.postsRegion.show(new ContainerView({ model: this.model.get('_transformedPosts'), renderOptions: renderOptions }));
|
||||||
},
|
},
|
||||||
notifyAboutSelf: function() {
|
notifyAboutSelf: function() {
|
||||||
return this;
|
return this;
|
||||||
@ -216,6 +241,7 @@ define([
|
|||||||
insertPosts: function() {
|
insertPosts: function() {
|
||||||
this.model.trigger('insertSelectedPosts');
|
this.model.trigger('insertSelectedPosts');
|
||||||
this.model.destroy();
|
this.model.destroy();
|
||||||
|
this.close();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -307,11 +333,6 @@ define([
|
|||||||
},
|
},
|
||||||
}).trigger( 'change' );
|
}).trigger( 'change' );
|
||||||
},
|
},
|
||||||
onBeforeDestroy: function() {
|
|
||||||
base.BlockSettingsView.prototype.onBeforeDestroy.apply(this, arguments);
|
|
||||||
// Force close select2 if it hasn't closed yet
|
|
||||||
this.$('.mailpoet_posts_categories_and_tags').select2('close');
|
|
||||||
},
|
|
||||||
changeField: function(field, event) {
|
changeField: function(field, event) {
|
||||||
this.model.set(field, jQuery(event.target).val());
|
this.model.set(field, jQuery(event.target).val());
|
||||||
},
|
},
|
||||||
@ -323,7 +344,7 @@ define([
|
|||||||
_.each(postTypes, function(type) {
|
_.each(postTypes, function(type) {
|
||||||
select.append(jQuery('<option>', {
|
select.append(jQuery('<option>', {
|
||||||
value: type.name,
|
value: type.name,
|
||||||
text: type.labels.singular_name,
|
text: type.label,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
select.val(selectedValue);
|
select.val(selectedValue);
|
||||||
|
@ -6,8 +6,9 @@ define([
|
|||||||
'backbone.marionette',
|
'backbone.marionette',
|
||||||
'jquery',
|
'jquery',
|
||||||
'blob',
|
'blob',
|
||||||
'filesaver'
|
'filesaver',
|
||||||
], function(App, MailPoet, Notice, Backbone, Marionette, jQuery, Blob, FileSaver) {
|
'html2canvas'
|
||||||
|
], function(App, MailPoet, Notice, Backbone, Marionette, jQuery, Blob, FileSaver, html2canvas) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
@ -56,16 +57,26 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Module.exportTemplate = function(options) {
|
Module.getThumbnail = function(element, options) {
|
||||||
var data = _.extend(options || {}, {
|
return html2canvas(element, options || {});
|
||||||
body: App.getBody(),
|
};
|
||||||
});
|
|
||||||
var blob = new Blob(
|
|
||||||
[JSON.stringify(data)],
|
|
||||||
{ type: 'application/json;charset=utf-8' }
|
|
||||||
);
|
|
||||||
|
|
||||||
FileSaver.saveAs(blob, 'template.json');
|
Module.exportTemplate = function(options) {
|
||||||
|
var that = this;
|
||||||
|
return Module.getThumbnail(
|
||||||
|
jQuery('#mailpoet_editor_content > .mailpoet_block').get(0)
|
||||||
|
).then(function(thumbnail) {
|
||||||
|
var data = _.extend(options || {}, {
|
||||||
|
thumbnail: thumbnail.toDataURL('image/jpeg'),
|
||||||
|
body: App.getBody(),
|
||||||
|
});
|
||||||
|
var blob = new Blob(
|
||||||
|
[JSON.stringify(data)],
|
||||||
|
{ type: 'application/json;charset=utf-8' }
|
||||||
|
);
|
||||||
|
|
||||||
|
FileSaver.saveAs(blob, 'template.json');
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Module.SaveView = Marionette.LayoutView.extend({
|
Module.SaveView = Marionette.LayoutView.extend({
|
||||||
|
@ -112,6 +112,9 @@ define(
|
|||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'trash'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -34,7 +34,10 @@ define(
|
|||||||
placeholder: "Select a list",
|
placeholder: "Select a list",
|
||||||
id: "mailpoet_segments",
|
id: "mailpoet_segments",
|
||||||
endpoint: "segments",
|
endpoint: "segments",
|
||||||
multiple: true
|
multiple: true,
|
||||||
|
filter: function(segment) {
|
||||||
|
return !!(!segment.deleted_at);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'sender',
|
name: 'sender',
|
||||||
|
@ -119,11 +119,11 @@ define(
|
|||||||
body: template.body
|
body: template.body
|
||||||
}
|
}
|
||||||
}).done(function(response) {
|
}).done(function(response) {
|
||||||
if(response === true) {
|
if(response.result === true) {
|
||||||
// TODO: Move this URL elsewhere
|
// TODO: Move this URL elsewhere
|
||||||
window.location = 'admin.php?page=mailpoet-newsletter-editor&id=' + this.props.params.id;
|
window.location = 'admin.php?page=mailpoet-newsletter-editor&id=' + this.props.params.id;
|
||||||
} else {
|
} else {
|
||||||
response.map(function(error) {
|
response.errors.map(function(error) {
|
||||||
MailPoet.Notice.error(error);
|
MailPoet.Notice.error(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -150,6 +150,13 @@ define(
|
|||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
handleShowTemplate: function(template) {
|
||||||
|
MailPoet.Modal.popup({
|
||||||
|
title: template.name,
|
||||||
|
template: '<img src="{{ thumbnail }}" />',
|
||||||
|
data: template,
|
||||||
|
});
|
||||||
|
},
|
||||||
handleTemplateImport: function() {
|
handleTemplateImport: function() {
|
||||||
this.getTemplates();
|
this.getTemplates();
|
||||||
},
|
},
|
||||||
@ -164,11 +171,22 @@ define(
|
|||||||
Delete
|
Delete
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
);
|
), thumbnail = '';
|
||||||
|
|
||||||
|
if (typeof template.thumbnail === 'string'
|
||||||
|
&& template.thumbnail.length > 0) {
|
||||||
|
thumbnail = (
|
||||||
|
<a href="javascript:;" onClick={this.handleShowTemplate.bind(null, template)}>
|
||||||
|
<img src={ template.thumbnail } />
|
||||||
|
<div className="mailpoet_overlay"></div>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li key={ 'template-'+index }>
|
<li key={ 'template-'+index }>
|
||||||
<div className="mailpoet_thumbnail">
|
<div className="mailpoet_thumbnail">
|
||||||
|
{ thumbnail }
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mailpoet_description">
|
<div className="mailpoet_description">
|
||||||
|
@ -47,6 +47,8 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
|||||||
type: 'success',
|
type: 'success',
|
||||||
message: '',
|
message: '',
|
||||||
static: false,
|
static: false,
|
||||||
|
hideClose: false,
|
||||||
|
id: null,
|
||||||
scroll: false,
|
scroll: false,
|
||||||
timeout: 2000,
|
timeout: 2000,
|
||||||
onOpen: null,
|
onOpen: null,
|
||||||
@ -60,6 +62,9 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
|||||||
// clone element
|
// clone element
|
||||||
this.element = jQuery('#mailpoet_notice_'+this.options.type).clone();
|
this.element = jQuery('#mailpoet_notice_'+this.options.type).clone();
|
||||||
|
|
||||||
|
// add data-id to the element
|
||||||
|
if (this.options.id) this.element.attr('data-id', 'notice_' + this.options.id);
|
||||||
|
|
||||||
// remove id from clone
|
// remove id from clone
|
||||||
this.element.removeAttr('id');
|
this.element.removeAttr('id');
|
||||||
|
|
||||||
@ -73,7 +78,6 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// listen to remove event
|
// listen to remove event
|
||||||
var element = this.element;
|
|
||||||
jQuery(this.element).on('close', function() {
|
jQuery(this.element).on('close', function() {
|
||||||
jQuery(this).fadeOut(200, function() {
|
jQuery(this).fadeOut(200, function() {
|
||||||
// on close callback
|
// on close callback
|
||||||
@ -148,7 +152,7 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
|||||||
// if the notice is not static, it has to disappear after a timeout
|
// if the notice is not static, it has to disappear after a timeout
|
||||||
if(this.options.static === false) {
|
if(this.options.static === false) {
|
||||||
this.element.delay(this.options.timeout).trigger('close');
|
this.element.delay(this.options.timeout).trigger('close');
|
||||||
} else {
|
} else if (this.options.hideClose === false) {
|
||||||
this.element.append('<a href="javascript:;" class="mailpoet_notice_close"><span class="dashicons dashicons-dismiss"></span></a>');
|
this.element.append('<a href="javascript:;" class="mailpoet_notice_close"><span class="dashicons dashicons-dismiss"></span></a>');
|
||||||
this.element.find('.mailpoet_notice_close').on('click', function() {
|
this.element.find('.mailpoet_notice_close').on('click', function() {
|
||||||
jQuery(this).trigger('close');
|
jQuery(this).trigger('close');
|
||||||
@ -163,6 +167,14 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
|||||||
hide: function(all) {
|
hide: function(all) {
|
||||||
if(all !== undefined && all === true) {
|
if(all !== undefined && all === true) {
|
||||||
jQuery('.mailpoet_notice:not([id])').trigger('close');
|
jQuery('.mailpoet_notice:not([id])').trigger('close');
|
||||||
|
} else if (all !== undefined && jQuery.isArray(all)) {
|
||||||
|
for (var id in all) {
|
||||||
|
jQuery('[data-id="notice_' + all[id] + '"]')
|
||||||
|
.trigger('close');
|
||||||
|
}
|
||||||
|
} if (all !== undefined) {
|
||||||
|
jQuery('[data-id="notice_' + all + '"]')
|
||||||
|
.trigger('close');
|
||||||
} else {
|
} else {
|
||||||
jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])')
|
jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])')
|
||||||
.trigger('close');
|
.trigger('close');
|
||||||
@ -188,4 +200,4 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
|||||||
}, options));
|
}, options));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
75
assets/js/src/queue.jsx
Normal file
75
assets/js/src/queue.jsx
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
define(
|
||||||
|
[
|
||||||
|
'react',
|
||||||
|
'react-dom',
|
||||||
|
'mailpoet',
|
||||||
|
'classnames'
|
||||||
|
],
|
||||||
|
function (
|
||||||
|
React,
|
||||||
|
ReactDOM,
|
||||||
|
MailPoet,
|
||||||
|
classNames
|
||||||
|
) {
|
||||||
|
var QueueDaemonControl = React.createClass({
|
||||||
|
getInitialState: function () {
|
||||||
|
return (queueDaemon) ? {
|
||||||
|
status: queueDaemon.status,
|
||||||
|
timeSinceStart: queueDaemon.time_since_start,
|
||||||
|
timeSinceUpdate: queueDaemon.time_since_update,
|
||||||
|
counter: queueDaemon.counter
|
||||||
|
} : null;
|
||||||
|
},
|
||||||
|
getDaemonData: function () {
|
||||||
|
MailPoet.Ajax.post({
|
||||||
|
endpoint: 'queue',
|
||||||
|
action: 'getQueueStatus'
|
||||||
|
}).done(function (response) {
|
||||||
|
this.setState({
|
||||||
|
status: response.status,
|
||||||
|
timeSinceStart: response.time_since_start,
|
||||||
|
timeSinceUpdate: response.time_since_update,
|
||||||
|
counter: response.counter,
|
||||||
|
});
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
componentDidMount: function () {
|
||||||
|
this.getDaemonData;
|
||||||
|
setInterval(this.getDaemonData, 5000);
|
||||||
|
},
|
||||||
|
render: function () {
|
||||||
|
if (!this.state) {
|
||||||
|
return (
|
||||||
|
<div className="QueueControl">
|
||||||
|
Woops, daemon is not running ;\
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
Queue is currently <b>{this.state.status}</b>.
|
||||||
|
<br/>
|
||||||
|
<br/>
|
||||||
|
It was started
|
||||||
|
<b> {this.state.timeSinceStart} </b> and was last executed
|
||||||
|
<b> {this.state.timeSinceUpdate} </b> for a total of
|
||||||
|
<b> {this.state.counter} </b> times (once every 30 seconds, unless it was interrupted and restarted).
|
||||||
|
<br />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let container = document.getElementById('queue_container');
|
||||||
|
if (container) {
|
||||||
|
ReactDOM.render(
|
||||||
|
<QueueDaemonControl />,
|
||||||
|
container
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
@ -26,10 +26,10 @@ define(
|
|||||||
];
|
];
|
||||||
|
|
||||||
var messages = {
|
var messages = {
|
||||||
updated: function() {
|
onUpdate: function() {
|
||||||
MailPoet.Notice.success('Segment successfully updated!');
|
MailPoet.Notice.success('Segment successfully updated!');
|
||||||
},
|
},
|
||||||
created: function() {
|
onCreate: function() {
|
||||||
MailPoet.Notice.success('Segment successfully added!');
|
MailPoet.Notice.success('Segment successfully added!');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -54,7 +54,7 @@ define(
|
|||||||
fields={ fields }
|
fields={ fields }
|
||||||
params={ this.props.params }
|
params={ this.props.params }
|
||||||
messages={ messages }
|
messages={ messages }
|
||||||
onSuccess={ this.history.goBack } />
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,30 @@ const item_actions = [
|
|||||||
);
|
);
|
||||||
refresh();
|
refresh();
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
display: function(segment) {
|
||||||
|
return (segment.type !== 'wp_users');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'synchronize_segment',
|
||||||
|
label: 'Update',
|
||||||
|
className: 'update',
|
||||||
|
onClick: function(item, refresh) {
|
||||||
|
return MailPoet.Ajax.post({
|
||||||
|
endpoint: 'segments',
|
||||||
|
action: 'synchronize'
|
||||||
|
}).done(function(response) {
|
||||||
|
if(response === true) {
|
||||||
|
MailPoet.Notice.success(
|
||||||
|
('List "%$1s" has been synchronized.').replace('%$1s', item.name)
|
||||||
|
);
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
display: function(segment) {
|
||||||
|
return (segment.type === 'wp_users');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -130,15 +154,16 @@ const item_actions = [
|
|||||||
<a href={ item.subscribers_url }>View subscribers</a>
|
<a href={ item.subscribers_url }>View subscribers</a>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'trash',
|
||||||
|
display: function(segment) {
|
||||||
|
return (segment.type !== 'wp_users');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
const bulk_actions = [
|
const bulk_actions = [
|
||||||
{
|
|
||||||
name: 'trash',
|
|
||||||
label: 'Trash',
|
|
||||||
onSuccess: messages.onTrash
|
|
||||||
}
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const SegmentList = React.createClass({
|
const SegmentList = React.createClass({
|
||||||
@ -148,7 +173,6 @@ const SegmentList = React.createClass({
|
|||||||
'column-primary',
|
'column-primary',
|
||||||
'has-row-actions'
|
'has-row-actions'
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<td className={ rowClasses }>
|
<td className={ rowClasses }>
|
||||||
|
@ -73,7 +73,7 @@ define(
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
jQuery(document).ready(function() {
|
jQuery(document).ready(function() {
|
||||||
Backbone.history.start();
|
if (!Backbone.History.started) Backbone.history.start();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
@ -37,14 +37,25 @@ define(
|
|||||||
'subscribed': 'Subscribed',
|
'subscribed': 'Subscribed',
|
||||||
'unsubscribed': 'Unsubscribed'
|
'unsubscribed': 'Unsubscribed'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'segments',
|
||||||
|
label: 'Lists',
|
||||||
|
type: 'selection',
|
||||||
|
placeholder: "Select a list",
|
||||||
|
endpoint: "segments",
|
||||||
|
multiple: true,
|
||||||
|
filter: function(segment) {
|
||||||
|
return !!(!segment.deleted_at);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
var messages = {
|
var messages = {
|
||||||
updated: function() {
|
onUpdate: function() {
|
||||||
MailPoet.Notice.success('Subscriber successfully updated!');
|
MailPoet.Notice.success('Subscriber successfully updated!');
|
||||||
},
|
},
|
||||||
created: function() {
|
onCreate: function() {
|
||||||
MailPoet.Notice.success('Subscriber successfully added!');
|
MailPoet.Notice.success('Subscriber successfully added!');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -71,7 +82,7 @@ define(
|
|||||||
fields={ fields }
|
fields={ fields }
|
||||||
params={ this.props.params }
|
params={ this.props.params }
|
||||||
messages={ messages }
|
messages={ messages }
|
||||||
onSuccess={ this.history.goBack } />
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
170
assets/js/src/subscribers/importExport/export.js
Normal file
170
assets/js/src/subscribers/importExport/export.js
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
define(
|
||||||
|
[
|
||||||
|
'underscore',
|
||||||
|
'jquery',
|
||||||
|
'mailpoet',
|
||||||
|
'handlebars',
|
||||||
|
'select2'
|
||||||
|
],
|
||||||
|
function (
|
||||||
|
_,
|
||||||
|
jQuery,
|
||||||
|
MailPoet,
|
||||||
|
Handlebars
|
||||||
|
) {
|
||||||
|
if (!jQuery("#mailpoet_subscribers_export").length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
jQuery(document).ready(function () {
|
||||||
|
if (!exportData.segments) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var subscribers_export_template =
|
||||||
|
Handlebars.compile(jQuery('#mailpoet_subscribers_export_template').html());
|
||||||
|
|
||||||
|
//render template
|
||||||
|
jQuery('#mailpoet_subscribers_export > div.inside').html(subscribers_export_template(exportData));
|
||||||
|
|
||||||
|
// define reusable variables
|
||||||
|
var segmentsContainerElement = jQuery("#export_lists"),
|
||||||
|
subscriberFieldsContainerElement = jQuery("#export_columns"),
|
||||||
|
exportConfirmedOptionElement = jQuery(':radio[name="option_confirmed"]'),
|
||||||
|
groupBySegmentOptionElement = jQuery(':checkbox[name="option_group_by_list"]'),
|
||||||
|
nextStepButton = jQuery("a.mailpoet_export_process"),
|
||||||
|
renderSegmentsAndFields = function (container, data) {
|
||||||
|
if (container.data('select2')) {
|
||||||
|
container
|
||||||
|
.html('')
|
||||||
|
.select2('destroy');
|
||||||
|
}
|
||||||
|
container
|
||||||
|
.select2({
|
||||||
|
data: data,
|
||||||
|
width: '20em',
|
||||||
|
templateResult: function (item) {
|
||||||
|
return (item.subscriberCount > 0)
|
||||||
|
? item.name + ' (' + item.subscriberCount + ')'
|
||||||
|
: item.name;
|
||||||
|
},
|
||||||
|
templateSelection: function (item) {
|
||||||
|
return (item.subscriberCount > 0)
|
||||||
|
? item.name + ' (' + item.subscriberCount + ')'
|
||||||
|
: item.name;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on('select2:selecting', function (selectEvent) {
|
||||||
|
var selectElement = this,
|
||||||
|
selectedOptionId = selectEvent.params.args.data.id,
|
||||||
|
fieldsToExclude = [
|
||||||
|
'select',
|
||||||
|
'deselect'
|
||||||
|
];
|
||||||
|
if (_.contains(fieldsToExclude, selectedOptionId)) {
|
||||||
|
selectEvent.preventDefault();
|
||||||
|
if (selectedOptionId === 'deselect') {
|
||||||
|
jQuery(selectElement).select2('val', '');
|
||||||
|
} else {
|
||||||
|
var allOptions = [];
|
||||||
|
_.each(container.find('option'), function (field) {
|
||||||
|
if (!_.contains(fieldsToExclude, field.value)) {
|
||||||
|
allOptions.push(field.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
jQuery(selectElement).select2('val', allOptions);
|
||||||
|
}
|
||||||
|
jQuery(selectElement).select2('close');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on('change', function () {
|
||||||
|
if ((exportData.segments && segmentsContainerElement.select2('data').length && subscriberFieldsContainerElement.select2('data').length)
|
||||||
|
||
|
||||||
|
(!exportData.segments && subscriberFieldsContainerElement.select2('data').length)
|
||||||
|
) {
|
||||||
|
toggleNextStepButton('on');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toggleNextStepButton('off');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (segmentsContainerElement.select2('data').length > 1 && exportData.groupBySegmentOption) {
|
||||||
|
jQuery('.mailpoet_group_by_list').show();
|
||||||
|
}
|
||||||
|
else if (exportData.groupBySegmentOption) {
|
||||||
|
jQuery('.mailpoet_group_by_list').hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
renderSegmentsAndFields(subscriberFieldsContainerElement, subscriberFieldsSelect2);
|
||||||
|
renderSegmentsAndFields(segmentsContainerElement, segments);
|
||||||
|
|
||||||
|
subscriberFieldsContainerElement.select2('val', [
|
||||||
|
'status',
|
||||||
|
'email',
|
||||||
|
'first_name',
|
||||||
|
'last_name'
|
||||||
|
]);
|
||||||
|
|
||||||
|
exportConfirmedOptionElement.change(function () {
|
||||||
|
var selectedSegments = segmentsContainerElement.val();
|
||||||
|
if (this.value == 1) {
|
||||||
|
exportData.exportConfirmedOption = true;
|
||||||
|
renderSegmentsAndFields(segmentsContainerElement, segmentsWithConfirmedSubscribers);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
exportData.exportConfirmedOption = false;
|
||||||
|
renderSegmentsAndFields(segmentsContainerElement, segments);
|
||||||
|
}
|
||||||
|
segmentsContainerElement.select2('val', selectedSegments);
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggleNextStepButton(condition) {
|
||||||
|
var disabled = 'button-disabled';
|
||||||
|
if (condition === 'on') {
|
||||||
|
nextStepButton.removeClass(disabled);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
nextStepButton.addClass(disabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nextStepButton.click(function () {
|
||||||
|
if (jQuery(this).hasClass('button-disabled')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MailPoet.Modal.loading(true);
|
||||||
|
MailPoet.Ajax
|
||||||
|
.post({
|
||||||
|
endpoint: 'ImportExport',
|
||||||
|
action: 'processExport',
|
||||||
|
data: JSON.stringify({
|
||||||
|
'exportConfirmedOption': exportData.exportConfirmedOption,
|
||||||
|
'exportFormatOption': jQuery(':radio[name="option_format"]:checked').val(),
|
||||||
|
'groupBySegmentOption': (groupBySegmentOptionElement.is(":visible")) ? groupBySegmentOptionElement.prop('checked') : false,
|
||||||
|
'segments': (exportData.segments) ? segmentsContainerElement.val() : false,
|
||||||
|
'subscriberFields': subscriberFieldsContainerElement.val()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.done(function (response) {
|
||||||
|
MailPoet.Modal.loading(false);
|
||||||
|
if (response.result === false) {
|
||||||
|
MailPoet.Notice.error(response.error);
|
||||||
|
} else {
|
||||||
|
resultMessage = MailPoetI18n.exportMessage
|
||||||
|
.replace('%1$s', '<strong>' + response.data.totalExported + '</strong>')
|
||||||
|
.replace('[link]', '<a href="' + response.data.exportFileURL + '" target="_blank" >')
|
||||||
|
.replace('[/link]', '</a>');
|
||||||
|
jQuery('#export_result_notice > ul > li').html(resultMessage);
|
||||||
|
jQuery('#export_result_notice').show();
|
||||||
|
window.location.href = response.data.exportFileURL;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.error(function (error) {
|
||||||
|
MailPoet.Modal.loading(false);
|
||||||
|
MailPoet.Notice.error(
|
||||||
|
MailPoetI18n.serverError + error.statusText.toLowerCase() + '.'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
1131
assets/js/src/subscribers/importExport/import.js
Normal file
1131
assets/js/src/subscribers/importExport/import.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -101,7 +101,12 @@ const bulk_actions = [
|
|||||||
onSelect: function() {
|
onSelect: function() {
|
||||||
let field = {
|
let field = {
|
||||||
id: 'move_to_segment',
|
id: 'move_to_segment',
|
||||||
endpoint: 'segments'
|
endpoint: 'segments',
|
||||||
|
filter: function(segment) {
|
||||||
|
return !!(
|
||||||
|
!segment.deleted_at && segment.type === 'default'
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -127,7 +132,12 @@ const bulk_actions = [
|
|||||||
onSelect: function() {
|
onSelect: function() {
|
||||||
let field = {
|
let field = {
|
||||||
id: 'add_to_segment',
|
id: 'add_to_segment',
|
||||||
endpoint: 'segments'
|
endpoint: 'segments',
|
||||||
|
filter: function(segment) {
|
||||||
|
return !!(
|
||||||
|
!segment.deleted_at && segment.type === 'default'
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -153,7 +163,12 @@ const bulk_actions = [
|
|||||||
onSelect: function() {
|
onSelect: function() {
|
||||||
let field = {
|
let field = {
|
||||||
id: 'remove_from_segment',
|
id: 'remove_from_segment',
|
||||||
endpoint: 'segments'
|
endpoint: 'segments',
|
||||||
|
filter: function(segment) {
|
||||||
|
return !!(
|
||||||
|
segment.type === 'default'
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -200,6 +215,21 @@ const bulk_actions = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const item_actions = [
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
label: 'Edit',
|
||||||
|
link: function(item) {
|
||||||
|
return (
|
||||||
|
<Link to={ `/edit/${item.id}` }>Edit</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'trash'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
const SubscriberList = React.createClass({
|
const SubscriberList = React.createClass({
|
||||||
renderItem: function(subscriber, actions) {
|
renderItem: function(subscriber, actions) {
|
||||||
let row_classes = classNames(
|
let row_classes = classNames(
|
||||||
@ -270,11 +300,16 @@ const SubscriberList = React.createClass({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
onGetItems: function(count) {
|
||||||
|
jQuery('#mailpoet_export_button')[(count > 0) ? 'show' : 'hide']();
|
||||||
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2 className="title">
|
<h2 className="title">
|
||||||
Subscribers <Link className="add-new-h2" to="/new">New</Link>
|
Subscribers <Link className="add-new-h2" to="/new">New</Link>
|
||||||
|
<a className="add-new-h2" href="?page=mailpoet-import#step1">Import</a>
|
||||||
|
<a id="mailpoet_export_button" className="add-new-h2" href="?page=mailpoet-export">Export</a>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<Listing
|
<Listing
|
||||||
@ -284,7 +319,9 @@ const SubscriberList = React.createClass({
|
|||||||
onRenderItem={ this.renderItem }
|
onRenderItem={ this.renderItem }
|
||||||
columns={ columns }
|
columns={ columns }
|
||||||
bulk_actions={ bulk_actions }
|
bulk_actions={ bulk_actions }
|
||||||
|
item_actions={ item_actions }
|
||||||
messages={ messages }
|
messages={ messages }
|
||||||
|
onGetItems={ this.onGetItems }
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
264
assets/js/src/vendor_static/jquery.sticky-kit.js
Normal file
264
assets/js/src/vendor_static/jquery.sticky-kit.js
Normal file
@ -0,0 +1,264 @@
|
|||||||
|
// Generated by CoffeeScript 1.9.2
|
||||||
|
|
||||||
|
/**
|
||||||
|
@license Sticky-kit v1.1.2 | WTFPL | Leaf Corcoran 2015 | http://leafo.net
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var $, win;
|
||||||
|
|
||||||
|
$ = this.jQuery || window.jQuery;
|
||||||
|
|
||||||
|
win = $(window);
|
||||||
|
|
||||||
|
$.fn.stick_in_parent = function(opts) {
|
||||||
|
var doc, elm, enable_bottoming, fn, i, inner_scrolling, len, manual_spacer, offset_top, outer_width, parent_selector, recalc_every, sticky_class;
|
||||||
|
if (opts == null) {
|
||||||
|
opts = {};
|
||||||
|
}
|
||||||
|
sticky_class = opts.sticky_class, inner_scrolling = opts.inner_scrolling, recalc_every = opts.recalc_every, parent_selector = opts.parent, offset_top = opts.offset_top, manual_spacer = opts.spacer, enable_bottoming = opts.bottoming;
|
||||||
|
if (offset_top == null) {
|
||||||
|
offset_top = 0;
|
||||||
|
}
|
||||||
|
if (parent_selector == null) {
|
||||||
|
parent_selector = void 0;
|
||||||
|
}
|
||||||
|
if (inner_scrolling == null) {
|
||||||
|
inner_scrolling = true;
|
||||||
|
}
|
||||||
|
if (sticky_class == null) {
|
||||||
|
sticky_class = "is_stuck";
|
||||||
|
}
|
||||||
|
doc = $(document);
|
||||||
|
if (enable_bottoming == null) {
|
||||||
|
enable_bottoming = true;
|
||||||
|
}
|
||||||
|
outer_width = function(el) {
|
||||||
|
var _el, computed, w;
|
||||||
|
if (window.getComputedStyle) {
|
||||||
|
_el = el[0];
|
||||||
|
computed = window.getComputedStyle(el[0]);
|
||||||
|
w = parseFloat(computed.getPropertyValue("width")) + parseFloat(computed.getPropertyValue("margin-left")) + parseFloat(computed.getPropertyValue("margin-right"));
|
||||||
|
if (computed.getPropertyValue("box-sizing") !== "border-box") {
|
||||||
|
w += parseFloat(computed.getPropertyValue("border-left-width")) + parseFloat(computed.getPropertyValue("border-right-width")) + parseFloat(computed.getPropertyValue("padding-left")) + parseFloat(computed.getPropertyValue("padding-right"));
|
||||||
|
}
|
||||||
|
return w;
|
||||||
|
} else {
|
||||||
|
return el.outerWidth(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fn = function(elm, padding_bottom, parent_top, parent_height, top, height, el_float, detached) {
|
||||||
|
var bottomed, detach, fixed, last_pos, last_scroll_height, offset, parent, recalc, recalc_and_tick, recalc_counter, spacer, tick;
|
||||||
|
if (elm.data("sticky_kit")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
elm.data("sticky_kit", true);
|
||||||
|
last_scroll_height = doc.height();
|
||||||
|
parent = elm.parent();
|
||||||
|
if (parent_selector != null) {
|
||||||
|
parent = parent.closest(parent_selector);
|
||||||
|
}
|
||||||
|
if (!parent.length) {
|
||||||
|
throw "failed to find stick parent";
|
||||||
|
}
|
||||||
|
fixed = false;
|
||||||
|
bottomed = false;
|
||||||
|
spacer = manual_spacer != null ? manual_spacer && elm.closest(manual_spacer) : $("<div />");
|
||||||
|
if (spacer) {
|
||||||
|
spacer.css('position', elm.css('position'));
|
||||||
|
}
|
||||||
|
recalc = function() {
|
||||||
|
var border_top, padding_top, restore;
|
||||||
|
if (detached) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
last_scroll_height = doc.height();
|
||||||
|
border_top = parseInt(parent.css("border-top-width"), 10);
|
||||||
|
padding_top = parseInt(parent.css("padding-top"), 10);
|
||||||
|
padding_bottom = parseInt(parent.css("padding-bottom"), 10);
|
||||||
|
parent_top = parent.offset().top + border_top + padding_top;
|
||||||
|
parent_height = parent.height();
|
||||||
|
if (fixed) {
|
||||||
|
fixed = false;
|
||||||
|
bottomed = false;
|
||||||
|
if (manual_spacer == null) {
|
||||||
|
elm.insertAfter(spacer);
|
||||||
|
spacer.detach();
|
||||||
|
}
|
||||||
|
elm.css({
|
||||||
|
position: "",
|
||||||
|
top: "",
|
||||||
|
width: "",
|
||||||
|
bottom: ""
|
||||||
|
}).removeClass(sticky_class);
|
||||||
|
restore = true;
|
||||||
|
}
|
||||||
|
top = elm.offset().top - (parseInt(elm.css("margin-top"), 10) || 0) - offset_top;
|
||||||
|
height = elm.outerHeight(true);
|
||||||
|
el_float = elm.css("float");
|
||||||
|
if (spacer) {
|
||||||
|
spacer.css({
|
||||||
|
width: outer_width(elm),
|
||||||
|
height: height,
|
||||||
|
display: elm.css("display"),
|
||||||
|
"vertical-align": elm.css("vertical-align"),
|
||||||
|
"float": el_float
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (restore) {
|
||||||
|
return tick();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
recalc();
|
||||||
|
|
||||||
|
last_pos = void 0;
|
||||||
|
offset = offset_top;
|
||||||
|
recalc_counter = recalc_every;
|
||||||
|
tick = function() {
|
||||||
|
var css, delta, recalced, scroll, will_bottom, win_height;
|
||||||
|
if (detached) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
recalced = false;
|
||||||
|
if (recalc_counter != null) {
|
||||||
|
recalc_counter -= 1;
|
||||||
|
if (recalc_counter <= 0) {
|
||||||
|
recalc_counter = recalc_every;
|
||||||
|
recalc();
|
||||||
|
recalced = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!recalced && doc.height() !== last_scroll_height) {
|
||||||
|
recalc();
|
||||||
|
recalced = true;
|
||||||
|
}
|
||||||
|
scroll = win.scrollTop();
|
||||||
|
if (last_pos != null) {
|
||||||
|
delta = scroll - last_pos;
|
||||||
|
}
|
||||||
|
last_pos = scroll;
|
||||||
|
if (fixed) {
|
||||||
|
if (enable_bottoming) {
|
||||||
|
will_bottom = scroll + height + offset > parent_height + parent_top;
|
||||||
|
if (bottomed && !will_bottom) {
|
||||||
|
bottomed = false;
|
||||||
|
elm.css({
|
||||||
|
position: "fixed",
|
||||||
|
bottom: "",
|
||||||
|
top: offset
|
||||||
|
}).trigger("sticky_kit:unbottom");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scroll < top) {
|
||||||
|
fixed = false;
|
||||||
|
offset = offset_top;
|
||||||
|
if (manual_spacer == null) {
|
||||||
|
if (el_float === "left" || el_float === "right") {
|
||||||
|
elm.insertAfter(spacer);
|
||||||
|
}
|
||||||
|
spacer.detach();
|
||||||
|
}
|
||||||
|
css = {
|
||||||
|
position: "",
|
||||||
|
width: "",
|
||||||
|
top: ""
|
||||||
|
};
|
||||||
|
elm.css(css).removeClass(sticky_class).trigger("sticky_kit:unstick");
|
||||||
|
}
|
||||||
|
if (inner_scrolling) {
|
||||||
|
win_height = win.height();
|
||||||
|
if (height + offset_top > win_height) {
|
||||||
|
if (!bottomed) {
|
||||||
|
offset -= delta;
|
||||||
|
offset = Math.max(win_height - height, offset);
|
||||||
|
offset = Math.min(offset_top, offset);
|
||||||
|
if (fixed) {
|
||||||
|
elm.css({
|
||||||
|
top: offset + "px"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (scroll > top) {
|
||||||
|
fixed = true;
|
||||||
|
css = {
|
||||||
|
position: "fixed",
|
||||||
|
top: offset
|
||||||
|
};
|
||||||
|
css.width = elm.css("box-sizing") === "border-box" ? elm.outerWidth() + "px" : elm.width() + "px";
|
||||||
|
elm.css(css).addClass(sticky_class);
|
||||||
|
if (manual_spacer == null) {
|
||||||
|
elm.after(spacer);
|
||||||
|
if (el_float === "left" || el_float === "right") {
|
||||||
|
spacer.append(elm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elm.trigger("sticky_kit:stick");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fixed && enable_bottoming) {
|
||||||
|
if (will_bottom == null) {
|
||||||
|
will_bottom = scroll + height + offset > parent_height + parent_top;
|
||||||
|
}
|
||||||
|
if (!bottomed && will_bottom) {
|
||||||
|
bottomed = true;
|
||||||
|
if (parent.css("position") === "static") {
|
||||||
|
parent.css({
|
||||||
|
position: "relative"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return elm.css({
|
||||||
|
position: "absolute",
|
||||||
|
bottom: padding_bottom,
|
||||||
|
top: "auto"
|
||||||
|
}).trigger("sticky_kit:bottom");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
recalc_and_tick = function() {
|
||||||
|
recalc();
|
||||||
|
return tick();
|
||||||
|
};
|
||||||
|
detach = function() {
|
||||||
|
detached = true;
|
||||||
|
win.off("touchmove", tick);
|
||||||
|
win.off("scroll", tick);
|
||||||
|
win.off("resize", recalc_and_tick);
|
||||||
|
$(document.body).off("sticky_kit:recalc", recalc_and_tick);
|
||||||
|
elm.off("sticky_kit:detach", detach);
|
||||||
|
elm.removeData("sticky_kit");
|
||||||
|
elm.css({
|
||||||
|
position: "",
|
||||||
|
bottom: "",
|
||||||
|
top: "",
|
||||||
|
width: ""
|
||||||
|
});
|
||||||
|
parent.position("position", "");
|
||||||
|
if (fixed) {
|
||||||
|
if (manual_spacer == null) {
|
||||||
|
if (el_float === "left" || el_float === "right") {
|
||||||
|
elm.insertAfter(spacer);
|
||||||
|
}
|
||||||
|
spacer.remove();
|
||||||
|
}
|
||||||
|
return elm.removeClass(sticky_class);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
win.on("touchmove", tick);
|
||||||
|
win.on("scroll", tick);
|
||||||
|
win.on("resize", recalc_and_tick);
|
||||||
|
$(document.body).on("sticky_kit:recalc", recalc_and_tick);
|
||||||
|
elm.on("sticky_kit:detach", detach);
|
||||||
|
return setTimeout(tick, 0);
|
||||||
|
};
|
||||||
|
for (i = 0, len = this.length; i < len; i++) {
|
||||||
|
elm = this[i];
|
||||||
|
fn($(elm));
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
}).call(this);
|
||||||
|
|
19
build
19
build
@ -7,21 +7,20 @@ rm wysija-newsletters.zip;
|
|||||||
mkdir wysija-newsletters;
|
mkdir wysija-newsletters;
|
||||||
|
|
||||||
# Production assets.
|
# Production assets.
|
||||||
|
npm install;
|
||||||
./do compile:all;
|
./do compile:all;
|
||||||
|
|
||||||
# Production libraries.
|
# Production libraries.
|
||||||
rm -rf vendor;
|
|
||||||
rm composer.lock;
|
|
||||||
./composer.phar install --no-dev;
|
./composer.phar install --no-dev;
|
||||||
|
|
||||||
# Copy release folders.
|
# Copy release folders.
|
||||||
cp -rf lang wysija-newsletters;
|
cp -Rf lang wysija-newsletters;
|
||||||
cp -rfL assets wysija-newsletters;
|
cp -RfL assets wysija-newsletters;
|
||||||
cp -rf lib wysija-newsletters;
|
cp -Rf lib wysija-newsletters;
|
||||||
cp -rf vendor wysija-newsletters;
|
cp -Rf vendor wysija-newsletters;
|
||||||
cp -rf views wysija-newsletters;
|
cp -Rf views wysija-newsletters;
|
||||||
rm -rf wysija-newsletters/assets/css/src;
|
rm -Rf wysija-newsletters/assets/css/src;
|
||||||
rm -rf wysija-newsletters/assets/js/src;
|
rm -Rf wysija-newsletters/assets/js/src;
|
||||||
|
|
||||||
# Copy release files.
|
# Copy release files.
|
||||||
cp LICENSE wysija-newsletters;
|
cp LICENSE wysija-newsletters;
|
||||||
@ -37,6 +36,4 @@ zip -r wysija-newsletters.zip wysija-newsletters;
|
|||||||
rm -rf wysija-newsletters;
|
rm -rf wysija-newsletters;
|
||||||
|
|
||||||
# Reinstall dev dependencies.
|
# Reinstall dev dependencies.
|
||||||
rm composer.lock;
|
|
||||||
./composer.phar install;
|
./composer.phar install;
|
||||||
./do install;
|
|
||||||
|
@ -8,7 +8,9 @@
|
|||||||
"tburry/pquery": "*",
|
"tburry/pquery": "*",
|
||||||
"j4mie/paris": "1.5.4",
|
"j4mie/paris": "1.5.4",
|
||||||
"swiftmailer/swiftmailer": "^5.4",
|
"swiftmailer/swiftmailer": "^5.4",
|
||||||
"phpseclib/phpseclib": "*"
|
"phpseclib/phpseclib": "*",
|
||||||
|
"mtdowling/cron-expression": "^1.0",
|
||||||
|
"nesbot/carbon": "^1.21"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"codeception/codeception": "*",
|
"codeception/codeception": "*",
|
||||||
|
30
composer.lock
generated
30
composer.lock
generated
@ -520,16 +520,16 @@
|
|||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
{
|
{
|
||||||
"name": "codeception/codeception",
|
"name": "codeception/codeception",
|
||||||
"version": "2.1.3",
|
"version": "2.1.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Codeception/Codeception.git",
|
"url": "https://github.com/Codeception/Codeception.git",
|
||||||
"reference": "cd810cb78a869408602e17271f9b7368b09a7ca8"
|
"reference": "6a812e8a0d1b1db939a29b4dc14cb398b21b6112"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/Codeception/Codeception/zipball/cd810cb78a869408602e17271f9b7368b09a7ca8",
|
"url": "https://api.github.com/repos/Codeception/Codeception/zipball/6a812e8a0d1b1db939a29b4dc14cb398b21b6112",
|
||||||
"reference": "cd810cb78a869408602e17271f9b7368b09a7ca8",
|
"reference": "6a812e8a0d1b1db939a29b4dc14cb398b21b6112",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -596,7 +596,7 @@
|
|||||||
"functional testing",
|
"functional testing",
|
||||||
"unit testing"
|
"unit testing"
|
||||||
],
|
],
|
||||||
"time": "2015-10-02 09:38:59"
|
"time": "2015-11-12 03:57:06"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "codeception/verify",
|
"name": "codeception/verify",
|
||||||
@ -740,16 +740,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "facebook/webdriver",
|
"name": "facebook/webdriver",
|
||||||
"version": "1.0.3",
|
"version": "1.0.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/facebook/php-webdriver.git",
|
"url": "https://github.com/facebook/php-webdriver.git",
|
||||||
"reference": "d843e33fd19b49db5ac9daaef2610079daab0bad"
|
"reference": "a6e209a309bf7cd71acf15476f40b11a25d5a79d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/facebook/php-webdriver/zipball/d843e33fd19b49db5ac9daaef2610079daab0bad",
|
"url": "https://api.github.com/repos/facebook/php-webdriver/zipball/a6e209a309bf7cd71acf15476f40b11a25d5a79d",
|
||||||
"reference": "d843e33fd19b49db5ac9daaef2610079daab0bad",
|
"reference": "a6e209a309bf7cd71acf15476f40b11a25d5a79d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -779,7 +779,7 @@
|
|||||||
"selenium",
|
"selenium",
|
||||||
"webdriver"
|
"webdriver"
|
||||||
],
|
],
|
||||||
"time": "2015-11-01 20:09:34"
|
"time": "2015-11-03 22:17:22"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "guzzlehttp/guzzle",
|
"name": "guzzlehttp/guzzle",
|
||||||
@ -1362,16 +1362,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/phpunit",
|
"name": "phpunit/phpunit",
|
||||||
"version": "4.8.16",
|
"version": "4.8.18",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||||
"reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e"
|
"reference": "fa33d4ad96481b91df343d83e8c8aabed6b1dfd3"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/625f8c345606ed0f3a141dfb88f4116f0e22978e",
|
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fa33d4ad96481b91df343d83e8c8aabed6b1dfd3",
|
||||||
"reference": "625f8c345606ed0f3a141dfb88f4116f0e22978e",
|
"reference": "fa33d4ad96481b91df343d83e8c8aabed6b1dfd3",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@ -1430,7 +1430,7 @@
|
|||||||
"testing",
|
"testing",
|
||||||
"xunit"
|
"xunit"
|
||||||
],
|
],
|
||||||
"time": "2015-10-23 06:48:33"
|
"time": "2015-11-11 11:32:49"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/phpunit-mock-objects",
|
"name": "phpunit/phpunit-mock-objects",
|
||||||
|
25
lib/Analytics/Reporter.php
Normal file
25
lib/Analytics/Reporter.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Analytics;
|
||||||
|
|
||||||
|
class Reporter {
|
||||||
|
|
||||||
|
private $fields = array(
|
||||||
|
'Plugin Version' => 'pluginVersion',
|
||||||
|
);
|
||||||
|
|
||||||
|
function __construct() {}
|
||||||
|
|
||||||
|
function getData() {
|
||||||
|
$_this = $this;
|
||||||
|
|
||||||
|
$analytics_data = array_map(function($func) use ($_this) {
|
||||||
|
return $_this->$func();
|
||||||
|
}, $this->fields);
|
||||||
|
|
||||||
|
return $analytics_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function pluginVersion() {
|
||||||
|
return MAILPOET_VERSION;
|
||||||
|
}
|
||||||
|
}
|
32
lib/Config/Analytics.php
Normal file
32
lib/Config/Analytics.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Config;
|
||||||
|
use \MailPoet\Analytics\Reporter;
|
||||||
|
use \MailPoet\Models\Setting;
|
||||||
|
|
||||||
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class Analytics {
|
||||||
|
function __construct() {
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
add_action('admin_enqueue_scripts', array($this, 'setupAdminDependencies'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupAdminDependencies() {
|
||||||
|
if(Setting::getValue('send_analytics_now', false)) {
|
||||||
|
$analytics = new Reporter();
|
||||||
|
wp_enqueue_script(
|
||||||
|
'analytics',
|
||||||
|
Env::$assets_url . '/js/lib/analytics.js',
|
||||||
|
array(),
|
||||||
|
Env::$version
|
||||||
|
);
|
||||||
|
wp_localize_script(
|
||||||
|
'analytics',
|
||||||
|
'mailpoet_analytics_data',
|
||||||
|
$analytics->getData()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
lib/Config/Changelog.php
Normal file
61
lib/Config/Changelog.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Config;
|
||||||
|
use \MailPoet\Models\Setting;
|
||||||
|
|
||||||
|
class Changelog {
|
||||||
|
function init() {
|
||||||
|
$doing_ajax = (bool)(defined('DOING_AJAX') && DOING_AJAX);
|
||||||
|
|
||||||
|
// don't run any check when it's an ajax request
|
||||||
|
if($doing_ajax) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't run any check when we're not on our pages
|
||||||
|
if(
|
||||||
|
!(isset($_GET['page']))
|
||||||
|
or
|
||||||
|
(isset($_GET['page']) && strpos($_GET['page'], 'mailpoet') !== 0)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
add_action(
|
||||||
|
'admin_init',
|
||||||
|
array($this, 'check')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function check() {
|
||||||
|
$version = Setting::getValue('version', null);
|
||||||
|
$redirect_url = null;
|
||||||
|
|
||||||
|
if($version === null) {
|
||||||
|
// new install
|
||||||
|
$redirect_url = admin_url('admin.php?page=mailpoet-welcome');
|
||||||
|
} else if($version !== Env::$version) {
|
||||||
|
// update
|
||||||
|
$redirect_url = admin_url('admin.php?page=mailpoet-update');
|
||||||
|
}
|
||||||
|
|
||||||
|
if($redirect_url !== null) {
|
||||||
|
// save version number
|
||||||
|
Setting::setValue('version', Env::$version);
|
||||||
|
|
||||||
|
global $wp;
|
||||||
|
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
|
||||||
|
|
||||||
|
if($redirect_url !== $current_url) {
|
||||||
|
wp_safe_redirect(
|
||||||
|
add_query_arg(
|
||||||
|
array(
|
||||||
|
'mailpoet_redirect' => urlencode($current_url)
|
||||||
|
),
|
||||||
|
$redirect_url
|
||||||
|
)
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,53 +4,84 @@ namespace MailPoet\Config;
|
|||||||
if(!defined('ABSPATH')) exit;
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
class Env {
|
class Env {
|
||||||
public static $version;
|
static $version;
|
||||||
public static $plugin_name;
|
static $plugin_name;
|
||||||
public static $file;
|
static $plugin_url;
|
||||||
public static $path;
|
static $plugin_path;
|
||||||
public static $views_path;
|
static $file;
|
||||||
public static $assets_path;
|
static $path;
|
||||||
public static $assets_url;
|
static $views_path;
|
||||||
public static $languages_path;
|
static $assets_path;
|
||||||
public static $lib_path;
|
static $assets_url;
|
||||||
public static $plugin_prefix;
|
static $temp_name;
|
||||||
public static $db_prefix;
|
static $temp_path;
|
||||||
public static $db_source_name;
|
static $languages_path;
|
||||||
public static $db_host;
|
static $lib_path;
|
||||||
public static $db_name;
|
static $plugin_prefix;
|
||||||
public static $db_username;
|
static $db_prefix;
|
||||||
public static $db_password;
|
static $db_source_name;
|
||||||
public static $db_charset;
|
static $db_host;
|
||||||
|
static $db_socket;
|
||||||
|
static $db_port;
|
||||||
|
static $db_name;
|
||||||
|
static $db_username;
|
||||||
|
static $db_password;
|
||||||
|
static $db_charset;
|
||||||
|
|
||||||
public static function init($file, $version) {
|
static function init($file, $version) {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
self::$version = $version;
|
self::$version = $version;
|
||||||
self::$plugin_name = 'mailpoet';
|
|
||||||
self::$file = $file;
|
self::$file = $file;
|
||||||
self::$path = dirname(self::$file);
|
self::$path = dirname(self::$file);
|
||||||
|
self::$plugin_name = 'mailpoet';
|
||||||
|
self::$plugin_url = plugin_dir_url(__FILE__);
|
||||||
self::$views_path = self::$path . '/views';
|
self::$views_path = self::$path . '/views';
|
||||||
self::$assets_path = self::$path . '/assets';
|
self::$assets_path = self::$path . '/assets';
|
||||||
self::$assets_url = plugins_url('/assets', $file);
|
self::$assets_url = plugins_url('/assets', $file);
|
||||||
|
self::$temp_name = 'temp';
|
||||||
|
self::$temp_path = self::$path . '/' . self::$temp_name;
|
||||||
self::$languages_path = self::$path . '/lang';
|
self::$languages_path = self::$path . '/lang';
|
||||||
self::$lib_path = self::$path . '/lib';
|
self::$lib_path = self::$path . '/lib';
|
||||||
self::$plugin_prefix = 'mailpoet_';
|
self::$plugin_prefix = 'mailpoet_';
|
||||||
self::$db_prefix = $wpdb->prefix . self::$plugin_prefix;
|
self::$db_prefix = $wpdb->prefix . self::$plugin_prefix;
|
||||||
self::$db_source_name = self::dbSourceName();
|
|
||||||
self::$db_host = DB_HOST;
|
self::$db_host = DB_HOST;
|
||||||
|
self::$db_port = 3306;
|
||||||
|
self::$db_socket = false;
|
||||||
|
if(preg_match('/(?=:\d+$)/', DB_HOST)) {
|
||||||
|
list(self::$db_host, self::$db_port) = explode(':', DB_HOST);
|
||||||
|
} else {
|
||||||
|
if(preg_match('/:/', DB_HOST)) {
|
||||||
|
self::$db_socket = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
self::$db_name = DB_NAME;
|
self::$db_name = DB_NAME;
|
||||||
self::$db_username = DB_USER;
|
self::$db_username = DB_USER;
|
||||||
self::$db_password = DB_PASSWORD;
|
self::$db_password = DB_PASSWORD;
|
||||||
self::$db_charset = $wpdb->get_charset_collate();
|
self::$db_charset = $wpdb->get_charset_collate();
|
||||||
|
self::$db_source_name = self::dbSourceName(self::$db_host, self::$db_socket, self::$db_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function dbSourceName() {
|
private static function dbSourceName($host, $socket, $port) {
|
||||||
$source_name = array(
|
$source_name = array(
|
||||||
'mysql:host=',
|
(!$socket) ? 'mysql:host=' : 'mysql:unix_socket=',
|
||||||
DB_HOST,
|
$host,
|
||||||
|
';',
|
||||||
|
'port=',
|
||||||
|
$port,
|
||||||
';',
|
';',
|
||||||
'dbname=',
|
'dbname=',
|
||||||
DB_NAME
|
DB_NAME
|
||||||
);
|
);
|
||||||
return implode('', $source_name);
|
return implode('', $source_name);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
static function isPluginActivated() {
|
||||||
|
$activatesPlugins = get_option('active_plugins');
|
||||||
|
$isActivated =
|
||||||
|
in_array(
|
||||||
|
sprintf('%s/%s.php', basename(self::$path), self::$plugin_name),
|
||||||
|
$activatesPlugins
|
||||||
|
);
|
||||||
|
return ($isActivated) ? true : false;
|
||||||
|
}
|
||||||
|
}
|
42
lib/Config/Hooks.php
Normal file
42
lib/Config/Hooks.php
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Config;
|
||||||
|
|
||||||
|
class Hooks {
|
||||||
|
function __construct() {
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
// WP Users synchronization
|
||||||
|
add_action(
|
||||||
|
'user_register',
|
||||||
|
'\MailPoet\Segments\WP::synchronizeUser',
|
||||||
|
1
|
||||||
|
);
|
||||||
|
add_action(
|
||||||
|
'added_existing_user',
|
||||||
|
'\MailPoet\Segments\WP::synchronizeUser',
|
||||||
|
1
|
||||||
|
);
|
||||||
|
add_action(
|
||||||
|
'profile_update',
|
||||||
|
'\MailPoet\Segments\WP::synchronizeUser',
|
||||||
|
1
|
||||||
|
);
|
||||||
|
add_action(
|
||||||
|
'delete_user',
|
||||||
|
'\MailPoet\Segments\WP::synchronizeUser',
|
||||||
|
1
|
||||||
|
);
|
||||||
|
// multisite
|
||||||
|
add_action(
|
||||||
|
'deleted_user',
|
||||||
|
'\MailPoet\Segments\WP::synchronizeUser',
|
||||||
|
1
|
||||||
|
);
|
||||||
|
add_action(
|
||||||
|
'remove_user_from_blog',
|
||||||
|
'\MailPoet\Segments\WP::synchronizeUser',
|
||||||
|
1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
namespace MailPoet\Config;
|
namespace MailPoet\Config;
|
||||||
|
|
||||||
use MailPoet\Models;
|
use MailPoet\Models;
|
||||||
|
use MailPoet\Queue\Supervisor;
|
||||||
use MailPoet\Router;
|
use MailPoet\Router;
|
||||||
|
|
||||||
if(!defined('ABSPATH')) exit;
|
if(!defined('ABSPATH')) exit;
|
||||||
@ -22,7 +23,12 @@ class Initializer {
|
|||||||
$this->setupMenu();
|
$this->setupMenu();
|
||||||
$this->setupRouter();
|
$this->setupRouter();
|
||||||
$this->setupWidget();
|
$this->setupWidget();
|
||||||
|
$this->setupAnalytics();
|
||||||
$this->setupPermissions();
|
$this->setupPermissions();
|
||||||
|
$this->setupChangelog();
|
||||||
|
$this->setupPublicAPI();
|
||||||
|
$this->runQueueSupervisor();
|
||||||
|
$this->setupHooks();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupDB() {
|
function setupDB() {
|
||||||
@ -31,7 +37,8 @@ class Initializer {
|
|||||||
\ORM::configure('password', Env::$db_password);
|
\ORM::configure('password', Env::$db_password);
|
||||||
\ORM::configure('logging', WP_DEBUG);
|
\ORM::configure('logging', WP_DEBUG);
|
||||||
\ORM::configure('driver_options', array(
|
\ORM::configure('driver_options', array(
|
||||||
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
|
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
|
||||||
|
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET TIME_ZONE = "+00:00"'
|
||||||
));
|
));
|
||||||
|
|
||||||
$subscribers = Env::$db_prefix . 'subscribers';
|
$subscribers = Env::$db_prefix . 'subscribers';
|
||||||
@ -39,6 +46,8 @@ class Initializer {
|
|||||||
$newsletters = Env::$db_prefix . 'newsletters';
|
$newsletters = Env::$db_prefix . 'newsletters';
|
||||||
$newsletter_templates = Env::$db_prefix . 'newsletter_templates';
|
$newsletter_templates = Env::$db_prefix . 'newsletter_templates';
|
||||||
$segments = Env::$db_prefix . 'segments';
|
$segments = Env::$db_prefix . 'segments';
|
||||||
|
$filters = Env::$db_prefix . 'filters';
|
||||||
|
$segment_filter = Env::$db_prefix . 'segment_filter';
|
||||||
$forms = Env::$db_prefix . 'forms';
|
$forms = Env::$db_prefix . 'forms';
|
||||||
$subscriber_segment = Env::$db_prefix . 'subscriber_segment';
|
$subscriber_segment = Env::$db_prefix . 'subscriber_segment';
|
||||||
$newsletter_segment = Env::$db_prefix . 'newsletter_segment';
|
$newsletter_segment = Env::$db_prefix . 'newsletter_segment';
|
||||||
@ -46,11 +55,15 @@ class Initializer {
|
|||||||
$subscriber_custom_field = Env::$db_prefix . 'subscriber_custom_field';
|
$subscriber_custom_field = Env::$db_prefix . 'subscriber_custom_field';
|
||||||
$newsletter_option_fields = Env::$db_prefix . 'newsletter_option_fields';
|
$newsletter_option_fields = Env::$db_prefix . 'newsletter_option_fields';
|
||||||
$newsletter_option = Env::$db_prefix . 'newsletter_option';
|
$newsletter_option = Env::$db_prefix . 'newsletter_option';
|
||||||
|
$queues = Env::$db_prefix . 'queues';
|
||||||
|
$newsletter_statistics = Env::$db_prefix . 'newsletter_statistics';
|
||||||
|
|
||||||
define('MP_SUBSCRIBERS_TABLE', $subscribers);
|
define('MP_SUBSCRIBERS_TABLE', $subscribers);
|
||||||
define('MP_SETTINGS_TABLE', $settings);
|
define('MP_SETTINGS_TABLE', $settings);
|
||||||
define('MP_NEWSLETTERS_TABLE', $newsletters);
|
define('MP_NEWSLETTERS_TABLE', $newsletters);
|
||||||
define('MP_SEGMENTS_TABLE', $segments);
|
define('MP_SEGMENTS_TABLE', $segments);
|
||||||
|
define('MP_FILTERS_TABLE', $filters);
|
||||||
|
define('MP_SEGMENT_FILTER_TABLE', $segment_filter);
|
||||||
define('MP_FORMS_TABLE', $forms);
|
define('MP_FORMS_TABLE', $forms);
|
||||||
define('MP_SUBSCRIBER_SEGMENT_TABLE', $subscriber_segment);
|
define('MP_SUBSCRIBER_SEGMENT_TABLE', $subscriber_segment);
|
||||||
define('MP_NEWSLETTER_TEMPLATES_TABLE', $newsletter_templates);
|
define('MP_NEWSLETTER_TEMPLATES_TABLE', $newsletter_templates);
|
||||||
@ -59,6 +72,8 @@ class Initializer {
|
|||||||
define('MP_SUBSCRIBER_CUSTOM_FIELD_TABLE', $subscriber_custom_field);
|
define('MP_SUBSCRIBER_CUSTOM_FIELD_TABLE', $subscriber_custom_field);
|
||||||
define('MP_NEWSLETTER_OPTION_FIELDS_TABLE', $newsletter_option_fields);
|
define('MP_NEWSLETTER_OPTION_FIELDS_TABLE', $newsletter_option_fields);
|
||||||
define('MP_NEWSLETTER_OPTION_TABLE', $newsletter_option);
|
define('MP_NEWSLETTER_OPTION_TABLE', $newsletter_option);
|
||||||
|
define('MP_QUEUES_TABLE', $queues);
|
||||||
|
define('MP_NEWSLETTER_STATISTICS_TABLE', $newsletter_statistics);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupActivator() {
|
function setupActivator() {
|
||||||
@ -94,8 +109,35 @@ class Initializer {
|
|||||||
$widget->init();
|
$widget->init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupAnalytics() {
|
||||||
|
$widget = new Analytics();
|
||||||
|
$widget->init();
|
||||||
|
}
|
||||||
|
|
||||||
function setupPermissions() {
|
function setupPermissions() {
|
||||||
$permissions = new Permissions();
|
$permissions = new Permissions();
|
||||||
$permissions->init();
|
$permissions->init();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
function setupChangelog() {
|
||||||
|
$changelog = new Changelog();
|
||||||
|
$changelog->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupHooks() {
|
||||||
|
$hooks = new Hooks();
|
||||||
|
$hooks->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupPublicAPI() {
|
||||||
|
$publicAPI = new PublicAPI();
|
||||||
|
$publicAPI->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
function runQueueSupervisor() {
|
||||||
|
try {
|
||||||
|
$supervisor = new Supervisor();
|
||||||
|
$supervisor->checkDaemon();
|
||||||
|
} catch (\Exception $e) {}
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,17 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace MailPoet\Config;
|
namespace MailPoet\Config;
|
||||||
use \MailPoet\Models\Segment;
|
|
||||||
use \MailPoet\Models\Setting;
|
use MailPoet\Form\Block;
|
||||||
use \MailPoet\Models\Form;
|
use MailPoet\Form\Renderer as FormRenderer;
|
||||||
use \MailPoet\Form\Block;
|
use MailPoet\Models\Form;
|
||||||
use \MailPoet\Form\Renderer as FormRenderer;
|
use MailPoet\Models\Segment;
|
||||||
use \MailPoet\Settings\Hosts;
|
use MailPoet\Models\Setting;
|
||||||
use \MailPoet\Settings\Pages;
|
use MailPoet\Settings\Charsets;
|
||||||
use \MailPoet\Settings\Charsets;
|
use MailPoet\Settings\Hosts;
|
||||||
use \MailPoet\Util\Permissions;
|
use MailPoet\Settings\Pages;
|
||||||
use \MailPoet\Util\DKIM;
|
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
|
||||||
|
use MailPoet\Util\DKIM;
|
||||||
|
use MailPoet\Util\Permissions;
|
||||||
|
|
||||||
if(!defined('ABSPATH')) exit;
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
@ -22,7 +24,10 @@ class Menu {
|
|||||||
function init() {
|
function init() {
|
||||||
add_action(
|
add_action(
|
||||||
'admin_menu',
|
'admin_menu',
|
||||||
array($this, 'setup')
|
array(
|
||||||
|
$this,
|
||||||
|
'setup'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +37,10 @@ class Menu {
|
|||||||
'MailPoet',
|
'MailPoet',
|
||||||
'manage_options',
|
'manage_options',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
array($this, 'home'),
|
array(
|
||||||
|
$this,
|
||||||
|
'home'
|
||||||
|
),
|
||||||
$this->assets_url . '/img/menu_icon.png',
|
$this->assets_url . '/img/menu_icon.png',
|
||||||
30
|
30
|
||||||
);
|
);
|
||||||
@ -42,7 +50,10 @@ class Menu {
|
|||||||
__('Newsletters'),
|
__('Newsletters'),
|
||||||
'manage_options',
|
'manage_options',
|
||||||
'mailpoet-newsletters',
|
'mailpoet-newsletters',
|
||||||
array($this, 'newsletters')
|
array(
|
||||||
|
$this,
|
||||||
|
'newsletters'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
@ -50,7 +61,10 @@ class Menu {
|
|||||||
__('Forms'),
|
__('Forms'),
|
||||||
'manage_options',
|
'manage_options',
|
||||||
'mailpoet-forms',
|
'mailpoet-forms',
|
||||||
array($this, 'forms')
|
array(
|
||||||
|
$this,
|
||||||
|
'forms'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
@ -58,7 +72,10 @@ class Menu {
|
|||||||
__('Subscribers'),
|
__('Subscribers'),
|
||||||
'manage_options',
|
'manage_options',
|
||||||
'mailpoet-subscribers',
|
'mailpoet-subscribers',
|
||||||
array($this, 'subscribers')
|
array(
|
||||||
|
$this,
|
||||||
|
'subscribers'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
@ -66,7 +83,10 @@ class Menu {
|
|||||||
__('Segments'),
|
__('Segments'),
|
||||||
'manage_options',
|
'manage_options',
|
||||||
'mailpoet-segments',
|
'mailpoet-segments',
|
||||||
array($this, 'segments')
|
array(
|
||||||
|
$this,
|
||||||
|
'segments'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
add_submenu_page(
|
add_submenu_page(
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
@ -74,32 +94,93 @@ class Menu {
|
|||||||
__('Settings'),
|
__('Settings'),
|
||||||
'manage_options',
|
'manage_options',
|
||||||
'mailpoet-settings',
|
'mailpoet-settings',
|
||||||
array($this, 'settings')
|
array(
|
||||||
|
$this,
|
||||||
|
'settings'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
add_submenu_page(
|
||||||
|
null,
|
||||||
|
__('Import'),
|
||||||
|
__('Import'),
|
||||||
|
'manage_options',
|
||||||
|
'mailpoet-import',
|
||||||
|
array(
|
||||||
|
$this,
|
||||||
|
'import'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
add_submenu_page(
|
||||||
|
null,
|
||||||
|
__('Export'),
|
||||||
|
__('Export'),
|
||||||
|
'manage_options',
|
||||||
|
'mailpoet-export',
|
||||||
|
array(
|
||||||
|
$this,
|
||||||
|
'export'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
// add_submenu_page(
|
|
||||||
// 'mailpoet',
|
|
||||||
// __('Newsletter editor'),
|
|
||||||
// __('Newsletter editor'),
|
|
||||||
// 'manage_options',
|
|
||||||
// 'mailpoet-newsletter-editor',
|
|
||||||
// array($this, 'newletterEditor')
|
|
||||||
// );
|
|
||||||
$this->registered_pages();
|
|
||||||
}
|
|
||||||
|
|
||||||
function registered_pages() {
|
add_submenu_page(
|
||||||
global $_registered_pages;
|
null,
|
||||||
$pages = array(
|
__('Welcome'),
|
||||||
'mailpoet-form-editor' => array($this, 'formEditor'),
|
__('Welcome'),
|
||||||
'mailpoet-newsletter-editor' => array($this, 'newletterEditor')
|
'manage_options',
|
||||||
|
'mailpoet-welcome',
|
||||||
|
array(
|
||||||
|
$this,
|
||||||
|
'welcome'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
add_submenu_page(
|
||||||
|
null,
|
||||||
|
__('Update'),
|
||||||
|
__('Update'),
|
||||||
|
'manage_options',
|
||||||
|
'mailpoet-update',
|
||||||
|
array(
|
||||||
|
$this,
|
||||||
|
'update'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
add_submenu_page(
|
||||||
|
null,
|
||||||
|
__('Form editor'),
|
||||||
|
__('Form editor'),
|
||||||
|
'manage_options',
|
||||||
|
'mailpoet-form-editor',
|
||||||
|
array(
|
||||||
|
$this,
|
||||||
|
'formEditor'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
add_submenu_page(
|
||||||
|
null,
|
||||||
|
__('Newsletter editor'),
|
||||||
|
__('Newsletter editor'),
|
||||||
|
'manage_options',
|
||||||
|
'mailpoet-newsletter-editor',
|
||||||
|
array(
|
||||||
|
$this,
|
||||||
|
'newletterEditor'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
add_submenu_page(
|
||||||
|
'mailpoet',
|
||||||
|
__('Queue'),
|
||||||
|
__('Queue'),
|
||||||
|
'manage_options',
|
||||||
|
'mailpoet-queue',
|
||||||
|
array(
|
||||||
|
$this,
|
||||||
|
'queue'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
foreach($pages as $menu_slug => $callback) {
|
|
||||||
$hookname = get_plugin_page_hookname($menu_slug, null);
|
|
||||||
if(!empty($hookname)) {
|
|
||||||
add_action($hookname, $callback);
|
|
||||||
}
|
|
||||||
$_registered_pages[$hookname] = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function home() {
|
function home() {
|
||||||
@ -107,26 +188,56 @@ class Menu {
|
|||||||
echo $this->renderer->render('index.html', $data);
|
echo $this->renderer->render('index.html', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function settings() {
|
function welcome() {
|
||||||
// flags (available features on WP install)
|
global $wp;
|
||||||
$flags = array();
|
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
|
||||||
|
$redirect_url =
|
||||||
|
(!empty($_GET['mailpoet_redirect']))
|
||||||
|
? urldecode($_GET['mailpoet_redirect'])
|
||||||
|
: wp_get_referer();
|
||||||
|
|
||||||
if(is_multisite()) {
|
if(
|
||||||
// get multisite registration option
|
$redirect_url === $current_url
|
||||||
$registration = apply_filters(
|
or
|
||||||
'wpmu_registration_enabled',
|
strpos($redirect_url, 'mailpoet') === false
|
||||||
get_site_option('registration', 'all')
|
) {
|
||||||
);
|
$redirect_url = admin_url('admin.php?page=mailpoet');
|
||||||
|
|
||||||
// check if users can register
|
|
||||||
$flags['registration_enabled'] =
|
|
||||||
!(in_array($registration, array('none', 'blog')));
|
|
||||||
} else {
|
|
||||||
// check if users can register
|
|
||||||
$flags['registration_enabled'] =
|
|
||||||
(bool)get_option('users_can_register', false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'settings' => Setting::getAll(),
|
||||||
|
'current_user' => wp_get_current_user(),
|
||||||
|
'redirect_url' => $redirect_url
|
||||||
|
);
|
||||||
|
echo $this->renderer->render('welcome.html', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update() {
|
||||||
|
global $wp;
|
||||||
|
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
|
||||||
|
$redirect_url =
|
||||||
|
(!empty($_GET['mailpoet_redirect']))
|
||||||
|
? urldecode($_GET['mailpoet_redirect'])
|
||||||
|
: wp_get_referer();
|
||||||
|
|
||||||
|
if(
|
||||||
|
$redirect_url === $current_url
|
||||||
|
or
|
||||||
|
strpos($redirect_url, 'mailpoet') === false
|
||||||
|
) {
|
||||||
|
$redirect_url = admin_url('admin.php?page=mailpoet');
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = array(
|
||||||
|
'settings' => Setting::getAll(),
|
||||||
|
'current_user' => wp_get_current_user(),
|
||||||
|
'redirect_url' => $redirect_url
|
||||||
|
);
|
||||||
|
|
||||||
|
echo $this->renderer->render('update.html', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function settings() {
|
||||||
$settings = Setting::getAll();
|
$settings = Setting::getAll();
|
||||||
|
|
||||||
// dkim: check if public/private keys have been generated
|
// dkim: check if public/private keys have been generated
|
||||||
@ -146,9 +257,10 @@ class Menu {
|
|||||||
|
|
||||||
$data = array(
|
$data = array(
|
||||||
'settings' => $settings,
|
'settings' => $settings,
|
||||||
'segments' => Segment::findArray(),
|
'segments' => Segment::getPublished()
|
||||||
|
->findArray(),
|
||||||
'pages' => Pages::getAll(),
|
'pages' => Pages::getAll(),
|
||||||
'flags' => $flags,
|
'flags' => $this->_getFlags(),
|
||||||
'charsets' => Charsets::getAll(),
|
'charsets' => Charsets::getAll(),
|
||||||
'current_user' => wp_get_current_user(),
|
'current_user' => wp_get_current_user(),
|
||||||
'permissions' => Permissions::getAll(),
|
'permissions' => Permissions::getAll(),
|
||||||
@ -161,12 +273,38 @@ class Menu {
|
|||||||
echo $this->renderer->render('settings.html', $data);
|
echo $this->renderer->render('settings.html', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function _getFlags() {
|
||||||
|
// flags (available features on WP install)
|
||||||
|
$flags = array();
|
||||||
|
|
||||||
|
if(is_multisite()) {
|
||||||
|
// get multisite registration option
|
||||||
|
$registration = apply_filters(
|
||||||
|
'wpmu_registration_enabled',
|
||||||
|
get_site_option('registration', 'all')
|
||||||
|
);
|
||||||
|
|
||||||
|
// check if users can register
|
||||||
|
$flags['registration_enabled'] =
|
||||||
|
!(in_array($registration, array(
|
||||||
|
'none',
|
||||||
|
'blog'
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
// check if users can register
|
||||||
|
$flags['registration_enabled'] =
|
||||||
|
(bool) get_option('users_can_register', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $flags;
|
||||||
|
}
|
||||||
|
|
||||||
function subscribers() {
|
function subscribers() {
|
||||||
$data = array();
|
$data = array();
|
||||||
|
|
||||||
$data['segments'] = Segment::findArray();
|
$data['segments'] = Segment::findArray();
|
||||||
|
|
||||||
echo $this->renderer->render('subscribers.html', $data);
|
echo $this->renderer->render('subscribers/subscribers.html', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
function segments() {
|
function segments() {
|
||||||
@ -189,7 +327,7 @@ class Menu {
|
|||||||
$data['segments'] = Segment::findArray();
|
$data['segments'] = Segment::findArray();
|
||||||
$settings = Setting::findArray();
|
$settings = Setting::findArray();
|
||||||
$data['settings'] = array();
|
$data['settings'] = array();
|
||||||
foreach($settings as $setting) {
|
foreach ($settings as $setting) {
|
||||||
$data['settings'][$setting['name']] = $setting['value'];
|
$data['settings'][$setting['name']] = $setting['value'];
|
||||||
}
|
}
|
||||||
$data['roles'] = $wp_roles->get_names();
|
$data['roles'] = $wp_roles->get_names();
|
||||||
@ -204,8 +342,20 @@ class Menu {
|
|||||||
echo $this->renderer->render('newsletter/form.html', $data);
|
echo $this->renderer->render('newsletter/form.html', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function import() {
|
||||||
|
$import = new BootStrapMenu('import');
|
||||||
|
$data = $import->bootstrap();
|
||||||
|
echo $this->renderer->render('subscribers/importExport/import.html', $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function export() {
|
||||||
|
$export = new BootStrapMenu('export');
|
||||||
|
$data = $export->bootstrap();
|
||||||
|
echo $this->renderer->render('subscribers/importExport/export.html', $data);
|
||||||
|
}
|
||||||
|
|
||||||
function formEditor() {
|
function formEditor() {
|
||||||
$id = (isset($_GET['id']) ? (int)$_GET['id'] : 0);
|
$id = (isset($_GET['id']) ? (int) $_GET['id'] : 0);
|
||||||
$form = Form::findOne($id);
|
$form = Form::findOne($id);
|
||||||
if($form !== false) {
|
if($form !== false) {
|
||||||
$form = $form->asArray();
|
$form = $form->asArray();
|
||||||
@ -214,7 +364,8 @@ class Menu {
|
|||||||
$data = array(
|
$data = array(
|
||||||
'form' => $form,
|
'form' => $form,
|
||||||
'pages' => Pages::getAll(),
|
'pages' => Pages::getAll(),
|
||||||
'segments' => Segment::findArray(),
|
'segments' => Segment::getPublished()
|
||||||
|
->findArray(),
|
||||||
'styles' => FormRenderer::getStyles($form),
|
'styles' => FormRenderer::getStyles($form),
|
||||||
'date_types' => Block\Date::getDateTypes(),
|
'date_types' => Block\Date::getDateTypes(),
|
||||||
'date_formats' => Block\Date::getDateFormats()
|
'date_formats' => Block\Date::getDateFormats()
|
||||||
@ -222,4 +373,10 @@ class Menu {
|
|||||||
|
|
||||||
echo $this->renderer->render('form/editor.html', $data);
|
echo $this->renderer->render('form/editor.html', $data);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
function queue() {
|
||||||
|
$daemon = new \MailPoet\Queue\BootStrapMenu();
|
||||||
|
$data['daemon'] = json_encode($daemon->bootstrap());
|
||||||
|
echo $this->renderer->render('queue.html', $data);
|
||||||
|
}
|
||||||
|
}
|
@ -21,6 +21,8 @@ class Migrator {
|
|||||||
'subscriber_custom_field',
|
'subscriber_custom_field',
|
||||||
'newsletter_option_fields',
|
'newsletter_option_fields',
|
||||||
'newsletter_option',
|
'newsletter_option',
|
||||||
|
'queues',
|
||||||
|
'newsletter_statistics',
|
||||||
'forms'
|
'forms'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -50,6 +52,7 @@ class Migrator {
|
|||||||
function subscribers() {
|
function subscribers() {
|
||||||
$attributes = array(
|
$attributes = array(
|
||||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||||
|
'wp_user_id bigint(20) NULL,',
|
||||||
'first_name tinytext NOT NULL,',
|
'first_name tinytext NOT NULL,',
|
||||||
'last_name tinytext NOT NULL,',
|
'last_name tinytext NOT NULL,',
|
||||||
'email varchar(150) NOT NULL,',
|
'email varchar(150) NOT NULL,',
|
||||||
@ -97,6 +100,7 @@ class Migrator {
|
|||||||
'name varchar(250) NOT NULL,',
|
'name varchar(250) NOT NULL,',
|
||||||
'description varchar(250) NOT NULL,',
|
'description varchar(250) NOT NULL,',
|
||||||
'body longtext,',
|
'body longtext,',
|
||||||
|
'thumbnail longtext,',
|
||||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||||
'PRIMARY KEY (id)'
|
'PRIMARY KEY (id)'
|
||||||
@ -106,14 +110,15 @@ class Migrator {
|
|||||||
|
|
||||||
function segments() {
|
function segments() {
|
||||||
$attributes = array(
|
$attributes = array(
|
||||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||||
'name varchar(90) NOT NULL,',
|
'name varchar(90) NOT NULL,',
|
||||||
'description varchar(250) NOT NULL,',
|
'type varchar(90) NOT NULL DEFAULT "default",',
|
||||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
'description varchar(250) NOT NULL,',
|
||||||
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
|
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
|
||||||
'PRIMARY KEY (id),',
|
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||||
'UNIQUE KEY name (name)'
|
'PRIMARY KEY (id),',
|
||||||
|
'UNIQUE KEY name (name)'
|
||||||
);
|
);
|
||||||
return $this->sqlify(__FUNCTION__, $attributes);
|
return $this->sqlify(__FUNCTION__, $attributes);
|
||||||
}
|
}
|
||||||
@ -123,6 +128,7 @@ class Migrator {
|
|||||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||||
'subscriber_id mediumint(9) NOT NULL,',
|
'subscriber_id mediumint(9) NOT NULL,',
|
||||||
'segment_id mediumint(9) NOT NULL,',
|
'segment_id mediumint(9) NOT NULL,',
|
||||||
|
'status varchar(12) NOT NULL DEFAULT "subscribed",',
|
||||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||||
'PRIMARY KEY (id),',
|
'PRIMARY KEY (id),',
|
||||||
@ -166,7 +172,8 @@ class Migrator {
|
|||||||
'value varchar(255) NOT NULL,',
|
'value varchar(255) NOT NULL,',
|
||||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||||
'PRIMARY KEY (id)'
|
'PRIMARY KEY (id),',
|
||||||
|
'UNIQUE KEY subscriber_id_custom_field_id (subscriber_id,custom_field_id)'
|
||||||
);
|
);
|
||||||
return $this->sqlify(__FUNCTION__, $attributes);
|
return $this->sqlify(__FUNCTION__, $attributes);
|
||||||
}
|
}
|
||||||
@ -192,7 +199,43 @@ class Migrator {
|
|||||||
'value varchar(255) NOT NULL,',
|
'value varchar(255) NOT NULL,',
|
||||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||||
'PRIMARY KEY (id)'
|
'PRIMARY KEY (id),',
|
||||||
|
'UNIQUE KEY newsletter_id_option_field_id (newsletter_id,option_field_id)'
|
||||||
|
);
|
||||||
|
return $this->sqlify(__FUNCTION__, $attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function queues() {
|
||||||
|
$attributes = array(
|
||||||
|
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||||
|
'newsletter_id mediumint(9) NOT NULL,',
|
||||||
|
'subscribers longtext,',
|
||||||
|
'status varchar(12) NOT NULL,',
|
||||||
|
'priority mediumint(9) NOT NULL DEFAULT 0,',
|
||||||
|
'count_total mediumint(9) NOT NULL DEFAULT 0,',
|
||||||
|
'count_processed mediumint(9) NOT NULL DEFAULT 0,',
|
||||||
|
'count_to_process mediumint(9) NOT NULL DEFAULT 0,',
|
||||||
|
'count_failed mediumint(9) NOT NULL DEFAULT 0,',
|
||||||
|
'processed_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||||
|
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||||
|
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||||
|
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
|
||||||
|
'PRIMARY KEY (id)',
|
||||||
|
);
|
||||||
|
return $this->sqlify(__FUNCTION__, $attributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
function newsletter_statistics() {
|
||||||
|
$attributes = array(
|
||||||
|
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||||
|
'newsletter_id mediumint(9) NOT NULL,',
|
||||||
|
'subscriber_id mediumint(9) NOT NULL,',
|
||||||
|
'queue_id mediumint(9) NOT NULL,',
|
||||||
|
'sent_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||||
|
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||||
|
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||||
|
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
|
||||||
|
'PRIMARY KEY (id)',
|
||||||
);
|
);
|
||||||
return $this->sqlify(__FUNCTION__, $attributes);
|
return $this->sqlify(__FUNCTION__, $attributes);
|
||||||
}
|
}
|
||||||
@ -222,4 +265,4 @@ class Migrator {
|
|||||||
|
|
||||||
return implode("\n", $sql);
|
return implode("\n", $sql);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace MailPoet\Config;
|
namespace MailPoet\Config;
|
||||||
|
|
||||||
|
use MailPoet\Config\PopulatorData\Templates\SampleTemplate;
|
||||||
|
|
||||||
if (!defined('ABSPATH')) exit;
|
if (!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||||
@ -10,6 +12,7 @@ class Populator {
|
|||||||
$this->prefix = Env::$db_prefix;
|
$this->prefix = Env::$db_prefix;
|
||||||
$this->models = array(
|
$this->models = array(
|
||||||
'newsletter_option_fields',
|
'newsletter_option_fields',
|
||||||
|
'newsletter_templates',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,6 +87,12 @@ class Populator {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function newsletter_templates() {
|
||||||
|
return array(
|
||||||
|
(new SampleTemplate(Env::$assets_url))->get(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private function populate($model) {
|
private function populate($model) {
|
||||||
$rows = $this->$model();
|
$rows = $this->$model();
|
||||||
$table = $this->prefix . $model;
|
$table = $this->prefix . $model;
|
||||||
|
379
lib/Config/PopulatorData/Templates/SampleTemplate.php
Normal file
379
lib/Config/PopulatorData/Templates/SampleTemplate.php
Normal file
File diff suppressed because one or more lines are too long
45
lib/Config/PublicAPI.php
Normal file
45
lib/Config/PublicAPI.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Config;
|
||||||
|
|
||||||
|
use MailPoet\Queue\Daemon;
|
||||||
|
use MailPoet\Util\Helpers;
|
||||||
|
|
||||||
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class PublicAPI {
|
||||||
|
function __construct() {
|
||||||
|
# http://example.com/?mailpoet-api§ion=&action=&payload=
|
||||||
|
$this->api = isset($_GET['mailpoet-api']) ? true : false;
|
||||||
|
$this->section = isset($_GET['section']) ? $_GET['section'] : false;
|
||||||
|
$this->action = isset($_GET['action']) ?
|
||||||
|
Helpers::underscoreToCamelCase($_GET['action']) :
|
||||||
|
false;
|
||||||
|
$this->payload = isset($_GET['payload']) ?
|
||||||
|
json_decode(urldecode($_GET['payload']), true) :
|
||||||
|
false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
if(!$this->api && !$this->section) return;
|
||||||
|
$this->_checkAndCallMethod($this, $this->section, $terminate = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function queue() {
|
||||||
|
$queue = new Daemon($this->payload);
|
||||||
|
$this->_checkAndCallMethod($queue, $this->action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _checkAndCallMethod($class, $method, $terminate = false) {
|
||||||
|
if(!method_exists($class, $method)) {
|
||||||
|
if(!$terminate) return;
|
||||||
|
header('HTTP/1.0 404 Not Found');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
call_user_func(
|
||||||
|
array(
|
||||||
|
$class,
|
||||||
|
$method
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -72,7 +72,7 @@ class Widget extends \WP_Widget {
|
|||||||
$selected_form = isset($instance['form']) ? (int)($instance['form']) : 0;
|
$selected_form = isset($instance['form']) ? (int)($instance['form']) : 0;
|
||||||
|
|
||||||
// get forms list
|
// get forms list
|
||||||
$forms = Form::whereNull('deleted_at')->orderByAsc('name')->findArray();
|
$forms = Form::getPublished()->orderByAsc('name')->findArray();
|
||||||
?><p>
|
?><p>
|
||||||
<label for="<?php $this->get_field_id( 'title' ) ?>"><?php _e( 'Title:' ); ?></label>
|
<label for="<?php $this->get_field_id( 'title' ) ?>"><?php _e( 'Title:' ); ?></label>
|
||||||
<input
|
<input
|
||||||
@ -94,23 +94,20 @@ class Widget extends \WP_Widget {
|
|||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="javascript:;" class="mailpoet_form_new"><?php _e("Create a new form"); ?></a>
|
<a href="javascript:;" onClick="createSubscriptionForm()" class="mailpoet_form_new"><?php _e("Create a new form"); ?></a>
|
||||||
</p>
|
</p>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
jQuery(function($) {
|
function createSubscriptionForm() {
|
||||||
$(function() {
|
MailPoet.Ajax.post({
|
||||||
$('.mailpoet_form_new').on('click', function() {
|
endpoint: 'forms',
|
||||||
MailPoet.Ajax.post({
|
action: 'create'
|
||||||
endpoint: 'forms',
|
}).done(function(response) {
|
||||||
action: 'create'
|
if(response !== false) {
|
||||||
}).done(function(response) {
|
window.location = response;
|
||||||
if(response !== false) {
|
}
|
||||||
window.location = response;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
return false;
|
||||||
});
|
}
|
||||||
</script>
|
</script>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
@ -134,7 +131,7 @@ class Widget extends \WP_Widget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// get form
|
// get form
|
||||||
$form = Form::whereNull('deleted_at')->findOne($instance['form']);
|
$form = Form::getPublished()->findOne($instance['form']);
|
||||||
|
|
||||||
// if the form was not found, return nothing.
|
// if the form was not found, return nothing.
|
||||||
if($form === false) {
|
if($form === false) {
|
||||||
|
@ -38,12 +38,12 @@ class CustomField extends Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function subscribers() {
|
function subscribers() {
|
||||||
return $this->has_many_through(
|
return $this->hasManyThrough(
|
||||||
__NAMESPACE__ . '\Subscriber',
|
__NAMESPACE__ . '\Subscriber',
|
||||||
__NAMESPACE__ . '\SubscriberCustomField',
|
__NAMESPACE__ . '\SubscriberCustomField',
|
||||||
'custom_field_id',
|
'custom_field_id',
|
||||||
'subscriber_id'
|
'subscriber_id'
|
||||||
)->select_expr(MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.value');
|
)->selectExpr(MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value');
|
||||||
}
|
}
|
||||||
|
|
||||||
static function createOrUpdate($data = array()) {
|
static function createOrUpdate($data = array()) {
|
||||||
|
50
lib/Models/Filter.php
Normal file
50
lib/Models/Filter.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Models;
|
||||||
|
|
||||||
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class Filter extends Model {
|
||||||
|
static $_table = MP_FILTERS_TABLE;
|
||||||
|
|
||||||
|
function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
|
||||||
|
$this->addValidations('name', array(
|
||||||
|
'required' => __('You need to specify a name.')
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete() {
|
||||||
|
// delete all relations to subscribers
|
||||||
|
SegmentFilter::where('filter_id', $this->id)->deleteMany();
|
||||||
|
return parent::delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
function segments() {
|
||||||
|
return $this->has_many_through(
|
||||||
|
__NAMESPACE__.'\Segment',
|
||||||
|
__NAMESPACE__.'\SegmentFilter',
|
||||||
|
'filter_id',
|
||||||
|
'segment_id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function createOrUpdate($data = array()) {
|
||||||
|
$filter = false;
|
||||||
|
|
||||||
|
if(isset($data['id']) && (int)$data['id'] > 0) {
|
||||||
|
$filter = self::findOne((int)$data['id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if($filter === false) {
|
||||||
|
$filter = self::create();
|
||||||
|
$filter->hydrate($data);
|
||||||
|
} else {
|
||||||
|
unset($data['id']);
|
||||||
|
$filter->set($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
$filter->save();
|
||||||
|
return $filter;
|
||||||
|
}
|
||||||
|
}
|
@ -54,12 +54,12 @@ class Form extends Model {
|
|||||||
array(
|
array(
|
||||||
'name' => 'all',
|
'name' => 'all',
|
||||||
'label' => __('All'),
|
'label' => __('All'),
|
||||||
'count' => Form::whereNull('deleted_at')->count()
|
'count' => Form::getPublished()->count()
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'name' => 'trash',
|
'name' => 'trash',
|
||||||
'label' => __('Trash'),
|
'label' => __('Trash'),
|
||||||
'count' => Form::whereNotNull('deleted_at')->count()
|
'count' => Form::getTrashed()->count()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -90,4 +90,12 @@ class Model extends \Sudzy\ValidModel {
|
|||||||
}, $searchCriteria);
|
}, $searchCriteria);
|
||||||
return $orm->having_raw(implode(' ' . $searchCondition . ' ', $havingFields), array_values($havingValues));
|
return $orm->having_raw(implode(' ' . $searchCondition . ' ', $havingFields), array_values($havingValues));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function getPublished() {
|
||||||
|
return static::whereNull('deleted_at');
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getTrashed() {
|
||||||
|
return static::whereNotNull('deleted_at');
|
||||||
|
}
|
||||||
}
|
}
|
@ -14,9 +14,17 @@ class Newsletter extends Model {
|
|||||||
if(is_string($this->deleted_at) && strlen(trim($this->deleted_at)) === 0) {
|
if(is_string($this->deleted_at) && strlen(trim($this->deleted_at)) === 0) {
|
||||||
$this->set_expr('deleted_at', 'NULL');
|
$this->set_expr('deleted_at', 'NULL');
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::save();
|
return parent::save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function delete() {
|
||||||
|
// delete all relations to segments
|
||||||
|
NewsletterSegment::where('newsletter_id', $this->id)->deleteMany();
|
||||||
|
|
||||||
|
return parent::delete();
|
||||||
|
}
|
||||||
|
|
||||||
function segments() {
|
function segments() {
|
||||||
return $this->has_many_through(
|
return $this->has_many_through(
|
||||||
__NAMESPACE__.'\Segment',
|
__NAMESPACE__.'\Segment',
|
||||||
@ -106,12 +114,12 @@ class Newsletter extends Model {
|
|||||||
array(
|
array(
|
||||||
'name' => 'all',
|
'name' => 'all',
|
||||||
'label' => __('All'),
|
'label' => __('All'),
|
||||||
'count' => Newsletter::whereNull('deleted_at')->count()
|
'count' => Newsletter::getPublished()->count()
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'name' => 'trash',
|
'name' => 'trash',
|
||||||
'label' => __('Trash'),
|
'label' => __('Trash'),
|
||||||
'count' => Newsletter::whereNotNull('deleted_at')->count()
|
'count' => Newsletter::getTrashed()->count()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -126,8 +134,8 @@ class Newsletter extends Model {
|
|||||||
static function createOrUpdate($data = array()) {
|
static function createOrUpdate($data = array()) {
|
||||||
$newsletter = false;
|
$newsletter = false;
|
||||||
|
|
||||||
if(isset($data['id']) && (int) $data['id'] > 0) {
|
if(isset($data['id']) && (int)$data['id'] > 0) {
|
||||||
$newsletter = self::findOne((int) $data['id']);
|
$newsletter = self::findOne((int)$data['id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($newsletter === false) {
|
if($newsletter === false) {
|
||||||
|
12
lib/Models/NewsletterStatistics.php
Normal file
12
lib/Models/NewsletterStatistics.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Models;
|
||||||
|
|
||||||
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class NewsletterStatistics extends Model {
|
||||||
|
public static $_table = MP_NEWSLETTER_STATISTICS_TABLE;
|
||||||
|
|
||||||
|
function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
}
|
12
lib/Models/Queue.php
Normal file
12
lib/Models/Queue.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Models;
|
||||||
|
|
||||||
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class Queue extends Model {
|
||||||
|
public static $_table = MP_QUEUES_TABLE;
|
||||||
|
|
||||||
|
function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,7 @@ class Segment extends Model {
|
|||||||
function delete() {
|
function delete() {
|
||||||
// delete all relations to subscribers
|
// delete all relations to subscribers
|
||||||
SubscriberSegment::where('segment_id', $this->id)->deleteMany();
|
SubscriberSegment::where('segment_id', $this->id)->deleteMany();
|
||||||
parent::delete();
|
return parent::delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
function newsletters() {
|
function newsletters() {
|
||||||
@ -38,6 +38,15 @@ class Segment extends Model {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function segmentFilters() {
|
||||||
|
return $this->has_many_through(
|
||||||
|
__NAMESPACE__.'\Filter',
|
||||||
|
__NAMESPACE__.'\SegmentFilter',
|
||||||
|
'segment_id',
|
||||||
|
'filter_id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function duplicate($data = array()) {
|
function duplicate($data = array()) {
|
||||||
$duplicate = parent::duplicate($data);
|
$duplicate = parent::duplicate($data);
|
||||||
|
|
||||||
@ -67,8 +76,25 @@ class Segment extends Model {
|
|||||||
->delete();
|
->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function getWPUsers() {
|
||||||
|
$segment = self::where('type', 'wp_users')->findOne();
|
||||||
|
|
||||||
|
if($segment === false) {
|
||||||
|
// create the wp users list
|
||||||
|
$segment = self::create();
|
||||||
|
$segment->hydrate(array(
|
||||||
|
'name' => __('WordPress Users'),
|
||||||
|
'type' => 'wp_users'
|
||||||
|
));
|
||||||
|
$segment->save();
|
||||||
|
return self::findOne($segment->id());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $segment;
|
||||||
|
}
|
||||||
|
|
||||||
static function search($orm, $search = '') {
|
static function search($orm, $search = '') {
|
||||||
return $orm->where_like('name', '%'.$search.'%');
|
return $orm->whereLike('name', '%'.$search.'%');
|
||||||
}
|
}
|
||||||
|
|
||||||
static function groups() {
|
static function groups() {
|
||||||
@ -76,12 +102,12 @@ class Segment extends Model {
|
|||||||
array(
|
array(
|
||||||
'name' => 'all',
|
'name' => 'all',
|
||||||
'label' => __('All'),
|
'label' => __('All'),
|
||||||
'count' => Segment::whereNull('deleted_at')->count()
|
'count' => Segment::getPublished()->count()
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'name' => 'trash',
|
'name' => 'trash',
|
||||||
'label' => __('Trash'),
|
'label' => __('Trash'),
|
||||||
'count' => Segment::whereNotNull('deleted_at')->count()
|
'count' => Segment::getTrashed()->count()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -94,6 +120,48 @@ class Segment extends Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function getSegmentsForImport() {
|
||||||
|
return self::selectMany(array(self::$_table.'.id', self::$_table.'.name'))
|
||||||
|
->select_expr(
|
||||||
|
'COUNT('.MP_SUBSCRIBER_SEGMENT_TABLE.'.subscriber_id)', 'subscribers'
|
||||||
|
)
|
||||||
|
->left_outer_join(
|
||||||
|
MP_SUBSCRIBER_SEGMENT_TABLE,
|
||||||
|
array(self::$_table.'.id', '=', MP_SUBSCRIBER_SEGMENT_TABLE.'.segment_id'))
|
||||||
|
->left_outer_join(
|
||||||
|
MP_SUBSCRIBERS_TABLE,
|
||||||
|
array(MP_SUBSCRIBER_SEGMENT_TABLE.'.subscriber_id', '=', MP_SUBSCRIBERS_TABLE.'.id'))
|
||||||
|
->whereNull(MP_SUBSCRIBERS_TABLE.'.deleted_at')
|
||||||
|
->group_by(self::$_table.'.id')
|
||||||
|
->group_by(self::$_table.'.name')
|
||||||
|
->where(self::$_table.'.type', 'default')
|
||||||
|
->findArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getSegmentsForExport($withConfirmedSubscribers = false) {
|
||||||
|
return self::raw_query(
|
||||||
|
'(SELECT segments.id, segments.name, COUNT(relation.subscriber_id) as subscribers ' .
|
||||||
|
'FROM ' . MP_SUBSCRIBER_SEGMENT_TABLE . ' relation ' .
|
||||||
|
'LEFT JOIN ' . self::$_table . ' segments ON segments.id = relation.segment_id ' .
|
||||||
|
'LEFT JOIN ' . MP_SUBSCRIBERS_TABLE . ' subscribers ON subscribers.id = relation.subscriber_id ' .
|
||||||
|
(($withConfirmedSubscribers) ?
|
||||||
|
'WHERE subscribers.status = "subscribed" ' :
|
||||||
|
'WHERE relation.segment_id IS NOT NULL ') .
|
||||||
|
'AND subscribers.deleted_at IS NULL ' .
|
||||||
|
'GROUP BY segments.id) ' .
|
||||||
|
'UNION ALL ' .
|
||||||
|
'(SELECT 0 as id, "' . __('Not In List') . '" as name, COUNT(*) as subscribers ' .
|
||||||
|
'FROM ' . MP_SUBSCRIBERS_TABLE . ' subscribers ' .
|
||||||
|
'LEFT JOIN ' . MP_SUBSCRIBER_SEGMENT_TABLE . ' relation on relation.subscriber_id = subscribers.id ' .
|
||||||
|
(($withConfirmedSubscribers) ?
|
||||||
|
'WHERE relation.subscriber_id is NULL AND subscribers.status = "subscribed" ' :
|
||||||
|
'WHERE relation.subscriber_id is NULL ') .
|
||||||
|
'AND subscribers.deleted_at IS NULL ' .
|
||||||
|
'HAVING subscribers) ' .
|
||||||
|
'ORDER BY name'
|
||||||
|
)->findArray();
|
||||||
|
}
|
||||||
|
|
||||||
static function createOrUpdate($data = array()) {
|
static function createOrUpdate($data = array()) {
|
||||||
$segment = false;
|
$segment = false;
|
||||||
|
|
||||||
@ -112,4 +180,8 @@ class Segment extends Model {
|
|||||||
$segment->save();
|
$segment->save();
|
||||||
return $segment;
|
return $segment;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
static function getPublic() {
|
||||||
|
return self::getPublished()->where('type', 'default');
|
||||||
|
}
|
||||||
|
}
|
12
lib/Models/SegmentFilter.php
Normal file
12
lib/Models/SegmentFilter.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Models;
|
||||||
|
|
||||||
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class SegmentFilter extends Model {
|
||||||
|
public static $_table = MP_SEGMENT_FILTER_TABLE;
|
||||||
|
|
||||||
|
function __construct() {
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,13 @@ class Setting extends Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function setValue($key, $value) {
|
||||||
|
return Setting::createOrUpdate(array(
|
||||||
|
'name' => $key,
|
||||||
|
'value' => $value
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
public static function getAll() {
|
public static function getAll() {
|
||||||
$settingsCollection = self::findMany();
|
$settingsCollection = self::findMany();
|
||||||
$settings = array();
|
$settings = array();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace MailPoet\Models;
|
namespace MailPoet\Models;
|
||||||
|
|
||||||
|
use MailPoet\Util\Helpers;
|
||||||
if(!defined('ABSPATH')) exit;
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
class Subscriber extends Model {
|
class Subscriber extends Model {
|
||||||
@ -89,7 +90,7 @@ class Subscriber extends Model {
|
|||||||
array(
|
array(
|
||||||
'name' => 'all',
|
'name' => 'all',
|
||||||
'label' => __('All'),
|
'label' => __('All'),
|
||||||
'count' => Subscriber::whereNull('deleted_at')->count()
|
'count' => Subscriber::getPublished()->count()
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
'name' => 'subscribed',
|
'name' => 'subscribed',
|
||||||
@ -109,7 +110,7 @@ class Subscriber extends Model {
|
|||||||
array(
|
array(
|
||||||
'name' => 'trash',
|
'name' => 'trash',
|
||||||
'label' => __('Trash'),
|
'label' => __('Trash'),
|
||||||
'count' => Subscriber::whereNotNull('deleted_at')->count()
|
'count' => Subscriber::getTrashed()->count()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -134,20 +135,41 @@ class Subscriber extends Model {
|
|||||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END), NULL) as "' . $customField['name'].'"');
|
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END), NULL) as "' . $customField['name'].'"');
|
||||||
}
|
}
|
||||||
$orm = $orm
|
$orm = $orm
|
||||||
->left_outer_join(
|
->leftOuterJoin(
|
||||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE,
|
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE,
|
||||||
array(MP_SUBSCRIBERS_TABLE.'.id', '=',
|
array(MP_SUBSCRIBERS_TABLE.'.id', '=',
|
||||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.subscriber_id'))
|
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.subscriber_id'))
|
||||||
->left_outer_join(
|
->leftOuterJoin(
|
||||||
MP_CUSTOM_FIELDS_TABLE,
|
MP_CUSTOM_FIELDS_TABLE,
|
||||||
array(MP_CUSTOM_FIELDS_TABLE.'.id','=',
|
array(MP_CUSTOM_FIELDS_TABLE.'.id','=',
|
||||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.custom_field_id'))
|
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.custom_field_id'))
|
||||||
->group_by(MP_SUBSCRIBERS_TABLE.'.id');
|
->groupBy(MP_SUBSCRIBERS_TABLE.'.id');
|
||||||
|
return $orm;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function filterWithCustomFieldsForExport($orm) {
|
||||||
|
$orm = $orm->select(MP_SUBSCRIBERS_TABLE.'.*');
|
||||||
|
$customFields = CustomField::findArray();
|
||||||
|
foreach ($customFields as $customField) {
|
||||||
|
$orm = $orm->selectExpr(
|
||||||
|
'CASE WHEN ' .
|
||||||
|
MP_CUSTOM_FIELDS_TABLE . '.id=' . $customField['id'] . ' THEN ' .
|
||||||
|
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END as "' . $customField['name'].'"');
|
||||||
|
}
|
||||||
|
$orm = $orm
|
||||||
|
->leftOuterJoin(
|
||||||
|
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE,
|
||||||
|
array(MP_SUBSCRIBERS_TABLE.'.id', '=',
|
||||||
|
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.subscriber_id'))
|
||||||
|
->leftOuterJoin(
|
||||||
|
MP_CUSTOM_FIELDS_TABLE,
|
||||||
|
array(MP_CUSTOM_FIELDS_TABLE.'.id','=',
|
||||||
|
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.custom_field_id'));
|
||||||
return $orm;
|
return $orm;
|
||||||
}
|
}
|
||||||
|
|
||||||
function customFields() {
|
function customFields() {
|
||||||
return $this->has_many_through(
|
return $this->hasManyThrough(
|
||||||
__NAMESPACE__.'\CustomField',
|
__NAMESPACE__.'\CustomField',
|
||||||
__NAMESPACE__.'\SubscriberCustomField',
|
__NAMESPACE__.'\SubscriberCustomField',
|
||||||
'subscriber_id',
|
'subscriber_id',
|
||||||
@ -302,4 +324,68 @@ class Subscriber extends Model {
|
|||||||
->whereNull('deleted_at')
|
->whereNull('deleted_at')
|
||||||
->where('status', 'unconfirmed');
|
->where('status', 'unconfirmed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function createMultiple($columns, $values) {
|
||||||
|
return self::rawExecute(
|
||||||
|
'INSERT INTO `' . self::$_table . '` ' .
|
||||||
|
'(' . implode(', ', $columns) . ') ' .
|
||||||
|
'VALUES ' . rtrim(
|
||||||
|
str_repeat(
|
||||||
|
'(' . rtrim(str_repeat('?,', count($columns)), ',') . ')' . ', '
|
||||||
|
, count($values)
|
||||||
|
)
|
||||||
|
, ', '),
|
||||||
|
Helpers::flattenArray($values)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function updateMultiple($columns, $subscribers, $currentTime = false) {
|
||||||
|
$ignoreColumnsOnUpdate = array(
|
||||||
|
'email',
|
||||||
|
'created_at'
|
||||||
|
);
|
||||||
|
$subscribers = array_map('array_values', $subscribers);
|
||||||
|
$emailPosition = array_search('email', $columns);
|
||||||
|
$sql =
|
||||||
|
function ($type) use (
|
||||||
|
$columns,
|
||||||
|
$subscribers,
|
||||||
|
$emailPosition,
|
||||||
|
$ignoreColumnsOnUpdate
|
||||||
|
) {
|
||||||
|
return array_filter(
|
||||||
|
array_map(function ($columnPosition, $columnName) use (
|
||||||
|
$type,
|
||||||
|
$subscribers,
|
||||||
|
$emailPosition,
|
||||||
|
$ignoreColumnsOnUpdate
|
||||||
|
) {
|
||||||
|
if(in_array($columnName, $ignoreColumnsOnUpdate)) return;
|
||||||
|
$query = array_map(
|
||||||
|
function ($subscriber) use ($type, $columnPosition, $emailPosition) {
|
||||||
|
return ($type === 'values') ?
|
||||||
|
array(
|
||||||
|
$subscriber[$emailPosition],
|
||||||
|
$subscriber[$columnPosition]
|
||||||
|
) :
|
||||||
|
'WHEN email = ? THEN ?';
|
||||||
|
}, $subscribers);
|
||||||
|
return ($type === 'values') ?
|
||||||
|
Helpers::flattenArray($query) :
|
||||||
|
$columnName . '= (CASE ' . implode(' ', $query) . ' END)';
|
||||||
|
}, array_keys($columns), $columns)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
return self::rawExecute(
|
||||||
|
'UPDATE `' . self::$_table . '` ' .
|
||||||
|
'SET ' . implode(', ', $sql('statement')) . ' '.
|
||||||
|
(($currentTime) ? ', updated_at = "' . $currentTime . '" ' : '') .
|
||||||
|
'WHERE email IN ' .
|
||||||
|
'(' . rtrim(str_repeat('?,', count($subscribers)), ',') . ')',
|
||||||
|
array_merge(
|
||||||
|
Helpers::flattenArray($sql('values')),
|
||||||
|
Helpers::arrayColumn($subscribers, $emailPosition)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,6 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace MailPoet\Models;
|
namespace MailPoet\Models;
|
||||||
|
|
||||||
|
use MailPoet\Util\Helpers;
|
||||||
|
|
||||||
if(!defined('ABSPATH')) exit;
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
class SubscriberCustomField extends Model {
|
class SubscriberCustomField extends Model {
|
||||||
@ -9,4 +11,40 @@ class SubscriberCustomField extends Model {
|
|||||||
function __construct() {
|
function __construct() {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function createMultiple($values) {
|
||||||
|
$values = array_map('array_values', $values);
|
||||||
|
return self::rawExecute(
|
||||||
|
'INSERT IGNORE INTO `' . self::$_table . '` ' .
|
||||||
|
'(custom_field_id, subscriber_id, value) ' .
|
||||||
|
'VALUES ' . rtrim(
|
||||||
|
str_repeat(
|
||||||
|
'(?, ?, ?)' . ', '
|
||||||
|
, count($values)
|
||||||
|
), ', '
|
||||||
|
),
|
||||||
|
Helpers::flattenArray($values)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function updateMultiple($values) {
|
||||||
|
self::createMultiple($values);
|
||||||
|
$values = array_map('array_values', $values);
|
||||||
|
self::rawExecute(
|
||||||
|
'UPDATE `' . self::$_table . '` ' .
|
||||||
|
'SET value = ' .
|
||||||
|
'(CASE ' .
|
||||||
|
str_repeat(
|
||||||
|
'WHEN custom_field_id = ? AND subscriber_id = ? THEN ? ',
|
||||||
|
count($values)
|
||||||
|
) .
|
||||||
|
'END) ' .
|
||||||
|
'WHERE subscriber_id IN (' .
|
||||||
|
implode(', ', Helpers::arrayColumn($values, 1)) .
|
||||||
|
') AND custom_field_id IN (' .
|
||||||
|
implode(', ', array_unique(Helpers::arrayColumn($values, 0)))
|
||||||
|
. ') ',
|
||||||
|
Helpers::flattenArray($values)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,6 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace MailPoet\Models;
|
namespace MailPoet\Models;
|
||||||
|
|
||||||
|
use MailPoet\Util\Helpers;
|
||||||
|
|
||||||
if(!defined('ABSPATH')) exit;
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
class SubscriberSegment extends Model {
|
class SubscriberSegment extends Model {
|
||||||
@ -9,4 +11,47 @@ class SubscriberSegment extends Model {
|
|||||||
function __construct() {
|
function __construct() {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
static function filterWithCustomFields($orm) {
|
||||||
|
$orm = $orm->select(MP_SUBSCRIBERS_TABLE.'.*');
|
||||||
|
$customFields = CustomField::findArray();
|
||||||
|
foreach ($customFields as $customField) {
|
||||||
|
$orm = $orm->select_expr(
|
||||||
|
'CASE WHEN ' .
|
||||||
|
MP_CUSTOM_FIELDS_TABLE . '.id=' . $customField['id'] . ' THEN ' .
|
||||||
|
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END as "' . $customField['name'].'"');
|
||||||
|
}
|
||||||
|
$orm = $orm
|
||||||
|
->left_outer_join(
|
||||||
|
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE,
|
||||||
|
array(MP_SUBSCRIBERS_TABLE.'.id', '=',
|
||||||
|
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.subscriber_id'))
|
||||||
|
->left_outer_join(
|
||||||
|
MP_CUSTOM_FIELDS_TABLE,
|
||||||
|
array(MP_CUSTOM_FIELDS_TABLE.'.id','=',
|
||||||
|
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.custom_field_id'));
|
||||||
|
return $orm;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function createMultiple($segmnets, $subscribers) {
|
||||||
|
$values = Helpers::flattenArray(
|
||||||
|
array_map(function ($segment) use ($subscribers) {
|
||||||
|
return array_map(function ($subscriber) use ($segment) {
|
||||||
|
return array(
|
||||||
|
$segment,
|
||||||
|
$subscriber
|
||||||
|
);
|
||||||
|
}, $subscribers);
|
||||||
|
}, $segmnets)
|
||||||
|
);
|
||||||
|
return self::rawExecute(
|
||||||
|
'INSERT IGNORE INTO `' . self::$_table . '` ' .
|
||||||
|
'(segment_id, subscriber_id) ' .
|
||||||
|
'VALUES ' . rtrim(
|
||||||
|
str_repeat(
|
||||||
|
'(?, ?), ', count($subscribers) * count($segmnets)), ', '
|
||||||
|
),
|
||||||
|
$values
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -50,9 +50,11 @@ class MetaInformationManager {
|
|||||||
// check if the user specified a label to be displayed before the author's name
|
// check if the user specified a label to be displayed before the author's name
|
||||||
if(strlen($preceded_by) > 0) {
|
if(strlen($preceded_by) > 0) {
|
||||||
$content = stripslashes($preceded_by) . ' ';
|
$content = stripslashes($preceded_by) . ' ';
|
||||||
|
} else {
|
||||||
|
$content = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return join(', ', $categories);
|
return $content . join(', ', $categories);
|
||||||
} else {
|
} else {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class PostListTransformer {
|
|||||||
|
|
||||||
function transform($posts) {
|
function transform($posts) {
|
||||||
$results = array();
|
$results = array();
|
||||||
$use_divider = (bool)$this->args['showDivider'];
|
$use_divider = $this->args['showDivider'] === 'true';
|
||||||
|
|
||||||
foreach ($posts as $index => $post) {
|
foreach ($posts as $index => $post) {
|
||||||
if ($use_divider && $index > 0) {
|
if ($use_divider && $index > 0) {
|
||||||
|
@ -22,16 +22,26 @@ class PostTransformer {
|
|||||||
$content = $content_manager->filterContent($content);
|
$content = $content_manager->filterContent($content);
|
||||||
|
|
||||||
$structure_transformer = new StructureTransformer();
|
$structure_transformer = new StructureTransformer();
|
||||||
$structure = $structure_transformer->transform($content, (bool)$this->args['imagePadded']);
|
$structure = $structure_transformer->transform($content, $this->args['imagePadded'] === 'true');
|
||||||
|
|
||||||
$structure = $this->appendFeaturedImage($post, (bool)$this->args['imagePadded'], $structure);
|
$structure = $this->appendFeaturedImage(
|
||||||
|
$post,
|
||||||
|
$this->args['displayType'],
|
||||||
|
$this->args['imagePadded'] === 'true',
|
||||||
|
$structure
|
||||||
|
);
|
||||||
$structure = $this->appendPostTitle($post, $structure);
|
$structure = $this->appendPostTitle($post, $structure);
|
||||||
$structure = $this->appendReadMore($post->ID, $structure);
|
$structure = $this->appendReadMore($post->ID, $structure);
|
||||||
|
|
||||||
return $structure;
|
return $structure;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function appendFeaturedImage($post, $image_padded, $structure) {
|
private function appendFeaturedImage($post, $display_type, $image_padded, $structure) {
|
||||||
|
if ($display_type === 'full') {
|
||||||
|
// No featured images for full posts
|
||||||
|
return $structure;
|
||||||
|
}
|
||||||
|
|
||||||
$featured_image = $this->getFeaturedImage(
|
$featured_image = $this->getFeaturedImage(
|
||||||
$post->ID,
|
$post->ID,
|
||||||
$post->post_title,
|
$post->post_title,
|
||||||
@ -68,7 +78,7 @@ class PostTransformer {
|
|||||||
|
|
||||||
return array(
|
return array(
|
||||||
'type' => 'image',
|
'type' => 'image',
|
||||||
'link' => '',
|
'link' => get_permalink($post_id),
|
||||||
'src' => $image_info[0],
|
'src' => $image_info[0],
|
||||||
'alt' => $alt_text,
|
'alt' => $alt_text,
|
||||||
'padded' => $image_padded,
|
'padded' => $image_padded,
|
||||||
@ -84,6 +94,8 @@ class PostTransformer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function appendPostTitle($post, $structure) {
|
private function appendPostTitle($post, $structure) {
|
||||||
|
$title = $this->getPostTitle($post);
|
||||||
|
|
||||||
if ($this->args['titlePosition'] === 'inTextBlock') {
|
if ($this->args['titlePosition'] === 'inTextBlock') {
|
||||||
// Attach title to the first text block
|
// Attach title to the first text block
|
||||||
$text_block_index = null;
|
$text_block_index = null;
|
||||||
@ -94,7 +106,6 @@ class PostTransformer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$title = $this->getPostTitle($post);
|
|
||||||
if ($text_block_index === null) {
|
if ($text_block_index === null) {
|
||||||
$structure[] = array(
|
$structure[] = array(
|
||||||
'type' => 'text',
|
'type' => 'text',
|
||||||
@ -103,6 +114,14 @@ class PostTransformer {
|
|||||||
} else {
|
} else {
|
||||||
$structure[$text_block_index]['text'] = $title . $structure[$text_block_index]['text'];
|
$structure[$text_block_index]['text'] = $title . $structure[$text_block_index]['text'];
|
||||||
}
|
}
|
||||||
|
} elseif ($this->args['titlePosition'] === 'aboveBlock') {
|
||||||
|
array_unshift(
|
||||||
|
$structure,
|
||||||
|
array(
|
||||||
|
'type' => 'text',
|
||||||
|
'text' => $title,
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $structure;
|
return $structure;
|
||||||
@ -113,6 +132,15 @@ class PostTransformer {
|
|||||||
$button = $this->args['readMoreButton'];
|
$button = $this->args['readMoreButton'];
|
||||||
$button['url'] = get_permalink($post_id);
|
$button['url'] = get_permalink($post_id);
|
||||||
$structure[] = $button;
|
$structure[] = $button;
|
||||||
|
} else {
|
||||||
|
$structure[] = array(
|
||||||
|
'type' => 'text',
|
||||||
|
'text' => sprintf(
|
||||||
|
'<a href="%s">%s</a>',
|
||||||
|
get_permalink($post_id),
|
||||||
|
$this->args['readMoreText']
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $structure;
|
return $structure;
|
||||||
@ -121,7 +149,7 @@ class PostTransformer {
|
|||||||
private function getPostTitle($post) {
|
private function getPostTitle($post) {
|
||||||
$title = $post->post_title;
|
$title = $post->post_title;
|
||||||
|
|
||||||
if ((bool)$this->args['titleIsLink']) {
|
if ($this->args['titleIsLink'] === 'true') {
|
||||||
$title = '<a href="' . get_permalink($post->ID) . '">' . $title . '</a>';
|
$title = '<a href="' . get_permalink($post->ID) . '">' . $title . '</a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
36
lib/Queue/BootStrapMenu.php
Normal file
36
lib/Queue/BootStrapMenu.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Queue;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use MailPoet\Models\Queue;
|
||||||
|
use MailPoet\Models\Setting;
|
||||||
|
|
||||||
|
class BootStrapMenu {
|
||||||
|
function __construct() {
|
||||||
|
$this->daemon = Setting::where('name', 'daemon')
|
||||||
|
->findOne();
|
||||||
|
}
|
||||||
|
|
||||||
|
function bootStrap() {
|
||||||
|
$queues = Queue::findMany();
|
||||||
|
return ($this->daemon) ?
|
||||||
|
array_merge(
|
||||||
|
array(
|
||||||
|
'time_since_start' =>
|
||||||
|
Carbon::createFromFormat(
|
||||||
|
'Y-m-d H:i:s',
|
||||||
|
$this->daemon->created_at,
|
||||||
|
'UTC'
|
||||||
|
)->diffForHumans(),
|
||||||
|
'time_since_update' =>
|
||||||
|
Carbon::createFromFormat(
|
||||||
|
'Y-m-d H:i:s',
|
||||||
|
$this->daemon->updated_at,
|
||||||
|
'UTC'
|
||||||
|
)->diffForHumans()
|
||||||
|
),
|
||||||
|
json_decode($this->daemon->value, true)
|
||||||
|
) :
|
||||||
|
"false";
|
||||||
|
}
|
||||||
|
}
|
128
lib/Queue/Daemon.php
Normal file
128
lib/Queue/Daemon.php
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Queue;
|
||||||
|
|
||||||
|
use MailPoet\Models\Setting;
|
||||||
|
use MailPoet\Util\Security;
|
||||||
|
|
||||||
|
require_once(ABSPATH . 'wp-includes/pluggable.php');
|
||||||
|
|
||||||
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class Daemon {
|
||||||
|
function __construct($payload = array()) {
|
||||||
|
set_time_limit(0);
|
||||||
|
ignore_user_abort();
|
||||||
|
list ($this->daemon, $this->daemonData) = $this->getDaemon();
|
||||||
|
$this->refreshedToken = $this->refreshToken();
|
||||||
|
$this->payload = $payload;
|
||||||
|
$this->timer = microtime(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
if(!isset($this->payload['session'])) {
|
||||||
|
$this->abortWithError('missing session ID');
|
||||||
|
}
|
||||||
|
$this->manageSession('start');
|
||||||
|
$daemon = $this->daemon;
|
||||||
|
$daemonData = $this->daemonData;
|
||||||
|
if(!$daemon) {
|
||||||
|
$daemon = Setting::create();
|
||||||
|
$daemon->name = 'daemon';
|
||||||
|
$daemon->value = json_encode(array('status' => 'stopped'));
|
||||||
|
$daemon->save();
|
||||||
|
}
|
||||||
|
if($daemonData['status'] !== 'started') {
|
||||||
|
$_SESSION['daemon'] = 'started';
|
||||||
|
$daemonData = array(
|
||||||
|
'status' => 'started',
|
||||||
|
'token' => $this->refreshedToken,
|
||||||
|
'counter' => ($daemonData['status'] === 'paused') ?
|
||||||
|
$daemonData['counter'] :
|
||||||
|
0
|
||||||
|
);
|
||||||
|
$_SESSION['daemon'] = array('result' => true);
|
||||||
|
$this->manageSession('end');
|
||||||
|
$daemon->value = json_encode($daemonData);
|
||||||
|
$daemon->save();
|
||||||
|
$this->callSelf();
|
||||||
|
} else {
|
||||||
|
$_SESSION['daemon'] = array(
|
||||||
|
'result' => false,
|
||||||
|
'error' => 'already started'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->manageSession('end');
|
||||||
|
}
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
if(!$this->daemon || $this->daemonData['status'] !== 'started') {
|
||||||
|
$this->abortWithError('not running');
|
||||||
|
}
|
||||||
|
if(!isset($this->payload['token']) ||
|
||||||
|
$this->payload['token'] !== $this->daemonData['token']
|
||||||
|
) {
|
||||||
|
$this->abortWithError('invalid token');
|
||||||
|
}
|
||||||
|
|
||||||
|
$worker = new Worker();
|
||||||
|
$worker->process();
|
||||||
|
$elapsedTime = microtime(true) - $this->timer;
|
||||||
|
if ($elapsedTime < 30) {
|
||||||
|
sleep(30 - $elapsedTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// after each execution, read daemon in case it's status was modified
|
||||||
|
list($daemon, $daemonData) = $this->getDaemon();
|
||||||
|
$daemonData['counter']++;
|
||||||
|
$daemonData['token'] = $this->refreshedToken;
|
||||||
|
$daemon->value = json_encode($daemonData);
|
||||||
|
$daemon->save();
|
||||||
|
$this->callSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDaemon() {
|
||||||
|
$daemon = Setting::where('name', 'daemon')
|
||||||
|
->findOne();
|
||||||
|
return array(
|
||||||
|
($daemon) ? $daemon : null,
|
||||||
|
($daemon) ? json_decode($daemon->value, true) : null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshToken() {
|
||||||
|
return Security::generateRandomString(5);
|
||||||
|
}
|
||||||
|
|
||||||
|
function manageSession($action) {
|
||||||
|
switch ($action) {
|
||||||
|
case 'start':
|
||||||
|
if(session_id()) {
|
||||||
|
session_write_close();
|
||||||
|
}
|
||||||
|
session_id($this->payload['session']);
|
||||||
|
session_start();
|
||||||
|
break;
|
||||||
|
case 'end':
|
||||||
|
session_write_close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function callSelf() {
|
||||||
|
$payload = json_encode(array('token' => $this->refreshedToken));
|
||||||
|
Supervisor::getRemoteUrl(
|
||||||
|
'/?mailpoet-api§ion=queue&action=run&payload=' . urlencode($payload)
|
||||||
|
|
||||||
|
);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
function abortWithError($error) {
|
||||||
|
wp_send_json(
|
||||||
|
array(
|
||||||
|
'result' => false,
|
||||||
|
'error' => $error
|
||||||
|
));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
89
lib/Queue/Supervisor.php
Normal file
89
lib/Queue/Supervisor.php
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Queue;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use MailPoet\Config\Env;
|
||||||
|
use MailPoet\Models\Setting;
|
||||||
|
|
||||||
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class Supervisor {
|
||||||
|
function __construct($forceStart = false) {
|
||||||
|
$this->forceStart = $forceStart;
|
||||||
|
if(!Env::isPluginActivated()) {
|
||||||
|
throw new \Exception('Database has not been configured.');
|
||||||
|
}
|
||||||
|
list ($this->daemon, $this->daemonData) = $this->getDaemon();
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkDaemon() {
|
||||||
|
if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])) return;
|
||||||
|
if(!$this->daemon) {
|
||||||
|
return $this->startDaemon();
|
||||||
|
} else {
|
||||||
|
if(!$this->forceStart && ($this->daemonData['status'] === 'paused' ||
|
||||||
|
$this->daemonData['status'] === 'stopped'
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$currentTime = Carbon::now('UTC');
|
||||||
|
$lastUpdateTime = Carbon::createFromFormat(
|
||||||
|
'Y-m-d H:i:s',
|
||||||
|
$this->daemon->updated_at, 'UTC'
|
||||||
|
);
|
||||||
|
$timeSinceLastStart = $currentTime->diffInSeconds($lastUpdateTime);
|
||||||
|
if($timeSinceLastStart < 50) return;
|
||||||
|
$this->daemonData['status'] = 'paused';
|
||||||
|
$this->daemon->value = json_encode($this->daemonData);
|
||||||
|
$this->daemon->save();
|
||||||
|
return $this->startDaemon();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startDaemon() {
|
||||||
|
if(!session_id()) session_start();
|
||||||
|
$sessionId = session_id();
|
||||||
|
session_write_close();
|
||||||
|
$_SESSION['daemon'] = null;
|
||||||
|
$payload = json_encode(array('session' => $sessionId));
|
||||||
|
self::getRemoteUrl(
|
||||||
|
'/?mailpoet-api§ion=queue&action=start&payload=' . urlencode($payload)
|
||||||
|
);
|
||||||
|
session_start();
|
||||||
|
$daemonStatus = $_SESSION['daemon'];
|
||||||
|
unset($_SESSION['daemon']);
|
||||||
|
session_write_close();
|
||||||
|
return $daemonStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDaemon() {
|
||||||
|
$daemon = Setting::where('name', 'daemon')
|
||||||
|
->findOne();
|
||||||
|
$daemonData = ($daemon) ? json_decode($daemon->value, true) : false;
|
||||||
|
return array(
|
||||||
|
$daemon,
|
||||||
|
$daemonData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getRemoteUrl($url) {
|
||||||
|
$args = array(
|
||||||
|
'timeout' => 1,
|
||||||
|
'user-agent' => 'MailPoet (www.mailpoet.com)'
|
||||||
|
);
|
||||||
|
wp_remote_get(
|
||||||
|
self::getSiteUrl() . $url,
|
||||||
|
$args
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getSiteUrl() {
|
||||||
|
if(preg_match('!:\d+/!', site_url())) return site_url();
|
||||||
|
preg_match('!http://(?P<host>.*?):(?P<port>\d+)!', site_url(), $server);
|
||||||
|
$fp = @fsockopen($server['host'], $server['port'], $errno, $errstr, 1);
|
||||||
|
return ($fp) ?
|
||||||
|
site_url() :
|
||||||
|
preg_replace('/(?=:\d+):\d+/', '$1', site_url());
|
||||||
|
}
|
||||||
|
}
|
62
lib/Queue/Worker.php
Normal file
62
lib/Queue/Worker.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Queue;
|
||||||
|
|
||||||
|
use MailPoet\Models\Newsletter;
|
||||||
|
use MailPoet\Models\NewsletterStatistics;
|
||||||
|
use MailPoet\Models\Queue;
|
||||||
|
|
||||||
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class Worker {
|
||||||
|
function __construct($timer = false) {
|
||||||
|
$this->timer = $timer;
|
||||||
|
$this->timer = microtime(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function process() {
|
||||||
|
$queues =
|
||||||
|
Queue::orderByDesc('priority')
|
||||||
|
->whereNotIn('status', array(
|
||||||
|
'paused',
|
||||||
|
'completed'
|
||||||
|
))
|
||||||
|
->findResultSet();
|
||||||
|
foreach ($queues as $queue) {
|
||||||
|
$newsletter = Newsletter::findOne($queue->newsletter_id)
|
||||||
|
->asArray();
|
||||||
|
$subscribers = json_decode($queue->subscribers, true);
|
||||||
|
if(!isset($subscribers['failed'])) $subscribers['failed'] = array();
|
||||||
|
if(!isset($subscribers['processed'])) $subscribers['processed'] = array();
|
||||||
|
$subscribersToProcess = $subscribers['to_process'];
|
||||||
|
foreach ($subscribersToProcess as $subscriber) {
|
||||||
|
$elapsedTime = microtime(true) - $this->timer;
|
||||||
|
if($elapsedTime >= 28) break;
|
||||||
|
// TODO: hook up to mailer
|
||||||
|
sleep(1);
|
||||||
|
$newsletterStatistics = NewsletterStatistics::create();
|
||||||
|
$newsletterStatistics->subscriber_id = $subscriber;
|
||||||
|
$newsletterStatistics->newsletter_id = $newsletter['id'];
|
||||||
|
$newsletterStatistics->queue_id = $queue->id;
|
||||||
|
$newsletterStatistics->save();
|
||||||
|
$subscribers['processed'][] = $subscriber;
|
||||||
|
$subscribers['to_process'] = array_values(
|
||||||
|
array_diff(
|
||||||
|
$subscribers['to_process'],
|
||||||
|
$subscribers['processed']
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$queue->count_processed = count($subscribers['processed']);
|
||||||
|
$queue->count_to_process = count($subscribers['to_process']);
|
||||||
|
$queue->count_failed = count($subscribers['failed']);
|
||||||
|
$queue->count_total =
|
||||||
|
$queue->count_processed + $queue->count_to_process + $queue->count_failed;
|
||||||
|
if(!$queue->count_to_process) {
|
||||||
|
$queue->processed_at = date('Y-m-d H:i:s');
|
||||||
|
$queue->status = 'completed';
|
||||||
|
}
|
||||||
|
$queue->subscribers = json_encode($subscribers);
|
||||||
|
$queue->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
lib/Router/ImportExport.php
Normal file
60
lib/Router/ImportExport.php
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Router;
|
||||||
|
|
||||||
|
use MailPoet\Subscribers\ImportExport\Import\MailChimp;
|
||||||
|
use MailPoet\Models\CustomField;
|
||||||
|
use MailPoet\Models\Segment;
|
||||||
|
|
||||||
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class ImportExport {
|
||||||
|
function getMailChimpLists($data) {
|
||||||
|
$mailChimp = new MailChimp($data['api_key']);
|
||||||
|
wp_send_json($mailChimp->getLists());
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMailChimpSubscribers($data) {
|
||||||
|
$mailChimp = new MailChimp($data['api_key']);
|
||||||
|
wp_send_json($mailChimp->getSubscribers($data['lists']));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addSegment($data) {
|
||||||
|
$segment = Segment::createOrUpdate($data);
|
||||||
|
wp_send_json(
|
||||||
|
($segment->id) ?
|
||||||
|
array(
|
||||||
|
'result' => true,
|
||||||
|
'segment' => $segment->asArray()
|
||||||
|
) :
|
||||||
|
array(
|
||||||
|
'result' => false
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function addCustomField($data) {
|
||||||
|
$customField = CustomField::create();
|
||||||
|
$customField->hydrate($data);
|
||||||
|
$result = $customField->save();
|
||||||
|
wp_send_json(
|
||||||
|
($result) ?
|
||||||
|
array(
|
||||||
|
'result' => true,
|
||||||
|
'customField' => $customField->asArray()
|
||||||
|
) :
|
||||||
|
array(
|
||||||
|
'result' => false
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function processImport($data) {
|
||||||
|
$import = new \MailPoet\Subscribers\ImportExport\Import\Import(json_decode($data, true));
|
||||||
|
wp_send_json($import->process());
|
||||||
|
}
|
||||||
|
|
||||||
|
function processExport($data) {
|
||||||
|
$export = new \MailPoet\Subscribers\ImportExport\Export\Export(json_decode($data, true));
|
||||||
|
wp_send_json($export->process());
|
||||||
|
}
|
||||||
|
}
|
@ -33,7 +33,6 @@ class Newsletters {
|
|||||||
}, $segments);
|
}, $segments);
|
||||||
$newsletter['options'] = $options;
|
$newsletter['options'] = $options;
|
||||||
|
|
||||||
|
|
||||||
wp_send_json($newsletter);
|
wp_send_json($newsletter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -54,9 +53,16 @@ class Newsletters {
|
|||||||
unset($data['options']);
|
unset($data['options']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$errors = array();
|
||||||
|
$result = false;
|
||||||
|
|
||||||
$newsletter = Newsletter::createOrUpdate($data);
|
$newsletter = Newsletter::createOrUpdate($data);
|
||||||
|
|
||||||
if($newsletter->id) {
|
if($newsletter !== false && !$newsletter->id()) {
|
||||||
|
$errors = $newsletter->getValidationErrors();
|
||||||
|
} else {
|
||||||
|
$result = true;
|
||||||
|
|
||||||
if(!empty($segment_ids)) {
|
if(!empty($segment_ids)) {
|
||||||
NewsletterSegment::where('newsletter_id', $newsletter->id)
|
NewsletterSegment::where('newsletter_id', $newsletter->id)
|
||||||
->deleteMany();
|
->deleteMany();
|
||||||
@ -89,35 +95,57 @@ class Newsletters {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
wp_send_json(array(
|
||||||
wp_send_json(($newsletter->id !== false));
|
'result' => $result,
|
||||||
}
|
'errors' => $errors
|
||||||
|
));
|
||||||
function delete($data = array()) {
|
|
||||||
$newsletter = newsletter::findOne($data['id']);
|
|
||||||
$confirm_delete = filter_var($data['confirm'], FILTER_VALIDATE_BOOLEAN);
|
|
||||||
if($newsletter !== false) {
|
|
||||||
if($confirm_delete) {
|
|
||||||
$newsletter->delete();
|
|
||||||
$result = array('newsletters' => 1);
|
|
||||||
} else {
|
|
||||||
$newsletter->set_expr('deleted_at', 'NOW()');
|
|
||||||
$result = array('newsletters' => (int)$newsletter->save());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$result = false;
|
|
||||||
}
|
|
||||||
wp_send_json($result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function restore($id) {
|
function restore($id) {
|
||||||
|
$result = false;
|
||||||
|
|
||||||
$newsletter = Newsletter::findOne($id);
|
$newsletter = Newsletter::findOne($id);
|
||||||
if($newsletter !== false) {
|
if($newsletter !== false) {
|
||||||
$newsletter->set_expr('deleted_at', 'NULL');
|
$result = $newsletter->restore();
|
||||||
$result = array('newsletters' => (int)$newsletter->save());
|
|
||||||
} else {
|
|
||||||
$result = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wp_send_json($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function trash($id) {
|
||||||
|
$result = false;
|
||||||
|
|
||||||
|
$newsletter = Newsletter::findOne($id);
|
||||||
|
if($newsletter !== false) {
|
||||||
|
$result = $newsletter->trash();
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_send_json($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function delete($id) {
|
||||||
|
$result = false;
|
||||||
|
|
||||||
|
$newsletter = Newsletter::findOne($id);
|
||||||
|
if($newsletter !== false) {
|
||||||
|
$newsletter->delete();
|
||||||
|
$result = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
wp_send_json($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
function duplicate($id) {
|
||||||
|
$result = false;
|
||||||
|
|
||||||
|
$newsletter = Newsletter::findOne($id);
|
||||||
|
if($newsletter !== false) {
|
||||||
|
$data = array(
|
||||||
|
'subject' => sprintf(__('Copy of %s'), $newsletter->subject)
|
||||||
|
);
|
||||||
|
$result = $newsletter->duplicate($data)->asArray();
|
||||||
|
}
|
||||||
|
|
||||||
wp_send_json($result);
|
wp_send_json($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
51
lib/Router/Queue.php
Normal file
51
lib/Router/Queue.php
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Router;
|
||||||
|
|
||||||
|
use MailPoet\Models\Setting;
|
||||||
|
use MailPoet\Queue\Daemon;
|
||||||
|
use MailPoet\Queue\Supervisor;
|
||||||
|
|
||||||
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
|
class Queue {
|
||||||
|
function start() {
|
||||||
|
$supervisor = new Supervisor();
|
||||||
|
wp_send_json(
|
||||||
|
array(
|
||||||
|
'result' => ($supervisor->checkDaemon($forceStart = true)) ?
|
||||||
|
true :
|
||||||
|
false
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update($data) {
|
||||||
|
switch ($data['action']) {
|
||||||
|
case 'stop':
|
||||||
|
$status = 'stopped';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$status = 'paused';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$daemon = new Daemon();
|
||||||
|
if(!$daemon->daemon || $daemon->daemonData['status'] !== 'started') {
|
||||||
|
$result = false;
|
||||||
|
} else {
|
||||||
|
$daemon->daemonData['status'] = $status;
|
||||||
|
$daemon->daemon->value = json_encode($daemon->daemonData);
|
||||||
|
$result = $daemon->daemon->save();
|
||||||
|
}
|
||||||
|
wp_send_json(
|
||||||
|
array(
|
||||||
|
'result' => $result
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getQueueStatus() {
|
||||||
|
$daemon = new \MailPoet\Queue\BootStrapMenu();
|
||||||
|
wp_send_json($daemon->bootStrap());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,9 @@
|
|||||||
namespace MailPoet\Router;
|
namespace MailPoet\Router;
|
||||||
use \MailPoet\Models\Segment;
|
use \MailPoet\Models\Segment;
|
||||||
use \MailPoet\Models\SubscriberSegment;
|
use \MailPoet\Models\SubscriberSegment;
|
||||||
|
use \MailPoet\Models\SegmentFilter;
|
||||||
use \MailPoet\Listing;
|
use \MailPoet\Listing;
|
||||||
|
use \MailPoet\Segments\WP;
|
||||||
|
|
||||||
if(!defined('ABSPATH')) exit;
|
if(!defined('ABSPATH')) exit;
|
||||||
|
|
||||||
@ -42,15 +44,15 @@ class Segments {
|
|||||||
'subscribers'
|
'subscribers'
|
||||||
)
|
)
|
||||||
->select_expr(
|
->select_expr(
|
||||||
'SUM(CASE status WHEN "subscribed" THEN 1 ELSE 0 END)',
|
'SUM(CASE subscribers.status WHEN "subscribed" THEN 1 ELSE 0 END)',
|
||||||
'subscribed'
|
'subscribed'
|
||||||
)
|
)
|
||||||
->select_expr(
|
->select_expr(
|
||||||
'SUM(CASE status WHEN "unsubscribed" THEN 1 ELSE 0 END)',
|
'SUM(CASE subscribers.status WHEN "unsubscribed" THEN 1 ELSE 0 END)',
|
||||||
'unsubscribed'
|
'unsubscribed'
|
||||||
)
|
)
|
||||||
->select_expr(
|
->select_expr(
|
||||||
'SUM(CASE status WHEN "unconfirmed" THEN 1 ELSE 0 END)',
|
'SUM(CASE subscribers.status WHEN "unconfirmed" THEN 1 ELSE 0 END)',
|
||||||
'unconfirmed'
|
'unconfirmed'
|
||||||
)
|
)
|
||||||
->findOne()->asArray();
|
->findOne()->asArray();
|
||||||
@ -71,13 +73,20 @@ class Segments {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function save($data = array()) {
|
function save($data = array()) {
|
||||||
$result = Segment::createOrUpdate($data);
|
$errors = array();
|
||||||
|
$result = false;
|
||||||
|
|
||||||
if($result !== true) {
|
$segment = Segment::createOrUpdate($data);
|
||||||
wp_send_json($result);
|
|
||||||
|
if($segment !== false && !$segment->id()) {
|
||||||
|
$errors = $segment->getValidationErrors();
|
||||||
} else {
|
} else {
|
||||||
wp_send_json(true);
|
$result = true;
|
||||||
}
|
}
|
||||||
|
wp_send_json(array(
|
||||||
|
'result' => $result,
|
||||||
|
'errors' => $errors
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
function restore($id) {
|
function restore($id) {
|
||||||
@ -128,6 +137,12 @@ class Segments {
|
|||||||
wp_send_json($result);
|
wp_send_json($result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function synchronize() {
|
||||||
|
$result = WP::synchronizeUsers();
|
||||||
|
|
||||||
|
wp_send_json($result);
|
||||||
|
}
|
||||||
|
|
||||||
function bulkAction($data = array()) {
|
function bulkAction($data = array()) {
|
||||||
$bulk_action = new Listing\BulkAction(
|
$bulk_action = new Listing\BulkAction(
|
||||||
'\MailPoet\Models\Segment',
|
'\MailPoet\Models\Segment',
|
||||||
|
@ -21,7 +21,14 @@ class Subscribers {
|
|||||||
if($subscriber === false) {
|
if($subscriber === false) {
|
||||||
wp_send_json(false);
|
wp_send_json(false);
|
||||||
} else {
|
} else {
|
||||||
wp_send_json($subscriber->asArray());
|
$segments = $subscriber->segments()->findArray();
|
||||||
|
|
||||||
|
$subscriber = $subscriber->asArray();
|
||||||
|
$subscriber['segments'] = array_map(function($segment) {
|
||||||
|
return $segment['id'];
|
||||||
|
}, $segments);
|
||||||
|
|
||||||
|
wp_send_json($subscriber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,18 +65,40 @@ class Subscribers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function save($data = array()) {
|
function save($data = array()) {
|
||||||
|
$errors = array();
|
||||||
$result = false;
|
$result = false;
|
||||||
|
$segments = false;
|
||||||
|
|
||||||
|
if(array_key_exists('segments', $data)) {
|
||||||
|
$segments = $data['segments'];
|
||||||
|
unset($data['segments']);
|
||||||
|
}
|
||||||
|
|
||||||
$subscriber = Subscriber::createOrUpdate($data);
|
$subscriber = Subscriber::createOrUpdate($data);
|
||||||
|
|
||||||
if($subscriber !== false && !$subscriber->id()) {
|
if($subscriber !== false && !$subscriber->id()) {
|
||||||
$result = array(
|
$errors = $subscriber->getValidationErrors();
|
||||||
'errors' => $subscriber->getValidationErrors()
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
$result = true;
|
$result = true;
|
||||||
|
|
||||||
|
if($segments !== false) {
|
||||||
|
SubscriberSegment::where('subscriber_id', $subscriber->id)
|
||||||
|
->deleteMany();
|
||||||
|
|
||||||
|
if(!empty($segments)) {
|
||||||
|
foreach($segments as $segment_id) {
|
||||||
|
$relation = SubscriberSegment::create();
|
||||||
|
$relation->segment_id = $segment_id;
|
||||||
|
$relation->subscriber_id = $subscriber->id;
|
||||||
|
$relation->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
wp_send_json($result);
|
wp_send_json(array(
|
||||||
|
'result' => $result,
|
||||||
|
'errors' => $errors
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
function subscribe($data = array()) {
|
function subscribe($data = array()) {
|
||||||
|
88
lib/Segments/WP.php
Normal file
88
lib/Segments/WP.php
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Segments;
|
||||||
|
use \MailPoet\Models\Subscriber;
|
||||||
|
use \MailPoet\Models\Segment;
|
||||||
|
|
||||||
|
class WP {
|
||||||
|
static function synchronizeUser($wp_user_id) {
|
||||||
|
$wpUser = \get_userdata($wp_user_id);
|
||||||
|
if($wpUser === false) return;
|
||||||
|
|
||||||
|
$subscriber = Subscriber::where('wp_user_id', $wpUser->ID)
|
||||||
|
->findOne();
|
||||||
|
|
||||||
|
switch(current_filter()) {
|
||||||
|
case 'delete_user':
|
||||||
|
case 'deleted_user':
|
||||||
|
case 'remove_user_from_blog':
|
||||||
|
if($subscriber !== false && $subscriber->id()) {
|
||||||
|
$subscriber->delete();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'user_register':
|
||||||
|
case 'added_existing_user':
|
||||||
|
case 'profile_update':
|
||||||
|
default:
|
||||||
|
// get first name & last name
|
||||||
|
$first_name = $wpUser->first_name;
|
||||||
|
$last_name = $wpUser->last_name;
|
||||||
|
if(empty($wpUser->first_name) && empty($wpUser->last_name)) {
|
||||||
|
$first_name = $wpUser->display_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// subscriber data
|
||||||
|
$data = array(
|
||||||
|
'wp_user_id'=> $wpUser->ID,
|
||||||
|
'email' => $wpUser->user_email,
|
||||||
|
'first_name' => $first_name,
|
||||||
|
'last_name' => $last_name,
|
||||||
|
'status' => 'subscribed'
|
||||||
|
);
|
||||||
|
|
||||||
|
if($subscriber !== false) {
|
||||||
|
$data['id'] = $subscriber->id();
|
||||||
|
}
|
||||||
|
$subscriber = Subscriber::createOrUpdate($data);
|
||||||
|
|
||||||
|
if($subscriber !== false && $subscriber->id()) {
|
||||||
|
$segment = Segment::getWPUsers();
|
||||||
|
$segment->addSubscriber($subscriber->id());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function synchronizeUsers() {
|
||||||
|
// get wordpress users list
|
||||||
|
$segment = Segment::getWPUsers();
|
||||||
|
|
||||||
|
// count WP users
|
||||||
|
$users_count = \count_users()['total_users'];
|
||||||
|
$linked_subscribers_count = $segment->subscribers()->count();
|
||||||
|
|
||||||
|
if($users_count !== $linked_subscribers_count) {
|
||||||
|
$linked_subscribers = Subscriber::select('wp_user_id')
|
||||||
|
->whereNotNull('wp_user_id')
|
||||||
|
->findArray();
|
||||||
|
|
||||||
|
$exclude_ids = array();
|
||||||
|
if(!empty($linked_subscribers)) {
|
||||||
|
$exclude_ids = array_map(function($subscriber) {
|
||||||
|
return $subscriber['wp_user_id'];
|
||||||
|
}, $linked_subscribers);
|
||||||
|
}
|
||||||
|
|
||||||
|
$users = \get_users(array(
|
||||||
|
'count_total' => false,
|
||||||
|
'fields' => 'ID',
|
||||||
|
'exclude' => $exclude_ids
|
||||||
|
));
|
||||||
|
|
||||||
|
foreach($users as $user) {
|
||||||
|
static::synchronizeUser($user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -4,17 +4,17 @@ namespace MailPoet\Settings;
|
|||||||
class Pages {
|
class Pages {
|
||||||
|
|
||||||
static function getAll() {
|
static function getAll() {
|
||||||
$mailpoet_pages = get_posts(array(
|
$mailpoet_pages = \get_posts(array(
|
||||||
'post_type' => 'mailpoet_page'
|
'post_type' => 'mailpoet_page'
|
||||||
));
|
));
|
||||||
|
|
||||||
$pages = array();
|
$pages = array();
|
||||||
foreach(array_merge($mailpoet_pages, get_pages()) as $page) {
|
foreach(array_merge($mailpoet_pages, \get_pages()) as $page) {
|
||||||
$pages[] = array(
|
$pages[] = array(
|
||||||
'id' => $page->ID,
|
'id' => $page->ID,
|
||||||
'title' => $page->post_title,
|
'title' => $page->post_title,
|
||||||
'preview_url' => get_permalink($page->ID),
|
'preview_url' => \get_permalink($page->ID),
|
||||||
'edit_url' => get_edit_post_link($page->ID)
|
'edit_url' => \get_edit_post_link($page->ID)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
135
lib/Subscribers/ImportExport/BootStrapMenu.php
Normal file
135
lib/Subscribers/ImportExport/BootStrapMenu.php
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Subscribers\ImportExport;
|
||||||
|
|
||||||
|
use MailPoet\Models\CustomField;
|
||||||
|
use MailPoet\Models\Segment;
|
||||||
|
use MailPoet\Util\Helpers;
|
||||||
|
|
||||||
|
class BootStrapMenu {
|
||||||
|
function __construct($action = null) {
|
||||||
|
$this->action = $action;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSegments($withConfirmedSubscribers = false) {
|
||||||
|
$segments = ($this->action === 'import') ?
|
||||||
|
Segment::getSegmentsForImport() :
|
||||||
|
Segment::getSegmentsForExport($withConfirmedSubscribers);
|
||||||
|
return array_map(function ($segment) {
|
||||||
|
return array(
|
||||||
|
'id' => $segment['id'],
|
||||||
|
'name' => $segment['name'],
|
||||||
|
'subscriberCount' => $segment['subscribers']
|
||||||
|
);
|
||||||
|
}, $segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSubscriberFields() {
|
||||||
|
return array(
|
||||||
|
'email' => __('Email'),
|
||||||
|
'first_name' => __('First name'),
|
||||||
|
'last_name' => __('Last name'),
|
||||||
|
'status' => __('Status')
|
||||||
|
/*
|
||||||
|
'confirmed_ip' => __('IP address')
|
||||||
|
'confirmed_at' => __('Subscription date')
|
||||||
|
*/
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSubscriberFields($subscriberFields) {
|
||||||
|
return array_map(function ($fieldId, $fieldName) {
|
||||||
|
return array(
|
||||||
|
'id' => $fieldId,
|
||||||
|
'name' => $fieldName,
|
||||||
|
'type' => ($fieldId === 'confirmed_at') ? 'date' : null,
|
||||||
|
'custom' => false
|
||||||
|
);
|
||||||
|
}, array_keys($subscriberFields), $subscriberFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSubscriberCustomFields() {
|
||||||
|
return CustomField::findArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSubscriberCustomFields($subscriberCustomFields) {
|
||||||
|
return array_map(function ($field) {
|
||||||
|
return array(
|
||||||
|
'id' => $field['id'],
|
||||||
|
'name' => $field['name'],
|
||||||
|
'type' => $field['type'],
|
||||||
|
'custom' => true
|
||||||
|
);
|
||||||
|
}, $subscriberCustomFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatFieldsForSelect2(
|
||||||
|
$subscriberFields,
|
||||||
|
$subscriberCustomFields) {
|
||||||
|
$actions = ($this->action === 'import') ?
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'id' => 'ignore',
|
||||||
|
'name' => __('Ignore column...'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'id' => 'create',
|
||||||
|
'name' => __('Create new column...')
|
||||||
|
),
|
||||||
|
) :
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'id' => 'select',
|
||||||
|
'name' => __('Select all...'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'id' => 'deselect',
|
||||||
|
'name' => __('Deselect all...')
|
||||||
|
),
|
||||||
|
);
|
||||||
|
$select2Fields = array(
|
||||||
|
array(
|
||||||
|
'name' => __('Actions'),
|
||||||
|
'children' => $actions
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => __('System columns'),
|
||||||
|
'children' => $this->formatSubscriberFields($subscriberFields)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if($subscriberCustomFields) {
|
||||||
|
array_push($select2Fields, array(
|
||||||
|
'name' => __('User columns'),
|
||||||
|
'children' => $this->formatSubscriberCustomFields(
|
||||||
|
$subscriberCustomFields
|
||||||
|
)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return $select2Fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bootstrap() {
|
||||||
|
$subscriberFields = $this->getSubscriberFields();
|
||||||
|
$subscriberCustomFields = $this->getSubscriberCustomFields();
|
||||||
|
$data['segments'] = json_encode($this->getSegments());
|
||||||
|
$data['subscriberFieldsSelect2'] = json_encode(
|
||||||
|
$this->formatFieldsForSelect2(
|
||||||
|
$subscriberFields,
|
||||||
|
$subscriberCustomFields
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if($this->action === 'import') {
|
||||||
|
$data['subscriberFields'] = json_encode(
|
||||||
|
array_merge(
|
||||||
|
$this->formatSubscriberFields($subscriberFields),
|
||||||
|
$this->formatSubscriberCustomFields($subscriberCustomFields)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$data['maxPostSizeBytes'] = Helpers::getMaxPostSize('bytes');
|
||||||
|
$data['maxPostSize'] = Helpers::getMaxPostSize();
|
||||||
|
} else {
|
||||||
|
$data['segmentsWithConfirmedSubscribers'] =
|
||||||
|
json_encode($this->getSegments($withConfirmedSubscribers = true));
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
220
lib/Subscribers/ImportExport/Export/Export.php
Normal file
220
lib/Subscribers/ImportExport/Export/Export.php
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Subscribers\ImportExport\Export;
|
||||||
|
|
||||||
|
use MailPoet\Config\Env;
|
||||||
|
use MailPoet\Models\CustomField;
|
||||||
|
use MailPoet\Models\Segment;
|
||||||
|
use MailPoet\Models\Subscriber;
|
||||||
|
use MailPoet\Models\SubscriberSegment;
|
||||||
|
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
|
||||||
|
use MailPoet\Util\Helpers;
|
||||||
|
use MailPoet\Util\XLSXWriter;
|
||||||
|
|
||||||
|
class Export {
|
||||||
|
public function __construct($data) {
|
||||||
|
$this->exportConfirmedOption = $data['exportConfirmedOption'];
|
||||||
|
$this->exportFormatOption = $data['exportFormatOption'];
|
||||||
|
$this->groupBySegmentOption = $data['groupBySegmentOption'];
|
||||||
|
$this->segments = $data['segments'];
|
||||||
|
$this->subscribersWithoutSegment = array_search(0, $this->segments);
|
||||||
|
$this->subscriberFields = $data['subscriberFields'];
|
||||||
|
$this->exportFile = $this->getExportFile($this->exportFormatOption);
|
||||||
|
$this->exportFileURL = $this->getExportFileURL($this->exportFile);
|
||||||
|
$this->profilerStart = microtime(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function process() {
|
||||||
|
$subscribers = $this->getSubscribers();
|
||||||
|
$subscriberCustomFields = $this->getSubscriberCustomFields();
|
||||||
|
$formattedSubscriberFields = $this->formatSubscriberFields(
|
||||||
|
$this->subscriberFields,
|
||||||
|
$subscriberCustomFields
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
if($this->exportFormatOption === 'csv') {
|
||||||
|
$CSVFile = fopen($this->exportFile, 'w');
|
||||||
|
$formatCSV = function ($row) {
|
||||||
|
return '"' . str_replace('"', '\"', $row) . '"';
|
||||||
|
};
|
||||||
|
// add UTF-8 BOM (3 bytes, hex EF BB BF) at the start of the file for
|
||||||
|
// Excel to automatically recognize the encoding
|
||||||
|
fwrite($CSVFile, chr(0xEF) . chr(0xBB) . chr(0xBF));
|
||||||
|
if($this->groupBySegmentOption) {
|
||||||
|
$formattedSubscriberFields[] = __('List');
|
||||||
|
}
|
||||||
|
fwrite(
|
||||||
|
$CSVFile,
|
||||||
|
implode(
|
||||||
|
',',
|
||||||
|
array_map(
|
||||||
|
$formatCSV,
|
||||||
|
$formattedSubscriberFields
|
||||||
|
)
|
||||||
|
) . "\n"
|
||||||
|
);
|
||||||
|
foreach ($subscribers as $subscriber) {
|
||||||
|
$row = $this->formatSubscriberData(
|
||||||
|
$subscriber,
|
||||||
|
$formattedSubscriberFields
|
||||||
|
);
|
||||||
|
if($this->groupBySegmentOption) {
|
||||||
|
$row[] = ucwords($subscriber['segment_name']);
|
||||||
|
}
|
||||||
|
fwrite($CSVFile, implode(',', array_map($formatCSV, $row)) . "\n");
|
||||||
|
}
|
||||||
|
fclose($CSVFile);
|
||||||
|
} else {
|
||||||
|
$writer = new XLSXWriter();
|
||||||
|
$writer->setAuthor('MailPoet (www.mailpoet.com)');
|
||||||
|
$headerRow = array($formattedSubscriberFields);
|
||||||
|
$lastSegment = false;
|
||||||
|
$rows = array();
|
||||||
|
foreach ($subscribers as $subscriber) {
|
||||||
|
if($lastSegment && $lastSegment !== $subscriber['segment_name'] &&
|
||||||
|
$this->groupBySegmentOption
|
||||||
|
) {
|
||||||
|
$writer->writeSheet(
|
||||||
|
array_merge($headerRow, $rows), ucwords($lastSegment)
|
||||||
|
);
|
||||||
|
$rows = array();
|
||||||
|
}
|
||||||
|
// detect RTL language and set Excel to properly display the sheet
|
||||||
|
$RTLRegex = '/\p{Arabic}|\p{Hebrew}/u';
|
||||||
|
if(!$writer->rtl && (
|
||||||
|
preg_grep($RTLRegex, $subscriber) ||
|
||||||
|
preg_grep($RTLRegex, $formattedSubscriberFields))
|
||||||
|
) {
|
||||||
|
$writer->rtl = true;
|
||||||
|
}
|
||||||
|
$rows[] = $this->formatSubscriberData(
|
||||||
|
$subscriber,
|
||||||
|
$formattedSubscriberFields
|
||||||
|
);
|
||||||
|
$lastSegment = $subscriber['segment_name'];
|
||||||
|
}
|
||||||
|
$writer->writeSheet(
|
||||||
|
array_merge($headerRow, $rows),
|
||||||
|
($this->groupBySegmentOption) ?
|
||||||
|
ucwords($subscriber['segment_name']) :
|
||||||
|
'MailPoet'
|
||||||
|
);
|
||||||
|
$writer->writeToFile($this->exportFile);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return array(
|
||||||
|
'result' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return array(
|
||||||
|
'result' => true,
|
||||||
|
'data' => array(
|
||||||
|
'totalExported' => count($subscribers),
|
||||||
|
'exportFileURL' => $this->exportFileURL
|
||||||
|
),
|
||||||
|
'profiler' => $this->timeExecution()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSubscribers() {
|
||||||
|
$subscribers = Subscriber::
|
||||||
|
left_outer_join(
|
||||||
|
SubscriberSegment::$_table,
|
||||||
|
array(
|
||||||
|
Subscriber::$_table . '.id',
|
||||||
|
'=',
|
||||||
|
SubscriberSegment::$_table . '.subscriber_id'
|
||||||
|
))
|
||||||
|
->left_outer_join(
|
||||||
|
Segment::$_table,
|
||||||
|
array(
|
||||||
|
Segment::$_table . '.id',
|
||||||
|
'=',
|
||||||
|
SubscriberSegment::$_table . '.segment_id'
|
||||||
|
))
|
||||||
|
->orderByAsc('segment_name')
|
||||||
|
->filter('filterWithCustomFieldsForExport');
|
||||||
|
if($this->subscribersWithoutSegment !== false) {
|
||||||
|
$subscribers = $subscribers
|
||||||
|
->selectExpr('CASE WHEN ' . Segment::$_table . '.name IS NOT NULL ' .
|
||||||
|
'THEN ' . Segment::$_table . '.name ' .
|
||||||
|
'ELSE "' . __('Not In List') . '" END as segment_name'
|
||||||
|
)
|
||||||
|
->whereRaw(
|
||||||
|
SubscriberSegment::$_table . '.segment_id IN (' .
|
||||||
|
rtrim(str_repeat('?,', count($this->segments)), ',') . ') ' .
|
||||||
|
'OR ' . SubscriberSegment::$_table . '.segment_id IS NULL ',
|
||||||
|
$this->segments
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$subscribers = $subscribers
|
||||||
|
->select(Segment::$_table . '.name', 'segment_name')
|
||||||
|
->whereIn(SubscriberSegment::$_table . '.segment_id', $this->segments);
|
||||||
|
}
|
||||||
|
if(!$this->groupBySegmentOption) {
|
||||||
|
$subscribers =
|
||||||
|
$subscribers->groupBy(Subscriber::$_table . '.id');
|
||||||
|
}
|
||||||
|
if($this->exportConfirmedOption) {
|
||||||
|
$subscribers =
|
||||||
|
$subscribers->where(Subscriber::$_table . '.status', 'subscribed');
|
||||||
|
}
|
||||||
|
$subscribers = $subscribers->whereNull(Subscriber::$_table . '.deleted_at');
|
||||||
|
|
||||||
|
return $subscribers->findArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExportFileURL($file) {
|
||||||
|
return sprintf(
|
||||||
|
'%s/%s/%s',
|
||||||
|
Env::$plugin_url,
|
||||||
|
Env::$temp_name,
|
||||||
|
basename($file)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExportFile($format) {
|
||||||
|
return sprintf(
|
||||||
|
Env::$temp_path . '/MailPoet_export_%s.%s',
|
||||||
|
substr(md5(time()), 0, 4),
|
||||||
|
$format
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSubscriberCustomFields() {
|
||||||
|
return Helpers::arrayColumn(
|
||||||
|
CustomField::findArray(),
|
||||||
|
'name',
|
||||||
|
'id'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSubscriberFields($subscriberFields, $subscriberCustomFields) {
|
||||||
|
$bootStrapMenu = new BootStrapMenu();
|
||||||
|
$translatedFields = $bootStrapMenu->getSubscriberFields();
|
||||||
|
return array_map(function ($field) use (
|
||||||
|
$translatedFields, $subscriberCustomFields
|
||||||
|
) {
|
||||||
|
$field = (isset($translatedFields[$field])) ?
|
||||||
|
ucfirst($translatedFields[$field]) :
|
||||||
|
ucfirst($field);
|
||||||
|
return (isset($subscriberCustomFields[$field])) ?
|
||||||
|
$subscriberCustomFields[$field] : $field;
|
||||||
|
}, $subscriberFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatSubscriberData($subscriber, $subscriberCustomFields) {
|
||||||
|
return array_map(function ($field) use (
|
||||||
|
$subscriber, $subscriberCustomFields
|
||||||
|
) {
|
||||||
|
return (isset($subscriberCustomFields[$field])) ?
|
||||||
|
$subscriberCustomFields[$field] :
|
||||||
|
$subscriber[$field];
|
||||||
|
}, $this->subscriberFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
function timeExecution() {
|
||||||
|
$profilerEnd = microtime(true);
|
||||||
|
return ($profilerEnd - $this->profilerStart) / 60;
|
||||||
|
}
|
||||||
|
}
|
337
lib/Subscribers/ImportExport/Import/Import.php
Normal file
337
lib/Subscribers/ImportExport/Import/Import.php
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Subscribers\ImportExport\Import;
|
||||||
|
|
||||||
|
use MailPoet\Models\Subscriber;
|
||||||
|
use MailPoet\Models\SubscriberCustomField;
|
||||||
|
use MailPoet\Models\SubscriberSegment;
|
||||||
|
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
|
||||||
|
use MailPoet\Util\Helpers;
|
||||||
|
|
||||||
|
class Import {
|
||||||
|
public function __construct($data) {
|
||||||
|
$this->subscribersData = $data['subscribers'];
|
||||||
|
$this->segments = $data['segments'];
|
||||||
|
$this->updateSubscribers = $data['updateSubscribers'];
|
||||||
|
$this->subscriberFields = $this->getSubscriberFields(
|
||||||
|
array_keys($this->subscribersData)
|
||||||
|
);
|
||||||
|
$this->subscriberCustomFields = $this->getCustomSubscriberFields(
|
||||||
|
array_keys($this->subscribersData)
|
||||||
|
);
|
||||||
|
$this->subscribersCount = count(reset($this->subscribersData));
|
||||||
|
$this->currentTime = date('Y-m-d H:i:s');
|
||||||
|
$this->profilerStart = microtime(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function process() {
|
||||||
|
$subscriberFields = $this->subscriberFields;
|
||||||
|
$subscriberCustomFields = $this->subscriberCustomFields;
|
||||||
|
$subscribersData = $this->subscribersData;
|
||||||
|
list ($subscribersData, $subscriberFields) =
|
||||||
|
$this->filterSubscriberStatus($subscribersData, $subscriberFields);
|
||||||
|
$this->deleteExistingTrashedSubscribers($subscribersData);
|
||||||
|
list($subscribersData, $subscriberFields) = $this->extendSubscribersAndFields(
|
||||||
|
$subscribersData, $subscriberFields
|
||||||
|
);
|
||||||
|
list($existingSubscribers, $newSubscribers) =
|
||||||
|
$this->filterExistingAndNewSubscribers($subscribersData);
|
||||||
|
$createdSubscribers = $updatedSubscribers = array();
|
||||||
|
try {
|
||||||
|
if($newSubscribers) {
|
||||||
|
$createdSubscribers =
|
||||||
|
$this->createOrUpdateSubscribers(
|
||||||
|
'create',
|
||||||
|
$newSubscribers,
|
||||||
|
$subscriberFields,
|
||||||
|
$subscriberCustomFields
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if($existingSubscribers && $this->updateSubscribers) {
|
||||||
|
$updatedSubscribers =
|
||||||
|
$this->createOrUpdateSubscribers(
|
||||||
|
'update',
|
||||||
|
$existingSubscribers,
|
||||||
|
$subscriberFields,
|
||||||
|
$subscriberCustomFields
|
||||||
|
);
|
||||||
|
if($createdSubscribers) {
|
||||||
|
// subtract added from updated subscribers when DB operation takes <1s
|
||||||
|
$updatedSubscribers = array_diff_key(
|
||||||
|
$updatedSubscribers,
|
||||||
|
$createdSubscribers,
|
||||||
|
$subscriberCustomFields
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (\PDOException $e) {
|
||||||
|
return array(
|
||||||
|
'result' => false,
|
||||||
|
'error' => $e->getMessage()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$segments = new BootStrapMenu('import');
|
||||||
|
return array(
|
||||||
|
'result' => true,
|
||||||
|
'data' => array(
|
||||||
|
'created' => count($createdSubscribers),
|
||||||
|
'updated' => count($updatedSubscribers),
|
||||||
|
'segments' => $segments->getSegments()
|
||||||
|
),
|
||||||
|
'profiler' => $this->timeExecution()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterExistingAndNewSubscribers($subscribersData) {
|
||||||
|
$existingRecords = array_filter(
|
||||||
|
array_map(function ($subscriberEmails) {
|
||||||
|
return Subscriber::selectMany(array('email'))
|
||||||
|
->whereIn('email', $subscriberEmails)
|
||||||
|
->whereNull('deleted_at')
|
||||||
|
->findArray();
|
||||||
|
}, array_chunk($subscribersData['email'], 200))
|
||||||
|
);
|
||||||
|
if(!$existingRecords) {
|
||||||
|
return array(
|
||||||
|
false,
|
||||||
|
$subscribersData
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$existingRecords = Helpers::flattenArray($existingRecords);
|
||||||
|
$newRecords = array_keys(
|
||||||
|
array_diff(
|
||||||
|
$subscribersData['email'],
|
||||||
|
$existingRecords
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if(!$newRecords) {
|
||||||
|
return array(
|
||||||
|
$subscribersData,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$newSubscribers =
|
||||||
|
array_filter(
|
||||||
|
array_map(function ($subscriber) use ($newRecords) {
|
||||||
|
return array_map(function ($index) use ($subscriber) {
|
||||||
|
return $subscriber[$index];
|
||||||
|
}, $newRecords);
|
||||||
|
}, $subscribersData)
|
||||||
|
);
|
||||||
|
|
||||||
|
$existingSubscribers =
|
||||||
|
array_map(function ($subscriber) use ($newRecords) {
|
||||||
|
return array_values( // reindex array
|
||||||
|
array_filter( // remove NULL entries
|
||||||
|
array_map(function ($index, $data) use ($newRecords) {
|
||||||
|
if(!in_array($index, $newRecords)) return $data;
|
||||||
|
}, array_keys($subscriber), $subscriber)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}, $subscribersData);
|
||||||
|
return array(
|
||||||
|
$existingSubscribers,
|
||||||
|
$newSubscribers
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteExistingTrashedSubscribers($subscribersData) {
|
||||||
|
$existingTrashedRecords = array_filter(
|
||||||
|
array_map(function ($subscriberEmails) {
|
||||||
|
return Subscriber::selectMany(array('id'))
|
||||||
|
->whereIn('email', $subscriberEmails)
|
||||||
|
->whereNotNull('deleted_at')
|
||||||
|
->findArray();
|
||||||
|
}, array_chunk($subscribersData['email'], 200))
|
||||||
|
);
|
||||||
|
if(!$existingTrashedRecords) return;
|
||||||
|
$existingTrashedRecords = Helpers::flattenArray($existingTrashedRecords);
|
||||||
|
foreach (array_chunk($existingTrashedRecords, 200) as $subscriberIds) {
|
||||||
|
Subscriber::whereIn('id', $subscriberIds)
|
||||||
|
->deleteMany();
|
||||||
|
SubscriberSegment::whereIn('subscriber_id', $subscriberIds)
|
||||||
|
->deleteMany();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extendSubscribersAndFields($subscribersData, $subscriberFields) {
|
||||||
|
$subscribersData['created_at'] = $this->filterSubscriberCreatedAtDate();
|
||||||
|
$subscriberFields[] = 'created_at';
|
||||||
|
return array(
|
||||||
|
$subscribersData,
|
||||||
|
$subscriberFields
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSubscriberFields($subscriberFields) {
|
||||||
|
return array_values(
|
||||||
|
array_filter(
|
||||||
|
array_map(function ($field) {
|
||||||
|
if(!is_int($field)) return $field;
|
||||||
|
}, $subscriberFields)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCustomSubscriberFields($subscriberFields) {
|
||||||
|
return array_values(
|
||||||
|
array_filter(
|
||||||
|
array_map(function ($field) {
|
||||||
|
if(is_int($field)) return $field;
|
||||||
|
}, $subscriberFields)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterSubscriberCreatedAtDate() {
|
||||||
|
return array_fill(0, $this->subscribersCount, $this->currentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterSubscriberStatus($subscribersData, $subscriberFields) {
|
||||||
|
if(!in_array('status', $subscriberFields)) {
|
||||||
|
$subscribersData['status'] =
|
||||||
|
array_fill(0, count($subscribersData['email']), 'subscribed');
|
||||||
|
$subscriberFields[] = 'status';
|
||||||
|
return array(
|
||||||
|
$subscribersData,
|
||||||
|
$subscriberFields
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$statuses = array(
|
||||||
|
'subscribed' => array(
|
||||||
|
'subscribed',
|
||||||
|
'confirmed',
|
||||||
|
1,
|
||||||
|
'1',
|
||||||
|
'true'
|
||||||
|
),
|
||||||
|
'unconfirmed' => array(
|
||||||
|
'unconfirmed',
|
||||||
|
0,
|
||||||
|
"0"
|
||||||
|
),
|
||||||
|
'unsubscribed' => array(
|
||||||
|
'unsubscribed',
|
||||||
|
-1,
|
||||||
|
'-1',
|
||||||
|
'false'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$subscribersData['status'] = array_map(function ($state) use ($statuses) {
|
||||||
|
if(in_array(strtolower($state), $statuses['subscribed'])) {
|
||||||
|
return 'subscribed';
|
||||||
|
}
|
||||||
|
if(in_array(strtolower($state), $statuses['unsubscribed'])) {
|
||||||
|
return 'unsubscribed';
|
||||||
|
}
|
||||||
|
if(in_array(strtolower($state), $statuses['unconfirmed'])) {
|
||||||
|
return 'unconfirmed';
|
||||||
|
}
|
||||||
|
return 'subscribed'; // make "subscribed" a default status
|
||||||
|
}, $subscribersData['status']);
|
||||||
|
return array(
|
||||||
|
$subscribersData,
|
||||||
|
$subscriberFields
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOrUpdateSubscribers(
|
||||||
|
$action,
|
||||||
|
$subscribersData,
|
||||||
|
$subscriberFields,
|
||||||
|
$subscriberCustomFields
|
||||||
|
) {
|
||||||
|
$subscribersCount = count(reset($subscribersData)) - 1;
|
||||||
|
$subscribers = array_map(function ($index) use ($subscribersData, $subscriberFields) {
|
||||||
|
return array_map(function ($field) use ($index, $subscribersData) {
|
||||||
|
return $subscribersData[$field][$index];
|
||||||
|
}, $subscriberFields);
|
||||||
|
}, range(0, $subscribersCount));
|
||||||
|
$currentTime = ($action === 'update') ? date('Y-m-d H:i:s') : $this->currentTime;
|
||||||
|
foreach (array_chunk($subscribers, 200) as $data) {
|
||||||
|
if($action == 'create') {
|
||||||
|
Subscriber::createMultiple(
|
||||||
|
$subscriberFields,
|
||||||
|
$data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if($action == 'update') {
|
||||||
|
Subscriber::updateMultiple(
|
||||||
|
$subscriberFields,
|
||||||
|
$data,
|
||||||
|
$currentTime
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$result = Helpers::arrayColumn( // return id=>email array of results
|
||||||
|
Subscriber::selectMany(
|
||||||
|
array(
|
||||||
|
'id',
|
||||||
|
'email'
|
||||||
|
))
|
||||||
|
->where(($action === 'create') ? 'created_at' : 'updated_at', $currentTime)
|
||||||
|
->findArray(),
|
||||||
|
'email', 'id'
|
||||||
|
);
|
||||||
|
if($subscriberCustomFields) {
|
||||||
|
$this->createOrUpdateCustomFields(
|
||||||
|
($action === 'create') ? 'create' : 'update',
|
||||||
|
$result,
|
||||||
|
$subscribersData,
|
||||||
|
$subscriberCustomFields
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->addSubscribersToSegments(
|
||||||
|
array_keys($result),
|
||||||
|
$this->segments
|
||||||
|
);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createOrUpdateCustomFields(
|
||||||
|
$action,
|
||||||
|
$dbSubscribers,
|
||||||
|
$subscribersData,
|
||||||
|
$subscriberCustomFields
|
||||||
|
) {
|
||||||
|
$subscribers = array_map(
|
||||||
|
function ($column) use ($dbSubscribers, $subscribersData) {
|
||||||
|
$count = range(0, count($subscribersData[$column]) - 1);
|
||||||
|
return array_map(
|
||||||
|
function ($index, $value)
|
||||||
|
use ($dbSubscribers, $subscribersData, $column) {
|
||||||
|
$subscriberId = array_search(
|
||||||
|
$subscribersData['email'][$index],
|
||||||
|
$dbSubscribers
|
||||||
|
);
|
||||||
|
return array(
|
||||||
|
$column,
|
||||||
|
$subscriberId,
|
||||||
|
$value
|
||||||
|
);
|
||||||
|
}, $count, $subscribersData[$column]);
|
||||||
|
}, $subscriberCustomFields)[0];
|
||||||
|
foreach (array_chunk($subscribers, 200) as $data) {
|
||||||
|
if($action === 'create') {
|
||||||
|
SubscriberCustomField::createMultiple(
|
||||||
|
$data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if($action === 'update') {
|
||||||
|
SubscriberCustomField::updateMultiple(
|
||||||
|
$data
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addSubscribersToSegments($subscribers, $segments) {
|
||||||
|
foreach (array_chunk($subscribers, 200) as $data) {
|
||||||
|
SubscriberSegment::createMultiple($segments, $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function timeExecution() {
|
||||||
|
$profilerEnd = microtime(true);
|
||||||
|
return ($profilerEnd - $this->profilerStart) / 60;
|
||||||
|
}
|
||||||
|
}
|
156
lib/Subscribers/ImportExport/Import/MailChimp.php
Normal file
156
lib/Subscribers/ImportExport/Import/MailChimp.php
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Subscribers\ImportExport\Import;
|
||||||
|
|
||||||
|
use MailPoet\Util\Helpers;
|
||||||
|
|
||||||
|
class MailChimp {
|
||||||
|
function __construct($APIKey, $lists = false) {
|
||||||
|
$this->APIKey = $this->getAPIKey($APIKey);
|
||||||
|
$this->maxPostSize = Helpers::getMaxPostSize('bytes');
|
||||||
|
$this->dataCenter = $this->getDataCenter($this->APIKey);
|
||||||
|
$this->listsURL = 'https://%s.api.mailchimp.com/2.0/lists/list?apikey=%s';
|
||||||
|
$this->exportURL = 'https://%s.api.mailchimp.com/export/1.0/list/?apikey=%s&id=%s';
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLists() {
|
||||||
|
if(!$this->APIKey || !$this->dataCenter) {
|
||||||
|
return $this->processError('API');
|
||||||
|
}
|
||||||
|
|
||||||
|
$connection = @fopen(sprintf($this->listsURL, $this->dataCenter, $this->APIKey), 'r');
|
||||||
|
|
||||||
|
if(!$connection) {
|
||||||
|
return $this->processError('connection');
|
||||||
|
} else {
|
||||||
|
$response = '';
|
||||||
|
while (!feof($connection)) {
|
||||||
|
$buffer = fgets($connection, 4096);
|
||||||
|
if(trim($buffer) !== '') {
|
||||||
|
$response .= $buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose($connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = json_decode($response);
|
||||||
|
|
||||||
|
if(!$response) {
|
||||||
|
return $this->processError('API');
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($response->data as $list) {
|
||||||
|
$lists[] = array(
|
||||||
|
'id' => $list->id,
|
||||||
|
'name' => $list->name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'result' => true,
|
||||||
|
'data' => $lists
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSubscribers($lists = array()) {
|
||||||
|
if(!$this->APIKey || !$this->dataCenter) {
|
||||||
|
return $this->processError('API');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$lists) {
|
||||||
|
return $this->processError('lists');
|
||||||
|
}
|
||||||
|
|
||||||
|
$bytesFetched = 0;
|
||||||
|
foreach ($lists as $list) {
|
||||||
|
$url = sprintf($this->exportURL, $this->dataCenter, $this->APIKey, $list);
|
||||||
|
$connection = @fopen($url, 'r');
|
||||||
|
if(!$connection) {
|
||||||
|
return $this->processError('connection');
|
||||||
|
} else {
|
||||||
|
$i = 0;
|
||||||
|
$header = array();
|
||||||
|
while (!feof($connection)) {
|
||||||
|
$buffer = fgets($connection, 4096);
|
||||||
|
if(trim($buffer) !== '') {
|
||||||
|
$obj = json_decode($buffer);
|
||||||
|
if($i === 0) {
|
||||||
|
$header = $obj;
|
||||||
|
if(is_object($header) && isset($header->error)) {
|
||||||
|
return $this->processError('lists');
|
||||||
|
}
|
||||||
|
if(!isset($headerHash)) {
|
||||||
|
$headerHash = md5(implode(',', $header));
|
||||||
|
} else {
|
||||||
|
if(md5(implode(',', $header) !== $headerHash)) {
|
||||||
|
return $this->processError('headers');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$subscribers[] = $obj;
|
||||||
|
}
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bytesFetched += strlen($buffer);
|
||||||
|
if($bytesFetched > $this->maxPostSize) {
|
||||||
|
return $this->processError('size');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose($connection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!count($subscribers)) {
|
||||||
|
return $this->processError('subscribers');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'result' => true,
|
||||||
|
'data' => array(
|
||||||
|
'subscribers' => $subscribers,
|
||||||
|
'invalid' => false,
|
||||||
|
'duplicate' => false,
|
||||||
|
'header' => $header,
|
||||||
|
'count' => count($subscribers)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDataCenter($APIKey) {
|
||||||
|
// double parantheses: http://phpsadness.com/sad/51
|
||||||
|
return ($APIKey) ? end((explode('-', $APIKey))) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAPIKey($APIKey) {
|
||||||
|
return (preg_match('/[a-zA-Z0-9]{32}-[a-zA-Z0-9]{3,}/', $APIKey)) ? $APIKey : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function processError($error) {
|
||||||
|
switch ($error) {
|
||||||
|
case 'API':
|
||||||
|
$errorMessage = __('Invalid API key.');
|
||||||
|
break;
|
||||||
|
case 'connection':
|
||||||
|
$errorMessage = __('Could not connect to your MailChimp account.');
|
||||||
|
break;
|
||||||
|
case 'headers':
|
||||||
|
$errorMessage = __('The selected lists do not have matching columns (headers).');
|
||||||
|
break;
|
||||||
|
case 'size':
|
||||||
|
$errorMessage = __('Information received from MailChimp is too large for processing. Please limit the number of lists.');
|
||||||
|
break;
|
||||||
|
case 'subscribers':
|
||||||
|
$errorMessage = __('Did not find any active subscribers.');
|
||||||
|
break;
|
||||||
|
case 'lists':
|
||||||
|
$errorMessage = __('Did not find any valid lists');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return array(
|
||||||
|
'result' => false,
|
||||||
|
'error' => $errorMessage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
187
lib/Util/Helpers.php
Normal file
187
lib/Util/Helpers.php
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
<?php
|
||||||
|
namespace MailPoet\Util;
|
||||||
|
|
||||||
|
class Helpers {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Matches each symbol of PHP date format standard
|
||||||
|
* with jQuery equivalent codeword
|
||||||
|
* @author Tristan Jahier
|
||||||
|
*/
|
||||||
|
static function dateformat_PHP_to_jQueryUI($php_format) {
|
||||||
|
$SYMBOLS_MATCHING = array(
|
||||||
|
// Day
|
||||||
|
'd' => 'dd',
|
||||||
|
'D' => 'D',
|
||||||
|
'j' => 'd',
|
||||||
|
'l' => 'DD',
|
||||||
|
'N' => '',
|
||||||
|
'S' => '',
|
||||||
|
'w' => '',
|
||||||
|
'z' => 'o',
|
||||||
|
// Week
|
||||||
|
'W' => '',
|
||||||
|
// Month
|
||||||
|
'F' => 'MM',
|
||||||
|
'm' => 'mm',
|
||||||
|
'M' => 'M',
|
||||||
|
'n' => 'm',
|
||||||
|
't' => '',
|
||||||
|
// Year
|
||||||
|
'L' => '',
|
||||||
|
'o' => '',
|
||||||
|
'Y' => 'yy',
|
||||||
|
'y' => 'y',
|
||||||
|
// Time
|
||||||
|
'a' => '',
|
||||||
|
'A' => '',
|
||||||
|
'B' => '',
|
||||||
|
'g' => '',
|
||||||
|
'G' => '',
|
||||||
|
'h' => '',
|
||||||
|
'H' => '',
|
||||||
|
'i' => '',
|
||||||
|
's' => '',
|
||||||
|
'u' => ''
|
||||||
|
);
|
||||||
|
$jqueryui_format = "";
|
||||||
|
$escaping = false;
|
||||||
|
for ($i = 0; $i < strlen($php_format); $i++) {
|
||||||
|
$char = $php_format[$i];
|
||||||
|
if($char === '\\') // PHP date format escaping character
|
||||||
|
{
|
||||||
|
$i++;
|
||||||
|
if($escaping) {
|
||||||
|
$jqueryui_format .= $php_format[$i];
|
||||||
|
} else {
|
||||||
|
$jqueryui_format .= '\'' . $php_format[$i];
|
||||||
|
}
|
||||||
|
$escaping = true;
|
||||||
|
} else {
|
||||||
|
if($escaping) {
|
||||||
|
$jqueryui_format .= "'";
|
||||||
|
$escaping = false;
|
||||||
|
}
|
||||||
|
if(isset($SYMBOLS_MATCHING[$char])) {
|
||||||
|
$jqueryui_format .= $SYMBOLS_MATCHING[$char];
|
||||||
|
} else {
|
||||||
|
$jqueryui_format .= $char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $jqueryui_format;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getMaxPostSize($bytes = false) {
|
||||||
|
$maxPostSize = ini_get('post_max_size');
|
||||||
|
if(!$bytes) return $maxPostSize;
|
||||||
|
switch (substr($maxPostSize, -1)) {
|
||||||
|
case 'M':
|
||||||
|
case 'm':
|
||||||
|
return (int) $maxPostSize * 1048576;
|
||||||
|
case 'K':
|
||||||
|
case 'k':
|
||||||
|
return (int) $maxPostSize * 1024;
|
||||||
|
case 'G':
|
||||||
|
case 'g':
|
||||||
|
return (int) $maxPostSize * 1073741824;
|
||||||
|
default:
|
||||||
|
return $maxPostSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static function flattenArray($array) {
|
||||||
|
if(!$array) return;
|
||||||
|
$flattened_array = array();
|
||||||
|
array_walk_recursive($array, function ($a) use (&$flattened_array) { $flattened_array[] = $a; });
|
||||||
|
return $flattened_array;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Using func_get_args() in order to check for proper number ofparameters and trigger errors exactly as the built-in array_column()
|
||||||
|
* does in PHP 5.5.
|
||||||
|
* @author Ben Ramsey (http://benramsey.com)
|
||||||
|
*/
|
||||||
|
static function arrayColumn($input = null, $columnKey = null, $indexKey = null) {
|
||||||
|
$argc = func_num_args();
|
||||||
|
$params = func_get_args();
|
||||||
|
if($argc < 2) {
|
||||||
|
trigger_error("array_column() expects at least 2 parameters, {$argc} given", E_USER_WARNING);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(!is_array($params[0])) {
|
||||||
|
trigger_error(
|
||||||
|
'array_column() expects parameter 1 to be array, ' . gettype($params[0]) . ' given',
|
||||||
|
E_USER_WARNING
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if(!is_int($params[1])
|
||||||
|
&& !is_float($params[1])
|
||||||
|
&& !is_string($params[1])
|
||||||
|
&& $params[1] !== null
|
||||||
|
&& !(is_object($params[1]) && method_exists($params[1], '__toString'))
|
||||||
|
) {
|
||||||
|
trigger_error('array_column(): The column key should be either a string or an integer', E_USER_WARNING);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(isset($params[2])
|
||||||
|
&& !is_int($params[2])
|
||||||
|
&& !is_float($params[2])
|
||||||
|
&& !is_string($params[2])
|
||||||
|
&& !(is_object($params[2]) && method_exists($params[2], '__toString'))
|
||||||
|
) {
|
||||||
|
trigger_error('array_column(): The index key should be either a string or an integer', E_USER_WARNING);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$paramsInput = $params[0];
|
||||||
|
$paramsColumnKey = ($params[1] !== null) ? (string) $params[1] : null;
|
||||||
|
$paramsIndexKey = null;
|
||||||
|
if(isset($params[2])) {
|
||||||
|
if(is_float($params[2]) || is_int($params[2])) {
|
||||||
|
$paramsIndexKey = (int) $params[2];
|
||||||
|
} else {
|
||||||
|
$paramsIndexKey = (string) $params[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$resultArray = array();
|
||||||
|
foreach ($paramsInput as $row) {
|
||||||
|
$key = $value = null;
|
||||||
|
$keySet = $valueSet = false;
|
||||||
|
if($paramsIndexKey !== null && array_key_exists($paramsIndexKey, $row)) {
|
||||||
|
$keySet = true;
|
||||||
|
$key = (string) $row[$paramsIndexKey];
|
||||||
|
}
|
||||||
|
if($paramsColumnKey === null) {
|
||||||
|
$valueSet = true;
|
||||||
|
$value = $row;
|
||||||
|
} elseif(is_array($row) && array_key_exists($paramsColumnKey, $row)) {
|
||||||
|
$valueSet = true;
|
||||||
|
$value = $row[$paramsColumnKey];
|
||||||
|
}
|
||||||
|
if($valueSet) {
|
||||||
|
if($keySet) {
|
||||||
|
$resultArray[$key] = $value;
|
||||||
|
} else {
|
||||||
|
$resultArray[] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $resultArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function underscoreToCamelCase($str, $capitalise_first_char = false) {
|
||||||
|
if($capitalise_first_char) {
|
||||||
|
$str[0] = strtoupper($str[0]);
|
||||||
|
}
|
||||||
|
$func = create_function('$c', 'return strtoupper($c[1]);');
|
||||||
|
return preg_replace_callback('/_([a-z])/', $func, $str);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function camelCaseToUnderscore($str) {
|
||||||
|
$str[0] = strtolower($str[0]);
|
||||||
|
$func = create_function('$c', 'return "_" . strtolower($c[1]);');
|
||||||
|
return preg_replace_callback('/([A-Z])/', $func, $str);
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
namespace MailPoet\Util;
|
namespace MailPoet\Util;
|
||||||
|
|
||||||
|
require_once(ABSPATH . 'wp-includes/pluggable.php');
|
||||||
|
|
||||||
class Security {
|
class Security {
|
||||||
static function generateToken() {
|
static function generateToken() {
|
||||||
return wp_create_nonce('mailpoet_token');
|
return wp_create_nonce('mailpoet_token');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static function generateRandomString($length) {
|
||||||
|
return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, $length);
|
||||||
|
}
|
||||||
}
|
}
|
@ -72,9 +72,9 @@ class XLSXWriter
|
|||||||
}
|
}
|
||||||
|
|
||||||
@unlink($filename);//if the zip already exists, overwrite it
|
@unlink($filename);//if the zip already exists, overwrite it
|
||||||
$zip = new ZipArchive();
|
$zip = new \ZipArchive();
|
||||||
if (empty($this->sheets)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", no worksheets defined."); return; }
|
if (empty($this->sheets)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", no worksheets defined."); return; }
|
||||||
if (!$zip->open($filename, ZipArchive::CREATE)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", unable to create zip."); return; }
|
if (!$zip->open($filename, \ZipArchive::CREATE)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", unable to create zip."); return; }
|
||||||
|
|
||||||
$zip->addEmptyDir("docProps/");
|
$zip->addEmptyDir("docProps/");
|
||||||
$zip->addFromString("docProps/app.xml" , self::buildAppXML() );
|
$zip->addFromString("docProps/app.xml" , self::buildAppXML() );
|
||||||
|
@ -5,7 +5,7 @@ if (!defined('ABSPATH')) exit;
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Plugin Name: MailPoet
|
* Plugin Name: MailPoet
|
||||||
* Version: 0.0.3
|
* Version: 0.0.5
|
||||||
* Plugin URI: http://www.mailpoet.com
|
* Plugin URI: http://www.mailpoet.com
|
||||||
* Description: MailPoet Newsletters.
|
* Description: MailPoet Newsletters.
|
||||||
* Author: MailPoet
|
* Author: MailPoet
|
||||||
@ -18,12 +18,12 @@ if (!defined('ABSPATH')) exit;
|
|||||||
*
|
*
|
||||||
* @package WordPress
|
* @package WordPress
|
||||||
* @author MailPoet
|
* @author MailPoet
|
||||||
* @since 0.0.3
|
* @since 0.0.5
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require 'vendor/autoload.php';
|
require 'vendor/autoload.php';
|
||||||
|
|
||||||
define('MAILPOET_VERSION', '0.0.3');
|
define('MAILPOET_VERSION', '0.0.5');
|
||||||
|
|
||||||
$initializer = new Initializer(array(
|
$initializer = new Initializer(array(
|
||||||
'file' => __FILE__,
|
'file' => __FILE__,
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
$console = new \Codeception\Lib\Console\Output([]);
|
$wp_load_file = getenv('WP_TEST_PATH').'/wp-load.php';
|
||||||
|
require_once($wp_load_file);
|
||||||
|
|
||||||
$console->writeln('Loading WP core...');
|
$console = new \Codeception\Lib\Console\Output([]);
|
||||||
require_once(getenv('WP_TEST_PATH') . '/wp-load.php');
|
$console->writeln('Loading WP core... ('.$wp_load_file.')');
|
||||||
|
|
||||||
$console->writeln('Cleaning up database...');
|
$console->writeln('Cleaning up database...');
|
||||||
$models = array(
|
$models = array(
|
||||||
|
@ -83,6 +83,7 @@ testHelpers.loadTemplate('blocks/container/oneColumnLayoutWidget.hbs', window, {
|
|||||||
testHelpers.loadTemplate('blocks/container/twoColumnLayoutWidget.hbs', window, {id: 'newsletter_editor_template_container_two_column_widget'});
|
testHelpers.loadTemplate('blocks/container/twoColumnLayoutWidget.hbs', window, {id: 'newsletter_editor_template_container_two_column_widget'});
|
||||||
testHelpers.loadTemplate('blocks/container/threeColumnLayoutWidget.hbs', window, {id: 'newsletter_editor_template_container_three_column_widget'});
|
testHelpers.loadTemplate('blocks/container/threeColumnLayoutWidget.hbs', window, {id: 'newsletter_editor_template_container_three_column_widget'});
|
||||||
testHelpers.loadTemplate('blocks/container/settings.hbs', window, {id: 'newsletter_editor_template_container_settings'});
|
testHelpers.loadTemplate('blocks/container/settings.hbs', window, {id: 'newsletter_editor_template_container_settings'});
|
||||||
|
testHelpers.loadTemplate('blocks/container/columnSettings.hbs', window, {id: 'newsletter_editor_template_container_column_settings'});
|
||||||
|
|
||||||
testHelpers.loadTemplate('blocks/divider/block.hbs', window, {id: 'newsletter_editor_template_divider_block'});
|
testHelpers.loadTemplate('blocks/divider/block.hbs', window, {id: 'newsletter_editor_template_divider_block'});
|
||||||
testHelpers.loadTemplate('blocks/divider/widget.hbs', window, {id: 'newsletter_editor_template_divider_widget'});
|
testHelpers.loadTemplate('blocks/divider/widget.hbs', window, {id: 'newsletter_editor_template_divider_widget'});
|
||||||
@ -154,6 +155,7 @@ global.templates = {
|
|||||||
twoColumnLayoutInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_container_two_column_widget').html()),
|
twoColumnLayoutInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_container_two_column_widget').html()),
|
||||||
threeColumnLayoutInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_container_three_column_widget').html()),
|
threeColumnLayoutInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_container_three_column_widget').html()),
|
||||||
containerBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_container_settings').html()),
|
containerBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_container_settings').html()),
|
||||||
|
containerBlockColumnSettings: Handlebars.compile(jQuery('#newsletter_editor_template_container_column_settings').html()),
|
||||||
|
|
||||||
buttonBlock: Handlebars.compile(jQuery('#newsletter_editor_template_button_block').html()),
|
buttonBlock: Handlebars.compile(jQuery('#newsletter_editor_template_button_block').html()),
|
||||||
buttonInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_button_widget').html()),
|
buttonInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_button_widget').html()),
|
||||||
|
@ -182,7 +182,7 @@ define([
|
|||||||
|
|
||||||
it('updates the model when padding changes', function () {
|
it('updates the model when padding changes', function () {
|
||||||
var newValue = 'false';
|
var newValue = 'false';
|
||||||
view.$('.mailpoet_field_image_padded').val(newValue).change();
|
view.$('.mailpoet_field_image_padded').prop('checked', false).change();
|
||||||
expect(model.get('padded')).to.equal(false);
|
expect(model.get('padded')).to.equal(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
define([
|
define([
|
||||||
'newsletter_editor/App',
|
'newsletter_editor/App',
|
||||||
'newsletter_editor/components/wordpress',
|
'newsletter_editor/components/wordpress',
|
||||||
'newsletter_editor/blocks/posts',
|
'newsletter_editor/blocks/posts'
|
||||||
], function(EditorApplication, WordpressComponent, PostsBlock) {
|
], function(EditorApplication, WordpressComponent, PostsBlock) {
|
||||||
|
|
||||||
describe('Posts', function () {
|
describe('Posts', function () {
|
||||||
@ -373,12 +373,7 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when "title as list" is selected', function() {
|
describe('when "title as list" is selected', function() {
|
||||||
var model, view;
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
model = new (PostsBlock.PostsBlockModel)();
|
|
||||||
model.request = sinon.stub().returns({$el: {}});
|
|
||||||
view = new (PostsBlock.PostsBlockSettingsView)({model: model});
|
|
||||||
view.render();
|
|
||||||
view.$('.mailpoet_posts_display_type').val('titleOnly').change();
|
view.$('.mailpoet_posts_display_type').val('titleOnly').change();
|
||||||
view.$('.mailpoet_posts_title_format').val('ul').change();
|
view.$('.mailpoet_posts_title_format').val('ul').change();
|
||||||
});
|
});
|
||||||
|
@ -1,48 +1,62 @@
|
|||||||
<?php
|
<?php
|
||||||
use \MailPoet\Config\Env;
|
use MailPoet\Config\Env;
|
||||||
|
|
||||||
class EnvCest {
|
class EnvCest {
|
||||||
function _before() {
|
function _before() {
|
||||||
Env::init('file', '1.0.0');
|
Env::init('file', '1.0.0');
|
||||||
}
|
}
|
||||||
|
|
||||||
function itCanReturnThePluginPrefix() {
|
function itCanReturnPluginPrefix() {
|
||||||
expect(Env::$plugin_prefix)->equals('mailpoet_');
|
expect(Env::$plugin_prefix)->equals('mailpoet_');
|
||||||
}
|
}
|
||||||
|
|
||||||
function itCanReturnTheDbPrefix() {
|
function itCanReturnDbPrefix() {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
$db_prefix = $wpdb->prefix . 'mailpoet_';
|
$db_prefix = $wpdb->prefix . 'mailpoet_';
|
||||||
expect(Env::$db_prefix)->equals($db_prefix);
|
expect(Env::$db_prefix)->equals($db_prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
function itCanReturnTheDbHost() {
|
function itCanReturnDbHost() {
|
||||||
expect(Env::$db_host)->equals(DB_HOST);
|
if(preg_match('/(?=:\d+$)/', DB_HOST)) {
|
||||||
|
expect(Env::$db_host)->equals(explode(':', DB_HOST)[0]);
|
||||||
|
} else expect(Env::$db_host)->equals(DB_HOST);
|
||||||
}
|
}
|
||||||
|
|
||||||
function itCanReturnTheDbName() {
|
function itCanReturnDbPort() {
|
||||||
|
if(preg_match('/(?=:\d+$)/', DB_HOST)) {
|
||||||
|
expect(Env::$db_port)->equals(explode(':', DB_HOST)[1]);
|
||||||
|
} else expect(Env::$db_port)->equals(3306);
|
||||||
|
}
|
||||||
|
|
||||||
|
function itCanReturnSocket() {
|
||||||
|
if(!preg_match('/(?=:\d+$)/', DB_HOST)
|
||||||
|
&& preg_match('/:/', DB_HOST)
|
||||||
|
) {
|
||||||
|
expect(Env::$db_socket)->true();
|
||||||
|
} else expect(Env::$db_socket)->false();
|
||||||
|
}
|
||||||
|
|
||||||
|
function itCanReturnDbName() {
|
||||||
expect(Env::$db_name)->equals(DB_NAME);
|
expect(Env::$db_name)->equals(DB_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
function itCanReturnTheDbUser() {
|
function itCanReturnDbUser() {
|
||||||
expect(Env::$db_username)->equals(DB_USER);
|
expect(Env::$db_username)->equals(DB_USER);
|
||||||
}
|
}
|
||||||
|
|
||||||
function itCanReturnTheDbPassword() {
|
function itCanReturnDbPassword() {
|
||||||
expect(Env::$db_password)->equals(DB_PASSWORD);
|
expect(Env::$db_password)->equals(DB_PASSWORD);
|
||||||
}
|
}
|
||||||
|
|
||||||
function itCanReturnTheDbCharset() {
|
function itCanReturnDbCharset() {
|
||||||
global $wpdb;
|
global $wpdb;
|
||||||
$charset = $wpdb->get_charset_collate();
|
$charset = $wpdb->get_charset_collate();
|
||||||
expect(Env::$db_charset)->equals($charset);
|
expect(Env::$db_charset)->equals($charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function itCanGenerateTheDbSourceName() {
|
function itCanGenerateDbSourceName() {
|
||||||
$source_name = 'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME;
|
$source_name = ((!ENV::$db_socket) ? 'mysql:host=' : 'mysql:unix_socket=') .
|
||||||
|
ENV::$db_host . ';port=' . ENV::$db_port . ';dbname=' . DB_NAME;
|
||||||
expect(Env::$db_source_name)->equals($source_name);
|
expect(Env::$db_source_name)->equals($source_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _after() {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ class CustomFieldCest {
|
|||||||
$this->data = array(
|
$this->data = array(
|
||||||
'name' => 'DOB',
|
'name' => 'DOB',
|
||||||
'type' => 'date',
|
'type' => 'date',
|
||||||
|
'params' => 'none'
|
||||||
);
|
);
|
||||||
$this->customField = CustomField::create();
|
$this->customField = CustomField::create();
|
||||||
$this->customField->hydrate($this->data);
|
$this->customField->hydrate($this->data);
|
||||||
@ -32,20 +33,19 @@ class CustomFieldCest {
|
|||||||
expect($this->saved)->equals(true);
|
expect($this->saved)->equals(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
function itHasName() {
|
function itCanHaveName() {
|
||||||
$customField = CustomField::where('name', $this->data['name'])
|
expect($this->customField->name)->equals($this->data['name']);
|
||||||
->findOne();
|
|
||||||
expect($customField->name)->equals($this->data['name']);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function itHasType() {
|
function itCanHaveType() {
|
||||||
$customField = CustomField::where('name', $this->data['name'])
|
expect($this->customField->type)->equals($this->data['type']);
|
||||||
->findOne();
|
}
|
||||||
expect($customField->type)->equals($this->data['type']);
|
|
||||||
|
function itCanHaveParams() {
|
||||||
|
expect(unserialize($this->customField->params))->equals($this->data['params']);
|
||||||
}
|
}
|
||||||
|
|
||||||
function itHasToBeValid() {
|
function itHasToBeValid() {
|
||||||
expect($this->saved)->equals(true);
|
|
||||||
$empty_model = CustomField::create();
|
$empty_model = CustomField::create();
|
||||||
expect($empty_model->save())->notEquals(true);
|
expect($empty_model->save())->notEquals(true);
|
||||||
$validations = $empty_model->getValidationErrors();
|
$validations = $empty_model->getValidationErrors();
|
||||||
|
@ -1,18 +1,40 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use MailPoet\Models\SubscriberSegment;
|
|
||||||
use MailPoet\Models\Subscriber;
|
|
||||||
use MailPoet\Models\Segment;
|
|
||||||
use MailPoet\Models\Newsletter;
|
use MailPoet\Models\Newsletter;
|
||||||
use MailPoet\Models\NewsletterSegment;
|
use MailPoet\Models\NewsletterSegment;
|
||||||
|
use MailPoet\Models\Segment;
|
||||||
|
use MailPoet\Models\Subscriber;
|
||||||
|
use MailPoet\Models\SubscriberSegment;
|
||||||
|
|
||||||
class SegmentCest {
|
class SegmentCest {
|
||||||
function _before() {
|
function _before() {
|
||||||
$this->before_time = time();
|
$this->before_time = time();
|
||||||
$this->data = array(
|
$this->data = array(
|
||||||
'name' => 'some name',
|
'name' => 'some name',
|
||||||
|
'description' => 'some description'
|
||||||
|
);
|
||||||
|
$this->subscribersData = array(
|
||||||
|
array(
|
||||||
|
'first_name' => 'John',
|
||||||
|
'last_name' => 'Mailer',
|
||||||
|
'status' => 0,
|
||||||
|
'email' => 'john@mailpoet.com'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'first_name' => 'Mike',
|
||||||
|
'last_name' => 'Smith',
|
||||||
|
'status' => 1,
|
||||||
|
'email' => 'mike@maipoet.com'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$this->newslettersData = array(
|
||||||
|
array(
|
||||||
|
'subject' => 'My first newsletter'
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'subject' => 'My second newsletter'
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->segment = Segment::create();
|
$this->segment = Segment::create();
|
||||||
$this->segment->hydrate($this->data);
|
$this->segment->hydrate($this->data);
|
||||||
$this->saved = $this->segment->save();
|
$this->saved = $this->segment->save();
|
||||||
@ -22,6 +44,20 @@ class SegmentCest {
|
|||||||
expect($this->saved)->equals(true);
|
expect($this->saved)->equals(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function itCanHaveName() {
|
||||||
|
expect($this->segment->name)->equals($this->data['name']);
|
||||||
|
}
|
||||||
|
|
||||||
|
function nameMustBeUnique() {
|
||||||
|
$segment = Segment::create();
|
||||||
|
$segment->hydrate($this->data);
|
||||||
|
expect($segment->save())->contains('Duplicate');
|
||||||
|
}
|
||||||
|
|
||||||
|
function itCanHaveDescription() {
|
||||||
|
expect($this->segment->description)->equals($this->data['description']);
|
||||||
|
}
|
||||||
|
|
||||||
function itHasToBeValid() {
|
function itHasToBeValid() {
|
||||||
expect($this->saved)->equals(true);
|
expect($this->saved)->equals(true);
|
||||||
$empty_model = Segment::create();
|
$empty_model = Segment::create();
|
||||||
@ -65,37 +101,27 @@ class SegmentCest {
|
|||||||
|
|
||||||
function itCanCreateOrUpdate() {
|
function itCanCreateOrUpdate() {
|
||||||
$is_created = Segment::createOrUpdate(array(
|
$is_created = Segment::createOrUpdate(array(
|
||||||
'name' => 'new list'
|
'name' => 'new list'
|
||||||
));
|
));
|
||||||
expect($is_created)->notEquals(false);
|
expect($is_created)->notEquals(false);
|
||||||
expect($is_created->getValidationErrors())->isEmpty();
|
expect($is_created->getValidationErrors())->isEmpty();
|
||||||
|
|
||||||
$segment = Segment::where('name', 'new list')->findOne();
|
$segment = Segment::where('name', 'new list')
|
||||||
|
->findOne();
|
||||||
expect($segment->name)->equals('new list');
|
expect($segment->name)->equals('new list');
|
||||||
|
|
||||||
$is_updated = Segment::createOrUpdate(array(
|
$is_updated = Segment::createOrUpdate(
|
||||||
'id' => $segment->id,
|
array(
|
||||||
'name' => 'updated list'
|
'id' => $segment->id,
|
||||||
));
|
'name' => 'updated list'
|
||||||
$segment = Segment::where('name', 'updated list')->findOne();
|
));
|
||||||
|
$segment = Segment::where('name', 'updated list')
|
||||||
|
->findOne();
|
||||||
expect($segment->name)->equals('updated list');
|
expect($segment->name)->equals('updated list');
|
||||||
}
|
}
|
||||||
|
|
||||||
function itCanHaveManySubscribers() {
|
function itCanHaveManySubscribers() {
|
||||||
$subscribersData = array(
|
foreach ($this->subscribersData as $subscriberData) {
|
||||||
array(
|
|
||||||
'first_name' => 'John',
|
|
||||||
'last_name' => 'Mailer',
|
|
||||||
'email' => 'john@mailpoet.com'
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'first_name' => 'Mike',
|
|
||||||
'last_name' => 'Smith',
|
|
||||||
'email' => 'mike@maipoet.com'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach($subscribersData as $subscriberData) {
|
|
||||||
$subscriber = Subscriber::create();
|
$subscriber = Subscriber::create();
|
||||||
$subscriber->hydrate($subscriberData);
|
$subscriber->hydrate($subscriberData);
|
||||||
$subscriber->save();
|
$subscriber->save();
|
||||||
@ -104,24 +130,15 @@ class SegmentCest {
|
|||||||
$association->segment_id = $this->segment->id;
|
$association->segment_id = $this->segment->id;
|
||||||
$association->save();
|
$association->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
$segment = Segment::findOne($this->segment->id);
|
$segment = Segment::findOne($this->segment->id);
|
||||||
$subscribers = $segment->subscribers()->findArray();
|
$subscribers = $segment->subscribers()
|
||||||
|
->findArray();
|
||||||
|
|
||||||
expect(count($subscribers))->equals(2);
|
expect(count($subscribers))->equals(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function itCanHaveManyNewsletters() {
|
function itCanHaveManyNewsletters() {
|
||||||
$newslettersData = array(
|
foreach ($this->newslettersData as $newsletterData) {
|
||||||
array(
|
|
||||||
'subject' => 'My first newsletter'
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
'subject' => 'My second newsletter'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach($newslettersData as $newsletterData) {
|
|
||||||
$newsletter = Newsletter::create();
|
$newsletter = Newsletter::create();
|
||||||
$newsletter->hydrate($newsletterData);
|
$newsletter->hydrate($newsletterData);
|
||||||
$newsletter->save();
|
$newsletter->save();
|
||||||
@ -130,13 +147,46 @@ class SegmentCest {
|
|||||||
$association->segment_id = $this->segment->id;
|
$association->segment_id = $this->segment->id;
|
||||||
$association->save();
|
$association->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
$segment = Segment::findOne($this->segment->id);
|
$segment = Segment::findOne($this->segment->id);
|
||||||
$newsletters = $segment->newsletters()->findArray();
|
$newsletters = $segment->newsletters()
|
||||||
|
->findArray();
|
||||||
|
|
||||||
expect(count($newsletters))->equals(2);
|
expect(count($newsletters))->equals(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function itCanGetSegmentsForImport() {
|
||||||
|
foreach ($this->subscribersData as $subscriberData) {
|
||||||
|
$subscriber = Subscriber::create();
|
||||||
|
$subscriber->hydrate($subscriberData);
|
||||||
|
$subscriber->save();
|
||||||
|
$association = SubscriberSegment::create();
|
||||||
|
$association->subscriber_id = $subscriber->id;
|
||||||
|
$association->segment_id = $this->segment->id;
|
||||||
|
$association->save();
|
||||||
|
}
|
||||||
|
$segment = Segment::getSegmentsForImport();
|
||||||
|
expect($segment[0]['subscribers'])->equals(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function itCanGetSegmentsForExport() {
|
||||||
|
foreach ($this->subscribersData as $index => $subscriberData) {
|
||||||
|
$subscriber = Subscriber::create();
|
||||||
|
$subscriber->hydrate($subscriberData);
|
||||||
|
$subscriber->save();
|
||||||
|
if(!$index) {
|
||||||
|
$association = SubscriberSegment::create();
|
||||||
|
$association->subscriber_id = $subscriber->id;
|
||||||
|
$association->segment_id = $this->segment->id;
|
||||||
|
$association->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$segments = Segment::getSegmentsForExport();
|
||||||
|
expect(count($segments))->equals(2);
|
||||||
|
expect($segments[0]['name'])->equals('Not In List');
|
||||||
|
$segments = Segment::getSegmentsForExport($withConfirmedSubscribers = true);
|
||||||
|
expect(count($segments))->equals(1);
|
||||||
|
}
|
||||||
|
|
||||||
function _after() {
|
function _after() {
|
||||||
ORM::forTable(Segment::$_table)
|
ORM::forTable(Segment::$_table)
|
||||||
->deleteMany();
|
->deleteMany();
|
||||||
@ -144,7 +194,9 @@ class SegmentCest {
|
|||||||
->deleteMany();
|
->deleteMany();
|
||||||
ORM::forTable(SubscriberSegment::$_table)
|
ORM::forTable(SubscriberSegment::$_table)
|
||||||
->deleteMany();
|
->deleteMany();
|
||||||
|
ORM::forTable(Newsletter::$_table)
|
||||||
|
->deleteMany();
|
||||||
|
ORM::forTable(NewsletterSegment::$_table)
|
||||||
|
->deleteMany();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user