Compare commits

...

78 Commits
3.0.0 ... 3.0.4

Author SHA1 Message Date
ff2a3cd19e Release 3.0.4 2017-10-05 12:09:28 +00:00
8419d95ea1 Fix spacer and divider resizing [MAILPOET-1131] 2017-10-05 10:48:52 +01:00
4ad317ac7b Release MP3 3.0.3 2017-10-03 21:37:51 +03:00
7cccebbf2c Merge pull request #1135 from mailpoet/wp_sync_collations_fix
Get rid of WP user IDs updating query in favor of an insert-update due to collation problems [MAILPOET-1132]
2017-10-03 21:01:44 +03:00
e4f76ee9eb Get rid of WP user IDs updating query in favor of an insert-update due to collation problems [MAILPOET-1132] 2017-10-03 20:52:35 +03:00
7f52f72c25 Releasing 3.0.2 2017-10-03 14:27:56 +00:00
44afcbbeaf Merge pull request #1134 from mailpoet/new-poll
Add a new poll to update page [MAILPOET-1129]
2017-10-02 19:55:36 +03:00
e816c59539 Add a new poll to update page 2017-10-02 18:47:39 +03:00
c74421a42a Merge pull request #1133 from mailpoet/permission_update_fix
Remove the check for a plugin update permission [MAILPOET-1130]
2017-10-02 14:58:08 +02:00
23eb4633c4 Remove the check for a plugin update permission [MAILPOET-1130] 2017-10-02 15:44:06 +03:00
92dbf966a1 Add a UI hint for managing capabilities using the Members plugin [MAILPOET-1123] 2017-10-02 10:33:20 +01:00
db226b54a8 Include admin-global.css only on admin pages [MAILPOET-493] 2017-10-02 10:22:40 +01:00
3af059f5c4 Fix MailPoet icon displaying in Members tab on production [MAILPOET-493] 2017-10-02 10:22:40 +01:00
8706abcdf0 Change access_plugin_admin permission label [MAILPOET-493] 2017-10-02 10:22:40 +01:00
2129d041ac Fix indentation [MAILPOET-493] 2017-10-02 10:22:40 +01:00
2a4a44ebb5 Make a condition more easy to read [MAILPOET-493] 2017-10-02 10:22:40 +01:00
a4f2d5402c Manage MP3 permissions with WP role capabilities, add Members plugin support [MAILPOET-493] 2017-10-02 10:22:40 +01:00
9f5fc151b4 Move throttling out of the Subscriber model to the API 'subscribe' method [MAILPOET-1115] 2017-09-28 15:45:35 +01:00
8a91eb46e6 Fix the possibility of repeatedly submitting a form with an existing e-mail address [MAILPOET-1115] 2017-09-28 12:59:57 +01:00
e4ab928e82 Merge pull request #1127 from mailpoet/presubscribed_wp_sync_fix
Fix synchronization of presubscribed WP users [MAILPOET-1127]
2017-09-28 11:24:32 +02:00
a1b02cb862 Fix synchronization of presubscribed WP users [MAILPOET-1127] 2017-09-28 10:44:29 +03:00
84b942b9d2 Merge pull request #1121 from mailpoet/template_sort
Applies sorting by date created and name [MAILPOET-1119]
2017-09-27 11:14:00 +02:00
1ca99a6209 Updates Premium tab language 2017-09-27 10:07:36 +01:00
6b61abe8c0 Removes text domain from plugin header 2017-09-27 10:05:10 +01:00
27028ca1ef Merge pull request #1124 from mailpoet/alc_and_post_exclude_search_results
Prevents excluded post types from being displayed in newsletter editor [MAILPOET-701]
2017-09-27 11:40:51 +03:00
eed88926a2 Merge pull request #1120 from mailpoet/editor_horizontal_scroll_fix
Fixes horizontal scrolling inside post/ALC options panel [MAILPOET-1118]
2017-09-27 11:31:34 +03:00
b25877c514 Bump up release version to 3.0.1 2017-09-26 18:18:17 +03:00
119e574495 Prevents excluded post types from being displayed in newsletter editor 2017-09-25 19:45:33 -04:00
7308d253b2 Applies sorting by date created and name 2017-09-25 18:47:43 -04:00
1c19b71697 Fixes horizontal scrolling inside post/ALC options panel 2017-09-25 18:09:58 -04:00
7551fff93f Merge pull request #1116 from mailpoet/fix-tests
Fixing Shortcodes issue [MAILPOET-1104]
2017-09-25 17:14:35 -04:00
b2aa919574 Merge pull request #1118 from mailpoet/fix-query
Add index to improve query performance [MAILPOET-1117]
2017-09-25 17:04:25 -04:00
1102bbe483 Merge pull request #1056 from mailpoet/resize-image
Add image resize feature [MAILPOET-1047]
2017-09-25 13:07:10 +03:00
b78dd22ba9 Merge pull request #1109 from mailpoet/templates-images-src
Adding https prefix the image sources [MAILPOET-1109]
2017-09-25 12:08:02 +03:00
73110ada46 fixing ESLint tests 2017-09-25 09:06:55 +00:00
74dedd06bc changing missing tempaltes 2017-09-25 08:57:18 +00:00
20c936d13b limitting width with CSS 2017-09-21 18:58:17 +00:00
f135b89de9 Fixed image resize bugs 2017-09-21 18:58:16 +00:00
6a83930ae0 Resizing the image fixed 2017-09-21 18:57:35 +00:00
a1d0acfac2 Add image resize feature 2017-09-21 18:56:47 +00:00
04be06c0cb remove additional new line 2017-09-21 18:47:43 +00:00
78d52d6298 Merge pull request #1117 from mailpoet/eslint
Eslint rules [MAILPOET-1081]
2017-09-21 19:42:19 +03:00
5ff7c28c43 Merge pull request #1110 from mailpoet/es6_spacing
Fix ESLint spacing rules for ES6 [MAILPOET-1082]
2017-09-21 17:15:57 +03:00
5526f315d2 Merge pull request #1115 from mailpoet/scheduled_newsletter_status_fix
Sets newsletter status to draft when it's unscheduled [MAILPOET-1060]
2017-09-21 16:32:12 +03:00
d5e25fdeb1 Merge pull request #1114 from mailpoet/long_email_sql_error_fix
Fixes SQL error resulting from subscription with long email [MAILPOET-1113]
2017-09-21 16:17:21 +03:00
90a7bf5179 Adds back rendered subject clearing test
Removes duplicate line
2017-09-21 09:13:13 -04:00
bf1f696870 Add index to improve query performance
[MAILPOET-1117]
2017-09-21 13:17:02 +01:00
95551ad049 ES5 keyword-spacing 2017-09-21 09:13:36 +00:00
1ad90680f4 ES5 object-curly-spacing 2017-09-21 09:12:52 +00:00
d69d3cb421 ES5 array-bracket-spacing 2017-09-21 09:12:37 +00:00
9adca07393 ES5 computed-property-spacing 2017-09-21 09:12:23 +00:00
a9d129fddc ES5 block-spacing 2017-09-21 09:12:09 +00:00
b4ac09bea3 ES5 key-spacing 2017-09-21 09:11:55 +00:00
b1a403d9b5 ES5 space-infix-ops 2017-09-21 09:09:29 +00:00
28504fb5e3 ES5 spaced-comment 2017-09-21 09:08:52 +00:00
8ebb8e3c02 ES5 no-trailing-spaces 2017-09-21 09:04:47 +00:00
c95c2cd1ae ES5 space-in-parens 2017-09-21 09:04:32 +00:00
946bee2194 ES5 space-before-blocks 2017-09-21 09:02:50 +00:00
1f9bd04308 ES5 space-unary-ops 2017-09-21 08:52:30 +00:00
33572b2dc7 ES5 no-multi-spaces 2017-09-21 08:47:23 +00:00
680446b77e ES5 space-before-function-parens 2017-09-21 08:35:12 +00:00
bf1d76a3a7 Fix readme.txt 2017-09-20 16:57:17 +01:00
42e3a97616 Fixing Shortcodes issue 2017-09-20 12:34:27 +00:00
6b16aa1692 Sets newsletter status to draft when it's unscheduled 2017-09-19 21:59:03 -04:00
c3b643df84 Prevents leaking SQL errors in API response 2017-09-19 20:32:26 -04:00
697f9ba5bc Adds min/max email length in UI and backend 2017-09-19 20:04:49 -04:00
475114c6f9 Fix ES6 no-irregular-whitespace eslint rule [MAILPOET-1082] 2017-09-18 18:31:13 +03:00
a0fec7d103 Fix ES6 space-infix-ops eslint rule [MAILPOET-1082] 2017-09-18 18:27:43 +03:00
4d9d92a026 Fix ES6 array-bracket-spacing eslint rule [MAILPOET-1082] 2017-09-18 18:26:43 +03:00
e51aa8c271 Fix ES6 space-in-parens eslint rule [MAILPOET-1082] 2017-09-18 18:25:37 +03:00
d44adedade Fix ES6 key-spacing eslint rule [MAILPOET-1082] 2017-09-18 18:24:26 +03:00
9fb3c50aa7 Fix ES6 no-multi-spaces eslint rule [MAILPOET-1082] 2017-09-18 18:23:36 +03:00
907053a349 Fix ES6 space-unary-ops eslint rule [MAILPOET-1082] 2017-09-18 18:22:48 +03:00
f0f85cfb59 Fix ES6 template-curly-spacing eslint rule [MAILPOET-1082] 2017-09-18 18:21:44 +03:00
44d0341fb2 Fix ES6 keyword-spacing eslint rule [MAILPOET-1082] 2017-09-18 18:20:55 +03:00
0cdae52c66 Fix ES6 react/jsx-curly-spacing eslint rule [MAILPOET-1082] 2017-09-18 18:18:45 +03:00
0cd9c8e416 Adding https prefix the image sources 2017-09-18 15:11:55 +00:00
9e3010ab52 Fix ES6 react/jsx-tag-spacing eslint rule [MAILPOET-1082] 2017-09-18 18:05:57 +03:00
165 changed files with 2603 additions and 2092 deletions

