Compare commits

...

52 Commits

Author SHA1 Message Date
e012bd6cbe Updates changelog & bumps up release version to 3.0.0-rc.2.0.2 2017-09-05 11:17:32 -04:00
a02e64e805 Merge pull request #1084 from mailpoet/editor_get_post_type_optimization
Removes unused properties from the object used to display post types in editor [MAILPOET-1086]
2017-09-05 17:43:27 +03:00
e4bb3e1133 Adds unit test 2017-09-05 10:33:10 -04:00
998795e0e0 Fix tests semi eslint rule [MAILPOET-1030] 2017-09-05 12:31:51 +01:00
ec44b84cc9 Fix ES6 no-extra-semi eslint rule [MAILPOET-1030] 2017-09-05 12:31:51 +01:00
efece061d0 Fix ES5 semi eslint rule [MAILPOET-1030] 2017-09-05 12:31:51 +01:00
e347fc74a2 Fix ES5 no-extra-semi eslint rule [MAILPOET-1030] 2017-09-05 12:31:51 +01:00
027418a86c Fix ES5 semi-spacing eslint rule [MAILPOET-1030] 2017-09-05 12:31:51 +01:00
864187aa02 Merge pull request #1080 from mailpoet/json_api_method_check_fix
Throws error when JSON API endpoint method is not found [MAILPOET-1074]
2017-09-05 13:08:37 +03:00
59ae6619c0 Browser preview mixed content error fix
Strips protocol from preview URL

[MAILPOET-1080]
2017-09-05 08:54:23 +01:00
6aa0be8d01 Removes unused properties from the object 2017-09-04 21:07:15 -04:00
657658ea2b Adds new poll 2017-09-04 10:49:11 +02:00
8647128e12 Merge pull request #1081 from mailpoet/default_status_update
Default status is set depending on the signup confirmation option [MAILPOET-1079]
2017-09-04 10:02:45 +03:00
c8d92b3cd2 Default status is set depending on the signup confirmation option 2017-08-31 18:48:43 -04:00
cc8b7b45ed Throws error when endpoint method is not found 2017-08-31 15:18:22 -04:00
5b8b8c8441 Merge pull request #1077 from mailpoet/helpscout
Split support inquiries into Free and Premium inboxes [MAILPOET-869]
2017-08-31 12:38:18 -04:00
7106c640ef Refactor HelpScout Beacon embed script 2017-08-31 19:24:32 +03:00
300b84983d Merge pull request #1078 from mailpoet/spellchecker
Enable browser spellchecker [MAILPOET-1078]
2017-08-31 18:14:47 +03:00
49c1b92838 Enable browser spellchecker 2017-08-31 18:03:19 +03:00
d900827850 Merge pull request #1076 from mailpoet/lint-lines
Lint lines [MAILPOET-1031]
2017-08-31 12:49:10 +03:00
1688d4dbe1 Split support inquiries into Free and Premium inboxes [MAILPOET-869] 2017-08-31 12:02:20 +03:00
856c636089 Releasing 3.0.0-rc.2.0.1 2017-08-30 16:06:39 +00:00
8f9e8ea185 Merge pull request #1075 from mailpoet/forms-bug
Fix form issue when using list selection field [MAILPOET-1077]
2017-08-30 18:10:33 +03:00
b0b88693f1 Merge pull request #1071 from mailpoet/initializer_cleanup
Fixes activation on MS environments and cleans up Initializer [MAILPOET-1076]
2017-08-30 17:56:16 +03:00
9916eb9da8 updated tests 2017-08-30 14:52:17 +00:00
79b5426e01 Fix a constant name [MAILPOET-1076] 2017-08-30 17:47:53 +03:00
5807fd2e02 Merge pull request #1067 from mailpoet/emoji
Add emoji support to newsletter body [MAILPOET-1009]
2017-08-30 10:04:26 -04:00
0ee39143f4 Runs hooks setup only when plugin is initilized 2017-08-30 09:50:49 -04:00
10c39bd650 Removes unused constructor 2017-08-30 09:47:34 -04:00
20593cc5a5 Fix form issue when using list selection field 2017-08-30 13:29:41 +00:00
f438eee842 Fix ESLint newline-per-chained-call in tests
[MAILPOET-1031]
2017-08-30 15:26:02 +02:00
cb4b599d97 Merge pull request #1073 from mailpoet/fix-notice
Fix php notice
2017-08-30 16:25:50 +03:00
33733219f6 Fix ESLint object-property-newline in tests
[MAILPOET-1031]
2017-08-30 15:20:39 +02:00
737a83cdf3 Fix ESLint linebreak-style in tests
[MAILPOET-1031]
2017-08-30 15:19:15 +02:00
9061e1b495 Fix ESLint no-multipe-empty-lines ES6
[MAILPOET-1031]
2017-08-30 15:11:56 +02:00
09199e41a1 Fix ESLint linebreak-style ES5
[MAILPOET-1031]
2017-08-30 14:59:34 +02:00
4e91932613 Fix ESLint lines-around-directive ES5
[MAILPOET-1031]
2017-08-30 14:45:25 +02:00
227de4ecfa Fix ESLint newline-per-chained-call ES5
[MAILPOET-1031]
2017-08-30 14:40:42 +02:00
c1ccacf851 Fix ESLint newline-per-chained-call
[MAILPOET-1031]
2017-08-30 14:30:48 +02:00
53f7953566 Fix browser preview bypassing emoji encoding [MAILPOET-1009] 2017-08-30 14:39:38 +03:00
61ae2da1e3 Fix a constant not defined in PHP 5.3 [MAILPOET-1009] 2017-08-30 14:02:29 +03:00
36abd8e5e6 Don't show network activation notice for other plugins
[MAILPOET-1072]
2017-08-30 11:39:42 +02:00
7e9de1fd07 Fix php notice 2017-08-30 11:25:42 +02:00
7ac5e65963 Fix php notice 2017-08-30 10:30:16 +02:00
cf992852b5 Validate for unsubscribe link only for MSS sending method [MAILPOET-1050] 2017-08-30 10:16:24 +02:00
59482b2bfa Uses init hook to initilize AccessControl 2017-08-29 23:30:35 -04:00
053f9e0cdf Adds higher priority to init hook so that it fires before the widgets hook 2017-08-29 23:20:46 -04:00
e1cc25239b Maintains code consistency when setting up JSON API
Updates exception handler return statement
2017-08-29 20:33:50 -04:00
2f4452ad36 Removes redundant exception handler 2017-08-29 20:28:20 -04:00
f453d685d6 Fixes translation not being picked up by makepot 2017-08-29 20:24:04 -04:00
2d2b4ca7f0 Moves setup actions from plugins_loaded hook to init hook
Rearranges class methods to follow the order by which they are called
2017-08-29 20:22:19 -04:00
b9bdc86fd9 Add emoji support to newsletter body [MAILPOET-1009] 2017-08-28 19:07:17 +03:00
64 changed files with 818 additions and 518 deletions

View File

@ -19,11 +19,9 @@
"guard-for-in": 0,
"no-prototype-builtins": 0,
"no-restricted-syntax": 0,
"newline-per-chained-call": 0,
"no-useless-concat": 0,
"no-multi-spaces": 0,
"no-nested-ternary": 0,
"semi-spacing": 0,
"no-sequences": 0,
"no-useless-return": 0,
"array-callback-return": 0,
@ -34,7 +32,6 @@
"no-redeclare": 0,
"no-console": 0,
"no-empty": 0,
"no-extra-semi": 0,
"no-useless-escape": 0,
"wrap-iife": 0,
"no-unused-expressions": 0,
@ -42,7 +39,6 @@
"computed-property-spacing": 0,
"no-plusplus": 0,
"array-bracket-spacing": 0,
"lines-around-directive": 0,
"no-unreachable": 0,
"default-case": 0,
"no-lonely-if": 0,
@ -51,7 +47,6 @@
"no-mixed-operators": 0,
"eqeqeq": 0,
"space-in-parens": 0,
"semi": 0,
"max-len": 0,
"no-trailing-spaces": 0,
"global-require": 0,
@ -79,7 +74,6 @@
"keyword-spacing": 0,
"eol-last": 0,
"dot-notation": 0,
"linebreak-style": 0,
"indent": 0,
"prefer-template": 0,
"func-names": 0

View File

@ -46,7 +46,6 @@
"array-callback-return": 0,
"consistent-return": 0,
"no-unreachable": 0,
"no-extra-semi": 0,
"import/no-unresolved": 0,
"import/extensions": 0,
"import/no-extraneous-dependencies": 0,
@ -60,11 +59,9 @@
"no-multi-spaces": 0,
"class-methods-use-this": 0,
"key-spacing": 0,
"no-multiple-empty-lines": 0,
"space-in-parens": 0,
"no-case-declarations": 0,
"array-bracket-spacing": 0,
"newline-per-chained-call": 0,
"no-else-return": 0,
"max-len": 0,
"no-useless-concat": 0,

