Compare commits
106 Commits
Author | SHA1 | Date | |
---|---|---|---|
d25070829d | |||
d194502b27 | |||
2cef99de2b | |||
05d3756a1c | |||
b3d9fc54fe | |||
20841eb5e8 | |||
9e23326d45 | |||
f81d639b19 | |||
ab3e272020 | |||
4c265d1339 | |||
da08de0e74 | |||
6074aa927b | |||
046127eeba | |||
0c8a8c6854 | |||
acfcfdd730 | |||
8f935df12a | |||
2784bb7282 | |||
08a8ca4969 | |||
5a03eb9a17 | |||
91bb215e4d | |||
0b3a388a78 | |||
66a93768e1 | |||
61df4899cd | |||
f8b1e153be | |||
f322433875 | |||
be155c38bc | |||
13ee338fb0 | |||
f8628c1f4e | |||
f7c70be5eb | |||
65df28d52e | |||
3e658033da | |||
31e082eb2b | |||
bf1ab3a593 | |||
8540c51679 | |||
d2a6b6bd4e | |||
06417c1e88 | |||
be238f4c67 | |||
343da0fdcc | |||
dbb3c96300 | |||
c8f7bea419 | |||
396ab50fa0 | |||
84e9ecef29 | |||
57472d1a2e | |||
cb4f055263 | |||
3f1bdd2c59 | |||
a6802a1925 | |||
a02b2d3aa0 | |||
607a151c23 | |||
c8748e5e6e | |||
6e45a8e788 | |||
395e95bc54 | |||
a8309b436d | |||
77fe385645 | |||
290f749220 | |||
8cb5b3729d | |||
ce4a3196ee | |||
40ea5a0b84 | |||
af95380a62 | |||
483eaffc0e | |||
7e86ee3266 | |||
65630e6726 | |||
23682011af | |||
00eaa768a6 | |||
bc92d9a61e | |||
a1d8dec047 | |||
ff030068b0 | |||
79e37018c9 | |||
7ae8248339 | |||
30720975ab | |||
b2359258d4 | |||
1bd7639cc2 | |||
2dab89135f | |||
c3368d69fd | |||
751d8e7852 | |||
8a4dec08e1 | |||
8b9f5ad5b1 | |||
fc597a53bb | |||
2f25dc6a20 | |||
fc38ee2f08 | |||
33bebc6629 | |||
14a8e02f99 | |||
0bf6c87ec7 | |||
422fba2835 | |||
f36dbb78e6 | |||
3213dd0d08 | |||
3f2199fd63 | |||
a4477a9bd6 | |||
52790d7bd6 | |||
0b9812210f | |||
756dbb4641 | |||
b38742ddc0 | |||
49e38549ec | |||
afcb0a0d7f | |||
d18f0e50b5 | |||
6868a07ead | |||
cca76d0d97 | |||
70fa77d333 | |||
412201d965 | |||
045a92c7d6 | |||
2ba2e3eca5 | |||
90c294f60e | |||
57b953dd14 | |||
0f81a8db60 | |||
2d6971f8df | |||
0abe8b5371 | |||
5bad682879 |
@ -1,6 +1,7 @@
|
||||
@import 'nib'
|
||||
|
||||
@require 'select2/dist/css/select2.css'
|
||||
@require 'datepicker/datepicker'
|
||||
|
||||
@require 'common'
|
||||
@require 'modal'
|
||||
@ -18,4 +19,4 @@
|
||||
@require 'settings'
|
||||
@require 'progress_bar'
|
||||
|
||||
@require 'subscribers'
|
||||
@require 'subscribers'
|
||||
|
2
assets/css/src/datepicker/datepicker.styl
Normal file
@ -0,0 +1,2 @@
|
||||
@require 'jquery-ui-1.10.1.css'
|
||||
@require 'melon.datepicker.css'
|
649
assets/css/src/datepicker/jquery-ui-1.10.1.css
vendored
Normal file
@ -0,0 +1,649 @@
|
||||
/*! jQuery UI - v1.10.1 - 2013-03-10
|
||||
* http://jqueryui.com
|
||||
* Includes: jquery.ui.core.css, jquery.ui.datepicker.css
|
||||
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS%2CTahoma%2CVerdana%2CArial%2Csans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=gloss_wave&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=highlight_soft&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=glass&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=glass&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=highlight_soft&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=diagonals_thick&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=diagonals_thick&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=flat&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px
|
||||
* Copyright (c) 2013 jQuery Foundation and other contributors Licensed MIT */
|
||||
|
||||
/* Layout helpers
|
||||
----------------------------------*/
|
||||
.ui-helper-hidden {
|
||||
display: none;
|
||||
}
|
||||
.ui-helper-hidden-accessible {
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.ui-helper-reset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
line-height: 1.3;
|
||||
text-decoration: none;
|
||||
font-size: 100%;
|
||||
list-style: none;
|
||||
}
|
||||
.ui-helper-clearfix:before,
|
||||
.ui-helper-clearfix:after {
|
||||
content: "";
|
||||
display: table;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.ui-helper-clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
.ui-helper-clearfix {
|
||||
min-height: 0; /* support: IE7 */
|
||||
}
|
||||
.ui-helper-zfix {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
filter:Alpha(Opacity=0);
|
||||
}
|
||||
|
||||
.ui-front {
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-disabled {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon {
|
||||
display: block;
|
||||
text-indent: -99999px;
|
||||
overflow: hidden;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.ui-datepicker {
|
||||
width: 17em;
|
||||
padding: .2em .2em 0;
|
||||
display: none;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-header {
|
||||
position: relative;
|
||||
padding: .2em 0;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev,
|
||||
.ui-datepicker .ui-datepicker-next {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
width: 1.8em;
|
||||
height: 1.8em;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev-hover,
|
||||
.ui-datepicker .ui-datepicker-next-hover {
|
||||
top: 1px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev {
|
||||
left: 2px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-next {
|
||||
right: 2px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev-hover {
|
||||
left: 1px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-next-hover {
|
||||
right: 1px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev span,
|
||||
.ui-datepicker .ui-datepicker-next span {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-left: -8px;
|
||||
top: 50%;
|
||||
margin-top: -8px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-title {
|
||||
margin: 0 2.3em;
|
||||
line-height: 1.8em;
|
||||
text-align: center;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-title select {
|
||||
font-size: 1em;
|
||||
margin: 1px 0;
|
||||
}
|
||||
.ui-datepicker select.ui-datepicker-month-year {
|
||||
width: 100%;
|
||||
}
|
||||
.ui-datepicker select.ui-datepicker-month,
|
||||
.ui-datepicker select.ui-datepicker-year {
|
||||
width: 49%;
|
||||
}
|
||||
.ui-datepicker table {
|
||||
width: 100%;
|
||||
font-size: .9em;
|
||||
border-collapse: collapse;
|
||||
margin: 0 0 .4em;
|
||||
}
|
||||
.ui-datepicker th {
|
||||
padding: .7em .3em;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
border: 0;
|
||||
}
|
||||
.ui-datepicker td {
|
||||
border: 0;
|
||||
padding: 1px;
|
||||
}
|
||||
.ui-datepicker td span,
|
||||
.ui-datepicker td a {
|
||||
display: block;
|
||||
padding: .2em;
|
||||
text-align: right;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-buttonpane {
|
||||
background-image: none;
|
||||
margin: .7em 0 0 0;
|
||||
padding: 0 .2em;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-buttonpane button {
|
||||
float: right;
|
||||
margin: .5em .2em .4em;
|
||||
cursor: pointer;
|
||||
padding: .2em .6em .3em .6em;
|
||||
width: auto;
|
||||
overflow: visible;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
|
||||
float: left;
|
||||
}
|
||||
|
||||
/* with multiple calendars */
|
||||
.ui-datepicker.ui-datepicker-multi {
|
||||
width: auto;
|
||||
}
|
||||
.ui-datepicker-multi .ui-datepicker-group {
|
||||
float: left;
|
||||
}
|
||||
.ui-datepicker-multi .ui-datepicker-group table {
|
||||
width: 95%;
|
||||
margin: 0 auto .4em;
|
||||
}
|
||||
.ui-datepicker-multi-2 .ui-datepicker-group {
|
||||
width: 50%;
|
||||
}
|
||||
.ui-datepicker-multi-3 .ui-datepicker-group {
|
||||
width: 33.3%;
|
||||
}
|
||||
.ui-datepicker-multi-4 .ui-datepicker-group {
|
||||
width: 25%;
|
||||
}
|
||||
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
|
||||
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
|
||||
border-left-width: 0;
|
||||
}
|
||||
.ui-datepicker-multi .ui-datepicker-buttonpane {
|
||||
clear: left;
|
||||
}
|
||||
.ui-datepicker-row-break {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
/* RTL support */
|
||||
.ui-datepicker-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-prev {
|
||||
right: 2px;
|
||||
left: auto;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-next {
|
||||
left: 2px;
|
||||
right: auto;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-prev:hover {
|
||||
right: 1px;
|
||||
left: auto;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-next:hover {
|
||||
left: 1px;
|
||||
right: auto;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane {
|
||||
clear: right;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button {
|
||||
float: left;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
|
||||
.ui-datepicker-rtl .ui-datepicker-group {
|
||||
float: right;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
|
||||
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
|
||||
border-right-width: 0;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
/* Component containers
|
||||
----------------------------------*/
|
||||
.ui-widget {
|
||||
font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.ui-widget .ui-widget {
|
||||
font-size: 1em;
|
||||
}
|
||||
.ui-widget input,
|
||||
.ui-widget select,
|
||||
.ui-widget textarea,
|
||||
.ui-widget button {
|
||||
font-family: Trebuchet MS,Tahoma,Verdana,Arial,sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
.ui-widget-content {
|
||||
border: 1px solid #dddddd;
|
||||
background: #eeeeee url(../img/datepicker/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x;
|
||||
color: #333333;
|
||||
}
|
||||
.ui-widget-content a {
|
||||
color: #333333;
|
||||
}
|
||||
.ui-widget-header {
|
||||
border: 1px solid #e78f08;
|
||||
background: #f6a828 url(../img/datepicker/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x;
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
}
|
||||
.ui-widget-header a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* Interaction states
|
||||
----------------------------------*/
|
||||
.ui-state-default,
|
||||
.ui-widget-content .ui-state-default,
|
||||
.ui-widget-header .ui-state-default {
|
||||
border: 1px solid #cccccc;
|
||||
background: #f6f6f6 url(../img/datepicker/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x;
|
||||
font-weight: bold;
|
||||
color: #1c94c4;
|
||||
}
|
||||
.ui-state-default a,
|
||||
.ui-state-default a:link,
|
||||
.ui-state-default a:visited {
|
||||
color: #1c94c4;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ui-state-hover,
|
||||
.ui-widget-content .ui-state-hover,
|
||||
.ui-widget-header .ui-state-hover,
|
||||
.ui-state-focus,
|
||||
.ui-widget-content .ui-state-focus,
|
||||
.ui-widget-header .ui-state-focus {
|
||||
border: 1px solid #fbcb09;
|
||||
background: #fdf5ce url(../img/datepicker/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x;
|
||||
font-weight: bold;
|
||||
color: #c77405;
|
||||
}
|
||||
.ui-state-hover a,
|
||||
.ui-state-hover a:hover,
|
||||
.ui-state-hover a:link,
|
||||
.ui-state-hover a:visited {
|
||||
color: #c77405;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ui-state-active,
|
||||
.ui-widget-content .ui-state-active,
|
||||
.ui-widget-header .ui-state-active {
|
||||
border: 1px solid #fbd850;
|
||||
background: #ffffff url(../img/datepicker/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;
|
||||
font-weight: bold;
|
||||
color: #eb8f00;
|
||||
}
|
||||
.ui-state-active a,
|
||||
.ui-state-active a:link,
|
||||
.ui-state-active a:visited {
|
||||
color: #eb8f00;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-highlight,
|
||||
.ui-widget-content .ui-state-highlight,
|
||||
.ui-widget-header .ui-state-highlight {
|
||||
border: 1px solid #fed22f;
|
||||
background: #ffe45c url(../img/datepicker/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x;
|
||||
color: #363636;
|
||||
}
|
||||
.ui-state-highlight a,
|
||||
.ui-widget-content .ui-state-highlight a,
|
||||
.ui-widget-header .ui-state-highlight a {
|
||||
color: #363636;
|
||||
}
|
||||
.ui-state-error,
|
||||
.ui-widget-content .ui-state-error,
|
||||
.ui-widget-header .ui-state-error {
|
||||
border: 1px solid #cd0a0a;
|
||||
background: #b81900 url(../img/datepicker/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat;
|
||||
color: #ffffff;
|
||||
}
|
||||
.ui-state-error a,
|
||||
.ui-widget-content .ui-state-error a,
|
||||
.ui-widget-header .ui-state-error a {
|
||||
color: #ffffff;
|
||||
}
|
||||
.ui-state-error-text,
|
||||
.ui-widget-content .ui-state-error-text,
|
||||
.ui-widget-header .ui-state-error-text {
|
||||
color: #ffffff;
|
||||
}
|
||||
.ui-priority-primary,
|
||||
.ui-widget-content .ui-priority-primary,
|
||||
.ui-widget-header .ui-priority-primary {
|
||||
font-weight: bold;
|
||||
}
|
||||
.ui-priority-secondary,
|
||||
.ui-widget-content .ui-priority-secondary,
|
||||
.ui-widget-header .ui-priority-secondary {
|
||||
opacity: .7;
|
||||
filter:Alpha(Opacity=70);
|
||||
font-weight: normal;
|
||||
}
|
||||
.ui-state-disabled,
|
||||
.ui-widget-content .ui-state-disabled,
|
||||
.ui-widget-header .ui-state-disabled {
|
||||
opacity: .35;
|
||||
filter:Alpha(Opacity=35);
|
||||
background-image: none;
|
||||
}
|
||||
.ui-state-disabled .ui-icon {
|
||||
filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
|
||||
}
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-position: 16px 16px;
|
||||
}
|
||||
.ui-icon,
|
||||
.ui-widget-content .ui-icon {
|
||||
background-image: url(../img/datepicker/ui-icons_222222_256x240.png);
|
||||
}
|
||||
.ui-widget-header .ui-icon {
|
||||
background-image: url(../img/datepicker/ui-icons_ffffff_256x240.png);
|
||||
}
|
||||
.ui-state-default .ui-icon {
|
||||
background-image: url(../img/datepicker/ui-icons_ef8c08_256x240.png);
|
||||
}
|
||||
.ui-state-hover .ui-icon,
|
||||
.ui-state-focus .ui-icon {
|
||||
background-image: url(../img/datepicker/ui-icons_ef8c08_256x240.png);
|
||||
}
|
||||
.ui-state-active .ui-icon {
|
||||
background-image: url(../img/datepicker/ui-icons_ef8c08_256x240.png);
|
||||
}
|
||||
.ui-state-highlight .ui-icon {
|
||||
background-image: url(../img/datepicker/ui-icons_228ef1_256x240.png);
|
||||
}
|
||||
.ui-state-error .ui-icon,
|
||||
.ui-state-error-text .ui-icon {
|
||||
background-image: url(../img/datepicker/ui-icons_ffd27a_256x240.png);
|
||||
}
|
||||
|
||||
/* positioning */
|
||||
.ui-icon-carat-1-n { background-position: 0 0; }
|
||||
.ui-icon-carat-1-ne { background-position: -16px 0; }
|
||||
.ui-icon-carat-1-e { background-position: -32px 0; }
|
||||
.ui-icon-carat-1-se { background-position: -48px 0; }
|
||||
.ui-icon-carat-1-s { background-position: -64px 0; }
|
||||
.ui-icon-carat-1-sw { background-position: -80px 0; }
|
||||
.ui-icon-carat-1-w { background-position: -96px 0; }
|
||||
.ui-icon-carat-1-nw { background-position: -112px 0; }
|
||||
.ui-icon-carat-2-n-s { background-position: -128px 0; }
|
||||
.ui-icon-carat-2-e-w { background-position: -144px 0; }
|
||||
.ui-icon-triangle-1-n { background-position: 0 -16px; }
|
||||
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
|
||||
.ui-icon-triangle-1-e { background-position: -32px -16px; }
|
||||
.ui-icon-triangle-1-se { background-position: -48px -16px; }
|
||||
.ui-icon-triangle-1-s { background-position: -64px -16px; }
|
||||
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
|
||||
.ui-icon-triangle-1-w { background-position: -96px -16px; }
|
||||
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
|
||||
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
|
||||
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
|
||||
.ui-icon-arrow-1-n { background-position: 0 -32px; }
|
||||
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
|
||||
.ui-icon-arrow-1-e { background-position: -32px -32px; }
|
||||
.ui-icon-arrow-1-se { background-position: -48px -32px; }
|
||||
.ui-icon-arrow-1-s { background-position: -64px -32px; }
|
||||
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
|
||||
.ui-icon-arrow-1-w { background-position: -96px -32px; }
|
||||
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
|
||||
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
|
||||
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
|
||||
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
|
||||
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
|
||||
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
|
||||
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
|
||||
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
|
||||
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
|
||||
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
|
||||
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
|
||||
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
|
||||
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
|
||||
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
|
||||
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
|
||||
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
|
||||
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
|
||||
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
|
||||
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
|
||||
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
|
||||
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
|
||||
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
|
||||
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
|
||||
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
|
||||
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
|
||||
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
|
||||
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
|
||||
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
|
||||
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
|
||||
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
|
||||
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
|
||||
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
|
||||
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
|
||||
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
|
||||
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
|
||||
.ui-icon-arrow-4 { background-position: 0 -80px; }
|
||||
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
|
||||
.ui-icon-extlink { background-position: -32px -80px; }
|
||||
.ui-icon-newwin { background-position: -48px -80px; }
|
||||
.ui-icon-refresh { background-position: -64px -80px; }
|
||||
.ui-icon-shuffle { background-position: -80px -80px; }
|
||||
.ui-icon-transfer-e-w { background-position: -96px -80px; }
|
||||
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
|
||||
.ui-icon-folder-collapsed { background-position: 0 -96px; }
|
||||
.ui-icon-folder-open { background-position: -16px -96px; }
|
||||
.ui-icon-document { background-position: -32px -96px; }
|
||||
.ui-icon-document-b { background-position: -48px -96px; }
|
||||
.ui-icon-note { background-position: -64px -96px; }
|
||||
.ui-icon-mail-closed { background-position: -80px -96px; }
|
||||
.ui-icon-mail-open { background-position: -96px -96px; }
|
||||
.ui-icon-suitcase { background-position: -112px -96px; }
|
||||
.ui-icon-comment { background-position: -128px -96px; }
|
||||
.ui-icon-person { background-position: -144px -96px; }
|
||||
.ui-icon-print { background-position: -160px -96px; }
|
||||
.ui-icon-trash { background-position: -176px -96px; }
|
||||
.ui-icon-locked { background-position: -192px -96px; }
|
||||
.ui-icon-unlocked { background-position: -208px -96px; }
|
||||
.ui-icon-bookmark { background-position: -224px -96px; }
|
||||
.ui-icon-tag { background-position: -240px -96px; }
|
||||
.ui-icon-home { background-position: 0 -112px; }
|
||||
.ui-icon-flag { background-position: -16px -112px; }
|
||||
.ui-icon-calendar { background-position: -32px -112px; }
|
||||
.ui-icon-cart { background-position: -48px -112px; }
|
||||
.ui-icon-pencil { background-position: -64px -112px; }
|
||||
.ui-icon-clock { background-position: -80px -112px; }
|
||||
.ui-icon-disk { background-position: -96px -112px; }
|
||||
.ui-icon-calculator { background-position: -112px -112px; }
|
||||
.ui-icon-zoomin { background-position: -128px -112px; }
|
||||
.ui-icon-zoomout { background-position: -144px -112px; }
|
||||
.ui-icon-search { background-position: -160px -112px; }
|
||||
.ui-icon-wrench { background-position: -176px -112px; }
|
||||
.ui-icon-gear { background-position: -192px -112px; }
|
||||
.ui-icon-heart { background-position: -208px -112px; }
|
||||
.ui-icon-star { background-position: -224px -112px; }
|
||||
.ui-icon-link { background-position: -240px -112px; }
|
||||
.ui-icon-cancel { background-position: 0 -128px; }
|
||||
.ui-icon-plus { background-position: -16px -128px; }
|
||||
.ui-icon-plusthick { background-position: -32px -128px; }
|
||||
.ui-icon-minus { background-position: -48px -128px; }
|
||||
.ui-icon-minusthick { background-position: -64px -128px; }
|
||||
.ui-icon-close { background-position: -80px -128px; }
|
||||
.ui-icon-closethick { background-position: -96px -128px; }
|
||||
.ui-icon-key { background-position: -112px -128px; }
|
||||
.ui-icon-lightbulb { background-position: -128px -128px; }
|
||||
.ui-icon-scissors { background-position: -144px -128px; }
|
||||
.ui-icon-clipboard { background-position: -160px -128px; }
|
||||
.ui-icon-copy { background-position: -176px -128px; }
|
||||
.ui-icon-contact { background-position: -192px -128px; }
|
||||
.ui-icon-image { background-position: -208px -128px; }
|
||||
.ui-icon-video { background-position: -224px -128px; }
|
||||
.ui-icon-script { background-position: -240px -128px; }
|
||||
.ui-icon-alert { background-position: 0 -144px; }
|
||||
.ui-icon-info { background-position: -16px -144px; }
|
||||
.ui-icon-notice { background-position: -32px -144px; }
|
||||
.ui-icon-help { background-position: -48px -144px; }
|
||||
.ui-icon-check { background-position: -64px -144px; }
|
||||
.ui-icon-bullet { background-position: -80px -144px; }
|
||||
.ui-icon-radio-on { background-position: -96px -144px; }
|
||||
.ui-icon-radio-off { background-position: -112px -144px; }
|
||||
.ui-icon-pin-w { background-position: -128px -144px; }
|
||||
.ui-icon-pin-s { background-position: -144px -144px; }
|
||||
.ui-icon-play { background-position: 0 -160px; }
|
||||
.ui-icon-pause { background-position: -16px -160px; }
|
||||
.ui-icon-seek-next { background-position: -32px -160px; }
|
||||
.ui-icon-seek-prev { background-position: -48px -160px; }
|
||||
.ui-icon-seek-end { background-position: -64px -160px; }
|
||||
.ui-icon-seek-start { background-position: -80px -160px; }
|
||||
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
|
||||
.ui-icon-seek-first { background-position: -80px -160px; }
|
||||
.ui-icon-stop { background-position: -96px -160px; }
|
||||
.ui-icon-eject { background-position: -112px -160px; }
|
||||
.ui-icon-volume-off { background-position: -128px -160px; }
|
||||
.ui-icon-volume-on { background-position: -144px -160px; }
|
||||
.ui-icon-power { background-position: 0 -176px; }
|
||||
.ui-icon-signal-diag { background-position: -16px -176px; }
|
||||
.ui-icon-signal { background-position: -32px -176px; }
|
||||
.ui-icon-battery-0 { background-position: -48px -176px; }
|
||||
.ui-icon-battery-1 { background-position: -64px -176px; }
|
||||
.ui-icon-battery-2 { background-position: -80px -176px; }
|
||||
.ui-icon-battery-3 { background-position: -96px -176px; }
|
||||
.ui-icon-circle-plus { background-position: 0 -192px; }
|
||||
.ui-icon-circle-minus { background-position: -16px -192px; }
|
||||
.ui-icon-circle-close { background-position: -32px -192px; }
|
||||
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
|
||||
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
|
||||
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
|
||||
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
|
||||
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
|
||||
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
|
||||
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
|
||||
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
|
||||
.ui-icon-circle-zoomin { background-position: -176px -192px; }
|
||||
.ui-icon-circle-zoomout { background-position: -192px -192px; }
|
||||
.ui-icon-circle-check { background-position: -208px -192px; }
|
||||
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
|
||||
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
|
||||
.ui-icon-circlesmall-close { background-position: -32px -208px; }
|
||||
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
|
||||
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
|
||||
.ui-icon-squaresmall-close { background-position: -80px -208px; }
|
||||
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
|
||||
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
|
||||
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
|
||||
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
|
||||
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
|
||||
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Corner radius */
|
||||
.ui-corner-all,
|
||||
.ui-corner-top,
|
||||
.ui-corner-left,
|
||||
.ui-corner-tl {
|
||||
border-top-left-radius: 4px;
|
||||
}
|
||||
.ui-corner-all,
|
||||
.ui-corner-top,
|
||||
.ui-corner-right,
|
||||
.ui-corner-tr {
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
.ui-corner-all,
|
||||
.ui-corner-bottom,
|
||||
.ui-corner-left,
|
||||
.ui-corner-bl {
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
.ui-corner-all,
|
||||
.ui-corner-bottom,
|
||||
.ui-corner-right,
|
||||
.ui-corner-br {
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay {
|
||||
background: #666666 url(../img/datepicker/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat;
|
||||
opacity: .5;
|
||||
filter: Alpha(Opacity=50);
|
||||
}
|
||||
.ui-widget-shadow {
|
||||
margin: -5px 0 0 -5px;
|
||||
padding: 5px;
|
||||
background: #000000 url(../img/datepicker/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x;
|
||||
opacity: .2;
|
||||
filter: Alpha(Opacity=20);
|
||||
border-radius: 5px;
|
||||
}
|
115
assets/css/src/datepicker/melon.datepicker.css
Normal file
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Melon skin from: https://github.com/rtsinani/jquery-datepicker-skins
|
||||
*/
|
||||
|
||||
.wp-admin {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.wp-admin .ui-widget {
|
||||
font-family: "Lucida Grande", "Lucida Sans Unicode", Helvetica, Arial, Verdana, sans-serif;
|
||||
background: #2e3641;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
}
|
||||
|
||||
.wp-admin .ui-datepicker {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wp-admin .ui-datepicker-header {
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-weight: normal;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.wp-admin .ui-datepicker-header .ui-state-hover {
|
||||
background: transparent;
|
||||
border-color: transparent;
|
||||
cursor: pointer;
|
||||
border-radius: 0;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
}
|
||||
|
||||
.wp-admin .ui-datepicker .ui-datepicker-title {
|
||||
margin-top: .4em;
|
||||
margin-bottom: .3em;
|
||||
color: #e9f0f4;
|
||||
}
|
||||
|
||||
.wp-admin .ui-datepicker .ui-datepicker-prev-hover,
|
||||
.wp-admin .ui-datepicker .ui-datepicker-next-hover,
|
||||
.wp-admin .ui-datepicker .ui-datepicker-next,
|
||||
.wp-admin .ui-datepicker .ui-datepicker-prev {
|
||||
top: .9em;
|
||||
border:none;
|
||||
}
|
||||
|
||||
.wp-admin .ui-datepicker .ui-datepicker-prev-hover {
|
||||
left: 2px;
|
||||
}
|
||||
|
||||
.wp-admin .ui-datepicker .ui-datepicker-next-hover {
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
.wp-admin .ui-datepicker .ui-datepicker-next span,
|
||||
.wp-admin .ui-datepicker .ui-datepicker-prev span {
|
||||
background-image: url(../img/datepicker/ui-icons_ffffff_256x240.png);
|
||||
background-position: -32px 0;
|
||||
margin-top: 0;
|
||||
top: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.wp-admin .ui-datepicker .ui-datepicker-prev span {
|
||||
background-position: -96px 0;
|
||||
}
|
||||
|
||||
.wp-admin .ui-datepicker table {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.wp-admin .ui-datepicker th {
|
||||
padding: 1em 0;
|
||||
color: #ccc;
|
||||
font-size: 13px;
|
||||
font-weight: normal;
|
||||
border: none;
|
||||
border-top: 1px solid #3a414d;
|
||||
}
|
||||
|
||||
.wp-admin .ui-datepicker td {
|
||||
background: #f97e76;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.wp-admin td .ui-state-default {
|
||||
background: transparent;
|
||||
border: none;
|
||||
text-align: center;
|
||||
padding: .5em;
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
color: #efefef;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.wp-admin .ui-state-disabled {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.wp-admin .ui-state-disabled .ui-state-default {
|
||||
color: #fba49e;
|
||||
}
|
||||
|
||||
.wp-admin td .ui-state-active,
|
||||
.wp-admin td .ui-state-hover {
|
||||
background: #2e3641;
|
||||
}
|
||||
|
@ -1,2 +1,5 @@
|
||||
.mailpoet_form
|
||||
margin: 0 0 20px 0
|
||||
|
||||
.mailpoet_form td
|
||||
vertical-align: top !important
|
||||
vertical-align: top !important
|
||||
|
BIN
assets/img/datepicker/animated-overlay.gif
Executable file
After Width: | Height: | Size: 1.7 KiB |
BIN
assets/img/datepicker/cal_logo.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
assets/img/datepicker/ui-bg_diagonals-thick_18_b81900_40x40.png
Executable file
After Width: | Height: | Size: 418 B |
BIN
assets/img/datepicker/ui-bg_diagonals-thick_20_666666_40x40.png
Executable file
After Width: | Height: | Size: 312 B |
BIN
assets/img/datepicker/ui-bg_flat_10_000000_40x100.png
Executable file
After Width: | Height: | Size: 205 B |
BIN
assets/img/datepicker/ui-bg_glass_100_f6f6f6_1x400.png
Executable file
After Width: | Height: | Size: 262 B |
BIN
assets/img/datepicker/ui-bg_glass_100_fdf5ce_1x400.png
Executable file
After Width: | Height: | Size: 348 B |
BIN
assets/img/datepicker/ui-bg_glass_65_ffffff_1x400.png
Executable file
After Width: | Height: | Size: 207 B |
BIN
assets/img/datepicker/ui-bg_gloss-wave_35_f6a828_500x100.png
Executable file
After Width: | Height: | Size: 5.7 KiB |
BIN
assets/img/datepicker/ui-bg_highlight-soft_100_eeeeee_1x100.png
Executable file
After Width: | Height: | Size: 278 B |
BIN
assets/img/datepicker/ui-bg_highlight-soft_75_ffe45c_1x100.png
Executable file
After Width: | Height: | Size: 328 B |
BIN
assets/img/datepicker/ui-icons_222222_256x240.png
Executable file
After Width: | Height: | Size: 6.8 KiB |
BIN
assets/img/datepicker/ui-icons_228ef1_256x240.png
Executable file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/img/datepicker/ui-icons_454545_256x240.png
Executable file
After Width: | Height: | Size: 4.3 KiB |
BIN
assets/img/datepicker/ui-icons_ef8c08_256x240.png
Executable file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/img/datepicker/ui-icons_ffd27a_256x240.png
Executable file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/img/datepicker/ui-icons_ffffff_256x240.png
Executable file
After Width: | Height: | Size: 6.2 KiB |
@ -40,8 +40,17 @@ define('date',
|
||||
return this;
|
||||
},
|
||||
format: function(date, options) {
|
||||
options = options || {};
|
||||
this.init(options);
|
||||
return Moment(date).format(this.convertFormat(this.options.format));
|
||||
|
||||
return Moment(date, this.convertFormat(options.parseFormat))
|
||||
.format(this.convertFormat(this.options.format));
|
||||
},
|
||||
toDate: function(date, options) {
|
||||
options = options || {};
|
||||
this.init(options);
|
||||
|
||||
return Moment(date, this.convertFormat(options.parseFormat)).toDate();
|
||||
},
|
||||
short: function(date) {
|
||||
return this.format(date, {
|
||||
@ -113,6 +122,8 @@ define('date',
|
||||
}
|
||||
};
|
||||
|
||||
if (!format || format.length <= 0) return format;
|
||||
|
||||
const replacements = format_mappings['date'];
|
||||
|
||||
let outputFormat = '';
|
||||
@ -131,4 +142,4 @@ define('date',
|
||||
return outputFormat;
|
||||
}
|
||||
};
|
||||
});
|
||||
});
|
||||
|
@ -9,6 +9,13 @@ define([
|
||||
render() {
|
||||
const yearsRange = 100;
|
||||
const years = [];
|
||||
|
||||
if (this.props.placeholder !== undefined) {
|
||||
years.push((
|
||||
<option value="" key={ 0 }>{ this.props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
|
||||
const currentYear = Moment().year();
|
||||
for (let i = currentYear; i >= currentYear - yearsRange; i--) {
|
||||
years.push((
|
||||
@ -33,6 +40,13 @@ define([
|
||||
class FormFieldDateMonth extends React.Component {
|
||||
render() {
|
||||
const months = [];
|
||||
|
||||
if (this.props.placeholder !== undefined) {
|
||||
months.push((
|
||||
<option value="" key={ 0 }>{ this.props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
|
||||
for (let i = 1; i <= 12; i++) {
|
||||
months.push((
|
||||
<option
|
||||
@ -56,6 +70,13 @@ define([
|
||||
class FormFieldDateDay extends React.Component {
|
||||
render() {
|
||||
const days = [];
|
||||
|
||||
if (this.props.placeholder !== undefined) {
|
||||
days.push((
|
||||
<option value="" key={ 0 }>{ this.props.placeholder }</option>
|
||||
));
|
||||
}
|
||||
|
||||
for (let i = 1; i <= 31; i++) {
|
||||
days.push((
|
||||
<option
|
||||
@ -81,9 +102,9 @@ define([
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
year: Moment().year(),
|
||||
month: 1,
|
||||
day: 1
|
||||
year: undefined,
|
||||
month: undefined,
|
||||
day: undefined
|
||||
}
|
||||
}
|
||||
componentDidMount() {
|
||||
@ -98,7 +119,6 @@ define([
|
||||
}
|
||||
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]
|
||||
@ -112,7 +132,7 @@ define([
|
||||
`${this.state.month}/${this.state.day}/${this.state.year}`,
|
||||
'M/D/YYYY'
|
||||
).valueOf();
|
||||
if (!isNaN(newTimeStamp) && parseInt(newTimeStamp, 10) > 0) {
|
||||
if (~~(newTimeStamp) > 0) {
|
||||
// convert milliseconds to seconds
|
||||
newTimeStamp /= 1000;
|
||||
return this.props.onValueChange({
|
||||
@ -158,6 +178,7 @@ define([
|
||||
key={ 'year' }
|
||||
name={ this.props.field.name }
|
||||
year={ this.state.year }
|
||||
placeholder={ this.props.field.year_placeholder }
|
||||
/>);
|
||||
break;
|
||||
|
||||
@ -169,6 +190,7 @@ define([
|
||||
name={ this.props.field.name }
|
||||
month={ this.state.month }
|
||||
monthNames={ monthNames }
|
||||
placeholder={ this.props.field.month_placeholder }
|
||||
/>);
|
||||
break;
|
||||
|
||||
@ -179,6 +201,7 @@ define([
|
||||
key={ 'day' }
|
||||
name={ this.props.field.name }
|
||||
day={ this.state.day }
|
||||
placeholder={ this.props.field.day_placeholder }
|
||||
/>);
|
||||
break;
|
||||
}
|
||||
|
@ -1,40 +1,54 @@
|
||||
define([
|
||||
'react'
|
||||
],
|
||||
function(
|
||||
React
|
||||
) {
|
||||
const FormFieldSelect = React.createClass({
|
||||
render: function() {
|
||||
if (this.props.field.values === undefined) {
|
||||
return false;
|
||||
}
|
||||
import React from 'react'
|
||||
|
||||
const options = Object.keys(this.props.field.values).map(
|
||||
(value, index) => {
|
||||
return (
|
||||
<option
|
||||
key={ 'option-' + index }
|
||||
value={ value }>
|
||||
{ this.props.field.values[value] }
|
||||
</option>
|
||||
);
|
||||
}
|
||||
);
|
||||
const FormFieldSelect = React.createClass({
|
||||
render() {
|
||||
if (this.props.field.values === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
<select
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
onChange={ this.props.onValueChange }
|
||||
{...this.props.field.validation}
|
||||
>
|
||||
{options}
|
||||
</select>
|
||||
let filter = false;
|
||||
let placeholder = false;
|
||||
|
||||
if (this.props.field.placeholder !== undefined) {
|
||||
placeholder = (
|
||||
<option value="">{ this.props.field.placeholder }</option>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return FormFieldSelect;
|
||||
});
|
||||
if (this.props.field['filter'] !== undefined) {
|
||||
filter = this.props.field.filter;
|
||||
}
|
||||
|
||||
const options = Object.keys(this.props.field.values).map(
|
||||
(value, index) => {
|
||||
|
||||
if (filter !== false && filter(this.props.item, value) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<option
|
||||
key={ 'option-' + index }
|
||||
value={ value }>
|
||||
{ this.props.field.values[value] }
|
||||
</option>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<select
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
onChange={ this.props.onValueChange }
|
||||
{...this.props.field.validation}
|
||||
>
|
||||
{placeholder}
|
||||
{options}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = FormFieldSelect;
|
@ -1,35 +1,35 @@
|
||||
define([
|
||||
'react'
|
||||
],
|
||||
function(
|
||||
React
|
||||
) {
|
||||
var FormFieldText = React.createClass({
|
||||
render: function() {
|
||||
var value = this.props.item[this.props.field.name];
|
||||
if(value === undefined) {
|
||||
value = this.props.field.defaultValue || '';
|
||||
}
|
||||
import React from 'react'
|
||||
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
className={ (this.props.field.size) ? '' : 'regular-text' }
|
||||
size={
|
||||
(this.props.field.size !== 'auto' && this.props.field.size > 0)
|
||||
? this.props.field.size
|
||||
: false
|
||||
}
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ value }
|
||||
placeholder={ this.props.field.placeholder }
|
||||
onChange={ this.props.onValueChange }
|
||||
{...this.props.field.validation}
|
||||
/>
|
||||
);
|
||||
const FormFieldText = React.createClass({
|
||||
render() {
|
||||
let value = this.props.item[this.props.field.name];
|
||||
if (value === undefined) {
|
||||
value = this.props.field.defaultValue || '';
|
||||
}
|
||||
});
|
||||
|
||||
return FormFieldText;
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
disabled={
|
||||
(this.props.field['disabled'] !== undefined)
|
||||
? this.props.field.disabled(this.props.item)
|
||||
: false
|
||||
}
|
||||
className={ (this.props.field.size) ? '' : 'regular-text' }
|
||||
size={
|
||||
(this.props.field.size !== 'auto' && this.props.field.size > 0)
|
||||
? this.props.field.size
|
||||
: false
|
||||
}
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ value }
|
||||
placeholder={ this.props.field.placeholder }
|
||||
onChange={ this.props.onValueChange }
|
||||
{...this.props.field.validation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = FormFieldText;
|
@ -37,9 +37,13 @@ define(
|
||||
return this.props.errors ? this.props.errors : this.state.errors;
|
||||
},
|
||||
componentDidMount: function() {
|
||||
if(this.props.params.id !== undefined) {
|
||||
if(this.isMounted()) {
|
||||
if(this.isMounted()) {
|
||||
if(this.props.params.id !== undefined) {
|
||||
this.loadItem(this.props.params.id);
|
||||
} else {
|
||||
this.setState({
|
||||
item: jQuery('.mailpoet_form').serializeObject()
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -102,7 +106,6 @@ define(
|
||||
item[field.name] = this.state.item[field.name];
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
// set id if specified
|
||||
if(this.props.params.id !== undefined) {
|
||||
item.id = this.props.params.id;
|
||||
@ -167,6 +170,17 @@ define(
|
||||
{ 'mailpoet_form_loading': this.state.loading || this.props.loading }
|
||||
);
|
||||
|
||||
var beforeFormContent = false;
|
||||
var afterFormContent = false;
|
||||
|
||||
if (this.props.beforeFormContent !== undefined) {
|
||||
beforeFormContent = this.props.beforeFormContent(this.getValues());
|
||||
}
|
||||
|
||||
if (this.props.afterFormContent !== undefined) {
|
||||
afterFormContent = this.props.afterFormContent(this.getValues());
|
||||
}
|
||||
|
||||
var fields = this.props.fields.map(function(field, i) {
|
||||
return (
|
||||
<FormField
|
||||
@ -191,26 +205,30 @@ define(
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
id={ this.props.id }
|
||||
ref="form"
|
||||
className={ formClasses }
|
||||
onSubmit={
|
||||
(this.props.onSubmit !== undefined)
|
||||
? this.props.onSubmit
|
||||
: this.handleSubmit
|
||||
}
|
||||
>
|
||||
{ errors }
|
||||
<div>
|
||||
{ beforeFormContent }
|
||||
<form
|
||||
id={ this.props.id }
|
||||
ref="form"
|
||||
className={ formClasses }
|
||||
onSubmit={
|
||||
(this.props.onSubmit !== undefined)
|
||||
? this.props.onSubmit
|
||||
: this.handleSubmit
|
||||
}
|
||||
>
|
||||
{ errors }
|
||||
|
||||
<table className="form-table">
|
||||
<tbody>
|
||||
{fields}
|
||||
</tbody>
|
||||
</table>
|
||||
<table className="form-table">
|
||||
<tbody>
|
||||
{fields}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{ actions }
|
||||
</form>
|
||||
{ actions }
|
||||
</form>
|
||||
{ afterFormContent }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -71,6 +71,14 @@ const messages = {
|
||||
}
|
||||
};
|
||||
|
||||
const bulk_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('trash'),
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
|
||||
const item_actions = [
|
||||
{
|
||||
name: 'edit',
|
||||
@ -141,7 +149,7 @@ const FormList = React.createClass({
|
||||
{ segments }
|
||||
</td>
|
||||
<td className="column-date" data-colname={MailPoet.I18n.t('createdOn')}>
|
||||
<abbr>{ MailPoet.Date.full(form.created_at) }</abbr>
|
||||
<abbr>{ MailPoet.Date.format(form.created_at) }</abbr>
|
||||
</td>
|
||||
</div>
|
||||
);
|
||||
@ -149,23 +157,24 @@ const FormList = React.createClass({
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
<h1 className="title">
|
||||
{MailPoet.I18n.t('pageTitle')} <a
|
||||
className="add-new-h2"
|
||||
className="page-title-action"
|
||||
href="javascript:;"
|
||||
onClick={ this.createForm }
|
||||
>{MailPoet.I18n.t('new')}</a>
|
||||
</h2>
|
||||
</h1>
|
||||
|
||||
<Listing
|
||||
limit={ mailpoet_listing_per_page }
|
||||
location={ this.props.location }
|
||||
params={ this.props.params }
|
||||
messages={ messages }
|
||||
search={ false }
|
||||
limit={ 1000 }
|
||||
endpoint="forms"
|
||||
onRenderItem={ this.renderItem }
|
||||
columns={ columns }
|
||||
bulk_actions={ bulk_actions }
|
||||
item_actions={ item_actions }
|
||||
/>
|
||||
</div>
|
||||
|
@ -21,7 +21,7 @@ define(['react', 'classnames'], function(React, classNames) {
|
||||
href="javascript:;"
|
||||
className={classes}
|
||||
onClick={this.handleSelect.bind(this, group.name)} >
|
||||
{group.label} <span className="count">({ group.count })</span>
|
||||
{group.label} <span className="count">({ group.count.toLocaleString() })</span>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
|
@ -733,6 +733,7 @@ define(
|
||||
{ search }
|
||||
<div className="tablenav top clearfix">
|
||||
<ListingBulkActions
|
||||
count={ this.state.count }
|
||||
bulk_actions={ bulk_actions }
|
||||
selection={ this.state.selection }
|
||||
selected_ids={ this.state.selected_ids }
|
||||
@ -795,6 +796,7 @@ define(
|
||||
</table>
|
||||
<div className="tablenav bottom">
|
||||
<ListingBulkActions
|
||||
count={ this.state.count }
|
||||
bulk_actions={ bulk_actions }
|
||||
selection={ this.state.selection }
|
||||
selected_ids={ this.state.selected_ids }
|
||||
|
@ -148,7 +148,7 @@ define([
|
||||
className="current-page" />
|
||||
{MailPoet.I18n.t('pageOutOf')}
|
||||
<span className="total-pages">
|
||||
{Math.ceil(this.props.count / this.props.limit)}
|
||||
{Math.ceil(this.props.count / this.props.limit).toLocaleString()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@ -167,7 +167,7 @@ define([
|
||||
return (
|
||||
<div className={ classes }>
|
||||
<span className="displaying-num">{
|
||||
MailPoet.I18n.t('numberOfItems').replace('%$1d', this.props.count)
|
||||
MailPoet.I18n.t('numberOfItems').replace('%$1d', this.props.count.toLocaleString())
|
||||
}</span>
|
||||
{ pagination }
|
||||
</div>
|
||||
|
@ -16,7 +16,7 @@ define([
|
||||
defaults: function() {
|
||||
return this._getDefaults({
|
||||
type: 'footer',
|
||||
text: '<a href="[subscription:unsubscribe_url]">Unsubscribe</a> | <a href="[subscription:manage_url]">Manage subscription</a><br /><b>Add your postal address here!</b>',
|
||||
text: '<a href="[link:subscription_unsubscribe_url]">Unsubscribe</a> | <a href="[link:subscription_manage_url]">Manage subscription</a><br /><b>Add your postal address here!</b>',
|
||||
styles: {
|
||||
block: {
|
||||
backgroundColor: 'transparent',
|
||||
|
@ -16,7 +16,7 @@ define([
|
||||
defaults: function() {
|
||||
return this._getDefaults({
|
||||
type: 'header',
|
||||
text: 'Display problems? <a href="[newsletter:view_in_browser_url]">View it in your browser</a>',
|
||||
text: 'Display problems? <a href="[link:newsletter_view_in_browser_url]">View it in your browser</a>',
|
||||
styles: {
|
||||
block: {
|
||||
backgroundColor: 'transparent',
|
||||
|
@ -284,7 +284,7 @@ define([
|
||||
}
|
||||
|
||||
if (App.getConfig().get('validation.validateUnsubscribeLinkPresent') &&
|
||||
JSON.stringify(jsonObject).indexOf("[subscription:unsubscribe_url]") < 0) {
|
||||
JSON.stringify(jsonObject).indexOf("[link:subscription_unsubscribe_url]") < 0) {
|
||||
this.showValidationError(MailPoet.I18n.t('unsubscribeLinkMissing'));
|
||||
return;
|
||||
}
|
||||
|
@ -245,14 +245,20 @@ define([
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'newsletters',
|
||||
action: 'render',
|
||||
action: 'showPreview',
|
||||
data: json,
|
||||
}).done(function(response){
|
||||
MailPoet.Modal.loading(false);
|
||||
window.open('data:text/html;charset=utf-8,' + encodeURIComponent(response.rendered_body), '_blank');
|
||||
|
||||
if (response.result === true) {
|
||||
window.open(response.data.url, '_blank')
|
||||
}
|
||||
MailPoet.Notice.error(response.errors);
|
||||
}).fail(function(error) {
|
||||
MailPoet.Modal.loading(false);
|
||||
alert('Something went wrong, check console');
|
||||
MailPoet.Notice.error(
|
||||
MailPoet.I18n.t('newsletterPreviewFailed')
|
||||
);
|
||||
});
|
||||
},
|
||||
sendPreview: function() {
|
||||
|
@ -148,7 +148,7 @@ define(
|
||||
} else {
|
||||
if (item.queue.status === 'scheduled') {
|
||||
return (
|
||||
<span>{MailPoet.I18n.t('scheduledFor')} { item.queue.scheduled_at } </span>
|
||||
<span>{MailPoet.I18n.t('scheduledFor')} { MailPoet.Date.format(item.queue.scheduled_at) } </span>
|
||||
)
|
||||
}
|
||||
var progressClasses = classNames(
|
||||
@ -253,7 +253,7 @@ define(
|
||||
var statistics_column =
|
||||
(!mailpoet_settings.tracking || !mailpoet_settings.tracking.enabled) ?
|
||||
false :
|
||||
<td className="column {statistics_class}" data-colname="Statistics">
|
||||
<td className="column {statistics_class}" data-colname={ MailPoet.I18n.t('statistics') }>
|
||||
{ this.renderStatistics(newsletter) }
|
||||
</td>;
|
||||
|
||||
@ -265,18 +265,18 @@ define(
|
||||
</strong>
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column" data-colname="Status">
|
||||
<td className="column" data-colname={ MailPoet.I18n.t('status') }>
|
||||
{ this.renderStatus(newsletter) }
|
||||
</td>
|
||||
<td className="column" data-colname="Lists">
|
||||
<td className="column" data-colname={ MailPoet.I18n.t('lists') }>
|
||||
{ segments }
|
||||
</td>
|
||||
{ statistics_column }
|
||||
<td className="column-date" data-colname="Subscribed on">
|
||||
<abbr>{ MailPoet.Date.full(newsletter.created_at) }</abbr>
|
||||
<td className="column-date" data-colname={ MailPoet.I18n.t('createdOn') }>
|
||||
<abbr>{ MailPoet.Date.format(newsletter.created_at) }</abbr>
|
||||
</td>
|
||||
<td className="column-date" data-colname="Last modified on">
|
||||
<abbr>{ MailPoet.Date.full(newsletter.updated_at) }</abbr>
|
||||
<td className="column-date" data-colname={ MailPoet.I18n.t('lastModifiedOn') }>
|
||||
<abbr>{ MailPoet.Date.format(newsletter.updated_at) }</abbr>
|
||||
</td>
|
||||
</div>
|
||||
);
|
||||
@ -287,11 +287,12 @@ define(
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
{MailPoet.I18n.t('pageTitle')} <Link className="add-new-h2" to="/new">{MailPoet.I18n.t('new')}</Link>
|
||||
</h2>
|
||||
<h1 className="title">
|
||||
{MailPoet.I18n.t('pageTitle')} <Link className="page-title-action" to="/new">{MailPoet.I18n.t('new')}</Link>
|
||||
</h1>
|
||||
|
||||
<Listing
|
||||
limit={ mailpoet_listing_per_page }
|
||||
params={ this.props.params }
|
||||
endpoint="newsletters"
|
||||
onRenderItem={this.renderItem}
|
||||
|
@ -42,7 +42,7 @@ define(
|
||||
getLabel: function(segment) {
|
||||
var name = segment.name;
|
||||
if (segment.subscribers > 0) {
|
||||
name += ' (%$1s)'.replace('%$1s', segment.subscribers);
|
||||
name += ' (%$1s)'.replace('%$1s', parseInt(segment.subscribers).toLocaleString());
|
||||
}
|
||||
return name;
|
||||
},
|
||||
|
@ -1,12 +1,320 @@
|
||||
define(
|
||||
[
|
||||
'mailpoet'
|
||||
'react',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'mailpoet',
|
||||
'form/fields/checkbox.jsx',
|
||||
'form/fields/select.jsx',
|
||||
'form/fields/text.jsx',
|
||||
],
|
||||
function(
|
||||
MailPoet
|
||||
React,
|
||||
jQuery,
|
||||
_,
|
||||
MailPoet,
|
||||
Checkbox,
|
||||
Select,
|
||||
Text
|
||||
) {
|
||||
|
||||
var settings = window.mailpoet_settings || {};
|
||||
var settings = window.mailpoet_settings || {},
|
||||
currentTime = window.mailpoet_current_time || '00:00',
|
||||
defaultDateTime = window.mailpoet_current_date + ' ' + '00:00:00';
|
||||
timeOfDayItems = window.mailpoet_schedule_time_of_day,
|
||||
dateDisplayFormat = window.mailpoet_date_display_format,
|
||||
dateStorageFormat = window.mailpoet_date_storage_format;
|
||||
|
||||
var datepickerTranslations = {
|
||||
closeText: MailPoet.I18n.t('close'),
|
||||
currentText: MailPoet.I18n.t('today'),
|
||||
nextText: MailPoet.I18n.t('next'),
|
||||
prevText: MailPoet.I18n.t('previous'),
|
||||
monthNames: [
|
||||
MailPoet.I18n.t('january'),
|
||||
MailPoet.I18n.t('february'),
|
||||
MailPoet.I18n.t('march'),
|
||||
MailPoet.I18n.t('april'),
|
||||
MailPoet.I18n.t('may'),
|
||||
MailPoet.I18n.t('june'),
|
||||
MailPoet.I18n.t('july'),
|
||||
MailPoet.I18n.t('august'),
|
||||
MailPoet.I18n.t('september'),
|
||||
MailPoet.I18n.t('october'),
|
||||
MailPoet.I18n.t('november'),
|
||||
MailPoet.I18n.t('december')
|
||||
],
|
||||
monthNamesShort: [
|
||||
MailPoet.I18n.t('januaryShort'),
|
||||
MailPoet.I18n.t('februaryShort'),
|
||||
MailPoet.I18n.t('marchShort'),
|
||||
MailPoet.I18n.t('aprilShort'),
|
||||
MailPoet.I18n.t('mayShort'),
|
||||
MailPoet.I18n.t('juneShort'),
|
||||
MailPoet.I18n.t('julyShort'),
|
||||
MailPoet.I18n.t('augustShort'),
|
||||
MailPoet.I18n.t('septemberShort'),
|
||||
MailPoet.I18n.t('octoberShort'),
|
||||
MailPoet.I18n.t('novemberShort'),
|
||||
MailPoet.I18n.t('decemberShort')
|
||||
],
|
||||
dayNames: [
|
||||
MailPoet.I18n.t('sunday'),
|
||||
MailPoet.I18n.t('monday'),
|
||||
MailPoet.I18n.t('tuesday'),
|
||||
MailPoet.I18n.t('wednesday'),
|
||||
MailPoet.I18n.t('thursday'),
|
||||
MailPoet.I18n.t('friday'),
|
||||
MailPoet.I18n.t('saturday')
|
||||
],
|
||||
dayNamesShort: [
|
||||
MailPoet.I18n.t('sundayShort'),
|
||||
MailPoet.I18n.t('mondayShort'),
|
||||
MailPoet.I18n.t('tuesdayShort'),
|
||||
MailPoet.I18n.t('wednesdayShort'),
|
||||
MailPoet.I18n.t('thursdayShort'),
|
||||
MailPoet.I18n.t('fridayShort'),
|
||||
MailPoet.I18n.t('saturdayShort')
|
||||
],
|
||||
dayNamesMin: [
|
||||
MailPoet.I18n.t('sundayMin'),
|
||||
MailPoet.I18n.t('mondayMin'),
|
||||
MailPoet.I18n.t('tuesdayMin'),
|
||||
MailPoet.I18n.t('wednesdayMin'),
|
||||
MailPoet.I18n.t('thursdayMin'),
|
||||
MailPoet.I18n.t('fridayMin'),
|
||||
MailPoet.I18n.t('saturdayMin')
|
||||
],
|
||||
};
|
||||
|
||||
var DateText = React.createClass({
|
||||
onChange: function(event) {
|
||||
// Swap display format to storage format
|
||||
var displayDate = event.target.value,
|
||||
storageDate = this.getStorageDate(displayDate);
|
||||
|
||||
event.target.value = storageDate;
|
||||
this.props.onChange(event);
|
||||
},
|
||||
componentDidMount: function() {
|
||||
var $element = jQuery(this.refs.dateInput),
|
||||
that = this;
|
||||
if ($element.datepicker) {
|
||||
// Override jQuery UI datepicker Date parsing and formatting
|
||||
jQuery.datepicker.parseDate = function(format, value) {
|
||||
// Transform string format to Date object
|
||||
return MailPoet.Date.toDate(value, {
|
||||
parseFormat: dateDisplayFormat,
|
||||
format: format
|
||||
});
|
||||
};
|
||||
jQuery.datepicker.formatDate = function(format, value) {
|
||||
// Transform Date object to string format
|
||||
var newValue = MailPoet.Date.format(value, {
|
||||
format: format
|
||||
});
|
||||
return newValue;
|
||||
};
|
||||
|
||||
$element.datepicker(_.extend({
|
||||
dateFormat: this.props.displayFormat,
|
||||
isRTL: false,
|
||||
onSelect: function(value) {
|
||||
that.onChange({
|
||||
target: {
|
||||
name: that.getFieldName(),
|
||||
value: value,
|
||||
},
|
||||
});
|
||||
},
|
||||
}, datepickerTranslations));
|
||||
|
||||
this.datepickerInitialized = true;
|
||||
}
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
if (this.datepickerInitialized) {
|
||||
jQuery(this.refs.dateInput).datepicker('destroy');
|
||||
}
|
||||
},
|
||||
getFieldName: function() {
|
||||
return this.props.name || 'date';
|
||||
},
|
||||
getDisplayDate: function(date) {
|
||||
return MailPoet.Date.format(date, {
|
||||
parseFormat: this.props.storageFormat,
|
||||
format: this.props.displayFormat
|
||||
});
|
||||
},
|
||||
getStorageDate: function(date) {
|
||||
return MailPoet.Date.format(date, {
|
||||
parseFormat: this.props.displayFormat,
|
||||
format: this.props.storageFormat
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
size="10"
|
||||
name={this.getFieldName()}
|
||||
value={this.getDisplayDate(this.props.value)}
|
||||
readOnly={ true }
|
||||
onChange={this.onChange}
|
||||
ref="dateInput"
|
||||
{...this.props.validation} />
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var TimeSelect = React.createClass({
|
||||
render: function() {
|
||||
const options = Object.keys(timeOfDayItems).map(
|
||||
(value, index) => {
|
||||
return (
|
||||
<option
|
||||
key={ 'option-' + index }
|
||||
value={ value }>
|
||||
{ timeOfDayItems[value] }
|
||||
</option>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<select
|
||||
name={this.props.name || 'time'}
|
||||
value={this.props.value}
|
||||
onChange={this.props.onChange}
|
||||
{...this.props.validation}
|
||||
>
|
||||
{options}
|
||||
</select>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var DateTime = React.createClass({
|
||||
_DATE_TIME_SEPARATOR: " ",
|
||||
getInitialState: function() {
|
||||
return this._buildStateFromProps(this.props);
|
||||
},
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
this.setState(this._buildStateFromProps(nextProps));
|
||||
},
|
||||
_buildStateFromProps: function(props) {
|
||||
let value = props.value || defaultDateTime;
|
||||
const [date, time] = value.split(this._DATE_TIME_SEPARATOR)
|
||||
return {
|
||||
date: date,
|
||||
time: time,
|
||||
};
|
||||
},
|
||||
handleChange: function(event) {
|
||||
var newState = {};
|
||||
newState[event.target.name] = event.target.value;
|
||||
|
||||
this.setState(newState, function() {
|
||||
this.propagateChange();
|
||||
});
|
||||
},
|
||||
propagateChange: function() {
|
||||
if (this.props.onChange) {
|
||||
return this.props.onChange({
|
||||
target: {
|
||||
name: this.props.name || '',
|
||||
value: this.getDateTime(),
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
getDateTime: function() {
|
||||
return [this.state.date, this.state.time].join(this._DATE_TIME_SEPARATOR);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<span>
|
||||
<DateText
|
||||
name="date"
|
||||
value={this.state.date}
|
||||
onChange={this.handleChange}
|
||||
displayFormat={dateDisplayFormat}
|
||||
storageFormat={dateStorageFormat}
|
||||
validation={this.props.dateValidation}/>
|
||||
<TimeSelect
|
||||
name="time"
|
||||
value={this.state.time}
|
||||
onChange={this.handleChange}
|
||||
validation={this.props.timeValidation} />
|
||||
</span>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var StandardScheduling = React.createClass({
|
||||
_getCurrentValue: function() {
|
||||
return this.props.item[this.props.field.name] || {};
|
||||
},
|
||||
handleValueChange: function(event) {
|
||||
var oldValue = this._getCurrentValue(),
|
||||
newValue = {};
|
||||
newValue[event.target.name] = event.target.value;
|
||||
|
||||
return this.props.onValueChange({
|
||||
target: {
|
||||
name: this.props.field.name,
|
||||
value: _.extend({}, oldValue, newValue)
|
||||
}
|
||||
});
|
||||
},
|
||||
handleCheckboxChange: function(event) {
|
||||
event.target.value = this.refs.isScheduled.checked ? '1' : '0';
|
||||
return this.handleValueChange(event);
|
||||
},
|
||||
isScheduled: function() {
|
||||
return this._getCurrentValue().isScheduled === '1';
|
||||
},
|
||||
getDateValidation: function() {
|
||||
return {
|
||||
'data-parsley-required': true,
|
||||
'data-parsley-required-message': MailPoet.I18n.t('noScheduledDateError'),
|
||||
'data-parsley-errors-container': '#mailpoet_scheduling',
|
||||
};
|
||||
},
|
||||
render: function() {
|
||||
var schedulingOptions;
|
||||
|
||||
if (this.isScheduled()) {
|
||||
schedulingOptions = (
|
||||
<span id="mailpoet_scheduling">
|
||||
<DateTime
|
||||
name="scheduledAt"
|
||||
value={this._getCurrentValue().scheduledAt}
|
||||
onChange={this.handleValueChange}
|
||||
dateValidation={this.getDateValidation()} />
|
||||
|
||||
<span>
|
||||
{MailPoet.I18n.t('websiteTimeIs')} <code>{currentTime}</code>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
ref="isScheduled"
|
||||
type="checkbox"
|
||||
value="1"
|
||||
checked={this.isScheduled()}
|
||||
name="isScheduled"
|
||||
onChange={this.handleCheckboxChange} />
|
||||
|
||||
{schedulingOptions}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var fields = [
|
||||
{
|
||||
@ -34,7 +342,7 @@ define(
|
||||
getLabel: function(segment) {
|
||||
var name = segment.name;
|
||||
if (segment.subscribers > 0) {
|
||||
name += ' (%$1s)'.replace('%$1s', segment.subscribers);
|
||||
name += ' (%$1s)'.replace('%$1s', parseInt(segment.subscribers).toLocaleString());
|
||||
}
|
||||
return name;
|
||||
},
|
||||
@ -84,6 +392,12 @@ define(
|
||||
placeholder: MailPoet.I18n.t('replyToAddressPlaceholder')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'options',
|
||||
label: MailPoet.I18n.t('scheduleIt'),
|
||||
type: 'reactComponent',
|
||||
component: StandardScheduling,
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -34,7 +34,7 @@ define(
|
||||
function(segment) {
|
||||
var name = segment.name;
|
||||
if (segment.subscribers > 0) {
|
||||
name += ' (%$1d)'.replace('%$1d', segment.subscribers);
|
||||
name += ' (%$1s)'.replace('%$1s', parseInt(segment.subscribers).toLocaleString());
|
||||
}
|
||||
return [segment.id, name];
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
||||
id: null,
|
||||
positionAfter: false,
|
||||
scroll: false,
|
||||
timeout: 2000,
|
||||
timeout: 5000,
|
||||
onOpen: null,
|
||||
onClose: null
|
||||
},
|
||||
|
@ -1,11 +1,13 @@
|
||||
define(
|
||||
[
|
||||
'react',
|
||||
'react-router',
|
||||
'mailpoet',
|
||||
'form/form.jsx'
|
||||
],
|
||||
function(
|
||||
React,
|
||||
Router,
|
||||
MailPoet,
|
||||
Form
|
||||
) {
|
||||
@ -33,13 +35,16 @@ define(
|
||||
}
|
||||
};
|
||||
|
||||
var Link = Router.Link;
|
||||
|
||||
const SegmentForm = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
<h1 className="title">
|
||||
{MailPoet.I18n.t('segment')}
|
||||
</h2>
|
||||
<Link className="page-title-action" to="/">{MailPoet.I18n.t('backToList')}</Link>
|
||||
</h1>
|
||||
|
||||
<Form
|
||||
endpoint="segments"
|
||||
|
@ -88,10 +88,17 @@ const messages = {
|
||||
}
|
||||
};
|
||||
|
||||
const bulk_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('trash'),
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
|
||||
const item_actions = [
|
||||
{
|
||||
name: 'edit',
|
||||
label: MailPoet.I18n.t('edit'),
|
||||
link: function(item) {
|
||||
return (
|
||||
<Link to={ `/edit/${item.id}` }>{MailPoet.I18n.t('edit')}</Link>
|
||||
@ -103,7 +110,7 @@ const item_actions = [
|
||||
},
|
||||
{
|
||||
name: 'duplicate_segment',
|
||||
label: 'Duplicate',
|
||||
label: MailPoet.I18n.t('duplicate'),
|
||||
onClick: function(item, refresh) {
|
||||
return MailPoet.Ajax.post({
|
||||
endpoint: 'segments',
|
||||
@ -120,10 +127,23 @@ const item_actions = [
|
||||
return (segment.type !== 'wp_users');
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'read_more',
|
||||
link: function(item) {
|
||||
return (
|
||||
<a
|
||||
href="https://www.mailpoet.com/#TODO"
|
||||
target="_blank"
|
||||
>{MailPoet.I18n.t('readMore')}</a>
|
||||
);
|
||||
},
|
||||
display: function(segment) {
|
||||
return (segment.type === 'wp_users');
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'synchronize_segment',
|
||||
label: MailPoet.I18n.t('update'),
|
||||
className: 'update',
|
||||
label: MailPoet.I18n.t('forceSync'),
|
||||
onClick: function(item, refresh) {
|
||||
MailPoet.Modal.loading(true);
|
||||
MailPoet.Ajax.post({
|
||||
@ -166,28 +186,42 @@ const SegmentList = React.createClass({
|
||||
'column-primary',
|
||||
'has-row-actions'
|
||||
);
|
||||
|
||||
const subscribed = ~~(segment.subscribers_count.subscribed || 0);
|
||||
const unconfirmed = ~~(segment.subscribers_count.unconfirmed || 0);
|
||||
const unsubscribed = ~~(segment.subscribers_count.unsubscribed || 0);
|
||||
|
||||
let segment_name = (
|
||||
<Link to={ `/edit/${segment.id}` }>{ segment.name }</Link>
|
||||
);
|
||||
|
||||
// the WP users segment is not editable so just display its name
|
||||
if (segment.type === 'wp_users') {
|
||||
segment_name = segment.name;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<td className={ rowClasses }>
|
||||
<strong>
|
||||
<a>{ segment.name }</a>
|
||||
{ segment_name }
|
||||
</strong>
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column-date" data-colname="Description">
|
||||
<td className="column-date" data-colname={ MailPoet.I18n.t('description') }>
|
||||
<abbr>{ segment.description }</abbr>
|
||||
</td>
|
||||
<td className="column-date" data-colname="Subscribed">
|
||||
<abbr>{ segment.subscribers_count.subscribed || 0 }</abbr>
|
||||
<td className="column-date" data-colname={ MailPoet.I18n.t('subscribed') }>
|
||||
<abbr>{ subscribed.toLocaleString() }</abbr>
|
||||
</td>
|
||||
<td className="column-date" data-colname="Unconfirmed">
|
||||
<abbr>{ segment.subscribers_count.unconfirmed || 0 }</abbr>
|
||||
<td className="column-date" data-colname={ MailPoet.I18n.t('unconfirmed') }>
|
||||
<abbr>{ unconfirmed.toLocaleString() }</abbr>
|
||||
</td>
|
||||
<td className="column-date" data-colname="Unsubscribed">
|
||||
<abbr>{ segment.subscribers_count.unsubscribed || 0 }</abbr>
|
||||
<td className="column-date" data-colname={ MailPoet.I18n.t('unsubscribed') }>
|
||||
<abbr>{ unsubscribed.toLocaleString() }</abbr>
|
||||
</td>
|
||||
<td className="column-date" data-colname="Created on">
|
||||
<abbr>{ MailPoet.Date.full(segment.created_at) }</abbr>
|
||||
<td className="column-date" data-colname={ MailPoet.I18n.t('createdOn') }>
|
||||
<abbr>{ MailPoet.Date.format(segment.created_at) }</abbr>
|
||||
</td>
|
||||
</div>
|
||||
);
|
||||
@ -195,19 +229,20 @@ const SegmentList = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
{MailPoet.I18n.t('pageTitle')} <Link className="add-new-h2" to="/new">{MailPoet.I18n.t('new')}</Link>
|
||||
</h2>
|
||||
<h1 className="title">
|
||||
{MailPoet.I18n.t('pageTitle')} <Link className="page-title-action" to="/new">{MailPoet.I18n.t('new')}</Link>
|
||||
</h1>
|
||||
|
||||
<Listing
|
||||
limit={ mailpoet_listing_per_page }
|
||||
location={ this.props.location }
|
||||
params={ this.props.params }
|
||||
messages={ messages }
|
||||
search={ false }
|
||||
limit={ 1000 }
|
||||
endpoint="segments"
|
||||
onRenderItem={ this.renderItem }
|
||||
columns={ columns }
|
||||
bulk_actions={ bulk_actions }
|
||||
item_actions={ item_actions }
|
||||
/>
|
||||
</div>
|
||||
|
@ -3,38 +3,55 @@ define(
|
||||
'react',
|
||||
'react-router',
|
||||
'mailpoet',
|
||||
'form/form.jsx'
|
||||
'form/form.jsx',
|
||||
'react-string-replace'
|
||||
],
|
||||
function(
|
||||
React,
|
||||
Router,
|
||||
MailPoet,
|
||||
Form
|
||||
Form,
|
||||
ReactStringReplace
|
||||
) {
|
||||
var fields = [
|
||||
{
|
||||
name: 'email',
|
||||
label: MailPoet.I18n.t('email'),
|
||||
type: 'text'
|
||||
type: 'text',
|
||||
disabled: function(subscriber) {
|
||||
return ~~(subscriber.wp_user_id > 0);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'first_name',
|
||||
label: MailPoet.I18n.t('firstname'),
|
||||
type: 'text'
|
||||
type: 'text',
|
||||
disabled: function(subscriber) {
|
||||
return ~~(subscriber.wp_user_id > 0);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'last_name',
|
||||
label: MailPoet.I18n.t('lastname'),
|
||||
type: 'text'
|
||||
type: 'text',
|
||||
disabled: function(subscriber) {
|
||||
return ~~(subscriber.wp_user_id > 0);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
label: MailPoet.I18n.t('status'),
|
||||
type: 'select',
|
||||
values: {
|
||||
'unconfirmed': MailPoet.I18n.t('unconfirmed'),
|
||||
'subscribed': MailPoet.I18n.t('subscribed'),
|
||||
'unconfirmed': MailPoet.I18n.t('unconfirmed'),
|
||||
'unsubscribed': MailPoet.I18n.t('unsubscribed')
|
||||
},
|
||||
filter: function(subscriber, value) {
|
||||
if (~~(subscriber.wp_user_id) > 0 && value === 'unconfirmed') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -100,6 +117,19 @@ define(
|
||||
field.values = custom_field.params.values;
|
||||
}
|
||||
|
||||
// add placeholders for selects (date, select)
|
||||
switch(custom_field.type) {
|
||||
case 'date':
|
||||
field.year_placeholder = MailPoet.I18n.t('year');
|
||||
field.month_placeholder = MailPoet.I18n.t('month');
|
||||
field.day_placeholder = MailPoet.I18n.t('day');
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
field.placeholder = '-';
|
||||
break;
|
||||
}
|
||||
|
||||
fields.push(field);
|
||||
});
|
||||
|
||||
@ -112,21 +142,54 @@ define(
|
||||
}
|
||||
};
|
||||
|
||||
var beforeFormContent = function(subscriber) {
|
||||
if (~~(subscriber.wp_user_id) > 0) {
|
||||
return (
|
||||
<p className="description">
|
||||
{ ReactStringReplace(
|
||||
MailPoet.I18n.t('WPUserEditNotice'),
|
||||
/\[link\](.*?)\[\/link\]/g,
|
||||
(match, i) => (
|
||||
<a
|
||||
key={ i }
|
||||
href={`user-edit.php?user_id=${ subscriber.wp_user_id }`}
|
||||
>{ match }</a>
|
||||
)
|
||||
)
|
||||
}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
var afterFormContent = function(subscriber) {
|
||||
return (
|
||||
<p className="description">
|
||||
<strong>
|
||||
{ MailPoet.I18n.t('tip') }
|
||||
</strong> { MailPoet.I18n.t('customFieldsTip') }
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
var Link = Router.Link;
|
||||
|
||||
var SubscriberForm = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
<h1 className="title">
|
||||
{MailPoet.I18n.t('subscriber')}
|
||||
</h2>
|
||||
<Link className="page-title-action" to="/">{MailPoet.I18n.t('backToList')}</Link>
|
||||
</h1>
|
||||
|
||||
<Form
|
||||
endpoint="subscribers"
|
||||
fields={ fields }
|
||||
params={ this.props.params }
|
||||
messages={ messages }
|
||||
beforeFormContent={ beforeFormContent }
|
||||
afterFormContent={ afterFormContent }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -43,12 +43,12 @@ define(
|
||||
width: '20em',
|
||||
templateResult: function (item) {
|
||||
return (item.subscriberCount > 0)
|
||||
? item.name + ' (' + item.subscriberCount + ')'
|
||||
? item.name + ' (' + parseInt(item.subscriberCount).toLocaleString() + ')'
|
||||
: item.name;
|
||||
},
|
||||
templateSelection: function (item) {
|
||||
return (item.subscriberCount > 0)
|
||||
? item.name + ' (' + item.subscriberCount + ')'
|
||||
? item.name + ' (' + parseInt(item.subscriberCount).toLocaleString() + ')'
|
||||
: item.name;
|
||||
}
|
||||
})
|
||||
@ -151,7 +151,7 @@ define(
|
||||
MailPoet.Notice.error(response.errors);
|
||||
} else {
|
||||
resultMessage = MailPoet.I18n.t('exportMessage')
|
||||
.replace('%1$s', '<strong>' + response.data.totalExported + '</strong>')
|
||||
.replace('%1$s', '<strong>' + parseInt(response.data.totalExported).toLocaleString() + '</strong>')
|
||||
.replace('[link]', '<a href="' + response.data.exportFileURL + '" target="_blank" >')
|
||||
.replace('[/link]', '</a>');
|
||||
jQuery('#export_result_notice > ul > li').html(resultMessage);
|
||||
|
@ -24,7 +24,6 @@ define(
|
||||
return;
|
||||
}
|
||||
jQuery(document).ready(function () {
|
||||
var noticeTimeout = 3000;
|
||||
jQuery('input[name="select_method"]').attr('checked', false);
|
||||
// configure router
|
||||
router = new (Backbone.Router.extend({
|
||||
@ -128,9 +127,7 @@ define(
|
||||
// get an approximate size of textarea paste in bytes
|
||||
var pasteSize = encodeURI(pasteInputElement.val()).split(/%..|./).length - 1;
|
||||
if (pasteSize > maxPostSizeBytes) {
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('maxPostSizeNotice'), {
|
||||
timeout: noticeTimeout,
|
||||
});
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('maxPostSizeNotice'));
|
||||
return;
|
||||
}
|
||||
// delay loading indicator for 10ms or else it's just too fast :)
|
||||
@ -148,9 +145,7 @@ define(
|
||||
var ext = this.value.match(/\.(.+)$/);
|
||||
if (ext === null || ext[1].toLowerCase() !== 'csv') {
|
||||
this.value = '';
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('wrongFileFormat'), {
|
||||
timeout: noticeTimeout,
|
||||
});
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('wrongFileFormat'));
|
||||
}
|
||||
|
||||
toggleNextStepButton(
|
||||
@ -198,9 +193,7 @@ define(
|
||||
}).done(function (response) {
|
||||
if (response.result === false) {
|
||||
MailPoet.Notice.hide();
|
||||
MailPoet.Notice.error(response.errors, {
|
||||
timeout: noticeTimeout,
|
||||
});
|
||||
MailPoet.Notice.error(response.errors);
|
||||
jQuery('.mailpoet_mailchimp-key-status')
|
||||
.removeClass()
|
||||
.addClass('mailpoet_mailchimp-key-status mailpoet_mailchimp-error');
|
||||
@ -223,9 +216,7 @@ define(
|
||||
}).error(function (error) {
|
||||
MailPoet.Modal.loading(false);
|
||||
MailPoet.Notice.error(
|
||||
MailPoet.I18n.t('serverError') + error.statusText.toLowerCase() + '.', {
|
||||
timeout: noticeTimeout,
|
||||
}
|
||||
MailPoet.I18n.t('serverError') + error.statusText.toLowerCase() + '.'
|
||||
);
|
||||
});
|
||||
MailPoet.Modal.loading(false);
|
||||
@ -250,17 +241,13 @@ define(
|
||||
}
|
||||
else {
|
||||
MailPoet.Notice.hide();
|
||||
MailPoet.Notice.error(response.errors, {
|
||||
timeout: noticeTimeout,
|
||||
});
|
||||
MailPoet.Notice.error(response.errors);
|
||||
}
|
||||
MailPoet.Modal.loading(false);
|
||||
}).error(function () {
|
||||
MailPoet.Modal.loading(false);
|
||||
MailPoet.Notice.error(
|
||||
MailPoet.I18n.t('serverError') + result.statusText.toLowerCase() + '.', {
|
||||
timeout: noticeTimeout,
|
||||
}
|
||||
MailPoet.I18n.t('serverError') + result.statusText.toLowerCase() + '.'
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -350,9 +337,7 @@ define(
|
||||
comments: advancedOptionComments,
|
||||
error: function () {
|
||||
MailPoet.Notice.hide();
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('dataProcessingError'), {
|
||||
timeout: noticeTimeout,
|
||||
});
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('dataProcessingError'));
|
||||
},
|
||||
complete: function (CSV) {
|
||||
for (var rowCount in CSV.data) {
|
||||
@ -434,9 +419,7 @@ define(
|
||||
var errorNotice = MailPoet.I18n.t('noValidRecords');
|
||||
errorNotice = errorNotice.replace('[link]', MailPoet.I18n.t('csvKBLink'));
|
||||
errorNotice = errorNotice.replace('[/link]', '</a>');
|
||||
MailPoet.Notice.error(errorNotice, {
|
||||
timeout: noticeTimeout,
|
||||
});
|
||||
MailPoet.Notice.error(errorNotice);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -507,7 +490,7 @@ define(
|
||||
),
|
||||
invalid: (subscribers.invalid.length)
|
||||
? MailPoet.I18n.t('importNoticeInvalid')
|
||||
.replace('%1$s', '<strong>' + subscribers.invalid.length + '</strong>')
|
||||
.replace('%1$s', '<strong>' + subscribers.invalid.length.toLocaleString() + '</strong>')
|
||||
.replace('%2$s', subscribers.invalid.join(', '))
|
||||
: null,
|
||||
duplicate: (subscribers.duplicate.length)
|
||||
@ -550,13 +533,15 @@ define(
|
||||
data: segments,
|
||||
width: '20em',
|
||||
templateResult: function (item) {
|
||||
item.subscriberCount = parseInt(item.subscriberCount);
|
||||
return (item.subscriberCount > 0)
|
||||
? item.name + ' (' + item.subscriberCount + ')'
|
||||
? item.name + ' (' + item.subscriberCount.toLocaleString() + ')'
|
||||
: item.name;
|
||||
},
|
||||
templateSelection: function (item) {
|
||||
item.subscriberCount = parseInt(item.subscriberCount);
|
||||
return (item.subscriberCount > 0)
|
||||
? item.name + ' (' + item.subscriberCount + ')'
|
||||
? item.name + ' (' + item.subscriberCount.toLocaleString() + ')'
|
||||
: item.name;
|
||||
}
|
||||
})
|
||||
@ -566,7 +551,6 @@ define(
|
||||
if (!segmentSelectionNotice.length) {
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('segmentSelectionRequired'), {
|
||||
static: true,
|
||||
timeout: noticeTimeout,
|
||||
scroll: true,
|
||||
id: 'segmentSelection',
|
||||
hideClose: true
|
||||
@ -644,18 +628,14 @@ define(
|
||||
else {
|
||||
MailPoet.Modal.close();
|
||||
MailPoet.Notice.error(
|
||||
MailPoet.I18n.t('segmentCreateError') + response.message + '.', {
|
||||
timeout: noticeTimeout,
|
||||
}
|
||||
MailPoet.I18n.t('segmentCreateError') + response.message + '.'
|
||||
);
|
||||
}
|
||||
})
|
||||
.error(function (error) {
|
||||
MailPoet.Modal.close();
|
||||
MailPoet.Notice.error(
|
||||
MailPoet.I18n.t('serverError') + error.statusText.toLowerCase() + '.', {
|
||||
timeout: noticeTimeout
|
||||
}
|
||||
MailPoet.I18n.t('serverError') + error.statusText.toLowerCase() + '.'
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -733,7 +713,7 @@ define(
|
||||
}
|
||||
// if we're on the last line, show the total count of subscribers data
|
||||
else if (index === (subscribers.subscribers.length - 1)) {
|
||||
return subscribers.subscribersCount;
|
||||
return subscribers.subscribersCount.toLocaleString();
|
||||
} else {
|
||||
return index + 1;
|
||||
}
|
||||
@ -860,18 +840,14 @@ define(
|
||||
filterSubscribers();
|
||||
}
|
||||
else {
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('customFieldCreateError'), {
|
||||
timeout: noticeTimeout,
|
||||
});
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('customFieldCreateError'));
|
||||
}
|
||||
MailPoet.Modal.loading(false);
|
||||
})
|
||||
.error(function (error) {
|
||||
MailPoet.Modal.loading(false);
|
||||
MailPoet.Notice.error(
|
||||
MailPoet.I18n.t('serverError') + error.statusText.toLowerCase() + '.', {
|
||||
timeout: noticeTimeout,
|
||||
}
|
||||
MailPoet.I18n.t('serverError') + error.statusText.toLowerCase() + '.'
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -935,7 +911,6 @@ define(
|
||||
if (!jQuery('[data-id="notice_invalidEmail"]').length) {
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('columnContainsInvalidElement'), {
|
||||
static: true,
|
||||
timeout: noticeTimeout,
|
||||
scroll: true,
|
||||
hideClose: true,
|
||||
id: 'invalidEmail'
|
||||
@ -1001,7 +976,7 @@ define(
|
||||
data[matchedColumn] +=
|
||||
'<span class="mailpoet_data_match" title="'
|
||||
+ MailPoet.I18n.t('verifyDateMatch') + '">'
|
||||
+ date + '</span>';
|
||||
+ MailPoet.Date.format(date) + '</span>';
|
||||
}
|
||||
else {
|
||||
data[matchedColumn] +=
|
||||
@ -1015,7 +990,6 @@ define(
|
||||
if (preventNextStep && !jQuery('.mailpoet_invalidDate').length) {
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('columnContainsInvalidDate'), {
|
||||
static: true,
|
||||
timeout: noticeTimeout,
|
||||
scroll: true,
|
||||
hideClose: true,
|
||||
id: 'invalidDate'
|
||||
@ -1127,9 +1101,7 @@ define(
|
||||
queue.onComplete(function () {
|
||||
MailPoet.Modal.loading(false);
|
||||
if (importResults.errors.length > 0 && !importResults.updated && !importResults.created) {
|
||||
MailPoet.Notice.error(_.flatten(importResults.errors), {
|
||||
timeout: noticeTimeout,
|
||||
}
|
||||
MailPoet.Notice.error(_.flatten(importResults.errors)
|
||||
);
|
||||
}
|
||||
else {
|
||||
@ -1140,6 +1112,7 @@ define(
|
||||
});
|
||||
importData.step2 = importResults;
|
||||
enableSegmentSelection(mailpoetSegments);
|
||||
toggleNextStepButton('off');
|
||||
router.navigate('step3', {trigger: true});
|
||||
}
|
||||
});
|
||||
@ -1158,9 +1131,7 @@ define(
|
||||
showCurrentStep();
|
||||
|
||||
if (importData.step2.errors.length > 0) {
|
||||
MailPoet.Notice.error(_.flatten(importData.step2.errors), {
|
||||
timeout: noticeTimeout,
|
||||
});
|
||||
MailPoet.Notice.error(_.flatten(importData.step2.errors));
|
||||
}
|
||||
|
||||
// display statistics
|
||||
@ -1172,12 +1143,12 @@ define(
|
||||
importResults = {
|
||||
created: (importData.step2.created)
|
||||
? MailPoet.I18n.t('subscribersCreated')
|
||||
.replace('%1$s', '<strong>' + importData.step2.created + '</strong>')
|
||||
.replace('%1$s', '<strong>' + importData.step2.created.toLocaleString() + '</strong>')
|
||||
.replace('%2$s', '"' + importData.step2.segments.join('", "') + '"')
|
||||
: false,
|
||||
updated: (importData.step2.updated)
|
||||
? MailPoet.I18n.t('subscribersUpdated')
|
||||
.replace('%1$s', '<strong>' + importData.step2.updated + '</strong>')
|
||||
.replace('%1$s', '<strong>' + importData.step2.updated.toLocaleString() + '</strong>')
|
||||
.replace('%2$s', '"' + importData.step2.segments.join('", "') + '"')
|
||||
: false,
|
||||
noaction: (!importData.step2.updated && !importData.step2.created)
|
||||
|
@ -106,6 +106,11 @@ const bulk_actions = [
|
||||
return !!(
|
||||
!segment.deleted_at && segment.type === 'default'
|
||||
);
|
||||
},
|
||||
getLabel: function (segment) {
|
||||
return (segment.subscribers > 0) ?
|
||||
segment.name + ' (' + parseInt(segment.subscribers).toLocaleString() + ')' :
|
||||
segment.name;
|
||||
}
|
||||
};
|
||||
|
||||
@ -137,6 +142,11 @@ const bulk_actions = [
|
||||
return !!(
|
||||
!segment.deleted_at && segment.type === 'default'
|
||||
);
|
||||
},
|
||||
getLabel: function (segment) {
|
||||
return (segment.subscribers > 0) ?
|
||||
segment.name + ' (' + parseInt(segment.subscribers).toLocaleString() + ')' :
|
||||
segment.name;
|
||||
}
|
||||
};
|
||||
|
||||
@ -168,6 +178,11 @@ const bulk_actions = [
|
||||
return !!(
|
||||
segment.type === 'default'
|
||||
);
|
||||
},
|
||||
getLabel: function (segment) {
|
||||
return (segment.subscribers > 0) ?
|
||||
segment.name + ' (' + parseInt(segment.subscribers).toLocaleString() + ')' :
|
||||
segment.name;
|
||||
}
|
||||
};
|
||||
|
||||
@ -198,16 +213,6 @@ const bulk_actions = [
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'confirmUnconfirmed',
|
||||
label: MailPoet.I18n.t('confirmUnconfirmed'),
|
||||
onSuccess: function(response) {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('multipleSubscribersConfirmed')
|
||||
.replace('%$1d', ~~response)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'sendConfirmationEmail',
|
||||
label: MailPoet.I18n.t('resendConfirmationEmail'),
|
||||
@ -275,10 +280,15 @@ const SubscriberList = React.createClass({
|
||||
}
|
||||
|
||||
let segments = false;
|
||||
let subscribed_segments = [];
|
||||
|
||||
// WordPress Users
|
||||
if (~~(subscriber.wp_user_id) > 0) {
|
||||
subscribed_segments.push(MailPoet.I18n.t('WPUsersSegment'));
|
||||
}
|
||||
|
||||
// Subscriptions
|
||||
if (subscriber.subscriptions.length > 0) {
|
||||
let subscribed_segments = [];
|
||||
|
||||
subscriber.subscriptions.map((subscription) => {
|
||||
const segment = this.getSegmentFromId(subscription.segment_id);
|
||||
if(segment === false) return;
|
||||
@ -286,15 +296,14 @@ const SubscriberList = React.createClass({
|
||||
subscribed_segments.push(segment.name);
|
||||
}
|
||||
});
|
||||
segments = (
|
||||
<span>
|
||||
<span className="mailpoet_segments_subscribed">
|
||||
{ subscribed_segments.join(', ') }
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
segments = (
|
||||
<span>
|
||||
{ subscribed_segments.join(', ') }
|
||||
</span>
|
||||
);
|
||||
|
||||
let avatar = false;
|
||||
if(subscriber.avatar_url) {
|
||||
avatar = (
|
||||
@ -326,10 +335,10 @@ const SubscriberList = React.createClass({
|
||||
{ segments }
|
||||
</td>
|
||||
<td className="column-date" data-colname={MailPoet.I18n.t('subscribedOn')}>
|
||||
<abbr>{ MailPoet.Date.full(subscriber.created_at) }</abbr>
|
||||
<abbr>{ MailPoet.Date.format(subscriber.created_at) }</abbr>
|
||||
</td>
|
||||
<td className="column-date" data-colname={MailPoet.I18n.t('lastModifiedOn')}>
|
||||
<abbr>{ MailPoet.Date.full(subscriber.updated_at) }</abbr>
|
||||
<abbr>{ MailPoet.Date.format(subscriber.updated_at) }</abbr>
|
||||
</td>
|
||||
</div>
|
||||
);
|
||||
@ -340,11 +349,11 @@ const SubscriberList = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
{MailPoet.I18n.t('pageTitle')} <Link className="add-new-h2" to="/new">{MailPoet.I18n.t('new')}</Link>
|
||||
<a className="add-new-h2" href="?page=mailpoet-import#step1">{MailPoet.I18n.t('import')}</a>
|
||||
<a id="mailpoet_export_button" className="add-new-h2" href="?page=mailpoet-export">{MailPoet.I18n.t('export')}</a>
|
||||
</h2>
|
||||
<h1 className="title">
|
||||
{MailPoet.I18n.t('pageTitle')} <Link className="page-title-action" to="/new">{MailPoet.I18n.t('new')}</Link>
|
||||
<a className="page-title-action" href="?page=mailpoet-import#step1">{MailPoet.I18n.t('import')}</a>
|
||||
<a id="mailpoet_export_button" className="page-title-action" href="?page=mailpoet-export">{MailPoet.I18n.t('export')}</a>
|
||||
</h1>
|
||||
|
||||
<Listing
|
||||
limit={ mailpoet_listing_per_page }
|
||||
|
262
composer.lock
generated
@ -251,16 +251,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpmailer/phpmailer",
|
||||
"version": "v5.2.14",
|
||||
"version": "v5.2.15",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHPMailer/PHPMailer.git",
|
||||
"reference": "e774bc9152de85547336e22b8926189e582ece95"
|
||||
"reference": "d0186171b28af4f06ac2ad8a84a8f3d6cbc3ba6c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/e774bc9152de85547336e22b8926189e582ece95",
|
||||
"reference": "e774bc9152de85547336e22b8926189e582ece95",
|
||||
"url": "https://api.github.com/repos/PHPMailer/PHPMailer/zipball/d0186171b28af4f06ac2ad8a84a8f3d6cbc3ba6c",
|
||||
"reference": "d0186171b28af4f06ac2ad8a84a8f3d6cbc3ba6c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -271,8 +271,7 @@
|
||||
"phpunit/phpunit": "4.7.*"
|
||||
},
|
||||
"suggest": {
|
||||
"league/oauth2-client": "Needed for XOAUTH2 authentication",
|
||||
"league/oauth2-google": "Needed for Gmail XOAUTH2"
|
||||
"league/oauth2-google": "Needed for Google XOAUTH2 authentication"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@ -308,7 +307,7 @@
|
||||
}
|
||||
],
|
||||
"description": "PHPMailer is a full-featured email creation and transfer class for PHP",
|
||||
"time": "2015-11-01 10:15:28"
|
||||
"time": "2016-05-10 18:39:36"
|
||||
},
|
||||
{
|
||||
"name": "phpseclib/phpseclib",
|
||||
@ -494,16 +493,16 @@
|
||||
},
|
||||
{
|
||||
"name": "swiftmailer/swiftmailer",
|
||||
"version": "v5.4.1",
|
||||
"version": "v5.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/swiftmailer/swiftmailer.git",
|
||||
"reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421"
|
||||
"reference": "d8db871a54619458a805229a057ea2af33c753e8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/0697e6aa65c83edf97bb0f23d8763f94e3f11421",
|
||||
"reference": "0697e6aa65c83edf97bb0f23d8763f94e3f11421",
|
||||
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/d8db871a54619458a805229a057ea2af33c753e8",
|
||||
"reference": "d8db871a54619458a805229a057ea2af33c753e8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -543,7 +542,7 @@
|
||||
"mail",
|
||||
"mailer"
|
||||
],
|
||||
"time": "2015-06-06 14:19:39"
|
||||
"time": "2016-05-01 08:45:47"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
@ -606,16 +605,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/translation",
|
||||
"version": "v2.8.3",
|
||||
"version": "v2.8.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/translation.git",
|
||||
"reference": "b7b4ebadd2b5e614ff7d2d6fc63e0ed0578909c7"
|
||||
"reference": "d60b8e076d22953aabebeebda53bf334438e7aca"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/b7b4ebadd2b5e614ff7d2d6fc63e0ed0578909c7",
|
||||
"reference": "b7b4ebadd2b5e614ff7d2d6fc63e0ed0578909c7",
|
||||
"url": "https://api.github.com/repos/symfony/translation/zipball/d60b8e076d22953aabebeebda53bf334438e7aca",
|
||||
"reference": "d60b8e076d22953aabebeebda53bf334438e7aca",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -666,7 +665,7 @@
|
||||
],
|
||||
"description": "Symfony Translation Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-02-02 09:49:18"
|
||||
"time": "2016-03-25 01:40:30"
|
||||
},
|
||||
{
|
||||
"name": "tburry/pquery",
|
||||
@ -785,16 +784,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "codeception/codeception",
|
||||
"version": "2.1.7",
|
||||
"version": "2.1.8",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Codeception/Codeception.git",
|
||||
"reference": "65971b0dee4972710365b6102154cd412a9bf7b1"
|
||||
"reference": "f3daa61f0f11c531b33eb3623ab0daa599d88a79"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Codeception/Codeception/zipball/65971b0dee4972710365b6102154cd412a9bf7b1",
|
||||
"reference": "65971b0dee4972710365b6102154cd412a9bf7b1",
|
||||
"url": "https://api.github.com/repos/Codeception/Codeception/zipball/f3daa61f0f11c531b33eb3623ab0daa599d88a79",
|
||||
"reference": "f3daa61f0f11c531b33eb3623ab0daa599d88a79",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -862,7 +861,7 @@
|
||||
"functional testing",
|
||||
"unit testing"
|
||||
],
|
||||
"time": "2016-03-12 01:15:25"
|
||||
"time": "2016-04-15 02:56:43"
|
||||
},
|
||||
{
|
||||
"name": "codeception/verify",
|
||||
@ -899,16 +898,16 @@
|
||||
},
|
||||
{
|
||||
"name": "codegyre/robo",
|
||||
"version": "0.7.1",
|
||||
"version": "0.7.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Codegyre/Robo.git",
|
||||
"reference": "1f4e0621fdc37521e2eaca67f33d1c4e240dc7d8"
|
||||
"reference": "9982edecb19f59420031dd9855b0d31e861b8b68"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Codegyre/Robo/zipball/1f4e0621fdc37521e2eaca67f33d1c4e240dc7d8",
|
||||
"reference": "1f4e0621fdc37521e2eaca67f33d1c4e240dc7d8",
|
||||
"url": "https://api.github.com/repos/Codegyre/Robo/zipball/9982edecb19f59420031dd9855b0d31e861b8b68",
|
||||
"reference": "9982edecb19f59420031dd9855b0d31e861b8b68",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -950,7 +949,7 @@
|
||||
}
|
||||
],
|
||||
"description": "Modern task runner",
|
||||
"time": "2016-02-25 19:28:51"
|
||||
"time": "2016-04-13 20:14:17"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/instantiator",
|
||||
@ -1051,16 +1050,16 @@
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "6.1.1",
|
||||
"version": "6.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "c6851d6e48f63b69357cbfa55bca116448140e0c"
|
||||
"reference": "d094e337976dff9d8e2424e8485872194e768662"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/c6851d6e48f63b69357cbfa55bca116448140e0c",
|
||||
"reference": "c6851d6e48f63b69357cbfa55bca116448140e0c",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/d094e337976dff9d8e2424e8485872194e768662",
|
||||
"reference": "d094e337976dff9d8e2424e8485872194e768662",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1076,7 +1075,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "6.1-dev"
|
||||
"dev-master": "6.2-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@ -1109,7 +1108,7 @@
|
||||
"rest",
|
||||
"web service"
|
||||
],
|
||||
"time": "2015-11-23 00:47:50"
|
||||
"time": "2016-03-21 20:02:09"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
@ -1164,16 +1163,16 @@
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "1.2.3",
|
||||
"version": "1.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "2e89629ff057ebb49492ba08e6995d3a6a80021b"
|
||||
"reference": "31382fef2889136415751badebbd1cb022a4ed72"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/2e89629ff057ebb49492ba08e6995d3a6a80021b",
|
||||
"reference": "2e89629ff057ebb49492ba08e6995d3a6a80021b",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/31382fef2889136415751badebbd1cb022a4ed72",
|
||||
"reference": "31382fef2889136415751badebbd1cb022a4ed72",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -1218,7 +1217,7 @@
|
||||
"stream",
|
||||
"uri"
|
||||
],
|
||||
"time": "2016-02-18 21:54:00"
|
||||
"time": "2016-04-13 19:56:01"
|
||||
},
|
||||
{
|
||||
"name": "henrikbjorn/lurker",
|
||||
@ -1976,16 +1975,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/environment",
|
||||
"version": "1.3.5",
|
||||
"version": "1.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/environment.git",
|
||||
"reference": "dc7a29032cf72b54f36dac15a1ca5b3a1b6029bf"
|
||||
"reference": "2292b116f43c272ff4328083096114f84ea46a56"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/dc7a29032cf72b54f36dac15a1ca5b3a1b6029bf",
|
||||
"reference": "dc7a29032cf72b54f36dac15a1ca5b3a1b6029bf",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/2292b116f43c272ff4328083096114f84ea46a56",
|
||||
"reference": "2292b116f43c272ff4328083096114f84ea46a56",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2022,7 +2021,7 @@
|
||||
"environment",
|
||||
"hhvm"
|
||||
],
|
||||
"time": "2016-02-26 18:40:46"
|
||||
"time": "2016-05-04 07:59:13"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
@ -2231,16 +2230,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/browser-kit",
|
||||
"version": "v3.0.3",
|
||||
"version": "v3.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/browser-kit.git",
|
||||
"reference": "dde849a0485b70a24b36f826ed3fb95b904d80c3"
|
||||
"reference": "e07127ac31230b30887c2dddf3708d883d239b14"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/dde849a0485b70a24b36f826ed3fb95b904d80c3",
|
||||
"reference": "dde849a0485b70a24b36f826ed3fb95b904d80c3",
|
||||
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/e07127ac31230b30887c2dddf3708d883d239b14",
|
||||
"reference": "e07127ac31230b30887c2dddf3708d883d239b14",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2284,20 +2283,20 @@
|
||||
],
|
||||
"description": "Symfony BrowserKit Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-01-27 11:34:55"
|
||||
"time": "2016-03-04 07:55:57"
|
||||
},
|
||||
{
|
||||
"name": "symfony/config",
|
||||
"version": "v2.8.3",
|
||||
"version": "v2.8.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/config.git",
|
||||
"reference": "0f8f94e6a32b5c480024eed5fa5cbd2790d0ad19"
|
||||
"reference": "edbbcf33cffa2a85104fc80de8dc052cc51596bb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/config/zipball/0f8f94e6a32b5c480024eed5fa5cbd2790d0ad19",
|
||||
"reference": "0f8f94e6a32b5c480024eed5fa5cbd2790d0ad19",
|
||||
"url": "https://api.github.com/repos/symfony/config/zipball/edbbcf33cffa2a85104fc80de8dc052cc51596bb",
|
||||
"reference": "edbbcf33cffa2a85104fc80de8dc052cc51596bb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2337,20 +2336,20 @@
|
||||
],
|
||||
"description": "Symfony Config Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-02-22 16:12:45"
|
||||
"time": "2016-04-20 18:52:26"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v3.0.3",
|
||||
"version": "v3.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "2ed5e2706ce92313d120b8fe50d1063bcfd12e04"
|
||||
"reference": "34a214710e0714b6efcf40ba3cd1e31373a97820"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/2ed5e2706ce92313d120b8fe50d1063bcfd12e04",
|
||||
"reference": "2ed5e2706ce92313d120b8fe50d1063bcfd12e04",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/34a214710e0714b6efcf40ba3cd1e31373a97820",
|
||||
"reference": "34a214710e0714b6efcf40ba3cd1e31373a97820",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2397,20 +2396,20 @@
|
||||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-02-28 16:24:34"
|
||||
"time": "2016-04-28 09:48:42"
|
||||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
"version": "v3.0.3",
|
||||
"version": "v3.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/css-selector.git",
|
||||
"reference": "6605602690578496091ac20ec7a5cbd160d4dff4"
|
||||
"reference": "65e764f404685f2dc20c057e889b3ad04b2e2db0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/6605602690578496091ac20ec7a5cbd160d4dff4",
|
||||
"reference": "6605602690578496091ac20ec7a5cbd160d4dff4",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/65e764f404685f2dc20c057e889b3ad04b2e2db0",
|
||||
"reference": "65e764f404685f2dc20c057e889b3ad04b2e2db0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2450,20 +2449,20 @@
|
||||
],
|
||||
"description": "Symfony CssSelector Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-01-27 05:14:46"
|
||||
"time": "2016-03-04 07:55:57"
|
||||
},
|
||||
{
|
||||
"name": "symfony/dom-crawler",
|
||||
"version": "v3.0.3",
|
||||
"version": "v3.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/dom-crawler.git",
|
||||
"reference": "981c8edb4538f88ba976ed44bdcaa683fce3d6c6"
|
||||
"reference": "49b588841225b205700e5122fa01911cabada857"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/981c8edb4538f88ba976ed44bdcaa683fce3d6c6",
|
||||
"reference": "981c8edb4538f88ba976ed44bdcaa683fce3d6c6",
|
||||
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/49b588841225b205700e5122fa01911cabada857",
|
||||
"reference": "49b588841225b205700e5122fa01911cabada857",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2506,20 +2505,20 @@
|
||||
],
|
||||
"description": "Symfony DomCrawler Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-02-28 16:24:34"
|
||||
"time": "2016-04-12 18:09:53"
|
||||
},
|
||||
{
|
||||
"name": "symfony/event-dispatcher",
|
||||
"version": "v2.8.3",
|
||||
"version": "v2.8.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/event-dispatcher.git",
|
||||
"reference": "78c468665c9568c3faaa9c416a7134308f2d85c3"
|
||||
"reference": "a158f13992a3147d466af7a23b564ac719a4ddd8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/78c468665c9568c3faaa9c416a7134308f2d85c3",
|
||||
"reference": "78c468665c9568c3faaa9c416a7134308f2d85c3",
|
||||
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a158f13992a3147d466af7a23b564ac719a4ddd8",
|
||||
"reference": "a158f13992a3147d466af7a23b564ac719a4ddd8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2566,20 +2565,20 @@
|
||||
],
|
||||
"description": "Symfony EventDispatcher Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-01-27 05:14:19"
|
||||
"time": "2016-05-03 18:59:18"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v2.8.3",
|
||||
"version": "v2.8.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "65cb36b6539b1d446527d60457248f30d045464d"
|
||||
"reference": "dee379131dceed90a429e951546b33edfe7dccbb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/65cb36b6539b1d446527d60457248f30d045464d",
|
||||
"reference": "65cb36b6539b1d446527d60457248f30d045464d",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/dee379131dceed90a429e951546b33edfe7dccbb",
|
||||
"reference": "dee379131dceed90a429e951546b33edfe7dccbb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2615,20 +2614,20 @@
|
||||
],
|
||||
"description": "Symfony Filesystem Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-02-22 15:02:30"
|
||||
"time": "2016-04-12 18:01:21"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v3.0.3",
|
||||
"version": "v3.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/finder.git",
|
||||
"reference": "623bda0abd9aa29e529c8e9c08b3b84171914723"
|
||||
"reference": "c54e407b35bc098916704e9fd090da21da4c4f52"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/623bda0abd9aa29e529c8e9c08b3b84171914723",
|
||||
"reference": "623bda0abd9aa29e529c8e9c08b3b84171914723",
|
||||
"url": "https://api.github.com/repos/symfony/finder/zipball/c54e407b35bc098916704e9fd090da21da4c4f52",
|
||||
"reference": "c54e407b35bc098916704e9fd090da21da4c4f52",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2664,20 +2663,20 @@
|
||||
],
|
||||
"description": "Symfony Finder Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-01-27 05:14:46"
|
||||
"time": "2016-03-10 11:13:05"
|
||||
},
|
||||
{
|
||||
"name": "symfony/form",
|
||||
"version": "v2.8.3",
|
||||
"version": "v2.8.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/form.git",
|
||||
"reference": "25b71aec36879b4a84c2ea06603dbe7498b31f06"
|
||||
"reference": "922807a06e25c0c6e06b86f83ffe4710080a8b2e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/form/zipball/25b71aec36879b4a84c2ea06603dbe7498b31f06",
|
||||
"reference": "25b71aec36879b4a84c2ea06603dbe7498b31f06",
|
||||
"url": "https://api.github.com/repos/symfony/form/zipball/922807a06e25c0c6e06b86f83ffe4710080a8b2e",
|
||||
"reference": "922807a06e25c0c6e06b86f83ffe4710080a8b2e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2738,20 +2737,20 @@
|
||||
],
|
||||
"description": "Symfony Form Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-02-28 15:05:09"
|
||||
"time": "2016-04-28 09:59:09"
|
||||
},
|
||||
{
|
||||
"name": "symfony/intl",
|
||||
"version": "v3.0.3",
|
||||
"version": "v3.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/intl.git",
|
||||
"reference": "cafee6f65148dab9058cdb6de5631222aca820d0"
|
||||
"reference": "a602fe5a36cfc6367c5495b38a88c443570e75da"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/intl/zipball/cafee6f65148dab9058cdb6de5631222aca820d0",
|
||||
"reference": "cafee6f65148dab9058cdb6de5631222aca820d0",
|
||||
"url": "https://api.github.com/repos/symfony/intl/zipball/a602fe5a36cfc6367c5495b38a88c443570e75da",
|
||||
"reference": "a602fe5a36cfc6367c5495b38a88c443570e75da",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2813,20 +2812,20 @@
|
||||
"l10n",
|
||||
"localization"
|
||||
],
|
||||
"time": "2016-02-23 15:16:06"
|
||||
"time": "2016-04-01 06:34:33"
|
||||
},
|
||||
{
|
||||
"name": "symfony/options-resolver",
|
||||
"version": "v2.8.3",
|
||||
"version": "v2.8.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/options-resolver.git",
|
||||
"reference": "d1e6e9182d9e5af6367bf85175e708f8b4a828c0"
|
||||
"reference": "5e4a8ee6e823428257f2002f6daf52de854d8384"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/d1e6e9182d9e5af6367bf85175e708f8b4a828c0",
|
||||
"reference": "d1e6e9182d9e5af6367bf85175e708f8b4a828c0",
|
||||
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/5e4a8ee6e823428257f2002f6daf52de854d8384",
|
||||
"reference": "5e4a8ee6e823428257f2002f6daf52de854d8384",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2867,7 +2866,7 @@
|
||||
"configuration",
|
||||
"options"
|
||||
],
|
||||
"time": "2016-01-21 09:05:51"
|
||||
"time": "2016-05-09 18:12:35"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-icu",
|
||||
@ -2929,16 +2928,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v3.0.3",
|
||||
"version": "v3.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "dfecef47506179db2501430e732adbf3793099c8"
|
||||
"reference": "53f9407c0bb1c5a79127db8f7bfe12f0f6f3dcdb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/dfecef47506179db2501430e732adbf3793099c8",
|
||||
"reference": "dfecef47506179db2501430e732adbf3793099c8",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/53f9407c0bb1c5a79127db8f7bfe12f0f6f3dcdb",
|
||||
"reference": "53f9407c0bb1c5a79127db8f7bfe12f0f6f3dcdb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -2974,20 +2973,20 @@
|
||||
],
|
||||
"description": "Symfony Process Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-02-02 13:44:19"
|
||||
"time": "2016-04-14 15:30:28"
|
||||
},
|
||||
{
|
||||
"name": "symfony/property-access",
|
||||
"version": "v3.0.3",
|
||||
"version": "v3.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/property-access.git",
|
||||
"reference": "f138bcc0cdaf6a6aba99ecab20d235b394f46eba"
|
||||
"reference": "d9deeca5d2f0dffd90f9547d3d9a2007bd6ee61b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/property-access/zipball/f138bcc0cdaf6a6aba99ecab20d235b394f46eba",
|
||||
"reference": "f138bcc0cdaf6a6aba99ecab20d235b394f46eba",
|
||||
"url": "https://api.github.com/repos/symfony/property-access/zipball/d9deeca5d2f0dffd90f9547d3d9a2007bd6ee61b",
|
||||
"reference": "d9deeca5d2f0dffd90f9547d3d9a2007bd6ee61b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3034,20 +3033,20 @@
|
||||
"property path",
|
||||
"reflection"
|
||||
],
|
||||
"time": "2016-02-13 09:23:44"
|
||||
"time": "2016-04-20 18:53:54"
|
||||
},
|
||||
{
|
||||
"name": "symfony/routing",
|
||||
"version": "v2.8.3",
|
||||
"version": "v2.8.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/routing.git",
|
||||
"reference": "ae38e64bae52753c0f4a1a6583f5ba9bb2b742ab"
|
||||
"reference": "0d814e0c1dc07cb688ca2298bbf7b32ace80cee1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/ae38e64bae52753c0f4a1a6583f5ba9bb2b742ab",
|
||||
"reference": "ae38e64bae52753c0f4a1a6583f5ba9bb2b742ab",
|
||||
"url": "https://api.github.com/repos/symfony/routing/zipball/0d814e0c1dc07cb688ca2298bbf7b32ace80cee1",
|
||||
"reference": "0d814e0c1dc07cb688ca2298bbf7b32ace80cee1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3109,20 +3108,20 @@
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"time": "2016-02-04 13:53:00"
|
||||
"time": "2016-05-03 12:21:46"
|
||||
},
|
||||
{
|
||||
"name": "symfony/twig-bridge",
|
||||
"version": "v2.8.3",
|
||||
"version": "v2.8.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/twig-bridge.git",
|
||||
"reference": "ba898e4714734948dc00c4d776e1d6be165ff8c4"
|
||||
"reference": "789ffb6f63574d8d5573520ba12156f5c395fcd5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/ba898e4714734948dc00c4d776e1d6be165ff8c4",
|
||||
"reference": "ba898e4714734948dc00c4d776e1d6be165ff8c4",
|
||||
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/789ffb6f63574d8d5573520ba12156f5c395fcd5",
|
||||
"reference": "789ffb6f63574d8d5573520ba12156f5c395fcd5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3134,7 +3133,7 @@
|
||||
"symfony/console": "~2.8|~3.0.0",
|
||||
"symfony/expression-language": "~2.4|~3.0.0",
|
||||
"symfony/finder": "~2.3|~3.0.0",
|
||||
"symfony/form": "~2.8",
|
||||
"symfony/form": "~2.8.4",
|
||||
"symfony/http-kernel": "~2.8|~3.0.0",
|
||||
"symfony/polyfill-intl-icu": "~1.0",
|
||||
"symfony/routing": "~2.2|~3.0.0",
|
||||
@ -3190,20 +3189,20 @@
|
||||
],
|
||||
"description": "Symfony Twig Bridge",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-02-22 15:02:30"
|
||||
"time": "2016-03-30 10:37:34"
|
||||
},
|
||||
{
|
||||
"name": "symfony/yaml",
|
||||
"version": "v3.0.3",
|
||||
"version": "v3.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/yaml.git",
|
||||
"reference": "b5ba64cd67ecd6887f63868fa781ca094bd1377c"
|
||||
"reference": "0047c8366744a16de7516622c5b7355336afae96"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/b5ba64cd67ecd6887f63868fa781ca094bd1377c",
|
||||
"reference": "b5ba64cd67ecd6887f63868fa781ca094bd1377c",
|
||||
"url": "https://api.github.com/repos/symfony/yaml/zipball/0047c8366744a16de7516622c5b7355336afae96",
|
||||
"reference": "0047c8366744a16de7516622c5b7355336afae96",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -3239,7 +3238,7 @@
|
||||
],
|
||||
"description": "Symfony Yaml Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"time": "2016-02-23 15:16:06"
|
||||
"time": "2016-03-04 07:55:57"
|
||||
},
|
||||
{
|
||||
"name": "twig/extensions",
|
||||
@ -3344,23 +3343,23 @@
|
||||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
"version": "v2.2.0",
|
||||
"version": "v2.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vlucas/phpdotenv.git",
|
||||
"reference": "9caf304153dc2288e4970caec6f1f3b3bc205412"
|
||||
"reference": "63f37b9395e8041cd4313129c08ece896d06ca8e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/9caf304153dc2288e4970caec6f1f3b3bc205412",
|
||||
"reference": "9caf304153dc2288e4970caec6f1f3b3bc205412",
|
||||
"url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/63f37b9395e8041cd4313129c08ece896d06ca8e",
|
||||
"reference": "63f37b9395e8041cd4313129c08ece896d06ca8e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.9"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8|^5.0"
|
||||
"phpunit/phpunit": "^4.8 || ^5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
@ -3375,7 +3374,7 @@
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD"
|
||||
"BSD-3-Clause-Attribution"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
@ -3385,13 +3384,12 @@
|
||||
}
|
||||
],
|
||||
"description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.",
|
||||
"homepage": "http://github.com/vlucas/phpdotenv",
|
||||
"keywords": [
|
||||
"dotenv",
|
||||
"env",
|
||||
"environment"
|
||||
],
|
||||
"time": "2015-12-29 15:10:30"
|
||||
"time": "2016-04-15 10:48:49"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
@ -39,6 +39,9 @@ class Env {
|
||||
self::$assets_url = plugins_url('/assets', $file);
|
||||
$wp_upload_dir = wp_upload_dir();
|
||||
self::$temp_path = $wp_upload_dir['basedir'].'/'.self::$plugin_name;
|
||||
if(!is_dir(self::$temp_path)) {
|
||||
mkdir(self::$temp_path);
|
||||
}
|
||||
self::$temp_url = $wp_upload_dir['baseurl'].'/'.self::$plugin_name;
|
||||
self::$languages_path = self::$path . '/lang';
|
||||
self::$lib_path = self::$path . '/lib';
|
||||
|
@ -13,6 +13,7 @@ class Hooks {
|
||||
$this->setupImageSize();
|
||||
$this->setupListing();
|
||||
$this->setupSubscriptionEvents();
|
||||
$this->setupPostNotifications();
|
||||
}
|
||||
|
||||
function setupSubscriptionEvents() {
|
||||
@ -146,4 +147,12 @@ class Hooks {
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
|
||||
function setupPostNotifications() {
|
||||
add_filter(
|
||||
'publish_post',
|
||||
'\MailPoet\Newsletter\Scheduler\Scheduler::schedulePostNotification',
|
||||
10, 1
|
||||
);
|
||||
}
|
||||
}
|
@ -82,6 +82,7 @@ class Initializer {
|
||||
$newsletter_option_fields = Env::$db_prefix . 'newsletter_option_fields';
|
||||
$newsletter_option = Env::$db_prefix . 'newsletter_option';
|
||||
$newsletter_links = Env::$db_prefix . 'newsletter_links';
|
||||
$newsletter_posts = Env::$db_prefix . 'newsletter_posts';
|
||||
$statistics_newsletters = Env::$db_prefix . 'statistics_newsletters';
|
||||
$statistics_clicks = Env::$db_prefix . 'statistics_clicks';
|
||||
$statistics_opens = Env::$db_prefix . 'statistics_opens';
|
||||
@ -101,6 +102,7 @@ class Initializer {
|
||||
define('MP_NEWSLETTER_SEGMENT_TABLE', $newsletter_segment);
|
||||
define('MP_NEWSLETTER_OPTION_FIELDS_TABLE', $newsletter_option_fields);
|
||||
define('MP_NEWSLETTER_LINKS_TABLE', $newsletter_links);
|
||||
define('MP_NEWSLETTER_POSTS_TABLE', $newsletter_posts);
|
||||
define('MP_NEWSLETTER_OPTION_TABLE', $newsletter_option);
|
||||
define('MP_STATISTICS_NEWSLETTERS_TABLE', $statistics_newsletters);
|
||||
define('MP_STATISTICS_CLICKS_TABLE', $statistics_clicks);
|
||||
|
@ -14,6 +14,7 @@ use MailPoet\Subscribers\ImportExport\BootStrapMenu;
|
||||
use MailPoet\Util\DKIM;
|
||||
use MailPoet\Util\Permissions;
|
||||
use MailPoet\Listing;
|
||||
use MailPoet\WP\DateTime;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -46,9 +47,9 @@ class Menu {
|
||||
$this->assets_url . '/img/menu_icon.png',
|
||||
30
|
||||
);
|
||||
add_submenu_page(
|
||||
$newsletters_page = add_submenu_page(
|
||||
'mailpoet',
|
||||
__('Newsletters'),
|
||||
$this->setPageTitle(__('Newsletters')),
|
||||
__('Newsletters'),
|
||||
'manage_options',
|
||||
'mailpoet-newsletters',
|
||||
@ -57,9 +58,21 @@ class Menu {
|
||||
'newsletters'
|
||||
)
|
||||
);
|
||||
add_submenu_page(
|
||||
|
||||
// add limit per page to screen options
|
||||
add_action('load-'.$newsletters_page, function() {
|
||||
add_screen_option('per_page', array(
|
||||
'label' => _x(
|
||||
'Number of newsletters per page',
|
||||
'newsletters per page (screen options)'
|
||||
),
|
||||
'option' => 'mailpoet_newsletters_per_page'
|
||||
));
|
||||
});
|
||||
|
||||
$forms_page = add_submenu_page(
|
||||
'mailpoet',
|
||||
__('Forms'),
|
||||
$this->setPageTitle(__('Forms')),
|
||||
__('Forms'),
|
||||
'manage_options',
|
||||
'mailpoet-forms',
|
||||
@ -68,9 +81,20 @@ class Menu {
|
||||
'forms'
|
||||
)
|
||||
);
|
||||
// add limit per page to screen options
|
||||
add_action('load-'.$forms_page, function() {
|
||||
add_screen_option('per_page', array(
|
||||
'label' => _x(
|
||||
'Number of forms per page',
|
||||
'forms per page (screen options)'
|
||||
),
|
||||
'option' => 'mailpoet_forms_per_page'
|
||||
));
|
||||
});
|
||||
|
||||
$subscribers_page = add_submenu_page(
|
||||
'mailpoet',
|
||||
__('Subscribers'),
|
||||
$this->setPageTitle(__('Subscribers')),
|
||||
__('Subscribers'),
|
||||
'manage_options',
|
||||
'mailpoet-subscribers',
|
||||
@ -90,9 +114,9 @@ class Menu {
|
||||
));
|
||||
});
|
||||
|
||||
add_submenu_page(
|
||||
$segments_page = add_submenu_page(
|
||||
'mailpoet',
|
||||
__('Segments'),
|
||||
$this->setPageTitle(__('Segments')),
|
||||
__('Segments'),
|
||||
'manage_options',
|
||||
'mailpoet-segments',
|
||||
@ -101,9 +125,21 @@ class Menu {
|
||||
'segments'
|
||||
)
|
||||
);
|
||||
|
||||
// add limit per page to screen options
|
||||
add_action('load-'.$segments_page, function() {
|
||||
add_screen_option('per_page', array(
|
||||
'label' => _x(
|
||||
'Number of segments per page',
|
||||
'segments per page (screen options)'
|
||||
),
|
||||
'option' => 'mailpoet_segments_per_page'
|
||||
));
|
||||
});
|
||||
|
||||
add_submenu_page(
|
||||
'mailpoet',
|
||||
__('Settings'),
|
||||
$this->setPageTitle( __('Settings')),
|
||||
__('Settings'),
|
||||
'manage_options',
|
||||
'mailpoet-settings',
|
||||
@ -113,8 +149,8 @@ class Menu {
|
||||
)
|
||||
);
|
||||
add_submenu_page(
|
||||
null,
|
||||
__('Import'),
|
||||
'admin.php?page=mailpoet-subscribers',
|
||||
$this->setPageTitle( __('Import')),
|
||||
__('Import'),
|
||||
'manage_options',
|
||||
'mailpoet-import',
|
||||
@ -123,9 +159,10 @@ class Menu {
|
||||
'import'
|
||||
)
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
null,
|
||||
__('Export'),
|
||||
true,
|
||||
$this->setPageTitle(__('Export')),
|
||||
__('Export'),
|
||||
'manage_options',
|
||||
'mailpoet-export',
|
||||
@ -136,8 +173,8 @@ class Menu {
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
null,
|
||||
__('Welcome'),
|
||||
true,
|
||||
$this->setPageTitle(__('Welcome')),
|
||||
__('Welcome'),
|
||||
'manage_options',
|
||||
'mailpoet-welcome',
|
||||
@ -148,8 +185,8 @@ class Menu {
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
null,
|
||||
__('Update'),
|
||||
true,
|
||||
$this->setPageTitle(__('Update')),
|
||||
__('Update'),
|
||||
'manage_options',
|
||||
'mailpoet-update',
|
||||
@ -160,8 +197,8 @@ class Menu {
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
null,
|
||||
__('Form editor'),
|
||||
true,
|
||||
$this->setPageTitle(__('Form')),
|
||||
__('Form editor'),
|
||||
'manage_options',
|
||||
'mailpoet-form-editor',
|
||||
@ -172,8 +209,8 @@ class Menu {
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
null,
|
||||
__('Newsletter editor'),
|
||||
true,
|
||||
$this->setPageTitle(__('Newsletter')),
|
||||
__('Newsletter editor'),
|
||||
'manage_options',
|
||||
'mailpoet-newsletter-editor',
|
||||
@ -185,7 +222,7 @@ class Menu {
|
||||
|
||||
add_submenu_page(
|
||||
'mailpoet',
|
||||
__('Cron'),
|
||||
$this->setPageTitle(__('Cron')),
|
||||
__('Cron'),
|
||||
'manage_options',
|
||||
'mailpoet-cron',
|
||||
@ -317,15 +354,8 @@ class Menu {
|
||||
function subscribers() {
|
||||
$data = array();
|
||||
|
||||
// listing: limit per page
|
||||
$listing_per_page = get_user_meta(
|
||||
get_current_user_id(), 'mailpoet_subscribers_per_page', true
|
||||
);
|
||||
$data['per_page'] = (!empty($listing_per_page))
|
||||
? (int)$listing_per_page
|
||||
: Listing\Handler::DEFAULT_LIMIT_PER_PAGE;
|
||||
|
||||
$data['segments'] = Segment::findArray();
|
||||
$data['items_per_page'] = $this->getLimitPerPage('subscribers');
|
||||
$data['segments'] = Segment::getSegmentsWithSubscriberCount();
|
||||
|
||||
$data['custom_fields'] = array_map(function($field) {
|
||||
$field['params'] = unserialize($field['params']);
|
||||
@ -349,11 +379,14 @@ class Menu {
|
||||
|
||||
function segments() {
|
||||
$data = array();
|
||||
$data['items_per_page'] = $this->getLimitPerPage('segments');
|
||||
echo $this->renderer->render('segments.html', $data);
|
||||
}
|
||||
|
||||
function forms() {
|
||||
$data = array();
|
||||
|
||||
$data['items_per_page'] = $this->getLimitPerPage('forms');
|
||||
$data['segments'] = Segment::findArray();
|
||||
|
||||
echo $this->renderer->render('forms.html', $data);
|
||||
@ -364,11 +397,24 @@ class Menu {
|
||||
|
||||
$data = array();
|
||||
|
||||
$data['items_per_page'] = $this->getLimitPerPage('newsletters');
|
||||
$data['segments'] = Segment::getSegmentsWithSubscriberCount();
|
||||
$data['settings'] = Setting::getAll();
|
||||
$data['roles'] = $wp_roles->get_names();
|
||||
$data['roles']['mailpoet_all'] = __('In any WordPress role');
|
||||
|
||||
$date_time = new DateTime();
|
||||
$data['current_date'] = $date_time->getCurrentDate(DateTime::DEFAULT_DATE_FORMAT);
|
||||
$data['current_time'] = $date_time->getCurrentTime();
|
||||
$data['schedule_time_of_day'] = $date_time->getTimeInterval(
|
||||
'00:00:00',
|
||||
'+1 hour',
|
||||
24
|
||||
);
|
||||
|
||||
wp_enqueue_script('jquery-ui');
|
||||
wp_enqueue_script('jquery-ui-datepicker');
|
||||
|
||||
echo $this->renderer->render('newsletters.html', $data);
|
||||
}
|
||||
|
||||
@ -383,6 +429,7 @@ class Menu {
|
||||
$data = array(
|
||||
'customFields' => $custom_fields,
|
||||
'settings' => Setting::getAll(),
|
||||
'sub_menu' => 'mailpoet-newsletters'
|
||||
);
|
||||
wp_enqueue_media();
|
||||
wp_enqueue_script('tinymce-wplink', includes_url('js/tinymce/plugins/wplink/plugin.js'));
|
||||
@ -393,17 +440,19 @@ class Menu {
|
||||
function import() {
|
||||
$import = new BootStrapMenu('import');
|
||||
$data = $import->bootstrap();
|
||||
$data['sub_menu'] = 'mailpoet-subscribers';
|
||||
echo $this->renderer->render('subscribers/importExport/import.html', $data);
|
||||
}
|
||||
|
||||
function export() {
|
||||
$export = new BootStrapMenu('export');
|
||||
$data = $export->bootstrap();
|
||||
$data['sub_menu'] = 'mailpoet-subscribers';
|
||||
echo $this->renderer->render('subscribers/importExport/export.html', $data);
|
||||
}
|
||||
|
||||
function formEditor() {
|
||||
$id = (isset($_GET['id']) ? (int) $_GET['id'] : 0);
|
||||
$id = (isset($_GET['id']) ? (int)$_GET['id'] : 0);
|
||||
$form = Form::findOne($id);
|
||||
if($form !== false) {
|
||||
$form = $form->asArray();
|
||||
@ -412,12 +461,12 @@ class Menu {
|
||||
$data = array(
|
||||
'form' => $form,
|
||||
'pages' => Pages::getAll(),
|
||||
'segments' => Segment::getPublic()
|
||||
->findArray(),
|
||||
'segments' => Segment::getPublic()->findArray(),
|
||||
'styles' => FormRenderer::getStyles($form),
|
||||
'date_types' => Block\Date::getDateTypes(),
|
||||
'date_formats' => Block\Date::getDateFormats(),
|
||||
'month_names' => Block\Date::getMonthNames()
|
||||
'month_names' => Block\Date::getMonthNames(),
|
||||
'sub_menu' => 'mailpoet-forms'
|
||||
);
|
||||
|
||||
echo $this->renderer->render('form/editor.html', $data);
|
||||
@ -426,4 +475,25 @@ class Menu {
|
||||
function cron() {
|
||||
echo $this->renderer->render('cron.html');
|
||||
}
|
||||
|
||||
function setPageTitle($title) {
|
||||
return sprintf(
|
||||
'%s - %s',
|
||||
__('MailPoet'),
|
||||
$title
|
||||
);
|
||||
}
|
||||
|
||||
function getLimitPerPage($model = null) {
|
||||
if($model === null) {
|
||||
return Listing\Handler::DEFAULT_LIMIT_PER_PAGE;
|
||||
}
|
||||
|
||||
$listing_per_page = get_user_meta(
|
||||
get_current_user_id(), 'mailpoet_'.$model.'_per_page', true
|
||||
);
|
||||
return (!empty($listing_per_page))
|
||||
? (int)$listing_per_page
|
||||
: Listing\Handler::DEFAULT_LIMIT_PER_PAGE;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ class Migrator {
|
||||
'newsletter_option',
|
||||
'newsletter_segment',
|
||||
'newsletter_links',
|
||||
'newsletter_posts',
|
||||
'forms',
|
||||
'statistics_newsletters',
|
||||
'statistics_clicks',
|
||||
@ -59,10 +60,10 @@ class Migrator {
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'name varchar(90) NOT NULL,',
|
||||
'type varchar(90) NOT NULL DEFAULT "default",',
|
||||
'description varchar(250) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'description varchar(250) NOT NULL DEFAULT "",',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'deleted_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id),',
|
||||
'UNIQUE KEY name (name)'
|
||||
);
|
||||
@ -101,7 +102,6 @@ class Migrator {
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'newsletter_id mediumint(9) NOT NULL,',
|
||||
'newsletter_rendered_body longtext,',
|
||||
'newsletter_rendered_body_hash varchar(250) NULL DEFAULT NULL,',
|
||||
'newsletter_rendered_subject varchar(250) NULL DEFAULT NULL,',
|
||||
'subscribers longtext,',
|
||||
'status varchar(12) NULL DEFAULT NULL,',
|
||||
@ -110,11 +110,11 @@ class Migrator {
|
||||
'count_processed mediumint(9) NOT NULL DEFAULT 0,',
|
||||
'count_to_process mediumint(9) NOT NULL DEFAULT 0,',
|
||||
'count_failed mediumint(9) NOT NULL DEFAULT 0,',
|
||||
'scheduled_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'processed_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
|
||||
'scheduled_at TIMESTAMP NULL,',
|
||||
'processed_at TIMESTAMP NULL,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'deleted_at TIMESTAMP NULL,',
|
||||
'PRIMARY KEY (id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
@ -124,13 +124,13 @@ class Migrator {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'wp_user_id bigint(20) NULL,',
|
||||
'first_name tinytext NOT NULL,',
|
||||
'last_name tinytext NOT NULL,',
|
||||
'first_name tinytext NOT NULL DEFAULT "",',
|
||||
'last_name tinytext NOT NULL DEFAULT "",',
|
||||
'email varchar(150) NOT NULL,',
|
||||
'status varchar(12) NOT NULL DEFAULT "unconfirmed",',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'deleted_at TIMESTAMP NULL,',
|
||||
'PRIMARY KEY (id),',
|
||||
'UNIQUE KEY email (email)'
|
||||
);
|
||||
@ -143,8 +143,8 @@ class Migrator {
|
||||
'subscriber_id mediumint(9) NOT NULL,',
|
||||
'segment_id mediumint(9) NOT NULL,',
|
||||
'status varchar(12) NOT NULL DEFAULT "subscribed",',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id),',
|
||||
'UNIQUE KEY subscriber_segment (subscriber_id,segment_id)'
|
||||
);
|
||||
@ -156,9 +156,9 @@ class Migrator {
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'subscriber_id mediumint(9) NOT NULL,',
|
||||
'custom_field_id mediumint(9) NOT NULL,',
|
||||
'value varchar(255) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'value varchar(255) NOT NULL DEFAULT "",',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id),',
|
||||
'UNIQUE KEY subscriber_id_custom_field_id (subscriber_id,custom_field_id)'
|
||||
);
|
||||
@ -168,17 +168,17 @@ class Migrator {
|
||||
function newsletters() {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'subject varchar(250) NOT NULL,',
|
||||
'subject varchar(250) NOT NULL DEFAULT "",',
|
||||
'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,',
|
||||
'sender_address varchar(150) NOT NULL DEFAULT "",',
|
||||
'sender_name varchar(150) NOT NULL DEFAULT "",',
|
||||
'reply_to_address varchar(150) NOT NULL DEFAULT "",',
|
||||
'reply_to_name varchar(150) NOT NULL DEFAULT "",',
|
||||
'preheader varchar(250) NOT NULL DEFAULT "",',
|
||||
'body longtext,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'deleted_at TIMESTAMP NULL,',
|
||||
'PRIMARY KEY (id)'
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
@ -192,8 +192,8 @@ class Migrator {
|
||||
'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,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id)'
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
@ -204,8 +204,8 @@ class Migrator {
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'name varchar(90) NOT NULL,',
|
||||
'newsletter_type varchar(90) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id),',
|
||||
'UNIQUE KEY name_newsletter_type (newsletter_type,name)'
|
||||
);
|
||||
@ -217,9 +217,9 @@ class Migrator {
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'newsletter_id mediumint(9) NOT NULL,',
|
||||
'option_field_id mediumint(9) NOT NULL,',
|
||||
'value varchar(255) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'value varchar(255) NOT NULL DEFAULT "",',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id),',
|
||||
'UNIQUE KEY newsletter_id_option_field_id (newsletter_id,option_field_id)'
|
||||
);
|
||||
@ -231,8 +231,8 @@ class Migrator {
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'newsletter_id mediumint(9) NOT NULL,',
|
||||
'segment_id mediumint(9) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id),',
|
||||
'UNIQUE KEY newsletter_segment (newsletter_id,segment_id)'
|
||||
);
|
||||
@ -246,6 +246,18 @@ class Migrator {
|
||||
'queue_id mediumint(9) NOT NULL,',
|
||||
'url varchar(255) NOT NULL,',
|
||||
'hash varchar(20) NOT NULL,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
|
||||
function newsletter_posts() {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'newsletter_id mediumint(9) NOT NULL,',
|
||||
'post_id mediumint(9) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id)',
|
||||
@ -260,9 +272,9 @@ class Migrator {
|
||||
'body longtext,',
|
||||
'settings longtext,',
|
||||
'styles longtext,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'deleted_at TIMESTAMP NULL,',
|
||||
'PRIMARY KEY (id)'
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
@ -274,7 +286,7 @@ class Migrator {
|
||||
'newsletter_id mediumint(9) NOT NULL,',
|
||||
'subscriber_id mediumint(9) NOT NULL,',
|
||||
'queue_id mediumint(9) NOT NULL,',
|
||||
'sent_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'sent_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
@ -288,8 +300,8 @@ class Migrator {
|
||||
'queue_id mediumint(9) NOT NULL,',
|
||||
'link_id mediumint(9) NOT NULL,',
|
||||
'count mediumint(9) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'updated_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
@ -301,7 +313,7 @@ class Migrator {
|
||||
'newsletter_id mediumint(9) NOT NULL,',
|
||||
'subscriber_id mediumint(9) NOT NULL,',
|
||||
'queue_id mediumint(9) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'PRIMARY KEY (id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
@ -313,7 +325,7 @@ class Migrator {
|
||||
'newsletter_id mediumint(9) NOT NULL,',
|
||||
'subscriber_id mediumint(9) NOT NULL,',
|
||||
'queue_id mediumint(9) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'PRIMARY KEY (id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
@ -324,7 +336,7 @@ class Migrator {
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'form_id mediumint(9) NOT NULL,',
|
||||
'subscriber_id mediumint(9) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'PRIMARY KEY (id),',
|
||||
'UNIQUE KEY form_subscriber (form_id,subscriber_id)'
|
||||
);
|
||||
|
@ -10,7 +10,7 @@ use \MailPoet\Segments\WP;
|
||||
use \MailPoet\Models\Setting;
|
||||
use \MailPoet\Settings\Pages;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||
|
||||
@ -36,7 +36,7 @@ class Populator {
|
||||
$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) {
|
||||
if($wpdb->get_var("SELECT COUNT(*) FROM " . $table . " WHERE " . implode(' AND ', $column_conditions)) === 0) {
|
||||
$wpdb->insert(
|
||||
$table,
|
||||
$field
|
||||
@ -140,6 +140,14 @@ class Populator {
|
||||
|
||||
function newsletter_option_fields() {
|
||||
return array(
|
||||
array(
|
||||
'name' => 'isScheduled',
|
||||
'newsletter_type' => 'standard',
|
||||
),
|
||||
array(
|
||||
'name' => 'scheduledAt',
|
||||
'newsletter_type' => 'standard',
|
||||
),
|
||||
array(
|
||||
'name' => 'event',
|
||||
'newsletter_type' => 'welcome',
|
||||
@ -184,10 +192,6 @@ class Populator {
|
||||
array(
|
||||
'name' => 'schedule',
|
||||
'newsletter_type' => 'notification',
|
||||
),
|
||||
array(
|
||||
'name' => 'segments',
|
||||
'newsletter_type' => 'notification',
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -207,7 +211,7 @@ class Populator {
|
||||
$_this = $this;
|
||||
|
||||
array_map(function($row) use ($_this, $table) {
|
||||
if (!$_this->rowExists($table, $row)) {
|
||||
if(!$_this->rowExists($table, $row)) {
|
||||
$_this->insertRow($table, $row);
|
||||
}
|
||||
}, $rows);
|
||||
|
@ -142,7 +142,7 @@ class BlankTemplate {
|
||||
"blocks" => array(
|
||||
array(
|
||||
"type" => "footer",
|
||||
"text" => "<a href=\"[subscription:unsubscribe_url]\">Unsubscribe</a> | <a href=\"[subscription:manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>",
|
||||
"text" => "<a href=\"[link:subscription_unsubscribe_url]\">Unsubscribe</a> | <a href=\"[link:subscription_manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>",
|
||||
"styles" => array(
|
||||
"block" => array(
|
||||
"backgroundColor" => "transparent"
|
||||
|
@ -46,7 +46,7 @@ class FranksRoastHouseTemplate {
|
||||
"blocks" => array(
|
||||
array(
|
||||
"type" => "header",
|
||||
"text" => __("Display problems? <a href=\"[newsletter:view_in_browser_url]\">View it in your browser</a>"),
|
||||
"text" => __("Display problems? <a href=\"[link:newsletter_view_in_browser_url]\">View it in your browser</a>"),
|
||||
"styles" => array(
|
||||
"block" => array(
|
||||
"backgroundColor" => "#ccc6c6"
|
||||
@ -280,7 +280,7 @@ class FranksRoastHouseTemplate {
|
||||
"blocks" => array(
|
||||
array(
|
||||
"type" => "footer",
|
||||
"text" => __("<p><a href=\"[subscription:unsubscribe_url]\">Unsubscribe</a> | <a href=\"[subscription:manage_url]\">Manage subscription</a><br />12345 MailPoet Drive, EmailVille, 76543</p>"),
|
||||
"text" => __("<p><a href=\"[link:subscription_unsubscribe_url]\">Unsubscribe</a> | <a href=\"[link:subscription_manage_url]\">Manage subscription</a><br />12345 MailPoet Drive, EmailVille, 76543</p>"),
|
||||
"styles" => array(
|
||||
"block" => array(
|
||||
"backgroundColor" => "#a9a7a7"
|
||||
|
@ -242,7 +242,7 @@ class PostNotificationsBlankTemplate {
|
||||
"blocks" => array(
|
||||
array(
|
||||
"type" => "footer",
|
||||
"text" => __("<a href=\"[subscription:unsubscribe_url]\">Unsubscribe</a> | <a href=\"[subscription:manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>"),
|
||||
"text" => __("<a href=\"[link:subscription_unsubscribe_url]\">Unsubscribe</a> | <a href=\"[link:subscription_manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>"),
|
||||
"styles" => array(
|
||||
"block" => array(
|
||||
"backgroundColor" => "transparent"
|
||||
|
@ -46,7 +46,7 @@ class WelcomeTemplate {
|
||||
"blocks" => array(
|
||||
array(
|
||||
"type" => "header",
|
||||
"text" => __("Display problems? <a href=\"[newsletter:view_in_browser_url]\">View it in your browser</a>"),
|
||||
"text" => __("Display problems? <a href=\"[link:newsletter_view_in_browser_url]\">View it in your browser</a>"),
|
||||
"styles" => array(
|
||||
"block" => array(
|
||||
"backgroundColor" => "transparent"
|
||||
@ -224,7 +224,7 @@ class WelcomeTemplate {
|
||||
"blocks" => array(
|
||||
array(
|
||||
"type" => "footer",
|
||||
"text" => __("<a href=\"[subscription:unsubscribe_url]\">Unsubscribe</a> | <a href=\"[subscription:manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>"),
|
||||
"text" => __("<a href=\"[link:subscription_unsubscribe_url]\">Unsubscribe</a> | <a href=\"[link:subscription_manage_url]\">Manage subscription</a><br /><b>Add your postal address here!</b>"),
|
||||
"styles" => array(
|
||||
"block" => array(
|
||||
"backgroundColor" => "transparent"
|
||||
|
@ -2,9 +2,10 @@
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Cron\Daemon;
|
||||
use MailPoet\Newsletter\Viewer\ViewInBrowser;
|
||||
use MailPoet\Statistics\Track\Clicks;
|
||||
use MailPoet\Statistics\Track\Opens;
|
||||
use MailPoet\Subscription;
|
||||
use MailPoet\Statistics\Track\Clicks;
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
@ -25,7 +26,7 @@ class PublicAPI {
|
||||
Helpers::underscoreToCamelCase($_GET['action']) :
|
||||
false;
|
||||
$this->data = isset($_GET['data']) ?
|
||||
$_GET['data'] :
|
||||
unserialize(base64_decode($_GET['data'])) :
|
||||
false;
|
||||
}
|
||||
|
||||
@ -35,33 +36,29 @@ class PublicAPI {
|
||||
}
|
||||
|
||||
function queue() {
|
||||
try {
|
||||
$queue = new Daemon($this->_decodeData());
|
||||
$this->_checkAndCallMethod($queue, $this->action);
|
||||
} catch(\Exception $e) {
|
||||
}
|
||||
$queue = new Daemon($this->data);
|
||||
$this->_checkAndCallMethod($queue, $this->action);
|
||||
}
|
||||
|
||||
function subscription() {
|
||||
try {
|
||||
$subscription = new Subscription\Pages($this->action, $this->_decodeData());
|
||||
$this->_checkAndCallMethod($subscription, $this->action);
|
||||
} catch(\Exception $e) {
|
||||
}
|
||||
$subscription = new Subscription\Pages($this->action, $this->data);
|
||||
$this->_checkAndCallMethod($subscription, $this->action);
|
||||
}
|
||||
|
||||
function track() {
|
||||
try {
|
||||
if($this->action === 'click') {
|
||||
$track_class = new Clicks($this->data);
|
||||
}
|
||||
if($this->action === 'open') {
|
||||
$track_class = new Opens($this->data);
|
||||
}
|
||||
if(!isset($track_class)) return;
|
||||
$track_class->track();
|
||||
} catch(\Exception $e) {
|
||||
if($this->action === 'click') {
|
||||
$track_class = new Clicks($this->data);
|
||||
}
|
||||
if($this->action === 'open') {
|
||||
$track_class = new Opens($this->data);
|
||||
}
|
||||
if(!isset($track_class)) return;
|
||||
$track_class->track();
|
||||
}
|
||||
|
||||
function viewInBrowser() {
|
||||
$viewer = new ViewInBrowser($this->data);
|
||||
$viewer->view();
|
||||
}
|
||||
|
||||
private function _checkAndCallMethod($class, $method, $terminate_request = false) {
|
||||
@ -77,12 +74,4 @@ class PublicAPI {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function _decodeData() {
|
||||
if($this->data !== false) {
|
||||
return unserialize(base64_decode($this->data));
|
||||
} else {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
}
|
@ -7,13 +7,13 @@ use MailPoet\Util\Security;
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class CronHelper {
|
||||
const daemon_execution_limit = 20;
|
||||
const daemon_execution_timeout = 35;
|
||||
const daemon_request_timeout = 2;
|
||||
const DAEMON_EXECUTION_LIMIT = 20;
|
||||
const DAEMON_EXECUTION_TIMEOUT = 35;
|
||||
const DAEMON_REQUEST_TIMEOUT = 2;
|
||||
|
||||
static function createDaemon($token) {
|
||||
$daemon = array(
|
||||
'status' => 'starting',
|
||||
'status' => Daemon::STATUS_STARTING,
|
||||
'counter' => 0,
|
||||
'token' => $token
|
||||
);
|
||||
@ -37,7 +37,7 @@ class CronHelper {
|
||||
return Security::generateRandomString();
|
||||
}
|
||||
|
||||
static function accessDaemon($token, $timeout = self::daemon_request_timeout) {
|
||||
static function accessDaemon($token, $timeout = self::DAEMON_REQUEST_TIMEOUT) {
|
||||
$data = serialize(array('token' => $token));
|
||||
$url = '/?mailpoet&endpoint=queue&action=run&data=' .
|
||||
base64_encode($data);
|
||||
@ -71,7 +71,7 @@ class CronHelper {
|
||||
|
||||
static function checkExecutionTimer($timer) {
|
||||
$elapsed_time = microtime(true) - $timer;
|
||||
if($elapsed_time >= self::daemon_execution_limit) {
|
||||
if($elapsed_time >= self::DAEMON_EXECUTION_LIMIT) {
|
||||
throw new \Exception(__('Maximum execution time reached.'));
|
||||
}
|
||||
}
|
||||
|
@ -12,12 +12,15 @@ class Daemon {
|
||||
public $daemon;
|
||||
public $data;
|
||||
public $refreshed_token;
|
||||
const daemon_request_timeout = 5;
|
||||
const STATUS_STOPPED = 'stopped';
|
||||
const STATUS_STOPPING = 'stopping';
|
||||
const STATUS_STARTED = 'started';
|
||||
const STATUS_STARTING = 'starting';
|
||||
const REQUEST_TIMEOUT = 5;
|
||||
private $timer;
|
||||
|
||||
function __construct($data) {
|
||||
if(empty($data)) $this->abortWithError(__('Invalid or missing cron data.'));
|
||||
set_time_limit(0);
|
||||
ignore_user_abort();
|
||||
$this->daemon = CronHelper::getDaemon();
|
||||
$this->token = CronHelper::createToken();
|
||||
@ -27,6 +30,7 @@ class Daemon {
|
||||
|
||||
function run() {
|
||||
$daemon = $this->daemon;
|
||||
set_time_limit(0);
|
||||
if(!$daemon) {
|
||||
$this->abortWithError(__('Daemon does not exist.'));
|
||||
}
|
||||
@ -42,10 +46,11 @@ class Daemon {
|
||||
$queue = new SendingQueue();
|
||||
$queue->process($this->timer);
|
||||
} catch(\Exception $e) {
|
||||
// continue processing, no need to catch errors
|
||||
}
|
||||
$elapsed_time = microtime(true) - $this->timer;
|
||||
if($elapsed_time < CronHelper::daemon_execution_limit) {
|
||||
sleep(CronHelper::daemon_execution_limit - $elapsed_time);
|
||||
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
|
||||
@ -55,8 +60,8 @@ class Daemon {
|
||||
}
|
||||
$daemon['counter']++;
|
||||
$this->abortIfStopped($daemon);
|
||||
if($daemon['status'] === 'starting') {
|
||||
$daemon['status'] = 'started';
|
||||
if($daemon['status'] === self::STATUS_STARTING) {
|
||||
$daemon['status'] = self::STATUS_STARTED;
|
||||
}
|
||||
$daemon['token'] = $this->token;
|
||||
CronHelper::saveDaemon($daemon);
|
||||
@ -64,9 +69,9 @@ class Daemon {
|
||||
}
|
||||
|
||||
function abortIfStopped($daemon) {
|
||||
if($daemon['status'] === 'stopped') exit;
|
||||
if($daemon['status'] === 'stopping') {
|
||||
$daemon['status'] = 'stopped';
|
||||
if($daemon['status'] === self::STATUS_STOPPED) exit;
|
||||
if($daemon['status'] === self::STATUS_STOPPING) {
|
||||
$daemon['status'] = self::STATUS_STOPPED;
|
||||
CronHelper::saveDaemon($daemon);
|
||||
exit;
|
||||
}
|
||||
@ -77,7 +82,7 @@ class Daemon {
|
||||
}
|
||||
|
||||
function callSelf() {
|
||||
CronHelper::accessDaemon($this->token, self::daemon_request_timeout);
|
||||
CronHelper::accessDaemon($this->token, self::REQUEST_TIMEOUT);
|
||||
exit;
|
||||
}
|
||||
}
|
@ -23,25 +23,25 @@ class Supervisor {
|
||||
// if the daemon is stopped, return its status and do nothing
|
||||
if(!$this->force_run &&
|
||||
isset($daemon['status']) &&
|
||||
$daemon['status'] === 'stopped'
|
||||
$daemon['status'] === Daemon::STATUS_STOPPED
|
||||
) {
|
||||
return $this->formatDaemonStatusMessage($daemon['status']);
|
||||
|
||||
}
|
||||
$elapsed_time = time() - (int) $daemon['updated_at'];
|
||||
$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_execution_timeout && !$this->force_run) {
|
||||
if($elapsed_time < CronHelper::DAEMON_EXECUTION_TIMEOUT && !$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_execution_timeout &&
|
||||
elseif($elapsed_time < CronHelper::DAEMON_EXECUTION_TIMEOUT &&
|
||||
$this->force_run &&
|
||||
in_array($daemon['status'], array(
|
||||
'stopping',
|
||||
'starting'
|
||||
Daemon::STATUS_STOPPING,
|
||||
Daemon::STATUS_STARTING
|
||||
))
|
||||
) {
|
||||
return $this->formatDaemonStatusMessage($daemon['status']);
|
||||
|
@ -2,16 +2,13 @@
|
||||
namespace MailPoet\Cron\Workers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Cron\CronExpression as Cron;
|
||||
use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\SendingQueue;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Models\SubscriberSegment;
|
||||
use MailPoet\Newsletter\Renderer\PostProcess\OpenTracking;
|
||||
use MailPoet\Newsletter\Renderer\Renderer;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\Newsletter\Scheduler\Scheduler as NewsletterScheduler;
|
||||
|
||||
require_once(ABSPATH . 'wp-includes/pluggable.php');
|
||||
|
||||
@ -19,6 +16,7 @@ if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Scheduler {
|
||||
public $timer;
|
||||
const UNCONFIRMED_SUBSCRIBER_RESCHEDULE_TIMEOUT = 5;
|
||||
|
||||
function __construct($timer = false) {
|
||||
$this->timer = ($timer) ? $timer : microtime(true);
|
||||
@ -30,17 +28,17 @@ class Scheduler {
|
||||
->whereLte('scheduled_at', Carbon::createFromTimestamp(current_time('timestamp')))
|
||||
->findMany();
|
||||
if(!count($scheduled_queues)) return;
|
||||
foreach($scheduled_queues as $i=>$queue) {
|
||||
foreach($scheduled_queues as $i => $queue) {
|
||||
$newsletter = Newsletter::filter('filterWithOptions')
|
||||
->findOne($queue->newsletter_id);
|
||||
if(!$newsletter || $newsletter->deleted_at !== null) {
|
||||
$queue->delete();
|
||||
}
|
||||
else if($newsletter->type === 'welcome') {
|
||||
} elseif($newsletter->type === 'welcome') {
|
||||
$this->processWelcomeNewsletter($newsletter, $queue);
|
||||
}
|
||||
else if($newsletter->type === 'notification') {
|
||||
} elseif($newsletter->type === 'notification') {
|
||||
$this->processPostNotificationNewsletter($newsletter, $queue);
|
||||
} elseif($newsletter->type === 'standard') {
|
||||
$this->processScheduledStandardNewsletter($newsletter, $queue);
|
||||
}
|
||||
CronHelper::checkExecutionTimer($this->timer);
|
||||
}
|
||||
@ -48,16 +46,20 @@ class Scheduler {
|
||||
|
||||
function processWelcomeNewsletter($newsletter, $queue) {
|
||||
$subscriber = unserialize($queue->subscribers);
|
||||
$subscriber_id = $subscriber['to_process'][0];
|
||||
if(empty($subscriber['to_process'][0])) {
|
||||
$queue->delete();
|
||||
return;
|
||||
}
|
||||
$subscriber_id = (int)$subscriber['to_process'][0];
|
||||
if($newsletter->event === 'segment') {
|
||||
if ($this->verifyMailPoetSubscriber($subscriber_id, $newsletter, $queue) === false) {
|
||||
if($this->verifyMailPoetSubscriber($subscriber_id, $newsletter, $queue) === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if($newsletter->event === 'user') {
|
||||
if ($this->verifyWPSubscriber($subscriber_id, $newsletter) === false) {
|
||||
$queue->delete();
|
||||
return;
|
||||
} else {
|
||||
if($newsletter->event === 'user') {
|
||||
if($this->verifyWPSubscriber($subscriber_id, $newsletter, $queue) === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
$queue->status = null;
|
||||
@ -65,17 +67,50 @@ class Scheduler {
|
||||
}
|
||||
|
||||
function processPostNotificationNewsletter($newsletter, $queue) {
|
||||
$next_run_date = $this->getQueueNextRunDate($newsletter->schedule);
|
||||
$segments = unserialize($newsletter->segments);
|
||||
$subscribers = Subscriber::getSubscribedInSegments($segments)
|
||||
$segments = $newsletter->segments()->findArray();
|
||||
if(empty($segments)) {
|
||||
$this->deleteQueueOrUpdateNextRunDate($queue, $newsletter);
|
||||
return;
|
||||
}
|
||||
$segment_ids = array_map(function($segment) {
|
||||
return $segment['id'];
|
||||
}, $segments);
|
||||
$subscribers = Subscriber::getSubscribedInSegments($segment_ids)
|
||||
->findArray();
|
||||
$subscribers = Helpers::arrayColumn($subscribers, 'subscriber_id');
|
||||
$subscribers = array_unique($subscribers);
|
||||
if(!count($subscribers) || !$this->checkIfNewsletterChanged($newsletter)) {
|
||||
$queue->scheduled_at = $next_run_date;
|
||||
$queue->save();
|
||||
if(empty($subscribers)) {
|
||||
$this->deleteQueueOrUpdateNextRunDate($queue, $newsletter);
|
||||
return;
|
||||
}
|
||||
// schedule new queue if the post notification is not destined for immediate delivery
|
||||
if ($newsletter->intervalType !== NewsletterScheduler::INTERVAL_IMMEDIATELY) {
|
||||
$new_queue = SendingQueue::create();
|
||||
$new_queue->newsletter_id = $newsletter->id;
|
||||
$new_queue->status = NewsletterScheduler::STATUS_SCHEDULED;
|
||||
self::deleteQueueOrUpdateNextRunDate($new_queue, $newsletter);
|
||||
}
|
||||
$queue->subscribers = serialize(
|
||||
array(
|
||||
'to_process' => $subscribers
|
||||
)
|
||||
);
|
||||
$queue->count_total = $queue->count_to_process = count($subscribers);
|
||||
$queue->status = null;
|
||||
$queue->save();
|
||||
}
|
||||
|
||||
function processScheduledStandardNewsletter($newsletter, $queue) {
|
||||
$segments = $newsletter->segments()->findArray();
|
||||
$segment_ids = array_map(function($segment) {
|
||||
return $segment['id'];
|
||||
}, $segments);
|
||||
|
||||
$subscribers = Subscriber::getSubscribedInSegments($segment_ids)
|
||||
->findArray();
|
||||
$subscribers = Helpers::arrayColumn($subscribers, 'subscriber_id');
|
||||
$subscribers = array_unique($subscribers);
|
||||
|
||||
// update current queue
|
||||
$queue->subscribers = serialize(
|
||||
array(
|
||||
@ -85,84 +120,58 @@ class Scheduler {
|
||||
$queue->count_total = $queue->count_to_process = count($subscribers);
|
||||
$queue->status = null;
|
||||
$queue->save();
|
||||
// schedule newsletter for next delivery
|
||||
$new_queue = SendingQueue::create();
|
||||
$new_queue->newsletter_id = $newsletter->id;
|
||||
$new_queue->scheduled_at = $next_run_date;
|
||||
$new_queue->status = 'scheduled';
|
||||
$new_queue->save();
|
||||
}
|
||||
|
||||
private function verifyMailPoetSubscriber($subscriber_id, $newsletter, $queue) {
|
||||
function verifyMailPoetSubscriber($subscriber_id, $newsletter, $queue) {
|
||||
$subscriber = Subscriber::findOne($subscriber_id);
|
||||
// check if subscriber is in proper segment
|
||||
$subscriber_in_segment =
|
||||
SubscriberSegment::where('subscriber_id', $subscriber_id)
|
||||
->where('segment_id', $newsletter->segment)
|
||||
->where('status', 'subscribed')
|
||||
->findOne();
|
||||
if (!$subscriber_in_segment) {
|
||||
if(!$subscriber || !$subscriber_in_segment) {
|
||||
$queue->delete();
|
||||
return false;
|
||||
}
|
||||
// check if subscriber is confirmed (subscribed)
|
||||
$subscriber = $subscriber_in_segment->subscriber()->findOne();
|
||||
if ($subscriber->status !== 'subscribed') {
|
||||
if($subscriber->status !== 'subscribed') {
|
||||
// reschedule delivery in 5 minutes
|
||||
$scheduled_at = Carbon::createFromTimestamp(current_time('timestamp'));
|
||||
$queue->scheduled_at = $scheduled_at->addMinutes(5);
|
||||
$queue->scheduled_at = $scheduled_at->addMinutes(
|
||||
self::UNCONFIRMED_SUBSCRIBER_RESCHEDULE_TIMEOUT
|
||||
);
|
||||
$queue->save();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function verifyWPSubscriber($subscriber_id, $newsletter) {
|
||||
function verifyWPSubscriber($subscriber_id, $newsletter, $queue) {
|
||||
// check if user has the proper role
|
||||
$subscriber = Subscriber::findOne($subscriber_id);
|
||||
if(!$subscriber || $subscriber->wp_user_id === null) {
|
||||
$queue->delete();
|
||||
return false;
|
||||
}
|
||||
$wp_user = (array) get_userdata($subscriber->wp_user_id);
|
||||
if($newsletter->role !== \MailPoet\Newsletter\Scheduler\Scheduler::WORDPRESS_ALL_ROLES
|
||||
&& !in_array($newsletter->role, $wp_user['roles'])) {
|
||||
&& !in_array($newsletter->role, $wp_user['roles'])
|
||||
) {
|
||||
$queue->delete();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: function will be depreciated once new post notification logic is done
|
||||
private function checkIfNewsletterChanged($newsletter) {
|
||||
$previous_queue = SendingQueue::whereNull('status')
|
||||
->where('newsletter_id', $newsletter->id)
|
||||
->orderByDesc('id')
|
||||
->findOne();
|
||||
if ($previous_queue) return false;
|
||||
$running_queue = SendingQueue::whereNull('status')
|
||||
->where('newsletter_id', $newsletter->id)
|
||||
->orderByDesc('id')
|
||||
->findOne();
|
||||
if ($running_queue) return false;
|
||||
$last_run_queue = SendingQueue::where('status', 'completed')
|
||||
->where('newsletter_id', $newsletter->id)
|
||||
->orderByDesc('id')
|
||||
->findOne();
|
||||
if(!$last_run_queue) return true;
|
||||
if((boolean) Setting::getValue('tracking.enabled')) {
|
||||
// insert tracking code
|
||||
add_filter('mailpoet_rendering_post_process', function ($template) {
|
||||
return OpenTracking::process($template);
|
||||
});
|
||||
private function deleteQueueOrUpdateNextRunDate($queue, $newsletter) {
|
||||
if($newsletter->intervalType === NewsletterScheduler::INTERVAL_IMMEDIATELY) {
|
||||
$queue->delete();
|
||||
} else {
|
||||
$next_run_date = NewsletterScheduler::getNextRunDate($newsletter->schedule);
|
||||
$queue->scheduled_at = $next_run_date;
|
||||
$queue->save();
|
||||
}
|
||||
$renderer = new Renderer($newsletter->asArray());
|
||||
$rendered_newsletter = $renderer->render();
|
||||
$new_hash = md5($rendered_newsletter['text']);
|
||||
$old_hash = $last_run_queue->newsletter_rendered_body_hash;
|
||||
return $new_hash !== $old_hash;
|
||||
}
|
||||
|
||||
private function getQueueNextRunDate($schedule) {
|
||||
$schedule = Cron::factory($schedule);
|
||||
return $schedule->getNextRunDate(current_time('mysql'))
|
||||
->format('Y-m-d H:i:s');
|
||||
return;
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ namespace MailPoet\Cron\Workers;
|
||||
use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Mailer\Mailer;
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\NewsletterLink;
|
||||
use MailPoet\Models\NewsletterPost;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Models\StatisticsNewsletters;
|
||||
use MailPoet\Models\Subscriber;
|
||||
@ -20,9 +20,10 @@ class SendingQueue {
|
||||
public $mta_config;
|
||||
public $mta_log;
|
||||
public $processing_method;
|
||||
public $divider = '***MailPoet***';
|
||||
private $timer;
|
||||
const batch_size = 50;
|
||||
const BATCH_SIZE = 50;
|
||||
const DIVIDER = '***MailPoet***';
|
||||
const STATUS_COMPLETED = 'completed';
|
||||
|
||||
function __construct($timer = false) {
|
||||
$this->mta_config = $this->getMailerConfig();
|
||||
@ -43,6 +44,12 @@ class SendingQueue {
|
||||
}
|
||||
$newsletter = $newsletter->asArray();
|
||||
$newsletter['body'] = $this->getOrRenderNewsletterBody($queue, $newsletter);
|
||||
if($newsletter['type'] === 'notification' &&
|
||||
strpos($newsletter['body']['html'], 'data-post-id') === false
|
||||
){
|
||||
$queue->delete();
|
||||
continue;
|
||||
}
|
||||
$queue->subscribers = (object) unserialize($queue->subscribers);
|
||||
if(!isset($queue->subscribers->processed)) {
|
||||
$queue->subscribers->processed = array();
|
||||
@ -51,18 +58,18 @@ class SendingQueue {
|
||||
$queue->subscribers->failed = array();
|
||||
}
|
||||
$mailer = $this->configureMailer($newsletter);
|
||||
foreach(array_chunk($queue->subscribers->to_process, self::batch_size) as
|
||||
foreach(array_chunk($queue->subscribers->to_process, self::BATCH_SIZE) as
|
||||
$subscribers_ids) {
|
||||
$subscribers = Subscriber::whereIn('id', $subscribers_ids)
|
||||
->findArray();
|
||||
if (count($subscribers_ids) !== count($subscribers)) {
|
||||
if(count($subscribers_ids) !== count($subscribers)) {
|
||||
$queue->subscribers->to_process = $this->recalculateSubscriberCount(
|
||||
Helpers::arrayColumn($subscribers, 'id'),
|
||||
$subscribers_ids,
|
||||
$queue->subscribers->to_process
|
||||
);
|
||||
}
|
||||
if (!count($queue->subscribers->to_process)) {
|
||||
if(!count($queue->subscribers->to_process)) {
|
||||
$this->updateQueue($queue);
|
||||
continue;
|
||||
}
|
||||
@ -92,10 +99,9 @@ class SendingQueue {
|
||||
return OpenTracking::process($template);
|
||||
});
|
||||
// render newsletter
|
||||
list($rendered_newsletter, $queue->newsletter_rendered_body_hash) =
|
||||
$this->renderNewsletter($newsletter);
|
||||
// extract and replace links
|
||||
$processed_newsletter = $this->processLinks(
|
||||
$rendered_newsletter = $this->renderNewsletter($newsletter);
|
||||
// process link shortcodes, extract and save links in the database
|
||||
$processed_newsletter = $this->processLinksAndShortcodes(
|
||||
$this->joinObject($rendered_newsletter),
|
||||
$newsletter['id'],
|
||||
$queue->id
|
||||
@ -105,9 +111,12 @@ class SendingQueue {
|
||||
}
|
||||
else {
|
||||
// render newsletter
|
||||
list($newsletter['body'], $queue->newsletter_rendered_body_hash) =
|
||||
$this->renderNewsletter($newsletter);
|
||||
$newsletter['body'] = $this->renderNewsletter($newsletter);
|
||||
}
|
||||
$this->extractAndSaveNewsletterPosts(
|
||||
$newsletter['id'],
|
||||
$newsletter['body']['html']
|
||||
);
|
||||
$queue->newsletter_rendered_body = json_encode($newsletter['body']);
|
||||
$queue->save();
|
||||
} else {
|
||||
@ -119,7 +128,7 @@ class SendingQueue {
|
||||
function processBulkSubscribers($mailer, $newsletter, $subscribers, $queue) {
|
||||
foreach($subscribers as $subscriber) {
|
||||
$processed_newsletters[] =
|
||||
$this->processNewsletter($newsletter, $subscriber, $queue);
|
||||
$this->processNewsletterBeforeSending($newsletter, $subscriber, $queue);
|
||||
if(!$queue->newsletter_rendered_subject) {
|
||||
$queue->newsletter_rendered_subject = $processed_newsletters[0]['subject'];
|
||||
}
|
||||
@ -163,7 +172,7 @@ class SendingQueue {
|
||||
function processIndividualSubscriber($mailer, $newsletter, $subscribers, $queue) {
|
||||
foreach($subscribers as $subscriber) {
|
||||
$this->checkSendingLimit();
|
||||
$processed_newsletter = $this->processNewsletter($newsletter, $subscriber, $queue);
|
||||
$processed_newsletter = $this->processNewsletterBeforeSending($newsletter, $subscriber, $queue);
|
||||
if(!$queue->newsletter_rendered_subject) {
|
||||
$queue->newsletter_rendered_subject = $processed_newsletter['subject'];
|
||||
}
|
||||
@ -197,26 +206,29 @@ class SendingQueue {
|
||||
|
||||
function renderNewsletter($newsletter) {
|
||||
$renderer = new Renderer($newsletter);
|
||||
$rendered_newsletter = $renderer->render();
|
||||
$rendered_newsletter_hash = md5($rendered_newsletter['text']);
|
||||
return array($rendered_newsletter, $rendered_newsletter_hash);
|
||||
return $renderer->render();
|
||||
}
|
||||
|
||||
function processLinks($text, $newsletter_id, $queue_id) {
|
||||
list($text, $processed_links) = Links::replace($text);
|
||||
foreach($processed_links as $link) {
|
||||
// save extracted and processed links
|
||||
$newsletter_link = NewsletterLink::create();
|
||||
$newsletter_link->newsletter_id = $newsletter_id;
|
||||
$newsletter_link->queue_id = $queue_id;
|
||||
$newsletter_link->hash = $link['hash'];
|
||||
$newsletter_link->url = $link['url'];
|
||||
$newsletter_link->save();
|
||||
}
|
||||
return $text;
|
||||
function processLinksAndShortcodes($content, $newsletter_id, $queue_id) {
|
||||
// process only link shortcodes
|
||||
$shortcodes = new Shortcodes($newsletter = false, $subscriber = false, $queue_id);
|
||||
$content = $shortcodes->replace(
|
||||
$content,
|
||||
$categories = array('link')
|
||||
);
|
||||
// extract and save links and link shortcodes
|
||||
list($content, $processed_links) =
|
||||
Links::process(
|
||||
$content,
|
||||
$links = false,
|
||||
$process_link_shortcodes = true,
|
||||
$queue_id
|
||||
);
|
||||
Links::save($processed_links, $newsletter_id, $queue_id);
|
||||
return $content;
|
||||
}
|
||||
|
||||
function processNewsletter($newsletter, $subscriber = false, $queue) {
|
||||
function processNewsletterBeforeSending($newsletter, $subscriber = false, $queue) {
|
||||
$data_for_shortcodes = array(
|
||||
$newsletter['subject'],
|
||||
$newsletter['body']['html'],
|
||||
@ -225,10 +237,11 @@ class SendingQueue {
|
||||
$processed_newsletter = $this->replaceShortcodes(
|
||||
$newsletter,
|
||||
$subscriber,
|
||||
$queue,
|
||||
$this->joinObject($data_for_shortcodes)
|
||||
);
|
||||
if((boolean) Setting::getValue('tracking.enabled')) {
|
||||
$processed_newsletter = $this->replaceLinks(
|
||||
$processed_newsletter = Links::replaceSubscriberData(
|
||||
$newsletter['id'],
|
||||
$subscriber['id'],
|
||||
$queue->id,
|
||||
@ -242,18 +255,11 @@ class SendingQueue {
|
||||
return $newsletter;
|
||||
}
|
||||
|
||||
function replaceLinks($newsletter_id, $subscriber_id, $queue_id, $body) {
|
||||
return str_replace(
|
||||
'[mailpoet_data]',
|
||||
sprintf('%s-%s-%s', $newsletter_id, $subscriber_id, $queue_id),
|
||||
$body
|
||||
);
|
||||
}
|
||||
|
||||
function replaceShortcodes($newsletter, $subscriber, $body) {
|
||||
function replaceShortcodes($newsletter, $subscriber, $queue, $body) {
|
||||
$shortcodes = new Shortcodes(
|
||||
$newsletter,
|
||||
$subscriber
|
||||
$subscriber,
|
||||
$queue
|
||||
);
|
||||
return $shortcodes->replace($body);
|
||||
}
|
||||
@ -315,7 +321,7 @@ class SendingQueue {
|
||||
$queue->count_processed + $queue->count_to_process;
|
||||
if(!$queue->count_to_process) {
|
||||
$queue->processed_at = current_time('mysql');
|
||||
$queue->status = 'completed';
|
||||
$queue->status = self::STATUS_COMPLETED;
|
||||
}
|
||||
$queue->subscribers = serialize((array) $queue->subscribers);
|
||||
$queue->save();
|
||||
@ -347,9 +353,9 @@ class SendingQueue {
|
||||
}
|
||||
|
||||
function checkSendingLimit() {
|
||||
$frequency_interval = (int) $this->mta_config['frequency']['interval'] * 60;
|
||||
$frequency_limit = (int) $this->mta_config['frequency']['emails'];
|
||||
$elapsed_time = time() - (int) $this->mta_log['started'];
|
||||
$frequency_interval = (int)$this->mta_config['frequency']['interval'] * 60;
|
||||
$frequency_limit = (int)$this->mta_config['frequency']['emails'];
|
||||
$elapsed_time = time() - (int)$this->mta_log['started'];
|
||||
if($this->mta_log['sent'] === $frequency_limit &&
|
||||
$elapsed_time <= $frequency_interval
|
||||
) {
|
||||
@ -371,11 +377,22 @@ class SendingQueue {
|
||||
return array_diff($subscribers_to_process, $subscibers_to_exclude);
|
||||
}
|
||||
|
||||
function extractAndSaveNewsletterPosts($newletter_id, $content) {
|
||||
preg_match_all('/data-post-id="(\d+)"/ism', $content, $posts);
|
||||
$posts = $posts[1];
|
||||
foreach($posts as $post) {
|
||||
$newletter_post = NewsletterPost::create();
|
||||
$newletter_post->newsletter_id = $newletter_id;
|
||||
$newletter_post->post_id = $post;
|
||||
$newletter_post->save();
|
||||
}
|
||||
}
|
||||
|
||||
private function joinObject($object = array()) {
|
||||
return implode($this->divider, $object);
|
||||
return implode(self::DIVIDER, $object);
|
||||
}
|
||||
|
||||
private function splitObject($object = array()) {
|
||||
return explode($this->divider, $object);
|
||||
return explode(self::DIVIDER, $object);
|
||||
}
|
||||
}
|
@ -109,6 +109,10 @@ class Date extends Base {
|
||||
$month_names = static::getMonthNames();
|
||||
|
||||
$html = '';
|
||||
|
||||
// empty value label
|
||||
$html .= '<option value="">'.__('Month').'</option>';
|
||||
|
||||
for($i = 1; $i < 13; $i++) {
|
||||
$is_selected = ($i === $block['selected']) ? 'selected="selected"' : '';
|
||||
$html .= '<option value="'.$i.'" '.$is_selected.'>';
|
||||
@ -125,6 +129,7 @@ class Date extends Base {
|
||||
'from' => (int)strftime('%Y') - 100,
|
||||
'to' => (int)strftime('%Y')
|
||||
);
|
||||
|
||||
// is default today
|
||||
if(!empty($block['params']['is_default_today'])) {
|
||||
$defaults['selected'] = (int)strftime('%Y');
|
||||
@ -135,6 +140,9 @@ class Date extends Base {
|
||||
|
||||
$html = '';
|
||||
|
||||
// empty value label
|
||||
$html .= '<option value="">'.__('Year').'</option>';
|
||||
|
||||
// return years as an array
|
||||
for($i = (int)$block['to']; $i > (int)($block['from'] - 1); $i--) {
|
||||
$is_selected = ($i === $block['selected']) ? 'selected="selected"' : '';
|
||||
@ -158,6 +166,9 @@ class Date extends Base {
|
||||
|
||||
$html = '';
|
||||
|
||||
// empty value label
|
||||
$html .= '<option value="">'.__('Day').'</option>';
|
||||
|
||||
// return days as an array
|
||||
for($i = 1; $i < 32; $i++) {
|
||||
$is_selected = ($i === $block['selected']) ? 'selected="selected"' : '';
|
||||
|
@ -13,9 +13,12 @@ class Select extends Base {
|
||||
$html .= static::renderLabel($block);
|
||||
$html .= '<select class="mailpoet_select" name="'.$field_name.'">';
|
||||
|
||||
if(isset($block['params']['label_within'])
|
||||
&& $block['params']['label_within']) {
|
||||
if(isset($block['params']['label_within']) && $block['params']['label_within']) {
|
||||
$html .= '<option value="">'.static::getFieldLabel($block).'</option>';
|
||||
} else {
|
||||
if(empty($block['params']['required']) || !$block['params']['required']) {
|
||||
$html .= '<option value="">-</option>';
|
||||
}
|
||||
}
|
||||
|
||||
$options = (!empty($block['params']['values'])
|
||||
|
@ -93,7 +93,7 @@ class Newsletter extends Model {
|
||||
}
|
||||
|
||||
function getStatistics() {
|
||||
if ($this->queue === false) {
|
||||
if($this->queue === false) {
|
||||
return false;
|
||||
}
|
||||
return SendingQueue::tableAlias('queues')
|
||||
|
12
lib/Models/NewsletterPost.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace MailPoet\Models;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class NewsletterPost extends Model {
|
||||
public static $_table = MP_NEWSLETTER_POSTS_TABLE;
|
||||
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
@ -28,8 +28,8 @@ class NewsletterTemplate extends Model {
|
||||
static function createOrUpdate($data = array()) {
|
||||
$template = false;
|
||||
|
||||
if(isset($data['id']) && (int) $data['id'] > 0) {
|
||||
$template = self::findOne((int) $data['id']);
|
||||
if(isset($data['id']) && (int)$data['id'] > 0) {
|
||||
$template = self::findOne((int)$data['id']);
|
||||
}
|
||||
|
||||
if($template === false) {
|
||||
|
@ -126,18 +126,19 @@ class Segment extends Model {
|
||||
|
||||
static function getSegmentsWithSubscriberCount() {
|
||||
return self::selectMany(array(self::$_table.'.id', self::$_table.'.name'))
|
||||
->select_expr(
|
||||
'COUNT('.MP_SUBSCRIBER_SEGMENT_TABLE.'.subscriber_id)', 'subscribers'
|
||||
->selectExpr(
|
||||
self::$_table.'.*, COUNT(IF('.MP_SUBSCRIBER_SEGMENT_TABLE.'.status="subscribed",1,NULL)) `subscribers`'
|
||||
)
|
||||
->left_outer_join(
|
||||
->leftOuterJoin(
|
||||
MP_SUBSCRIBER_SEGMENT_TABLE,
|
||||
array(self::$_table.'.id', '=', MP_SUBSCRIBER_SEGMENT_TABLE.'.segment_id'))
|
||||
->left_outer_join(
|
||||
->leftOuterJoin(
|
||||
MP_SUBSCRIBERS_TABLE,
|
||||
array(MP_SUBSCRIBER_SEGMENT_TABLE.'.subscriber_id', '=', MP_SUBSCRIBERS_TABLE.'.id'))
|
||||
->whereNull(MP_SUBSCRIBERS_TABLE.'.deleted_at')
|
||||
->group_by(self::$_table.'.id')
|
||||
->group_by(self::$_table.'.name')
|
||||
->groupBy(self::$_table.'.id')
|
||||
->groupBy(self::$_table.'.name')
|
||||
->orderByAsc(self::$_table.'.name')
|
||||
->where(self::$_table.'.type', 'default')
|
||||
->whereNull(self::$_table.'.deleted_at')
|
||||
->findArray();
|
||||
|
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\Models;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Setting extends Model {
|
||||
public static $_table = MP_SETTINGS_TABLE;
|
||||
|
@ -124,6 +124,10 @@ class Subscriber extends Model {
|
||||
return false;
|
||||
}
|
||||
|
||||
static function verifyToken($email, $token) {
|
||||
return (self::generateToken($email) === $token);
|
||||
}
|
||||
|
||||
static function subscribe($subscriber_data = array(), $segment_ids = array()) {
|
||||
if(empty($subscriber_data) or empty($segment_ids)) {
|
||||
return false;
|
||||
@ -163,7 +167,10 @@ class Subscriber extends Model {
|
||||
}
|
||||
|
||||
// welcome email
|
||||
Scheduler::welcomeForSegmentSubscription($subscriber->id, $segment_ids);
|
||||
Scheduler::scheduleSubscriberWelcomeNotification(
|
||||
$subscriber->id,
|
||||
$segment_ids
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,11 +195,15 @@ class Subscriber extends Model {
|
||||
'label' => __('All segments'),
|
||||
'value' => ''
|
||||
);
|
||||
|
||||
$subscribers_without_segment = self::filter('withoutSegments')->count();
|
||||
$subscribers_without_segment_label = sprintf(
|
||||
__('Subscribers without a segment (%s)'),
|
||||
number_format($subscribers_without_segment)
|
||||
);
|
||||
|
||||
$segment_list[] = array(
|
||||
'label' => sprintf(
|
||||
__('Subscribers without a segment (%d)'),
|
||||
self::filter('withoutSegments')->count()
|
||||
),
|
||||
'label' => str_replace(' (0)', '', $subscribers_without_segment_label),
|
||||
'value' => 'none'
|
||||
);
|
||||
|
||||
@ -201,8 +212,9 @@ class Subscriber extends Model {
|
||||
->filter('groupBy', $group)
|
||||
->count();
|
||||
|
||||
$label = sprintf('%s (%s)', $segment->name, number_format($subscribers_count));
|
||||
$segment_list[] = array(
|
||||
'label' => sprintf('%s (%d)', $segment->name, $subscribers_count),
|
||||
'label' => str_replace(' (0)', '', $label),
|
||||
'value' => $segment->id()
|
||||
);
|
||||
}
|
||||
@ -300,9 +312,10 @@ class Subscriber extends Model {
|
||||
$customFields = CustomField::findArray();
|
||||
foreach ($customFields as $customField) {
|
||||
$orm = $orm->selectExpr(
|
||||
'CASE WHEN ' .
|
||||
'MAX(CASE WHEN ' .
|
||||
MP_CUSTOM_FIELDS_TABLE . '.id=' . $customField['id'] . ' THEN ' .
|
||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END as "' . $customField['id'].'"');
|
||||
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END) as "' . $customField['id'].'"'
|
||||
);
|
||||
}
|
||||
$orm = $orm
|
||||
->leftOuterJoin(
|
||||
@ -526,12 +539,6 @@ class Subscriber extends Model {
|
||||
return false;
|
||||
}
|
||||
|
||||
static function bulkConfirmUnconfirmed($orm) {
|
||||
$subscribers = $orm->findResultSet();
|
||||
$subscribers->set('status', self::STATUS_SUBSCRIBED)->save();
|
||||
return $subscribers->count();
|
||||
}
|
||||
|
||||
static function bulkSendConfirmationEmail($orm) {
|
||||
$subscribers = $orm
|
||||
->where('status', self::STATUS_UNCONFIRMED)
|
||||
|
@ -2,13 +2,41 @@
|
||||
namespace MailPoet\Newsletter;
|
||||
|
||||
use MailPoet\Newsletter\Editor\Transformer;
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class AutomatedLatestContent {
|
||||
function getPosts($args) {
|
||||
const DEFAULT_POSTS_PER_PAGE = 10;
|
||||
|
||||
function __construct($newsletter_id = false) {
|
||||
$this->newsletter_id = $newsletter_id;
|
||||
|
||||
$this->_attachSentPostsFilter();
|
||||
}
|
||||
|
||||
function __destruct() {
|
||||
$this->_detachSentPostsFilter();
|
||||
}
|
||||
|
||||
function filterOutSentPosts($where) {
|
||||
$sentPostsQuery = 'SELECT ' . MP_NEWSLETTER_POSTS_TABLE . '.post_id FROM '
|
||||
. MP_NEWSLETTER_POSTS_TABLE . ' WHERE '
|
||||
. MP_NEWSLETTER_POSTS_TABLE . ".newsletter_id='" . $this->newsletter_id . "'";
|
||||
|
||||
$wherePostUnsent = 'ID NOT IN (' . $sentPostsQuery . ')';
|
||||
|
||||
if(!empty($where)) $wherePostUnsent = ' AND ' . $wherePostUnsent;
|
||||
|
||||
return $where . $wherePostUnsent;
|
||||
}
|
||||
|
||||
function getPosts($args, $posts_to_exclude = array()) {
|
||||
$posts_per_page = (!empty($args['amount']) && (int)$args['amount'] > 0)
|
||||
? (int)$args['amount']
|
||||
: self::DEFAULT_POSTS_PER_PAGE;
|
||||
$parameters = array(
|
||||
'posts_per_page' => (isset($args['amount'])) ? (int) $args['amount'] : 10,
|
||||
'posts_per_page' => $posts_per_page,
|
||||
'post_type' => (isset($args['contentType'])) ? $args['contentType'] : 'post',
|
||||
'post_status' => (isset($args['postStatus'])) ? $args['postStatus'] : 'publish',
|
||||
'orderby' => 'date',
|
||||
@ -20,7 +48,17 @@ class AutomatedLatestContent {
|
||||
if(isset($args['posts']) && is_array($args['posts'])) {
|
||||
$parameters['post__in'] = $args['posts'];
|
||||
}
|
||||
if(!empty($posts_to_exclude)) {
|
||||
$parameters['post__not_in'] = $posts_to_exclude;
|
||||
}
|
||||
$parameters['tax_query'] = $this->constructTaxonomiesQuery($args);
|
||||
|
||||
// This enables using posts query filters for get_posts, where by default
|
||||
// it is disabled.
|
||||
// However, it also enables other plugins and themes to hook in and alter
|
||||
// the query.
|
||||
$parameters['suppress_filters'] = false;
|
||||
|
||||
return get_posts($parameters);
|
||||
}
|
||||
|
||||
@ -66,4 +104,16 @@ class AutomatedLatestContent {
|
||||
}
|
||||
return $taxonomies_query;
|
||||
}
|
||||
}
|
||||
|
||||
private function _attachSentPostsFilter() {
|
||||
if($this->newsletter_id > 0) {
|
||||
add_action('posts_where', array($this, 'filterOutSentPosts'));
|
||||
}
|
||||
}
|
||||
|
||||
private function _detachSentPostsFilter() {
|
||||
if($this->newsletter_id > 0) {
|
||||
remove_action('posts_where', array($this, 'filterOutSentPosts'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,14 +11,14 @@ class MetaInformationManager {
|
||||
$position_field = $position . 'Text';
|
||||
$text = array();
|
||||
|
||||
if ($args['showAuthor'] === $position_field) {
|
||||
if($args['showAuthor'] === $position_field) {
|
||||
$text[] = self::getPostAuthor(
|
||||
$post->post_author,
|
||||
$args['authorPrecededBy']
|
||||
);
|
||||
}
|
||||
|
||||
if ($args['showCategories'] === $position_field) {
|
||||
if($args['showCategories'] === $position_field) {
|
||||
$text[] = self::getPostCategories(
|
||||
$post->ID,
|
||||
$post->post_type,
|
||||
@ -26,10 +26,10 @@ class MetaInformationManager {
|
||||
);
|
||||
}
|
||||
|
||||
if (!empty($text)) {
|
||||
if(!empty($text)) {
|
||||
$text = '<p>' . implode('<br />', $text) . '</p>';
|
||||
if ($position === 'above') $content = $text . $content;
|
||||
else if ($position === 'below') $content .= $text;
|
||||
if($position === 'above') $content = $text . $content;
|
||||
else if($position === 'below') $content .= $text;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,9 +8,9 @@ class PostContentManager {
|
||||
const MAX_EXCERPT_LENGTH = 60;
|
||||
|
||||
function getContent($post, $displayType) {
|
||||
if ($displayType === 'titleOnly') {
|
||||
if($displayType === 'titleOnly') {
|
||||
return '';
|
||||
} elseif ($displayType === 'excerpt') {
|
||||
} elseif($displayType === 'excerpt') {
|
||||
// get excerpt
|
||||
if(!empty($post->post_excerpt)) {
|
||||
return $post->post_excerpt;
|
||||
@ -50,7 +50,7 @@ class PostContentManager {
|
||||
private function generateExcerpt($content) {
|
||||
// if excerpt is empty then try to find the "more" tag
|
||||
$excerpts = explode('<!--more-->', $content);
|
||||
if (count($excerpts) > 1) {
|
||||
if(count($excerpts) > 1) {
|
||||
// <!--more--> separator was present
|
||||
return $excerpts[0];
|
||||
} else {
|
||||
|
@ -17,7 +17,7 @@ class PostListTransformer {
|
||||
$use_divider = $this->args['showDivider'] === 'true';
|
||||
|
||||
foreach ($posts as $index => $post) {
|
||||
if ($use_divider && $index > 0) {
|
||||
if($use_divider && $index > 0) {
|
||||
$results[] = $this->args['divider'];
|
||||
}
|
||||
|
||||
|
@ -155,13 +155,13 @@ class PostTransformer {
|
||||
private function getPostTitle($post) {
|
||||
$title = $post->post_title;
|
||||
|
||||
if ($this->args['titleIsLink'] === 'true') {
|
||||
if($this->args['titleIsLink'] === 'true') {
|
||||
$title = '<a href="' . get_permalink($post->ID) . '">' . $title . '</a>';
|
||||
}
|
||||
|
||||
if (in_array($this->args['titleFormat'], array('h1', 'h2', 'h3'))) {
|
||||
if(in_array($this->args['titleFormat'], array('h1', 'h2', 'h3'))) {
|
||||
$tag = $this->args['titleFormat'];
|
||||
} elseif ($this->args['titleFormat'] === 'ul') {
|
||||
} elseif($this->args['titleFormat'] === 'ul') {
|
||||
$tag = 'li';
|
||||
} else {
|
||||
$tag = 'h1';
|
||||
|
@ -25,7 +25,7 @@ class StructureTransformer {
|
||||
$top_ancestor = $this->findTopAncestor($item);
|
||||
$offset = $top_ancestor->index();
|
||||
|
||||
if ($item->hasParent('a')) {
|
||||
if($item->hasParent('a')) {
|
||||
$item = $item->parent;
|
||||
}
|
||||
|
||||
@ -46,8 +46,8 @@ class StructureTransformer {
|
||||
*/
|
||||
private function transformTagsToBlocks($root, $image_full_width) {
|
||||
return array_map(function($item) use ($image_full_width) {
|
||||
if ($item->tag === 'img' || $item->tag === 'a' && $item->query('img')) {
|
||||
if ($item->tag === 'a') {
|
||||
if($item->tag === 'img' || $item->tag === 'a' && $item->query('img')) {
|
||||
if($item->tag === 'a') {
|
||||
$link = $item->getAttribute('href');
|
||||
$image = $item->children[0];
|
||||
} else {
|
||||
@ -87,11 +87,11 @@ class StructureTransformer {
|
||||
$updated_structure = array();
|
||||
$text_accumulator = '';
|
||||
foreach ($structure as $item) {
|
||||
if ($item['type'] === 'text') {
|
||||
if($item['type'] === 'text') {
|
||||
$text_accumulator .= $item['text'];
|
||||
}
|
||||
if ($item['type'] !== 'text') {
|
||||
if (!empty($text_accumulator)) {
|
||||
if($item['type'] !== 'text') {
|
||||
if(!empty($text_accumulator)) {
|
||||
$updated_structure[] = array(
|
||||
'type' => 'text',
|
||||
'text' => trim($text_accumulator),
|
||||
@ -102,7 +102,7 @@ class StructureTransformer {
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($text_accumulator)) {
|
||||
if(!empty($text_accumulator)) {
|
||||
$updated_structure[] = array(
|
||||
'type' => 'text',
|
||||
'text' => trim($text_accumulator),
|
||||
|
@ -25,7 +25,7 @@ class TitleListTransformer {
|
||||
$alignment = $this->args['titleAlignment'];
|
||||
$alignment = (in_array($alignment, array('left', 'right', 'center'))) ? $alignment : 'left';
|
||||
|
||||
if ($this->args['titleIsLink']) {
|
||||
if($this->args['titleIsLink']) {
|
||||
$title = '<a href="' . get_permalink($post->ID) . '">' . $title . '</a>';
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ class Transformer {
|
||||
function __construct($args) {
|
||||
$title_list_only = $args['displayType'] === 'titleOnly' && $args['titleFormat'] === 'ul';
|
||||
|
||||
if ($title_list_only) $transformer = new TitleListTransformer($args);
|
||||
if($title_list_only) $transformer = new TitleListTransformer($args);
|
||||
else $transformer = new PostListTransformer($args);
|
||||
$this->transformer = $transformer;
|
||||
}
|
||||
|
@ -1,13 +1,16 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Links;
|
||||
|
||||
use MailPoet\Models\NewsletterLink;
|
||||
use MailPoet\Newsletter\Shortcodes\Shortcodes;
|
||||
use MailPoet\Util\Security;
|
||||
|
||||
class Links {
|
||||
static function extract($text) {
|
||||
const DATA_TAG = '[mailpoet_data]';
|
||||
|
||||
static function extract($content) {
|
||||
// adopted from WP's wp_extract_urls() function & modified to work on hrefs
|
||||
# match href=' or href="
|
||||
# match href=' or href="
|
||||
$regex = '#(?:href.*?=.*?)(["\']?)('
|
||||
# match http://
|
||||
. '(?:([\w-]+:)?//?)'
|
||||
@ -18,22 +21,24 @@ class Links {
|
||||
. '(?:'
|
||||
. '\([\w\d]+\)|'
|
||||
. '(?:'
|
||||
. '[^`!()\[\]{};:\'".,<>«»“”‘’\s]|'
|
||||
. '[^`!()\[\]{}:;\'".,<>«»“”‘’\s]|'
|
||||
. '(?:[:]\d+)?/?'
|
||||
. ')+'
|
||||
. ')'
|
||||
. ')\\1#';
|
||||
preg_match_all($regex, $text, $links);
|
||||
$shortcodes = new Shortcodes();;
|
||||
$shortcodes = $shortcodes->extract($text, $limit = array('subscription'));
|
||||
// extract shortcodes with [link:*] format
|
||||
$shortcodes = new Shortcodes();
|
||||
$shortcodes = $shortcodes->extract($content, $categories = array('link'));
|
||||
// extract links
|
||||
preg_match_all($regex, $content, $links);
|
||||
return array_merge(
|
||||
array_unique($links[2]),
|
||||
$shortcodes
|
||||
);
|
||||
}
|
||||
|
||||
static function replace($text, $links = false) {
|
||||
$links = ($links) ? $links : self::extract($text);
|
||||
static function process($content) {
|
||||
$links = self::extract($content);
|
||||
$processed_links = array();
|
||||
foreach($links as $link) {
|
||||
$hash = Security::generateRandomString(5);
|
||||
@ -42,13 +47,53 @@ class Links {
|
||||
'url' => $link
|
||||
);
|
||||
$encoded_link = sprintf(
|
||||
'%s/?mailpoet&endpoint=track&action=click&data=%s',
|
||||
'%s/?mailpoet&endpoint=track&action=click&data=%s-%s',
|
||||
home_url(),
|
||||
'[mailpoet_data]-'.$hash
|
||||
self::DATA_TAG,
|
||||
$hash
|
||||
);
|
||||
$link_regex = '/' . preg_quote($link, '/') . '/';
|
||||
$text = preg_replace($link_regex, $encoded_link, $text);
|
||||
$content = preg_replace($link_regex, $encoded_link, $content);
|
||||
}
|
||||
return array(
|
||||
$content,
|
||||
$processed_links
|
||||
);
|
||||
}
|
||||
|
||||
static function replaceSubscriberData(
|
||||
$newsletter_id,
|
||||
$subscriber_id,
|
||||
$queue_id,
|
||||
$content
|
||||
) {
|
||||
$regex = sprintf('/data=(%s(?:-\w+)?)/', preg_quote(self::DATA_TAG));
|
||||
preg_match_all($regex, $content, $links);
|
||||
foreach($links[1] as $link) {
|
||||
$hash = null;
|
||||
if(preg_match('/-/', $link)) {
|
||||
list(, $hash) = explode('-', $link);
|
||||
}
|
||||
$data = array(
|
||||
'newsletter' => $newsletter_id,
|
||||
'subscriber' => $subscriber_id,
|
||||
'queue' => $queue_id,
|
||||
'hash' => $hash
|
||||
);
|
||||
$data = rtrim(base64_encode(serialize($data)), '=');
|
||||
$content = str_replace($link, $data, $content);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
static function save($links, $newsletter_id, $queue_id) {
|
||||
foreach($links as $link) {
|
||||
$newsletter_link = NewsletterLink::create();
|
||||
$newsletter_link->newsletter_id = $newsletter_id;
|
||||
$newsletter_link->queue_id = $queue_id;
|
||||
$newsletter_link->hash = $link['hash'];
|
||||
$newsletter_link->url = $link['url'];
|
||||
$newsletter_link->save();
|
||||
}
|
||||
return array($text, $processed_links);
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
class AutomatedLatestContent {
|
||||
static function render($element, $column_count) {
|
||||
$ALC = new \MailPoet\Newsletter\AutomatedLatestContent();
|
||||
$posts = $ALC->getPosts($element);
|
||||
$transformed_posts = array('blocks' => $ALC->transformPosts($element, $posts));
|
||||
$renderer = new Renderer();
|
||||
return $renderer->render($transformed_posts, $column_count);
|
||||
}
|
||||
}
|
@ -43,8 +43,8 @@ class Button {
|
||||
static function calculateWidth($element, $column_count) {
|
||||
$column_width = ColumnsHelper::columnWidth($column_count);
|
||||
$column_width = $column_width - (StylesHelper::$padding_width * 2);
|
||||
$border_width = (int) $element['styles']['block']['borderWidth'];
|
||||
$button_width = (int) $element['styles']['block']['width'];
|
||||
$border_width = (int)$element['styles']['block']['borderWidth'];
|
||||
$button_width = (int)$element['styles']['block']['width'];
|
||||
$button_width = ($button_width > $column_width) ?
|
||||
$column_width :
|
||||
$button_width;
|
||||
|
@ -8,7 +8,7 @@ class Footer {
|
||||
$element['text'] = preg_replace('/\n/', '<br />', $element['text']);
|
||||
$element['text'] = preg_replace('/(<\/?p.*?>)/i', '', $element['text']);
|
||||
$line_height = sprintf(
|
||||
'%spx', StylesHelper::$line_height_multiplier * (int) $element['styles']['text']['fontSize']
|
||||
'%spx', StylesHelper::$line_height_multiplier * (int)$element['styles']['text']['fontSize']
|
||||
);
|
||||
$DOM_parser = new \pQuery();
|
||||
$DOM = $DOM_parser->parseStr($element['text']);
|
||||
|
@ -8,7 +8,7 @@ class Header {
|
||||
$element['text'] = preg_replace('/\n/', '<br />', $element['text']);
|
||||
$element['text'] = preg_replace('/(<\/?p.*?>)/i', '', $element['text']);
|
||||
$line_height = sprintf(
|
||||
'%spx', StylesHelper::$line_height_multiplier * (int) $element['styles']['text']['fontSize']
|
||||
'%spx', StylesHelper::$line_height_multiplier * (int)$element['styles']['text']['fontSize']
|
||||
);
|
||||
$DOM_parser = new \pQuery();
|
||||
$DOM = $DOM_parser->parseStr($element['text']);
|
||||
|
@ -6,8 +6,8 @@ use MailPoet\Newsletter\Renderer\StylesHelper;
|
||||
|
||||
class Image {
|
||||
static function render($element, $column_count) {
|
||||
$element['width'] = (int) $element['width'];
|
||||
$element['height'] = (int) $element['height'];
|
||||
$element['width'] = (int)$element['width'];
|
||||
$element['height'] = (int)$element['height'];
|
||||
$element = self::adjustImageDimensions($element, $column_count);
|
||||
$image_template = '
|
||||
<img style="max-width:' . $element['width'] . 'px;" src="' . $element['src'] . '"
|
||||
@ -32,7 +32,7 @@ class Image {
|
||||
if($element['width'] > $column_width) {
|
||||
$ratio = $element['width'] / $column_width;
|
||||
$element['width'] = $column_width;
|
||||
$element['height'] = (int) ceil($element['height'] / $ratio);
|
||||
$element['height'] = (int)ceil($element['height'] / $ratio);
|
||||
}
|
||||
// resize image if the image is padded and wider than padded column width
|
||||
if($element['fullWidth'] === false &&
|
||||
@ -40,7 +40,7 @@ class Image {
|
||||
) {
|
||||
$ratio = $element['width'] / ($column_width - $padded_width);
|
||||
$element['width'] = $column_width - $padded_width;
|
||||
$element['height'] = (int) ceil($element['height'] / $ratio);
|
||||
$element['height'] = (int)ceil($element['height'] / $ratio);
|
||||
}
|
||||
return $element;
|
||||
}
|
||||
|
@ -4,6 +4,15 @@ namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
use MailPoet\Newsletter\Renderer\StylesHelper;
|
||||
|
||||
class Renderer {
|
||||
public $newsletter;
|
||||
public $posts;
|
||||
|
||||
function __construct(array $newsletter, $posts = false) {
|
||||
$this->newsletter = $newsletter;
|
||||
$this->posts = array();
|
||||
$this->ALC = new \MailPoet\Newsletter\AutomatedLatestContent($this->newsletter['id']);
|
||||
}
|
||||
|
||||
function render($data, $column_count) {
|
||||
$block_content = '';
|
||||
array_map(function($block) use (&$block_content, &$columns, $column_count) {
|
||||
@ -20,8 +29,37 @@ class Renderer {
|
||||
}
|
||||
|
||||
function createElementFromBlockType($block, $column_count) {
|
||||
if($block['type'] === 'automatedLatestContent') {
|
||||
$content = $this->processAutomatedLatestContent($block, $column_count);
|
||||
return $content;
|
||||
}
|
||||
$block = StylesHelper::applyTextAlignment($block);
|
||||
$block_class = __NAMESPACE__ . '\\' . ucfirst($block['type']);
|
||||
return (class_exists($block_class)) ? $block_class::render($block, $column_count) : '';
|
||||
if(!class_exists($block_class)) {
|
||||
return '';
|
||||
}
|
||||
return $block_class::render($block, $column_count);
|
||||
}
|
||||
}
|
||||
|
||||
function processAutomatedLatestContent($args, $column_count) {
|
||||
$posts_to_exclude = $this->getPosts();
|
||||
$ALCPosts = $this->ALC->getPosts($args, $posts_to_exclude);
|
||||
foreach($ALCPosts as $post) {
|
||||
$posts_to_exclude[] = $post->ID;
|
||||
}
|
||||
$transformed_posts = array(
|
||||
'blocks' => $this->ALC->transformPosts($args, $ALCPosts)
|
||||
);
|
||||
$this->setPosts($posts_to_exclude);
|
||||
$rendered_posts = $this->render($transformed_posts, $column_count);
|
||||
return $rendered_posts;
|
||||
}
|
||||
|
||||
function getPosts() {
|
||||
return $this->posts;
|
||||
}
|
||||
|
||||
function setPosts($posts) {
|
||||
return $this->posts = $posts;
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ class Social {
|
||||
foreach($element['icons'] as $index => $icon) {
|
||||
$icons_block .= '
|
||||
<a href="' . $icon['link'] . '" style="text-decoration:none!important;">
|
||||
<img src="' . $icon['image'] . '" width="' . (int) $icon['width'] . '" height="' . (int) $icon['height'] . '" style="width:' . $icon['width'] . ';height:' . $icon['width'] . ';-ms-interpolation-mode:bicubic;border:0;display:inline;outline:none;" alt="' . $icon['iconType'] . '">
|
||||
<img src="' . $icon['image'] . '" width="' . (int)$icon['width'] . '" height="' . (int)$icon['height'] . '" style="width:' . $icon['width'] . ';height:' . $icon['width'] . ';-ms-interpolation-mode:bicubic;border:0;display:inline;outline:none;" alt="' . $icon['iconType'] . '">
|
||||
</a>';
|
||||
}
|
||||
$template = '
|
||||
|
@ -3,7 +3,7 @@ namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
class Spacer {
|
||||
static function render($element) {
|
||||
$height = (int) $element['styles']['block']['height'];
|
||||
$height = (int)$element['styles']['block']['height'];
|
||||
$background_color = $element['styles']['block']['backgroundColor'];
|
||||
$template = '
|
||||
<tr>
|
||||
|
@ -57,14 +57,7 @@ class Text {
|
||||
</tr>
|
||||
</tbody>'
|
||||
);
|
||||
$blockquote->parent->insertChild(
|
||||
array(
|
||||
'tag_name' => 'br',
|
||||
'self_close' => true,
|
||||
'attributes' => array()
|
||||
),
|
||||
$blockquote->index() + 1
|
||||
);
|
||||
$blockquote = self::insertLineBreak($blockquote);
|
||||
}
|
||||
return $DOM->__toString();
|
||||
}
|
||||
@ -75,8 +68,25 @@ class Text {
|
||||
$paragraphs = $DOM->query('p');
|
||||
if(!$paragraphs->count()) return $html;
|
||||
foreach($paragraphs as $paragraph) {
|
||||
// remove empty paragraphs
|
||||
// process empty paragraphs
|
||||
if(!trim($paragraph->html())) {
|
||||
$next_element = ($paragraph->getNextSibling()) ?
|
||||
trim($paragraph->getNextSibling()->text()) :
|
||||
false;
|
||||
$previous_element = ($paragraph->getPreviousSibling()) ?
|
||||
trim($paragraph->getPreviousSibling()->text()) :
|
||||
false;
|
||||
$previous_element_tag = ($previous_element) ?
|
||||
$paragraph->getPreviousSibling()->tag :
|
||||
false;
|
||||
// if previous or next paragraphs are empty OR previous paragraph
|
||||
// is a heading, insert a break line
|
||||
if(!$next_element ||
|
||||
!$previous_element ||
|
||||
(preg_match('/h\d+/', $previous_element_tag))
|
||||
) {
|
||||
$paragraph = self::insertLineBreak($paragraph);
|
||||
}
|
||||
$paragraph->remove();
|
||||
continue;
|
||||
}
|
||||
@ -91,9 +101,13 @@ class Text {
|
||||
$paragraph->cellpadding = 0;
|
||||
$next_element = $paragraph->getNextSibling();
|
||||
// unless this is the last element in column, add double line breaks
|
||||
$line_breaks = ($next_element && !$next_element->getInnerText()) ? '<br /><br />' : '';
|
||||
$line_breaks = ($next_element && !trim($next_element->text())) ?
|
||||
'<br /><br />' :
|
||||
'';
|
||||
// if this element is followed by a list, add single line break
|
||||
$line_breaks = ($next_element && preg_match('/<li>/i', $next_element->getInnerText())) ? '<br />' : $line_breaks;
|
||||
$line_breaks = ($next_element && preg_match('/<li>/i', $next_element->text())) ?
|
||||
'<br />' :
|
||||
$line_breaks;
|
||||
$paragraph->html('
|
||||
<tr>
|
||||
<td class="mailpoet_paragraph" style="word-break:break-word;word-wrap:break-word;' . $style . '">
|
||||
@ -139,4 +153,16 @@ class Text {
|
||||
static function removeLastLineBreak($html) {
|
||||
return preg_replace('/(^)?(<br.*?\/?>)+$/i', '', $html);
|
||||
}
|
||||
|
||||
static function insertLineBreak($element) {
|
||||
$element->parent->insertChild(
|
||||
array(
|
||||
'tag_name' => 'br',
|
||||
'self_close' => true,
|
||||
'attributes' => array()
|
||||
),
|
||||
$element->index() + 1
|
||||
);
|
||||
return $element;
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Renderer\PostProcess;
|
||||
|
||||
use MailPoet\Newsletter\Links\Links;
|
||||
|
||||
class OpenTracking {
|
||||
static function process($template) {
|
||||
$DOM = new \pQuery();
|
||||
@ -9,7 +11,7 @@ class OpenTracking {
|
||||
$open_tracking_link = sprintf(
|
||||
'<img alt="" class="" src="%s/%s"/>',
|
||||
home_url(),
|
||||
htmlentities('?mailpoet&endpoint=track&action=open&data=[mailpoet_data]')
|
||||
esc_attr('?mailpoet&endpoint=track&action=open&data=' . Links::DATA_TAG)
|
||||
);
|
||||
$template->html($template->html() . $open_tracking_link);
|
||||
return $DOM->__toString();
|
||||
|
@ -4,35 +4,31 @@ namespace MailPoet\Newsletter\Renderer;
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Renderer {
|
||||
public $template = 'Template.html';
|
||||
public $blocks_renderer;
|
||||
public $columns_renderer;
|
||||
public $DOM_parser;
|
||||
public $CSS_inliner;
|
||||
public $newsletter;
|
||||
const NEWSLETTER_TEMPLATE = 'Template.html';
|
||||
|
||||
function __construct($newsletter) {
|
||||
$this->blocks_renderer = new Blocks\Renderer();
|
||||
function __construct(array $newsletter) {
|
||||
$this->newsletter = $newsletter;
|
||||
$this->blocks_renderer = new Blocks\Renderer($this->newsletter);
|
||||
$this->columns_renderer = new Columns\Renderer();
|
||||
$this->DOM_parser = new \pQuery();
|
||||
$this->CSS_inliner = new \MailPoet\Util\CSS();
|
||||
$this->newsletter = $newsletter;
|
||||
$this->template = file_get_contents(dirname(__FILE__) . '/' . $this->template);
|
||||
$this->template = file_get_contents(dirname(__FILE__) . '/' . self::NEWSLETTER_TEMPLATE);
|
||||
}
|
||||
|
||||
function render() {
|
||||
$newsletter_data = (is_array($this->newsletter['body'])) ?
|
||||
$this->newsletter['body'] :
|
||||
json_decode($this->newsletter['body'], true);
|
||||
$newsletter_body = $this->renderBody($newsletter_data['content']);
|
||||
$newsletter_styles = $this->renderStyles($newsletter_data['globalStyles']);
|
||||
$newsletter_subject = $this->newsletter['subject'];
|
||||
$newsletter_preheader = $this->newsletter['preheader'];
|
||||
$newsletter = $this->newsletter;
|
||||
$rendered_body = $this->renderBody($newsletter['body']['content']);
|
||||
$rendered_styles = $this->renderStyles($newsletter['body']['globalStyles']);
|
||||
$template = $this->injectContentIntoTemplate($this->template, array(
|
||||
$newsletter_subject,
|
||||
$newsletter_styles,
|
||||
$newsletter_preheader,
|
||||
$newsletter_body
|
||||
$newsletter['subject'],
|
||||
$rendered_styles,
|
||||
$newsletter['preheader'],
|
||||
$rendered_body
|
||||
));
|
||||
$template = $this->inlineCSSStyles($template);
|
||||
$template = $this->postProcessTemplate($template);
|
||||
@ -43,7 +39,7 @@ class Renderer {
|
||||
}
|
||||
|
||||
function renderBody($content) {
|
||||
$content = array_map(function($content_block) {
|
||||
$rendered_content = array_map(function($content_block) {
|
||||
$column_count = count($content_block['blocks']);
|
||||
$column_data = $this->blocks_renderer->render(
|
||||
$content_block,
|
||||
@ -55,7 +51,7 @@ class Renderer {
|
||||
$column_data
|
||||
);
|
||||
}, $content['blocks']);
|
||||
return implode('', $content);
|
||||
return implode('', $rendered_content);
|
||||
}
|
||||
|
||||
function renderStyles($styles) {
|
||||
@ -80,9 +76,9 @@ class Renderer {
|
||||
return $css;
|
||||
}
|
||||
|
||||
function injectContentIntoTemplate($template, $data) {
|
||||
return preg_replace_callback('/{{\w+}}/', function($matches) use (&$data) {
|
||||
return array_shift($data);
|
||||
function injectContentIntoTemplate($template, $content) {
|
||||
return preg_replace_callback('/{{\w+}}/', function($matches) use (&$content) {
|
||||
return array_shift($content);
|
||||
}, $template);
|
||||
}
|
||||
|
||||
|
@ -89,15 +89,15 @@ class StylesHelper {
|
||||
}
|
||||
|
||||
static function applyHeadingMargin($style, $selector) {
|
||||
if (!preg_match('/h[1-4]/i', $selector)) return $style;
|
||||
$font_size = (int) $style['fontSize'];
|
||||
if(!preg_match('/h[1-4]/i', $selector)) return $style;
|
||||
$font_size = (int)$style['fontSize'];
|
||||
$style['margin'] = sprintf('0 0 %spx 0', self::$heading_margin_multiplier * $font_size);
|
||||
return $style;
|
||||
}
|
||||
|
||||
static function applyLineHeight($style, $selector) {
|
||||
if (!preg_match('/mailpoet_paragraph|h[1-4]/i', $selector)) return $style;
|
||||
$font_size = (int) $style['fontSize'];
|
||||
if(!preg_match('/mailpoet_paragraph|h[1-4]/i', $selector)) return $style;
|
||||
$font_size = (int)$style['fontSize'];
|
||||
$style['lineHeight'] = sprintf('%spx', self::$line_height_multiplier * $font_size);
|
||||
return $style;
|
||||
}
|
||||
|
@ -1,132 +1,192 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Scheduler;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\NewsletterOption;
|
||||
use MailPoet\Models\NewsletterOptionField;
|
||||
use MailPoet\Models\SendingQueue;
|
||||
|
||||
class Scheduler {
|
||||
const SECONDS_IN_HOUR = 3600;
|
||||
const LAST_WEEKDAY_FORMAT = 'L';
|
||||
const WORDPRESS_ALL_ROLES = 'mailpoet_all';
|
||||
|
||||
static function postNotification($newsletter_id) {
|
||||
$newsletter = Newsletter::filter('filterWithOptions')
|
||||
->findOne($newsletter_id)
|
||||
->asArray();
|
||||
$interval_type = $newsletter['intervalType'];
|
||||
$hour = (int) $newsletter['timeOfDay'] / self::SECONDS_IN_HOUR;
|
||||
$week_day = $newsletter['weekDay'];
|
||||
$month_day = $newsletter['monthDay'];
|
||||
$nth_week_day = ($newsletter['nthWeekDay'] === self::LAST_WEEKDAY_FORMAT) ?
|
||||
$newsletter['nthWeekDay'] :
|
||||
'#' . $newsletter['nthWeekDay'];
|
||||
switch($interval_type) {
|
||||
case 'immediately':
|
||||
$schedule = '* * * * *';
|
||||
break;
|
||||
case 'immediate':
|
||||
case 'daily':
|
||||
$schedule = sprintf('0 %s * * *', $hour);
|
||||
break;
|
||||
case 'weekly':
|
||||
$schedule = sprintf('0 %s * * %s', $hour, $week_day);
|
||||
break;
|
||||
case 'monthly':
|
||||
$schedule = sprintf('0 %s %s * *', $hour, $month_day);
|
||||
break;
|
||||
case 'nthWeekDay':
|
||||
$schedule = sprintf('0 %s ? * %s%s', $hour, $week_day, $nth_week_day);
|
||||
break;
|
||||
}
|
||||
$option_field = NewsletterOptionField::where('name', 'schedule')
|
||||
->findOne()
|
||||
->asArray();
|
||||
$relation = NewsletterOption::where('newsletter_id', $newsletter_id)
|
||||
->where('option_field_id', $option_field['id'])
|
||||
->findOne();
|
||||
if(!$relation) {
|
||||
$relation = NewsletterOption::create();
|
||||
$relation->newsletter_id = $newsletter['id'];
|
||||
$relation->option_field_id = $option_field['id'];
|
||||
}
|
||||
$relation->value = $schedule;
|
||||
$relation->save();
|
||||
}
|
||||
|
||||
static function welcomeForSegmentSubscription($subscriber_id, array $segments) {
|
||||
$newsletters = self::getWelcomeNewsletters();
|
||||
if(!count($newsletters)) return;
|
||||
foreach($newsletters as $newsletter) {
|
||||
if($newsletter['event'] === 'segment' &&
|
||||
in_array($newsletter['segment'], $segments)
|
||||
) {
|
||||
self::createSendingQueueEntry($newsletter, $subscriber_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static function welcomeForNewWPUser($subscriber_id, array $wp_user, $old_user_data) {
|
||||
$newsletters = self::getWelcomeNewsletters();
|
||||
if(!count($newsletters)) return;
|
||||
foreach($newsletters as $newsletter) {
|
||||
if($newsletter['event'] === 'user') {
|
||||
if($old_user_data) {
|
||||
// do not schedule welcome newsletter if roles have not changed
|
||||
$old_role = (array) $old_user_data->roles;
|
||||
$new_role = (array) $wp_user->roles;
|
||||
if($newsletter['role'] === self::WORDPRESS_ALL_ROLES ||
|
||||
!array_diff($old_role, $new_role)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if($newsletter['role'] === self::WORDPRESS_ALL_ROLES ||
|
||||
in_array($newsletter['role'], $wp_user['roles'])
|
||||
) {
|
||||
self::createSendingQueueEntry($newsletter, $subscriber_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static function getWelcomeNewsletters() {
|
||||
return Newsletter::where('type', 'welcome')
|
||||
->whereNull('deleted_at')
|
||||
->filter('filterWithOptions')
|
||||
->findArray();
|
||||
}
|
||||
|
||||
private static function createSendingQueueEntry($newsletter, $subscriber_id) {
|
||||
$queue = SendingQueue::create();
|
||||
$queue->newsletter_id = $newsletter['id'];
|
||||
$queue->subscribers = serialize(
|
||||
array(
|
||||
'to_process' => array($subscriber_id)
|
||||
)
|
||||
);
|
||||
$queue->count_total = $queue->count_to_process = 1;
|
||||
$after_time_type = $newsletter['afterTimeType'];
|
||||
$after_time_number = $newsletter['afterTimeNumber'];
|
||||
$scheduled_at = null;
|
||||
$current_time = Carbon::createFromTimestamp(current_time('timestamp'));
|
||||
switch($after_time_type) {
|
||||
case 'hours':
|
||||
$scheduled_at = $current_time->addHours($after_time_number);
|
||||
break;
|
||||
case 'days':
|
||||
$scheduled_at = $current_time->addDays($after_time_number);
|
||||
break;
|
||||
case 'weeks':
|
||||
$scheduled_at = $current_time->addWeeks($after_time_number);
|
||||
break;
|
||||
default:
|
||||
$scheduled_at = $current_time;
|
||||
}
|
||||
$queue->status = 'scheduled';
|
||||
$queue->scheduled_at = $scheduled_at;
|
||||
$queue->save();
|
||||
}
|
||||
}
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Scheduler;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\NewsletterOption;
|
||||
use MailPoet\Models\NewsletterOptionField;
|
||||
use MailPoet\Models\NewsletterPost;
|
||||
use MailPoet\Models\SendingQueue;
|
||||
|
||||
class Scheduler {
|
||||
const SECONDS_IN_HOUR = 3600;
|
||||
const LAST_WEEKDAY_FORMAT = 'L';
|
||||
const WORDPRESS_ALL_ROLES = 'mailpoet_all';
|
||||
const INTERVAL_IMMEDIATELY = 'immediately';
|
||||
const INTERVAL_IMMEDIATE = 'immediate';
|
||||
const INTERVAL_DAILY = 'daily';
|
||||
const INTERVAL_WEEKLY = 'weekly';
|
||||
const INTERVAL_MONTHLY = 'monthly';
|
||||
const INTERVAL_NTHWEEKDAY = 'nthWeekDay';
|
||||
const STATUS_SCHEDULED = 'scheduled';
|
||||
|
||||
static function processPostNotificationSchedule($newsletter_id) {
|
||||
$newsletter = Newsletter::filter('filterWithOptions')
|
||||
->findOne($newsletter_id);
|
||||
if(!$newsletter) return;
|
||||
$newsletter = $newsletter->asArray();
|
||||
$interval_type = $newsletter['intervalType'];
|
||||
$hour = (int)$newsletter['timeOfDay'] / self::SECONDS_IN_HOUR;
|
||||
$week_day = $newsletter['weekDay'];
|
||||
$month_day = $newsletter['monthDay'];
|
||||
$nth_week_day = ($newsletter['nthWeekDay'] === self::LAST_WEEKDAY_FORMAT) ?
|
||||
$newsletter['nthWeekDay'] :
|
||||
'#' . $newsletter['nthWeekDay'];
|
||||
switch($interval_type) {
|
||||
case self::INTERVAL_IMMEDIATELY:
|
||||
$schedule = '* * * * *';
|
||||
break;
|
||||
case self::INTERVAL_IMMEDIATE:
|
||||
case self::INTERVAL_DAILY:
|
||||
$schedule = sprintf('0 %s * * *', $hour);
|
||||
break;
|
||||
case self::INTERVAL_WEEKLY:
|
||||
$schedule = sprintf('0 %s * * %s', $hour, $week_day);
|
||||
break;
|
||||
case self::INTERVAL_NTHWEEKDAY:
|
||||
$schedule = sprintf('0 %s ? * %s%s', $hour, $week_day, $nth_week_day);
|
||||
break;
|
||||
case self::INTERVAL_MONTHLY:
|
||||
$schedule = sprintf('0 %s %s * *', $hour, $month_day);
|
||||
break;
|
||||
}
|
||||
$option_field = NewsletterOptionField::where('name', 'schedule')
|
||||
->findOne()
|
||||
->asArray();
|
||||
$relation = NewsletterOption::where('newsletter_id', $newsletter_id)
|
||||
->where('option_field_id', $option_field['id'])
|
||||
->findOne();
|
||||
if(!$relation) {
|
||||
$relation = NewsletterOption::create();
|
||||
$relation->newsletter_id = $newsletter['id'];
|
||||
$relation->option_field_id = $option_field['id'];
|
||||
}
|
||||
$relation->value = $schedule;
|
||||
$relation->save();
|
||||
return Newsletter::filter('filterWithOptions')
|
||||
->findOne($newsletter_id)->asArray();
|
||||
}
|
||||
|
||||
static function schedulePostNotification($post_id) {
|
||||
$newsletters = self::getNewsletters('notification');
|
||||
if(!count($newsletters)) return;
|
||||
foreach($newsletters as $newsletter) {
|
||||
$post = NewsletterPost::where('newsletter_id', $newsletter['id'])
|
||||
->where('post_id', $post_id)
|
||||
->findOne();
|
||||
if($post === false) {
|
||||
$scheduled_notification = self::createPostNotificationQueue($newsletter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a properly formatted timestamp for use in Scheduler from
|
||||
* arbitrarily formatted timestamp strings.
|
||||
*/
|
||||
static function scheduleFromTimestamp($timestamp) {
|
||||
return Carbon::parse($timestamp)->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
static function scheduleSubscriberWelcomeNotification(
|
||||
$subscriber_id,
|
||||
array $segments
|
||||
) {
|
||||
$newsletters = self::getNewsletters('welcome');
|
||||
if(!count($newsletters)) return;
|
||||
foreach($newsletters as $newsletter) {
|
||||
if($newsletter['event'] === 'segment' &&
|
||||
in_array($newsletter['segment'], $segments)
|
||||
) {
|
||||
self::createWelcomeNotificationQueue($newsletter, $subscriber_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static function scheduleWPUserWelcomeNotification(
|
||||
$subscriber_id,
|
||||
array $wp_user,
|
||||
$old_user_data
|
||||
) {
|
||||
$newsletters = self::getNewsletters('welcome');
|
||||
if(!count($newsletters)) return;
|
||||
foreach($newsletters as $newsletter) {
|
||||
if($newsletter['event'] === 'user') {
|
||||
if($old_user_data) {
|
||||
// do not schedule welcome newsletter if roles have not changed
|
||||
$old_role = (array) $old_user_data->roles;
|
||||
$new_role = (array) $wp_user->roles;
|
||||
if($newsletter['role'] === self::WORDPRESS_ALL_ROLES ||
|
||||
!array_diff($old_role, $new_role)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if($newsletter['role'] === self::WORDPRESS_ALL_ROLES ||
|
||||
in_array($newsletter['role'], $wp_user['roles'])
|
||||
) {
|
||||
self::createWelcomeNotificationQueue($newsletter, $subscriber_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static function getNewsletters($type) {
|
||||
return Newsletter::where('type', $type)
|
||||
->whereNull('deleted_at')
|
||||
->filter('filterWithOptions')
|
||||
->findArray();
|
||||
}
|
||||
|
||||
static function createWelcomeNotificationQueue($newsletter, $subscriber_id) {
|
||||
$queue = SendingQueue::create();
|
||||
$queue->newsletter_id = $newsletter['id'];
|
||||
$queue->subscribers = serialize(
|
||||
array(
|
||||
'to_process' => array($subscriber_id)
|
||||
)
|
||||
);
|
||||
$queue->count_total = $queue->count_to_process = 1;
|
||||
$after_time_type = $newsletter['afterTimeType'];
|
||||
$after_time_number = $newsletter['afterTimeNumber'];
|
||||
$scheduled_at = null;
|
||||
$current_time = Carbon::createFromTimestamp(current_time('timestamp'));
|
||||
switch($after_time_type) {
|
||||
case 'hours':
|
||||
$scheduled_at = $current_time->addHours($after_time_number);
|
||||
break;
|
||||
case 'days':
|
||||
$scheduled_at = $current_time->addDays($after_time_number);
|
||||
break;
|
||||
case 'weeks':
|
||||
$scheduled_at = $current_time->addWeeks($after_time_number);
|
||||
break;
|
||||
default:
|
||||
$scheduled_at = $current_time;
|
||||
}
|
||||
$queue->status = self::STATUS_SCHEDULED;
|
||||
$queue->scheduled_at = $scheduled_at;
|
||||
$queue->save();
|
||||
}
|
||||
|
||||
static function createPostNotificationQueue($newsletter) {
|
||||
$next_run_date = self::getNextRunDate($newsletter['schedule']);
|
||||
// do not schedule duplicate queues for the same time
|
||||
$existing_queue = SendingQueue::where('newsletter_id', $newsletter['id'])
|
||||
->where('scheduled_at', $next_run_date)
|
||||
->findOne();
|
||||
if($existing_queue) return;
|
||||
$queue = SendingQueue::create();
|
||||
$queue->newsletter_id = $newsletter['id'];
|
||||
$queue->status = 'scheduled';
|
||||
$queue->scheduled_at = $next_run_date;
|
||||
$queue->save();
|
||||
return $queue;
|
||||
}
|
||||
|
||||
static function getNextRunDate($schedule) {
|
||||
$schedule = \Cron\CronExpression::factory($schedule);
|
||||
return $schedule->getNextRunDate(current_time('mysql'))
|
||||
->format('Y-m-d H:i:s');
|
||||
}
|
||||
}
|
||||
|
171
lib/Newsletter/Shortcodes/Categories/Link.php
Normal file
@ -0,0 +1,171 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Shortcodes\Categories;
|
||||
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Statistics\Track\Unsubscribes;
|
||||
use MailPoet\Subscription\Url as SubscriptionUrl;
|
||||
|
||||
class Link {
|
||||
/*
|
||||
{
|
||||
text: '<%= __('Unsubscribe') %>',-
|
||||
shortcode: 'subscription:unsubscribe',
|
||||
},
|
||||
{
|
||||
text: '<%= __('Manage subscription') %>',
|
||||
shortcode: 'subscription:manage',
|
||||
},
|
||||
{
|
||||
text: '<%= __('View in browser link') %>',
|
||||
shortcode: 'newsletter:view_in_browser',
|
||||
}
|
||||
*/
|
||||
static function process($action,
|
||||
$default_value = false,
|
||||
$newsletter,
|
||||
$subscriber,
|
||||
$queue = false
|
||||
) {
|
||||
switch($action) {
|
||||
case 'subscription_unsubscribe':
|
||||
$action = 'subscription_unsubscribe_url';
|
||||
$url = self::processUrl(
|
||||
$action,
|
||||
esc_attr(SubscriptionUrl::getUnsubscribeUrl($subscriber)),
|
||||
$queue
|
||||
);
|
||||
return sprintf(
|
||||
'<a target="_blank" href="%s">%s</a>',
|
||||
$url,
|
||||
__('Unsubscribe')
|
||||
);
|
||||
break;
|
||||
|
||||
case 'subscription_unsubscribe_url':
|
||||
return self::processUrl(
|
||||
$action,
|
||||
SubscriptionUrl::getUnsubscribeUrl($subscriber),
|
||||
$queue
|
||||
);
|
||||
break;
|
||||
|
||||
case 'subscription_manage':
|
||||
$url = self::processUrl(
|
||||
$action = 'subscription_manage_url',
|
||||
esc_attr(SubscriptionUrl::getManageUrl($subscriber)),
|
||||
$queue
|
||||
);
|
||||
return sprintf(
|
||||
'<a target="_blank" href="%s">%s</a>',
|
||||
$url,
|
||||
__('Manage subscription')
|
||||
);
|
||||
break;
|
||||
|
||||
case 'subscription_manage_url':
|
||||
return self::processUrl(
|
||||
$action,
|
||||
SubscriptionUrl::getManageUrl($subscriber),
|
||||
$queue
|
||||
);
|
||||
break;
|
||||
|
||||
case 'newsletter_view_in_browser':
|
||||
$action = 'view_in_browser_url';
|
||||
$url = esc_attr(self::getViewInBrowserUrl($newsletter, $subscriber, $queue));
|
||||
$url = self::processUrl($action, $url, $queue);
|
||||
return sprintf(
|
||||
'<a target="_blank" href="%s">%s</a>',
|
||||
$url,
|
||||
__('View in your browser')
|
||||
);
|
||||
break;
|
||||
|
||||
case 'newsletter_view_in_browser_url':
|
||||
$url = self::getViewInBrowserUrl($newsletter, $subscriber, $queue);
|
||||
return self::processUrl($action, $url, $queue);
|
||||
break;
|
||||
|
||||
default:
|
||||
$shortcode = self::getShortcode($action);
|
||||
$url = apply_filters(
|
||||
'mailpoet_newsletter_shortcode_link',
|
||||
$shortcode,
|
||||
$newsletter,
|
||||
$subscriber,
|
||||
$queue
|
||||
);
|
||||
return ($url !== $shortcode) ?
|
||||
self::processUrl($action, $url, $queue) :
|
||||
false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static function getViewInBrowserUrl(
|
||||
$newsletter,
|
||||
$subscriber = false,
|
||||
$queue = false
|
||||
) {
|
||||
$data = array(
|
||||
'newsletter' => (isset($newsletter['id'])) ?
|
||||
$newsletter['id'] :
|
||||
$newsletter,
|
||||
'subscriber' => (isset($subscriber['id'])) ?
|
||||
$subscriber['id'] :
|
||||
$subscriber,
|
||||
'subscriber_token' => (isset($subscriber['id'])) ?
|
||||
Subscriber::generateToken($subscriber['email']) :
|
||||
false,
|
||||
'queue' => (isset($queue['id'])) ?
|
||||
$queue['id'] :
|
||||
$queue
|
||||
);
|
||||
$data = rtrim(base64_encode(serialize($data)), '=');
|
||||
return home_url() . '/?mailpoet&endpoint=view_in_browser&data=' . $data;
|
||||
}
|
||||
|
||||
static function processUrl($action, $url, $queue) {
|
||||
return ($queue !== false && (boolean) Setting::getValue('tracking.enabled')) ?
|
||||
self::getShortcode($action) :
|
||||
$url;
|
||||
}
|
||||
|
||||
static function processShortcodeAction(
|
||||
$shortcode_action, $newsletter, $subscriber, $queue
|
||||
) {
|
||||
switch($shortcode_action) {
|
||||
case 'subscription_unsubscribe_url':
|
||||
// track unsubscribe event
|
||||
if((boolean) Setting::getValue('tracking.enabled')) {
|
||||
$unsubscribe = new Unsubscribes();
|
||||
$unsubscribe->track($subscriber['id'], $queue['id'], $newsletter['id']);
|
||||
}
|
||||
$url = SubscriptionUrl::getUnsubscribeUrl($subscriber);
|
||||
break;
|
||||
case 'subscription_manage_url':
|
||||
$url = SubscriptionUrl::getManageUrl($subscriber);
|
||||
break;
|
||||
case 'newsletter_view_in_browser_url':
|
||||
$url = Link::getViewInBrowserUrl($newsletter, $subscriber, $queue);
|
||||
break;
|
||||
default:
|
||||
$shortcode = self::getShortcode($shortcode_action);
|
||||
$url = apply_filters(
|
||||
'mailpoet_newsletter_shortcode_link',
|
||||
$shortcode,
|
||||
$newsletter,
|
||||
$subscriber,
|
||||
$queue
|
||||
);
|
||||
$url = ($url !== $shortcode_action) ? $url : false;
|
||||
break;
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
private static function getShortcode($action) {
|
||||
return sprintf('[link:%s]', $action);
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
namespace MailPoet\Newsletter\Shortcodes\Categories;
|
||||
|
||||
use MailPoet\Models\SendingQueue;
|
||||
use MailPoet\Newsletter\Shortcodes\ShortcodesHelper;
|
||||
|
||||
require_once( ABSPATH . "wp-includes/pluggable.php" );
|
||||
|
||||
@ -26,36 +27,33 @@ class Newsletter {
|
||||
{
|
||||
text: '<%= __('Issue number') %>',
|
||||
shortcode: 'newsletter:number',
|
||||
},
|
||||
{
|
||||
text: '<%= __('View in browser link') %>',
|
||||
shortcode: 'newsletter:view_in_browser',
|
||||
}
|
||||
*/
|
||||
static function process($action,
|
||||
$default_value = false,
|
||||
$newsletter, $subscriber = false, $text) {
|
||||
if(is_object($newsletter)) {
|
||||
$newsletter = $newsletter->asArray();
|
||||
}
|
||||
$newsletter,
|
||||
$subscriber,
|
||||
$queue = false,
|
||||
$content
|
||||
) {
|
||||
switch($action) {
|
||||
case 'subject':
|
||||
return ($newsletter) ? $newsletter['subject'] : false;
|
||||
break;
|
||||
|
||||
case 'total':
|
||||
return substr_count($text, 'data-post-id');
|
||||
return substr_count($content, 'data-post-id');
|
||||
break;
|
||||
|
||||
case 'post_title':
|
||||
preg_match_all('/data-post-id="(\w+)"/ism', $text, $posts);
|
||||
preg_match_all('/data-post-id="(\d+)"/ism', $content, $posts);
|
||||
$post_ids = array_unique($posts[1]);
|
||||
$latest_post = self::getLatestWPPost($post_ids);
|
||||
return ($latest_post) ? $latest_post['post_title'] : false;
|
||||
break;
|
||||
|
||||
case 'number':
|
||||
if ($newsletter['type'] !== 'notification') return false;
|
||||
if($newsletter['type'] !== 'notification') return false;
|
||||
$sent_newsletters =
|
||||
SendingQueue::where('newsletter_id', $newsletter['id'])
|
||||
->where('status', 'completed')
|
||||
@ -63,14 +61,6 @@ class Newsletter {
|
||||
return ++$sent_newsletters;
|
||||
break;
|
||||
|
||||
case 'view_in_browser':
|
||||
return '<a href="#TODO">'.__('View in your browser').'</a>';
|
||||
break;
|
||||
|
||||
case 'view_in_browser_url':
|
||||
return '#TODO';
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
|
@ -1,69 +0,0 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Shortcodes\Categories;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Subscription\Url as SubscriptionUrl;
|
||||
|
||||
class Subscription {
|
||||
/*
|
||||
{
|
||||
text: '<%= __('Unsubscribe') %>',-
|
||||
shortcode: 'subscription:unsubscribe',
|
||||
},
|
||||
{
|
||||
text: '<%= __('Manage subscriptions') %>',
|
||||
shortcode: 'subscription:manage',
|
||||
},
|
||||
*/
|
||||
static function process(
|
||||
$action,
|
||||
$default_value = false,
|
||||
$newsletter = false,
|
||||
$subscriber = false,
|
||||
$text = false,
|
||||
$shortcode
|
||||
) {
|
||||
switch($action) {
|
||||
case 'unsubscribe':
|
||||
return '<a target="_blank" href="'.
|
||||
self::getShortcodeUrl(
|
||||
$shortcode,
|
||||
esc_attr(SubscriptionUrl::getUnsubscribeUrl($subscriber))
|
||||
)
|
||||
.'">'.__('Unsubscribe').'</a>';
|
||||
break;
|
||||
|
||||
case 'unsubscribe_url':
|
||||
return self::getShortcodeUrl(
|
||||
$shortcode,
|
||||
SubscriptionUrl::getUnsubscribeUrl($subscriber)
|
||||
);
|
||||
break;
|
||||
|
||||
case 'manage':
|
||||
return '<a target="_blank" href="'.
|
||||
self::getShortcodeUrl(
|
||||
$shortcode,
|
||||
esc_attr(SubscriptionUrl::getManageUrl($subscriber))
|
||||
)
|
||||
.'">'.__('Manage subscription').'</a>';
|
||||
break;
|
||||
|
||||
case 'manage_url':
|
||||
return self::getShortcodeUrl(
|
||||
$shortcode,
|
||||
SubscriptionUrl::getManageUrl($subscriber)
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static function getShortcodeUrl($shortcode, $url) {
|
||||
return ((boolean) Setting::getValue('tracking.enabled')) ?
|
||||
$shortcode :
|
||||
$url;
|
||||
}
|
||||
}
|
@ -28,10 +28,12 @@ class User {
|
||||
shortcode: 'user:count',
|
||||
}
|
||||
*/
|
||||
static function process($action, $default_value, $newsletter = false, $subscriber) {
|
||||
if(is_object($subscriber)) {
|
||||
$subscriber = $subscriber->asArray();
|
||||
}
|
||||
static function process(
|
||||
$action,
|
||||
$default_value,
|
||||
$newsletter = false,
|
||||
$subscriber
|
||||
) {
|
||||
switch($action) {
|
||||
case 'firstname':
|
||||
return ($subscriber) ? $subscriber['first_name'] : $default_value;
|
||||
|
@ -4,61 +4,86 @@ namespace MailPoet\Newsletter\Shortcodes;
|
||||
class Shortcodes {
|
||||
public $newsletter;
|
||||
public $subscriber;
|
||||
public $queue;
|
||||
|
||||
function __construct(
|
||||
$newsletter = false,
|
||||
$subscriber = false
|
||||
$subscriber = false,
|
||||
$queue = false
|
||||
) {
|
||||
$this->newsletter = $newsletter;
|
||||
$this->subscriber = $subscriber;
|
||||
$this->newsletter = (is_object($newsletter)) ?
|
||||
$newsletter->asArray() :
|
||||
$newsletter;
|
||||
$this->subscriber = (is_object($subscriber)) ?
|
||||
$subscriber->asArray() :
|
||||
$subscriber;
|
||||
$this->queue = (is_object($queue)) ?
|
||||
$queue->asArray() :
|
||||
$queue;
|
||||
}
|
||||
|
||||
function extract($text, $limit = false) {
|
||||
$limit = (is_array($limit)) ? implode('|', $limit) : false;
|
||||
function extract($content, $categories= false) {
|
||||
$categories = (is_array($categories)) ? implode('|', $categories) : false;
|
||||
$regex = sprintf(
|
||||
'/\[%s:.*?\]/ism',
|
||||
($limit) ? '(?:' . $limit . ')' : '(?:\w+)'
|
||||
($categories) ? '(?:' . $categories . ')' : '(?:\w+)'
|
||||
);
|
||||
preg_match_all($regex, $text, $shortcodes);
|
||||
preg_match_all($regex, $content, $shortcodes);
|
||||
return array_unique($shortcodes[0]);
|
||||
}
|
||||
|
||||
function match($shortcode) {
|
||||
preg_match(
|
||||
'/\[(?P<type>\w+):(?P<action>\w+)(?:.*?default:(?P<default>.*?))?\]/',
|
||||
'/\[(?P<category>\w+)?:(?P<action>\w+)(?:.*?\|.*?default:(?P<default>.*?))?\]/',
|
||||
$shortcode,
|
||||
$match
|
||||
);
|
||||
return $match;
|
||||
}
|
||||
|
||||
function process($shortcodes, $text) {
|
||||
function process($shortcodes, $content = false) {
|
||||
$processed_shortcodes = array_map(
|
||||
function($shortcode) use($text) {
|
||||
function($shortcode) use($content) {
|
||||
$shortcode_details = $this->match($shortcode);
|
||||
$shortcode_type = ucfirst($shortcode_details['type']);
|
||||
$shortcode_action = $shortcode_details['action'];
|
||||
$shortcode_category = isset($shortcode_details['category']) ?
|
||||
ucfirst($shortcode_details['category']) :
|
||||
false;
|
||||
$shortcode_action = isset($shortcode_details['action']) ?
|
||||
$shortcode_details['action'] :
|
||||
false;
|
||||
$shortcode_class =
|
||||
__NAMESPACE__ . '\\Categories\\' . $shortcode_type;
|
||||
__NAMESPACE__ . '\\Categories\\' . $shortcode_category;
|
||||
$shortcode_default_value = isset($shortcode_details['default'])
|
||||
? $shortcode_details['default'] : false;
|
||||
if(!class_exists($shortcode_class)) return false;
|
||||
if(!class_exists($shortcode_class)) {
|
||||
$custom_shortcode = apply_filters(
|
||||
'mailpoet_newsletter_shortcode',
|
||||
$shortcode,
|
||||
$this->newsletter,
|
||||
$this->subscriber,
|
||||
$this->queue,
|
||||
$content
|
||||
);
|
||||
return ($custom_shortcode === $shortcode) ?
|
||||
false :
|
||||
$custom_shortcode;
|
||||
}
|
||||
return $shortcode_class::process(
|
||||
$shortcode_action,
|
||||
$shortcode_default_value,
|
||||
$this->newsletter,
|
||||
$this->subscriber,
|
||||
$text,
|
||||
$shortcode
|
||||
$this->queue,
|
||||
$content
|
||||
);
|
||||
}, $shortcodes);
|
||||
return $processed_shortcodes;
|
||||
}
|
||||
|
||||
function replace($text) {
|
||||
$shortcodes = $this->extract($text);
|
||||
$processed_shortcodes = $this->process($shortcodes, $text);
|
||||
function replace($content, $categories = false) {
|
||||
$shortcodes = $this->extract($content, $categories);
|
||||
$processed_shortcodes = $this->process($shortcodes, $content);
|
||||
$shortcodes = array_intersect_key($shortcodes, $processed_shortcodes);
|
||||
return str_replace($shortcodes, $processed_shortcodes, $text);
|
||||
return str_replace($shortcodes, $processed_shortcodes, $content);
|
||||
}
|
||||
}
|
76
lib/Newsletter/Viewer/ViewInBrowser.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Viewer;
|
||||
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\SendingQueue;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Newsletter\Links\Links;
|
||||
use MailPoet\Newsletter\Renderer\Renderer;
|
||||
use MailPoet\Newsletter\Shortcodes\Shortcodes;
|
||||
|
||||
class ViewInBrowser {
|
||||
public $data;
|
||||
|
||||
function __construct($data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
function view($data = false) {
|
||||
$data = ($data) ? $data : $this->data;
|
||||
$newsletter = ($data['newsletter'] !== false) ?
|
||||
Newsletter::findOne($data['newsletter']) :
|
||||
false;
|
||||
if(!$newsletter) $this->abort();
|
||||
$subscriber = ($data['subscriber'] !== false) ?
|
||||
$this->verifySubscriber($data['subscriber'], $data['subscriber_token']) :
|
||||
false;
|
||||
$queue = ($data['queue'] !== false) ?
|
||||
SendingQueue::findOne($data['queue']) :
|
||||
false;
|
||||
$rendered_newsletter =
|
||||
$this->getAndRenderNewsletter($newsletter, $subscriber, $queue);
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
echo $rendered_newsletter;
|
||||
exit;
|
||||
}
|
||||
|
||||
function verifySubscriber($subscriber_id, $subscriber_token) {
|
||||
$subscriber = Subscriber::findOne($subscriber_id);
|
||||
if(!$subscriber ||
|
||||
!Subscriber::verifyToken($subscriber->email, $subscriber_token)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return $subscriber;
|
||||
}
|
||||
|
||||
function getAndRenderNewsletter($newsletter, $subscriber, $queue) {
|
||||
if($queue) {
|
||||
$newsletter_body = json_decode($queue->newsletter_rendered_body, true);
|
||||
} else {
|
||||
$renderer = new Renderer($newsletter->asArray());
|
||||
$newsletter_body = $renderer->render();
|
||||
}
|
||||
$shortcodes = new Shortcodes(
|
||||
$newsletter,
|
||||
$subscriber,
|
||||
$queue
|
||||
);
|
||||
$rendered_newsletter = $shortcodes->replace($newsletter_body['html']);
|
||||
if($queue && (boolean) Setting::getValue('tracking.enabled')) {
|
||||
$rendered_newsletter = Links::replaceSubscriberData(
|
||||
$newsletter->id,
|
||||
$subscriber->id,
|
||||
$queue->id,
|
||||
$rendered_newsletter
|
||||
);
|
||||
}
|
||||
return $rendered_newsletter;
|
||||
}
|
||||
|
||||
private function abort() {
|
||||
header('HTTP/1.0 404 Not Found');
|
||||
exit;
|
||||
}
|
||||
}
|
@ -1,24 +1,23 @@
|
||||
<?php
|
||||
namespace MailPoet\Router;
|
||||
|
||||
use MailPoet\Config\Shortcodes;
|
||||
use MailPoet\Listing;
|
||||
use MailPoet\Mailer\API\MailPoet;
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Models\NewsletterTemplate;
|
||||
use MailPoet\Models\NewsletterSegment;
|
||||
use MailPoet\Models\NewsletterOptionField;
|
||||
use MailPoet\Models\NewsletterOption;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Newsletter\Renderer\Renderer;
|
||||
use MailPoet\Models\SendingQueue;
|
||||
use MailPoet\Newsletter\Scheduler\Scheduler;
|
||||
use MailPoet\Newsletter\Shortcodes\Categories\Link;
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
require_once(ABSPATH . 'wp-includes/pluggable.php');
|
||||
|
||||
class Newsletters {
|
||||
function __construct() {
|
||||
}
|
||||
@ -134,19 +133,36 @@ class Newsletters {
|
||||
return false;
|
||||
}
|
||||
|
||||
function render($data = array()) {
|
||||
function showPreview($data = array()) {
|
||||
if(!isset($data['body'])) {
|
||||
return false;
|
||||
return array(
|
||||
'result' => false,
|
||||
'errors' => array(__('Newsletter data is missing.'))
|
||||
);
|
||||
}
|
||||
$renderer = new Renderer($data);
|
||||
$rendered_newsletter = $renderer->render();
|
||||
$shortcodes = new \MailPoet\Newsletter\Shortcodes\Shortcodes($data);
|
||||
$rendered_newsletter = $shortcodes->replace($rendered_newsletter['html']);
|
||||
return array('rendered_body' => $rendered_newsletter);
|
||||
$newsletter_id = (isset($data['id'])) ? (int)$data['id'] : 0;
|
||||
$newsletter = Newsletter::findOne($newsletter_id);
|
||||
if(!$newsletter) {
|
||||
return array(
|
||||
'result' => false,
|
||||
'errors' => array(__('Newsletter could not be read.'))
|
||||
);
|
||||
}
|
||||
$newsletter->body = $data['body'];
|
||||
$newsletter->save();
|
||||
$wp_user =wp_get_current_user();
|
||||
$subscriber = Subscriber::where('email', $wp_user->data->user_email)
|
||||
->findOne();
|
||||
$subscriber = ($subscriber) ? $subscriber->asArray() : $subscriber;
|
||||
$preview_url = Link::getViewInBrowserUrl($data, $subscriber);
|
||||
return array(
|
||||
'result' => true,
|
||||
'data' => array('url' => $preview_url)
|
||||
);
|
||||
}
|
||||
|
||||
function sendPreview($data = array()) {
|
||||
$id = (isset($data['id'])) ? (int) $data['id'] : 0;
|
||||
$id = (isset($data['id'])) ? (int)$data['id'] : 0;
|
||||
$newsletter = Newsletter::findOne($id);
|
||||
|
||||
if($newsletter === false) {
|
||||
@ -268,7 +284,7 @@ class Newsletters {
|
||||
isset($data['type']) &&
|
||||
$data['type'] === 'notification'
|
||||
) {
|
||||
Scheduler::postNotification($newsletter->id);
|
||||
Scheduler::processPostNotificationSchedule($newsletter->id);
|
||||
}
|
||||
return array(
|
||||
'result' => true,
|
||||
|