View File

@ -9,9 +9,7 @@
},
"rules": {
"import/no-amd": 0,
"space-before-function-paren": 0,
"prefer-arrow-callback": 0,
"key-spacing": 0,
"radix": 0,
"no-alert": 0,
"block-scoped-var": 0,
@ -19,7 +17,6 @@
"no-prototype-builtins": 0,
"no-restricted-syntax": 0,
"no-useless-concat": 0,
"no-multi-spaces": 0,
"no-nested-ternary": 0,
"no-sequences": 0,
"no-useless-return": 0,
@ -27,24 +24,17 @@
"new-cap": 0,
"no-continue": 0,
"no-new": 0,
"space-unary-ops": 0,
"no-redeclare": 0,
"no-console": 0,
"no-empty": 0,
"no-useless-escape": 0,
"wrap-iife": 0,
"block-spacing": 0,
"computed-property-spacing": 0,
"no-plusplus": 0,
"array-bracket-spacing": 0,
"default-case": 0,
"no-lonely-if": 0,
"space-before-blocks": 0,
"no-mixed-operators": 0,
"eqeqeq": 0,
"space-in-parens": 0,
"max-len": 0,
"no-trailing-spaces": 0,
"global-require": 0,
"no-throw-literal": 0,
"no-extra-bind": 0,
@ -57,17 +47,13 @@
"no-use-before-define": 0,
"one-var": 0,
"camelcase": 0,
"spaced-comment": 0,
"padded-blocks": 0,
"object-curly-spacing": 0,
"strict": 0,
"vars-on-top": 0,
"no-var": 0,
"space-infix-ops": 0,
"no-unused-vars": 0,
"object-shorthand": 0,
"new-parens": 0,
"keyword-spacing": 0,
"eol-last": 0,
"dot-notation": 0,
"prefer-template": 0,

View File

@ -28,10 +28,8 @@
"react/jsx-no-bind": 0,
"react/no-array-index-key": 0,
"react/self-closing-comp": 0,
"react/jsx-tag-spacing": 0,
"react/jsx-closing-bracket-location": 0,
"react/no-string-refs": 0,
"react/jsx-curly-spacing": 0,
"react/no-did-mount-set-state": 0,
"react/prefer-stateless-function": 0,
"jsx-a11y/label-has-for": 0,
@ -42,25 +40,18 @@
"no-bitwise": 0,
"arrow-body-style": 0,
"prefer-template": 0,
"keyword-spacing": 0,
"default-case": 0,
"array-callback-return": 0,
"consistent-return": 0,
"import/extensions": 0,
"import/no-extraneous-dependencies": 0,
"camelcase": 0,
"template-curly-spacing": 0,
"eqeqeq": 0,
"no-lonely-if": 0,
"space-unary-ops": 0,
"block-scoped-var": 0,
"no-extra-bind": 0,
"no-multi-spaces": 0,
"class-methods-use-this": 0,
"key-spacing": 0,
"space-in-parens": 0,
"no-case-declarations": 0,
"array-bracket-spacing": 0,
"no-else-return": 0,
"max-len": 0,
"no-useless-concat": 0,
@ -74,8 +65,6 @@
"no-script-url": 0,
"wrap-iife": 0,
"vars-on-top": 0,
"space-infix-ops": 0,
"no-irregular-whitespace": 0,
"padded-blocks": 0,
"no-underscore-dangle": 0
}

View File

