Compare commits
285 Commits
Author | SHA1 | Date | |
---|---|---|---|
ffc1d0a61c | |||
d1b160def7 | |||
493fd01754 | |||
9ced4b1757 | |||
17010e5ba9 | |||
42ad7584d4 | |||
dbc0f9b238 | |||
e62e9a5892 | |||
bc25fa61b4 | |||
2590967183 | |||
86eafd3c17 | |||
90a6f160c2 | |||
c774aec6a2 | |||
8f2fd1d76e | |||
4df11163a1 | |||
82a736ffbb | |||
87052986e8 | |||
0c73c0fadc | |||
5c7e11076d | |||
d1df94c759 | |||
53cc39c6f5 | |||
4955c72ee1 | |||
16661af8c3 | |||
bc80f69e41 | |||
0192934e65 | |||
2793e74858 | |||
5996696cc9 | |||
7f6cf5bbf3 | |||
c0ef2254cd | |||
0dbe04c3f8 | |||
ef1805d9b5 | |||
514f539e83 | |||
50f072705e | |||
f8f7bc3d3d | |||
f1bf2bb097 | |||
bbe2f69a7f | |||
c844488b0b | |||
112fe0cd6e | |||
c9e6dce785 | |||
d1c09c015a | |||
8d61866b77 | |||
0831c748b1 | |||
b2a0bc3860 | |||
1f99345e7b | |||
89782bc94b | |||
155ff09280 | |||
132e6d3342 | |||
f02699158f | |||
be3462925d | |||
3d82230d10 | |||
c20c46fd86 | |||
6e63c72aa5 | |||
e059eec5ea | |||
a2d38c9076 | |||
8d507b2ee0 | |||
5e2979c283 | |||
84ec0de3cd | |||
ee07780833 | |||
ed104156a9 | |||
90f2e9ff9d | |||
98fb7aa65e | |||
a51eb59cf8 | |||
270023b89c | |||
7d77e075e9 | |||
9bbe36b3cb | |||
7a904ed093 | |||
e3c065b353 | |||
4ca2872e0e | |||
ce338f7fe2 | |||
fa28b0a955 | |||
a298650187 | |||
95772ef68a | |||
e1c94db516 | |||
7be1a11d1e | |||
c04b95c09a | |||
cdfeb8d512 | |||
3ef8067968 | |||
9fb04bc3c0 | |||
25727ea0ba | |||
4c8ac369b7 | |||
268dabdc9f | |||
c0ba218949 | |||
ee85139089 | |||
b4f83fe1bd | |||
bd83001ef5 | |||
fb3a9f485f | |||
fd44776ae9 | |||
6dbe338b01 | |||
1f06a7dd0b | |||
37c218f782 | |||
25016f2a8d | |||
792744a270 | |||
c5fbfca132 | |||
c2fde308cb | |||
533d9b0d38 | |||
2035b802e3 | |||
a5e66ec6a0 | |||
beb939df9e | |||
44e342c692 | |||
3a417d460f | |||
1950d6661f | |||
da6e154642 | |||
acebf669a7 | |||
4a2bbe3f88 | |||
9b011c0281 | |||
bf58d8a22d | |||
72d1eb79a6 | |||
bb4893c0a0 | |||
9929cf0aee | |||
83967e84ba | |||
9621cb3ca9 | |||
a413f666fe | |||
d1e2c6c074 | |||
3b6a9f7a6e | |||
d2e5fb89c2 | |||
97d1e95037 | |||
48fbce22e7 | |||
916fe76795 | |||
e10310fb5c | |||
367afcf814 | |||
67fa9e0993 | |||
d7553a5f27 | |||
8c847825fa | |||
d21d9b99b0 | |||
8461c13532 | |||
3b7ffe9ba7 | |||
1724fa22c1 | |||
01089d7a72 | |||
717ebfd20c | |||
daff3d5016 | |||
98fb838169 | |||
f13a4dd4f3 | |||
62a164e4c6 | |||
04238f3339 | |||
bc62474f3c | |||
98005a2a6f | |||
b7fe8dc6d6 | |||
436faea591 | |||
4208b148b4 | |||
5ce5f0bf8a | |||
18518de397 | |||
68f747211a | |||
ebd26ddd98 | |||
924aa0439f | |||
3124d6a61b | |||
149d031b52 | |||
fa96c4697d | |||
d46c9d5412 | |||
9ab8b1f0c5 | |||
9425ac1593 | |||
4e32479609 | |||
7d95b38dc4 | |||
17c83c5bd4 | |||
23bb2f111f | |||
4655b2c64c | |||
84fede11b8 | |||
f37488fc0b | |||
20e2e03982 | |||
a5d96f1534 | |||
a516eb1a95 | |||
6dd8270bec | |||
981cbd579f | |||
52a0aae10f | |||
ebca4257a6 | |||
c3944095bc | |||
a1445d1b6a | |||
e62c24879b | |||
00f06ea202 | |||
32ca24ce38 | |||
44e3adb422 | |||
a1346ebecc | |||
25b51d0446 | |||
556a170903 | |||
7ac386a8e2 | |||
d91b55ec52 | |||
786fbc36a2 | |||
160e8b7a12 | |||
0b1f85da09 | |||
fbc6f54ddc | |||
a603e97b8c | |||
0875b627b6 | |||
035784ece0 | |||
aa93c7349f | |||
82cf4a28fd | |||
832e5ef342 | |||
e3de3a123a | |||
db91590159 | |||
28c61fca0b | |||
e62a8c2ec5 | |||
fdbd1245e3 | |||
0eef46db57 | |||
080ae88a04 | |||
225be9f3cd | |||
c9a42ebb76 | |||
ae9b3df92d | |||
63a08ebb55 | |||
2ef5096fa9 | |||
95cfe2d8ec | |||
49676b3fc5 | |||
c96ac06423 | |||
6a3166c311 | |||
0017df1c2d | |||
f2a0d4ce96 | |||
cb50517cbc | |||
0fedd1779f | |||
1625e1771b | |||
bde78b607b | |||
f3c58c27be | |||
9fec460295 | |||
162859529e | |||
3bdaaf8368 | |||
f6ab0050b2 | |||
10a20935c3 | |||
8135b677f7 | |||
90382bc86d | |||
47af8939cc | |||
4a0deb2182 | |||
3a206b2c88 | |||
70cfcbe7cc | |||
6342cb17bd | |||
64c35b12c8 | |||
6a14f97419 | |||
dfec34eda9 | |||
893231e8e5 | |||
e9110680ee | |||
7b54285ca6 | |||
33ea16eb0f | |||
b1ae07d38e | |||
3f168d052f | |||
158d26ef86 | |||
ae03ee2c46 | |||
ad0adb48e1 | |||
ff5353c894 | |||
abb2389378 | |||
3cf50810f9 | |||
20a6e6d6de | |||
0b1fc8f6c3 | |||
0a771acb02 | |||
0199e2c7e1 | |||
d1f407bf19 | |||
f18d2842b9 | |||
f640cbb307 | |||
b20d92c9b1 | |||
795485d42a | |||
dfadda2d12 | |||
31305a04c0 | |||
cfdb886e88 | |||
1ce4b16327 | |||
b12f7f29de | |||
5473f94e24 | |||
a31dce6226 | |||
d996b78561 | |||
c2e7513381 | |||
541696863e | |||
6c8d2be18c | |||
907fe585de | |||
e24f8c9653 | |||
5df0475b1a | |||
cf154455e3 | |||
dcfe6357cf | |||
983df216f3 | |||
f750d2359f | |||
d85f51e9fc | |||
40a62687cf | |||
136e09e9fb | |||
f509dc0d7e | |||
c100130f39 | |||
9922ecd93c | |||
a4cf2f9c76 | |||
576fbf2085 | |||
5c63971314 | |||
7418923bbc | |||
a8f8134f67 | |||
103da61d45 | |||
01e6a5e6b2 | |||
f5ccf3b38a | |||
c8929351ba | |||
6ca536e9ca | |||
89b04e8691 | |||
588b441fb1 | |||
13dc3577f1 | |||
505b979ac5 | |||
3b4c5c83e1 | |||
056e79eeac | |||
4bde705f04 |
5
.gitignore
vendored
@ -1,7 +1,7 @@
|
||||
.DS_Store
|
||||
TODO
|
||||
composer.phar
|
||||
vendor
|
||||
/vendor
|
||||
tests/_output/*
|
||||
tests/acceptance.suite.yml
|
||||
tests/_support/_generated/*
|
||||
@ -15,4 +15,5 @@ temp
|
||||
wysija-newsletters.zip
|
||||
tests/javascript/testBundles
|
||||
assets/css/*.css
|
||||
assets/js/*.js
|
||||
assets/js/*.js
|
||||
.vagrant
|
||||
|
11
README.md
@ -8,7 +8,6 @@ MailPoet done the right way.
|
||||
```
|
||||
php
|
||||
nodejs
|
||||
phantomjs
|
||||
wordpress
|
||||
```
|
||||
|
||||
@ -47,16 +46,6 @@ $ ./do compile:all
|
||||
$ ./do test:unit
|
||||
```
|
||||
|
||||
- Acceptance tests:
|
||||
```sh
|
||||
$ ./do test:acceptance
|
||||
```
|
||||
|
||||
- Run all tests:
|
||||
```sh
|
||||
$ ./do test:all
|
||||
```
|
||||
|
||||
- Debug tests:
|
||||
```sh
|
||||
$ ./do test:debug
|
||||
|
19
RoboFile.php
@ -47,6 +47,19 @@ class RoboFile extends \Robo\Tasks {
|
||||
->run();
|
||||
}
|
||||
|
||||
function watchCss() {
|
||||
$css_files = $this->rsearch('assets/css/src/', array('styl'));
|
||||
$this->taskWatch()
|
||||
->monitor($css_files, function() {
|
||||
$this->compileCss();
|
||||
})
|
||||
->run();
|
||||
}
|
||||
|
||||
function watchJs() {
|
||||
$this->_exec('./node_modules/webpack/bin/webpack.js --watch');
|
||||
}
|
||||
|
||||
function compileAll() {
|
||||
$this->compileJs();
|
||||
$this->compileCss();
|
||||
@ -61,7 +74,8 @@ class RoboFile extends \Robo\Tasks {
|
||||
'assets/css/src/admin.styl',
|
||||
'assets/css/src/newsletter_editor/newsletter_editor.styl',
|
||||
'assets/css/src/public.styl',
|
||||
'assets/css/src/rtl.styl'
|
||||
'assets/css/src/rtl.styl',
|
||||
'assets/css/src/importExport.styl'
|
||||
);
|
||||
|
||||
$this->_exec(join(' ', array(
|
||||
@ -113,7 +127,6 @@ class RoboFile extends \Robo\Tasks {
|
||||
function testFailed() {
|
||||
$this->loadEnv();
|
||||
$this->_exec('vendor/bin/codecept build');
|
||||
$this->startPhantomJS();
|
||||
$this->_exec('vendor/bin/codecept run -g failed');
|
||||
}
|
||||
|
||||
@ -121,4 +134,4 @@ class RoboFile extends \Robo\Tasks {
|
||||
$dotenv = new Dotenv\Dotenv(__DIR__);
|
||||
$dotenv->load();
|
||||
}
|
||||
}
|
||||
}
|
@ -5,12 +5,15 @@
|
||||
@require 'common'
|
||||
@require 'modal'
|
||||
@require 'notice'
|
||||
@require 'validation_engine'
|
||||
|
||||
@require 'form_editor'
|
||||
@require 'listing'
|
||||
@require 'box'
|
||||
@require 'breadcrumb'
|
||||
@require 'form'
|
||||
|
||||
@require 'settings'
|
||||
@require 'form'
|
||||
@require 'parsley'
|
||||
@require 'form_validation'
|
||||
|
||||
@require 'settings'
|
||||
@require 'progress_bar'
|
||||
|
@ -19,8 +19,32 @@ a:focus
|
||||
|
||||
// select 2
|
||||
.select2-container
|
||||
// textareas
|
||||
textarea.regular-text
|
||||
width: 25em !important
|
||||
|
||||
@media screen and (max-width: 782px)
|
||||
.select2-container
|
||||
width: 100% !important
|
||||
width: 100% !important
|
||||
|
||||
// progress bars
|
||||
progress-border-radius = 5px
|
||||
progress-background = #efefef
|
||||
progress-foreground = #69b1e9
|
||||
|
||||
progress
|
||||
background-color: progress-background;
|
||||
height: 2em
|
||||
border: 0
|
||||
width: 100%
|
||||
|
||||
progress::-webkit-progress-bar
|
||||
background-color: progress-background;
|
||||
|
||||
progress::-webkit-progress-value
|
||||
background-color: progress-foreground
|
||||
border-radius: progress-border-radius
|
||||
|
||||
progress::-moz-progress-bar
|
||||
background-color: progress-foreground
|
||||
border-radius: progress-border-radius
|
||||
|
@ -1,6 +1,12 @@
|
||||
@require 'codemirror/lib/codemirror.css'
|
||||
@require 'codemirror/theme/neo.css'
|
||||
|
||||
icons = '../img/form_editor_icons.png'
|
||||
handle_icon = '../img/handle.png'
|
||||
|
||||
#mailpoet_form_name
|
||||
font-size: 23px
|
||||
|
||||
#mailpoet_form_history
|
||||
display: none
|
||||
|
||||
@ -96,6 +102,7 @@ handle_icon = '../img/handle.png'
|
||||
/* MailPoet Form wrapper */
|
||||
#mailpoet_form_wrapper
|
||||
position: relative
|
||||
margin: 20px 0 0 0
|
||||
|
||||
/* MailPoet Form container */
|
||||
#mailpoet_form_container
|
||||
|
6
assets/css/src/form_validation.styl
Normal file
@ -0,0 +1,6 @@
|
||||
.parsley-errors-list
|
||||
margin-top: 8px
|
||||
|
||||
.parsley-required
|
||||
.parsley-custom-error-message
|
||||
color: #b94a48
|
78
assets/css/src/importExport.styl
Normal file
@ -0,0 +1,78 @@
|
||||
.mailpoet_hidden, .mailpoet_validation_error
|
||||
display none
|
||||
|
||||
.form-table
|
||||
th
|
||||
width 300px
|
||||
|
||||
#paste_input
|
||||
width 100%
|
||||
|
||||
input[type="radio"]
|
||||
margin-right 0.5em !important
|
||||
& + span
|
||||
margin-right 2.5em
|
||||
|
||||
span
|
||||
&.mailpoet_mailchimp-key-status
|
||||
&.mailpoet_mailchimp-ok
|
||||
&:before
|
||||
content "\2713"
|
||||
color #0e90d2
|
||||
margin-left 15px
|
||||
&.mailpoet_mailchimp-error
|
||||
&:before
|
||||
content "\2717"
|
||||
color #900
|
||||
margin-left 15px
|
||||
|
||||
#subscribers_data
|
||||
overflow auto
|
||||
table
|
||||
width auto
|
||||
td
|
||||
padding 0.5em
|
||||
& > table
|
||||
& > tbody
|
||||
& > td
|
||||
padding 0.5em
|
||||
& > tr
|
||||
&:nth-child(odd)
|
||||
background #f9f9f9
|
||||
.mailpoet_header
|
||||
text-transform uppercase
|
||||
font-weight 600
|
||||
text-decoration underline
|
||||
|
||||
#subscribers_data th:first-child, #subscribers_data td:first-child
|
||||
width 10em !important
|
||||
text-align center !important
|
||||
padding 0 1em 0 1em !important
|
||||
vertical-align inherit !important
|
||||
|
||||
#subscribers_data
|
||||
& > table
|
||||
& > thead
|
||||
& > tr
|
||||
& > th
|
||||
& > span
|
||||
width 15em !important
|
||||
|
||||
.mailpoet_data_match
|
||||
color #0e90d2
|
||||
margin-left 0.25em
|
||||
|
||||
.mailpoet_import_error, .mailpoet_validation_error
|
||||
color #900
|
||||
|
||||
tr
|
||||
&.mailpoet_segments
|
||||
& > td
|
||||
& > a
|
||||
margin-left 15px
|
||||
|
||||
span
|
||||
&.select2-search
|
||||
&.select2-search--dropdown
|
||||
display none !important
|
||||
|
@ -1,4 +1,4 @@
|
||||
.mailpoet_listing_loading tbody tr,
|
||||
.mailpoet_listing_loading tbody tr
|
||||
.mailpoet_form_loading tbody tr
|
||||
opacity: 0.2
|
||||
|
||||
@ -8,6 +8,20 @@
|
||||
.mailpoet_select_all td
|
||||
text-align: center
|
||||
|
||||
table.widefat thead .check-column,
|
||||
table.widefat tfoot .check-column
|
||||
padding: 10px 0 0 3px
|
||||
.mailpoet_listing_table
|
||||
th span
|
||||
white-space: nowrap
|
||||
|
||||
thead .check-column
|
||||
tfoot .check-column
|
||||
padding: 10px 0 0 3px
|
||||
|
||||
thead th.column-primary
|
||||
tfoot th.column-primary
|
||||
width: 25em
|
||||
|
||||
// responsive
|
||||
@media screen and (max-width: 782px)
|
||||
thead th.column-primary
|
||||
tfoot th.column-primary
|
||||
width: 100%
|
@ -90,7 +90,7 @@ body.mailpoet_modal_opened
|
||||
padding: 0
|
||||
margin: 0
|
||||
width: 100%
|
||||
transition: margin 0.3s ease-out
|
||||
transition: margin 250ms ease-out
|
||||
|
||||
.mailpoet_panel_wrapper
|
||||
background-color: #f1f1f1
|
||||
@ -159,23 +159,6 @@ body.mailpoet_modal_opened
|
||||
margin: 0
|
||||
text-align: right
|
||||
|
||||
.mailpoet_button
|
||||
padding: 3px 15px
|
||||
border: 1px solid #444
|
||||
font-weight: normal
|
||||
cursor: pointer
|
||||
background-color: #222
|
||||
color: #cfcfcf
|
||||
font-size: 1em
|
||||
|
||||
.mailpoet_button:hover
|
||||
background-color: #00aacc
|
||||
color: #fff
|
||||
|
||||
.mailpoet_button:active
|
||||
background-color: #00ccff
|
||||
color: #fff
|
||||
|
||||
@media screen and (max-width: 782px)
|
||||
#mailpoet_modal_overlay.mailpoet_panel_overlay
|
||||
top: 46px
|
||||
@ -217,4 +200,4 @@ body.mailpoet_modal_opened
|
||||
0%
|
||||
50%
|
||||
background-color: #064E6D
|
||||
100%
|
||||
100%
|
||||
|
@ -76,13 +76,23 @@ $layer-selector-width = 30px
|
||||
display: inline-block
|
||||
padding: 2px
|
||||
vertical-align: top
|
||||
animation-background-color()
|
||||
|
||||
.mailpoet_tool
|
||||
padding: 0
|
||||
|
||||
.mailpoet_delete_block_activate
|
||||
max-width: 100%
|
||||
display: inline-block
|
||||
opacity: 1
|
||||
|
||||
.mailpoet_delete_block_confirm,
|
||||
.mailpoet_delete_block_cancel
|
||||
display: none
|
||||
max-width: 0
|
||||
opacity: 0
|
||||
overflow: hidden
|
||||
display: inline-block
|
||||
animation-fade-in-and-scale-horizontally()
|
||||
|
||||
.mailpoet_delete_block_activated
|
||||
width: auto
|
||||
@ -93,11 +103,17 @@ $layer-selector-width = 30px
|
||||
height: auto
|
||||
|
||||
.mailpoet_delete_block_activate
|
||||
display: none
|
||||
overflow: hidden
|
||||
max-width: 0
|
||||
opacity: 0
|
||||
|
||||
.mailpoet_delete_block_confirm,
|
||||
.mailpoet_delete_block_cancel
|
||||
display: inline-block
|
||||
max-width: 100%
|
||||
opacity: 1
|
||||
|
||||
.mailpoet_delete_block_cancel
|
||||
margin-left: 3px
|
||||
|
||||
.mailpoet_delete_block_confirm
|
||||
color: $warning-text-color
|
||||
|
@ -52,6 +52,7 @@ $draggable-widget-z-index = 2
|
||||
padding: 0
|
||||
margin: 0
|
||||
z-index: $draggable-widget-z-index
|
||||
animation-fade-in()
|
||||
|
||||
.mailpoet_widget_icon
|
||||
padding: 0
|
||||
|
@ -45,9 +45,10 @@
|
||||
&::before
|
||||
content: '\f140'
|
||||
|
||||
.mailpoet_save_as_template_container
|
||||
.mailpoet_save_as_template_container,
|
||||
.mailpoet_export_template_container
|
||||
border-radius(3px)
|
||||
float: left
|
||||
display: inline-block
|
||||
clear: both
|
||||
|
||||
margin-top: 5px
|
||||
@ -55,7 +56,8 @@
|
||||
background-color: $white-color
|
||||
border: 1px solid $structure-border-color
|
||||
|
||||
.mailpoet_save_as_template_title
|
||||
.mailpoet_save_as_template_title,
|
||||
.mailpoet_export_template_title
|
||||
font-size: 1.1em
|
||||
|
||||
.mailpoet_editor_last_saved
|
||||
|
@ -26,13 +26,9 @@ $widget-icon-width = 30px
|
||||
border-right: 0
|
||||
|
||||
&.closed .mailpoet_region_content
|
||||
max-height: 0px
|
||||
overflow: hidden
|
||||
margin-top: 0
|
||||
display: none
|
||||
|
||||
.mailpoet_region_content
|
||||
max-height: 2000px
|
||||
transition: max-height 0.2s ease
|
||||
padding: 0 20px
|
||||
margin-top: 12px
|
||||
|
||||
|
@ -18,3 +18,6 @@
|
||||
|
||||
& > .mailpoet_block
|
||||
width: 100%
|
||||
|
||||
.mailpoet_automated_latest_content_display_options
|
||||
animation-slide-open-downwards()
|
||||
|
@ -1,3 +1,8 @@
|
||||
$column-margin = 20px
|
||||
$one-column-width = $newsletter-width - (2 * $column-margin)
|
||||
$two-column-width = ($newsletter-width / 2) - (2 * $column-margin)
|
||||
$three-column-width = ($newsletter-width / 3) - (2 * $column-margin)
|
||||
|
||||
.mailpoet_container
|
||||
width: 100%
|
||||
min-height: 15px
|
||||
@ -44,7 +49,7 @@
|
||||
|
||||
.mailpoet_container_horizontal > .mailpoet_container_block
|
||||
margin-bottom: 0
|
||||
width: 20px + 560px + 20px
|
||||
width: $column-margin + $one-column-width + $column-margin
|
||||
|
||||
// More than one column
|
||||
& > .mailpoet_container_block > .mailpoet_container > .mailpoet_container_block > .mailpoet_container_horizontal
|
||||
@ -57,14 +62,14 @@
|
||||
& > .mailpoet_block:first-child:nth-last-child(2) ~ .mailpoet_block
|
||||
//padding-left: 20px
|
||||
//padding-right: 20px
|
||||
width: 260px + 20px + 20px
|
||||
width: $column-margin + $two-column-width + $column-margin
|
||||
|
||||
// Three columns
|
||||
& > .mailpoet_block:first-child:nth-last-child(3)
|
||||
& > .mailpoet_block:first-child:nth-last-child(3) ~ .mailpoet_block
|
||||
//padding-left: 20px
|
||||
//padding-right: 20px
|
||||
width: 160px + 20px + 20px
|
||||
width: $column-margin + $three-column-width + $column-margin
|
||||
|
||||
.mailpoet_container_empty
|
||||
text-align: center
|
||||
@ -74,3 +79,4 @@
|
||||
box-shadow(inset 1px 2px 1px $primary-inactive-color)
|
||||
color: #656565
|
||||
border-radius(3px)
|
||||
animation-background-color()
|
||||
|
@ -19,6 +19,10 @@ $divider-hover-border-color = $primary-active-color
|
||||
width: 100%
|
||||
border: 1px solid transparent
|
||||
|
||||
.mailpoet_active_divider_style
|
||||
border: 1px solid $active-divider-border-color
|
||||
background: $active-divider-background-color
|
||||
|
||||
.mailpoet_field_divider_style:hover
|
||||
border: 1px solid $divider-hover-border-color
|
||||
|
||||
|
@ -1,15 +1,19 @@
|
||||
.mailpoet_image_block
|
||||
|
||||
img
|
||||
vertical-align: bottom
|
||||
max-width: 100%
|
||||
width: auto
|
||||
height: auto
|
||||
|
||||
&.mailpoet_full_image
|
||||
padding-left: 0
|
||||
padding-right: 0
|
||||
margin-bottom: 0
|
||||
|
||||
img
|
||||
width: 100%
|
||||
|
||||
.mailpoet_content a:hover
|
||||
cursor: all-scroll
|
||||
|
||||
img
|
||||
vertical-align: bottom
|
||||
max-width: $newsletter-width
|
||||
width: 100%
|
||||
height: auto
|
||||
|
@ -1,15 +1,12 @@
|
||||
.mailpoet_posts_block
|
||||
box-shadow(none)
|
||||
padding-left: 0
|
||||
padding-right: 0
|
||||
|
||||
& > .mailpoet_content
|
||||
font-size: 1em
|
||||
text-align: center
|
||||
background-color: $primary-active-color
|
||||
margin: 20px 0
|
||||
padding: 15px
|
||||
box-shadow(inset 1px 2px 1px $primary-inset-shadow-color)
|
||||
color: $white-color
|
||||
border-radius(3px)
|
||||
.mailpoet_posts_block_posts
|
||||
overflow: auto
|
||||
|
||||
& > .mailpoet_block
|
||||
width: 100%
|
||||
|
||||
.mailpoet_post_selection_filter_row
|
||||
margin-top: 5px
|
||||
@ -18,7 +15,11 @@
|
||||
.mailpoet_posts_categories_and_tags
|
||||
width: 100%
|
||||
|
||||
.mailpoet_settings_posts_show_display_options
|
||||
.mailpoet_settings_posts_display_options
|
||||
.mailpoet_settings_posts_selection
|
||||
animation-slide-open-downwards()
|
||||
|
||||
.mailpoet_settings_posts_show_display_options,
|
||||
.mailpoet_settings_posts_show_post_selection
|
||||
display: block
|
||||
margin-top: 10px
|
||||
|
@ -1,12 +1,25 @@
|
||||
/* Fix select2 z-index to work with MailPoet.Modal */
|
||||
.select2-drop
|
||||
z-index: 101000
|
||||
.select2-dropdown
|
||||
z-index: 101000 !important
|
||||
|
||||
/* Remove input field styles from select2 type input */
|
||||
.select2-container
|
||||
border: none
|
||||
padding: 0
|
||||
|
||||
/* Fix select2 input glow and margins that wordpress may insert */
|
||||
.select2 input,
|
||||
.select2 input:focus
|
||||
border-color: none
|
||||
box-shadow: none
|
||||
|
||||
margin: 0
|
||||
padding: 0
|
||||
|
||||
/* Fix width overrides for select2 */
|
||||
.mailpoet_editor_settings .select2-container
|
||||
width: 100% !important
|
||||
|
||||
/* Fix inline TinyMCE toolbar to have minimal width instead of being close to 100% of the screen */
|
||||
div.mce-toolbar-grp.mce-container
|
||||
position: absolute
|
||||
|
30
assets/css/src/newsletter_editor/mixins/transitions.styl
Normal file
@ -0,0 +1,30 @@
|
||||
animation-slide-open-downwards()
|
||||
transition: all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
|
||||
max-height: 2000px
|
||||
opacity: 1
|
||||
|
||||
&.mailpoet_closed
|
||||
max-height: 0
|
||||
opacity: 0
|
||||
overflow-y: hidden
|
||||
|
||||
animation-background-color()
|
||||
transition: background 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
|
||||
|
||||
animation-fade-in()
|
||||
animation-name: fadeIn
|
||||
animation-duration: 250ms
|
||||
animation-fill-mode: forwards
|
||||
|
||||
animation-fade-in-and-scale-horizontally()
|
||||
transition: all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
|
||||
|
||||
@keyframes fadeIn {
|
||||
0% {
|
||||
opacity: 0.3
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
@require 'mixins/border-radius'
|
||||
@require 'mixins/box-shadow'
|
||||
@require 'mixins/filter-shadow'
|
||||
@require 'mixins/transitions'
|
||||
|
||||
@require 'variables'
|
||||
@require 'common'
|
||||
|
@ -23,4 +23,4 @@ $warning-alternate-text-color = #f4c6c8
|
||||
$error-text-color = #d54e21
|
||||
|
||||
// Dimensions
|
||||
$newsletter-width = 600px
|
||||
$newsletter-width = 660px
|
||||
|
27
assets/css/src/parsley.styl
Normal file
@ -0,0 +1,27 @@
|
||||
input.parsley-success,
|
||||
select.parsley-success,
|
||||
textarea.parsley-success
|
||||
color #468847
|
||||
background-color #DFF0D8
|
||||
border 1px solid #D6E9C6
|
||||
|
||||
input.parsley-error,
|
||||
select.parsley-error,
|
||||
textarea.parsley-error
|
||||
color #B94A48
|
||||
background-color #F2DEDE
|
||||
border 1px solid #EED3D7
|
||||
|
||||
.parsley-errors-list
|
||||
margin 2px 0 3px
|
||||
padding 0
|
||||
list-style-type none
|
||||
font-size 0.9em
|
||||
line-height 0.9em
|
||||
opacity 0
|
||||
transition all .3s ease-in
|
||||
-o-transition all .3s ease-in
|
||||
-moz-transition all .3s ease-in
|
||||
-webkit-transition all .3s ease-in
|
||||
&.filled
|
||||
opacity 1
|
29
assets/css/src/progress_bar.styl
Normal file
@ -0,0 +1,29 @@
|
||||
.mailpoet_progress
|
||||
background-color: #efefef
|
||||
height: 25px
|
||||
padding: 0
|
||||
width: 100%
|
||||
margin: 0
|
||||
border-radius: 5px
|
||||
position: relative
|
||||
|
||||
.mailpoet_progress_label
|
||||
position: absolute
|
||||
width: 100%
|
||||
text-align: center
|
||||
display: inline-block
|
||||
margin: 2px 0 0 0
|
||||
|
||||
.mailpoet_progress_bar
|
||||
position: absolute
|
||||
display: inline-block
|
||||
height: 100%
|
||||
border-radius: 3px
|
||||
box-shadow: 0 1px 0 rgba(255, 255, 255, .5) inset
|
||||
background-color: #34c2e3
|
||||
background-image: linear-gradient(top, #34c2e3, darken(#34c2e3, 20%))
|
||||
|
||||
.mailpoet_progress_complete
|
||||
.mailpoet_progress_bar
|
||||
background-color: #fecf23
|
||||
background-image: linear-gradient(top, #fecf23, #fd9215)
|
@ -0,0 +1,4 @@
|
||||
@import 'nib'
|
||||
|
||||
@require 'parsley'
|
||||
@require 'form_validation'
|
||||
|
@ -1,141 +0,0 @@
|
||||
lesscss-percentage(n)
|
||||
(n * 100)%
|
||||
popupBg = rgb(0, 87, 154, 1)
|
||||
popupTextColor = rgb(255, 255, 255, 1)
|
||||
borderColor = rgb(255, 255, 255, 1)
|
||||
borderWidth = 1px
|
||||
popupFontSize = 12px
|
||||
popupRadius = 0
|
||||
popupShadowWidth = 2px
|
||||
popupShadowColor = rgb(51, 51, 51, 1)
|
||||
/* Z-INDEX */
|
||||
.formError
|
||||
z-index 990
|
||||
.formError .formErrorContent
|
||||
z-index 991
|
||||
.formError .formErrorArrow
|
||||
z-index 996
|
||||
.ui-dialog .formError
|
||||
z-index 5000
|
||||
.ui-dialog .formError .formErrorContent
|
||||
z-index 5001
|
||||
.ui-dialog .formError .formErrorArrow
|
||||
z-index 5006
|
||||
.inputContainer
|
||||
position relative
|
||||
float left
|
||||
.formError
|
||||
position absolute
|
||||
top 300px
|
||||
left 300px
|
||||
display block
|
||||
cursor pointer
|
||||
text-align left
|
||||
.formError.inline
|
||||
position relative
|
||||
top 0
|
||||
left 0
|
||||
display inline-block
|
||||
.ajaxSubmit
|
||||
padding 20px
|
||||
background #55ea55
|
||||
border 1px solid #999
|
||||
display none
|
||||
.formError .formErrorContent
|
||||
width 100%
|
||||
background popupBg
|
||||
position relative
|
||||
color popupTextColor
|
||||
min-width 120px
|
||||
font-size popupFontSize
|
||||
border borderWidth solid borderColor
|
||||
box-shadow 0 0 popupShadowWidth popupShadowColor
|
||||
-moz-box-shadow 0 0 popupShadowWidth popupShadowColor
|
||||
-webkit-box-shadow 0 0 popupShadowWidth popupShadowColor
|
||||
-o-box-shadow 0 0 popupShadowWidth popupShadowColor
|
||||
padding 4px 10px 4px 10px
|
||||
border-radius popupRadius
|
||||
-moz-border-radius popupRadius
|
||||
-webkit-border-radius popupRadius
|
||||
-o-border-radius popupRadius
|
||||
.formError.inline .formErrorContent
|
||||
box-shadow none
|
||||
-moz-box-shadow none
|
||||
-webkit-box-shadow none
|
||||
-o-box-shadow none
|
||||
border none
|
||||
border-radius 0
|
||||
-moz-border-radius 0
|
||||
-webkit-border-radius 0
|
||||
-o-border-radius 0
|
||||
.greenPopup .formErrorContent
|
||||
background #33be40
|
||||
.blackPopup .formErrorContent
|
||||
background #393939
|
||||
color #FFF
|
||||
.formError .formErrorArrow
|
||||
width 15px
|
||||
margin -2px 0 0 13px
|
||||
position relative
|
||||
body[dir='rtl'] .formError .formErrorArrow, body.rtl .formError .formErrorArrow
|
||||
margin -2px 13px 0 0
|
||||
.formError .formErrorArrowBottom
|
||||
box-shadow none
|
||||
-moz-box-shadow none
|
||||
-webkit-box-shadow none
|
||||
-o-box-shadow none
|
||||
margin 0px 0 0 12px
|
||||
top 2px
|
||||
.formError .formErrorArrow div
|
||||
border-left borderWidth solid borderColor
|
||||
border-right borderWidth solid borderColor
|
||||
box-shadow 0 ceil((popupShadowWidth / 3)) ceil((popupShadowWidth / 2)) lighten(popupShadowColor, 10%)
|
||||
-moz-box-shadow 0 ceil((popupShadowWidth / 3)) ceil((popupShadowWidth / 2)) lighten(popupShadowColor, 10%)
|
||||
-webkit-box-shadow 0 ceil((popupShadowWidth / 3)) ceil((popupShadowWidth / 2)) lighten(popupShadowColor, 10%)
|
||||
-o-box-shadow 0 ceil((popupShadowWidth / 3)) ceil((popupShadowWidth / 2)) lighten(popupShadowColor, 10%)
|
||||
font-size 0px
|
||||
height 1px
|
||||
background popupBg
|
||||
margin 0 auto
|
||||
line-height 0
|
||||
font-size 0
|
||||
display block
|
||||
.formError .formErrorArrowBottom div
|
||||
box-shadow none
|
||||
-moz-box-shadow none
|
||||
-webkit-box-shadow none
|
||||
-o-box-shadow none
|
||||
.greenPopup .formErrorArrow div
|
||||
background #33be40
|
||||
.blackPopup .formErrorArrow div
|
||||
background #393939
|
||||
color #FFF
|
||||
.formError .formErrorArrow .line10
|
||||
width 13px
|
||||
border none
|
||||
.formError .formErrorArrow .line9
|
||||
width 11px
|
||||
border none
|
||||
.formError .formErrorArrow .line8
|
||||
width 11px
|
||||
.formError .formErrorArrow .line7
|
||||
width 9px
|
||||
.formError .formErrorArrow .line6
|
||||
width 7px
|
||||
.formError .formErrorArrow .line5
|
||||
width 5px
|
||||
.formError .formErrorArrow .line4
|
||||
width 3px
|
||||
.formError .formErrorArrow .line3
|
||||
width ceil((borderWidth / 2))
|
||||
border-left borderWidth solid borderColor
|
||||
border-right borderWidth solid borderColor
|
||||
border-bottom 0 solid borderColor
|
||||
.formError .formErrorArrow .line2
|
||||
width 3px
|
||||
border none
|
||||
background borderColor
|
||||
.formError .formErrorArrow .line1
|
||||
width 1px
|
||||
border none
|
||||
background borderColor
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
BIN
assets/img/post_notifications_template/ALC-widget-icon.png
Executable file
After Width: | Height: | Size: 3.9 KiB |
BIN
assets/img/post_notifications_template/settings-icon-highlight.png
Executable file
After Width: | Height: | Size: 7.2 KiB |
12
assets/img/preview_magnifying_glass.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="47.002px" height="38.003px" viewBox="0 0 47.002 38.003" enable-background="new 0 0 47.002 38.003" xml:space="preserve">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#565656" d="M46.328,36.365c-1.188,1.725-3.553,2.158-5.273,0.962L25.479,26.52
|
||||
c-1.104-0.763-1.674-2.007-1.631-3.257c-2.006,2.157-4.642,3.606-7.594,4.145c-3.626,0.663-7.288-0.13-10.311-2.227
|
||||
c-3.024-2.098-5.054-5.253-5.714-8.887c-0.661-3.636,0.127-7.31,2.221-10.344c4.325-6.264,12.927-7.834,19.177-3.5
|
||||
c5.672,3.938,7.486,11.412,4.537,17.443c1.152-0.486,2.519-0.392,3.627,0.377l15.58,10.808C47.09,32.274,47.52,34.641,46.328,36.365
|
||||
z M23.235,12.09c-0.459-2.534-1.874-4.734-3.982-6.196C14.897,2.87,8.896,3.963,5.878,8.331c-3.014,4.373-1.922,10.388,2.435,13.408
|
||||
c4.356,3.025,10.356,1.932,13.374-2.438C23.146,17.187,23.696,14.625,23.235,12.09z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/img/sample_template/coffee-grain.jpg
Normal file
After Width: | Height: | Size: 178 KiB |
BIN
assets/img/sample_template/header-v2.jpg
Normal file
After Width: | Height: | Size: 40 KiB |
BIN
assets/img/sample_template/map-v2.jpg
Normal file
After Width: | Height: | Size: 130 KiB |
BIN
assets/img/sample_template/sandwich.jpg
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
assets/img/welcome_template/logo-header.gif
Executable file
After Width: | Height: | Size: 4.8 KiB |
8
assets/js/lib/analytics.js
Normal file
@ -0,0 +1,8 @@
|
||||
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";c.people=c.people||[];c.toString=function(b){var a="mixpanel";"mixpanel"!==d&&(a+="."+d);b||(a+=" (stub)");return a};c.people.toString=function(){return c.toString(1)+".people (stub)"};i="disable time_event track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.union people.track_charge people.clear_charges people.delete_user".split(" ");
|
||||
for(g=0;g<i.length;g++)f(c,i[g]);b._i.push([a,e,d])};b.__SV=1.2;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src="undefined"!==typeof MIXPANEL_CUSTOM_LIB_URL?MIXPANEL_CUSTOM_LIB_URL:"file:"===e.location.protocol&&"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js".match(/^\/\//)?"https://cdn.mxpnl.com/libs/mixpanel-2-latest.min.js":"//cdn.mxpnl.com/libs/mixpanel-2-latest.min.js";f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f)}})(document,window.mixpanel||[]);
|
||||
|
||||
mixpanel.init("f683d388fb25fcf331f1b2b5c4449798");
|
||||
|
||||
if (typeof mailpoet_analytics_data === 'object') {
|
||||
mixpanel.track('Wysija Usage', mailpoet_analytics_data || {});
|
||||
}
|
4
assets/js/lib/prototype.min.js
vendored
Normal file
2
assets/js/lib/scriptaculous.min.js
vendored
Normal file
106
assets/js/src/cron.jsx
Normal file
@ -0,0 +1,106 @@
|
||||
define(
|
||||
[
|
||||
'react',
|
||||
'react-dom',
|
||||
'mailpoet'
|
||||
],
|
||||
function(
|
||||
React,
|
||||
ReactDOM,
|
||||
MailPoet
|
||||
) {
|
||||
var CronControl = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
status: 'loading'
|
||||
};
|
||||
},
|
||||
getCronData: function() {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'cron',
|
||||
action: 'getStatus'
|
||||
})
|
||||
.done(function(response) {
|
||||
jQuery('.button-primary')
|
||||
.removeClass('disabled');
|
||||
if(response.status !== undefined) {
|
||||
this.setState(response);
|
||||
} else {
|
||||
this.replaceState();
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
componentDidMount: function() {
|
||||
if(this.isMounted()) {
|
||||
this.getCronData();
|
||||
setInterval(this.getCronData, 5000);
|
||||
}
|
||||
},
|
||||
controlCron: function(action) {
|
||||
if(jQuery('.button-primary').hasClass('disabled')) {
|
||||
return;
|
||||
}
|
||||
jQuery('.button-primary')
|
||||
.addClass('disabled');
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'cron',
|
||||
action: action,
|
||||
})
|
||||
.done(function(response) {
|
||||
if(!response.result) {
|
||||
MailPoet.Notice.error(MailPoetI18n.daemonControlError);
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
render: function() {
|
||||
if(this.state.status === 'loading') {
|
||||
return(<div>Loading daemon status...</div>);
|
||||
}
|
||||
switch(this.state.status) {
|
||||
case 'started':
|
||||
return(
|
||||
<div>
|
||||
Cron daemon is running.
|
||||
<br/>
|
||||
<br/>
|
||||
It was started
|
||||
<strong> {this.state.timeSinceStart} </strong> and last executed
|
||||
<strong> {this.state.timeSinceUpdate} </strong> for a total of
|
||||
<strong> {this.state.counter} </strong> times (once every 30 seconds, unless it was interrupted and restarted).
|
||||
<br />
|
||||
<br />
|
||||
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'stop')}>Stop</a>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'starting':
|
||||
case 'stopping':
|
||||
return(
|
||||
<div>
|
||||
Daemon is {this.state.status}
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
case 'stopped':
|
||||
return(
|
||||
<div>
|
||||
Daemon is {this.state.status}
|
||||
<br />
|
||||
<br />
|
||||
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'start')}>Start</a>
|
||||
</div>
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const container = document.getElementById('cron_container');
|
||||
|
||||
if(container) {
|
||||
ReactDOM.render(
|
||||
<CronControl />,
|
||||
container
|
||||
);
|
||||
}
|
||||
});
|
@ -23,7 +23,9 @@ function(
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
onChange={ this.props.onValueChange }>
|
||||
onChange={ this.props.onValueChange }
|
||||
{...this.props.field.validation}
|
||||
>
|
||||
{options}
|
||||
</select>
|
||||
);
|
||||
|
@ -1,10 +1,12 @@
|
||||
define([
|
||||
'react',
|
||||
'react-dom',
|
||||
'jquery',
|
||||
'select2'
|
||||
],
|
||||
function(
|
||||
React,
|
||||
ReactDOM,
|
||||
jQuery
|
||||
) {
|
||||
var Selection = React.createClass({
|
||||
@ -16,87 +18,114 @@ function(
|
||||
},
|
||||
componentDidMount: function() {
|
||||
this.loadCachedItems();
|
||||
},
|
||||
componentDidUpdate: function() {
|
||||
this.setupSelect2();
|
||||
},
|
||||
componentDidUpdate: function(prevProps, prevState) {
|
||||
if(
|
||||
(this.props.item !== undefined && prevProps.item !== undefined)
|
||||
&& (this.props.item.id !== prevProps.item.id)
|
||||
) {
|
||||
jQuery('#'+this.refs.select.id)
|
||||
.val(this.props.item[this.props.field.name])
|
||||
.trigger('change');
|
||||
}
|
||||
},
|
||||
setupSelect2: function() {
|
||||
if(this.state.initialized === true) {
|
||||
if(
|
||||
!this.props.field.multiple
|
||||
|| this.state.initialized === true
|
||||
|| this.refs.select === undefined
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.props.field.select2 && Object.keys(this.props.item).length > 0) {
|
||||
var select2 = jQuery('#'+this.props.field.id).select2({
|
||||
width: (this.props.width || ''),
|
||||
templateResult: function(item) {
|
||||
if (item.element && item.element.selected) {
|
||||
return null;
|
||||
} else {
|
||||
return item.text;
|
||||
}
|
||||
var select2 = jQuery('#'+this.refs.select.id).select2({
|
||||
width: (this.props.width || ''),
|
||||
templateResult: function(item) {
|
||||
if(item.element && item.element.selected) {
|
||||
return null;
|
||||
} else {
|
||||
return item.text;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
select2.on('change', this.handleChange)
|
||||
var hasRemoved = false;
|
||||
select2.on('select2:unselecting', function(e) {
|
||||
hasRemoved = true;
|
||||
});
|
||||
select2.on('select2:opening', function(e) {
|
||||
if(hasRemoved === true) {
|
||||
hasRemoved = false;
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
|
||||
select2.select2(
|
||||
'val',
|
||||
this.props.item[this.props.field.name]
|
||||
);
|
||||
select2.on('change', this.handleChange);
|
||||
|
||||
this.setState({ initialized: true });
|
||||
}
|
||||
select2.select2(
|
||||
'val',
|
||||
this.props.item[this.props.field.name]
|
||||
);
|
||||
|
||||
this.setState({ initialized: true });
|
||||
},
|
||||
loadCachedItems: function() {
|
||||
if(typeof(window['mailpoet_'+this.props.field.endpoint]) !== 'undefined') {
|
||||
var items = window['mailpoet_'+this.props.field.endpoint];
|
||||
|
||||
if(this.props.field['filter'] !== undefined) {
|
||||
items = items.filter(this.props.field.filter);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
items: items
|
||||
});
|
||||
}
|
||||
},
|
||||
handleChange: function() {
|
||||
handleChange: function(e) {
|
||||
if(this.props.onValueChange !== undefined) {
|
||||
if(this.props.field.multiple) {
|
||||
value = jQuery('#'+this.refs.select.id).val();
|
||||
} else {
|
||||
value = e.target.value;
|
||||
}
|
||||
this.props.onValueChange({
|
||||
target: {
|
||||
value: jQuery('#'+this.props.field.id).select2('val'),
|
||||
value: value,
|
||||
name: this.props.field.name
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
},
|
||||
render: function() {
|
||||
if(this.state.items.length === 0) {
|
||||
return false;
|
||||
} else {
|
||||
var options = this.state.items.map(function(item, index) {
|
||||
return (
|
||||
<option
|
||||
key={ item.id }
|
||||
value={ item.id }
|
||||
>
|
||||
{ item.name }
|
||||
</option>
|
||||
);
|
||||
});
|
||||
|
||||
var default_value = (
|
||||
(this.props.item !== undefined && this.props.field.name !== undefined)
|
||||
? this.props.item[this.props.field.name]
|
||||
: null
|
||||
);
|
||||
|
||||
var options = this.state.items.map(function(item, index) {
|
||||
return (
|
||||
<select
|
||||
id={ this.props.field.id }
|
||||
placeholder={ this.props.field.placeholder }
|
||||
multiple={ this.props.field.multiple }
|
||||
onChange={ this.handleChange }
|
||||
defaultValue={ default_value }
|
||||
>{ options }</select>
|
||||
<option
|
||||
key={ item.id }
|
||||
value={ item.id }
|
||||
>
|
||||
{ item.name }
|
||||
</option>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
var default_value = (
|
||||
(this.props.item !== undefined && this.props.field.name !== undefined)
|
||||
? this.props.item[this.props.field.name]
|
||||
: null
|
||||
);
|
||||
|
||||
return (
|
||||
<select
|
||||
id={ this.props.field.id || this.props.field.name }
|
||||
ref="select"
|
||||
placeholder={ this.props.field.placeholder }
|
||||
multiple={ this.props.field.multiple }
|
||||
defaultValue={ default_value }
|
||||
{...this.props.field.validation}
|
||||
>{ options }</select>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -6,6 +6,8 @@ function(
|
||||
) {
|
||||
var FormFieldText = React.createClass({
|
||||
render: function() {
|
||||
var value = this.props.item[this.props.field.name];
|
||||
if(!value) { value = null; }
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
@ -17,10 +19,12 @@ function(
|
||||
}
|
||||
name={ this.props.field.name }
|
||||
id={ 'field_'+this.props.field.name }
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
value={ value }
|
||||
placeholder={ this.props.field.placeholder }
|
||||
defaultValue={ this.props.field.defaultValue }
|
||||
onChange={ this.props.onValueChange } />
|
||||
onChange={ this.props.onValueChange }
|
||||
{...this.props.field.validation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -15,7 +15,9 @@ function(
|
||||
value={ this.props.item[this.props.field.name] }
|
||||
placeholder={ this.props.field.placeholder }
|
||||
defaultValue={ this.props.field.defaultValue }
|
||||
onChange={ this.props.onValueChange } />
|
||||
onChange={ this.props.onValueChange }
|
||||
{...this.props.field.validation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
@ -68,27 +68,56 @@ define(
|
||||
handleSubmit: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// handle validation
|
||||
if(this.props.isValid !== undefined) {
|
||||
if(this.props.isValid() === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ loading: true });
|
||||
|
||||
// only get values from displayed fields
|
||||
var item = {};
|
||||
this.props.fields.map(function(field) {
|
||||
if(field['fields'] !== undefined) {
|
||||
field.fields.map(function(subfield) {
|
||||
item[subfield.name] = this.state.item[subfield.name];
|
||||
}.bind(this));
|
||||
} else {
|
||||
item[field.name] = this.state.item[field.name];
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
// set id if specified
|
||||
if(this.props.params.id !== undefined) {
|
||||
item.id = this.props.params.id;
|
||||
}
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'save',
|
||||
data: this.state.item
|
||||
data: item
|
||||
}).done(function(response) {
|
||||
this.setState({ loading: false });
|
||||
|
||||
if(response === true) {
|
||||
this.history.pushState(null, '/');
|
||||
if(this.props.params.id !== undefined) {
|
||||
this.props.messages['updated']();
|
||||
if(response.result === true) {
|
||||
if(this.props.onSuccess !== undefined) {
|
||||
this.props.onSuccess();
|
||||
} else {
|
||||
this.props.messages['created']();
|
||||
this.history.pushState(null, '/')
|
||||
}
|
||||
|
||||
if(this.props.params.id !== undefined) {
|
||||
this.props.messages.onUpdate();
|
||||
} else {
|
||||
this.props.messages.onCreate();
|
||||
}
|
||||
} else {
|
||||
if(response === false) {
|
||||
// unknown error occurred
|
||||
} else {
|
||||
this.setState({ errors: response });
|
||||
if(response.result === false) {
|
||||
if(response.errors.length > 0) {
|
||||
this.setState({ errors: response.errors });
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
@ -105,13 +134,15 @@ define(
|
||||
return true;
|
||||
},
|
||||
render: function() {
|
||||
var errors = this.state.errors.map(function(error, index) {
|
||||
return (
|
||||
<p key={ 'error-'+index } className="mailpoet_error">
|
||||
{ error }
|
||||
</p>
|
||||
);
|
||||
});
|
||||
if(this.state.errors !== undefined) {
|
||||
var errors = this.state.errors.map(function(error, index) {
|
||||
return (
|
||||
<p key={ 'error-'+index } className="mailpoet_error">
|
||||
{ error }
|
||||
</p>
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
var formClasses = classNames(
|
||||
'mailpoet_form',
|
||||
|
@ -1,993 +0,0 @@
|
||||
/*
|
||||
* name: MailPoet Form Editor
|
||||
* author: Jonathan Labreuille
|
||||
* company: Wysija
|
||||
* framework: prototype 1.7.2
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
Event.cacheDelegated = {};
|
||||
Object.extend(document, (function () {
|
||||
var cache = Event.cacheDelegated;
|
||||
|
||||
function getCacheForSelector(selector) {
|
||||
return cache[selector] = cache[selector] || {};
|
||||
}
|
||||
|
||||
function getWrappersForSelector(selector, eventName) {
|
||||
var c = getCacheForSelector(selector);
|
||||
return c[eventName] = c[eventName] || [];
|
||||
}
|
||||
|
||||
function findWrapper(selector, eventName, handler) {
|
||||
var c = getWrappersForSelector(selector, eventName);
|
||||
return c.find(function (wrapper) {
|
||||
return wrapper.handler === handler
|
||||
});
|
||||
}
|
||||
|
||||
function destroyWrapper(selector, eventName, handler) {
|
||||
var c = getCacheForSelector(selector);
|
||||
if (!c[eventName]) return false;
|
||||
var wrapper = findWrapper(selector, eventName, handler)
|
||||
c[eventName] = c[eventName].without(wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function createWrapper(selector, eventName, handler, context) {
|
||||
var wrapper, c = getWrappersForSelector(selector, eventName);
|
||||
if (c.pluck('handler').include(handler)) return false;
|
||||
wrapper = function (event) {
|
||||
var element = event.findElement(selector);
|
||||
if (element) handler.call(context || element, event, element);
|
||||
};
|
||||
wrapper.handler = handler;
|
||||
c.push(wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
return {
|
||||
delegate: function (selector, eventName, handler, context) {
|
||||
var wrapper = createWrapper.apply(null, arguments);
|
||||
if (wrapper) document.observe(eventName, wrapper);
|
||||
return document;
|
||||
},
|
||||
stopDelegating: function (selector, eventName, handler) {
|
||||
var length = arguments.length;
|
||||
switch (length) {
|
||||
case 2:
|
||||
getWrappersForSelector(selector, eventName).each(function (wrapper) {
|
||||
document.stopDelegating(selector, eventName, wrapper.handler);
|
||||
});
|
||||
break;
|
||||
case 1:
|
||||
Object.keys(getCacheForSelector(selector)).each(function (eventName) {
|
||||
document.stopDelegating(selector, eventName);
|
||||
});
|
||||
break;
|
||||
case 0:
|
||||
Object.keys(cache).each(function (selector) {
|
||||
document.stopDelegating(selector);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
var wrapper = destroyWrapper.apply(null, arguments);
|
||||
if (wrapper) document.stopObserving(eventName, wrapper);
|
||||
}
|
||||
return document;
|
||||
}
|
||||
}
|
||||
})());
|
||||
|
||||
var Observable = (function () {
|
||||
function getEventName(name, namespace) {
|
||||
name = name.substring(2);
|
||||
if (namespace) name = namespace + ':' + name;
|
||||
return name.underscore().split('_').join(':');
|
||||
}
|
||||
|
||||
function getHandlers(klass) {
|
||||
var proto = klass.prototype,
|
||||
namespace = proto.namespace;
|
||||
return Object.keys(proto).grep(/^on/).inject($H(), function (handlers, name) {
|
||||
if (name === 'onDomLoaded') return handlers;
|
||||
handlers.set(getEventName(name, namespace), getWrapper(proto[name], klass));
|
||||
return handlers;
|
||||
});
|
||||
}
|
||||
|
||||
function getWrapper(handler, klass) {
|
||||
return function (event) {
|
||||
return handler.call(new klass(this), event, event.memo);
|
||||
}
|
||||
}
|
||||
|
||||
function onDomLoad(selector, klass) {
|
||||
$$(selector).each(function (element) {
|
||||
new klass(element).onDomLoaded();
|
||||
});
|
||||
}
|
||||
return {
|
||||
observe: function (selector) {
|
||||
if (!this.handlers) this.handlers = {};
|
||||
if (this.handlers[selector]) return;
|
||||
var klass = this;
|
||||
if (this.prototype.onDomLoaded) document.loaded ? onDomLoad(selector, klass) : document.observe('dom:loaded', onDomLoad.curry(selector, klass));
|
||||
this.handlers[selector] = getHandlers(klass).each(function (handler) {
|
||||
document.delegate(selector, handler.key, handler.value);
|
||||
});
|
||||
},
|
||||
stopObserving: function (selector) {
|
||||
if (!this.handlers || !this.handlers[selector]) return;
|
||||
this.handlers[selector].each(function (handler) {
|
||||
document.stopDelegating(selector, handler.key, handler.value);
|
||||
});
|
||||
delete this.handlers[selector];
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
// override droppables
|
||||
Object.extend(Droppables, {
|
||||
deactivate: Droppables.deactivate.wrap(function (proceed, drop, draggable) {
|
||||
if (drop.onLeave) drop.onLeave(draggable, drop.element);
|
||||
return proceed(drop);
|
||||
}),
|
||||
activate: Droppables.activate.wrap(function (proceed, drop, draggable) {
|
||||
if (drop.onEnter) drop.onEnter(draggable, drop.element);
|
||||
return proceed(drop);
|
||||
}),
|
||||
show: function (point, element) {
|
||||
if (!this.drops.length) return;
|
||||
var drop, affected = [];
|
||||
this.drops.each(function (drop) {
|
||||
if (Droppables.isAffected(point, element, drop)) affected.push(drop);
|
||||
});
|
||||
if (affected.length > 0) drop = Droppables.findDeepestChild(affected);
|
||||
if (this.last_active && this.last_active !== drop) this.deactivate(this.last_active, element);
|
||||
if (drop) {
|
||||
Position.within(drop.element, point[0], point[1]);
|
||||
if (drop.onHover) drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
|
||||
if (drop !== this.last_active) Droppables.activate(drop, element);
|
||||
}
|
||||
},
|
||||
displayArea: function(draggable) {
|
||||
if(!this.drops.length) return;
|
||||
|
||||
// hide controls when displaying drop areas.
|
||||
WysijaForm.hideBlockControls();
|
||||
|
||||
this.drops.each(function (drop, iterator) {
|
||||
if(drop.element.hasClassName('block_placeholder')) {
|
||||
drop.element.addClassName('active');
|
||||
}
|
||||
});
|
||||
},
|
||||
hideArea: function() {
|
||||
if (!this.drops.length) return;
|
||||
this.drops.each(function (drop, iterator) {
|
||||
if(drop.element.hasClassName('block_placeholder')) {
|
||||
drop.element.removeClassName('active');
|
||||
} else if(drop.element.hasClassName('image_placeholder')) {
|
||||
drop.element.removeClassName('active');
|
||||
drop.element.up().removeClassName('active');
|
||||
} else if(drop.element.hasClassName('text_placeholder')) {
|
||||
drop.element.removeClassName('active');
|
||||
}
|
||||
});
|
||||
},
|
||||
reset: function (draggable) {
|
||||
if (this.last_active) this.deactivate(this.last_active, draggable);
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
Wysija History handling
|
||||
POTENTIAL FEATURES:
|
||||
- set a maximum number of items to be stored
|
||||
|
||||
*/
|
||||
var WysijaHistory = {
|
||||
container: 'mailpoet_form_history',
|
||||
size: 30,
|
||||
enqueue: function(element) {
|
||||
// create deep clone (includes child elements) of passed element
|
||||
var clone = element.clone(true);
|
||||
|
||||
// check if the field is unique
|
||||
if(parseInt(clone.readAttribute('wysija_unique'), 10) === 1) {
|
||||
// check if the field is already in the queue
|
||||
$(WysijaHistory.container).select('[wysija_field="'+clone.readAttribute('wysija_field')+'"]').invoke('remove');
|
||||
}
|
||||
|
||||
// check history size
|
||||
if($(WysijaHistory.container).select('> div').length >= WysijaHistory.size) {
|
||||
// remove oldest element (last in the list)
|
||||
$(WysijaHistory.container).select('> div').last().remove();
|
||||
}
|
||||
|
||||
// store block in history
|
||||
$(WysijaHistory.container).insert({ top: clone });
|
||||
},
|
||||
dequeue: function() {
|
||||
// pop last block off the history
|
||||
var block = $(WysijaHistory.container).select('div').first();
|
||||
|
||||
if(block !== undefined) {
|
||||
// insert block back into the editor
|
||||
$(WysijaForm.options.body).insert({top: block});
|
||||
}
|
||||
},
|
||||
clear: function() {
|
||||
$(WysijaHistory.container).innerHTML = '';
|
||||
},
|
||||
remove: function(field) {
|
||||
$(WysijaHistory.container).select('[wysija_field="'+field+'"]').invoke('remove');
|
||||
}
|
||||
};
|
||||
|
||||
/* MailPoet Form */
|
||||
var WysijaForm = {
|
||||
version: '0.6',
|
||||
options: {
|
||||
container: 'mailpoet_form_container',
|
||||
editor: 'mailpoet_form_editor',
|
||||
body: 'mailpoet_form_body',
|
||||
toolbar: 'mailpoet_form_toolbar',
|
||||
templates: 'wysija_widget_templates',
|
||||
debug: false
|
||||
},
|
||||
toolbar: {
|
||||
effect: null,
|
||||
x: null,
|
||||
y: null,
|
||||
top: null,
|
||||
left: null
|
||||
},
|
||||
scroll: {
|
||||
top: 0,
|
||||
left: 0
|
||||
},
|
||||
flags: {
|
||||
doSave: false
|
||||
},
|
||||
locks: {
|
||||
dragging: false,
|
||||
selectingColor: false,
|
||||
showingTools: false
|
||||
},
|
||||
encodeHtmlValue: function(str) {
|
||||
return str.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"');
|
||||
// ": fix for FileMerge because the previous line fucks up its syntax coloring
|
||||
},
|
||||
decodeHtmlValue: function(str) {
|
||||
return str.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"');
|
||||
// ": fix for FileMerge because the previous line fucks up its syntax coloring
|
||||
},
|
||||
loading: function(is_loading) {
|
||||
if(is_loading) {
|
||||
$(WysijaForm.options.editor).addClassName('loading');
|
||||
$(WysijaForm.options.toolbar).addClassName('loading');
|
||||
} else {
|
||||
$(WysijaForm.options.editor).removeClassName('loading');
|
||||
$(WysijaForm.options.toolbar).removeClassName('loading');
|
||||
}
|
||||
},
|
||||
loadStatic: function(blocks) {
|
||||
$A(blocks).each(function(block) {
|
||||
// create block
|
||||
WysijaForm.Block.create(block, $('block_placeholder'));
|
||||
});
|
||||
},
|
||||
load: function(form) {
|
||||
if(form.data === undefined) return;
|
||||
|
||||
// load body
|
||||
if(form.data.body !== undefined) {
|
||||
$A(form.data.body).each(function(block) {
|
||||
// create block
|
||||
WysijaForm.Block.create(block, $('block_placeholder'));
|
||||
});
|
||||
|
||||
// load settings
|
||||
var settings_elements = $('mailpoet_form_settings').getElements();
|
||||
settings_elements.each(function(setting) {
|
||||
// skip lists
|
||||
if(setting.name === 'lists') {
|
||||
return true;
|
||||
} else if(setting.name === 'on_success') {
|
||||
// if the input value is equal to the one stored in the settings
|
||||
if(setting.value === form.data.settings[setting.name]) {
|
||||
// check selected value
|
||||
$(setting).checked = true;
|
||||
}
|
||||
} else if(form.data.settings[setting.name] !== undefined) {
|
||||
if(typeof form.data.settings[setting.name] === 'string') {
|
||||
setting.setValue(WysijaForm.decodeHtmlValue(form.data.settings[setting.name]));
|
||||
} else {
|
||||
setting.setValue(form.data.settings[setting.name]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
save: function() {
|
||||
var position = 1,
|
||||
data = {
|
||||
'version': WysijaForm.version,
|
||||
'settings': $('mailpoet_form_settings').serialize(true),
|
||||
'body': [],
|
||||
'styles': (MailPoet.CodeEditor !== undefined) ? MailPoet.CodeEditor.getValue() : null
|
||||
};
|
||||
// body
|
||||
WysijaForm.getBlocks().each(function(b) {
|
||||
var block_data = (typeof(b.block['save']) === 'function') ? b.block.save() : null;
|
||||
|
||||
if(block_data !== null) {
|
||||
// set block position
|
||||
block_data['position'] = position;
|
||||
|
||||
// increment position
|
||||
position++;
|
||||
|
||||
// add block data to body
|
||||
data['body'].push(block_data);
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
},
|
||||
init: function() {
|
||||
// set document scroll
|
||||
info('init -> set scroll offsets');
|
||||
WysijaForm.setScrollOffsets();
|
||||
|
||||
// position toolbar
|
||||
info('init -> set toolbar position');
|
||||
WysijaForm.setToolbarPosition();
|
||||
|
||||
// enable droppable targets
|
||||
info('init -> make droppable');
|
||||
WysijaForm.makeDroppable();
|
||||
|
||||
// enable sortable
|
||||
info('init -> make sortable');
|
||||
WysijaForm.makeSortable();
|
||||
|
||||
// hide controls
|
||||
info('init -> hide controls');
|
||||
WysijaForm.hideControls();
|
||||
|
||||
// hide settings
|
||||
info('init -> hide settings');
|
||||
WysijaForm.hideSettings();
|
||||
|
||||
// set settings buttons position
|
||||
info('init -> init settings');
|
||||
WysijaForm.setSettingsPosition();
|
||||
|
||||
// toggle widgets
|
||||
info('init -> toggle widgets');
|
||||
WysijaForm.toggleWidgets();
|
||||
},
|
||||
getFieldData: function(element) {
|
||||
// get basic field data
|
||||
var data = {
|
||||
type: element.readAttribute('wysija_type'),
|
||||
field: element.readAttribute('wysija_field'),
|
||||
name: element.readAttribute('wysija_name'),
|
||||
unique: parseInt(element.readAttribute('wysija_unique') || 0, 10),
|
||||
static: parseInt(element.readAttribute('wysija_static') || 0, 10),
|
||||
element: element,
|
||||
params: ''
|
||||
};
|
||||
|
||||
// get params (may be empty)
|
||||
if(element.readAttribute('wysija_params') !== null && element.readAttribute('wysija_params').length > 0) {
|
||||
data.params = JSON.parse(element.readAttribute('wysija_params'));
|
||||
}
|
||||
return data;
|
||||
},
|
||||
toggleWidgets: function() {
|
||||
$$('a[wysija_unique="1"]').invoke('removeClassName', 'disabled');
|
||||
|
||||
// loop through each unique field already inserted in the editor and disable its toolbar equivalent
|
||||
$$('#'+WysijaForm.options.editor+' [wysija_unique="1"]').each(function(element) {
|
||||
var field = $$('#'+WysijaForm.options.toolbar+' [wysija_field="'+element.readAttribute('wysija_field')+'"]').first();
|
||||
if(field !== undefined) {
|
||||
field.addClassName('disabled');
|
||||
}
|
||||
});
|
||||
|
||||
// hide list selection if a list widget has been dragged into the editor
|
||||
$('mailpoet_settings_list_selection')[(($$('#'+WysijaForm.options.editor+' [wysija_field="list"]').length > 0) === true) ? 'hide': 'show']();
|
||||
},
|
||||
setBlockPositions: function(event, target) {
|
||||
// release dragging lock
|
||||
WysijaForm.locks.dragging = false;
|
||||
|
||||
var index = 1;
|
||||
WysijaForm.getBlocks().each(function (container) {
|
||||
container.setPosition(index++);
|
||||
// remove z-index value to avoid issues when resizing images
|
||||
if(container['block'] !== undefined) {
|
||||
container.block.element.setStyle({zIndex: ''});
|
||||
}
|
||||
});
|
||||
|
||||
if(target !== undefined) {
|
||||
// get placeholders (previous placeholder matches the placeholder linked to the next block)
|
||||
var block_placeholder = $(target.element.readAttribute('wysija_placeholder')),
|
||||
previous_placeholder = target.element.previous('.block_placeholder');
|
||||
|
||||
if(block_placeholder !== null) {
|
||||
// put block placeholder before the current block
|
||||
target.element.insert({before: block_placeholder});
|
||||
|
||||
// if the next block is a wysija_block, insert previous placeholder
|
||||
if(target.element.next() !== undefined && target.element.next().hasClassName('mailpoet_form_block') && previous_placeholder !== undefined) {
|
||||
target.element.insert({after: previous_placeholder});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
setScrollOffsets: function() {
|
||||
WysijaForm.scroll = document.viewport.getScrollOffsets();
|
||||
},
|
||||
hideSettings: function() {
|
||||
$(WysijaForm.options.container).select('.wysija_settings').invoke('hide');
|
||||
},
|
||||
setSettingsPosition: function() {
|
||||
// get viewport offsets and dimensions
|
||||
var viewportHeight = document.viewport.getHeight(),
|
||||
blockPadding = 5;
|
||||
|
||||
$(WysijaForm.options.container).select('.wysija_settings').each(function(element) {
|
||||
// get parent dimensions and position
|
||||
var parentDim = element.up('.mailpoet_form_block').getDimensions(),
|
||||
parentPos = element.up('.mailpoet_form_block').cumulativeOffset(),
|
||||
is_visible = (parentPos.top <= (WysijaForm.scroll.top + viewportHeight)) ? true : false,
|
||||
buttonMargin = 5,
|
||||
relativeTop = buttonMargin;
|
||||
|
||||
if(is_visible) {
|
||||
// desired position is set to center of viewport
|
||||
var absoluteTop = parseInt(WysijaForm.scroll.top + ((viewportHeight / 2) - (element.getHeight() / 2)), 10),
|
||||
parentTop = parseInt(parentPos.top - blockPadding, 10),
|
||||
parentBottom = parseInt(parentPos.top + parentDim.height - blockPadding, 10);
|
||||
|
||||
// always center
|
||||
relativeTop = parseInt((parentDim.height / 2) - (element.getHeight() / 2), 10);
|
||||
}
|
||||
// set position for button
|
||||
$(element).setStyle({
|
||||
left: parseInt((parentDim.width / 2) - (element.getWidth() / 2)) + 'px',
|
||||
top: relativeTop + 'px'
|
||||
});
|
||||
});
|
||||
},
|
||||
initToolbarPosition: function() {
|
||||
if(WysijaForm.toolbar.top === null) WysijaForm.toolbar.top = parseInt($(WysijaForm.options.container).positionedOffset().top);
|
||||
if(WysijaForm.toolbar.y === null) WysijaForm.toolbar.y = parseInt(WysijaForm.toolbar.top);
|
||||
|
||||
if(isRtl) {
|
||||
if(WysijaForm.toolbar.left === null) WysijaForm.toolbar.left = 0;
|
||||
} else {
|
||||
if(WysijaForm.toolbar.left === null) WysijaForm.toolbar.left = parseInt($(WysijaForm.options.container).positionedOffset().left);
|
||||
}
|
||||
if(WysijaForm.toolbar.x === null) WysijaForm.toolbar.x = parseInt(WysijaForm.toolbar.left + $(WysijaForm.options.container).getDimensions().width + 15);
|
||||
|
||||
},
|
||||
setToolbarPosition: function() {
|
||||
WysijaForm.initToolbarPosition();
|
||||
|
||||
var position = { top: WysijaForm.toolbar.y + 'px', visibility: 'visible' };
|
||||
|
||||
if(isRtl) {
|
||||
position.right = WysijaForm.toolbar.x + 'px';
|
||||
} else {
|
||||
position.left = WysijaForm.toolbar.x + 'px';
|
||||
}
|
||||
|
||||
$(WysijaForm.options.toolbar).setStyle(position);
|
||||
},
|
||||
updateToolbarPosition: function() {
|
||||
// init toolbar position (updates scroll and toolbar y)
|
||||
WysijaForm.initToolbarPosition();
|
||||
|
||||
// cancel previous effect
|
||||
if(WysijaForm.toolbar.effect !== null) WysijaForm.toolbar.effect.cancel();
|
||||
|
||||
if(WysijaForm.scroll.top >= (WysijaForm.toolbar.top - 20)) {
|
||||
WysijaForm.toolbar.y = parseInt(20 + WysijaForm.scroll.top);
|
||||
// start effect
|
||||
WysijaForm.toolbar.effect = new Effect.Move(WysijaForm.options.toolbar, {
|
||||
x: WysijaForm.toolbar.x,
|
||||
y: WysijaForm.toolbar.y,
|
||||
mode: 'absolute',
|
||||
duration: 0.2
|
||||
});
|
||||
} else {
|
||||
$(WysijaForm.options.toolbar).setStyle({
|
||||
left: WysijaForm.toolbar.x + 'px',
|
||||
top: WysijaForm.toolbar.top + 'px'
|
||||
});
|
||||
}
|
||||
},
|
||||
blockDropOptions: {
|
||||
accept: $w('mailpoet_form_field'), // acceptable items (classes array)
|
||||
onEnter: function (draggable, droppable) {
|
||||
$(droppable).addClassName('hover');
|
||||
},
|
||||
onLeave: function (draggable, droppable) {
|
||||
$(droppable).removeClassName('hover');
|
||||
},
|
||||
onDrop: function (draggable, droppable) {
|
||||
// custom data for images
|
||||
droppable.fire('wjfe:item:drop', WysijaForm.getFieldData(draggable));
|
||||
$(droppable).removeClassName('hover');
|
||||
}
|
||||
},
|
||||
hideControls: function() {
|
||||
try {
|
||||
return WysijaForm.getBlocks().invoke('hideControls');
|
||||
} catch(e) { return; }
|
||||
},
|
||||
hideTools: function() {
|
||||
$$('.wysija_tools').invoke('hide');
|
||||
WysijaForm.locks.showingTools = false;
|
||||
},
|
||||
instances: {},
|
||||
get: function (element, type) {
|
||||
if(type === undefined) type = 'block';
|
||||
// identify element
|
||||
var id = element.identify();
|
||||
var instance = WysijaForm.instances[id] || new WysijaForm[type.capitalize().camelize()](id);
|
||||
|
||||
WysijaForm.instances[id] = instance;
|
||||
return instance;
|
||||
},
|
||||
makeDroppable: function() {
|
||||
Droppables.add('block_placeholder', WysijaForm.blockDropOptions);
|
||||
},
|
||||
makeSortable: function () {
|
||||
var body = $(WysijaForm.options.body);
|
||||
Sortable.create(body, {
|
||||
tag: 'div',
|
||||
only: 'mailpoet_form_block',
|
||||
scroll: window,
|
||||
handle: 'handle',
|
||||
constraint: 'vertical'
|
||||
|
||||
});
|
||||
Draggables.removeObserver(body);
|
||||
Draggables.addObserver({
|
||||
element: body,
|
||||
onStart: WysijaForm.startBlockPositions,
|
||||
onEnd: WysijaForm.setBlockPositions
|
||||
});
|
||||
},
|
||||
hideBlockControls: function() {
|
||||
$$('.wysija_controls').invoke('hide');
|
||||
this.getBlockElements().invoke('removeClassName', 'hover');
|
||||
},
|
||||
getBlocks: function () {
|
||||
return WysijaForm.getBlockElements().map(function (element) {
|
||||
return WysijaForm.get(element);
|
||||
});
|
||||
},
|
||||
getBlockElements: function () {
|
||||
return $(WysijaForm.options.container).select('.mailpoet_form_block');
|
||||
},
|
||||
startBlockPositions: function(event, target) {
|
||||
if(target.element.hasClassName('mailpoet_form_block')) {
|
||||
// store block placeholder id for the block that is being repositionned
|
||||
if(target.element.previous('.block_placeholder') !== undefined) {
|
||||
target.element.writeAttribute('wysija_placeholder', target.element.previous('.block_placeholder').identify());
|
||||
}
|
||||
}
|
||||
WysijaForm.locks.dragging = true;
|
||||
},
|
||||
encodeURIComponent: function(str) {
|
||||
// check if it's a url and if so, prevent encoding of protocol
|
||||
var regexp = new RegExp(/^http[s]?:\/\//),
|
||||
protocol = regexp.exec(str);
|
||||
|
||||
if(protocol === null) {
|
||||
// this is not a url so encode the whole thing
|
||||
return encodeURIComponent(str).replace(/[!'()*]/g, escape);
|
||||
} else if(protocol.length === 1) {
|
||||
// this is a url, so do not encode the protocol
|
||||
return encodeURI(str).replace(/[!'()*]/g, escape);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
WysijaForm.DraggableItem = Class.create({
|
||||
initialize: function (element) {
|
||||
this.elementType = $(element).readAttribute('wysija_type');
|
||||
this.element = $(element).down() || $(element);
|
||||
this.clone = this.cloneElement();
|
||||
this.insert();
|
||||
},
|
||||
STYLES: new Template('position: absolute; top: #{top}px; left: #{left}px;'),
|
||||
cloneElement: function () {
|
||||
var clone = this.element.clone(),
|
||||
offset = this.element.cumulativeOffset(),
|
||||
list = this.getList(),
|
||||
styles = this.STYLES.evaluate({
|
||||
top: offset.top - list.scrollTop,
|
||||
left: offset.left - list.scrollLeft
|
||||
});
|
||||
clone.setStyle(styles);
|
||||
|
||||
clone.addClassName('mailpoet_form_widget');
|
||||
clone.addClassName(this.elementType);
|
||||
clone.innerHTML = this.element.innerHTML;
|
||||
return clone;
|
||||
},
|
||||
getOffset: function () {
|
||||
return this.element.offsetTop - this.getList().scrollTop;
|
||||
},
|
||||
getList: function () {
|
||||
return this.element.up('ul');
|
||||
},
|
||||
insert: function () {
|
||||
$$("body")[0].insert(this.clone);
|
||||
},
|
||||
onMousedown: function (event) {
|
||||
var draggable = new Draggable(this.clone, {
|
||||
scroll: window,
|
||||
onStart: function () {
|
||||
Droppables.displayArea(draggable);
|
||||
},
|
||||
onEnd: function (drag) {
|
||||
drag.destroy();
|
||||
drag.element.remove();
|
||||
Droppables.hideArea();
|
||||
},
|
||||
starteffect: function (element) {
|
||||
new Effect.Opacity(element, {
|
||||
duration: 0.2,
|
||||
from: element.getOpacity(),
|
||||
to: 0.7
|
||||
});
|
||||
},
|
||||
endeffect: Prototype.emptyFunction
|
||||
});
|
||||
draggable.initDrag(event);
|
||||
draggable.startDrag(event);
|
||||
return draggable;
|
||||
}
|
||||
});
|
||||
Object.extend(WysijaForm.DraggableItem, Observable).observe('a[class="mailpoet_form_field"]');
|
||||
|
||||
|
||||
WysijaForm.Block = Class.create({
|
||||
/* Invoked on load */
|
||||
initialize: function(element) {
|
||||
info('block -> init');
|
||||
|
||||
this.element = $(element);
|
||||
this.block = new WysijaForm.Widget(this.element);
|
||||
|
||||
// enable block placeholder
|
||||
this.block.makeBlockDroppable();
|
||||
|
||||
// setup events
|
||||
if(this.block['setup'] !== undefined) {
|
||||
this.block.setup();
|
||||
}
|
||||
return this;
|
||||
},
|
||||
setPosition: function(position) {
|
||||
this.element.writeAttribute('wysija_position', position);
|
||||
},
|
||||
hideControls: function() {
|
||||
if(this['getControls']) {
|
||||
this.element.removeClassName('hover');
|
||||
this.getControls().hide();
|
||||
}
|
||||
},
|
||||
showControls: function() {
|
||||
if(this['getControls']) {
|
||||
this.element.addClassName('hover');
|
||||
try {
|
||||
this.getControls().show();
|
||||
} catch(e) {
|
||||
;
|
||||
}
|
||||
}
|
||||
},
|
||||
makeBlockDroppable: function() {
|
||||
if(this.isBlockDroppableEnabled() === false) {
|
||||
var block_placeholder = this.getBlockDroppable();
|
||||
Droppables.add(block_placeholder.identify(), WysijaForm.blockDropOptions);
|
||||
block_placeholder.addClassName('enabled');
|
||||
}
|
||||
},
|
||||
removeBlockDroppable: function() {
|
||||
if(this.isBlockDroppableEnabled()) {
|
||||
var block_placeholder = this.getBlockDroppable();
|
||||
Droppables.remove(block_placeholder.identify());
|
||||
block_placeholder.removeClassName('enabled');
|
||||
}
|
||||
},
|
||||
isBlockDroppableEnabled: function() {
|
||||
// if the block_placeholder does not exist, create it
|
||||
var block_placeholder = this.getBlockDroppable();
|
||||
if(block_placeholder === null) {
|
||||
return this.createBlockDroppable().hasClassName('enabled');
|
||||
} else {
|
||||
return block_placeholder.hasClassName('enabled');
|
||||
}
|
||||
},
|
||||
createBlockDroppable: function() {
|
||||
info('block -> createBlockDroppable');
|
||||
this.element.insert({before: '<div class=\"block_placeholder\">'+$('block_placeholder').innerHTML+'</div>'});
|
||||
return this.element.previous('.block_placeholder');
|
||||
},
|
||||
getBlockDroppable: function() {
|
||||
if(this.element.previous() === undefined || this.element.previous().hasClassName('block_placeholder') === false) {
|
||||
return null;
|
||||
} else {
|
||||
return this.element.previous();
|
||||
}
|
||||
},
|
||||
getControls: function() {
|
||||
return this.element.down('.wysija_controls');
|
||||
},
|
||||
setupControls: function() {
|
||||
// enable controls
|
||||
this.controls = this.getControls();
|
||||
|
||||
if(this.controls) {
|
||||
// setup events for block controls
|
||||
this.element.observe('mouseover', function() {
|
||||
// special cases where controls shouldn't be displayed
|
||||
if(WysijaForm.locks.dragging === true || WysijaForm.locks.selectingColor === true || WysijaForm.locks.showingTools === true) return;
|
||||
|
||||
// set block flag
|
||||
this.element.addClassName('hover');
|
||||
|
||||
// show controls
|
||||
this.showControls();
|
||||
|
||||
// show settings if present
|
||||
if(this.element.down('.wysija_settings') !== undefined) {
|
||||
this.element.down('.wysija_settings').show();
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this.element.observe('mouseout', function() {
|
||||
// special cases where controls shouldn't hide
|
||||
if(WysijaForm.locks.dragging === true || WysijaForm.locks.selectingColor === true) return;
|
||||
|
||||
// hide controls
|
||||
this.hideControls();
|
||||
|
||||
// hide settings if present
|
||||
if(this.element.down('.wysija_settings') !== undefined) {
|
||||
this.element.down('.wysija_settings').hide();
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
|
||||
// setup click event for remove button
|
||||
this.removeButton = this.controls.down('.remove') || null;
|
||||
if(this.removeButton !== null) {
|
||||
this.removeButton.observe('click', function() {
|
||||
this.removeBlock();
|
||||
this.removeButton.stopObserving('click');
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
// setup click event for settings button
|
||||
this.settingsButton = this.element.down('.settings') || null;
|
||||
|
||||
if(this.settingsButton !== null) {
|
||||
this.settingsButton.observe('click', function(event) {
|
||||
// TODO: refactor
|
||||
var block = $(event.target).up('.mailpoet_form_block') || null;
|
||||
if(block !== null) {
|
||||
var field = WysijaForm.getFieldData(block);
|
||||
this.editSettings();
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
removeBlock: function(callback) {
|
||||
info('block -> removeBlock');
|
||||
|
||||
// save block in history
|
||||
WysijaHistory.enqueue(this.element);
|
||||
|
||||
Effect.Fade(this.element.identify(), {
|
||||
duration: 0.2,
|
||||
afterFinish: function(effect) {
|
||||
if(effect.element.next('.mailpoet_form_block') !== undefined && callback !== false) {
|
||||
// show controls of next block to allow mass delete
|
||||
WysijaForm.get(effect.element.next('.mailpoet_form_block')).block.showControls();
|
||||
}
|
||||
// remove placeholder
|
||||
if(effect.element.previous('.block_placeholder') !== undefined) {
|
||||
effect.element.previous('.block_placeholder').remove();
|
||||
}
|
||||
|
||||
// remove element from the DOM
|
||||
this.element.remove();
|
||||
|
||||
// reset block positions
|
||||
WysijaForm.setBlockPositions();
|
||||
|
||||
// toggle widgets
|
||||
WysijaForm.toggleWidgets();
|
||||
|
||||
// optional callback execution after completely removing block
|
||||
if(callback !== undefined && typeof(callback) === 'function') {
|
||||
callback();
|
||||
}
|
||||
|
||||
// remove block instance
|
||||
delete WysijaForm.instances[this.element.identify()];
|
||||
}.bind(this)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* Invoked on item dropped */
|
||||
WysijaForm.Block.create = function(block, target) {
|
||||
if($('form_template_'+block.type) === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var body = $(WysijaForm.options.body),
|
||||
block_template = Handlebars.compile($('form_template_block').innerHTML),
|
||||
template = Handlebars.compile($('form_template_'+block.type).innerHTML),
|
||||
output = '';
|
||||
|
||||
// set block template (depending on the block type)
|
||||
block.template = template(block);
|
||||
output = block_template(block);
|
||||
|
||||
// check if the new block is unique and if there's already an instance
|
||||
// of it in the history. If so, remove its former instance from the history
|
||||
if(block.unique === 1) {
|
||||
WysijaHistory.remove(block.field);
|
||||
}
|
||||
|
||||
// if the drop target was the bottom placeholder
|
||||
if(target.identify() === 'block_placeholder') {
|
||||
// insert block at the bottom
|
||||
body.insert(output);
|
||||
//block = body.childElements().last();
|
||||
} else {
|
||||
// insert block before the drop target
|
||||
target.insert({before: output });
|
||||
//block = target.previous('.mailpoet_form_block');
|
||||
}
|
||||
// refresh sortable items
|
||||
WysijaForm.makeSortable();
|
||||
|
||||
// refresh block positions
|
||||
WysijaForm.setBlockPositions();
|
||||
|
||||
// position settings
|
||||
WysijaForm.setSettingsPosition();
|
||||
};
|
||||
|
||||
document.observe('wjfe:item:drop', function(event) {
|
||||
info('create block');
|
||||
WysijaForm.Block.create(event.memo, event.target);
|
||||
|
||||
// hide block controls
|
||||
info('hide controls');
|
||||
WysijaForm.hideBlockControls();
|
||||
|
||||
// toggle widgets
|
||||
setTimeout(function() {
|
||||
WysijaForm.toggleWidgets();
|
||||
}, 1);
|
||||
});
|
||||
|
||||
/* Form Widget */
|
||||
WysijaForm.Widget = Class.create(WysijaForm.Block, {
|
||||
initialize: function(element) {
|
||||
info('widget -> init');
|
||||
this.element = $(element);
|
||||
return this;
|
||||
},
|
||||
setup: function() {
|
||||
info('widget -> setup');
|
||||
this.setupControls();
|
||||
},
|
||||
save: function() {
|
||||
info('widget -> save');
|
||||
var data = this.getData();
|
||||
|
||||
if(data.element !== undefined) {
|
||||
delete data.element;
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
setData: function(data) {
|
||||
var current_data = this.getData(),
|
||||
params = $H(current_data.params).merge(data.params).toObject();
|
||||
|
||||
// update type if it changed
|
||||
if(data.type !== undefined && data.type !== current_data.type) {
|
||||
this.element.writeAttribute('wysija_type', data.type);
|
||||
}
|
||||
|
||||
// update params
|
||||
this.element.writeAttribute('wysija_params', JSON.stringify(params));
|
||||
},
|
||||
getData: function() {
|
||||
var data = WysijaForm.getFieldData(this.element);
|
||||
|
||||
// decode params
|
||||
if(data.params.length > 0) {
|
||||
data.params = JSON.parse(data.params);
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
getControls: function() {
|
||||
return this.element.down('.wysija_controls');
|
||||
},
|
||||
remove: function() {
|
||||
this.removeBlock();
|
||||
},
|
||||
redraw: function(data) {
|
||||
// set parameters
|
||||
this.setData(data);
|
||||
var options = this.getData();
|
||||
// redraw block
|
||||
var block_template = Handlebars.compile($('form_template_block').innerHTML),
|
||||
template = Handlebars.compile($('form_template_'+options.type).innerHTML),
|
||||
data = $H(options).merge({ template: template(options) }).toObject();
|
||||
this.element.replace(block_template(data));
|
||||
|
||||
WysijaForm.init();
|
||||
},
|
||||
editSettings: function() {
|
||||
MailPoet.Modal.popup({
|
||||
title: 'Edit field settings', // TODO: translate!
|
||||
template: jQuery('#form_template_field_settings').html(),
|
||||
data: this.getData(),
|
||||
onSuccess: function() {
|
||||
var data = jQuery('#form_field_settings').serializeObject();
|
||||
this.redraw(data);
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
getSettings: function() {
|
||||
return this.element.down('.wysija_settings');
|
||||
}
|
||||
});
|
||||
|
||||
/* When dom is loaded, initialize WysijaForm */
|
||||
document.observe('dom:loaded', WysijaForm.init);
|
||||
|
||||
/* LOGGING */
|
||||
function info(value) {
|
||||
if(WysijaForm.options.debug === false) return;
|
||||
|
||||
if(!(window.console && console.log)) {
|
||||
(function() {
|
||||
var noop = function() {};
|
||||
var methods = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'markTimeline', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn'];
|
||||
var length = methods.length;
|
||||
var console = window.console = {};
|
||||
while(length--) {
|
||||
console[methods[length]] = noop;
|
||||
}
|
||||
}());
|
||||
}
|
||||
try {
|
||||
console.log('[DEBUG] '+value);
|
||||
} catch(e) {}
|
||||
}
|
1027
assets/js/src/form_editor/form_editor.js
Normal file
26
assets/js/src/forms/forms.jsx
Normal file
@ -0,0 +1,26 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Router, Route, IndexRoute } from 'react-router'
|
||||
import FormList from 'forms/list.jsx'
|
||||
import createHashHistory from 'history/lib/createHashHistory'
|
||||
|
||||
let history = createHashHistory({ queryKey: false })
|
||||
|
||||
const App = React.createClass({
|
||||
render() {
|
||||
return this.props.children
|
||||
}
|
||||
});
|
||||
|
||||
let container = document.getElementById('forms_container');
|
||||
|
||||
if(container) {
|
||||
ReactDOM.render((
|
||||
<Router history={ history }>
|
||||
<Route path="/" component={ App }>
|
||||
<IndexRoute component={ FormList } />
|
||||
<Route path="*" component={ FormList } />
|
||||
</Route>
|
||||
</Router>
|
||||
), container);
|
||||
}
|
191
assets/js/src/forms/list.jsx
Normal file
@ -0,0 +1,191 @@
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { Router, Link } from 'react-router'
|
||||
import Listing from 'listing/listing.jsx'
|
||||
import classNames from 'classnames'
|
||||
import MailPoet from 'mailpoet'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'segments',
|
||||
label: 'Lists',
|
||||
sortable: false
|
||||
},
|
||||
{
|
||||
name: 'created_at',
|
||||
label: 'Created on',
|
||||
sortable: true
|
||||
}
|
||||
];
|
||||
|
||||
const messages = {
|
||||
onTrash: function(response) {
|
||||
if(response) {
|
||||
let message = null;
|
||||
if(~~response === 1) {
|
||||
message = (
|
||||
'1 form was moved to the trash.'
|
||||
);
|
||||
} else if(~~response > 1) {
|
||||
message = (
|
||||
'%$1d forms were moved to the trash.'
|
||||
).replace('%$1d', ~~response);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
}
|
||||
},
|
||||
onDelete: function(response) {
|
||||
if(response) {
|
||||
let message = null;
|
||||
if(~~response === 1) {
|
||||
message = (
|
||||
'1 form was permanently deleted.'
|
||||
);
|
||||
} else if(~~response > 1) {
|
||||
message = (
|
||||
'%$1d forms were permanently deleted.'
|
||||
).replace('%$1d', ~~response);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
}
|
||||
},
|
||||
onRestore: function(response) {
|
||||
if(response) {
|
||||
let message = null;
|
||||
if(~~response === 1) {
|
||||
message = (
|
||||
'1 form has been restored from the trash.'
|
||||
);
|
||||
} else if(~~response > 1) {
|
||||
message = (
|
||||
'%$1d forms have been restored from the trash.'
|
||||
).replace('%$1d', ~~response);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const item_actions = [
|
||||
{
|
||||
name: 'edit',
|
||||
label: 'Edit',
|
||||
link: function(item) {
|
||||
return (
|
||||
<a href={ `admin.php?page=mailpoet-form-editor&id=${item.id}` }>Edit</a>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'duplicate_form',
|
||||
label: 'Duplicate',
|
||||
onClick: function(item, refresh) {
|
||||
return MailPoet.Ajax.post({
|
||||
endpoint: 'forms',
|
||||
action: 'duplicate',
|
||||
data: item.id
|
||||
}).done(function(response) {
|
||||
MailPoet.Notice.success(
|
||||
('Form "%$1s" has been duplicated.').replace('%$1s', response.name)
|
||||
);
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'trash'
|
||||
}
|
||||
];
|
||||
|
||||
const bulk_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: 'Trash',
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
|
||||
const FormList = React.createClass({
|
||||
createForm() {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'forms',
|
||||
action: 'create'
|
||||
}).done(function(response) {
|
||||
if(response !== false) {
|
||||
window.location = response;
|
||||
}
|
||||
});
|
||||
},
|
||||
renderItem(form, actions) {
|
||||
let row_classes = classNames(
|
||||
'manage-column',
|
||||
'column-primary',
|
||||
'has-row-actions'
|
||||
);
|
||||
|
||||
let segments = mailpoet_segments.filter(function(segment) {
|
||||
return (jQuery.inArray(segment.id, form.segments) !== -1);
|
||||
}).map(function(segment) {
|
||||
return segment.name;
|
||||
}).join(', ');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<td className={ row_classes }>
|
||||
<strong>
|
||||
<a>{ form.name }</a>
|
||||
</strong>
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column-format" data-colname="Lists">
|
||||
{ segments }
|
||||
</td>
|
||||
<td className="column-date" data-colname="Created on">
|
||||
<abbr>{ form.created_at }</abbr>
|
||||
</td>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
Forms <a
|
||||
className="add-new-h2"
|
||||
href="javascript:;"
|
||||
onClick={ this.createForm }
|
||||
>New</a>
|
||||
</h2>
|
||||
|
||||
<Listing
|
||||
location={ this.props.location }
|
||||
params={ this.props.params }
|
||||
messages={ messages }
|
||||
search={ false }
|
||||
limit={ 1000 }
|
||||
endpoint="forms"
|
||||
onRenderItem={ this.renderItem }
|
||||
columns={ columns }
|
||||
bulk_actions={ bulk_actions }
|
||||
item_actions={ item_actions }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = FormList;
|
3
assets/js/src/helpscout.js
Normal file
@ -0,0 +1,3 @@
|
||||
define([], function() {
|
||||
!function(e,o,n){window.HSCW=o,window.HS=n,n.beacon=n.beacon||{};var t=n.beacon;t.userConfig={},t.readyQueue=[],t.config=function(e){this.userConfig=e},t.ready=function(e){this.readyQueue.push(e)},o.config={docs:{enabled:!1,baseUrl:""},contact:{enabled:!0,formId:"e5c408c7-895e-11e5-9e75-0a7d6919297d"}};var r=e.getElementsByTagName("script")[0],c=e.createElement("script");c.type="text/javascript",c.async=!0,c.src="https://djtflbt20bdde.cloudfront.net/",r.parentNode.insertBefore(c,r)}(document,window.HSCW||{},window.HS||{});
|
||||
});
|
@ -15,16 +15,16 @@ function(
|
||||
this.setState({
|
||||
action: e.target.value,
|
||||
extra: false
|
||||
});
|
||||
}, function() {
|
||||
var action = this.getSelectedAction();
|
||||
|
||||
var action = this.getSelectedAction();
|
||||
|
||||
// action on select callback
|
||||
if(action !== null && action['onSelect'] !== undefined) {
|
||||
this.setState({
|
||||
extra: action.onSelect(e)
|
||||
});
|
||||
}
|
||||
// action on select callback
|
||||
if(action !== null && action['onSelect'] !== undefined) {
|
||||
this.setState({
|
||||
extra: action.onSelect(e)
|
||||
});
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
handleApplyAction: function(e) {
|
||||
e.preventDefault();
|
||||
@ -45,12 +45,13 @@ function(
|
||||
|
||||
data.action = this.state.action;
|
||||
|
||||
var callback = function() {};
|
||||
if(action['onSuccess'] !== undefined) {
|
||||
data.onSuccess = action.onSuccess;
|
||||
callback = action.onSuccess;
|
||||
}
|
||||
|
||||
if(data.action) {
|
||||
this.props.onBulkAction(selected_ids, data);
|
||||
this.props.onBulkAction(selected_ids, data, callback);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
@ -84,7 +85,12 @@ function(
|
||||
Select bulk action
|
||||
</label>
|
||||
|
||||
<select ref="action" value={ this.state.action } onChange={this.handleChangeAction}>
|
||||
<select
|
||||
name="bulk_actions"
|
||||
ref="action"
|
||||
value={ this.state.action }
|
||||
onChange={this.handleChangeAction}
|
||||
>
|
||||
<option value="">Bulk Actions</option>
|
||||
{ this.props.bulk_actions.map(function(action, index) {
|
||||
return (
|
||||
|
@ -1,57 +1,66 @@
|
||||
define([
|
||||
'react'
|
||||
'react',
|
||||
'jquery'
|
||||
],
|
||||
function(
|
||||
React
|
||||
React,
|
||||
jQuery
|
||||
) {
|
||||
var ListingFilters = React.createClass({
|
||||
handleFilterAction: function() {
|
||||
var filters = this.props.filters.map(function(filter, index) {
|
||||
var value = this.refs['filter-'+index].value;
|
||||
if(value) {
|
||||
return {
|
||||
'name': filter.name,
|
||||
'value': value
|
||||
};
|
||||
}
|
||||
}.bind(this));
|
||||
let filters = {}
|
||||
this.getAvailableFilters().map((filter, i) => {
|
||||
filters[this.refs['filter-'+i].name] = this.refs['filter-'+i].value
|
||||
})
|
||||
return this.props.onSelectFilter(filters);
|
||||
},
|
||||
handleChangeAction: function() {
|
||||
return true;
|
||||
getAvailableFilters: function() {
|
||||
let filters = this.props.filters;
|
||||
|
||||
return Object.keys(filters).filter(function(filter) {
|
||||
return !(
|
||||
filters[filter].length === 0
|
||||
|| (
|
||||
filters[filter].length === 1
|
||||
&& !filters[filter][0].value
|
||||
)
|
||||
);
|
||||
})
|
||||
},
|
||||
render: function() {
|
||||
var filters = this.props.filters
|
||||
.filter(function(filter) {
|
||||
return !(
|
||||
filter.options.length === 0
|
||||
|| (
|
||||
filter.options.length === 1
|
||||
&& !filter.options[0].value
|
||||
)
|
||||
);
|
||||
})
|
||||
const filters = this.props.filters;
|
||||
const selected_filters = this.props.filter;
|
||||
|
||||
const available_filters = this.getAvailableFilters()
|
||||
.map(function(filter, i) {
|
||||
let default_value = false;
|
||||
if(selected_filters[filter] !== undefined && selected_filters[filter]) {
|
||||
default_value = selected_filters[filter]
|
||||
} else {
|
||||
jQuery(`select[name="${filter}"]`).val('');
|
||||
}
|
||||
return (
|
||||
<select
|
||||
ref={ 'filter-'+i }
|
||||
key={ 'filter-'+i }
|
||||
onChange={ this.handleChangeAction }>
|
||||
{ filter.options.map(function(option, j) {
|
||||
return (
|
||||
<option
|
||||
value={ option.value }
|
||||
key={ 'filter-option-' + j }
|
||||
>{ option.label }</option>
|
||||
);
|
||||
}.bind(this)) }
|
||||
ref={ `filter-${i}` }
|
||||
key={ `filter-${i}` }
|
||||
name={ filter }
|
||||
defaultValue={ default_value }
|
||||
>
|
||||
{ filters[filter].map(function(option, j) {
|
||||
return (
|
||||
<option
|
||||
value={ option.value }
|
||||
key={ 'filter-option-' + j }
|
||||
>{ option.label }</option>
|
||||
);
|
||||
}.bind(this)) }
|
||||
</select>
|
||||
);
|
||||
}.bind(this));
|
||||
|
||||
var button = false;
|
||||
let button = false;
|
||||
|
||||
if(filters.length > 0) {
|
||||
if(available_filters.length > 0) {
|
||||
button = (
|
||||
<input
|
||||
onClick={ this.handleFilterAction }
|
||||
@ -63,7 +72,7 @@ function(
|
||||
|
||||
return (
|
||||
<div className="alignleft actions actions">
|
||||
{ filters }
|
||||
{ available_filters }
|
||||
{ button }
|
||||
</div>
|
||||
);
|
||||
|
@ -9,6 +9,9 @@ define(['react', 'classnames'], function(React, classNames) {
|
||||
render: function() {
|
||||
var columns = this.props.columns.map(function(column, index) {
|
||||
column.is_primary = (index === 0);
|
||||
column.sorted = (this.props.sort_by === column.name)
|
||||
? this.props.sort_order
|
||||
: 'desc';
|
||||
return (
|
||||
<ListingColumn
|
||||
onSort={this.props.onSort}
|
||||
@ -29,6 +32,7 @@ define(['react', 'classnames'], function(React, classNames) {
|
||||
</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
name="select_all"
|
||||
ref="toggle"
|
||||
checked={ this.props.selection }
|
||||
onChange={ this.handleSelectItems } />
|
||||
|
@ -34,11 +34,9 @@ define(
|
||||
};
|
||||
},
|
||||
handleSelectItem: function(e) {
|
||||
var is_checked = jQuery(e.target).is(':checked');
|
||||
|
||||
this.props.onSelectItem(
|
||||
parseInt(e.target.value, 10),
|
||||
is_checked
|
||||
e.target.checked
|
||||
);
|
||||
|
||||
return !e.target.checked;
|
||||
@ -46,8 +44,11 @@ define(
|
||||
handleRestoreItem: function(id) {
|
||||
this.props.onRestoreItem(id);
|
||||
},
|
||||
handleDeleteItem: function(id, confirm = false) {
|
||||
this.props.onDeleteItem(id, confirm);
|
||||
handleTrashItem: function(id) {
|
||||
this.props.onTrashItem(id);
|
||||
},
|
||||
handleDeleteItem: function(id) {
|
||||
this.props.onDeleteItem(id);
|
||||
},
|
||||
handleToggleItem: function(id) {
|
||||
this.setState({ toggled: !this.state.toggled });
|
||||
@ -58,11 +59,12 @@ define(
|
||||
if(this.props.is_selectable === true) {
|
||||
checkbox = (
|
||||
<th className="check-column" scope="row">
|
||||
<label className="screen-reader-text">
|
||||
{ 'Select ' + this.props.item.email }</label>
|
||||
<label className="screen-reader-text">{
|
||||
'Select ' + this.props.item[this.props.columns[0].name]
|
||||
}</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
defaultValue={ this.props.item.id }
|
||||
value={ this.props.item.id }
|
||||
checked={
|
||||
this.props.item.selected || this.props.selection === 'all'
|
||||
}
|
||||
@ -77,12 +79,59 @@ define(
|
||||
|
||||
if(custom_actions.length > 0) {
|
||||
item_actions = custom_actions.map(function(action, index) {
|
||||
return (
|
||||
<span key={ 'action-'+index } className={ action.name }>
|
||||
{ action.link(this.props.item.id) }
|
||||
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
|
||||
</span>
|
||||
);
|
||||
if(action.display !== undefined) {
|
||||
if(action.display(this.props.item) === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(action.name === 'trash') {
|
||||
return (
|
||||
<span key={ 'action-'+index } className="trash">
|
||||
{(index > 0) ? ' | ' : ''}
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={ this.handleTrashItem.bind(
|
||||
null,
|
||||
this.props.item.id
|
||||
) }>
|
||||
Trash
|
||||
</a>
|
||||
</span>
|
||||
);
|
||||
} else if(action.refresh) {
|
||||
return (
|
||||
<span
|
||||
onClick={ this.props.onRefreshItems }
|
||||
key={ 'action-'+index } className={ action.name }>
|
||||
{(index > 0) ? ' | ' : ''}
|
||||
{ action.link(this.props.item) }
|
||||
</span>
|
||||
);
|
||||
} else if(action.link) {
|
||||
return (
|
||||
<span
|
||||
key={ 'action-'+index } className={ action.name }>
|
||||
{(index > 0) ? ' | ' : ''}
|
||||
{ action.link(this.props.item) }
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<span
|
||||
key={ 'action-'+index } className={ action.name }>
|
||||
{(index > 0) ? ' | ' : ''}
|
||||
<a href="javascript:;" onClick={
|
||||
(action.onClick !== undefined)
|
||||
? action.onClick.bind(null,
|
||||
this.props.item,
|
||||
this.props.onRefreshItems
|
||||
)
|
||||
: false
|
||||
}>{ action.label }</a>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}.bind(this));
|
||||
} else {
|
||||
item_actions = (
|
||||
@ -112,8 +161,7 @@ define(
|
||||
href="javascript:;"
|
||||
onClick={ this.handleDeleteItem.bind(
|
||||
null,
|
||||
this.props.item.id,
|
||||
true
|
||||
this.props.item.id
|
||||
)}
|
||||
>Delete permanently</a>
|
||||
</span>
|
||||
@ -130,18 +178,6 @@ define(
|
||||
<div>
|
||||
<div className="row-actions">
|
||||
{ item_actions }
|
||||
{ ' | ' }
|
||||
<span className="trash">
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={ this.handleDeleteItem.bind(
|
||||
null,
|
||||
this.props.item.id,
|
||||
false
|
||||
) }>
|
||||
Trash
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
|
||||
@ -222,7 +258,7 @@ define(
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{this.props.items.map(function(item) {
|
||||
{this.props.items.map(function(item, index) {
|
||||
item.id = parseInt(item.id, 10);
|
||||
item.selected = (this.props.selected_ids.indexOf(item.id) !== -1);
|
||||
|
||||
@ -233,11 +269,13 @@ define(
|
||||
onRenderItem={ this.props.onRenderItem }
|
||||
onDeleteItem={ this.props.onDeleteItem }
|
||||
onRestoreItem={ this.props.onRestoreItem }
|
||||
onTrashItem={ this.props.onTrashItem }
|
||||
onRefreshItems={ this.props.onRefreshItems }
|
||||
selection={ this.props.selection }
|
||||
is_selectable={ this.props.is_selectable }
|
||||
item_actions={ this.props.item_actions }
|
||||
group={ this.props.group }
|
||||
key={ 'item-' + item.id }
|
||||
key={ `item-${item.id}-${index}` }
|
||||
item={ item } />
|
||||
);
|
||||
}.bind(this))}
|
||||
@ -248,6 +286,9 @@ define(
|
||||
});
|
||||
|
||||
var Listing = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
getInitialState: function() {
|
||||
return {
|
||||
loading: false,
|
||||
@ -260,43 +301,148 @@ define(
|
||||
items: [],
|
||||
groups: [],
|
||||
group: 'all',
|
||||
filters: [],
|
||||
filter: [],
|
||||
filters: {},
|
||||
filter: {},
|
||||
selected_ids: [],
|
||||
selection: false
|
||||
};
|
||||
},
|
||||
componentDidUpdate: function(prevProps, prevState) {
|
||||
// reset group to "all" if trash gets emptied
|
||||
if(
|
||||
// we were viewing the trash
|
||||
(prevState.group === 'trash' && prevState.count > 0)
|
||||
&&
|
||||
// we are still viewing the trash but there are no items left
|
||||
(this.state.group === 'trash' && this.state.count === 0)
|
||||
&&
|
||||
// only do this when no filter is set
|
||||
(Object.keys(this.state.filter).length === 0)
|
||||
) {
|
||||
this.handleGroup('all');
|
||||
}
|
||||
},
|
||||
getParam: function(param) {
|
||||
var regex = /(.*)\[(.*)\]/
|
||||
var matches = regex.exec(param)
|
||||
return [matches[1], matches[2]]
|
||||
},
|
||||
initWithParams: function(params) {
|
||||
let state = this.state || {}
|
||||
let original_state = state
|
||||
// check for url params
|
||||
if(params.splat !== undefined) {
|
||||
params.splat.split('/').map(param => {
|
||||
let [key, value] = this.getParam(param);
|
||||
switch(key) {
|
||||
case 'filter':
|
||||
let filters = {}
|
||||
value.split('&').map(function(pair) {
|
||||
let [k, v] = pair.split('=')
|
||||
filters[k] = v
|
||||
}
|
||||
)
|
||||
|
||||
state.filter = filters
|
||||
break;
|
||||
default:
|
||||
state[key] = value
|
||||
}
|
||||
})
|
||||
}
|
||||
if(this.props.limit !== undefined) {
|
||||
state.limit = Math.abs(~~this.props.limit);
|
||||
}
|
||||
this.setState(state, function() {
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
},
|
||||
setParams: function() {
|
||||
var params = Object.keys(this.state)
|
||||
.filter(key => {
|
||||
return (
|
||||
[
|
||||
'group',
|
||||
'filter',
|
||||
'search',
|
||||
'page',
|
||||
'sort_by',
|
||||
'sort_order'
|
||||
].indexOf(key) !== -1
|
||||
)
|
||||
})
|
||||
.map(key => {
|
||||
let value = this.state[key]
|
||||
if(value === Object(value)) {
|
||||
value = jQuery.param(value)
|
||||
} else if(value === Boolean(value)) {
|
||||
value = value.toString()
|
||||
}
|
||||
|
||||
if(value !== '') {
|
||||
return `${key}[${value}]`
|
||||
}
|
||||
})
|
||||
.filter(key => { return (key !== undefined) })
|
||||
.join('/');
|
||||
params = '/'+params
|
||||
|
||||
if(this.props.location) {
|
||||
if(this.props.location.pathname !== params) {
|
||||
this.history.pushState(null, `${params}`)
|
||||
}
|
||||
}
|
||||
},
|
||||
componentDidMount: function() {
|
||||
this.getItems();
|
||||
if(this.isMounted()) {
|
||||
const params = this.props.params || {}
|
||||
this.initWithParams(params)
|
||||
|
||||
if(this.props.auto_refresh) {
|
||||
jQuery(document).on('heartbeat-tick.mailpoet', function(e, data) {
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
},
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
const params = nextProps.params || {}
|
||||
this.initWithParams(params)
|
||||
},
|
||||
getItems: function() {
|
||||
this.setState({ loading: true });
|
||||
if(this.isMounted()) {
|
||||
this.setState({ loading: true });
|
||||
|
||||
this.clearSelection();
|
||||
this.clearSelection();
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'listing',
|
||||
data: {
|
||||
offset: (this.state.page - 1) * this.state.limit,
|
||||
limit: this.state.limit,
|
||||
group: this.state.group,
|
||||
filter: this.state.filter,
|
||||
search: this.state.search,
|
||||
sort_by: this.state.sort_by,
|
||||
sort_order: this.state.sort_order
|
||||
}
|
||||
}).done(function(response) {
|
||||
if(this.isMounted()) {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'listing',
|
||||
data: {
|
||||
offset: (this.state.page - 1) * this.state.limit,
|
||||
limit: this.state.limit,
|
||||
group: this.state.group,
|
||||
filter: this.state.filter,
|
||||
search: this.state.search,
|
||||
sort_by: this.state.sort_by,
|
||||
sort_order: this.state.sort_order
|
||||
}
|
||||
}).done(function(response) {
|
||||
this.setState({
|
||||
items: response.items || [],
|
||||
filters: response.filters || [],
|
||||
filters: response.filters || {},
|
||||
groups: response.groups || [],
|
||||
count: response.count || 0,
|
||||
loading: false
|
||||
});
|
||||
}
|
||||
}.bind(this));
|
||||
}, function() {
|
||||
if(this.props['onGetItems'] !== undefined) {
|
||||
this.props.onGetItems(
|
||||
~~(this.state.groups[0]['count'])
|
||||
);
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
handleRestoreItem: function(id) {
|
||||
this.setState({
|
||||
@ -318,7 +464,27 @@ define(
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
},
|
||||
handleDeleteItem: function(id, confirm = false) {
|
||||
handleTrashItem: function(id) {
|
||||
this.setState({
|
||||
loading: true,
|
||||
page: 1
|
||||
});
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'trash',
|
||||
data: id
|
||||
}).done(function(response) {
|
||||
if(
|
||||
this.props.messages !== undefined
|
||||
&& this.props.messages['onTrash'] !== undefined
|
||||
) {
|
||||
this.props.messages.onTrash(response);
|
||||
}
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
},
|
||||
handleDeleteItem: function(id) {
|
||||
this.setState({
|
||||
loading: true,
|
||||
page: 1
|
||||
@ -327,31 +493,18 @@ define(
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'delete',
|
||||
data: {
|
||||
id: id,
|
||||
confirm: confirm
|
||||
}
|
||||
data: id
|
||||
}).done(function(response) {
|
||||
if(confirm === true) {
|
||||
if(
|
||||
this.props.messages !== undefined
|
||||
&& this.props.messages['onConfirmDelete'] !== undefined
|
||||
) {
|
||||
this.props.messages.onConfirmDelete(response);
|
||||
}
|
||||
} else {
|
||||
if(
|
||||
this.props.messages !== undefined
|
||||
&& this.props.messages['onDelete'] !== undefined
|
||||
) {
|
||||
this.props.messages.onDelete(response);
|
||||
}
|
||||
if(
|
||||
this.props.messages !== undefined
|
||||
&& this.props.messages['onDelete'] !== undefined
|
||||
) {
|
||||
this.props.messages.onDelete(response);
|
||||
}
|
||||
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
},
|
||||
handleBulkAction: function(selected_ids, params) {
|
||||
handleBulkAction: function(selected_ids, params, callback) {
|
||||
if(
|
||||
this.state.selection === false
|
||||
&& this.state.selected_ids.length === 0
|
||||
@ -362,12 +515,6 @@ define(
|
||||
this.setState({ loading: true });
|
||||
|
||||
var data = params || {};
|
||||
var callback = ((data['onSuccess'] !== undefined)
|
||||
? data['onSuccess']
|
||||
: function() {}
|
||||
);
|
||||
delete data.onSuccess;
|
||||
|
||||
data.listing = {
|
||||
offset: 0,
|
||||
limit: 0,
|
||||
@ -379,7 +526,7 @@ define(
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'bulk_action',
|
||||
action: 'bulkAction',
|
||||
data: data
|
||||
}).done(function(response) {
|
||||
this.getItems();
|
||||
@ -393,6 +540,7 @@ define(
|
||||
selection: false,
|
||||
selected_ids: []
|
||||
}, function() {
|
||||
this.setParams();
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
},
|
||||
@ -401,6 +549,7 @@ define(
|
||||
sort_by: sort_by,
|
||||
sort_order: sort_order,
|
||||
}, function() {
|
||||
this.setParams();
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
},
|
||||
@ -460,6 +609,7 @@ define(
|
||||
filter: filters,
|
||||
page: 1
|
||||
}, function() {
|
||||
this.setParams();
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
},
|
||||
@ -469,10 +619,11 @@ define(
|
||||
|
||||
this.setState({
|
||||
group: group,
|
||||
filter: [],
|
||||
filter: {},
|
||||
search: '',
|
||||
page: 1
|
||||
}, function() {
|
||||
this.setParams();
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
},
|
||||
@ -482,6 +633,7 @@ define(
|
||||
selection: false,
|
||||
selected_ids: []
|
||||
}, function() {
|
||||
this.setParams();
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
},
|
||||
@ -489,21 +641,18 @@ define(
|
||||
var render = this.props.onRenderItem(item, actions);
|
||||
return render.props.children;
|
||||
},
|
||||
handleRefreshItems: function() {
|
||||
this.getItems();
|
||||
},
|
||||
render: function() {
|
||||
var items = this.state.items,
|
||||
sort_by = this.state.sort_by,
|
||||
sort_order = this.state.sort_order;
|
||||
|
||||
// set sortable columns
|
||||
columns = this.props.columns.map(function(column) {
|
||||
column.sorted = (column.name === sort_by) ? sort_order : false;
|
||||
return column;
|
||||
});
|
||||
|
||||
// bulk actions
|
||||
var bulk_actions = this.props.bulk_actions || [];
|
||||
|
||||
if(this.state.group === 'trash') {
|
||||
if(this.state.group === 'trash' && bulk_actions.length > 0) {
|
||||
bulk_actions = [
|
||||
{
|
||||
name: 'restore',
|
||||
@ -511,12 +660,9 @@ define(
|
||||
onSuccess: this.props.messages.onRestore
|
||||
},
|
||||
{
|
||||
name: 'trash',
|
||||
name: 'delete',
|
||||
label: 'Delete permanently',
|
||||
onSuccess: this.props.messages.onConfirmDelete,
|
||||
getData: function() {
|
||||
return { confirm: true };
|
||||
}
|
||||
onSuccess: this.props.messages.onDelete
|
||||
}
|
||||
];
|
||||
}
|
||||
@ -524,22 +670,42 @@ define(
|
||||
// item actions
|
||||
var item_actions = this.props.item_actions || [];
|
||||
|
||||
var tableClasses = classNames(
|
||||
var table_classes = classNames(
|
||||
'mailpoet_listing_table',
|
||||
'wp-list-table',
|
||||
'widefat',
|
||||
'fixed',
|
||||
'striped',
|
||||
{ 'mailpoet_listing_loading': this.state.loading }
|
||||
);
|
||||
|
||||
// search
|
||||
var search = (
|
||||
<ListingSearch
|
||||
onSearch={ this.handleSearch }
|
||||
search={ this.state.search }
|
||||
/>
|
||||
);
|
||||
if(this.props.search === false) {
|
||||
search = false;
|
||||
}
|
||||
|
||||
// groups
|
||||
var groups = (
|
||||
<ListingGroups
|
||||
groups={ this.state.groups }
|
||||
group={ this.state.group }
|
||||
onSelectGroup={ this.handleGroup }
|
||||
/>
|
||||
);
|
||||
if(this.props.groups === false) {
|
||||
groups = false;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ListingGroups
|
||||
groups={ this.state.groups }
|
||||
group={ this.state.group }
|
||||
onSelectGroup={ this.handleGroup } />
|
||||
<ListingSearch
|
||||
onSearch={ this.handleSearch }
|
||||
search={ this.state.search } />
|
||||
{ groups }
|
||||
{ search }
|
||||
<div className="tablenav top clearfix">
|
||||
<ListingBulkActions
|
||||
bulk_actions={ bulk_actions }
|
||||
@ -548,7 +714,7 @@ define(
|
||||
onBulkAction={ this.handleBulkAction } />
|
||||
<ListingFilters
|
||||
filters={ this.state.filters }
|
||||
filter={ this.state.filter }
|
||||
filter={ this.state.filter }
|
||||
onSelectFilter={ this.handleFilter } />
|
||||
<ListingPages
|
||||
count={ this.state.count }
|
||||
@ -556,7 +722,7 @@ define(
|
||||
limit={ this.state.limit }
|
||||
onSetPage={ this.handleSetPage } />
|
||||
</div>
|
||||
<table className={ tableClasses }>
|
||||
<table className={ table_classes }>
|
||||
<thead>
|
||||
<ListingHeader
|
||||
onSort={ this.handleSort }
|
||||
@ -572,6 +738,8 @@ define(
|
||||
onRenderItem={ this.handleRenderItem }
|
||||
onDeleteItem={ this.handleDeleteItem }
|
||||
onRestoreItem={ this.handleRestoreItem }
|
||||
onTrashItem={ this.handleTrashItem }
|
||||
onRefreshItems={ this.handleRefreshItems }
|
||||
columns={ this.props.columns }
|
||||
is_selectable={ bulk_actions.length > 0 }
|
||||
onSelectItem={ this.handleSelectItem }
|
||||
|
@ -40,23 +40,23 @@ define(['react', 'classnames'], function(React, classNames) {
|
||||
},
|
||||
render: function() {
|
||||
if(this.props.count === 0) {
|
||||
return (<div></div>);
|
||||
return false;
|
||||
} else {
|
||||
var pagination,
|
||||
firstPage = (
|
||||
var pagination = false;
|
||||
var firstPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">«</span>
|
||||
),
|
||||
previousPage = (
|
||||
);
|
||||
var previousPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">‹</span>
|
||||
),
|
||||
nextPage = (
|
||||
);
|
||||
var nextPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">›</span>
|
||||
),
|
||||
lastPage = (
|
||||
);
|
||||
var lastPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">»</span>
|
||||
);
|
||||
|
||||
if(this.props.count > this.props.limit) {
|
||||
if(this.props.limit > 0 && this.props.count > this.props.limit) {
|
||||
if(this.props.page > 1) {
|
||||
previousPage = (
|
||||
<a href="javascript:;"
|
||||
@ -104,6 +104,7 @@ define(['react', 'classnames'], function(React, classNames) {
|
||||
pagination = (
|
||||
<span className="pagination-links">
|
||||
{firstPage}
|
||||
|
||||
{previousPage}
|
||||
|
||||
<span className="paging-input">
|
||||
@ -128,6 +129,7 @@ define(['react', 'classnames'], function(React, classNames) {
|
||||
</span>
|
||||
|
||||
{nextPage}
|
||||
|
||||
{lastPage}
|
||||
</span>
|
||||
);
|
||||
@ -140,7 +142,7 @@ define(['react', 'classnames'], function(React, classNames) {
|
||||
|
||||
return (
|
||||
<div className={ classes }>
|
||||
<span className="displaying-num">{ this.props.count } item(s)</span>
|
||||
<span className="displaying-num">{ this.props.count } items</span>
|
||||
{ pagination }
|
||||
</div>
|
||||
);
|
||||
|
@ -7,26 +7,33 @@ define(['react'], function(React) {
|
||||
this.refs.search.value
|
||||
);
|
||||
},
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
this.refs.search.value = nextProps.search
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<form name="search" onSubmit={this.handleSearch}>
|
||||
<p className="search-box">
|
||||
<label htmlFor="search_input" className="screen-reader-text">
|
||||
Search
|
||||
</label>
|
||||
<input
|
||||
type="search"
|
||||
id="search_input"
|
||||
ref="search"
|
||||
name="s"
|
||||
defaultValue={this.props.search} />
|
||||
<input
|
||||
type="submit"
|
||||
defaultValue={MailPoetI18n.searchLabel}
|
||||
className="button" />
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
if(this.props.search === false) {
|
||||
return false;
|
||||
} else {
|
||||
return (
|
||||
<form name="search" onSubmit={this.handleSearch}>
|
||||
<p className="search-box">
|
||||
<label htmlFor="search_input" className="screen-reader-text">
|
||||
Search
|
||||
</label>
|
||||
<input
|
||||
type="search"
|
||||
id="search_input"
|
||||
ref="search"
|
||||
name="s"
|
||||
defaultValue={this.props.search} />
|
||||
<input
|
||||
type="submit"
|
||||
defaultValue={MailPoetI18n.searchLabel}
|
||||
className="button" />
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -47,7 +47,6 @@ define([
|
||||
autoScroll: true,
|
||||
|
||||
onstart: function(event) {
|
||||
console.log('Drag start', event, this);
|
||||
|
||||
if (that.options.cloneOriginal === true) {
|
||||
// Use substitution instead of a clone
|
||||
|
@ -11,10 +11,10 @@ define([
|
||||
'newsletter_editor/blocks/base',
|
||||
'newsletter_editor/blocks/button',
|
||||
'newsletter_editor/blocks/divider',
|
||||
'newsletter_editor/components/wordpress',
|
||||
'newsletter_editor/components/communication',
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function(App, BaseBlock, ButtonBlock, DividerBlock, WordpressComponent, _, jQuery) {
|
||||
], function(App, BaseBlock, ButtonBlock, DividerBlock, CommunicationComponent, _, jQuery) {
|
||||
|
||||
"use strict";
|
||||
|
||||
@ -69,9 +69,9 @@ define([
|
||||
},
|
||||
fetchPosts: function() {
|
||||
var that = this;
|
||||
WordpressComponent.getTransformedPosts(this.toJSON()).done(function(content) {
|
||||
console.log('ALC fetched', arguments);
|
||||
CommunicationComponent.getTransformedPosts(this.toJSON()).done(function(content) {
|
||||
that.get('_container').get('blocks').reset(content, {parse: true});
|
||||
that.trigger('postsChanged');
|
||||
}).fail(function(error) {
|
||||
console.log('ALC fetchPosts error', arguments);
|
||||
});
|
||||
@ -100,6 +100,11 @@ define([
|
||||
toolsRegion: '.mailpoet_tools',
|
||||
postsRegion: '.mailpoet_automated_latest_content_block_posts',
|
||||
},
|
||||
modelEvents: _.extend(
|
||||
_.omit(base.BlockView.prototype.modelEvents, 'change'),
|
||||
{
|
||||
'postsChanged': 'render',
|
||||
}),
|
||||
events: _.extend(base.BlockView.prototype.events, {
|
||||
'click .mailpoet_automated_latest_content_block_overlay': 'showSettings',
|
||||
}),
|
||||
@ -161,95 +166,76 @@ define([
|
||||
var that = this;
|
||||
|
||||
// Dynamically update available post types
|
||||
WordpressComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
|
||||
CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
|
||||
|
||||
this.$('.mailpoet_automated_latest_content_categories_and_tags').select2({
|
||||
multiple: true,
|
||||
allowClear: true,
|
||||
query: function(options) {
|
||||
var taxonomies = [];
|
||||
// Delegate data loading to our own endpoints
|
||||
WordpressComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
|
||||
taxonomies = tax;
|
||||
// Fetch available terms based on the list of taxonomies already fetched
|
||||
var promise = WordpressComponent.getTerms({
|
||||
search: options.term,
|
||||
taxonomies: _.keys(taxonomies)
|
||||
}).then(function(terms) {
|
||||
return {
|
||||
taxonomies: taxonomies,
|
||||
terms: terms,
|
||||
};
|
||||
ajax: {
|
||||
data: function (params) {
|
||||
return {
|
||||
term: params.term
|
||||
};
|
||||
},
|
||||
transport: function(options, success, failure) {
|
||||
var taxonomies,
|
||||
promise = CommunicationComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
|
||||
taxonomies = tax;
|
||||
// Fetch available terms based on the list of taxonomies already fetched
|
||||
var promise = CommunicationComponent.getTerms({
|
||||
search: options.data.term,
|
||||
taxonomies: _.keys(taxonomies)
|
||||
}).then(function(terms) {
|
||||
return {
|
||||
taxonomies: taxonomies,
|
||||
terms: terms,
|
||||
};
|
||||
});
|
||||
return promise;
|
||||
});
|
||||
|
||||
promise.then(success);
|
||||
promise.fail(failure);
|
||||
return promise;
|
||||
}).done(function(args) {
|
||||
},
|
||||
processResults: function(data) {
|
||||
// Transform taxonomies and terms into select2 compatible format
|
||||
options.callback({
|
||||
return {
|
||||
results: _.map(
|
||||
args.terms,
|
||||
data.terms,
|
||||
function(item) {
|
||||
return _.defaults({
|
||||
text: args.taxonomies[item.taxonomy].labels.singular_name + ': ' + item.name,
|
||||
text: data.taxonomies[item.taxonomy].labels.singular_name + ': ' + item.name,
|
||||
id: item.term_id
|
||||
}, item);
|
||||
}
|
||||
)
|
||||
});
|
||||
});
|
||||
};
|
||||
},
|
||||
},
|
||||
initSelection: function(element, callback) {
|
||||
// On external data load tell select2 which terms to preselect
|
||||
|
||||
callback(_.map(
|
||||
that.model.get('terms').toJSON(),
|
||||
function(item) {
|
||||
return {
|
||||
id: item.id,
|
||||
text: item.text,
|
||||
};
|
||||
}
|
||||
));
|
||||
}).on({
|
||||
'select2:select': function(event) {
|
||||
var terms = that.model.get('terms');
|
||||
terms.add(event.params.data);
|
||||
// Reset whole model in order for change events to propagate properly
|
||||
that.model.set('terms', terms.toJSON());
|
||||
},
|
||||
}).trigger( 'change' ).on({
|
||||
'change': function(e){
|
||||
var data = jQuery(this).data('selected');
|
||||
|
||||
if (typeof data === 'string') {
|
||||
if (data === '') {
|
||||
data = [];
|
||||
} else {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
}
|
||||
|
||||
if ( e.added ){
|
||||
data.push(e.added);
|
||||
} else {
|
||||
data = _.filter(data, function(item) {
|
||||
return item.id !== e.removed.id;
|
||||
});
|
||||
}
|
||||
|
||||
// Update ALC model
|
||||
that.model.set('terms', data);
|
||||
|
||||
jQuery(this).data('selected', JSON.stringify(data));
|
||||
}
|
||||
});
|
||||
},
|
||||
onBeforeDestroy: function() {
|
||||
base.BlockSettingsView.prototype.onBeforeDestroy.apply(this, arguments);
|
||||
// Force close select2 if it hasn't closed yet
|
||||
this.$('.mailpoet_automated_latest_content_categories_and_tags').select2('close');
|
||||
'select2:unselect': function(event) {
|
||||
var terms = that.model.get('terms');
|
||||
terms.remove(event.params.data);
|
||||
// Reset whole model in order for change events to propagate properly
|
||||
that.model.set('terms', terms.toJSON());
|
||||
},
|
||||
}).trigger( 'change' );
|
||||
},
|
||||
toggleDisplayOptions: function(event) {
|
||||
var el = this.$('.mailpoet_automated_latest_content_display_options'),
|
||||
showControl = this.$('.mailpoet_automated_latest_content_show_display_options');
|
||||
if (el.hasClass('mailpoet_hidden')) {
|
||||
el.removeClass('mailpoet_hidden');
|
||||
if (el.hasClass('mailpoet_closed')) {
|
||||
el.removeClass('mailpoet_closed');
|
||||
showControl.addClass('mailpoet_hidden');
|
||||
} else {
|
||||
el.addClass('mailpoet_hidden');
|
||||
el.addClass('mailpoet_closed');
|
||||
showControl.removeClass('mailpoet_hidden');
|
||||
}
|
||||
},
|
||||
@ -325,7 +311,7 @@ define([
|
||||
_.each(postTypes, function(type) {
|
||||
select.append(jQuery('<option>', {
|
||||
value: type.name,
|
||||
text: type.labels.singular_name,
|
||||
text: type.label,
|
||||
}));
|
||||
});
|
||||
select.val(selectedValue);
|
||||
|
@ -48,6 +48,7 @@ define([
|
||||
},
|
||||
modelEvents: {
|
||||
'change': 'render',
|
||||
'delete': 'deleteBlock',
|
||||
},
|
||||
events: {
|
||||
"mouseenter": "showTools",
|
||||
@ -88,7 +89,9 @@ define([
|
||||
this.$el.addClass('mailpoet_editor_view_' + this.cid);
|
||||
},
|
||||
initialize: function() {
|
||||
this.on('showSettings', this.showSettings);
|
||||
this.on('showSettings', this.showSettings, this);
|
||||
this.on('dom:refresh', this.showBlock, this);
|
||||
this._isFirstRender = true;
|
||||
},
|
||||
showTools: function(_event) {
|
||||
if (!this.showingToolsDisabled) {
|
||||
@ -114,12 +117,49 @@ define([
|
||||
* Defines drop behavior of BlockView instance
|
||||
*/
|
||||
getDropFunc: function() {
|
||||
var that = this;
|
||||
return function() {
|
||||
var newModel = that.model.clone();
|
||||
//that.model.destroy();
|
||||
return newModel;
|
||||
};
|
||||
return this.model.clone();
|
||||
}.bind(this);
|
||||
},
|
||||
showBlock: function() {
|
||||
if (this._isFirstRender) {
|
||||
this.transitionIn();
|
||||
this._isFirstRender = false;
|
||||
}
|
||||
},
|
||||
deleteBlock: function() {
|
||||
this.transitionOut().then(function() {
|
||||
this.model.destroy();
|
||||
}.bind(this));
|
||||
},
|
||||
transitionIn: function() {
|
||||
return this._transition('slideDown', 'fadeIn', 'easeIn');
|
||||
},
|
||||
transitionOut: function() {
|
||||
return this._transition('slideUp', 'fadeOut', 'easeOut');
|
||||
},
|
||||
_transition: function(slideDirection, fadeDirection, easing) {
|
||||
var promise = jQuery.Deferred();
|
||||
|
||||
this.$el.velocity(
|
||||
slideDirection,
|
||||
{
|
||||
duration: 250,
|
||||
easing: easing,
|
||||
complete: function() {
|
||||
promise.resolve();
|
||||
}.bind(this),
|
||||
}
|
||||
).velocity(
|
||||
fadeDirection,
|
||||
{
|
||||
duration: 250,
|
||||
easing: easing,
|
||||
queue: false, // Do not enqueue, trigger animation in parallel
|
||||
}
|
||||
);
|
||||
|
||||
return promise;
|
||||
},
|
||||
});
|
||||
|
||||
@ -168,24 +208,22 @@ define([
|
||||
},
|
||||
deleteBlock: function(event) {
|
||||
event.preventDefault();
|
||||
this.model.destroy();
|
||||
this.model.trigger('delete');
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
Module.BlockSettingsView = Marionette.LayoutView.extend({
|
||||
className: 'mailpoet_editor_settings',
|
||||
initialize: function() {
|
||||
var that = this;
|
||||
|
||||
MailPoet.Modal.panel({
|
||||
element: this.$el,
|
||||
template: '',
|
||||
position: 'right',
|
||||
width: App.getConfig().get('sidepanelWidth'),
|
||||
onCancel: function() {
|
||||
that.destroy();
|
||||
},
|
||||
this.destroy();
|
||||
}.bind(this),
|
||||
});
|
||||
},
|
||||
close: function(event) {
|
||||
@ -203,6 +241,9 @@ define([
|
||||
changeBoolField: function(field, event) {
|
||||
this.model.set(field, (jQuery(event.target).val() === 'true') ? true : false);
|
||||
},
|
||||
changeBoolCheckboxField: function(field, event) {
|
||||
this.model.set(field, (!!jQuery(event.target).prop('checked')));
|
||||
},
|
||||
changeColorField: function(field, event) {
|
||||
var value = jQuery(event.target).val();
|
||||
if (value === '') {
|
||||
|
@ -42,16 +42,12 @@ define([
|
||||
Module.ButtonBlockView = base.BlockView.extend({
|
||||
className: "mailpoet_block mailpoet_button_block mailpoet_droppable_block",
|
||||
getTemplate: function() { return templates.buttonBlock; },
|
||||
modelEvents: {
|
||||
'change': 'render',
|
||||
},
|
||||
onDragSubstituteBy: function() { return Module.ButtonWidgetView; },
|
||||
initialize: function() {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
var that = this;
|
||||
|
||||
// Listen for attempts to change all dividers in one go
|
||||
this._replaceButtonStylesHandler = function(data) { that.model.set(data); };
|
||||
this._replaceButtonStylesHandler = function(data) { this.model.set(data); }.bind(this);
|
||||
App.getChannel().on('replaceAllButtonStyles', this._replaceButtonStylesHandler);
|
||||
},
|
||||
onRender: function() {
|
||||
|
@ -75,7 +75,8 @@ define([
|
||||
getEmptyView: function() { return Module.ContainerBlockEmptyView; },
|
||||
emptyViewOptions: function() { return { renderOptions: this.renderOptions }; },
|
||||
modelEvents: {
|
||||
'change': 'render'
|
||||
'change': 'render',
|
||||
'delete': 'deleteBlock',
|
||||
},
|
||||
events: {
|
||||
"mouseenter": "showTools",
|
||||
@ -136,6 +137,8 @@ define([
|
||||
},
|
||||
initialize: function(options) {
|
||||
this.renderOptions = _.defaults(options.renderOptions || {}, {});
|
||||
this.on('dom:refresh', this.showBlock, this);
|
||||
this._isFirstRender = true;
|
||||
},
|
||||
// Determines which view type should be used for a child
|
||||
getChildView: function(model) {
|
||||
@ -162,10 +165,10 @@ define([
|
||||
this.toolsView = new Module.ContainerBlockToolsView({
|
||||
model: this.model,
|
||||
tools: {
|
||||
settings: this.renderOptions.depth > 1,
|
||||
settings: this.renderOptions.depth === 1,
|
||||
delete: this.renderOptions.depth === 1,
|
||||
move: this.renderOptions.depth === 1,
|
||||
layerSelector: this.renderOptions.depth === 1,
|
||||
layerSelector: false,
|
||||
},
|
||||
});
|
||||
this.toolsRegion.show(this.toolsView);
|
||||
@ -229,12 +232,49 @@ define([
|
||||
_.extend(this, this._buildRegions(this.regions));
|
||||
},
|
||||
getDropFunc: function() {
|
||||
var that = this;
|
||||
return function() {
|
||||
var newModel = that.model.clone();
|
||||
that.model.destroy();
|
||||
return newModel;
|
||||
};
|
||||
return this.model.clone();
|
||||
}.bind(this);
|
||||
},
|
||||
showBlock: function() {
|
||||
if (this._isFirstRender) {
|
||||
this.transitionIn();
|
||||
this._isFirstRender = false;
|
||||
}
|
||||
},
|
||||
deleteBlock: function() {
|
||||
this.transitionOut().done(function() {
|
||||
this.model.destroy();
|
||||
}.bind(this));
|
||||
},
|
||||
transitionIn: function() {
|
||||
return this._transition('slideDown', 'fadeIn', 'easeIn');
|
||||
},
|
||||
transitionOut: function() {
|
||||
return this._transition('slideUp', 'fadeOut', 'easeOut');
|
||||
},
|
||||
_transition: function(slideDirection, fadeDirection, easing) {
|
||||
var promise = jQuery.Deferred();
|
||||
|
||||
this.$el.velocity(
|
||||
slideDirection,
|
||||
{
|
||||
duration: 250,
|
||||
easing: easing,
|
||||
complete: function() {
|
||||
promise.resolve();
|
||||
}.bind(this),
|
||||
}
|
||||
).velocity(
|
||||
fadeDirection,
|
||||
{
|
||||
duration: 250,
|
||||
easing: easing,
|
||||
queue: false, // Do not enqueue, trigger animation in parallel
|
||||
}
|
||||
);
|
||||
|
||||
return promise;
|
||||
},
|
||||
});
|
||||
|
||||
@ -265,6 +305,41 @@ define([
|
||||
behaviors: {
|
||||
ColorPickerBehavior: {},
|
||||
},
|
||||
regions: {
|
||||
columnsSettingsRegion: '.mailpoet_container_columns_settings',
|
||||
},
|
||||
initialize: function() {
|
||||
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this._columnsSettingsView = new (Module.ContainerBlockColumnsSettingsView)({
|
||||
collection: this.model.get('blocks'),
|
||||
});
|
||||
},
|
||||
onRender: function() {
|
||||
this.columnsSettingsRegion.show(this._columnsSettingsView);
|
||||
},
|
||||
});
|
||||
|
||||
Module.ContainerBlockColumnsSettingsView = Marionette.CollectionView.extend({
|
||||
getChildView: function() { return Module.ContainerBlockColumnSettingsView; },
|
||||
childViewOptions: function(model, index) {
|
||||
return {
|
||||
columnIndex: index,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
Module.ContainerBlockColumnSettingsView = Marionette.ItemView.extend({
|
||||
getTemplate: function() { return templates.containerBlockColumnSettings; },
|
||||
initialize: function(options) {
|
||||
this.columnNumber = (options.columnIndex || 0) + 1;
|
||||
},
|
||||
templateHelpers: function() {
|
||||
return {
|
||||
model: this.model.toJSON(),
|
||||
columnNumber: this.columnNumber,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
Module.OneColumnContainerWidgetView = base.WidgetView.extend({
|
||||
|
@ -57,11 +57,9 @@ define([
|
||||
this.listenTo(this.model, 'change:styles.block.padding', this.changePadding);
|
||||
},
|
||||
templateHelpers: function() {
|
||||
return {
|
||||
model: this.model.toJSON(),
|
||||
viewCid: this.cid,
|
||||
return _.extend({
|
||||
totalHeight: parseInt(this.model.get('styles.block.padding'), 10)*2 + parseInt(this.model.get('styles.block.borderWidth')) + 'px',
|
||||
};
|
||||
}, base.BlockView.prototype.templateHelpers.apply(this));
|
||||
},
|
||||
onRender: function() {
|
||||
this.toolsView = new Module.DividerBlockToolsView({ model: this.model });
|
||||
|
@ -39,9 +39,9 @@ define([
|
||||
Module.FooterBlockView = base.BlockView.extend({
|
||||
className: "mailpoet_block mailpoet_footer_block mailpoet_droppable_block",
|
||||
getTemplate: function() { return templates.footerBlock; },
|
||||
modelEvents: {
|
||||
modelEvents: _.extend({
|
||||
'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render',
|
||||
},
|
||||
}, base.BlockView.prototype.modelEvents),
|
||||
onDragSubstituteBy: function() { return Module.FooterWidgetView; },
|
||||
onRender: function() {
|
||||
this.toolsView = new Module.FooterBlockToolsView({ model: this.model });
|
||||
|
@ -39,9 +39,9 @@ define([
|
||||
Module.HeaderBlockView = base.BlockView.extend({
|
||||
className: "mailpoet_block mailpoet_header_block mailpoet_droppable_block",
|
||||
getTemplate: function() { return templates.headerBlock; },
|
||||
modelEvents: {
|
||||
modelEvents: _.extend({
|
||||
'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render',
|
||||
},
|
||||
}, base.BlockView.prototype.modelEvents),
|
||||
onDragSubstituteBy: function() { return Module.HeaderWidgetView; },
|
||||
onRender: function() {
|
||||
this.toolsView = new Module.HeaderBlockToolsView({ model: this.model });
|
||||
|
@ -37,11 +37,9 @@ define([
|
||||
getTemplate: function() { return templates.imageBlock; },
|
||||
onDragSubstituteBy: function() { return Module.ImageWidgetView; },
|
||||
templateHelpers: function() {
|
||||
return {
|
||||
model: this.model.toJSON(),
|
||||
viewCid: this.cid,
|
||||
return _.extend({
|
||||
imageMissingSrc: App.getConfig().get('urls.imageMissing'),
|
||||
};
|
||||
}, base.BlockView.prototype.templateHelpers.apply(this));
|
||||
},
|
||||
onRender: function() {
|
||||
this.toolsView = new Module.ImageBlockToolsView({ model: this.model });
|
||||
@ -66,7 +64,7 @@ define([
|
||||
"keyup .mailpoet_field_image_link": _.partial(this.changeField, "link"),
|
||||
"keyup .mailpoet_field_image_address": _.partial(this.changeField, "src"),
|
||||
"keyup .mailpoet_field_image_alt_text": _.partial(this.changeField, "alt"),
|
||||
"change .mailpoet_field_image_padded": _.partial(this.changeBoolField, "padded"),
|
||||
"change .mailpoet_field_image_padded": _.partial(this.changeBoolCheckboxField, "padded"),
|
||||
"change .mailpoet_field_image_alignment": _.partial(this.changeField, "styles.block.textAlign"),
|
||||
"click .mailpoet_field_image_select_another_image": "showMediaManager",
|
||||
"click .mailpoet_done_editing": "close",
|
||||
@ -297,9 +295,9 @@ define([
|
||||
// Following advice from Becs, the target width should
|
||||
// be a double of one column width to render well on
|
||||
// retina screen devices
|
||||
targetImageWidth = 1200,
|
||||
targetImageWidth = 1320,
|
||||
|
||||
// For main image use the size, that's closest to being 600px in width
|
||||
// For main image use the size, that's closest to being 660px in width
|
||||
sizeKeys = _.keys(sizes),
|
||||
|
||||
// Pick the width that is closest to target width
|
||||
|
@ -18,11 +18,12 @@ define([
|
||||
'jquery',
|
||||
'mailpoet',
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/components/wordpress',
|
||||
'newsletter_editor/components/communication',
|
||||
'newsletter_editor/blocks/base',
|
||||
'newsletter_editor/blocks/button',
|
||||
'newsletter_editor/blocks/divider'
|
||||
], function(Backbone, Marionette, Radio, _, jQuery, MailPoet, App, WordpressComponent, BaseBlock, ButtonBlock, DividerBlock) {
|
||||
'newsletter_editor/blocks/divider',
|
||||
'select2'
|
||||
], function(Backbone, Marionette, Radio, _, jQuery, MailPoet, App, CommunicationComponent, BaseBlock, ButtonBlock, DividerBlock) {
|
||||
|
||||
"use strict";
|
||||
|
||||
@ -30,7 +31,7 @@ define([
|
||||
base = BaseBlock;
|
||||
|
||||
Module.PostsBlockModel = base.BlockModel.extend({
|
||||
stale: ['_selectedPosts', '_availablePosts'],
|
||||
stale: ['_selectedPosts', '_availablePosts', '_transformedPosts'],
|
||||
defaults: function() {
|
||||
return this._getDefaults({
|
||||
type: 'posts',
|
||||
@ -62,6 +63,7 @@ define([
|
||||
divider: {},
|
||||
_selectedPosts: [],
|
||||
_availablePosts: [],
|
||||
_transformedPosts: new (App.getBlockTypeModel('container'))(),
|
||||
}, App.getConfig().get('blockDefaults.posts'));
|
||||
},
|
||||
relations: function() {
|
||||
@ -70,21 +72,31 @@ define([
|
||||
divider: App.getBlockTypeModel('divider'),
|
||||
_selectedPosts: Backbone.Collection,
|
||||
_availablePosts: Backbone.Collection,
|
||||
_transformedPosts: App.getBlockTypeModel('container'),
|
||||
};
|
||||
},
|
||||
initialize: function() {
|
||||
var that = this;
|
||||
var that = this,
|
||||
POST_REFRESH_DELAY_MS = 500,
|
||||
refreshAvailablePosts = _.debounce(this.fetchAvailablePosts.bind(this), POST_REFRESH_DELAY_MS),
|
||||
refreshTransformedPosts = _.debounce(this._refreshTransformedPosts.bind(this), POST_REFRESH_DELAY_MS);
|
||||
|
||||
// Attach Radio.Requests API primarily for highlighting
|
||||
_.extend(this, Radio.Requests);
|
||||
|
||||
this.fetchAvailablePosts();
|
||||
this.on('change:amount change:contentType change:terms change:inclusionType change:postStatus change:search change:sortBy', this._scheduleFetchAvailablePosts, this);
|
||||
this.on('change:amount change:contentType change:terms change:inclusionType change:postStatus change:search change:sortBy', refreshAvailablePosts);
|
||||
|
||||
this.listenTo(this.get('_selectedPosts'), 'add remove reset', refreshTransformedPosts);
|
||||
this.on('change:displayType change:titleFormat change:titlePosition change:titleAlignment change:titleIsLink change:imagePadded change:showAuthor change:authorPrecededBy change:showCategories change:categoriesPrecededBy change:readMoreType change:readMoreText change:showDivider', refreshTransformedPosts);
|
||||
this.listenTo(this.get('readMoreButton'), 'change', refreshTransformedPosts);
|
||||
this.listenTo(this.get('divider'), 'change', refreshTransformedPosts);
|
||||
|
||||
this.on('insertSelectedPosts', this._insertSelectedPosts, this);
|
||||
},
|
||||
fetchAvailablePosts: function() {
|
||||
var that = this;
|
||||
WordpressComponent.getPosts(this.toJSON()).done(function(posts) {
|
||||
console.log('Posts fetched', arguments);
|
||||
CommunicationComponent.getPosts(this.toJSON()).done(function(posts) {
|
||||
that.get('_availablePosts').reset(posts);
|
||||
that.get('_selectedPosts').reset(); // Empty out the collection
|
||||
that.trigger('change:_availablePosts');
|
||||
@ -92,20 +104,22 @@ define([
|
||||
console.log('Posts fetchPosts error', arguments);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Batch more changes during a specific time, instead of fetching
|
||||
* ALC posts on each model change
|
||||
*/
|
||||
_scheduleFetchAvailablePosts: function() {
|
||||
var timeout = 500,
|
||||
that = this;
|
||||
if (this._fetchPostsTimer !== undefined) {
|
||||
clearTimeout(this._fetchPostsTimer);
|
||||
_refreshTransformedPosts: function() {
|
||||
var that = this,
|
||||
data = this.toJSON();
|
||||
|
||||
data.posts = this.get('_selectedPosts').pluck('ID');
|
||||
|
||||
if (data.posts.length === 0) {
|
||||
this.get('_transformedPosts.blocks').reset();
|
||||
return;
|
||||
}
|
||||
this._fetchPostsTimer = setTimeout(function() {
|
||||
that.fetchAvailablePosts();
|
||||
that._fetchPostsTimer = undefined;
|
||||
}, timeout);
|
||||
|
||||
CommunicationComponent.getTransformedPosts(data).done(function(posts) {
|
||||
that.get('_transformedPosts').get('blocks').reset(posts, {parse: true});
|
||||
}).fail(function() {
|
||||
console.log('Posts _refreshTransformedPosts error', arguments);
|
||||
});
|
||||
},
|
||||
_insertSelectedPosts: function() {
|
||||
var that = this,
|
||||
@ -117,8 +131,7 @@ define([
|
||||
|
||||
if (data.posts.length === 0) return;
|
||||
|
||||
WordpressComponent.getTransformedPosts(data).done(function(posts) {
|
||||
console.log('Available posts fetched', arguments);
|
||||
CommunicationComponent.getTransformedPosts(data).done(function(posts) {
|
||||
collection.add(posts, { at: index });
|
||||
}).fail(function() {
|
||||
console.log('Posts fetchPosts error', arguments);
|
||||
@ -129,10 +142,14 @@ define([
|
||||
Module.PostsBlockView = base.BlockView.extend({
|
||||
className: "mailpoet_block mailpoet_posts_block mailpoet_droppable_block",
|
||||
getTemplate: function() { return templates.postsBlock; },
|
||||
modelEvents: {},
|
||||
modelEvents: {}, // Forcefully disable all events
|
||||
regions: _.extend({
|
||||
postsRegion: '.mailpoet_posts_block_posts',
|
||||
}, base.BlockView.prototype.regions),
|
||||
onDragSubstituteBy: function() { return Module.PostsWidgetView; },
|
||||
initialize: function() {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this.toolsView = new Module.PostsBlockToolsView({ model: this.model });
|
||||
this.model.reply('blockView', this.notifyAboutSelf, this);
|
||||
},
|
||||
@ -141,6 +158,13 @@ define([
|
||||
this.toolsRegion.show(this.toolsView);
|
||||
}
|
||||
this.trigger('showSettings');
|
||||
|
||||
var ContainerView = App.getBlockTypeView('container'),
|
||||
renderOptions = {
|
||||
disableTextEditor: true,
|
||||
disableDragAndDrop: true,
|
||||
};
|
||||
this.postsRegion.show(new ContainerView({ model: this.model.get('_transformedPosts'), renderOptions: renderOptions }));
|
||||
},
|
||||
notifyAboutSelf: function() {
|
||||
return this;
|
||||
@ -196,8 +220,8 @@ define([
|
||||
},
|
||||
switchToDisplayOptions: function() {
|
||||
// Switch content view
|
||||
this.$('.mailpoet_settings_posts_selection').addClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_settings_posts_display_options').removeClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_settings_posts_selection').addClass('mailpoet_closed');
|
||||
this.$('.mailpoet_settings_posts_display_options').removeClass('mailpoet_closed');
|
||||
|
||||
// Switch controls
|
||||
this.$('.mailpoet_settings_posts_show_display_options').addClass('mailpoet_hidden');
|
||||
@ -205,8 +229,8 @@ define([
|
||||
},
|
||||
switchToPostSelection: function() {
|
||||
// Switch content view
|
||||
this.$('.mailpoet_settings_posts_display_options').addClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_settings_posts_selection').removeClass('mailpoet_hidden');
|
||||
this.$('.mailpoet_settings_posts_display_options').addClass('mailpoet_closed');
|
||||
this.$('.mailpoet_settings_posts_selection').removeClass('mailpoet_closed');
|
||||
|
||||
// Switch controls
|
||||
this.$('.mailpoet_settings_posts_show_post_selection').addClass('mailpoet_hidden');
|
||||
@ -215,6 +239,7 @@ define([
|
||||
insertPosts: function() {
|
||||
this.model.trigger('insertSelectedPosts');
|
||||
this.model.destroy();
|
||||
this.close();
|
||||
},
|
||||
});
|
||||
|
||||
@ -244,69 +269,67 @@ define([
|
||||
var that = this;
|
||||
|
||||
// Dynamically update available post types
|
||||
WordpressComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
|
||||
CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
|
||||
|
||||
this.$('.mailpoet_posts_categories_and_tags').select2({
|
||||
multiple: true,
|
||||
allowClear: true,
|
||||
query: function(options) {
|
||||
var taxonomies = [];
|
||||
// Delegate data loading to our own endpoints
|
||||
WordpressComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
|
||||
taxonomies = tax;
|
||||
// Fetch available terms based on the list of taxonomies already fetched
|
||||
var promise = WordpressComponent.getTerms({
|
||||
search: options.term,
|
||||
taxonomies: _.keys(taxonomies)
|
||||
}).then(function(terms) {
|
||||
return {
|
||||
taxonomies: taxonomies,
|
||||
terms: terms,
|
||||
};
|
||||
ajax: {
|
||||
data: function (params) {
|
||||
return {
|
||||
term: params.term
|
||||
};
|
||||
},
|
||||
transport: function(options, success, failure) {
|
||||
var taxonomies,
|
||||
promise = CommunicationComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
|
||||
taxonomies = tax;
|
||||
// Fetch available terms based on the list of taxonomies already fetched
|
||||
var promise = CommunicationComponent.getTerms({
|
||||
search: options.data.term,
|
||||
taxonomies: _.keys(taxonomies)
|
||||
}).then(function(terms) {
|
||||
return {
|
||||
taxonomies: taxonomies,
|
||||
terms: terms,
|
||||
};
|
||||
});
|
||||
return promise;
|
||||
});
|
||||
|
||||
promise.then(success);
|
||||
promise.fail(failure);
|
||||
return promise;
|
||||
}).done(function(args) {
|
||||
},
|
||||
processResults: function(data) {
|
||||
// Transform taxonomies and terms into select2 compatible format
|
||||
options.callback({
|
||||
return {
|
||||
results: _.map(
|
||||
args.terms,
|
||||
data.terms,
|
||||
function(item) {
|
||||
return _.defaults({
|
||||
text: args.taxonomies[item.taxonomy].labels.singular_name + ': ' + item.name,
|
||||
text: data.taxonomies[item.taxonomy].labels.singular_name + ': ' + item.name,
|
||||
id: item.term_id
|
||||
}, item);
|
||||
}
|
||||
)
|
||||
});
|
||||
});
|
||||
};
|
||||
},
|
||||
},
|
||||
}).trigger( 'change' ).on({
|
||||
'change': function(e){
|
||||
var data = [];
|
||||
|
||||
if (typeof data === 'string') {
|
||||
if (data === '') {
|
||||
data = [];
|
||||
} else {
|
||||
data = JSON.parse(data);
|
||||
}
|
||||
}
|
||||
|
||||
if ( e.added ){
|
||||
data.push(e.added);
|
||||
}
|
||||
|
||||
// Update ALC model
|
||||
that.model.set('terms', data);
|
||||
|
||||
jQuery(this).data('selected', JSON.stringify(data));
|
||||
}
|
||||
});
|
||||
},
|
||||
onBeforeDestroy: function() {
|
||||
base.BlockSettingsView.prototype.onBeforeDestroy.apply(this, arguments);
|
||||
// Force close select2 if it hasn't closed yet
|
||||
this.$('.mailpoet_posts_categories_and_tags').select2('close');
|
||||
}).on({
|
||||
'select2:select': function(event) {
|
||||
var terms = that.model.get('terms');
|
||||
terms.add(event.params.data);
|
||||
// Reset whole model in order for change events to propagate properly
|
||||
that.model.set('terms', terms.toJSON());
|
||||
},
|
||||
'select2:unselect': function(event) {
|
||||
var terms = that.model.get('terms');
|
||||
terms.remove(event.params.data);
|
||||
// Reset whole model in order for change events to propagate properly
|
||||
that.model.set('terms', terms.toJSON());
|
||||
},
|
||||
}).trigger( 'change' );
|
||||
},
|
||||
changeField: function(field, event) {
|
||||
this.model.set(field, jQuery(event.target).val());
|
||||
@ -319,7 +342,7 @@ define([
|
||||
_.each(postTypes, function(type) {
|
||||
select.append(jQuery('<option>', {
|
||||
value: type.name,
|
||||
text: type.labels.singular_name,
|
||||
text: type.label,
|
||||
}));
|
||||
});
|
||||
select.val(selectedValue);
|
||||
|
@ -103,7 +103,8 @@ define([
|
||||
getTemplate: function() { return templates.socialBlock; },
|
||||
childViewContainer: '.mailpoet_social',
|
||||
modelEvents: {
|
||||
'change': 'render'
|
||||
'change': 'render',
|
||||
'delete': 'deleteBlock',
|
||||
},
|
||||
events: {
|
||||
"mouseover": "showTools",
|
||||
@ -145,6 +146,10 @@ define([
|
||||
arguments[0].collection = arguments[0].model.get('icons');
|
||||
Marionette.CompositeView.apply(this, arguments);
|
||||
},
|
||||
initialize: function() {
|
||||
this.on('dom:refresh', this.showBlock, this);
|
||||
this._isFirstRender = true;
|
||||
},
|
||||
// Determines which view type should be used for a child
|
||||
childView: SocialIconView,
|
||||
templateHelpers: function() {
|
||||
@ -170,12 +175,9 @@ define([
|
||||
_event.stopPropagation();
|
||||
},
|
||||
getDropFunc: function() {
|
||||
var that = this;
|
||||
return function() {
|
||||
var newModel = that.model.clone();
|
||||
//that.model.destroy();
|
||||
return newModel;
|
||||
};
|
||||
return this.model.clone();
|
||||
}.bind(this);
|
||||
},
|
||||
_buildRegions: function(regions) {
|
||||
var that = this;
|
||||
@ -194,6 +196,46 @@ define([
|
||||
this.regionManager.destroy();
|
||||
_.extend(this, this._buildRegions(this.regions));
|
||||
},
|
||||
showBlock: function() {
|
||||
if (this._isFirstRender) {
|
||||
this.transitionIn();
|
||||
this._isFirstRender = false;
|
||||
}
|
||||
},
|
||||
deleteBlock: function() {
|
||||
this.transitionOut().done(function() {
|
||||
this.model.destroy();
|
||||
}.bind(this));
|
||||
},
|
||||
transitionIn: function() {
|
||||
return this._transition('slideDown', 'fadeIn', 'easeIn');
|
||||
},
|
||||
transitionOut: function() {
|
||||
return this._transition('slideUp', 'fadeOut', 'easeOut');
|
||||
},
|
||||
_transition: function(slideDirection, fadeDirection, easing) {
|
||||
var promise = jQuery.Deferred();
|
||||
|
||||
this.$el.velocity(
|
||||
slideDirection,
|
||||
{
|
||||
duration: 250,
|
||||
easing: easing,
|
||||
complete: function() {
|
||||
promise.resolve();
|
||||
}.bind(this),
|
||||
}
|
||||
).velocity(
|
||||
fadeDirection,
|
||||
{
|
||||
duration: 250,
|
||||
easing: easing,
|
||||
queue: false, // Do not enqueue, trigger animation in parallel
|
||||
}
|
||||
);
|
||||
|
||||
return promise;
|
||||
},
|
||||
});
|
||||
|
||||
Module.SocialBlockToolsView = base.BlockToolsView.extend({
|
||||
|
@ -26,6 +26,8 @@ define([
|
||||
getTemplate: function() { return templates.textBlock; },
|
||||
modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'), // Prevent rerendering on model change due to text editor redrawing
|
||||
initialize: function(options) {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
this.renderOptions = _.defaults(options.renderOptions || {}, {
|
||||
disableTextEditor: false,
|
||||
});
|
||||
|
@ -70,9 +70,10 @@ define([
|
||||
};
|
||||
|
||||
Module.previewNewsletter = function(options) {
|
||||
return Module._query({
|
||||
return MailPoet.Ajax.post({
|
||||
endpoint: 'newsletters',
|
||||
action: 'preview',
|
||||
options: options,
|
||||
data: options || {},
|
||||
});
|
||||
};
|
||||
|
@ -44,10 +44,10 @@ define([
|
||||
};
|
||||
|
||||
Module.getBody = function() {
|
||||
return JSON.stringify({
|
||||
return {
|
||||
content: App._contentContainer.toJSON(),
|
||||
globalStyles: App.getGlobalStyles().toJSON(),
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
Module.toJSON = function() {
|
||||
@ -73,8 +73,7 @@ define([
|
||||
});
|
||||
|
||||
App.on('start', function(options) {
|
||||
// TODO: Other newsletter information will be needed as well.
|
||||
var body = JSON.parse(options.newsletter.body);
|
||||
var body = options.newsletter.body;
|
||||
App._contentContainer = new (App.getBlockTypeModel('container'))(body.content, {parse: true});
|
||||
App._contentContainerView = new (App.getBlockTypeView('container'))({
|
||||
model: App._contentContainer,
|
||||
|
@ -1,9 +1,14 @@
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'mailpoet',
|
||||
'notice',
|
||||
'backbone',
|
||||
'backbone.marionette'
|
||||
], function(App, MailPoet, Backbone, Marionette) {
|
||||
'backbone.marionette',
|
||||
'jquery',
|
||||
'blob',
|
||||
'filesaver',
|
||||
'html2canvas'
|
||||
], function(App, MailPoet, Notice, Backbone, Marionette, jQuery, Blob, FileSaver, html2canvas) {
|
||||
|
||||
"use strict";
|
||||
|
||||
@ -42,13 +47,51 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
Module.getThumbnail = function(element, options) {
|
||||
return html2canvas(element, options || {});
|
||||
};
|
||||
|
||||
Module.saveTemplate = function(options) {
|
||||
return MailPoet.Ajax.post({
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'save',
|
||||
data: _.extend(options || {}, {
|
||||
var that = this,
|
||||
promise = jQuery.Deferred();
|
||||
|
||||
promise.then(function(thumbnail) {
|
||||
var data = _.extend(options || {}, {
|
||||
thumbnail: thumbnail.toDataURL('image/jpeg'),
|
||||
body: App.getBody(),
|
||||
}),
|
||||
});
|
||||
|
||||
return MailPoet.Ajax.post({
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'save',
|
||||
data: data,
|
||||
});
|
||||
});
|
||||
|
||||
Module.getThumbnail(
|
||||
jQuery('#mailpoet_editor_content > .mailpoet_block').get(0)
|
||||
).then(function(thumbnail) {
|
||||
promise.resolve(thumbnail);
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
Module.exportTemplate = function(options) {
|
||||
var that = this;
|
||||
return Module.getThumbnail(
|
||||
jQuery('#mailpoet_editor_content > .mailpoet_block').get(0)
|
||||
).then(function(thumbnail) {
|
||||
var data = _.extend(options || {}, {
|
||||
thumbnail: thumbnail.toDataURL('image/jpeg'),
|
||||
body: App.getBody(),
|
||||
});
|
||||
var blob = new Blob(
|
||||
[JSON.stringify(data)],
|
||||
{ type: 'application/json;charset=utf-8' }
|
||||
);
|
||||
|
||||
FileSaver.saveAs(blob, 'template.json');
|
||||
});
|
||||
};
|
||||
|
||||
@ -62,7 +105,8 @@ define([
|
||||
'click .mailpoet_save_template': 'toggleSaveAsTemplate',
|
||||
'click .mailpoet_save_as_template': 'saveAsTemplate',
|
||||
/* Export template */
|
||||
'click .mailpoet_save_export': 'exportTemplate',
|
||||
'click .mailpoet_save_export': 'toggleExportTemplate',
|
||||
'click .mailpoet_export_template': 'exportTemplate',
|
||||
},
|
||||
initialize: function(options) {
|
||||
App.getChannel().on('beforeEditorSave', this.beforeSave, this);
|
||||
@ -102,27 +146,87 @@ define([
|
||||
},
|
||||
saveAsTemplate: function() {
|
||||
var templateName = this.$('.mailpoet_save_as_template_name').val(),
|
||||
templateDescription = this.$('.mailpoet_save_as_template_description').val();
|
||||
templateDescription = this.$('.mailpoet_save_as_template_description').val(),
|
||||
that = this;
|
||||
|
||||
console.log('Saving template with ', templateName, templateDescription);
|
||||
Module.saveTemplate({
|
||||
name: templateName,
|
||||
description: templateDescription,
|
||||
}).done(function() {
|
||||
console.log('Template saved', arguments);
|
||||
}).fail(function() {
|
||||
// TODO: Handle error messages
|
||||
console.log('Template save failed', arguments);
|
||||
});
|
||||
if (templateName === '') {
|
||||
MailPoet.Notice.error(
|
||||
App.getConfig().get('translations.templateNameMissing'),
|
||||
{
|
||||
positionAfter: that.$el,
|
||||
}
|
||||
);
|
||||
} else if (templateDescription === '') {
|
||||
MailPoet.Notice.error(
|
||||
App.getConfig().get('translations.templateDescriptionMissing'),
|
||||
{
|
||||
positionAfter: that.$el,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
console.log('Saving template with ', templateName, templateDescription);
|
||||
Module.saveTemplate({
|
||||
name: templateName,
|
||||
description: templateDescription,
|
||||
}).done(function() {
|
||||
console.log('Template saved', arguments);
|
||||
MailPoet.Notice.success(
|
||||
App.getConfig().get('translations.templateSaved'),
|
||||
{
|
||||
positionAfter: that.$el,
|
||||
}
|
||||
);
|
||||
}).fail(function() {
|
||||
console.log('Template save failed', arguments);
|
||||
MailPoet.Notice.error(
|
||||
App.getConfig().get('translations.templateSaveFailed'),
|
||||
{
|
||||
positionAfter: that.$el,
|
||||
}
|
||||
);
|
||||
});
|
||||
this.hideOptionContents();
|
||||
}
|
||||
|
||||
this.hideOptionContents();
|
||||
},
|
||||
toggleExportTemplate: function() {
|
||||
this.$('.mailpoet_export_template_container').toggleClass('mailpoet_hidden');
|
||||
this.toggleSaveOptions();
|
||||
},
|
||||
hideExportTemplate: function() {
|
||||
this.$('.mailpoet_export_template_container').addClass('mailpoet_hidden');
|
||||
},
|
||||
exportTemplate: function() {
|
||||
console.log('Exporting template');
|
||||
this.hideOptionContents();
|
||||
var templateName = this.$('.mailpoet_export_template_name').val(),
|
||||
templateDescription = this.$('.mailpoet_export_template_description').val(),
|
||||
that = this;
|
||||
|
||||
if (templateName === '') {
|
||||
MailPoet.Notice.error(
|
||||
App.getConfig().get('translations.templateNameMissing'),
|
||||
{
|
||||
positionAfter: that.$el,
|
||||
}
|
||||
);
|
||||
} else if (templateDescription === '') {
|
||||
MailPoet.Notice.error(
|
||||
App.getConfig().get('translations.templateDescriptionMissing'),
|
||||
{
|
||||
positionAfter: that.$el,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
console.log('Exporting template with ', templateName, templateDescription);
|
||||
Module.exportTemplate({
|
||||
name: templateName,
|
||||
description: templateDescription,
|
||||
});
|
||||
this.hideExportTemplate();
|
||||
}
|
||||
},
|
||||
hideOptionContents: function() {
|
||||
this.hideSaveAsTemplate();
|
||||
this.hideExportTemplate();
|
||||
this.$('.mailpoet_save_options').addClass('mailpoet_hidden');
|
||||
},
|
||||
next: function() {
|
||||
|
@ -1,12 +1,13 @@
|
||||
define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/components/communication',
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'backbone.supermodel',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'sticky-kit'
|
||||
], function(App, Backbone, Marionette, SuperModel, _, jQuery, StickyKit) {
|
||||
], function(App, CommunicationComponent, Backbone, Marionette, SuperModel, _, jQuery, StickyKit) {
|
||||
|
||||
"use strict";
|
||||
|
||||
@ -50,8 +51,33 @@ define([
|
||||
},
|
||||
events: {
|
||||
'click .mailpoet_sidebar_region h3, .mailpoet_sidebar_region .handlediv': function(event) {
|
||||
this.$el.find('.mailpoet_sidebar_region').addClass('closed');
|
||||
this.$el.find(event.target).parent().parent().removeClass('closed');
|
||||
var $openRegion = this.$el.find('.mailpoet_sidebar_region:not(.closed)'),
|
||||
$targetRegion = this.$el.find(event.target).closest('.mailpoet_sidebar_region');
|
||||
|
||||
if ($openRegion.get(0) === $targetRegion.get(0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$openRegion.find('.mailpoet_region_content').velocity(
|
||||
'slideUp',
|
||||
{
|
||||
duration: 250,
|
||||
easing: "easeOut",
|
||||
complete: function() {
|
||||
$openRegion.addClass('closed');
|
||||
}.bind(this)
|
||||
}
|
||||
);
|
||||
$targetRegion.find('.mailpoet_region_content').velocity(
|
||||
'slideDown',
|
||||
{
|
||||
duration: 250,
|
||||
easing: "easeIn",
|
||||
complete: function() {
|
||||
$targetRegion.removeClass('closed');
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
initialize: function(options) {
|
||||
@ -90,7 +116,6 @@ define([
|
||||
});
|
||||
},
|
||||
onDomRefresh: function() {
|
||||
var that = this;
|
||||
this.$el.parent().stick_in_parent({
|
||||
offset_top: 32,
|
||||
});
|
||||
@ -169,10 +194,8 @@ define([
|
||||
},
|
||||
initialize: function(options) {
|
||||
this.availableStyles = options.availableStyles;
|
||||
var that = this;
|
||||
},
|
||||
onRender: function() {
|
||||
var that = this;
|
||||
this.$('.mailpoet_color').spectrum({
|
||||
clickoutFiresChange: true,
|
||||
showInput: true,
|
||||
@ -229,7 +252,7 @@ define([
|
||||
MailPoet.Modal.loading(true);
|
||||
|
||||
// TODO: Migrate logic to new AJAX format
|
||||
Wordpress.previewNewsletter(data).done(function(response) {
|
||||
CommunicationComponent.previewNewsletter(data).done(function(response) {
|
||||
if(response.success !== undefined && response.success === true) {
|
||||
MailPoet.Notice.success(App.getConfig().get('translations.testEmailSent'));
|
||||
} else if(response.error !== undefined) {
|
||||
|
@ -17,7 +17,7 @@ define([
|
||||
},
|
||||
h1: {
|
||||
fontColor: '#111111',
|
||||
fontFamily: 'Arial Black',
|
||||
fontFamily: 'Arial',
|
||||
fontSize: '40px'
|
||||
},
|
||||
h2: {
|
||||
@ -72,7 +72,7 @@ define([
|
||||
|
||||
App.getAvailableStyles = Module.getAvailableStyles;
|
||||
|
||||
var body = JSON.parse(options.newsletter.body);
|
||||
var body = options.newsletter.body;
|
||||
this.setGlobalStyles(body.globalStyles);
|
||||
});
|
||||
|
||||
|
@ -21,6 +21,10 @@ define(
|
||||
label: 'Subject',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
label: 'Status'
|
||||
},
|
||||
{
|
||||
name: 'segments',
|
||||
label: 'Lists'
|
||||
@ -37,29 +41,176 @@ define(
|
||||
}
|
||||
];
|
||||
|
||||
var messages = {
|
||||
onTrash: function(response) {
|
||||
var count = ~~response.newsletters;
|
||||
var message = null;
|
||||
|
||||
if(count === 1 || response === true) {
|
||||
message = (
|
||||
'1 newsletter was moved to the trash.'
|
||||
);
|
||||
} else if(count > 1) {
|
||||
message = (
|
||||
'%$1d newsletters were moved to the trash.'
|
||||
).replace('%$1d', count);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
},
|
||||
onDelete: function(response) {
|
||||
var count = ~~response.newsletters;
|
||||
var message = null;
|
||||
|
||||
if(count === 1 || response === true) {
|
||||
message = (
|
||||
'1 newsletter was permanently deleted.'
|
||||
);
|
||||
} else if(count > 1) {
|
||||
message = (
|
||||
'%$1d newsletters were permanently deleted.'
|
||||
).replace('%$1d', count);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
},
|
||||
onRestore: function(response) {
|
||||
var count = ~~response.newsletters;
|
||||
var message = null;
|
||||
|
||||
if(count === 1 || response === true) {
|
||||
message = (
|
||||
'1 newsletter has been restored from the trash.'
|
||||
);
|
||||
} else if(count > 1) {
|
||||
message = (
|
||||
'%$1d newsletters have been restored from the trash.'
|
||||
).replace('%$1d', count);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var bulk_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: 'Trash'
|
||||
label: 'Trash',
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
|
||||
var item_actions = [
|
||||
{
|
||||
name: 'edit',
|
||||
link: function(id) {
|
||||
link: function(item) {
|
||||
return (
|
||||
<a href={ '?page=mailpoet-newsletter-editor&id=' + id }>
|
||||
<a href={ `?page=mailpoet-newsletter-editor&id=${ item.id }` }>
|
||||
Edit
|
||||
</a>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'trash'
|
||||
}
|
||||
];
|
||||
|
||||
var NewsletterList = React.createClass({
|
||||
renderItem: function(newsletter, actions) {
|
||||
pauseSending: function(item) {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'sendingQueue',
|
||||
action: 'pause',
|
||||
data: item.id
|
||||
}).done(function() {
|
||||
jQuery('#resume_'+item.id).show();
|
||||
jQuery('#pause_'+item.id).hide();
|
||||
});
|
||||
},
|
||||
resumeSending: function(item) {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'sendingQueue',
|
||||
action: 'resume',
|
||||
data: item.id
|
||||
}).done(function() {
|
||||
jQuery('#pause_'+item.id).show();
|
||||
jQuery('#resume_'+item.id).hide();
|
||||
});
|
||||
},
|
||||
renderStatus: function(item) {
|
||||
if(item.queue === null) {
|
||||
return (
|
||||
<span>Not sent yet.</span>
|
||||
);
|
||||
} else {
|
||||
var progressClasses = classNames(
|
||||
'mailpoet_progress',
|
||||
{ 'mailpoet_progress_complete': item.queue.status === 'completed'}
|
||||
);
|
||||
|
||||
// calculate percentage done
|
||||
var percentage = Math.round(
|
||||
(item.queue.count_processed * 100) / (item.queue.count_total)
|
||||
);
|
||||
|
||||
var label = false;
|
||||
|
||||
if(item.queue.status === 'completed') {
|
||||
label = (
|
||||
<span>
|
||||
Sent to {
|
||||
item.queue.count_processed - item.queue.count_failed
|
||||
} out of { item.queue.count_total }.
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
label = (
|
||||
<span>
|
||||
{ item.queue.count_processed } / { item.queue.count_total }
|
||||
|
||||
<a
|
||||
id={ 'resume_'+item.id }
|
||||
className="button"
|
||||
style={{ display: (item.queue.status === 'paused') ? 'inline-block': 'none' }}
|
||||
href="javascript:;"
|
||||
onClick={ this.resumeSending.bind(null, item) }
|
||||
>Resume</a>
|
||||
<a
|
||||
id={ 'pause_'+item.id }
|
||||
className="button mailpoet_pause"
|
||||
style={{ display: (item.queue.status === null) ? 'inline-block': 'none' }}
|
||||
href="javascript:;"
|
||||
onClick={ this.pauseSending.bind(null, item) }
|
||||
>Pause</a>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={ progressClasses }>
|
||||
<span
|
||||
className="mailpoet_progress_bar"
|
||||
style={ { width: percentage + "%"} }
|
||||
></span>
|
||||
<span className="mailpoet_progress_label">
|
||||
{ percentage + "%" }
|
||||
</span>
|
||||
</div>
|
||||
<p style={{ textAlign:'center' }}>
|
||||
{ label }
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
renderItem: function(newsletter, actions) {
|
||||
var rowClasses = classNames(
|
||||
'manage-column',
|
||||
'column-primary',
|
||||
@ -80,6 +231,9 @@ define(
|
||||
</strong>
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column" data-colname="Lists">
|
||||
{ this.renderStatus(newsletter) }
|
||||
</td>
|
||||
<td className="column" data-colname="Lists">
|
||||
{ segments }
|
||||
</td>
|
||||
@ -100,11 +254,14 @@ define(
|
||||
</h2>
|
||||
|
||||
<Listing
|
||||
params={ this.props.params }
|
||||
endpoint="newsletters"
|
||||
onRenderItem={this.renderItem}
|
||||
columns={columns}
|
||||
bulk_actions={ bulk_actions }
|
||||
item_actions={ item_actions } />
|
||||
item_actions={ item_actions }
|
||||
messages={ messages }
|
||||
auto_refresh={ true } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import NewsletterTemplates from 'newsletters/templates.jsx'
|
||||
import NewsletterSend from 'newsletters/send.jsx'
|
||||
import NewsletterStandard from 'newsletters/types/standard.jsx'
|
||||
import NewsletterWelcome from 'newsletters/types/welcome.jsx'
|
||||
import NewsletterNotification from 'newsletters/types/notification.jsx'
|
||||
import createHashHistory from 'history/lib/createHashHistory'
|
||||
|
||||
let history = createHashHistory({ queryKey: false })
|
||||
@ -27,8 +28,10 @@ if(container) {
|
||||
<Route path="new" component={ NewsletterTypes } />
|
||||
<Route name="standard" path="new/standard" component={ NewsletterStandard } />
|
||||
<Route name="welcome" path="new/welcome" component={ NewsletterWelcome } />
|
||||
<Route name="notification" path="new/notification" component={ NewsletterNotification } />
|
||||
<Route name="template" path="template/:id" component={ NewsletterTemplates } />
|
||||
<Route path="send/:id" component={ NewsletterSend } />
|
||||
<Route path="filter[:filter]" component={ NewsletterList } />
|
||||
<Route path="*" component={ NewsletterList } />
|
||||
</Route>
|
||||
</Router>
|
||||
|
@ -16,7 +16,7 @@ define(
|
||||
Breadcrumb
|
||||
) {
|
||||
|
||||
var settings = window.mailpoet_settings || {};
|
||||
var settings = window.mailpoet_settings || {};
|
||||
|
||||
var fields = [
|
||||
{
|
||||
@ -24,18 +24,26 @@ define(
|
||||
label: 'Subject line',
|
||||
tip: "Be creative! It's the first thing your subscribers see."+
|
||||
"Tempt them to open your email.",
|
||||
type: 'text'
|
||||
type: 'text',
|
||||
validation: {
|
||||
'data-parsley-required': true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'segments',
|
||||
label: 'Lists',
|
||||
tip: "The subscriber list that will be used for this campaign.",
|
||||
label: 'Segments',
|
||||
tip: "The subscriber segment that will be used for this campaign.",
|
||||
type: 'selection',
|
||||
placeholder: "Select a list",
|
||||
placeholder: "Select a segment",
|
||||
id: "mailpoet_segments",
|
||||
endpoint: "segments",
|
||||
multiple: true,
|
||||
select2: true
|
||||
filter: function(segment) {
|
||||
return !!(!segment.deleted_at);
|
||||
},
|
||||
validation: {
|
||||
'data-parsley-required': true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'sender',
|
||||
@ -43,17 +51,24 @@ define(
|
||||
tip: "Name & email of yourself or your company.",
|
||||
fields: [
|
||||
{
|
||||
name: 'from_name',
|
||||
name: 'sender_name',
|
||||
type: 'text',
|
||||
placeholder: 'John Doe',
|
||||
defaultValue: settings.from_name
|
||||
defaultValue: (settings.sender !== undefined) ? settings.sender.name : '',
|
||||
validation: {
|
||||
'data-parsley-required': true
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'from_email',
|
||||
name: 'sender_address',
|
||||
type: 'text',
|
||||
placeholder: 'john.doe@email.com',
|
||||
defaultValue: settings.from_address
|
||||
},
|
||||
defaultValue: (settings.sender !== undefined) ? settings.sender.address : '',
|
||||
validation: {
|
||||
'data-parsley-required': true,
|
||||
'data-parsley-type': 'email'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -66,20 +81,25 @@ define(
|
||||
{
|
||||
name: 'reply_to_name',
|
||||
type: 'text',
|
||||
placeholder: 'John Doe'
|
||||
placeholder: 'John Doe',
|
||||
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.name : '',
|
||||
},
|
||||
{
|
||||
name: 'reply_to_email',
|
||||
name: 'reply_to_address',
|
||||
type: 'text',
|
||||
placeholder: 'john.doe@email.com'
|
||||
placeholder: 'john.doe@email.com',
|
||||
defaultValue: (settings.reply_to !== undefined) ? settings.reply_to.address : ''
|
||||
},
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
var messages = {
|
||||
updated: function() {
|
||||
MailPoet.Notice.success('The newsletter has been updated!');
|
||||
onUpdate: function() {
|
||||
MailPoet.Notice.success('Newsletter successfully updated!');
|
||||
},
|
||||
onCreate: function() {
|
||||
MailPoet.Notice.success('Newsletter successfully added!');
|
||||
}
|
||||
};
|
||||
|
||||
@ -88,34 +108,50 @@ define(
|
||||
Router.History
|
||||
],
|
||||
handleSend: function() {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'newsletters',
|
||||
action: 'send',
|
||||
data: {
|
||||
id: this.props.params.id,
|
||||
newsletter: jQuery('#mailpoet_newsletter').serializeObject(),
|
||||
segments: jQuery('#mailpoet_segments').val()
|
||||
}
|
||||
}).done(function(response) {
|
||||
if(response === true) {
|
||||
this.history.pushState(null, '/');
|
||||
|
||||
MailPoet.Notice.success(
|
||||
'The newsletter has been sent!'
|
||||
);
|
||||
} else {
|
||||
if(response.errors) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.join("<br />")
|
||||
if(jQuery('#mailpoet_newsletter').parsley().validate() === true) {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'sendingQueue',
|
||||
action: 'add',
|
||||
data: {
|
||||
newsletter_id: this.props.params.id,
|
||||
segments: jQuery('#mailpoet_segments').val(),
|
||||
sender: {
|
||||
'name': jQuery('#mailpoet_newsletter [name="sender_name"]').val(),
|
||||
'address': jQuery('#mailpoet_newsletter [name="sender_address"]').val()
|
||||
},
|
||||
reply_to: {
|
||||
'name': jQuery('#mailpoet_newsletter [name="reply_to_name"]').val(),
|
||||
'address': jQuery('#mailpoet_newsletter [name="reply_to_address"]').val()
|
||||
}
|
||||
}
|
||||
}).done(function(response) {
|
||||
if(response.result === true) {
|
||||
this.history.pushState(null, '/');
|
||||
MailPoet.Notice.success(
|
||||
'The newsletter is being sent...'
|
||||
);
|
||||
} else {
|
||||
MailPoet.Notice.error(
|
||||
'An error occurred while trying to send. '+
|
||||
'<a href="?page=mailpoet-settings">Check your settings.</a>'
|
||||
);
|
||||
if(response.errors) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.join("<br />")
|
||||
);
|
||||
} else {
|
||||
MailPoet.Notice.error(
|
||||
'An error occurred while trying to send. '+
|
||||
'<a href="?page=mailpoet-settings">Check your settings.</a>'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}
|
||||
},
|
||||
componentDidMount: function() {
|
||||
if(this.isMounted()) {
|
||||
jQuery('#mailpoet_newsletter').parsley();
|
||||
}
|
||||
},
|
||||
isValid: function() {
|
||||
return (jQuery('#mailpoet_newsletter').parsley().validate());
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
@ -129,7 +165,8 @@ define(
|
||||
endpoint="newsletters"
|
||||
fields={ fields }
|
||||
params={ this.props.params }
|
||||
messages={ messages }>
|
||||
messages={ messages }
|
||||
isValid={ this.isValid }>
|
||||
|
||||
<p className="submit">
|
||||
<input
|
||||
|
@ -1,6 +1,7 @@
|
||||
define(
|
||||
[
|
||||
'react',
|
||||
'underscore',
|
||||
'mailpoet',
|
||||
'react-router',
|
||||
'classnames',
|
||||
@ -8,11 +9,67 @@ define(
|
||||
],
|
||||
function(
|
||||
React,
|
||||
_,
|
||||
MailPoet,
|
||||
Router,
|
||||
classNames,
|
||||
Breadcrumb
|
||||
) {
|
||||
|
||||
var ImportTemplate = React.createClass({
|
||||
saveTemplate: function(template) {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'save',
|
||||
data: template
|
||||
}).done(function(response) {
|
||||
if(response === true) {
|
||||
this.props.onImport(template);
|
||||
} else {
|
||||
response.map(function(error) {
|
||||
MailPoet.Notice.error(error);
|
||||
});
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
handleSubmit: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (_.size(this.refs.templateFile.files) <= 0) return false;
|
||||
|
||||
var file = _.first(this.refs.templateFile.files),
|
||||
reader = new FileReader(),
|
||||
saveTemplate = this.saveTemplate;
|
||||
|
||||
reader.onload = function(e) {
|
||||
try {
|
||||
saveTemplate(JSON.parse(e.target.result));
|
||||
} catch (err) {
|
||||
MailPoet.Notice.error('This template file appears to be malformed. Please try another one.');
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
reader.readAsText(file);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<h2>Import a template</h2>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input type="file" placeholder="Select a .json file to upload" ref="templateFile" />
|
||||
|
||||
<p className="submit">
|
||||
<input
|
||||
className="button button-primary"
|
||||
type="submit"
|
||||
value="Upload" />
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var NewsletterTemplates = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
@ -42,7 +99,7 @@ define(
|
||||
"MailPoet's Guide",
|
||||
description:
|
||||
"This is the standard template that comes with MailPoet.",
|
||||
readonly: true
|
||||
readonly: "1"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -62,11 +119,11 @@ define(
|
||||
body: template.body
|
||||
}
|
||||
}).done(function(response) {
|
||||
if(response === true) {
|
||||
if(response.result === true) {
|
||||
// TODO: Move this URL elsewhere
|
||||
window.location = 'admin.php?page=mailpoet-newsletter-editor&id=' + this.props.params.id;
|
||||
} else {
|
||||
response.map(function(error) {
|
||||
response.errors.map(function(error) {
|
||||
MailPoet.Notice.error(error);
|
||||
});
|
||||
}
|
||||
@ -93,6 +150,16 @@ define(
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
},
|
||||
handleShowTemplate: function(template) {
|
||||
MailPoet.Modal.popup({
|
||||
title: template.name,
|
||||
template: '<div class="mailpoet_boxes_preview" style="background-color: {{ body.globalStyles.body.backgroundColor }}"><img src="{{ thumbnail }}" /></div>',
|
||||
data: template,
|
||||
});
|
||||
},
|
||||
handleTemplateImport: function() {
|
||||
this.getTemplates();
|
||||
},
|
||||
render: function() {
|
||||
var templates = this.state.templates.map(function(template, index) {
|
||||
var deleteLink = (
|
||||
@ -104,11 +171,22 @@ define(
|
||||
Delete
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
), thumbnail = '';
|
||||
|
||||
if (typeof template.thumbnail === 'string'
|
||||
&& template.thumbnail.length > 0) {
|
||||
thumbnail = (
|
||||
<a href="javascript:;" onClick={this.handleShowTemplate.bind(null, template)}>
|
||||
<img src={ template.thumbnail } />
|
||||
<div className="mailpoet_overlay"></div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={ 'template-'+index }>
|
||||
<div className="mailpoet_thumbnail">
|
||||
{ thumbnail }
|
||||
</div>
|
||||
|
||||
<div className="mailpoet_description">
|
||||
@ -132,7 +210,7 @@ define(
|
||||
Preview
|
||||
</a>
|
||||
</div>
|
||||
{ (template.readonly) ? false : deleteLink }
|
||||
{ (template.readonly === "1") ? false : deleteLink }
|
||||
</li>
|
||||
);
|
||||
}.bind(this));
|
||||
@ -152,6 +230,8 @@ define(
|
||||
<ul className={ boxClasses }>
|
||||
{ templates }
|
||||
</ul>
|
||||
|
||||
<ImportTemplate onImport={this.handleTemplateImport} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ define(
|
||||
action: 'create',
|
||||
data: {
|
||||
type: type,
|
||||
subject: 'Draft newsletter',
|
||||
}
|
||||
}).done(function(response) {
|
||||
if(response.id !== undefined) {
|
||||
@ -85,6 +86,26 @@ define(
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li data-type="notification">
|
||||
<div className="mailpoet_thumbnail"></div>
|
||||
|
||||
<div className="mailpoet_description">
|
||||
<h3>Post notifications</h3>
|
||||
<p>
|
||||
Automatically send posts immediately, daily, weekly or monthly. Filter by categories, if you like.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="mailpoet_actions">
|
||||
<a
|
||||
className="button button-primary"
|
||||
onClick={ this.setupNewsletter.bind(null, 'notification') }
|
||||
>
|
||||
Set up
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
225
assets/js/src/newsletters/types/notification.jsx
Normal file
@ -0,0 +1,225 @@
|
||||
define(
|
||||
[
|
||||
'underscore',
|
||||
'react',
|
||||
'react-router',
|
||||
'mailpoet',
|
||||
'form/form.jsx',
|
||||
'form/fields/select.jsx',
|
||||
'form/fields/selection.jsx',
|
||||
'form/fields/text.jsx',
|
||||
'newsletters/breadcrumb.jsx'
|
||||
],
|
||||
function(
|
||||
_,
|
||||
React,
|
||||
Router,
|
||||
MailPoet,
|
||||
Form,
|
||||
Select,
|
||||
Selection,
|
||||
Text,
|
||||
Breadcrumb
|
||||
) {
|
||||
|
||||
var intervalField = {
|
||||
name: 'interval',
|
||||
values: {
|
||||
'daily': 'Once a day at...',
|
||||
'weekly': 'Weekly on...',
|
||||
'monthly': 'Monthly on the...',
|
||||
'nthWeekDay': 'Monthly every...',
|
||||
'immediately': 'Immediately.',
|
||||
},
|
||||
};
|
||||
|
||||
var SECONDS_IN_DAY = 86400;
|
||||
var TIME_STEP_SECONDS = 3600; // Default: 3600
|
||||
var numberOfTimeSteps = SECONDS_IN_DAY / TIME_STEP_SECONDS;
|
||||
var timeOfDayValues = _.object(_.map(
|
||||
_.times(numberOfTimeSteps, function(step) { return step * TIME_STEP_SECONDS; }),
|
||||
function(seconds) {
|
||||
var date = new Date(null);
|
||||
date.setSeconds(seconds);
|
||||
var timeLabel = date.toISOString().substr(11, 5);
|
||||
return [seconds, timeLabel];
|
||||
}
|
||||
));
|
||||
var timeOfDayField = {
|
||||
name: 'timeOfDay',
|
||||
values: timeOfDayValues,
|
||||
};
|
||||
|
||||
var weekDayField = {
|
||||
name: 'weekDay',
|
||||
values: {
|
||||
0: 'Monday',
|
||||
1: 'Tuesday',
|
||||
2: 'Wednesday',
|
||||
3: 'Thursday',
|
||||
4: 'Friday',
|
||||
5: 'Saturday',
|
||||
6: 'Sunday',
|
||||
},
|
||||
};
|
||||
|
||||
var NUMBER_OF_DAYS_IN_MONTH = 28; // 28 for compatibility with MP2
|
||||
var monthDayField = {
|
||||
name: 'monthDay',
|
||||
values: _.object(_.map(
|
||||
_.times(NUMBER_OF_DAYS_IN_MONTH, function(day) { return day; }),
|
||||
function(day) {
|
||||
var suffixes = {
|
||||
0: 'st',
|
||||
1: 'nd',
|
||||
2: 'rd'
|
||||
};
|
||||
var suffix = suffixes[day] || 'th';
|
||||
|
||||
return [day, (day + 1).toString() + suffix];
|
||||
},
|
||||
)),
|
||||
};
|
||||
|
||||
var nthWeekDayField = {
|
||||
name: 'nthWeekDay',
|
||||
values: {
|
||||
'0': '1st',
|
||||
'1': '2nd',
|
||||
'2': '3rd',
|
||||
'3': 'last',
|
||||
},
|
||||
};
|
||||
|
||||
var NewsletterWelcome = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
getInitialState: function() {
|
||||
return {
|
||||
intervalType: 'immediate', // 'immediate'|'daily'|'weekly'|'monthly'
|
||||
timeOfDay: 0,
|
||||
weekDay: 0,
|
||||
monthDay: 0,
|
||||
nthWeekDay: 0,
|
||||
};
|
||||
},
|
||||
handleIntervalChange: function(event) {
|
||||
this.setState({
|
||||
intervalType: event.target.value,
|
||||
});
|
||||
},
|
||||
handleTimeOfDayChange: function(event) {
|
||||
this.setState({
|
||||
timeOfDay: event.target.value,
|
||||
});
|
||||
},
|
||||
handleWeekDayChange: function(event) {
|
||||
this.setState({
|
||||
weekDay: event.target.value,
|
||||
});
|
||||
},
|
||||
handleMonthDayChange: function(event) {
|
||||
this.setState({
|
||||
monthDay: event.target.value,
|
||||
});
|
||||
},
|
||||
handleNthWeekDayChange: function(event) {
|
||||
this.setState({
|
||||
nthWeekDay: event.target.value,
|
||||
});
|
||||
},
|
||||
handleNext: function() {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'newsletters',
|
||||
action: 'create',
|
||||
data: {
|
||||
type: 'notification',
|
||||
options: this.state,
|
||||
},
|
||||
}).done(function(response) {
|
||||
if(response.id !== undefined) {
|
||||
this.showTemplateSelection(response.id);
|
||||
} else {
|
||||
response.map(function(error) {
|
||||
MailPoet.Notice.error(error);
|
||||
});
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
showTemplateSelection: function(newsletterId) {
|
||||
this.history.pushState(null, `/template/${newsletterId}`);
|
||||
},
|
||||
render: function() {
|
||||
var timeOfDaySelection,
|
||||
weekDaySelection,
|
||||
monthDaySelection,
|
||||
nthWeekDaySelection;
|
||||
|
||||
if (this.state.intervalType !== 'immediately') {
|
||||
timeOfDaySelection = (
|
||||
<Select
|
||||
field={timeOfDayField}
|
||||
item={this.state}
|
||||
onValueChange={this.handleTimeOfDayChange} />
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.intervalType === 'weekly'
|
||||
|| this.state.intervalType === 'nthWeekDay') {
|
||||
weekDaySelection = (
|
||||
<Select
|
||||
field={weekDayField}
|
||||
item={this.state}
|
||||
onValueChange={this.handleWeekDayChange} />
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.intervalType === 'monthly') {
|
||||
monthDaySelection = (
|
||||
<Select
|
||||
field={monthDayField}
|
||||
item={this.state}
|
||||
onValueChange={this.handleMonthDayChange} />
|
||||
);
|
||||
}
|
||||
|
||||
if (this.state.intervalType === 'nthWeekDay') {
|
||||
nthWeekDaySelection = (
|
||||
<Select
|
||||
field={nthWeekDayField}
|
||||
item={this.state}
|
||||
onValueChange={this.handleNthWeekDayChange} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Post notifications</h1>
|
||||
<Breadcrumb step="type" />
|
||||
|
||||
<Select
|
||||
field={intervalField}
|
||||
item={this.state}
|
||||
onValueChange={this.handleIntervalChange} />
|
||||
|
||||
{nthWeekDaySelection}
|
||||
{monthDaySelection}
|
||||
{weekDaySelection}
|
||||
{timeOfDaySelection}
|
||||
|
||||
<p className="submit">
|
||||
<input
|
||||
className="button button-primary"
|
||||
type="button"
|
||||
onClick={ this.handleNext }
|
||||
value="Next" />
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return NewsletterWelcome;
|
||||
}
|
||||
);
|
@ -1,191 +1,212 @@
|
||||
define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
||||
"use strict";
|
||||
/*==================================================================================================
|
||||
|
||||
MailPoet Notice:
|
||||
|
||||
description: Handles notices
|
||||
version: 0.2
|
||||
author: Jonathan Labreuille
|
||||
company: Wysija
|
||||
dependencies: jQuery
|
||||
|
||||
Usage:
|
||||
|
||||
// success message (static: false)
|
||||
MailPoet.Notice.success('Yatta!');
|
||||
|
||||
// error message (static: false)
|
||||
MailPoet.Notice.error('Boo!');
|
||||
|
||||
// system message (static: true)
|
||||
MailPoet.Notice.system('You need to updated ASAP!');
|
||||
|
||||
Examples:
|
||||
|
||||
MailPoet.Notice.success('- success #1 -');
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.success('- success #2 -');
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.error('- error -');
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.system('- system -');
|
||||
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.hide();
|
||||
}, 2500);
|
||||
}, 300);
|
||||
}, 400);
|
||||
}, 500);
|
||||
|
||||
==================================================================================================*/
|
||||
|
||||
MailPoet.Notice = {
|
||||
version: 0.2,
|
||||
// default options
|
||||
defaults: {
|
||||
type: 'success',
|
||||
message: '',
|
||||
static: false,
|
||||
scroll: false,
|
||||
timeout: 2000,
|
||||
onOpen: null,
|
||||
onClose: null
|
||||
},
|
||||
options: {},
|
||||
init: function(options) {
|
||||
// set options
|
||||
this.options = jQuery.extend({}, this.defaults, options);
|
||||
|
||||
// clone element
|
||||
this.element = jQuery('#mailpoet_notice_'+this.options.type).clone();
|
||||
|
||||
// remove id from clone
|
||||
this.element.removeAttr('id');
|
||||
|
||||
// insert notice after its parent
|
||||
jQuery('#mailpoet_notice_'+this.options.type).after(this.element);
|
||||
|
||||
// setup onClose callback
|
||||
var onClose = null;
|
||||
if(this.options.onClose !== null) {
|
||||
onClose = this.options.onClose;
|
||||
}
|
||||
|
||||
// listen to remove event
|
||||
var element = this.element;
|
||||
jQuery(this.element).on('close', function() {
|
||||
jQuery(this).fadeOut(200, function() {
|
||||
// on close callback
|
||||
if(onClose !== null) {
|
||||
onClose();
|
||||
}
|
||||
// remove notice
|
||||
jQuery(this).remove();
|
||||
});
|
||||
}.bind(this.element));
|
||||
|
||||
// listen to message event
|
||||
jQuery(this.element).on('message', function(e, message) {
|
||||
MailPoet.Notice.setMessage(message);
|
||||
}.bind(this.element));
|
||||
|
||||
return this;
|
||||
},
|
||||
isHTML: function(str) {
|
||||
var a = document.createElement('div');
|
||||
a.innerHTML = str;
|
||||
for(var c = a.childNodes, i = c.length; i--;) {
|
||||
if(c[i].nodeType == 1) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
setMessage: function(message) {
|
||||
// if it's not an html message, let's sugar coat the message with a fancy <p>
|
||||
if(this.isHTML(message) === false) {
|
||||
message = '<p>'+message+'</p>';
|
||||
}
|
||||
// set message
|
||||
return this.element.html(message);
|
||||
},
|
||||
show: function(options) {
|
||||
// initialize
|
||||
this.init(options);
|
||||
|
||||
// show notice
|
||||
this.showNotice();
|
||||
|
||||
// return this;
|
||||
},
|
||||
showNotice: function() {
|
||||
// set message
|
||||
this.setMessage(this.options.message);
|
||||
|
||||
// position notice
|
||||
this.element.insertAfter(jQuery('h2.title'));
|
||||
|
||||
// set class name
|
||||
switch(this.options.type) {
|
||||
case 'success':
|
||||
this.element.addClass('updated');
|
||||
break;
|
||||
case 'system':
|
||||
this.element.addClass('update-nag');
|
||||
break;
|
||||
case 'error':
|
||||
this.element.addClass('error');
|
||||
break;
|
||||
}
|
||||
|
||||
// make the notice appear
|
||||
this.element.fadeIn(200);
|
||||
|
||||
// if scroll option is enabled, scroll to the notice
|
||||
if(this.options.scroll === true) {
|
||||
this.element.get(0).scrollIntoView(false);
|
||||
}
|
||||
|
||||
// if the notice is not static, it has to disappear after a timeout
|
||||
if(this.options.static === false) {
|
||||
this.element.delay(this.options.timeout).trigger('close');
|
||||
} else {
|
||||
this.element.append('<a href="javascript:;" class="mailpoet_notice_close"><span class="dashicons dashicons-dismiss"></span></a>');
|
||||
this.element.find('.mailpoet_notice_close').on('click', function() {
|
||||
jQuery(this).trigger('close');
|
||||
});
|
||||
}
|
||||
|
||||
// call onOpen callback
|
||||
if(this.options.onOpen !== null) {
|
||||
this.options.onOpen(this.element);
|
||||
}
|
||||
},
|
||||
hide: function(all) {
|
||||
if(all !== undefined && all === true) {
|
||||
jQuery('.mailpoet_notice:not([id])').trigger('close');
|
||||
} else {
|
||||
jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])')
|
||||
.trigger('close');
|
||||
}
|
||||
},
|
||||
error: function(message, options) {
|
||||
this.show(jQuery.extend({}, {
|
||||
type: 'error',
|
||||
message: '<p>'+message+'</p>'
|
||||
}, options));
|
||||
},
|
||||
success: function(message, options) {
|
||||
this.show(jQuery.extend({}, {
|
||||
type: 'success',
|
||||
message: '<p>'+message+'</p>'
|
||||
}, options));
|
||||
},
|
||||
system: function(message, options) {
|
||||
this.show(jQuery.extend({}, {
|
||||
type: 'system',
|
||||
static: true,
|
||||
message: message
|
||||
}, options));
|
||||
}
|
||||
};
|
||||
});
|
||||
define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
||||
"use strict";
|
||||
/*==================================================================================================
|
||||
|
||||
MailPoet Notice:
|
||||
|
||||
description: Handles notices
|
||||
version: 0.2
|
||||
author: Jonathan Labreuille
|
||||
company: Wysija
|
||||
dependencies: jQuery
|
||||
|
||||
Usage:
|
||||
|
||||
// success message (static: false)
|
||||
MailPoet.Notice.success('Yatta!');
|
||||
|
||||
// error message (static: false)
|
||||
MailPoet.Notice.error('Boo!');
|
||||
|
||||
// system message (static: true)
|
||||
MailPoet.Notice.system('You need to updated ASAP!');
|
||||
|
||||
Examples:
|
||||
|
||||
MailPoet.Notice.success('- success #1 -');
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.success('- success #2 -');
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.error('- error -');
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.system('- system -');
|
||||
|
||||
setTimeout(function() {
|
||||
MailPoet.Notice.hide();
|
||||
}, 2500);
|
||||
}, 300);
|
||||
}, 400);
|
||||
}, 500);
|
||||
|
||||
==================================================================================================*/
|
||||
|
||||
MailPoet.Notice = {
|
||||
version: 0.2,
|
||||
// default options
|
||||
defaults: {
|
||||
type: 'success',
|
||||
message: '',
|
||||
static: false,
|
||||
hideClose: false,
|
||||
id: null,
|
||||
positionAfter: false,
|
||||
scroll: false,
|
||||
timeout: 2000,
|
||||
onOpen: null,
|
||||
onClose: null
|
||||
},
|
||||
options: {},
|
||||
init: function(options) {
|
||||
// set options
|
||||
this.options = jQuery.extend({}, this.defaults, options);
|
||||
|
||||
// clone element
|
||||
this.element = jQuery('#mailpoet_notice_'+this.options.type).clone();
|
||||
|
||||
// add data-id to the element
|
||||
if (this.options.id) this.element.attr('data-id', 'notice_' + this.options.id);
|
||||
|
||||
// remove id from clone
|
||||
this.element.removeAttr('id');
|
||||
|
||||
// insert notice after its parent
|
||||
var positionAfter;
|
||||
if (typeof this.options.positionAfter === 'object') {
|
||||
positionAfter = this.options.positionAfter;
|
||||
} else if (typeof this.options.positionAfter === 'string') {
|
||||
positionAfter = jQuery(this.options.positionAfter);
|
||||
} else {
|
||||
positionAfter = jQuery('#mailpoet_notice_'+this.options.type);
|
||||
}
|
||||
positionAfter.after(this.element);
|
||||
|
||||
// setup onClose callback
|
||||
var onClose = null;
|
||||
if(this.options.onClose !== null) {
|
||||
onClose = this.options.onClose;
|
||||
}
|
||||
|
||||
// listen to remove event
|
||||
jQuery(this.element).on('close', function() {
|
||||
jQuery(this).fadeOut(200, function() {
|
||||
// on close callback
|
||||
if(onClose !== null) {
|
||||
onClose();
|
||||
}
|
||||
// remove notice
|
||||
jQuery(this).remove();
|
||||
});
|
||||
}.bind(this.element));
|
||||
|
||||
// listen to message event
|
||||
jQuery(this.element).on('message', function(e, message) {
|
||||
MailPoet.Notice.setMessage(message);
|
||||
}.bind(this.element));
|
||||
|
||||
return this;
|
||||
},
|
||||
isHTML: function(str) {
|
||||
var a = document.createElement('div');
|
||||
a.innerHTML = str;
|
||||
for(var c = a.childNodes, i = c.length; i--;) {
|
||||
if(c[i].nodeType == 1) return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
setMessage: function(message) {
|
||||
// if it's not an html message, let's sugar coat the message with a fancy <p>
|
||||
if(this.isHTML(message) === false) {
|
||||
message = '<p>'+message+'</p>';
|
||||
}
|
||||
// set message
|
||||
return this.element.html(message);
|
||||
},
|
||||
show: function(options) {
|
||||
// initialize
|
||||
this.init(options);
|
||||
|
||||
// show notice
|
||||
this.showNotice();
|
||||
|
||||
// return this;
|
||||
},
|
||||
showNotice: function() {
|
||||
// set message
|
||||
this.setMessage(this.options.message);
|
||||
|
||||
// position notice
|
||||
this.element.insertAfter(jQuery('h2.title'));
|
||||
|
||||
// set class name
|
||||
switch(this.options.type) {
|
||||
case 'success':
|
||||
this.element.addClass('updated');
|
||||
break;
|
||||
case 'system':
|
||||
this.element.addClass('update-nag');
|
||||
break;
|
||||
case 'error':
|
||||
this.element.addClass('error');
|
||||
break;
|
||||
}
|
||||
|
||||
// make the notice appear
|
||||
this.element.fadeIn(200);
|
||||
|
||||
// if scroll option is enabled, scroll to the notice
|
||||
if(this.options.scroll === true) {
|
||||
this.element.get(0).scrollIntoView(false);
|
||||
}
|
||||
|
||||
// if the notice is not static, it has to disappear after a timeout
|
||||
if(this.options.static === false) {
|
||||
this.element.delay(this.options.timeout).trigger('close');
|
||||
} else if (this.options.hideClose === false) {
|
||||
this.element.append('<a href="javascript:;" class="mailpoet_notice_close"><span class="dashicons dashicons-dismiss"></span></a>');
|
||||
this.element.find('.mailpoet_notice_close').on('click', function() {
|
||||
jQuery(this).trigger('close');
|
||||
});
|
||||
}
|
||||
|
||||
// call onOpen callback
|
||||
if(this.options.onOpen !== null) {
|
||||
this.options.onOpen(this.element);
|
||||
}
|
||||
},
|
||||
hide: function(all) {
|
||||
if(all !== undefined && all === true) {
|
||||
jQuery('.mailpoet_notice:not([id])').trigger('close');
|
||||
} else if (all !== undefined && jQuery.isArray(all)) {
|
||||
for (var id in all) {
|
||||
jQuery('[data-id="notice_' + all[id] + '"]')
|
||||
.trigger('close');
|
||||
}
|
||||
} if (all !== undefined) {
|
||||
jQuery('[data-id="notice_' + all + '"]')
|
||||
.trigger('close');
|
||||
} else {
|
||||
jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])')
|
||||
.trigger('close');
|
||||
}
|
||||
},
|
||||
error: function(message, options) {
|
||||
this.show(jQuery.extend({}, {
|
||||
type: 'error',
|
||||
message: '<p>'+message+'</p>'
|
||||
}, options));
|
||||
},
|
||||
success: function(message, options) {
|
||||
this.show(jQuery.extend({}, {
|
||||
type: 'success',
|
||||
message: '<p>'+message+'</p>'
|
||||
}, options));
|
||||
},
|
||||
system: function(message, options) {
|
||||
this.show(jQuery.extend({}, {
|
||||
type: 'system',
|
||||
static: true,
|
||||
message: message
|
||||
}, options));
|
||||
}
|
||||
};
|
||||
});
|
||||
|
@ -1,88 +1,81 @@
|
||||
define('public', ['mailpoet', 'jquery', 'jquery-validation'],
|
||||
function(MailPoet, $) {
|
||||
'use strict';
|
||||
|
||||
define([
|
||||
'mailpoet',
|
||||
'jquery',
|
||||
'parsleyjs'
|
||||
],
|
||||
function(
|
||||
MailPoet,
|
||||
jQuery,
|
||||
Parsley
|
||||
) {
|
||||
jQuery(function($) {
|
||||
function isSameDomain(url) {
|
||||
var link = document.createElement('a');
|
||||
link.href = url;
|
||||
return (window.location.hostname === link.hostname);
|
||||
}
|
||||
|
||||
function formatData(raw) {
|
||||
var data = {};
|
||||
|
||||
$.each(raw, function(index, value) {
|
||||
if(value.name.endsWith('[]')) {
|
||||
var value_name = value.name.substr(0, value.name.length - 2);
|
||||
// it's an array
|
||||
if(data[value_name] === undefined) {
|
||||
data[value_name] = [];
|
||||
}
|
||||
data[value_name].push(value.value);
|
||||
} else {
|
||||
data[value.name] = value.value;
|
||||
}
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
$(function() {
|
||||
// setup form validation
|
||||
$('form.mailpoet_form').each(function() {
|
||||
$(this).validate({
|
||||
submitHandler: function(form) {
|
||||
var data = $(form).serializeArray() || {};
|
||||
var form = $(this);
|
||||
|
||||
// clear messages
|
||||
$(form).find('.mailpoet_message').html('');
|
||||
form.parsley({
|
||||
errorsWrapper: '<p></p>',
|
||||
errorTemplate: '<span></span>'
|
||||
}).on('form:submit', function(parsley) {
|
||||
|
||||
// check if we're on the same domain
|
||||
if(isSameDomain(MailPoetForm.ajax_url) === false) {
|
||||
// non ajax post request
|
||||
return true;
|
||||
} else {
|
||||
// ajax request
|
||||
MailPoet.Ajax.post({
|
||||
url: MailPoetForm.ajax_url,
|
||||
token: MailPoetForm.token,
|
||||
endpoint: 'subscribers',
|
||||
action: 'save',
|
||||
data: formatData(data),
|
||||
onSuccess: function(response) {
|
||||
if(response !== true) {
|
||||
// errors
|
||||
$.each(response, function(index, error) {
|
||||
$(form)
|
||||
.find('.mailpoet_message')
|
||||
.append('<p class="mailpoet_validate_error">'+
|
||||
error+
|
||||
'</p>');
|
||||
});
|
||||
} else {
|
||||
// successfully subscribed
|
||||
if(response.page !== undefined) {
|
||||
// go to page
|
||||
window.location.href = response.page;
|
||||
} else if(response.message !== undefined) {
|
||||
// display success message
|
||||
$(form)
|
||||
.find('.mailpoet_message')
|
||||
.html('<p class="mailpoet_validate_success">'+
|
||||
response.message+
|
||||
'</p>');
|
||||
}
|
||||
var data = form.serializeObject() || {};
|
||||
|
||||
// reset form
|
||||
$(form).trigger('reset');
|
||||
}
|
||||
// clear messages
|
||||
form.find('.mailpoet_message').html('');
|
||||
|
||||
// check if we're on the same domain
|
||||
if(isSameDomain(MailPoetForm.ajax_url) === false) {
|
||||
// non ajax post request
|
||||
return true;
|
||||
} else {
|
||||
// ajax request
|
||||
MailPoet.Ajax.post({
|
||||
url: MailPoetForm.ajax_url,
|
||||
token: MailPoetForm.token,
|
||||
endpoint: 'subscribers',
|
||||
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>');
|
||||
});
|
||||
} else {
|
||||
// successfully subscribed
|
||||
if(response.page !== undefined) {
|
||||
// go to page
|
||||
window.location.href = response.page;
|
||||
} else if(response.message !== undefined) {
|
||||
// display success message
|
||||
form
|
||||
.find('.mailpoet_message')
|
||||
.html('<p class="mailpoet_validate_success">'+
|
||||
response.message+
|
||||
'</p>');
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
|
||||
// reset form
|
||||
form.trigger('reset');
|
||||
// reset validation
|
||||
parsley.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
@ -17,33 +17,40 @@ define(
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
type: 'textarea'
|
||||
}
|
||||
];
|
||||
|
||||
var messages = {
|
||||
updated: function() {
|
||||
onUpdate: function() {
|
||||
MailPoet.Notice.success('Segment successfully updated!');
|
||||
},
|
||||
created: function() {
|
||||
onCreate: function() {
|
||||
MailPoet.Notice.success('Segment successfully added!');
|
||||
}
|
||||
};
|
||||
|
||||
var Link = Router.Link;
|
||||
|
||||
var SegmentForm = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
Segment <Link className="add-new-h2" to="/">Back to list</Link>
|
||||
Segment
|
||||
</h2>
|
||||
|
||||
<Form
|
||||
endpoint="segments"
|
||||
fields={ fields }
|
||||
params={ this.props.params }
|
||||
messages={ messages } />
|
||||
messages={ messages }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,85 +1,228 @@
|
||||
define(
|
||||
[
|
||||
'react',
|
||||
'react-router',
|
||||
'listing/listing.jsx',
|
||||
'classnames'
|
||||
],
|
||||
function(
|
||||
React,
|
||||
Router,
|
||||
Listing,
|
||||
classNames
|
||||
) {
|
||||
var columns = [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'created_at',
|
||||
label: 'Created on',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'updated_at',
|
||||
label: 'Last modified on',
|
||||
sortable: true
|
||||
}
|
||||
];
|
||||
import React from 'react'
|
||||
import { Router, Link } from 'react-router'
|
||||
|
||||
var bulk_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: 'Trash'
|
||||
}
|
||||
];
|
||||
import jQuery from 'jquery'
|
||||
import MailPoet from 'mailpoet'
|
||||
import classNames from 'classnames'
|
||||
|
||||
var Link = Router.Link;
|
||||
import Listing from 'listing/listing.jsx'
|
||||
|
||||
var SegmentList = React.createClass({
|
||||
renderItem: function(segment, actions) {
|
||||
var rowClasses = classNames(
|
||||
'manage-column',
|
||||
'column-primary',
|
||||
'has-row-actions'
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<td className={ rowClasses }>
|
||||
<strong>
|
||||
<a>{ segment.name }</a>
|
||||
</strong>
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column-date" data-colname="Subscribed on">
|
||||
<abbr>{ segment.created_at }</abbr>
|
||||
</td>
|
||||
<td className="column-date" data-colname="Last modified on">
|
||||
<abbr>{ segment.updated_at }</abbr>
|
||||
</td>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
Segments <Link className="add-new-h2" to="/new">New</Link>
|
||||
</h2>
|
||||
|
||||
<Listing
|
||||
endpoint="segments"
|
||||
onRenderItem={this.renderItem}
|
||||
columns={columns}
|
||||
bulk_actions={ bulk_actions } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return SegmentList;
|
||||
var columns = [
|
||||
{
|
||||
name: 'name',
|
||||
label: 'Name',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
label: 'Description',
|
||||
sortable: false
|
||||
},
|
||||
{
|
||||
name: 'subscribed',
|
||||
label: 'Subscribed',
|
||||
sortable: false
|
||||
},
|
||||
{
|
||||
name: 'unconfirmed',
|
||||
label: 'Unconfirmed',
|
||||
sortable: false
|
||||
},
|
||||
{
|
||||
name: 'unsubscribed',
|
||||
label: 'Unsubscribed',
|
||||
sortable: false
|
||||
},
|
||||
{
|
||||
name: 'created_at',
|
||||
label: 'Created on',
|
||||
sortable: true
|
||||
}
|
||||
);
|
||||
];
|
||||
|
||||
const messages = {
|
||||
onTrash: function(response) {
|
||||
if(response) {
|
||||
let message = null;
|
||||
if(~~response === 1) {
|
||||
message = (
|
||||
'1 segment was moved to the trash.'
|
||||
);
|
||||
} else if(~~response > 1) {
|
||||
message = (
|
||||
'%$1d segments were moved to the trash.'
|
||||
).replace('%$1d', ~~response);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
}
|
||||
},
|
||||
onDelete: function(response) {
|
||||
if(response) {
|
||||
let message = null;
|
||||
if(~~response === 1) {
|
||||
message = (
|
||||
'1 segment was permanently deleted.'
|
||||
);
|
||||
} else if(~~response > 1) {
|
||||
message = (
|
||||
'%$1d segments were permanently deleted.'
|
||||
).replace('%$1d', ~~response);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
}
|
||||
},
|
||||
onRestore: function(response) {
|
||||
if(response) {
|
||||
let message = null;
|
||||
if(~~response === 1) {
|
||||
message = (
|
||||
'1 segment has been restored from the trash.'
|
||||
);
|
||||
} else if(~~response > 1) {
|
||||
message = (
|
||||
'%$1d segments have been restored from the trash.'
|
||||
).replace('%$1d', ~~response);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const item_actions = [
|
||||
{
|
||||
name: 'edit',
|
||||
label: 'Edit',
|
||||
link: function(item) {
|
||||
return (
|
||||
<Link to={ `/edit/${item.id}` }>Edit</Link>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'duplicate_segment',
|
||||
label: 'Duplicate',
|
||||
onClick: function(item, refresh) {
|
||||
return MailPoet.Ajax.post({
|
||||
endpoint: 'segments',
|
||||
action: 'duplicate',
|
||||
data: item.id
|
||||
}).done(function(response) {
|
||||
MailPoet.Notice.success(
|
||||
('List "%$1s" has been duplicated.').replace('%$1s', response.name)
|
||||
);
|
||||
refresh();
|
||||
});
|
||||
},
|
||||
display: function(segment) {
|
||||
return (segment.type !== 'wp_users');
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'synchronize_segment',
|
||||
label: 'Update',
|
||||
className: 'update',
|
||||
onClick: function(item, refresh) {
|
||||
MailPoet.Modal.loading(true);
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'segments',
|
||||
action: 'synchronize'
|
||||
}).done(function(response) {
|
||||
MailPoet.Modal.loading(false);
|
||||
if(response === true) {
|
||||
MailPoet.Notice.success(
|
||||
('List "%$1s" has been synchronized.').replace('%$1s', item.name)
|
||||
);
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
},
|
||||
display: function(segment) {
|
||||
return (segment.type === 'wp_users');
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'view_subscribers',
|
||||
link: function(item) {
|
||||
return (
|
||||
<a href={ item.subscribers_url }>View subscribers</a>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'trash',
|
||||
display: function(segment) {
|
||||
return (segment.type !== 'wp_users');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const bulk_actions = [
|
||||
];
|
||||
|
||||
const SegmentList = React.createClass({
|
||||
renderItem: function(segment, actions) {
|
||||
var rowClasses = classNames(
|
||||
'manage-column',
|
||||
'column-primary',
|
||||
'has-row-actions'
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<td className={ rowClasses }>
|
||||
<strong>
|
||||
<a>{ segment.name }</a>
|
||||
</strong>
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column-date" data-colname="Description">
|
||||
<abbr>{ segment.description }</abbr>
|
||||
</td>
|
||||
<td className="column-date" data-colname="Subscribed">
|
||||
<abbr>{ segment.subscribed || 0 }</abbr>
|
||||
</td>
|
||||
<td className="column-date" data-colname="Unconfirmed">
|
||||
<abbr>{ segment.unconfirmed || 0 }</abbr>
|
||||
</td>
|
||||
<td className="column-date" data-colname="Unsubscribed">
|
||||
<abbr>{ segment.unsubscribed || 0 }</abbr>
|
||||
</td>
|
||||
<td className="column-date" data-colname="Created on">
|
||||
<abbr>{ segment.created_at }</abbr>
|
||||
</td>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
Segments <Link className="add-new-h2" to="/new">New</Link>
|
||||
</h2>
|
||||
|
||||
<Listing
|
||||
location={ this.props.location }
|
||||
params={ this.props.params }
|
||||
messages={ messages }
|
||||
search={ false }
|
||||
limit={ 1000 }
|
||||
endpoint="segments"
|
||||
onRenderItem={ this.renderItem }
|
||||
columns={ columns }
|
||||
bulk_actions={ bulk_actions }
|
||||
item_actions={ item_actions }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SegmentList;
|
@ -15,10 +15,10 @@ define(
|
||||
|
||||
MailPoet.Router = new (Backbone.Router.extend({
|
||||
routes: {
|
||||
'mta(/:method)': 'sendingMethod',
|
||||
'mta(/:group)': 'sendingMethodGroup',
|
||||
'(:tab)': 'tabs',
|
||||
},
|
||||
sendingMethod: function(method) {
|
||||
sendingMethodGroup: function(group) {
|
||||
// display mta tab
|
||||
this.tabs('mta');
|
||||
|
||||
@ -30,13 +30,13 @@ define(
|
||||
// hide "save settings" button
|
||||
jQuery('.mailpoet_settings_submit').hide();
|
||||
|
||||
if(method === null) {
|
||||
if(group === null) {
|
||||
// show sending methods
|
||||
jQuery('.mailpoet_sending_methods').fadeIn();
|
||||
} else {
|
||||
// hide DKIM option when using MailPoet's API
|
||||
jQuery('#mailpoet_mta_dkim')[
|
||||
(method === 'mailpoet')
|
||||
(group === 'mailpoet')
|
||||
? 'hide'
|
||||
: 'show'
|
||||
]();
|
||||
@ -45,7 +45,7 @@ define(
|
||||
jQuery('.mailpoet_sending_methods').hide();
|
||||
|
||||
// display selected sending method's settings
|
||||
jQuery('.mailpoet_sending_method[data-method="'+ method +'"]').show();
|
||||
jQuery('.mailpoet_sending_method[data-group="'+ group +'"]').show();
|
||||
jQuery('#mailpoet_sending_method_setup').fadeIn();
|
||||
}
|
||||
},
|
||||
@ -73,7 +73,7 @@ define(
|
||||
}));
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
Backbone.history.start();
|
||||
if (!Backbone.History.started) Backbone.history.start();
|
||||
});
|
||||
}
|
||||
);
|
@ -37,14 +37,25 @@ define(
|
||||
'subscribed': 'Subscribed',
|
||||
'unsubscribed': 'Unsubscribed'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'segments',
|
||||
label: 'Lists',
|
||||
type: 'selection',
|
||||
placeholder: "Select a list",
|
||||
endpoint: "segments",
|
||||
multiple: true,
|
||||
filter: function(segment) {
|
||||
return !!(!segment.deleted_at);
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
var messages = {
|
||||
updated: function() {
|
||||
onUpdate: function() {
|
||||
MailPoet.Notice.success('Subscriber successfully updated!');
|
||||
},
|
||||
created: function() {
|
||||
onCreate: function() {
|
||||
MailPoet.Notice.success('Subscriber successfully added!');
|
||||
}
|
||||
};
|
||||
@ -52,18 +63,22 @@ define(
|
||||
var Link = Router.Link;
|
||||
|
||||
var SubscriberForm = React.createClass({
|
||||
mixins: [
|
||||
Router.History
|
||||
],
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
Subscriber <Link className="add-new-h2" to="/">Back to list</Link>
|
||||
Subscriber
|
||||
</h2>
|
||||
|
||||
<Form
|
||||
endpoint="subscribers"
|
||||
fields={ fields }
|
||||
params={ this.props.params }
|
||||
messages={ messages } />
|
||||
messages={ messages }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
170
assets/js/src/subscribers/importExport/export.js
Normal file
@ -0,0 +1,170 @@
|
||||
define(
|
||||
[
|
||||
'underscore',
|
||||
'jquery',
|
||||
'mailpoet',
|
||||
'handlebars',
|
||||
'select2'
|
||||
],
|
||||
function (
|
||||
_,
|
||||
jQuery,
|
||||
MailPoet,
|
||||
Handlebars
|
||||
) {
|
||||
if (!jQuery("#mailpoet_subscribers_export").length) {
|
||||
return;
|
||||
}
|
||||
jQuery(document).ready(function () {
|
||||
if (!exportData.segments) {
|
||||
return;
|
||||
}
|
||||
var subscribers_export_template =
|
||||
Handlebars.compile(jQuery('#mailpoet_subscribers_export_template').html());
|
||||
|
||||
//render template
|
||||
jQuery('#mailpoet_subscribers_export > div.inside').html(subscribers_export_template(exportData));
|
||||
|
||||
// define reusable variables
|
||||
var segmentsContainerElement = jQuery("#export_lists"),
|
||||
subscriberFieldsContainerElement = jQuery("#export_columns"),
|
||||
exportConfirmedOptionElement = jQuery(':radio[name="option_confirmed"]'),
|
||||
groupBySegmentOptionElement = jQuery(':checkbox[name="option_group_by_list"]'),
|
||||
nextStepButton = jQuery("a.mailpoet_export_process"),
|
||||
renderSegmentsAndFields = function (container, data) {
|
||||
if (container.data('select2')) {
|
||||
container
|
||||
.html('')
|
||||
.select2('destroy');
|
||||
}
|
||||
container
|
||||
.select2({
|
||||
data: data,
|
||||
width: '20em',
|
||||
templateResult: function (item) {
|
||||
return (item.subscriberCount > 0)
|
||||
? item.name + ' (' + item.subscriberCount + ')'
|
||||
: item.name;
|
||||
},
|
||||
templateSelection: function (item) {
|
||||
return (item.subscriberCount > 0)
|
||||
? item.name + ' (' + item.subscriberCount + ')'
|
||||
: item.name;
|
||||
}
|
||||
})
|
||||
.on('select2:selecting', function (selectEvent) {
|
||||
var selectElement = this,
|
||||
selectedOptionId = selectEvent.params.args.data.id,
|
||||
fieldsToExclude = [
|
||||
'select',
|
||||
'deselect'
|
||||
];
|
||||
if (_.contains(fieldsToExclude, selectedOptionId)) {
|
||||
selectEvent.preventDefault();
|
||||
if (selectedOptionId === 'deselect') {
|
||||
jQuery(selectElement).val('').trigger('change');
|
||||
} else {
|
||||
var allOptions = [];
|
||||
_.each(container.find('option'), function (field) {
|
||||
if (!_.contains(fieldsToExclude, field.value)) {
|
||||
allOptions.push(field.value);
|
||||
}
|
||||
});
|
||||
jQuery(selectElement).val(allOptions).trigger('change');
|
||||
}
|
||||
jQuery(selectElement).select2('close');
|
||||
}
|
||||
})
|
||||
.on('change', function () {
|
||||
if ((exportData.segments && segmentsContainerElement.select2('data').length && subscriberFieldsContainerElement.select2('data').length)
|
||||
||
|
||||
(!exportData.segments && subscriberFieldsContainerElement.select2('data').length)
|
||||
) {
|
||||
toggleNextStepButton('on');
|
||||
}
|
||||
else {
|
||||
toggleNextStepButton('off');
|
||||
}
|
||||
|
||||
if (segmentsContainerElement.select2('data').length > 1 && exportData.groupBySegmentOption) {
|
||||
jQuery('.mailpoet_group_by_list').show();
|
||||
}
|
||||
else if (exportData.groupBySegmentOption) {
|
||||
jQuery('.mailpoet_group_by_list').hide();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
renderSegmentsAndFields(subscriberFieldsContainerElement, subscriberFieldsSelect2);
|
||||
renderSegmentsAndFields(segmentsContainerElement, segments);
|
||||
|
||||
subscriberFieldsContainerElement.select2('val', [
|
||||
'status',
|
||||
'email',
|
||||
'first_name',
|
||||
'last_name'
|
||||
]);
|
||||
|
||||
exportConfirmedOptionElement.change(function () {
|
||||
var selectedSegments = segmentsContainerElement.val();
|
||||
if (this.value == 1) {
|
||||
exportData.exportConfirmedOption = true;
|
||||
renderSegmentsAndFields(segmentsContainerElement, segmentsWithConfirmedSubscribers);
|
||||
}
|
||||
else {
|
||||
exportData.exportConfirmedOption = false;
|
||||
renderSegmentsAndFields(segmentsContainerElement, segments);
|
||||
}
|
||||
segmentsContainerElement.select2('val', selectedSegments);
|
||||
});
|
||||
|
||||
function toggleNextStepButton(condition) {
|
||||
var disabled = 'button-disabled';
|
||||
if (condition === 'on') {
|
||||
nextStepButton.removeClass(disabled);
|
||||
}
|
||||
else {
|
||||
nextStepButton.addClass(disabled);
|
||||
}
|
||||
}
|
||||
|
||||
nextStepButton.click(function () {
|
||||
if (jQuery(this).hasClass('button-disabled')) {
|
||||
return;
|
||||
}
|
||||
MailPoet.Modal.loading(true);
|
||||
MailPoet.Ajax
|
||||
.post({
|
||||
endpoint: 'ImportExport',
|
||||
action: 'processExport',
|
||||
data: JSON.stringify({
|
||||
'export_confirmed_option': exportData.exportConfirmedOption,
|
||||
'export_format_option': jQuery(':radio[name="option_format"]:checked').val(),
|
||||
'group_by_segment_option': (groupBySegmentOptionElement.is(":visible")) ? groupBySegmentOptionElement.prop('checked') : false,
|
||||
'segments': (exportData.segments) ? segmentsContainerElement.val() : false,
|
||||
'subscriber_fields': subscriberFieldsContainerElement.val()
|
||||
})
|
||||
})
|
||||
.done(function (response) {
|
||||
MailPoet.Modal.loading(false);
|
||||
if (response.result === false) {
|
||||
MailPoet.Notice.error(response.error);
|
||||
} else {
|
||||
resultMessage = MailPoetI18n.exportMessage
|
||||
.replace('%1$s', '<strong>' + response.data.totalExported + '</strong>')
|
||||
.replace('[link]', '<a href="' + response.data.exportFileURL + '" target="_blank" >')
|
||||
.replace('[/link]', '</a>');
|
||||
jQuery('#export_result_notice > ul > li').html(resultMessage);
|
||||
jQuery('#export_result_notice').show();
|
||||
window.location.href = response.data.exportFileURL;
|
||||
}
|
||||
})
|
||||
.error(function (error) {
|
||||
MailPoet.Modal.loading(false);
|
||||
MailPoet.Notice.error(
|
||||
MailPoetI18n.serverError + error.statusText.toLowerCase() + '.'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
1157
assets/js/src/subscribers/importExport/import.js
Normal file
@ -1,313 +1,331 @@
|
||||
define(
|
||||
[
|
||||
'react',
|
||||
'react-router',
|
||||
'listing/listing.jsx',
|
||||
'form/fields/selection.jsx',
|
||||
'classnames',
|
||||
'mailpoet',
|
||||
'jquery',
|
||||
'select2'
|
||||
],
|
||||
function(
|
||||
React,
|
||||
Router,
|
||||
Listing,
|
||||
Selection,
|
||||
classNames,
|
||||
MailPoet,
|
||||
jQuery
|
||||
) {
|
||||
var Link = Router.Link;
|
||||
import React from 'react'
|
||||
import { Router, Route, Link } from 'react-router'
|
||||
|
||||
var columns = [
|
||||
{
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'first_name',
|
||||
label: 'Firstname',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'last_name',
|
||||
label: 'Lastname',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
label: 'Status',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'segments',
|
||||
label: 'Lists',
|
||||
sortable: false
|
||||
},
|
||||
import jQuery from 'jquery'
|
||||
import MailPoet from 'mailpoet'
|
||||
import classNames from 'classnames'
|
||||
|
||||
{
|
||||
name: 'created_at',
|
||||
label: 'Subscribed on',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'updated_at',
|
||||
label: 'Last modified on',
|
||||
sortable: true
|
||||
},
|
||||
];
|
||||
import Listing from 'listing/listing.jsx'
|
||||
import Selection from 'form/fields/selection.jsx'
|
||||
|
||||
var messages = {
|
||||
onDelete: function(response) {
|
||||
var count = ~~response.subscribers;
|
||||
var message = null;
|
||||
const columns = [
|
||||
{
|
||||
name: 'email',
|
||||
label: 'Subscriber',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'status',
|
||||
label: 'Status',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'segments',
|
||||
label: 'Lists',
|
||||
sortable: false
|
||||
},
|
||||
|
||||
if(count === 1) {
|
||||
message = (
|
||||
'1 subscriber was moved to the trash.'
|
||||
).replace('%$1d', count);
|
||||
} else if(count > 1) {
|
||||
message = (
|
||||
'%$1d subscribers were moved to the trash.'
|
||||
).replace('%$1d', count);
|
||||
}
|
||||
{
|
||||
name: 'created_at',
|
||||
label: 'Subscribed on',
|
||||
sortable: true
|
||||
},
|
||||
{
|
||||
name: 'updated_at',
|
||||
label: 'Last modified on',
|
||||
sortable: true
|
||||
},
|
||||
];
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
},
|
||||
onConfirmDelete: function(response) {
|
||||
var count = ~~response.subscribers;
|
||||
var message = null;
|
||||
|
||||
if(count === 1) {
|
||||
message = (
|
||||
'1 subscriber was permanently deleted.'
|
||||
).replace('%$1d', count);
|
||||
} else if(count > 1) {
|
||||
message = (
|
||||
'%$1d subscribers were permanently deleted.'
|
||||
).replace('%$1d', count);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
},
|
||||
onRestore: function(response) {
|
||||
var count = ~~response.subscribers;
|
||||
var message = null;
|
||||
|
||||
if(count === 1) {
|
||||
message = (
|
||||
'1 subscriber has been restored from the trash.'
|
||||
).replace('%$1d', count);
|
||||
} else if(count > 1) {
|
||||
message = (
|
||||
'%$1d subscribers have been restored from the trash.'
|
||||
).replace('%$1d', count);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var bulk_actions = [
|
||||
{
|
||||
name: 'moveToList',
|
||||
label: 'Move to list...',
|
||||
onSelect: function() {
|
||||
var field = {
|
||||
id: 'move_to_segment',
|
||||
endpoint: 'segments'
|
||||
};
|
||||
|
||||
return (
|
||||
<Selection field={ field }/>
|
||||
);
|
||||
},
|
||||
getData: function() {
|
||||
return {
|
||||
segment_id: ~~(jQuery('#move_to_segment').val())
|
||||
}
|
||||
},
|
||||
onSuccess: function(response) {
|
||||
MailPoet.Notice.success(
|
||||
'%$1d subscribers were moved to list <strong>%$2s</strong>.'
|
||||
.replace('%$1d', ~~response.subscribers)
|
||||
.replace('%$2s', response.segment)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'addToList',
|
||||
label: 'Add to list...',
|
||||
onSelect: function() {
|
||||
var field = {
|
||||
id: 'add_to_segment',
|
||||
endpoint: 'segments'
|
||||
};
|
||||
|
||||
return (
|
||||
<Selection field={ field }/>
|
||||
);
|
||||
},
|
||||
getData: function() {
|
||||
return {
|
||||
segment_id: ~~(jQuery('#add_to_segment').val())
|
||||
}
|
||||
},
|
||||
onSuccess: function(response) {
|
||||
MailPoet.Notice.success(
|
||||
'%$1d subscribers were added to list <strong>%$2s</strong>.'
|
||||
.replace('%$1d', ~~response.subscribers)
|
||||
.replace('%$2s', response.segment)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'removeFromList',
|
||||
label: 'Remove from list...',
|
||||
onSelect: function() {
|
||||
var field = {
|
||||
id: 'remove_from_segment',
|
||||
endpoint: 'segments'
|
||||
};
|
||||
|
||||
return (
|
||||
<Selection field={ field }/>
|
||||
);
|
||||
},
|
||||
getData: function() {
|
||||
return {
|
||||
segment_id: ~~(jQuery('#remove_from_segment').val())
|
||||
}
|
||||
},
|
||||
onSuccess: function(response) {
|
||||
MailPoet.Notice.success(
|
||||
'%$1d subscribers were removed from list <strong>%$2s</strong>.'
|
||||
.replace('%$1d', ~~response.subscribers)
|
||||
.replace('%$2s', response.segment)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'removeFromAllLists',
|
||||
label: 'Remove from all lists',
|
||||
onSuccess: function(response) {
|
||||
MailPoet.Notice.success(
|
||||
'%$1d subscribers were removed from all lists.'
|
||||
.replace('%$1d', ~~response.subscribers)
|
||||
.replace('%$2s', response.segment)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'confirmUnconfirmed',
|
||||
label: 'Confirm unconfirmed',
|
||||
onSuccess: function(response) {
|
||||
MailPoet.Notice.success(
|
||||
'%$1d subscribers have been confirmed.'
|
||||
.replace('%$1d', ~~response.subscribers)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'trash',
|
||||
label: 'Trash',
|
||||
getData: function() {
|
||||
return {
|
||||
confirm: false
|
||||
}
|
||||
},
|
||||
onSuccess: messages.onDelete
|
||||
}
|
||||
];
|
||||
|
||||
var SubscriberList = React.createClass({
|
||||
renderItem: function(subscriber, actions) {
|
||||
var row_classes = classNames(
|
||||
'manage-column',
|
||||
'column-primary',
|
||||
'has-row-actions'
|
||||
);
|
||||
|
||||
var status = '';
|
||||
|
||||
switch(subscriber.status) {
|
||||
case 'subscribed':
|
||||
status = 'Subscribed';
|
||||
break;
|
||||
|
||||
case 'unconfirmed':
|
||||
status = 'Unconfirmed';
|
||||
break;
|
||||
|
||||
case 'unsubscribed':
|
||||
status = 'Unsubscribed';
|
||||
break;
|
||||
}
|
||||
|
||||
var segments = mailpoet_segments.filter(function(segment) {
|
||||
return (jQuery.inArray(segment.id, subscriber.segments) !== -1);
|
||||
}).map(function(segment) {
|
||||
return segment.name;
|
||||
}).join(', ');
|
||||
|
||||
var row_actions = false;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<td className={ row_classes }>
|
||||
<strong><Link to={ `/edit/${ subscriber.id }` }>
|
||||
{ subscriber.email }
|
||||
</Link></strong>
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column" data-colname="First name">
|
||||
{ subscriber.first_name }
|
||||
</td>
|
||||
<td className="column" data-colname="Last name">
|
||||
{ subscriber.last_name }
|
||||
</td>
|
||||
<td className="column" data-colname="Status">
|
||||
{ status }
|
||||
</td>
|
||||
<td className="column" data-colname="Lists">
|
||||
{ segments }
|
||||
</td>
|
||||
<td className="column-date" data-colname="Subscribed on">
|
||||
<abbr>{ subscriber.created_at }</abbr>
|
||||
</td>
|
||||
<td className="column-date" data-colname="Last modified on">
|
||||
<abbr>{ subscriber.updated_at }</abbr>
|
||||
</td>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
Subscribers <Link className="add-new-h2" to="/new">New</Link>
|
||||
</h2>
|
||||
|
||||
<Listing
|
||||
endpoint="subscribers"
|
||||
onRenderItem={ this.renderItem }
|
||||
columns={ columns }
|
||||
bulk_actions={ bulk_actions }
|
||||
messages={ messages }
|
||||
/>
|
||||
</div>
|
||||
const messages = {
|
||||
onTrash: function(response) {
|
||||
if(response) {
|
||||
var message = null;
|
||||
if(~~response === 1) {
|
||||
message = (
|
||||
'1 subscriber was moved to the trash.'
|
||||
);
|
||||
} else if(~~response > 1) {
|
||||
message = (
|
||||
'%$1d subscribers were moved to the trash.'
|
||||
).replace('%$1d', ~~response);
|
||||
}
|
||||
});
|
||||
|
||||
return SubscriberList;
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
}
|
||||
},
|
||||
onDelete: function(response) {
|
||||
if(response) {
|
||||
var message = null;
|
||||
if(~~response === 1) {
|
||||
message = (
|
||||
'1 subscriber was permanently deleted.'
|
||||
);
|
||||
} else if(~~response > 1) {
|
||||
message = (
|
||||
'%$1d subscribers were permanently deleted.'
|
||||
).replace('%$1d', ~~response);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
}
|
||||
},
|
||||
onRestore: function(response) {
|
||||
if(response) {
|
||||
var message = null;
|
||||
if(~~response === 1) {
|
||||
message = (
|
||||
'1 subscriber has been restored from the trash.'
|
||||
);
|
||||
} else if(~~response > 1) {
|
||||
message = (
|
||||
'%$1d subscribers have been restored from the trash.'
|
||||
).replace('%$1d', ~~response);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const bulk_actions = [
|
||||
{
|
||||
name: 'moveToList',
|
||||
label: 'Move to list...',
|
||||
onSelect: function() {
|
||||
let field = {
|
||||
id: 'move_to_segment',
|
||||
endpoint: 'segments',
|
||||
filter: function(segment) {
|
||||
return !!(
|
||||
!segment.deleted_at && segment.type === 'default'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Selection field={ field }/>
|
||||
);
|
||||
},
|
||||
getData: function() {
|
||||
return {
|
||||
segment_id: ~~(jQuery('#move_to_segment').val())
|
||||
}
|
||||
},
|
||||
onSuccess: function(response) {
|
||||
MailPoet.Notice.success(
|
||||
'%$1d subscribers were moved to list <strong>%$2s</strong>.'
|
||||
.replace('%$1d', ~~response.subscribers)
|
||||
.replace('%$2s', response.segment)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'addToList',
|
||||
label: 'Add to list...',
|
||||
onSelect: function() {
|
||||
let field = {
|
||||
id: 'add_to_segment',
|
||||
endpoint: 'segments',
|
||||
filter: function(segment) {
|
||||
return !!(
|
||||
!segment.deleted_at && segment.type === 'default'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Selection field={ field }/>
|
||||
);
|
||||
},
|
||||
getData: function() {
|
||||
return {
|
||||
segment_id: ~~(jQuery('#add_to_segment').val())
|
||||
}
|
||||
},
|
||||
onSuccess: function(response) {
|
||||
MailPoet.Notice.success(
|
||||
'%$1d subscribers were added to list <strong>%$2s</strong>.'
|
||||
.replace('%$1d', ~~response.subscribers)
|
||||
.replace('%$2s', response.segment)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'removeFromList',
|
||||
label: 'Remove from list...',
|
||||
onSelect: function() {
|
||||
let field = {
|
||||
id: 'remove_from_segment',
|
||||
endpoint: 'segments',
|
||||
filter: function(segment) {
|
||||
return !!(
|
||||
segment.type === 'default'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Selection field={ field }/>
|
||||
);
|
||||
},
|
||||
getData: function() {
|
||||
return {
|
||||
segment_id: ~~(jQuery('#remove_from_segment').val())
|
||||
}
|
||||
},
|
||||
onSuccess: function(response) {
|
||||
MailPoet.Notice.success(
|
||||
'%$1d subscribers were removed from list <strong>%$2s</strong>.'
|
||||
.replace('%$1d', ~~response.subscribers)
|
||||
.replace('%$2s', response.segment)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'removeFromAllLists',
|
||||
label: 'Remove from all lists',
|
||||
onSuccess: function(response) {
|
||||
MailPoet.Notice.success(
|
||||
'%$1d subscribers were removed from all lists.'
|
||||
.replace('%$1d', ~~response)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'confirmUnconfirmed',
|
||||
label: 'Confirm unconfirmed',
|
||||
onSuccess: function(response) {
|
||||
MailPoet.Notice.success(
|
||||
'%$1d subscribers have been confirmed.'
|
||||
.replace('%$1d', ~~response)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'trash',
|
||||
label: 'Trash',
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
|
||||
const item_actions = [
|
||||
{
|
||||
name: 'edit',
|
||||
label: 'Edit',
|
||||
link: function(item) {
|
||||
return (
|
||||
<Link to={ `/edit/${item.id}` }>Edit</Link>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'trash'
|
||||
}
|
||||
];
|
||||
|
||||
const SubscriberList = React.createClass({
|
||||
renderItem: function(subscriber, actions) {
|
||||
let row_classes = classNames(
|
||||
'manage-column',
|
||||
'column-primary',
|
||||
'has-row-actions',
|
||||
'column-username'
|
||||
);
|
||||
|
||||
let status = '';
|
||||
|
||||
switch(subscriber.status) {
|
||||
case 'subscribed':
|
||||
status = 'Subscribed';
|
||||
break;
|
||||
|
||||
case 'unconfirmed':
|
||||
status = 'Unconfirmed';
|
||||
break;
|
||||
|
||||
case 'unsubscribed':
|
||||
status = 'Unsubscribed';
|
||||
break;
|
||||
}
|
||||
|
||||
let segments = mailpoet_segments.filter(function(segment) {
|
||||
return (jQuery.inArray(segment.id, subscriber.segments) !== -1);
|
||||
}).map(function(segment) {
|
||||
return segment.name;
|
||||
}).join(', ');
|
||||
|
||||
let avatar = false;
|
||||
if(subscriber.avatar_url) {
|
||||
avatar = (
|
||||
<img
|
||||
className="avatar"
|
||||
src={ subscriber.avatar_url }
|
||||
title=""
|
||||
width="32"
|
||||
height="32"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<td className={ row_classes }>
|
||||
<strong><Link to={ `/edit/${ subscriber.id }` }>
|
||||
{ subscriber.email }
|
||||
</Link></strong>
|
||||
<p style={{margin: 0}}>
|
||||
{ subscriber.first_name } { subscriber.last_name }
|
||||
</p>
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column" data-colname="Status">
|
||||
{ status }
|
||||
</td>
|
||||
<td className="column" data-colname="Lists">
|
||||
{ segments }
|
||||
</td>
|
||||
<td className="column-date" data-colname="Subscribed on">
|
||||
<abbr>{ subscriber.created_at }</abbr>
|
||||
</td>
|
||||
<td className="column-date" data-colname="Last modified on">
|
||||
<abbr>{ subscriber.updated_at }</abbr>
|
||||
</td>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
onGetItems: function(count) {
|
||||
jQuery('#mailpoet_export_button')[(count > 0) ? 'show' : 'hide']();
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<div>
|
||||
<h2 className="title">
|
||||
Subscribers <Link className="add-new-h2" to="/new">New</Link>
|
||||
<a className="add-new-h2" href="?page=mailpoet-import#step1">Import</a>
|
||||
<a id="mailpoet_export_button" className="add-new-h2" href="?page=mailpoet-export">Export</a>
|
||||
</h2>
|
||||
|
||||
<Listing
|
||||
location={ this.props.location }
|
||||
params={ this.props.params }
|
||||
endpoint="subscribers"
|
||||
onRenderItem={ this.renderItem }
|
||||
columns={ columns }
|
||||
bulk_actions={ bulk_actions }
|
||||
item_actions={ item_actions }
|
||||
messages={ messages }
|
||||
onGetItems={ this.onGetItems }
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SubscriberList;
|
@ -5,15 +5,15 @@ import SubscriberList from 'subscribers/list.jsx'
|
||||
import SubscriberForm from 'subscribers/form.jsx'
|
||||
import createHashHistory from 'history/lib/createHashHistory'
|
||||
|
||||
let history = createHashHistory({ queryKey: false })
|
||||
const history = createHashHistory({ queryKey: false })
|
||||
|
||||
const App = React.createClass({
|
||||
render() {
|
||||
return this.props.children
|
||||
return this.props.children;
|
||||
}
|
||||
});
|
||||
|
||||
let container = document.getElementById('subscribers_container');
|
||||
const container = document.getElementById('subscribers_container')
|
||||
|
||||
if(container) {
|
||||
ReactDOM.render((
|
||||
|
264
assets/js/src/vendor/jquery.sticky-kit.js
vendored
Normal file
@ -0,0 +1,264 @@
|
||||
// Generated by CoffeeScript 1.9.2
|
||||
|
||||
/**
|
||||
@license Sticky-kit v1.1.2 | WTFPL | Leaf Corcoran 2015 | http://leafo.net
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var $, win;
|
||||
|
||||
$ = this.jQuery || window.jQuery;
|
||||
|
||||
win = $(window);
|
||||
|
||||
$.fn.stick_in_parent = function(opts) {
|
||||
var doc, elm, enable_bottoming, fn, i, inner_scrolling, len, manual_spacer, offset_top, outer_width, parent_selector, recalc_every, sticky_class;
|
||||
if (opts == null) {
|
||||
opts = {};
|
||||
}
|
||||
sticky_class = opts.sticky_class, inner_scrolling = opts.inner_scrolling, recalc_every = opts.recalc_every, parent_selector = opts.parent, offset_top = opts.offset_top, manual_spacer = opts.spacer, enable_bottoming = opts.bottoming;
|
||||
if (offset_top == null) {
|
||||
offset_top = 0;
|
||||
}
|
||||
if (parent_selector == null) {
|
||||
parent_selector = void 0;
|
||||
}
|
||||
if (inner_scrolling == null) {
|
||||
inner_scrolling = true;
|
||||
}
|
||||
if (sticky_class == null) {
|
||||
sticky_class = "is_stuck";
|
||||
}
|
||||
doc = $(document);
|
||||
if (enable_bottoming == null) {
|
||||
enable_bottoming = true;
|
||||
}
|
||||
outer_width = function(el) {
|
||||
var _el, computed, w;
|
||||
if (window.getComputedStyle) {
|
||||
_el = el[0];
|
||||
computed = window.getComputedStyle(el[0]);
|
||||
w = parseFloat(computed.getPropertyValue("width")) + parseFloat(computed.getPropertyValue("margin-left")) + parseFloat(computed.getPropertyValue("margin-right"));
|
||||
if (computed.getPropertyValue("box-sizing") !== "border-box") {
|
||||
w += parseFloat(computed.getPropertyValue("border-left-width")) + parseFloat(computed.getPropertyValue("border-right-width")) + parseFloat(computed.getPropertyValue("padding-left")) + parseFloat(computed.getPropertyValue("padding-right"));
|
||||
}
|
||||
return w;
|
||||
} else {
|
||||
return el.outerWidth(true);
|
||||
}
|
||||
};
|
||||
fn = function(elm, padding_bottom, parent_top, parent_height, top, height, el_float, detached) {
|
||||
var bottomed, detach, fixed, last_pos, last_scroll_height, offset, parent, recalc, recalc_and_tick, recalc_counter, spacer, tick;
|
||||
if (elm.data("sticky_kit")) {
|
||||
return;
|
||||
}
|
||||
elm.data("sticky_kit", true);
|
||||
last_scroll_height = doc.height();
|
||||
parent = elm.parent();
|
||||
if (parent_selector != null) {
|
||||
parent = parent.closest(parent_selector);
|
||||
}
|
||||
if (!parent.length) {
|
||||
throw "failed to find stick parent";
|
||||
}
|
||||
fixed = false;
|
||||
bottomed = false;
|
||||
spacer = manual_spacer != null ? manual_spacer && elm.closest(manual_spacer) : $("<div />");
|
||||
if (spacer) {
|
||||
spacer.css('position', elm.css('position'));
|
||||
}
|
||||
recalc = function() {
|
||||
var border_top, padding_top, restore;
|
||||
if (detached) {
|
||||
return;
|
||||
}
|
||||
last_scroll_height = doc.height();
|
||||
border_top = parseInt(parent.css("border-top-width"), 10);
|
||||
padding_top = parseInt(parent.css("padding-top"), 10);
|
||||
padding_bottom = parseInt(parent.css("padding-bottom"), 10);
|
||||
parent_top = parent.offset().top + border_top + padding_top;
|
||||
parent_height = parent.height();
|
||||
if (fixed) {
|
||||
fixed = false;
|
||||
bottomed = false;
|
||||
if (manual_spacer == null) {
|
||||
elm.insertAfter(spacer);
|
||||
spacer.detach();
|
||||
}
|
||||
elm.css({
|
||||
position: "",
|
||||
top: "",
|
||||
width: "",
|
||||
bottom: ""
|
||||
}).removeClass(sticky_class);
|
||||
restore = true;
|
||||
}
|
||||
top = elm.offset().top - (parseInt(elm.css("margin-top"), 10) || 0) - offset_top;
|
||||
height = elm.outerHeight(true);
|
||||
el_float = elm.css("float");
|
||||
if (spacer) {
|
||||
spacer.css({
|
||||
width: outer_width(elm),
|
||||
height: height,
|
||||
display: elm.css("display"),
|
||||
"vertical-align": elm.css("vertical-align"),
|
||||
"float": el_float
|
||||
});
|
||||
}
|
||||
if (restore) {
|
||||
return tick();
|
||||
}
|
||||
};
|
||||
recalc();
|
||||
|
||||
last_pos = void 0;
|
||||
offset = offset_top;
|
||||
recalc_counter = recalc_every;
|
||||
tick = function() {
|
||||
var css, delta, recalced, scroll, will_bottom, win_height;
|
||||
if (detached) {
|
||||
return;
|
||||
}
|
||||
recalced = false;
|
||||
if (recalc_counter != null) {
|
||||
recalc_counter -= 1;
|
||||
if (recalc_counter <= 0) {
|
||||
recalc_counter = recalc_every;
|
||||
recalc();
|
||||
recalced = true;
|
||||
}
|
||||
}
|
||||
if (!recalced && doc.height() !== last_scroll_height) {
|
||||
recalc();
|
||||
recalced = true;
|
||||
}
|
||||
scroll = win.scrollTop();
|
||||
if (last_pos != null) {
|
||||
delta = scroll - last_pos;
|
||||
}
|
||||
last_pos = scroll;
|
||||
if (fixed) {
|
||||
if (enable_bottoming) {
|
||||
will_bottom = scroll + height + offset > parent_height + parent_top;
|
||||
if (bottomed && !will_bottom) {
|
||||
bottomed = false;
|
||||
elm.css({
|
||||
position: "fixed",
|
||||
bottom: "",
|
||||
top: offset
|
||||
}).trigger("sticky_kit:unbottom");
|
||||
}
|
||||
}
|
||||
if (scroll < top) {
|
||||
fixed = false;
|
||||
offset = offset_top;
|
||||
if (manual_spacer == null) {
|
||||
if (el_float === "left" || el_float === "right") {
|
||||
elm.insertAfter(spacer);
|
||||
}
|
||||
spacer.detach();
|
||||
}
|
||||
css = {
|
||||
position: "",
|
||||
width: "",
|
||||
top: ""
|
||||
};
|
||||
elm.css(css).removeClass(sticky_class).trigger("sticky_kit:unstick");
|
||||
}
|
||||
if (inner_scrolling) {
|
||||
win_height = win.height();
|
||||
if (height + offset_top > win_height) {
|
||||
if (!bottomed) {
|
||||
offset -= delta;
|
||||
offset = Math.max(win_height - height, offset);
|
||||
offset = Math.min(offset_top, offset);
|
||||
if (fixed) {
|
||||
elm.css({
|
||||
top: offset + "px"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (scroll > top) {
|
||||
fixed = true;
|
||||
css = {
|
||||
position: "fixed",
|
||||
top: offset
|
||||
};
|
||||
css.width = elm.css("box-sizing") === "border-box" ? elm.outerWidth() + "px" : elm.width() + "px";
|
||||
elm.css(css).addClass(sticky_class);
|
||||
if (manual_spacer == null) {
|
||||
elm.after(spacer);
|
||||
if (el_float === "left" || el_float === "right") {
|
||||
spacer.append(elm);
|
||||
}
|
||||
}
|
||||
elm.trigger("sticky_kit:stick");
|
||||
}
|
||||
}
|
||||
if (fixed && enable_bottoming) {
|
||||
if (will_bottom == null) {
|
||||
will_bottom = scroll + height + offset > parent_height + parent_top;
|
||||
}
|
||||
if (!bottomed && will_bottom) {
|
||||
bottomed = true;
|
||||
if (parent.css("position") === "static") {
|
||||
parent.css({
|
||||
position: "relative"
|
||||
});
|
||||
}
|
||||
return elm.css({
|
||||
position: "absolute",
|
||||
bottom: padding_bottom,
|
||||
top: "auto"
|
||||
}).trigger("sticky_kit:bottom");
|
||||
}
|
||||
}
|
||||
};
|
||||
recalc_and_tick = function() {
|
||||
recalc();
|
||||
return tick();
|
||||
};
|
||||
detach = function() {
|
||||
detached = true;
|
||||
win.off("touchmove", tick);
|
||||
win.off("scroll", tick);
|
||||
win.off("resize", recalc_and_tick);
|
||||
$(document.body).off("sticky_kit:recalc", recalc_and_tick);
|
||||
elm.off("sticky_kit:detach", detach);
|
||||
elm.removeData("sticky_kit");
|
||||
elm.css({
|
||||
position: "",
|
||||
bottom: "",
|
||||
top: "",
|
||||
width: ""
|
||||
});
|
||||
parent.position("position", "");
|
||||
if (fixed) {
|
||||
if (manual_spacer == null) {
|
||||
if (el_float === "left" || el_float === "right") {
|
||||
elm.insertAfter(spacer);
|
||||
}
|
||||
spacer.remove();
|
||||
}
|
||||
return elm.removeClass(sticky_class);
|
||||
}
|
||||
};
|
||||
win.on("touchmove", tick);
|
||||
win.on("scroll", tick);
|
||||
win.on("resize", recalc_and_tick);
|
||||
$(document.body).on("sticky_kit:recalc", recalc_and_tick);
|
||||
elm.on("sticky_kit:detach", detach);
|
||||
return setTimeout(tick, 0);
|
||||
};
|
||||
for (i = 0, len = this.length; i < len; i++) {
|
||||
elm = this[i];
|
||||
fn($(elm));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
}).call(this);
|
||||
|
42
build
@ -1,42 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Remove previous build.
|
||||
rm wysija-newsletters.zip;
|
||||
|
||||
# Create temp dir.
|
||||
mkdir wysija-newsletters;
|
||||
|
||||
# Production assets.
|
||||
./do compile:all;
|
||||
|
||||
# Production libraries.
|
||||
rm -rf vendor;
|
||||
rm composer.lock;
|
||||
./composer.phar install --no-dev;
|
||||
|
||||
# Copy release folders.
|
||||
cp -rf lang wysija-newsletters;
|
||||
cp -rfL assets wysija-newsletters;
|
||||
cp -rf lib wysija-newsletters;
|
||||
cp -rf vendor wysija-newsletters;
|
||||
cp -rf views wysija-newsletters;
|
||||
rm -rf wysija-newsletters/assets/css/src;
|
||||
rm -rf wysija-newsletters/assets/js/src;
|
||||
|
||||
# Copy release files.
|
||||
cp LICENSE wysija-newsletters;
|
||||
cp index.php wysija-newsletters;
|
||||
cp mailpoet.php wysija-newsletters;
|
||||
cp readme.txt wysija-newsletters;
|
||||
cp uninstall.php wysija-newsletters;
|
||||
|
||||
# Zip final release.
|
||||
zip -r wysija-newsletters.zip wysija-newsletters;
|
||||
|
||||
# Remove temp dir.
|
||||
rm -rf wysija-newsletters;
|
||||
|
||||
# Reinstall dev dependencies.
|
||||
rm composer.lock;
|
||||
./composer.phar install;
|
||||
./do install;
|
42
build.sh
Executable file
@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
|
||||
plugin_name='mailpoet'
|
||||
|
||||
# Remove previous build.
|
||||
rm $plugin_name.zip
|
||||
|
||||
# Create temp dir.
|
||||
mkdir $plugin_name
|
||||
|
||||
# Production assets.
|
||||
rm -rf node_modules
|
||||
npm install
|
||||
./do compile:all
|
||||
|
||||
# Production libraries.
|
||||
./composer.phar install --no-dev
|
||||
|
||||
# Copy release folders.
|
||||
cp -Rf lang $plugin_name
|
||||
cp -RfL assets $plugin_name
|
||||
cp -Rf lib $plugin_name
|
||||
cp -Rf vendor $plugin_name
|
||||
cp -Rf views $plugin_name
|
||||
rm -Rf $plugin_name/assets/css/src
|
||||
rm -Rf $plugin_name/assets/js/src
|
||||
|
||||
# Copy release files.
|
||||
cp LICENSE $plugin_name
|
||||
cp index.php $plugin_name
|
||||
cp $plugin_name.php $plugin_name
|
||||
cp readme.txt $plugin_name
|
||||
cp uninstall.php $plugin_name
|
||||
|
||||
# Zip final release.
|
||||
zip -r $plugin_name.zip $plugin_name
|
||||
|
||||
# Remove temp dir.
|
||||
rm -rf $plugin_name
|
||||
|
||||
# Reinstall dev dependencies.
|
||||
./composer.phar install
|
@ -7,7 +7,10 @@
|
||||
"sunra/php-simple-html-dom-parser": "*",
|
||||
"tburry/pquery": "*",
|
||||
"j4mie/paris": "1.5.4",
|
||||
"swiftmailer/swiftmailer": "^5.4"
|
||||
"swiftmailer/swiftmailer": "^5.4",
|
||||
"phpseclib/phpseclib": "*",
|
||||
"mtdowling/cron-expression": "^1.0",
|
||||
"nesbot/carbon": "^1.21"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "*",
|
||||
|
710
composer.lock
generated
25
lib/Analytics/Reporter.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
namespace MailPoet\Analytics;
|
||||
|
||||
class Reporter {
|
||||
|
||||
private $fields = array(
|
||||
'Plugin Version' => 'pluginVersion',
|
||||
);
|
||||
|
||||
function __construct() {}
|
||||
|
||||
function getData() {
|
||||
$_this = $this;
|
||||
|
||||
$analytics_data = array_map(function($func) use ($_this) {
|
||||
return $_this->$func();
|
||||
}, $this->fields);
|
||||
|
||||
return $analytics_data;
|
||||
}
|
||||
|
||||
private function pluginVersion() {
|
||||
return MAILPOET_VERSION;
|
||||
}
|
||||
}
|
@ -23,4 +23,9 @@ class Activator {
|
||||
$populator = new Populator();
|
||||
$populator->up();
|
||||
}
|
||||
|
||||
function deactivate() {
|
||||
$migrator = new Migrator();
|
||||
$migrator->down();
|
||||
}
|
||||
}
|
||||
|
33
lib/Config/Analytics.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
use \MailPoet\Analytics\Reporter;
|
||||
use \MailPoet\Models\Setting;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Analytics {
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
function init() {
|
||||
// review: this creates a fatal error when mailpoet tables are dropped.
|
||||
//add_action('admin_enqueue_scripts', array($this, 'setupAdminDependencies'));
|
||||
}
|
||||
|
||||
function setupAdminDependencies() {
|
||||
if(Setting::getValue('send_analytics_now', false)) {
|
||||
$analytics = new Reporter();
|
||||
wp_enqueue_script(
|
||||
'analytics',
|
||||
Env::$assets_url . '/js/lib/analytics.js',
|
||||
array(),
|
||||
Env::$version
|
||||
);
|
||||
wp_localize_script(
|
||||
'analytics',
|
||||
'mailpoet_analytics_data',
|
||||
$analytics->getData()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
61
lib/Config/Changelog.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
use \MailPoet\Models\Setting;
|
||||
|
||||
class Changelog {
|
||||
function init() {
|
||||
$doing_ajax = (bool)(defined('DOING_AJAX') && DOING_AJAX);
|
||||
|
||||
// don't run any check when it's an ajax request
|
||||
if($doing_ajax) {
|
||||
return;
|
||||
}
|
||||
|
||||
// don't run any check when we're not on our pages
|
||||
if(
|
||||
!(isset($_GET['page']))
|
||||
or
|
||||
(isset($_GET['page']) && strpos($_GET['page'], 'mailpoet') !== 0)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_action(
|
||||
'admin_init',
|
||||
array($this, 'check')
|
||||
);
|
||||
}
|
||||
|
||||
function check() {
|
||||
$version = Setting::getValue('version', null);
|
||||
$redirect_url = null;
|
||||
|
||||
if($version === null) {
|
||||
// new install
|
||||
$redirect_url = admin_url('admin.php?page=mailpoet-welcome');
|
||||
} else if($version !== Env::$version) {
|
||||
// update
|
||||
$redirect_url = admin_url('admin.php?page=mailpoet-update');
|
||||
}
|
||||
|
||||
if($redirect_url !== null) {
|
||||
// save version number
|
||||
Setting::setValue('version', Env::$version);
|
||||
|
||||
global $wp;
|
||||
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
|
||||
|
||||
if($redirect_url !== $current_url) {
|
||||
wp_safe_redirect(
|
||||
add_query_arg(
|
||||
array(
|
||||
'mailpoet_redirect' => urlencode($current_url)
|
||||
),
|
||||
$redirect_url
|
||||
)
|
||||
);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,53 +4,87 @@ namespace MailPoet\Config;
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Env {
|
||||
public static $version;
|
||||
public static $plugin_name;
|
||||
public static $file;
|
||||
public static $path;
|
||||
public static $views_path;
|
||||
public static $assets_path;
|
||||
public static $assets_url;
|
||||
public static $languages_path;
|
||||
public static $lib_path;
|
||||
public static $plugin_prefix;
|
||||
public static $db_prefix;
|
||||
public static $db_source_name;
|
||||
public static $db_host;
|
||||
public static $db_name;
|
||||
public static $db_username;
|
||||
public static $db_password;
|
||||
public static $db_charset;
|
||||
static $version;
|
||||
static $plugin_name;
|
||||
static $plugin_path;
|
||||
static $file;
|
||||
static $path;
|
||||
static $views_path;
|
||||
static $assets_path;
|
||||
static $assets_url;
|
||||
static $temp_path;
|
||||
static $temp_URL;
|
||||
static $languages_path;
|
||||
static $lib_path;
|
||||
static $plugin_prefix;
|
||||
static $db_prefix;
|
||||
static $db_source_name;
|
||||
static $db_host;
|
||||
static $db_socket;
|
||||
static $db_port;
|
||||
static $db_name;
|
||||
static $db_username;
|
||||
static $db_password;
|
||||
static $db_charset;
|
||||
|
||||
public static function init($file, $version) {
|
||||
static function init($file, $version) {
|
||||
global $wpdb;
|
||||
self::$version = $version;
|
||||
self::$plugin_name = 'mailpoet';
|
||||
self::$file = $file;
|
||||
self::$path = dirname(self::$file);
|
||||
self::$plugin_name = 'mailpoet';
|
||||
self::$views_path = self::$path . '/views';
|
||||
self::$assets_path = self::$path . '/assets';
|
||||
self::$assets_url = plugins_url('/assets', $file);
|
||||
self::$temp_path = wp_upload_dir()['path'];
|
||||
self::$temp_URL = wp_upload_dir()['url'];
|
||||
self::$languages_path = self::$path . '/lang';
|
||||
self::$lib_path = self::$path . '/lib';
|
||||
self::$plugin_prefix = 'mailpoet_';
|
||||
self::$db_prefix = $wpdb->prefix . self::$plugin_prefix;
|
||||
self::$db_source_name = self::dbSourceName();
|
||||
self::$db_host = DB_HOST;
|
||||
self::$db_port = 3306;
|
||||
self::$db_socket = false;
|
||||
if(preg_match('/(?=:\d+$)/', DB_HOST)) {
|
||||
list(self::$db_host, self::$db_port) = explode(':', DB_HOST);
|
||||
} else {
|
||||
if(preg_match('/:/', DB_HOST)) {
|
||||
self::$db_socket = true;
|
||||
}
|
||||
}
|
||||
self::$db_name = DB_NAME;
|
||||
self::$db_username = DB_USER;
|
||||
self::$db_password = DB_PASSWORD;
|
||||
self::$db_charset = $wpdb->get_charset_collate();
|
||||
self::$db_source_name = self::dbSourceName(self::$db_host, self::$db_socket, self::$db_port);
|
||||
}
|
||||
|
||||
private static function dbSourceName() {
|
||||
private static function dbSourceName($host, $socket, $port) {
|
||||
$source_name = array(
|
||||
'mysql:host=',
|
||||
DB_HOST,
|
||||
(!$socket) ? 'mysql:host=' : 'mysql:unix_socket=',
|
||||
$host,
|
||||
';',
|
||||
'port=',
|
||||
$port,
|
||||
';',
|
||||
'dbname=',
|
||||
DB_NAME
|
||||
);
|
||||
return implode('', $source_name);
|
||||
}
|
||||
}
|
||||
|
||||
static function isPluginActivated() {
|
||||
$activatesPlugins = get_option('active_plugins');
|
||||
$isActivated = (
|
||||
in_array(
|
||||
sprintf('%s/%s.php', basename(self::$path), self::$plugin_name),
|
||||
$activatesPlugins
|
||||
) ||
|
||||
in_array(
|
||||
sprintf('%s/%s.php', explode('/', plugin_basename(__FILE__))[0], self::$plugin_name),
|
||||
$activatesPlugins
|
||||
)
|
||||
);
|
||||
return ($isActivated) ? true : false;
|
||||
}
|
||||
}
|