View File

@ -12,14 +12,10 @@
"no-undef": 0,
"one-var": 0,
"indent": 0,
"linebreak-style": 0,
"no-whitespace-before-property": 0,
"object-property-newline": 0,
"global-require": 0,
"semi": 0,
"keyword-spacing": 0,
"no-bitwise": 0,
"newline-per-chained-call": 0,
"no-spaced-func": 0,
"func-call-spacing": 0,
"max-len": 0,

View File

@ -1,12 +1,12 @@
define('admin', [
'jquery'
],
function(jQuery) {
jQuery(function($) {
// dom ready
$(function() {
});
});
}
);
define('admin', [
'jquery'
],
function(jQuery) {
jQuery(function($) {
// dom ready
$(function() {
});
});
}
);

View File

@ -38,7 +38,7 @@ function exportMixpanel(mp) {
function trackCachedEvents() {
eventsCache.map(function (event) {
if (window.mailpoet_analytics_enabled || event.forced) {
window.mixpanel.track(event.name, event.data)
window.mixpanel.track(event.name, event.data);
}
});
}

View File

@ -245,7 +245,7 @@ define([
</div>
);
}
};
}
return FormFieldDate;
});

View File

@ -4,6 +4,7 @@
* company: Wysija
* framework: prototype 1.7.2
*/
'use strict';
Event.cacheDelegated = {};
@ -24,14 +25,14 @@ Object.extend(document, (function() {
function findWrapper(selector, eventName, handler) {
var c = getWrappersForSelector(selector, eventName);
return c.find(function(wrapper) {
return wrapper.handler === handler
return wrapper.handler === handler;
});
}
function destroyWrapper(selector, eventName, handler) {
var c = getCacheForSelector(selector);
if(!c[eventName]) return false;
var wrapper = findWrapper(selector, eventName, handler)
var wrapper = findWrapper(selector, eventName, handler);
c[eventName] = c[eventName].without(wrapper);
return wrapper;
}
@ -77,7 +78,7 @@ Object.extend(document, (function() {
}
return document;
}
}
};
})());
var Observable = (function() {
@ -100,7 +101,7 @@ var Observable = (function() {
function getWrapper(handler, klass) {
return function(event) {
return handler.call(new klass(this), event, event.memo);
}
};
}
function onDomLoad(selector, klass) {
@ -125,7 +126,7 @@ var Observable = (function() {
});
delete this.handlers[selector];
}
}
};
})();
// override droppables
@ -755,7 +756,7 @@ WysijaForm.Block = Class.create({
this.element.addClassName('hover');
try {
this.getControls().show();
} catch(e) {;
} catch(e) {
}
}
},

View File

@ -5,7 +5,7 @@ define('handlebars_helpers', ['handlebars'], function(Handlebars) {
output = '';
for(var i = 0; i < size; i++) {
output += arguments[i];
};
}
return output;
});
@ -28,7 +28,7 @@ define('handlebars_helpers', ['handlebars'], function(Handlebars) {
}
} else {
return timestamp;
};
}
});
Handlebars.registerHelper('cycle', function(value, block) {

View File

@ -1,6 +1,7 @@
define('helpTooltip', ['mailpoet', 'react', 'react-dom', 'help-tooltip.jsx'],
function (mp, React, ReactDOM, TooltipComponent) {
'use strict';
var MailPoet = mp;
MailPoet.helpTooltip = {

View File

@ -24,6 +24,6 @@ function KnowledgeBase() {
<a target="_blank" href="http://beta.docs.mailpoet.com/" className="button button-primary">{MailPoet.I18n.t('knowledgeBaseButton')}</a>
</div>
);
};
}
module.exports = KnowledgeBase;

View File

@ -42,6 +42,6 @@ function KnowledgeBase() {
{printData(data)}
</div>
);
};
}
module.exports = KnowledgeBase;

View File

@ -38,7 +38,7 @@ function Tabs(props) {
{ tabLinks }
</h2>
);
};
}
Tabs.propTypes = { tab: React.PropTypes.string };
Tabs.defaultProps = { tab: 'knowledgeBase' };

View File

@ -1,3 +0,0 @@
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:!0, baseUrl:"//mailpoet3.helpscoutdocs.com/"}, contact:{enabled:!0, formId:"aa21ca80-a4f5-11e6-91aa-0a5fecc78a4d"}};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||{});
});

View File

