Compare commits
200 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 | |||
fa9d32c230 | |||
1b3ceca7b2 | |||
9be326b45d | |||
2d2e1298c4 | |||
5dc3a4386e | |||
b617dde266 | |||
962e91f9dc | |||
4047b41a7f | |||
59199140bf | |||
1079c0beae | |||
1694baab7d | |||
335ac9c778 | |||
65efd234f9 | |||
f726d943db | |||
2d1e950097 | |||
ca2f16970b | |||
4a123f8fe9 | |||
7b224328e1 | |||
699dfa19f0 | |||
06d56fe19d | |||
1b7ac62b5c | |||
ae358ce13e | |||
18f35b5e91 | |||
67ca305b7f | |||
cb0908fc70 | |||
42df59076d | |||
ff8be3bdc6 | |||
f7b6dcf409 | |||
940f3848dd | |||
e847ad2df2 | |||
2459a103fd | |||
0dd3f2178e | |||
de873eca71 | |||
ef461da77f | |||
caf6dcddfa | |||
9684c88651 | |||
256bca8ed9 | |||
8d56e8582f | |||
56959f2f49 | |||
bbc7de6898 | |||
0df246da15 | |||
757e18355d | |||
4b29b04bd1 | |||
6cb94bc413 | |||
f624c891ab | |||
b83abf0ac5 | |||
ef1b0036e5 | |||
5efa9f65c6 | |||
a8e3dd424e | |||
26628ba156 | |||
78cabde9e1 | |||
1ec0372c2d | |||
31e2d5e771 | |||
d2dbf86a9c | |||
1c39d39078 | |||
6d0795abba | |||
5f45c6cc74 | |||
d4d806e247 | |||
758a545eb6 | |||
578088d2e5 | |||
e17dba2b07 | |||
7e5cf533f0 | |||
f7c656aed5 | |||
2e42305710 | |||
b3e310652e | |||
6737158130 | |||
906558a772 | |||
d559483c7b | |||
bec3e02285 | |||
9d6d72dd8c | |||
3b76f838d1 | |||
909ad86e33 | |||
dd8f58e35e | |||
4b2061fcfa | |||
8e6ca502b3 | |||
ef85834db5 | |||
2bdcd0eb42 | |||
3b90be4122 | |||
375e70d84b | |||
42d586610e | |||
e4a5438512 | |||
11b22fa63a | |||
45b933d635 | |||
15cf087d40 | |||
ed09c3e5d4 | |||
285a556f21 | |||
dc1ef2af47 | |||
6f17f0d2d1 | |||
91076580ef | |||
d4abaa7150 | |||
d2bc0fd24a | |||
1f26079b5f | |||
87a1211a55 | |||
0ca0a7d029 |
@ -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
|
||||
|
@ -1,4 +1,5 @@
|
||||
$block-hover-highlight-color = $primary-active-color
|
||||
$block-text-line-height = $text-line-height
|
||||
|
||||
.mailpoet_block
|
||||
box-sizing: border-box
|
||||
@ -30,8 +31,34 @@ $block-hover-highlight-color = $primary-active-color
|
||||
|
||||
.mailpoet_content
|
||||
position: relative
|
||||
line-height: 1.61803398875
|
||||
line-height: $block-text-line-height
|
||||
|
||||
p, h1, h2, h3, h4, h5, h6
|
||||
line-height: 1.61803398875
|
||||
line-height: $block-text-line-height
|
||||
padding: 0
|
||||
margin: 0
|
||||
font-style: normal
|
||||
font-weight: normal
|
||||
|
||||
h1, h2, h3, h4, h5, h6
|
||||
margin-bottom: 0.3em
|
||||
|
||||
p
|
||||
margin-top: 0
|
||||
margin-bottom: 0
|
||||
font-weight: normal
|
||||
|
||||
ul
|
||||
padding: 0
|
||||
margin-top: 10px
|
||||
margin-bottom: 10px
|
||||
|
||||
li
|
||||
margin-top: 0
|
||||
font-weight: normal
|
||||
margin-bottom: 10px
|
||||
|
||||
blockquote
|
||||
margin: 0 0 $block-text-line-height
|
||||
padding-left: 10px
|
||||
border-left: 2px #565656 solid
|
||||
|
@ -1,9 +1,4 @@
|
||||
$button-vertical-padding = 13px
|
||||
|
||||
.mailpoet_button_block
|
||||
padding-top: $button-vertical-padding
|
||||
padding-bottom: $button-vertical-padding
|
||||
|
||||
overflow: hidden
|
||||
|
||||
.mailpoet_editor_button
|
||||
|
@ -6,5 +6,5 @@
|
||||
.mailpoet_content
|
||||
padding: 10px 20px
|
||||
|
||||
p
|
||||
margin: 0
|
||||
& > *:last-child
|
||||
margin-bottom: 0
|
||||
|
@ -6,5 +6,5 @@
|
||||
.mailpoet_content
|
||||
padding: 10px 20px
|
||||
|
||||
p
|
||||
margin: 0
|
||||
& > *:last-child
|
||||
margin-bottom: 0
|
||||
|
@ -1,4 +1,4 @@
|
||||
$social-block-vertical-padding = 11px
|
||||
$social-block-vertical-padding = 0px
|
||||
|
||||
$social-icon-width = 32px
|
||||
$active-social-icon-set-border-color = #adadad
|
||||
|
@ -9,16 +9,5 @@
|
||||
padding-left: 20px
|
||||
padding-right: 20px
|
||||
|
||||
h1, h2, h3, h4, h5, h6
|
||||
padding: 0
|
||||
margin: 0
|
||||
font-weight: normal
|
||||
|
||||
p
|
||||
margin-top: 0
|
||||
font-weight: normal
|
||||
|
||||
blockquote
|
||||
margin: 1em
|
||||
padding-left: 1em
|
||||
border-left: 2px #565656 solid
|
||||
& > *:last-child
|
||||
margin-bottom: 0
|
||||
|
@ -24,3 +24,5 @@ $error-text-color = #d54e21
|
||||
|
||||
// Dimensions
|
||||
$newsletter-width = 660px
|
||||
|
||||
$text-line-height = 1.6em
|
||||
|
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,11 +40,18 @@ define('date',
|
||||
return this;
|
||||
},
|
||||
format: function(date, options) {
|
||||
options = options || {};
|
||||
this.init(options);
|
||||
return Moment.utc(date)
|
||||
.local()
|
||||
|
||||
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, {
|
||||
format: 'F, j Y'
|
||||
@ -115,6 +122,8 @@ define('date',
|
||||
}
|
||||
};
|
||||
|
||||
if (!format || format.length <= 0) return format;
|
||||
|
||||
const replacements = format_mappings['date'];
|
||||
|
||||
let outputFormat = '';
|
||||
@ -133,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;
|
@ -13,14 +13,22 @@ function(
|
||||
getInitialState: function() {
|
||||
return {
|
||||
items: [],
|
||||
initialized: false
|
||||
select2: false
|
||||
};
|
||||
},
|
||||
componentWillMount: function() {
|
||||
this.loadCachedItems();
|
||||
},
|
||||
allowMultipleValues: function() {
|
||||
return (this.props.field.multiple === true);
|
||||
},
|
||||
isSelect2Initialized: function() {
|
||||
return (this.state.select2 === true);
|
||||
},
|
||||
componentDidMount: function() {
|
||||
this.setupSelect2();
|
||||
if(this.allowMultipleValues()) {
|
||||
this.setupSelect2();
|
||||
}
|
||||
},
|
||||
componentDidUpdate: function(prevProps, prevState) {
|
||||
if(
|
||||
@ -33,14 +41,17 @@ function(
|
||||
}
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
jQuery('#'+this.refs.select.id).select2('destroy');
|
||||
if(this.allowMultipleValues()) {
|
||||
this.destroySelect2();
|
||||
}
|
||||
},
|
||||
destroySelect2: function() {
|
||||
if(this.isSelect2Initialized()) {
|
||||
jQuery('#'+this.refs.select.id).select2('destroy');
|
||||
}
|
||||
},
|
||||
setupSelect2: function() {
|
||||
if(
|
||||
!this.props.field.multiple
|
||||
|| this.state.initialized === true
|
||||
|| this.refs.select === undefined
|
||||
) {
|
||||
if(this.isSelect2Initialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -72,7 +83,7 @@ function(
|
||||
|
||||
select2.on('change', this.handleChange);
|
||||
|
||||
this.setState({ initialized: true });
|
||||
this.setState({ select2: true });
|
||||
},
|
||||
getSelectedValues: function() {
|
||||
if(this.props.field['selected'] !== undefined) {
|
||||
|
@ -1,33 +1,35 @@
|
||||
define([
|
||||
'react'
|
||||
],
|
||||
function(
|
||||
React
|
||||
) {
|
||||
var FormFieldText = React.createClass({
|
||||
render: function() {
|
||||
var value = this.props.item[this.props.field.name];
|
||||
if(!value) { value = null; }
|
||||
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 }
|
||||
defaultValue={ this.props.field.defaultValue }
|
||||
onChange={ this.props.onValueChange }
|
||||
{...this.props.field.validation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
import React from 'react'
|
||||
|
||||
return FormFieldText;
|
||||
const FormFieldText = React.createClass({
|
||||
render() {
|
||||
let value = this.props.item[this.props.field.name];
|
||||
if (value === undefined) {
|
||||
value = this.props.field.defaultValue || '';
|
||||
}
|
||||
|
||||
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;
|
@ -13,10 +13,11 @@ define(
|
||||
Router,
|
||||
FormField
|
||||
) {
|
||||
|
||||
var Form = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired
|
||||
},
|
||||
getDefaultProps: function() {
|
||||
return {
|
||||
params: {},
|
||||
@ -36,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()
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -68,7 +73,7 @@ define(
|
||||
loading: false,
|
||||
item: {}
|
||||
}, function() {
|
||||
this.history.pushState(null, '/new');
|
||||
this.context.router.push('/new');
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.setState({
|
||||
@ -101,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;
|
||||
@ -118,7 +122,7 @@ define(
|
||||
if(this.props.onSuccess !== undefined) {
|
||||
this.props.onSuccess();
|
||||
} else {
|
||||
this.history.pushState(null, '/')
|
||||
this.context.router.push('/');
|
||||
}
|
||||
|
||||
if(this.props.params.id !== undefined) {
|
||||
@ -166,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
|
||||
@ -190,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>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -1,10 +1,10 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Router, Route, IndexRoute } from 'react-router'
|
||||
import { Router, Route, IndexRoute, Link, useRouterHistory } from 'react-router'
|
||||
import { createHashHistory } from 'history'
|
||||
import FormList from 'forms/list.jsx'
|
||||
import createHashHistory from 'history/lib/createHashHistory'
|
||||
|
||||
let history = createHashHistory({ queryKey: false })
|
||||
const history = useRouterHistory(createHashHistory)({ queryKey: false });
|
||||
|
||||
const App = React.createClass({
|
||||
render() {
|
||||
@ -12,7 +12,7 @@ const App = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
let container = document.getElementById('forms_container');
|
||||
const container = document.getElementById('forms_container');
|
||||
|
||||
if(container) {
|
||||
ReactDOM.render((
|
||||
|
@ -71,6 +71,14 @@ const messages = {
|
||||
}
|
||||
};
|
||||
|
||||
const bulk_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('trash'),
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
|
||||
const item_actions = [
|
||||
{
|
||||
name: 'edit',
|
||||
@ -102,14 +110,6 @@ const item_actions = [
|
||||
}
|
||||
];
|
||||
|
||||
const bulk_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('trash'),
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
|
||||
const FormList = React.createClass({
|
||||
createForm() {
|
||||
MailPoet.Ajax.post({
|
||||
@ -138,7 +138,10 @@ const FormList = React.createClass({
|
||||
<div>
|
||||
<td className={ row_classes }>
|
||||
<strong>
|
||||
<a>{ form.name }</a>
|
||||
<a
|
||||
className="row-title"
|
||||
href={ `admin.php?page=mailpoet-form-editor&id=${form.id}` }
|
||||
>{ form.name }</a>
|
||||
</strong>
|
||||
{ actions }
|
||||
</td>
|
||||
@ -146,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>
|
||||
);
|
||||
@ -154,20 +157,20 @@ 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 }
|
||||
|
@ -140,5 +140,20 @@ define('handlebars_helpers', ['handlebars'], function(Handlebars) {
|
||||
return parseInt(string, 10);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('fontWithFallback', function(font) {
|
||||
switch(font) {
|
||||
case 'Arial': return new Handlebars.SafeString("Arial, 'Helvetica Neue', Helvetica, sans-serif");
|
||||
case 'Comic Sans MS': return new Handlebars.SafeString("'Comic Sans MS', 'Marker Felt-Thin', Arial, sans-serif");
|
||||
case 'Courier New': return new Handlebars.SafeString("'Courier New', Courier, 'Lucida Sans Typewriter', 'Lucida Typewriter', monospace");
|
||||
case 'Georgia': return new Handlebars.SafeString("Georgia, Times, 'Times New Roman', serif");
|
||||
case 'Lucida': return new Handlebars.SafeString("'Lucida Sans Unicode', 'Lucida Grande', sans-serif");
|
||||
case 'Tahoma': return new Handlebars.SafeString("Tahoma, Verdana, Segoe, sans-serif");
|
||||
case 'Times New Roman': return new Handlebars.SafeString("'Times New Roman', Times, Baskerville, Georgia, serif");
|
||||
case 'Trebuchet MS': return new Handlebars.SafeString("'Trebuchet MS', 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans', Tahoma, sans-serif");
|
||||
case 'Verdana': return new Handlebars.SafeString("Verdana, Geneva, sans-serif");
|
||||
default: return font;
|
||||
}
|
||||
});
|
||||
|
||||
window.Handlebars = Handlebars;
|
||||
});
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -295,9 +295,9 @@ define(
|
||||
});
|
||||
|
||||
var Listing = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {
|
||||
loading: false,
|
||||
@ -359,9 +359,12 @@ define(
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// default overrides
|
||||
if(this.props.limit !== undefined) {
|
||||
state.limit = Math.abs(~~this.props.limit);
|
||||
}
|
||||
|
||||
this.setState(state, function() {
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
@ -398,7 +401,7 @@ define(
|
||||
|
||||
if(this.props.location) {
|
||||
if(this.props.location.pathname !== params) {
|
||||
this.history.pushState(null, `${params}`)
|
||||
this.context.router.push(`${params}`);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -730,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 }
|
||||
@ -753,8 +757,8 @@ define(
|
||||
onSort={ this.handleSort }
|
||||
onSelectItems={ this.handleSelectItems }
|
||||
selection={ this.state.selection }
|
||||
sort_by={ this.state.sort_by }
|
||||
sort_order={ this.state.sort_order }
|
||||
sort_by={ sort_by }
|
||||
sort_order={ sort_order }
|
||||
columns={ this.props.columns }
|
||||
is_selectable={ bulk_actions.length > 0 } />
|
||||
</thead>
|
||||
@ -783,8 +787,8 @@ define(
|
||||
onSort={ this.handleSort }
|
||||
onSelectItems={ this.handleSelectItems }
|
||||
selection={ this.state.selection }
|
||||
sort_by={ this.state.sort_by }
|
||||
sort_order={ this.state.sort_order }
|
||||
sort_by={ sort_by }
|
||||
sort_order={ sort_order }
|
||||
columns={ this.props.columns }
|
||||
is_selectable={ bulk_actions.length > 0 } />
|
||||
</tfoot>
|
||||
@ -792,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() {
|
||||
|
@ -14,9 +14,6 @@ define(
|
||||
var Link = Router.Link;
|
||||
|
||||
var Breadcrumb = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
getInitialState: function() {
|
||||
return {
|
||||
step: null,
|
||||
|
@ -31,6 +31,10 @@ define(
|
||||
name: 'segments',
|
||||
label: MailPoet.I18n.t('lists')
|
||||
},
|
||||
{
|
||||
name: 'statistics',
|
||||
label: MailPoet.I18n.t('statistics')
|
||||
},
|
||||
{
|
||||
name: 'created_at',
|
||||
label: MailPoet.I18n.t('createdOn'),
|
||||
@ -142,6 +146,11 @@ define(
|
||||
<span>{MailPoet.I18n.t('notSentYet')}</span>
|
||||
);
|
||||
} else {
|
||||
if (item.queue.status === 'scheduled') {
|
||||
return (
|
||||
<span>{MailPoet.I18n.t('scheduledFor')} { MailPoet.Date.format(item.queue.scheduled_at) } </span>
|
||||
)
|
||||
}
|
||||
var progressClasses = classNames(
|
||||
'mailpoet_progress',
|
||||
{ 'mailpoet_progress_complete': item.queue.status === 'completed'}
|
||||
@ -205,6 +214,31 @@ define(
|
||||
);
|
||||
}
|
||||
},
|
||||
renderStatistics: function(item) {
|
||||
if(!item.statistics || !item.queue || item.queue.count_processed == 0 || item.queue.status === 'scheduled') {
|
||||
return (
|
||||
<span>
|
||||
{MailPoet.I18n.t('notSentYet')}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
var percentage_clicked = Math.round(
|
||||
(item.statistics.clicked * 100) / (item.queue.count_processed)
|
||||
);
|
||||
var percentage_opened = Math.round(
|
||||
(item.statistics.opened * 100) / (item.queue.count_processed)
|
||||
);
|
||||
var percentage_unsubscribed = Math.round(
|
||||
(item.statistics.unsubscribed * 100) / (item.queue.count_processed)
|
||||
);
|
||||
|
||||
return (
|
||||
<span>
|
||||
{ percentage_opened }%, { percentage_clicked }%, { percentage_unsubscribed }%
|
||||
</span>
|
||||
);
|
||||
},
|
||||
renderItem: function(newsletter, actions) {
|
||||
var rowClasses = classNames(
|
||||
'manage-column',
|
||||
@ -216,6 +250,13 @@ define(
|
||||
return segment.name
|
||||
}).join(', ');
|
||||
|
||||
var statistics_column =
|
||||
(!mailpoet_settings.tracking || !mailpoet_settings.tracking.enabled) ?
|
||||
false :
|
||||
<td className="column {statistics_class}" data-colname={ MailPoet.I18n.t('statistics') }>
|
||||
{ this.renderStatistics(newsletter) }
|
||||
</td>;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<td className={ rowClasses }>
|
||||
@ -224,29 +265,34 @@ define(
|
||||
</strong>
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column" data-colname="Lists">
|
||||
<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>
|
||||
<td className="column-date" data-colname="Subscribed on">
|
||||
<abbr>{ MailPoet.Date.full(newsletter.created_at) }</abbr>
|
||||
{ statistics_column }
|
||||
<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>
|
||||
);
|
||||
},
|
||||
render: function() {
|
||||
if (!mailpoet_settings.tracking || !mailpoet_settings.tracking.enabled) {
|
||||
columns = _.without(columns, _.findWhere(columns, {name: 'statistics'}));
|
||||
}
|
||||
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}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Router, Route, IndexRoute, Link } from 'react-router'
|
||||
import { Router, Route, IndexRoute, Link, useRouterHistory } from 'react-router'
|
||||
import { createHashHistory } from 'history'
|
||||
import NewsletterList from 'newsletters/list.jsx'
|
||||
import NewsletterTypes from 'newsletters/types.jsx'
|
||||
import NewsletterTemplates from 'newsletters/templates.jsx'
|
||||
@ -8,9 +9,8 @@ import NewsletterSend from 'newsletters/send.jsx'
|
||||
import NewsletterStandard from 'newsletters/types/standard.jsx'
|
||||
import NewsletterWelcome from 'newsletters/types/welcome/welcome.jsx'
|
||||
import NewsletterNotification from 'newsletters/types/notification/notification.jsx'
|
||||
import createHashHistory from 'history/lib/createHashHistory'
|
||||
|
||||
let history = createHashHistory({ queryKey: false })
|
||||
const history = useRouterHistory(createHashHistory)({ queryKey: false });
|
||||
|
||||
const App = React.createClass({
|
||||
render() {
|
||||
@ -18,7 +18,7 @@ const App = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
let container = document.getElementById('newsletters_container');
|
||||
const container = document.getElementById('newsletters_container');
|
||||
|
||||
if(container) {
|
||||
ReactDOM.render((
|
||||
|
@ -23,9 +23,9 @@ define(
|
||||
) {
|
||||
|
||||
var NewsletterSend = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {
|
||||
fields: [],
|
||||
@ -68,7 +68,7 @@ define(
|
||||
loading: false,
|
||||
item: {},
|
||||
}, function() {
|
||||
this.history.pushState(null, '/new');
|
||||
this.context.router.push('/new');
|
||||
}.bind(this));
|
||||
} else {
|
||||
this.setState({
|
||||
@ -106,10 +106,8 @@ define(
|
||||
}).done((response) => {
|
||||
this.setState({ loading: false });
|
||||
if(response.result === true) {
|
||||
this.history.pushState(null, '/');
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('newsletterIsBeingSent')
|
||||
);
|
||||
this.context.router.push('/');
|
||||
MailPoet.Notice.success(response.data.message);
|
||||
} else {
|
||||
if(response.errors) {
|
||||
MailPoet.Notice.error(response.errors);
|
||||
@ -135,7 +133,7 @@ define(
|
||||
this.setState({ loading: false });
|
||||
|
||||
if(response.result === true) {
|
||||
this.history.pushState(null, '/');
|
||||
this.context.router.push('/');
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('newsletterUpdated')
|
||||
);
|
||||
|
@ -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;
|
||||
},
|
||||
@ -60,7 +60,6 @@ define(
|
||||
name: 'sender_name',
|
||||
type: 'text',
|
||||
placeholder: MailPoet.I18n.t('senderNamePlaceholder'),
|
||||
defaultValue: (settings.sender !== undefined) ? settings.sender.name : '',
|
||||
validation: {
|
||||
'data-parsley-required': true
|
||||
}
|
||||
@ -69,7 +68,6 @@ define(
|
||||
name: 'sender_address',
|
||||
type: 'text',
|
||||
placeholder: MailPoet.I18n.t('senderAddressPlaceholder'),
|
||||
defaultValue: (settings.sender !== undefined) ? settings.sender.address : '',
|
||||
validation: {
|
||||
'data-parsley-required': true,
|
||||
'data-parsley-type': 'email'
|
||||
@ -86,15 +84,13 @@ define(
|
||||
{
|
||||
name: 'reply_to_name',
|
||||
type: 'text',
|
||||
placeholder: MailPoet.I18n.t('replyToNamePlaceholder'),
|
||||
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.name : '',
|
||||
placeholder: MailPoet.I18n.t('replyToNamePlaceholder')
|
||||
},
|
||||
{
|
||||
name: 'reply_to_address',
|
||||
type: 'text',
|
||||
placeholder: MailPoet.I18n.t('replyToAddressPlaceholder'),
|
||||
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.address : ''
|
||||
},
|
||||
placeholder: MailPoet.I18n.t('replyToAddressPlaceholder')
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
@ -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;
|
||||
},
|
||||
@ -52,7 +360,6 @@ define(
|
||||
name: 'sender_name',
|
||||
type: 'text',
|
||||
placeholder: MailPoet.I18n.t('senderNamePlaceholder'),
|
||||
defaultValue: (settings.sender !== undefined) ? settings.sender.name : '',
|
||||
validation: {
|
||||
'data-parsley-required': true
|
||||
}
|
||||
@ -61,7 +368,6 @@ define(
|
||||
name: 'sender_address',
|
||||
type: 'text',
|
||||
placeholder: MailPoet.I18n.t('senderAddressPlaceholder'),
|
||||
defaultValue: (settings.sender !== undefined) ? settings.sender.address : '',
|
||||
validation: {
|
||||
'data-parsley-required': true,
|
||||
'data-parsley-type': 'email'
|
||||
@ -78,16 +384,20 @@ define(
|
||||
{
|
||||
name: 'reply_to_name',
|
||||
type: 'text',
|
||||
placeholder: MailPoet.I18n.t('replyToNamePlaceholder'),
|
||||
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.name : '',
|
||||
placeholder: MailPoet.I18n.t('replyToNamePlaceholder')
|
||||
},
|
||||
{
|
||||
name: 'reply_to_address',
|
||||
type: 'text',
|
||||
placeholder: MailPoet.I18n.t('replyToAddressPlaceholder'),
|
||||
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.address : ''
|
||||
},
|
||||
placeholder: MailPoet.I18n.t('replyToAddressPlaceholder')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'options',
|
||||
label: MailPoet.I18n.t('scheduleIt'),
|
||||
type: 'reactComponent',
|
||||
component: StandardScheduling,
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -36,7 +36,6 @@ define(
|
||||
name: 'sender_name',
|
||||
type: 'text',
|
||||
placeholder: MailPoet.I18n.t('senderNamePlaceholder'),
|
||||
defaultValue: (settings.sender !== undefined) ? settings.sender.name : '',
|
||||
validation: {
|
||||
'data-parsley-required': true
|
||||
}
|
||||
@ -45,7 +44,6 @@ define(
|
||||
name: 'sender_address',
|
||||
type: 'text',
|
||||
placeholder: MailPoet.I18n.t('senderAddressPlaceholder'),
|
||||
defaultValue: (settings.sender !== undefined) ? settings.sender.address : '',
|
||||
validation: {
|
||||
'data-parsley-required': true,
|
||||
'data-parsley-type': 'email'
|
||||
@ -62,15 +60,13 @@ define(
|
||||
{
|
||||
name: 'reply_to_name',
|
||||
type: 'text',
|
||||
placeholder: MailPoet.I18n.t('replyToNamePlaceholder'),
|
||||
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.name : '',
|
||||
placeholder: MailPoet.I18n.t('replyToNamePlaceholder')
|
||||
},
|
||||
{
|
||||
name: 'reply_to_address',
|
||||
type: 'text',
|
||||
placeholder: MailPoet.I18n.t('replyToAddressPlaceholder'),
|
||||
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.address : ''
|
||||
},
|
||||
placeholder: MailPoet.I18n.t('replyToAddressPlaceholder')
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
@ -80,9 +80,6 @@ define(
|
||||
});
|
||||
|
||||
var NewsletterTemplates = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
getInitialState: function() {
|
||||
return {
|
||||
loading: false,
|
||||
|
@ -12,12 +12,12 @@ define(
|
||||
Breadcrumb
|
||||
) {
|
||||
var NewsletterTypes = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired
|
||||
},
|
||||
setupNewsletter: function(type) {
|
||||
if(type !== undefined) {
|
||||
this.history.pushState(null, `/new/${type}`);
|
||||
this.context.router.push(`/new/${type}`);
|
||||
}
|
||||
},
|
||||
createNewsletter: function(type) {
|
||||
@ -30,7 +30,7 @@ define(
|
||||
}
|
||||
}).done(function(response) {
|
||||
if(response.result && response.newsletter.id) {
|
||||
this.history.pushState(null, `/template/${response.newsletter.id}`);
|
||||
this.context.router.push(`/template/${response.newsletter.id}`);
|
||||
} else {
|
||||
if(response.errors.length > 0) {
|
||||
response.errors.map(function(error) {
|
||||
|
@ -24,9 +24,9 @@ define(
|
||||
};
|
||||
|
||||
var NewsletterNotification = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {
|
||||
options: {
|
||||
@ -64,7 +64,7 @@ define(
|
||||
}.bind(this));
|
||||
},
|
||||
showTemplateSelection: function(newsletterId) {
|
||||
this.history.pushState(null, `/template/${newsletterId}`);
|
||||
this.context.router.push(`/template/${newsletterId}`);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
|
@ -13,11 +13,11 @@ define(
|
||||
) {
|
||||
|
||||
var NewsletterStandard = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired
|
||||
},
|
||||
showTemplateSelection: function(newsletterId) {
|
||||
this.history.pushState(null, `/template/${newsletterId}`);
|
||||
this.context.router.push(`/template/${newsletterId}`);
|
||||
},
|
||||
componentDidMount: function() {
|
||||
// No options for this type, create a newsletter upon mounting
|
||||
|
@ -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];
|
||||
}
|
||||
@ -65,6 +65,9 @@ define(
|
||||
};
|
||||
|
||||
var WelcomeScheduling = React.createClass({
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired
|
||||
},
|
||||
_getCurrentValue: function() {
|
||||
return this.props.item[this.props.field.name] || {};
|
||||
},
|
||||
@ -131,7 +134,7 @@ define(
|
||||
}.bind(this));
|
||||
},
|
||||
showTemplateSelection: function(newsletterId) {
|
||||
this.history.pushState(null, `/template/${newsletterId}`);
|
||||
this.context.router.push(`/template/${newsletterId}`);
|
||||
},
|
||||
render: function() {
|
||||
var value = this._getCurrentValue(),
|
||||
|
@ -23,15 +23,22 @@ define(
|
||||
component: Scheduling,
|
||||
};
|
||||
|
||||
var availableSegments = window.mailpoet_segments || {},
|
||||
defaultSegment = 1;
|
||||
|
||||
if (_.size(availableSegments) > 0) {
|
||||
defaultSegment = _.first(availableSegments).id;
|
||||
}
|
||||
|
||||
var NewsletterWelcome = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired
|
||||
},
|
||||
getInitialState: function() {
|
||||
return {
|
||||
options: {
|
||||
event: 'segment',
|
||||
segment: 1,
|
||||
segment: defaultSegment,
|
||||
role: 'subscriber',
|
||||
afterTimeNumber: 1,
|
||||
afterTimeType: 'immediate',
|
||||
@ -64,7 +71,7 @@ define(
|
||||
}.bind(this));
|
||||
},
|
||||
showTemplateSelection: function(newsletterId) {
|
||||
this.history.pushState(null, `/template/${newsletterId}`);
|
||||
this.context.router.push(`/template/${newsletterId}`);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
|
@ -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
|
||||
},
|
||||
|
@ -22,7 +22,7 @@ function(
|
||||
|
||||
form.parsley().on('form:validated', function(parsley) {
|
||||
// clear messages
|
||||
form.find('.mailpoet_message').html('');
|
||||
form.find('.mailpoet_message > p').hide();
|
||||
|
||||
// resize iframe
|
||||
if(window.frameElement !== null) {
|
||||
@ -45,27 +45,18 @@ function(
|
||||
action: 'subscribe',
|
||||
data: data
|
||||
}).done(function(response) {
|
||||
if(response.result !== true) {
|
||||
// errors
|
||||
$.each(response.errors, function(index, error) {
|
||||
form
|
||||
.find('.mailpoet_message')
|
||||
.append('<p class="mailpoet_validate_error">'+
|
||||
error+
|
||||
'</p>');
|
||||
});
|
||||
if(response.result === false) {
|
||||
form.find('.mailpoet_validate_error').html(
|
||||
response.errors.join('<br />')
|
||||
).show();
|
||||
} else {
|
||||
// successfully subscribed
|
||||
if(response.page !== undefined) {
|
||||
// go to page
|
||||
window.location.href = response.page;
|
||||
} else if(response.message !== undefined) {
|
||||
} else {
|
||||
// display success message
|
||||
form
|
||||
.find('.mailpoet_message')
|
||||
.html('<p class="mailpoet_validate_success">'+
|
||||
response.message+
|
||||
'</p>');
|
||||
form.find('.mailpoet_validate_success').show();
|
||||
}
|
||||
|
||||
// reset form
|
||||
|
@ -21,7 +21,8 @@ define(
|
||||
{
|
||||
name: 'description',
|
||||
label: MailPoet.I18n.t('description'),
|
||||
type: 'textarea'
|
||||
type: 'textarea',
|
||||
tip: MailPoet.I18n.t('segmentDescriptionTip')
|
||||
}
|
||||
];
|
||||
|
||||
@ -34,16 +35,16 @@ define(
|
||||
}
|
||||
};
|
||||
|
||||
var Link = Router.Link;
|
||||
|
||||
const SegmentForm = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
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({
|
||||
@ -159,9 +179,6 @@ const item_actions = [
|
||||
}
|
||||
];
|
||||
|
||||
const bulk_actions = [
|
||||
];
|
||||
|
||||
const SegmentList = React.createClass({
|
||||
renderItem: function(segment, actions) {
|
||||
var rowClasses = classNames(
|
||||
@ -169,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>
|
||||
);
|
||||
@ -198,16 +229,16 @@ 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 }
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Router, Route, IndexRoute, Link } from 'react-router'
|
||||
import { Router, Route, IndexRoute, Link, useRouterHistory } from 'react-router'
|
||||
import { createHashHistory } from 'history'
|
||||
import SegmentList from 'segments/list.jsx'
|
||||
import SegmentForm from 'segments/form.jsx'
|
||||
import createHashHistory from 'history/lib/createHashHistory'
|
||||
|
||||
let history = createHashHistory({ queryKey: false })
|
||||
const history = useRouterHistory(createHashHistory)({ queryKey: false });
|
||||
|
||||
const App = React.createClass({
|
||||
render() {
|
||||
@ -13,7 +13,7 @@ const App = React.createClass({
|
||||
}
|
||||
});
|
||||
|
||||
let container = document.getElementById('segments_container');
|
||||
const container = document.getElementById('segments_container');
|
||||
|
||||
if(container) {
|
||||
ReactDOM.render((
|
||||
|
@ -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,24 +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({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
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 }
|
||||
|
@ -1,11 +1,11 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Router, Route, IndexRoute, Link } from 'react-router'
|
||||
import { Router, Route, IndexRoute, Link, useRouterHistory } from 'react-router'
|
||||
import { createHashHistory } from 'history'
|
||||
import SubscriberList from 'subscribers/list.jsx'
|
||||
import SubscriberForm from 'subscribers/form.jsx'
|
||||
import createHashHistory from 'history/lib/createHashHistory'
|
||||
|
||||
const history = createHashHistory({ queryKey: false })
|
||||
const history = useRouterHistory(createHashHistory)({ queryKey: false });
|
||||
|
||||
const App = React.createClass({
|
||||
render() {
|
||||
|
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": [],
|
||||
|
@ -38,8 +38,11 @@ class Env {
|
||||
self::$assets_path = self::$path . '/assets';
|
||||
self::$assets_url = plugins_url('/assets', $file);
|
||||
$wp_upload_dir = wp_upload_dir();
|
||||
self::$temp_path = $wp_upload_dir['path'];
|
||||
self::$temp_url = $wp_upload_dir['url'];
|
||||
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';
|
||||
self::$plugin_prefix = 'mailpoet_';
|
||||
|
@ -9,14 +9,14 @@ class Hooks {
|
||||
}
|
||||
|
||||
function init() {
|
||||
$this->setupSubscribe();
|
||||
$this->setupWPUsers();
|
||||
$this->setupImageSize();
|
||||
$this->setupListing();
|
||||
$this->setupManageSubscription();
|
||||
$this->setupSubscriptionEvents();
|
||||
$this->setupPostNotifications();
|
||||
}
|
||||
|
||||
function setupSubscribe() {
|
||||
function setupSubscriptionEvents() {
|
||||
$subscribe = Setting::getValue('subscribe', array());
|
||||
// Subscribe in comments
|
||||
if(
|
||||
@ -98,7 +98,7 @@ class Hooks {
|
||||
add_action(
|
||||
'profile_update',
|
||||
'\MailPoet\Segments\WP::synchronizeUser',
|
||||
1,2
|
||||
1, 2
|
||||
);
|
||||
add_action(
|
||||
'delete_user',
|
||||
@ -140,18 +140,6 @@ class Hooks {
|
||||
);
|
||||
}
|
||||
|
||||
function setupManageSubscription() {
|
||||
// handle subscription form submission
|
||||
add_action(
|
||||
'admin_post_mailpoet_subscription_update',
|
||||
'\MailPoet\Subscription\Manage::onSave'
|
||||
);
|
||||
add_action(
|
||||
'admin_post_nopriv_mailpoet_subscription_update',
|
||||
'\MailPoet\Subscription\Manage::onSave'
|
||||
);
|
||||
}
|
||||
|
||||
function setScreenOption($status, $option, $value) {
|
||||
if(preg_match('/^mailpoet_(.*)_per_page$/', $option)) {
|
||||
return $value;
|
||||
@ -159,4 +147,12 @@ class Hooks {
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setupPostNotifications() {
|
||||
add_filter(
|
||||
'publish_post',
|
||||
'\MailPoet\Newsletter\Scheduler\Scheduler::schedulePostNotification',
|
||||
10, 1
|
||||
);
|
||||
}
|
||||
}
|
@ -82,8 +82,12 @@ 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';
|
||||
$statistics_unsubscribes = Env::$db_prefix . 'statistics_unsubscribes';
|
||||
$statistics_forms = Env::$db_prefix . 'statistics_forms';
|
||||
|
||||
define('MP_SETTINGS_TABLE', $settings);
|
||||
define('MP_SEGMENTS_TABLE', $segments);
|
||||
@ -98,9 +102,13 @@ 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);
|
||||
define('MP_STATISTICS_OPENS_TABLE', $statistics_opens);
|
||||
define('MP_STATISTICS_UNSUBSCRIBES_TABLE', $statistics_unsubscribes);
|
||||
define('MP_STATISTICS_FORMS_TABLE', $statistics_forms);
|
||||
}
|
||||
|
||||
function runMigrator() {
|
||||
|
@ -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,9 +23,13 @@ class Migrator {
|
||||
'newsletter_option',
|
||||
'newsletter_segment',
|
||||
'newsletter_links',
|
||||
'newsletter_posts',
|
||||
'forms',
|
||||
'statistics_newsletters',
|
||||
'statistics_clicks',
|
||||
'statistics_opens',
|
||||
'statistics_unsubscribes',
|
||||
'statistics_forms'
|
||||
);
|
||||
}
|
||||
|
||||
@ -56,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)'
|
||||
);
|
||||
@ -98,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,',
|
||||
@ -107,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);
|
||||
@ -121,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)'
|
||||
);
|
||||
@ -140,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)'
|
||||
);
|
||||
@ -153,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)'
|
||||
);
|
||||
@ -165,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);
|
||||
@ -189,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);
|
||||
@ -201,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)'
|
||||
);
|
||||
@ -214,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)'
|
||||
);
|
||||
@ -228,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)'
|
||||
);
|
||||
@ -243,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)',
|
||||
@ -257,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);
|
||||
@ -271,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);
|
||||
@ -285,14 +300,49 @@ 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,',
|
||||
'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,',
|
||||
'PRIMARY KEY (id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
|
||||
function statistics_opens() {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'newsletter_id mediumint(9) NOT NULL,',
|
||||
'subscriber_id mediumint(9) NOT NULL,',
|
||||
'queue_id mediumint(9) NOT NULL,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'PRIMARY KEY (id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
|
||||
function statistics_unsubscribes() {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'newsletter_id mediumint(9) NOT NULL,',
|
||||
'subscriber_id mediumint(9) NOT NULL,',
|
||||
'queue_id mediumint(9) NOT NULL,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'PRIMARY KEY (id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
|
||||
function statistics_forms() {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'form_id mediumint(9) NOT NULL,',
|
||||
'subscriber_id mediumint(9) NOT NULL,',
|
||||
'created_at TIMESTAMP NULL,',
|
||||
'PRIMARY KEY (id),',
|
||||
'UNIQUE KEY form_subscriber (form_id,subscriber_id)'
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
|
||||
private function sqlify($model, $attributes) {
|
||||
$table = $this->prefix . $model;
|
||||
|
||||
|
@ -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,8 +2,10 @@
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Cron\Daemon;
|
||||
use MailPoet\Subscription;
|
||||
use MailPoet\Newsletter\Viewer\ViewInBrowser;
|
||||
use MailPoet\Statistics\Track\Clicks;
|
||||
use MailPoet\Statistics\Track\Opens;
|
||||
use MailPoet\Subscription;
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
@ -24,7 +26,7 @@ class PublicAPI {
|
||||
Helpers::underscoreToCamelCase($_GET['action']) :
|
||||
false;
|
||||
$this->data = isset($_GET['data']) ?
|
||||
$_GET['data'] :
|
||||
unserialize(base64_decode($_GET['data'])) :
|
||||
false;
|
||||
}
|
||||
|
||||
@ -34,30 +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(!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) {
|
||||
@ -73,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,14 +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\Subscriber;
|
||||
use MailPoet\Models\SubscriberSegment;
|
||||
use MailPoet\Newsletter\Renderer\Renderer;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\Newsletter\Scheduler\Scheduler as NewsletterScheduler;
|
||||
|
||||
require_once(ABSPATH . 'wp-includes/pluggable.php');
|
||||
|
||||
@ -17,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);
|
||||
@ -28,17 +28,17 @@ class Scheduler {
|
||||
->whereLte('scheduled_at', Carbon::createFromTimestamp(current_time('timestamp')))
|
||||
->findMany();
|
||||
if(!count($scheduled_queues)) return;
|
||||
foreach($scheduled_queues as $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);
|
||||
}
|
||||
@ -46,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;
|
||||
@ -63,17 +67,50 @@ class Scheduler {
|
||||
}
|
||||
|
||||
function processPostNotificationNewsletter($newsletter, $queue) {
|
||||
$next_run_date = $this->getQueueNextRunDate($newsletter->schedule);
|
||||
$segments = unserialize($newsletter->segments);
|
||||
$subscribers = SubscriberSegment::whereIn('segment_id', $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(
|
||||
@ -83,66 +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(!in_array($newsletter->role, $wp_user['roles'])) {
|
||||
if($newsletter->role !== \MailPoet\Newsletter\Scheduler\Scheduler::WORDPRESS_ALL_ROLES
|
||||
&& !in_array($newsletter->role, $wp_user['roles'])
|
||||
) {
|
||||
$queue->delete();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private function checkIfNewsletterChanged($newsletter) {
|
||||
$last_run_queue = SendingQueue::where('status', 'completed')
|
||||
->where('newsletter_id', $newsletter->id)
|
||||
->orderByDesc('id')
|
||||
->findOne();
|
||||
if(!$last_run_queue) return true;
|
||||
$renderer = new Renderer($newsletter->asArray());
|
||||
$rendered_newsletter = $renderer->render();
|
||||
$new_hash = md5($rendered_newsletter['html']);
|
||||
$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');
|
||||
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();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
@ -4,11 +4,12 @@ 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;
|
||||
use MailPoet\Newsletter\Links\Links;
|
||||
use MailPoet\Newsletter\Renderer\PostProcess\OpenTracking;
|
||||
use MailPoet\Newsletter\Renderer\Renderer;
|
||||
use MailPoet\Newsletter\Shortcodes\Shortcodes;
|
||||
use MailPoet\Util\Helpers;
|
||||
@ -19,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();
|
||||
@ -42,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();
|
||||
@ -50,10 +58,21 @@ 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)) {
|
||||
$queue->subscribers->to_process = $this->recalculateSubscriberCount(
|
||||
Helpers::arrayColumn($subscribers, 'id'),
|
||||
$subscribers_ids,
|
||||
$queue->subscribers->to_process
|
||||
);
|
||||
}
|
||||
if(!count($queue->subscribers->to_process)) {
|
||||
$this->updateQueue($queue);
|
||||
continue;
|
||||
}
|
||||
$queue->subscribers = call_user_func_array(
|
||||
array(
|
||||
$this,
|
||||
@ -74,11 +93,15 @@ class SendingQueue {
|
||||
// check if newsletter has been rendered, in which case return its contents
|
||||
// or render and save for future reuse
|
||||
if($queue->newsletter_rendered_body === null) {
|
||||
// render newsletter
|
||||
$rendered_newsletter = $this->renderNewsletter($newsletter);
|
||||
if((boolean) Setting::getValue('tracking.enabled')) {
|
||||
// extract and replace links
|
||||
$processed_newsletter = $this->processLinks(
|
||||
// insert tracking code
|
||||
add_filter('mailpoet_rendering_post_process', function($template) {
|
||||
return OpenTracking::process($template);
|
||||
});
|
||||
// render newsletter
|
||||
$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
|
||||
@ -87,10 +110,14 @@ class SendingQueue {
|
||||
$this->splitObject($processed_newsletter);
|
||||
}
|
||||
else {
|
||||
$newsletter['body'] = $rendered_newsletter;
|
||||
// render newsletter
|
||||
$newsletter['body'] = $this->renderNewsletter($newsletter);
|
||||
}
|
||||
$this->extractAndSaveNewsletterPosts(
|
||||
$newsletter['id'],
|
||||
$newsletter['body']['html']
|
||||
);
|
||||
$queue->newsletter_rendered_body = json_encode($newsletter['body']);
|
||||
$queue->newsletter_rendered_body_hash = md5($newsletter['body']['text']);
|
||||
$queue->save();
|
||||
} else {
|
||||
$newsletter['body'] = json_decode($queue->newsletter_rendered_body);
|
||||
@ -101,7 +128,10 @@ 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'];
|
||||
}
|
||||
$transformed_subscribers[] =
|
||||
$mailer->transformSubscriber($subscriber);
|
||||
}
|
||||
@ -142,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'];
|
||||
}
|
||||
@ -179,22 +209,26 @@ class SendingQueue {
|
||||
return $renderer->render();
|
||||
}
|
||||
|
||||
function processLinks($text, $newsletter_id, $queue_id) {
|
||||
$links = new Links();
|
||||
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'],
|
||||
@ -203,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,
|
||||
@ -220,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);
|
||||
}
|
||||
@ -292,8 +320,8 @@ class SendingQueue {
|
||||
$queue->count_total =
|
||||
$queue->count_processed + $queue->count_to_process;
|
||||
if(!$queue->count_to_process) {
|
||||
$queue->processed_at = date('Y-m-d H:i:s');
|
||||
$queue->status = 'completed';
|
||||
$queue->processed_at = current_time('mysql');
|
||||
$queue->status = self::STATUS_COMPLETED;
|
||||
}
|
||||
$queue->subscribers = serialize((array) $queue->subscribers);
|
||||
$queue->save();
|
||||
@ -325,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
|
||||
) {
|
||||
@ -343,11 +371,28 @@ class SendingQueue {
|
||||
return;
|
||||
}
|
||||
|
||||
function recalculateSubscriberCount(
|
||||
$found_subscriber, $existing_subscribers, $subscribers_to_process) {
|
||||
$subscibers_to_exclude = array_diff($existing_subscribers, $found_subscriber);
|
||||
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'])
|
||||
|
@ -7,33 +7,12 @@ use \MailPoet\Models\Setting;
|
||||
use \MailPoet\Models\Subscriber;
|
||||
use \MailPoet\Form\Renderer as FormRenderer;
|
||||
use \MailPoet\Form\Util;
|
||||
use \MailPoet\Util\Security;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Widget extends \WP_Widget {
|
||||
function __construct () {
|
||||
// add_action(
|
||||
// 'wp_ajax_mailpoet_form_subscribe',
|
||||
// array($this, 'subscribe')
|
||||
// );
|
||||
// add_action(
|
||||
// 'wp_ajax_nopriv_mailpoet_form_subscribe',
|
||||
// array($this, 'subscribe')
|
||||
// );
|
||||
// add_action(
|
||||
// 'admin_post_nopriv_mailpoet_form_subscribe',
|
||||
// array($this, 'subscribe')
|
||||
// );
|
||||
// add_action(
|
||||
// 'admin_post_mailpoet_form_subscribe',
|
||||
// array($this, 'subscribe')
|
||||
// );
|
||||
|
||||
// add_action(
|
||||
// 'init',
|
||||
// array($this, 'subscribe')
|
||||
// );
|
||||
|
||||
return parent::__construct(
|
||||
'mailpoet_form',
|
||||
__('MailPoet Form'),
|
||||
@ -169,21 +148,15 @@ class Widget extends \WP_Widget {
|
||||
'after_title' => (!empty($after_title) ? $after_title : '')
|
||||
);
|
||||
|
||||
// if(isset($_GET['mailpoet_form']) && (int)$_GET['mailpoet_form'] === $form['id']) {
|
||||
// // form messages (success / error)
|
||||
// $output .= '<div class="mailpoet_message">';
|
||||
// // success message
|
||||
// if(isset($_GET['mailpoet_success'])) {
|
||||
// $output .= '<p class="mailpoet_validate_success">'.strip_tags(urldecode($_GET['mailpoet_success']), '<a><strong><em><br><p>').'</p>';
|
||||
// }
|
||||
// // error message
|
||||
// if(isset($_GET['mailpoet_error'])) {
|
||||
// $output .= '<p class="mailpoet_validate_error">'.strip_tags(urldecode($_GET['mailpoet_error']), '<a><strong><em><br><p>').'</p>';
|
||||
// }
|
||||
// $output .= '</div>';
|
||||
// } else {
|
||||
// $output .= '<div class="mailpoet_message"></div>';
|
||||
// }
|
||||
// check if the form was successfully submitted via POST (non ajax)
|
||||
$data['success'] = (
|
||||
(isset($_GET['mailpoet_success']))
|
||||
&&
|
||||
((int)$_GET['mailpoet_success'] === (int)$form['id'])
|
||||
);
|
||||
|
||||
// generate security token
|
||||
$data['token'] = Security::generateToken();
|
||||
|
||||
// render form
|
||||
$renderer = new Renderer();
|
||||
|
@ -100,9 +100,9 @@ class Mailer {
|
||||
}
|
||||
|
||||
function getSender($sender = false) {
|
||||
if(!$sender) {
|
||||
$sender = Setting::getValue('sender', null);
|
||||
if(!$sender['address']) throw new \Exception(__('Sender name and email are not configured.'));
|
||||
if(empty($sender)) {
|
||||
$sender = Setting::getValue('sender', array());
|
||||
if(empty($sender['address'])) throw new \Exception(__('Sender name and email are not configured.'));
|
||||
}
|
||||
return array(
|
||||
'from_name' => $sender['name'],
|
||||
|
@ -82,6 +82,45 @@ class Newsletter extends Model {
|
||||
return $this;
|
||||
}
|
||||
|
||||
function withStatistics() {
|
||||
$statistics = $this->getStatistics();
|
||||
if($statistics === false) {
|
||||
$this->statistics = false;
|
||||
} else {
|
||||
$this->statistics = $statistics->asArray();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
function getStatistics() {
|
||||
if($this->queue === false) {
|
||||
return false;
|
||||
}
|
||||
return SendingQueue::tableAlias('queues')
|
||||
->selectExpr(
|
||||
'count(DISTINCT(clicks.subscriber_id)) as clicked, ' .
|
||||
'count(DISTINCT(opens.subscriber_id)) as opened, ' .
|
||||
'count(DISTINCT(unsubscribes.subscriber_id)) as unsubscribed '
|
||||
)
|
||||
->leftOuterJoin(
|
||||
MP_STATISTICS_CLICKS_TABLE,
|
||||
'queues.id = clicks.queue_id',
|
||||
'clicks'
|
||||
)
|
||||
->leftOuterJoin(
|
||||
MP_STATISTICS_OPENS_TABLE,
|
||||
'queues.id = opens.queue_id',
|
||||
'opens'
|
||||
)
|
||||
->leftOuterJoin(
|
||||
MP_STATISTICS_UNSUBSCRIBES_TABLE,
|
||||
'queues.id = unsubscribes.queue_id',
|
||||
'unsubscribes'
|
||||
)
|
||||
->where('queues.id', $this->queue['id'])
|
||||
->findOne();
|
||||
}
|
||||
|
||||
static function search($orm, $search = '') {
|
||||
return $orm->where_like('subject', '%' . $search . '%');
|
||||
}
|
||||
@ -180,6 +219,37 @@ class Newsletter extends Model {
|
||||
|
||||
if($newsletter === false) {
|
||||
$newsletter = self::create();
|
||||
|
||||
// set default sender based on settings
|
||||
if(empty($data['sender'])) {
|
||||
$sender = Setting::getValue('sender', array());
|
||||
$data['sender_name'] = (
|
||||
!empty($sender['name'])
|
||||
? $sender['name']
|
||||
: ''
|
||||
);
|
||||
$data['sender_address'] = (
|
||||
!empty($sender['address'])
|
||||
? $sender['address']
|
||||
: ''
|
||||
);
|
||||
}
|
||||
|
||||
// set default reply_to based on settings
|
||||
if(empty($data['reply_to'])) {
|
||||
$reply_to = Setting::getValue('reply_to', array());
|
||||
$data['reply_to_name'] = (
|
||||
!empty($reply_to['name'])
|
||||
? $reply_to['name']
|
||||
: ''
|
||||
);
|
||||
$data['reply_to_address'] = (
|
||||
!empty($reply_to['address'])
|
||||
? $reply_to['address']
|
||||
: ''
|
||||
);
|
||||
}
|
||||
|
||||
$newsletter->hydrate($data);
|
||||
} else {
|
||||
unset($data['id']);
|
||||
|
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;
|
||||
|
33
lib/Models/StatisticsForms.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
namespace MailPoet\Models;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class StatisticsForms extends Model {
|
||||
public static $_table = MP_STATISTICS_FORMS_TABLE;
|
||||
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public static function record($form_id, $subscriber_id) {
|
||||
if($form_id > 0 && $subscriber_id > 0) {
|
||||
// check if we already have a record for today
|
||||
$record = self::where('form_id', $form_id)
|
||||
->where('subscriber_id', $subscriber_id)
|
||||
->findOne();
|
||||
|
||||
if($record === false) {
|
||||
// create a new entry
|
||||
$record = self::create();
|
||||
$record->hydrate(array(
|
||||
'form_id' => $form_id,
|
||||
'subscriber_id' => $subscriber_id
|
||||
));
|
||||
$record->save();
|
||||
}
|
||||
return $record;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
12
lib/Models/StatisticsOpens.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace MailPoet\Models;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class StatisticsOpens extends Model {
|
||||
public static $_table = MP_STATISTICS_OPENS_TABLE;
|
||||
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
12
lib/Models/StatisticsUnsubscribes.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace MailPoet\Models;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class StatisticsUnsubscribes extends Model {
|
||||
public static $_table = MP_STATISTICS_UNSUBSCRIBES_TABLE;
|
||||
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
@ -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(
|
||||
@ -316,6 +329,19 @@ class Subscriber extends Model {
|
||||
return $orm;
|
||||
}
|
||||
|
||||
static function getSubscribedInSegments($segment_ids) {
|
||||
$subscribers = SubscriberSegment::table_alias('relation')
|
||||
->whereIn('relation.segment_id', $segment_ids)
|
||||
->where('relation.status', 'subscribed')
|
||||
->join(
|
||||
MP_SUBSCRIBERS_TABLE,
|
||||
'subscribers.id = relation.subscriber_id',
|
||||
'subscribers'
|
||||
)
|
||||
->where('subscribers.status', 'subscribed');
|
||||
return $subscribers;
|
||||
}
|
||||
|
||||
function customFields() {
|
||||
return $this->hasManyThrough(
|
||||
__NAMESPACE__.'\CustomField',
|
||||
@ -513,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;
|
||||
}
|
||||
}
|
||||
|
||||
|