Compare commits
162 Commits
Author | SHA1 | Date | |
---|---|---|---|
8dfaf9ba32 | |||
89d0da93d3 | |||
a8ccfec5c6 | |||
d8609c9e84 | |||
5461c975d4 | |||
11298bc101 | |||
c42cf2f622 | |||
c9f1d38baa | |||
3d78d6bbe9 | |||
2b4288f301 | |||
09a2dd231a | |||
3fd4ef9985 | |||
05979965ba | |||
e625a7602a | |||
a0c41ad7ab | |||
7163747eb9 | |||
c30e2b6cf3 | |||
e9eae92ba9 | |||
0fd6fa8879 | |||
52ca6eac18 | |||
28776b8558 | |||
588ad3eab7 | |||
d791538086 | |||
62173e7996 | |||
0acdcd1ca7 | |||
f39a2c8dda | |||
b648852ef5 | |||
6e45892118 | |||
cd145c51f7 | |||
2c12d9ee2d | |||
5fe28623f1 | |||
3086b3cfc2 | |||
5fd29872ff | |||
732c8a314f | |||
23c650bfa6 | |||
f5ced785e0 | |||
4dc9004303 | |||
893c6bd72b | |||
2ac32484e1 | |||
b31e8ce5f2 | |||
4270e4c315 | |||
4b13395b0c | |||
2e67029ef5 | |||
257517b9a9 | |||
88ee64e15d | |||
c2030e9a86 | |||
16111a99fb | |||
543b3e5a91 | |||
e33b60065e | |||
3bcfadd2ab | |||
4729583d8d | |||
aa3f457595 | |||
3ad490f840 | |||
eb27ed65ae | |||
d543f62c5b | |||
4e6f7a05de | |||
5d467115ad | |||
6858b266fe | |||
852b1a4c08 | |||
b2324db7b4 | |||
e8c85e2a54 | |||
a4ac74c84a | |||
4e378484ea | |||
c55be70b2c | |||
70898790f5 | |||
bfdb535f5b | |||
996e6b16e7 | |||
0217e21753 | |||
f6f79d42e1 | |||
4bbd26b098 | |||
028179af37 | |||
87283bf838 | |||
c680badaa2 | |||
df4a936e43 | |||
e27946ebcc | |||
13e64c012f | |||
f69302be48 | |||
16edfc16ea | |||
229a9c8102 | |||
490091a7e2 | |||
5f58e5ca82 | |||
16beda530a | |||
f1b373924f | |||
6a73c463cb | |||
0271675cd0 | |||
b7555aa640 | |||
fa9ef6e5bd | |||
1c97b004ca | |||
f0ab42adf1 | |||
165d8358d4 | |||
6bd6e74bcb | |||
b23df9e0a4 | |||
7b12affb77 | |||
8c8c01aa75 | |||
0150e699a2 | |||
e6943e2638 | |||
6f80dcb1de | |||
60ed294302 | |||
e8017b58f5 | |||
c79bf7d337 | |||
ac268c1ec9 | |||
1ef131fa2d | |||
1873007550 | |||
fa2ccb51c9 | |||
8f87d654af | |||
dee6e9fbad | |||
ef90264316 | |||
70bf4be723 | |||
07ef727654 | |||
a346e5be29 | |||
5ce1eadde7 | |||
8a4d5395b1 | |||
b5feed0f46 | |||
11f9579101 | |||
c6000c959a | |||
19e67ea2b0 | |||
0f6619e25d | |||
daf747d3be | |||
7393b1f2cf | |||
efe861a9ba | |||
2c358ab179 | |||
ca157fc91d | |||
1fbe5d7bc6 | |||
71c031ccf9 | |||
6449b7ccca | |||
6a956472fe | |||
d6af88d667 | |||
b9184a202f | |||
f898746967 | |||
68165b7b78 | |||
bb8591a67b | |||
bda71ae78e | |||
abd4f6cac2 | |||
87e6cc2a4f | |||
dde598eb64 | |||
0c4407f43a | |||
b6c864e7a1 | |||
3d9dc6465d | |||
9a6fec094a | |||
61af224d7d | |||
9b41641e97 | |||
4e2e9f6f8f | |||
c29dc8b4c7 | |||
98a3c6b156 | |||
69c540288b | |||
651c9f5692 | |||
9ad3778cf7 | |||
c90e0e9f64 | |||
cb1730c4e2 | |||
3dd8a973fd | |||
c3ea088fca | |||
a11d6d7868 | |||
a596add838 | |||
7e7103ddab | |||
0064970ed7 | |||
9d93f3ea95 | |||
ff2a3cd19e | |||
8419d95ea1 | |||
4ad317ac7b | |||
7cccebbf2c | |||
e4f76ee9eb | |||
26241afb86 |
@ -81,6 +81,7 @@ jobs:
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
php composer.phar install
|
||||
./do install
|
||||
./do compile:all --env production
|
||||
- save_cache:
|
||||
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
|
||||
paths:
|
||||
@ -94,9 +95,9 @@ jobs:
|
||||
command: |
|
||||
docker-compose run codeception --steps --debug -vvv --html --xml
|
||||
- store_artifacts:
|
||||
path: ~/mailpoet/tests/acceptance-tests/_output
|
||||
path: tests/_output
|
||||
- store_test_results:
|
||||
path: ~/mailpoet/tests/acceptance-tests/_output
|
||||
path: tests/_output
|
||||
php7:
|
||||
working_directory: /home/circleci/mailpoet
|
||||
docker:
|
||||
|
@ -37,6 +37,7 @@ function setup {
|
||||
./wp-cli.phar core download --allow-root --path=wordpress
|
||||
# Generate `wp-config.php` file with debugging enabled
|
||||
echo "define(\"WP_DEBUG\", true);" | ./wp-cli.phar core config --allow-root --dbname=wordpress --dbuser=root --dbhost=127.0.0.1 --path=wordpress --extra-php
|
||||
sed -i "s/\$table_prefix = 'wp_';/\$table_prefix = 'mp_';/" ./wordpress/wp-config.php
|
||||
# Install WordPress
|
||||
./wp-cli.phar core install --allow-root --admin_name=admin --admin_password=admin --admin_email=admin@mailpoet.loc --url=http://mailpoet.loc:8080 --title=WordPress --path=wordpress
|
||||
# Softlink plugin to plugin path
|
||||
|
@ -12,7 +12,6 @@
|
||||
"prefer-arrow-callback": 0,
|
||||
"radix": 0,
|
||||
"no-alert": 0,
|
||||
"block-scoped-var": 0,
|
||||
"guard-for-in": 0,
|
||||
"no-prototype-builtins": 0,
|
||||
"no-restricted-syntax": 0,
|
||||
@ -38,20 +37,16 @@
|
||||
"global-require": 0,
|
||||
"no-throw-literal": 0,
|
||||
"no-extra-bind": 0,
|
||||
"one-var-declaration-per-line": 0,
|
||||
"consistent-return": 0,
|
||||
"no-shadow": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"brace-style": 0,
|
||||
"no-else-return": 0,
|
||||
"no-use-before-define": 0,
|
||||
"one-var": 0,
|
||||
"camelcase": 0,
|
||||
"padded-blocks": 0,
|
||||
"strict": 0,
|
||||
"vars-on-top": 0,
|
||||
"no-var": 0,
|
||||
"no-unused-vars": 0,
|
||||
"space-infix-ops": 0,
|
||||
"object-shorthand": 0,
|
||||
"new-parens": 0,
|
||||
"eol-last": 0,
|
||||
|
@ -47,24 +47,14 @@
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"camelcase": 0,
|
||||
"eqeqeq": 0,
|
||||
"no-lonely-if": 0,
|
||||
"block-scoped-var": 0,
|
||||
"no-extra-bind": 0,
|
||||
"class-methods-use-this": 0,
|
||||
"no-case-declarations": 0,
|
||||
"no-else-return": 0,
|
||||
"max-len": 0,
|
||||
"no-useless-concat": 0,
|
||||
"no-sequences": 0,
|
||||
"no-extra-boolean-cast": 0,
|
||||
"space-unary-ops": 0,
|
||||
"dot-notation": 0,
|
||||
"no-shadow": 0,
|
||||
"one-var": 0,
|
||||
"no-alert": 0,
|
||||
"one-var-declaration-per-line": 0,
|
||||
"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
|
||||
}
|
||||
|
@ -8,25 +8,9 @@
|
||||
"ecmaVersion": 6
|
||||
},
|
||||
"rules": {
|
||||
"import/no-amd": 0,
|
||||
"one-var": 0,
|
||||
"no-whitespace-before-property": 0,
|
||||
"global-require": 0,
|
||||
"keyword-spacing": 0,
|
||||
"no-bitwise": 0,
|
||||
"no-spaced-func": 0,
|
||||
"func-call-spacing": 0,
|
||||
"max-len": 0,
|
||||
"space-unary-ops": 0,
|
||||
"no-unused-vars": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"no-shadow": 0,
|
||||
"padded-blocks": 0,
|
||||
"vars-on-top": 0,
|
||||
"space-before-blocks": 0,
|
||||
"object-curly-spacing": 0,
|
||||
"one-var-declaration-per-line": 0,
|
||||
// Exceptions
|
||||
"func-names": 0,
|
||||
"space-before-function-paren": 0
|
||||
// Temporary
|
||||
"no-underscore-dangle": 0
|
||||
}
|
||||
}
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
TODO
|
||||
composer.phar
|
||||
/vendor
|
||||
/vendor_backup
|
||||
tests/_output/*
|
||||
tests/_support/_generated/*
|
||||
node_modules
|
||||
|
25
RoboFile.php
25
RoboFile.php
@ -289,15 +289,16 @@ class RoboFile extends \Robo\Tasks {
|
||||
}
|
||||
|
||||
function svnPublish($opts = ['force' => false]) {
|
||||
$this->loadWPFunctions();
|
||||
$this->loadEnv();
|
||||
|
||||
$svn_dir = ".mp_svn";
|
||||
$plugin_data = get_plugin_data('mailpoet.php', false, false);
|
||||
$plugin_version = $plugin_data['Version'];
|
||||
$plugin_dist_name = sanitize_title_with_dashes($plugin_data['Name']);
|
||||
$plugin_dist_name = explode('-', $plugin_dist_name);
|
||||
$plugin_dist_name = $plugin_dist_name[0];
|
||||
$plugin_version = $this->getPluginVersion('mailpoet.php');
|
||||
$plugin_dist_name = 'mailpoet';
|
||||
$plugin_dist_file = $plugin_dist_name . '.zip';
|
||||
|
||||
if(!$plugin_version) {
|
||||
throw new \Exception('Could not parse plugin version, check the plugin header');
|
||||
}
|
||||
$this->say('Publishing version: ' . $plugin_version);
|
||||
|
||||
// Sanity checks
|
||||
@ -416,13 +417,9 @@ class RoboFile extends \Robo\Tasks {
|
||||
$dotenv->load();
|
||||
}
|
||||
|
||||
protected function loadWPFunctions() {
|
||||
$this->loadEnv();
|
||||
define('ABSPATH', getenv('WP_TEST_PATH') . '/');
|
||||
define('WPINC', 'wp-includes');
|
||||
require_once(ABSPATH . WPINC . '/functions.php');
|
||||
require_once(ABSPATH . WPINC . '/formatting.php');
|
||||
require_once(ABSPATH . WPINC . '/plugin.php');
|
||||
require_once(ABSPATH . 'wp-admin/includes/plugin.php');
|
||||
protected function getPluginVersion($file) {
|
||||
$data = file_get_contents($file);
|
||||
preg_match('/^[ \t*]*Version:(.*)$/mi', $data, $m);
|
||||
return !empty($m[1]) ? trim($m[1]) : false;
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,10 @@ function requestFailed(errorMessage, xhr) {
|
||||
if (xhr.responseJSON) {
|
||||
return xhr.responseJSON;
|
||||
}
|
||||
var message = errorMessage.replace('%d', xhr.status);
|
||||
return {
|
||||
errors: [
|
||||
{
|
||||
message: message
|
||||
message: errorMessage.replace('%d', xhr.status)
|
||||
}
|
||||
]
|
||||
};
|
||||
@ -54,21 +53,23 @@ define('ajax', ['mailpoet', 'jquery', 'underscore'], function (mp, jQuery, _) {
|
||||
};
|
||||
},
|
||||
request: function (method, options) {
|
||||
// set options
|
||||
var params;
|
||||
var deferred;
|
||||
// set options
|
||||
this.init(options);
|
||||
|
||||
// set request params
|
||||
var params = this.getParams();
|
||||
// set request params
|
||||
params = this.getParams();
|
||||
|
||||
// remove null values from the data object
|
||||
// remove null values from the data object
|
||||
if (_.isObject(params.data)) {
|
||||
params.data = _.pick(params.data, function (value) {
|
||||
return (value !== null);
|
||||
});
|
||||
}
|
||||
|
||||
// ajax request
|
||||
var deferred = jQuery.post(
|
||||
// ajax request
|
||||
deferred = jQuery.post(
|
||||
this.options.url,
|
||||
params,
|
||||
null,
|
||||
|
@ -43,9 +43,10 @@ define('date',
|
||||
},
|
||||
format: function (date, opts) {
|
||||
var options = opts || {};
|
||||
var momentDate;
|
||||
this.init(options);
|
||||
|
||||
var momentDate = Moment(date, this.convertFormat(options.parseFormat));
|
||||
momentDate = Moment(date, this.convertFormat(options.parseFormat));
|
||||
if (options.offset === 0) momentDate = momentDate.utc();
|
||||
return momentDate.format(this.convertFormat(this.options.format));
|
||||
},
|
||||
@ -71,6 +72,11 @@ define('date',
|
||||
});
|
||||
},
|
||||
convertFormat: function (format) {
|
||||
var replacements;
|
||||
var convertedFormat;
|
||||
var escapeToken;
|
||||
var index;
|
||||
var token;
|
||||
var format_mappings = {
|
||||
date: {
|
||||
d: 'DD',
|
||||
@ -140,12 +146,11 @@ define('date',
|
||||
|
||||
if (!format || format.length <= 0) return format;
|
||||
|
||||
var replacements = format_mappings['date'];
|
||||
replacements = format_mappings['date'];
|
||||
convertedFormat = [];
|
||||
escapeToken = false;
|
||||
|
||||
var convertedFormat = [];
|
||||
var escapeToken = false;
|
||||
|
||||
for (var index = 0, token = ''; format.charAt(index); index += 1) {
|
||||
for (index = 0, token = ''; format.charAt(index); index += 1) {
|
||||
token = format.charAt(index);
|
||||
if (escapeToken === true) {
|
||||
convertedFormat.push('[' + token + ']');
|
||||
|
@ -77,14 +77,14 @@ define([
|
||||
{ description }
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div key={'field-' + (data.index || 0)}>
|
||||
{ field }
|
||||
{ description }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div key={'field-' + (data.index || 0)}>
|
||||
{ field }
|
||||
{ description }
|
||||
</div>
|
||||
);
|
||||
|
||||
},
|
||||
render: function () {
|
||||
let field = false;
|
||||
|
@ -60,13 +60,11 @@ define([
|
||||
templateResult: function (item) {
|
||||
if (item.element && item.element.selected) {
|
||||
return null;
|
||||
} else {
|
||||
if (item.title) {
|
||||
return item.title;
|
||||
} else {
|
||||
return item.text;
|
||||
}
|
||||
} else if (item.title) {
|
||||
return item.title;
|
||||
}
|
||||
return item.text;
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
@ -156,9 +154,9 @@ define([
|
||||
transformChangedValue: function (value) {
|
||||
if (typeof this.props.field['transformChangedValue'] === 'function') {
|
||||
return this.props.field.transformChangedValue.call(this, value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
|
||||
},
|
||||
render: function () {
|
||||
const options = this.state.items.map((item, index) => {
|
||||
|
@ -44,7 +44,7 @@ define(
|
||||
this.loadItem(this.props.params.id);
|
||||
} else {
|
||||
this.setState({
|
||||
item: jQuery('.mailpoet_form').serializeObject(),
|
||||
item: jQuery('.mailpoet_form').mailpoetSerializeObject(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -142,17 +142,17 @@ define(
|
||||
handleValueChange: function (e) {
|
||||
if (this.props.onChange) {
|
||||
return this.props.onChange(e);
|
||||
} else {
|
||||
const item = this.state.item;
|
||||
const field = e.target.name;
|
||||
|
||||
item[field] = e.target.value;
|
||||
|
||||
this.setState({
|
||||
item: item,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
const item = this.state.item;
|
||||
const field = e.target.name;
|
||||
|
||||
item[field] = e.target.value;
|
||||
|
||||
this.setState({
|
||||
item: item,
|
||||
});
|
||||
return true;
|
||||
|
||||
},
|
||||
render: function () {
|
||||
let errors;
|
||||
|
@ -7,6 +7,10 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var Observable;
|
||||
var WysijaHistory;
|
||||
var WysijaForm;
|
||||
|
||||
Event.cacheDelegated = {};
|
||||
Object.extend(document, (function () {
|
||||
var cache = Event.cacheDelegated;
|
||||
@ -30,18 +34,21 @@ Object.extend(document, (function () {
|
||||
}
|
||||
|
||||
function destroyWrapper(selector, eventName, handler) {
|
||||
var wrapper;
|
||||
var c = getCacheForSelector(selector);
|
||||
if (!c[eventName]) return false;
|
||||
var wrapper = findWrapper(selector, eventName, handler);
|
||||
wrapper = findWrapper(selector, eventName, handler);
|
||||
c[eventName] = c[eventName].without(wrapper);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function createWrapper(selector, eventName, handler, context) {
|
||||
var wrapper, c = getWrappersForSelector(selector, eventName);
|
||||
var wrapper;
|
||||
var element;
|
||||
var c = getWrappersForSelector(selector, eventName);
|
||||
if (c.pluck('handler').include(handler)) return false;
|
||||
wrapper = function (event) {
|
||||
var element = event.findElement(selector);
|
||||
element = event.findElement(selector);
|
||||
if (element) handler.call(context || element, event, element);
|
||||
};
|
||||
wrapper.handler = handler;
|
||||
@ -49,13 +56,14 @@ Object.extend(document, (function () {
|
||||
return wrapper;
|
||||
}
|
||||
return {
|
||||
delegate: function (selector, eventName, handler, context) {
|
||||
delegate: function (selector, eventName) {
|
||||
var wrapper = createWrapper.apply(null, arguments);
|
||||
if (wrapper) document.observe(eventName, wrapper);
|
||||
return document;
|
||||
},
|
||||
stopDelegating: function (selector, eventName, handler) {
|
||||
stopDelegating: function (selector, eventName) {
|
||||
var length = arguments.length;
|
||||
var wrapper;
|
||||
switch (length) {
|
||||
case 2:
|
||||
getWrappersForSelector(selector, eventName).each(function (wrapper) {
|
||||
@ -73,7 +81,7 @@ Object.extend(document, (function () {
|
||||
});
|
||||
break;
|
||||
default:
|
||||
var wrapper = destroyWrapper.apply(null, arguments);
|
||||
wrapper = destroyWrapper.apply(null, arguments);
|
||||
if (wrapper) document.stopObserving(eventName, wrapper);
|
||||
}
|
||||
return document;
|
||||
@ -81,7 +89,7 @@ Object.extend(document, (function () {
|
||||
};
|
||||
})());
|
||||
|
||||
var Observable = (function () {
|
||||
Observable = (function () {
|
||||
function getEventName(nameA, namespace) {
|
||||
var name = nameA.substring(2);
|
||||
if (namespace) name = namespace + ':' + name;
|
||||
@ -89,8 +97,8 @@ var Observable = (function () {
|
||||
}
|
||||
|
||||
function getHandlers(klass) {
|
||||
var proto = klass.prototype,
|
||||
namespace = proto.namespace;
|
||||
var proto = klass.prototype;
|
||||
var namespace = proto.namespace;
|
||||
return Object.keys(proto).grep(/^on/).inject(window.$H(), function (handlers, name) {
|
||||
if (name === 'onDomLoaded') return handlers;
|
||||
handlers.set(getEventName(name, namespace), getWrapper(proto[name], klass));
|
||||
@ -111,9 +119,9 @@ var Observable = (function () {
|
||||
}
|
||||
return {
|
||||
observe: function (selector) {
|
||||
var klass = this;
|
||||
if (!this.handlers) this.handlers = {};
|
||||
if (this.handlers[selector]) return;
|
||||
var klass = this;
|
||||
if (this.prototype.onDomLoaded) {
|
||||
if (document.loaded) {
|
||||
onDomLoad(selector, klass);
|
||||
@ -146,8 +154,9 @@ Object.extend(window.Droppables, {
|
||||
return proceed(drop);
|
||||
}),
|
||||
show: function (point, element) {
|
||||
var drop;
|
||||
var affected = [];
|
||||
if (!this.drops.length) return;
|
||||
var drop, affected = [];
|
||||
this.drops.each(function (drop) {
|
||||
if (window.Droppables.isAffected(point, element, drop)) affected.push(drop);
|
||||
});
|
||||
@ -159,13 +168,13 @@ Object.extend(window.Droppables, {
|
||||
if (drop !== this.last_active) window.Droppables.activate(drop, element);
|
||||
}
|
||||
},
|
||||
displayArea: function (draggable) {
|
||||
displayArea: function () {
|
||||
if (!this.drops.length) return;
|
||||
|
||||
// hide controls when displaying drop areas.
|
||||
WysijaForm.hideBlockControls();
|
||||
|
||||
this.drops.each(function (drop, iterator) {
|
||||
this.drops.each(function (drop) {
|
||||
if (drop.element.hasClassName('block_placeholder')) {
|
||||
drop.element.addClassName('active');
|
||||
}
|
||||
@ -173,7 +182,7 @@ Object.extend(window.Droppables, {
|
||||
},
|
||||
hideArea: function () {
|
||||
if (!this.drops.length) return;
|
||||
this.drops.each(function (drop, iterator) {
|
||||
this.drops.each(function (drop) {
|
||||
if (drop.element.hasClassName('block_placeholder')) {
|
||||
drop.element.removeClassName('active');
|
||||
} else if (drop.element.hasClassName('image_placeholder')) {
|
||||
@ -195,7 +204,7 @@ Object.extend(window.Droppables, {
|
||||
- set a maximum number of items to be stored
|
||||
|
||||
*/
|
||||
var WysijaHistory = {
|
||||
WysijaHistory = {
|
||||
container: 'mailpoet_form_history',
|
||||
size: 30,
|
||||
enqueue: function (element) {
|
||||
@ -239,7 +248,7 @@ var WysijaHistory = {
|
||||
};
|
||||
|
||||
/* MailPoet Form */
|
||||
var WysijaForm = {
|
||||
WysijaForm = {
|
||||
version: '0.7',
|
||||
options: {
|
||||
container: 'mailpoet_form_container',
|
||||
@ -292,6 +301,7 @@ var WysijaForm = {
|
||||
});
|
||||
},
|
||||
load: function (data) {
|
||||
var settings_elements;
|
||||
if (data === undefined) return;
|
||||
|
||||
// load body
|
||||
@ -302,7 +312,7 @@ var WysijaForm = {
|
||||
});
|
||||
|
||||
// load settings
|
||||
var settings_elements = window.$('mailpoet_form_settings').getElements();
|
||||
settings_elements = window.$('mailpoet_form_settings').getElements();
|
||||
settings_elements.each(function (setting) {
|
||||
// skip lists
|
||||
if (setting.name === 'segments') {
|
||||
@ -324,13 +334,13 @@ var WysijaForm = {
|
||||
}
|
||||
},
|
||||
save: function () {
|
||||
var position = 1,
|
||||
data = {
|
||||
name: window.$F('mailpoet_form_name'),
|
||||
settings: window.$('mailpoet_form_settings').serialize(true),
|
||||
body: [],
|
||||
styles: (window.MailPoet.CodeEditor !== undefined) ? window.MailPoet.CodeEditor.getValue() : null
|
||||
};
|
||||
var position = 1;
|
||||
var data = {
|
||||
name: window.$F('mailpoet_form_name'),
|
||||
settings: window.$('mailpoet_form_settings').serialize(true),
|
||||
body: [],
|
||||
styles: (window.MailPoet.CodeEditor !== undefined) ? window.MailPoet.CodeEditor.getValue() : null
|
||||
};
|
||||
// body
|
||||
WysijaForm.getBlocks().each(function (b) {
|
||||
var block_data = (typeof (b.block['save']) === 'function') ? b.block.save() : null;
|
||||
@ -401,6 +411,7 @@ var WysijaForm = {
|
||||
return data;
|
||||
},
|
||||
toggleWidgets: function () {
|
||||
var hasSegmentSelection;
|
||||
window.$$('a[wysija_unique="1"]').invoke('removeClassName', 'disabled');
|
||||
|
||||
// loop through each unique field already inserted in the editor and disable its toolbar equivalent
|
||||
@ -411,7 +422,7 @@ var WysijaForm = {
|
||||
}
|
||||
});
|
||||
|
||||
var hasSegmentSelection = WysijaForm.hasSegmentSelection();
|
||||
hasSegmentSelection = WysijaForm.hasSegmentSelection();
|
||||
|
||||
if (hasSegmentSelection) {
|
||||
window.$('mailpoet_form_segments').writeAttribute('required', false).disable();
|
||||
@ -426,8 +437,9 @@ var WysijaForm = {
|
||||
},
|
||||
isSegmentSelectionValid: function () {
|
||||
var segment_selection = window.$$('#' + WysijaForm.options.editor + ' [wysija_id="segments"]')[0];
|
||||
var block;
|
||||
if (segment_selection !== undefined) {
|
||||
var block = WysijaForm.get(segment_selection).block.getData();
|
||||
block = WysijaForm.get(segment_selection).block.getData();
|
||||
return (
|
||||
(block.params.values !== undefined)
|
||||
&&
|
||||
@ -437,10 +449,12 @@ var WysijaForm = {
|
||||
return false;
|
||||
},
|
||||
setBlockPositions: function (event, target) {
|
||||
var index = 1;
|
||||
var block_placeholder;
|
||||
var previous_placeholder;
|
||||
// release dragging lock
|
||||
WysijaForm.locks.dragging = false;
|
||||
|
||||
var index = 1;
|
||||
WysijaForm.getBlocks().each(function (container) {
|
||||
container.setPosition(index++);
|
||||
// remove z-index value to avoid issues when resizing images
|
||||
@ -453,8 +467,8 @@ var WysijaForm = {
|
||||
|
||||
if (target !== undefined) {
|
||||
// get placeholders (previous placeholder matches the placeholder linked to the next block)
|
||||
var block_placeholder = window.$(target.element.readAttribute('wysija_placeholder')),
|
||||
previous_placeholder = target.element.previous('.block_placeholder');
|
||||
block_placeholder = window.$(target.element.readAttribute('wysija_placeholder'));
|
||||
previous_placeholder = target.element.previous('.block_placeholder');
|
||||
|
||||
if (block_placeholder !== null) {
|
||||
// put block placeholder before the current block
|
||||
@ -479,23 +493,17 @@ var WysijaForm = {
|
||||
},
|
||||
setSettingsPosition: function () {
|
||||
// get viewport offsets and dimensions
|
||||
var viewportHeight = document.viewport.getHeight(),
|
||||
blockPadding = 5;
|
||||
var viewportHeight = document.viewport.getHeight();
|
||||
|
||||
window.$(WysijaForm.options.container).select('.wysija_settings').each(function (element) {
|
||||
// get parent dimensions and position
|
||||
var parentDim = element.up('.mailpoet_form_block').getDimensions(),
|
||||
parentPos = element.up('.mailpoet_form_block').cumulativeOffset(),
|
||||
is_visible = (parentPos.top <= (WysijaForm.scroll.top + viewportHeight)),
|
||||
buttonMargin = 5,
|
||||
relativeTop = buttonMargin;
|
||||
var parentDim = element.up('.mailpoet_form_block').getDimensions();
|
||||
var parentPos = element.up('.mailpoet_form_block').cumulativeOffset();
|
||||
var is_visible = (parentPos.top <= (WysijaForm.scroll.top + viewportHeight));
|
||||
var buttonMargin = 5;
|
||||
var relativeTop = buttonMargin;
|
||||
|
||||
if (is_visible) {
|
||||
// desired position is set to center of viewport
|
||||
var absoluteTop = parseInt(WysijaForm.scroll.top + ((viewportHeight / 2) - (element.getHeight() / 2)), 10),
|
||||
parentTop = parseInt(parentPos.top - blockPadding, 10),
|
||||
parentBottom = parseInt(parentPos.top + parentDim.height - blockPadding, 10);
|
||||
|
||||
// always center
|
||||
relativeTop = parseInt((parentDim.height / 2) - (element.getHeight() / 2), 10);
|
||||
}
|
||||
@ -519,9 +527,10 @@ var WysijaForm = {
|
||||
|
||||
},
|
||||
setToolbarPosition: function () {
|
||||
var position;
|
||||
WysijaForm.initToolbarPosition();
|
||||
|
||||
var position = {
|
||||
position = {
|
||||
top: WysijaForm.toolbar.y + 'px',
|
||||
visibility: 'visible'
|
||||
};
|
||||
@ -585,10 +594,12 @@ var WysijaForm = {
|
||||
instances: {},
|
||||
get: function (element, typ) {
|
||||
var type = typ;
|
||||
var id;
|
||||
var instance;
|
||||
if (type === undefined) type = 'block';
|
||||
// identify element
|
||||
var id = element.identify();
|
||||
var instance = WysijaForm.instances[id] || new WysijaForm[type.capitalize().camelize()](id);
|
||||
id = element.identify();
|
||||
instance = WysijaForm.instances[id] || new WysijaForm[type.capitalize().camelize()](id);
|
||||
|
||||
WysijaForm.instances[id] = instance;
|
||||
return instance;
|
||||
@ -636,8 +647,8 @@ var WysijaForm = {
|
||||
},
|
||||
encodeURIComponent: function (str) {
|
||||
// check if it's a url and if so, prevent encoding of protocol
|
||||
var regexp = new RegExp(/^http[s]?:\/\//),
|
||||
protocol = regexp.exec(str);
|
||||
var regexp = new RegExp(/^http[s]?:\/\//);
|
||||
var protocol = regexp.exec(str);
|
||||
|
||||
if (protocol === null) {
|
||||
// this is not a url so encode the whole thing
|
||||
@ -680,13 +691,13 @@ WysijaForm.DraggableItem = window.Class.create({
|
||||
},
|
||||
STYLES: new window.Template('position: absolute; top: #{top}px; left: #{left}px;'),
|
||||
cloneElement: function () {
|
||||
var clone = this.element.clone(),
|
||||
offset = this.element.cumulativeOffset(),
|
||||
list = this.getList(),
|
||||
styles = this.STYLES.evaluate({
|
||||
top: offset.top - list.scrollTop,
|
||||
left: offset.left - list.scrollLeft
|
||||
});
|
||||
var clone = this.element.clone();
|
||||
var offset = this.element.cumulativeOffset();
|
||||
var list = this.getList();
|
||||
var styles = this.STYLES.evaluate({
|
||||
top: offset.top - list.scrollTop,
|
||||
left: offset.left - list.scrollLeft
|
||||
});
|
||||
clone.setStyle(styles);
|
||||
|
||||
clone.addClassName('mailpoet_form_widget');
|
||||
@ -767,15 +778,17 @@ WysijaForm.Block = window.Class.create({
|
||||
}
|
||||
},
|
||||
makeBlockDroppable: function () {
|
||||
var block_placeholder;
|
||||
if (this.isBlockDroppableEnabled() === false) {
|
||||
var block_placeholder = this.getBlockDroppable();
|
||||
block_placeholder = this.getBlockDroppable();
|
||||
window.Droppables.add(block_placeholder.identify(), WysijaForm.blockDropOptions);
|
||||
block_placeholder.addClassName('enabled');
|
||||
}
|
||||
},
|
||||
removeBlockDroppable: function () {
|
||||
var block_placeholder;
|
||||
if (this.isBlockDroppableEnabled()) {
|
||||
var block_placeholder = this.getBlockDroppable();
|
||||
block_placeholder = this.getBlockDroppable();
|
||||
window.Droppables.remove(block_placeholder.identify());
|
||||
block_placeholder.removeClassName('enabled');
|
||||
}
|
||||
@ -807,6 +820,7 @@ WysijaForm.Block = window.Class.create({
|
||||
return this.element.down('.wysija_controls');
|
||||
},
|
||||
setupControls: function () {
|
||||
var block;
|
||||
// enable controls
|
||||
this.controls = this.getControls();
|
||||
|
||||
@ -857,9 +871,8 @@ WysijaForm.Block = window.Class.create({
|
||||
if (this.settingsButton !== null) {
|
||||
this.settingsButton.observe('click', function (event) {
|
||||
// TODO: refactor
|
||||
var block = window.$(event.target).up('.mailpoet_form_block') || null;
|
||||
block = window.$(event.target).up('.mailpoet_form_block') || null;
|
||||
if (block !== null) {
|
||||
var field = WysijaForm.getFieldData(block);
|
||||
this.editSettings();
|
||||
}
|
||||
}.bind(this));
|
||||
@ -905,18 +918,23 @@ WysijaForm.Block = window.Class.create({
|
||||
/* Invoked on item dropped */
|
||||
WysijaForm.Block.create = function (createBlock, target) {
|
||||
var block = createBlock;
|
||||
var body;
|
||||
var block_template;
|
||||
var template;
|
||||
var output;
|
||||
var settings_segments;
|
||||
if (window.$('form_template_' + block.type) === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var body = window.$(WysijaForm.options.body),
|
||||
block_template = window.Handlebars.compile(window.$('form_template_block').innerHTML),
|
||||
template = window.Handlebars.compile(window.$('form_template_' + block.type).innerHTML),
|
||||
output = '';
|
||||
body = window.$(WysijaForm.options.body);
|
||||
block_template = window.Handlebars.compile(window.$('form_template_block').innerHTML);
|
||||
template = window.Handlebars.compile(window.$('form_template_' + block.type).innerHTML);
|
||||
output = '';
|
||||
|
||||
if (block.type === 'segment') {
|
||||
if (block.params.values === undefined) {
|
||||
var settings_segments = window.jQuery('#mailpoet_form_segments').val();
|
||||
settings_segments = window.jQuery('#mailpoet_form_segments').val();
|
||||
if (settings_segments !== null && settings_segments.length > 0) {
|
||||
block.params.values = window.mailpoet_segments.filter(function (segment) {
|
||||
return (settings_segments.indexOf(segment.id) !== -1);
|
||||
@ -936,14 +954,13 @@ WysijaForm.Block.create = function (createBlock, target) {
|
||||
}
|
||||
|
||||
// if the drop target was the bottom placeholder
|
||||
var element = null;
|
||||
if (target.identify() === 'block_placeholder') {
|
||||
// insert block at the bottom
|
||||
element = body.insert(output);
|
||||
body.insert(output);
|
||||
// block = body.childElements().last();
|
||||
} else {
|
||||
// insert block before the drop target
|
||||
element = target.insert({
|
||||
target.insert({
|
||||
before: output
|
||||
});
|
||||
// block = target.previous('.mailpoet_form_block');
|
||||
@ -984,8 +1001,8 @@ WysijaForm.Widget = window.Class.create(WysijaForm.Block, {
|
||||
this.setupControls();
|
||||
},
|
||||
save: function () {
|
||||
info('widget -> save');
|
||||
var data = this.getData();
|
||||
info('widget -> save');
|
||||
|
||||
if (data.element !== undefined) {
|
||||
delete data.element;
|
||||
@ -994,8 +1011,8 @@ WysijaForm.Widget = window.Class.create(WysijaForm.Block, {
|
||||
return data;
|
||||
},
|
||||
setData: function (data) {
|
||||
var current_data = this.getData(),
|
||||
params = window.$H(current_data.params).merge(data.params).toObject();
|
||||
var current_data = this.getData();
|
||||
var params = window.$H(current_data.params).merge(data.params).toObject();
|
||||
|
||||
// update type if it changed
|
||||
if (data.type !== undefined && data.type !== current_data.type) {
|
||||
@ -1020,16 +1037,20 @@ WysijaForm.Widget = window.Class.create(WysijaForm.Block, {
|
||||
this.removeBlock();
|
||||
},
|
||||
redraw: function (data) {
|
||||
var options;
|
||||
var block_template;
|
||||
var template;
|
||||
var params;
|
||||
// set parameters
|
||||
this.setData(data);
|
||||
var options = this.getData();
|
||||
options = this.getData();
|
||||
// redraw block
|
||||
var block_template = window.Handlebars.compile(window.$('form_template_block').innerHTML),
|
||||
template = window.Handlebars.compile(window.$('form_template_' + options.type).innerHTML),
|
||||
data = window.$H(options).merge({
|
||||
template: template(options)
|
||||
}).toObject();
|
||||
this.element.replace(block_template(data));
|
||||
block_template = window.Handlebars.compile(window.$('form_template_block').innerHTML);
|
||||
template = window.Handlebars.compile(window.$('form_template_' + options.type).innerHTML);
|
||||
params = window.$H(options).merge({
|
||||
template: template(options)
|
||||
}).toObject();
|
||||
this.element.replace(block_template(params));
|
||||
|
||||
WysijaForm.init();
|
||||
},
|
||||
@ -1039,7 +1060,7 @@ WysijaForm.Widget = window.Class.create(WysijaForm.Block, {
|
||||
template: window.jQuery('#form_template_field_settings').html(),
|
||||
data: this.getData(),
|
||||
onSuccess: function () {
|
||||
var data = window.jQuery('#form_field_settings').serializeObject();
|
||||
var data = window.jQuery('#form_field_settings').mailpoetSerializeObject();
|
||||
this.redraw(data);
|
||||
}.bind(this)
|
||||
});
|
||||
@ -1061,8 +1082,8 @@ function info(value) {
|
||||
var noop = function () {};
|
||||
var methods = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'markTimeline', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn'];
|
||||
var length = methods.length;
|
||||
window.console = {};
|
||||
var console = {};
|
||||
window.console = {};
|
||||
while (length--) {
|
||||
console[methods[length]] = noop;
|
||||
}
|
||||
|
@ -1,26 +1,28 @@
|
||||
define('handlebars_helpers', ['handlebars'], function (Handlebars) {
|
||||
// Handlebars helpers
|
||||
Handlebars.registerHelper('concat', function () {
|
||||
var size = (arguments.length - 1),
|
||||
output = '';
|
||||
for (var i = 0; i < size; i++) {
|
||||
var size = (arguments.length - 1);
|
||||
var output = '';
|
||||
var i;
|
||||
for (i = 0; i < size; i++) {
|
||||
output += arguments[i];
|
||||
}
|
||||
return output;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('number_format', function (value, block) {
|
||||
Handlebars.registerHelper('number_format', function (value) {
|
||||
return Number(value).toLocaleString();
|
||||
});
|
||||
Handlebars.registerHelper('date_format', function (timestamp, block) {
|
||||
var f;
|
||||
if (window.moment) {
|
||||
if (timestamp === undefined || isNaN(timestamp) || timestamp <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// set date format
|
||||
var f = block.hash.format || 'MMM Do, YYYY';
|
||||
// check if we passed a timestamp
|
||||
// set date format
|
||||
f = block.hash.format || 'MMM Do, YYYY';
|
||||
// check if we passed a timestamp
|
||||
if (parseInt(timestamp, 10) == timestamp) {
|
||||
return window.moment.unix(timestamp).format(f);
|
||||
} else {
|
||||
@ -59,25 +61,24 @@ define('handlebars_helpers', ['handlebars'], function (Handlebars) {
|
||||
case '||':
|
||||
return (v1 || v2) ? options.fn(this) : options.inverse(this);
|
||||
case 'in':
|
||||
var values = v2.split(',');
|
||||
return (v2.indexOf(v1) !== -1) ? options.fn(this) : options.inverse(this);
|
||||
default:
|
||||
return options.inverse(this);
|
||||
}
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('nl2br', function (value, block) {
|
||||
Handlebars.registerHelper('nl2br', function (value) {
|
||||
return value.gsub('\n', '<br />');
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('json_encode', function (value, block) {
|
||||
Handlebars.registerHelper('json_encode', function (value) {
|
||||
return JSON.stringify(value);
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('json_decode', function (value, block) {
|
||||
Handlebars.registerHelper('json_decode', function (value) {
|
||||
return JSON.parse(value);
|
||||
});
|
||||
Handlebars.registerHelper('url', function (value, block) {
|
||||
Handlebars.registerHelper('url', function (value) {
|
||||
var url = window.location.protocol + '//' + window.location.host + window.location.pathname;
|
||||
|
||||
return url + value;
|
||||
@ -90,16 +91,17 @@ define('handlebars_helpers', ['handlebars'], function (Handlebars) {
|
||||
return value;
|
||||
}
|
||||
});
|
||||
Handlebars.registerHelper('lookup', function (obj, field, options) {
|
||||
Handlebars.registerHelper('lookup', function (obj, field) {
|
||||
return obj && obj[field];
|
||||
});
|
||||
|
||||
|
||||
Handlebars.registerHelper('rsa_key', function (value, block) {
|
||||
// extract all lines into an array
|
||||
Handlebars.registerHelper('rsa_key', function (value) {
|
||||
var lines;
|
||||
// extract all lines into an array
|
||||
if (value === undefined) return '';
|
||||
|
||||
var lines = value.trim().split('\n');
|
||||
lines = value.trim().split('\n');
|
||||
|
||||
// remove header & footer
|
||||
lines.shift();
|
||||
@ -109,7 +111,7 @@ define('handlebars_helpers', ['handlebars'], function (Handlebars) {
|
||||
return lines.join('');
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('trim', function (value, block) {
|
||||
Handlebars.registerHelper('trim', function (value) {
|
||||
if (value === null || value === undefined) return '';
|
||||
return value.trim();
|
||||
});
|
||||
@ -126,10 +128,10 @@ define('handlebars_helpers', ['handlebars'], function (Handlebars) {
|
||||
*/
|
||||
Handlebars.registerHelper('ellipsis', function (str, limit, append) {
|
||||
var strAppend = append;
|
||||
var sanitized = str.replace(/(<([^>]+)>)/g, '');
|
||||
if (strAppend === undefined) {
|
||||
strAppend = '';
|
||||
}
|
||||
var sanitized = str.replace(/(<([^>]+)>)/g, '');
|
||||
if (sanitized.length > limit) {
|
||||
return sanitized.substr(0, limit - strAppend.length) + strAppend;
|
||||
} else {
|
||||
|
@ -23,9 +23,9 @@ function printData(data) {
|
||||
height: '400px',
|
||||
}}
|
||||
/>);
|
||||
} else {
|
||||
return (<p>{MailPoet.I18n.t('systemInfoDataError')}</p>);
|
||||
}
|
||||
return (<p>{MailPoet.I18n.t('systemInfoDataError')}</p>);
|
||||
|
||||
}
|
||||
|
||||
function KnowledgeBase() {
|
||||
|
@ -23,21 +23,21 @@ define(
|
||||
* Dual licensed under the MIT and GPL licenses.
|
||||
* http://benalman.com/about/license/
|
||||
*/
|
||||
$.fn.serializeObject = function (coerce) {
|
||||
var obj = {},
|
||||
coerce_types = { true: !0, false: !1, null: null };
|
||||
$.fn.mailpoetSerializeObject = function (coerce) {
|
||||
var obj = {};
|
||||
var coerce_types = { true: !0, false: !1, null: null };
|
||||
|
||||
// Iterate over all name=value pairs.
|
||||
$.each(this.serializeArray(), function (j, v) {
|
||||
var key = v.name,
|
||||
val = v.value,
|
||||
cur = obj,
|
||||
i = 0,
|
||||
var key = v.name;
|
||||
var val = v.value;
|
||||
var cur = obj;
|
||||
var i = 0;
|
||||
|
||||
// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
|
||||
// into its component parts.
|
||||
keys = key.split(']['),
|
||||
keys_last = keys.length - 1;
|
||||
// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
|
||||
// into its component parts.
|
||||
var keys = key.split('][');
|
||||
var keys_last = keys.length - 1;
|
||||
|
||||
// If the first keys part contains [ and the last ends with ], then []
|
||||
// are correctly balanced.
|
||||
@ -106,4 +106,4 @@ define(
|
||||
|
||||
return $;
|
||||
}
|
||||
);
|
||||
);
|
||||
|
@ -223,24 +223,24 @@ const ListingItems = React.createClass({
|
||||
</tr>
|
||||
</tbody>
|
||||
);
|
||||
} else {
|
||||
const select_all_classes = classNames(
|
||||
}
|
||||
const select_all_classes = classNames(
|
||||
'mailpoet_select_all',
|
||||
{ mailpoet_hidden: (
|
||||
{ mailpoet_hidden: (
|
||||
this.props.selection === false
|
||||
|| (this.props.count <= this.props.limit)
|
||||
),
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<tbody>
|
||||
<tr className={select_all_classes}>
|
||||
<td colSpan={
|
||||
return (
|
||||
<tbody>
|
||||
<tr className={select_all_classes}>
|
||||
<td colSpan={
|
||||
this.props.columns.length
|
||||
+ (this.props.is_selectable ? 1 : 0)
|
||||
}>
|
||||
{
|
||||
{
|
||||
(this.props.selection !== 'all')
|
||||
? MailPoet.I18n.t('selectAllLabel')
|
||||
: MailPoet.I18n.t('selectedAllLabel').replace(
|
||||
@ -249,41 +249,41 @@ const ListingItems = React.createClass({
|
||||
)
|
||||
}
|
||||
|
||||
<a
|
||||
onClick={this.props.onSelectAll}
|
||||
href="javascript:;">{
|
||||
<a
|
||||
onClick={this.props.onSelectAll}
|
||||
href="javascript:;">{
|
||||
(this.props.selection !== 'all')
|
||||
? MailPoet.I18n.t('selectAllLink')
|
||||
: MailPoet.I18n.t('clearSelection')
|
||||
}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{this.props.items.map((item, index) => {
|
||||
const renderItem = item;
|
||||
renderItem.id = parseInt(item.id, 10);
|
||||
renderItem.selected = (this.props.selected_ids.indexOf(renderItem.id) !== -1);
|
||||
{this.props.items.map((item, index) => {
|
||||
const renderItem = item;
|
||||
renderItem.id = parseInt(item.id, 10);
|
||||
renderItem.selected = (this.props.selected_ids.indexOf(renderItem.id) !== -1);
|
||||
|
||||
return (
|
||||
<ListingItem
|
||||
columns={this.props.columns}
|
||||
onSelectItem={this.props.onSelectItem}
|
||||
onRenderItem={this.props.onRenderItem}
|
||||
onDeleteItem={this.props.onDeleteItem}
|
||||
onRestoreItem={this.props.onRestoreItem}
|
||||
onTrashItem={this.props.onTrashItem}
|
||||
onRefreshItems={this.props.onRefreshItems}
|
||||
selection={this.props.selection}
|
||||
is_selectable={this.props.is_selectable}
|
||||
item_actions={this.props.item_actions}
|
||||
group={this.props.group}
|
||||
key={`item-${renderItem.id}-${index}`}
|
||||
item={renderItem} />
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
);
|
||||
|
||||
return (
|
||||
<ListingItem
|
||||
columns={this.props.columns}
|
||||
onSelectItem={this.props.onSelectItem}
|
||||
onRenderItem={this.props.onRenderItem}
|
||||
onDeleteItem={this.props.onDeleteItem}
|
||||
onRestoreItem={this.props.onRestoreItem}
|
||||
onTrashItem={this.props.onTrashItem}
|
||||
onRefreshItems={this.props.onRefreshItems}
|
||||
selection={this.props.selection}
|
||||
is_selectable={this.props.is_selectable}
|
||||
item_actions={this.props.item_actions}
|
||||
group={this.props.group}
|
||||
key={`item-${renderItem.id}-${index}`}
|
||||
item={renderItem} />
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@ -321,14 +321,13 @@ const Listing = React.createClass({
|
||||
if (params.splat) {
|
||||
params.splat.split('/').map((param) => {
|
||||
const [key, value] = this.getParam(param);
|
||||
const filters = {};
|
||||
switch (key) {
|
||||
case 'filter':
|
||||
const filters = {};
|
||||
value.split('&').map((pair) => {
|
||||
const [k, v] = pair.split('=');
|
||||
filters[k] = v;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
state.filter = filters;
|
||||
break;
|
||||
@ -414,9 +413,9 @@ const Listing = React.createClass({
|
||||
if (base_url !== null) {
|
||||
base_url = this.setBaseUrlParams(base_url);
|
||||
return `/${base_url}/${params}`;
|
||||
} else {
|
||||
return `/${params}`;
|
||||
}
|
||||
return `/${params}`;
|
||||
|
||||
},
|
||||
setBaseUrlParams: function (base_url) {
|
||||
let ret = base_url;
|
||||
@ -658,8 +657,8 @@ const Listing = React.createClass({
|
||||
});
|
||||
},
|
||||
handleSelectItem: function (id, is_checked) {
|
||||
let selected_ids = this.state.selected_ids,
|
||||
selection = false;
|
||||
let selected_ids = this.state.selected_ids;
|
||||
let selection = false;
|
||||
|
||||
if (is_checked) {
|
||||
selected_ids = jQuery.merge(selected_ids, [id]);
|
||||
|
@ -59,125 +59,125 @@ define([
|
||||
render: function () {
|
||||
if (this.props.count === 0) {
|
||||
return false;
|
||||
} else {
|
||||
let pagination = false;
|
||||
let firstPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">«</span>
|
||||
}
|
||||
let pagination = false;
|
||||
let firstPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">«</span>
|
||||
);
|
||||
let previousPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">‹</span>
|
||||
let previousPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">‹</span>
|
||||
);
|
||||
let nextPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">›</span>
|
||||
let nextPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">›</span>
|
||||
);
|
||||
let lastPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">»</span>
|
||||
let lastPage = (
|
||||
<span aria-hidden="true" className="tablenav-pages-navspan">»</span>
|
||||
);
|
||||
|
||||
if (this.props.limit > 0 && this.props.count > this.props.limit) {
|
||||
if (this.props.page > 1) {
|
||||
previousPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={this.setPreviousPage}
|
||||
className="prev-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('previousPage')}</span>
|
||||
<span aria-hidden="true">‹</span>
|
||||
</a>
|
||||
if (this.props.limit > 0 && this.props.count > this.props.limit) {
|
||||
if (this.props.page > 1) {
|
||||
previousPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={this.setPreviousPage}
|
||||
className="prev-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('previousPage')}</span>
|
||||
<span aria-hidden="true">‹</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.page > 2) {
|
||||
firstPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={this.setFirstPage}
|
||||
className="first-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('firstPage')}</span>
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.page < this.getLastPage()) {
|
||||
nextPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={this.setNextPage}
|
||||
className="next-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('nextPage')}</span>
|
||||
<span aria-hidden="true">›</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.page < this.getLastPage() - 1) {
|
||||
lastPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={this.setLastPage}
|
||||
className="last-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('lastPage')}</span>
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
let pageValue = this.props.page;
|
||||
if (this.state.page !== null) {
|
||||
pageValue = this.state.page;
|
||||
}
|
||||
|
||||
pagination = (
|
||||
<span className="pagination-links">
|
||||
{firstPage}
|
||||
|
||||
{previousPage}
|
||||
|
||||
<span className="paging-input">
|
||||
<label
|
||||
className="screen-reader-text"
|
||||
htmlFor="current-page-selector">{MailPoet.I18n.t('currentPage')}</label>
|
||||
<input
|
||||
type="text"
|
||||
onChange={this.handleChangeManualPage}
|
||||
onKeyUp={this.handleSetManualPage}
|
||||
onBlur={this.handleBlurManualPage}
|
||||
aria-describedby="table-paging"
|
||||
size="2"
|
||||
ref="page"
|
||||
value={pageValue}
|
||||
name="paged"
|
||||
id="current-page-selector"
|
||||
className="current-page" />
|
||||
{MailPoet.I18n.t('pageOutOf')}
|
||||
<span className="total-pages">
|
||||
{Math.ceil(this.props.count / this.props.limit).toLocaleString()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
{nextPage}
|
||||
|
||||
{lastPage}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const classes = classNames(
|
||||
if (this.props.page > 2) {
|
||||
firstPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={this.setFirstPage}
|
||||
className="first-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('firstPage')}</span>
|
||||
<span aria-hidden="true">«</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.page < this.getLastPage()) {
|
||||
nextPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={this.setNextPage}
|
||||
className="next-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('nextPage')}</span>
|
||||
<span aria-hidden="true">›</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.page < this.getLastPage() - 1) {
|
||||
lastPage = (
|
||||
<a href="javascript:;"
|
||||
onClick={this.setLastPage}
|
||||
className="last-page">
|
||||
<span className="screen-reader-text">{MailPoet.I18n.t('lastPage')}</span>
|
||||
<span aria-hidden="true">»</span>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
let pageValue = this.props.page;
|
||||
if (this.state.page !== null) {
|
||||
pageValue = this.state.page;
|
||||
}
|
||||
|
||||
pagination = (
|
||||
<span className="pagination-links">
|
||||
{firstPage}
|
||||
|
||||
{previousPage}
|
||||
|
||||
<span className="paging-input">
|
||||
<label
|
||||
className="screen-reader-text"
|
||||
htmlFor="current-page-selector">{MailPoet.I18n.t('currentPage')}</label>
|
||||
<input
|
||||
type="text"
|
||||
onChange={this.handleChangeManualPage}
|
||||
onKeyUp={this.handleSetManualPage}
|
||||
onBlur={this.handleBlurManualPage}
|
||||
aria-describedby="table-paging"
|
||||
size="2"
|
||||
ref="page"
|
||||
value={pageValue}
|
||||
name="paged"
|
||||
id="current-page-selector"
|
||||
className="current-page" />
|
||||
{MailPoet.I18n.t('pageOutOf')}
|
||||
<span className="total-pages">
|
||||
{Math.ceil(this.props.count / this.props.limit).toLocaleString()}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
{nextPage}
|
||||
|
||||
{lastPage}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const classes = classNames(
|
||||
'tablenav-pages',
|
||||
{ 'one-page': (this.props.count <= this.props.limit) }
|
||||
);
|
||||
|
||||
let numberOfItemsLabel;
|
||||
if (this.props.count == 1) {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsSingular');
|
||||
} else {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsMultiple')
|
||||
let numberOfItemsLabel;
|
||||
if (this.props.count == 1) {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsSingular');
|
||||
} else {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsMultiple')
|
||||
.replace('%$1d', this.props.count.toLocaleString());
|
||||
}
|
||||
return (
|
||||
<div className={classes}>
|
||||
<span className="displaying-num">{ numberOfItemsLabel }</span>
|
||||
{ pagination }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className={classes}>
|
||||
<span className="displaying-num">{ numberOfItemsLabel }</span>
|
||||
{ pagination }
|
||||
</div>
|
||||
);
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -10,7 +10,7 @@ define([
|
||||
handleSearch: function (e) {
|
||||
e.preventDefault();
|
||||
this.props.onSearch(
|
||||
this.refs.search.value
|
||||
this.refs.search.value.trim()
|
||||
);
|
||||
},
|
||||
componentWillReceiveProps: function (nextProps) {
|
||||
@ -19,27 +19,27 @@ define([
|
||||
render: function () {
|
||||
if (this.props.search === false) {
|
||||
return false;
|
||||
} else {
|
||||
return (
|
||||
<form name="search" onSubmit={this.handleSearch}>
|
||||
<p className="search-box">
|
||||
<label htmlFor="search_input" className="screen-reader-text">
|
||||
{MailPoet.I18n.t('searchLabel')}
|
||||
</label>
|
||||
<input
|
||||
type="search"
|
||||
id="search_input"
|
||||
ref="search"
|
||||
name="s"
|
||||
defaultValue={this.props.search} />
|
||||
<input
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('searchLabel')}
|
||||
className="button" />
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<form name="search" onSubmit={this.handleSearch}>
|
||||
<p className="search-box">
|
||||
<label htmlFor="search_input" className="screen-reader-text">
|
||||
{MailPoet.I18n.t('searchLabel')}
|
||||
</label>
|
||||
<input
|
||||
type="search"
|
||||
id="search_input"
|
||||
ref="search"
|
||||
name="s"
|
||||
defaultValue={this.props.search} />
|
||||
<input
|
||||
type="submit"
|
||||
defaultValue={MailPoet.I18n.t('searchLabel')}
|
||||
className="button" />
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -116,6 +116,7 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
}
|
||||
},
|
||||
init: function (options) {
|
||||
var modal;
|
||||
if (this.initialized === true) {
|
||||
this.close();
|
||||
}
|
||||
@ -134,7 +135,7 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
if (this.options.type !== null) {
|
||||
// insert modal depending on its type
|
||||
if (this.options.type === 'popup') {
|
||||
var modal = this.compileTemplate(
|
||||
modal = this.compileTemplate(
|
||||
this.templates[this.options.type]
|
||||
);
|
||||
// create modal
|
||||
@ -178,7 +179,7 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
|
||||
return this;
|
||||
},
|
||||
initOverlay: function (toggle) {
|
||||
initOverlay: function () {
|
||||
if (jQuery('#mailpoet_modal_overlay').length === 0) {
|
||||
// insert overlay into the DOM
|
||||
jQuery('body').append(this.templates.overlay);
|
||||
@ -343,20 +344,21 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
return this;
|
||||
},
|
||||
setPosition: function () {
|
||||
var screenWidth;
|
||||
var screenHeight;
|
||||
var modalWidth;
|
||||
var modalHeight;
|
||||
switch (this.options.type) {
|
||||
case 'popup':
|
||||
var screenWidth = jQuery(window).width(),
|
||||
screenHeight = jQuery(window).height(),
|
||||
modalWidth = jQuery('.mailpoet_' + this.options.type + '_wrapper').width(),
|
||||
modalHeight = jQuery('.mailpoet_' + this.options.type + '_wrapper').height();
|
||||
screenWidth = jQuery(window).width();
|
||||
screenHeight = jQuery(window).height();
|
||||
modalWidth = jQuery('.mailpoet_'+ this.options.type +'_wrapper').width();
|
||||
modalHeight = jQuery('.mailpoet_'+ this.options.type +'_wrapper').height();
|
||||
|
||||
var top = Math.max(48, parseInt((screenHeight / 2) - (modalHeight / 2))),
|
||||
left = Math.max(0, parseInt((screenWidth / 2) - (modalWidth / 2)));
|
||||
|
||||
// set position of popup depending on screen dimensions.
|
||||
// set position of popup depending on screen dimensions.
|
||||
jQuery('#mailpoet_popup').css({
|
||||
top: top,
|
||||
left: left
|
||||
top: Math.max(48, parseInt((screenHeight / 2) - (modalHeight / 2))),
|
||||
left: Math.max(0, parseInt((screenWidth / 2) - (modalWidth / 2)))
|
||||
});
|
||||
break;
|
||||
case 'panel':
|
||||
@ -384,9 +386,6 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
// remember the previously focused element
|
||||
this.prevFocus = jQuery(':focus');
|
||||
|
||||
// add a flag on the body so that we can prevent scrolling
|
||||
jQuery('body').addClass('mailpoet_modal_opened');
|
||||
|
||||
// show popup
|
||||
jQuery('#mailpoet_' + this.options.type).show();
|
||||
|
||||
@ -436,7 +435,7 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
.removeClass('mailpoet_modal_highlight');
|
||||
return this;
|
||||
},
|
||||
hideModal: function (callback) {
|
||||
hideModal: function () {
|
||||
// set modal as closed
|
||||
this.opened = false;
|
||||
|
||||
@ -446,12 +445,9 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
// remove class on highlighted elements
|
||||
this.highlightOff();
|
||||
|
||||
// remove class from body to let it be scrollable
|
||||
jQuery('body').removeClass('mailpoet_modal_opened');
|
||||
|
||||
return this;
|
||||
},
|
||||
showOverlay: function (force) {
|
||||
showOverlay: function () {
|
||||
jQuery('#mailpoet_modal_overlay').show();
|
||||
return this;
|
||||
},
|
||||
|
@ -1,12 +1,8 @@
|
||||
define([
|
||||
'backbone',
|
||||
'backbone.marionette',
|
||||
'backbone.radio',
|
||||
'jquery',
|
||||
'underscore',
|
||||
'handlebars',
|
||||
'handlebars_helpers'
|
||||
], function (Backbone, Marionette, BackboneRadio, jQuery, _, Handlebars) {
|
||||
'backbone.radio'
|
||||
], function (Backbone, Marionette, BackboneRadio) {
|
||||
var Radio = BackboneRadio;
|
||||
|
||||
var AppView = Marionette.View.extend({
|
||||
|
@ -8,13 +8,13 @@ define([
|
||||
'newsletter_editor/behaviors/BehaviorsLookup',
|
||||
'mailpoet',
|
||||
'spectrum'
|
||||
], function (Marionette, BehaviorsLookup, MailPoet, Spectrum) {
|
||||
], function (Marionette, BehaviorsLookup, MailPoet) {
|
||||
var BL = BehaviorsLookup;
|
||||
|
||||
BL.ColorPickerBehavior = Marionette.Behavior.extend({
|
||||
onRender: function () {
|
||||
var that = this,
|
||||
preferredFormat = 'hex6';
|
||||
var that = this;
|
||||
var preferredFormat = 'hex6';
|
||||
this.view.$('.mailpoet_color').each(function () {
|
||||
var $input = that.view.$(this);
|
||||
var updateColorInput = function (color) {
|
||||
|
@ -25,11 +25,11 @@ define([
|
||||
this.addDropZone();
|
||||
}
|
||||
},
|
||||
addDropZone: function (_event) {
|
||||
var that = this,
|
||||
view = this.view,
|
||||
domElement = that.$el.get(0),
|
||||
acceptableElementSelector;
|
||||
addDropZone: function () {
|
||||
var that = this;
|
||||
var view = this.view;
|
||||
var domElement = that.$el.get(0);
|
||||
var acceptableElementSelector;
|
||||
|
||||
// TODO: Extract this limitation code to be controlled from containers
|
||||
if (this.view.renderOptions.depth === 0) {
|
||||
@ -47,11 +47,11 @@ define([
|
||||
interact(domElement).dropzone({
|
||||
accept: acceptableElementSelector,
|
||||
overlap: 'pointer', // Mouse pointer denotes location of a droppable
|
||||
ondragenter: function (event) {
|
||||
ondragenter: function () {
|
||||
// 1. Visually mark block as active for dropping
|
||||
view.$el.addClass('mailpoet_drop_active');
|
||||
},
|
||||
ondragleave: function (event) {
|
||||
ondragleave: function () {
|
||||
// 1. Remove visual markings of active dropping container
|
||||
// 2. Remove visual markings of drop position visualization
|
||||
that.cleanup();
|
||||
@ -63,20 +63,26 @@ define([
|
||||
// 3b. If insertion is special, compute position (which side) and which cell the insertion belongs to
|
||||
// 4. If insertion at that position is not visualized, display position visualization there, remove other visualizations from this container
|
||||
var dropPosition = that.getDropPosition(
|
||||
event.dragmove.pageX,
|
||||
event.dragmove.pageY,
|
||||
view.$el,
|
||||
view.model.get('orientation'),
|
||||
view.model.get('blocks').length
|
||||
),
|
||||
element = view.$el,
|
||||
markerWidth = '',
|
||||
markerHeight = '',
|
||||
containerOffset = element.offset(),
|
||||
viewCollection = that.getCollection(),
|
||||
marker, targetModel, targetView, targetElement,
|
||||
topOffset, leftOffset, isLastBlockInsertion,
|
||||
$targetBlock, margin;
|
||||
event.dragmove.pageX,
|
||||
event.dragmove.pageY,
|
||||
view.$el,
|
||||
view.model.get('orientation'),
|
||||
view.model.get('blocks').length
|
||||
);
|
||||
var element = view.$el;
|
||||
var markerWidth = '';
|
||||
var markerHeight = '';
|
||||
var containerOffset = element.offset();
|
||||
var viewCollection = that.getCollection();
|
||||
var marker;
|
||||
var targetModel;
|
||||
var targetView;
|
||||
var targetElement;
|
||||
var topOffset;
|
||||
var leftOffset;
|
||||
var isLastBlockInsertion;
|
||||
var $targetBlock;
|
||||
var margin;
|
||||
|
||||
if (dropPosition === undefined) return;
|
||||
|
||||
@ -178,15 +184,19 @@ define([
|
||||
// 4. Perform cleanup actions
|
||||
|
||||
var dropPosition = that.getDropPosition(
|
||||
event.dragEvent.pageX,
|
||||
event.dragEvent.pageY,
|
||||
view.$el,
|
||||
view.model.get('orientation'),
|
||||
view.model.get('blocks').length
|
||||
),
|
||||
droppableModel = event.draggable.getDropModel(),
|
||||
viewCollection = that.getCollection(),
|
||||
droppedView, droppedModel, index, tempCollection, tempCollection2;
|
||||
event.dragEvent.pageX,
|
||||
event.dragEvent.pageY,
|
||||
view.$el,
|
||||
view.model.get('orientation'),
|
||||
view.model.get('blocks').length
|
||||
);
|
||||
var droppableModel = event.draggable.getDropModel();
|
||||
var viewCollection = that.getCollection();
|
||||
var droppedView;
|
||||
var index;
|
||||
var tempCollection;
|
||||
var tempCollection2;
|
||||
var tempModel;
|
||||
|
||||
if (dropPosition === undefined) return;
|
||||
|
||||
@ -210,7 +220,7 @@ define([
|
||||
} else {
|
||||
// Special insertion by replacing target block with collection
|
||||
// and inserting dropModel into that
|
||||
var tempModel = viewCollection.at(dropPosition.index);
|
||||
tempModel = viewCollection.at(dropPosition.index);
|
||||
|
||||
tempCollection = new (window.EditorApplication.getBlockTypeModel('container'))({
|
||||
orientation: (view.model.get('orientation') === 'vertical') ? 'horizontal' : 'vertical'
|
||||
@ -272,26 +282,30 @@ define([
|
||||
this.view.$('.mailpoet_drop_marker').remove();
|
||||
},
|
||||
getDropPosition: function (eventX, eventY, is_unsafe) {
|
||||
var SPECIAL_AREA_INSERTION_WIDTH = 0.00, // Disable special insertion. Default: 0.3
|
||||
var SPECIAL_AREA_INSERTION_WIDTH = 0.00; // Disable special insertion. Default: 0.3
|
||||
|
||||
element = this.view.$el,
|
||||
orientation = this.view.model.get('orientation'),
|
||||
var element = this.view.$el;
|
||||
var orientation = this.view.model.get('orientation');
|
||||
|
||||
elementOffset = element.offset(),
|
||||
elementPageX = elementOffset.left,
|
||||
elementPageY = elementOffset.top,
|
||||
elementWidth = element.outerWidth(true),
|
||||
elementHeight = element.outerHeight(true),
|
||||
var elementOffset = element.offset();
|
||||
var elementPageX = elementOffset.left;
|
||||
var elementPageY = elementOffset.top;
|
||||
var elementWidth = element.outerWidth(true);
|
||||
var elementHeight = element.outerHeight(true);
|
||||
|
||||
relativeX = eventX - elementPageX,
|
||||
relativeY = eventY - elementPageY,
|
||||
var relativeX = eventX - elementPageX;
|
||||
var relativeY = eventY - elementPageY;
|
||||
|
||||
relativeOffset, elementLength,
|
||||
var relativeOffset;
|
||||
var elementLength;
|
||||
|
||||
canAcceptNormalInsertion = this._canAcceptNormalInsertion(),
|
||||
canAcceptSpecialInsertion = this._canAcceptSpecialInsertion(),
|
||||
var canAcceptNormalInsertion = this._canAcceptNormalInsertion();
|
||||
var canAcceptSpecialInsertion = this._canAcceptSpecialInsertion();
|
||||
|
||||
insertionType, index, position, indexAndPosition;
|
||||
var insertionType;
|
||||
var index;
|
||||
var position;
|
||||
var indexAndPosition;
|
||||
|
||||
var unsafe = !!is_unsafe;
|
||||
|
||||
@ -359,12 +373,14 @@ define([
|
||||
// target element if event happens on the second half of the element.
|
||||
// Halves depend on orientation.
|
||||
|
||||
var index = this._computeCellIndex(eventX, eventY),
|
||||
// TODO: Handle case when there are no children, container is empty
|
||||
targetView = this.getChildren().findByModel(this.getCollection().at(index)),
|
||||
orientation = this.view.model.get('orientation'),
|
||||
element = targetView.$el,
|
||||
eventOffset, closeOffset, elementDimension;
|
||||
var index = this._computeCellIndex(eventX, eventY);
|
||||
// TODO: Handle case when there are no children, container is empty
|
||||
var targetView = this.getChildren().findByModel(this.getCollection().at(index));
|
||||
var orientation = this.view.model.get('orientation');
|
||||
var element = targetView.$el;
|
||||
var eventOffset;
|
||||
var closeOffset;
|
||||
var elementDimension;
|
||||
|
||||
if (orientation === 'vertical') {
|
||||
eventOffset = eventY;
|
||||
@ -394,39 +410,40 @@ define([
|
||||
return this._computeCellIndex(eventX, eventY);
|
||||
},
|
||||
_computeCellIndex: function (eventX, eventY) {
|
||||
var orientation = this.view.model.get('orientation'),
|
||||
eventOffset = (orientation === 'vertical') ? eventY : eventX,
|
||||
resultView = this.getChildren().find(function (view) {
|
||||
var element = view.$el,
|
||||
closeOffset, farOffset;
|
||||
var orientation = this.view.model.get('orientation');
|
||||
var eventOffset = (orientation === 'vertical') ? eventY : eventX;
|
||||
var resultView = this.getChildren().find(function (view) {
|
||||
var element = view.$el;
|
||||
var closeOffset;
|
||||
var farOffset;
|
||||
|
||||
if (orientation === 'vertical') {
|
||||
closeOffset = element.offset().top;
|
||||
farOffset = element.outerHeight(true);
|
||||
} else {
|
||||
closeOffset = element.offset().left;
|
||||
farOffset = element.outerWidth(true);
|
||||
}
|
||||
farOffset += closeOffset;
|
||||
if (orientation === 'vertical') {
|
||||
closeOffset = element.offset().top;
|
||||
farOffset = element.outerHeight(true);
|
||||
} else {
|
||||
closeOffset = element.offset().left;
|
||||
farOffset = element.outerWidth(true);
|
||||
}
|
||||
farOffset += closeOffset;
|
||||
|
||||
return closeOffset <= eventOffset && eventOffset <= farOffset;
|
||||
});
|
||||
return closeOffset <= eventOffset && eventOffset <= farOffset;
|
||||
});
|
||||
|
||||
var index = (typeof resultView === 'object') ? resultView._index : 0;
|
||||
|
||||
return index;
|
||||
},
|
||||
_canAcceptNormalInsertion: function () {
|
||||
var orientation = this.view.model.get('orientation'),
|
||||
depth = this.view.renderOptions.depth,
|
||||
childCount = this.getChildren().length;
|
||||
var orientation = this.view.model.get('orientation');
|
||||
var depth = this.view.renderOptions.depth;
|
||||
var childCount = this.getChildren().length;
|
||||
// Note that depth is zero indexed. Root container has depth=0
|
||||
return orientation === 'vertical' || (orientation === 'horizontal' && depth === 1 && childCount < this.options.columnLimit);
|
||||
},
|
||||
_canAcceptSpecialInsertion: function () {
|
||||
var orientation = this.view.model.get('orientation'),
|
||||
depth = this.view.renderOptions.depth,
|
||||
childCount = this.getChildren().length;
|
||||
var orientation = this.view.model.get('orientation');
|
||||
var depth = this.view.renderOptions.depth;
|
||||
var childCount = this.getChildren().length;
|
||||
return depth === 0 || (depth === 1 && orientation === 'horizontal' && childCount <= this.options.columnLimit);
|
||||
},
|
||||
getCollectionView: function () {
|
||||
|
@ -28,12 +28,12 @@ define([
|
||||
throw "Missing 'drop' function for DraggableBehavior";
|
||||
},
|
||||
|
||||
onDrop: function (model, view) {},
|
||||
testAttachToInstance: function (model, view) { return true; }
|
||||
onDrop: function () {},
|
||||
testAttachToInstance: function () { return true; }
|
||||
},
|
||||
onRender: function () {
|
||||
var that = this,
|
||||
interactable;
|
||||
var that = this;
|
||||
var interactable;
|
||||
|
||||
// Give instances more control over whether Draggable should be applied
|
||||
if (!this.options.testAttachToInstance(this.view.model, this.view)) return;
|
||||
@ -49,16 +49,19 @@ define([
|
||||
|
||||
onstart: function (startEvent) {
|
||||
var event = startEvent;
|
||||
var centerXOffset;
|
||||
var centerYOffset;
|
||||
var tempClone;
|
||||
var clone;
|
||||
var $clone;
|
||||
|
||||
if (that.options.cloneOriginal === true) {
|
||||
// Use substitution instead of a clone
|
||||
var tempClone = (_.isFunction(that.options.onDragSubstituteBy)) ? that.options.onDragSubstituteBy(that) : undefined,
|
||||
// Or use a clone
|
||||
clone = tempClone || event.target.cloneNode(true),
|
||||
|
||||
$original = jQuery(event.target),
|
||||
$clone = jQuery(clone),
|
||||
centerXOffset, centerYOffset, parentOffset;
|
||||
tempClone = (_.isFunction(that.options.onDragSubstituteBy)) ? that.options.onDragSubstituteBy(that) : undefined;
|
||||
// Or use a clone
|
||||
clone = tempClone || event.target.cloneNode(true);
|
||||
jQuery(event.target);
|
||||
$clone = jQuery(clone);
|
||||
|
||||
$clone.addClass('mailpoet_droppable_active');
|
||||
$clone.css('position', 'absolute');
|
||||
@ -85,10 +88,10 @@ define([
|
||||
},
|
||||
// call this function on every dragmove event
|
||||
onmove: function (event) {
|
||||
var target = event.target,
|
||||
// keep the dragged position in the data-x/data-y attributes
|
||||
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
|
||||
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
|
||||
var target = event.target;
|
||||
// keep the dragged position in the data-x/data-y attributes
|
||||
var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
|
||||
var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
|
||||
|
||||
// translate the element
|
||||
target.style.transform = 'translate(' + x + 'px, ' + y + 'px)';
|
||||
|
@ -14,12 +14,13 @@ define([
|
||||
defaults: {
|
||||
elementSelector: null,
|
||||
resizeHandleSelector: true, // true will use edges of the element itself
|
||||
transformationFunction: function (y) { return y; }, // for blocks that use the default onResize function
|
||||
minLength: 0,
|
||||
maxLength: Infinity,
|
||||
modelField: 'styles.block.height',
|
||||
onResize: function (event) {
|
||||
var currentLength = parseFloat(this.view.model.get(this.options.modelField)),
|
||||
newLength = currentLength + event.y;
|
||||
var currentLength = parseFloat(this.view.model.get(this.options.modelField));
|
||||
var 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');
|
||||
}
|
||||
@ -36,8 +37,8 @@ define([
|
||||
}
|
||||
},
|
||||
attachResize: function () {
|
||||
var domElement = (this.options.elementSelector === null) ? this.view.$el.get(0) : this.view.$(this.options.elementSelector).get(0),
|
||||
that = this;
|
||||
var domElement = (this.options.elementSelector === null) ? this.view.$el.get(0) : this.view.$(this.options.elementSelector).get(0);
|
||||
var that = this;
|
||||
interact(domElement).resizable({
|
||||
// axis: 'y',
|
||||
edges: {
|
||||
@ -47,15 +48,14 @@ define([
|
||||
bottom: (typeof this.options.resizeHandleSelector === 'string') ? this.view.$(this.options.resizeHandleSelector).get(0) : this.options.resizeHandleSelector
|
||||
}
|
||||
})
|
||||
.on('resizestart', function (event) {
|
||||
.on('resizestart', function () {
|
||||
that.isBeingResized = true;
|
||||
that.$el.addClass('mailpoet_resize_active');
|
||||
})
|
||||
.on('resizemove', function (event) {
|
||||
}).on('resizemove', function (event) {
|
||||
var onResize = that.options.onResize.bind(that);
|
||||
return onResize(event);
|
||||
})
|
||||
.on('resizeend', function (event) {
|
||||
.on('resizeend', function () {
|
||||
that.isBeingResized = null;
|
||||
that.$el.removeClass('mailpoet_resize_active');
|
||||
});
|
||||
|
@ -24,9 +24,9 @@ define([
|
||||
ui.item.removeData('previousIndex');
|
||||
},
|
||||
update: function (event, ui) {
|
||||
var previousIndex = ui.item.data('previousIndex'),
|
||||
newIndex = ui.item.index(),
|
||||
model = collection.at(previousIndex);
|
||||
var previousIndex = ui.item.data('previousIndex');
|
||||
var newIndex = ui.item.index();
|
||||
var model = collection.at(previousIndex);
|
||||
|
||||
// Replicate DOM changes. Move target model to a new position
|
||||
// within the collection
|
||||
|
@ -42,7 +42,7 @@ define([
|
||||
relative_urls: false,
|
||||
remove_script_host: false,
|
||||
convert_urls: true,
|
||||
urlconverter_callback: function (url, node, on_save, name) {
|
||||
urlconverter_callback: function (url) {
|
||||
if (url.match(/\[.+\]/g)) {
|
||||
// Do not convert URLs with shortcodes
|
||||
return url;
|
||||
@ -57,7 +57,7 @@ define([
|
||||
plugins: this.options.plugins,
|
||||
|
||||
setup: function (editor) {
|
||||
editor.on('change', function (e) {
|
||||
editor.on('change', function () {
|
||||
that.view.triggerMethod('text:editor:change', editor.getContent());
|
||||
});
|
||||
|
||||
@ -71,12 +71,12 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
editor.on('focus', function (e) {
|
||||
editor.on('focus', function () {
|
||||
that.view.triggerMethod('text:editor:focus');
|
||||
that._isActivationClick = true;
|
||||
});
|
||||
|
||||
editor.on('blur', function (e) {
|
||||
editor.on('blur', function () {
|
||||
that.view.triggerMethod('text:editor:blur');
|
||||
});
|
||||
}
|
||||
|
@ -30,8 +30,8 @@ define([
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.ALCSupervisor = SuperModel.extend({
|
||||
initialize: function () {
|
||||
@ -43,12 +43,13 @@ define([
|
||||
);
|
||||
},
|
||||
refresh: function () {
|
||||
var blocks;
|
||||
var models = App.findModels(function (model) {
|
||||
return model.get('type') === 'automatedLatestContent';
|
||||
}) || [];
|
||||
|
||||
if (models.length === 0) return;
|
||||
var blocks = _.map(models, function (model) {
|
||||
blocks = _.map(models, function (model) {
|
||||
return model.toJSON();
|
||||
});
|
||||
|
||||
@ -60,8 +61,8 @@ define([
|
||||
_.each(
|
||||
_.zip(models, renderedBlocks),
|
||||
function (args) {
|
||||
var model = args[0],
|
||||
contents = args[1];
|
||||
var model = args[0];
|
||||
var contents = args[1];
|
||||
model.trigger('refreshPosts', contents);
|
||||
}
|
||||
);
|
||||
@ -150,12 +151,12 @@ define([
|
||||
}),
|
||||
onDragSubstituteBy: function () { return Module.AutomatedLatestContentWidgetView; },
|
||||
onRender: function () {
|
||||
var ContainerView = App.getBlockTypeView('container'),
|
||||
renderOptions = {
|
||||
disableTextEditor: true,
|
||||
disableDragAndDrop: true,
|
||||
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay')
|
||||
};
|
||||
var ContainerView = App.getBlockTypeView('container');
|
||||
var renderOptions = {
|
||||
disableTextEditor: true,
|
||||
disableDragAndDrop: true,
|
||||
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay')
|
||||
};
|
||||
this.toolsView = new Module.AutomatedLatestContentBlockToolsView({ model: this.model });
|
||||
this.showChildView('toolsRegion', this.toolsView);
|
||||
this.showChildView('postsRegion', new ContainerView({ model: this.model.get('_container'), renderOptions: renderOptions }));
|
||||
@ -213,12 +214,13 @@ define([
|
||||
},
|
||||
transport: function (options, success, failure) {
|
||||
var taxonomies;
|
||||
var termsPromise;
|
||||
var promise = CommunicationComponent.getTaxonomies(
|
||||
that.model.get('contentType')
|
||||
).then(function (tax) {
|
||||
taxonomies = tax;
|
||||
// Fetch available terms based on the list of taxonomies already fetched
|
||||
var promise = CommunicationComponent.getTerms({
|
||||
termsPromise = CommunicationComponent.getTerms({
|
||||
search: options.data.term,
|
||||
taxonomies: _.keys(taxonomies)
|
||||
}).then(function (terms) {
|
||||
@ -227,7 +229,7 @@ define([
|
||||
terms: terms
|
||||
};
|
||||
});
|
||||
return promise;
|
||||
return termsPromise;
|
||||
});
|
||||
|
||||
promise.then(success);
|
||||
@ -264,9 +266,9 @@ define([
|
||||
}
|
||||
}).trigger('change');
|
||||
},
|
||||
toggleDisplayOptions: function (event) {
|
||||
var el = this.$('.mailpoet_automated_latest_content_display_options'),
|
||||
showControl = this.$('.mailpoet_automated_latest_content_show_display_options');
|
||||
toggleDisplayOptions: function () {
|
||||
var el = this.$('.mailpoet_automated_latest_content_display_options');
|
||||
var showControl = this.$('.mailpoet_automated_latest_content_show_display_options');
|
||||
if (el.hasClass('mailpoet_closed')) {
|
||||
el.removeClass('mailpoet_closed');
|
||||
showControl.addClass('mailpoet_hidden');
|
||||
@ -275,7 +277,7 @@ define([
|
||||
showControl.removeClass('mailpoet_hidden');
|
||||
}
|
||||
},
|
||||
showButtonSettings: function (event) {
|
||||
showButtonSettings: function () {
|
||||
var buttonModule = ButtonBlock;
|
||||
(new buttonModule.ButtonBlockSettingsView({
|
||||
model: this.model.get('readMoreButton'),
|
||||
@ -286,7 +288,7 @@ define([
|
||||
}
|
||||
})).render();
|
||||
},
|
||||
showDividerSettings: function (event) {
|
||||
showDividerSettings: function () {
|
||||
var dividerModule = DividerBlock;
|
||||
(new dividerModule.DividerBlockSettingsView({
|
||||
model: this.model.get('divider'),
|
||||
@ -349,8 +351,8 @@ define([
|
||||
this.changeField('titleFormat', event);
|
||||
},
|
||||
_updateContentTypes: function (postTypes) {
|
||||
var select = this.$('.mailpoet_automated_latest_content_content_type'),
|
||||
selectedValue = this.model.get('contentType');
|
||||
var select = this.$('.mailpoet_automated_latest_content_content_type');
|
||||
var selectedValue = this.model.get('contentType');
|
||||
|
||||
select.find('option').remove();
|
||||
_.each(postTypes, function (type) {
|
||||
@ -378,7 +380,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('automatedLatestContent', {
|
||||
blockModel: Module.AutomatedLatestContentBlockModel,
|
||||
blockView: Module.AutomatedLatestContentBlockView
|
||||
@ -391,7 +393,7 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
App.on('start', function (App, options) {
|
||||
App.on('start', function (App) {
|
||||
var Application = App;
|
||||
Application._ALCSupervisor = new Module.ALCSupervisor();
|
||||
Application._ALCSupervisor.refresh();
|
||||
|
@ -12,17 +12,16 @@ define([
|
||||
'jquery',
|
||||
'mailpoet',
|
||||
'modal'
|
||||
], function (App, Marionette, SuperModel, _, jQuery, MailPoet, Modal) {
|
||||
], function (App, Marionette, SuperModel, _, jQuery, MailPoet) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
AugmentedView = Marionette.View.extend({});
|
||||
var Module = {};
|
||||
var AugmentedView = Marionette.View.extend({});
|
||||
|
||||
Module.BlockModel = SuperModel.extend({
|
||||
stale: [], // Attributes to be removed upon saving
|
||||
initialize: function () {
|
||||
var that = this;
|
||||
this.on('change', function () {
|
||||
App.getChannel().trigger('autoSave');
|
||||
});
|
||||
@ -68,7 +67,8 @@ define([
|
||||
options.dragBehavior.view.model.destroy();
|
||||
},
|
||||
onDragSubstituteBy: function (behavior) {
|
||||
var WidgetView, node;
|
||||
var WidgetView;
|
||||
var node;
|
||||
// When block is being dragged, display the widget icon instead.
|
||||
// This will create an instance of block's widget view and
|
||||
// use it's rendered DOM element instead of the content block
|
||||
@ -98,13 +98,13 @@ define([
|
||||
this.on('dom:refresh', this.showBlock, this);
|
||||
this._isFirstRender = true;
|
||||
},
|
||||
showTools: function (_event) {
|
||||
showTools: function () {
|
||||
if (!this.showingToolsDisabled) {
|
||||
this.$('> .mailpoet_tools').addClass('mailpoet_display_tools');
|
||||
this.toolsView.triggerMethod('showTools');
|
||||
}
|
||||
},
|
||||
hideTools: function (e) {
|
||||
hideTools: function () {
|
||||
this.$('> .mailpoet_tools').removeClass('mailpoet_display_tools');
|
||||
this.toolsView.triggerMethod('hideTools');
|
||||
},
|
||||
@ -240,8 +240,9 @@ define([
|
||||
ColorPickerBehavior: {}
|
||||
},
|
||||
initialize: function (params) {
|
||||
var panelParams;
|
||||
this.model.trigger('startEditing');
|
||||
var panelParams = {
|
||||
panelParams = {
|
||||
element: this.$el,
|
||||
template: '',
|
||||
position: 'right',
|
||||
@ -262,7 +263,7 @@ define([
|
||||
model: this.model.toJSON()
|
||||
};
|
||||
},
|
||||
close: function (event) {
|
||||
close: function () {
|
||||
this.destroy();
|
||||
},
|
||||
changeField: function (field, event) {
|
||||
|
@ -11,8 +11,8 @@ define([
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.ButtonBlockModel = base.BlockModel.extend({
|
||||
defaults: function () {
|
||||
@ -132,7 +132,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('button', {
|
||||
blockModel: Module.ButtonBlockModel,
|
||||
blockView: Module.ButtonBlockView
|
||||
|
@ -14,9 +14,9 @@ define([
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock,
|
||||
BlockCollection;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
var BlockCollection;
|
||||
|
||||
BlockCollection = Backbone.Collection.extend({
|
||||
model: base.BlockModel,
|
||||
@ -24,7 +24,6 @@ define([
|
||||
this.on('add change remove', function () { App.getChannel().trigger('autoSave'); });
|
||||
},
|
||||
parse: function (response) {
|
||||
var self = this;
|
||||
return _.map(response, function (block) {
|
||||
var Type = App.getBlockTypeModel(block.type);
|
||||
// TODO: If type has no registered model, use a backup one
|
||||
@ -66,7 +65,7 @@ define([
|
||||
return response;
|
||||
},
|
||||
getChildren: function () {
|
||||
var models = this.get('blocks').map(function (model, index, list) {
|
||||
var models = this.get('blocks').map(function (model) {
|
||||
return [model, model.getChildren()];
|
||||
});
|
||||
|
||||
@ -121,7 +120,8 @@ define([
|
||||
options.dragBehavior.view.model.destroy();
|
||||
},
|
||||
onDragSubstituteBy: function (behavior) {
|
||||
var WidgetView, node;
|
||||
var WidgetView;
|
||||
var node;
|
||||
// When block is being dragged, display the widget icon instead.
|
||||
// This will create an instance of block's widget view and
|
||||
// use it's rendered DOM element instead of the content block
|
||||
@ -190,24 +190,24 @@ define([
|
||||
}
|
||||
},
|
||||
toggleEditingLayer: function (event) {
|
||||
var that = this,
|
||||
$toggleButton = this.$('> .mailpoet_tools .mailpoet_newsletter_layer_selector'),
|
||||
$overlay = jQuery('.mailpoet_layer_overlay'),
|
||||
$container = this.$('> .mailpoet_container'),
|
||||
enableContainerLayer = function () {
|
||||
that.$el.addClass('mailpoet_container_layer_active');
|
||||
$toggleButton.addClass('mailpoet_container_layer_active');
|
||||
$container.addClass('mailpoet_layer_highlight');
|
||||
$overlay.click(disableContainerLayer);
|
||||
$overlay.show();
|
||||
},
|
||||
disableContainerLayer = function () {
|
||||
that.$el.removeClass('mailpoet_container_layer_active');
|
||||
$toggleButton.removeClass('mailpoet_container_layer_active');
|
||||
$container.removeClass('mailpoet_layer_highlight');
|
||||
$overlay.hide();
|
||||
$overlay.off('click');
|
||||
};
|
||||
var that = this;
|
||||
var $toggleButton = this.$('> .mailpoet_tools .mailpoet_newsletter_layer_selector');
|
||||
var $overlay = jQuery('.mailpoet_layer_overlay');
|
||||
var $container = this.$('> .mailpoet_container');
|
||||
var enableContainerLayer = function () {
|
||||
that.$el.addClass('mailpoet_container_layer_active');
|
||||
$toggleButton.addClass('mailpoet_container_layer_active');
|
||||
$container.addClass('mailpoet_layer_highlight');
|
||||
$overlay.click(disableContainerLayer);
|
||||
$overlay.show();
|
||||
};
|
||||
var disableContainerLayer = function () {
|
||||
that.$el.removeClass('mailpoet_container_layer_active');
|
||||
$toggleButton.removeClass('mailpoet_container_layer_active');
|
||||
$container.removeClass('mailpoet_layer_highlight');
|
||||
$overlay.hide();
|
||||
$overlay.off('click');
|
||||
};
|
||||
if ($toggleButton.hasClass('mailpoet_container_layer_active')) {
|
||||
disableContainerLayer();
|
||||
} else {
|
||||
@ -336,7 +336,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('container', {
|
||||
blockModel: Module.ContainerBlockModel,
|
||||
blockView: Module.ContainerBlockView
|
||||
|
@ -5,14 +5,13 @@ define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'mailpoet'
|
||||
], function (App, BaseBlock, _, jQuery, MailPoet) {
|
||||
'jquery'
|
||||
], function (App, BaseBlock, _, jQuery) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.DividerBlockModel = base.BlockModel.extend({
|
||||
defaults: function () {
|
||||
@ -49,8 +48,8 @@ define([
|
||||
}, base.BlockView.prototype.behaviors),
|
||||
onDragSubstituteBy: function () { return Module.DividerWidgetView; },
|
||||
initialize: function () {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
var that = this;
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
|
||||
// Listen for attempts to change all dividers in one go
|
||||
this._replaceDividerHandler = function (data) { that.model.set(data); that.model.trigger('applyToAll'); };
|
||||
@ -119,7 +118,7 @@ define([
|
||||
repaintDividerStyleOptions: function () {
|
||||
this.$('.mailpoet_field_divider_style > div').css('border-top-color', this.model.get('styles.block.borderColor'));
|
||||
},
|
||||
applyToAll: function (event) {
|
||||
applyToAll: function () {
|
||||
App.getChannel().trigger('replaceAllDividers', this.model.toJSON());
|
||||
},
|
||||
updateValueAndCall: function (fieldToUpdate, callable, event) {
|
||||
@ -139,7 +138,7 @@ define([
|
||||
}
|
||||
}
|
||||
});
|
||||
App.on('before:start', function (App, options) {
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('divider', {
|
||||
blockModel: Module.DividerBlockModel,
|
||||
blockView: Module.DividerBlockView
|
||||
|
@ -10,8 +10,8 @@ define([
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.FooterBlockModel = base.BlockModel.extend({
|
||||
defaults: function () {
|
||||
@ -110,7 +110,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('footer', {
|
||||
blockModel: Module.FooterBlockModel,
|
||||
blockView: Module.FooterBlockView
|
||||
|
@ -10,8 +10,8 @@ define([
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.HeaderBlockModel = base.BlockModel.extend({
|
||||
defaults: function () {
|
||||
@ -110,7 +110,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('header', {
|
||||
blockModel: Module.HeaderBlockModel,
|
||||
blockView: Module.HeaderBlockView
|
||||
|
@ -11,9 +11,9 @@ define([
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock,
|
||||
ImageWidgetView;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
var ImageWidgetView;
|
||||
|
||||
Module.ImageBlockModel = base.BlockModel.extend({
|
||||
defaults: function () {
|
||||
@ -48,8 +48,8 @@ define([
|
||||
elementSelector: '.mailpoet_image',
|
||||
resizeHandleSelector: '.mailpoet_image_resize_handle',
|
||||
onResize: function (event) {
|
||||
var corner = this.$('.mailpoet_image').offset(),
|
||||
width = event.pageX - corner.left;
|
||||
var corner = this.$('.mailpoet_image').offset();
|
||||
var width = event.pageX - corner.left;
|
||||
this.view.model.set('width', width + 'px');
|
||||
}
|
||||
},
|
||||
@ -127,13 +127,16 @@ define([
|
||||
}
|
||||
},
|
||||
showMediaManager: function () {
|
||||
var that = this;
|
||||
var MediaManager;
|
||||
var theFrame;
|
||||
if (this._mediaManager) {
|
||||
this._mediaManager.resetSelections();
|
||||
this._mediaManager.open();
|
||||
return;
|
||||
}
|
||||
|
||||
var MediaManager = window.wp.media.view.MediaFrame.Select.extend({
|
||||
MediaManager = window.wp.media.view.MediaFrame.Select.extend({
|
||||
|
||||
initialize: function () {
|
||||
window.wp.media.view.MediaFrame.prototype.initialize.apply(this, arguments);
|
||||
@ -196,6 +199,7 @@ define([
|
||||
},
|
||||
|
||||
bindHandlers: function () {
|
||||
var handlers;
|
||||
// from Select
|
||||
this.on('router:create:browse', this.createRouter, this);
|
||||
this.on('router:render:browse', this.browseRouter, this);
|
||||
@ -210,7 +214,7 @@ define([
|
||||
|
||||
this.on('updateExcluded', this.browseContent, this);
|
||||
|
||||
var handlers = {
|
||||
handlers = {
|
||||
content: {
|
||||
embed: 'embedContent',
|
||||
'edit-selection': 'editSelectionContent'
|
||||
@ -244,9 +248,9 @@ define([
|
||||
},
|
||||
|
||||
editSelectionContent: function () {
|
||||
var state = this.state(),
|
||||
selection = state.get('selection'),
|
||||
view;
|
||||
var state = this.state();
|
||||
var selection = state.get('selection');
|
||||
var view;
|
||||
|
||||
view = new window.wp.media.view.AttachmentsBrowser({
|
||||
controller: this,
|
||||
@ -302,8 +306,8 @@ define([
|
||||
requires: { selection: true },
|
||||
|
||||
click: function () {
|
||||
var state = controller.state(),
|
||||
selection = state.get('selection');
|
||||
var state = controller.state();
|
||||
var selection = state.get('selection');
|
||||
|
||||
controller.close();
|
||||
state.trigger('insert', selection).reset();
|
||||
@ -321,46 +325,42 @@ define([
|
||||
|
||||
});
|
||||
|
||||
var theFrame = new MediaManager({
|
||||
id: 'mailpoet-media-manager',
|
||||
frame: 'select',
|
||||
title: 'Select image',
|
||||
editing: false,
|
||||
multiple: false,
|
||||
library: {
|
||||
type: 'image'
|
||||
},
|
||||
displaySettings: false,
|
||||
button: {
|
||||
text: 'Select'
|
||||
}
|
||||
}),
|
||||
that = this;
|
||||
theFrame = new MediaManager({
|
||||
id: 'mailpoet-media-manager',
|
||||
frame: 'select',
|
||||
title: 'Select image',
|
||||
editing: false,
|
||||
multiple: false,
|
||||
library: {
|
||||
type: 'image'
|
||||
},
|
||||
displaySettings: false,
|
||||
button: {
|
||||
text: 'Select'
|
||||
}
|
||||
});
|
||||
this._mediaManager = theFrame;
|
||||
|
||||
this._mediaManager.on('insert', function () {
|
||||
// Append media manager image selections to Images tab
|
||||
var selection = theFrame.state().get('selection');
|
||||
selection.each(function (attachment) {
|
||||
var sizes = attachment.get('sizes'),
|
||||
// Following advice from Becs, the target width should
|
||||
// be a double of one column width to render well on
|
||||
// retina screen devices
|
||||
targetImageWidth = 1320,
|
||||
var sizes = attachment.get('sizes');
|
||||
// Following advice from Becs, the target width should
|
||||
// be a double of one column width to render well on
|
||||
// retina screen devices
|
||||
var targetImageWidth = 1320;
|
||||
|
||||
// For main image use the size, that's closest to being 660px in width
|
||||
sizeKeys = _.keys(sizes),
|
||||
// Pick the width that is closest to target width
|
||||
var increasingByWidthDifference = _.sortBy(
|
||||
_.keys(sizes),
|
||||
function (size) { return Math.abs(targetImageWidth - sizes[size].width); }
|
||||
);
|
||||
var bestWidth = sizes[_.first(increasingByWidthDifference)].width;
|
||||
var imagesOfBestWidth = _.filter(_.values(sizes), function (size) { return size.width === bestWidth; });
|
||||
|
||||
// Pick the width that is closest to target width
|
||||
increasingByWidthDifference = _.sortBy(
|
||||
_.keys(sizes),
|
||||
function (size) { return Math.abs(targetImageWidth - sizes[size].width); }
|
||||
),
|
||||
bestWidth = sizes[_.first(increasingByWidthDifference)].width,
|
||||
imagesOfBestWidth = _.filter(_.values(sizes), function (size) { return size.width === bestWidth; }),
|
||||
|
||||
// Maximize the height if there are multiple images with same width
|
||||
mainSize = _.max(imagesOfBestWidth, function (size) { return size.height; });
|
||||
// Maximize the height if there are multiple images with same width
|
||||
var mainSize = _.max(imagesOfBestWidth, function (size) { return size.height; });
|
||||
|
||||
that.model.set({
|
||||
height: mainSize.height + 'px',
|
||||
@ -413,7 +413,7 @@ define([
|
||||
});
|
||||
Module.ImageWidgetView = ImageWidgetView;
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('image', {
|
||||
blockModel: Module.ImageBlockModel,
|
||||
blockView: Module.ImageBlockView
|
||||
|
@ -39,8 +39,13 @@ define([
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
var PostsDisplayOptionsSettingsView;
|
||||
var SinglePostSelectionSettingsView;
|
||||
var EmptyPostSelectionSettingsView;
|
||||
var PostSelectionSettingsView;
|
||||
var PostsSelectionCollectionView;
|
||||
|
||||
Module.PostsBlockModel = base.BlockModel.extend({
|
||||
stale: ['_selectedPosts', '_availablePosts', '_transformedPosts'],
|
||||
@ -89,10 +94,9 @@ define([
|
||||
};
|
||||
},
|
||||
initialize: function () {
|
||||
var that = this,
|
||||
POST_REFRESH_DELAY_MS = 500,
|
||||
refreshAvailablePosts = _.debounce(this.fetchAvailablePosts.bind(this), POST_REFRESH_DELAY_MS),
|
||||
refreshTransformedPosts = _.debounce(this._refreshTransformedPosts.bind(this), POST_REFRESH_DELAY_MS);
|
||||
var POST_REFRESH_DELAY_MS = 500;
|
||||
var refreshAvailablePosts = _.debounce(this.fetchAvailablePosts.bind(this), POST_REFRESH_DELAY_MS);
|
||||
var refreshTransformedPosts = _.debounce(this._refreshTransformedPosts.bind(this), POST_REFRESH_DELAY_MS);
|
||||
|
||||
// Attach Radio.Requests API primarily for highlighting
|
||||
_.extend(this, Radio.Requests);
|
||||
@ -120,9 +124,9 @@ define([
|
||||
});
|
||||
},
|
||||
_loadMorePosts: function () {
|
||||
var that = this,
|
||||
postCount = this.get('_availablePosts').length,
|
||||
nextOffset = this.get('offset') + Number(this.get('amount'));
|
||||
var that = this;
|
||||
var postCount = this.get('_availablePosts').length;
|
||||
var nextOffset = this.get('offset') + Number(this.get('amount'));
|
||||
|
||||
if (postCount === 0 || postCount < nextOffset) {
|
||||
// No more posts to load
|
||||
@ -141,8 +145,8 @@ define([
|
||||
});
|
||||
},
|
||||
_refreshTransformedPosts: function () {
|
||||
var that = this,
|
||||
data = this.toJSON();
|
||||
var that = this;
|
||||
var data = this.toJSON();
|
||||
|
||||
data.posts = this.get('_selectedPosts').pluck('ID');
|
||||
|
||||
@ -158,10 +162,9 @@ define([
|
||||
});
|
||||
},
|
||||
_insertSelectedPosts: function () {
|
||||
var that = this,
|
||||
data = this.toJSON(),
|
||||
index = this.collection.indexOf(this),
|
||||
collection = this.collection;
|
||||
var data = this.toJSON();
|
||||
var index = this.collection.indexOf(this);
|
||||
var collection = this.collection;
|
||||
|
||||
data.posts = this.get('_selectedPosts').pluck('ID');
|
||||
|
||||
@ -190,17 +193,19 @@ define([
|
||||
this.model.reply('blockView', this.notifyAboutSelf, this);
|
||||
},
|
||||
onRender: function () {
|
||||
var ContainerView;
|
||||
var renderOptions;
|
||||
if (!this.getRegion('toolsRegion').hasView()) {
|
||||
this.showChildView('toolsRegion', this.toolsView);
|
||||
}
|
||||
this.trigger('showSettings');
|
||||
|
||||
var ContainerView = App.getBlockTypeView('container'),
|
||||
renderOptions = {
|
||||
disableTextEditor: true,
|
||||
disableDragAndDrop: true,
|
||||
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay')
|
||||
};
|
||||
ContainerView = App.getBlockTypeView('container');
|
||||
renderOptions = {
|
||||
disableTextEditor: true,
|
||||
disableDragAndDrop: true,
|
||||
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay')
|
||||
};
|
||||
this.showChildView('postsRegion', new ContainerView({ model: this.model.get('_transformedPosts'), renderOptions: renderOptions }));
|
||||
},
|
||||
notifyAboutSelf: function () {
|
||||
@ -237,8 +242,8 @@ define([
|
||||
this.displayOptionsView = new PostsDisplayOptionsSettingsView({ model: this.model });
|
||||
},
|
||||
onRender: function () {
|
||||
var that = this,
|
||||
blockView = this.model.request('blockView');
|
||||
var that = this;
|
||||
this.model.request('blockView');
|
||||
|
||||
this.showChildView('selectionRegion', this.selectionView);
|
||||
this.showChildView('displayOptionsRegion', this.displayOptionsView);
|
||||
@ -283,7 +288,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
var PostsSelectionCollectionView = Marionette.CollectionView.extend({
|
||||
PostsSelectionCollectionView = Marionette.CollectionView.extend({
|
||||
className: 'mailpoet_post_scroll_container',
|
||||
childView: function () { return SinglePostSelectionSettingsView; },
|
||||
emptyView: function () { return EmptyPostSelectionSettingsView; },
|
||||
@ -307,7 +312,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
var PostSelectionSettingsView = Marionette.View.extend({
|
||||
PostSelectionSettingsView = Marionette.View.extend({
|
||||
getTemplate: function () { return window.templates.postSelectionPostsBlockSettings; },
|
||||
regions: {
|
||||
posts: '.mailpoet_post_selection_container'
|
||||
@ -334,9 +339,10 @@ define([
|
||||
}
|
||||
},
|
||||
onRender: function () {
|
||||
var postsView;
|
||||
// Dynamically update available post types
|
||||
CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
|
||||
var postsView = new PostsSelectionCollectionView({
|
||||
postsView = new PostsSelectionCollectionView({
|
||||
collection: this.model.get('_availablePosts'),
|
||||
blockModel: this.model
|
||||
});
|
||||
@ -358,12 +364,13 @@ define([
|
||||
},
|
||||
transport: function (options, success, failure) {
|
||||
var taxonomies;
|
||||
var termsPromise;
|
||||
var promise = CommunicationComponent.getTaxonomies(
|
||||
that.model.get('contentType')
|
||||
).then(function (tax) {
|
||||
taxonomies = tax;
|
||||
// Fetch available terms based on the list of taxonomies already fetched
|
||||
var promise = CommunicationComponent.getTerms({
|
||||
termsPromise = CommunicationComponent.getTerms({
|
||||
search: options.data.term,
|
||||
taxonomies: _.keys(taxonomies)
|
||||
}).then(function (terms) {
|
||||
@ -372,7 +379,7 @@ define([
|
||||
terms: terms
|
||||
};
|
||||
});
|
||||
return promise;
|
||||
return termsPromise;
|
||||
});
|
||||
|
||||
promise.then(success);
|
||||
@ -413,8 +420,8 @@ define([
|
||||
this.model.set(field, jQuery(event.target).val());
|
||||
},
|
||||
_updateContentTypes: function (postTypes) {
|
||||
var select = this.$('.mailpoet_settings_posts_content_type'),
|
||||
selectedValue = this.model.get('contentType');
|
||||
var select = this.$('.mailpoet_settings_posts_content_type');
|
||||
var selectedValue = this.model.get('contentType');
|
||||
|
||||
select.find('option').remove();
|
||||
_.each(postTypes, function (type) {
|
||||
@ -427,11 +434,11 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
var EmptyPostSelectionSettingsView = Marionette.View.extend({
|
||||
EmptyPostSelectionSettingsView = Marionette.View.extend({
|
||||
getTemplate: function () { return window.templates.emptyPostPostsBlockSettings; }
|
||||
});
|
||||
|
||||
var SinglePostSelectionSettingsView = Marionette.View.extend({
|
||||
SinglePostSelectionSettingsView = Marionette.View.extend({
|
||||
getTemplate: function () { return window.templates.singlePostPostsBlockSettings; },
|
||||
events: function () {
|
||||
return {
|
||||
@ -448,8 +455,8 @@ define([
|
||||
this.blockModel = options.blockModel;
|
||||
},
|
||||
postSelectionChange: function (event) {
|
||||
var checkBox = jQuery(event.target),
|
||||
selectedPostsCollection = this.blockModel.get('_selectedPosts');
|
||||
var checkBox = jQuery(event.target);
|
||||
var selectedPostsCollection = this.blockModel.get('_selectedPosts');
|
||||
if (checkBox.prop('checked')) {
|
||||
selectedPostsCollection.add(this.model);
|
||||
} else {
|
||||
@ -458,7 +465,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
var PostsDisplayOptionsSettingsView = base.BlockSettingsView.extend({
|
||||
PostsDisplayOptionsSettingsView = base.BlockSettingsView.extend({
|
||||
getTemplate: function () { return window.templates.displayOptionsPostsBlockSettings; },
|
||||
events: function () {
|
||||
return {
|
||||
@ -488,7 +495,7 @@ define([
|
||||
model: this.model.toJSON()
|
||||
};
|
||||
},
|
||||
showButtonSettings: function (event) {
|
||||
showButtonSettings: function () {
|
||||
var buttonModule = ButtonBlock;
|
||||
(new buttonModule.ButtonBlockSettingsView({
|
||||
model: this.model.get('readMoreButton'),
|
||||
@ -499,7 +506,7 @@ define([
|
||||
}
|
||||
})).render();
|
||||
},
|
||||
showDividerSettings: function (event) {
|
||||
showDividerSettings: function () {
|
||||
var dividerModule = DividerBlock;
|
||||
(new dividerModule.DividerBlockSettingsView({
|
||||
model: this.model.get('divider'),
|
||||
@ -575,7 +582,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('posts', {
|
||||
blockModel: Module.PostsBlockModel,
|
||||
blockView: Module.PostsBlockView
|
||||
|
@ -13,12 +13,13 @@ define([
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock,
|
||||
SocialBlockSettingsIconSelectorView,
|
||||
SocialBlockSettingsIconView,
|
||||
SocialBlockSettingsIconCollectionView,
|
||||
SocialBlockSettingsStylesView;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
var SocialBlockSettingsIconSelectorView;
|
||||
var SocialBlockSettingsIconView;
|
||||
var SocialBlockSettingsIconCollectionView;
|
||||
var SocialBlockSettingsStylesView;
|
||||
var SocialIconView;
|
||||
|
||||
Module.SocialIconModel = SuperModel.extend({
|
||||
defaults: function () {
|
||||
@ -33,12 +34,12 @@ define([
|
||||
text: defaultValues.get('title')
|
||||
};
|
||||
},
|
||||
initialize: function (options) {
|
||||
initialize: function () {
|
||||
var that = this;
|
||||
// Make model swap to default values for that type when iconType changes
|
||||
this.on('change:iconType', function () {
|
||||
var defaultValues = App.getConfig().get('socialIcons').get(that.get('iconType')),
|
||||
iconSet = that.collection.iconBlockModel.getIconSet();
|
||||
var defaultValues = App.getConfig().get('socialIcons').get(that.get('iconType'));
|
||||
var iconSet = that.collection.iconBlockModel.getIconSet();
|
||||
this.set({
|
||||
link: defaultValues.get('defaultLink'),
|
||||
image: iconSet.get(that.get('iconType')),
|
||||
@ -83,7 +84,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
var SocialIconView = Marionette.View.extend({
|
||||
SocialIconView = Marionette.View.extend({
|
||||
tagName: 'span',
|
||||
getTemplate: function () { return window.templates.socialIconBlock; },
|
||||
modelEvents: {
|
||||
@ -175,10 +176,10 @@ define([
|
||||
}
|
||||
},
|
||||
templateContext: function () {
|
||||
var icons = App.getConfig().get('socialIcons'),
|
||||
// Construct icon type list of format [{iconType: 'type', title: 'Title'}, ...]
|
||||
availableIconTypes = _.map(_.keys(icons.attributes), function (key) { return { iconType: key, title: icons.get(key).get('title') }; }),
|
||||
allIconSets = App.getAvailableStyles().get('socialIconSets');
|
||||
var icons = App.getConfig().get('socialIcons');
|
||||
// Construct icon type list of format [{iconType: 'type', title: 'Title'}, ...]
|
||||
var availableIconTypes = _.map(_.keys(icons.attributes), function (key) { return { iconType: key, title: icons.get(key).get('title') }; });
|
||||
var allIconSets = App.getAvailableStyles().get('socialIconSets');
|
||||
return _.extend({}, base.BlockView.prototype.templateContext.apply(this, arguments), {
|
||||
iconTypes: availableIconTypes,
|
||||
currentType: icons.get(this.model.get('iconType')).toJSON(),
|
||||
@ -297,7 +298,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('social', {
|
||||
blockModel: Module.SocialBlockModel,
|
||||
blockView: Module.SocialBlockView
|
||||
|
@ -9,8 +9,8 @@ define([
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.SpacerBlockModel = base.BlockModel.extend({
|
||||
defaults: function () {
|
||||
@ -87,7 +87,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('spacer', {
|
||||
blockModel: Module.SpacerBlockModel,
|
||||
blockView: Module.SpacerBlockView
|
||||
|
@ -10,8 +10,8 @@ define([
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
base = BaseBlock;
|
||||
var Module = {};
|
||||
var base = BaseBlock;
|
||||
|
||||
Module.TextBlockModel = base.BlockModel.extend({
|
||||
defaults: function () {
|
||||
@ -94,7 +94,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('text', {
|
||||
blockModel: Module.TextBlockModel,
|
||||
blockView: Module.TextBlockView
|
||||
|
@ -7,15 +7,15 @@
|
||||
*/
|
||||
|
||||
(function (root, factory) {
|
||||
var Marionette = require('backbone.marionette');
|
||||
var Radio = require('backbone.radio');
|
||||
var _ = require('underscore');
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define(['backbone.marionette', 'backbone.radio', 'underscore'], function (Marionette, Radio, _) {
|
||||
return factory(Marionette, Radio, _);
|
||||
});
|
||||
}
|
||||
else if (typeof exports !== 'undefined') {
|
||||
var Marionette = require('backbone.marionette');
|
||||
var Radio = require('backbone.radio');
|
||||
var _ = require('underscore');
|
||||
module.exports = factory(Marionette, Radio, _);
|
||||
}
|
||||
else {
|
||||
|
@ -98,7 +98,7 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
App.on('start', function (App, options) {
|
||||
App.on('start', function () {
|
||||
// Prefetch post types
|
||||
Module.getPostTypes();
|
||||
});
|
||||
|
@ -13,7 +13,7 @@ define([
|
||||
// handled by other components.
|
||||
Module.NewsletterModel = SuperModel.extend({
|
||||
whitelisted: ['id', 'subject', 'preheader'],
|
||||
initialize: function (options) {
|
||||
initialize: function () {
|
||||
this.on('change', function () {
|
||||
App.getChannel().trigger('autoSave');
|
||||
});
|
||||
|
@ -29,7 +29,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('start', function (App, options) {
|
||||
App.on('start', function (App) {
|
||||
App._appView.showChildView('headingRegion', new Module.HeadingView({ model: App.getNewsletter() }));
|
||||
MailPoet.helpTooltip.show(document.getElementById('tooltip-designer-subject-line'), {
|
||||
tooltipId: 'tooltip-designer-subject-line-ti',
|
||||
|
@ -28,8 +28,8 @@ define([
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {},
|
||||
saveTimeout;
|
||||
var Module = {};
|
||||
var saveTimeout;
|
||||
|
||||
// Save editor contents to server
|
||||
Module.save = function () {
|
||||
@ -76,10 +76,9 @@ define([
|
||||
// Temporary workaround for html2canvas-alpha2.
|
||||
// Removes 1px left transparent border from resulting canvas.
|
||||
|
||||
var oldContext = oldCanvas.getContext('2d'),
|
||||
newCanvas = document.createElement('canvas'),
|
||||
newContext = newCanvas.getContext('2d'),
|
||||
leftBorderWidth = 1;
|
||||
var newCanvas = document.createElement('canvas');
|
||||
var newContext = newCanvas.getContext('2d');
|
||||
var leftBorderWidth = 1;
|
||||
|
||||
newCanvas.width = oldCanvas.width;
|
||||
newCanvas.height = oldCanvas.height;
|
||||
@ -95,8 +94,7 @@ define([
|
||||
};
|
||||
|
||||
Module.saveTemplate = function (options) {
|
||||
var that = this,
|
||||
promise = jQuery.Deferred();
|
||||
var promise = jQuery.Deferred();
|
||||
|
||||
promise.then(function (thumbnail) {
|
||||
var data = _.extend(options || {}, {
|
||||
@ -122,7 +120,6 @@ define([
|
||||
};
|
||||
|
||||
Module.exportTemplate = function (options) {
|
||||
var that = this;
|
||||
return Module.getThumbnail(
|
||||
jQuery('#mailpoet_editor_content > .mailpoet_block').get(0)
|
||||
).then(function (thumbnail) {
|
||||
@ -155,7 +152,7 @@ define([
|
||||
'click .mailpoet_save_export': 'toggleExportTemplate',
|
||||
'click .mailpoet_export_template': 'exportTemplate'
|
||||
},
|
||||
initialize: function (options) {
|
||||
initialize: function () {
|
||||
App.getChannel().on('beforeEditorSave', this.beforeSave, this);
|
||||
App.getChannel().on('afterEditorSave', this.afterSave, this);
|
||||
},
|
||||
@ -170,7 +167,7 @@ define([
|
||||
// TODO: Add a loading animation instead
|
||||
this.$('.mailpoet_autosaved_at').text(MailPoet.I18n.t('saving'));
|
||||
},
|
||||
afterSave: function (json, response) {
|
||||
afterSave: function (json) {
|
||||
this.validateNewsletter(json);
|
||||
// Update 'Last saved timer'
|
||||
this.$('.mailpoet_editor_last_saved').removeClass('mailpoet_hidden');
|
||||
@ -192,9 +189,9 @@ define([
|
||||
this.$('.mailpoet_save_as_template_container').addClass('mailpoet_hidden');
|
||||
},
|
||||
saveAsTemplate: function () {
|
||||
var templateName = this.$('.mailpoet_save_as_template_name').val(),
|
||||
templateDescription = this.$('.mailpoet_save_as_template_description').val(),
|
||||
that = this;
|
||||
var templateName = this.$('.mailpoet_save_as_template_name').val();
|
||||
var templateDescription = this.$('.mailpoet_save_as_template_description').val();
|
||||
var that = this;
|
||||
|
||||
if (templateName === '') {
|
||||
MailPoet.Notice.error(
|
||||
@ -248,9 +245,9 @@ define([
|
||||
this.$('.mailpoet_export_template_container').addClass('mailpoet_hidden');
|
||||
},
|
||||
exportTemplate: function () {
|
||||
var templateName = this.$('.mailpoet_export_template_name').val(),
|
||||
templateDescription = this.$('.mailpoet_export_template_description').val(),
|
||||
that = this;
|
||||
var templateName = this.$('.mailpoet_export_template_name').val();
|
||||
var templateDescription = this.$('.mailpoet_export_template_description').val();
|
||||
var that = this;
|
||||
|
||||
if (templateName === '') {
|
||||
MailPoet.Notice.error(
|
||||
@ -285,18 +282,19 @@ define([
|
||||
this.hideOptionContents();
|
||||
if (!this.$('.mailpoet_save_next').hasClass('button-disabled')) {
|
||||
Module._cancelAutosave();
|
||||
Module.save().done(function (response) {
|
||||
Module.save().done(function () {
|
||||
window.location.href = App.getConfig().get('urls.send');
|
||||
});
|
||||
}
|
||||
},
|
||||
validateNewsletter: function (jsonObject) {
|
||||
var contents;
|
||||
if (!App._contentContainer.isValid()) {
|
||||
this.showValidationError(App._contentContainer.validationError);
|
||||
return;
|
||||
}
|
||||
|
||||
var contents = JSON.stringify(jsonObject);
|
||||
contents = JSON.stringify(jsonObject);
|
||||
if (App.getConfig().get('validation.validateUnsubscribeLinkPresent') &&
|
||||
contents.indexOf('[link:subscription_unsubscribe_url]') < 0 &&
|
||||
contents.indexOf('[link:subscription_unsubscribe]') < 0) {
|
||||
@ -340,9 +338,11 @@ define([
|
||||
};
|
||||
|
||||
Module.beforeExitWithUnsavedChanges = function (e) {
|
||||
var message;
|
||||
var event;
|
||||
if (saveTimeout) {
|
||||
var message = MailPoet.I18n.t('unsavedChangesWillBeLost');
|
||||
var event = e || window.event;
|
||||
message = MailPoet.I18n.t('unsavedChangesWillBeLost');
|
||||
event = e || window.event;
|
||||
|
||||
if (event) {
|
||||
event.returnValue = message;
|
||||
@ -352,7 +352,7 @@ define([
|
||||
}
|
||||
};
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
App.on('before:start', function (App) {
|
||||
var Application = App;
|
||||
Application.save = Module.save;
|
||||
Application.getChannel().on('autoSave', Module.autoSave);
|
||||
@ -362,7 +362,7 @@ define([
|
||||
Application.getChannel().reply('save', Application.save);
|
||||
});
|
||||
|
||||
App.on('start', function (App, options) {
|
||||
App.on('start', function (App) {
|
||||
var saveView = new Module.SaveView();
|
||||
App._appView.showChildView('bottomRegion', saveView);
|
||||
});
|
||||
|
@ -6,8 +6,7 @@ define([
|
||||
'backbone.marionette',
|
||||
'backbone.supermodel',
|
||||
'underscore',
|
||||
'jquery',
|
||||
'sticky-kit'
|
||||
'jquery'
|
||||
], function (
|
||||
App,
|
||||
CommunicationComponent,
|
||||
@ -16,14 +15,13 @@ define([
|
||||
Marionette,
|
||||
SuperModel,
|
||||
_,
|
||||
jQuery,
|
||||
StickyKit
|
||||
jQuery
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
|
||||
var SidebarView;
|
||||
// Widget handlers for use to create new content blocks via drag&drop
|
||||
Module._contentWidgets = new (Backbone.Collection.extend({
|
||||
model: SuperModel.extend({
|
||||
@ -52,7 +50,7 @@ define([
|
||||
Module.registerLayoutWidget = function (widget) { return Module._layoutWidgets.add(widget); };
|
||||
Module.getLayoutWidgets = function () { return Module._layoutWidgets; };
|
||||
|
||||
var SidebarView = Marionette.View.extend({
|
||||
SidebarView = Marionette.View.extend({
|
||||
getTemplate: function () { return window.templates.sidebar; },
|
||||
regions: {
|
||||
contentRegion: '.mailpoet_content_region',
|
||||
@ -62,8 +60,8 @@ define([
|
||||
},
|
||||
events: {
|
||||
'click .mailpoet_sidebar_region h3, .mailpoet_sidebar_region .handlediv': function (event) {
|
||||
var $openRegion = this.$el.find('.mailpoet_sidebar_region:not(.closed)'),
|
||||
$targetRegion = this.$el.find(event.target).closest('.mailpoet_sidebar_region');
|
||||
var $openRegion = this.$el.find('.mailpoet_sidebar_region:not(.closed)');
|
||||
var $targetRegion = this.$el.find(event.target).closest('.mailpoet_sidebar_region');
|
||||
|
||||
$openRegion.find('.mailpoet_region_content').velocity(
|
||||
'slideUp',
|
||||
@ -90,7 +88,7 @@ define([
|
||||
}
|
||||
}
|
||||
},
|
||||
initialize: function (options) {
|
||||
initialize: function () {
|
||||
jQuery(window)
|
||||
.on('resize', this.updateHorizontalScroll.bind(this))
|
||||
.on('scroll', this.updateHorizontalScroll.bind(this));
|
||||
@ -113,9 +111,8 @@ define([
|
||||
// position of the sidebar would be scrollable and not fixed
|
||||
// partially out of visible screen
|
||||
this.$el.parent().each(function () {
|
||||
var calculated_left, self;
|
||||
|
||||
self = jQuery(this);
|
||||
var calculated_left;
|
||||
var self = jQuery(this);
|
||||
|
||||
if (self.css('position') === 'fixed') {
|
||||
calculated_left = self.parent().offset().left - jQuery(window).scrollLeft();
|
||||
@ -271,7 +268,7 @@ define([
|
||||
previewUrl: response.meta.preview_url
|
||||
});
|
||||
|
||||
var view = this.previewView.render();
|
||||
this.previewView.render();
|
||||
this.previewView.$el.css('height', '100%');
|
||||
|
||||
MailPoet.Modal.popup({
|
||||
@ -324,7 +321,7 @@ define([
|
||||
App.getChannel().request('save').always(function () {
|
||||
CommunicationComponent.previewNewsletter(data).always(function () {
|
||||
MailPoet.Modal.loading(false);
|
||||
}).done(function (response) {
|
||||
}).done(function () {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('newsletterPreviewSent'),
|
||||
{ scroll: true }
|
||||
@ -363,7 +360,7 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
App.on('before:start', function (App) {
|
||||
var Application = App;
|
||||
Application.registerWidget = Module.registerWidget;
|
||||
Application.getWidgets = Module.getWidgets;
|
||||
@ -371,9 +368,8 @@ define([
|
||||
Application.getLayoutWidgets = Module.getLayoutWidgets;
|
||||
});
|
||||
|
||||
App.on('start', function (App, options) {
|
||||
var stylesModel = App.getGlobalStyles(),
|
||||
sidebarView = new SidebarView();
|
||||
App.on('start', function (App) {
|
||||
var sidebarView = new SidebarView();
|
||||
|
||||
App._appView.showChildView('sidebarRegion', sidebarView);
|
||||
|
||||
|
@ -71,17 +71,19 @@ define([
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
var Application = App;
|
||||
var body;
|
||||
var globalStyles;
|
||||
// Expose style methods to global application
|
||||
Application.getGlobalStyles = Module.getGlobalStyles;
|
||||
Application.setGlobalStyles = Module.setGlobalStyles;
|
||||
Application.getAvailableStyles = Module.getAvailableStyles;
|
||||
|
||||
var body = options.newsletter.body;
|
||||
var globalStyles = (_.has(body, 'globalStyles')) ? body.globalStyles : {};
|
||||
body = options.newsletter.body;
|
||||
globalStyles = (_.has(body, 'globalStyles')) ? body.globalStyles : {};
|
||||
this.setGlobalStyles(globalStyles);
|
||||
});
|
||||
|
||||
App.on('start', function (App, options) {
|
||||
App.on('start', function (App) {
|
||||
var stylesView = new Module.StylesView({ model: App.getGlobalStyles() });
|
||||
App._appView.showChildView('stylesRegion', stylesView);
|
||||
});
|
||||
|
@ -10,31 +10,33 @@
|
||||
|
||||
/* jshint unused:false */
|
||||
/* global tinymce:true */
|
||||
tinymce.PluginManager.add('mailpoet_shortcodes', function (editor, url) {
|
||||
tinymce.PluginManager.add('mailpoet_shortcodes', function (editor) {
|
||||
var appendLabelAndClose = function (shortcode) {
|
||||
editor.insertContent(shortcode);
|
||||
editor.windowManager.close();
|
||||
},
|
||||
generateOnClickFunc = function (shortcode) {
|
||||
return function () {
|
||||
appendLabelAndClose(shortcode);
|
||||
};
|
||||
editor.insertContent(shortcode);
|
||||
editor.windowManager.close();
|
||||
};
|
||||
var generateOnClickFunc = function (shortcode) {
|
||||
return function () {
|
||||
appendLabelAndClose(shortcode);
|
||||
};
|
||||
};
|
||||
|
||||
editor.addButton('mailpoet_shortcodes', {
|
||||
icon: 'mailpoet_shortcodes',
|
||||
onclick: function () {
|
||||
var shortcodes = [],
|
||||
configShortcodes = editor.settings.mailpoet_shortcodes;
|
||||
var shortcodes = [];
|
||||
var configShortcodes = editor.settings.mailpoet_shortcodes;
|
||||
var segment;
|
||||
var i;
|
||||
|
||||
for (var segment in configShortcodes) {
|
||||
for (segment in configShortcodes) {
|
||||
if (configShortcodes.hasOwnProperty(segment)) {
|
||||
shortcodes.push({
|
||||
type: 'label',
|
||||
text: segment
|
||||
});
|
||||
|
||||
for (var i = 0; i < configShortcodes[segment].length; i += 1) {
|
||||
for (i = 0; i < configShortcodes[segment].length; i += 1) {
|
||||
shortcodes.push({
|
||||
type: 'button',
|
||||
text: configShortcodes[segment][i].text,
|
||||
|
@ -49,7 +49,7 @@ const stats = {
|
||||
};
|
||||
|
||||
class StatsBadge extends React.Component {
|
||||
getBadgeType(stat, rate) {
|
||||
static getBadgeType(stat, rate) {
|
||||
const len = stat.badgeRanges.length;
|
||||
for (let i = 0; i < len; i += 1) {
|
||||
if (rate > stat.badgeRanges[i]) {
|
||||
@ -70,7 +70,7 @@ class StatsBadge extends React.Component {
|
||||
return null;
|
||||
}
|
||||
|
||||
const badgeType = this.getBadgeType(stat, rate);
|
||||
const badgeType = StatsBadge.getBadgeType(stat, rate);
|
||||
const badge = badges[badgeType] || null;
|
||||
if (!badge) {
|
||||
return null;
|
||||
|
@ -59,90 +59,97 @@ const _QueueMixin = {
|
||||
return (
|
||||
<span>{MailPoet.I18n.t('paused')}</span>
|
||||
);
|
||||
} else {
|
||||
if (newsletter.queue.status === 'scheduled') {
|
||||
return (
|
||||
<span>
|
||||
{ MailPoet.I18n.t('scheduledFor') } { MailPoet.Date.format(newsletter.queue.scheduled_at) }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
const progressClasses = classNames(
|
||||
}
|
||||
if (newsletter.queue.status === 'scheduled') {
|
||||
return (
|
||||
<span>
|
||||
{ MailPoet.I18n.t('scheduledFor') } { MailPoet.Date.format(newsletter.queue.scheduled_at) }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
const progressClasses = classNames(
|
||||
'mailpoet_progress',
|
||||
{ mailpoet_progress_complete: newsletter.queue.status === 'completed' }
|
||||
);
|
||||
|
||||
// calculate percentage done
|
||||
let percentage = Math.round(
|
||||
let percentage = Math.round(
|
||||
(newsletter.queue.count_processed * 100) / (newsletter.queue.count_total)
|
||||
);
|
||||
|
||||
let label;
|
||||
let label;
|
||||
|
||||
if (newsletter.queue.status === 'completed') {
|
||||
label = (
|
||||
<span>
|
||||
{
|
||||
if (newsletter.queue.status === 'completed') {
|
||||
label = (
|
||||
<span>
|
||||
{
|
||||
MailPoet.I18n.t('newsletterQueueCompleted')
|
||||
.replace('%$1d', newsletter.queue.count_processed)
|
||||
.replace('%$2d', newsletter.queue.count_total)
|
||||
.replace('%$1d', parseInt(newsletter.queue.count_processed, 10).toLocaleString())
|
||||
.replace('%$2d', parseInt(newsletter.queue.count_total, 10).toLocaleString())
|
||||
}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
label = (
|
||||
<span>
|
||||
{ newsletter.queue.count_processed } / { newsletter.queue.count_total }
|
||||
} else {
|
||||
label = (
|
||||
<span>
|
||||
{ newsletter.queue.count_processed } / { newsletter.queue.count_total }
|
||||
|
||||
<a
|
||||
id={'resume_' + newsletter.id}
|
||||
className="button"
|
||||
style={{ display: (newsletter.queue.status === 'paused')
|
||||
<a
|
||||
id={'resume_' + newsletter.id}
|
||||
className="button"
|
||||
style={{ display: (newsletter.queue.status === 'paused')
|
||||
? 'inline-block' : 'none' }}
|
||||
href="javascript:;"
|
||||
onClick={this.resumeSending.bind(null, newsletter)}
|
||||
href="javascript:;"
|
||||
onClick={this.resumeSending.bind(null, newsletter)}
|
||||
>{MailPoet.I18n.t('resume')}</a>
|
||||
<a
|
||||
id={'pause_' + newsletter.id}
|
||||
className="button mailpoet_pause"
|
||||
style={{ display: (newsletter.queue.status === null)
|
||||
<a
|
||||
id={'pause_' + newsletter.id}
|
||||
className="button mailpoet_pause"
|
||||
style={{ display: (newsletter.queue.status === null)
|
||||
? 'inline-block' : 'none' }}
|
||||
href="javascript:;"
|
||||
onClick={this.pauseSending.bind(null, newsletter)}
|
||||
href="javascript:;"
|
||||
onClick={this.pauseSending.bind(null, newsletter)}
|
||||
>{MailPoet.I18n.t('pause')}</a>
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
let progress_bar_width = 0;
|
||||
|
||||
if (isNaN(percentage)) {
|
||||
percentage = MailPoet.I18n.t('noSubscribers');
|
||||
} else {
|
||||
progress_bar_width = percentage;
|
||||
percentage += '%';
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={progressClasses}>
|
||||
<span
|
||||
className="mailpoet_progress_bar"
|
||||
style={{ width: progress_bar_width + '%' }}
|
||||
></span>
|
||||
<span className="mailpoet_progress_label">
|
||||
{ percentage }
|
||||
</span>
|
||||
</div>
|
||||
<p style={{ textAlign: 'center' }}>
|
||||
{ label }
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let progress_bar_width = 0;
|
||||
|
||||
if (isNaN(percentage)) {
|
||||
percentage = MailPoet.I18n.t('noSubscribers');
|
||||
} else {
|
||||
progress_bar_width = percentage;
|
||||
percentage += '%';
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={progressClasses}>
|
||||
<span
|
||||
className="mailpoet_progress_bar"
|
||||
style={{ width: progress_bar_width + '%' }}
|
||||
></span>
|
||||
<span className="mailpoet_progress_label">
|
||||
{ percentage }
|
||||
</span>
|
||||
</div>
|
||||
<p style={{ textAlign: 'center' }}>
|
||||
{ label }
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
const trackStatsCTAClicked = function () {
|
||||
MailPoet.trackEvent(
|
||||
'User has clicked a CTA to view detailed stats',
|
||||
{ 'MailPoet Free version': window.mailpoet_version }
|
||||
);
|
||||
};
|
||||
|
||||
const _StatisticsMixin = {
|
||||
renderStatistics: function (newsletter, is_sent, current_time) {
|
||||
let sent = is_sent;
|
||||
@ -159,6 +166,7 @@ const _StatisticsMixin = {
|
||||
}
|
||||
|
||||
let params = {};
|
||||
Hooks.addFilter('mailpoet_newsletters_listing_stats_before', this.addStatsCTALink);
|
||||
params = Hooks.applyFilters('mailpoet_newsletters_listing_stats_before', params, newsletter);
|
||||
|
||||
// welcome emails provide explicit total_sent value
|
||||
@ -179,11 +187,11 @@ const _StatisticsMixin = {
|
||||
const percentage_opened_display = MailPoet.Num.toLocaleFixed(percentage_opened, 1);
|
||||
const percentage_unsubscribed_display = MailPoet.Num.toLocaleFixed(percentage_unsubscribed, 1);
|
||||
|
||||
let show_stats_timeout,
|
||||
newsletter_date,
|
||||
sent_hours_ago,
|
||||
too_early_for_stats,
|
||||
show_kb_link;
|
||||
let show_stats_timeout;
|
||||
let newsletter_date;
|
||||
let sent_hours_ago;
|
||||
let too_early_for_stats;
|
||||
let show_kb_link;
|
||||
if (current_time !== undefined) {
|
||||
// standard emails and post notifications:
|
||||
// display green box for newsletters that were just sent
|
||||
@ -282,18 +290,33 @@ const _StatisticsMixin = {
|
||||
|
||||
if (total_sent > 0 && params.link) {
|
||||
// wrap content in a link
|
||||
if (params.externalLink) {
|
||||
return (
|
||||
<div>
|
||||
<a
|
||||
key={`stats-${newsletter.id}`}
|
||||
href={params.link}
|
||||
onClick={params.onClick || null}
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
{after_content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Link
|
||||
key={`stats-${newsletter.id}`}
|
||||
to={params.link}
|
||||
onClick={params.onClick || null}
|
||||
>
|
||||
>
|
||||
{content}
|
||||
</Link>
|
||||
{after_content}
|
||||
</div>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
@ -303,6 +326,37 @@ const _StatisticsMixin = {
|
||||
</div>
|
||||
);
|
||||
},
|
||||
addStatsCTAAction: function (actions) {
|
||||
if (window.mailpoet_premium_active) {
|
||||
return actions;
|
||||
}
|
||||
actions.unshift({
|
||||
name: 'stats',
|
||||
link: function () {
|
||||
return (
|
||||
<a href={'admin.php?page=mailpoet-premium'} onClick={trackStatsCTAClicked}>
|
||||
{MailPoet.I18n.t('statsListingActionTitle')}
|
||||
</a>
|
||||
);
|
||||
},
|
||||
display: function (newsletter) {
|
||||
// welcome emails provide explicit total_sent value
|
||||
const count_processed = newsletter.queue && newsletter.queue.count_processed;
|
||||
return ~~(newsletter.total_sent || count_processed) > 0;
|
||||
},
|
||||
});
|
||||
return actions;
|
||||
},
|
||||
addStatsCTALink: function (params) {
|
||||
if (window.mailpoet_premium_active) {
|
||||
return params;
|
||||
}
|
||||
const newParams = params;
|
||||
newParams.link = 'admin.php?page=mailpoet-premium';
|
||||
newParams.externalLink = true;
|
||||
newParams.onClick = trackStatsCTAClicked;
|
||||
return newParams;
|
||||
},
|
||||
};
|
||||
|
||||
const _MailerMixin = {
|
||||
|
@ -195,12 +195,14 @@ const NewsletterListNotification = React.createClass({
|
||||
},
|
||||
renderSettings: function (newsletter) {
|
||||
let sendingFrequency;
|
||||
let sendingToSegments;
|
||||
|
||||
// get list of segments' name
|
||||
const segments = newsletter.segments.map((segment) => {
|
||||
return segment.name;
|
||||
});
|
||||
const sendingToSegments = MailPoet.I18n.t('ifNewContentToSegments').replace(
|
||||
'%$1s', segments.join(', ')
|
||||
);
|
||||
|
||||
// check if the user has specified segments to send to
|
||||
if (segments.length === 0) {
|
||||
@ -209,51 +211,48 @@ const NewsletterListNotification = React.createClass({
|
||||
{ MailPoet.I18n.t('sendingToSegmentsNotSpecified') }
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
sendingToSegments = MailPoet.I18n.t('ifNewContentToSegments').replace(
|
||||
'%$1s', segments.join(', ')
|
||||
);
|
||||
}
|
||||
|
||||
// set sending frequency
|
||||
switch (newsletter.options.intervalType) {
|
||||
case 'daily':
|
||||
sendingFrequency = MailPoet.I18n.t('sendDaily').replace(
|
||||
// set sending frequency
|
||||
switch (newsletter.options.intervalType) {
|
||||
case 'daily':
|
||||
sendingFrequency = MailPoet.I18n.t('sendDaily').replace(
|
||||
'%$1s', timeOfDayValues[newsletter.options.timeOfDay]
|
||||
);
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'weekly':
|
||||
sendingFrequency = MailPoet.I18n.t('sendWeekly').replace(
|
||||
case 'weekly':
|
||||
sendingFrequency = MailPoet.I18n.t('sendWeekly').replace(
|
||||
'%$1s', weekDayValues[newsletter.options.weekDay]
|
||||
).replace(
|
||||
'%$2s', timeOfDayValues[newsletter.options.timeOfDay]
|
||||
);
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'monthly':
|
||||
sendingFrequency = MailPoet.I18n.t('sendMonthly').replace(
|
||||
case 'monthly':
|
||||
sendingFrequency = MailPoet.I18n.t('sendMonthly').replace(
|
||||
'%$1s', monthDayValues[newsletter.options.monthDay]
|
||||
).replace(
|
||||
'%$2s', timeOfDayValues[newsletter.options.timeOfDay]
|
||||
);
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'nthWeekDay':
|
||||
sendingFrequency = MailPoet.I18n.t('sendNthWeekDay').replace(
|
||||
case 'nthWeekDay':
|
||||
sendingFrequency = MailPoet.I18n.t('sendNthWeekDay').replace(
|
||||
'%$1s', nthWeekDayValues[newsletter.options.nthWeekDay]
|
||||
).replace(
|
||||
'%$2s', weekDayValues[newsletter.options.weekDay]
|
||||
).replace(
|
||||
'%$3s', timeOfDayValues[newsletter.options.timeOfDay]
|
||||
);
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'immediately':
|
||||
sendingFrequency = MailPoet.I18n.t('sendImmediately');
|
||||
break;
|
||||
}
|
||||
case 'immediately':
|
||||
sendingFrequency = MailPoet.I18n.t('sendImmediately');
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<span>
|
||||
{ sendingFrequency } { sendingToSegments }
|
||||
@ -266,13 +265,13 @@ const NewsletterListNotification = React.createClass({
|
||||
return (
|
||||
MailPoet.I18n.t('notSentYet')
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Link
|
||||
to={`/notification/history/${newsletter.id}`}
|
||||
>{ MailPoet.I18n.t('viewHistory') }</Link>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Link
|
||||
to={`/notification/history/${newsletter.id}`}
|
||||
>{ MailPoet.I18n.t('viewHistory') }</Link>
|
||||
);
|
||||
|
||||
},
|
||||
renderItem: function (newsletter, actions) {
|
||||
const rowClasses = classNames(
|
||||
|
@ -52,6 +52,7 @@ let newsletter_actions = [
|
||||
},
|
||||
];
|
||||
|
||||
Hooks.addFilter('mailpoet_newsletters_listings_notification_history_actions', StatisticsMixin.addStatsCTAAction);
|
||||
newsletter_actions = Hooks.applyFilters('mailpoet_newsletters_listings_notification_history_actions', newsletter_actions);
|
||||
|
||||
const NewsletterListNotificationHistory = React.createClass({
|
||||
|
@ -157,6 +157,7 @@ let newsletter_actions = [
|
||||
},
|
||||
];
|
||||
|
||||
Hooks.addFilter('mailpoet_newsletters_listings_standard_actions', StatisticsMixin.addStatsCTAAction);
|
||||
newsletter_actions = Hooks.applyFilters('mailpoet_newsletters_listings_standard_actions', newsletter_actions);
|
||||
|
||||
const NewsletterListStandard = React.createClass({
|
||||
|
@ -124,6 +124,7 @@ let newsletter_actions = [
|
||||
},
|
||||
];
|
||||
|
||||
Hooks.addFilter('mailpoet_newsletters_listings_welcome_notification_actions', StatisticsMixin.addStatsCTAAction);
|
||||
newsletter_actions = Hooks.applyFilters('mailpoet_newsletters_listings_welcome_notification_actions', newsletter_actions);
|
||||
|
||||
const NewsletterListWelcome = React.createClass({
|
||||
@ -179,6 +180,7 @@ const NewsletterListWelcome = React.createClass({
|
||||
renderSettings: function (newsletter) {
|
||||
let sendingEvent;
|
||||
let sendingDelay;
|
||||
let segment;
|
||||
|
||||
// set sending event
|
||||
switch (newsletter.options.event) {
|
||||
@ -195,7 +197,7 @@ const NewsletterListWelcome = React.createClass({
|
||||
|
||||
case 'segment':
|
||||
// get segment
|
||||
const segment = _.find(mailpoet_segments, (segment) => {
|
||||
segment = _.find(mailpoet_segments, (segment) => {
|
||||
return (~~(segment.id) === ~~(newsletter.options.segment));
|
||||
});
|
||||
|
||||
@ -205,11 +207,11 @@ const NewsletterListWelcome = React.createClass({
|
||||
{ MailPoet.I18n.t('sendingToSegmentsNotSpecified') }
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
sendingEvent = MailPoet.I18n.t('welcomeEventSegment').replace(
|
||||
}
|
||||
sendingEvent = MailPoet.I18n.t('welcomeEventSegment').replace(
|
||||
'%$1s', segment.name
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,8 @@ if (container) {
|
||||
{/* Listings */}
|
||||
<Route path="standard(/)**" params={{ tab: 'standard' }} component={NewsletterListStandard} />
|
||||
<Route path="welcome(/)**" component={NewsletterListWelcome} />
|
||||
<Route path="notification/history/:parent_id(/)**" component={NewsletterListNotificationHistory} />
|
||||
<Route path="notification/history/:parent_id(/)**"
|
||||
component={NewsletterListNotificationHistory} />
|
||||
<Route path="notification(/)**" component={NewsletterListNotification} />
|
||||
{/* Newsletter: type selection */}
|
||||
<Route path="new" component={NewsletterTypes} />
|
||||
|
@ -40,7 +40,7 @@ define(
|
||||
endpoint: 'segments',
|
||||
multiple: true,
|
||||
filter: function (segment) {
|
||||
return !!(!segment.deleted_at);
|
||||
return !segment.deleted_at;
|
||||
},
|
||||
getLabel: function (segment) {
|
||||
return segment.name + ' (' + parseInt(segment.subscribers, 10).toLocaleString() + ')';
|
||||
|
@ -17,7 +17,7 @@ define(
|
||||
const jQuery = jq;
|
||||
|
||||
const currentTime = window.mailpoet_current_time || '00:00';
|
||||
const defaultDateTime = window.mailpoet_current_date + ' ' + '00:00:00';
|
||||
const defaultDateTime = window.mailpoet_current_date + ' 00:00:00';
|
||||
const timeOfDayItems = window.mailpoet_schedule_time_of_day;
|
||||
const dateDisplayFormat = window.mailpoet_date_display_format;
|
||||
const dateStorageFormat = window.mailpoet_date_storage_format;
|
||||
@ -348,7 +348,7 @@ define(
|
||||
endpoint: 'segments',
|
||||
multiple: true,
|
||||
filter: function (segment) {
|
||||
return !!(!segment.deleted_at);
|
||||
return !segment.deleted_at;
|
||||
},
|
||||
getLabel: function (segment) {
|
||||
return segment.name + ' (' + parseInt(segment.subscribers, 10).toLocaleString() + ')';
|
||||
|
@ -47,6 +47,8 @@ define('notice', ['mailpoet', 'jquery'], function (mp, jQuery) {
|
||||
return this;
|
||||
},
|
||||
createNotice: function () {
|
||||
var onClose;
|
||||
var positionAfter;
|
||||
// clone element
|
||||
this.element = jQuery('#mailpoet_notice_' + this.options.type).clone();
|
||||
|
||||
@ -62,7 +64,6 @@ define('notice', ['mailpoet', 'jquery'], function (mp, jQuery) {
|
||||
this.element.removeAttr('id');
|
||||
|
||||
// insert notice after its parent
|
||||
var positionAfter;
|
||||
if (typeof this.options.positionAfter === 'object') {
|
||||
positionAfter = this.options.positionAfter;
|
||||
} else if (typeof this.options.positionAfter === 'string') {
|
||||
@ -73,7 +74,7 @@ define('notice', ['mailpoet', 'jquery'], function (mp, jQuery) {
|
||||
positionAfter.after(this.element);
|
||||
|
||||
// setup onClose callback
|
||||
var onClose = null;
|
||||
onClose = null;
|
||||
if (this.options.onClose !== null) {
|
||||
onClose = this.options.onClose;
|
||||
}
|
||||
@ -177,12 +178,13 @@ define('notice', ['mailpoet', 'jquery'], function (mp, jQuery) {
|
||||
}
|
||||
},
|
||||
hide: function (all) {
|
||||
var id;
|
||||
if (all !== undefined && all === true) {
|
||||
// all notices
|
||||
jQuery('.mailpoet_notice:not([id])').trigger('close');
|
||||
} else if (all !== undefined && jQuery.isArray(all)) {
|
||||
// array of ids
|
||||
for (var id in all) {
|
||||
for (id in all) {
|
||||
jQuery('[data-id="' + all[id] + '"]').trigger('close');
|
||||
}
|
||||
} if (all !== undefined) {
|
||||
|
@ -5,8 +5,7 @@ define([
|
||||
],
|
||||
function (
|
||||
MailPoet,
|
||||
jQuery,
|
||||
Parsley
|
||||
jQuery
|
||||
) {
|
||||
jQuery(function ($) {
|
||||
function isSameDomain(url) {
|
||||
@ -20,7 +19,7 @@ function (
|
||||
$('form.mailpoet_form').each(function () {
|
||||
var form = $(this);
|
||||
|
||||
form.parsley().on('form:validated', function (parsley) {
|
||||
form.parsley().on('form:validated', function () {
|
||||
// clear messages
|
||||
form.find('.mailpoet_message > p').hide();
|
||||
|
||||
@ -31,7 +30,7 @@ function (
|
||||
});
|
||||
|
||||
form.parsley().on('form:submit', function (parsley) {
|
||||
var form_data = form.serializeObject() || {};
|
||||
var form_data = form.mailpoetSerializeObject() || {};
|
||||
// check if we're on the same domain
|
||||
if (isSameDomain(window.MailPoetForm.ajax_url) === false) {
|
||||
// non ajax post request
|
||||
@ -84,4 +83,4 @@ function (
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -5,7 +5,7 @@ define(
|
||||
function (
|
||||
MailPoet
|
||||
) {
|
||||
|
||||
var element;
|
||||
function eventHandler() {
|
||||
if (confirm(MailPoet.I18n.t('reinstallConfirmation'))) {
|
||||
MailPoet.trackEvent(
|
||||
@ -36,8 +36,8 @@ define(
|
||||
return false;
|
||||
}
|
||||
|
||||
var element = document.getElementById('mailpoet_reinstall');
|
||||
element = document.getElementById('mailpoet_reinstall');
|
||||
if (element) {
|
||||
element.addEventListener('click', eventHandler, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -51,7 +51,7 @@ define(
|
||||
jQuery('#mailpoet_sending_method_setup').fadeIn();
|
||||
}
|
||||
},
|
||||
tabs: function (tabStr, section) {
|
||||
tabs: function (tabStr) {
|
||||
// set default tab
|
||||
var tab = tabStr || 'mta';
|
||||
|
||||
@ -86,4 +86,4 @@ define(
|
||||
if (!Backbone.History.started) Backbone.history.start();
|
||||
});
|
||||
}
|
||||
);
|
||||
);
|
||||
|
@ -3,98 +3,104 @@ define(
|
||||
'underscore',
|
||||
'jquery',
|
||||
'mailpoet',
|
||||
'handlebars',
|
||||
'select2'
|
||||
'handlebars'
|
||||
],
|
||||
function (
|
||||
_,
|
||||
jQuery,
|
||||
MailPoet,
|
||||
Handlebars,
|
||||
select2
|
||||
Handlebars
|
||||
) {
|
||||
if (!jQuery('#mailpoet_subscribers_export').length) {
|
||||
return;
|
||||
}
|
||||
jQuery(document).ready(function () {
|
||||
var segmentsContainerElement;
|
||||
var subscriberFieldsContainerElement;
|
||||
var exportConfirmedOptionElement;
|
||||
var groupBySegmentOptionElement;
|
||||
var nextStepButton;
|
||||
var renderSegmentsAndFields;
|
||||
var subscribers_export_template;
|
||||
if (!window.exportData.segments) {
|
||||
return;
|
||||
}
|
||||
var subscribers_export_template =
|
||||
subscribers_export_template =
|
||||
Handlebars.compile(jQuery('#mailpoet_subscribers_export_template').html());
|
||||
|
||||
// render template
|
||||
jQuery('#mailpoet_subscribers_export > div.inside').html(subscribers_export_template(window.exportData));
|
||||
|
||||
// define reusable variables
|
||||
var segmentsContainerElement = jQuery('#export_lists'),
|
||||
subscriberFieldsContainerElement = jQuery('#export_columns'),
|
||||
exportConfirmedOptionElement = jQuery(':radio[name="option_confirmed"]'),
|
||||
groupBySegmentOptionElement = jQuery(':checkbox[name="option_group_by_list"]'),
|
||||
nextStepButton = jQuery('a.mailpoet_export_process'),
|
||||
renderSegmentsAndFields = function (container, data) {
|
||||
if (container.data('select2')) {
|
||||
container
|
||||
.html('')
|
||||
.select2('destroy');
|
||||
}
|
||||
segmentsContainerElement = jQuery('#export_lists');
|
||||
subscriberFieldsContainerElement = jQuery('#export_columns');
|
||||
exportConfirmedOptionElement = jQuery(':radio[name="option_confirmed"]');
|
||||
groupBySegmentOptionElement = jQuery(':checkbox[name="option_group_by_list"]');
|
||||
nextStepButton = jQuery('a.mailpoet_export_process');
|
||||
renderSegmentsAndFields = function (container, data) {
|
||||
if (container.data('select2')) {
|
||||
container
|
||||
.select2({
|
||||
data: data,
|
||||
width: '20em',
|
||||
templateResult: function (item) {
|
||||
return (item.subscriberCount > 0)
|
||||
? item.name + ' (' + parseInt(item.subscriberCount).toLocaleString() + ')'
|
||||
: item.name;
|
||||
},
|
||||
templateSelection: function (item) {
|
||||
return (item.subscriberCount > 0)
|
||||
? item.name + ' (' + parseInt(item.subscriberCount).toLocaleString() + ')'
|
||||
: item.name;
|
||||
}
|
||||
})
|
||||
.on('select2:selecting', function (selectEvent) {
|
||||
var selectElement = this,
|
||||
selectedOptionId = selectEvent.params.args.data.id,
|
||||
fieldsToExclude = [
|
||||
'select',
|
||||
'deselect'
|
||||
];
|
||||
if (_.contains(fieldsToExclude, selectedOptionId)) {
|
||||
selectEvent.preventDefault();
|
||||
if (selectedOptionId === 'deselect') {
|
||||
jQuery(selectElement).val('').trigger('change');
|
||||
} else {
|
||||
var allOptions = [];
|
||||
_.each(container.find('option'), function (field) {
|
||||
if (!_.contains(fieldsToExclude, field.value)) {
|
||||
allOptions.push(field.value);
|
||||
}
|
||||
});
|
||||
jQuery(selectElement).val(allOptions).trigger('change');
|
||||
}
|
||||
jQuery(selectElement).select2('close');
|
||||
}
|
||||
})
|
||||
.on('change', function () {
|
||||
if ((window.exportData.segments && segmentsContainerElement.select2('data').length && subscriberFieldsContainerElement.select2('data').length)
|
||||
||
|
||||
(!window.exportData.segments && subscriberFieldsContainerElement.select2('data').length)
|
||||
) {
|
||||
toggleNextStepButton('on');
|
||||
}
|
||||
else {
|
||||
toggleNextStepButton('off');
|
||||
.html('')
|
||||
.select2('destroy');
|
||||
}
|
||||
container
|
||||
.select2({
|
||||
data: data,
|
||||
width: '20em',
|
||||
templateResult: function (item) {
|
||||
return (item.subscriberCount > 0)
|
||||
? item.name + ' (' + parseInt(item.subscriberCount).toLocaleString() + ')'
|
||||
: item.name;
|
||||
},
|
||||
templateSelection: function (item) {
|
||||
return (item.subscriberCount > 0)
|
||||
? item.name + ' (' + parseInt(item.subscriberCount).toLocaleString() + ')'
|
||||
: item.name;
|
||||
}
|
||||
})
|
||||
.on('select2:selecting', function (selectEvent) {
|
||||
var selectElement = this;
|
||||
var selectedOptionId = selectEvent.params.args.data.id;
|
||||
var fieldsToExclude = [
|
||||
'select',
|
||||
'deselect'
|
||||
];
|
||||
var allOptions;
|
||||
if (_.contains(fieldsToExclude, selectedOptionId)) {
|
||||
selectEvent.preventDefault();
|
||||
if (selectedOptionId === 'deselect') {
|
||||
jQuery(selectElement).val('').trigger('change');
|
||||
} else {
|
||||
allOptions = [];
|
||||
_.each(container.find('option'), function (field) {
|
||||
if (!_.contains(fieldsToExclude, field.value)) {
|
||||
allOptions.push(field.value);
|
||||
}
|
||||
});
|
||||
jQuery(selectElement).val(allOptions).trigger('change');
|
||||
}
|
||||
jQuery(selectElement).select2('close');
|
||||
}
|
||||
})
|
||||
.on('change', function () {
|
||||
if ((window.exportData.segments && segmentsContainerElement.select2('data').length && subscriberFieldsContainerElement.select2('data').length)
|
||||
||
|
||||
(!window.exportData.segments && subscriberFieldsContainerElement.select2('data').length)
|
||||
) {
|
||||
toggleNextStepButton('on');
|
||||
}
|
||||
else {
|
||||
toggleNextStepButton('off');
|
||||
}
|
||||
|
||||
if (segmentsContainerElement.select2('data').length > 1 && window.exportData.groupBySegmentOption) {
|
||||
jQuery('.mailpoet_group_by_list').show();
|
||||
}
|
||||
else if (window.exportData.groupBySegmentOption) {
|
||||
jQuery('.mailpoet_group_by_list').hide();
|
||||
}
|
||||
});
|
||||
};
|
||||
if (segmentsContainerElement.select2('data').length > 1 && window.exportData.groupBySegmentOption) {
|
||||
jQuery('.mailpoet_group_by_list').show();
|
||||
}
|
||||
else if (window.exportData.groupBySegmentOption) {
|
||||
jQuery('.mailpoet_group_by_list').hide();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// set confirmed subscribers export option to false
|
||||
window.exportData.exportConfirmedOption = false;
|
||||
@ -133,11 +139,12 @@ define(
|
||||
}
|
||||
|
||||
nextStepButton.click(function () {
|
||||
var exportFormat;
|
||||
if (jQuery(this).hasClass('button-disabled')) {
|
||||
return;
|
||||
}
|
||||
MailPoet.Modal.loading(true);
|
||||
var exportFormat = jQuery(':radio[name="option_format"]:checked').val();
|
||||
exportFormat = jQuery(':radio[name="option_format"]:checked').val();
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'ImportExport',
|
||||
@ -149,7 +156,7 @@ define(
|
||||
segments: (window.exportData.segments) ? segmentsContainerElement.val() : false,
|
||||
subscriber_fields: subscriberFieldsContainerElement.val()
|
||||
})
|
||||
}).always(function (response) {
|
||||
}).always(function () {
|
||||
MailPoet.Modal.loading(false);
|
||||
}).done(function (response) {
|
||||
var resultMessage = MailPoet.I18n.t('exportMessage')
|
||||
|
@ -7,8 +7,7 @@ define(
|
||||
'handlebars',
|
||||
'papaparse',
|
||||
'asyncqueue',
|
||||
'moment',
|
||||
'select2'
|
||||
'moment'
|
||||
],
|
||||
function (
|
||||
Backbone,
|
||||
@ -18,16 +17,16 @@ define(
|
||||
Handlebars,
|
||||
Papa,
|
||||
AsyncQueue,
|
||||
Moment,
|
||||
select2
|
||||
Moment
|
||||
) {
|
||||
if (!jQuery('#mailpoet_subscribers_import').length) {
|
||||
return;
|
||||
}
|
||||
jQuery(document).ready(function () {
|
||||
var router;
|
||||
jQuery('input[name="select_method"]').attr('checked', false);
|
||||
// configure router
|
||||
var router = new (Backbone.Router.extend({
|
||||
router = new (Backbone.Router.extend({
|
||||
routes: {
|
||||
'': 'home',
|
||||
step1: 'step1',
|
||||
@ -50,8 +49,20 @@ define(
|
||||
* STEP 1 (upload or copy/paste)
|
||||
*/
|
||||
router.on('route:step1', function () {
|
||||
var methodProcessContainerTemplate;
|
||||
var currentStepE;
|
||||
var methodSelectionElement;
|
||||
var pasteInputElement;
|
||||
var pasteInputPlaceholderElement;
|
||||
var pasteProcessButtonElement;
|
||||
var mailChimpKeyInputElement;
|
||||
var mailChimpKeyVerifyButtonElement;
|
||||
var mailChimpListsContainerElement;
|
||||
var mailChimpProcessButtonElement;
|
||||
var uploadElement;
|
||||
var uploadProcessButtonElement;
|
||||
// set or reset temporary validation rule on all columns
|
||||
window.mailpoetColumns = jQuery.map(window.mailpoetColumns, function (column, columnIndex) {
|
||||
window.mailpoetColumns = jQuery.map(window.mailpoetColumns, function (column) {
|
||||
var col = column;
|
||||
col.validation_rule = false;
|
||||
return col;
|
||||
@ -63,36 +74,34 @@ define(
|
||||
}
|
||||
|
||||
// render process button for each method
|
||||
var methodProcessContainerTemplate =
|
||||
methodProcessContainerTemplate =
|
||||
Handlebars.compile(jQuery('#method_process_template').html());
|
||||
jQuery('.mailpoet_method_process').html(methodProcessContainerTemplate());
|
||||
|
||||
// define reusable variables
|
||||
var currentStepE = jQuery(location.hash),
|
||||
methodSelectionElement = jQuery('#select_method'),
|
||||
pasteInputElement = jQuery('#paste_input'),
|
||||
pasteInputPlaceholderElement =
|
||||
pasteInputElement.data('placeholder').replace(/\\n/g, '\n'),
|
||||
pasteProcessButtonElement =
|
||||
jQuery('#method_paste > div.mailpoet_method_process')
|
||||
.find('a.mailpoet_process'),
|
||||
mailChimpKeyInputElement = jQuery('#mailchimp_key'),
|
||||
mailChimpKeyVerifyButtonElement = jQuery('#mailchimp_key_verify'),
|
||||
mailChimpListsContainerElement = jQuery('#mailchimp_lists'),
|
||||
mailChimpProcessButtonElement =
|
||||
jQuery('#method_mailchimp > div.mailpoet_method_process')
|
||||
.find('a.mailpoet_process'),
|
||||
uploadElement = jQuery('#file_local'),
|
||||
uploadProcessButtonElement =
|
||||
jQuery('#method_file > div.mailpoet_method_process')
|
||||
.find('a.mailpoet_process');
|
||||
currentStepE = jQuery(location.hash);
|
||||
methodSelectionElement = jQuery('#select_method');
|
||||
pasteInputElement = jQuery('#paste_input');
|
||||
pasteInputPlaceholderElement =
|
||||
pasteInputElement.data('placeholder').replace(/\\n/g, '\n');
|
||||
pasteProcessButtonElement =
|
||||
jQuery('#method_paste > div.mailpoet_method_process')
|
||||
.find('a.mailpoet_process');
|
||||
mailChimpKeyInputElement = jQuery('#mailchimp_key');
|
||||
mailChimpKeyVerifyButtonElement = jQuery('#mailchimp_key_verify');
|
||||
mailChimpListsContainerElement = jQuery('#mailchimp_lists');
|
||||
mailChimpProcessButtonElement = jQuery('#method_mailchimp > div.mailpoet_method_process')
|
||||
.find('a.mailpoet_process');
|
||||
uploadElement = jQuery('#file_local');
|
||||
uploadProcessButtonElement =
|
||||
jQuery('#method_file > div.mailpoet_method_process')
|
||||
.find('a.mailpoet_process');
|
||||
|
||||
// define method change behavior
|
||||
methodSelectionElement.change(function () {
|
||||
var available_methods = jQuery(':radio[name="select_method"]');
|
||||
var selected_method = available_methods.index(available_methods.filter(':checked'));
|
||||
MailPoet.Notice.hide();
|
||||
var available_methods = jQuery(':radio[name="select_method"]'),
|
||||
selected_method =
|
||||
available_methods.index(available_methods.filter(':checked'));
|
||||
// hide all methods
|
||||
currentStepE.find('.inside')
|
||||
.children('div[id^="method_"]')
|
||||
@ -131,9 +140,9 @@ define(
|
||||
});
|
||||
|
||||
pasteProcessButtonElement.click(function () {
|
||||
var pasteSize = encodeURI(pasteInputElement.val()).split(/%..|./).length - 1;
|
||||
MailPoet.Notice.hide();
|
||||
// get an approximate size of textarea paste in bytes
|
||||
var pasteSize = encodeURI(pasteInputElement.val()).split(/%..|./).length - 1;
|
||||
if (pasteSize > window.maxPostSizeBytes) {
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('maxPostSizeNotice'));
|
||||
return;
|
||||
@ -149,8 +158,8 @@ define(
|
||||
* CSV file
|
||||
*/
|
||||
uploadElement.change(function () {
|
||||
MailPoet.Notice.hide();
|
||||
var ext = this.value.match(/\.(.+)$/);
|
||||
MailPoet.Notice.hide();
|
||||
if (ext === null || ext[1].toLowerCase() !== 'csv') {
|
||||
this.value = '';
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('wrongFileFormat'));
|
||||
@ -234,7 +243,7 @@ define(
|
||||
api_key: mailChimpKeyInputElement.val(),
|
||||
lists: mailChimpListsContainerElement.find('select').val()
|
||||
}
|
||||
}).always(function (response) {
|
||||
}).always(function () {
|
||||
MailPoet.Modal.loading(false);
|
||||
}).done(function (response) {
|
||||
window.importData.step1 = response.data;
|
||||
@ -294,48 +303,48 @@ define(
|
||||
}
|
||||
|
||||
function parseCSV(isFile) {
|
||||
var processedSubscribers = [],
|
||||
parsedEmails = [],
|
||||
duplicateEmails = [],
|
||||
invalidEmails = [],
|
||||
emailColumnPosition = null,
|
||||
columnCount = null,
|
||||
isHeaderFound = false,
|
||||
advancedOptionHeader = true,
|
||||
advancedOptionDelimiter = '',
|
||||
advancedOptionNewline = '',
|
||||
advancedOptionComments = false,
|
||||
// trim spaces, commas, periods,
|
||||
// single/double quotes and convert to lowercase
|
||||
detectAndCleanupEmail = function (emailString) {
|
||||
var test;
|
||||
// decode HTML entities
|
||||
var email = jQuery('<div />').html(emailString).text();
|
||||
email = email
|
||||
.toLowerCase()
|
||||
// left/right trim spaces, punctuation (e.g., " 'email@email.com'; ")
|
||||
// right trim non-printable characters (e.g., "email@email.com<6F>")
|
||||
.replace(/^["';.,\s]+|[^\x20-\x7E]+$|["';.,_\s]+$/g, '')
|
||||
// remove spaces (e.g., "email @ email . com")
|
||||
// remove urlencoded characters
|
||||
.replace(/\s+|%\d+|,+/g, '');
|
||||
// detect e-mails that will be otherwise rejected by email regex
|
||||
test = /<(.*?)>/.exec(email);
|
||||
if (test) {
|
||||
// is the email inside angle brackets (e.g., 'some@email.com <some@email.com>')?
|
||||
email = test[1].trim();
|
||||
}
|
||||
test = /mailto:(?:\s+)?(.*)/.exec(email);
|
||||
if (test) {
|
||||
// is the email in 'mailto:email' format?
|
||||
email = test[1].trim();
|
||||
}
|
||||
// test for valid characters using WP's rule (https://core.trac.wordpress.org/browser/tags/4.7.3/src/wp-includes/formatting.php#L2902)
|
||||
if (!/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.\-@]+$/.test(email)) {
|
||||
return false;
|
||||
}
|
||||
return email;
|
||||
};
|
||||
var processedSubscribers = [];
|
||||
var parsedEmails = [];
|
||||
var duplicateEmails = [];
|
||||
var invalidEmails = [];
|
||||
var emailColumnPosition = null;
|
||||
var columnCount = null;
|
||||
var isHeaderFound = false;
|
||||
var advancedOptionHeader = true;
|
||||
var advancedOptionDelimiter = '';
|
||||
var advancedOptionNewline = '';
|
||||
var advancedOptionComments = false;
|
||||
// trim spaces, commas, periods,
|
||||
// single/double quotes and convert to lowercase
|
||||
var detectAndCleanupEmail = function (emailString) {
|
||||
var test;
|
||||
// decode HTML entities
|
||||
var email = jQuery('<div />').html(emailString).text();
|
||||
email = email
|
||||
.toLowerCase()
|
||||
// left/right trim spaces, punctuation (e.g., " 'email@email.com'; ")
|
||||
// right trim non-printable characters (e.g., "email@email.com<6F>")
|
||||
.replace(/^["';.,\s]+|[^\x20-\x7E]+$|["';.,_\s]+$/g, '')
|
||||
// remove spaces (e.g., "email @ email . com")
|
||||
// remove urlencoded characters
|
||||
.replace(/\s+|%\d+|,+/g, '');
|
||||
// detect e-mails that will be otherwise rejected by email regex
|
||||
test = /<(.*?)>/.exec(email);
|
||||
if (test) {
|
||||
// is the email inside angle brackets (e.g., 'some@email.com <some@email.com>')?
|
||||
email = test[1].trim();
|
||||
}
|
||||
test = /mailto:(?:\s+)?(.*)/.exec(email);
|
||||
if (test) {
|
||||
// is the email in 'mailto:email' format?
|
||||
email = test[1].trim();
|
||||
}
|
||||
// test for valid characters using WP's rule (https://core.trac.wordpress.org/browser/tags/4.7.3/src/wp-includes/formatting.php#L2902)
|
||||
if (!/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.\-@]+$/.test(email)) {
|
||||
return false;
|
||||
}
|
||||
return email;
|
||||
};
|
||||
|
||||
return {
|
||||
skipEmptyLines: true,
|
||||
@ -347,11 +356,18 @@ define(
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('dataProcessingError'));
|
||||
},
|
||||
complete: function (CSV) {
|
||||
for (var rowCount in CSV.data) {
|
||||
var rowData = CSV.data[rowCount].map(function (el) {
|
||||
var email;
|
||||
var emailAddress;
|
||||
var column;
|
||||
var rowCount;
|
||||
var rowData;
|
||||
var rowColumnCount;
|
||||
var errorNotice;
|
||||
for (rowCount in CSV.data) {
|
||||
rowData = CSV.data[rowCount].map(function (el) {
|
||||
return el.trim();
|
||||
});
|
||||
var rowColumnCount = rowData.length;
|
||||
rowColumnCount = rowData.length;
|
||||
// set the number of row elements based on the first non-empty row
|
||||
if (columnCount === null) {
|
||||
columnCount = rowColumnCount;
|
||||
@ -365,14 +381,14 @@ define(
|
||||
// determine position of email address inside an array; this is
|
||||
// done once and then email regex is run just on that element for each row
|
||||
if (emailColumnPosition === null) {
|
||||
for (var column in rowData) {
|
||||
var email = detectAndCleanupEmail(rowData[column]);
|
||||
for (column in rowData) {
|
||||
emailAddress = detectAndCleanupEmail(rowData[column]);
|
||||
if (emailColumnPosition === null
|
||||
&& window.emailRegex.test(email)) {
|
||||
&& window.emailRegex.test(emailAddress)) {
|
||||
emailColumnPosition = column;
|
||||
parsedEmails[email] = true; // add current e-mail to an object index
|
||||
rowData[column] = email;
|
||||
processedSubscribers[email] = rowData;
|
||||
parsedEmails[emailAddress] = true; // add current e-mail to an object index
|
||||
rowData[column] = emailAddress;
|
||||
processedSubscribers[emailAddress] = rowData;
|
||||
}
|
||||
}
|
||||
if (emailColumnPosition === null
|
||||
@ -383,7 +399,7 @@ define(
|
||||
}
|
||||
}
|
||||
else if (rowData[emailColumnPosition] !== '') {
|
||||
var email = detectAndCleanupEmail(rowData[emailColumnPosition]);
|
||||
email = detectAndCleanupEmail(rowData[emailColumnPosition]);
|
||||
if (_.has(parsedEmails, email)) {
|
||||
duplicateEmails.push(email);
|
||||
}
|
||||
@ -427,7 +443,7 @@ define(
|
||||
}
|
||||
else {
|
||||
MailPoet.Modal.loading(false);
|
||||
var errorNotice = MailPoet.I18n.t('noValidRecords');
|
||||
errorNotice = MailPoet.I18n.t('noValidRecords');
|
||||
errorNotice = errorNotice.replace('[link]', MailPoet.I18n.t('csvKBLink'));
|
||||
errorNotice = errorNotice.replace('[/link]', '</a>');
|
||||
MailPoet.Notice.error(errorNotice);
|
||||
@ -438,37 +454,41 @@ define(
|
||||
});
|
||||
|
||||
router.on('route:step2', function () {
|
||||
var nextStepButton;
|
||||
var previousStepButton;
|
||||
var subscribers;
|
||||
var subscribersDataTemplate;
|
||||
var subscribersDataTemplatePartial;
|
||||
var subscribersDataParseResultsTemplate;
|
||||
var segmentSelectElement;
|
||||
var maxRowsToShow;
|
||||
var filler;
|
||||
var fillerArray;
|
||||
var fillerPosition;
|
||||
var import_results;
|
||||
var duplicates;
|
||||
var email;
|
||||
if (typeof (window.importData.step1) === 'undefined') {
|
||||
router.navigate('step1', { trigger: true });
|
||||
return;
|
||||
}
|
||||
// define reusable variables
|
||||
var nextStepButton = jQuery('#step2_process'),
|
||||
previousStepButton = jQuery('#return_to_step1'),
|
||||
nextStepButton = jQuery('#step2_process');
|
||||
previousStepButton = jQuery('#return_to_step1');
|
||||
// create a copy of subscribers object for further manipulation
|
||||
subscribers = jQuery.extend(true, {}, window.importData.step1),
|
||||
subscribersDataTemplate =
|
||||
Handlebars
|
||||
.compile(jQuery('#subscribers_data_template')
|
||||
.html()),
|
||||
subscribersDataTemplatePartial =
|
||||
Handlebars
|
||||
.compile(jQuery('#subscribers_data_template_partial')
|
||||
.html()),
|
||||
subscribersDataParseResultsTemplate =
|
||||
Handlebars
|
||||
.compile(jQuery('#subscribers_data_parse_results_template')
|
||||
.html()),
|
||||
segmentSelectElement = jQuery('#mailpoet_segments_select'),
|
||||
maxRowsToShow = 10,
|
||||
filler = '. . .',
|
||||
subscribers = jQuery.extend(true, {}, window.importData.step1);
|
||||
subscribersDataTemplate = Handlebars.compile(jQuery('#subscribers_data_template').html());
|
||||
subscribersDataTemplatePartial = Handlebars.compile(jQuery('#subscribers_data_template_partial').html());
|
||||
subscribersDataParseResultsTemplate = Handlebars.compile(jQuery('#subscribers_data_parse_results_template').html());
|
||||
segmentSelectElement = jQuery('#mailpoet_segments_select');
|
||||
maxRowsToShow = 10;
|
||||
filler = '. . .';
|
||||
// create an array of filler data with the same number of
|
||||
// elements as in the subscribers' data row
|
||||
fillerArray = Array.apply(
|
||||
null,
|
||||
new Array(subscribers.subscribers[0].length)
|
||||
).map(String.prototype.valueOf, filler),
|
||||
fillerPosition;
|
||||
fillerArray = Array.apply(
|
||||
null,
|
||||
new Array(subscribers.subscribers[0].length)
|
||||
).map(String.prototype.valueOf, filler);
|
||||
|
||||
showCurrentStep();
|
||||
|
||||
@ -480,12 +500,12 @@ define(
|
||||
if (subscribers.invalid.length || subscribers.duplicate.length) {
|
||||
// count repeating e-mails inside duplicate array and present them in
|
||||
// 'email (xN)' format
|
||||
var duplicates = {};
|
||||
duplicates = {};
|
||||
subscribers.duplicate.forEach(function (email) {
|
||||
duplicates[email] = (duplicates[email] || 0) + 1;
|
||||
});
|
||||
subscribers.duplicate = [];
|
||||
for (var email in duplicates) {
|
||||
for (email in duplicates) {
|
||||
if (duplicates[email] > 1) {
|
||||
subscribers.duplicate.push(email + ' (x' + duplicates[email] + ')');
|
||||
}
|
||||
@ -494,7 +514,7 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
var import_results = {
|
||||
import_results = {
|
||||
notice: MailPoet.I18n.t('importNoticeSkipped').replace(
|
||||
'%1$s',
|
||||
'<strong>' + (subscribers.invalid.length + subscribers.duplicate.length) + '</strong>'
|
||||
@ -599,13 +619,14 @@ define(
|
||||
description: segmentDescription
|
||||
}
|
||||
}).done(function (response) {
|
||||
var selected_values;
|
||||
window.mailpoetSegments.push({
|
||||
id: response.data.id,
|
||||
name: response.data.name,
|
||||
subscriberCount: 0
|
||||
});
|
||||
|
||||
var selected_values = segmentSelectElement.val();
|
||||
selected_values = segmentSelectElement.val();
|
||||
if (selected_values === null) {
|
||||
selected_values = [response.data.id];
|
||||
} else {
|
||||
@ -642,20 +663,25 @@ define(
|
||||
Handlebars.registerHelper(
|
||||
'show_and_match_columns',
|
||||
function (subscribers, options) {
|
||||
var displayedColumns = [],
|
||||
displayedColumnsIds = [];
|
||||
var displayedColumns = [];
|
||||
var displayedColumnsIds = [];
|
||||
var i;
|
||||
var columnData;
|
||||
var columnId;
|
||||
var headerName;
|
||||
var headerNameMatch;
|
||||
// go through all elements of the first row in subscribers data
|
||||
for (var i in subscribers.subscribers[0]) {
|
||||
var columnData = subscribers.subscribers[0][i],
|
||||
columnId = 'ignore'; // set default column type
|
||||
for (i in subscribers.subscribers[0]) {
|
||||
columnData = subscribers.subscribers[0][i];
|
||||
columnId = 'ignore'; // set default column type
|
||||
// if the column is not undefined and has a valid e-mail, set type as email
|
||||
if (columnData % 1 !== 0 && window.emailRegex.test(columnData)) {
|
||||
columnId = 'email';
|
||||
} else if (subscribers.header) {
|
||||
var headerName = subscribers.header[i],
|
||||
headerNameMatch = window.mailpoetColumns.map(function (el) {
|
||||
return el.name;
|
||||
}).indexOf(headerName);
|
||||
headerName = subscribers.header[i];
|
||||
headerNameMatch = window.mailpoetColumns.map(function (el) {
|
||||
return el.name;
|
||||
}).indexOf(headerName);
|
||||
// set column type using header
|
||||
if (headerNameMatch !== -1) {
|
||||
columnId = window.mailpoetColumns[headerNameMatch].id;
|
||||
@ -733,8 +759,8 @@ define(
|
||||
}
|
||||
})
|
||||
.on('select2:selecting', function (selectEvent) {
|
||||
var selectElement = this,
|
||||
selectedOptionId = selectEvent.params.args.data.id;
|
||||
var selectElement = this;
|
||||
var selectedOptionId = selectEvent.params.args.data.id;
|
||||
// CREATE CUSTOM FIELD
|
||||
if (selectedOptionId === 'create') {
|
||||
selectEvent.preventDefault();
|
||||
@ -743,9 +769,9 @@ define(
|
||||
title: MailPoet.I18n.t('addNewField'),
|
||||
template: jQuery('#form_template_field_form').html()
|
||||
});
|
||||
jQuery('#form_field_new').parsley().on('form:submit', function (parsley) {
|
||||
jQuery('#form_field_new').parsley().on('form:submit', function () {
|
||||
// get data
|
||||
var data = jQuery(this.$element).serializeObject();
|
||||
var data = jQuery(this.$element).mailpoetSerializeObject();
|
||||
|
||||
// save custom field
|
||||
MailPoet.Ajax.post({
|
||||
@ -807,8 +833,8 @@ define(
|
||||
// check for duplicate values in all select options
|
||||
jQuery('select.mailpoet_subscribers_column_data_match')
|
||||
.each(function () {
|
||||
var element = this,
|
||||
elementId = jQuery(element).val();
|
||||
var element = this;
|
||||
var elementId = jQuery(element).val();
|
||||
// if another column has the same value and it's not an 'ignore', prompt user
|
||||
if (elementId === selectedOptionId
|
||||
&& elementId !== 'ignore') {
|
||||
@ -824,28 +850,34 @@ define(
|
||||
}
|
||||
})
|
||||
.on('select2:select', function (selectEvent) {
|
||||
var selectElement = this,
|
||||
selectedOptionId = selectEvent.params.data.id;
|
||||
var selectElement = this;
|
||||
var selectedOptionId = selectEvent.params.data.id;
|
||||
jQuery(selectElement).data('column-id', selectedOptionId);
|
||||
filterSubscribers();
|
||||
});
|
||||
|
||||
// filter subscribers' data to detect dates, emails, etc.
|
||||
function filterSubscribers() {
|
||||
var subscribersClone = jQuery.extend(true, {}, subscribers);
|
||||
var preventNextStep = false;
|
||||
var displayedColumns;
|
||||
jQuery(
|
||||
'[data-id="notice_invalidEmail"], [data-id="notice_invalidDate"]')
|
||||
.remove();
|
||||
var subscribersClone = jQuery.extend(true, {}, subscribers),
|
||||
preventNextStep = false,
|
||||
displayedColumns = jQuery.map(
|
||||
jQuery('.mailpoet_subscribers_column_data_match'), function (element, elementIndex) {
|
||||
var columnId = jQuery(element).data('column-id');
|
||||
var validationRule = jQuery(element).data('validation-rule');
|
||||
jQuery(element).val(columnId).trigger('change');
|
||||
return { id: columnId, index: elementIndex, validationRule: validationRule, element: element };
|
||||
});
|
||||
displayedColumns = jQuery.map(
|
||||
jQuery('.mailpoet_subscribers_column_data_match'), function (element, elementIndex) {
|
||||
var columnId = jQuery(element).data('column-id');
|
||||
var validationRule = jQuery(element).data('validation-rule');
|
||||
jQuery(element).val(columnId).trigger('change');
|
||||
return { id: columnId, index: elementIndex, validationRule: validationRule, element: element };
|
||||
});
|
||||
// iterate through the object of mailpoet columns
|
||||
jQuery.map(window.mailpoetColumns, function (column, columnIndex) {
|
||||
jQuery.map(window.mailpoetColumns, function (column) {
|
||||
var firstRowData;
|
||||
var validationRule;
|
||||
var testedFormat;
|
||||
var format;
|
||||
var allowedDateFormats;
|
||||
// check if the column id matches the selected id of one of the
|
||||
// subscriber's data columns
|
||||
var matchedColumn = _.find(displayedColumns, function (data) { return data.id === column.id; });
|
||||
@ -869,7 +901,7 @@ define(
|
||||
}
|
||||
// DATE filter: if column type is date, check if we can recognize it
|
||||
if (column.type === 'date' && matchedColumn) {
|
||||
var allowedDateFormats = [
|
||||
allowedDateFormats = [
|
||||
Moment.ISO_8601,
|
||||
'YYYY/MM/DD',
|
||||
'MM/DD/YYYY',
|
||||
@ -880,8 +912,8 @@ define(
|
||||
'YYYY/MM',
|
||||
'YYYY'
|
||||
];
|
||||
var firstRowData = subscribersClone.subscribers[0][matchedColumn.index];
|
||||
var validationRule = false;
|
||||
firstRowData = subscribersClone.subscribers[0][matchedColumn.index];
|
||||
validationRule = false;
|
||||
// check if date exists
|
||||
if (firstRowData.trim() === '') {
|
||||
subscribersClone.subscribers[0][matchedColumn.index] =
|
||||
@ -892,10 +924,10 @@ define(
|
||||
preventNextStep = true;
|
||||
}
|
||||
else {
|
||||
for (var format in allowedDateFormats) {
|
||||
var testedFormat = allowedDateFormats[format];
|
||||
for (format in allowedDateFormats) {
|
||||
testedFormat = allowedDateFormats[format];
|
||||
if (Moment(firstRowData, testedFormat, true).isValid()) {
|
||||
var validationRule = (typeof (testedFormat) === 'function') ?
|
||||
validationRule = (typeof (testedFormat) === 'function') ?
|
||||
'datetime' :
|
||||
testedFormat;
|
||||
// set validation on the column element
|
||||
@ -910,8 +942,8 @@ define(
|
||||
jQuery.map(subscribersClone.subscribers, function (dataSubscribers, index) {
|
||||
var data = dataSubscribers;
|
||||
var rowData = data[matchedColumn.index];
|
||||
if (index === fillerPosition || rowData.trim() === '') return;
|
||||
var date = Moment(rowData, testedFormat, true);
|
||||
if (index === fillerPosition || rowData.trim() === '') return;
|
||||
// validate date
|
||||
if (date.isValid()) {
|
||||
data[matchedColumn.index] = new Handlebars.SafeString(
|
||||
@ -970,33 +1002,35 @@ define(
|
||||
});
|
||||
|
||||
nextStepButton.off().on('click', function () {
|
||||
var columns = {};
|
||||
var queue = new jQuery.AsyncQueue();
|
||||
var batchNumber = 0;
|
||||
var batchSize = 2000;
|
||||
var timestamp = Date.now() / 1000;
|
||||
var subscribers = [];
|
||||
var importResults = {
|
||||
created: 0,
|
||||
updated: 0,
|
||||
errors: [],
|
||||
segments: []
|
||||
};
|
||||
var subscribers;
|
||||
var splitSubscribers;
|
||||
|
||||
if (jQuery(this).hasClass('button-disabled')) {
|
||||
return;
|
||||
}
|
||||
MailPoet.Modal.loading(true);
|
||||
|
||||
var columns = {},
|
||||
queue = new jQuery.AsyncQueue(),
|
||||
batchNumber = 0,
|
||||
batchSize = 2000,
|
||||
timestamp = Date.now() / 1000,
|
||||
subscribers = [],
|
||||
importResults = {
|
||||
created: 0,
|
||||
updated: 0,
|
||||
errors: [],
|
||||
segments: []
|
||||
},
|
||||
splitSubscribers = function (subscribers, size) {
|
||||
return subscribers.reduce(function (res, item, index) {
|
||||
if (index % size === 0) {
|
||||
res.push([]);
|
||||
}
|
||||
res[res.length - 1].push(item);
|
||||
return res;
|
||||
}, []);
|
||||
},
|
||||
subscribers = splitSubscribers(window.importData.step1.subscribers, batchSize);
|
||||
splitSubscribers = function (subscribers, size) {
|
||||
return subscribers.reduce(function (res, item, index) {
|
||||
if (index % size === 0) {
|
||||
res.push([]);
|
||||
}
|
||||
res[res.length - 1].push(item);
|
||||
return res;
|
||||
}, []);
|
||||
};
|
||||
subscribers = splitSubscribers(window.importData.step1.subscribers, batchSize);
|
||||
|
||||
_.each(jQuery('select.mailpoet_subscribers_column_data_match'),
|
||||
function (column, columnIndex) {
|
||||
@ -1067,6 +1101,9 @@ define(
|
||||
});
|
||||
|
||||
router.on('route:step3', function () {
|
||||
var subscribersDataImportResultsTemplate;
|
||||
var exportMenuElement;
|
||||
var importResults;
|
||||
if (typeof (window.importData.step2) === 'undefined') {
|
||||
router.navigate('step2', { trigger: true });
|
||||
return;
|
||||
@ -1085,25 +1122,23 @@ define(
|
||||
});
|
||||
|
||||
// display statistics
|
||||
var subscribersDataImportResultsTemplate =
|
||||
Handlebars
|
||||
.compile(jQuery('#subscribers_data_import_results_template')
|
||||
.html()),
|
||||
exportMenuElement = jQuery('span.mailpoet_export'),
|
||||
importResults = {
|
||||
created: (window.importData.step2.created)
|
||||
? MailPoet.I18n.t('subscribersCreated')
|
||||
.replace('%1$s', '<strong>' + window.importData.step2.created.toLocaleString() + '</strong>')
|
||||
.replace('%2$s', '"' + window.importData.step2.segments.join('", "') + '"')
|
||||
: false,
|
||||
updated: (window.importData.step2.updated)
|
||||
? MailPoet.I18n.t('subscribersUpdated')
|
||||
.replace('%1$s', '<strong>' + window.importData.step2.updated.toLocaleString() + '</strong>')
|
||||
.replace('%2$s', '"' + window.importData.step2.segments.join('", "') + '"')
|
||||
: false,
|
||||
no_action: (!window.importData.step2.created && !window.importData.step2.updated),
|
||||
added_to_segment_with_welcome_notification: window.importData.step2.added_to_segment_with_welcome_notification
|
||||
};
|
||||
subscribersDataImportResultsTemplate =
|
||||
Handlebars.compile(jQuery('#subscribers_data_import_results_template').html());
|
||||
exportMenuElement = jQuery('span.mailpoet_export');
|
||||
importResults = {
|
||||
created: (window.importData.step2.created)
|
||||
? MailPoet.I18n.t('subscribersCreated')
|
||||
.replace('%1$s', '<strong>' + window.importData.step2.created.toLocaleString() + '</strong>')
|
||||
.replace('%2$s', '"' + window.importData.step2.segments.join('", "') + '"')
|
||||
: false,
|
||||
updated: (window.importData.step2.updated)
|
||||
? MailPoet.I18n.t('subscribersUpdated')
|
||||
.replace('%1$s', '<strong>' + window.importData.step2.updated.toLocaleString() + '</strong>')
|
||||
.replace('%2$s', '"' + window.importData.step2.segments.join('", "') + '"')
|
||||
: false,
|
||||
no_action: (!window.importData.step2.created && !window.importData.step2.updated),
|
||||
added_to_segment_with_welcome_notification: window.importData.step2.added_to_segment_with_welcome_notification
|
||||
};
|
||||
|
||||
jQuery('#subscribers_data_import_results')
|
||||
.html(subscribersDataImportResultsTemplate(importResults))
|
||||
|
@ -26,17 +26,27 @@ modules:
|
||||
adminUsername: admin
|
||||
adminPassword: password
|
||||
adminPath: /wp-admin
|
||||
log_js_errors: true
|
||||
WPLoader:
|
||||
loadOnly: true
|
||||
wpRootFolder: /wp-core
|
||||
dbName: wordpress
|
||||
dbHost: mysql
|
||||
dbUser: wordpress
|
||||
dbPassword: wordpress
|
||||
wpDebug: false
|
||||
tablePrefix: wp_
|
||||
domain: wordpress
|
||||
plugins: ['mailpoet/mailpoet.php']
|
||||
activatePlugins: ['mailpoet/mailpoet.php']
|
||||
WPDb:
|
||||
dsn: 'mysql:host=mysql;dbname=wordpress'
|
||||
user: wordpress
|
||||
password: wordpress
|
||||
dump: tests/_data/acceptanceDump.sql
|
||||
populate: true
|
||||
cleanup: false
|
||||
url: 'http://wordpress'
|
||||
urlReplacement: true
|
||||
tablePrefix: 'wp_'
|
||||
WPCLI:
|
||||
path: /wp-core
|
||||
throw: true
|
||||
coverage:
|
||||
enabled: true
|
||||
whitelist:
|
||||
|
@ -24,7 +24,7 @@
|
||||
"codeception/verify": "^0.3.3",
|
||||
"consolidation/robo": "^1.0.5",
|
||||
"henrikbjorn/lurker": "^1.2",
|
||||
"lucatume/wp-browser": "1.19.12",
|
||||
"lucatume/wp-browser": "1.21.20",
|
||||
"phpunit/phpunit": "4.8.21",
|
||||
"vlucas/phpdotenv": "^2.4.0",
|
||||
"umpirsky/twig-gettext-extractor": "1.1.*",
|
||||
|
2229
composer.lock
generated
2229
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -25,6 +25,7 @@ services:
|
||||
- 8080:80
|
||||
environment:
|
||||
WORDPRESS_DB_PASSWORD: wordpress
|
||||
WORDPRESS_TABLE_PREFIX: mp_
|
||||
|
||||
mysql:
|
||||
image: mysql:5.6
|
||||
@ -44,4 +45,4 @@ services:
|
||||
- '4444'
|
||||
- '5900:5900'
|
||||
volumes:
|
||||
wp-core:
|
||||
wp-core:
|
||||
|
@ -14,7 +14,9 @@ done
|
||||
# Make sure permissions are correct.
|
||||
cd /wp-core
|
||||
chown www-data:www-data wp-content/plugins
|
||||
chown www-data:www-data wp-content/uploads
|
||||
chmod 755 wp-content/plugins
|
||||
chmod -R 777 wp-content/uploads
|
||||
|
||||
# Make sure WordPress is installed.
|
||||
if ! $(wp-su core is-installed); then
|
||||
@ -29,14 +31,27 @@ if ! $(wp-su core is-installed); then
|
||||
|
||||
fi
|
||||
|
||||
rm -rf /project/vendor_backup
|
||||
mv /project/vendor /project/vendor_backup
|
||||
cd /project
|
||||
php composer.phar install
|
||||
# Load Composer dependencies
|
||||
# Set KEEP_DEPS environment flag to not redownload them on each run, only for the 1st time, useful for development.
|
||||
# Example: docker-compose run -e KEEP_DEPS=1 codeception ...
|
||||
# Don't forget to restore your original /vendor folder from /vendor_backup manually or by running acceptance tests without this flag.
|
||||
if [[ -z "${KEEP_DEPS}" ]]; then
|
||||
rm -rf /project/vendor_backup
|
||||
fi
|
||||
if [ ! -d "/project/vendor_backup" ]; then
|
||||
mv /project/vendor /project/vendor_backup
|
||||
cd /project
|
||||
php composer.phar install
|
||||
fi
|
||||
|
||||
cd /wp-core/wp-content/plugins/mailpoet
|
||||
|
||||
/project/vendor/bin/codecept run acceptance -c codeception.acceptance.yml $@
|
||||
exitcode=$?
|
||||
|
||||
rm -rf /project/vendor
|
||||
mv /project/vendor_backup /project/vendor
|
||||
if [[ -z "${KEEP_DEPS}" ]]; then
|
||||
rm -rf /project/vendor
|
||||
mv /project/vendor_backup /project/vendor
|
||||
fi
|
||||
|
||||
exit $exitcode
|
||||
|
@ -57,7 +57,7 @@ class API {
|
||||
$this->setRequestData($_POST);
|
||||
|
||||
if($this->checkToken() === false) {
|
||||
$error_message = __('Invalid API request.', 'mailpoet');
|
||||
$error_message = __('Sorry, but we couldn\'t connect to the MailPoet server. Please refresh the web page and try again.', 'mailpoet');
|
||||
$error_response = $this->createErrorResponse(Error::UNAUTHORIZED, $error_message, Response::STATUS_UNAUTHORIZED);
|
||||
return $error_response->send();
|
||||
}
|
||||
@ -200,4 +200,4 @@ class API {
|
||||
);
|
||||
return $error_response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ abstract class Response {
|
||||
const STATUS_UNAUTHORIZED = 401;
|
||||
const STATUS_FORBIDDEN = 403;
|
||||
const STATUS_NOT_FOUND = 404;
|
||||
const STATUS_UNKNOWN = 500;
|
||||
|
||||
public $status;
|
||||
public $meta;
|
||||
|
@ -10,6 +10,7 @@ use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\SendingQueue as SendingQueueModel;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Newsletter\Scheduler\Scheduler;
|
||||
use MailPoet\Segments\SubscribersFinder;
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
@ -75,10 +76,8 @@ class SendingQueue extends APIEndpoint {
|
||||
$queue->count_total = $queue->count_to_process = 0;
|
||||
} else {
|
||||
$segments = $newsletter->segments()->findArray();
|
||||
$segment_ids = array_map(function($segment) {
|
||||
return $segment['id'];
|
||||
}, $segments);
|
||||
$subscribers = Subscriber::getSubscribedInSegments($segment_ids)->findArray();
|
||||
$finder = new SubscribersFinder();
|
||||
$subscribers = $finder->getSubscribersByList($segments);
|
||||
$subscribers = Helpers::flattenArray($subscribers);
|
||||
if(!count($subscribers)) {
|
||||
return $this->errorResponse(array(
|
||||
|
@ -10,7 +10,7 @@ use MailPoet\Form\Util\FieldNameObfuscator;
|
||||
use MailPoet\Models\Form;
|
||||
use MailPoet\Models\StatisticsForms;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\Subscription\Throttling as SubscriptionThrottling;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -98,16 +98,10 @@ class Subscribers extends APIEndpoint {
|
||||
$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();
|
||||
$timeout = SubscriptionThrottling::throttle();
|
||||
|
||||
if($subscription_count > 0) {
|
||||
throw new \Exception(__('You need to wait before subscribing again.', 'mailpoet'));
|
||||
if($timeout > 0) {
|
||||
throw new \Exception(sprintf(__('You need to wait %d seconds before subscribing again.', 'mailpoet'), $timeout));
|
||||
}
|
||||
|
||||
$subscriber = Subscriber::subscribe($data, $segment_ids);
|
||||
|
@ -133,6 +133,37 @@ class API {
|
||||
return $new_subscriber->withCustomFields()->withSubscriptions()->asArray();
|
||||
}
|
||||
|
||||
function addList(array $list) {
|
||||
// throw exception when list name is missing
|
||||
if(empty($list['name'])) {
|
||||
throw new \Exception(
|
||||
__('List name is required.', 'mailpoet')
|
||||
);
|
||||
}
|
||||
|
||||
// throw exception when list already exists
|
||||
if(Segment::where('name', $list['name'])->findOne()) {
|
||||
throw new \Exception(
|
||||
__('This list already exists.', 'mailpoet')
|
||||
);
|
||||
}
|
||||
|
||||
// add list
|
||||
$new_list = Segment::create();
|
||||
$new_list->hydrate($list);
|
||||
$new_list->save();
|
||||
if($new_list->getErrors() !== false) {
|
||||
throw new \Exception(
|
||||
__(sprintf('Failed to add list: %s', strtolower(implode(', ', $new_list->getErrors()))), 'mailpoet')
|
||||
);
|
||||
}
|
||||
|
||||
// reload list to get the saved created|updated|delete dates/other fields
|
||||
$new_list = Segment::findOne($new_list->id);
|
||||
|
||||
return $new_list->asArray();
|
||||
}
|
||||
|
||||
protected function _sendConfirmationEmail(Subscriber $subscriber) {
|
||||
return $subscriber->sendConfirmationEmail();
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use MailPoet\Settings\Pages;
|
||||
class Reporter {
|
||||
|
||||
function getData() {
|
||||
|
||||
global $wpdb, $wp_version;
|
||||
$mta = Setting::getValue('mta', array());
|
||||
$newsletters = Newsletter::getAnalytics();
|
||||
$isCronTriggerMethodWP = Setting::getValue('cron_trigger.method') === CronTrigger::$available_methods['wordpress'];
|
||||
@ -21,6 +21,16 @@ class Reporter {
|
||||
|
||||
|
||||
return array(
|
||||
'PHP version' => PHP_VERSION,
|
||||
'MySQL version' => $wpdb->db_version(),
|
||||
'WordPress version' => $wp_version,
|
||||
'Multisite environment' => is_multisite() ? 'yes' : 'no',
|
||||
'RTL' => is_rtl() ? 'yes' : 'no',
|
||||
'WP_MEMORY_LIMIT' => WP_MEMORY_LIMIT,
|
||||
'WP_MAX_MEMORY_LIMIT' => WP_MAX_MEMORY_LIMIT,
|
||||
'PHP memory_limit' => ini_get('memory_limit'),
|
||||
'PHP max_execution_time' => ini_get('max_execution_time'),
|
||||
'users_can_register' => get_option('users_can_register') ? 'yes' : 'no',
|
||||
'MailPoet Free version' => MAILPOET_VERSION,
|
||||
'MailPoet Premium version' => (defined('MAILPOET_PREMIUM_VERSION')) ? MAILPOET_PREMIUM_VERSION : 'N/A',
|
||||
'Total number of subscribers' => Subscriber::getTotalSubscribers(),
|
||||
|
@ -22,8 +22,6 @@ class AccessControl {
|
||||
|
||||
function __construct() {
|
||||
$this->permissions = self::getDefaultPermissions();
|
||||
$this->user_roles = $this->getUserRoles();
|
||||
$this->user_capabilities = $this->getUserCapabilities();
|
||||
}
|
||||
|
||||
static function getDefaultPermissions() {
|
||||
@ -80,30 +78,8 @@ class AccessControl {
|
||||
);
|
||||
}
|
||||
|
||||
function getUserRoles() {
|
||||
$user = wp_get_current_user();
|
||||
return $user->roles;
|
||||
}
|
||||
|
||||
function getUserCapabilities() {
|
||||
$user = wp_get_current_user();
|
||||
return array_keys($user->allcaps);
|
||||
}
|
||||
|
||||
function getUserFirstCapability() {
|
||||
return (!empty($this->user_capabilities)) ?
|
||||
$this->user_capabilities[0] :
|
||||
null;
|
||||
}
|
||||
|
||||
function validatePermission($permission) {
|
||||
if($permission === self::NO_ACCESS_RESTRICTION) return true;
|
||||
foreach($this->user_roles as $role) {
|
||||
$role_object = get_role($role);
|
||||
if($role_object && $role_object->has_cap($permission)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return current_user_can($permission);
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ class Capabilities {
|
||||
if(!isset($role_objects[$role])) {
|
||||
$role_objects[$role] = get_role($role);
|
||||
}
|
||||
if(!is_object($role_objects[$role])) continue;
|
||||
$role_objects[$role]->add_cap($name);
|
||||
}
|
||||
}
|
||||
@ -40,6 +41,7 @@ class Capabilities {
|
||||
if(!isset($role_objects[$role])) {
|
||||
$role_objects[$role] = get_role($role);
|
||||
}
|
||||
if(!is_object($role_objects[$role])) continue;
|
||||
$role_objects[$role]->remove_cap($name);
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ class Database {
|
||||
$subscribers = Env::$db_prefix . 'subscribers';
|
||||
$subscriber_segment = Env::$db_prefix . 'subscriber_segment';
|
||||
$subscriber_custom_field = Env::$db_prefix . 'subscriber_custom_field';
|
||||
$subscriber_ips = Env::$db_prefix . 'subscriber_ips';
|
||||
$newsletter_segment = Env::$db_prefix . 'newsletter_segment';
|
||||
$scheduled_tasks = Env::$db_prefix . 'scheduled_tasks';
|
||||
$scheduled_task_subscribers = Env::$db_prefix . 'scheduled_task_subscribers';
|
||||
@ -92,6 +93,7 @@ class Database {
|
||||
define('MP_SUBSCRIBERS_TABLE', $subscribers);
|
||||
define('MP_SUBSCRIBER_SEGMENT_TABLE', $subscriber_segment);
|
||||
define('MP_SUBSCRIBER_CUSTOM_FIELD_TABLE', $subscriber_custom_field);
|
||||
define('MP_SUBSCRIBER_IPS_TABLE', $subscriber_ips);
|
||||
define('MP_SCHEDULED_TASKS_TABLE', $scheduled_tasks);
|
||||
define('MP_SCHEDULED_TASK_SUBSCRIBERS_TABLE', $scheduled_task_subscribers);
|
||||
define('MP_SENDING_QUEUES_TABLE', $sending_queues);
|
||||
|
@ -36,6 +36,9 @@ class Initializer {
|
||||
return;
|
||||
}
|
||||
|
||||
// load translations
|
||||
$this->setupLocalizer();
|
||||
|
||||
try {
|
||||
$this->setupDB();
|
||||
} catch(\Exception $e) {
|
||||
@ -124,7 +127,6 @@ class Initializer {
|
||||
$this->setupInstaller();
|
||||
$this->setupUpdater();
|
||||
|
||||
$this->setupLocalizer();
|
||||
$this->setupCapabilities();
|
||||
$this->setupMenu();
|
||||
$this->setupShortcodes();
|
||||
|
@ -228,6 +228,8 @@ class Menu {
|
||||
));
|
||||
});
|
||||
|
||||
do_action('mailpoet_menu_after_lists');
|
||||
|
||||
// Settings page
|
||||
add_submenu_page(
|
||||
self::MAIN_PAGE_SLUG,
|
||||
@ -460,7 +462,12 @@ class Menu {
|
||||
$data = array();
|
||||
|
||||
$data['items_per_page'] = $this->getLimitPerPage('subscribers');
|
||||
$data['segments'] = Segment::getSegmentsWithSubscriberCount($type = false);
|
||||
$segments = Segment::getSegmentsWithSubscriberCount($type = false);
|
||||
$segments = apply_filters('mailpoet_segments_with_subscriber_count', $segments);
|
||||
usort($segments, function ($a, $b) {
|
||||
return strcasecmp($a["name"], $b["name"]);
|
||||
});
|
||||
$data['segments'] = $segments;
|
||||
|
||||
$data['custom_fields'] = array_map(function($field) {
|
||||
$field['params'] = unserialize($field['params']);
|
||||
@ -514,7 +521,12 @@ class Menu {
|
||||
$data = array();
|
||||
|
||||
$data['items_per_page'] = $this->getLimitPerPage('newsletters');
|
||||
$data['segments'] = Segment::getSegmentsWithSubscriberCount($type = false);
|
||||
$segments = Segment::getSegmentsWithSubscriberCount($type = false);
|
||||
$segments = apply_filters('mailpoet_segments_with_subscriber_count', $segments);
|
||||
usort($segments, function ($a, $b) {
|
||||
return strcasecmp($a["name"], $b["name"]);
|
||||
});
|
||||
$data['segments'] = $segments;
|
||||
$data['settings'] = Setting::getAll();
|
||||
$data['roles'] = $wp_roles->get_names();
|
||||
$data['roles']['mailpoet_all'] = __('In any WordPress role', 'mailpoet');
|
||||
@ -529,6 +541,7 @@ class Menu {
|
||||
);
|
||||
|
||||
$data['tracking_enabled'] = Setting::getValue('tracking.enabled');
|
||||
$data['premium_plugin_active'] = License::getLicense();
|
||||
|
||||
wp_enqueue_script('jquery-ui');
|
||||
wp_enqueue_script('jquery-ui-datepicker');
|
||||
@ -677,7 +690,7 @@ class Menu {
|
||||
$this->premium_key_valid = $checker->isPremiumKeyValid($show_notices);
|
||||
}
|
||||
|
||||
private function getLimitPerPage($model = null) {
|
||||
function getLimitPerPage($model = null) {
|
||||
if($model === null) {
|
||||
return Listing\Handler::DEFAULT_LIMIT_PER_PAGE;
|
||||
}
|
||||
@ -690,7 +703,7 @@ class Menu {
|
||||
: Listing\Handler::DEFAULT_LIMIT_PER_PAGE;
|
||||
}
|
||||
|
||||
private function displayPage($template, $data) {
|
||||
function displayPage($template, $data) {
|
||||
try {
|
||||
echo $this->renderer->render($template, $data);
|
||||
} catch(\Exception $e) {
|
||||
|
@ -23,6 +23,7 @@ class Migrator {
|
||||
'subscribers',
|
||||
'subscriber_segment',
|
||||
'subscriber_custom_field',
|
||||
'subscriber_ips',
|
||||
'newsletters',
|
||||
'newsletter_templates',
|
||||
'newsletter_option_fields',
|
||||
@ -207,6 +208,16 @@ class Migrator {
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
|
||||
function subscriberIps() {
|
||||
$attributes = array(
|
||||
'ip varchar(45) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (created_at, ip),',
|
||||
'KEY ip (ip)'
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
|
||||
function newsletters() {
|
||||
$attributes = array(
|
||||
'id int(11) unsigned NOT NULL AUTO_INCREMENT,',
|
||||
|
@ -56,12 +56,14 @@ class Shortcodes {
|
||||
}
|
||||
|
||||
if(empty($segment_ids)) {
|
||||
return Subscriber::filter('subscribed')->count();
|
||||
return number_format_i18n(Subscriber::filter('subscribed')->count());
|
||||
} else {
|
||||
return SubscriberSegment::whereIn('segment_id', $segment_ids)
|
||||
->select('subscriber_id')->distinct()
|
||||
->filter('subscribed')
|
||||
->findResultSet()->count();
|
||||
return number_format_i18n(
|
||||
SubscriberSegment::whereIn('segment_id', $segment_ids)
|
||||
->select('subscriber_id')->distinct()
|
||||
->filter('subscribed')
|
||||
->findResultSet()->count()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ class Widget {
|
||||
wp_enqueue_script(
|
||||
'mailpoet_public',
|
||||
Env::$assets_url . '/js/' . $this->renderer->getJsAsset('public.js'),
|
||||
array(),
|
||||
array('jquery'),
|
||||
Env::$version,
|
||||
true
|
||||
);
|
||||
|
@ -7,6 +7,7 @@ use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\SendingQueue;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Models\SubscriberSegment;
|
||||
use MailPoet\Segments\SubscribersFinder;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\Newsletter\Scheduler\Scheduler as NewsletterScheduler;
|
||||
|
||||
@ -73,12 +74,11 @@ class Scheduler {
|
||||
if(empty($segments)) {
|
||||
return $this->deleteQueueOrUpdateNextRunDate($queue, $newsletter);
|
||||
}
|
||||
$segment_ids = array_map(function($segment) {
|
||||
return (int)$segment['id'];
|
||||
}, $segments);
|
||||
|
||||
// ensure that subscribers are in segments
|
||||
$subscribers = Subscriber::getSubscribedInSegments($segment_ids)->findArray();
|
||||
|
||||
$finder = new SubscribersFinder();
|
||||
$subscribers = $finder->getSubscribersByList($segments);
|
||||
$subscribers = Helpers::flattenArray($subscribers);
|
||||
|
||||
if(empty($subscribers)) {
|
||||
|
@ -10,6 +10,7 @@ use MailPoet\Models\Newsletter as NewsletterModel;
|
||||
use MailPoet\Models\SendingQueue as SendingQueueModel;
|
||||
use MailPoet\Models\StatisticsNewsletters as StatisticsNewslettersModel;
|
||||
use MailPoet\Models\Subscriber as SubscriberModel;
|
||||
use MailPoet\Segments\SubscribersFinder;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -51,10 +52,11 @@ class SendingQueue {
|
||||
foreach($subscriber_batches as $subscribers_to_process_ids) {
|
||||
if(!empty($newsletter_segments_ids[0])) {
|
||||
// Check that subscribers are in segments
|
||||
$found_subscribers = SubscriberModel::findSubscribersInSegments(
|
||||
$subscribers_to_process_ids, $newsletter_segments_ids
|
||||
)->findMany();
|
||||
$found_subscribers_ids = SubscriberModel::extractSubscribersIds($found_subscribers);
|
||||
$finder = new SubscribersFinder();
|
||||
$found_subscribers_ids = $finder->findSubscribersInSegments($subscribers_to_process_ids, $newsletter_segments_ids);
|
||||
$found_subscribers = SubscriberModel::whereIn('id', $subscribers_to_process_ids)
|
||||
->whereNull('deleted_at')
|
||||
->findMany();
|
||||
} else {
|
||||
// No segments = Welcome emails
|
||||
$found_subscribers = SubscriberModel::whereIn('id', $subscribers_to_process_ids)
|
||||
|
@ -26,6 +26,7 @@ abstract class Base {
|
||||
|
||||
if(!empty($block['params']['required'])) {
|
||||
$rules['required'] = true;
|
||||
$rules['required-message'] = __('This field is required.', 'mailpoet');
|
||||
}
|
||||
|
||||
if(!empty($block['params']['validate'])) {
|
||||
|
@ -41,7 +41,7 @@ class Renderer {
|
||||
static function renderBlocks($blocks = array(), $honeypot_enabled = true) {
|
||||
// add honeypot for spambots
|
||||
$html = ($honeypot_enabled) ?
|
||||
'<label class="mailpoet_hp_email_label">' . __('Please leave this field empty', 'mailpoet') . '<input type="email" name="data[email]"></label>' :
|
||||
'<label class="mailpoet_hp_email_label">' . __('Please leave this field empty', 'mailpoet') . '<input autocomplete="not-really-email" type="email" name="data[email]"></label>' :
|
||||
'';
|
||||
foreach($blocks as $key => $block) {
|
||||
$html .= static::renderBlock($block) . PHP_EOL;
|
||||
|
@ -1,10 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Helpscout;
|
||||
|
||||
use MailPoet\Cron\CronHelper;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Router\Endpoints\CronDaemon;
|
||||
use MailPoet\Router\Router;
|
||||
use MailPoet\Services\Bridge;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
@ -16,11 +17,6 @@ class Beacon {
|
||||
$mta = Setting::getValue('mta');
|
||||
$current_theme = wp_get_theme();
|
||||
$current_user = wp_get_current_user();
|
||||
$cron_ping_url = Router::buildRequest(
|
||||
CronDaemon::ENDPOINT,
|
||||
CronDaemon::ACTION_PING
|
||||
);
|
||||
$cron_ping_url = str_replace(home_url(), CronHelper::getSiteUrl(), $cron_ping_url);
|
||||
$premium_key = Setting::getValue(Bridge::PREMIUM_KEY_SETTING_NAME) ?: Setting::getValue(Bridge::API_KEY_SETTING_NAME);
|
||||
return array(
|
||||
'name' => $current_user->display_name,
|
||||
@ -51,7 +47,9 @@ class Beacon {
|
||||
$mta['frequency']['interval']
|
||||
),
|
||||
'Task Scheduler method' => Setting::getValue('cron_trigger.method'),
|
||||
'Cron ping URL' => $cron_ping_url,
|
||||
'Cron ping URL' => CronHelper::getCronUrl(
|
||||
CronDaemon::ACTION_PING
|
||||
),
|
||||
'Default FROM address' => Setting::getValue('sender.address'),
|
||||
'Default Reply-To address' => Setting::getValue('reply_to.address'),
|
||||
'Bounce Email Address' => Setting::getValue('bounce.address'),
|
||||
|
@ -152,6 +152,7 @@ class Segment extends Model {
|
||||
|
||||
static function getSegmentsWithSubscriberCount($type = self::TYPE_DEFAULT) {
|
||||
$query = self::selectMany(array(self::$_table.'.id', self::$_table.'.name'))
|
||||
->whereIn('type', array(Segment::TYPE_WP_USERS, Segment::TYPE_DEFAULT))
|
||||
->selectExpr(
|
||||
self::$_table.'.*, ' .
|
||||
'COUNT(IF('.
|
||||
@ -209,6 +210,15 @@ class Segment extends Model {
|
||||
)->findArray();
|
||||
}
|
||||
|
||||
static function listingQuery(array $data = array()) {
|
||||
$query = self::select('*');
|
||||
$query->whereIn('type', array(Segment::TYPE_WP_USERS, Segment::TYPE_DEFAULT));
|
||||
if(isset($data['group'])) {
|
||||
$query->filter('groupBy', $data['group']);
|
||||
}
|
||||
return $query;
|
||||
}
|
||||
|
||||
static function createOrUpdate($data = array()) {
|
||||
$segment = false;
|
||||
|
||||
|
@ -261,6 +261,7 @@ class Subscriber extends Model {
|
||||
|
||||
$segments = Segment::orderByAsc('name')
|
||||
->whereNull('deleted_at')
|
||||
->whereIn('type', array(Segment::TYPE_WP_USERS, Segment::TYPE_DEFAULT))
|
||||
->findMany();
|
||||
$segment_list = array();
|
||||
$segment_list[] = array(
|
||||
|
8
lib/Models/SubscriberIP.php
Normal file
8
lib/Models/SubscriberIP.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace MailPoet\Models;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class SubscriberIP extends Model {
|
||||
public static $_table = MP_SUBSCRIBER_IPS_TABLE;
|
||||
}
|
@ -43,7 +43,7 @@ class MetaInformationManager {
|
||||
// Get categories
|
||||
$categories = wp_get_post_terms(
|
||||
$post_id,
|
||||
array('post_tag', 'category'),
|
||||
array('category'),
|
||||
array('fields' => 'names')
|
||||
);
|
||||
if(!empty($categories)) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace MailPoet\Newsletter\Renderer\Blocks;
|
||||
|
||||
use MailPoet\Logger;
|
||||
use MailPoet\Newsletter\Renderer\Columns\ColumnsHelper;
|
||||
use MailPoet\Newsletter\Renderer\StylesHelper;
|
||||
|
||||
@ -9,6 +10,9 @@ class Image {
|
||||
if(empty($element['src'])) {
|
||||
return '';
|
||||
}
|
||||
if(substr($element['src'], 0, 1) == '/' && substr($element['src'], 1, 1) != '/') {
|
||||
$element['src'] = get_option('siteurl') . $element['src'];
|
||||
}
|
||||
|
||||
$element['width'] = (int)$element['width'];
|
||||
$element['height'] = (int)$element['height'];
|
||||
@ -20,9 +24,13 @@ class Image {
|
||||
if(!empty($element['link'])) {
|
||||
$image_template = '<a href="' . $element['link'] . '">' . $image_template . '</a>';
|
||||
}
|
||||
$align = 'center';
|
||||
if(!empty($element['styles']['block']['textAlign']) && in_array($element['styles']['block']['textAlign'], array('left', 'right'))) {
|
||||
$align = $element['styles']['block']['textAlign'];
|
||||
}
|
||||
$template = '
|
||||
<tr>
|
||||
<td class="mailpoet_image ' . (($element['fullWidth'] === false) ? 'mailpoet_padded_bottom mailpoet_padded_side' : '') . '" align="center" valign="top">
|
||||
<td class="mailpoet_image ' . (($element['fullWidth'] === false) ? 'mailpoet_padded_bottom mailpoet_padded_side' : '') . '" align="' . $align . '" valign="top">
|
||||
' . $image_template . '
|
||||
</td>
|
||||
</tr>';
|
||||
|
@ -23,7 +23,7 @@ class Link {
|
||||
case 'subscription_unsubscribe_url':
|
||||
return self::processUrl(
|
||||
$action,
|
||||
SubscriptionUrl::getUnsubscribeUrl($subscriber),
|
||||
SubscriptionUrl::getUnsubscribeUrl($wp_user_preview ? false : $subscriber),
|
||||
$queue,
|
||||
$wp_user_preview
|
||||
);
|
||||
@ -31,7 +31,7 @@ class Link {
|
||||
case 'subscription_manage_url':
|
||||
return self::processUrl(
|
||||
$action,
|
||||
SubscriptionUrl::getManageUrl($subscriber),
|
||||
SubscriptionUrl::getManageUrl($wp_user_preview ? false : $subscriber),
|
||||
$queue,
|
||||
$wp_user_preview
|
||||
);
|
||||
@ -40,7 +40,7 @@ class Link {
|
||||
$url = NewsletterUrl::getViewInBrowserUrl(
|
||||
$type = null,
|
||||
$newsletter,
|
||||
$subscriber,
|
||||
$wp_user_preview ? false : $subscriber,
|
||||
$queue,
|
||||
$wp_user_preview
|
||||
);
|
||||
@ -63,7 +63,8 @@ class Link {
|
||||
}
|
||||
|
||||
static function processUrl($action, $url, $queue, $wp_user_preview = false) {
|
||||
if($wp_user_preview) return '#';
|
||||
if ($wp_user_preview)
|
||||
return $url;
|
||||
return ($queue !== false && (boolean)Setting::getValue('tracking.enabled')) ?
|
||||
self::getFullShortcode($action) :
|
||||
$url;
|
||||
@ -111,4 +112,4 @@ class Link {
|
||||
private static function getFullShortcode($action) {
|
||||
return sprintf('[link:%s]', $action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
71
lib/Segments/SubscribersFinder.php
Normal file
71
lib/Segments/SubscribersFinder.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Segments;
|
||||
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\WP\Hooks;
|
||||
|
||||
class SubscribersFinder {
|
||||
|
||||
function findSubscribersInSegments($subscribers_to_process_ids, $newsletter_segments_ids) {
|
||||
$result = array();
|
||||
foreach($newsletter_segments_ids as $segment_id) {
|
||||
$segment = Segment::find_one($segment_id)->asArray();
|
||||
$result = array_merge($result, $this->findSubscribersInSegment($segment, $subscribers_to_process_ids));
|
||||
}
|
||||
return $this->unique($result);
|
||||
}
|
||||
|
||||
private function findSubscribersInSegment($segment, $subscribers_to_process_ids) {
|
||||
if($segment['type'] === Segment::TYPE_DEFAULT || $segment['type'] === Segment::TYPE_WP_USERS) {
|
||||
$subscribers = Subscriber::findSubscribersInSegments($subscribers_to_process_ids, array($segment['id']))->findMany();
|
||||
return Subscriber::extractSubscribersIds($subscribers);
|
||||
}
|
||||
$finders = Hooks::applyFilters('mailpoet_get_subscribers_in_segment_finders', array());
|
||||
foreach($finders as $finder) {
|
||||
$subscribers = $finder->findSubscribersInSegment($segment, $subscribers_to_process_ids);
|
||||
if($subscribers) {
|
||||
return $subscribers;
|
||||
}
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
function getSubscribersByList($segments) {
|
||||
$result = array();
|
||||
foreach($segments as $segment) {
|
||||
$result = array_merge($result, $this->getSubscribers($segment));
|
||||
}
|
||||
return $this->unique($result);
|
||||
}
|
||||
|
||||
private function getSubscribers($segment) {
|
||||
if($segment['type'] === Segment::TYPE_DEFAULT || $segment['type'] === Segment::TYPE_WP_USERS) {
|
||||
return Subscriber::getSubscribedInSegments(array($segment['id']))->findArray();
|
||||
}
|
||||
$finders = Hooks::applyFilters('mailpoet_get_subscribers_in_segment_finders', array());
|
||||
foreach($finders as $finder) {
|
||||
$subscribers = $finder->getSubscriberIdsInSegment($segment);
|
||||
if($subscribers) {
|
||||
return $subscribers;
|
||||
}
|
||||
}
|
||||
return array();
|
||||
}
|
||||
|
||||
private function unique($subscribers) {
|
||||
$result = array();
|
||||
foreach($subscribers as $subscriber) {
|
||||
if(is_a($subscriber, 'MailPoet\Models\Model')) {
|
||||
$result[$subscriber->id] = $subscriber;
|
||||
} elseif(is_scalar($subscriber)) {
|
||||
$result[$subscriber] = $subscriber;
|
||||
} else {
|
||||
$result[$subscriber['id']] = $subscriber;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
@ -80,86 +80,77 @@ class WP {
|
||||
|
||||
static function synchronizeUsers() {
|
||||
|
||||
self::updateSubscriberWPUserIds();
|
||||
self::updateSubscribersEmails();
|
||||
self::insertSubscribers();
|
||||
self::removeFromTrash();
|
||||
self::updateFirstNames();
|
||||
self::updateLastNames();
|
||||
self::updateFristNameIfMissing();
|
||||
self::updateFirstNameIfMissing();
|
||||
self::insertUsersToSegment();
|
||||
self::removeOrphanedSubscribers();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static function updateSubscriberWPUserIds() {
|
||||
global $wpdb;
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
UPDATE IGNORE %s mps
|
||||
JOIN %s as wu ON mps.email = wu.user_email
|
||||
SET mps.wp_user_id = wu.ID
|
||||
WHERE mps.wp_user_id IS NULL
|
||||
', $subscribers_table, $wpdb->users));
|
||||
}
|
||||
|
||||
private static function updateSubscribersEmails() {
|
||||
global $wpdb;
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
UPDATE IGNORE %s
|
||||
JOIN %s as wu ON %s.wp_user_id = wu.id
|
||||
SET email = user_email
|
||||
WHERE %s.wp_user_id IS NOT NULL
|
||||
', $subscribers_table, $wpdb->users, $subscribers_table, $subscribers_table));
|
||||
UPDATE IGNORE %1$s
|
||||
JOIN %2$s as wu ON %1$s.wp_user_id = wu.id
|
||||
SET %1$s.email = wu.user_email
|
||||
WHERE %1$s.wp_user_id IS NOT NULL AND wu.user_email != ""
|
||||
', $subscribers_table, $wpdb->users));
|
||||
}
|
||||
|
||||
private static function insertSubscribers() {
|
||||
global $wpdb;
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
INSERT IGNORE INTO %s(wp_user_id, email, status, created_at)
|
||||
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
|
||||
', $subscribers_table, $wpdb->users, $subscribers_table));
|
||||
INSERT IGNORE INTO %1$s(wp_user_id, email, status, created_at)
|
||||
SELECT wu.id, wu.user_email, "subscribed", CURRENT_TIMESTAMP() FROM %2$s wu
|
||||
LEFT JOIN %1$s mps ON wu.id = mps.wp_user_id
|
||||
WHERE mps.wp_user_id IS NULL AND wu.user_email != ""
|
||||
ON DUPLICATE KEY UPDATE wp_user_id = wu.id
|
||||
', $subscribers_table, $wpdb->users));
|
||||
}
|
||||
|
||||
private static function updateFirstNames() {
|
||||
global $wpdb;
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
UPDATE %s
|
||||
JOIN %s as wpum ON %s.wp_user_id = wpum.user_id AND meta_key = "first_name"
|
||||
SET first_name = meta_value
|
||||
WHERE %s.first_name = ""
|
||||
AND %s.wp_user_id IS NOT NULL
|
||||
', $subscribers_table, $wpdb->usermeta, $subscribers_table, $subscribers_table, $subscribers_table));
|
||||
UPDATE %1$s
|
||||
JOIN %2$s as wpum ON %1$s.wp_user_id = wpum.user_id AND wpum.meta_key = "first_name"
|
||||
SET %1$s.first_name = wpum.meta_value
|
||||
WHERE %1$s.first_name = ""
|
||||
AND %1$s.wp_user_id IS NOT NULL
|
||||
AND wpum.meta_value IS NOT NULL
|
||||
', $subscribers_table, $wpdb->usermeta));
|
||||
}
|
||||
|
||||
private static function updateLastNames() {
|
||||
global $wpdb;
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
UPDATE %s
|
||||
JOIN %s as wpum ON %s.wp_user_id = wpum.user_id AND meta_key = "last_name"
|
||||
SET last_name = meta_value
|
||||
WHERE %s.last_name = ""
|
||||
AND %s.wp_user_id IS NOT NULL
|
||||
', $subscribers_table, $wpdb->usermeta, $subscribers_table, $subscribers_table, $subscribers_table));
|
||||
UPDATE %1$s
|
||||
JOIN %2$s as wpum ON %1$s.wp_user_id = wpum.user_id AND wpum.meta_key = "last_name"
|
||||
SET %1$s.last_name = wpum.meta_value
|
||||
WHERE %1$s.last_name = ""
|
||||
AND %1$s.wp_user_id IS NOT NULL
|
||||
AND wpum.meta_value IS NOT NULL
|
||||
', $subscribers_table, $wpdb->usermeta));
|
||||
}
|
||||
|
||||
private static function updateFristNameIfMissing() {
|
||||
private static function updateFirstNameIfMissing() {
|
||||
global $wpdb;
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
UPDATE %s
|
||||
JOIN %s wu ON %s.wp_user_id = wu.id
|
||||
SET first_name = display_name
|
||||
WHERE %s.first_name = ""
|
||||
AND %s.wp_user_id IS NOT NULL
|
||||
', $subscribers_table, $wpdb->users, $subscribers_table, $subscribers_table, $subscribers_table));
|
||||
UPDATE %1$s
|
||||
JOIN %2$s wu ON %1$s.wp_user_id = wu.id
|
||||
SET %1$s.first_name = wu.display_name
|
||||
WHERE %1$s.first_name = ""
|
||||
AND %1$s.wp_user_id IS NOT NULL
|
||||
', $subscribers_table, $wpdb->users));
|
||||
}
|
||||
|
||||
private static function insertUsersToSegment() {
|
||||
@ -176,10 +167,10 @@ class WP {
|
||||
private static function removeFromTrash() {
|
||||
$subscribers_table = Subscriber::$_table;
|
||||
Subscriber::raw_execute(sprintf('
|
||||
UPDATE %s
|
||||
SET deleted_at = NULL
|
||||
WHERE %s.wp_user_id IS NOT NULL
|
||||
', $subscribers_table, $subscribers_table));
|
||||
UPDATE %1$s
|
||||
SET %1$s.deleted_at = NULL
|
||||
WHERE %1$s.wp_user_id IS NOT NULL
|
||||
', $subscribers_table));
|
||||
}
|
||||
|
||||
private static function removeOrphanedSubscribers() {
|
||||
@ -191,7 +182,7 @@ class WP {
|
||||
|
||||
$wp_segment->subscribers()
|
||||
->leftOuterJoin($wpdb->users, array(MP_SUBSCRIBERS_TABLE . '.wp_user_id', '=', 'wu.id'), 'wu')
|
||||
->whereNull('wu.id')
|
||||
->whereRaw('(wu.id IS NULL OR ' . MP_SUBSCRIBERS_TABLE . '.email = "")')
|
||||
->findResultSet()
|
||||
->set('wp_user_id', null)
|
||||
->delete();
|
||||
|
55
lib/Subscription/Throttling.php
Normal file
55
lib/Subscription/Throttling.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
namespace MailPoet\Subscription;
|
||||
|
||||
use MailPoet\Models\SubscriberIP;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\WP\Hooks;
|
||||
|
||||
class Throttling {
|
||||
static function throttle() {
|
||||
$subscription_limit_enabled = Hooks::applyFilters('mailpoet_subscription_limit_enabled', true);
|
||||
|
||||
$subscription_limit_window = Hooks::applyFilters('mailpoet_subscription_limit_window', DAY_IN_SECONDS);
|
||||
$subscription_limit_base = Hooks::applyFilters('mailpoet_subscription_limit_base', MINUTE_IN_SECONDS);
|
||||
|
||||
$subscriber_ip = Helpers::getIP();
|
||||
|
||||
if($subscription_limit_enabled && !is_user_logged_in()) {
|
||||
if(!empty($subscriber_ip)) {
|
||||
$subscription_count = SubscriberIP::where('ip', $subscriber_ip)
|
||||
->whereRaw(
|
||||
'(`created_at` >= NOW() - INTERVAL ? SECOND)',
|
||||
array((int)$subscription_limit_window)
|
||||
)->count();
|
||||
|
||||
if($subscription_count > 0) {
|
||||
$timeout = $subscription_limit_base * pow(2, $subscription_count - 1);
|
||||
$existing_user = SubscriberIP::where('ip', $subscriber_ip)
|
||||
->whereRaw(
|
||||
'(`created_at` >= NOW() - INTERVAL ? SECOND)',
|
||||
array((int)$timeout)
|
||||
)->findOne();
|
||||
|
||||
if(!empty($existing_user)) {
|
||||
return $timeout;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$ip = SubscriberIP::create();
|
||||
$ip->ip = $subscriber_ip;
|
||||
$ip->save();
|
||||
|
||||
self::purge($subscription_limit_window);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static function purge($interval) {
|
||||
return SubscriberIP::whereRaw(
|
||||
'(`created_at` < NOW() - INTERVAL ? SECOND)',
|
||||
array($interval)
|
||||
)->deleteMany();
|
||||
}
|
||||
}
|
@ -10,6 +10,10 @@ class Hooks {
|
||||
return self::callWithFallback('apply_filters', func_get_args());
|
||||
}
|
||||
|
||||
static function removeFilter() {
|
||||
return self::callWithFallback('remove_filter', func_get_args());
|
||||
}
|
||||
|
||||
static function addAction() {
|
||||
return self::callWithFallback('add_action', func_get_args());
|
||||
}
|
||||
@ -18,6 +22,10 @@ class Hooks {
|
||||
return self::callWithFallback('do_action', func_get_args());
|
||||
}
|
||||
|
||||
static function removeAction() {
|
||||
return self::callWithFallback('remove_action', func_get_args());
|
||||
}
|
||||
|
||||
private static function callWithFallback($func, $args) {
|
||||
$local_func = __NAMESPACE__ . '\\' . $func;
|
||||
if(function_exists($local_func)) {
|
||||
|
@ -3,8 +3,8 @@
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
/*
|
||||
* Plugin Name: MailPoet 3 (new)
|
||||
* Version: 3.0.2
|
||||
* Plugin Name: MailPoet 3 (New)
|
||||
* Version: 3.1.0
|
||||
* Plugin URI: http://www.mailpoet.com
|
||||
* Description: Create and send newsletters, post notifications and welcome emails from your WordPress.
|
||||
* Author: MailPoet
|
||||
@ -20,7 +20,7 @@ if(!defined('ABSPATH')) exit;
|
||||
*/
|
||||
|
||||
$mailpoet_plugin = array(
|
||||
'version' => '3.0.2',
|
||||
'version' => '3.1.0',
|
||||
'filename' => __FILE__,
|
||||
'path' => dirname(__FILE__),
|
||||
'autoloader' => dirname(__FILE__) . '/vendor/autoload.php',
|
||||
|
38
readme.txt
38
readme.txt
@ -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.2
|
||||
Stable tag: 3.1.0
|
||||
License: GPLv3
|
||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
@ -114,6 +114,42 @@ Stop by our [support site](https://www.mailpoet.com/support).
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 3.1.0 - 2017-11-07 =
|
||||
* Added: a method to create a new list via our public API;
|
||||
* Fixed: javascript files are loaded with a dependency on jquery; Thanks George!
|
||||
* Fixed: WP users sync no longer chokes on NULL values for first/last names. Thanks @cartpauj!
|
||||
* Fixed: superadmin users on Multisite installations can always access MailPoet on subsites. Thanks Ryan!
|
||||
|
||||
= 3.0.9 - 2017-10-31 =
|
||||
* Improved: search forms in listings ignore preceding and trailing whitespace;
|
||||
* Fixed: tags aren't shown within categories for automated latest content posts anymore. Thanks Gregor!
|
||||
* Fixed: our subscription form no longer conflicts with themes/plugins that use jQuery Serialize Object function. Thanks Albert!
|
||||
|
||||
= 3.0.8 - 2017-10-24 =
|
||||
* Improved: unsubscribe link now works in browser and email previews;
|
||||
* Improved: relative URLs in images are replaced by absolute URLs. Thanks Xavier!
|
||||
* Fixed: default email templates to be translated into your language. Thanks Webteam!
|
||||
* Fixed: subscription form's "required field" validation message is now translatable. Thanks, Frank!
|
||||
* Fixed: plugin doesn't fail to activate when WP users table contains custom columns. Thanks Brian!
|
||||
|
||||
= 3.0.7 - 2017-10-17 =
|
||||
* Improved: subscribing from the same IP address is progressively throttled. Thanks Suyog Palav, Piyush Kumar and Bits of Freedom!
|
||||
* Fixed: WordPress users without an email address will not be added as subscribers;
|
||||
* Fixed: bug asking subscribers to leave the first field empty in MailPoet subscription forms;
|
||||
* Fixed: plugin no longer fails to activate on sites when certain user roles do not exist. Thanks to all who reported this!
|
||||
|
||||
= 3.0.6 - 2017-10-10 =
|
||||
* Fixed: subscription forms to not throw form validation engine errors;
|
||||
|
||||
= 3.0.5 - 2017-10-10 =
|
||||
* Added: images can now be aligned left, center or right in email designer;
|
||||
|
||||
= 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;
|
||||
|
@ -25,6 +25,7 @@ $models = array(
|
||||
'Subscriber',
|
||||
'SubscriberCustomField',
|
||||
'SubscriberSegment',
|
||||
'SubscriberIP',
|
||||
'StatisticsOpens',
|
||||
'StatisticsClicks',
|
||||
'StatisticsNewsletters',
|
||||
|
1284
tests/_data/acceptanceDump.sql
Normal file
1284
tests/_data/acceptanceDump.sql
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user