@ -5,6 +5,7 @@ define('i18n',
mp
) {
'use strict';
var MailPoet = mp;
var translations = {};

View File

@ -1,5 +1,6 @@
define('iframe', ['mailpoet'], function(mp) {
'use strict';
var MailPoet = mp;
MailPoet.Iframe = {
marginY: 20,

View File

@ -56,7 +56,7 @@ define([
const promise = this.props.onBulkAction(selected_ids, data);
if (promise !== false) {
promise.then(onSuccess);
};
}
}
this.setState({

View File

@ -1,6 +1,7 @@
define('modal', ['mailpoet', 'jquery'],
function(mp, jQuery) {
'use strict';
var MailPoet = mp;
/***************************************************************************
MailPoet Modal:

View File

@ -1,5 +1,6 @@
define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
'use strict';
var MailPoet = mp;
MailPoet.MP2Migrator = {
@ -11,7 +12,7 @@ define('mp2migrator', ['mailpoet', 'jquery'], function(mp, jQuery) {
clearTimeout(MailPoet.MP2Migrator.displayLogs_timeout);
clearTimeout(MailPoet.MP2Migrator.updateProgressbar_timeout);
clearTimeout(MailPoet.MP2Migrator.update_wordpress_info_timeout);
setTimeout(MailPoet.MP2Migrator.updateDisplay, 1000)
setTimeout(MailPoet.MP2Migrator.updateDisplay, 1000);
},
stopLogger: function () {

View File

@ -50,7 +50,8 @@ define([
if (newLength < that.options.minLength) newLength = that.options.minLength;
that.view.model.set(that.options.modelField, newLength + 'px');
}).on('resizeend', function(event) {
})
.on('resizeend', function(event) {
that.isBeingResized = null;
that.$el.removeClass('mailpoet_resize_active');
});

View File

@ -34,6 +34,8 @@ define([
toolbar1: this.options.toolbar1,
toolbar2: this.options.toolbar2,
browser_spellcheck: true,
valid_elements: this.options.validElements,
invalid_elements: this.options.invalidElements,
block_formats: this.options.blockFormats,

View File

@ -121,7 +121,7 @@ define([
this.showChildView('toolsRegion', this.toolsView);
this.showChildView('icons', new Module.SocialIconCollectionView({
collection: this.model.get('icons')
}))
}));
}
});

View File

@ -23,6 +23,7 @@
}
}(this, function(Marionette, Radio, _) {
'use strict';
var MarionetteApplication = Marionette.Application;
MarionetteApplication.prototype._initChannel = function () {
this.channelName = _.result(this, 'channelName') || 'global';

View File

@ -374,8 +374,6 @@ const _MailerMixin = {
};
export { _QueueMixin as QueueMixin };
export { _StatisticsMixin as StatisticsMixin };
export { _MailerMixin as MailerMixin };

View File

@ -162,7 +162,9 @@ define(
}
}).fail(this._showError);
}
}).fail(this._showError).always(() => {
})
.fail(this._showError)
.always(() => {
this.setState({ loading: false });
});
}
@ -196,7 +198,9 @@ define(
);
}
});
}).fail(this._showError).always(() => {
})
.fail(this._showError)
.always(() => {
this.setState({ loading: false });
});
}

View File

@ -1,5 +1,6 @@
define('notice', ['mailpoet', 'jquery'], function(mp, jQuery) {
'use strict';
/*==================================================================================================
MailPoet Notice:

View File

@ -1,87 +1,87 @@
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() {
// setup form validation
$('form.mailpoet_form').each(function() {
var form = $(this);
form.parsley().on('form:validated', function(parsley) {
// clear messages
form.find('.mailpoet_message > p').hide();
// resize iframe
if(window.frameElement !== null) {
MailPoet.Iframe.autoSize(window.frameElement);
}
});
form.parsley().on('form:submit', function(parsley) {
var form_data = form.serializeObject() || {};
// 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: form_data.token,
api_version: form_data.api_version,
endpoint: 'subscribers',
action: 'subscribe',
data: form_data.data
}).fail(function(response) {
form.find('.mailpoet_validate_error').html(
response.errors.map(function(error) {
return error.message;
}).join('<br />')
).show();
}).done(function(response) {
// successfully subscribed
if (
response.meta !== undefined
&& response.meta.redirect_url !== undefined
) {
// go to page
window.location.href = response.meta.redirect_url;
} else {
// display success message
form.find('.mailpoet_validate_success').show();
}
// reset form
form.trigger('reset');
// reset validation
parsley.reset();
// resize iframe
if (
window.frameElement !== null
&& MailPoet !== undefined
&& MailPoet['Iframe']
) {
MailPoet.Iframe.autoSize(window.frameElement);
}
});
}
return false;
});
});
});
});
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() {
// setup form validation
$('form.mailpoet_form').each(function() {
var form = $(this);
form.parsley().on('form:validated', function(parsley) {
// clear messages
form.find('.mailpoet_message > p').hide();
// resize iframe
if(window.frameElement !== null) {
MailPoet.Iframe.autoSize(window.frameElement);
}
});
form.parsley().on('form:submit', function(parsley) {
var form_data = form.serializeObject() || {};
// 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: form_data.token,
api_version: form_data.api_version,
endpoint: 'subscribers',
action: 'subscribe',
data: form_data.data
}).fail(function(response) {
form.find('.mailpoet_validate_error').html(
response.errors.map(function(error) {
return error.message;
}).join('<br />')
).show();
}).done(function(response) {
// successfully subscribed
if (
response.meta !== undefined
&& response.meta.redirect_url !== undefined
) {
// go to page
window.location.href = response.meta.redirect_url;
} else {
// display success message
form.find('.mailpoet_validate_success').show();
}
// reset form
form.trigger('reset');
// reset validation
parsley.reset();
// resize iframe
if (
window.frameElement !== null
&& MailPoet !== undefined
&& MailPoet['Iframe']
) {
MailPoet.Iframe.autoSize(window.frameElement);
}
});
}
return false;
});
});
});
});
});

View File

@ -169,7 +169,7 @@ define(
setTimeout(function () {
uploadElement.parse({
config: parseCSV(true)
})
});
}, 10);
}
});
@ -433,7 +433,7 @@ define(
MailPoet.Notice.error(errorNotice);
}
}
}
};
}
});
@ -573,14 +573,14 @@ define(
toggleNextStepButton('on');
}
}
})
});
}
jQuery('.mailpoet_create_segment').click(function() {
MailPoet.Modal.popup({
title: MailPoet.I18n.t('addNewList'),
template: jQuery('#new_segment_template').html()
})
});
jQuery('#new_segment_name').keypress(function(e) {
if (e.which == 13) {
jQuery('#new_segment_process').click();
@ -607,7 +607,7 @@ define(
var selected_values = segmentSelectElement.val();
if (selected_values === null) {
selected_values = [response.data.id]
selected_values = [response.data.id];
} else {
selected_values.push(response.data.id);
}
@ -784,7 +784,7 @@ define(
templateSelection: function (item) {
return item.name;
}
})
});
});
jQuery(selectElement).data('column-id', new_column_data.id);
jQuery(selectElement).data('validation-rule', false);
@ -893,11 +893,11 @@ define(
}
else {
for (var format in allowedDateFormats) {
var testedFormat = allowedDateFormats[format]
var testedFormat = allowedDateFormats[format];
if (Moment(firstRowData, testedFormat, true).isValid()) {
var validationRule = (typeof(testedFormat) === 'function') ?
'datetime' :
testedFormat
testedFormat;
// set validation on the column element
jQuery(matchedColumn.element).data('validation-rule', validationRule);
break;
@ -931,7 +931,7 @@ define(
+ '</span> '
);
preventNextStep = true;
};
}
});
if (preventNextStep && !jQuery('.mailpoet_invalidDate').length) {
MailPoet.Notice.error(MailPoet.I18n.t('columnContainsInvalidDate'), {
@ -1038,7 +1038,7 @@ define(
}
});
batchNumber++;
})
});
});
queue.run();

View File

@ -130,6 +130,10 @@ class API {
$endpoint = new $this->_request_endpoint_class();
if(!method_exists($endpoint, $this->_request_method)) {
throw new \Exception(__('Invalid API endpoint method.', 'mailpoet'));
}
// check the accessibility of the requested endpoint's action
// by default, an endpoint's action is considered "private"
if(!$this->validatePermissions($this->_request_method, $endpoint->permissions)) {

View File

@ -19,8 +19,14 @@ class AutomatedLatestContent extends APIEndpoint {
}
function getPostTypes() {
$post_types = array_map(function($post_type) {
return array(
'name' => $post_type->name,
'label' => $post_type->label
);
}, get_post_types(array(), 'objects'));
return $this->successResponse(
get_post_types(array(), 'objects')
$post_types
);
}
@ -82,4 +88,4 @@ class AutomatedLatestContent extends APIEndpoint {
return $this->successResponse($rendered_posts);
}
}
}

View File

@ -267,6 +267,8 @@ class Newsletters extends APIEndpoint {
$newsletter,
$subscriber
);
// strip protocol to avoid mix content error
$preview_url = preg_replace('{^https?:}i', '', $preview_url);
return $this->successResponse(
Newsletter::findOne($newsletter->id)->asArray(),

View File

@ -74,6 +74,8 @@ class Subscribers extends APIEndpoint {
));
}
$data = $this->deobfuscateFormPayload($data);
$segment_ids = (!empty($data['segments'])
? (array)$data['segments']
: array()
@ -81,7 +83,6 @@ class Subscribers extends APIEndpoint {
$segment_ids = $form->filterSegments($segment_ids);
unset($data['segments']);
$data = $this->deobfuscateFormPayload($data);
if(empty($segment_ids)) {
return $this->badRequest(array(

View File

@ -1,11 +1,10 @@
<?php
namespace MailPoet\Config;
use MailPoet\Models\Setting;
class Hooks {
function __construct() {
}
function init() {
$this->setupWPUsers();
$this->setupImageSize();

View File

@ -1,4 +1,5 @@
<?php
namespace MailPoet\Config;
use MailPoet\API;
@ -13,18 +14,16 @@ if(!defined('ABSPATH')) exit;
require_once(ABSPATH . 'wp-admin/includes/plugin.php');
class Initializer {
const UNABLE_TO_CONNECT = 'Unable to connect to the database (the database is unable to open a file or folder), the connection is likely not configured correctly. Please read our [link] Knowledge Base article [/link] for steps how to resolve it.';
const SOLVE_DB_ISSUE_URL = 'http://beta.docs.mailpoet.com/article/200-solving-database-connection-issues';
protected $plugin_initialized = false;
private $access_control;
private $renderer;
const INITIALIZED = 'MAILPOET_INITIALIZED';
function __construct($params = array(
'file' => '',
'version' => '1.0.0'
)) {
Env::init($params['file'], $params['version']);
$this->access_control = new AccessControl();
}
function init() {
@ -40,8 +39,8 @@ class Initializer {
$this->setupDB();
} catch(\Exception $e) {
return WPNotice::displayError(Helpers::replaceLinkTags(
__(self::UNABLE_TO_CONNECT, 'mailpoet'),
self::SOLVE_DB_ISSUE_URL,
__('Unable to connect to the database (the database is unable to open a file or folder), the connection is likely not configured correctly. Please read our [link] Knowledge Base article [/link] for steps how to resolve it.', 'mailpoet'),
'//beta.docs.mailpoet.com/article/200-solving-database-connection-issues',
array('target' => '_blank')
));
}
@ -50,8 +49,8 @@ class Initializer {
register_activation_hook(
Env::$file,
array(
'MailPoet\Config\Activator',
'activate'
$this,
'runActivator'
)
);
@ -60,27 +59,20 @@ class Initializer {
'action'
), 10, 2);
add_action('admin_init', array(
new DeferredAdminNotices,
'printAndClean'
));
add_action('plugins_loaded', array(
$this,
'setup'
));
add_action('init', array(
$this,
'onInit'
));
add_action('widgets_init', array(
$this,
'setupWidget'
));
), 0);
add_action('wp_loaded', array(
$this,
'setupHooks'
));
add_action('admin_init', array(
new DeferredAdminNotices,
'printAndClean'
));
}
function checkRequirements() {
@ -88,47 +80,45 @@ class Initializer {
return $requirements->checkAllRequirements();
}
function runActivator() {
$activator = new Activator();
return $activator->activate();
}
function setupDB() {
$database = new Database();
$database->init();
}
function setup() {
function onInit() {
try {
$this->setupAccessControl();
$this->maybeDbUpdate();
$this->setupRenderer();
$this->setupInstaller();
$this->setupUpdater();
$this->setupRenderer();
$this->setupWidget();
$this->setupLocalizer();
$this->setupMenu();
$this->setupChangelog();
$this->setupShortcodes();
$this->setupImages();
$this->setupChangelog();
$this->setupCronTrigger();
$this->setupConflictResolver();
$this->plugin_initialized = true;
do_action('mailpoet_initialized', MAILPOET_VERSION);
} catch(\Exception $e) {
$this->handleFailedInitialization($e);
}
}
function onInit() {
if(!$this->plugin_initialized) {
define('MAILPOET_INITIALIZED', false);
return;
}
try {
$this->setupJSONAPI();
$this->setupRouter();
$this->setupPages();
do_action('mailpoet_initialized', MAILPOET_VERSION);
} catch(\Exception $e) {
$this->handleFailedInitialization($e);
return $this->handleFailedInitialization($e);
}
define('MAILPOET_INITIALIZED', true);
define(self::INITIALIZED, true);
}
function maybeDbUpdate() {
@ -139,28 +129,12 @@ class Initializer {
if(!$this->access_control->validatePermission(AccessControl::PERMISSION_UPDATE_PLUGIN)) {
throw new \Exception(__('You do not have permission to activate/deactivate MailPoet plugin.', 'mailpoet'));
}
$activator = new Activator();
$activator->activate();
$this->runActivator();
}
}
function setupWidget() {
if(!$this->plugin_initialized) {
return;
}
try {
$widget = new Widget($this->renderer);
$widget->init();
} catch(\Exception $e) {
$this->handleFailedInitialization($e);
}
}
function setupRenderer() {
$caching = !WP_DEBUG;
$debugging = WP_DEBUG;
$this->renderer = new Renderer($caching, $debugging);
function setupAccessControl() {
$this->access_control = new AccessControl();
}
function setupInstaller() {
@ -184,6 +158,17 @@ class Initializer {
$updater->init();
}
function setupRenderer() {
$caching = !WP_DEBUG;
$debugging = WP_DEBUG;
$this->renderer = new Renderer($caching, $debugging);
}
function setupWidget() {
$widget = new Widget($this->renderer);
$widget->init();
}
function setupLocalizer() {
$localizer = new Localizer($this->renderer);
$localizer->init();
@ -194,41 +179,18 @@ class Initializer {
$menu->init();
}
function setupChangelog() {
$changelog = new Changelog();
$changelog->init();
}
function setupPages() {
$pages = new \MailPoet\Settings\Pages();
$pages->init();
}
function setupShortcodes() {
$shortcodes = new Shortcodes();
$shortcodes->init();
}
function setupHooks() {
if(!$this->plugin_initialized) {
return;
}
try {
$hooks = new Hooks();
$hooks->init();
} catch(\Exception $e) {
$this->handleFailedInitialization($e);
}
function setupImages() {
add_image_size('mailpoet_newsletter_max', 1320);
}
function setupJSONAPI() {
API\API::JSON($this->access_control)->init();
}
function setupRouter() {
$router = new Router\Router($this->access_control);
$router->init();
function setupChangelog() {
$changelog = new Changelog();
$changelog->init();
}
function setupCronTrigger() {
@ -239,20 +201,41 @@ class Initializer {
}
}
function setupImages() {
add_image_size('mailpoet_newsletter_max', 1320);
}
function setupConflictResolver() {
$conflict_resolver = new ConflictResolver();
$conflict_resolver->init();
}
function setupJSONAPI() {
$json_api = API\API::JSON($this->access_control);
$json_api->init();
}
function setupRouter() {
$router = new Router\Router($this->access_control);
$router->init();
}
function setupPages() {
$pages = new \MailPoet\Settings\Pages();
$pages->init();
}
function setupHooks() {
if(!defined(self::INITIALIZED)) return;
try {
$hooks = new Hooks();
$hooks->init();
} catch(\Exception $e) {
$this->handleFailedInitialization($e);
}
}
function handleFailedInitialization($exception) {
// Check if we are able to add pages at this point
// check if we are able to add pages at this point
if(function_exists('wp_get_current_user')) {
Menu::addErrorPage($this->access_control);
}
return WPNotice::displayError($exception);
}
}
}

View File

@ -59,6 +59,11 @@ class Menu {
if(self::isOnMailPoetAdminPage()) {
do_action('mailpoet_conflict_resolver_styles');
do_action('mailpoet_conflict_resolver_scripts');
if($_REQUEST['page'] === 'mailpoet-newsletter-editor') {
// Disable WP emojis to not interfere with the newsletter editor emoji handling
$this->disableWPEmojis();
}
}
// Main page
@ -314,6 +319,11 @@ class Menu {
);
}
function disableWPEmojis() {
remove_action('admin_print_scripts', 'print_emoji_detection_script');
remove_action('admin_print_styles', 'print_emoji_styles');
}
function welcome() {
if((bool)(defined('DOING_AJAX') && DOING_AJAX)) return;
@ -541,7 +551,8 @@ class Menu {
'shortcodes' => ShortcodesHelper::getShortcodes(),
'settings' => Setting::getAll(),
'current_wp_user' => Subscriber::getCurrentWPUser(),
'sub_menu' => self::MAIN_PAGE_SLUG
'sub_menu' => self::MAIN_PAGE_SLUG,
'mss_active' => Bridge::isMPSendingServiceEnabled()
);
wp_enqueue_media();
wp_enqueue_script('tinymce-wplink', includes_url('js/tinymce/plugins/wplink/plugin.js'));

View File

@ -12,7 +12,7 @@ class PluginActivatedHook {
}
public function action($plugin, $network_wide) {
if($network_wide) {
if($plugin === plugin_basename(Env::$file) && $network_wide) {
$this->deferred_admin_notices->addNetworkAdminNotice(__('We noticed that you\'re using an unsupported environment. While MailPoet might work within a MultiSite environment, we dont support it.', 'mailpoet'));
}
}

View File

@ -4,6 +4,7 @@ use Carbon\Carbon;
use MailPoet\Newsletter\Renderer\Renderer;
use MailPoet\Util\Helpers;
use MailPoet\Util\Security;
use MailPoet\WP\Emoji;
if(!defined('ABSPATH')) exit;
@ -79,11 +80,15 @@ class Newsletter extends Model {
$this->set_expr('deleted_at', 'NULL');
}
$this->set('body',
is_array($this->body)
? json_encode($this->body)
: $this->body
);
if(isset($this->body)) {
if(is_array($this->body)) {
$this->body = json_encode($this->body);
}
$this->set(
'body',
Emoji::encodeForUTF8Column(self::$_table, 'body', $this->body)
);
}
$this->set('hash',
($this->hash)

View File

@ -1,6 +1,8 @@
<?php
namespace MailPoet\Models;
use MailPoet\WP\Emoji;
if(!defined('ABSPATH')) exit;
class SendingQueue extends Model {
@ -55,7 +57,10 @@ class SendingQueue extends Model {
$this->set('subscribers', serialize($this->subscribers));
}
if(!is_serialized($this->newsletter_rendered_body) && !is_null($this->newsletter_rendered_body)) {
$this->set('newsletter_rendered_body', serialize($this->newsletter_rendered_body));
$this->set(
'newsletter_rendered_body',
serialize($this->encodeEmojisInBody($this->newsletter_rendered_body))
);
}
// set the default priority to medium
if(!$this->priority) {
@ -81,12 +86,34 @@ class SendingQueue extends Model {
function getNewsletterRenderedBody($type = false) {
$rendered_newsletter = (!is_serialized($this->newsletter_rendered_body)) ?
$this->newsletter_rendered_body :
unserialize($this->newsletter_rendered_body);
$this->decodeEmojisInBody(unserialize($this->newsletter_rendered_body));
return ($type && !empty($rendered_newsletter[$type])) ?
$rendered_newsletter[$type] :
$rendered_newsletter;
}
function encodeEmojisInBody($newsletter_rendered_body) {
if(is_array($newsletter_rendered_body)) {
foreach($newsletter_rendered_body as $key => $value) {
$newsletter_rendered_body[$key] = Emoji::encodeForUTF8Column(
self::$_table,
'newsletter_rendered_body',
$value
);
}
}
return $newsletter_rendered_body;
}
function decodeEmojisInBody($newsletter_rendered_body) {
if(is_array($newsletter_rendered_body)) {
foreach($newsletter_rendered_body as $key => $value) {
$newsletter_rendered_body[$key] = Emoji::decodeEntities($value);
}
}
return $newsletter_rendered_body;
}
function isSubscriberProcessed($subscriber_id) {
$subscribers = $this->getSubscribers();
return in_array($subscriber_id, $subscribers['processed']);

View File

@ -149,7 +149,11 @@ class Subscriber extends Model {
static function generateToken($email = null) {
if($email !== null) {
return substr(md5(AUTH_KEY . $email), 0, self::SUBSCRIBER_TOKEN_LENGTH);
$auth_key = '';
if(defined('AUTH_KEY')) {
$auth_key = AUTH_KEY;
}
return substr(md5($auth_key . $email), 0, self::SUBSCRIBER_TOKEN_LENGTH);
}
return false;
}
@ -871,7 +875,8 @@ class Subscriber extends Model {
static function setRequiredFieldsDefaultValues($data) {
$required_field_default_values = array(
'first_name' => '',
'last_name' => ''
'last_name' => '',
'status' => (!Setting::getValue('signup_confirmation.enabled')) ? self::STATUS_SUBSCRIBED : self::STATUS_UNCONFIRMED
);
foreach($required_field_default_values as $field => $value) {
if(!isset($data[$field])) {

View File

@ -1,5 +1,6 @@
<?php
namespace MailPoet\Twig;
use MailPoet\Config\ServicesChecker;
if(!defined('ABSPATH')) exit;
@ -66,6 +67,11 @@ class Functions extends \Twig_Extension {
array($this, 'getMailPoetPremiumVersion'),
array('is_safe' => array('all'))
),
new \Twig_SimpleFunction(
'mailpoet_has_valid_premium_key',
array($this, 'hasValidPremiumKey'),
array('is_safe' => array('all'))
),
new \Twig_SimpleFunction(
'wp_time_format',
array($this, 'getWPTimeFormat'),
@ -151,4 +157,9 @@ class Functions extends \Twig_Extension {
}
return null;
}
}
function hasValidPremiumKey() {
$checker = new ServicesChecker();
return $checker->isPremiumKeyValid(false);
}
}

View File

@ -22,8 +22,12 @@ class Security {
static function generateHash($length = false) {
$length = ($length) ? $length : self::HASH_LENGTH;
$auth_key = '';
if(defined('AUTH_KEY')) {
$auth_key = AUTH_KEY;
}
return substr(
md5(AUTH_KEY . self::generateRandomString(64)),
md5($auth_key . self::generateRandomString(64)),
0,
$length
);

32
lib/WP/Emoji.php Normal file
View File

@ -0,0 +1,32 @@
<?php
namespace MailPoet\WP;
class Emoji {
static function encodeForUTF8Column($table, $field, $value) {
global $wpdb;
$charset = $wpdb->get_col_charset($table, $field);
if($charset === 'utf8') {
$value = wp_encode_emoji($value);
}
return $value;
}
static function decodeEntities($content) {
// Based on wp_staticize_emoji()
// Loosely match the Emoji Unicode range.
$regex = '/(&#x[2-3][0-9a-f]{3};|&#x1f[1-6][0-9a-f]{2};)/';
$matches = array();
if(preg_match_all($regex, $content, $matches)) {
if(!empty($matches[1])) {
foreach($matches[1] as $emoji) {
$entity = html_entity_decode($emoji, ENT_COMPAT, 'UTF-8');
$content = str_replace($emoji, $entity, $content);
}
}
}
return $content;
}
}

View File

@ -4,7 +4,7 @@ if(!defined('ABSPATH')) exit;
/*
* Plugin Name: MailPoet 3 (new)
* Version: 3.0.0-rc.2.0.0
* Version: 3.0.0-rc.2.0.2
* Plugin URI: http://www.mailpoet.com
* Description: Create and send newsletters, post notifications and welcome emails from your WordPress.
* Author: MailPoet
@ -21,7 +21,7 @@ if(!defined('ABSPATH')) exit;
*/
$mailpoet_plugin = array(
'version' => '3.0.0-rc.2.0.0',
'version' => '3.0.0-rc.2.0.2',
'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-rc.2.0.0
Stable tag: 3.0.0-rc.2.0.2
Create and send beautiful emails and newsletters from WordPress.
== Description ==
@ -94,6 +94,18 @@ Our [support site](https://beta.docs.mailpoet.com) has plenty of articles. You c
== Changelog ==
= 3.0.0-rc.2.0.2 - 2017-09-05 =
* Added: browser spellchecker in newsletter editor;
* Improved: newsletter editor uses an optimized data object to display post types. Thanks Facundo;
* Fixed: when signup confirmation is disabled, subscribers added via API are automatically confirmed;
* Fixed: browser preview from within newsletter editor works on HTTP sites loaded over HTTPS. Thanks @dave2084!
= 3.0.0-rc.2.0.1 - 2017-08-30 =
* Fixed: newsletters with emojis are properly saved and sent on certain hosts. Thanks Alison, Scott and Swann!
* Fixed: plugin activates on multisite environments;
* Fixed: subscription forms with list selection field are working now;
* Fixed: newsletter editor does not require "unsubscribe" link when a third-party sending method is used;
= 3.0.0-rc.2.0.0 - 2017-08-29 =
* Improved: MailPoet updates on high traffic sites now use less resources;
* Improved: newsletter is saved when "next" button is pressed in newsletter editor;
@ -101,7 +113,7 @@ Our [support site](https://beta.docs.mailpoet.com) has plenty of articles. You c
* Improved: we collect more informative data from those who share their data with us. You should too!
* Fixed: subscription management form works again;
* Fixed: MailPoet 3 no longer processes the "wysija_form" shortcode used by the old MailPoet 2 to allow both plugins to display forms. Please use the newer "mailpoet_form" shortcode instead. Thx Lynn!
* Fixed: reactivated post notifications will be sent on next scheduled time. Thx Luc!
* Fixed: reactivated post notifications will be sent on next scheduled time. Thx Luc!
* Fixed: updating subscription information of WP users no longer erases their first/last name;
* Fixed: automated latest content in welcome emails always displays the latest posts. Kudos Ehi!

View File

@ -91,7 +91,7 @@ global.stubImage = function(defaultWidth, defaultHeight) {
}
});
};
}
};
testHelpers.loadTemplate('blocks/base/toolsGeneric.hbs', window, {id: 'newsletter_editor_template_tools_generic'});

View File

@ -292,12 +292,12 @@ define([
});
beforeEach(function () {
onStub = sinon.stub()
global.stubChannel(EditorApplication, {on: onStub})
onStub = sinon.stub();
global.stubChannel(EditorApplication, {on: onStub});
global.stubConfig(EditorApplication);
EditorApplication.getBlockTypeModel = sinon.stub().returns(Backbone.Model);
EditorApplication.getBlockTypeView = sinon.stub().returns(Backbone.View);
model = {set: sinon.stub()}
model = {set: sinon.stub()};
view = new (module.AutomatedLatestContentBlockView)({model: model});
});

View File

@ -265,9 +265,9 @@ define([
beforeEach(function () {
onStub = sinon.stub()
global.stubChannel(EditorApplication, {on: onStub})
model = {set: sinon.stub(), toJSON: sinon.stub()}
onStub = sinon.stub();
global.stubChannel(EditorApplication, {on: onStub});
model = {set: sinon.stub(), toJSON: sinon.stub()};
view = new (ButtonBlock.ButtonBlockView)({model: model});
view.render();
});

View File

@ -93,7 +93,11 @@ define([
expect(model.get('blocks')).to.have.length(1);
expect(model.get('blocks').at(0).get('blocks')).to.have.length(2);
expect(model.get('blocks').at(0).get('blocks').at(1).get('someField')).to.equal('some text 2');
expect(
model.get('blocks').at(0)
.get('blocks').at(1)
.get('someField')
).to.equal('some text 2');
});
});
});

View File

@ -1,223 +1,223 @@
define([
'newsletter_editor/App',
'newsletter_editor/blocks/divider'
], function(App, DividerBlock) {
var EditorApplication = App;
describe('Divider', function () {
describe('model', function () {
var model;
beforeEach(function () {
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication, {
blockDefaults: {}
});
global.stubAvailableStyles(EditorApplication);
model = new (DividerBlock.DividerBlockModel)();
});
afterEach(function () {
delete EditorApplication.getChannel;
});
it('has a divider type', function () {
expect(model.get('type')).to.equal('divider');
});
it('has a background color', function () {
expect(model.get('styles.block.backgroundColor')).to.match(/^(#[abcdef0-9]{6})|transparent$/);
});
it('has padding', function () {
expect(model.get('styles.block.padding')).to.match(/^\d+px$/);
});
it('has border style', function () {
expect(model.get('styles.block.borderStyle')).to.match(/^(none|dotted|dashed|solid|double|groove|ridge|inset|outset)$/);
});
it('has border width', function () {
expect(model.get('styles.block.borderWidth')).to.match(/^\d+px$/);
});
it('has border color', function () {
expect(model.get('styles.block.borderColor')).to.match(/^(#[abcdef0-9]{6})|transparent$/);
});
it('changes attributes with set', function () {
var newValue = 'outset';
model.set('styles.block.borderStyle', newValue);
expect(model.get('styles.block.borderStyle')).to.equal(newValue);
});
it('triggers autosave if any attribute changes', function () {
var mock = sinon.mock().exactly(5).withArgs('autoSave');
EditorApplication.getChannel = sinon.stub().returns({
trigger: mock
});
model.set('styles.block.backgroundColor', '#000000');
model.set('styles.block.padding', '19px');
model.set('styles.block.borderStyle', 'double');
model.set('styles.block.borderWidth', '17px');
model.set('styles.block.borderColor', '#123456');
mock.verify();
});
it('uses defaults from config when they are set', function () {
global.stubConfig(EditorApplication, {
blockDefaults: {
divider: {
styles: {
block: {
backgroundColor: '#123456',
padding: '37px',
borderStyle: 'inset',
borderWidth: '7px',
borderColor: '#345678'
}
}
}
}
});
var model = new (DividerBlock.DividerBlockModel)();
expect(model.get('styles.block.backgroundColor')).to.equal('#123456');
expect(model.get('styles.block.padding')).to.equal('37px');
expect(model.get('styles.block.borderStyle')).to.equal('inset');
expect(model.get('styles.block.borderWidth')).to.equal('7px');
expect(model.get('styles.block.borderColor')).to.equal('#345678');
});
});
describe('block view', function () {
var model;
var view;
beforeEach(function () {
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication);
model = new (DividerBlock.DividerBlockModel)();
view = new (DividerBlock.DividerBlockView)({model: model});
});
it('renders', function () {
expect(view.render).to.not.throw();
expect(view.$('.mailpoet_divider')).to.have.length(1);
});
it('rerenders if model attributes change', function () {
view.render();
model.set('styles.block.borderStyle', 'inset');
expect(view.$('.mailpoet_divider').css('border-top-style')).to.equal('inset');
});
it('opens settings if clicked', function () {
var mock = sinon.mock().once();
model.on('startEditing', mock);
view.render();
view.$('.mailpoet_divider').click();
mock.verify();
});
it('does not open settings if clicked on the resize handle', function () {
var mock = sinon.mock().never();
model.on('startEditing', mock);
view.render();
view.$('.mailpoet_resize_handle').click();
mock.verify();
});
});
describe('settings view', function () {
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication);
global.stubAvailableStyles(EditorApplication, {
dividers: ['solid', 'inset']
});
var model = new (DividerBlock.DividerBlockModel)(),
view = new (DividerBlock.DividerBlockSettingsView)({model: model});
it('renders', function () {
expect(view.render).to.not.throw();
expect(view.$('.mailpoet_divider_selector')).to.have.length(1);
});
describe('once rendered', function () {
var model, view;
before(function() {
global.stubChannel(EditorApplication);
global.stubAvailableStyles(EditorApplication, {
dividers: ['solid', 'inset']
});
});
beforeEach(function () {
model = new (DividerBlock.DividerBlockModel)();
view = new (DividerBlock.DividerBlockSettingsView)({model: model});
view.render();
});
it('updates the model when divider style changes', function () {
view.$('.mailpoet_field_divider_style').last().click();
expect(model.get('styles.block.borderStyle')).to.equal('inset');
});
it('updates the model when divider width slider changes', function () {
view.$('.mailpoet_field_divider_border_width').val('17').change();
expect(model.get('styles.block.borderWidth')).to.equal('17px');
});
it('updates the range slider when divider width input changes', function () {
view.$('.mailpoet_field_divider_border_width_input').val('19').trigger('input');
expect(view.$('.mailpoet_field_divider_border_width').val()).to.equal('19');
});
it('updates the input when divider width range slider changes', function () {
view.$('.mailpoet_field_divider_border_width').val('19').change();
expect(view.$('.mailpoet_field_divider_border_width_input').val()).to.equal('19');
});
it('updates the model when divider color changes', function () {
view.$('.mailpoet_field_divider_border_color').val('#123457').change();
expect(model.get('styles.block.borderColor')).to.equal('#123457');
});
it('updates the model when divider background color changes', function () {
view.$('.mailpoet_field_divider_background_color').val('#cccccc').change();
expect(model.get('styles.block.backgroundColor')).to.equal('#cccccc');
});
it ('changes color of available divider styles when actual divider color changes', function() {
var newColor = '#889912';
view.$('.mailpoet_field_divider_border_color').val(newColor).change();
expect(view.$('.mailpoet_field_divider_style div')).to.have.$css('border-top-color', newColor);
});
it('does not display "Apply to all" option when `hideApplyToAll` option is active', function() {
view = new (DividerBlock.DividerBlockSettingsView)({
model: model,
renderOptions: {
hideApplyToAll: true
}
});
view.render();
expect(view.$('.mailpoet_button_divider_apply_to_all').length).to.equal(0);
});
it.skip('closes the sidepanel after "Done" is clicked', function () {
var mock = sinon.mock().once();
global.MailPoet.Modal.cancel = mock;
view.$('.mailpoet_done_editing').click();
mock.verify();
delete(global.MailPoet.Modal.cancel);
});
});
});
});
});
define([
'newsletter_editor/App',
'newsletter_editor/blocks/divider'
], function(App, DividerBlock) {
var EditorApplication = App;
describe('Divider', function () {
describe('model', function () {
var model;
beforeEach(function () {
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication, {
blockDefaults: {}
});
global.stubAvailableStyles(EditorApplication);
model = new (DividerBlock.DividerBlockModel)();
});
afterEach(function () {
delete EditorApplication.getChannel;
});
it('has a divider type', function () {
expect(model.get('type')).to.equal('divider');
});
it('has a background color', function () {
expect(model.get('styles.block.backgroundColor')).to.match(/^(#[abcdef0-9]{6})|transparent$/);
});
it('has padding', function () {
expect(model.get('styles.block.padding')).to.match(/^\d+px$/);
});
it('has border style', function () {
expect(model.get('styles.block.borderStyle')).to.match(/^(none|dotted|dashed|solid|double|groove|ridge|inset|outset)$/);
});
it('has border width', function () {
expect(model.get('styles.block.borderWidth')).to.match(/^\d+px$/);
});
it('has border color', function () {
expect(model.get('styles.block.borderColor')).to.match(/^(#[abcdef0-9]{6})|transparent$/);
});
it('changes attributes with set', function () {
var newValue = 'outset';
model.set('styles.block.borderStyle', newValue);
expect(model.get('styles.block.borderStyle')).to.equal(newValue);
});
it('triggers autosave if any attribute changes', function () {
var mock = sinon.mock().exactly(5).withArgs('autoSave');
EditorApplication.getChannel = sinon.stub().returns({
trigger: mock
});
model.set('styles.block.backgroundColor', '#000000');
model.set('styles.block.padding', '19px');
model.set('styles.block.borderStyle', 'double');
model.set('styles.block.borderWidth', '17px');
model.set('styles.block.borderColor', '#123456');
mock.verify();
});
it('uses defaults from config when they are set', function () {
global.stubConfig(EditorApplication, {
blockDefaults: {
divider: {
styles: {
block: {
backgroundColor: '#123456',
padding: '37px',
borderStyle: 'inset',
borderWidth: '7px',
borderColor: '#345678'
}
}
}
}
});
var model = new (DividerBlock.DividerBlockModel)();
expect(model.get('styles.block.backgroundColor')).to.equal('#123456');
expect(model.get('styles.block.padding')).to.equal('37px');
expect(model.get('styles.block.borderStyle')).to.equal('inset');
expect(model.get('styles.block.borderWidth')).to.equal('7px');
expect(model.get('styles.block.borderColor')).to.equal('#345678');
});
});
describe('block view', function () {
var model;
var view;
beforeEach(function () {
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication);
model = new (DividerBlock.DividerBlockModel)();
view = new (DividerBlock.DividerBlockView)({model: model});
});
it('renders', function () {
expect(view.render).to.not.throw();
expect(view.$('.mailpoet_divider')).to.have.length(1);
});
it('rerenders if model attributes change', function () {
view.render();
model.set('styles.block.borderStyle', 'inset');
expect(view.$('.mailpoet_divider').css('border-top-style')).to.equal('inset');
});
it('opens settings if clicked', function () {
var mock = sinon.mock().once();
model.on('startEditing', mock);
view.render();
view.$('.mailpoet_divider').click();
mock.verify();
});
it('does not open settings if clicked on the resize handle', function () {
var mock = sinon.mock().never();
model.on('startEditing', mock);
view.render();
view.$('.mailpoet_resize_handle').click();
mock.verify();
});
});
describe('settings view', function () {
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication);
global.stubAvailableStyles(EditorApplication, {
dividers: ['solid', 'inset']
});
var model = new (DividerBlock.DividerBlockModel)(),
view = new (DividerBlock.DividerBlockSettingsView)({model: model});
it('renders', function () {
expect(view.render).to.not.throw();
expect(view.$('.mailpoet_divider_selector')).to.have.length(1);
});
describe('once rendered', function () {
var model, view;
before(function() {
global.stubChannel(EditorApplication);
global.stubAvailableStyles(EditorApplication, {
dividers: ['solid', 'inset']
});
});
beforeEach(function () {
model = new (DividerBlock.DividerBlockModel)();
view = new (DividerBlock.DividerBlockSettingsView)({model: model});
view.render();
});
it('updates the model when divider style changes', function () {
view.$('.mailpoet_field_divider_style').last().click();
expect(model.get('styles.block.borderStyle')).to.equal('inset');
});
it('updates the model when divider width slider changes', function () {
view.$('.mailpoet_field_divider_border_width').val('17').change();
expect(model.get('styles.block.borderWidth')).to.equal('17px');
});
it('updates the range slider when divider width input changes', function () {
view.$('.mailpoet_field_divider_border_width_input').val('19').trigger('input');
expect(view.$('.mailpoet_field_divider_border_width').val()).to.equal('19');
});
it('updates the input when divider width range slider changes', function () {
view.$('.mailpoet_field_divider_border_width').val('19').change();
expect(view.$('.mailpoet_field_divider_border_width_input').val()).to.equal('19');
});
it('updates the model when divider color changes', function () {
view.$('.mailpoet_field_divider_border_color').val('#123457').change();
expect(model.get('styles.block.borderColor')).to.equal('#123457');
});
it('updates the model when divider background color changes', function () {
view.$('.mailpoet_field_divider_background_color').val('#cccccc').change();
expect(model.get('styles.block.backgroundColor')).to.equal('#cccccc');
});
it ('changes color of available divider styles when actual divider color changes', function() {
var newColor = '#889912';
view.$('.mailpoet_field_divider_border_color').val(newColor).change();
expect(view.$('.mailpoet_field_divider_style div')).to.have.$css('border-top-color', newColor);
});
it('does not display "Apply to all" option when `hideApplyToAll` option is active', function() {
view = new (DividerBlock.DividerBlockSettingsView)({
model: model,
renderOptions: {
hideApplyToAll: true
}
});
view.render();
expect(view.$('.mailpoet_button_divider_apply_to_all').length).to.equal(0);
});
it.skip('closes the sidepanel after "Done" is clicked', function () {
var mock = sinon.mock().once();
global.MailPoet.Modal.cancel = mock;
view.$('.mailpoet_done_editing').click();
mock.verify();
delete(global.MailPoet.Modal.cancel);
});
});
});
});
});

View File

@ -11,7 +11,8 @@ define([
Backbone.Radio = {
Requests: {
request: function () {
}, reply: function () {
},
reply: function () {
}
}
};

View File

@ -119,7 +119,7 @@ define([
var deferred = jQuery.Deferred();
deferred.resolve({});
return deferred;
}
};
var module;
spy = sinon.spy(post);
module = CommunicationInjector({

View File

@ -54,7 +54,7 @@ define([
expect(json.subject).to.equal('some subject');
expect(json.preheader).to.equal('some preheader');
expect(json).to.not.include.keys('segments', 'modified_at', 'someField');
})
});
});
});

View File

@ -221,6 +221,26 @@ class APITest extends \MailPoetTest {
expect($api->validatePermissions('test', $permissions))->true();
}
function testItThrowsExceptionWhenInvalidEndpointMethodIsCalled() {
$this->api = API::JSON(new AccessControl());
$namespace = array(
'name' => 'MailPoet\API\JSON\v2',
'version' => 'v2'
);
$this->api->addEndpointNamespace($namespace['name'], $namespace['version']);
$data = array(
'endpoint' => 'a_p_i_test_namespaced_endpoint_stub_v2',
'api_version' => 'v2',
'method' => 'fakeMethod'
);
$this->api->setRequestData($data);
$response = $this->api->processRoute();
expect($response->status)->equals(Response::STATUS_BAD_REQUEST);
expect($response->errors[0]['message'])->equals('Invalid API endpoint method.');
}
function _after() {
WPHooksHelper::releaseAllHooks();
wp_delete_user($this->wp_user_id);

View File

@ -0,0 +1,18 @@
<?php
namespace MailPoet\Test\API\JSON\v1;
use MailPoet\API\JSON\v1\AutomatedLatestContent;
class AutomatedLatestContentTest extends \MailPoetTest {
function testItGetsPostTypes() {
$router = new AutomatedLatestContent();
$response = $router->getPostTypes();
expect($response->data)->notEmpty();
foreach($response->data as $post_type) {
expect($post_type)->count(2);
expect($post_type['name'])->notEmpty();
expect($post_type['label'])->notEmpty();
}
}
}

View File

@ -1,12 +1,13 @@
<?php
namespace MailPoet\Test\API\JSON\v1;
use Carbon\Carbon;
use Codeception\Util\Fixtures;
use Codeception\Util\Stub;
use Helper\WordPressHooks as WPHooksHelper;
use MailPoet\API\JSON\v1\Newsletters;
use MailPoet\API\JSON\Response as APIResponse;
use MailPoet\API\JSON\v1\Newsletters;
use MailPoet\Models\Newsletter;
use MailPoet\Models\NewsletterOption;
use MailPoet\Models\NewsletterOptionField;
@ -747,6 +748,17 @@ class NewslettersTest extends \MailPoetTest {
expect($response->errors[0]['message'])->equals('The email could not be sent: failed');
}
function testItReturnsBrowserPreviewUrlWithoutProtocol() {
$data = array(
'id' => $this->newsletter->id,
'body' => 'fake body'
);
$router = new Newsletters();
$response = $router->showPreview($data);
expect($response->meta['preview_url'])->notContains('http');
expect($response->meta['preview_url'])->regExp('!^\/\/!');
}
function testItGeneratesPreviewLinksWithNewsletterHashAndNoSubscriberData() {
$router = new Newsletters();
$response = $router->listing();
@ -767,4 +779,4 @@ class NewslettersTest extends \MailPoetTest {
\ORM::raw_execute('TRUNCATE ' . Segment::$_table);
\ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table);
}
}
}

View File

@ -14,6 +14,7 @@ class SubscribersTest extends \MailPoetTest {
function _before() {
$obfuscator = new FieldNameObfuscator();
$this->obfuscatedEmail = $obfuscator->obfuscate('email');
$this->obfuscatedSegments = $obfuscator->obfuscate('segments');
$this->segment_1 = Segment::createOrUpdate(array('name' => 'Segment 1'));
$this->segment_2 = Segment::createOrUpdate(array('name' => 'Segment 2'));
@ -433,7 +434,7 @@ class SubscribersTest extends \MailPoetTest {
$response = $router->subscribe(array(
$this->obfuscatedEmail => 'toto@mailpoet.com',
'form_id' => $this->form->id,
'segments' => array($this->segment_1->id, $this->segment_2->id)
$this->obfuscatedSegments => array($this->segment_1->id, $this->segment_2->id)
));
expect($response->status)->equals(APIResponse::STATUS_OK);
}
@ -470,7 +471,7 @@ class SubscribersTest extends \MailPoetTest {
$response = $router->subscribe(array(
$this->obfuscatedEmail => 'toto@mailpoet.com',
'form_id' => $this->form->id,
'segments' => array($this->segment_1->id, $this->segment_2->id)
$this->obfuscatedSegments => array($this->segment_1->id, $this->segment_2->id)
));
expect($response->status)->equals(APIResponse::STATUS_BAD_REQUEST);
@ -482,7 +483,7 @@ class SubscribersTest extends \MailPoetTest {
$response = $router->subscribe(array(
$this->obfuscatedEmail => 'toto@mailpoet.com',
'form_id' => $this->form->id,
'segments' => array($this->segment_1->id, $this->segment_2->id),
$this->obfuscatedSegments => array($this->segment_1->id, $this->segment_2->id),
// exists in table and in the form
'first_name' => 'aaa',
// exists in table, but not in the form
@ -503,14 +504,14 @@ class SubscribersTest extends \MailPoetTest {
$response = $router->subscribe(array(
$this->obfuscatedEmail => 'toto@mailpoet.com',
'form_id' => $this->form->id,
'segments' => array($this->segment_1->id, $this->segment_2->id)
$this->obfuscatedSegments => array($this->segment_1->id, $this->segment_2->id)
));
try {
$response = $router->subscribe(array(
$this->obfuscatedEmail => 'tata@mailpoet.com',
'form_id' => $this->form->id,
'segments' => array($this->segment_1->id, $this->segment_2->id)
$this->obfuscatedSegments => array($this->segment_1->id, $this->segment_2->id)
));
$this->fail('It should not be possible to subscribe a second time so soon');
} catch(\Exception $e) {

View File

@ -5,6 +5,10 @@ use MailPoet\Config\Env;
class EnvTest extends \MailPoetTest {
function _before() {
// Back up original environment values
$this->file = Env::$file;
$this->version = Env::$version;
Env::init('file', '1.0.0');
}
@ -80,4 +84,9 @@ class EnvTest extends \MailPoetTest {
expect(Env::getDbTimezoneOffset('+11'))->equals("+11:00");
expect(Env::getDbTimezoneOffset('-5.5'))->equals("-05:30");
}
function _after() {
// Restore the original environment
Env::init($this->file, $this->version);
}
}

View File

@ -17,10 +17,10 @@ class PluginActivatedHookTest extends \MailPoetTest {
$this
);
$hook = new PluginActivatedHook($deferred_admin_notices);
$hook->action("mailpoet", true);
$hook->action("mailpoet/mailpoet.php", true);
}
public function testItDoesntAddsAMessageIfNoNetworkActivation() {
public function testItDoesntAddAMessageIfPluginNameDiffers() {
$deferred_admin_notices = Stub::makeEmpty(
'MailPoet\Config\DeferredAdminNotices',
array(
@ -29,7 +29,19 @@ class PluginActivatedHookTest extends \MailPoetTest {
$this
);
$hook = new PluginActivatedHook($deferred_admin_notices);
$hook->action("mailpoet", false);
$hook->action("some/plugin.php", true);
}
public function testItDoesntAddAMessageIfNoNetworkActivation() {
$deferred_admin_notices = Stub::makeEmpty(
'MailPoet\Config\DeferredAdminNotices',
array(
'addNetworkAdminNotice' => Stub::never(),
),
$this
);
$hook = new PluginActivatedHook($deferred_admin_notices);
$hook->action("mailpoet/mailpoet.php", false);
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace MailPoet\Test\Models;
use AspectMock\Test as Mock;
use MailPoet\Models\SendingQueue;
class SendingQueueTest extends \MailPoetTest {
function _before() {
$this->queue = SendingQueue::create();
$this->queue->save();
$this->rendered_body = array(
'html' => 'some html',
'text' => 'some text'
);
}
function testItCanEncodeEmojisInBody() {
$mock = Mock::double('MailPoet\WP\Emoji', [
'encodeForUTF8Column' => function($params) {
return $params;
}
]);
$this->queue->encodeEmojisInBody($this->rendered_body);
$mock->verifyInvokedMultipleTimes('encodeForUTF8Column', 2);
}
function testItCanDecodeEmojisInBody() {
$mock = Mock::double('MailPoet\WP\Emoji', [
'decodeEntities' => function($params) {
return $params;
}
]);
$this->queue->decodeEmojisInBody($this->rendered_body);
$mock->verifyInvokedMultipleTimes('decodeEntities', 2);
}
function _after() {
Mock::clean();
\ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table);
}
}

View File

@ -1020,7 +1020,40 @@ class SubscriberTest extends \MailPoetTest {
expect(Subscriber::setRequiredFieldsDefaultValues(array()))->equals(
array(
'first_name' => '',
'last_name' => ''
'last_name' => '',
'status' => Subscriber::STATUS_UNCONFIRMED
)
);
}
function testItSetsDefaultStatusDependingOnSingupConfirmationOption() {
// when signup confirmation is disabled, status should be 'subscribed'
Setting::setValue('signup_confirmation.enabled', false);
expect(Subscriber::setRequiredFieldsDefaultValues(array()))->equals(
array(
'first_name' => '',
'last_name' => '',
'status' => Subscriber::STATUS_SUBSCRIBED
)
);
Setting::setValue('signup_confirmation.enabled', true);
// when signup confirmation is enabled, status should be 'unconfirmed'
expect(Subscriber::setRequiredFieldsDefaultValues(array()))->equals(
array(
'first_name' => '',
'last_name' => '',
'status' => Subscriber::STATUS_UNCONFIRMED
)
);
// when status is specified, it should not change regardless of signup confirmation option
Setting::setValue('signup_confirmation.enabled', true);
expect(Subscriber::setRequiredFieldsDefaultValues(array('status' => Subscriber::STATUS_SUBSCRIBED)))->equals(
array(
'first_name' => '',
'last_name' => '',
'status' => Subscriber::STATUS_SUBSCRIBED
)
);
}
@ -1034,6 +1067,7 @@ class SubscriberTest extends \MailPoetTest {
expect($result->getErrors())->false();
expect($result->first_name)->isEmpty();
expect($result->last_name)->isEmpty();
expect($result->status)->equals(Subscriber::STATUS_UNCONFIRMED);
}
function testItDoesNotSetDefaultValuesForExistingSubscribers() {

View File

@ -0,0 +1,51 @@
<?php
namespace MailPoet\Test\WP;
use MailPoet\Config\Env;
use MailPoet\WP\Emoji;
class EmojiTest extends \MailPoetTest {
function _before() {
$this->data_encoded = "Emojis: &#x1f603;&#x1f635;&#x1f4aa;, not emojis: &#046;&#0142;";
$this->data_decoded = "Emojis: 😃😵💪, not emojis: &#046;&#0142;";
$this->column = 'dummycol';
}
function testItCanEncodeForUTF8Column() {
$table = Env::$db_prefix . 'dummytable_utf8';
$this->createTable($table, 'utf8');
$result = Emoji::encodeForUTF8Column($table, $this->column, $this->data_decoded);
expect($result)->equals($this->data_encoded);
$this->dropTable($table);
}
function testItDoesNotEncodeForUTF8MB4Column() {
$table = Env::$db_prefix . 'dummytable_utf8mb4';
$this->createTable($table, 'utf8mb4');
$result = Emoji::encodeForUTF8Column($table, $this->column, $this->data_decoded);
expect($result)->equals($this->data_decoded);
$this->dropTable($table);
}
function testItCanDecodeEntities() {
$result = Emoji::decodeEntities($this->data_encoded);
expect($result)->equals($this->data_decoded);
}
private function createTable($table, $charset) {
\ORM::raw_execute(
'CREATE TABLE IF NOT EXISTS ' . $table
. ' (' . $this->column . ' TEXT) '
. 'DEFAULT CHARSET=' . $charset . ';'
);
}
private function dropTable($table) {
\ORM::raw_execute('DROP TABLE IF EXISTS ' . $table);
}
}

View File

@ -75,6 +75,9 @@ jQuery('.toplevel_page_mailpoet-newsletters.menu-top-last')
<%= javascript('lib/analytics.js') %>
<% set helpscout_form_id = (mailpoet_has_valid_premium_key()) ? '6974b88d-8d85-11e7-b5b5-0ec85169275a' : 'dd918048-8d73-11e7-b5b5-0ec85169275a' %>
<script>!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:!0,baseUrl:"//mailpoet3.helpscoutdocs.com/"},contact:{enabled:!0,formId:"<%= helpscout_form_id %>"}};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||{});</script>
<script type="text/javascript">
if(window['HS'] !== undefined) {
// HelpScout Beacon: Configuration

View File

@ -1200,7 +1200,7 @@
height: '768px'
},
validation: {
validateUnsubscribeLinkPresent: true, // TODO: Add validation based on whether Mailpoet MTA is used or not
validateUnsubscribeLinkPresent: <%= mss_active ? 'true' : 'false' %>,
},
urls: {
send: '<%= admin_url('admin.php?page=mailpoet-newsletters#/send/' ~ (params('id') | intval)) %>',

View File

@ -60,8 +60,8 @@
<div class="feature-section one-col mailpoet_centered">
<h2><%= __('Care to Give Your Opinion?') %></h2>
<script type="text/javascript" charset="utf-8" src="//secure.polldaddy.com/p/9801032.js"></script>
<noscript><a href="//polldaddy.com/poll/9801032/">To which gender identity to you identify?</a></noscript>
<script type="text/javascript" charset="utf-8" src="//secure.polldaddy.com/p/9801036.js"></script>
<noscript><a href="//polldaddy.com/poll/9801036/">Should MailPoet include more default templates?</a></noscript>
</div>
<hr>

View File

@ -36,7 +36,6 @@ var baseConfig = {
'wp-js-hooks': 'WP-JS-Hooks/src/event-manager.js',
'blob$': 'blob-tmp/Blob.js',
'papaparse': 'papaparse/papaparse.min.js',
'helpscout': 'helpscout.js',
'html2canvas': 'html2canvas/dist/html2canvas.js',
'asyncqueue': 'vendor/jquery.asyncqueue.js'
},
@ -118,10 +117,6 @@ var baseConfig = {
include: require.resolve('handlebars'),
loader: 'expose-loader?Handlebars',
},
{
include: /helpscout.js$/,
loader: 'exports-loader?window.HS',
},
{
include: /html2canvas.js$/,
loader: 'expose-loader?html2canvas',
@ -177,7 +172,6 @@ var adminConfig = {
'settings/reinstall_from_scratch.js',
'subscribers/importExport/import.js',
'subscribers/importExport/export.js',
'helpscout'
],
form_editor: [
'form_editor/form_editor.js',
@ -388,4 +382,4 @@ module.exports = _.map([adminConfig, publicConfig, migratorConfig, testConfig],
);
}
return _.extend({}, baseConfig, config);
});
});