@ -90,6 +90,7 @@ class RoboFile extends \Robo\Tasks {
$css_files = array(
'assets/css/src/admin.styl',
'assets/css/src/admin-global.styl',
'assets/css/src/newsletter_editor/newsletter_editor.styl',
'assets/css/src/public.styl',
'assets/css/src/rtl.styl',

View File

@ -0,0 +1,15 @@
@import 'nib'
@require 'icons'
/*
Style for Members plugin
*/
.members-tab-title
.mailpoet-icon-logo
vertical-align: middle;
height: 20px;
width: 20px;
font-size: 20px;
margin-right: 3px;

24
assets/css/src/icons.styl Normal file
View File

@ -0,0 +1,24 @@
icon-font-path ?= "../fonts"
@font-face
font-family 'mailpoet'
src url(icon-font-path + '/mailpoet.ttf?mx0b6n') format('truetype'), url(icon-font-path + '/mailpoet.woff?mx0b6n') format('woff'), url(icon-font-path + '/mailpoet.svg?mx0b6n#mailpoet') format('svg')
font-weight normal
font-style normal
[class^="mailpoet-icon-"], [class*=" mailpoet-icon-"]
font-family 'mailpoet' !important
speak none
font-style normal
font-weight normal
font-variant normal
text-transform none
line-height 1
/* Better Font Rendering =========== */
-webkit-font-smoothing antialiased
-moz-osx-font-smoothing grayscale
.mailpoet-icon-logo
&:before
content "\e900"

View File

@ -48,3 +48,41 @@ $resize-handle-z-index = 2
.mailpoet_resize_handle
display: inline-block
.mailpoet_image_resize_handle_container
position: absolute
bottom: 0
right: 0
width: 20px
height: 20px
.mailpoet_image_resize_handle
position: relative
background: $resize-handle-background-color
border-radius(3px)
display: inline-block
width: 20px
height: 20px
cursor: nwse-resize
z-index: $resize-handle-z-index
.mailpoet_image_resize_handle_text,
.mailpoet_image_resize_handle_icon
pointer-events: none
.mailpoet_image_resize_handle_icon
position: absolute
top: 0
right: 0
& > svg
width: 100%
height: 100%
fill: $resize-handle-font-color
.mailpoet_block.mailpoet_image_resize_active > .mailpoet_block_highlight
border: 1px dashed $resize-active-color
.mailpoet_image_resize_handle
display: inline-block

View File

@ -3,14 +3,19 @@
img
vertical-align: bottom
max-width: 100%
width: auto
height: auto
&.mailpoet_full_image
padding-left: 0
padding-right: 0
margin: auto
margin-bottom: 0
.mailpoet_content a:hover
.mailpoet_content
margin: auto
max-width: 100%
min-width: 36px
a:hover
cursor: all-scroll

View File

@ -2,7 +2,7 @@ animation-slide-open-downwards($max-height = 2000px)
transition: all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000) /* ease-in-out */
max-height: $max-height
opacity: 1
overflow-y: hidden
overflow-y: inherit
&.mailpoet_closed
max-height: 0px

11
assets/fonts/mailpoet.svg Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="mailpoet" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="0" d="" />
<glyph unicode="&#xe900;" glyph-name="optimised" horiz-adv-x="972" d="M230.188 949.695c-21.982-3.361-41.376-14.741-48.618-28.188l-5.948-11.637 0.517-265.588c0.776-263.779 0.776-265.847 6.206-273.088 11.12-15.258 40.601-22.24 68.79-16.551 16.551 3.621 25.861 9.827 32.584 21.723 3.879 7.499 4.397 23.791 5.689 185.161l1.293 176.628 60.514-161.111c33.102-88.702 63.875-168.612 68.013-177.404 13.707-29.481 35.687-41.376 72.151-39.049 21.206 1.293 39.308 9.827 47.584 22.499 3.361 5.172 35.947 91.806 72.41 192.662s67.237 185.42 68.53 187.49c1.551 2.587 2.587-69.307 2.587-185.937v-190.335l5.948-11.379c8.533-16.809 20.172-21.982 49.652-21.982 27.929 0.259 39.825 4.914 49.911 20.172l6.982 10.603v530.401l-5.689 9.31c-12.671 20.43-50.17 31.033-91.806 25.601-34.394-4.138-53.79-16.292-66.72-41.118-2.587-5.172-35.947-101.89-73.961-214.902s-69.824-206.367-70.6-207.403c-1.034-1.034-32.326 86.115-69.824 193.954-37.757 107.581-71.892 205.075-76.030 216.453-10.086 26.118-25.601 42.929-45.514 48.877-17.326 5.172-46.807 6.982-64.652 4.138zM54.854 243.443c-20.172-3.879-43.963-19.136-51.204-33.619-6.206-11.379-4.914-32.843 2.587-47.841 23.533-46.807 71.634-86.892 126.717-104.736 17.068-5.689 23.274-5.948 120.252-7.499 97.235-1.551 102.926-1.81 116.373-7.241 29.739-11.896 51.204-35.687 61.807-68.013 3.621-11.12 13.964-21.206 25.861-25.344 4.914-1.551 18.361-2.844 29.739-2.844 16.809 0 23.533 1.293 32.584 5.689 11.896 6.206 13.964 9.31 26.895 38.791 11.896 27.671 39.567 49.652 70.858 56.117 8.533 1.81 47.067 2.844 100.856 2.844 99.563 0 113.786 2.068 151.801 20.689 49.652 24.567 96.978 77.84 101.373 113.529 3.104 26.118-17.326 49.394-51.204 58.187-25.601 6.465-41.635-0.517-54.825-24.050-11.12-19.655-29.998-38.015-47.841-46.29l-14.741-6.982-99.563-1.551c-90.77-1.293-101.373-2.068-120.252-6.982-27.154-7.499-58.444-23.016-80.427-40.084l-17.844-13.964-16.809 13.964c-20.689 16.809-51.462 32.584-78.875 39.825-19.136 5.172-28.705 5.948-120.252 7.241l-99.563 1.551-15.775 7.241c-18.102 8.533-32.584 21.982-48.36 45.773-16.034 24.567-26.895 29.998-50.17 25.601z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
assets/fonts/mailpoet.ttf Normal file

Binary file not shown.

BIN
assets/fonts/mailpoet.woff Normal file

Binary file not shown.

View File

@ -276,7 +276,7 @@ const ListingItems = React.createClass({
selection={this.props.selection}
is_selectable={this.props.is_selectable}
item_actions={this.props.item_actions}
group={ this.props.group }
group={this.props.group}
key={`item-${renderItem.id}-${index}`}
item={renderItem} />
);

View File

@ -14,9 +14,16 @@ define([
defaults: {
elementSelector: null,
resizeHandleSelector: true, // true will use edges of the element itself
transformationFunction: function(y) { return y; },
transformationFunction: function (y) { return y; }, // for blocks that use the default onResize function
minLength: 0,
modelField: 'styles.block.height'
maxLength: Infinity,
modelField: 'styles.block.height',
onResize: function (event) {
var currentLength = parseFloat(this.view.model.get(this.options.modelField)),
newLength = currentLength + this.options.transformationFunction(event.dy);
newLength = Math.min(this.options.maxLength, Math.max(this.options.minLength, newLength));
this.view.model.set(this.options.modelField, newLength + 'px');
}
},
events: {
mouseenter: 'showResizeHandle',
@ -40,16 +47,14 @@ define([
right: false,
bottom: (typeof this.options.resizeHandleSelector === 'string') ? this.view.$(this.options.resizeHandleSelector).get(0) : this.options.resizeHandleSelector
}
}).on('resizestart', function(event) {
})
.on('resizestart', function (event) {
that.isBeingResized = true;
that.$el.addClass('mailpoet_resize_active');
}).on('resizemove', function(event) {
var currentLength = parseFloat(that.view.model.get(that.options.modelField)),
newLength = currentLength + that.options.transformationFunction(event.dy);
if (newLength < that.options.minLength) newLength = that.options.minLength;
that.view.model.set(that.options.modelField, newLength + 'px');
})
.on('resizemove', function (event) {
var onResize = that.options.onResize.bind(that);
return onResize(event);
})
.on('resizeend', function (event) {
that.isBeingResized = null;

View File

@ -44,17 +44,28 @@ define([
}, base.BlockView.prototype.templateContext.apply(this));
},
behaviors: _.extend({}, base.BlockView.prototype.behaviors, {
ShowSettingsBehavior: {}
ResizableBehavior: {
elementSelector: '.mailpoet_image',
resizeHandleSelector: '.mailpoet_image_resize_handle',
onResize: function (event) {
var corner = this.$('.mailpoet_image').offset(),
width = event.pageX - corner.left;
this.view.model.set('width', width + 'px');
}
},
ShowSettingsBehavior: {
ignoreFrom: '.mailpoet_image_resize_handle'
}
}),
onRender: function () {
this.toolsView = new Module.ImageBlockToolsView({ model: this.model });
this.showChildView('toolsRegion', this.toolsView);
if (this.model.get('fullWidth')) {
this.$el.addClass('mailpoet_full_image');
} else {
this.$el.removeClass('mailpoet_full_image');
}
this.$('.mailpoet_content').css('width', this.model.get('width'));
}
});
@ -82,9 +93,32 @@ define([
'change .mailpoet_field_image_full_width': _.partial(this.changeBoolCheckboxField, 'fullWidth'),
'change .mailpoet_field_image_alignment': _.partial(this.changeField, 'styles.block.textAlign'),
'click .mailpoet_field_image_select_another_image': 'showMediaManager',
'click .mailpoet_done_editing': 'close'
'click .mailpoet_done_editing': 'close',
'input .mailpoet_field_image_width': _.partial(this.updateValueAndCall, '.mailpoet_field_image_width_input', _.partial(this.changePixelField, 'width').bind(this)),
'change .mailpoet_field_image_width': _.partial(this.updateValueAndCall, '.mailpoet_field_image_width_input', _.partial(this.changePixelField, 'width').bind(this)),
'input .mailpoet_field_image_width_input': _.partial(this.updateValueAndCall, '.mailpoet_field_image_width', _.partial(this.changePixelField, 'width').bind(this))
};
},
modelEvents: function () {
return {
'change:maxWidth': 'updateMaxWidth',
'change:width': 'updateWidth'
};
},
updateValueAndCall: function (fieldToUpdate, callable, event) {
this.$(fieldToUpdate).val(jQuery(event.target).val());
callable(event);
},
updateMaxWidth: function () {
var maxWidth = parseInt(this.model.get('maxWidth'));
this.$('.mailpoet_field_image_width').attr('max', maxWidth);
this.$('.mailpoet_field_image_width_input').attr('max', maxWidth);
},
updateWidth: function () {
var width = parseInt(this.model.get('width'));
this.$('.mailpoet_field_image_width').val(width);
this.$('.mailpoet_field_image_width_input').val(width);
},
initialize: function (options) {
base.BlockSettingsView.prototype.initialize.apply(this, arguments);

View File

@ -185,7 +185,7 @@ const NewsletterListNotification = React.createClass({
return (
<select
data-id={newsletter.id}
defaultValue={ newsletter.status }
defaultValue={newsletter.status}
onChange={this.updateStatus}
>
<option value="active">{ MailPoet.I18n.t('active') }</option>

View File

@ -165,7 +165,7 @@ const NewsletterListWelcome = React.createClass({
<p>
<select
data-id={newsletter.id}
defaultValue={ newsletter.status }
defaultValue={newsletter.status}
onChange={this.updateStatus}
>
<option value="active">{ MailPoet.I18n.t('active') }</option>

View File

@ -210,7 +210,7 @@ const SegmentList = React.createClass({
if (segment.type === 'wp_users') {
// the WP users segment is not editable so just display its name
segment_name = (
<span className="row-title">{ segment.name }</span>
<span className="row-title">{ segment.name }</span>
);
} else {
segment_name = (

View File

@ -107,7 +107,7 @@ define(
},
];
const custom_fields = window.mailpoet_custom_fields || [];
const custom_fields = window.mailpoet_custom_fields || [];
custom_fields.map((custom_field) => {
const field = {
name: 'cf_' + custom_field.id,

View File

@ -1,4 +1,5 @@
<?php
namespace MailPoet\API\JSON;
if(!defined('ABSPATH')) exit;
@ -12,23 +13,19 @@ class ErrorResponse extends Response {
}
function getData() {
if(empty($this->errors)) {
return null;
} else {
return array(
'errors' => $this->errors
);
}
return (empty($this->errors)) ? null : array('errors' => $this->errors);
}
function formatErrors($errors = array()) {
$formatted_errors = array();
foreach($errors as $error => $message) {
$formatted_errors[] = array(
return array_map(function($error, $message) {
// sanitize SQL error
if(preg_match('/^SQLSTATE/i', $message)) {
$message = __('An unknown error occurred.', 'mailpoet');
}
return array(
'error' => $error,
'message' => $message
);
}
return $formatted_errors;
}, array_keys($errors), array_values($errors));
}
}

View File

@ -20,13 +20,14 @@ class AutomatedLatestContent extends APIEndpoint {
function getPostTypes() {
$post_types = array_map(function($post_type) {
if(!empty($post_type->exclude_from_search)) return;
return array(
'name' => $post_type->name,
'label' => $post_type->label
);
}, get_post_types(array(), 'objects'));
return $this->successResponse(
$post_types
array_filter($post_types)
);
}

View File

@ -29,7 +29,7 @@ class NewsletterTemplates extends APIEndpoint {
}
function getAll() {
$collection = NewsletterTemplate::findMany();
$collection = NewsletterTemplate::orderByDesc('created_at')->orderByAsc('name')->findMany();
$templates = array_map(function($item) {
return $item->asArray();
}, $collection);

View File

@ -65,13 +65,11 @@ class Newsletters extends APIEndpoint {
$newsletter = Newsletter::createOrUpdate($data);
$errors = $newsletter->getErrors();
if(!empty($errors)) {
return $this->badRequest($errors);
} else {
if(!empty($errors)) return $this->badRequest($errors);
if(!empty($segments)) {
NewsletterSegment::where('newsletter_id', $newsletter->id)
->deleteMany();
foreach($segments as $segment) {
if(!is_array($segment)) continue;
$relation = NewsletterSegment::create();
@ -86,7 +84,6 @@ class Newsletters extends APIEndpoint {
'newsletter_type',
$newsletter->type
)->findMany();
// update newsletter options
foreach($option_fields as $option_field) {
if(isset($options[$option_field->name])) {
@ -99,11 +96,9 @@ class Newsletters extends APIEndpoint {
);
}
}
// reload newsletter with updated options
$newsletter = Newsletter::filter('filterWithOptions')
->findOne($newsletter->id);
// if this is a post notification, process newsletter options and update its schedule
if($newsletter->type === Newsletter::TYPE_NOTIFICATION) {
// generate the new schedule from options and get the new "next run" date
@ -120,16 +115,22 @@ class Newsletters extends APIEndpoint {
$queue = $newsletter->getQueue();
if($queue) {
// if newsletter was previously scheduled and is now unscheduled, set its status to DRAFT and delete associated queue record
if($newsletter->status === Newsletter::STATUS_SCHEDULED && isset($options['isScheduled']) && empty($options['isScheduled'])) {
$queue->delete();
$newsletter->status = Newsletter::STATUS_DRAFT;
$newsletter->save();
} else {
$queue->newsletter_rendered_body = null;
$queue->newsletter_rendered_subject = null;
$queue->save();
}
}
Hooks::doAction('mailpoet_api_newsletters_save_after', $newsletter);
return $this->successResponse($newsletter->asArray());
}
}
function setStatus($data = array()) {
$status = (isset($data['status']) ? $data['status'] : null);

View File

@ -10,10 +10,13 @@ use MailPoet\Form\Util\FieldNameObfuscator;
use MailPoet\Models\Form;
use MailPoet\Models\StatisticsForms;
use MailPoet\Models\Subscriber;
use MailPoet\Util\Helpers;
if(!defined('ABSPATH')) exit;
class Subscribers extends APIEndpoint {
const SUBSCRIPTION_LIMIT_COOLDOWN = 60;
public $permissions = array(
'global' => AccessControl::PERMISSION_MANAGE_SUBSCRIBERS,
'methods' => array('subscribe' => AccessControl::NO_ACCESS_RESTRICTION)
@ -94,6 +97,19 @@ class Subscribers extends APIEndpoint {
$form_fields = $form->getFieldList();
$data = array_intersect_key($data, array_flip($form_fields));
// make sure we don't allow too many subscriptions with the same ip address
$subscription_count = Subscriber::where(
'subscribed_ip',
Helpers::getIP()
)->whereRaw(
'(TIME_TO_SEC(TIMEDIFF(NOW(), created_at)) < ? OR TIME_TO_SEC(TIMEDIFF(NOW(), updated_at)) < ?)',
array(self::SUBSCRIPTION_LIMIT_COOLDOWN, self::SUBSCRIPTION_LIMIT_COOLDOWN)
)->count();
if($subscription_count > 0) {
throw new \Exception(__('You need to wait before subscribing again.', 'mailpoet'));
}
$subscriber = Subscriber::subscribe($data, $segment_ids);
$errors = $subscriber->getErrors();

View File

@ -8,26 +8,25 @@ if(!defined('ABSPATH')) exit;
require_once(ABSPATH . 'wp-includes/pluggable.php');
class AccessControl {
const PERMISSION_ACCESS_PLUGIN_ADMIN = 'access_plugin_admin';
const PERMISSION_MANAGE_SETTINGS = 'manage_settings';
const PERMISSION_MANAGE_EMAILS = 'manage_emails';
const PERMISSION_MANAGE_SUBSCRIBERS = 'manage_subscribers';
const PERMISSION_MANAGE_FORMS = 'manage_forms';
const PERMISSION_MANAGE_SEGMENTS = 'manage_segments';
const PERMISSION_UPDATE_PLUGIN = 'update_plugin';
const NO_ACCESS_RESTRICTION = 'no_access_restriction';
const PERMISSION_ACCESS_PLUGIN_ADMIN = 'mailpoet_access_plugin_admin';
const PERMISSION_MANAGE_SETTINGS = 'mailpoet_manage_settings';
const PERMISSION_MANAGE_EMAILS = 'mailpoet_manage_emails';
const PERMISSION_MANAGE_SUBSCRIBERS = 'mailpoet_manage_subscribers';
const PERMISSION_MANAGE_FORMS = 'mailpoet_manage_forms';
const PERMISSION_MANAGE_SEGMENTS = 'mailpoet_manage_segments';
const NO_ACCESS_RESTRICTION = 'mailpoet_no_access_restriction';
public $permissions;
public $current_user_roles;
public $user_roles;
public $user_capabilities;
function __construct() {
$this->permissions = $this->getDefaultPermissions();
$this->permissions = self::getDefaultPermissions();
$this->user_roles = $this->getUserRoles();
$this->user_capabilities = $this->getUserCapabilities();
}
private function getDefaultPermissions() {
static function getDefaultPermissions() {
return array(
self::PERMISSION_ACCESS_PLUGIN_ADMIN => WPHooks::applyFilters(
'mailpoet_permission_access_plugin_admin',
@ -67,12 +66,17 @@ class AccessControl {
'administrator'
)
),
self::PERMISSION_UPDATE_PLUGIN => WPHooks::applyFilters(
'mailpoet_permission_update_plugin',
array(
'administrator'
)
),
);
}
static function getPermissionLabels() {
return array(
self::PERMISSION_ACCESS_PLUGIN_ADMIN => __('Admin menu item', 'mailpoet'),
self::PERMISSION_MANAGE_SETTINGS => __('Manage settings', 'mailpoet'),
self::PERMISSION_MANAGE_EMAILS => __('Manage emails', 'mailpoet'),
self::PERMISSION_MANAGE_SUBSCRIBERS => __('Manage subscribers', 'mailpoet'),
self::PERMISSION_MANAGE_FORMS => __('Manage forms', 'mailpoet'),
self::PERMISSION_MANAGE_SEGMENTS => __('Manage segments', 'mailpoet'),
);
}
@ -94,11 +98,12 @@ class AccessControl {
function validatePermission($permission) {
if($permission === self::NO_ACCESS_RESTRICTION) return true;
if(empty($this->permissions[$permission])) return false;
$permitted_roles = array_intersect(
$this->user_roles,
$this->permissions[$permission]
);
return (!empty($permitted_roles));
foreach($this->user_roles as $role) {
$role_object = get_role($role);
if($role_object && $role_object->has_cap($permission)) {
return true;
}
}
return false;
}
}

View File

@ -14,10 +14,16 @@ class Activator {
$populator = new Populator();
$populator->up();
Setting::setValue('db_version', Env::$version);
$caps = new Capabilities();
$caps->setupWPCapabilities();
}
function deactivate() {
$migrator = new Migrator();
$migrator->down();
$caps = new Capabilities();
$caps->removeWPCapabilities();
}
}

View File

@ -0,0 +1,85 @@
<?php
namespace MailPoet\Config;
use MailPoet\WP\Hooks;
class Capabilities {
const MEMBERS_CAP_GROUP_NAME = 'mailpoet';
private $renderer = null;
function __construct($renderer = null) {
if($renderer !== null) {
$this->renderer = $renderer;
}
}
function init() {
$this->setupMembersCapabilities();
}
function setupWPCapabilities() {
$permissions = AccessControl::getDefaultPermissions();
$role_objects = array();
foreach($permissions as $name => $roles) {
foreach($roles as $role) {
if(!isset($role_objects[$role])) {
$role_objects[$role] = get_role($role);
}
$role_objects[$role]->add_cap($name);
}
}
}
function removeWPCapabilities() {
$permissions = AccessControl::getDefaultPermissions();
$role_objects = array();
foreach($permissions as $name => $roles) {
foreach($roles as $role) {
if(!isset($role_objects[$role])) {
$role_objects[$role] = get_role($role);
}
$role_objects[$role]->remove_cap($name);
}
}
}
function setupMembersCapabilities() {
Hooks::addAction('admin_enqueue_scripts', array($this, 'enqueueMembersStyles'));
Hooks::addAction('members_register_cap_groups', array($this, 'registerMembersCapGroup'));
Hooks::addAction('members_register_caps', array($this, 'registerMembersCapabilities'));
}
function enqueueMembersStyles() {
wp_enqueue_style(
'mailpoet-admin-global',
Env::$assets_url . '/css/' . $this->renderer->getCssAsset('admin-global.css')
);
}
function registerMembersCapGroup() {
members_register_cap_group(
self::MEMBERS_CAP_GROUP_NAME,
array(
'label' => __('MailPoet', 'mailpoet'),
'caps' => array(),
'icon' => 'mailpoet-icon-logo',
'priority' => 30
)
);
}
function registerMembersCapabilities() {
$permissions = AccessControl::getPermissionLabels();
foreach($permissions as $name => $label) {
members_register_cap(
$name,
array(
'label' => $label,
'group' => self::MEMBERS_CAP_GROUP_NAME
)
);
}
}
}

View File

@ -125,6 +125,7 @@ class Initializer {
$this->setupUpdater();
$this->setupLocalizer();
$this->setupCapabilities();
$this->setupMenu();
$this->setupShortcodes();
$this->setupImages();
@ -152,9 +153,6 @@ class Initializer {
// if current db version and plugin version differ
if(version_compare($current_db_version, Env::$version) !== 0) {
if(!$this->access_control->validatePermission(AccessControl::PERMISSION_UPDATE_PLUGIN)) {
throw new \Exception(__('You do not have permission to activate/deactivate MailPoet plugin.', 'mailpoet'));
}
$this->runActivator();
}
}
@ -189,6 +187,11 @@ class Initializer {
$localizer->init();
}
function setupCapabilities() {
$caps = new Capabilities($this->renderer);
$caps->init();
}
function setupMenu() {
$menu = new Menu($this->renderer, Env::$assets_url, $this->access_control);
$menu->init();

View File

@ -37,7 +37,6 @@ class Menu {
$this->renderer = $renderer;
$this->assets_url = $assets_url;
$this->access_control = $access_control;
$this->user_capability = $this->access_control->getUserFirstCapability();
$subscribers_feature = new SubscribersFeature();
$this->subscribers_over_limit = $subscribers_feature->check();
$this->checkMailPoetAPIKey();
@ -70,7 +69,7 @@ class Menu {
add_menu_page(
'MailPoet',
'MailPoet',
$this->user_capability,
AccessControl::PERMISSION_ACCESS_PLUGIN_ADMIN,
self::MAIN_PAGE_SLUG,
null,
$this->assets_url . '/img/menu_icon.png',
@ -78,12 +77,11 @@ class Menu {
);
// Emails page
if($this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_EMAILS)) {
$newsletters_page = add_submenu_page(
self::MAIN_PAGE_SLUG,
$this->setPageTitle(__('Emails', 'mailpoet')),
__('Emails', 'mailpoet'),
$this->user_capability,
AccessControl::PERMISSION_MANAGE_EMAILS,
self::MAIN_PAGE_SLUG,
array(
$this,
@ -108,22 +106,20 @@ class Menu {
true,
$this->setPageTitle(__('Newsletter', 'mailpoet')),
__('Newsletter Editor', 'mailpoet'),
$this->user_capability,
AccessControl::PERMISSION_MANAGE_EMAILS,
'mailpoet-newsletter-editor',
array(
$this,
'newletterEditor'
)
);
}
// Forms page
if($this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_FORMS)) {
$forms_page = add_submenu_page(
self::MAIN_PAGE_SLUG,
$this->setPageTitle(__('Forms', 'mailpoet')),
__('Forms', 'mailpoet'),
$this->user_capability,
AccessControl::PERMISSION_MANAGE_FORMS,
'mailpoet-forms',
array(
$this,
@ -148,22 +144,20 @@ class Menu {
true,
$this->setPageTitle(__('Form Editor', 'mailpoet')),
__('Form Editor', 'mailpoet'),
$this->user_capability,
AccessControl::PERMISSION_MANAGE_FORMS,
'mailpoet-form-editor',
array(
$this,
'formEditor'
)
);
}
// Subscribers page
if($this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_SUBSCRIBERS)) {
$subscribers_page = add_submenu_page(
self::MAIN_PAGE_SLUG,
$this->setPageTitle(__('Subscribers', 'mailpoet')),
__('Subscribers', 'mailpoet'),
$this->user_capability,
AccessControl::PERMISSION_MANAGE_SUBSCRIBERS,
'mailpoet-subscribers',
array(
$this,
@ -188,7 +182,7 @@ class Menu {
'admin.php?page=mailpoet-subscribers',
$this->setPageTitle(__('Import', 'mailpoet')),
__('Import', 'mailpoet'),
$this->user_capability,
AccessControl::PERMISSION_MANAGE_SUBSCRIBERS,
'mailpoet-import',
array(
$this,
@ -201,22 +195,20 @@ class Menu {
true,
$this->setPageTitle(__('Export', 'mailpoet')),
__('Export', 'mailpoet'),
$this->user_capability,
AccessControl::PERMISSION_MANAGE_SUBSCRIBERS,
'mailpoet-export',
array(
$this,
'export'
)
);
}
// Segments page
if($this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_SEGMENTS)) {
$segments_page = add_submenu_page(
self::MAIN_PAGE_SLUG,
$this->setPageTitle(__('Lists', 'mailpoet')),
__('Lists', 'mailpoet'),
$this->user_capability,
AccessControl::PERMISSION_MANAGE_SEGMENTS,
'mailpoet-segments',
array(
$this,
@ -235,29 +227,26 @@ class Menu {
'option' => 'mailpoet_segments_per_page'
));
});
}
// Settings page
if($this->access_control->validatePermission(AccessControl::PERMISSION_MANAGE_SETTINGS)) {
add_submenu_page(
self::MAIN_PAGE_SLUG,
$this->setPageTitle(__('Settings', 'mailpoet')),
__('Settings', 'mailpoet'),
$this->user_capability,
AccessControl::PERMISSION_MANAGE_SETTINGS,
'mailpoet-settings',
array(
$this,
'settings'
)
);
}
// Help page
add_submenu_page(
self::MAIN_PAGE_SLUG,
$this->setPageTitle(__('Help', 'mailpoet')),
__('Help', 'mailpoet'),
$this->user_capability,
AccessControl::PERMISSION_ACCESS_PLUGIN_ADMIN,
'mailpoet-help',
array(
$this,
@ -271,7 +260,7 @@ class Menu {
License::getLicense() ? true : self::MAIN_PAGE_SLUG,
$this->setPageTitle(__('Premium', 'mailpoet')),
__('Premium', 'mailpoet'),
$this->user_capability,
AccessControl::PERMISSION_ACCESS_PLUGIN_ADMIN,
'mailpoet-premium',
array(
$this,
@ -284,7 +273,7 @@ class Menu {
true,
$this->setPageTitle(__('Welcome', 'mailpoet')),
__('Welcome', 'mailpoet'),
$this->user_capability,
AccessControl::PERMISSION_ACCESS_PLUGIN_ADMIN,
'mailpoet-welcome',
array(
$this,
@ -297,7 +286,7 @@ class Menu {
true,
$this->setPageTitle(__('Update', 'mailpoet')),
__('Update', 'mailpoet'),
$this->user_capability,
AccessControl::PERMISSION_ACCESS_PLUGIN_ADMIN,
'mailpoet-update',
array(
$this,
@ -310,7 +299,7 @@ class Menu {
true,
$this->setPageTitle(__('Migration', 'mailpoet')),
'',
$this->user_capability,
AccessControl::PERMISSION_ACCESS_PLUGIN_ADMIN,
'mailpoet-migration',
array(
$this,
@ -422,6 +411,7 @@ class Menu {
'premium_key_valid' => !empty($this->premium_key_valid),
'mss_active' => Bridge::isMPSendingServiceEnabled(),
'mss_key_valid' => !empty($mp_api_key_valid),
'members_plugin_active' => is_plugin_active('members/members.php'),
'pages' => Pages::getAll(),
'flags' => $flags,
'current_user' => wp_get_current_user(),
@ -658,7 +648,7 @@ class Menu {
true,
'MailPoet',
'MailPoet',
$access_control->getUserFirstCapability(),
AccessControl::PERMISSION_ACCESS_PLUGIN_ADMIN,
$_REQUEST['page'],
array(
__CLASS__,

View File

@ -187,7 +187,8 @@ class Migrator {
'created_at TIMESTAMP NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),',
'UNIQUE KEY subscriber_segment (subscriber_id,segment_id)'
'UNIQUE KEY subscriber_segment (subscriber_id,segment_id),',
'KEY segment_id (segment_id)',
);
return $this->sqlify(__FUNCTION__, $attributes);
}

View File

@ -8,7 +8,7 @@ class AppWelcome {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/app_welcome';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/app_welcome';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class BurgerJoint {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = 'http://ps.w.org/mailpoet/assets/newsletter-templates/burger_joint';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/burger_joint';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class ChocolateStore {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/chocolate_store';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/chocolate_store';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class CoffeeShop {
private $template_image_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/franks-roast-house';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/franks-roast-house';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class Discount {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/discount';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/discount';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class Faith {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/faith';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/faith';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class FestivalEvent {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/festival_event';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/festival_event';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -9,7 +9,7 @@ class FoodBox {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/food_box';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/food_box';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class KickOff {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/kick_off';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/kick_off';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class NewsDay {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/news_day';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/news_day';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -7,7 +7,7 @@ class NewsletterBlank121Column {
function __construct($assets_url) {
$this->assets_url = $assets_url;
$this->external_template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/newsletter-blank-1-2-1-column';
$this->external_template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/newsletter-blank-1-2-1-column';
$this->template_image_url = $this->assets_url . '/img/blank_templates';
$this->social_icon_url = $this->assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -7,7 +7,7 @@ class NewsletterBlank12Column {
function __construct($assets_url) {
$this->assets_url = $assets_url;
$this->external_template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/newsletter-blank-1-2-column';
$this->external_template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/newsletter-blank-1-2-column';
$this->template_image_url = $this->assets_url . '/img/blank_templates';
$this->social_icon_url = $this->assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -7,7 +7,7 @@ class NewsletterBlank13Column {
function __construct($assets_url) {
$this->assets_url = $assets_url;
$this->external_template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/newsletter-blank-1-3-column';
$this->external_template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/newsletter-blank-1-3-column';
$this->template_image_url = $this->assets_url . '/img/blank_templates';
$this->social_icon_url = $this->assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -7,7 +7,7 @@ class NewsletterBlank1Column {
function __construct($assets_url) {
$this->assets_url = $assets_url;
$this->external_template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/newsletter-blank-1-column';
$this->external_template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/newsletter-blank-1-column';
$this->template_image_url = $this->assets_url . '/img/blank_templates';
$this->social_icon_url = $this->assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class PieceOfCake {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/piece_of_cake';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/piece_of_cake';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -7,7 +7,7 @@ class PostNotificationsBlank1Column {
function __construct($assets_url) {
$this->assets_url = $assets_url;
$this->external_template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/post-notifications-blank-1-column';
$this->external_template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/post-notifications-blank-1-column';
$this->template_image_url = $this->assets_url . '/img/blank_templates';
$this->social_icon_url = $this->assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class ScienceWeekly {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/science_weekly';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/science_weekly';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class Shoes {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/shoes';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/shoes';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -7,7 +7,7 @@ class SimpleText {
function __construct($assets_url) {
$this->assets_url = $assets_url;
$this->external_template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/simple-text';
$this->external_template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/simple-text';
$this->template_image_url = $this->assets_url . '/img/blank_templates';
$this->social_icon_url = $this->assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class TakeAHike {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/take_a_hike';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/take_a_hike';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class TravelNomads {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/travel_nomads';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/travel_nomads';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -7,7 +7,7 @@ class WelcomeBlank12Column {
function __construct($assets_url) {
$this->assets_url = $assets_url;
$this->external_template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/welcome-email-blank-1-2-column';
$this->external_template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/welcome-email-blank-1-2-column';
$this->template_image_url = $this->assets_url . '/img/blank_templates';
$this->social_icon_url = $this->assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -7,7 +7,7 @@ class WelcomeBlank1Column {
function __construct($assets_url) {
$this->assets_url = $assets_url;
$this->external_template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/welcome-email-blank-1-column';
$this->external_template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/welcome-email-blank-1-column';
$this->template_image_url = $this->assets_url . '/img/blank_templates';
$this->social_icon_url = $this->assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class WorldCup {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/world_cup';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/world_cup';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -8,7 +8,7 @@ class YogaStudio {
private $social_icon_url;
function __construct($assets_url) {
$this->template_image_url = '//ps.w.org/mailpoet/assets/newsletter-templates/yoga_studio';
$this->template_image_url = 'https://ps.w.org/mailpoet/assets/newsletter-templates/yoga_studio';
$this->social_icon_url = $assets_url . '/img/newsletter_editor/social-icons';
}

View File

@ -4,6 +4,7 @@ use MailPoet\Models\Newsletter;
use MailPoet\Models\Subscriber;
use MailPoet\Models\SubscriberSegment;
use MailPoet\Newsletter\Url as NewsletterUrl;
use MailPoet\WP\Hooks;
class Shortcodes {
function __construct() {
@ -26,10 +27,10 @@ class Shortcodes {
$this, 'getArchive'
));
add_filter('mailpoet_archive_date', array(
Hooks::addFilter('mailpoet_archive_date', array(
$this, 'renderArchiveDate'
), 2);
add_filter('mailpoet_archive_subject', array(
Hooks::addFilter('mailpoet_archive_subject', array(
$this, 'renderArchiveSubject'
), 2, 3);
}
@ -79,12 +80,12 @@ class Shortcodes {
$subscriber = Subscriber::getCurrentWPUser();
if(empty($newsletters)) {
return apply_filters(
return Hooks::applyFilters(
'mailpoet_archive_no_newsletters',
__('Oops! There are no newsletters to display.', 'mailpoet')
);
} else {
$title = apply_filters('mailpoet_archive_title', '');
$title = Hooks::applyFilters('mailpoet_archive_title', '');
if(!empty($title)) {
$html .= '<h3 class="mailpoet_archive_title">'.$title.'</h3>';
}
@ -93,10 +94,10 @@ class Shortcodes {
$queue = $newsletter->queue()->findOne();
$html .= '<li>'.
'<span class="mailpoet_archive_date">'.
apply_filters('mailpoet_archive_date', $newsletter).
Hooks::applyFilters('mailpoet_archive_date', $newsletter).
'</span>
<span class="mailpoet_archive_subject">'.
apply_filters('mailpoet_archive_subject', $newsletter, $subscriber, $queue).
Hooks::applyFilters('mailpoet_archive_subject', $newsletter, $subscriber, $queue).
'</span>
</li>';
}

View File

@ -1,7 +1,9 @@
<?php
namespace MailPoet\Form\Block;
use MailPoet\Form\Util\FieldNameObfuscator;
use MailPoet\Models\ModelValidator;
abstract class Base {
protected static function getInputValidation($block, $extra_rules = array()) {
@ -9,6 +11,8 @@ abstract class Base {
if($block['id'] === 'email') {
$rules['required'] = true;
$rules['minlength'] = ModelValidator::EMAIL_MIN_LENGTH;
$rules['maxlength'] = ModelValidator::EMAIL_MAX_LENGTH;
$rules['error-message'] = __('Please specify a valid email address.', 'mailpoet');
}

View File

@ -7,6 +7,9 @@ if(!defined('ABSPATH')) exit;
class ModelValidator extends \Sudzy\Engine {
public $validators;
const EMAIL_MIN_LENGTH = 6;
const EMAIL_MAX_LENGTH = 150;
function __construct() {
parent::__construct();
$this->validators = array(
@ -26,7 +29,9 @@ class ModelValidator extends \Sudzy\Engine {
}
function validateEmail($email) {
return is_email($email) !== false;
$permitted_length = (strlen($email) >= self::EMAIL_MIN_LENGTH && strlen($email) <= self::EMAIL_MAX_LENGTH);
$valid_email = (is_email($email) !== false);
return ($permitted_length && $valid_email);
}
function validateRenderedNewsletterBody($newsletter_body) {

View File

@ -14,7 +14,6 @@ class Subscriber extends Model {
const STATUS_UNSUBSCRIBED = 'unsubscribed';
const STATUS_UNCONFIRMED = 'unconfirmed';
const STATUS_BOUNCED = 'bounced';
const SUBSCRIPTION_LIMIT_COOLDOWN = 60;
const SUBSCRIBER_TOKEN_LENGTH = 6;
function __construct() {
@ -175,22 +174,7 @@ class Subscriber extends Model {
'signup_confirmation.enabled'
);
$subscriber_data['subscribed_ip'] = (isset($_SERVER['REMOTE_ADDR']))
? $_SERVER['REMOTE_ADDR']
: null;
// make sure we don't allow too many subscriptions with the same ip address
$subscription_count = Subscriber::where(
'subscribed_ip',
$subscriber_data['subscribed_ip']
)->whereRaw(
'TIME_TO_SEC(TIMEDIFF(NOW(), created_at)) < ?',
self::SUBSCRIPTION_LIMIT_COOLDOWN
)->count();
if($subscription_count > 0) {
throw new \Exception(__('You need to wait before subscribing again.', 'mailpoet'));
}
$subscriber_data['subscribed_ip'] = Helpers::getIP();
$subscriber = self::findOne($subscriber_data['email']);
@ -205,6 +189,7 @@ class Subscriber extends Model {
} else {
// store subscriber data to be updated after confirmation
$subscriber->setUnconfirmedData($subscriber_data);
$subscriber->setExpr('updated_at', 'NOW()');
}
// restore trashed subscriber

View File

@ -111,6 +111,7 @@ class WP {
SELECT wu.id, wu.user_email, "subscribed", CURRENT_TIMESTAMP() FROM %s wu
LEFT JOIN %s mps ON wu.id = mps.wp_user_id
WHERE mps.wp_user_id IS NULL
ON DUPLICATE KEY UPDATE wp_user_id = wu.id
', $subscribers_table, $wpdb->users, $subscribers_table));
}

View File

@ -64,7 +64,7 @@ class Pages {
$subscriber_data = $this->subscriber->getUnconfirmedData();
$this->subscriber->status = Subscriber::STATUS_SUBSCRIBED;
$this->subscriber->confirmed_ip = (!empty($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : null;
$this->subscriber->confirmed_ip = Helpers::getIP();
$this->subscriber->setExpr('confirmed_at', 'NOW()');
$this->subscriber->unconfirmed_data = null;
$this->subscriber->save();

View File

@ -146,4 +146,9 @@ class Helpers {
return explode(self::DIVIDER, $object);
}
static function getIP() {
return (isset($_SERVER['REMOTE_ADDR']))
? $_SERVER['REMOTE_ADDR']
: null;
}
}

View File

@ -4,7 +4,7 @@ if(!defined('ABSPATH')) exit;
/*
* Plugin Name: MailPoet 3 (new)
* Version: 3.0.0
* Version: 3.0.4
* Plugin URI: http://www.mailpoet.com
* Description: Create and send newsletters, post notifications and welcome emails from your WordPress.
* Author: MailPoet
@ -12,7 +12,6 @@ if(!defined('ABSPATH')) exit;
* Requires at least: 4.6
* Tested up to: 4.8
*
* Text Domain: mailpoet
* Domain Path: /lang/
*
* @package WordPress
@ -21,7 +20,7 @@ if(!defined('ABSPATH')) exit;
*/
$mailpoet_plugin = array(
'version' => '3.0.0',
'version' => '3.0.4',
'filename' => __FILE__,
'path' => dirname(__FILE__),
'autoloader' => dirname(__FILE__) . '/vendor/autoload.php',

View File

@ -4,7 +4,7 @@ Tags: newsletter, email, welcome email, post notification, autoresponder, signup
Requires at least: 4.6
Tested up to: 4.8
Requires PHP: 5.3
Stable tag: 3.0.0
Stable tag: 3.0.4
License: GPLv3
License URI: https://www.gnu.org/licenses/gpl-3.0.html
@ -30,6 +30,7 @@ The new MailPoet is here! With our new free sending plan, send unlimited emails
= Before you install =
Take note:
* Not optimized for right-to-left (RTL) languages yet
* Multisite works, but is not officially supported
* Please check the translations in your language
@ -40,6 +41,7 @@ Take note:
MailPoet is fully featured in its free version and works up until you have 2000 subscribers.
The Premium version adds the following features:
* for each newsletter, see which subscribers opened it and which links they clicked
* ability to send Welcome Emails automatically; i.e. "Welcome to my Newsletter” autoresponders or multi-email courses
* removes the small MailPoet logo in the footer of your emails
@ -112,6 +114,25 @@ Stop by our [support site](https://www.mailpoet.com/support).
== Changelog ==
= 3.0.4 - 2017-10-05 =
* Fixed: dividers and spacers' height can be changed on mouse drag again;
= 3.0.3 - 2017-10-03 =
* Fixed: mixed collation error in WordPress user synchronization. Thanks Chris, Till, Robin, Robero, @Seph, @kaiwen and others for the reports!
= 3.0.2 - 2017-10-03 =
* Improved: plugin capabilities can be managed with Members plugin;
* Improved: removes unsightly horizontal scrollbar in some parts of the newsletter editor;
* Improved: email templates to be displayed in order of last modification;
* Fixed: it's not possible to submit a subscription form multiple times with an existing e-mail address anymore. Thanks Suyog Palav and Bits of Freedom!
* Fixed: users subscribed before registering on a site are synchronized during WP users sync. Thanks Nicolas!
= 3.0.1 - 2017-09-26 =
* Added: images can be resized in newsletter editor;
* Fixed: scheduled newsletters that are unscheduled will not be mistakenly sent. Thx Georges in Provence;
* Fixed: plugin does not time out on sites with a large number of lists and subscribers. Thanks Roy;
* Fixed: long email addresses no longer trigger MySQL errors during subscription. Thank you Sameer Bhatt and Bits of Freedom!
= 3.0.0 - 2017-09-20 =
* Official launch of the new MailPoet. :)
* Improved: MailPoet 3 now works with other plugins that use a supported version of Twig templating engine. Thanks @supsysticcom;

View File

@ -0,0 +1,34 @@
<?php
namespace MailPoet\Test\API\JSON;
use MailPoet\API\JSON\ErrorResponse;
class ErrorResponseTest extends \MailPoetTest {
function testItSanitizesSqlErrorsWhenReturningResponse() {
$errors = array(
'valid error',
'SQLSTATE[22001]: Some SQL error',
'another valid error'
);
$error_response = new ErrorResponse($errors);
expect($error_response->getData())->equals(
array(
'errors' => array(
array(
'error' => 0,
'message' => 'valid error'
),
array(
'error' => 1,
'message' => 'An unknown error occurred.'
),
array(
'error' => 2,
'message' => 'another valid error'
)
)
)
);
}
}

View File

@ -15,4 +15,14 @@ class AutomatedLatestContentTest extends \MailPoetTest {
expect($post_type['label'])->notEmpty();
}
}
function testItDoesNotGetPostTypesExludedFromSearch() {
$router = new AutomatedLatestContent();
$response = $router->getPostTypes();
// WP's default post type 'revision' is excluded from search
// https://codex.wordpress.org/Post_Types
$revision_post_type = get_post_type_object('revision');
expect($revision_post_type->exclude_from_search)->true();
expect(isset($response->data['revision']))->false();
}
}

View File

@ -169,9 +169,10 @@ class NewslettersTest extends \MailPoetTest {
$sending_queue = SendingQueue::create();
$sending_queue->newsletter_id = $this->newsletter->id;
$sending_queue->status = SendingQueue::STATUS_SCHEDULED;
$sending_queue->newsletter_rendered_body = 'Rendered body ...';
$sending_queue->newsletter_rendered_body = array('html' => 'html', 'text' => 'text');
$sending_queue->newsletter_rendered_subject = 'Rendered subject ...';
$sending_queue->save();
expect($sending_queue->getErrors())->false();
$router = new Newsletters();
$newsletter_data = array(
@ -771,6 +772,38 @@ class NewslettersTest extends \MailPoetTest {
expect((boolean)$preview_link_data['preview'])->true();
}
function testItDeletesSendingQueueAndSetsNewsletterStatusToDraftWhenItIsUnscheduled() {
$newsletter = $this->newsletter;
$newsletter->status = Newsletter::STATUS_SCHEDULED;
$newsletter->save();
expect($newsletter->getErrors())->false();
$sending_queue = SendingQueue::create();
$sending_queue->newsletter_id = $newsletter->id;
$sending_queue->newsletter_rendered_body = array(
'html' => 'html',
'text' => 'text'
);
$sending_queue->status = SendingQueue::STATUS_SCHEDULED;
$sending_queue->scheduled_at = Carbon::now()->format('Y-m-d H:i');
$sending_queue->save();
expect($sending_queue->getErrors())->false();
$newsletter_data = array(
'id' => $newsletter->id,
'options' => array(
'isScheduled' => false
)
);
$router = new Newsletters();
$response = $router->save($newsletter_data);
$newsletter = Newsletter::findOne($newsletter->id);
$sending_queue = SendingQueue::findOne($sending_queue->id);
expect($newsletter->status)->equals(Newsletter::STATUS_DRAFT);
expect($sending_queue)->false();
}
function _after() {
WPHooksHelper::releaseAllHooks();
\ORM::raw_execute('TRUNCATE ' . Newsletter::$_table);

View File

@ -1,6 +1,7 @@
<?php
namespace MailPoet\Test\API\JSON\v1;
use Carbon\Carbon;
use Codeception\Util\Fixtures;
use MailPoet\API\JSON\v1\Subscribers;
use MailPoet\API\JSON\Response as APIResponse;
@ -519,6 +520,34 @@ class SubscribersTest extends \MailPoetTest {
}
}
function testItCannotMassResubscribe() {
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$router = new Subscribers();
$response = $router->subscribe(array(
$this->obfuscatedEmail => 'toto@mailpoet.com',
'form_id' => $this->form->id,
$this->obfuscatedSegments => array($this->segment_1->id, $this->segment_2->id)
));
// Try to resubscribe an existing subscriber that was updated just now
$subscriber = Subscriber::findOne($response->data['id']);
$subscriber->created_at = Carbon::yesterday();
$subscriber->updated_at = Carbon::now();
$subscriber->save();
try {
$response = $router->subscribe(array(
$this->obfuscatedEmail => $subscriber->email,
'form_id' => $this->form->id,
$this->obfuscatedSegments => array($this->segment_1->id, $this->segment_2->id)
));
$this->fail('It should not be possible to resubscribe a second time so soon');
} catch(\Exception $e) {
expect($e->getMessage())->equals('You need to wait before subscribing again.');
}
}
function _after() {
Segment::deleteMany();
Subscriber::deleteMany();

View File

@ -29,9 +29,6 @@ class AccessControlTest extends \MailPoetTest {
AccessControl::PERMISSION_MANAGE_SEGMENTS => array(
'administrator'
),
AccessControl::PERMISSION_UPDATE_PLUGIN => array(
'administrator'
)
);
$access_control = new AccessControl();
expect($access_control->permissions)->equals($default_permissions);
@ -74,12 +71,6 @@ class AccessControlTest extends \MailPoetTest {
return array('custom_manage_segments_role');
}
);
Hooks::addFilter(
'mailpoet_permission_update_plugin',
function() {
return array('custom_update_plugin_role');
}
);
$access_control = new AccessControl();
expect($access_control->permissions)->equals(
@ -102,13 +93,16 @@ class AccessControlTest extends \MailPoetTest {
AccessControl::PERMISSION_MANAGE_SEGMENTS => array(
'custom_manage_segments_role'
),
AccessControl::PERMISSION_UPDATE_PLUGIN => array(
'custom_update_plugin_role'
),
)
);
}
function testItGetsPermissionLabels() {
$permissions = AccessControl::getDefaultPermissions();
$labels = AccessControl::getPermissionLabels();
expect(count($permissions))->equals(count($labels));
}
function _after() {
WPHooksHelper::releaseAllHooks();
}

View File

@ -0,0 +1,98 @@
<?php
namespace MailPoet\Test\Config;
use AspectMock\Test as Mock;
use Codeception\Util\Stub;
use Helper\WordPressHooks as WPHooksHelper;
use MailPoet\Config\AccessControl;
use MailPoet\Config\Capabilities;
use MailPoet\Config\Renderer;
class CapabilitiesTest extends \MailPoetTest {
function _before() {
$renderer = new Renderer();
$this->caps = new Capabilities($renderer);
}
function testItInitializes() {
$caps = Stub::makeEmptyExcept(
$this->caps,
'init',
array('setupMembersCapabilities' => Stub::once()),
$this
);
$caps->init();
}
function testItSetsUpWPCapabilities() {
$permissions = AccessControl::getDefaultPermissions();
$this->caps->setupWPCapabilities();
$checked = false;
foreach($permissions as $name => $roles) {
foreach($roles as $role) {
$checked = true;
expect(get_role($role)->has_cap($name))->true();
}
}
expect($checked)->true();
}
function testItRemovesWPCapabilities() {
$permissions = AccessControl::getDefaultPermissions();
$this->caps->removeWPCapabilities();
$checked = false;
foreach($permissions as $name => $roles) {
foreach($roles as $role) {
$checked = true;
expect(get_role($role)->has_cap($name))->false();
}
}
expect($checked)->true();
// Restore capabilities
$this->caps->setupWPCapabilities();
}
function testItSetsUpMembersCapabilities() {
WPHooksHelper::interceptAddAction();
$this->caps->setupMembersCapabilities();
$hook_name = 'members_register_cap_groups';
expect(WPHooksHelper::isActionAdded($hook_name))->true();
expect(is_callable(WPHooksHelper::getActionAdded($hook_name)[0]))->true();
$hook_name = 'members_register_caps';
expect(WPHooksHelper::isActionAdded($hook_name))->true();
expect(is_callable(WPHooksHelper::getActionAdded($hook_name)[0]))->true();
}
function testItRegistersMembersCapGroup() {
if(function_exists('members_register_cap_group')) { // Members plugin active
$this->caps->registerMembersCapGroup();
expect_that(members_cap_group_exists(Capabilities::MEMBERS_CAP_GROUP_NAME));
} else {
$func = Mock::func('MailPoet\Config', 'members_register_cap_group', true);
$this->caps->registerMembersCapGroup();
$func->verifyInvoked([Capabilities::MEMBERS_CAP_GROUP_NAME]);
}
}
function testItRegistersMembersCapabilities() {
$permissions = AccessControl::getPermissionLabels();
$permission_count = count($permissions);
if(function_exists('members_register_cap')) { // Members plugin active
$this->caps->registerMembersCapabilities();
expect(members_get_cap_group(Capabilities::MEMBERS_CAP_GROUP_NAME)->caps)
->count($permission_count);
} else {
$func = Mock::func('MailPoet\Config', 'members_register_cap', true);
$this->caps->registerMembersCapabilities();
$func->verifyInvokedMultipleTimes($permission_count);
}
}
function _after() {
WPHooksHelper::releaseAllHooks();
Mock::clean();
}
}

View File

@ -1,6 +1,7 @@
<?php
namespace MailPoet\Test\Config;
use Helper\WordPress;
use MailPoet\Config\Shortcodes;
use MailPoet\Models\Newsletter;
use MailPoet\Models\SendingQueue;
@ -21,8 +22,20 @@ class ShortcodesTest extends \MailPoetTest {
function testItGetsArchives() {
$shortcodes = new Shortcodes();
WordPress::interceptFunction('apply_filters', function() use($shortcodes) {
$args = func_get_args();
$filter_name = array_shift($args);
switch ($filter_name) {
case 'mailpoet_archive_date':
return $shortcodes->renderArchiveDate($args[0]);
case 'mailpoet_archive_subject':
return $shortcodes->renderArchiveSubject($args[0], $args[1], $args[2]);
}
return '';
});
// result contains a link pointing to the "view in browser" router endpoint
$result = $shortcodes->getArchive($params = false);
WordPress::releaseFunction('apply_filters');
$dom = \pQuery::parseStr($result);
$link = $dom->query('a');
$link = $link->attr('href');

View File

@ -21,8 +21,9 @@ class ModelValidatorTest extends \MailPoetTest {
function testItValidatesEmail() {
expect($this->validator->validateEmail('test'))->false();
expect($this->validator->validateEmail('tést@éxample.com'))->false();
expect($this->validator->validateEmail('test@example.com'))->true();
expect($this->validator->validateEmail('loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_email@example.com'))->false();
expect($this->validator->validateEmail('a@b.c'))->false();
}
function testItValidatesRenderedNewsletterBody() {

View File

@ -32,6 +32,19 @@ class WPTest extends \MailPoetTest {
expect($subscribersCount)->equals(3);
}
function testItSynchronizesPresubscribedUsers() {
$random_number = 12345;
$subscriber = Subscriber::createOrUpdate(array(
'email' => 'user-sync-test' . $random_number . '@example.com',
'status' => Subscriber::STATUS_SUBSCRIBED
));
$id = $this->insertUser($random_number);
WP::synchronizeUsers();
$wp_subscriber = Segment::getWPSegment()->subscribers()->where('wp_user_id', $id)->findOne();
expect($wp_subscriber)->notEmpty();
expect($wp_subscriber->id)->equals($subscriber->id);
}
function testItSynchronizeEmails() {
$id = $this->insertUser();
WP::synchronizeUsers();
@ -216,16 +229,17 @@ class WPTest extends \MailPoetTest {
*
* @return string
*/
private function insertUser() {
private function insertUser($number = null) {
global $wpdb;
$db = \ORM::getDb();
$number_sql = !is_null($number) ? (int)$number : 'rand()';
$db->exec(sprintf('
INSERT INTO
%s (user_login, user_email, user_registered)
VALUES
(
CONCAT("user-sync-test", rand()),
CONCAT("user-sync-test", rand(), "@example.com"),
CONCAT("user-sync-test", ' . $number_sql . '),
CONCAT("user-sync-test", ' . $number_sql . ', "@example.com"),
"2017-01-02 12:31:12"
)', $wpdb->users));
$id = $db->lastInsertId();

View File

@ -1,3 +1,14 @@
<div class="mailpoet_tools"></div>
<div class="mailpoet_content" style="text-align: {{ model.styles.block.textAlign }}"><a href="{{ model.link }}" onClick="return false;"><img src="{{#ifCond model.src '!=' ''}}{{ model.src }}{{ else }}{{ imageMissingSrc }}{{/ifCond}}" alt="{{ model.alt }}" onerror="if(this.src != '{{ imageMissingSrc }}') {this.src = '{{ imageMissingSrc }}'; this.style.width='auto';}" /></a></div>
<div class="mailpoet_content" style="text-align: {{ model.styles.block.textAlign }}; width: {{model.width}}">
<div class="mailpoet_image">
<a href="{{ model.link }}" onClick="return false;">
<img src="{{#ifCond model.src '!=' ''}}{{ model.src }}{{ else }}{{ imageMissingSrc }}{{/ifCond}}" alt="{{ model.alt }}" onerror="if(this.src != '{{ imageMissingSrc }}') {this.src = '{{ imageMissingSrc }}'; this.style.width='auto';}" width="{{model.width}}" />
</a>
<div class="mailpoet_image_resize_handle_container">
<div class="mailpoet_image_resize_handle">
<span class="mailpoet_image_resize_handle_icon"><%= source('newsletter/templates/svg/block-tools/resize.svg') %></span>
</div>
</div>
</div>
</div>
<div class="mailpoet_block_highlight"></div>

View File

@ -23,6 +23,31 @@
</div>
</label>
</div>
<div class="mailpoet_form_field">
<label>
<div class="mailpoet_form_field_title"><%= __('Width') %></div>
<div class="mailpoet_form_field_input_option">
<input
class="mailpoet_input mailpoet_input_small mailpoet_field_image_width_input"
name="image-width-input"
type="number"
value="{{getNumber model.width}}"
min="36"
max="660"
step="2"
/> px
<input
class="mailpoet_range mailpoet_range_small mailpoet_field_image_width"
name="image-width"
type="range"
value="{{getNumber model.width}}"
min="36"
max="660"
step="2"
/>
</div>
</label>
</div>
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_checkbox_option">
<label>

View File

@ -0,0 +1,4 @@
<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"
viewBox="0 0 150 150" enable-background="new 0 0 150 150" xml:space="preserve">
<path d="M30,30L100,30L30,100ZM120,120L120,50L50,120Z"/>
</svg>

After

Width:  |  Height:  |  Size: 272 B

View File

@ -19,7 +19,7 @@
<a class="nav-tab" href="#basics"><%= __('Basics') %></a>
<a class="nav-tab" href="#signup"><%= __('Sign-up Confirmation') %></a>
<a class="nav-tab" href="#advanced"><%= __('Advanced') %></a>
<a class="nav-tab" href="#premium"><%= __('Premium') %></a>
<a class="nav-tab" href="#premium"><%= __('Key Activation') %></a>
</h2>
<!-- sending method -->

View File

@ -67,6 +67,27 @@
</p>
</td>
</tr>
<!-- roles and capabilities -->
<tr>
<th scope="row">
<%= __('Roles and capabilities') %>
<p class="description">
<%= __('Manage which WordPress roles access which features of MailPoet.') %>
</p>
</th>
<td>
<% if (members_plugin_active) %>
<p>
<a href="<%= admin_url('users.php?page=roles') %>"><%= __('Manage using the Members plugin') %></a>
</p>
<% else %>
<%= __('Install the plugin [link]Members[/link] (free) to manage permissions.')
|replaceLinkTags('https://wordpress.org/plugins/members/', {'target' : '_blank'})
|raw
%>
<% endif %>
</td>
</tr>
<!-- link tracking -->
<tr>
<th scope="row">

View File

@ -4,10 +4,10 @@
<tr>
<th scope="row">
<label for="mailpoet_premium_key">
<%= __('Premium License Key') %>
<%= __('Activation Key') %>
</label>
<p class="description">
<%= __('This key is used for automatic upgrades of your Premium features and access to support.') %>
<%= __('This key is used to validate your free or paid subscription. Paying customers will enjoy automatic upgrades of their Premium plugin and access to faster support.') %>
</p>
</th>
<td>
@ -32,7 +32,7 @@
<div
class="mailpoet_premium_key_invalid mailpoet_error_item mailpoet_error<% if not(settings.premium.premium_key) or premium_key_valid %> mailpoet_hidden<% endif %>"
>
<%= __('Your Premium key is invalid.') %>
<%= __('Your key is not valid for the Premium plugin.') %>
</div>
<div
class="mailpoet_mss_key_valid mailpoet_success_item mailpoet_success<% if not(settings.mta.mailpoet_api_key) or not(mss_key_valid) %> mailpoet_hidden<% endif %>"
@ -42,7 +42,7 @@
<div
class="mailpoet_mss_key_invalid mailpoet_error_item mailpoet_error<% if not(settings.mta.mailpoet_api_key) or mss_key_valid %> mailpoet_hidden<% endif %>"
>
<%= __('Your MailPoet Sending Service key is invalid.') %>
<%= __('Your key is not valid for the MailPoet Sending Service.') %>
</div>
<div
class="mailpoet_premium_download mailpoet_spaced_block"

View File

@ -60,18 +60,8 @@
<div class="feature-section one-col mailpoet_centered">
<h2><%= __('Care to Give Your Opinion?') %></h2>
<div class="pd-embed" id="pd_1505214143"></div>
<script type="text/javascript">
var _polldaddy = [] || _polldaddy;
_polldaddy.push( {
type: "iframe",
auto: "1",
domain: "mailpoet.polldaddy.com/s/",
id: "what-s-one-feature-that-s-missing-in-mailpoet",
placeholder: "pd_1505214143"
} );
(function(d,c,j){if(!document.getElementById(j)){var pd=d.createElement(c),s;pd.id=j;pd.src=('https:'==document.location.protocol)?'https://polldaddy.com/survey.js':'http://i0.poll.fm/survey.js';s=document.getElementsByTagName(c)[0];s.parentNode.insertBefore(pd,s);}}(document,'script','pd-embed'));
</script>
<script type="text/javascript" charset="utf-8" src="https://static.polldaddy.com/p/9840615.js"></script>
<noscript><a href="https://polldaddy.com/poll/9840615/">How did you find out about our plugin?</a></noscript>
</div>
<hr>