Compare commits
221 Commits
Author | SHA1 | Date | |
---|---|---|---|
b577d33414 | |||
70de0a01bf | |||
3b7f77d9af | |||
21847ca875 | |||
6153316047 | |||
32f8f07602 | |||
70a04d9bf6 | |||
bb1cc997cc | |||
24f96d9d7d | |||
46c7332da2 | |||
2f42f643ab | |||
63c87f3746 | |||
d4d575cda4 | |||
2cf03ec0a3 | |||
72ad98a77f | |||
b5094f568c | |||
7d224274fc | |||
f3b9f7be92 | |||
6e74f82ace | |||
831eb6af44 | |||
1e8e5aecee | |||
c0f98c9ba6 | |||
746c19d6ed | |||
894a9e8c90 | |||
8fea917337 | |||
c60425afb2 | |||
0776e9ad73 | |||
91981cc324 | |||
1906fafacb | |||
c11d95b402 | |||
b4c8fe6f45 | |||
d0e770e0fc | |||
3d6d1a4282 | |||
dc3b47db00 | |||
900d6694e2 | |||
e8074a61a5 | |||
64501a914a | |||
de70e855ad | |||
03e3b5a94b | |||
3331bed31c | |||
8c5a33a0fe | |||
01eb6c7a98 | |||
f502e0b677 | |||
a6b64a1c5d | |||
5019131b21 | |||
da483fb88f | |||
788bed4622 | |||
3fbe5423d0 | |||
8357295be2 | |||
8072b162d4 | |||
3f2f0ec1a9 | |||
5a5a777b7d | |||
6cac7f3652 | |||
6a9313107c | |||
72c9d301b7 | |||
ad925de801 | |||
1da28b7299 | |||
e837ad7014 | |||
daec56191f | |||
7bd25660df | |||
3b9821fbe1 | |||
cabe2d61b7 | |||
a6d802e2fa | |||
1732c4f634 | |||
bb77134224 | |||
f1cb64b240 | |||
3689545589 | |||
9b67c56281 | |||
dc38b19667 | |||
a574733217 | |||
b90aaa629e | |||
8de186c0e6 | |||
e3719967f9 | |||
138a631ed7 | |||
07b7636a72 | |||
a63ce3cdac | |||
f5c7bb87af | |||
2c8d925971 | |||
0c5beb2511 | |||
9c0316a87d | |||
46c1b682fa | |||
7954346a3f | |||
d87ff67f50 | |||
6642bb3bfa | |||
2cb32e7a78 | |||
fcea9adbd9 | |||
bbdd0dbb6e | |||
1b2cf7bd16 | |||
b7cfa549d5 | |||
ffc1d0a61c | |||
d1b160def7 | |||
493fd01754 | |||
9ced4b1757 | |||
17010e5ba9 | |||
42ad7584d4 | |||
dbc0f9b238 | |||
e62e9a5892 | |||
bc25fa61b4 | |||
2590967183 | |||
86eafd3c17 | |||
90a6f160c2 | |||
c774aec6a2 | |||
8f2fd1d76e | |||
4df11163a1 | |||
82a736ffbb | |||
87052986e8 | |||
0c73c0fadc | |||
5c7e11076d | |||
d1df94c759 | |||
53cc39c6f5 | |||
4955c72ee1 | |||
16661af8c3 | |||
bc80f69e41 | |||
0192934e65 | |||
2793e74858 | |||
5996696cc9 | |||
7f6cf5bbf3 | |||
c0ef2254cd | |||
0dbe04c3f8 | |||
ef1805d9b5 | |||
514f539e83 | |||
50f072705e | |||
f8f7bc3d3d | |||
f1bf2bb097 | |||
bbe2f69a7f | |||
c844488b0b | |||
112fe0cd6e | |||
c9e6dce785 | |||
d1c09c015a | |||
8d61866b77 | |||
0831c748b1 | |||
b2a0bc3860 | |||
1f99345e7b | |||
89782bc94b | |||
155ff09280 | |||
132e6d3342 | |||
f02699158f | |||
be3462925d | |||
3d82230d10 | |||
c20c46fd86 | |||
6e63c72aa5 | |||
e059eec5ea | |||
a2d38c9076 | |||
8d507b2ee0 | |||
5e2979c283 | |||
84ec0de3cd | |||
ee07780833 | |||
ed104156a9 | |||
90f2e9ff9d | |||
98fb7aa65e | |||
a51eb59cf8 | |||
270023b89c | |||
7d77e075e9 | |||
9bbe36b3cb | |||
7a904ed093 | |||
e3c065b353 | |||
4ca2872e0e | |||
ce338f7fe2 | |||
fa28b0a955 | |||
a298650187 | |||
95772ef68a | |||
e1c94db516 | |||
7be1a11d1e | |||
c04b95c09a | |||
cdfeb8d512 | |||
3ef8067968 | |||
9fb04bc3c0 | |||
25727ea0ba | |||
4c8ac369b7 | |||
268dabdc9f | |||
c0ba218949 | |||
ee85139089 | |||
b4f83fe1bd | |||
bd83001ef5 | |||
fb3a9f485f | |||
fd44776ae9 | |||
6dbe338b01 | |||
1f06a7dd0b | |||
37c218f782 | |||
25016f2a8d | |||
792744a270 | |||
c5fbfca132 | |||
c2fde308cb | |||
533d9b0d38 | |||
2035b802e3 | |||
a5e66ec6a0 | |||
beb939df9e | |||
44e342c692 | |||
3a417d460f | |||
1950d6661f | |||
da6e154642 | |||
acebf669a7 | |||
4a2bbe3f88 | |||
9b011c0281 | |||
bf58d8a22d | |||
72d1eb79a6 | |||
bb4893c0a0 | |||
9929cf0aee | |||
83967e84ba | |||
9621cb3ca9 | |||
a413f666fe | |||
d1e2c6c074 | |||
3b6a9f7a6e | |||
d2e5fb89c2 | |||
97d1e95037 | |||
48fbce22e7 | |||
916fe76795 | |||
e10310fb5c | |||
367afcf814 | |||
67fa9e0993 | |||
d7553a5f27 | |||
8c847825fa | |||
d21d9b99b0 | |||
8461c13532 | |||
3b7ffe9ba7 | |||
1724fa22c1 | |||
01089d7a72 | |||
717ebfd20c | |||
98fb838169 | |||
62a164e4c6 | |||
9ab8b1f0c5 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,7 +1,7 @@
|
||||
.DS_Store
|
||||
TODO
|
||||
composer.phar
|
||||
vendor
|
||||
/vendor
|
||||
tests/_output/*
|
||||
tests/acceptance.suite.yml
|
||||
tests/_support/_generated/*
|
||||
|
11
README.md
11
README.md
@ -8,7 +8,6 @@ MailPoet done the right way.
|
||||
```
|
||||
php
|
||||
nodejs
|
||||
phantomjs
|
||||
wordpress
|
||||
```
|
||||
|
||||
@ -47,16 +46,6 @@ $ ./do compile:all
|
||||
$ ./do test:unit
|
||||
```
|
||||
|
||||
- Acceptance tests:
|
||||
```sh
|
||||
$ ./do test:acceptance
|
||||
```
|
||||
|
||||
- Run all tests:
|
||||
```sh
|
||||
$ ./do test:all
|
||||
```
|
||||
|
||||
- Debug tests:
|
||||
```sh
|
||||
$ ./do test:debug
|
||||
|
@ -47,6 +47,15 @@ class RoboFile extends \Robo\Tasks {
|
||||
->run();
|
||||
}
|
||||
|
||||
function watchCss() {
|
||||
$css_files = $this->rsearch('assets/css/src/', array('styl'));
|
||||
$this->taskWatch()
|
||||
->monitor($css_files, function() {
|
||||
$this->compileCss();
|
||||
})
|
||||
->run();
|
||||
}
|
||||
|
||||
function watchJs() {
|
||||
$this->_exec('./node_modules/webpack/bin/webpack.js --watch');
|
||||
}
|
||||
|
@ -5,12 +5,15 @@
|
||||
@require 'common'
|
||||
@require 'modal'
|
||||
@require 'notice'
|
||||
@require 'parsley'
|
||||
|
||||
@require 'form_editor'
|
||||
@require 'listing'
|
||||
@require 'box'
|
||||
@require 'breadcrumb'
|
||||
@require 'form'
|
||||
|
||||
@require 'settings'
|
||||
@require 'form'
|
||||
@require 'parsley'
|
||||
@require 'form_validation'
|
||||
|
||||
@require 'settings'
|
||||
@require 'progress_bar'
|
||||
|
File diff suppressed because one or more lines are too long
@ -19,6 +19,8 @@ a:focus
|
||||
|
||||
// select 2
|
||||
.select2-container
|
||||
width: 25em !important
|
||||
|
||||
// textareas
|
||||
textarea.regular-text
|
||||
width: 25em !important
|
||||
@ -26,3 +28,25 @@ textarea.regular-text
|
||||
@media screen and (max-width: 782px)
|
||||
.select2-container
|
||||
width: 100% !important
|
||||
|
||||
// progress bars
|
||||
progress-border-radius = 5px
|
||||
progress-background = #efefef
|
||||
progress-foreground = #69b1e9
|
||||
|
||||
progress
|
||||
background-color: progress-background;
|
||||
height: 2em
|
||||
border: 0
|
||||
width: 100%
|
||||
|
||||
progress::-webkit-progress-bar
|
||||
background-color: progress-background;
|
||||
|
||||
progress::-webkit-progress-value
|
||||
background-color: progress-foreground
|
||||
border-radius: progress-border-radius
|
||||
|
||||
progress::-moz-progress-bar
|
||||
background-color: progress-foreground
|
||||
border-radius: progress-border-radius
|
||||
|
@ -4,6 +4,9 @@
|
||||
icons = '../img/form_editor_icons.png'
|
||||
handle_icon = '../img/handle.png'
|
||||
|
||||
#mailpoet_form_name
|
||||
font-size: 23px
|
||||
|
||||
#mailpoet_form_history
|
||||
display: none
|
||||
|
||||
@ -99,6 +102,7 @@ handle_icon = '../img/handle.png'
|
||||
/* MailPoet Form wrapper */
|
||||
#mailpoet_form_wrapper
|
||||
position: relative
|
||||
margin: 20px 0 0 0
|
||||
|
||||
/* MailPoet Form container */
|
||||
#mailpoet_form_container
|
||||
@ -121,6 +125,7 @@ handle_icon = '../img/handle.png'
|
||||
float: none
|
||||
|
||||
#mailpoet_form_toolbar
|
||||
z-index: 999
|
||||
position: absolute
|
||||
width: 400px
|
||||
|
||||
|
6
assets/css/src/form_validation.styl
Normal file
6
assets/css/src/form_validation.styl
Normal file
@ -0,0 +1,6 @@
|
||||
.parsley-errors-list
|
||||
margin-top: 8px
|
||||
|
||||
.parsley-required
|
||||
.parsley-custom-error-message
|
||||
color: #b94a48
|
@ -90,7 +90,7 @@ body.mailpoet_modal_opened
|
||||
padding: 0
|
||||
margin: 0
|
||||
width: 100%
|
||||
transition: margin 0.3s ease-out
|
||||
transition: margin 350ms ease-out
|
||||
|
||||
.mailpoet_panel_wrapper
|
||||
background-color: #f1f1f1
|
||||
@ -200,4 +200,4 @@ body.mailpoet_modal_opened
|
||||
0%
|
||||
50%
|
||||
background-color: #064E6D
|
||||
100%
|
||||
100%
|
||||
|
@ -7,7 +7,6 @@ $tool-active-secondary-color = #ffffff
|
||||
|
||||
$tool-width = 20px
|
||||
$master-column-tool-width = 24px
|
||||
$layer-selector-width = 30px
|
||||
|
||||
.mailpoet_tools
|
||||
position: absolute
|
||||
@ -33,10 +32,35 @@ $layer-selector-width = 30px
|
||||
width: $master-column-tool-width
|
||||
height: $master-column-tool-width
|
||||
|
||||
|
||||
.mailpoet_delete_block_activate
|
||||
max-width: 100%
|
||||
max-height: $master-column-tool-width
|
||||
opacity: 1
|
||||
display: block
|
||||
|
||||
.mailpoet_delete_block_confirm,
|
||||
.mailpoet_delete_block_cancel
|
||||
max-width: 100%
|
||||
max-height: 0
|
||||
opacity: 0
|
||||
overflow: hidden
|
||||
display: block
|
||||
|
||||
.mailpoet_delete_block_activated
|
||||
width: auto
|
||||
height: auto
|
||||
|
||||
.mailpoet_delete_block_activate
|
||||
overflow: hidden
|
||||
max-height: 0
|
||||
opacity: 0
|
||||
|
||||
.mailpoet_delete_block_confirm,
|
||||
.mailpoet_delete_block_cancel
|
||||
max-height: $master-column-tool-width*2
|
||||
opacity: 1
|
||||
|
||||
.mailpoet_tool
|
||||
display: inline-block
|
||||
width: $tool-width
|
||||
@ -76,28 +100,45 @@ $layer-selector-width = 30px
|
||||
display: inline-block
|
||||
padding: 2px
|
||||
vertical-align: top
|
||||
animation-background-color()
|
||||
|
||||
.mailpoet_tool
|
||||
padding: 0
|
||||
|
||||
.mailpoet_delete_block_activate
|
||||
max-width: $tool-width
|
||||
display: inline-block
|
||||
opacity: 1
|
||||
animation-fade-in-and-scale-horizontally()
|
||||
|
||||
.mailpoet_delete_block_confirm,
|
||||
.mailpoet_delete_block_cancel
|
||||
display: none
|
||||
max-width: 0
|
||||
opacity: 0
|
||||
overflow: hidden
|
||||
display: inline-block
|
||||
animation-fade-in-and-scale-horizontally()
|
||||
|
||||
.mailpoet_delete_block_activated
|
||||
height: auto
|
||||
width: auto
|
||||
border-radius(3px)
|
||||
background-color: $warning-background-color
|
||||
padding: 3px 5px
|
||||
line-height: 1.2em
|
||||
height: auto
|
||||
|
||||
.mailpoet_delete_block_activate
|
||||
display: none
|
||||
overflow: hidden
|
||||
max-width: 0
|
||||
opacity: 0
|
||||
|
||||
.mailpoet_delete_block_confirm,
|
||||
.mailpoet_delete_block_cancel
|
||||
display: inline-block
|
||||
max-width: 100%
|
||||
opacity: 1
|
||||
|
||||
.mailpoet_delete_block_cancel
|
||||
margin-left: 3px
|
||||
|
||||
.mailpoet_delete_block_confirm
|
||||
color: $warning-text-color
|
||||
|
@ -52,6 +52,7 @@ $draggable-widget-z-index = 2
|
||||
padding: 0
|
||||
margin: 0
|
||||
z-index: $draggable-widget-z-index
|
||||
animation-fade-in()
|
||||
|
||||
.mailpoet_widget_icon
|
||||
padding: 0
|
||||
|
@ -48,7 +48,7 @@
|
||||
.mailpoet_save_as_template_container,
|
||||
.mailpoet_export_template_container
|
||||
border-radius(3px)
|
||||
float: left
|
||||
display: inline-block
|
||||
clear: both
|
||||
|
||||
margin-top: 5px
|
||||
|
@ -26,13 +26,9 @@ $widget-icon-width = 30px
|
||||
border-right: 0
|
||||
|
||||
&.closed .mailpoet_region_content
|
||||
max-height: 0px
|
||||
overflow: hidden
|
||||
margin-top: 0
|
||||
display: none
|
||||
|
||||
.mailpoet_region_content
|
||||
max-height: 2000px
|
||||
transition: max-height 0.2s ease
|
||||
padding: 0 20px
|
||||
margin-top: 12px
|
||||
|
||||
|
@ -18,3 +18,6 @@
|
||||
|
||||
& > .mailpoet_block
|
||||
width: 100%
|
||||
|
||||
.mailpoet_automated_latest_content_display_options
|
||||
animation-slide-open-downwards()
|
||||
|
@ -79,3 +79,4 @@ $three-column-width = ($newsletter-width / 3) - (2 * $column-margin)
|
||||
box-shadow(inset 1px 2px 1px $primary-inactive-color)
|
||||
color: #656565
|
||||
border-radius(3px)
|
||||
animation-background-color()
|
||||
|
@ -15,7 +15,11 @@
|
||||
.mailpoet_posts_categories_and_tags
|
||||
width: 100%
|
||||
|
||||
.mailpoet_settings_posts_show_display_options
|
||||
.mailpoet_settings_posts_display_options
|
||||
.mailpoet_settings_posts_selection
|
||||
animation-slide-open-downwards()
|
||||
|
||||
.mailpoet_settings_posts_show_display_options,
|
||||
.mailpoet_settings_posts_show_post_selection
|
||||
display: block
|
||||
margin-top: 10px
|
||||
|
@ -129,3 +129,7 @@ body
|
||||
|
||||
#mailpoet_modal_close
|
||||
display: none
|
||||
|
||||
.wrap > .mailpoet_notice,
|
||||
.update-nag
|
||||
margin-left: 2px + 15px !important
|
||||
|
31
assets/css/src/newsletter_editor/mixins/transitions.styl
Normal file
31
assets/css/src/newsletter_editor/mixins/transitions.styl
Normal file
@ -0,0 +1,31 @@
|
||||
animation-slide-open-downwards()
|
||||
transition: all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
|
||||
max-height: 2000px
|
||||
opacity: 1
|
||||
|
||||
&.mailpoet_closed
|
||||
max-height: 0
|
||||
opacity: 0
|
||||
overflow-y: hidden
|
||||
|
||||
animation-background-color()
|
||||
transition: background 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
|
||||
|
||||
animation-fade-in()
|
||||
animation-name: fadeIn
|
||||
animation-duration: 300ms
|
||||
animation-fill-mode: forwards
|
||||
animation-timing-function: ease-in
|
||||
|
||||
animation-fade-in-and-scale-horizontally()
|
||||
transition: all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0.3
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
@require 'mixins/border-radius'
|
||||
@require 'mixins/box-shadow'
|
||||
@require 'mixins/filter-shadow'
|
||||
@require 'mixins/transitions'
|
||||
|
||||
@require 'variables'
|
||||
@require 'common'
|
||||
|
29
assets/css/src/progress_bar.styl
Normal file
29
assets/css/src/progress_bar.styl
Normal file
@ -0,0 +1,29 @@
|
||||
.mailpoet_progress
|
||||
background-color: #efefef
|
||||
height: 25px
|
||||
padding: 0
|
||||
width: 100%
|
||||
margin: 0
|
||||
border-radius: 5px
|
||||
position: relative
|
||||
|
||||
.mailpoet_progress_label
|
||||
position: absolute
|
||||
width: 100%
|
||||
text-align: center
|
||||
display: inline-block
|
||||
margin: 2px 0 0 0
|
||||
|
||||
.mailpoet_progress_bar
|
||||
position: absolute
|
||||
display: inline-block
|
||||
height: 100%
|
||||
border-radius: 3px
|
||||
box-shadow: 0 1px 0 rgba(255, 255, 255, .5) inset
|
||||
background-color: #34c2e3
|
||||
background-image: linear-gradient(top, #34c2e3, darken(#34c2e3, 20%))
|
||||
|
||||
.mailpoet_progress_complete
|
||||
.mailpoet_progress_bar
|
||||
background-color: #fecf23
|
||||
background-image: linear-gradient(top, #fecf23, #fd9215)
|
@ -1,3 +1,4 @@
|
||||
@import 'nib'
|
||||
|
||||
@require 'parsley'
|
||||
@require 'form_validation'
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
BIN
assets/img/post_notifications_template/ALC-widget-icon.png
Executable file
BIN
assets/img/post_notifications_template/ALC-widget-icon.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 3.9 KiB |
BIN
assets/img/post_notifications_template/settings-icon-highlight.png
Executable file
BIN
assets/img/post_notifications_template/settings-icon-highlight.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
BIN
assets/img/welcome_template/logo-header.gif
Executable file
BIN
assets/img/welcome_template/logo-header.gif
Executable file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
106
assets/js/src/cron.jsx
Normal file
106
assets/js/src/cron.jsx
Normal file
@ -0,0 +1,106 @@
|
||||
define(
|
||||
[
|
||||
'react',
|
||||
'react-dom',
|
||||
'mailpoet'
|
||||
],
|
||||
function(
|
||||
React,
|
||||
ReactDOM,
|
||||
MailPoet
|
||||
) {
|
||||
var CronControl = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
status: 'loading'
|
||||
};
|
||||
},
|
||||
getCronData: function() {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'cron',
|
||||
action: 'getStatus'
|
||||
})
|
||||
.done(function(response) {
|
||||
jQuery('.button-primary')
|
||||
.removeClass('disabled');
|
||||
if(response.status !== undefined) {
|
||||
this.setState(response);
|
||||
} else {
|
||||
this.replaceState();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
componentDidMount: function() {
|
||||
if(this.isMounted()) {
|
||||
this.getCronData();
|
||||
setInterval(this.getCronData, 5000);
|
||||
}
|
||||
},
|
||||
controlCron: function(action) {
|
||||
if(jQuery('.button-primary').hasClass('disabled')) {
|
||||
return;
|
||||
}
|
||||
jQuery('.button-primary')
|
||||
.addClass('disabled');
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'cron',
|
||||
action: action,
|
||||
})
|
||||
.done(function(response) {
|
||||
if(!response.result) {
|
||||
MailPoet.Notice.error(MailPoetI18n.daemonControlError);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
render: function() {
|
||||
if(this.state.status === 'loading') {
|
||||
return(<div>Loading daemon status...</div>);
|
||||
}
|
||||
switch(this.state.status) {
|
||||
case 'started':
|
||||
return(
|
||||
<div>
|
||||
Cron daemon is running.
|
||||
<br/>
|
||||
<br/>
|
||||
It was started
|
||||
<strong> {this.state.timeSinceStart} </strong> and last executed
|
||||
<strong> {this.state.timeSinceUpdate} </strong> for a total of
|
||||
<strong> {this.state.counter} </strong> times (once every 30 seconds, unless it was interrupted and restarted).
|
||||
<br />
|
||||
<br />
|
||||
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'stop')}>Stop</a>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'starting':
|
||||
case 'stopping':
|
||||
return(
|
||||
<div>
|
||||
Daemon is {this.state.status}
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'stopped':
|
||||
return(
|
||||
<div>
|
||||
Daemon is {this.state.status}
|
||||
<br />
|
||||
<br />
|
||||
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'start')}>Start</a>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const container = document.getElementById('cron_container');
|
||||
|
||||
if(container) {
|
||||
ReactDOM.render(
|
||||
<CronControl />,
|
||||
container
|
||||
);
|
||||
}
|
||||
});
|
@ -1,31 +1,31 @@
|
||||
define([
|
||||
'react',
|
||||
'react-checkbox-group'
|
||||
'react'
|
||||
],
|
||||
function(
|
||||
React,
|
||||
CheckboxGroup
|
||||
React
|
||||
) {
|
||||
var FormFieldCheckbox = React.createClass({
|
||||
const FormFieldCheckbox = React.createClass({
|
||||
onValueChange: function(e) {
|
||||
e.target.value = this.refs.checkbox.checked ? '1' : '';
|
||||
return this.props.onValueChange(e);
|
||||
},
|
||||
render: function() {
|
||||
var selected_values = this.props.item[this.props.field.name] || '';
|
||||
if(
|
||||
selected_values !== undefined
|
||||
&& selected_values.constructor !== Array
|
||||
) {
|
||||
selected_values = selected_values.split(';').map(function(value) {
|
||||
return value.trim();
|
||||
});
|
||||
}
|
||||
var count = Object.keys(this.props.field.values).length;
|
||||
const isChecked = !!(this.props.item[this.props.field.name]);
|
||||
|
||||
var options = Object.keys(this.props.field.values).map(
|
||||
const options = Object.keys(this.props.field.values).map(
|
||||
function(value, index) {
|
||||
return (
|
||||
<p key={ 'checkbox-' + index }>
|
||||
<label>
|
||||
<input type="checkbox" value={ value } />
|
||||
{ this.props.field.values[value] }
|
||||
<input
|
||||
ref="checkbox"
|
||||
type="checkbox"
|
||||
value="1"
|
||||
checked={ isChecked }
|
||||
onChange={ this.onValueChange }
|
||||
name={ this.props.field.name }
|
||||
/>
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
@ -33,30 +33,10 @@ function(
|
||||
);
|
||||
|
||||
return (
|
||||
<CheckboxGroup
|
||||
name={ this.props.field.name }
|
||||
value={ selected_values }
|
||||
ref={ this.props.field.name }
|
||||
onChange={ this.handleValueChange }>
|
||||
<div>
|
||||
{ options }
|
||||
</CheckboxGroup>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
handleValueChange: function() {
|
||||
var field = this.props.field.name;
|
||||
var group = this.refs[field];
|
||||
var selected_values = [];
|
||||
|
||||
if(group !== undefined) {
|
||||
selected_values = group.getCheckedValues();
|
||||
}
|
||||
|
||||
return this.props.onValueChange({
|
||||
target: {
|
||||
name: field,
|
||||
value: selected_values.join(';')
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
196
assets/js/src/form/fields/date.jsx
Normal file
196
assets/js/src/form/fields/date.jsx
Normal file
@ -0,0 +1,196 @@
|
||||
define([
|
||||
'react',
|
||||
'moment',
|
||||
], function(
|
||||
React,
|
||||
Moment
|
||||
) {
|
||||
class FormFieldDateYear extends React.Component {
|
||||
render() {
|
||||
const yearsRange = 100;
|
||||
const years = [];
|
||||
const currentYear = Moment().year();
|
||||
for (let i = currentYear; i >= currentYear - yearsRange; i--) {
|
||||
years.push((
|
||||
<option
|
||||
key={ i }
|
||||
value={ i }
|
||||
>{ i }</option>
|
||||
));
|
||||
}
|
||||
return (
|
||||
<select
|
||||
name={ this.props.name + '[year]' }
|
||||
value={ this.props.year }
|
||||
onChange={ this.props.onValueChange }
|
||||
>
|
||||
{ years }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FormFieldDateMonth extends React.Component {
|
||||
render() {
|
||||
const months = [];
|
||||
for (let i = 1; i <= 12; i++) {
|
||||
months.push((
|
||||
<option
|
||||
key={ i }
|
||||
value={ i }
|
||||
>{ this.props.monthNames[i - 1] }</option>
|
||||
));
|
||||
}
|
||||
return (
|
||||
<select
|
||||
name={ this.props.name + '[month]' }
|
||||
value={ this.props.month }
|
||||
onChange={ this.props.onValueChange }
|
||||
>
|
||||
{ months }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FormFieldDateDay extends React.Component {
|
||||
render() {
|
||||
const days = [];
|
||||
for (let i = 1; i <= 31; i++) {
|
||||
days.push((
|
||||
<option
|
||||
key={ i }
|
||||
value={ i }
|
||||
>{ i }</option>
|
||||
));
|
||||
}
|
||||
|
||||
return (
|
||||
<select
|
||||
name={ this.props.name + '[day]' }
|
||||
value={ this.props.day }
|
||||
onChange={ this.props.onValueChange }
|
||||
>
|
||||
{ days }
|
||||
</select>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class FormFieldDate extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
year: Moment().year(),
|
||||
month: 1,
|
||||
day: 1
|
||||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
}
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
if (
|
||||
(this.props.item !== undefined && prevProps.item !== undefined)
|
||||
&& (this.props.item.id !== prevProps.item.id)
|
||||
) {
|
||||
this.extractTimeStamp();
|
||||
}
|
||||
}
|
||||
extractTimeStamp() {
|
||||
const timeStamp = parseInt(this.props.item[this.props.field.name], 10);
|
||||
|
||||
this.setState({
|
||||
year: Moment.unix(timeStamp).year(),
|
||||
// Moment returns the month as [0..11]
|
||||
// We increment it to match PHP's mktime() which expects [1..12]
|
||||
month: Moment.unix(timeStamp).month() + 1,
|
||||
day: Moment.unix(timeStamp).date()
|
||||
});
|
||||
}
|
||||
updateTimeStamp(field) {
|
||||
let newTimeStamp = Moment(
|
||||
`${this.state.month}/${this.state.day}/${this.state.year}`,
|
||||
'M/D/YYYY'
|
||||
).valueOf();
|
||||
if (!isNaN(newTimeStamp) && parseInt(newTimeStamp, 10) > 0) {
|
||||
// convert milliseconds to seconds
|
||||
newTimeStamp /= 1000;
|
||||
return this.props.onValueChange({
|
||||
target: {
|
||||
name: field,
|
||||
value: newTimeStamp
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
onValueChange(e) {
|
||||
// extract property from name
|
||||
const matches = e.target.name.match(/(.*?)\[(.*?)\]/);
|
||||
let field = null;
|
||||
let property = null;
|
||||
|
||||
if (matches !== null && matches.length === 3) {
|
||||
field = matches[1];
|
||||
property = matches[2];
|
||||
|
||||
let value = parseInt(e.target.value, 10);
|
||||
|
||||
this.setState({
|
||||
[`${property}`]: value
|
||||
}, () => {
|
||||
this.updateTimeStamp(field);
|
||||
});
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const monthNames = window.mailpoet_month_names || [];
|
||||
|
||||
const dateType = this.props.field.params.date_type;
|
||||
|
||||
const dateSelects = dateType.split('_');
|
||||
|
||||
const fields = dateSelects.map(type => {
|
||||
switch(type) {
|
||||
case 'year':
|
||||
return (<FormFieldDateYear
|
||||
onValueChange={ this.onValueChange.bind(this) }
|
||||
ref={ 'year' }
|
||||
key={ 'year' }
|
||||
name={ this.props.field.name }
|
||||
year={ this.state.year }
|
||||
/>);
|
||||
break;
|
||||
|
||||
case 'month':
|
||||
return (<FormFieldDateMonth
|
||||
onValueChange={ this.onValueChange.bind(this) }
|
||||
ref={ 'month' }
|
||||
key={ 'month' }
|
||||
name={ this.props.field.name }
|
||||
month={ this.state.month }
|
||||
monthNames={ monthNames }
|
||||
/>);
|
||||
break;
|
||||
|
||||
case 'day':
|
||||
return (<FormFieldDateDay
|
||||
onValueChange={ this.onValueChange.bind(this) }
|
||||
ref={ 'day' }
|
||||
key={ 'day' }
|
||||
name={ this.props.field.name }
|
||||
day={ this.state.day }
|
||||
/>);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{fields}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return FormFieldDate;
|
||||
});
|
@ -5,7 +5,8 @@ define([
|
||||
'form/fields/select.jsx',
|
||||
'form/fields/radio.jsx',
|
||||
'form/fields/checkbox.jsx',
|
||||
'form/fields/selection.jsx'
|
||||
'form/fields/selection.jsx',
|
||||
'form/fields/date.jsx',
|
||||
],
|
||||
function(
|
||||
React,
|
||||
@ -14,7 +15,8 @@ function(
|
||||
FormFieldSelect,
|
||||
FormFieldRadio,
|
||||
FormFieldCheckbox,
|
||||
FormFieldSelection
|
||||
FormFieldSelection,
|
||||
FormFieldDate
|
||||
) {
|
||||
var FormField = React.createClass({
|
||||
renderField: function(data, inline = false) {
|
||||
@ -55,6 +57,10 @@ function(
|
||||
case 'selection':
|
||||
field = (<FormFieldSelection {...data} />);
|
||||
break;
|
||||
|
||||
case 'date':
|
||||
field = (<FormFieldDate {...data} />);
|
||||
break;
|
||||
}
|
||||
|
||||
if(inline === true) {
|
||||
@ -66,10 +72,10 @@ function(
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<p key={ 'field-' + (data.index || 0) }>
|
||||
<div key={ 'field-' + (data.index || 0) }>
|
||||
{ field }
|
||||
{ description }
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -7,7 +7,6 @@ function(
|
||||
var FormFieldRadio = React.createClass({
|
||||
render: function() {
|
||||
var selected_value = this.props.item[this.props.field.name];
|
||||
var count = Object.keys(this.props.field.values).length;
|
||||
|
||||
var options = Object.keys(this.props.field.values).map(
|
||||
function(value, index) {
|
||||
@ -20,7 +19,7 @@ function(
|
||||
value={ value }
|
||||
onChange={ this.props.onValueChange }
|
||||
name={ this.props.field.name } />
|
||||
{ this.props.field.values[value] }
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
|
@ -23,7 +23,9 @@ function(
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
onChange={ this.props.onValueChange }>
|
||||
onChange={ this.props.onValueChange }
|
||||
{...this.props.field.validation}
|
||||
>
|
||||
{options}
|
||||
</select>
|
||||
);
|
||||
|
@ -25,13 +25,10 @@ function(
|
||||
(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]
|
||||
);
|
||||
jQuery('#'+this.refs.select.id)
|
||||
.val(this.props.item[this.props.field.name])
|
||||
.trigger('change');
|
||||
}
|
||||
|
||||
this.setupSelect2();
|
||||
},
|
||||
setupSelect2: function() {
|
||||
if(
|
||||
@ -53,7 +50,19 @@ function(
|
||||
}
|
||||
});
|
||||
|
||||
var hasRemoved = false;
|
||||
select2.on('select2:unselecting', function(e) {
|
||||
hasRemoved = true;
|
||||
});
|
||||
select2.on('select2:opening', function(e) {
|
||||
if(hasRemoved === true) {
|
||||
hasRemoved = false;
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
select2.on('change', this.handleChange);
|
||||
|
||||
select2.select2(
|
||||
'val',
|
||||
this.props.item[this.props.field.name]
|
||||
@ -77,7 +86,7 @@ function(
|
||||
handleChange: function(e) {
|
||||
if(this.props.onValueChange !== undefined) {
|
||||
if(this.props.field.multiple) {
|
||||
value = jQuery('#'+this.refs.select.id).select2('val');
|
||||
value = jQuery('#'+this.refs.select.id).val();
|
||||
} else {
|
||||
value = e.target.value;
|
||||
}
|
||||
@ -88,7 +97,6 @@ function(
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
},
|
||||
render: function() {
|
||||
var options = this.state.items.map(function(item, index) {
|
||||
@ -112,10 +120,10 @@ function(
|
||||
<select
|
||||
id={ this.props.field.id || this.props.field.name }
|
||||
ref="select"
|
||||
placeholder={ this.props.field.placeholder }
|
||||
data-placeholder={ this.props.field.placeholder }
|
||||
multiple={ this.props.field.multiple }
|
||||
onChange={ this.handleChange }
|
||||
defaultValue={ default_value }
|
||||
{...this.props.field.validation}
|
||||
>{ options }</select>
|
||||
);
|
||||
}
|
||||
|
@ -6,6 +6,8 @@ function(
|
||||
) {
|
||||
var FormFieldText = React.createClass({
|
||||
render: function() {
|
||||
var value = this.props.item[this.props.field.name];
|
||||
if(!value) { value = null; }
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
@ -17,10 +19,12 @@ function(
|
||||
}
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
value={ value }
|
||||
placeholder={ this.props.field.placeholder }
|
||||
defaultValue={ this.props.field.defaultValue }
|
||||
onChange={ this.props.onValueChange } />
|
||||
onChange={ this.props.onValueChange }
|
||||
{...this.props.field.validation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -15,7 +15,9 @@ function(
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
placeholder={ this.props.field.placeholder }
|
||||
defaultValue={ this.props.field.defaultValue }
|
||||
onChange={ this.props.onValueChange } />
|
||||
onChange={ this.props.onValueChange }
|
||||
{...this.props.field.validation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -68,12 +68,25 @@ define(
|
||||
handleSubmit: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// handle validation
|
||||
if(this.props.isValid !== undefined) {
|
||||
if(this.props.isValid() === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
// only get values from displayed fields
|
||||
item = {};
|
||||
var item = {};
|
||||
this.props.fields.map(function(field) {
|
||||
item[field.name] = this.state.item[field.name];
|
||||
if(field['fields'] !== undefined) {
|
||||
field.fields.map(function(subfield) {
|
||||
item[subfield.name] = this.state.item[subfield.name];
|
||||
}.bind(this));
|
||||
} else {
|
||||
item[field.name] = this.state.item[field.name];
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
// set id if specified
|
||||
|
@ -231,7 +231,7 @@ var WysijaHistory = {
|
||||
|
||||
/* MailPoet Form */
|
||||
var WysijaForm = {
|
||||
version: '0.6',
|
||||
version: '0.7',
|
||||
options: {
|
||||
container: 'mailpoet_form_container',
|
||||
editor: 'mailpoet_form_editor',
|
||||
@ -317,6 +317,7 @@ var WysijaForm = {
|
||||
save: function() {
|
||||
var position = 1,
|
||||
data = {
|
||||
'name': $F('mailpoet_form_name'),
|
||||
'settings': $('mailpoet_form_settings').serialize(true),
|
||||
'body': [],
|
||||
'styles': (MailPoet.CodeEditor !== undefined) ? MailPoet.CodeEditor.getValue() : null
|
||||
@ -616,6 +617,28 @@ var WysijaForm = {
|
||||
// this is a url, so do not encode the protocol
|
||||
return encodeURI(str).replace(/[!'()*]/g, escape);
|
||||
}
|
||||
},
|
||||
updateBlock: function(field) {
|
||||
var hasUpdated = false;
|
||||
WysijaForm.getBlocks().each(function(b) {
|
||||
if(b.block.getData().id === field.id) {
|
||||
hasUpdated = true;
|
||||
b.block.redraw(field);
|
||||
}
|
||||
});
|
||||
|
||||
return hasUpdated;
|
||||
},
|
||||
removeBlock: function(field, callback) {
|
||||
var hasRemoved = false;
|
||||
WysijaForm.getBlocks().each(function(b) {
|
||||
if(b.block.getData().id === field.id) {
|
||||
hasRemoved = true;
|
||||
b.block.removeBlock(callback);
|
||||
}
|
||||
});
|
||||
|
||||
return hasRemoved;
|
||||
}
|
||||
};
|
||||
|
||||
@ -824,10 +847,6 @@ WysijaForm.Block = Class.create({
|
||||
Effect.Fade(this.element.identify(), {
|
||||
duration: 0.2,
|
||||
afterFinish: function(effect) {
|
||||
if(effect.element.next('.mailpoet_form_block') !== undefined && callback !== false) {
|
||||
// show controls of next block to allow mass delete
|
||||
WysijaForm.get(effect.element.next('.mailpoet_form_block')).block.showControls();
|
||||
}
|
||||
// remove placeholder
|
||||
if(effect.element.previous('.block_placeholder') !== undefined) {
|
||||
effect.element.previous('.block_placeholder').remove();
|
||||
|
@ -397,6 +397,12 @@ define(
|
||||
if(this.isMounted()) {
|
||||
const params = this.props.params || {}
|
||||
this.initWithParams(params)
|
||||
|
||||
if(this.props.auto_refresh) {
|
||||
jQuery(document).on('heartbeat-tick.mailpoet', function(e, data) {
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
},
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
|
@ -47,7 +47,6 @@ define([
|
||||
autoScroll: true,
|
||||
|
||||
onstart: function(event) {
|
||||
console.log('Drag start', event, this);
|
||||
|
||||
if (that.options.cloneOriginal === true) {
|
||||
// Use substitution instead of a clone
|
||||
|
@ -11,10 +11,10 @@ define([
|
||||
'newsletter_editor/blocks/base',
|
||||
'newsletter_editor/blocks/button',
|
||||
'newsletter_editor/blocks/divider',
|
||||
'newsletter_editor/components/wordpress',
|
||||
'newsletter_editor/components/communication',
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function(App, BaseBlock, ButtonBlock, DividerBlock, WordpressComponent, _, jQuery) {
|
||||
], function(App, BaseBlock, ButtonBlock, DividerBlock, CommunicationComponent, _, jQuery) {
|
||||
|
||||
"use strict";
|
||||
|
||||
@ -69,9 +69,9 @@ define([
|
||||
},
|
||||
fetchPosts: function() {
|
||||
var that = this;
|
||||
WordpressComponent.getTransformedPosts(this.toJSON()).done(function(content) {
|
||||
console.log('ALC fetched', arguments);
|
||||
CommunicationComponent.getTransformedPosts(this.toJSON()).done(function(content) {
|
||||
that.get('_container').get('blocks').reset(content, {parse: true});
|
||||
that.trigger('postsChanged');
|
||||
}).fail(function(error) {
|
||||
console.log('ALC fetchPosts error', arguments);
|
||||
});
|
||||
@ -100,6 +100,11 @@ define([
|
||||
toolsRegion: '.mailpoet_tools',
|
||||
postsRegion: '.mailpoet_automated_latest_content_block_posts',
|
||||
},
|
||||
modelEvents: _.extend(
|
||||
_.omit(base.BlockView.prototype.modelEvents, 'change'),
|
||||
{
|
||||
'postsChanged': 'render',
|
||||
}),
|
||||
events: _.extend(base.BlockView.prototype.events, {
|
||||
'click .mailpoet_automated_latest_content_block_overlay': 'showSettings',
|
||||
}),
|
||||
@ -161,7 +166,7 @@ define([
|
||||
var that = this;
|
||||
|
||||
// Dynamically update available post types
|
||||
WordpressComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
|
||||
CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
|
||||
|
||||
this.$('.mailpoet_automated_latest_content_categories_and_tags').select2({
|
||||
multiple: true,
|
||||
@ -174,10 +179,10 @@ define([
|
||||
},
|
||||
transport: function(options, success, failure) {
|
||||
var taxonomies,
|
||||
promise = WordpressComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
|
||||
promise = CommunicationComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
|
||||
taxonomies = tax;
|
||||
// Fetch available terms based on the list of taxonomies already fetched
|
||||
var promise = WordpressComponent.getTerms({
|
||||
var promise = CommunicationComponent.getTerms({
|
||||
search: options.data.term,
|
||||
taxonomies: _.keys(taxonomies)
|
||||
}).then(function(terms) {
|
||||
@ -226,11 +231,11 @@ define([
|
||||
toggleDisplayOptions: function(event) {
|
||||
var el = this.$('.mailpoet_automated_latest_content_display_options'),
|
||||
showControl = this.$('.mailpoet_automated_latest_content_show_display_options');
|
||||
if (el.hasClass('mailpoet_hidden')) {
|
||||
el.removeClass('mailpoet_hidden');
|
||||
if (el.hasClass('mailpoet_closed')) {
|
||||
el.removeClass('mailpoet_closed');
|
||||
showControl.addClass('mailpoet_hidden');
|
||||
} else {
|
||||
el.addClass('mailpoet_hidden');
|
||||
el.addClass('mailpoet_closed');
|
||||
showControl.removeClass('mailpoet_hidden');
|
||||
}
|
||||
},
|
||||
|
@ -48,6 +48,7 @@ define([
|
||||
},
|
||||
modelEvents: {
|
||||
'change': 'render',
|
||||
'delete': 'deleteBlock',
|
||||
},
|
||||
events: {
|
||||
"mouseenter": "showTools",
|
||||
@ -88,7 +89,9 @@ define([
|
||||
this.$el.addClass('mailpoet_editor_view_' + this.cid);
|
||||
},
|
||||
initialize: function() {
|
||||
this.on('showSettings', this.showSettings);
|
||||
this.on('showSettings', this.showSettings, this);
|
||||
this.on('dom:refresh', this.showBlock, this);
|
||||
this._isFirstRender = true;
|
||||
},
|
||||
showTools: function(_event) {
|
||||
if (!this.showingToolsDisabled) {
|
||||
@ -114,12 +117,49 @@ define([
|
||||
* Defines drop behavior of BlockView instance
|
||||
*/
|
||||
getDropFunc: function() {
|
||||
var that = this;
|
||||
return function() {
|
||||
var newModel = that.model.clone();
|
||||
//that.model.destroy();
|
||||
return newModel;
|
||||
};
|
||||
return this.model.clone();
|
||||
}.bind(this);
|
||||
},
|
||||
showBlock: function() {
|
||||
if (this._isFirstRender) {
|
||||
this.transitionIn();
|
||||
this._isFirstRender = false;
|
||||
}
|
||||
},
|
||||
deleteBlock: function() {
|
||||
this.transitionOut().then(function() {
|
||||
this.model.destroy();
|
||||
}.bind(this));
|
||||
},
|
||||
transitionIn: function() {
|
||||
return this._transition('slideDown', 'fadeIn', 'easeOut');
|
||||
},
|
||||
transitionOut: function() {
|
||||
return this._transition('slideUp', 'fadeOut', 'easeIn');
|
||||
},
|
||||
_transition: function(slideDirection, fadeDirection, easing) {
|
||||
var promise = jQuery.Deferred();
|
||||
|
||||
this.$el.velocity(
|
||||
slideDirection,
|
||||
{
|
||||
duration: 250,
|
||||
easing: easing,
|
||||
complete: function() {
|
||||
promise.resolve();
|
||||
}.bind(this),
|
||||
}
|
||||
).velocity(
|
||||
fadeDirection,
|
||||
{
|
||||
duration: 250,
|
||||
easing: easing,
|
||||
queue: false, // Do not enqueue, trigger animation in parallel
|
||||
}
|
||||
);
|
||||
|
||||
return promise;
|
||||
},
|
||||
});
|
||||
|
||||
@ -168,24 +208,22 @@ define([
|
||||
},
|
||||
deleteBlock: function(event) {
|
||||
event.preventDefault();
|
||||
this.model.destroy();
|
||||
this.model.trigger('delete');
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Module.BlockSettingsView = Marionette.LayoutView.extend({
|
||||
className: 'mailpoet_editor_settings',
|
||||
initialize: function() {
|
||||
var that = this;
|
||||
|
||||
MailPoet.Modal.panel({
|
||||
element: this.$el,
|
||||
template: '',
|
||||
position: 'right',
|
||||
width: App.getConfig().get('sidepanelWidth'),
|
||||
onCancel: function() {
|
||||
that.destroy();
|
||||
},
|
||||
this.destroy();
|
||||
}.bind(this),
|
||||
});
|
||||
},
|
||||
close: function(event) {
|
||||
|
@ -42,16 +42,12 @@ define([
|
||||
Module.ButtonBlockView = base.BlockView.extend({
|
||||
className: "mailpoet_block mailpoet_button_block mailpoet_droppable_block",
|
||||
getTemplate: function() { return templates.buttonBlock; },
|
||||
modelEvents: {
|
||||
'change': 'render',
|
||||
},
|
||||
onDragSubstituteBy: function() { return Module.ButtonWidgetView; },
|
||||
initialize: function() {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
var that = this;
|
||||
|
||||
// Listen for attempts to change all dividers in one go
|
||||
this._replaceButtonStylesHandler = function(data) { that.model.set(data); };
|
||||
this._replaceButtonStylesHandler = function(data) { this.model.set(data); }.bind(this);
|
||||
App.getChannel().on('replaceAllButtonStyles', this._replaceButtonStylesHandler);
|
||||
},
|
||||
onRender: function() {
|
||||
|
@ -75,7 +75,8 @@ define([
|
||||
getEmptyView: function() { return Module.ContainerBlockEmptyView; },
|
||||
emptyViewOptions: function() { return { renderOptions: this.renderOptions }; },
|
||||
modelEvents: {
|
||||
'change': 'render'
|
||||
'change': 'render',
|
||||
'delete': 'deleteBlock',
|
||||
},
|
||||
events: {
|
||||
"mouseenter": "showTools",
|
||||
@ -136,6 +137,8 @@ define([
|
||||
},
|
||||
initialize: function(options) {
|
||||
this.renderOptions = _.defaults(options.renderOptions || {}, {});
|
||||
this.on('dom:refresh', this.showBlock, this);
|
||||
this._isFirstRender = true;
|
||||
},
|
||||
// Determines which view type should be used for a child
|
||||
getChildView: function(model) {
|
||||
@ -229,12 +232,49 @@ define([
|
||||
_.extend(this, this._buildRegions(this.regions));
|
||||
},
|
||||
getDropFunc: function() {
|
||||
var that = this;
|
||||
return function() {
|
||||
var newModel = that.model.clone();
|
||||
that.model.destroy();
|
||||
return newModel;
|
||||
};
|
||||
return this.model.clone();
|
||||
}.bind(this);
|
||||
},
|
||||
showBlock: function() {
|
||||
if (this._isFirstRender) {
|
||||
this.transitionIn();
|
||||
this._isFirstRender = false;
|
||||
}
|
||||
},
|
||||
deleteBlock: function() {
|
||||
this.transitionOut().done(function() {
|
||||
this.model.destroy();
|
||||
}.bind(this));
|
||||
},
|
||||
transitionIn: function() {
|
||||
return this._transition('slideDown', 'fadeIn', 'easeIn');
|
||||
},
|
||||
transitionOut: function() {
|
||||
return this._transition('slideUp', 'fadeOut', 'easeOut');
|
||||
},
|
||||
_transition: function(slideDirection, fadeDirection, easing) {
|
||||
var promise = jQuery.Deferred();
|
||||
|
||||
this.$el.velocity(
|
||||
slideDirection,
|
||||
{
|
||||
duration: 250,
|
||||
easing: easing,
|
||||
complete: function() {
|
||||
promise.resolve();
|
||||
}.bind(this),
|
||||
}
|
||||
).velocity(
|
||||
fadeDirection,
|
||||
{
|
||||
duration: 250,
|
||||
easing: easing,
|
||||
queue: false, // Do not enqueue, trigger animation in parallel
|
||||
}
|
||||
);
|
||||
|
||||
return promise;
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -57,11 +57,9 @@ define([
|
||||
this.listenTo(this.model, 'change:styles.block.padding', this.changePadding);
|
||||
},
|
||||
templateHelpers: function() {
|
||||
return {
|
||||
model: this.model.toJSON(),
|
||||
viewCid: this.cid,
|
||||
return _.extend({
|
||||
totalHeight: parseInt(this.model.get('styles.block.padding'), 10)*2 + parseInt(this.model.get('styles.block.borderWidth')) + 'px',
|
||||
};
|
||||
}, base.BlockView.prototype.templateHelpers.apply(this));
|
||||
},
|
||||
onRender: function() {
|
||||
this.toolsView = new Module.DividerBlockToolsView({ model: this.model });
|
||||
|
@ -39,9 +39,9 @@ define([
|
||||
Module.FooterBlockView = base.BlockView.extend({
|
||||
className: "mailpoet_block mailpoet_footer_block mailpoet_droppable_block",
|
||||
getTemplate: function() { return templates.footerBlock; },
|
||||
modelEvents: {
|
||||
modelEvents: _.extend({
|
||||
'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render',
|
||||
},
|
||||
}, base.BlockView.prototype.modelEvents),
|
||||
onDragSubstituteBy: function() { return Module.FooterWidgetView; },
|
||||
onRender: function() {
|
||||
this.toolsView = new Module.FooterBlockToolsView({ model: this.model });
|
||||
|
@ -39,9 +39,9 @@ define([
|
||||
Module.HeaderBlockView = base.BlockView.extend({
|
||||
className: "mailpoet_block mailpoet_header_block mailpoet_droppable_block",
|
||||
getTemplate: function() { return templates.headerBlock; },
|
||||
modelEvents: {
|
||||
modelEvents: _.extend({
|
||||
'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render',
|
||||
},
|
||||
}, base.BlockView.prototype.modelEvents),
|
||||
onDragSubstituteBy: function() { return Module.HeaderWidgetView; },
|
||||
onRender: function() {
|
||||
this.toolsView = new Module.HeaderBlockToolsView({ model: this.model });
|
||||
|
@ -37,11 +37,9 @@ define([
|
||||
getTemplate: function() { return templates.imageBlock; },
|
||||
onDragSubstituteBy: function() { return Module.ImageWidgetView; },
|
||||
templateHelpers: function() {
|
||||
return {
|
||||
model: this.model.toJSON(),
|
||||
viewCid: this.cid,
|
||||
return _.extend({
|
||||
imageMissingSrc: App.getConfig().get('urls.imageMissing'),
|
||||
};
|
||||
}, base.BlockView.prototype.templateHelpers.apply(this));
|
||||
},
|
||||
onRender: function() {
|
||||
this.toolsView = new Module.ImageBlockToolsView({ model: this.model });
|
||||
|
@ -18,12 +18,12 @@ define([
|
||||
'jquery',
|
||||
'mailpoet',
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/components/wordpress',
|
||||
'newsletter_editor/components/communication',
|
||||
'newsletter_editor/blocks/base',
|
||||
'newsletter_editor/blocks/button',
|
||||
'newsletter_editor/blocks/divider',
|
||||
'select2'
|
||||
], function(Backbone, Marionette, Radio, _, jQuery, MailPoet, App, WordpressComponent, BaseBlock, ButtonBlock, DividerBlock) {
|
||||
], function(Backbone, Marionette, Radio, _, jQuery, MailPoet, App, CommunicationComponent, BaseBlock, ButtonBlock, DividerBlock) {
|
||||
|
||||
"use strict";
|
||||
|
||||
@ -96,8 +96,7 @@ define([
|
||||
},
|
||||
fetchAvailablePosts: function() {
|
||||
var that = this;
|
||||
WordpressComponent.getPosts(this.toJSON()).done(function(posts) {
|
||||
console.log('Posts fetched', arguments);
|
||||
CommunicationComponent.getPosts(this.toJSON()).done(function(posts) {
|
||||
that.get('_availablePosts').reset(posts);
|
||||
that.get('_selectedPosts').reset(); // Empty out the collection
|
||||
that.trigger('change:_availablePosts');
|
||||
@ -116,8 +115,7 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
WordpressComponent.getTransformedPosts(data).done(function(posts) {
|
||||
console.log('Transformed posts fetched', arguments);
|
||||
CommunicationComponent.getTransformedPosts(data).done(function(posts) {
|
||||
that.get('_transformedPosts').get('blocks').reset(posts, {parse: true});
|
||||
}).fail(function() {
|
||||
console.log('Posts _refreshTransformedPosts error', arguments);
|
||||
@ -133,8 +131,7 @@ define([
|
||||
|
||||
if (data.posts.length === 0) return;
|
||||
|
||||
WordpressComponent.getTransformedPosts(data).done(function(posts) {
|
||||
console.log('Available posts fetched', arguments);
|
||||
CommunicationComponent.getTransformedPosts(data).done(function(posts) {
|
||||
collection.add(posts, { at: index });
|
||||
}).fail(function() {
|
||||
console.log('Posts fetchPosts error', arguments);
|
||||
@ -145,13 +142,14 @@ define([
|
||||
Module.PostsBlockView = base.BlockView.extend({
|
||||
className: "mailpoet_block mailpoet_posts_block mailpoet_droppable_block",
|
||||
getTemplate: function() { return templates.postsBlock; },
|
||||
modelEvents: {},
|
||||
modelEvents: {}, // Forcefully disable all events
|
||||
regions: _.extend({
|
||||
postsRegion: '.mailpoet_posts_block_posts',
|
||||
}, base.BlockView.prototype.regions),
|
||||
onDragSubstituteBy: function() { return Module.PostsWidgetView; },
|
||||
initialize: function() {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this.toolsView = new Module.PostsBlockToolsView({ model: this.model });
|
||||
this.model.reply('blockView', this.notifyAboutSelf, this);
|
||||
},
|
||||
@ -222,8 +220,8 @@ define([
|
||||
},
|
||||
switchToDisplayOptions: function() {
|
||||
// Switch content view
|
||||
this.$('.mailpoet_settings_posts_selection').addClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_settings_posts_display_options').removeClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_settings_posts_selection').addClass('mailpoet_closed');
|
||||
this.$('.mailpoet_settings_posts_display_options').removeClass('mailpoet_closed');
|
||||
|
||||
// Switch controls
|
||||
this.$('.mailpoet_settings_posts_show_display_options').addClass('mailpoet_hidden');
|
||||
@ -231,8 +229,8 @@ define([
|
||||
},
|
||||
switchToPostSelection: function() {
|
||||
// Switch content view
|
||||
this.$('.mailpoet_settings_posts_display_options').addClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_settings_posts_selection').removeClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_settings_posts_display_options').addClass('mailpoet_closed');
|
||||
this.$('.mailpoet_settings_posts_selection').removeClass('mailpoet_closed');
|
||||
|
||||
// Switch controls
|
||||
this.$('.mailpoet_settings_posts_show_post_selection').addClass('mailpoet_hidden');
|
||||
@ -271,7 +269,7 @@ define([
|
||||
var that = this;
|
||||
|
||||
// Dynamically update available post types
|
||||
WordpressComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
|
||||
CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
|
||||
|
||||
this.$('.mailpoet_posts_categories_and_tags').select2({
|
||||
multiple: true,
|
||||
@ -284,10 +282,10 @@ define([
|
||||
},
|
||||
transport: function(options, success, failure) {
|
||||
var taxonomies,
|
||||
promise = WordpressComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
|
||||
promise = CommunicationComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
|
||||
taxonomies = tax;
|
||||
// Fetch available terms based on the list of taxonomies already fetched
|
||||
var promise = WordpressComponent.getTerms({
|
||||
var promise = CommunicationComponent.getTerms({
|
||||
search: options.data.term,
|
||||
taxonomies: _.keys(taxonomies)
|
||||
}).then(function(terms) {
|
||||
|
@ -103,7 +103,8 @@ define([
|
||||
getTemplate: function() { return templates.socialBlock; },
|
||||
childViewContainer: '.mailpoet_social',
|
||||
modelEvents: {
|
||||
'change': 'render'
|
||||
'change': 'render',
|
||||
'delete': 'deleteBlock',
|
||||
},
|
||||
events: {
|
||||
"mouseover": "showTools",
|
||||
@ -145,6 +146,10 @@ define([
|
||||
arguments[0].collection = arguments[0].model.get('icons');
|
||||
Marionette.CompositeView.apply(this, arguments);
|
||||
},
|
||||
initialize: function() {
|
||||
this.on('dom:refresh', this.showBlock, this);
|
||||
this._isFirstRender = true;
|
||||
},
|
||||
// Determines which view type should be used for a child
|
||||
childView: SocialIconView,
|
||||
templateHelpers: function() {
|
||||
@ -170,12 +175,9 @@ define([
|
||||
_event.stopPropagation();
|
||||
},
|
||||
getDropFunc: function() {
|
||||
var that = this;
|
||||
return function() {
|
||||
var newModel = that.model.clone();
|
||||
//that.model.destroy();
|
||||
return newModel;
|
||||
};
|
||||
return this.model.clone();
|
||||
}.bind(this);
|
||||
},
|
||||
_buildRegions: function(regions) {
|
||||
var that = this;
|
||||
@ -194,6 +196,46 @@ define([
|
||||
this.regionManager.destroy();
|
||||
_.extend(this, this._buildRegions(this.regions));
|
||||
},
|
||||
showBlock: function() {
|
||||
if (this._isFirstRender) {
|
||||
this.transitionIn();
|
||||
this._isFirstRender = false;
|
||||
}
|
||||
},
|
||||
deleteBlock: function() {
|
||||
this.transitionOut().done(function() {
|
||||
this.model.destroy();
|
||||
}.bind(this));
|
||||
},
|
||||
transitionIn: function() {
|
||||
return this._transition('slideDown', 'fadeIn', 'easeIn');
|
||||
},
|
||||
transitionOut: function() {
|
||||
return this._transition('slideUp', 'fadeOut', 'easeOut');
|
||||
},
|
||||
_transition: function(slideDirection, fadeDirection, easing) {
|
||||
var promise = jQuery.Deferred();
|
||||
|
||||
this.$el.velocity(
|
||||
slideDirection,
|
||||
{
|
||||
duration: 250,
|
||||
easing: easing,
|
||||
complete: function() {
|
||||
promise.resolve();
|
||||
}.bind(this),
|
||||
}
|
||||
).velocity(
|
||||
fadeDirection,
|
||||
{
|
||||
duration: 250,
|
||||
easing: easing,
|
||||
queue: false, // Do not enqueue, trigger animation in parallel
|
||||
}
|
||||
);
|
||||
|
||||
return promise;
|
||||
},
|
||||
});
|
||||
|
||||
Module.SocialBlockToolsView = base.BlockToolsView.extend({
|
||||
|
@ -26,6 +26,8 @@ define([
|
||||
getTemplate: function() { return templates.textBlock; },
|
||||
modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'), // Prevent rerendering on model change due to text editor redrawing
|
||||
initialize: function(options) {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this.renderOptions = _.defaults(options.renderOptions || {}, {
|
||||
disableTextEditor: false,
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ define([
|
||||
|
||||
Module._query = function(args) {
|
||||
return MailPoet.Ajax.post({
|
||||
endpoint: 'wordpress',
|
||||
endpoint: 'automatedLatestContent',
|
||||
action: args.action,
|
||||
data: args.options || {},
|
||||
});
|
||||
@ -63,16 +63,18 @@ define([
|
||||
};
|
||||
|
||||
Module.saveNewsletter = function(options) {
|
||||
return Module._query({
|
||||
return MailPoet.Ajax.post({
|
||||
endpoint: 'newsletters',
|
||||
action: 'save',
|
||||
options: options,
|
||||
data: options || {},
|
||||
});
|
||||
};
|
||||
|
||||
Module.previewNewsletter = function(options) {
|
||||
return Module._query({
|
||||
action: 'preview',
|
||||
options: options,
|
||||
return MailPoet.Ajax.post({
|
||||
endpoint: 'newsletters',
|
||||
action: 'sendPreview',
|
||||
data: options || {},
|
||||
});
|
||||
};
|
||||
|
@ -44,10 +44,10 @@ define([
|
||||
};
|
||||
|
||||
Module.getBody = function() {
|
||||
return JSON.stringify({
|
||||
return {
|
||||
content: App._contentContainer.toJSON(),
|
||||
globalStyles: App.getGlobalStyles().toJSON(),
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
Module.toJSON = function() {
|
||||
@ -73,8 +73,7 @@ define([
|
||||
});
|
||||
|
||||
App.on('start', function(options) {
|
||||
// TODO: Other newsletter information will be needed as well.
|
||||
var body = JSON.parse(options.newsletter.body);
|
||||
var body = options.newsletter.body;
|
||||
App._contentContainer = new (App.getBlockTypeModel('container'))(body.content, {parse: true});
|
||||
App._contentContainerView = new (App.getBlockTypeView('container'))({
|
||||
model: App._contentContainer,
|
||||
|
@ -1,5 +1,6 @@
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/components/communication',
|
||||
'mailpoet',
|
||||
'notice',
|
||||
'backbone',
|
||||
@ -8,7 +9,18 @@ define([
|
||||
'blob',
|
||||
'filesaver',
|
||||
'html2canvas'
|
||||
], function(App, MailPoet, Notice, Backbone, Marionette, jQuery, Blob, FileSaver, html2canvas) {
|
||||
], function(
|
||||
App,
|
||||
CommunicationComponent,
|
||||
MailPoet,
|
||||
Notice,
|
||||
Backbone,
|
||||
Marionette,
|
||||
jQuery,
|
||||
Blob,
|
||||
FileSaver,
|
||||
html2canvas
|
||||
) {
|
||||
|
||||
"use strict";
|
||||
|
||||
@ -17,26 +29,33 @@ define([
|
||||
|
||||
// Save editor contents to server
|
||||
Module.save = function() {
|
||||
App.getChannel().trigger('beforeEditorSave');
|
||||
|
||||
var json = App.toJSON();
|
||||
|
||||
// Stringify to enable transmission of primitive non-string value types
|
||||
if (!_.isUndefined(json.body)) {
|
||||
json.body = JSON.stringify(json.body);
|
||||
}
|
||||
|
||||
App.getChannel().trigger('beforeEditorSave', json);
|
||||
|
||||
// save newsletter
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'newsletters',
|
||||
action: 'save',
|
||||
data: json,
|
||||
}).done(function(response) {
|
||||
CommunicationComponent.saveNewsletter(json).done(function(response) {
|
||||
if(response.success !== undefined && response.success === true) {
|
||||
// TODO: Handle translations
|
||||
//MailPoet.Notice.success("<?php _e('Newsletter has been saved.'); ?>");
|
||||
} else if(response.error !== undefined) {
|
||||
if(response.error.length === 0) {
|
||||
// TODO: Handle translations
|
||||
MailPoet.Notice.error("<?php _e('An unknown error occurred, please check your settings.'); ?>");
|
||||
MailPoet.Notice.error(
|
||||
"An unknown error occurred, please check your settings.",
|
||||
{
|
||||
scroll: true,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$(response.error).each(function(i, error) {
|
||||
MailPoet.Notice.error(error);
|
||||
MailPoet.Notice.error(error, { scroll: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -47,20 +66,36 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
Module.saveTemplate = function(options) {
|
||||
return MailPoet.Ajax.post({
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'save',
|
||||
data: _.extend(options || {}, {
|
||||
body: App.getBody(),
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
Module.getThumbnail = function(element, options) {
|
||||
return html2canvas(element, options || {});
|
||||
};
|
||||
|
||||
Module.saveTemplate = function(options) {
|
||||
var that = this,
|
||||
promise = jQuery.Deferred();
|
||||
|
||||
promise.then(function(thumbnail) {
|
||||
var data = _.extend(options || {}, {
|
||||
thumbnail: thumbnail.toDataURL('image/jpeg'),
|
||||
body: JSON.stringify(App.getBody()),
|
||||
});
|
||||
|
||||
return MailPoet.Ajax.post({
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'save',
|
||||
data: data,
|
||||
});
|
||||
});
|
||||
|
||||
Module.getThumbnail(
|
||||
jQuery('#mailpoet_editor_content > .mailpoet_block').get(0)
|
||||
).then(function(thumbnail) {
|
||||
promise.resolve(thumbnail);
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
Module.exportTemplate = function(options) {
|
||||
var that = this;
|
||||
return Module.getThumbnail(
|
||||
@ -130,20 +165,52 @@ define([
|
||||
},
|
||||
saveAsTemplate: function() {
|
||||
var templateName = this.$('.mailpoet_save_as_template_name').val(),
|
||||
templateDescription = this.$('.mailpoet_save_as_template_description').val();
|
||||
templateDescription = this.$('.mailpoet_save_as_template_description').val(),
|
||||
that = this;
|
||||
|
||||
console.log('Saving template with ', templateName, templateDescription);
|
||||
Module.saveTemplate({
|
||||
name: templateName,
|
||||
description: templateDescription,
|
||||
}).done(function() {
|
||||
console.log('Template saved', arguments);
|
||||
}).fail(function() {
|
||||
// TODO: Handle error messages
|
||||
console.log('Template save failed', arguments);
|
||||
});
|
||||
if (templateName === '') {
|
||||
MailPoet.Notice.error(
|
||||
App.getConfig().get('translations.templateNameMissing'),
|
||||
{
|
||||
positionAfter: that.$el,
|
||||
scroll: true,
|
||||
}
|
||||
);
|
||||
} else if (templateDescription === '') {
|
||||
MailPoet.Notice.error(
|
||||
App.getConfig().get('translations.templateDescriptionMissing'),
|
||||
{
|
||||
positionAfter: that.$el,
|
||||
scroll: true,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
console.log('Saving template with ', templateName, templateDescription);
|
||||
Module.saveTemplate({
|
||||
name: templateName,
|
||||
description: templateDescription,
|
||||
}).done(function() {
|
||||
console.log('Template saved', arguments);
|
||||
MailPoet.Notice.success(
|
||||
App.getConfig().get('translations.templateSaved'),
|
||||
{
|
||||
positionAfter: that.$el,
|
||||
scroll: true,
|
||||
}
|
||||
);
|
||||
}).fail(function() {
|
||||
console.log('Template save failed', arguments);
|
||||
MailPoet.Notice.error(
|
||||
App.getConfig().get('translations.templateSaveFailed'),
|
||||
{
|
||||
positionAfter: that.$el,
|
||||
scroll: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
this.hideOptionContents();
|
||||
}
|
||||
|
||||
this.hideOptionContents();
|
||||
},
|
||||
toggleExportTemplate: function() {
|
||||
this.$('.mailpoet_export_template_container').toggleClass('mailpoet_hidden');
|
||||
@ -154,12 +221,25 @@ define([
|
||||
},
|
||||
exportTemplate: function() {
|
||||
var templateName = this.$('.mailpoet_export_template_name').val(),
|
||||
templateDescription = this.$('.mailpoet_export_template_description').val();
|
||||
templateDescription = this.$('.mailpoet_export_template_description').val(),
|
||||
that = this;
|
||||
|
||||
if (templateName === '') {
|
||||
MailPoet.Notice.error(App.getConfig().get('translations.templateNameMissing'));
|
||||
MailPoet.Notice.error(
|
||||
App.getConfig().get('translations.templateNameMissing'),
|
||||
{
|
||||
positionAfter: that.$el,
|
||||
scroll: true,
|
||||
}
|
||||
);
|
||||
} else if (templateDescription === '') {
|
||||
MailPoet.Notice.error(App.getConfig().get('translations.templateDescriptionMissing'));
|
||||
MailPoet.Notice.error(
|
||||
App.getConfig().get('translations.templateDescriptionMissing'),
|
||||
{
|
||||
positionAfter: that.$el,
|
||||
scroll: true,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
console.log('Exporting template with ', templateName, templateDescription);
|
||||
Module.exportTemplate({
|
||||
|
@ -1,12 +1,13 @@
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/components/communication',
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'backbone.supermodel',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'sticky-kit'
|
||||
], function(App, Backbone, Marionette, SuperModel, _, jQuery, StickyKit) {
|
||||
], function(App, CommunicationComponent, Backbone, Marionette, SuperModel, _, jQuery, StickyKit) {
|
||||
|
||||
"use strict";
|
||||
|
||||
@ -50,8 +51,33 @@ define([
|
||||
},
|
||||
events: {
|
||||
'click .mailpoet_sidebar_region h3, .mailpoet_sidebar_region .handlediv': function(event) {
|
||||
this.$el.find('.mailpoet_sidebar_region').addClass('closed');
|
||||
this.$el.find(event.target).parent().parent().removeClass('closed');
|
||||
var $openRegion = this.$el.find('.mailpoet_sidebar_region:not(.closed)'),
|
||||
$targetRegion = this.$el.find(event.target).closest('.mailpoet_sidebar_region');
|
||||
|
||||
if ($openRegion.get(0) === $targetRegion.get(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$openRegion.find('.mailpoet_region_content').velocity(
|
||||
'slideUp',
|
||||
{
|
||||
duration: 250,
|
||||
easing: "easeOut",
|
||||
complete: function() {
|
||||
$openRegion.addClass('closed');
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
$targetRegion.find('.mailpoet_region_content').velocity(
|
||||
'slideDown',
|
||||
{
|
||||
duration: 250,
|
||||
easing: "easeIn",
|
||||
complete: function() {
|
||||
$targetRegion.removeClass('closed');
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
initialize: function(options) {
|
||||
@ -90,7 +116,6 @@ define([
|
||||
});
|
||||
},
|
||||
onDomRefresh: function() {
|
||||
var that = this;
|
||||
this.$el.parent().stick_in_parent({
|
||||
offset_top: 32,
|
||||
});
|
||||
@ -169,10 +194,8 @@ define([
|
||||
},
|
||||
initialize: function(options) {
|
||||
this.availableStyles = options.availableStyles;
|
||||
var that = this;
|
||||
},
|
||||
onRender: function() {
|
||||
var that = this;
|
||||
this.$('.mailpoet_color').spectrum({
|
||||
clickoutFiresChange: true,
|
||||
showInput: true,
|
||||
@ -219,26 +242,29 @@ define([
|
||||
console.log('trying to send a preview');
|
||||
// get form data
|
||||
var data = {
|
||||
from_name: this.$('#mailpoet_preview_from_name').val(),
|
||||
from_email: this.$('#mailpoet_preview_from_email').val(),
|
||||
to_email: this.$('#mailpoet_preview_to_email').val(),
|
||||
newsletter: App.newsletterId,
|
||||
subscriber: this.$('#mailpoet_preview_to_email').val(),
|
||||
id: App.getNewsletter().get('id'),
|
||||
};
|
||||
|
||||
// send test email
|
||||
MailPoet.Modal.loading(true);
|
||||
|
||||
// TODO: Migrate logic to new AJAX format
|
||||
Wordpress.previewNewsletter(data).done(function(response) {
|
||||
if(response.success !== undefined && response.success === true) {
|
||||
MailPoet.Notice.success(App.getConfig().get('translations.testEmailSent'));
|
||||
} else if(response.error !== undefined) {
|
||||
if(response.error.length === 0) {
|
||||
MailPoet.Notice.error(App.getConfig().get('translations.unknownErrorOccurred'));
|
||||
} else {
|
||||
$(response.error).each(function(i, error) {
|
||||
MailPoet.Notice.error(error);
|
||||
CommunicationComponent.previewNewsletter(data).done(function(response) {
|
||||
if(response.result !== undefined && response.result === true) {
|
||||
MailPoet.Notice.success(App.getConfig().get('translations.newsletterPreviewSent'), { scroll: true });
|
||||
} else {
|
||||
if (_.isArray(response.errors)) {
|
||||
response.errors.map(function(error) {
|
||||
MailPoet.Notice.error(error, { scroll: true });
|
||||
});
|
||||
} else {
|
||||
MailPoet.Notice.error(
|
||||
App.getConfig().get('translations.newsletterPreviewFailedToSend'),
|
||||
{
|
||||
scroll: true,
|
||||
static: true,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
MailPoet.Modal.loading(false);
|
||||
|
@ -17,7 +17,7 @@ define([
|
||||
},
|
||||
h1: {
|
||||
fontColor: '#111111',
|
||||
fontFamily: 'Arial Black',
|
||||
fontFamily: 'Arial',
|
||||
fontSize: '40px'
|
||||
},
|
||||
h2: {
|
||||
@ -72,7 +72,7 @@ define([
|
||||
|
||||
App.getAvailableStyles = Module.getAvailableStyles;
|
||||
|
||||
var body = JSON.parse(options.newsletter.body);
|
||||
var body = options.newsletter.body;
|
||||
this.setGlobalStyles(body.globalStyles);
|
||||
});
|
||||
|
||||
|
@ -21,6 +21,10 @@ define(
|
||||
label: 'Subject',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
label: 'Status'
|
||||
},
|
||||
{
|
||||
name: 'segments',
|
||||
label: 'Lists'
|
||||
@ -119,8 +123,94 @@ define(
|
||||
];
|
||||
|
||||
var NewsletterList = React.createClass({
|
||||
renderItem: function(newsletter, actions) {
|
||||
pauseSending: function(item) {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'sendingQueue',
|
||||
action: 'pause',
|
||||
data: item.id
|
||||
}).done(function() {
|
||||
jQuery('#resume_'+item.id).show();
|
||||
jQuery('#pause_'+item.id).hide();
|
||||
});
|
||||
},
|
||||
resumeSending: function(item) {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'sendingQueue',
|
||||
action: 'resume',
|
||||
data: item.id
|
||||
}).done(function() {
|
||||
jQuery('#pause_'+item.id).show();
|
||||
jQuery('#resume_'+item.id).hide();
|
||||
});
|
||||
},
|
||||
renderStatus: function(item) {
|
||||
if(item.queue === null) {
|
||||
return (
|
||||
<span>Not sent yet.</span>
|
||||
);
|
||||
} else {
|
||||
var progressClasses = classNames(
|
||||
'mailpoet_progress',
|
||||
{ 'mailpoet_progress_complete': item.queue.status === 'completed'}
|
||||
);
|
||||
|
||||
// calculate percentage done
|
||||
var percentage = Math.round(
|
||||
(item.queue.count_processed * 100) / (item.queue.count_total)
|
||||
);
|
||||
|
||||
var label = false;
|
||||
|
||||
if(item.queue.status === 'completed') {
|
||||
label = (
|
||||
<span>
|
||||
Sent to {
|
||||
item.queue.count_processed - item.queue.count_failed
|
||||
} out of { item.queue.count_total }.
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
label = (
|
||||
<span>
|
||||
{ item.queue.count_processed } / { item.queue.count_total }
|
||||
|
||||
<a
|
||||
id={ 'resume_'+item.id }
|
||||
className="button"
|
||||
style={{ display: (item.queue.status === 'paused') ? 'inline-block': 'none' }}
|
||||
href="javascript:;"
|
||||
onClick={ this.resumeSending.bind(null, item) }
|
||||
>Resume</a>
|
||||
<a
|
||||
id={ 'pause_'+item.id }
|
||||
className="button mailpoet_pause"
|
||||
style={{ display: (item.queue.status === null) ? 'inline-block': 'none' }}
|
||||
href="javascript:;"
|
||||
onClick={ this.pauseSending.bind(null, item) }
|
||||
>Pause</a>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={ progressClasses }>
|
||||
<span
|
||||
className="mailpoet_progress_bar"
|
||||
style={ { width: percentage + "%"} }
|
||||
></span>
|
||||
<span className="mailpoet_progress_label">
|
||||
{ percentage + "%" }
|
||||
</span>
|
||||
</div>
|
||||
<p style={{ textAlign:'center' }}>
|
||||
{ label }
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
renderItem: function(newsletter, actions) {
|
||||
var rowClasses = classNames(
|
||||
'manage-column',
|
||||
'column-primary',
|
||||
@ -141,6 +231,9 @@ define(
|
||||
</strong>
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column" data-colname="Lists">
|
||||
{ this.renderStatus(newsletter) }
|
||||
</td>
|
||||
<td className="column" data-colname="Lists">
|
||||
{ segments }
|
||||
</td>
|
||||
@ -167,7 +260,8 @@ define(
|
||||
columns={columns}
|
||||
bulk_actions={ bulk_actions }
|
||||
item_actions={ item_actions }
|
||||
messages={ messages } />
|
||||
messages={ messages }
|
||||
auto_refresh={ true } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ define(
|
||||
Breadcrumb
|
||||
) {
|
||||
|
||||
var settings = window.mailpoet_settings || {};
|
||||
var settings = window.mailpoet_settings || {};
|
||||
|
||||
var fields = [
|
||||
{
|
||||
@ -24,19 +24,25 @@ define(
|
||||
label: 'Subject line',
|
||||
tip: "Be creative! It's the first thing your subscribers see."+
|
||||
"Tempt them to open your email.",
|
||||
type: 'text'
|
||||
type: 'text',
|
||||
validation: {
|
||||
'data-parsley-required': true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'segments',
|
||||
label: 'Lists',
|
||||
tip: "The subscriber list that will be used for this campaign.",
|
||||
label: 'Segments',
|
||||
tip: "The subscriber segment that will be used for this campaign.",
|
||||
type: 'selection',
|
||||
placeholder: "Select a list",
|
||||
placeholder: "Select a segment",
|
||||
id: "mailpoet_segments",
|
||||
endpoint: "segments",
|
||||
multiple: true,
|
||||
filter: function(segment) {
|
||||
return !!(!segment.deleted_at);
|
||||
},
|
||||
validation: {
|
||||
'data-parsley-required': true
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -45,17 +51,24 @@ define(
|
||||
tip: "Name & email of yourself or your company.",
|
||||
fields: [
|
||||
{
|
||||
name: 'from_name',
|
||||
name: 'sender_name',
|
||||
type: 'text',
|
||||
placeholder: 'John Doe',
|
||||
defaultValue: settings.from_name
|
||||
defaultValue: (settings.sender !== undefined) ? settings.sender.name : '',
|
||||
validation: {
|
||||
'data-parsley-required': true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'from_email',
|
||||
name: 'sender_address',
|
||||
type: 'text',
|
||||
placeholder: 'john.doe@email.com',
|
||||
defaultValue: settings.from_address
|
||||
},
|
||||
defaultValue: (settings.sender !== undefined) ? settings.sender.address : '',
|
||||
validation: {
|
||||
'data-parsley-required': true,
|
||||
'data-parsley-type': 'email'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -68,20 +81,25 @@ define(
|
||||
{
|
||||
name: 'reply_to_name',
|
||||
type: 'text',
|
||||
placeholder: 'John Doe'
|
||||
placeholder: 'John Doe',
|
||||
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.name : '',
|
||||
},
|
||||
{
|
||||
name: 'reply_to_email',
|
||||
name: 'reply_to_address',
|
||||
type: 'text',
|
||||
placeholder: 'john.doe@email.com'
|
||||
placeholder: 'john.doe@email.com',
|
||||
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.address : ''
|
||||
},
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
var messages = {
|
||||
updated: function() {
|
||||
MailPoet.Notice.success('The newsletter has been updated!');
|
||||
onUpdate: function() {
|
||||
MailPoet.Notice.success('Newsletter successfully updated!');
|
||||
},
|
||||
onCreate: function() {
|
||||
MailPoet.Notice.success('Newsletter successfully added!');
|
||||
}
|
||||
};
|
||||
|
||||
@ -90,34 +108,42 @@ define(
|
||||
Router.History
|
||||
],
|
||||
handleSend: function() {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'newsletters',
|
||||
action: 'send',
|
||||
data: {
|
||||
id: this.props.params.id,
|
||||
newsletter: jQuery('#mailpoet_newsletter').serializeObject(),
|
||||
segments: jQuery('#mailpoet_segments').val()
|
||||
}
|
||||
}).done(function(response) {
|
||||
if(response === true) {
|
||||
this.history.pushState(null, '/');
|
||||
|
||||
MailPoet.Notice.success(
|
||||
'The newsletter has been sent!'
|
||||
);
|
||||
} else {
|
||||
if(response.errors) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.join("<br />")
|
||||
if(jQuery('#mailpoet_newsletter').parsley().validate() === true) {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'sendingQueue',
|
||||
action: 'add',
|
||||
data: {
|
||||
newsletter_id: this.props.params.id,
|
||||
segments: jQuery('#mailpoet_segments').val(),
|
||||
sender: {
|
||||
'name': jQuery('#mailpoet_newsletter [name="sender_name"]').val(),
|
||||
'address': jQuery('#mailpoet_newsletter [name="sender_address"]').val()
|
||||
},
|
||||
reply_to: {
|
||||
'name': jQuery('#mailpoet_newsletter [name="reply_to_name"]').val(),
|
||||
'address': jQuery('#mailpoet_newsletter [name="reply_to_address"]').val()
|
||||
}
|
||||
}
|
||||
}).done(function(response) {
|
||||
if(response.result === true) {
|
||||
this.history.pushState(null, '/');
|
||||
MailPoet.Notice.success(
|
||||
'The newsletter is being sent...'
|
||||
);
|
||||
} else {
|
||||
MailPoet.Notice.error(
|
||||
'An error occurred while trying to send. '+
|
||||
'<a href="?page=mailpoet-settings">Check your settings.</a>'
|
||||
);
|
||||
if(response.errors) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.join("<br />")
|
||||
);
|
||||
} else {
|
||||
MailPoet.Notice.error(
|
||||
'An error occurred while trying to send. '+
|
||||
'<a href="?page=mailpoet-settings">Check your settings.</a>'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
@ -131,8 +157,8 @@ define(
|
||||
endpoint="newsletters"
|
||||
fields={ fields }
|
||||
params={ this.props.params }
|
||||
messages={ messages }>
|
||||
|
||||
messages={ messages }
|
||||
>
|
||||
<p className="submit">
|
||||
<input
|
||||
className="button button-primary"
|
||||
|
@ -18,6 +18,12 @@ define(
|
||||
|
||||
var ImportTemplate = React.createClass({
|
||||
saveTemplate: function(template) {
|
||||
|
||||
// Stringify to enable transmission of primitive non-string value types
|
||||
if (!_.isUndefined(template.body)) {
|
||||
template.body = JSON.stringify(template.body);
|
||||
}
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'save',
|
||||
@ -99,7 +105,7 @@ define(
|
||||
"MailPoet's Guide",
|
||||
description:
|
||||
"This is the standard template that comes with MailPoet.",
|
||||
readonly: true
|
||||
readonly: "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -111,12 +117,19 @@ define(
|
||||
}.bind(this));
|
||||
},
|
||||
handleSelectTemplate: function(template) {
|
||||
var body = template.body;
|
||||
|
||||
// Stringify to enable transmission of primitive non-string value types
|
||||
if (!_.isUndefined(body)) {
|
||||
body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'newsletters',
|
||||
action: 'save',
|
||||
data: {
|
||||
id: this.props.params.id,
|
||||
body: template.body
|
||||
body: body
|
||||
}
|
||||
}).done(function(response) {
|
||||
if(response.result === true) {
|
||||
@ -153,7 +166,7 @@ define(
|
||||
handleShowTemplate: function(template) {
|
||||
MailPoet.Modal.popup({
|
||||
title: template.name,
|
||||
template: '<img src="{{ thumbnail }}" />',
|
||||
template: '<div class="mailpoet_boxes_preview" style="background-color: {{ body.globalStyles.body.backgroundColor }}"><img src="{{ thumbnail }}" /></div>',
|
||||
data: template,
|
||||
});
|
||||
},
|
||||
@ -210,7 +223,7 @@ define(
|
||||
Preview
|
||||
</a>
|
||||
</div>
|
||||
{ (template.readonly) ? false : deleteLink }
|
||||
{ (template.readonly === "1") ? false : deleteLink }
|
||||
</li>
|
||||
);
|
||||
}.bind(this));
|
||||
|
@ -26,6 +26,7 @@ define(
|
||||
action: 'create',
|
||||
data: {
|
||||
type: type,
|
||||
subject: 'Draft newsletter',
|
||||
}
|
||||
}).done(function(response) {
|
||||
if(response.id !== undefined) {
|
||||
|
@ -1,203 +1,212 @@
|
||||
define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
||||
"use strict";
|
||||
/*==================================================================================================
|
||||
|
||||
MailPoet Notice:
|
||||
|
||||
description: Handles notices
|
||||
version: 0.2
|
||||
author: Jonathan Labreuille
|
||||
company: Wysija
|
||||
dependencies: jQuery
|
||||
|
||||
Usage:
|
||||
|
||||
// success message (static: false)
|
||||
MailPoet.Notice.success('Yatta!');
|
||||
|
||||
// error message (static: false)
|
||||
MailPoet.Notice.error('Boo!');
|
||||
|
||||
// system message (static: true)
|
||||
MailPoet.Notice.system('You need to updated ASAP!');
|
||||
|
||||
Examples:
|
||||
|
||||
MailPoet.Notice.success('- success #1 -');
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.success('- success #2 -');
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.error('- error -');
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.system('- system -');
|
||||
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.hide();
|
||||
}, 2500);
|
||||
}, 300);
|
||||
}, 400);
|
||||
}, 500);
|
||||
|
||||
==================================================================================================*/
|
||||
|
||||
MailPoet.Notice = {
|
||||
version: 0.2,
|
||||
// default options
|
||||
defaults: {
|
||||
type: 'success',
|
||||
message: '',
|
||||
static: false,
|
||||
hideClose: false,
|
||||
id: null,
|
||||
scroll: false,
|
||||
timeout: 2000,
|
||||
onOpen: null,
|
||||
onClose: null
|
||||
},
|
||||
options: {},
|
||||
init: function(options) {
|
||||
// set options
|
||||
this.options = jQuery.extend({}, this.defaults, options);
|
||||
|
||||
// clone element
|
||||
this.element = jQuery('#mailpoet_notice_'+this.options.type).clone();
|
||||
|
||||
// add data-id to the element
|
||||
if (this.options.id) this.element.attr('data-id', 'notice_' + this.options.id);
|
||||
|
||||
// remove id from clone
|
||||
this.element.removeAttr('id');
|
||||
|
||||
// insert notice after its parent
|
||||
jQuery('#mailpoet_notice_'+this.options.type).after(this.element);
|
||||
|
||||
// setup onClose callback
|
||||
var onClose = null;
|
||||
if(this.options.onClose !== null) {
|
||||
onClose = this.options.onClose;
|
||||
}
|
||||
|
||||
// listen to remove event
|
||||
jQuery(this.element).on('close', function() {
|
||||
jQuery(this).fadeOut(200, function() {
|
||||
// on close callback
|
||||
if(onClose !== null) {
|
||||
onClose();
|
||||
}
|
||||
// remove notice
|
||||
jQuery(this).remove();
|
||||
});
|
||||
}.bind(this.element));
|
||||
|
||||
// listen to message event
|
||||
jQuery(this.element).on('message', function(e, message) {
|
||||
MailPoet.Notice.setMessage(message);
|
||||
}.bind(this.element));
|
||||
|
||||
return this;
|
||||
},
|
||||
isHTML: function(str) {
|
||||
var a = document.createElement('div');
|
||||
a.innerHTML = str;
|
||||
for(var c = a.childNodes, i = c.length; i--;) {
|
||||
if(c[i].nodeType == 1) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
setMessage: function(message) {
|
||||
// if it's not an html message, let's sugar coat the message with a fancy <p>
|
||||
if(this.isHTML(message) === false) {
|
||||
message = '<p>'+message+'</p>';
|
||||
}
|
||||
// set message
|
||||
return this.element.html(message);
|
||||
},
|
||||
show: function(options) {
|
||||
// initialize
|
||||
this.init(options);
|
||||
|
||||
// show notice
|
||||
this.showNotice();
|
||||
|
||||
// return this;
|
||||
},
|
||||
showNotice: function() {
|
||||
// set message
|
||||
this.setMessage(this.options.message);
|
||||
|
||||
// position notice
|
||||
this.element.insertAfter(jQuery('h2.title'));
|
||||
|
||||
// set class name
|
||||
switch(this.options.type) {
|
||||
case 'success':
|
||||
this.element.addClass('updated');
|
||||
break;
|
||||
case 'system':
|
||||
this.element.addClass('update-nag');
|
||||
break;
|
||||
case 'error':
|
||||
this.element.addClass('error');
|
||||
break;
|
||||
}
|
||||
|
||||
// make the notice appear
|
||||
this.element.fadeIn(200);
|
||||
|
||||
// if scroll option is enabled, scroll to the notice
|
||||
if(this.options.scroll === true) {
|
||||
this.element.get(0).scrollIntoView(false);
|
||||
}
|
||||
|
||||
// if the notice is not static, it has to disappear after a timeout
|
||||
if(this.options.static === false) {
|
||||
this.element.delay(this.options.timeout).trigger('close');
|
||||
} else if (this.options.hideClose === false) {
|
||||
this.element.append('<a href="javascript:;" class="mailpoet_notice_close"><span class="dashicons dashicons-dismiss"></span></a>');
|
||||
this.element.find('.mailpoet_notice_close').on('click', function() {
|
||||
jQuery(this).trigger('close');
|
||||
});
|
||||
}
|
||||
|
||||
// call onOpen callback
|
||||
if(this.options.onOpen !== null) {
|
||||
this.options.onOpen(this.element);
|
||||
}
|
||||
},
|
||||
hide: function(all) {
|
||||
if(all !== undefined && all === true) {
|
||||
jQuery('.mailpoet_notice:not([id])').trigger('close');
|
||||
} else if (all !== undefined && jQuery.isArray(all)) {
|
||||
for (var id in all) {
|
||||
jQuery('[data-id="notice_' + all[id] + '"]')
|
||||
.trigger('close');
|
||||
}
|
||||
} if (all !== undefined) {
|
||||
jQuery('[data-id="notice_' + all + '"]')
|
||||
.trigger('close');
|
||||
} else {
|
||||
jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])')
|
||||
.trigger('close');
|
||||
}
|
||||
},
|
||||
error: function(message, options) {
|
||||
this.show(jQuery.extend({}, {
|
||||
type: 'error',
|
||||
message: '<p>'+message+'</p>'
|
||||
}, options));
|
||||
},
|
||||
success: function(message, options) {
|
||||
this.show(jQuery.extend({}, {
|
||||
type: 'success',
|
||||
message: '<p>'+message+'</p>'
|
||||
}, options));
|
||||
},
|
||||
system: function(message, options) {
|
||||
this.show(jQuery.extend({}, {
|
||||
type: 'system',
|
||||
static: true,
|
||||
message: message
|
||||
}, options));
|
||||
}
|
||||
};
|
||||
define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
||||
"use strict";
|
||||
/*==================================================================================================
|
||||
|
||||
MailPoet Notice:
|
||||
|
||||
description: Handles notices
|
||||
version: 0.2
|
||||
author: Jonathan Labreuille
|
||||
company: Wysija
|
||||
dependencies: jQuery
|
||||
|
||||
Usage:
|
||||
|
||||
// success message (static: false)
|
||||
MailPoet.Notice.success('Yatta!');
|
||||
|
||||
// error message (static: false)
|
||||
MailPoet.Notice.error('Boo!');
|
||||
|
||||
// system message (static: true)
|
||||
MailPoet.Notice.system('You need to updated ASAP!');
|
||||
|
||||
Examples:
|
||||
|
||||
MailPoet.Notice.success('- success #1 -');
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.success('- success #2 -');
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.error('- error -');
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.system('- system -');
|
||||
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.hide();
|
||||
}, 2500);
|
||||
}, 300);
|
||||
}, 400);
|
||||
}, 500);
|
||||
|
||||
==================================================================================================*/
|
||||
|
||||
MailPoet.Notice = {
|
||||
version: 0.2,
|
||||
// default options
|
||||
defaults: {
|
||||
type: 'success',
|
||||
message: '',
|
||||
static: false,
|
||||
hideClose: false,
|
||||
id: null,
|
||||
positionAfter: false,
|
||||
scroll: false,
|
||||
timeout: 2000,
|
||||
onOpen: null,
|
||||
onClose: null
|
||||
},
|
||||
options: {},
|
||||
init: function(options) {
|
||||
// set options
|
||||
this.options = jQuery.extend({}, this.defaults, options);
|
||||
|
||||
// clone element
|
||||
this.element = jQuery('#mailpoet_notice_'+this.options.type).clone();
|
||||
|
||||
// add data-id to the element
|
||||
if (this.options.id) this.element.attr('data-id', 'notice_' + this.options.id);
|
||||
|
||||
// remove id from clone
|
||||
this.element.removeAttr('id');
|
||||
|
||||
// insert notice after its parent
|
||||
var positionAfter;
|
||||
if (typeof this.options.positionAfter === 'object') {
|
||||
positionAfter = this.options.positionAfter;
|
||||
} else if (typeof this.options.positionAfter === 'string') {
|
||||
positionAfter = jQuery(this.options.positionAfter);
|
||||
} else {
|
||||
positionAfter = jQuery('#mailpoet_notice_'+this.options.type);
|
||||
}
|
||||
positionAfter.after(this.element);
|
||||
|
||||
// setup onClose callback
|
||||
var onClose = null;
|
||||
if(this.options.onClose !== null) {
|
||||
onClose = this.options.onClose;
|
||||
}
|
||||
|
||||
// listen to remove event
|
||||
jQuery(this.element).on('close', function() {
|
||||
jQuery(this).fadeOut(200, function() {
|
||||
// on close callback
|
||||
if(onClose !== null) {
|
||||
onClose();
|
||||
}
|
||||
// remove notice
|
||||
jQuery(this).remove();
|
||||
});
|
||||
}.bind(this.element));
|
||||
|
||||
// listen to message event
|
||||
jQuery(this.element).on('message', function(e, message) {
|
||||
MailPoet.Notice.setMessage(message);
|
||||
}.bind(this.element));
|
||||
|
||||
return this;
|
||||
},
|
||||
isHTML: function(str) {
|
||||
var a = document.createElement('div');
|
||||
a.innerHTML = str;
|
||||
for(var c = a.childNodes, i = c.length; i--;) {
|
||||
if(c[i].nodeType == 1) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
setMessage: function(message) {
|
||||
// if it's not an html message, let's sugar coat the message with a fancy <p>
|
||||
if(this.isHTML(message) === false) {
|
||||
message = '<p>'+message+'</p>';
|
||||
}
|
||||
// set message
|
||||
return this.element.html(message);
|
||||
},
|
||||
show: function(options) {
|
||||
// initialize
|
||||
this.init(options);
|
||||
|
||||
// show notice
|
||||
this.showNotice();
|
||||
|
||||
// return this;
|
||||
},
|
||||
showNotice: function() {
|
||||
// set message
|
||||
this.setMessage(this.options.message);
|
||||
|
||||
// position notice
|
||||
this.element.insertAfter(jQuery('h2.title'));
|
||||
|
||||
// set class name
|
||||
switch(this.options.type) {
|
||||
case 'success':
|
||||
this.element.addClass('updated');
|
||||
break;
|
||||
case 'system':
|
||||
this.element.addClass('update-nag');
|
||||
break;
|
||||
case 'error':
|
||||
this.element.addClass('error');
|
||||
break;
|
||||
}
|
||||
|
||||
// make the notice appear
|
||||
this.element.fadeIn(200);
|
||||
|
||||
// if scroll option is enabled, scroll to the notice
|
||||
if(this.options.scroll === true) {
|
||||
this.element.get(0).scrollIntoView(false);
|
||||
}
|
||||
|
||||
// if the notice is not static, it has to disappear after a timeout
|
||||
if(this.options.static === false) {
|
||||
this.element.delay(this.options.timeout).trigger('close');
|
||||
} else if (this.options.hideClose === false) {
|
||||
this.element.append('<a href="javascript:;" class="mailpoet_notice_close"><span class="dashicons dashicons-dismiss"></span></a>');
|
||||
this.element.find('.mailpoet_notice_close').on('click', function() {
|
||||
jQuery(this).trigger('close');
|
||||
});
|
||||
}
|
||||
|
||||
// call onOpen callback
|
||||
if(this.options.onOpen !== null) {
|
||||
this.options.onOpen(this.element);
|
||||
}
|
||||
},
|
||||
hide: function(all) {
|
||||
if(all !== undefined && all === true) {
|
||||
jQuery('.mailpoet_notice:not([id])').trigger('close');
|
||||
} else if (all !== undefined && jQuery.isArray(all)) {
|
||||
for (var id in all) {
|
||||
jQuery('[data-id="notice_' + all[id] + '"]')
|
||||
.trigger('close');
|
||||
}
|
||||
} if (all !== undefined) {
|
||||
jQuery('[data-id="notice_' + all + '"]')
|
||||
.trigger('close');
|
||||
} else {
|
||||
jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])')
|
||||
.trigger('close');
|
||||
}
|
||||
},
|
||||
error: function(message, options) {
|
||||
this.show(jQuery.extend({}, {
|
||||
type: 'error',
|
||||
message: '<p>'+message+'</p>'
|
||||
}, options));
|
||||
},
|
||||
success: function(message, options) {
|
||||
this.show(jQuery.extend({}, {
|
||||
type: 'success',
|
||||
message: '<p>'+message+'</p>'
|
||||
}, options));
|
||||
},
|
||||
system: function(message, options) {
|
||||
this.show(jQuery.extend({}, {
|
||||
type: 'system',
|
||||
static: true,
|
||||
message: message
|
||||
}, options));
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -1,75 +0,0 @@
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
);
|
@ -42,11 +42,7 @@ define(
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
Segment <a
|
||||
href="javascript:;"
|
||||
className="add-new-h2"
|
||||
onClick={ this.history.goBack }
|
||||
>Back to list</a>
|
||||
Segment
|
||||
</h2>
|
||||
|
||||
<Form
|
||||
|
@ -131,10 +131,12 @@ const item_actions = [
|
||||
label: 'Update',
|
||||
className: 'update',
|
||||
onClick: function(item, refresh) {
|
||||
return MailPoet.Ajax.post({
|
||||
MailPoet.Modal.loading(true);
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'segments',
|
||||
action: 'synchronize'
|
||||
}).done(function(response) {
|
||||
MailPoet.Modal.loading(false);
|
||||
if(response === true) {
|
||||
MailPoet.Notice.success(
|
||||
('List "%$1s" has been synchronized.').replace('%$1s', item.name)
|
||||
|
@ -15,10 +15,10 @@ define(
|
||||
|
||||
MailPoet.Router = new (Backbone.Router.extend({
|
||||
routes: {
|
||||
'mta(/:method)': 'sendingMethod',
|
||||
'mta(/:group)': 'sendingMethodGroup',
|
||||
'(:tab)': 'tabs',
|
||||
},
|
||||
sendingMethod: function(method) {
|
||||
sendingMethodGroup: function(group) {
|
||||
// display mta tab
|
||||
this.tabs('mta');
|
||||
|
||||
@ -30,13 +30,13 @@ define(
|
||||
// hide "save settings" button
|
||||
jQuery('.mailpoet_settings_submit').hide();
|
||||
|
||||
if(method === null) {
|
||||
if(group === null) {
|
||||
// show sending methods
|
||||
jQuery('.mailpoet_sending_methods').fadeIn();
|
||||
} else {
|
||||
// hide DKIM option when using MailPoet's API
|
||||
jQuery('#mailpoet_mta_dkim')[
|
||||
(method === 'mailpoet')
|
||||
(group === 'mailpoet')
|
||||
? 'hide'
|
||||
: 'show'
|
||||
]();
|
||||
@ -45,7 +45,7 @@ define(
|
||||
jQuery('.mailpoet_sending_methods').hide();
|
||||
|
||||
// display selected sending method's settings
|
||||
jQuery('.mailpoet_sending_method[data-method="'+ method +'"]').show();
|
||||
jQuery('.mailpoet_sending_method[data-group="'+ group +'"]').show();
|
||||
jQuery('#mailpoet_sending_method_setup').fadeIn();
|
||||
}
|
||||
},
|
||||
|
@ -51,6 +51,24 @@ define(
|
||||
}
|
||||
];
|
||||
|
||||
var custom_fields = window.mailpoet_custom_fields || [];
|
||||
custom_fields.map(custom_field => {
|
||||
let field = {
|
||||
name: 'cf_' + custom_field.id,
|
||||
label: custom_field.name,
|
||||
type: custom_field.type
|
||||
};
|
||||
if(custom_field.params) {
|
||||
field.params = custom_field.params;
|
||||
}
|
||||
|
||||
if(custom_field.params.values) {
|
||||
field.values = custom_field.params.values;
|
||||
}
|
||||
|
||||
fields.push(field);
|
||||
});
|
||||
|
||||
var messages = {
|
||||
onUpdate: function() {
|
||||
MailPoet.Notice.success('Subscriber successfully updated!');
|
||||
@ -70,11 +88,7 @@ define(
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
Subscriber <a
|
||||
href="javascript:;"
|
||||
className="add-new-h2"
|
||||
onClick={ this.history.goBack }
|
||||
>Back to list</a>
|
||||
Subscriber
|
||||
</h2>
|
||||
|
||||
<Form
|
||||
|
@ -62,7 +62,7 @@ define(
|
||||
if (_.contains(fieldsToExclude, selectedOptionId)) {
|
||||
selectEvent.preventDefault();
|
||||
if (selectedOptionId === 'deselect') {
|
||||
jQuery(selectElement).select2('val', '');
|
||||
jQuery(selectElement).val('').trigger('change');
|
||||
} else {
|
||||
var allOptions = [];
|
||||
_.each(container.find('option'), function (field) {
|
||||
@ -70,7 +70,7 @@ define(
|
||||
allOptions.push(field.value);
|
||||
}
|
||||
});
|
||||
jQuery(selectElement).select2('val', allOptions);
|
||||
jQuery(selectElement).val(allOptions).trigger('change');
|
||||
}
|
||||
jQuery(selectElement).select2('close');
|
||||
}
|
||||
@ -138,11 +138,11 @@ define(
|
||||
endpoint: 'ImportExport',
|
||||
action: 'processExport',
|
||||
data: JSON.stringify({
|
||||
'exportConfirmedOption': exportData.exportConfirmedOption,
|
||||
'exportFormatOption': jQuery(':radio[name="option_format"]:checked').val(),
|
||||
'groupBySegmentOption': (groupBySegmentOptionElement.is(":visible")) ? groupBySegmentOptionElement.prop('checked') : false,
|
||||
'export_confirmed_option': exportData.exportConfirmedOption,
|
||||
'export_format_option': jQuery(':radio[name="option_format"]:checked').val(),
|
||||
'group_by_segment_option': (groupBySegmentOptionElement.is(":visible")) ? groupBySegmentOptionElement.prop('checked') : false,
|
||||
'segments': (exportData.segments) ? segmentsContainerElement.val() : false,
|
||||
'subscriberFields': subscriberFieldsContainerElement.val()
|
||||
'subscriber_fields': subscriberFieldsContainerElement.val()
|
||||
})
|
||||
})
|
||||
.done(function (response) {
|
||||
|
File diff suppressed because it is too large
Load Diff
39
build
39
build
@ -1,39 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Remove previous build.
|
||||
rm wysija-newsletters.zip;
|
||||
|
||||
# Create temp dir.
|
||||
mkdir wysija-newsletters;
|
||||
|
||||
# Production assets.
|
||||
npm install;
|
||||
./do compile:all;
|
||||
|
||||
# Production libraries.
|
||||
./composer.phar install --no-dev;
|
||||
|
||||
# Copy release folders.
|
||||
cp -Rf lang wysija-newsletters;
|
||||
cp -RfL assets wysija-newsletters;
|
||||
cp -Rf lib wysija-newsletters;
|
||||
cp -Rf vendor wysija-newsletters;
|
||||
cp -Rf views wysija-newsletters;
|
||||
rm -Rf wysija-newsletters/assets/css/src;
|
||||
rm -Rf wysija-newsletters/assets/js/src;
|
||||
|
||||
# Copy release files.
|
||||
cp LICENSE wysija-newsletters;
|
||||
cp index.php wysija-newsletters;
|
||||
cp mailpoet.php wysija-newsletters;
|
||||
cp readme.txt wysija-newsletters;
|
||||
cp uninstall.php wysija-newsletters;
|
||||
|
||||
# Zip final release.
|
||||
zip -r wysija-newsletters.zip wysija-newsletters;
|
||||
|
||||
# Remove temp dir.
|
||||
rm -rf wysija-newsletters;
|
||||
|
||||
# Reinstall dev dependencies.
|
||||
./composer.phar install;
|
42
build.sh
Executable file
42
build.sh
Executable file
@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
|
||||
plugin_name='mailpoet'
|
||||
|
||||
# Remove previous build.
|
||||
rm $plugin_name.zip
|
||||
|
||||
# Create temp dir.
|
||||
mkdir $plugin_name
|
||||
|
||||
# Production assets.
|
||||
rm -rf node_modules
|
||||
npm install
|
||||
./do compile:all
|
||||
|
||||
# Production libraries.
|
||||
./composer.phar install --no-dev
|
||||
|
||||
# Copy release folders.
|
||||
cp -Rf lang $plugin_name
|
||||
cp -RfL assets $plugin_name
|
||||
cp -Rf lib $plugin_name
|
||||
cp -Rf vendor $plugin_name
|
||||
cp -Rf views $plugin_name
|
||||
rm -Rf $plugin_name/assets/css/src
|
||||
rm -Rf $plugin_name/assets/js/src
|
||||
|
||||
# Copy release files.
|
||||
cp LICENSE $plugin_name
|
||||
cp index.php $plugin_name
|
||||
cp $plugin_name.php $plugin_name
|
||||
cp readme.txt $plugin_name
|
||||
cp uninstall.php $plugin_name
|
||||
|
||||
# Zip final release.
|
||||
zip -r $plugin_name.zip $plugin_name
|
||||
|
||||
# Remove temp dir.
|
||||
rm -rf $plugin_name
|
||||
|
||||
# Reinstall dev dependencies.
|
||||
./composer.phar install
|
@ -10,7 +10,8 @@
|
||||
"swiftmailer/swiftmailer": "^5.4",
|
||||
"phpseclib/phpseclib": "*",
|
||||
"mtdowling/cron-expression": "^1.0",
|
||||
"nesbot/carbon": "^1.21"
|
||||
"nesbot/carbon": "^1.21",
|
||||
"soundasleep/html2text": "^0.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "*",
|
||||
|
954
composer.lock
generated
954
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -9,13 +9,6 @@ class Activator {
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
function init() {
|
||||
register_activation_hook(
|
||||
Env::$file,
|
||||
array($this, 'activate')
|
||||
);
|
||||
}
|
||||
|
||||
function activate() {
|
||||
$migrator = new Migrator();
|
||||
$migrator->up();
|
||||
@ -23,4 +16,9 @@ class Activator {
|
||||
$populator = new Populator();
|
||||
$populator->up();
|
||||
}
|
||||
|
||||
function deactivate() {
|
||||
$migrator = new Migrator();
|
||||
$migrator->down();
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,8 @@ class Analytics {
|
||||
}
|
||||
|
||||
function init() {
|
||||
add_action('admin_enqueue_scripts', array($this, 'setupAdminDependencies'));
|
||||
// review: this creates a fatal error when mailpoet tables are dropped.
|
||||
//add_action('admin_enqueue_scripts', array($this, 'setupAdminDependencies'));
|
||||
}
|
||||
|
||||
function setupAdminDependencies() {
|
||||
|
@ -6,15 +6,14 @@ if(!defined('ABSPATH')) exit;
|
||||
class Env {
|
||||
static $version;
|
||||
static $plugin_name;
|
||||
static $plugin_url;
|
||||
static $plugin_path;
|
||||
static $file;
|
||||
static $path;
|
||||
static $views_path;
|
||||
static $assets_path;
|
||||
static $assets_url;
|
||||
static $temp_name;
|
||||
static $temp_path;
|
||||
static $temp_URL;
|
||||
static $languages_path;
|
||||
static $lib_path;
|
||||
static $plugin_prefix;
|
||||
@ -34,12 +33,11 @@ class Env {
|
||||
self::$file = $file;
|
||||
self::$path = dirname(self::$file);
|
||||
self::$plugin_name = 'mailpoet';
|
||||
self::$plugin_url = plugin_dir_url(__FILE__);
|
||||
self::$views_path = self::$path . '/views';
|
||||
self::$assets_path = self::$path . '/assets';
|
||||
self::$assets_url = plugins_url('/assets', $file);
|
||||
self::$temp_name = 'temp';
|
||||
self::$temp_path = self::$path . '/' . self::$temp_name;
|
||||
self::$temp_path = wp_upload_dir()['path'];
|
||||
self::$temp_URL = wp_upload_dir()['url'];
|
||||
self::$languages_path = self::$path . '/lang';
|
||||
self::$lib_path = self::$path . '/lib';
|
||||
self::$plugin_prefix = 'mailpoet_';
|
||||
@ -77,11 +75,16 @@ class Env {
|
||||
|
||||
static function isPluginActivated() {
|
||||
$activatesPlugins = get_option('active_plugins');
|
||||
$isActivated =
|
||||
$isActivated = (
|
||||
in_array(
|
||||
sprintf('%s/%s.php', basename(self::$path), self::$plugin_name),
|
||||
$activatesPlugins
|
||||
);
|
||||
) ||
|
||||
in_array(
|
||||
sprintf('%s/%s.php', explode('/', plugin_basename(__FILE__))[0], self::$plugin_name),
|
||||
$activatesPlugins
|
||||
)
|
||||
);
|
||||
return ($isActivated) ? true : false;
|
||||
}
|
||||
}
|
@ -1,11 +1,61 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
use \MailPoet\Models\Setting;
|
||||
|
||||
class Hooks {
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
function init() {
|
||||
// Subscribe in comments
|
||||
if((bool)Setting::getValue('subscribe.on_comment.enabled')) {
|
||||
add_action(
|
||||
'comment_form_after_fields',
|
||||
'\MailPoet\Subscription\Comment::extendForm'
|
||||
);
|
||||
|
||||
add_action(
|
||||
'comment_post',
|
||||
'\MailPoet\Subscription\Comment::onSubmit',
|
||||
60,
|
||||
2
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wp_set_comment_status',
|
||||
'\MailPoet\Subscription\Comment::onStatusUpdate',
|
||||
60,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
// Subscribe in registration form
|
||||
if((bool)Setting::getValue('subscribe.on_register.enabled')) {
|
||||
if(is_multisite()) {
|
||||
add_action(
|
||||
'signup_extra_fields',
|
||||
'\MailPoet\Subscription\Registration::extendForm'
|
||||
);
|
||||
add_action(
|
||||
'wpmu_validate_user_signup',
|
||||
'\MailPoet\Subscription\Registration::onMultiSiteRegister',
|
||||
60,
|
||||
1
|
||||
);
|
||||
} else {
|
||||
add_action(
|
||||
'register_form',
|
||||
'\MailPoet\Subscription\Registration::extendForm'
|
||||
);
|
||||
add_action(
|
||||
'register_post',
|
||||
'\MailPoet\Subscription\Registration::onRegister',
|
||||
60,
|
||||
3
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// WP Users synchronization
|
||||
add_action(
|
||||
'user_register',
|
||||
@ -38,5 +88,19 @@ class Hooks {
|
||||
'\MailPoet\Segments\WP::synchronizeUser',
|
||||
1
|
||||
);
|
||||
|
||||
add_filter(
|
||||
'image_size_names_choose',
|
||||
array(
|
||||
$this,
|
||||
'appendImageSizes'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function appendImageSizes($sizes) {
|
||||
return array_merge($sizes, array(
|
||||
'mailpoet_newsletter_max' => __('MailPoet Newsletter'),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,10 @@
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Models;
|
||||
use MailPoet\Queue\Supervisor;
|
||||
use MailPoet\Cron\Supervisor;
|
||||
use MailPoet\Router;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Settings\Pages;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -16,19 +18,27 @@ class Initializer {
|
||||
}
|
||||
|
||||
function init() {
|
||||
$this->setupDB();
|
||||
$this->setupActivator();
|
||||
$this->setupRenderer();
|
||||
$this->setupLocalizer();
|
||||
$this->setupMenu();
|
||||
$this->setupRouter();
|
||||
$this->setupWidget();
|
||||
$this->setupAnalytics();
|
||||
$this->setupPermissions();
|
||||
$this->setupChangelog();
|
||||
$this->setupPublicAPI();
|
||||
$this->runQueueSupervisor();
|
||||
$this->setupHooks();
|
||||
try {
|
||||
$this->setupDB();
|
||||
$this->setupRenderer();
|
||||
$this->setupLocalizer();
|
||||
$this->setupMenu();
|
||||
$this->setupRouter();
|
||||
$this->setupWidget();
|
||||
$this->setupAnalytics();
|
||||
$this->setupPermissions();
|
||||
$this->setupChangelog();
|
||||
$this->setupPublicAPI();
|
||||
$this->runQueueSupervisor();
|
||||
$this->setupShortcodes();
|
||||
$this->setupHooks();
|
||||
$this->setupPages();
|
||||
$this->setupImages();
|
||||
} catch(\Exception $e) {
|
||||
// if anything goes wrong during init
|
||||
// automatically deactivate the plugin
|
||||
deactivate_plugins(Env::$file);
|
||||
}
|
||||
}
|
||||
|
||||
function setupDB() {
|
||||
@ -55,7 +65,7 @@ class Initializer {
|
||||
$subscriber_custom_field = Env::$db_prefix . 'subscriber_custom_field';
|
||||
$newsletter_option_fields = Env::$db_prefix . 'newsletter_option_fields';
|
||||
$newsletter_option = Env::$db_prefix . 'newsletter_option';
|
||||
$queues = Env::$db_prefix . 'queues';
|
||||
$sending_queues = Env::$db_prefix . 'sending_queues';
|
||||
$newsletter_statistics = Env::$db_prefix . 'newsletter_statistics';
|
||||
|
||||
define('MP_SUBSCRIBERS_TABLE', $subscribers);
|
||||
@ -72,13 +82,14 @@ class Initializer {
|
||||
define('MP_SUBSCRIBER_CUSTOM_FIELD_TABLE', $subscriber_custom_field);
|
||||
define('MP_NEWSLETTER_OPTION_FIELDS_TABLE', $newsletter_option_fields);
|
||||
define('MP_NEWSLETTER_OPTION_TABLE', $newsletter_option);
|
||||
define('MP_QUEUES_TABLE', $queues);
|
||||
define('MP_SENDING_QUEUES_TABLE', $sending_queues);
|
||||
define('MP_NEWSLETTER_STATISTICS_TABLE', $newsletter_statistics);
|
||||
}
|
||||
|
||||
function setupActivator() {
|
||||
$activator = new Activator();
|
||||
$activator->init();
|
||||
function runPopulator() {
|
||||
$this->init();
|
||||
$populator = new Populator();
|
||||
$populator->up();
|
||||
}
|
||||
|
||||
function setupRenderer() {
|
||||
@ -124,20 +135,38 @@ class Initializer {
|
||||
$changelog->init();
|
||||
}
|
||||
|
||||
function setupPages() {
|
||||
$pages = new Pages();
|
||||
$pages->init();
|
||||
}
|
||||
|
||||
function setupShortcodes() {
|
||||
$shortcodes = new Shortcodes();
|
||||
$shortcodes->init();
|
||||
}
|
||||
|
||||
function setupHooks() {
|
||||
$hooks = new Hooks();
|
||||
$hooks->init();
|
||||
}
|
||||
|
||||
|
||||
function setupPublicAPI() {
|
||||
$publicAPI = new PublicAPI();
|
||||
$publicAPI->init();
|
||||
}
|
||||
|
||||
function runQueueSupervisor() {
|
||||
if(php_sapi_name() === 'cli' ||
|
||||
!Env::isPluginActivated()
|
||||
) return;
|
||||
try {
|
||||
$supervisor = new Supervisor();
|
||||
$supervisor->checkDaemon();
|
||||
} catch (\Exception $e) {}
|
||||
} catch(\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
function setupImages() {
|
||||
add_image_size('mailpoet_newsletter_max', 1320);
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Form\Block;
|
||||
use MailPoet\Form\Renderer as FormRenderer;
|
||||
use MailPoet\Models\CustomField;
|
||||
use MailPoet\Models\Form;
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Models\Setting;
|
||||
@ -172,13 +173,13 @@ class Menu {
|
||||
|
||||
add_submenu_page(
|
||||
'mailpoet',
|
||||
__('Queue'),
|
||||
__('Queue'),
|
||||
__('Cron'),
|
||||
__('Cron'),
|
||||
'manage_options',
|
||||
'mailpoet-queue',
|
||||
'mailpoet-cron',
|
||||
array(
|
||||
$this,
|
||||
'queue'
|
||||
'cron'
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -189,6 +190,8 @@ class Menu {
|
||||
}
|
||||
|
||||
function welcome() {
|
||||
if((bool)(defined('DOING_AJAX') && DOING_AJAX)) return;
|
||||
|
||||
global $wp;
|
||||
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
|
||||
$redirect_url =
|
||||
@ -239,6 +242,7 @@ class Menu {
|
||||
|
||||
function settings() {
|
||||
$settings = Setting::getAll();
|
||||
$flags = $this->_getFlags();
|
||||
|
||||
// dkim: check if public/private keys have been generated
|
||||
if(
|
||||
@ -257,10 +261,9 @@ class Menu {
|
||||
|
||||
$data = array(
|
||||
'settings' => $settings,
|
||||
'segments' => Segment::getPublished()
|
||||
->findArray(),
|
||||
'segments' => Segment::getPublic()->findArray(),
|
||||
'pages' => Pages::getAll(),
|
||||
'flags' => $this->_getFlags(),
|
||||
'flags' => $flags,
|
||||
'charsets' => Charsets::getAll(),
|
||||
'current_user' => wp_get_current_user(),
|
||||
'permissions' => Permissions::getAll(),
|
||||
@ -293,7 +296,7 @@ class Menu {
|
||||
} else {
|
||||
// check if users can register
|
||||
$flags['registration_enabled'] =
|
||||
(bool) get_option('users_can_register', false);
|
||||
(bool)get_option('users_can_register', false);
|
||||
}
|
||||
|
||||
return $flags;
|
||||
@ -304,6 +307,23 @@ class Menu {
|
||||
|
||||
$data['segments'] = Segment::findArray();
|
||||
|
||||
$data['custom_fields'] = array_map(function($field) {
|
||||
$field['params'] = unserialize($field['params']);
|
||||
|
||||
if(!empty($field['params']['values'])) {
|
||||
$values = array();
|
||||
|
||||
foreach($field['params']['values'] as $value) {
|
||||
$values[$value['value']] = $value['value'];
|
||||
}
|
||||
$field['params']['values'] = $values;
|
||||
}
|
||||
return $field;
|
||||
}, CustomField::findArray());
|
||||
|
||||
$data['date_formats'] = Block\Date::getDateFormats();
|
||||
$data['month_names'] = Block\Date::getMonthNames();
|
||||
|
||||
echo $this->renderer->render('subscribers/subscribers.html', $data);
|
||||
}
|
||||
|
||||
@ -325,17 +345,22 @@ class Menu {
|
||||
$data = array();
|
||||
|
||||
$data['segments'] = Segment::findArray();
|
||||
$settings = Setting::findArray();
|
||||
$data['settings'] = array();
|
||||
foreach ($settings as $setting) {
|
||||
$data['settings'][$setting['name']] = $setting['value'];
|
||||
}
|
||||
$data['settings'] = Setting::getAll();
|
||||
$data['roles'] = $wp_roles->get_names();
|
||||
echo $this->renderer->render('newsletters.html', $data);
|
||||
}
|
||||
|
||||
function newletterEditor() {
|
||||
$data = array();
|
||||
$custom_fields = array_map(function($field) {
|
||||
return array(
|
||||
'text' => $field['name'],
|
||||
'shortcode' => 'field:' . $field['id'],
|
||||
);
|
||||
}, CustomField::findArray());
|
||||
|
||||
$data = array(
|
||||
'customFields' => $custom_fields,
|
||||
);
|
||||
wp_enqueue_media();
|
||||
wp_enqueue_script('tinymce-wplink', includes_url('js/tinymce/plugins/wplink/plugin.js'));
|
||||
wp_enqueue_style('editor', includes_url('css/editor.css'));
|
||||
@ -364,19 +389,18 @@ class Menu {
|
||||
$data = array(
|
||||
'form' => $form,
|
||||
'pages' => Pages::getAll(),
|
||||
'segments' => Segment::getPublished()
|
||||
'segments' => Segment::getPublic()
|
||||
->findArray(),
|
||||
'styles' => FormRenderer::getStyles($form),
|
||||
'date_types' => Block\Date::getDateTypes(),
|
||||
'date_formats' => Block\Date::getDateFormats()
|
||||
'date_formats' => Block\Date::getDateFormats(),
|
||||
'month_names' => Block\Date::getMonthNames()
|
||||
);
|
||||
|
||||
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);
|
||||
function cron() {
|
||||
echo $this->renderer->render('cron.html');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||
|
||||
@ -21,7 +21,7 @@ class Migrator {
|
||||
'subscriber_custom_field',
|
||||
'newsletter_option_fields',
|
||||
'newsletter_option',
|
||||
'queues',
|
||||
'sending_queues',
|
||||
'newsletter_statistics',
|
||||
'forms'
|
||||
);
|
||||
@ -41,7 +41,7 @@ class Migrator {
|
||||
function down() {
|
||||
global $wpdb;
|
||||
|
||||
$drop_table = function($model) {
|
||||
$drop_table = function($model) use($wpdb) {
|
||||
$table = $this->prefix . $model;
|
||||
$wpdb->query("DROP TABLE {$table}");
|
||||
};
|
||||
@ -84,6 +84,10 @@ class Migrator {
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'subject varchar(250) NOT NULL,',
|
||||
'type varchar(20) NOT NULL DEFAULT "standard",',
|
||||
'sender_address varchar(150) NOT NULL,',
|
||||
'sender_name varchar(150) NOT NULL,',
|
||||
'reply_to_address varchar(150) NOT NULL,',
|
||||
'reply_to_name varchar(150) NOT NULL,',
|
||||
'preheader varchar(250) NOT NULL,',
|
||||
'body longtext,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
@ -99,8 +103,9 @@ class Migrator {
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'name varchar(250) NOT NULL,',
|
||||
'description varchar(250) NOT NULL,',
|
||||
'body longtext,',
|
||||
'thumbnail longtext,',
|
||||
'body LONGTEXT,',
|
||||
'thumbnail LONGTEXT,',
|
||||
'readonly TINYINT(1) DEFAULT 0,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id)'
|
||||
@ -205,12 +210,12 @@ class Migrator {
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
|
||||
function queues() {
|
||||
function sending_queues() {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'newsletter_id mediumint(9) NOT NULL,',
|
||||
'subscribers longtext,',
|
||||
'status varchar(12) NOT NULL,',
|
||||
'status varchar(12) NULL DEFAULT NULL,',
|
||||
'priority mediumint(9) NOT NULL DEFAULT 0,',
|
||||
'count_total mediumint(9) NOT NULL DEFAULT 0,',
|
||||
'count_processed mediumint(9) NOT NULL DEFAULT 0,',
|
||||
@ -265,4 +270,4 @@ class Migrator {
|
||||
|
||||
return implode("\n", $sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,129 +1,223 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Config\PopulatorData\Templates\SampleTemplate;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||
|
||||
class Populator {
|
||||
function __construct() {
|
||||
$this->prefix = Env::$db_prefix;
|
||||
$this->models = array(
|
||||
'newsletter_option_fields',
|
||||
'newsletter_templates',
|
||||
);
|
||||
}
|
||||
|
||||
function up() {
|
||||
global $wpdb;
|
||||
|
||||
$_this = $this;
|
||||
|
||||
$populate = function($model) use($_this, $wpdb) {
|
||||
$fields = $_this->$model();
|
||||
$table = $_this->prefix . $model;
|
||||
|
||||
array_map(function($field) use ($wpdb, $table) {
|
||||
$column_conditions = array_map(function($key) use ($field) {
|
||||
return $key . '=' . $field[$key];
|
||||
}, $field);
|
||||
if ($wpdb->get_var("SELECT COUNT(*) FROM " . $table . " WHERE " . implode(' AND ', $column_conditions)) === 0) {
|
||||
$wpdb->insert(
|
||||
$table,
|
||||
$field
|
||||
);
|
||||
}
|
||||
}, $fields);
|
||||
};
|
||||
|
||||
array_map(array($this, 'populate'), $this->models);
|
||||
}
|
||||
|
||||
function newsletter_option_fields() {
|
||||
return array(
|
||||
array(
|
||||
'name' => 'event',
|
||||
'newsletter_type' => 'welcome',
|
||||
),
|
||||
array(
|
||||
'name' => 'segment',
|
||||
'newsletter_type' => 'welcome',
|
||||
),
|
||||
array(
|
||||
'name' => 'role',
|
||||
'newsletter_type' => 'welcome',
|
||||
),
|
||||
array(
|
||||
'name' => 'afterTimeNumber',
|
||||
'newsletter_type' => 'welcome',
|
||||
),
|
||||
array(
|
||||
'name' => 'afterTimeType',
|
||||
'newsletter_type' => 'welcome',
|
||||
),
|
||||
|
||||
array(
|
||||
'name' => 'intervalType',
|
||||
'newsletter_type' => 'notification',
|
||||
),
|
||||
array(
|
||||
'name' => 'timeOfDay',
|
||||
'newsletter_type' => 'notification',
|
||||
),
|
||||
array(
|
||||
'name' => 'weekDay',
|
||||
'newsletter_type' => 'notification',
|
||||
),
|
||||
array(
|
||||
'name' => 'monthDay',
|
||||
'newsletter_type' => 'notification',
|
||||
),
|
||||
array(
|
||||
'name' => 'nthWeekDay',
|
||||
'newsletter_type' => 'notification',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private function newsletter_templates() {
|
||||
return array(
|
||||
(new SampleTemplate(Env::$assets_url))->get(),
|
||||
);
|
||||
}
|
||||
|
||||
private function populate($model) {
|
||||
$rows = $this->$model();
|
||||
$table = $this->prefix . $model;
|
||||
$_this = $this;
|
||||
|
||||
array_map(function($row) use ($_this, $table) {
|
||||
if (!$_this->rowExists($table, $row)) {
|
||||
$_this->insertRow($table, $row);
|
||||
}
|
||||
}, $rows);
|
||||
}
|
||||
|
||||
private function rowExists($table, $columns) {
|
||||
global $wpdb;
|
||||
|
||||
$conditions = array_map(function($key) use ($columns) {
|
||||
return $key . '=%s';
|
||||
}, array_keys($columns));
|
||||
|
||||
return $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $table WHERE " . implode(' AND ', $conditions),
|
||||
array_values($columns)
|
||||
)) > 0;
|
||||
}
|
||||
|
||||
private function insertRow($table, $row) {
|
||||
global $wpdb;
|
||||
|
||||
return $wpdb->insert(
|
||||
$table,
|
||||
$row
|
||||
);
|
||||
}
|
||||
}
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Config\PopulatorData\Templates\FranksRoastHouseTemplate;
|
||||
use MailPoet\Config\PopulatorData\Templates\BlankTemplate;
|
||||
use MailPoet\Config\PopulatorData\Templates\WelcomeTemplate;
|
||||
use MailPoet\Config\PopulatorData\Templates\PostNotificationsBlankTemplate;
|
||||
use \MailPoet\Models\Segment;
|
||||
use \MailPoet\Segments\WP;
|
||||
use \MailPoet\Models\Setting;
|
||||
use \MailPoet\Settings\Pages;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||
|
||||
class Populator {
|
||||
function __construct() {
|
||||
$this->prefix = Env::$db_prefix;
|
||||
$this->models = array(
|
||||
'newsletter_option_fields',
|
||||
'newsletter_templates',
|
||||
);
|
||||
}
|
||||
|
||||
function up() {
|
||||
global $wpdb;
|
||||
|
||||
$_this = $this;
|
||||
|
||||
$populate = function($model) use($_this, $wpdb) {
|
||||
$fields = $_this->$model();
|
||||
$table = $_this->prefix . $model;
|
||||
|
||||
array_map(function($field) use ($wpdb, $table) {
|
||||
$column_conditions = array_map(function($key) use ($field) {
|
||||
return $key . '=' . $field[$key];
|
||||
}, $field);
|
||||
if ($wpdb->get_var("SELECT COUNT(*) FROM " . $table . " WHERE " . implode(' AND ', $column_conditions)) === 0) {
|
||||
$wpdb->insert(
|
||||
$table,
|
||||
$field
|
||||
);
|
||||
}
|
||||
}, $fields);
|
||||
};
|
||||
|
||||
array_map(array($this, 'populate'), $this->models);
|
||||
|
||||
$this->createDefaultSegments();
|
||||
$this->createDefaultSettings();
|
||||
$this->createMailPoetPage();
|
||||
}
|
||||
|
||||
private function createMailPoetPage() {
|
||||
$pages = get_posts(array(
|
||||
'posts_per_page' => 1,
|
||||
'orderby' => 'date',
|
||||
'order' => 'DESC',
|
||||
'post_type' => 'mailpoet_page'
|
||||
));
|
||||
|
||||
$page = null;
|
||||
if(!empty($pages)) {
|
||||
$page = array_shift($pages);
|
||||
if(strpos($page->post_content, '[mailpoet_page]') === false) {
|
||||
$page = null;
|
||||
}
|
||||
}
|
||||
|
||||
if($page === null) {
|
||||
$mailpoet_page_id = Pages::createMailPoetPage();
|
||||
Setting::setValue('subscription.page', $mailpoet_page_id);
|
||||
}
|
||||
}
|
||||
|
||||
private function createDefaultSettings() {
|
||||
$current_user = wp_get_current_user();
|
||||
|
||||
// user name
|
||||
$user_name = '';
|
||||
if($current_user->user_firstname) {
|
||||
$user_name = $current_user->user_firstname;
|
||||
}
|
||||
if($current_user->user_lastname) {
|
||||
if($user_name) {
|
||||
$user_name .= ' '.$current_user->user_lastname;
|
||||
}
|
||||
}
|
||||
if(!$user_name) {
|
||||
$user_name = $current_user->display_name;
|
||||
}
|
||||
|
||||
// default from name & address
|
||||
Setting::setValue('sender', array(
|
||||
'name' => $user_name,
|
||||
'address' => $current_user->user_email
|
||||
));
|
||||
|
||||
// enable signup confirmation by default
|
||||
Setting::setValue('signup_confirmation.enabled', true);
|
||||
}
|
||||
|
||||
private function createDefaultSegments() {
|
||||
// WP Users segment
|
||||
$wp_users_segment = Segment::getWPUsers();
|
||||
|
||||
if($wp_users_segment === false) {
|
||||
// create the wp users list
|
||||
$wp_users_segment = Segment::create();
|
||||
$wp_users_segment->hydrate(array(
|
||||
'name' => __('WordPress Users'),
|
||||
'description' =>
|
||||
__('The list containing all of your WordPress users.'),
|
||||
'type' => 'wp_users'
|
||||
));
|
||||
$wp_users_segment->save();
|
||||
}
|
||||
|
||||
// Synchronize WP Users
|
||||
WP::synchronizeUsers();
|
||||
|
||||
// Default segment
|
||||
if(Segment::where('type', 'default')->count() === 0) {
|
||||
$default_segment = Segment::create();
|
||||
$default_segment->hydrate(array(
|
||||
'name' => __('My First List'),
|
||||
'description' =>
|
||||
__('The list created automatically on install of MailPoet')
|
||||
));
|
||||
$default_segment->save();
|
||||
}
|
||||
}
|
||||
|
||||
function newsletter_option_fields() {
|
||||
return array(
|
||||
array(
|
||||
'name' => 'event',
|
||||
'newsletter_type' => 'welcome',
|
||||
),
|
||||
array(
|
||||
'name' => 'segment',
|
||||
'newsletter_type' => 'welcome',
|
||||
),
|
||||
array(
|
||||
'name' => 'role',
|
||||
'newsletter_type' => 'welcome',
|
||||
),
|
||||
array(
|
||||
'name' => 'afterTimeNumber',
|
||||
'newsletter_type' => 'welcome',
|
||||
),
|
||||
array(
|
||||
'name' => 'afterTimeType',
|
||||
'newsletter_type' => 'welcome',
|
||||
),
|
||||
|
||||
array(
|
||||
'name' => 'intervalType',
|
||||
'newsletter_type' => 'notification',
|
||||
),
|
||||
array(
|
||||
'name' => 'timeOfDay',
|
||||
'newsletter_type' => 'notification',
|
||||
),
|
||||
array(
|
||||
'name' => 'weekDay',
|
||||
'newsletter_type' => 'notification',
|
||||
),
|
||||
array(
|
||||
'name' => 'monthDay',
|
||||
'newsletter_type' => 'notification',
|
||||
),
|
||||
array(
|
||||
'name' => 'nthWeekDay',
|
||||
'newsletter_type' => 'notification',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private function newsletter_templates() {
|
||||
return array(
|
||||
(new FranksRoastHouseTemplate(Env::$assets_url))->get(),
|
||||
(new BlankTemplate(Env::$assets_url))->get(),
|
||||
(new WelcomeTemplate(Env::$assets_url))->get(),
|
||||
(new PostNotificationsBlankTemplate(Env::$assets_url))->get(),
|
||||
);
|
||||
}
|
||||
|
||||
private function populate($model) {
|
||||
$rows = $this->$model();
|
||||
$table = $this->prefix . $model;
|
||||
$_this = $this;
|
||||
|
||||
array_map(function($row) use ($_this, $table) {
|
||||
if (!$_this->rowExists($table, $row)) {
|
||||
$_this->insertRow($table, $row);
|
||||
}
|
||||
}, $rows);
|
||||
}
|
||||
|
||||
private function rowExists($table, $columns) {
|
||||
global $wpdb;
|
||||
|
||||
$conditions = array_map(function($key) use ($columns) {
|
||||
return $key . '=%s';
|
||||
}, array_keys($columns));
|
||||
|
||||
return $wpdb->get_var($wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $table WHERE " . implode(' AND ', $conditions),
|
||||
array_values($columns)
|
||||
)) > 0;
|
||||
}
|
||||
|
||||
private function insertRow($table, $row) {
|
||||
global $wpdb;
|
||||
|
||||
return $wpdb->insert(
|
||||
$table,
|
||||
$row
|
||||
);
|
||||
}
|
||||
}
|
||||
|
213
lib/Config/PopulatorData/Templates/BlankTemplate.php
Normal file
213
lib/Config/PopulatorData/Templates/BlankTemplate.php
Normal file
File diff suppressed because one or more lines are too long
350
lib/Config/PopulatorData/Templates/FranksRoastHouseTemplate.php
Normal file
350
lib/Config/PopulatorData/Templates/FranksRoastHouseTemplate.php
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
295
lib/Config/PopulatorData/Templates/WelcomeTemplate.php
Normal file
295
lib/Config/PopulatorData/Templates/WelcomeTemplate.php
Normal file
File diff suppressed because one or more lines are too long
@ -1,21 +1,26 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Queue\Daemon;
|
||||
use MailPoet\Cron\Daemon;
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class PublicAPI {
|
||||
public $api;
|
||||
public $section;
|
||||
public $action;
|
||||
public $request_payload;
|
||||
|
||||
function __construct() {
|
||||
# http://example.com/?mailpoet-api§ion=&action=&payload=
|
||||
# http://example.com/?mailpoet-api§ion=&action=&request_payload=
|
||||
$this->api = isset($_GET['mailpoet-api']) ? true : false;
|
||||
$this->section = isset($_GET['section']) ? $_GET['section'] : false;
|
||||
$this->action = isset($_GET['action']) ?
|
||||
Helpers::underscoreToCamelCase($_GET['action']) :
|
||||
false;
|
||||
$this->payload = isset($_GET['payload']) ?
|
||||
json_decode(urldecode($_GET['payload']), true) :
|
||||
$this->request_payload = isset($_GET['request_payload']) ?
|
||||
unserialize(base64_decode($_GET['request_payload'])) :
|
||||
false;
|
||||
}
|
||||
|
||||
@ -25,8 +30,11 @@ class PublicAPI {
|
||||
}
|
||||
|
||||
function queue() {
|
||||
$queue = new Daemon($this->payload);
|
||||
$this->_checkAndCallMethod($queue, $this->action);
|
||||
try {
|
||||
$queue = new Daemon($this->request_payload);
|
||||
$this->_checkAndCallMethod($queue, $this->action);
|
||||
} catch(\Exception $e) {
|
||||
}
|
||||
}
|
||||
|
||||
private function _checkAndCallMethod($class, $method, $terminate = false) {
|
||||
|
121
lib/Config/Shortcodes.php
Normal file
121
lib/Config/Shortcodes.php
Normal file
@ -0,0 +1,121 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
use \MailPoet\Models\Newsletter;
|
||||
use \MailPoet\Models\Subscriber;
|
||||
use \MailPoet\Models\SubscriberSegment;
|
||||
|
||||
class Shortcodes {
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
function init() {
|
||||
// form widget shortcode
|
||||
add_shortcode('mailpoet_form', array($this, 'formWidget'));
|
||||
add_shortcode('wysija_form', array($this, 'formWidget'));
|
||||
|
||||
// subscribers count shortcode
|
||||
add_shortcode('mailpoet_subscribers_count', array(
|
||||
$this, 'getSubscribersCount'
|
||||
));
|
||||
add_shortcode('wysija_subscribers_count', array(
|
||||
$this, 'getSubscribersCount'
|
||||
));
|
||||
|
||||
// archives page
|
||||
add_shortcode('mailpoet_archive', array(
|
||||
$this, 'getArchive'
|
||||
));
|
||||
|
||||
add_filter('mailpoet_archive_date', array(
|
||||
$this, 'renderArchiveDate'
|
||||
), 2);
|
||||
add_filter('mailpoet_archive_subject', array(
|
||||
$this, 'renderArchiveSubject'
|
||||
), 2);
|
||||
}
|
||||
|
||||
function formWidget($params = array()) {
|
||||
// IMPORTANT: fixes conflict with MagicMember
|
||||
remove_shortcode('user_list');
|
||||
|
||||
if(isset($params['id']) && (int)$params['id'] > 0) {
|
||||
$form_widget = new \MailPoet\Form\Widget();
|
||||
return $form_widget->widget(array(
|
||||
'form' => (int)$params['id'],
|
||||
'form_type' => 'shortcode'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function getSubscribersCount($params) {
|
||||
if(!empty($params['segments'])) {
|
||||
$segment_ids = array_map(function($segment_id) {
|
||||
return (int)trim($segment_id);
|
||||
}, explode(',', $params['segments']));
|
||||
}
|
||||
|
||||
if(empty($segment_ids)) {
|
||||
return Subscriber::filter('subscribed')->count();
|
||||
} else {
|
||||
return SubscriberSegment::whereIn('segment_id', $segment_ids)
|
||||
->select('subscriber_id')->distinct()
|
||||
->filter('subscribed')
|
||||
->findResultSet()->count();
|
||||
}
|
||||
}
|
||||
|
||||
function getArchive($params) {
|
||||
if(!empty($params['segments'])) {
|
||||
$segment_ids = array_map(function($segment_id) {
|
||||
return (int)trim($segment_id);
|
||||
}, explode(',', $params['segments']));
|
||||
}
|
||||
|
||||
$newsletters = array();
|
||||
$html = '';
|
||||
|
||||
// TODO: needs more advanced newsletters in order to finish
|
||||
$newsletters = Newsletter::limit(10)->orderByDesc('created_at')->findMany();
|
||||
|
||||
if(empty($newsletters)) {
|
||||
return apply_filters(
|
||||
'mailpoet_archive_no_newsletters',
|
||||
__('Oops! There are no newsletters to display.')
|
||||
);
|
||||
} else {
|
||||
$title = apply_filters('mailpoet_archive_title', '');
|
||||
if(!empty($title)) {
|
||||
$html .= '<h3 class="mailpoet_archive_title">'.$title.'</h3>';
|
||||
}
|
||||
|
||||
$html .= '<ul class="mailpoet_archive">';
|
||||
foreach($newsletters as $newsletter) {
|
||||
$html .= '<li>'.
|
||||
'<span class="mailpoet_archive_date">'.
|
||||
apply_filters('mailpoet_archive_date', $newsletter).
|
||||
'</span>
|
||||
<span class="mailpoet_archive_subject">'.
|
||||
apply_filters('mailpoet_archive_subject', $newsletter).
|
||||
'</span>
|
||||
</li>';
|
||||
}
|
||||
$html .= '</ul>';
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
function renderArchiveDate($newsletter) {
|
||||
return date_i18n(
|
||||
get_option('date_format'),
|
||||
strtotime($newsletter->created_at)
|
||||
);
|
||||
}
|
||||
|
||||
function renderArchiveSubject($newsletter) {
|
||||
return '<a href="TODO" target="_blank" title="'
|
||||
.esc_attr(__('Preview in new tab')).'">'
|
||||
.esc_attr($newsletter->subject).
|
||||
'</a>';
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
use \MailPoet\Models\Subscriber;
|
||||
use \MailPoet\Util\Security;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
@ -22,18 +21,6 @@ class Widget {
|
||||
|
||||
function registerWidget() {
|
||||
register_widget('\MailPoet\Form\Widget');
|
||||
|
||||
// subscribers count shortcode
|
||||
add_shortcode('mailpoet_subscribers_count', array(
|
||||
$this, 'getSubscribersCount'
|
||||
));
|
||||
add_shortcode('wysija_subscribers_count', array(
|
||||
$this, 'getSubscribersCount'
|
||||
));
|
||||
}
|
||||
|
||||
function getSubscribersCount($params) {
|
||||
return Subscriber::filter('subscribed')->count();
|
||||
}
|
||||
|
||||
function setupDependencies() {
|
||||
@ -61,19 +48,25 @@ class Widget {
|
||||
}
|
||||
|
||||
function setupAdminDependencies() {
|
||||
wp_enqueue_script('mailpoet_vendor',
|
||||
Env::$assets_url.'/js/vendor.js',
|
||||
array(),
|
||||
Env::$version,
|
||||
true
|
||||
);
|
||||
if(
|
||||
empty($_GET['page'])
|
||||
or
|
||||
isset($_GET['page']) && strpos($_GET['page'], 'mailpoet') === false
|
||||
) {
|
||||
wp_enqueue_script('mailpoet_vendor',
|
||||
Env::$assets_url.'/js/vendor.js',
|
||||
array(),
|
||||
Env::$version,
|
||||
true
|
||||
);
|
||||
|
||||
wp_enqueue_script('mailpoet_admin',
|
||||
Env::$assets_url.'/js/mailpoet.js',
|
||||
array(),
|
||||
Env::$version,
|
||||
true
|
||||
);
|
||||
wp_enqueue_script('mailpoet_admin',
|
||||
Env::$assets_url.'/js/mailpoet.js',
|
||||
array(),
|
||||
Env::$version,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function setupActions() {
|
||||
|
@ -1,33 +1,33 @@
|
||||
<?php
|
||||
namespace MailPoet\Queue;
|
||||
namespace MailPoet\Cron;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use MailPoet\Models\Queue;
|
||||
use MailPoet\Models\Setting;
|
||||
|
||||
class BootStrapMenu {
|
||||
function __construct() {
|
||||
$this->daemon = Setting::where('name', 'daemon')
|
||||
$this->daemon = Setting::where('name', 'cron_daemon')
|
||||
->findOne();
|
||||
}
|
||||
|
||||
function bootStrap() {
|
||||
$queues = Queue::findMany();
|
||||
return ($this->daemon) ?
|
||||
array_merge(
|
||||
array(
|
||||
'time_since_start' =>
|
||||
'timeSinceStart' =>
|
||||
Carbon::createFromFormat(
|
||||
'Y-m-d H:i:s',
|
||||
$this->daemon->created_at,
|
||||
'UTC'
|
||||
)->diffForHumans(),
|
||||
'time_since_update' =>
|
||||
)
|
||||
->diffForHumans(),
|
||||
'timeSinceUpdate' =>
|
||||
Carbon::createFromFormat(
|
||||
'Y-m-d H:i:s',
|
||||
$this->daemon->updated_at,
|
||||
'UTC'
|
||||
)->diffForHumans()
|
||||
)
|
||||
->diffForHumans()
|
||||
),
|
||||
json_decode($this->daemon->value, true)
|
||||
) :
|
70
lib/Cron/CronHelper.php
Normal file
70
lib/Cron/CronHelper.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
namespace MailPoet\Cron;
|
||||
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Util\Security;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class CronHelper {
|
||||
static $daemon_execution_limit = 30;
|
||||
static $daemon_timeout_limit = 30;
|
||||
|
||||
static function createDaemon($token) {
|
||||
$daemon = array(
|
||||
'status' => 'starting',
|
||||
'counter' => 0,
|
||||
'token' => $token
|
||||
);
|
||||
self::saveDaemon($daemon);
|
||||
return $daemon;
|
||||
}
|
||||
|
||||
static function getDaemon() {
|
||||
return Setting::getValue('cron_daemon');
|
||||
}
|
||||
|
||||
static function saveDaemon($daemon) {
|
||||
$daemon['updated_at'] = time();
|
||||
return Setting::setValue(
|
||||
'cron_daemon',
|
||||
$daemon
|
||||
);
|
||||
}
|
||||
|
||||
static function createToken() {
|
||||
return Security::generateRandomString();
|
||||
}
|
||||
|
||||
static function accessDaemon($token) {
|
||||
$payload = serialize(array('token' => $token));
|
||||
$url = '/?mailpoet-api§ion=queue&action=run&request_payload=' .
|
||||
base64_encode($payload);
|
||||
$args = array(
|
||||
'timeout' => 1,
|
||||
'user-agent' => 'MailPoet (www.mailpoet.com) Cron'
|
||||
);
|
||||
$result = wp_remote_get(
|
||||
self::getSiteUrl() . $url,
|
||||
$args
|
||||
);
|
||||
return wp_remote_retrieve_body($result);
|
||||
}
|
||||
|
||||
private static function getSiteUrl() {
|
||||
// additional check for some sites running on a virtual machine or behind
|
||||
// proxy where there could be different ports (e.g., host:8080 => guest:80)
|
||||
|
||||
// if the site URL does not contain a port, return the URL
|
||||
if(!preg_match('!^https?://.*?:\d+!', site_url())) return site_url();
|
||||
preg_match('!://(?P<host>.*?):(?P<port>\d+)!', site_url(), $server);
|
||||
// connect to the URL with port
|
||||
$fp = @fsockopen($server['host'], $server['port'], $errno, $errstr, 1);
|
||||
if($fp) return site_url();
|
||||
// connect to the URL without port
|
||||
$fp = @fsockopen($server['host'], $server['port'], $errno, $errstr, 1);
|
||||
if($fp) return preg_replace('!(?=:\d+):\d+!', '$1', site_url());
|
||||
// throw an error if all connection attempts failed
|
||||
throw new \Exception(__('Site URL is unreachable.'));
|
||||
}
|
||||
}
|
78
lib/Cron/Daemon.php
Normal file
78
lib/Cron/Daemon.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
namespace MailPoet\Cron;
|
||||
|
||||
use MailPoet\Cron\Workers\SendingQueue;
|
||||
|
||||
require_once(ABSPATH . 'wp-includes/pluggable.php');
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Daemon {
|
||||
public $daemon;
|
||||
public $request_payload;
|
||||
public $refreshed_token;
|
||||
private $timer;
|
||||
|
||||
function __construct($request_payload = array()) {
|
||||
set_time_limit(0);
|
||||
ignore_user_abort();
|
||||
$this->daemon = CronHelper::getDaemon();
|
||||
$this->token = CronHelper::createToken();
|
||||
$this->request_payload = $request_payload;
|
||||
$this->timer = microtime(true);
|
||||
}
|
||||
|
||||
function run() {
|
||||
$daemon = $this->daemon;
|
||||
if(!$daemon) {
|
||||
$this->abortWithError(__('Daemon does not exist.'));
|
||||
}
|
||||
if(!isset($this->request_payload['token']) ||
|
||||
$this->request_payload['token'] !== $daemon['token']
|
||||
) {
|
||||
$this->abortWithError(__('Invalid or missing token.'));
|
||||
}
|
||||
$this->abortIfStopped($daemon);
|
||||
try {
|
||||
$sending_queue = new SendingQueue($this->timer);
|
||||
$sending_queue->process();
|
||||
} catch(Exception $e) {
|
||||
}
|
||||
$elapsed_time = microtime(true) - $this->timer;
|
||||
if($elapsed_time < CronHelper::$daemon_execution_limit) {
|
||||
sleep(CronHelper::$daemon_execution_limit - $elapsed_time);
|
||||
}
|
||||
// after each execution, re-read daemon data in case it was deleted or
|
||||
// its status has changed
|
||||
$daemon = CronHelper::getDaemon();
|
||||
if(!$daemon || $daemon['token'] !== $this->request_payload['token']) {
|
||||
exit;
|
||||
}
|
||||
$daemon['counter']++;
|
||||
$this->abortIfStopped($daemon);
|
||||
if($daemon['status'] === 'starting') {
|
||||
$daemon['status'] = 'started';
|
||||
}
|
||||
$daemon['token'] = $this->token;
|
||||
CronHelper::saveDaemon($daemon);
|
||||
$this->callSelf();
|
||||
}
|
||||
|
||||
function abortIfStopped($daemon) {
|
||||
if($daemon['status'] === 'stopped') exit;
|
||||
if($daemon['status'] === 'stopping') {
|
||||
$daemon['status'] = 'stopped';
|
||||
CronHelper::saveDaemon($daemon);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
function abortWithError($message) {
|
||||
exit('[mailpoet_cron_error:' . base64_encode($message) . ']');
|
||||
}
|
||||
|
||||
function callSelf() {
|
||||
CronHelper::accessDaemon($this->token);
|
||||
exit;
|
||||
}
|
||||
}
|
94
lib/Cron/Supervisor.php
Normal file
94
lib/Cron/Supervisor.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
namespace MailPoet\Cron;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Supervisor {
|
||||
public $daemon;
|
||||
public $token;
|
||||
public $force_run;
|
||||
|
||||
function __construct($force_run = false) {
|
||||
$this->daemon = CronHelper::getDaemon();
|
||||
$this->token = CronHelper::createToken();
|
||||
$this->force_run = $force_run;
|
||||
}
|
||||
|
||||
function checkDaemon() {
|
||||
$daemon = $this->daemon;
|
||||
if(!$daemon) {
|
||||
$daemon = CronHelper::createDaemon($this->token);
|
||||
return $this->runDaemon($daemon);
|
||||
}
|
||||
// if the daemon is stopped, return its status and do nothing
|
||||
if(!$this->force_run &&
|
||||
isset($daemon['status']) &&
|
||||
$daemon['status'] === 'stopped'
|
||||
) {
|
||||
return $this->formatDaemonStatusMessage($daemon['status']);
|
||||
|
||||
}
|
||||
$elapsed_time = time() - (int) $daemon['updated_at'];
|
||||
// if it's been less than 40 seconds since last execution and we're not
|
||||
// force-running the daemon, return its status and do nothing
|
||||
if($elapsed_time < CronHelper::$daemon_timeout_limit && !$this->force_run) {
|
||||
return $this->formatDaemonStatusMessage($daemon['status']);
|
||||
}
|
||||
// if it's been less than 40 seconds since last execution, we are
|
||||
// force-running the daemon and it's either being started or stopped,
|
||||
// return its status and do nothing
|
||||
elseif($elapsed_time < CronHelper::$daemon_timeout_limit &&
|
||||
$this->force_run &&
|
||||
in_array($daemon['status'], array(
|
||||
'stopping',
|
||||
'starting'
|
||||
))
|
||||
) {
|
||||
return $this->formatDaemonStatusMessage($daemon['status']);
|
||||
}
|
||||
// re-create (restart) daemon
|
||||
CronHelper::createDaemon($this->token);
|
||||
return $this->runDaemon();
|
||||
}
|
||||
|
||||
function runDaemon() {
|
||||
$request = CronHelper::accessDaemon($this->token);
|
||||
preg_match('/\[(mailpoet_cron_error:.*?)\]/i', $request, $status);
|
||||
$daemon = CronHelper::getDaemon();
|
||||
if(!empty($status) || !$daemon) {
|
||||
if(!$daemon) {
|
||||
$message = __('Daemon failed to run.');
|
||||
} else {
|
||||
list(, $message) = explode(':', $status[0]);
|
||||
$message = base64_decode($message);
|
||||
}
|
||||
return $this->formatResult(
|
||||
false,
|
||||
$message
|
||||
);
|
||||
}
|
||||
return $this->formatDaemonStatusMessage($daemon['status']);
|
||||
}
|
||||
|
||||
private function formatDaemonStatusMessage($status) {
|
||||
return $this->formatResultMessage(
|
||||
true,
|
||||
sprintf(
|
||||
__('Daemon is currently %s.'),
|
||||
__($status)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function formatResultMessage($result, $message) {
|
||||
$formattedResult = array(
|
||||
'result' => $result
|
||||
);
|
||||
if(!$result) {
|
||||
$formattedResult['errors'] = array($message);
|
||||
} else {
|
||||
$formattedResult['message'] = $message;
|
||||
}
|
||||
return $formattedResult;
|
||||
}
|
||||
}
|
139
lib/Cron/Workers/SendingQueue.php
Normal file
139
lib/Cron/Workers/SendingQueue.php
Normal file
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
namespace MailPoet\Cron\Workers;
|
||||
|
||||
use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Mailer\Mailer;
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\NewsletterStatistics;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Newsletter\Renderer\Renderer;
|
||||
use MailPoet\Newsletter\Shortcodes\Shortcodes;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class SendingQueue {
|
||||
private $timer;
|
||||
|
||||
function __construct($timer = false) {
|
||||
$this->timer = ($timer) ? $timer : microtime(true);
|
||||
}
|
||||
|
||||
function process() {
|
||||
// TODO: implement mailer sending frequency limits
|
||||
foreach($this->getQueues() as $queue) {
|
||||
$newsletter = Newsletter::findOne($queue->newsletter_id)
|
||||
->asArray();
|
||||
if(!$newsletter) {
|
||||
continue;
|
||||
};
|
||||
$mailer = $this->configureMailerForNewsletter($newsletter);
|
||||
$subscribers = json_decode($queue->subscribers, true);
|
||||
$subscribers_to_process = $subscribers['to_process'];
|
||||
if(!isset($subscribers['processed'])) $subscribers['processed'] = array();
|
||||
if(!isset($subscribers['failed'])) $subscribers['failed'] = array();
|
||||
foreach(array_chunk($subscribers_to_process, 200) as $subscriber_ids) {
|
||||
$db_subscribers = Subscriber::whereIn('id', $subscriber_ids)
|
||||
->findArray();
|
||||
foreach($db_subscribers as $db_subscriber) {
|
||||
$this->checkExecutionTimer();
|
||||
$result = $this->sendNewsletter(
|
||||
$mailer,
|
||||
$this->processNewsletter($newsletter, $db_subscriber),
|
||||
$db_subscriber);
|
||||
if($result) {
|
||||
$this->updateStatistics($newsletter['id'], $db_subscriber['id'], $queue->id);
|
||||
$subscribers['processed'][] = $db_subscriber['id'];
|
||||
} else {
|
||||
$subscribers['failed'][] = $db_subscriber['id'];
|
||||
}
|
||||
$this->updateQueue($queue, $subscribers);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function processNewsletter($newsletter, $subscriber) {
|
||||
$rendered_newsletter = $this->renderNewsletter($newsletter);
|
||||
$shortcodes = new Shortcodes($rendered_newsletter['body']['html'], $newsletter, $subscriber);
|
||||
$processed_newsletter['body']['html'] = $shortcodes->replace();
|
||||
$shortcodes = new Shortcodes($rendered_newsletter['body']['text'], $newsletter, $subscriber);
|
||||
$processed_newsletter['body']['text'] = $shortcodes->replace();
|
||||
$processed_newsletter['subject'] = $rendered_newsletter['subject'];
|
||||
return $processed_newsletter;
|
||||
}
|
||||
|
||||
function sendNewsletter($mailer, $newsletter, $subscriber) {
|
||||
return $mailer->mailer_instance->send(
|
||||
$newsletter,
|
||||
$mailer->transformSubscriber($subscriber)
|
||||
);
|
||||
}
|
||||
|
||||
function updateStatistics($newsletter_id, $subscriber_id, $queue_id) {
|
||||
$newsletter_statistic = NewsletterStatistics::create();
|
||||
$newsletter_statistic->subscriber_id = $newsletter_id;
|
||||
$newsletter_statistic->newsletter_id = $subscriber_id;
|
||||
$newsletter_statistic->queue_id = $queue_id;
|
||||
$newsletter_statistic->save();
|
||||
}
|
||||
|
||||
function updateQueue($queue, $subscribers) {
|
||||
$subscribers['to_process'] = array_values(
|
||||
array_diff(
|
||||
$subscribers['to_process'],
|
||||
array_merge($subscribers['processed'], $subscribers['failed'])
|
||||
)
|
||||
);
|
||||
$queue->count_processed =
|
||||
count($subscribers['processed']) + count($subscribers['failed']);
|
||||
$queue->count_to_process = count($subscribers['to_process']);
|
||||
$queue->count_failed = count($subscribers['failed']);
|
||||
$queue->count_total =
|
||||
$queue->count_processed + $queue->count_to_process;
|
||||
if(!$queue->count_to_process) {
|
||||
$queue->processed_at = date('Y-m-d H:i:s');
|
||||
$queue->status = 'completed';
|
||||
}
|
||||
$queue->subscribers = json_encode($subscribers);
|
||||
$queue->save();
|
||||
}
|
||||
|
||||
function configureMailerForNewsletter($newsletter) {
|
||||
if(!empty($newsletter['sender_address']) && !empty($newsletter['sender_name'])) {
|
||||
$sender = array(
|
||||
'name' => $newsletter['sender_name'],
|
||||
'address' => $newsletter['sender_address']
|
||||
);
|
||||
} else {
|
||||
$sender = false;
|
||||
}
|
||||
if(!empty($newsletter['reply_to_address']) && !empty($newsletter['reply_to_name'])) {
|
||||
$reply_to = array(
|
||||
'name' => $newsletter['reply_to_name'],
|
||||
'address' => $newsletter['reply_to_address']
|
||||
);
|
||||
} else {
|
||||
$reply_to = false;
|
||||
}
|
||||
$mailer = new Mailer($method = false, $sender, $reply_to);
|
||||
return $mailer;
|
||||
}
|
||||
|
||||
function checkExecutionTimer() {
|
||||
$elapsed_time = microtime(true) - $this->timer;
|
||||
if($elapsed_time >= CronHelper::$daemon_execution_limit) throw new \Exception(__('Maximum execution time reached.'));
|
||||
}
|
||||
|
||||
function getQueues() {
|
||||
return \MailPoet\Models\SendingQueue::orderByDesc('priority')
|
||||
->whereNull('deleted_at')
|
||||
->whereNull('status')
|
||||
->findResultSet();
|
||||
}
|
||||
|
||||
function renderNewsletter($newsletter) {
|
||||
$renderer = new Renderer($newsletter);
|
||||
$newsletter['body'] = $renderer->render();
|
||||
return $newsletter;
|
||||
}
|
||||
}
|
@ -13,7 +13,9 @@ abstract class Base {
|
||||
if($block['id'] === 'segments') {
|
||||
$rules['required'] = true;
|
||||
$rules['mincheck'] = 1;
|
||||
$rules['error-message'] = __('You need to select a list');
|
||||
$rules['group'] = $block['id'];
|
||||
$rules['errors-container'] = '.mailpoet_error_'.$block['id'];
|
||||
$rules['required-message'] = __('You need to select a list');
|
||||
}
|
||||
|
||||
if(!empty($block['params']['required'])) {
|
||||
@ -29,7 +31,13 @@ abstract class Base {
|
||||
}
|
||||
}
|
||||
|
||||
$validation = '';
|
||||
if(in_array($block['type'], array('radio', 'checkbox'))) {
|
||||
$rules['group'] = 'custom_field_'.$block['id'];
|
||||
$rules['errors-container'] = '.mailpoet_error_'.$block['id'];
|
||||
$rules['required-message'] = __('You need to select at least one option.');
|
||||
}
|
||||
|
||||
$validation = array();
|
||||
|
||||
if(!empty($rules)) {
|
||||
$rules = array_unique($rules);
|
||||
@ -37,10 +45,10 @@ abstract class Base {
|
||||
if(is_bool($value)) {
|
||||
$value = ($value) ? 'true' : 'false';
|
||||
}
|
||||
$validation .= 'data-parsley-'.$rule.'="'.$value.'"';
|
||||
$validation[] = 'data-parsley-'.$rule.'="'.$value.'"';
|
||||
}
|
||||
}
|
||||
return $validation;
|
||||
return join(' ', $validation);
|
||||
}
|
||||
|
||||
protected static function renderLabel($block) {
|
||||
@ -86,7 +94,11 @@ abstract class Base {
|
||||
|
||||
// return field name depending on block data
|
||||
protected static function getFieldName($block = array()) {
|
||||
return $block['id'];
|
||||
if((int)$block['id'] > 0) {
|
||||
return 'cf_'.$block['id'];
|
||||
} else {
|
||||
return $block['id'];
|
||||
}
|
||||
}
|
||||
|
||||
protected static function getFieldLabel($block = array()) {
|
||||
@ -98,6 +110,6 @@ abstract class Base {
|
||||
protected static function getFieldValue($block = array()) {
|
||||
return (isset($block['params']['value'])
|
||||
&& strlen(trim($block['params']['value'])) > 0)
|
||||
? trim($block['params']['value']) : '';
|
||||
? esc_attr(trim($block['params']['value'])) : '';
|
||||
}
|
||||
}
|
@ -9,15 +9,16 @@ class Checkbox extends Base {
|
||||
$field_name = static::getFieldName($block);
|
||||
$field_validation = static::getInputValidation($block);
|
||||
|
||||
// TODO: check if it still makes sense
|
||||
// create hidden default value
|
||||
// $html .= '<input type="hidden"name="'.$field_name.'" value="0" '.static::getInputValidation($block).'/>';
|
||||
|
||||
$html .= '<p class="mailpoet_paragraph">';
|
||||
|
||||
$html .= static::renderLabel($block);
|
||||
|
||||
foreach($block['params']['values'] as $option) {
|
||||
$options = (!empty($block['params']['values'])
|
||||
? $block['params']['values']
|
||||
: array()
|
||||
);
|
||||
|
||||
foreach($options as $option) {
|
||||
$html .= '<label class="mailpoet_checkbox_label">';
|
||||
|
||||
$html .= '<input type="checkbox" class="mailpoet_checkbox" ';
|
||||
@ -35,6 +36,8 @@ class Checkbox extends Base {
|
||||
$html .= '</label>';
|
||||
}
|
||||
|
||||
$html .= '<span class="mailpoet_error_'.$block['id'].'"></span>';
|
||||
|
||||
$html .= '</p>';
|
||||
|
||||
return $html;
|
||||
|
@ -9,32 +9,35 @@ class Radio extends Base {
|
||||
$field_name = static::getFieldName($block);
|
||||
$field_validation = static::getInputValidation($block);
|
||||
|
||||
// TODO: check if it still makes sense
|
||||
// create hidden default value
|
||||
// $html .= '<input type="hidden"name="'.$field_name.'" value="0" '.static::getInputValidation($block).'/>';
|
||||
|
||||
$html .= '<p class="mailpoet_paragraph">';
|
||||
|
||||
$html .= static::renderLabel($block);
|
||||
|
||||
foreach($block['params']['values'] as $option) {
|
||||
$options = (!empty($block['params']['values'])
|
||||
? $block['params']['values']
|
||||
: array()
|
||||
);
|
||||
|
||||
foreach($options as $option) {
|
||||
$html .= '<label class="mailpoet_radio_label">';
|
||||
|
||||
$html .= '<input type="radio" class="mailpoet_radio" ';
|
||||
|
||||
$html .= 'name="'.$field_name.'" ';
|
||||
|
||||
$html .= 'value="'.$option['value'].'" ';
|
||||
$html .= 'value="'.esc_attr($option['value']).'" ';
|
||||
|
||||
$html .= (isset($option['is_checked']) && $option['is_checked'])
|
||||
? 'checked="checked"' : '';
|
||||
$html .= $field_validation;
|
||||
|
||||
$html .= ' /> '.$option['value'];
|
||||
$html .= ' /> '.esc_attr($option['value']);
|
||||
|
||||
$html .= '</label>';
|
||||
}
|
||||
|
||||
$html .= '<span class="mailpoet_error_'.$block['id'].'"></span>';
|
||||
|
||||
$html .= '</p>';
|
||||
|
||||
return $html;
|
||||
|
@ -13,23 +13,27 @@ class Segment extends Base {
|
||||
|
||||
$html .= static::renderLabel($block);
|
||||
|
||||
if(!empty($block['params']['values'])) {
|
||||
// display values
|
||||
foreach($block['params']['values'] as $segment) {
|
||||
if(!isset($segment['id']) || !isset($segment['name'])) continue;
|
||||
$options = (!empty($block['params']['values'])
|
||||
? $block['params']['values']
|
||||
: array()
|
||||
);
|
||||
|
||||
$is_checked = (isset($segment['is_checked']) && $segment['is_checked']) ? 'checked="checked"' : '';
|
||||
foreach($options as $option) {
|
||||
if(!isset($option['id']) || !isset($option['name'])) continue;
|
||||
|
||||
$html .= '<label class="mailpoet_checkbox_label">';
|
||||
$html .= '<input type="checkbox" class="mailpoet_checkbox" ';
|
||||
$html .= 'name="'.$field_name.'[]" ';
|
||||
$html .= 'value="'.$segment['id'].'" '.$is_checked.' ';
|
||||
$html .= $field_validation;
|
||||
$html .= ' />'.$segment['name'];
|
||||
$html .= '</label>';
|
||||
}
|
||||
$is_checked = (isset($option['is_checked']) && $option['is_checked']) ? 'checked="checked"' : '';
|
||||
|
||||
$html .= '<label class="mailpoet_checkbox_label">';
|
||||
$html .= '<input type="checkbox" class="mailpoet_checkbox" ';
|
||||
$html .= 'name="'.$field_name.'[]" ';
|
||||
$html .= 'value="'.$option['id'].'" '.$is_checked.' ';
|
||||
$html .= $field_validation;
|
||||
$html .= ' />'.$option['name'];
|
||||
$html .= '</label>';
|
||||
}
|
||||
|
||||
$html .= '<span class="mailpoet_error_'.$block['id'].'"></span>';
|
||||
|
||||
$html .= '</p>';
|
||||
|
||||
return $html;
|
||||
|
@ -6,11 +6,11 @@ class Submit extends Base {
|
||||
static function render($block) {
|
||||
$html = '';
|
||||
|
||||
$html .= '<input class="mailpoet_submit" type="submit" ';
|
||||
$html .= '<p class="mailpoet_submit"><input type="submit" ';
|
||||
|
||||
$html .= 'value="'.static::getFieldLabel($block).'" ';
|
||||
|
||||
$html .= '/>';
|
||||
$html .= '/></p>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\Form\Block;
|
||||
|
||||
class Input extends Base {
|
||||
class Text extends Base {
|
||||
|
||||
static function render($block) {
|
||||
$type = 'text';
|
||||
@ -15,7 +15,7 @@ class Input extends Base {
|
||||
|
||||
$html .= static::renderLabel($block);
|
||||
|
||||
$html .= '<input type="'.$type.'" class="mailpoet_input" ';
|
||||
$html .= '<input type="'.$type.'" class="mailpoet_text" ';
|
||||
|
||||
$html .= 'name="'.static::getFieldName($block).'" ';
|
||||
|
@ -49,7 +49,7 @@ class Renderer {
|
||||
|
||||
private static function renderBlock($block = array()) {
|
||||
$html = '';
|
||||
switch ($block['type']) {
|
||||
switch($block['type']) {
|
||||
case 'html':
|
||||
$html .= Block\Html::render($block);
|
||||
break;
|
||||
@ -78,8 +78,8 @@ class Renderer {
|
||||
$html .= Block\Select::render($block);
|
||||
break;
|
||||
|
||||
case 'input':
|
||||
$html .= Block\Input::render($block);
|
||||
case 'text':
|
||||
$html .= Block\Text::render($block);
|
||||
break;
|
||||
|
||||
case 'textarea':
|
||||
|
@ -36,9 +36,9 @@ class Widget extends \WP_Widget {
|
||||
|
||||
return parent::__construct(
|
||||
'mailpoet_form',
|
||||
__("MailPoet Subscription Form"),
|
||||
__('MailPoet Form'),
|
||||
array(
|
||||
'title' => __("Newsletter subscription form"),
|
||||
'description' => __('Add a newsletter subscription form.')
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -194,114 +194,4 @@ class Widget extends \WP_Widget {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// mailpoet shortcodes
|
||||
// form shortcode
|
||||
add_shortcode('mailpoet_form', 'mailpoet_form_shortcode');
|
||||
add_shortcode('wysija_form', 'mailpoet_form_shortcode');
|
||||
|
||||
function mailpoet_form_shortcode($params = array()) {
|
||||
// IMPORTANT: this is to make sure MagicMember won't scan our form and find [user_list] as a code they should replace.
|
||||
remove_shortcode('user_list');
|
||||
|
||||
if(isset($params['id']) && (int)$params['id'] > 0) {
|
||||
$form_widget = new \MailPoet\Form\Widget();
|
||||
return $form_widget->widget(array(
|
||||
'form' => (int)$params['id'],
|
||||
'form_type' => 'shortcode'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// set the content filter to replace the shortcode
|
||||
if(isset($_GET['mailpoet_page']) && strlen(trim($_GET['mailpoet_page'])) > 0) {
|
||||
switch($_GET['mailpoet_page']) {
|
||||
|
||||
case 'mailpoet_form_iframe':
|
||||
$id = (isset($_GET['mailpoet_form']) && (int)$_GET['mailpoet_form'] > 0) ? (int)$_GET['mailpoet_form'] : null;
|
||||
$form = Form::findOne($id);
|
||||
|
||||
if($form !== false) {
|
||||
// render form
|
||||
$output = Util\Export::get('html', $form->asArray());
|
||||
// $output = do_shortcode($output);
|
||||
print $output;
|
||||
exit;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// add_filter('wp_title', 'mailpoet_meta_page_title'));
|
||||
add_filter('the_title', 'mailpoet_page_title', 10, 2);
|
||||
add_filter('the_content', 'mailpoet_page_content', 98, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function mailpoet_page_title($title = '', $id = null) {
|
||||
// get signup confirmation page id
|
||||
$signup_confirmation = Setting::getValue('signup_confirmation');
|
||||
$page_id = $signup_confirmation['page'];
|
||||
|
||||
// check if we're on the signup confirmation page
|
||||
if((int)$page_id === (int)$id) {
|
||||
global $post;
|
||||
|
||||
// disable comments
|
||||
$post->comment_status = 'close';
|
||||
// disable password
|
||||
$post->post_password = '';
|
||||
|
||||
$subscriber = null;
|
||||
|
||||
// get subscriber key from url
|
||||
$subscriber_digest = (isset($_GET['mailpoet_key']) && strlen(trim($_GET['mailpoet_key'])) === 32) ? trim($_GET['mailpoet_key']) : null;
|
||||
|
||||
if($subscriber_digest !== null) {
|
||||
// get subscriber
|
||||
// TODO: change select() to selectOne() once it's implemented
|
||||
$subscribers = $mailpoet->subscribers()->select(array(
|
||||
'filter' => array(
|
||||
'subscriber_digest' => $subscriber_digest
|
||||
),
|
||||
'limit' => 1
|
||||
));
|
||||
|
||||
if(!empty($subscribers)) {
|
||||
$subscriber = array_shift($subscribers);
|
||||
}
|
||||
}
|
||||
|
||||
// check if we have a subscriber record
|
||||
if($subscriber === null) {
|
||||
return __('Your confirmation link expired, please subscribe again.');
|
||||
} else {
|
||||
// we have a subscriber, let's check its state
|
||||
switch($subscriber['subscriber_state']) {
|
||||
case MailPoetSubscribers::STATE_UNCONFIRMED:
|
||||
case MailPoetSubscribers::STATE_UNSUBSCRIBED:
|
||||
// set subscriber state as confirmed
|
||||
$mailpoet->subscribers()->update(array(
|
||||
'subscriber' => $subscriber['subscriber'],
|
||||
'subscriber_state' => MailPoetSubscribers::STATE_SUBSCRIBED,
|
||||
'subscriber_confirmed_at' => time()
|
||||
));
|
||||
return __("You've subscribed");
|
||||
break;
|
||||
case MailPoetSubscribers::STATE_SUBSCRIBED:
|
||||
return __("You've already subscribed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
|
||||
function mailpoet_page_content($content = '') {
|
||||
if(strpos($content, '[mailpoet_page]') !== FALSE) {
|
||||
$content = str_replace('[mailpoet_page]', '', $content);
|
||||
}
|
||||
return $content;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user