Compare commits
182 Commits
Author | SHA1 | Date | |
---|---|---|---|
57366e972a | |||
884f476598 | |||
55296d52ec | |||
8fb4d2e78f | |||
840473aae5 | |||
0e2a67c203 | |||
aaed5add52 | |||
f616ef4d6c | |||
083a6fd3a3 | |||
9a1ec60750 | |||
bd67f5fd36 | |||
ed2a2a6fa8 | |||
6a28fdcac2 | |||
fda1828637 | |||
ebcc094b4d | |||
51cde55217 | |||
23dff7403d | |||
de164f918c | |||
84840c5261 | |||
16b9e0d467 | |||
3c75e7b0bc | |||
c41d5894d0 | |||
1f76f0d98f | |||
e859c090c8 | |||
42528ab309 | |||
d761867a7d | |||
6996615999 | |||
3450a6fc97 | |||
554dbb8dda | |||
d13aa67a07 | |||
1f9c956637 | |||
59d09866a1 | |||
f8ade27a6c | |||
313bcddba1 | |||
fd64702eaa | |||
e2884ac625 | |||
9ce9cb929f | |||
52d33d7fe8 | |||
2ee33b2fc2 | |||
b107be3241 | |||
76148d0a85 | |||
61a7f99e42 | |||
62e3ca8a80 | |||
d431efb288 | |||
adf6485b7d | |||
170fd7f051 | |||
e7ddfc3d29 | |||
b08b53e9c0 | |||
570529a8d8 | |||
5505ffc783 | |||
6e5fc6ad6b | |||
e731b261ab | |||
955f24e71a | |||
89ebc6051a | |||
79fff7b65d | |||
7864e08900 | |||
878cdf46e6 | |||
131d341744 | |||
513062b15d | |||
4926267e36 | |||
aa4d78b1c9 | |||
8d7289e8ba | |||
eb768ff1ee | |||
8afe7f5d97 | |||
97fb5cf66f | |||
8ea4a219e2 | |||
0e08e58288 | |||
dc52ba0d93 | |||
dc569672a9 | |||
159e946093 | |||
3d2a433df1 | |||
fe0476e1c0 | |||
ea552508b4 | |||
dee2ff810c | |||
63ed835d64 | |||
fbf58f23fc | |||
b6f62bd9bc | |||
88ef454844 | |||
3572df6c0c | |||
714e6e013f | |||
91568cbeb6 | |||
393c89b21f | |||
0b491b7943 | |||
e5d7f66561 | |||
b7a7f40cde | |||
a267c7524c | |||
20b59d11a7 | |||
94c7e2a5c0 | |||
d603d99a04 | |||
e7ffe4d694 | |||
018d7bce77 | |||
64c40d5a1c | |||
7e49328d5e | |||
b50b636371 | |||
ec3bb5b95c | |||
6b2503fb36 | |||
e71d47f983 | |||
085d4f566a | |||
29b249de6e | |||
883ae5b0e4 | |||
d78990cda3 | |||
e5b5a8df37 | |||
2ea2db66ec | |||
7b76ddefd5 | |||
6c56ac8509 | |||
dc074bcb14 | |||
0193644b18 | |||
66de11ecd4 | |||
1238b1711d | |||
0061a9daf9 | |||
52a55d3bd1 | |||
7d686eb1d1 | |||
962188bdb8 | |||
72c3f763ec | |||
44c5d8490f | |||
fb85623b86 | |||
d7bf6addf1 | |||
adea1e9be1 | |||
4f77ca31a3 | |||
087f2ebcdd | |||
3d2a63f319 | |||
54dd3b621a | |||
7fea134109 | |||
fcf4bd12d9 | |||
6694555592 | |||
e6eb3d691e | |||
00f2d418cc | |||
c63f218edd | |||
fae849bbfc | |||
2a253ccb8d | |||
ff55e55ad2 | |||
d798ec446d | |||
56b4038073 | |||
076a55f1fb | |||
10eca98dc3 | |||
37aec3ee4f | |||
dc7c629e3b | |||
e4f16eda49 | |||
bf15bda84f | |||
8472a837e8 | |||
fc326131ae | |||
a19753205f | |||
ee404e3b84 | |||
f1918ac953 | |||
d399ddf6b6 | |||
3f06448f37 | |||
83d84e67d6 | |||
46c42c1bb4 | |||
944bf67190 | |||
7cac061a73 | |||
17764b708f | |||
2e0f4dfb19 | |||
7f5bc8681c | |||
bfcb85f744 | |||
cb9aaf120e | |||
97a9465db3 | |||
8dfaf9ba32 | |||
89d0da93d3 | |||
c9cbfb4317 | |||
a8ccfec5c6 | |||
d8609c9e84 | |||
5461c975d4 | |||
11298bc101 | |||
c42cf2f622 | |||
c9f1d38baa | |||
3d78d6bbe9 | |||
2b4288f301 | |||
09a2dd231a | |||
3fd4ef9985 | |||
05979965ba | |||
e625a7602a | |||
a0c41ad7ab | |||
7163747eb9 | |||
c30e2b6cf3 | |||
e9eae92ba9 | |||
0fd6fa8879 | |||
52ca6eac18 | |||
28776b8558 | |||
588ad3eab7 | |||
d791538086 | |||
f39a2c8dda | |||
cd145c51f7 |
@ -1,6 +1,4 @@
|
||||
Listen 8080
|
||||
|
||||
<VirtualHost *:8080>
|
||||
<VirtualHost *:80>
|
||||
UseCanonicalName Off
|
||||
ServerName mailpoet.loc
|
||||
DocumentRoot /home/circleci/mailpoet/wordpress
|
||||
@ -8,6 +6,9 @@ Listen 8080
|
||||
LogLevel notice
|
||||
|
||||
<Directory /home/circleci/mailpoet/wordpress>
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride All
|
||||
RewriteEngine On
|
||||
Require all granted
|
||||
</Directory>
|
||||
</VirtualHost>
|
||||
|
@ -1,6 +1,6 @@
|
||||
version: 2
|
||||
jobs:
|
||||
qa_js_php5:
|
||||
qa_js_security_php5:
|
||||
working_directory: /home/circleci/mailpoet
|
||||
docker:
|
||||
- image: circleci/php:5.6.30-apache-browsers
|
||||
@ -38,6 +38,10 @@ jobs:
|
||||
command: |
|
||||
mkdir test-results/mocha
|
||||
./do t:j test-results/mocha/junit.xml
|
||||
- run:
|
||||
name: "Composer security check"
|
||||
command: |
|
||||
./do s:composer
|
||||
- run:
|
||||
name: "PHP Unit tests"
|
||||
command: |
|
||||
@ -120,7 +124,38 @@ jobs:
|
||||
- run:
|
||||
name: "PHP Unit tests"
|
||||
command: |
|
||||
WP_TEST_PATH="/home/circleci/mailpoet/wordpress" ./do t:u --xml
|
||||
./do t:u --xml
|
||||
- store_test_results:
|
||||
path: tests/_output
|
||||
- store_artifacts:
|
||||
path: tests/_output
|
||||
destination: codeception
|
||||
- store_artifacts:
|
||||
path: /tmp/fake-mailer/
|
||||
destination: fake-mailer
|
||||
php7_multisite:
|
||||
working_directory: /home/circleci/mailpoet
|
||||
docker:
|
||||
- image: circleci/php:7.1-apache-browsers
|
||||
- image: circleci/mysql:5.7
|
||||
environment:
|
||||
TZ: /usr/share/zoneinfo/Etc/UTC
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: "Set up virtual host"
|
||||
command: echo 127.0.0.1 mailpoet.loc | sudo tee -a /etc/hosts
|
||||
- restore_cache:
|
||||
key: composer-{{ checksum "composer.json" }}-{{ checksum "composer.lock" }}
|
||||
- restore_cache:
|
||||
key: npm-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: "Set up test environment"
|
||||
command: source ./.circleci/setup.bash && setup php7_multisite
|
||||
- run:
|
||||
name: "PHP Unit tests"
|
||||
command: |
|
||||
./do t:multisite-unit --xml
|
||||
- store_test_results:
|
||||
path: tests/_output
|
||||
- store_artifacts:
|
||||
@ -133,6 +168,7 @@ workflows:
|
||||
version: 2
|
||||
build_and_test:
|
||||
jobs:
|
||||
- qa_js_php5
|
||||
- qa_js_security_php5
|
||||
- php7
|
||||
- acceptance_tests
|
||||
- php7_multisite
|
||||
- acceptance_tests
|
@ -2,8 +2,11 @@
|
||||
|
||||
function setup {
|
||||
local version=$1
|
||||
local wp_cli_wordpress_path="--path=wordpress"
|
||||
local wp_cli_allow_root="--allow-root"
|
||||
|
||||
# install PHP dependencies for WordPress
|
||||
if [[ $version == "php7" ]]; then
|
||||
if [[ $version == "php7" ]] || [[ $version == "php7_multisite" ]]; then
|
||||
echo "deb http://packages.dotdeb.org jessie all" | sudo tee -a /etc/apt/sources.list.d/dotdeb.list
|
||||
echo "deb-src http://packages.dotdeb.org jessie all" | sudo tee -a /etc/apt/sources.list.d/dotdeb.list
|
||||
wget -qO - http://www.dotdeb.org/dotdeb.gpg | sudo apt-key add -
|
||||
@ -15,35 +18,66 @@ function setup {
|
||||
sudo apt-get install mysql-client php5-mysql zlib1g-dev
|
||||
sudo docker-php-ext-install mysql mysqli pdo pdo_mysql zip
|
||||
fi
|
||||
|
||||
# Add a fake sendmail mailer
|
||||
sudo cp ./.circleci/fake-sendmail.php /usr/local/bin/
|
||||
|
||||
# configure Apache
|
||||
sudo cp ./.circleci/mailpoet_php.ini /usr/local/etc/php/conf.d/
|
||||
sudo cp ./.circleci/apache/mailpoet.loc.conf /etc/apache2/sites-available
|
||||
sudo a2dissite 000-default.conf
|
||||
sudo a2ensite mailpoet.loc
|
||||
sudo a2enmod rewrite
|
||||
sudo service apache2 restart
|
||||
|
||||
# Install NodeJS+NPM
|
||||
curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
|
||||
sudo apt-get install nodejs build-essential
|
||||
|
||||
# install plugin dependencies
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
./composer.phar install
|
||||
./do install
|
||||
# Set up Wordpress
|
||||
|
||||
# Set up WordPress
|
||||
mysql -h 127.0.0.1 -u root -e "create database wordpress"
|
||||
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
|
||||
chmod +x wp-cli.phar
|
||||
./wp-cli.phar core download --allow-root --path=wordpress
|
||||
sudo mv wp-cli.phar /usr/local/bin/wp
|
||||
wp core download $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
|
||||
# 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
|
||||
echo "define(\"WP_DEBUG\", true);" | wp core config --dbname=wordpress --dbuser=root --dbhost=127.0.0.1 --extra-php $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
|
||||
# Change default table prefix
|
||||
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
|
||||
if [[ $version == "php7_multisite" ]]; then
|
||||
# Configure multisite environment
|
||||
wp core multisite-install --admin_name=admin --admin_password=admin --admin_email=admin@mailpoet.loc --url=http://mailpoet.loc --title="WordPress MultiSite" $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
cp ./.circleci/wordpress/.htaccess ./wordpress/
|
||||
|
||||
# Add a second blog
|
||||
wp site create --slug=php7_multisite $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
echo "WP_TEST_MULTISITE_SLUG=php7_multisite" >> .env
|
||||
echo "WP_TEST_PATH_MULTISITE=/home/circleci/mailpoet/wordpress" >> .env
|
||||
echo "HTTP_HOST=mailpoet.loc" >> .env
|
||||
|
||||
# Add a third dummy blog
|
||||
wp site create --slug=dummy_multisite $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
else
|
||||
wp core install --admin_name=admin --admin_password=admin --admin_email=admin@mailpoet.loc --url=http://mailpoet.loc --title="WordPress Single" $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
echo "WP_TEST_PATH=/home/circleci/mailpoet/wordpress" >> .env
|
||||
fi
|
||||
|
||||
# Softlink plugin to plugin path
|
||||
ln -s ../../.. wordpress/wp-content/plugins/mailpoet
|
||||
./wp-cli.phar plugin activate mailpoet --path=wordpress
|
||||
# Create .env file with correct path to WP installation
|
||||
# TODO: Remove this line after PR gets merged and CircleCI env variables change
|
||||
echo "WP_TEST_PATH=\"/home/circleci/mailpoet/wordpress\"" > .env
|
||||
}
|
||||
|
||||
# Activate plugin
|
||||
if [[ $version == "php7_multisite" ]]; then
|
||||
wp plugin activate mailpoet --url=http://mailpoet.loc/php7_multisite/ $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
else
|
||||
wp plugin activate mailpoet $wp_cli_wordpress_path $wp_cli_allow_root
|
||||
fi
|
||||
}
|
12
.circleci/wordpress/.htaccess
Normal file
12
.circleci/wordpress/.htaccess
Normal file
@ -0,0 +1,12 @@
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
RewriteRule ^index\.php$ - [L]
|
||||
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?wp-admin$ $1wp-admin/ [R=301,L]
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} -f [OR]
|
||||
RewriteCond %{REQUEST_FILENAME} -d
|
||||
RewriteRule ^ - [L]
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(wp-(content|admin|includes).*) $2 [L]
|
||||
RewriteRule ^([_0-9a-zA-Z-]+/)?(.*\.php)$ $2 [L]
|
||||
RewriteRule . index.php [L]
|
@ -1,4 +1,6 @@
|
||||
WP_TEST_PATH="/var/www/wordpress"
|
||||
WP_TEST_PATH_MULTISITE="/var/www/wordpress"
|
||||
WP_TEST_MULTISITE_SLUG=""
|
||||
WP_TEST_ENABLE_NETWORK_TESTS="true"
|
||||
WP_TEST_IMPORT_MAILCHIMP_API=""
|
||||
WP_TEST_IMPORT_MAILCHIMP_LISTS="" // (separated with comma)
|
||||
@ -16,4 +18,5 @@ WP_TEST_MAILER_SMTP_LOGIN=""
|
||||
WP_TEST_MAILER_SMTP_PASSWORD=""
|
||||
WP_SVN_USERNAME=""
|
||||
WP_SVN_PASSWORD=""
|
||||
WP_TRANSIFEX_API_TOKEN=""
|
||||
WP_TRANSIFEX_API_TOKEN=""
|
||||
HTTP_HOST="" // URL of your site (used for multisite env and equals to DOMAIN_CURRENT_SITE from wp-config.php)
|
@ -8,6 +8,9 @@
|
||||
"ecmaVersion": 5
|
||||
},
|
||||
"rules": {
|
||||
// Exceptions
|
||||
"no-underscore-dangle": 0, // Backbone uses underscores, we cannot remove them
|
||||
// Temporary
|
||||
"import/no-amd": 0,
|
||||
"prefer-arrow-callback": 0,
|
||||
"radix": 0,
|
||||
@ -37,14 +40,7 @@
|
||||
"global-require": 0,
|
||||
"no-throw-literal": 0,
|
||||
"no-extra-bind": 0,
|
||||
"consistent-return": 0,
|
||||
"no-shadow": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"brace-style": 0,
|
||||
"no-else-return": 0,
|
||||
"no-use-before-define": 0,
|
||||
"camelcase": 0,
|
||||
"padded-blocks": 0,
|
||||
"strict": 0,
|
||||
"space-infix-ops": 0,
|
||||
"object-shorthand": 0,
|
||||
|
@ -14,7 +14,11 @@
|
||||
"import/resolver": "webpack"
|
||||
},
|
||||
"rules": {
|
||||
// Exceptions
|
||||
"comma-dangle": ["error", "always-multiline"],
|
||||
"no-script-url": 0,
|
||||
"import/extensions": 0, // we wouldn't be able to import jQuery without this line
|
||||
// Temporary
|
||||
"import/no-amd": 0,
|
||||
"react/no-multi-comp": 0,
|
||||
"react/sort-comp": 0,
|
||||
@ -37,34 +41,8 @@
|
||||
"jsx-a11y/alt-text": 0,
|
||||
"func-names": 0,
|
||||
"object-shorthand": 0,
|
||||
"no-bitwise": 0,
|
||||
"arrow-body-style": 0,
|
||||
"prefer-template": 0,
|
||||
"default-case": 0,
|
||||
"array-callback-return": 0,
|
||||
"consistent-return": 0,
|
||||
"import/extensions": 0,
|
||||
"import/no-extraneous-dependencies": 0,
|
||||
"camelcase": 0,
|
||||
"eqeqeq": 0,
|
||||
"no-lonely-if": 0,
|
||||
"space-unary-ops": 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,
|
||||
"dot-notation": 0,
|
||||
"no-shadow": 0,
|
||||
"no-alert": 0,
|
||||
"no-script-url": 0,
|
||||
"wrap-iife": 0,
|
||||
"space-infix-ops": 0,
|
||||
"no-irregular-whitespace": 0,
|
||||
"padded-blocks": 0,
|
||||
"no-underscore-dangle": 0
|
||||
"no-irregular-whitespace": 0
|
||||
}
|
||||
}
|
||||
|
21
RoboFile.php
21
RoboFile.php
@ -153,17 +153,30 @@ class RoboFile extends \Robo\Tasks {
|
||||
return $this->_exec('./tasks/transifex_init.sh');
|
||||
}
|
||||
|
||||
function testUnit($opts=['file' => null, 'xml' => false]) {
|
||||
function testUnit($opts=['file' => null, 'xml' => false, 'multisite' => false]) {
|
||||
$this->loadEnv();
|
||||
|
||||
$command = 'vendor/bin/codecept run unit -c codeception.unit.yml -f '.(($opts['file']) ? $opts['file'] : '');
|
||||
$command = 'vendor/bin/codecept run unit -c codeception.unit.yml';
|
||||
|
||||
if($opts['multisite']) {
|
||||
$command = 'MULTISITE=true ' . $command;
|
||||
}
|
||||
|
||||
if($opts['file']) {
|
||||
$command .= ' -f ' . $opts['file'];
|
||||
}
|
||||
|
||||
if($opts['xml']) {
|
||||
$command .= ' --xml';
|
||||
}
|
||||
|
||||
return $this->_exec($command);
|
||||
}
|
||||
|
||||
function testMultisiteUnit($opts=['file' => null, 'xml' => false, 'multisite' => true]) {
|
||||
return $this->testUnit($opts);
|
||||
}
|
||||
|
||||
function testCoverage($opts=['file' => null, 'xml' => false]) {
|
||||
$this->loadEnv();
|
||||
$command = join(' ', array(
|
||||
@ -198,6 +211,10 @@ class RoboFile extends \Robo\Tasks {
|
||||
return $this->_exec($command);
|
||||
}
|
||||
|
||||
function securityComposer() {
|
||||
return $this->_exec('vendor/bin/security-checker security:check --format=simple');
|
||||
}
|
||||
|
||||
function testDebug($opts=['file' => null, 'xml' => false]) {
|
||||
$this->loadEnv();
|
||||
$this->_exec('vendor/bin/codecept build -c codeception.unit.yml');
|
||||
|
@ -27,3 +27,5 @@
|
||||
@require 'pages_custom'
|
||||
|
||||
@require 'mp2migrator'
|
||||
|
||||
@require '../../../node_modules/react-confirm-alert/src/react-confirm-alert.css'
|
||||
|
@ -77,7 +77,7 @@ define('date',
|
||||
var escapeToken;
|
||||
var index;
|
||||
var token;
|
||||
var format_mappings = {
|
||||
var formatMappings = {
|
||||
date: {
|
||||
d: 'DD',
|
||||
D: 'ddd',
|
||||
@ -146,7 +146,7 @@ define('date',
|
||||
|
||||
if (!format || format.length <= 0) return format;
|
||||
|
||||
replacements = format_mappings['date'];
|
||||
replacements = formatMappings['date'];
|
||||
convertedFormat = [];
|
||||
escapeToken = false;
|
||||
|
||||
|
@ -16,25 +16,23 @@ define([
|
||||
|
||||
// isChecked will be true only if the value is "1"
|
||||
// it will be false in case value is "0" or empty
|
||||
const isChecked = !!(~~(this.props.item[this.props.field.name]));
|
||||
const isChecked = !!(Number(this.props.item[this.props.field.name]));
|
||||
const options = Object.keys(this.props.field.values).map(
|
||||
(value, index) => {
|
||||
return (
|
||||
<p key={'checkbox-' + index}>
|
||||
<label>
|
||||
<input
|
||||
ref="checkbox"
|
||||
type="checkbox"
|
||||
value="1"
|
||||
checked={isChecked}
|
||||
onChange={this.onValueChange}
|
||||
name={this.props.field.name}
|
||||
(value, index) => (
|
||||
<p key={`checkbox-${index}`}>
|
||||
<label>
|
||||
<input
|
||||
ref="checkbox"
|
||||
type="checkbox"
|
||||
value="1"
|
||||
checked={isChecked}
|
||||
onChange={this.onValueChange}
|
||||
name={this.props.field.name}
|
||||
/>
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -167,6 +167,11 @@ define([
|
||||
year: this.state.year,
|
||||
};
|
||||
break;
|
||||
default:
|
||||
value = {
|
||||
value: 'invalid type',
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
@ -181,7 +186,7 @@ define([
|
||||
field = matches[1];
|
||||
property = matches[2];
|
||||
|
||||
const value = ~~(e.target.value);
|
||||
const value = Number(e.target.value);
|
||||
|
||||
this.setState({
|
||||
[`${property}`]: value,
|
||||
@ -233,6 +238,9 @@ define([
|
||||
day={this.state.day}
|
||||
placeholder={this.props.field.day_placeholder}
|
||||
/>);
|
||||
|
||||
default:
|
||||
return <div>Invalid date type</div>;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -32,7 +32,7 @@ define([
|
||||
let field = false;
|
||||
let dataField = data.field;
|
||||
|
||||
if (data.field['field'] !== undefined) {
|
||||
if (data.field.field !== undefined) {
|
||||
dataField = jQuery.merge(dataField, data.field.field);
|
||||
}
|
||||
|
||||
@ -68,36 +68,37 @@ define([
|
||||
case 'reactComponent':
|
||||
field = (<data.field.component {...data} />);
|
||||
break;
|
||||
|
||||
default:
|
||||
field = 'invalid';
|
||||
break;
|
||||
}
|
||||
|
||||
if (inline === true) {
|
||||
return (
|
||||
<span key={'field-' + (data.index || 0)}>
|
||||
<span key={`field-${data.index || 0}`}>
|
||||
{ field }
|
||||
{ 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;
|
||||
|
||||
if (this.props.field['fields'] !== undefined) {
|
||||
field = this.props.field.fields.map((subfield, index) => {
|
||||
return this.renderField({
|
||||
index: index,
|
||||
field: subfield,
|
||||
item: this.props.item,
|
||||
onValueChange: this.props.onValueChange || false,
|
||||
});
|
||||
});
|
||||
if (this.props.field.fields !== undefined) {
|
||||
field = this.props.field.fields.map((subfield, index) => this.renderField({
|
||||
index: index,
|
||||
field: subfield,
|
||||
item: this.props.item,
|
||||
onValueChange: this.props.onValueChange || false,
|
||||
}));
|
||||
} else {
|
||||
field = this.renderField(this.props);
|
||||
}
|
||||
@ -110,10 +111,10 @@ define([
|
||||
}
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<tr className={`form-field-row-${this.props.field.name}`}>
|
||||
<th scope="row">
|
||||
<label
|
||||
htmlFor={'field_' + this.props.field.name}
|
||||
htmlFor={`field_${this.props.field.name}`}
|
||||
>
|
||||
{ this.props.field.label }
|
||||
{ tip }
|
||||
|
@ -10,23 +10,21 @@ define([
|
||||
return false;
|
||||
}
|
||||
|
||||
const selected_value = this.props.item[this.props.field.name];
|
||||
const selectedValue = this.props.item[this.props.field.name];
|
||||
const options = Object.keys(this.props.field.values).map(
|
||||
(value, index) => {
|
||||
return (
|
||||
<p key={'radio-' + index}>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
checked={selected_value === value}
|
||||
value={value}
|
||||
onChange={this.props.onValueChange}
|
||||
name={this.props.field.name} />
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
);
|
||||
}
|
||||
(value, index) => (
|
||||
<p key={`radio-${index}`}>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
checked={selectedValue === value}
|
||||
value={value}
|
||||
onChange={this.props.onValueChange}
|
||||
name={this.props.field.name} />
|
||||
{ this.props.field.values[value] }
|
||||
</label>
|
||||
</p>
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
|
@ -17,7 +17,7 @@ const FormFieldSelect = React.createClass({
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.field['filter'] !== undefined) {
|
||||
if (this.props.field.filter !== undefined) {
|
||||
filter = this.props.field.filter;
|
||||
}
|
||||
|
||||
@ -41,27 +41,25 @@ const FormFieldSelect = React.createClass({
|
||||
keys = Object.keys(this.props.field.values);
|
||||
}
|
||||
|
||||
const options = keys.map(
|
||||
(value, index) => {
|
||||
|
||||
if (filter !== false && filter(this.props.item, value) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
const options = keys
|
||||
.filter((value) => {
|
||||
if (filter === false) return true;
|
||||
return filter(this.props.item, value);
|
||||
})
|
||||
.map(
|
||||
(value, index) => (
|
||||
<option
|
||||
key={'option-' + index}
|
||||
key={`option-${index}`}
|
||||
value={value}>
|
||||
{ this.props.field.values[value] }
|
||||
</option>
|
||||
);
|
||||
}
|
||||
);
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
<select
|
||||
name={this.props.field.name}
|
||||
id={'field_' + this.props.field.name}
|
||||
id={`field_${this.props.field.name}`}
|
||||
value={this.props.item[this.props.field.name]}
|
||||
onChange={this.props.onValueChange}
|
||||
{...this.props.field.validation}
|
||||
|
@ -26,7 +26,7 @@ define([
|
||||
return (this.state.select2 === true);
|
||||
},
|
||||
componentDidMount: function () {
|
||||
if (this.allowMultipleValues()) {
|
||||
if (this.allowMultipleValues() || this.props.field.forceSelect2) {
|
||||
this.setupSelect2();
|
||||
}
|
||||
},
|
||||
@ -35,19 +35,19 @@ define([
|
||||
(this.props.item !== undefined && prevProps.item !== undefined)
|
||||
&& (this.props.item.id !== prevProps.item.id)
|
||||
) {
|
||||
jQuery('#' + this.refs.select.id)
|
||||
jQuery(`#${this.refs.select.id}`)
|
||||
.val(this.getSelectedValues())
|
||||
.trigger('change');
|
||||
}
|
||||
},
|
||||
componentWillUnmount: function () {
|
||||
if (this.allowMultipleValues()) {
|
||||
if (this.allowMultipleValues() || this.props.field.forceSelect2) {
|
||||
this.destroySelect2();
|
||||
}
|
||||
},
|
||||
destroySelect2: function () {
|
||||
if (this.isSelect2Initialized()) {
|
||||
jQuery('#' + this.refs.select.id).select2('destroy');
|
||||
jQuery(`#${this.refs.select.id}`).select2('destroy');
|
||||
}
|
||||
},
|
||||
setupSelect2: function () {
|
||||
@ -55,18 +55,15 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
const select2 = jQuery('#' + this.refs.select.id).select2({
|
||||
const select2 = jQuery(`#${this.refs.select.id}`).select2({
|
||||
width: (this.props.width || ''),
|
||||
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;
|
||||
},
|
||||
});
|
||||
|
||||
@ -86,14 +83,12 @@ define([
|
||||
this.setState({ select2: true });
|
||||
},
|
||||
getSelectedValues: function () {
|
||||
if (this.props.field['selected'] !== undefined) {
|
||||
return this.props.field['selected'](this.props.item);
|
||||
if (this.props.field.selected !== undefined) {
|
||||
return this.props.field.selected(this.props.item);
|
||||
} else if (this.props.item !== undefined && this.props.field.name !== undefined) {
|
||||
if (this.allowMultipleValues()) {
|
||||
if (Array.isArray(this.props.item[this.props.field.name])) {
|
||||
return this.props.item[this.props.field.name].map((item) => {
|
||||
return item.id;
|
||||
});
|
||||
return this.props.item[this.props.field.name].map(item => item.id);
|
||||
}
|
||||
} else {
|
||||
return this.props.item[this.props.field.name];
|
||||
@ -102,11 +97,15 @@ define([
|
||||
return null;
|
||||
},
|
||||
loadCachedItems: function () {
|
||||
if (typeof (window['mailpoet_' + this.props.field.endpoint]) !== 'undefined') {
|
||||
let items = window['mailpoet_' + this.props.field.endpoint];
|
||||
let items;
|
||||
if (typeof (window[`mailpoet_${this.props.field.endpoint}`]) !== 'undefined') {
|
||||
items = window[`mailpoet_${this.props.field.endpoint}`];
|
||||
} else if (this.props.field.values !== undefined) {
|
||||
items = this.props.field.values;
|
||||
}
|
||||
|
||||
|
||||
if (this.props.field['filter'] !== undefined) {
|
||||
if (Array.isArray(items)) {
|
||||
if (this.props.field.filter !== undefined) {
|
||||
items = items.filter(this.props.field.filter);
|
||||
}
|
||||
|
||||
@ -119,7 +118,7 @@ define([
|
||||
let value;
|
||||
if (this.props.onValueChange !== undefined) {
|
||||
if (this.props.field.multiple) {
|
||||
value = jQuery('#' + this.refs.select.id).val();
|
||||
value = jQuery(`#${this.refs.select.id}`).val();
|
||||
} else {
|
||||
value = e.target.value;
|
||||
}
|
||||
@ -133,19 +132,19 @@ define([
|
||||
}
|
||||
},
|
||||
getLabel: function (item) {
|
||||
if (this.props.field['getLabel'] !== undefined) {
|
||||
if (this.props.field.getLabel !== undefined) {
|
||||
return this.props.field.getLabel(item, this.props.item);
|
||||
}
|
||||
return item.name;
|
||||
},
|
||||
getSearchLabel: function (item) {
|
||||
if (this.props.field['getSearchLabel'] !== undefined) {
|
||||
if (this.props.field.getSearchLabel !== undefined) {
|
||||
return this.props.field.getSearchLabel(item, this.props.item);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
getValue: function (item) {
|
||||
if (this.props.field['getValue'] !== undefined) {
|
||||
if (this.props.field.getValue !== undefined) {
|
||||
return this.props.field.getValue(item, this.props.item);
|
||||
}
|
||||
return item.id;
|
||||
@ -154,11 +153,18 @@ define([
|
||||
// this function may be used to transform the placeholder value into
|
||||
// desired value.
|
||||
transformChangedValue: function (value) {
|
||||
if (typeof this.props.field['transformChangedValue'] === 'function') {
|
||||
if (typeof this.props.field.transformChangedValue === 'function') {
|
||||
return this.props.field.transformChangedValue.call(this, value);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
insertEmptyOption: function () {
|
||||
// https://select2.org/placeholders
|
||||
// For single selects only, in order for the placeholder value to appear,
|
||||
// we must have a blank <option> as the first option in the <select> control.
|
||||
if (this.allowMultipleValues()) return undefined;
|
||||
if (this.props.field.placeholder) return (<option />);
|
||||
return undefined;
|
||||
},
|
||||
render: function () {
|
||||
const options = this.state.items.map((item, index) => {
|
||||
@ -168,7 +174,7 @@ define([
|
||||
|
||||
return (
|
||||
<option
|
||||
key={'option-' + index}
|
||||
key={`option-${index}`}
|
||||
value={value}
|
||||
title={searchLabel}
|
||||
>
|
||||
@ -186,7 +192,10 @@ define([
|
||||
multiple={this.props.field.multiple}
|
||||
defaultValue={this.getSelectedValues()}
|
||||
{...this.props.field.validation}
|
||||
>{ options }</select>
|
||||
>
|
||||
{ this.insertEmptyOption() }
|
||||
{ options }
|
||||
</select>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
@ -11,7 +11,7 @@ const FormFieldText = React.createClass({
|
||||
<input
|
||||
type="text"
|
||||
disabled={
|
||||
(this.props.field['disabled'] !== undefined)
|
||||
(this.props.field.disabled !== undefined)
|
||||
? this.props.field.disabled(this.props.item)
|
||||
: false
|
||||
}
|
||||
@ -22,7 +22,7 @@ const FormFieldText = React.createClass({
|
||||
: false
|
||||
}
|
||||
name={this.props.field.name}
|
||||
id={'field_' + this.props.field.name}
|
||||
id={`field_${this.props.field.name}`}
|
||||
value={value}
|
||||
placeholder={this.props.field.placeholder}
|
||||
onChange={this.props.onValueChange}
|
||||
|
@ -11,7 +11,7 @@ define([
|
||||
type="text"
|
||||
className="regular-text"
|
||||
name={this.props.field.name}
|
||||
id={'field_' + this.props.field.name}
|
||||
id={`field_${this.props.field.name}`}
|
||||
value={this.props.item[this.props.field.name]}
|
||||
placeholder={this.props.field.placeholder}
|
||||
defaultValue={this.props.field.defaultValue}
|
||||
|
@ -15,7 +15,6 @@ define(
|
||||
FormField,
|
||||
jQuery
|
||||
) => {
|
||||
|
||||
const Form = React.createClass({
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired,
|
||||
@ -58,8 +57,6 @@ define(
|
||||
if (props.item === undefined) {
|
||||
this.refs.form.reset();
|
||||
}
|
||||
} else {
|
||||
this.loadItem(props.params.id);
|
||||
}
|
||||
},
|
||||
loadItem: function (id) {
|
||||
@ -77,6 +74,9 @@ define(
|
||||
loading: false,
|
||||
item: response.data,
|
||||
});
|
||||
if (typeof this.props.onItemLoad === 'function') {
|
||||
this.props.onItemLoad(response.data);
|
||||
}
|
||||
}).fail(() => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
@ -100,9 +100,9 @@ define(
|
||||
|
||||
// only get values from displayed fields
|
||||
const item = {};
|
||||
this.props.fields.map((field) => {
|
||||
if (field['fields'] !== undefined) {
|
||||
field.fields.map((subfield) => {
|
||||
this.props.fields.forEach((field) => {
|
||||
if (field.fields !== undefined) {
|
||||
field.fields.forEach((subfield) => {
|
||||
item[subfield.name] = this.state.item[subfield.name];
|
||||
});
|
||||
} else {
|
||||
@ -142,28 +142,25 @@ 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;
|
||||
if (this.getErrors() !== undefined) {
|
||||
errors = this.getErrors().map((error, index) => {
|
||||
return (
|
||||
<p key={'error-' + index} className="mailpoet_error">
|
||||
{ error.message }
|
||||
</p>
|
||||
);
|
||||
});
|
||||
errors = this.getErrors().map((error, index) => (
|
||||
<p key={`error-${index}`} className="mailpoet_error">
|
||||
{ error.message }
|
||||
</p>
|
||||
));
|
||||
}
|
||||
|
||||
const formClasses = classNames(
|
||||
@ -197,7 +194,7 @@ define(
|
||||
field={field}
|
||||
item={this.getValues()}
|
||||
onValueChange={onValueChange}
|
||||
key={'field-' + i} />
|
||||
key={`field-${i}`} />
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -11,6 +11,27 @@ var Observable;
|
||||
var WysijaHistory;
|
||||
var WysijaForm;
|
||||
|
||||
/* LOGGING */
|
||||
function info(value) {
|
||||
if (WysijaForm.options.debug === false) return;
|
||||
|
||||
if (!(window.console && console.log)) {
|
||||
(function () {
|
||||
var noop = function () {};
|
||||
var methods = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'markTimeline', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn'];
|
||||
var length = methods.length;
|
||||
var console = {};
|
||||
window.console = {};
|
||||
while (length--) {
|
||||
console[methods[length]] = noop;
|
||||
}
|
||||
}());
|
||||
}
|
||||
try {
|
||||
console.log('[DEBUG] ' + value);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
Event.cacheDelegated = {};
|
||||
Object.extend(document, (function () {
|
||||
var cache = Event.cacheDelegated;
|
||||
@ -66,18 +87,18 @@ Object.extend(document, (function () {
|
||||
var wrapper;
|
||||
switch (length) {
|
||||
case 2:
|
||||
getWrappersForSelector(selector, eventName).each(function (wrapper) {
|
||||
document.stopDelegating(selector, eventName, wrapper.handler);
|
||||
getWrappersForSelector(selector, eventName).each(function (selectorWrapper) {
|
||||
document.stopDelegating(selector, eventName, selectorWrapper.handler);
|
||||
});
|
||||
break;
|
||||
case 1:
|
||||
Object.keys(getCacheForSelector(selector)).each(function (eventName) {
|
||||
document.stopDelegating(selector, eventName);
|
||||
Object.keys(getCacheForSelector(selector)).each(function (event) {
|
||||
document.stopDelegating(selector, event);
|
||||
});
|
||||
break;
|
||||
case 0:
|
||||
Object.keys(cache).each(function (selector) {
|
||||
document.stopDelegating(selector);
|
||||
Object.keys(cache).each(function (cacheSelector) {
|
||||
document.stopDelegating(cacheSelector);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
@ -96,6 +117,12 @@ Observable = (function () {
|
||||
return name.underscore().split('_').join(':');
|
||||
}
|
||||
|
||||
function getWrapper(handler, klass) {
|
||||
return function (event) {
|
||||
return handler.call(new klass(this), event, event.memo);
|
||||
};
|
||||
}
|
||||
|
||||
function getHandlers(klass) {
|
||||
var proto = klass.prototype;
|
||||
var namespace = proto.namespace;
|
||||
@ -106,12 +133,6 @@ Observable = (function () {
|
||||
});
|
||||
}
|
||||
|
||||
function getWrapper(handler, klass) {
|
||||
return function (event) {
|
||||
return handler.call(new klass(this), event, event.memo);
|
||||
};
|
||||
}
|
||||
|
||||
function onDomLoad(selector, klass) {
|
||||
window.$$(selector).each(function (element) {
|
||||
new klass(element).onDomLoaded();
|
||||
@ -157,8 +178,8 @@ Object.extend(window.Droppables, {
|
||||
var drop;
|
||||
var affected = [];
|
||||
if (!this.drops.length) return;
|
||||
this.drops.each(function (drop) {
|
||||
if (window.Droppables.isAffected(point, element, drop)) affected.push(drop);
|
||||
this.drops.each(function (dropsDrop) {
|
||||
if (window.Droppables.isAffected(point, element, dropsDrop)) affected.push(dropsDrop);
|
||||
});
|
||||
if (affected.length > 0) drop = window.Droppables.findDeepestChild(affected);
|
||||
if (this.last_active && this.last_active !== drop) this.deactivate(this.last_active, element);
|
||||
@ -285,8 +306,8 @@ WysijaForm = {
|
||||
return str.replace(/&/g, '&').replace(/>/g, '>').replace(/</g, '<').replace(/"/g, '"');
|
||||
// ": fix for FileMerge because the previous line fucks up its syntax coloring
|
||||
},
|
||||
loading: function (is_loading) {
|
||||
if (is_loading) {
|
||||
loading: function (isLoading) {
|
||||
if (isLoading) {
|
||||
window.$(WysijaForm.options.editor).addClassName('loading');
|
||||
window.$(WysijaForm.options.toolbar).addClassName('loading');
|
||||
} else {
|
||||
@ -301,7 +322,7 @@ WysijaForm = {
|
||||
});
|
||||
},
|
||||
load: function (data) {
|
||||
var settings_elements;
|
||||
var settingsElements;
|
||||
if (data === undefined) return;
|
||||
|
||||
// load body
|
||||
@ -312,8 +333,8 @@ WysijaForm = {
|
||||
});
|
||||
|
||||
// load settings
|
||||
settings_elements = window.$('mailpoet_form_settings').getElements();
|
||||
settings_elements.each(function (setting) {
|
||||
settingsElements = window.$('mailpoet_form_settings').getElements();
|
||||
settingsElements.each(function (setting) {
|
||||
// skip lists
|
||||
if (setting.name === 'segments') {
|
||||
return true;
|
||||
@ -330,6 +351,7 @@ WysijaForm = {
|
||||
setting.setValue(data.settings[setting.name]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -343,17 +365,17 @@ WysijaForm = {
|
||||
};
|
||||
// body
|
||||
WysijaForm.getBlocks().each(function (b) {
|
||||
var block_data = (typeof (b.block['save']) === 'function') ? b.block.save() : null;
|
||||
var blockData = (typeof (b.block['save']) === 'function') ? b.block.save() : null;
|
||||
|
||||
if (block_data !== null) {
|
||||
if (blockData !== null) {
|
||||
// set block position
|
||||
block_data['position'] = position;
|
||||
blockData['position'] = position;
|
||||
|
||||
// increment position
|
||||
position++;
|
||||
|
||||
// add block data to body
|
||||
data['body'].push(block_data);
|
||||
data['body'].push(blockData);
|
||||
}
|
||||
});
|
||||
|
||||
@ -436,10 +458,10 @@ WysijaForm = {
|
||||
return (window.$$('#' + WysijaForm.options.editor + ' [wysija_id="segments"]').length > 0);
|
||||
},
|
||||
isSegmentSelectionValid: function () {
|
||||
var segment_selection = window.$$('#' + WysijaForm.options.editor + ' [wysija_id="segments"]')[0];
|
||||
var segmentSelection = window.$$('#' + WysijaForm.options.editor + ' [wysija_id="segments"]')[0];
|
||||
var block;
|
||||
if (segment_selection !== undefined) {
|
||||
block = WysijaForm.get(segment_selection).block.getData();
|
||||
if (segmentSelection !== undefined) {
|
||||
block = WysijaForm.get(segmentSelection).block.getData();
|
||||
return (
|
||||
(block.params.values !== undefined)
|
||||
&&
|
||||
@ -450,8 +472,8 @@ WysijaForm = {
|
||||
},
|
||||
setBlockPositions: function (event, target) {
|
||||
var index = 1;
|
||||
var block_placeholder;
|
||||
var previous_placeholder;
|
||||
var blockPlaceholder;
|
||||
var previousPlaceholder;
|
||||
// release dragging lock
|
||||
WysijaForm.locks.dragging = false;
|
||||
|
||||
@ -467,19 +489,19 @@ WysijaForm = {
|
||||
|
||||
if (target !== undefined) {
|
||||
// get placeholders (previous placeholder matches the placeholder linked to the next block)
|
||||
block_placeholder = window.$(target.element.readAttribute('wysija_placeholder'));
|
||||
previous_placeholder = target.element.previous('.block_placeholder');
|
||||
blockPlaceholder = window.$(target.element.readAttribute('wysija_placeholder'));
|
||||
previousPlaceholder = target.element.previous('.block_placeholder');
|
||||
|
||||
if (block_placeholder !== null) {
|
||||
if (blockPlaceholder !== null) {
|
||||
// put block placeholder before the current block
|
||||
target.element.insert({
|
||||
before: block_placeholder
|
||||
before: blockPlaceholder
|
||||
});
|
||||
|
||||
// if the next block is a wysija_block, insert previous placeholder
|
||||
if (target.element.next() !== undefined && target.element.next().hasClassName('mailpoet_form_block') && previous_placeholder !== undefined) {
|
||||
if (target.element.next() !== undefined && target.element.next().hasClassName('mailpoet_form_block') && previousPlaceholder !== undefined) {
|
||||
target.element.insert({
|
||||
after: previous_placeholder
|
||||
after: previousPlaceholder
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -499,11 +521,11 @@ WysijaForm = {
|
||||
// get parent dimensions and position
|
||||
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 isVisible = (parentPos.top <= (WysijaForm.scroll.top + viewportHeight));
|
||||
var buttonMargin = 5;
|
||||
var relativeTop = buttonMargin;
|
||||
|
||||
if (is_visible) {
|
||||
if (isVisible) {
|
||||
// always center
|
||||
relativeTop = parseInt((parentDim.height / 2) - (element.getHeight() / 2), 10);
|
||||
}
|
||||
@ -524,7 +546,6 @@ WysijaForm = {
|
||||
if (WysijaForm.toolbar.left === null) WysijaForm.toolbar.left = parseInt(window.$(WysijaForm.options.container).positionedOffset().left);
|
||||
}
|
||||
if (WysijaForm.toolbar.x === null) WysijaForm.toolbar.x = parseInt(WysijaForm.toolbar.left + window.$(WysijaForm.options.container).getDimensions().width + 15);
|
||||
|
||||
},
|
||||
setToolbarPosition: function () {
|
||||
var position;
|
||||
@ -582,10 +603,8 @@ WysijaForm = {
|
||||
},
|
||||
hideControls: function () {
|
||||
try {
|
||||
return WysijaForm.getBlocks().invoke('hideControls');
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
WysijaForm.getBlocks().invoke('hideControls');
|
||||
} catch (e) {}
|
||||
},
|
||||
hideTools: function () {
|
||||
window.$$('.wysija_tools').invoke('hide');
|
||||
@ -657,6 +676,7 @@ WysijaForm = {
|
||||
// this is a url, so do not encode the protocol
|
||||
return encodeURI(str).replace(/[!'()*]/g, escape);
|
||||
}
|
||||
return str;
|
||||
},
|
||||
updateBlock: function (field) {
|
||||
var hasUpdated = false;
|
||||
@ -778,29 +798,28 @@ WysijaForm.Block = window.Class.create({
|
||||
}
|
||||
},
|
||||
makeBlockDroppable: function () {
|
||||
var block_placeholder;
|
||||
var blockPlaceholder;
|
||||
if (this.isBlockDroppableEnabled() === false) {
|
||||
block_placeholder = this.getBlockDroppable();
|
||||
window.Droppables.add(block_placeholder.identify(), WysijaForm.blockDropOptions);
|
||||
block_placeholder.addClassName('enabled');
|
||||
blockPlaceholder = this.getBlockDroppable();
|
||||
window.Droppables.add(blockPlaceholder.identify(), WysijaForm.blockDropOptions);
|
||||
blockPlaceholder.addClassName('enabled');
|
||||
}
|
||||
},
|
||||
removeBlockDroppable: function () {
|
||||
var block_placeholder;
|
||||
var blockPlaceholder;
|
||||
if (this.isBlockDroppableEnabled()) {
|
||||
block_placeholder = this.getBlockDroppable();
|
||||
window.Droppables.remove(block_placeholder.identify());
|
||||
block_placeholder.removeClassName('enabled');
|
||||
blockPlaceholder = this.getBlockDroppable();
|
||||
window.Droppables.remove(blockPlaceholder.identify());
|
||||
blockPlaceholder.removeClassName('enabled');
|
||||
}
|
||||
},
|
||||
isBlockDroppableEnabled: function () {
|
||||
// if the block_placeholder does not exist, create it
|
||||
var block_placeholder = this.getBlockDroppable();
|
||||
if (block_placeholder === null) {
|
||||
var blockPlaceholder = this.getBlockDroppable();
|
||||
if (blockPlaceholder === null) {
|
||||
return this.createBlockDroppable().hasClassName('enabled');
|
||||
} else {
|
||||
return block_placeholder.hasClassName('enabled');
|
||||
}
|
||||
return blockPlaceholder.hasClassName('enabled');
|
||||
},
|
||||
createBlockDroppable: function () {
|
||||
info('block -> createBlockDroppable');
|
||||
@ -812,9 +831,8 @@ WysijaForm.Block = window.Class.create({
|
||||
getBlockDroppable: function () {
|
||||
if (this.element.previous() === undefined || this.element.previous().hasClassName('block_placeholder') === false) {
|
||||
return null;
|
||||
} else {
|
||||
return this.element.previous();
|
||||
}
|
||||
return this.element.previous();
|
||||
},
|
||||
getControls: function () {
|
||||
return this.element.down('.wysija_controls');
|
||||
@ -919,25 +937,25 @@ WysijaForm.Block = window.Class.create({
|
||||
WysijaForm.Block.create = function (createBlock, target) {
|
||||
var block = createBlock;
|
||||
var body;
|
||||
var block_template;
|
||||
var blockTemplate;
|
||||
var template;
|
||||
var output;
|
||||
var settings_segments;
|
||||
var settingsSegments;
|
||||
if (window.$('form_template_' + block.type) === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
body = window.$(WysijaForm.options.body);
|
||||
block_template = window.Handlebars.compile(window.$('form_template_block').innerHTML);
|
||||
blockTemplate = 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) {
|
||||
settings_segments = window.jQuery('#mailpoet_form_segments').val();
|
||||
if (settings_segments !== null && settings_segments.length > 0) {
|
||||
settingsSegments = window.jQuery('#mailpoet_form_segments').val();
|
||||
if (settingsSegments !== null && settingsSegments.length > 0) {
|
||||
block.params.values = window.mailpoet_segments.filter(function (segment) {
|
||||
return (settings_segments.indexOf(segment.id) !== -1);
|
||||
return (settingsSegments.indexOf(segment.id) !== -1);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -945,7 +963,7 @@ WysijaForm.Block.create = function (createBlock, target) {
|
||||
|
||||
// set block template (depending on the block type)
|
||||
block.template = template(block);
|
||||
output = block_template(block);
|
||||
output = blockTemplate(block);
|
||||
|
||||
// check if the new block is unique and if there's already an instance
|
||||
// of it in the history. If so, remove its former instance from the history
|
||||
@ -973,6 +991,7 @@ WysijaForm.Block.create = function (createBlock, target) {
|
||||
|
||||
// position settings
|
||||
WysijaForm.setSettingsPosition();
|
||||
return true;
|
||||
};
|
||||
|
||||
document.observe('wjfe:item:drop', function (event) {
|
||||
@ -1011,11 +1030,11 @@ WysijaForm.Widget = window.Class.create(WysijaForm.Block, {
|
||||
return data;
|
||||
},
|
||||
setData: function (data) {
|
||||
var current_data = this.getData();
|
||||
var params = window.$H(current_data.params).merge(data.params).toObject();
|
||||
var currentData = this.getData();
|
||||
var params = window.$H(currentData.params).merge(data.params).toObject();
|
||||
|
||||
// update type if it changed
|
||||
if (data.type !== undefined && data.type !== current_data.type) {
|
||||
if (data.type !== undefined && data.type !== currentData.type) {
|
||||
this.element.writeAttribute('wysija_type', data.type);
|
||||
}
|
||||
|
||||
@ -1038,19 +1057,19 @@ WysijaForm.Widget = window.Class.create(WysijaForm.Block, {
|
||||
},
|
||||
redraw: function (data) {
|
||||
var options;
|
||||
var block_template;
|
||||
var blockTemplate;
|
||||
var template;
|
||||
var params;
|
||||
// set parameters
|
||||
this.setData(data);
|
||||
options = this.getData();
|
||||
// redraw block
|
||||
block_template = window.Handlebars.compile(window.$('form_template_block').innerHTML);
|
||||
blockTemplate = 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));
|
||||
this.element.replace(blockTemplate(params));
|
||||
|
||||
WysijaForm.init();
|
||||
},
|
||||
@ -1073,25 +1092,4 @@ WysijaForm.Widget = window.Class.create(WysijaForm.Block, {
|
||||
/* When dom is loaded, initialize WysijaForm */
|
||||
document.observe('dom:loaded', WysijaForm.init);
|
||||
|
||||
/* LOGGING */
|
||||
function info(value) {
|
||||
if (WysijaForm.options.debug === false) return;
|
||||
|
||||
if (!(window.console && console.log)) {
|
||||
(function () {
|
||||
var noop = function () {};
|
||||
var methods = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'markTimeline', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn'];
|
||||
var length = methods.length;
|
||||
var console = {};
|
||||
window.console = {};
|
||||
while (length--) {
|
||||
console[methods[length]] = noop;
|
||||
}
|
||||
}());
|
||||
}
|
||||
try {
|
||||
console.log('[DEBUG] ' + value);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
module.exports = WysijaForm;
|
||||
|
@ -27,7 +27,7 @@ const columns = [
|
||||
|
||||
const messages = {
|
||||
onTrash: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -42,7 +42,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onDelete: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -57,7 +57,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onRestore: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -73,7 +73,7 @@ const messages = {
|
||||
},
|
||||
};
|
||||
|
||||
const bulk_actions = [
|
||||
const bulkActions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('moveToTrash'),
|
||||
@ -81,7 +81,7 @@ const bulk_actions = [
|
||||
},
|
||||
];
|
||||
|
||||
const item_actions = [
|
||||
const itemActions = [
|
||||
{
|
||||
name: 'edit',
|
||||
label: MailPoet.I18n.t('edit'),
|
||||
@ -110,7 +110,7 @@ const item_actions = [
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
@ -133,32 +133,31 @@ const FormList = React.createClass({
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
renderItem(form, actions) {
|
||||
const row_classes = classNames(
|
||||
const rowClasses = classNames(
|
||||
'manage-column',
|
||||
'column-primary',
|
||||
'has-row-actions'
|
||||
);
|
||||
|
||||
let segments = window.mailpoet_segments.filter((segment) => {
|
||||
return (jQuery.inArray(segment.id, form.segments) !== -1);
|
||||
}).map((segment) => {
|
||||
return segment.name;
|
||||
}).join(', ');
|
||||
let segments = window.mailpoet_segments
|
||||
.filter(segment => (jQuery.inArray(segment.id, form.segments) !== -1))
|
||||
.map(segment => segment.name)
|
||||
.join(', ');
|
||||
|
||||
if (form.settings.segments_selected_by === 'user') {
|
||||
segments = MailPoet.I18n.t('userChoice') + ' ' + segments;
|
||||
segments = `${MailPoet.I18n.t('userChoice')} ${segments}`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<td className={row_classes}>
|
||||
<td className={rowClasses}>
|
||||
<strong>
|
||||
<a
|
||||
className="row-title"
|
||||
@ -199,8 +198,8 @@ const FormList = React.createClass({
|
||||
endpoint="forms"
|
||||
onRenderItem={this.renderItem}
|
||||
columns={columns}
|
||||
bulk_actions={bulk_actions}
|
||||
item_actions={item_actions}
|
||||
bulk_actions={bulkActions}
|
||||
item_actions={itemActions}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
@ -17,7 +17,7 @@ define('handlebars_helpers', ['handlebars'], function (Handlebars) {
|
||||
var f;
|
||||
if (window.moment) {
|
||||
if (timestamp === undefined || isNaN(timestamp) || timestamp <= 0) {
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// set date format
|
||||
@ -25,12 +25,10 @@ define('handlebars_helpers', ['handlebars'], function (Handlebars) {
|
||||
// check if we passed a timestamp
|
||||
if (parseInt(timestamp, 10) == timestamp) {
|
||||
return window.moment.unix(timestamp).format(f);
|
||||
} else {
|
||||
return window.moment.utc(timestamp).format(f);
|
||||
}
|
||||
} else {
|
||||
return timestamp;
|
||||
return window.moment.utc(timestamp).format(f);
|
||||
}
|
||||
return timestamp;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('cycle', function (value, block) {
|
||||
@ -87,9 +85,8 @@ define('handlebars_helpers', ['handlebars'], function (Handlebars) {
|
||||
var mailtoMatchingRegex = /^mailto\:/i;
|
||||
if (typeof value === 'string' && value.match(mailtoMatchingRegex)) {
|
||||
return value.replace(mailtoMatchingRegex, '');
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
});
|
||||
Handlebars.registerHelper('lookup', function (obj, field) {
|
||||
return obj && obj[field];
|
||||
@ -134,9 +131,8 @@ define('handlebars_helpers', ['handlebars'], function (Handlebars) {
|
||||
}
|
||||
if (sanitized.length > limit) {
|
||||
return sanitized.substr(0, limit - strAppend.length) + strAppend;
|
||||
} else {
|
||||
return sanitized;
|
||||
}
|
||||
return sanitized;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('getNumber', function (string) {
|
||||
|
@ -6,7 +6,6 @@ define('helpTooltip', ['mailpoet', 'react', 'react-dom', 'help-tooltip.jsx'],
|
||||
|
||||
MailPoet.helpTooltip = {
|
||||
show: function (domContainerNode, opts) {
|
||||
|
||||
ReactDOM.render(React.createElement(
|
||||
TooltipComponent, {
|
||||
tooltip: opts.tooltip,
|
||||
@ -16,7 +15,6 @@ define('helpTooltip', ['mailpoet', 'react', 'react-dom', 'help-tooltip.jsx'],
|
||||
), domContainerNode);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -3,8 +3,9 @@ import ReactDOM from 'react-dom';
|
||||
import { Router, Route, IndexRedirect, useRouterHistory } from 'react-router';
|
||||
import { createHashHistory } from 'history';
|
||||
|
||||
import KnowledgeBase from 'help/knowledge_base.jsx';
|
||||
import SystemStatus from 'help/system_status.jsx';
|
||||
import SystemInfo from 'help/system_info.jsx';
|
||||
import KnowledgeBase from 'help/knowledge_base.jsx';
|
||||
|
||||
const history = useRouterHistory(createHashHistory)({ queryKey: false });
|
||||
|
||||
@ -17,16 +18,15 @@ const App = React.createClass({
|
||||
const container = document.getElementById('help_container');
|
||||
|
||||
if (container) {
|
||||
|
||||
ReactDOM.render((
|
||||
<Router history={history}>
|
||||
<Route path="/" component={App}>
|
||||
<IndexRedirect to="knowledgeBase" />
|
||||
<IndexRedirect to="systemStatus" />
|
||||
{/* Pages */}
|
||||
<Route path="knowledgeBase(/)**" params={{ tab: 'knowledgeBase' }} component={KnowledgeBase} />
|
||||
<Route path="systemStatus(/)**" params={{ tab: 'systemStatus' }} component={SystemStatus} />
|
||||
<Route path="systemInfo(/)**" params={{ tab: 'systemInfo' }} component={SystemInfo} />
|
||||
<Route path="knowledgeBase(/)**" params={{ tab: 'knowledgeBase' }} component={KnowledgeBase} />
|
||||
</Route>
|
||||
</Router>
|
||||
), container);
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import MailPoet from 'mailpoet';
|
||||
import Tabs from './tabs.jsx';
|
||||
|
||||
function KnowledgeBase() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import MailPoet from 'mailpoet';
|
||||
import _ from 'underscore';
|
||||
|
||||
import Tabs from './tabs.jsx';
|
||||
|
||||
function handleFocus(event) {
|
||||
@ -10,9 +9,7 @@ function handleFocus(event) {
|
||||
|
||||
function printData(data) {
|
||||
if (_.isObject(data)) {
|
||||
const printableData = Object.keys(data).map((key) => {
|
||||
return `${key}: ${data[key]}`;
|
||||
});
|
||||
const printableData = Object.keys(data).map(key => `${key}: ${data[key]}`);
|
||||
|
||||
return (<textarea
|
||||
readOnly={true}
|
||||
@ -23,25 +20,24 @@ function printData(data) {
|
||||
height: '400px',
|
||||
}}
|
||||
/>);
|
||||
} else {
|
||||
return (<p>{MailPoet.I18n.t('systemInfoDataError')}</p>);
|
||||
}
|
||||
return (<p>{MailPoet.I18n.t('systemInfoDataError')}</p>);
|
||||
}
|
||||
|
||||
function KnowledgeBase() {
|
||||
const data = window.help_scout_data;
|
||||
function SystemInfo() {
|
||||
const systemInfoData = window.systemInfoData;
|
||||
return (
|
||||
<div>
|
||||
|
||||
<Tabs tab="systemInfo" />
|
||||
|
||||
<div className="mailpoet_notice notice inline notice-success" style={{ marginTop: '1em' }}>
|
||||
<div className="mailpoet_notice notice inline" style={{ marginTop: '1em' }}>
|
||||
<p>{MailPoet.I18n.t('systemInfoIntro')}</p>
|
||||
</div>
|
||||
|
||||
{printData(data)}
|
||||
{printData(systemInfoData)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = KnowledgeBase;
|
||||
module.exports = SystemInfo;
|
||||
|
71
assets/js/src/help/system_status.jsx
Normal file
71
assets/js/src/help/system_status.jsx
Normal file
@ -0,0 +1,71 @@
|
||||
import MailPoet from 'mailpoet';
|
||||
import React from 'react';
|
||||
import ReactStringReplace from 'react-string-replace';
|
||||
import Tabs from './tabs.jsx';
|
||||
|
||||
function renderStatusMessage(status, error, link) {
|
||||
const noticeType = (status) ? 'success' : 'error';
|
||||
let noticeMessage = (status) ?
|
||||
MailPoet.I18n.t('systemStatusConnectionSuccessful') :
|
||||
`${MailPoet.I18n.t('systemStatusConnectionUnsuccessful')} ${error}`;
|
||||
|
||||
if (link) {
|
||||
noticeMessage = ReactStringReplace(
|
||||
noticeMessage,
|
||||
/\[link\](.*?)\[\/link\]/g,
|
||||
match => (
|
||||
<a href={`${link}`} key="kb-link">{ match }</a>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`mailpoet_notice notice inline notice-${noticeType}`} style={{ marginTop: '1em' }}>
|
||||
<p>{noticeMessage}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderCronSection(data) {
|
||||
const status = data.cron.isReachable;
|
||||
const url = data.cron.url;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{MailPoet.I18n.t('systemStatusCronTitle')}</h2>
|
||||
<p>
|
||||
<a href={url} target="_blank">{url}</a>
|
||||
</p>
|
||||
{renderStatusMessage(status, MailPoet.I18n.t('systemStatusCronConnectionUnsuccessfulInfo'), '//beta.docs.mailpoet.com/article/231-sending-does-not-work')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderMSSSection(data) {
|
||||
if (!data.mss.enabled) return undefined;
|
||||
|
||||
const status = data.mss.enabled.isReachable;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>{MailPoet.I18n.t('systemStatusMSSTitle')}</h2>
|
||||
{renderStatusMessage(status, MailPoet.I18n.t('systemStatusMSSConnectionUnsuccessfulInfo'), false)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SystemStatus() {
|
||||
const systemStatusData = window.systemStatusData;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tabs tab="systemStatus" />
|
||||
<div className="mailpoet_notice notice inline" style={{ marginTop: '1em' }}>
|
||||
<p>{systemStatusData.mss.enabled ? MailPoet.I18n.t('systemStatusIntroCronMSS') : MailPoet.I18n.t('systemStatusIntroCron')}</p>
|
||||
</div>
|
||||
{renderCronSection(systemStatusData)}
|
||||
{renderMSSSection(systemStatusData)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
module.exports = SystemStatus;
|
@ -5,19 +5,23 @@ import MailPoet from 'mailpoet';
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
name: 'knowledgeBase',
|
||||
label: MailPoet.I18n.t('tabKnowledgeBaseTitle'),
|
||||
link: '/knowledgeBase',
|
||||
name: 'systemStatus',
|
||||
label: MailPoet.I18n.t('tabSystemStatusTitle'),
|
||||
link: '/systemStatus',
|
||||
},
|
||||
{
|
||||
name: 'systemInfo',
|
||||
label: MailPoet.I18n.t('tabSystemInfoTitle'),
|
||||
link: '/systemInfo',
|
||||
},
|
||||
{
|
||||
name: 'knowledgeBase',
|
||||
label: MailPoet.I18n.t('tabKnowledgeBaseTitle'),
|
||||
link: '/knowledgeBase',
|
||||
},
|
||||
];
|
||||
|
||||
function Tabs(props) {
|
||||
|
||||
const tabLinks = tabs.map((tab, index) => {
|
||||
const tabClasses = classNames(
|
||||
'nav-tab',
|
||||
@ -26,7 +30,7 @@ function Tabs(props) {
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={'tab-' + index}
|
||||
key={`tab-${index}`}
|
||||
className={tabClasses}
|
||||
to={tab.link}
|
||||
>{ tab.label }</Link>
|
||||
@ -41,6 +45,6 @@ function Tabs(props) {
|
||||
}
|
||||
|
||||
Tabs.propTypes = { tab: React.PropTypes.string };
|
||||
Tabs.defaultProps = { tab: 'knowledgeBase' };
|
||||
Tabs.defaultProps = { tab: 'systemStatus' };
|
||||
|
||||
module.exports = Tabs;
|
||||
|
@ -21,5 +21,4 @@ define('i18n',
|
||||
return translations;
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
@ -25,7 +25,7 @@ define(
|
||||
*/
|
||||
$.fn.mailpoetSerializeObject = function (coerce) {
|
||||
var obj = {};
|
||||
var coerce_types = { true: !0, false: !1, null: null };
|
||||
var coerceTypes = { true: !0, false: !1, null: null };
|
||||
|
||||
// Iterate over all name=value pairs.
|
||||
$.each(this.serializeArray(), function (j, v) {
|
||||
@ -37,33 +37,33 @@ define(
|
||||
// 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;
|
||||
var keysLast = keys.length - 1;
|
||||
|
||||
// If the first keys part contains [ and the last ends with ], then []
|
||||
// are correctly balanced.
|
||||
if (/\[/.test(keys[0]) && /\]$/.test(keys[keys_last])) {
|
||||
if (/\[/.test(keys[0]) && /\]$/.test(keys[keysLast])) {
|
||||
// Remove the trailing ] from the last keys part.
|
||||
keys[keys_last] = keys[keys_last].replace(/\]$/, '');
|
||||
keys[keysLast] = keys[keysLast].replace(/\]$/, '');
|
||||
|
||||
// Split first keys part into two parts on the [ and add them back onto
|
||||
// the beginning of the keys array.
|
||||
keys = keys.shift().split('[').concat(keys);
|
||||
|
||||
keys_last = keys.length - 1;
|
||||
keysLast = keys.length - 1;
|
||||
} else {
|
||||
// Basic 'foo' style key.
|
||||
keys_last = 0;
|
||||
keysLast = 0;
|
||||
}
|
||||
|
||||
// Coerce values.
|
||||
if (coerce) {
|
||||
val = val && !isNaN(val) ? +val // number
|
||||
: val === 'undefined' ? undefined // undefined
|
||||
: coerce_types[val] !== undefined ? coerce_types[val] // true, false, null
|
||||
: coerceTypes[val] !== undefined ? coerceTypes[val] // true, false, null
|
||||
: val; // string
|
||||
}
|
||||
|
||||
if (keys_last) {
|
||||
if (keysLast) {
|
||||
// Complex key, build deep object structure based on a few rules:
|
||||
// * The 'cur' pointer starts at the object top-level.
|
||||
// * [] = array push (n is set to array length), [n] = array if n is
|
||||
@ -73,14 +73,13 @@ define(
|
||||
// object or array based on the type of the next keys part.
|
||||
// * Move the 'cur' pointer to the next level.
|
||||
// * Rinse & repeat.
|
||||
for (; i <= keys_last; i++) {
|
||||
for (; i <= keysLast; i++) {
|
||||
key = keys[i] === '' ? cur.length : keys[i];
|
||||
cur[key] = i < keys_last
|
||||
cur[key] = i < keysLast
|
||||
? cur[key] || (keys[i + 1] && isNaN(keys[i + 1]) ? {} : [])
|
||||
: val;
|
||||
cur = cur[key];
|
||||
}
|
||||
|
||||
} else {
|
||||
// Simple key, even simpler rules, since only scalars and shallow
|
||||
// arrays are allowed.
|
||||
@ -88,12 +87,10 @@ define(
|
||||
if ($.isArray(obj[key])) {
|
||||
// val is already an array, so push on the next value.
|
||||
obj[key].push(val);
|
||||
|
||||
} else if (obj[key] !== undefined) {
|
||||
// val isn't an array, but since a second value has been specified,
|
||||
// convert val into an array.
|
||||
obj[key] = [obj[key], val];
|
||||
|
||||
} else {
|
||||
// val is a scalar.
|
||||
obj[key] = val;
|
||||
|
@ -21,7 +21,7 @@ define([
|
||||
const action = this.getSelectedAction();
|
||||
|
||||
// action on select callback
|
||||
if (action !== null && action['onSelect'] !== undefined) {
|
||||
if (action !== null && action.onSelect !== undefined) {
|
||||
this.setState({
|
||||
extra: action.onSelect(e),
|
||||
});
|
||||
@ -37,23 +37,23 @@ define([
|
||||
return;
|
||||
}
|
||||
|
||||
const selected_ids = (this.props.selection !== 'all')
|
||||
const selectedIds = (this.props.selection !== 'all')
|
||||
? this.props.selected_ids
|
||||
: [];
|
||||
|
||||
const data = (action['getData'] !== undefined)
|
||||
const data = (action.getData !== undefined)
|
||||
? action.getData()
|
||||
: {};
|
||||
|
||||
data.action = this.state.action;
|
||||
|
||||
let onSuccess = function () {};
|
||||
if (action['onSuccess'] !== undefined) {
|
||||
if (action.onSuccess !== undefined) {
|
||||
onSuccess = action.onSuccess;
|
||||
}
|
||||
|
||||
if (data.action) {
|
||||
const promise = this.props.onBulkAction(selected_ids, data);
|
||||
const promise = this.props.onBulkAction(selectedIds, data);
|
||||
if (promise !== false) {
|
||||
promise.then(onSuccess);
|
||||
}
|
||||
@ -65,11 +65,9 @@ define([
|
||||
});
|
||||
},
|
||||
getSelectedAction: function () {
|
||||
const selected_action = this.refs.action.value;
|
||||
if (selected_action.length > 0) {
|
||||
const action = this.props.bulk_actions.filter((action) => {
|
||||
return (action.name === selected_action);
|
||||
});
|
||||
const selectedAction = this.refs.action.value;
|
||||
if (selectedAction.length > 0) {
|
||||
const action = this.props.bulk_actions.filter(act => (act.name === selectedAction));
|
||||
|
||||
if (action.length > 0) {
|
||||
return action[0];
|
||||
@ -97,14 +95,12 @@ define([
|
||||
onChange={this.handleChangeAction}
|
||||
>
|
||||
<option value="">{MailPoet.I18n.t('bulkActions')}</option>
|
||||
{ this.props.bulk_actions.map((action, index) => {
|
||||
return (
|
||||
<option
|
||||
value={action.name}
|
||||
key={'action-' + index}
|
||||
{ this.props.bulk_actions.map((action, index) => (
|
||||
<option
|
||||
value={action.name}
|
||||
key={`action-${index}`}
|
||||
>{ action.label }</option>
|
||||
);
|
||||
}) }
|
||||
)) }
|
||||
</select>
|
||||
<input
|
||||
onClick={this.handleApplyAction}
|
||||
|
@ -11,8 +11,8 @@ define([
|
||||
const ListingFilters = React.createClass({
|
||||
handleFilterAction: function () {
|
||||
const filters = {};
|
||||
this.getAvailableFilters().map((filter, i) => {
|
||||
filters[this.refs['filter-' + i].name] = this.refs['filter-' + i].value;
|
||||
this.getAvailableFilters().forEach((filter, i) => {
|
||||
filters[this.refs[`filter-${i}`].name] = this.refs[`filter-${i}`].value;
|
||||
});
|
||||
if (this.props.onBeforeSelectFilter) {
|
||||
this.props.onBeforeSelectFilter(filters);
|
||||
@ -24,23 +24,21 @@ define([
|
||||
},
|
||||
getAvailableFilters: function () {
|
||||
const filters = this.props.filters;
|
||||
return Object.keys(filters).filter((filter) => {
|
||||
return !(
|
||||
return Object.keys(filters).filter(filter => !(
|
||||
filters[filter].length === 0
|
||||
|| (
|
||||
filters[filter].length === 1
|
||||
&& !filters[filter][0].value
|
||||
)
|
||||
);
|
||||
});
|
||||
));
|
||||
},
|
||||
componentDidUpdate: function () {
|
||||
const selected_filters = this.props.filter;
|
||||
this.getAvailableFilters().map(
|
||||
const selectedFilters = this.props.filter;
|
||||
this.getAvailableFilters().forEach(
|
||||
(filter, i) => {
|
||||
if (selected_filters[filter] !== undefined && selected_filters[filter]) {
|
||||
jQuery(this.refs['filter-' + i])
|
||||
.val(selected_filters[filter])
|
||||
if (selectedFilters[filter] !== undefined && selectedFilters[filter]) {
|
||||
jQuery(this.refs[`filter-${i}`])
|
||||
.val(selectedFilters[filter])
|
||||
.trigger('change');
|
||||
}
|
||||
}
|
||||
@ -48,29 +46,25 @@ define([
|
||||
},
|
||||
render: function () {
|
||||
const filters = this.props.filters;
|
||||
const available_filters = this.getAvailableFilters()
|
||||
.map((filter, i) => {
|
||||
return (
|
||||
<select
|
||||
ref={`filter-${i}`}
|
||||
key={`filter-${i}`}
|
||||
name={filter}
|
||||
const availableFilters = this.getAvailableFilters()
|
||||
.map((filter, i) => (
|
||||
<select
|
||||
ref={`filter-${i}`}
|
||||
key={`filter-${i}`}
|
||||
name={filter}
|
||||
>
|
||||
{ filters[filter].map((option, j) => {
|
||||
return (
|
||||
<option
|
||||
value={option.value}
|
||||
key={'filter-option-' + j}
|
||||
{ filters[filter].map((option, j) => (
|
||||
<option
|
||||
value={option.value}
|
||||
key={`filter-option-${j}`}
|
||||
>{ option.label }</option>
|
||||
);
|
||||
}) }
|
||||
</select>
|
||||
);
|
||||
});
|
||||
)) }
|
||||
</select>
|
||||
));
|
||||
|
||||
let button;
|
||||
|
||||
if (available_filters.length > 0) {
|
||||
if (availableFilters.length > 0) {
|
||||
button = (
|
||||
<input
|
||||
id="post-query-submit"
|
||||
@ -81,9 +75,9 @@ define([
|
||||
);
|
||||
}
|
||||
|
||||
let empty_trash;
|
||||
let emptyTrash;
|
||||
if (this.props.group === 'trash') {
|
||||
empty_trash = (
|
||||
emptyTrash = (
|
||||
<input
|
||||
onClick={this.handleEmptyTrash}
|
||||
type="submit"
|
||||
@ -95,9 +89,9 @@ define([
|
||||
|
||||
return (
|
||||
<div className="alignleft actions actions">
|
||||
{ available_filters }
|
||||
{ availableFilters }
|
||||
{ button }
|
||||
{ empty_trash }
|
||||
{ emptyTrash }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@ -1,5 +1,4 @@
|
||||
define(['react', 'classnames'], (React, classNames) => {
|
||||
|
||||
const ListingGroups = React.createClass({
|
||||
handleSelect: function (group) {
|
||||
return this.props.onSelectGroup(group);
|
||||
@ -21,7 +20,8 @@ define(['react', 'classnames'], (React, classNames) => {
|
||||
href="javascript:;"
|
||||
className={classes}
|
||||
onClick={this.handleSelect.bind(this, group.name)} >
|
||||
{group.label} <span className="count">({ group.count.toLocaleString() })</span>
|
||||
{group.label}
|
||||
<span className="count">({ parseInt(group.count, 10).toLocaleString() })</span>
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
|
@ -19,7 +19,7 @@ const ListingHeader = React.createClass({
|
||||
<ListingColumn
|
||||
onSort={this.props.onSort}
|
||||
sort_by={this.props.sort_by}
|
||||
key={'column-' + index}
|
||||
key={`column-${index}`}
|
||||
column={renderColumn} />
|
||||
);
|
||||
});
|
||||
@ -54,9 +54,9 @@ const ListingHeader = React.createClass({
|
||||
|
||||
const ListingColumn = React.createClass({
|
||||
handleSort: function () {
|
||||
const sort_by = this.props.column.name;
|
||||
const sort_order = (this.props.column.sorted === 'asc') ? 'desc' : 'asc';
|
||||
this.props.onSort(sort_by, sort_order);
|
||||
const sortBy = this.props.column.name;
|
||||
const sortOrder = (this.props.column.sorted === 'asc') ? 'desc' : 'asc';
|
||||
this.props.onSort(sortBy, sortOrder);
|
||||
},
|
||||
render: function () {
|
||||
const classes = classNames(
|
||||
|
@ -44,7 +44,7 @@ const ListingItem = React.createClass({
|
||||
checkbox = (
|
||||
<th className="check-column" scope="row">
|
||||
<label className="screen-reader-text">{
|
||||
'Select ' + this.props.item[this.props.columns[0].name]
|
||||
`Select ${this.props.item[this.props.columns[0].name]}`
|
||||
}</label>
|
||||
<input
|
||||
type="checkbox"
|
||||
@ -58,24 +58,20 @@ const ListingItem = React.createClass({
|
||||
);
|
||||
}
|
||||
|
||||
const custom_actions = this.props.item_actions;
|
||||
let item_actions = false;
|
||||
const customActions = this.props.item_actions;
|
||||
let itemActions = false;
|
||||
|
||||
if (custom_actions.length > 0) {
|
||||
let is_first = true;
|
||||
item_actions = custom_actions.map((action, index) => {
|
||||
if (action.display !== undefined) {
|
||||
if (action.display(this.props.item) === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let custom_action = null;
|
||||
if (customActions.length > 0) {
|
||||
let isFirst = true;
|
||||
itemActions = customActions
|
||||
.filter(action => action.display === undefined || action.display(this.props.item))
|
||||
.map((action, index) => {
|
||||
let customAction = null;
|
||||
|
||||
if (action.name === 'trash') {
|
||||
custom_action = (
|
||||
<span key={'action-' + index} className="trash">
|
||||
{(!is_first) ? ' | ' : ''}
|
||||
customAction = (
|
||||
<span key={`action-${index}`} className="trash">
|
||||
{(!isFirst) ? ' | ' : ''}
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={this.handleTrashItem.bind(
|
||||
@ -87,27 +83,27 @@ const ListingItem = React.createClass({
|
||||
</span>
|
||||
);
|
||||
} else if (action.refresh) {
|
||||
custom_action = (
|
||||
customAction = (
|
||||
<span
|
||||
onClick={this.props.onRefreshItems}
|
||||
key={'action-' + index} className={action.name}>
|
||||
{(!is_first) ? ' | ' : ''}
|
||||
key={`action-${index}`} className={action.name}>
|
||||
{(!isFirst) ? ' | ' : ''}
|
||||
{ action.link(this.props.item) }
|
||||
</span>
|
||||
);
|
||||
} else if (action.link) {
|
||||
custom_action = (
|
||||
customAction = (
|
||||
<span
|
||||
key={'action-' + index} className={action.name}>
|
||||
{(!is_first) ? ' | ' : ''}
|
||||
key={`action-${index}`} className={action.name}>
|
||||
{(!isFirst) ? ' | ' : ''}
|
||||
{ action.link(this.props.item) }
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
custom_action = (
|
||||
customAction = (
|
||||
<span
|
||||
key={'action-' + index} className={action.name}>
|
||||
{(!is_first) ? ' | ' : ''}
|
||||
key={`action-${index}`} className={action.name}>
|
||||
{(!isFirst) ? ' | ' : ''}
|
||||
<a href="javascript:;" onClick={
|
||||
(action.onClick !== undefined)
|
||||
? action.onClick.bind(null,
|
||||
@ -120,14 +116,14 @@ const ListingItem = React.createClass({
|
||||
);
|
||||
}
|
||||
|
||||
if (custom_action !== null && is_first === true) {
|
||||
is_first = false;
|
||||
if (customAction !== null && isFirst === true) {
|
||||
isFirst = false;
|
||||
}
|
||||
|
||||
return custom_action;
|
||||
return customAction;
|
||||
});
|
||||
} else {
|
||||
item_actions = (
|
||||
itemActions = (
|
||||
<span className="edit">
|
||||
<Link to={`/edit/${this.props.item.id}`}>{MailPoet.I18n.t('edit')}</Link>
|
||||
</span>
|
||||
@ -172,7 +168,7 @@ const ListingItem = React.createClass({
|
||||
actions = (
|
||||
<div>
|
||||
<div className="row-actions">
|
||||
{ item_actions }
|
||||
{ itemActions }
|
||||
</div>
|
||||
<button
|
||||
onClick={this.handleToggleItem.bind(null, this.props.item.id)}
|
||||
@ -183,10 +179,10 @@ const ListingItem = React.createClass({
|
||||
);
|
||||
}
|
||||
|
||||
const row_classes = classNames({ 'is-expanded': this.state.expanded });
|
||||
const rowClasses = classNames({ 'is-expanded': this.state.expanded });
|
||||
|
||||
return (
|
||||
<tr className={row_classes}>
|
||||
<tr className={rowClasses} data-automation-id={`listing_item_${this.props.item.id}`}>
|
||||
{ checkbox }
|
||||
{ this.props.onRenderItem(this.props.item, actions) }
|
||||
</tr>
|
||||
@ -223,24 +219,24 @@ const ListingItems = React.createClass({
|
||||
</tr>
|
||||
</tbody>
|
||||
);
|
||||
} else {
|
||||
const select_all_classes = classNames(
|
||||
}
|
||||
const selectAllClasses = 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={selectAllClasses}>
|
||||
<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 +245,40 @@ 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>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
@ -319,16 +314,15 @@ const Listing = React.createClass({
|
||||
const state = this.getInitialState();
|
||||
// check for url params
|
||||
if (params.splat) {
|
||||
params.splat.split('/').map((param) => {
|
||||
params.splat.split('/').forEach((param) => {
|
||||
const [key, value] = this.getParam(param);
|
||||
const filters = {};
|
||||
switch (key) {
|
||||
case 'filter':
|
||||
const filters = {};
|
||||
value.split('&').map((pair) => {
|
||||
value.split('&').forEach((pair) => {
|
||||
const [k, v] = pair.split('=');
|
||||
filters[k] = v;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
state.filter = filters;
|
||||
break;
|
||||
@ -340,7 +334,7 @@ const Listing = React.createClass({
|
||||
|
||||
// limit per page
|
||||
if (this.props.limit !== undefined) {
|
||||
state.limit = Math.abs(~~this.props.limit);
|
||||
state.limit = Math.abs(Number(this.props.limit));
|
||||
}
|
||||
|
||||
// sort by
|
||||
@ -371,8 +365,7 @@ const Listing = React.createClass({
|
||||
setParams: function () {
|
||||
if (this.props.location) {
|
||||
const params = Object.keys(this.state)
|
||||
.filter((key) => {
|
||||
return (
|
||||
.filter(key => (
|
||||
[
|
||||
'group',
|
||||
'filter',
|
||||
@ -381,8 +374,7 @@ const Listing = React.createClass({
|
||||
'sort_by',
|
||||
'sort_order',
|
||||
].indexOf(key) !== -1
|
||||
);
|
||||
})
|
||||
))
|
||||
.map((key) => {
|
||||
let value = this.state[key];
|
||||
if (value === Object(value)) {
|
||||
@ -390,12 +382,13 @@ const Listing = React.createClass({
|
||||
} else if (value === Boolean(value)) {
|
||||
value = value.toString();
|
||||
}
|
||||
|
||||
if (value !== '' && value !== null) {
|
||||
return `${key}[${value}]`;
|
||||
}
|
||||
return {
|
||||
key,
|
||||
value,
|
||||
};
|
||||
})
|
||||
.filter((key) => { return (key !== undefined); })
|
||||
.filter(({ value }) => value !== '' && value !== null)
|
||||
.map(({ key, value }) => `${key}[${value}]`)
|
||||
.join('/');
|
||||
|
||||
// set url
|
||||
@ -407,24 +400,23 @@ const Listing = React.createClass({
|
||||
}
|
||||
},
|
||||
getUrlWithParams: function (params) {
|
||||
let base_url = (this.props.base_url !== undefined)
|
||||
let baseUrl = (this.props.base_url !== undefined)
|
||||
? this.props.base_url
|
||||
: null;
|
||||
|
||||
if (base_url !== null) {
|
||||
base_url = this.setBaseUrlParams(base_url);
|
||||
return `/${base_url}/${params}`;
|
||||
} else {
|
||||
return `/${params}`;
|
||||
if (baseUrl !== null) {
|
||||
baseUrl = this.setBaseUrlParams(baseUrl);
|
||||
return `/${baseUrl}/${params}`;
|
||||
}
|
||||
return `/${params}`;
|
||||
},
|
||||
setBaseUrlParams: function (base_url) {
|
||||
let ret = base_url;
|
||||
setBaseUrlParams: function (baseUrl) {
|
||||
let ret = baseUrl;
|
||||
if (ret.indexOf(':') !== -1) {
|
||||
const params = this.getParams();
|
||||
Object.keys(params).map((key) => {
|
||||
if (ret.indexOf(':' + key) !== -1) {
|
||||
ret = ret.replace(':' + key, params[key]);
|
||||
Object.keys(params).forEach((key) => {
|
||||
if (ret.indexOf(`:${key}`) !== -1) {
|
||||
ret = ret.replace(`:${key}`, params[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -491,7 +483,7 @@ const Listing = React.createClass({
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
@ -514,14 +506,14 @@ const Listing = React.createClass({
|
||||
}).done((response) => {
|
||||
if (
|
||||
this.props.messages !== undefined
|
||||
&& this.props.messages['onRestore'] !== undefined
|
||||
&& this.props.messages.onRestore !== undefined
|
||||
) {
|
||||
this.props.messages.onRestore(response);
|
||||
}
|
||||
this.getItems();
|
||||
}).fail((response) => {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
});
|
||||
@ -542,14 +534,14 @@ const Listing = React.createClass({
|
||||
}).done((response) => {
|
||||
if (
|
||||
this.props.messages !== undefined
|
||||
&& this.props.messages['onTrash'] !== undefined
|
||||
&& this.props.messages.onTrash !== undefined
|
||||
) {
|
||||
this.props.messages.onTrash(response);
|
||||
}
|
||||
this.getItems();
|
||||
}).fail((response) => {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
});
|
||||
@ -570,14 +562,14 @@ const Listing = React.createClass({
|
||||
}).done((response) => {
|
||||
if (
|
||||
this.props.messages !== undefined
|
||||
&& this.props.messages['onDelete'] !== undefined
|
||||
&& this.props.messages.onDelete !== undefined
|
||||
) {
|
||||
this.props.messages.onDelete(response);
|
||||
}
|
||||
this.getItems();
|
||||
}).fail((response) => {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
});
|
||||
@ -594,16 +586,16 @@ const Listing = React.createClass({
|
||||
this.handleGroup('all');
|
||||
}).fail((response) => {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
});
|
||||
},
|
||||
handleBulkAction: function (selected_ids, params) {
|
||||
handleBulkAction: function (selectedIds, params) {
|
||||
if (
|
||||
this.state.selection === false
|
||||
&& this.state.selected_ids.length === 0
|
||||
&& selected_ids !== 'all'
|
||||
&& selectedIds !== 'all'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
@ -619,8 +611,8 @@ const Listing = React.createClass({
|
||||
group: this.state.group,
|
||||
search: this.state.search,
|
||||
};
|
||||
if (selected_ids !== 'all') {
|
||||
data.listing.selection = selected_ids;
|
||||
if (selectedIds !== 'all') {
|
||||
data.listing.selection = selectedIds;
|
||||
}
|
||||
|
||||
return MailPoet.Ajax.post({
|
||||
@ -633,7 +625,7 @@ const Listing = React.createClass({
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
@ -649,20 +641,20 @@ const Listing = React.createClass({
|
||||
this.setParams();
|
||||
});
|
||||
},
|
||||
handleSort: function (sort_by, sort_order = 'asc') {
|
||||
handleSort: function (sortBy, sortOrder = 'asc') {
|
||||
this.setState({
|
||||
sort_by: sort_by,
|
||||
sort_order: (sort_order === 'asc') ? 'asc' : 'desc',
|
||||
sort_by: sortBy,
|
||||
sort_order: (sortOrder === 'asc') ? 'asc' : 'desc',
|
||||
}, () => {
|
||||
this.setParams();
|
||||
});
|
||||
},
|
||||
handleSelectItem: function (id, is_checked) {
|
||||
let selected_ids = this.state.selected_ids;
|
||||
handleSelectItem: function (id, isChecked) {
|
||||
let selectedIds = this.state.selected_ids;
|
||||
let selection = false;
|
||||
|
||||
if (is_checked) {
|
||||
selected_ids = jQuery.merge(selected_ids, [id]);
|
||||
if (isChecked) {
|
||||
selectedIds = jQuery.merge(selectedIds, [id]);
|
||||
// check whether all items on the page are selected
|
||||
if (
|
||||
jQuery('tbody .check-column :checkbox:not(:checked)').length === 0
|
||||
@ -670,24 +662,22 @@ const Listing = React.createClass({
|
||||
selection = 'page';
|
||||
}
|
||||
} else {
|
||||
selected_ids.splice(selected_ids.indexOf(id), 1);
|
||||
selectedIds.splice(selectedIds.indexOf(id), 1);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
selection: selection,
|
||||
selected_ids: selected_ids,
|
||||
selected_ids: selectedIds,
|
||||
});
|
||||
},
|
||||
handleSelectItems: function (is_checked) {
|
||||
if (is_checked === false) {
|
||||
handleSelectItems: function (isChecked) {
|
||||
if (isChecked === false) {
|
||||
this.clearSelection();
|
||||
} else {
|
||||
const selected_ids = this.state.items.map((item) => {
|
||||
return ~~item.id;
|
||||
});
|
||||
const selectedIds = this.state.items.map(item => Number(item.id));
|
||||
|
||||
this.setState({
|
||||
selected_ids: selected_ids,
|
||||
selected_ids: selectedIds,
|
||||
selection: 'page',
|
||||
});
|
||||
}
|
||||
@ -747,20 +737,20 @@ const Listing = React.createClass({
|
||||
},
|
||||
render: function () {
|
||||
const items = this.state.items;
|
||||
const sort_by = this.state.sort_by;
|
||||
const sort_order = this.state.sort_order;
|
||||
const sortBy = this.state.sort_by;
|
||||
const sortOrder = this.state.sort_order;
|
||||
|
||||
// columns
|
||||
let columns = this.props.columns || [];
|
||||
columns = columns.filter((column) => {
|
||||
return (column.display === undefined || !!(column.display) === true);
|
||||
});
|
||||
columns = columns.filter(
|
||||
column => (column.display === undefined || !!(column.display) === true)
|
||||
);
|
||||
|
||||
// bulk actions
|
||||
let bulk_actions = this.props.bulk_actions || [];
|
||||
let bulkActions = this.props.bulk_actions || [];
|
||||
|
||||
if (this.state.group === 'trash' && bulk_actions.length > 0) {
|
||||
bulk_actions = [
|
||||
if (this.state.group === 'trash' && bulkActions.length > 0) {
|
||||
bulkActions = [
|
||||
{
|
||||
name: 'restore',
|
||||
label: MailPoet.I18n.t('restore'),
|
||||
@ -775,9 +765,9 @@ const Listing = React.createClass({
|
||||
}
|
||||
|
||||
// item actions
|
||||
const item_actions = this.props.item_actions || [];
|
||||
const itemActions = this.props.item_actions || [];
|
||||
|
||||
const table_classes = classNames(
|
||||
const tableClasses = classNames(
|
||||
'mailpoet_listing_table',
|
||||
'wp-list-table',
|
||||
'widefat',
|
||||
@ -822,7 +812,7 @@ const Listing = React.createClass({
|
||||
<div className="tablenav top clearfix">
|
||||
<ListingBulkActions
|
||||
count={this.state.count}
|
||||
bulk_actions={bulk_actions}
|
||||
bulk_actions={bulkActions}
|
||||
selection={this.state.selection}
|
||||
selected_ids={this.state.selected_ids}
|
||||
onBulkAction={this.handleBulkAction} />
|
||||
@ -840,16 +830,16 @@ const Listing = React.createClass({
|
||||
limit={this.state.limit}
|
||||
onSetPage={this.handleSetPage} />
|
||||
</div>
|
||||
<table className={table_classes}>
|
||||
<table className={tableClasses}>
|
||||
<thead>
|
||||
<ListingHeader
|
||||
onSort={this.handleSort}
|
||||
onSelectItems={this.handleSelectItems}
|
||||
selection={this.state.selection}
|
||||
sort_by={sort_by}
|
||||
sort_order={sort_order}
|
||||
sort_by={sortBy}
|
||||
sort_order={sortOrder}
|
||||
columns={columns}
|
||||
is_selectable={bulk_actions.length > 0} />
|
||||
is_selectable={bulkActions.length > 0} />
|
||||
</thead>
|
||||
|
||||
<ListingItems
|
||||
@ -859,7 +849,7 @@ const Listing = React.createClass({
|
||||
onTrashItem={this.handleTrashItem}
|
||||
onRefreshItems={this.handleRefreshItems}
|
||||
columns={columns}
|
||||
is_selectable={bulk_actions.length > 0}
|
||||
is_selectable={bulkActions.length > 0}
|
||||
onSelectItem={this.handleSelectItem}
|
||||
onSelectAll={this.handleSelectAll}
|
||||
selection={this.state.selection}
|
||||
@ -868,7 +858,7 @@ const Listing = React.createClass({
|
||||
group={this.state.group}
|
||||
count={this.state.count}
|
||||
limit={this.state.limit}
|
||||
item_actions={item_actions}
|
||||
item_actions={itemActions}
|
||||
messages={messages}
|
||||
items={items} />
|
||||
|
||||
@ -877,17 +867,17 @@ const Listing = React.createClass({
|
||||
onSort={this.handleSort}
|
||||
onSelectItems={this.handleSelectItems}
|
||||
selection={this.state.selection}
|
||||
sort_by={sort_by}
|
||||
sort_order={sort_order}
|
||||
sort_by={sortBy}
|
||||
sort_order={sortOrder}
|
||||
columns={columns}
|
||||
is_selectable={bulk_actions.length > 0} />
|
||||
is_selectable={bulkActions.length > 0} />
|
||||
</tfoot>
|
||||
|
||||
</table>
|
||||
<div className="tablenav bottom">
|
||||
<ListingBulkActions
|
||||
count={this.state.count}
|
||||
bulk_actions={bulk_actions}
|
||||
bulk_actions={bulkActions}
|
||||
selection={this.state.selection}
|
||||
selected_ids={this.state.selected_ids}
|
||||
onBulkAction={this.handleBulkAction} />
|
||||
|
@ -7,7 +7,6 @@ define([
|
||||
classNames,
|
||||
MailPoet
|
||||
) => {
|
||||
|
||||
const ListingPages = React.createClass({
|
||||
getInitialState: function () {
|
||||
return {
|
||||
@ -38,7 +37,7 @@ define([
|
||||
);
|
||||
},
|
||||
constrainPage: function (page) {
|
||||
return Math.min(Math.max(1, Math.abs(~~page)), this.getLastPage());
|
||||
return Math.min(Math.max(1, Math.abs(Number(page))), this.getLastPage());
|
||||
},
|
||||
handleSetManualPage: function (e) {
|
||||
if (e.which === 13) {
|
||||
@ -59,125 +58,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')
|
||||
.replace('%$1d', this.props.count.toLocaleString());
|
||||
}
|
||||
return (
|
||||
<div className={classes}>
|
||||
<span className="displaying-num">{ numberOfItemsLabel }</span>
|
||||
{ pagination }
|
||||
</div>
|
||||
);
|
||||
let numberOfItemsLabel;
|
||||
if (Number(this.props.count) === 1) {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsSingular');
|
||||
} else {
|
||||
numberOfItemsLabel = MailPoet.I18n.t('numberOfItemsMultiple')
|
||||
.replace('%$1d', parseInt(this.props.count, 10).toLocaleString());
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<span className="displaying-num">{ numberOfItemsLabel }</span>
|
||||
{ pagination }
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -5,7 +5,6 @@ define([
|
||||
MailPoet,
|
||||
React
|
||||
) => {
|
||||
|
||||
const ListingSearch = React.createClass({
|
||||
handleSearch: function (e) {
|
||||
e.preventDefault();
|
||||
@ -19,27 +18,26 @@ 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>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -111,9 +111,8 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
compileTemplate: function (template) {
|
||||
if (this.renderer === 'html') {
|
||||
return function () { return template; };
|
||||
} else {
|
||||
return window.Handlebars.compile(template);
|
||||
}
|
||||
return window.Handlebars.compile(template);
|
||||
},
|
||||
init: function (options) {
|
||||
var modal;
|
||||
@ -215,6 +214,7 @@ define('modal', ['mailpoet', 'jquery'],
|
||||
jQuery(document).on('keyup.mailpoet_modal', function (e) {
|
||||
if (this.opened === false) { return false; }
|
||||
if (e.keyCode === 27) { this.cancel(); }
|
||||
return true;
|
||||
}.bind(this));
|
||||
|
||||
// make sure the popup is repositioned when the window is resized
|
||||
|
@ -41,7 +41,6 @@ define('mp2migrator', ['mailpoet', 'jquery'], function (mp, jQuery) {
|
||||
jQuery('#upgrade-completed').show();
|
||||
}
|
||||
jQuery('#logger').append(row + '<br />\n');
|
||||
|
||||
});
|
||||
jQuery('#logger').append('<span class="error_msg">' + MailPoet.MP2Migrator.fatal_error + '</span>' + '<br />\n');
|
||||
}).always(function () {
|
||||
@ -206,5 +205,4 @@ define('mp2migrator', ['mailpoet', 'jquery'], function (mp, jQuery) {
|
||||
// Update the display
|
||||
MailPoet.MP2Migrator.updateDisplay();
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -36,5 +36,4 @@ define([
|
||||
window.EditorApplication = app;
|
||||
|
||||
return app;
|
||||
|
||||
});
|
||||
|
@ -281,7 +281,7 @@ define([
|
||||
// 2. Remove visual markings of drop position visualization
|
||||
this.view.$('.mailpoet_drop_marker').remove();
|
||||
},
|
||||
getDropPosition: function (eventX, eventY, is_unsafe) {
|
||||
getDropPosition: function (eventX, eventY, isUnsafe) {
|
||||
var SPECIAL_AREA_INSERTION_WIDTH = 0.00; // Disable special insertion. Default: 0.3
|
||||
|
||||
var element = this.view.$el;
|
||||
@ -307,7 +307,7 @@ define([
|
||||
var position;
|
||||
var indexAndPosition;
|
||||
|
||||
var unsafe = !!is_unsafe;
|
||||
var unsafe = !!isUnsafe;
|
||||
|
||||
if (this.getCollection().length === 0) {
|
||||
return {
|
||||
@ -358,7 +358,7 @@ define([
|
||||
|
||||
if (orientation === 'horizontal' && insertionType === 'special') {
|
||||
// Disable special insertion for horizontal containers
|
||||
return;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
@ -398,13 +398,12 @@ define([
|
||||
index: index,
|
||||
position: 'before'
|
||||
};
|
||||
} else {
|
||||
// Second half of the element
|
||||
return {
|
||||
index: index,
|
||||
position: 'after'
|
||||
};
|
||||
}
|
||||
// Second half of the element
|
||||
return {
|
||||
index: index,
|
||||
position: 'after'
|
||||
};
|
||||
},
|
||||
_computeSpecialIndex: function (eventX, eventY) {
|
||||
return this._computeCellIndex(eventX, eventY);
|
||||
|
@ -84,7 +84,6 @@ define([
|
||||
that.view.$el.addClass('mailpoet_hidden');
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
// call this function on every dragmove event
|
||||
onmove: function (event) {
|
||||
|
@ -27,7 +27,6 @@ define([
|
||||
_,
|
||||
jQuery
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -380,21 +379,21 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('automatedLatestContent', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('automatedLatestContent', {
|
||||
blockModel: Module.AutomatedLatestContentBlockModel,
|
||||
blockView: Module.AutomatedLatestContentBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'automatedLatestContent',
|
||||
widgetView: Module.AutomatedLatestContentWidgetView,
|
||||
priority: 97
|
||||
});
|
||||
});
|
||||
|
||||
App.on('start', function (App) {
|
||||
var Application = App;
|
||||
App.on('start', function (StartApp) {
|
||||
var Application = StartApp;
|
||||
Application._ALCSupervisor = new Module.ALCSupervisor();
|
||||
Application._ALCSupervisor.refresh();
|
||||
});
|
||||
|
@ -13,7 +13,6 @@ define([
|
||||
'mailpoet',
|
||||
'modal'
|
||||
], function (App, Marionette, SuperModel, _, jQuery, MailPoet) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -79,6 +78,7 @@ define([
|
||||
WidgetView.destroy();
|
||||
return node;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
},
|
||||
HighlightEditingBehavior: {}
|
||||
|
@ -8,7 +8,6 @@ define([
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function (App, BaseBlock, MailPoet, _, jQuery) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -132,13 +131,13 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('button', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('button', {
|
||||
blockModel: Module.ButtonBlockModel,
|
||||
blockView: Module.ButtonBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'button',
|
||||
widgetView: Module.ButtonWidgetView,
|
||||
priority: 92
|
||||
|
@ -11,7 +11,6 @@ define([
|
||||
'newsletter_editor/App',
|
||||
'newsletter_editor/blocks/base'
|
||||
], function (Backbone, Marionette, _, jQuery, App, BaseBlock) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -54,6 +53,7 @@ define([
|
||||
if (invalidBlock) {
|
||||
return invalidBlock.validationError;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
parse: function (response) {
|
||||
// If container has any blocks - add them to a collection
|
||||
@ -132,6 +132,7 @@ define([
|
||||
WidgetView.destroy();
|
||||
return node;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
testAttachToInstance: function (model, view) {
|
||||
// Attach Draggable only to layout containers and disable it
|
||||
@ -149,7 +150,6 @@ define([
|
||||
if (this.model.get('blocks').length === 2) return Module.TwoColumnContainerWidgetView;
|
||||
}
|
||||
return Module.OneColumnContainerWidgetView;
|
||||
|
||||
},
|
||||
initialize: function (options) {
|
||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||
@ -194,13 +194,6 @@ define([
|
||||
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');
|
||||
@ -208,6 +201,13 @@ define([
|
||||
$overlay.hide();
|
||||
$overlay.off('click');
|
||||
};
|
||||
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();
|
||||
};
|
||||
if ($toggleButton.hasClass('mailpoet_container_layer_active')) {
|
||||
disableContainerLayer();
|
||||
} else {
|
||||
@ -336,25 +336,25 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('container', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('container', {
|
||||
blockModel: Module.ContainerBlockModel,
|
||||
blockView: Module.ContainerBlockView
|
||||
});
|
||||
|
||||
App.registerLayoutWidget({
|
||||
BeforeStartApp.registerLayoutWidget({
|
||||
name: 'oneColumnLayout',
|
||||
priority: 100,
|
||||
widgetView: Module.OneColumnContainerWidgetView
|
||||
});
|
||||
|
||||
App.registerLayoutWidget({
|
||||
BeforeStartApp.registerLayoutWidget({
|
||||
name: 'twoColumnLayout',
|
||||
priority: 100,
|
||||
widgetView: Module.TwoColumnContainerWidgetView
|
||||
});
|
||||
|
||||
App.registerLayoutWidget({
|
||||
BeforeStartApp.registerLayoutWidget({
|
||||
name: 'threeColumnLayout',
|
||||
priority: 100,
|
||||
widgetView: Module.ThreeColumnContainerWidgetView
|
||||
|
@ -7,7 +7,6 @@ define([
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function (App, BaseBlock, _, jQuery) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -138,13 +137,13 @@ define([
|
||||
}
|
||||
}
|
||||
});
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('divider', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('divider', {
|
||||
blockModel: Module.DividerBlockModel,
|
||||
blockView: Module.DividerBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'divider',
|
||||
widgetView: Module.DividerWidgetView,
|
||||
priority: 93
|
||||
|
@ -7,7 +7,6 @@ define([
|
||||
'underscore',
|
||||
'mailpoet'
|
||||
], function (App, BaseBlock, _, MailPoet) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -110,13 +109,13 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('footer', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('footer', {
|
||||
blockModel: Module.FooterBlockModel,
|
||||
blockView: Module.FooterBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'footer',
|
||||
widgetView: Module.FooterWidgetView,
|
||||
priority: 99
|
||||
|
@ -7,7 +7,6 @@ define([
|
||||
'underscore',
|
||||
'mailpoet'
|
||||
], function (App, BaseBlock, _, MailPoet) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -110,13 +109,13 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('header', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('header', {
|
||||
blockModel: Module.HeaderBlockModel,
|
||||
blockView: Module.HeaderBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'header',
|
||||
widgetView: Module.HeaderWidgetView,
|
||||
priority: 98
|
||||
|
@ -8,7 +8,6 @@ define([
|
||||
'mailpoet',
|
||||
'jquery'
|
||||
], function (App, BaseBlock, _, MailPoet, jQuery) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -413,13 +412,13 @@ define([
|
||||
});
|
||||
Module.ImageWidgetView = ImageWidgetView;
|
||||
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('image', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('image', {
|
||||
blockModel: Module.ImageBlockModel,
|
||||
blockView: Module.ImageBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'image',
|
||||
widgetView: Module.ImageWidgetView,
|
||||
priority: 91
|
||||
|
@ -36,7 +36,6 @@ define([
|
||||
ButtonBlock,
|
||||
DividerBlock
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -143,6 +142,7 @@ define([
|
||||
}).always(function () {
|
||||
that.trigger('morePostsLoaded');
|
||||
});
|
||||
return true;
|
||||
},
|
||||
_refreshTransformedPosts: function () {
|
||||
var that = this;
|
||||
@ -582,13 +582,13 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('posts', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('posts', {
|
||||
blockModel: Module.PostsBlockModel,
|
||||
blockView: Module.PostsBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'posts',
|
||||
widgetView: Module.PostsWidgetView,
|
||||
priority: 96
|
||||
|
@ -10,7 +10,6 @@ define([
|
||||
'underscore',
|
||||
'jquery'
|
||||
], function (App, BaseBlock, Backbone, Marionette, SuperModel, _, jQuery) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -195,6 +194,7 @@ define([
|
||||
} else {
|
||||
return this.changeField('link', event);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
changeField: function (field, event) {
|
||||
this.model.set(field, jQuery(event.target).val());
|
||||
@ -298,13 +298,13 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('social', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('social', {
|
||||
blockModel: Module.SocialBlockModel,
|
||||
blockView: Module.SocialBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'social',
|
||||
widgetView: Module.SocialWidgetView,
|
||||
priority: 95
|
||||
|
@ -6,7 +6,6 @@ define([
|
||||
'newsletter_editor/blocks/base',
|
||||
'underscore'
|
||||
], function (App, BaseBlock, _) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -87,13 +86,13 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('spacer', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('spacer', {
|
||||
blockModel: Module.SpacerBlockModel,
|
||||
blockView: Module.SpacerBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'spacer',
|
||||
widgetView: Module.SpacerWidgetView,
|
||||
priority: 94
|
||||
|
@ -7,7 +7,6 @@ define([
|
||||
'underscore',
|
||||
'mailpoet'
|
||||
], function (App, BaseBlock, _, MailPoet) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -94,13 +93,13 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App) {
|
||||
App.registerBlockType('text', {
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
BeforeStartApp.registerBlockType('text', {
|
||||
blockModel: Module.TextBlockModel,
|
||||
blockView: Module.TextBlockView
|
||||
});
|
||||
|
||||
App.registerWidget({
|
||||
BeforeStartApp.registerWidget({
|
||||
name: 'text',
|
||||
widgetView: Module.TextWidgetView,
|
||||
priority: 90
|
||||
|
@ -11,8 +11,8 @@
|
||||
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, _);
|
||||
define(['backbone.marionette', 'backbone.radio', 'underscore'], function (BackboneMarionette, BackboneRadio, underscore) {
|
||||
return factory(BackboneMarionette, BackboneRadio, underscore);
|
||||
});
|
||||
}
|
||||
else if (typeof exports !== 'undefined') {
|
||||
|
@ -4,7 +4,6 @@ define([
|
||||
'mailpoet',
|
||||
'ajax'
|
||||
], function (App, _, MailPoet) {
|
||||
|
||||
var Module = {};
|
||||
|
||||
Module._query = function (args) {
|
||||
|
@ -2,7 +2,6 @@ define([
|
||||
'newsletter_editor/App',
|
||||
'backbone.supermodel'
|
||||
], function (App, SuperModel) {
|
||||
|
||||
var Module = {};
|
||||
|
||||
Module.ConfigModel = SuperModel.extend({
|
||||
@ -24,8 +23,8 @@ define([
|
||||
return Module._config;
|
||||
};
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
var Application = App;
|
||||
App.on('before:start', function (BeforeStartApp, options) {
|
||||
var Application = BeforeStartApp;
|
||||
// Expose config methods globally
|
||||
Application.getConfig = Module.getConfig;
|
||||
Application.setConfig = Module.setConfig;
|
||||
|
@ -33,16 +33,14 @@ define([
|
||||
Module.getBlockTypeModel = function (type) {
|
||||
if (type in Module._blockTypes) {
|
||||
return Module._blockTypes[type].blockModel;
|
||||
} else {
|
||||
throw 'Block type not supported: ' + type;
|
||||
}
|
||||
throw 'Block type not supported: ' + type;
|
||||
};
|
||||
Module.getBlockTypeView = function (type) {
|
||||
if (type in Module._blockTypes) {
|
||||
return Module._blockTypes[type].blockView;
|
||||
} else {
|
||||
throw 'Block type not supported: ' + type;
|
||||
}
|
||||
throw 'Block type not supported: ' + type;
|
||||
};
|
||||
|
||||
Module.getBody = function () {
|
||||
@ -68,21 +66,21 @@ define([
|
||||
};
|
||||
|
||||
App.on('before:start', function (Application, options) {
|
||||
var App = Application;
|
||||
var BeforeStartApp = Application;
|
||||
// Expose block methods globally
|
||||
App.registerBlockType = Module.registerBlockType;
|
||||
App.getBlockTypeModel = Module.getBlockTypeModel;
|
||||
App.getBlockTypeView = Module.getBlockTypeView;
|
||||
App.toJSON = Module.toJSON;
|
||||
App.getBody = Module.getBody;
|
||||
App.getNewsletter = Module.getNewsletter;
|
||||
App.findModels = Module.findModels;
|
||||
BeforeStartApp.registerBlockType = Module.registerBlockType;
|
||||
BeforeStartApp.getBlockTypeModel = Module.getBlockTypeModel;
|
||||
BeforeStartApp.getBlockTypeView = Module.getBlockTypeView;
|
||||
BeforeStartApp.toJSON = Module.toJSON;
|
||||
BeforeStartApp.getBody = Module.getBody;
|
||||
BeforeStartApp.getNewsletter = Module.getNewsletter;
|
||||
BeforeStartApp.findModels = Module.findModels;
|
||||
|
||||
Module.newsletter = new Module.NewsletterModel(_.omit(_.clone(options.newsletter), ['body']));
|
||||
});
|
||||
|
||||
App.on('start', function (Application, options) {
|
||||
var App = Application;
|
||||
var StartApp = Application;
|
||||
var body = options.newsletter.body;
|
||||
var content = (_.has(body, 'content')) ? body.content : {};
|
||||
|
||||
@ -93,13 +91,13 @@ define([
|
||||
);
|
||||
}
|
||||
|
||||
App._contentContainer = new (App.getBlockTypeModel('container'))(content, { parse: true });
|
||||
App._contentContainerView = new (App.getBlockTypeView('container'))({
|
||||
model: App._contentContainer,
|
||||
StartApp._contentContainer = new (StartApp.getBlockTypeModel('container'))(content, { parse: true });
|
||||
StartApp._contentContainerView = new (StartApp.getBlockTypeView('container'))({
|
||||
model: StartApp._contentContainer,
|
||||
renderOptions: { depth: 0 }
|
||||
});
|
||||
|
||||
App._appView.showChildView('contentRegion', App._contentContainerView);
|
||||
StartApp._appView.showChildView('contentRegion', StartApp._contentContainerView);
|
||||
});
|
||||
|
||||
|
||||
|
@ -6,7 +6,6 @@ define([
|
||||
'jquery',
|
||||
'mailpoet'
|
||||
], function (App, Backbone, Marionette, _, jQuery, MailPoet) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -29,8 +28,8 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('start', function (App) {
|
||||
App._appView.showChildView('headingRegion', new Module.HeadingView({ model: App.getNewsletter() }));
|
||||
App.on('start', function (StartApp) {
|
||||
StartApp._appView.showChildView('headingRegion', new Module.HeadingView({ model: StartApp.getNewsletter() }));
|
||||
MailPoet.helpTooltip.show(document.getElementById('tooltip-designer-subject-line'), {
|
||||
tooltipId: 'tooltip-designer-subject-line-ti',
|
||||
tooltip: MailPoet.I18n.t('helpTooltipDesignerSubjectLine'),
|
||||
|
@ -25,7 +25,6 @@ define([
|
||||
_,
|
||||
$
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -33,7 +32,6 @@ define([
|
||||
|
||||
// Save editor contents to server
|
||||
Module.save = function () {
|
||||
|
||||
var json = App.toJSON();
|
||||
|
||||
// Stringify to enable transmission of primitive non-string value types
|
||||
@ -235,7 +233,6 @@ define([
|
||||
});
|
||||
this.hideOptionContents();
|
||||
}
|
||||
|
||||
},
|
||||
toggleExportTemplate: function () {
|
||||
this.$('.mailpoet_export_template_container').toggleClass('mailpoet_hidden');
|
||||
@ -350,10 +347,11 @@ define([
|
||||
|
||||
return message;
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
App.on('before:start', function (App) {
|
||||
var Application = App;
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
var Application = BeforeStartApp;
|
||||
Application.save = Module.save;
|
||||
Application.getChannel().on('autoSave', Module.autoSave);
|
||||
|
||||
@ -362,9 +360,9 @@ define([
|
||||
Application.getChannel().reply('save', Application.save);
|
||||
});
|
||||
|
||||
App.on('start', function (App) {
|
||||
App.on('start', function (BeforeStartApp) {
|
||||
var saveView = new Module.SaveView();
|
||||
App._appView.showChildView('bottomRegion', saveView);
|
||||
BeforeStartApp._appView.showChildView('bottomRegion', saveView);
|
||||
});
|
||||
|
||||
return Module;
|
||||
|
@ -17,7 +17,6 @@ define([
|
||||
_,
|
||||
jQuery
|
||||
) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -111,12 +110,12 @@ define([
|
||||
// position of the sidebar would be scrollable and not fixed
|
||||
// partially out of visible screen
|
||||
this.$el.parent().each(function () {
|
||||
var calculated_left;
|
||||
var calculatedLeft;
|
||||
var self = jQuery(this);
|
||||
|
||||
if (self.css('position') === 'fixed') {
|
||||
calculated_left = self.parent().offset().left - jQuery(window).scrollLeft();
|
||||
self.css('left', calculated_left + 'px');
|
||||
calculatedLeft = self.parent().offset().left - jQuery(window).scrollLeft();
|
||||
self.css('left', calculatedLeft + 'px');
|
||||
} else {
|
||||
self.css('left', '');
|
||||
}
|
||||
@ -339,6 +338,7 @@ define([
|
||||
}
|
||||
});
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
@ -360,18 +360,18 @@ define([
|
||||
}
|
||||
});
|
||||
|
||||
App.on('before:start', function (App) {
|
||||
var Application = App;
|
||||
App.on('before:start', function (BeforeStartApp) {
|
||||
var Application = BeforeStartApp;
|
||||
Application.registerWidget = Module.registerWidget;
|
||||
Application.getWidgets = Module.getWidgets;
|
||||
Application.registerLayoutWidget = Module.registerLayoutWidget;
|
||||
Application.getLayoutWidgets = Module.getLayoutWidgets;
|
||||
});
|
||||
|
||||
App.on('start', function (App) {
|
||||
App.on('start', function (StartApp) {
|
||||
var sidebarView = new SidebarView();
|
||||
|
||||
App._appView.showChildView('sidebarRegion', sidebarView);
|
||||
StartApp._appView.showChildView('sidebarRegion', sidebarView);
|
||||
|
||||
MailPoet.helpTooltip.show(document.getElementById('tooltip-send-preview'), {
|
||||
tooltipId: 'tooltip-editor-send-preview',
|
||||
|
@ -4,7 +4,6 @@ define([
|
||||
'backbone.supermodel',
|
||||
'underscore'
|
||||
], function (App, Marionette, SuperModel, _) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var Module = {};
|
||||
@ -69,8 +68,8 @@ define([
|
||||
return App.getConfig().get('availableStyles');
|
||||
};
|
||||
|
||||
App.on('before:start', function (App, options) {
|
||||
var Application = App;
|
||||
App.on('before:start', function (BeforeStartApp, options) {
|
||||
var Application = BeforeStartApp;
|
||||
var body;
|
||||
var globalStyles;
|
||||
// Expose style methods to global application
|
||||
@ -83,9 +82,9 @@ define([
|
||||
this.setGlobalStyles(globalStyles);
|
||||
});
|
||||
|
||||
App.on('start', function (App) {
|
||||
var stylesView = new Module.StylesView({ model: App.getGlobalStyles() });
|
||||
App._appView.showChildView('stylesRegion', stylesView);
|
||||
App.on('start', function (StartApp) {
|
||||
var stylesView = new Module.StylesView({ model: StartApp.getGlobalStyles() });
|
||||
StartApp._appView.showChildView('stylesRegion', stylesView);
|
||||
});
|
||||
|
||||
return Module;
|
||||
|
@ -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;
|
||||
|
@ -46,14 +46,14 @@ define(
|
||||
|
||||
let label = step.label;
|
||||
|
||||
if (step['link'] !== undefined && this.props.step !== step.name) {
|
||||
if (step.link !== undefined && this.props.step !== step.name) {
|
||||
label = (
|
||||
<Link to={step.link}>{ step.label }</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span key={'step-' + index}>
|
||||
<span key={`step-${index}`}>
|
||||
<span className={stepClasses}>
|
||||
{ label }
|
||||
</span>
|
||||
|
@ -9,7 +9,7 @@ import jQuery from 'jquery';
|
||||
import Hooks from 'wp-js-hooks';
|
||||
import StatsBadge from 'newsletters/badges/stats.jsx';
|
||||
|
||||
const _QueueMixin = {
|
||||
const QueueMixin = {
|
||||
pauseSending: function (newsletter) {
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
@ -19,12 +19,12 @@ const _QueueMixin = {
|
||||
newsletter_id: newsletter.id,
|
||||
},
|
||||
}).done(() => {
|
||||
jQuery('#resume_' + newsletter.id).show();
|
||||
jQuery('#pause_' + newsletter.id).hide();
|
||||
jQuery(`#resume_${newsletter.id}`).show();
|
||||
jQuery(`#pause_${newsletter.id}`).hide();
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
@ -39,107 +39,106 @@ const _QueueMixin = {
|
||||
newsletter_id: newsletter.id,
|
||||
},
|
||||
}).done(() => {
|
||||
jQuery('#pause_' + newsletter.id).show();
|
||||
jQuery('#resume_' + newsletter.id).hide();
|
||||
jQuery(`#pause_${newsletter.id}`).show();
|
||||
jQuery(`#resume_${newsletter.id}`).hide();
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
renderQueueStatus: function (newsletter, mailer_log) {
|
||||
renderQueueStatus: function (newsletter, mailerLog) {
|
||||
if (!newsletter.queue) {
|
||||
return (
|
||||
<span>{MailPoet.I18n.t('notSentYet')}</span>
|
||||
);
|
||||
} else if (mailer_log.status === 'paused' && newsletter.queue.status !== 'completed') {
|
||||
} else if (mailerLog.status === 'paused' && newsletter.queue.status !== 'completed') {
|
||||
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', 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 progressBarWidth = 0;
|
||||
|
||||
if (isNaN(percentage)) {
|
||||
percentage = MailPoet.I18n.t('noSubscribers');
|
||||
} else {
|
||||
progressBarWidth = percentage;
|
||||
percentage += '%';
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={progressClasses}>
|
||||
<span
|
||||
className="mailpoet_progress_bar"
|
||||
style={{ width: `${progressBarWidth}%` }}
|
||||
></span>
|
||||
<span className="mailpoet_progress_label">
|
||||
{ percentage }
|
||||
</span>
|
||||
</div>
|
||||
<p style={{ textAlign: 'center' }}>
|
||||
{ label }
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@ -150,9 +149,9 @@ const trackStatsCTAClicked = function () {
|
||||
);
|
||||
};
|
||||
|
||||
const _StatisticsMixin = {
|
||||
renderStatistics: function (newsletter, is_sent, current_time) {
|
||||
let sent = is_sent;
|
||||
const StatisticsMixin = {
|
||||
renderStatistics: function (newsletter, isSent, currentTime) {
|
||||
let sent = isSent;
|
||||
if (sent === undefined) {
|
||||
// condition for standard and post notification listings
|
||||
sent = newsletter.statistics
|
||||
@ -170,74 +169,74 @@ const _StatisticsMixin = {
|
||||
params = Hooks.applyFilters('mailpoet_newsletters_listing_stats_before', params, newsletter);
|
||||
|
||||
// welcome emails provide explicit total_sent value
|
||||
const total_sent = ~~(newsletter.total_sent || newsletter.queue.count_processed);
|
||||
const totalSent = Number((newsletter.total_sent || newsletter.queue.count_processed));
|
||||
|
||||
let percentage_clicked = 0;
|
||||
let percentage_opened = 0;
|
||||
let percentage_unsubscribed = 0;
|
||||
let percentageClicked = 0;
|
||||
let percentageOpened = 0;
|
||||
let percentageUnsubscribed = 0;
|
||||
|
||||
if (total_sent > 0) {
|
||||
percentage_clicked = (newsletter.statistics.clicked * 100) / total_sent;
|
||||
percentage_opened = (newsletter.statistics.opened * 100) / total_sent;
|
||||
percentage_unsubscribed = (newsletter.statistics.unsubscribed * 100) / total_sent;
|
||||
if (totalSent > 0) {
|
||||
percentageClicked = (newsletter.statistics.clicked * 100) / totalSent;
|
||||
percentageOpened = (newsletter.statistics.opened * 100) / totalSent;
|
||||
percentageUnsubscribed = (newsletter.statistics.unsubscribed * 100) / totalSent;
|
||||
}
|
||||
|
||||
// format to 1 decimal place
|
||||
const percentage_clicked_display = MailPoet.Num.toLocaleFixed(percentage_clicked, 1);
|
||||
const percentage_opened_display = MailPoet.Num.toLocaleFixed(percentage_opened, 1);
|
||||
const percentage_unsubscribed_display = MailPoet.Num.toLocaleFixed(percentage_unsubscribed, 1);
|
||||
const percentageClickedDisplay = MailPoet.Num.toLocaleFixed(percentageClicked, 1);
|
||||
const percentageOpenedDisplay = MailPoet.Num.toLocaleFixed(percentageOpened, 1);
|
||||
const percentageUnsubscribedDisplay = MailPoet.Num.toLocaleFixed(percentageUnsubscribed, 1);
|
||||
|
||||
let show_stats_timeout;
|
||||
let newsletter_date;
|
||||
let sent_hours_ago;
|
||||
let too_early_for_stats;
|
||||
let show_kb_link;
|
||||
if (current_time !== undefined) {
|
||||
let showStatsTimeout;
|
||||
let newsletterDate;
|
||||
let sentHoursAgo;
|
||||
let tooEarlyForStats;
|
||||
let showKbLink;
|
||||
if (currentTime !== undefined) {
|
||||
// standard emails and post notifications:
|
||||
// display green box for newsletters that were just sent
|
||||
show_stats_timeout = 6; // in hours
|
||||
newsletter_date = newsletter.queue.scheduled_at || newsletter.queue.created_at;
|
||||
sent_hours_ago = moment(current_time).diff(moment(newsletter_date), 'hours');
|
||||
too_early_for_stats = sent_hours_ago < show_stats_timeout;
|
||||
show_kb_link = true;
|
||||
showStatsTimeout = 6; // in hours
|
||||
newsletterDate = newsletter.queue.scheduled_at || newsletter.queue.created_at;
|
||||
sentHoursAgo = moment(currentTime).diff(moment(newsletterDate), 'hours');
|
||||
tooEarlyForStats = sentHoursAgo < showStatsTimeout;
|
||||
showKbLink = true;
|
||||
} else {
|
||||
// welcome emails: no green box and KB link
|
||||
too_early_for_stats = false;
|
||||
show_kb_link = false;
|
||||
tooEarlyForStats = false;
|
||||
showKbLink = false;
|
||||
}
|
||||
|
||||
const improveStatsKBLink = 'http://beta.docs.mailpoet.com/article/191-how-to-improve-my-open-and-click-rates';
|
||||
|
||||
// thresholds to display badges
|
||||
const min_newsletters_sent = 20;
|
||||
const min_newsletter_opens = 5;
|
||||
const minNewslettersSent = 20;
|
||||
const minNewsletterOpens = 5;
|
||||
|
||||
let content;
|
||||
if (total_sent >= min_newsletters_sent
|
||||
&& newsletter.statistics.opened >= min_newsletter_opens
|
||||
&& !too_early_for_stats
|
||||
if (totalSent >= minNewslettersSent
|
||||
&& newsletter.statistics.opened >= minNewsletterOpens
|
||||
&& !tooEarlyForStats
|
||||
) {
|
||||
// display stats with badges
|
||||
content = (
|
||||
<div className="mailpoet_stats_text">
|
||||
<div>
|
||||
<span>{ percentage_opened_display }% </span>
|
||||
<span>{ percentageOpenedDisplay }% </span>
|
||||
<StatsBadge
|
||||
stat="opened"
|
||||
rate={percentage_opened}
|
||||
rate={percentageOpened}
|
||||
tooltipId={`opened-${newsletter.id}`}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span>{ percentage_clicked_display }% </span>
|
||||
<span>{ percentageClickedDisplay }% </span>
|
||||
<StatsBadge
|
||||
stat="clicked"
|
||||
rate={percentage_clicked}
|
||||
rate={percentageClicked}
|
||||
tooltipId={`clicked-${newsletter.id}`}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<span className="mailpoet_stat_hidden">{ percentage_unsubscribed_display }%</span>
|
||||
<span className="mailpoet_stat_hidden">{ percentageUnsubscribedDisplay }%</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -246,17 +245,17 @@ const _StatisticsMixin = {
|
||||
content = (
|
||||
<div>
|
||||
<span className="mailpoet_stats_text">
|
||||
{ percentage_opened_display }%,
|
||||
{ percentageOpenedDisplay }%,
|
||||
{ ' ' }
|
||||
{ percentage_clicked_display }%
|
||||
{ percentageClickedDisplay }%
|
||||
<span className="mailpoet_stat_hidden">
|
||||
, { percentage_unsubscribed_display }%
|
||||
, { percentageUnsubscribedDisplay }%
|
||||
</span>
|
||||
</span>
|
||||
{ too_early_for_stats && (
|
||||
{ tooEarlyForStats && (
|
||||
<div className="mailpoet_badge mailpoet_badge_green">
|
||||
{MailPoet.I18n.t('checkBackInHours')
|
||||
.replace('%$1d', show_stats_timeout - sent_hours_ago)}
|
||||
.replace('%$1d', showStatsTimeout - sentHoursAgo)}
|
||||
</div>
|
||||
) }
|
||||
</div>
|
||||
@ -264,18 +263,18 @@ const _StatisticsMixin = {
|
||||
}
|
||||
|
||||
// thresholds to display bad open rate help
|
||||
const max_percentage_opened = 5;
|
||||
const min_sent_hours_ago = 24;
|
||||
const min_total_sent = 10;
|
||||
const maxPercentageOpened = 5;
|
||||
const minSentHoursAgo = 24;
|
||||
const minTotalSent = 10;
|
||||
|
||||
let after_content;
|
||||
if (show_kb_link
|
||||
&& percentage_opened < max_percentage_opened
|
||||
&& sent_hours_ago >= min_sent_hours_ago
|
||||
&& total_sent >= min_total_sent
|
||||
let afterContent;
|
||||
if (showKbLink
|
||||
&& percentageOpened < maxPercentageOpened
|
||||
&& sentHoursAgo >= minSentHoursAgo
|
||||
&& totalSent >= minTotalSent
|
||||
) {
|
||||
// help link for bad open rate
|
||||
after_content = (
|
||||
afterContent = (
|
||||
<div>
|
||||
<a
|
||||
href={improveStatsKBLink}
|
||||
@ -288,7 +287,7 @@ const _StatisticsMixin = {
|
||||
);
|
||||
}
|
||||
|
||||
if (total_sent > 0 && params.link) {
|
||||
if (totalSent > 0 && params.link) {
|
||||
// wrap content in a link
|
||||
if (params.externalLink) {
|
||||
return (
|
||||
@ -300,29 +299,28 @@ const _StatisticsMixin = {
|
||||
>
|
||||
{content}
|
||||
</a>
|
||||
{after_content}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<Link
|
||||
key={`stats-${newsletter.id}`}
|
||||
to={params.link}
|
||||
onClick={params.onClick || null}
|
||||
>
|
||||
{content}
|
||||
</Link>
|
||||
{after_content}
|
||||
{afterContent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<Link
|
||||
key={`stats-${newsletter.id}`}
|
||||
to={params.link}
|
||||
onClick={params.onClick || null}
|
||||
>
|
||||
{content}
|
||||
</Link>
|
||||
{afterContent}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{content}
|
||||
{after_content}
|
||||
{afterContent}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
@ -341,8 +339,8 @@ const _StatisticsMixin = {
|
||||
},
|
||||
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;
|
||||
const countProcessed = newsletter.queue && newsletter.queue.count_processed;
|
||||
return Number(newsletter.total_sent || countProcessed) > 0;
|
||||
},
|
||||
});
|
||||
return actions;
|
||||
@ -359,7 +357,7 @@ const _StatisticsMixin = {
|
||||
},
|
||||
};
|
||||
|
||||
const _MailerMixin = {
|
||||
const MailerMixin = {
|
||||
checkMailerStatus: function (state) {
|
||||
if (state.meta.mta_log.error && state.meta.mta_log.status === 'paused') {
|
||||
MailPoet.Notice.error(
|
||||
@ -376,8 +374,8 @@ const _MailerMixin = {
|
||||
}
|
||||
},
|
||||
getMailerError(state) {
|
||||
let mailer_error_notice;
|
||||
const mailer_check_settings_notice = ReactStringReplace(
|
||||
let mailerErrorNotice;
|
||||
const mailerCheckSettingsNotice = ReactStringReplace(
|
||||
MailPoet.I18n.t('mailerCheckSettingsNotice'),
|
||||
/\[link\](.*?)\[\/link\]/g,
|
||||
match => (
|
||||
@ -385,19 +383,23 @@ const _MailerMixin = {
|
||||
)
|
||||
);
|
||||
if (state.meta.mta_log.error.operation === 'send') {
|
||||
mailer_error_notice =
|
||||
mailerErrorNotice =
|
||||
MailPoet.I18n.t('mailerSendErrorNotice')
|
||||
.replace('%$1s', state.meta.mta_method)
|
||||
.replace('%$2s', state.meta.mta_log.error.error_message);
|
||||
} else {
|
||||
mailer_error_notice =
|
||||
mailerErrorNotice =
|
||||
MailPoet.I18n.t('mailerConnectionErrorNotice')
|
||||
.replace('%$1s', state.meta.mta_log.error.error_message);
|
||||
}
|
||||
if (state.meta.mta_log.error.error_code) {
|
||||
mailerErrorNotice += ` ${MailPoet.I18n.t('mailerErrorCode')
|
||||
.replace('%$1s', state.meta.mta_log.error.error_code)}`;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<p>{ mailer_error_notice }</p>
|
||||
<p>{ mailer_check_settings_notice }</p>
|
||||
<p>{ mailerErrorNotice }</p>
|
||||
<p>{ mailerCheckSettingsNotice }</p>
|
||||
<p>
|
||||
<a href="javascript:;"
|
||||
className="button"
|
||||
@ -419,7 +421,7 @@ const _MailerMixin = {
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
@ -428,6 +430,6 @@ const _MailerMixin = {
|
||||
};
|
||||
|
||||
|
||||
export { _QueueMixin as QueueMixin };
|
||||
export { _StatisticsMixin as StatisticsMixin };
|
||||
export { _MailerMixin as MailerMixin };
|
||||
export { QueueMixin };
|
||||
export { StatisticsMixin };
|
||||
export { MailerMixin };
|
||||
|
@ -18,7 +18,7 @@ import {
|
||||
|
||||
const messages = {
|
||||
onTrash: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -33,7 +33,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onDelete: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -48,7 +48,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onRestore: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -91,7 +91,7 @@ const columns = [
|
||||
},
|
||||
];
|
||||
|
||||
const bulk_actions = [
|
||||
const bulkActions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('moveToTrash'),
|
||||
@ -99,7 +99,7 @@ const bulk_actions = [
|
||||
},
|
||||
];
|
||||
|
||||
const newsletter_actions = [
|
||||
const newsletterActions = [
|
||||
{
|
||||
name: 'view',
|
||||
link: function (newsletter) {
|
||||
@ -141,7 +141,7 @@ const newsletter_actions = [
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
@ -165,7 +165,7 @@ const NewsletterListNotification = React.createClass({
|
||||
endpoint: 'newsletters',
|
||||
action: 'setStatus',
|
||||
data: {
|
||||
id: ~~(e.target.getAttribute('data-id')),
|
||||
id: Number(e.target.getAttribute('data-id')),
|
||||
status: e.target.value,
|
||||
},
|
||||
}).done((response) => {
|
||||
@ -195,12 +195,12 @@ 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 segments = newsletter.segments.map(segment => 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 +209,52 @@ 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;
|
||||
|
||||
default:
|
||||
sendingFrequency = 'Invalid sending frequency';
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<span>
|
||||
{ sendingFrequency } { sendingToSegments }
|
||||
@ -261,18 +262,17 @@ const NewsletterListNotification = React.createClass({
|
||||
);
|
||||
},
|
||||
renderHistoryLink: function (newsletter) {
|
||||
const childrenCount = ~~(newsletter.children_count);
|
||||
const childrenCount = Number((newsletter.children_count));
|
||||
if (childrenCount === 0) {
|
||||
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(
|
||||
@ -311,7 +311,7 @@ const NewsletterListNotification = React.createClass({
|
||||
return (
|
||||
<div>
|
||||
<h1 className="title">
|
||||
{MailPoet.I18n.t('pageTitle')} <Link className="page-title-action" to="/new">{MailPoet.I18n.t('new')}</Link>
|
||||
{MailPoet.I18n.t('pageTitle')} <Link className="page-title-action" to="/new" data-automation-id="new_email">{MailPoet.I18n.t('new')}</Link>
|
||||
</h1>
|
||||
|
||||
<ListingTabs tab="notification" />
|
||||
@ -325,8 +325,8 @@ const NewsletterListNotification = React.createClass({
|
||||
base_url="notification"
|
||||
onRenderItem={this.renderItem}
|
||||
columns={columns}
|
||||
bulk_actions={bulk_actions}
|
||||
item_actions={newsletter_actions}
|
||||
bulk_actions={bulkActions}
|
||||
item_actions={newsletterActions}
|
||||
messages={messages}
|
||||
auto_refresh={true}
|
||||
sort_by="updated_at"
|
||||
|
@ -13,7 +13,7 @@ import {
|
||||
MailerMixin,
|
||||
} from 'newsletters/listings/mixins.jsx';
|
||||
|
||||
const mailpoet_tracking_enabled = (!!(window['mailpoet_tracking_enabled']));
|
||||
const mailpoetTrackingEnabled = (!!(window.mailpoet_tracking_enabled));
|
||||
|
||||
const columns = [
|
||||
{
|
||||
@ -31,7 +31,7 @@ const columns = [
|
||||
{
|
||||
name: 'statistics',
|
||||
label: MailPoet.I18n.t('statistics'),
|
||||
display: mailpoet_tracking_enabled,
|
||||
display: mailpoetTrackingEnabled,
|
||||
},
|
||||
{
|
||||
name: 'sent_at',
|
||||
@ -39,7 +39,7 @@ const columns = [
|
||||
},
|
||||
];
|
||||
|
||||
let newsletter_actions = [
|
||||
let newsletterActions = [
|
||||
{
|
||||
name: 'view',
|
||||
link: function (newsletter) {
|
||||
@ -53,7 +53,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);
|
||||
newsletterActions = Hooks.applyFilters('mailpoet_newsletters_listings_notification_history_actions', newsletterActions);
|
||||
|
||||
const NewsletterListNotificationHistory = React.createClass({
|
||||
mixins: [QueueMixin, StatisticsMixin, MailerMixin],
|
||||
@ -64,9 +64,7 @@ const NewsletterListNotificationHistory = React.createClass({
|
||||
'has-row-actions'
|
||||
);
|
||||
|
||||
const segments = newsletter.segments.map((segment) => {
|
||||
return segment.name;
|
||||
}).join(', ');
|
||||
const segments = newsletter.segments.map(segment => segment.name).join(', ');
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -85,7 +83,7 @@ const NewsletterListNotificationHistory = React.createClass({
|
||||
<td className="column" data-colname={MailPoet.I18n.t('lists')}>
|
||||
{ segments }
|
||||
</td>
|
||||
{ (mailpoet_tracking_enabled === true) ? (
|
||||
{ (mailpoetTrackingEnabled === true) ? (
|
||||
<td className="column" data-colname={MailPoet.I18n.t('statistics')}>
|
||||
{ this.renderStatistics(newsletter, undefined, meta.current_time) }
|
||||
</td>
|
||||
@ -100,7 +98,7 @@ const NewsletterListNotificationHistory = React.createClass({
|
||||
return (
|
||||
<div>
|
||||
<h1 className="title">
|
||||
{MailPoet.I18n.t('pageTitle')} <Link className="page-title-action" to="/new">{MailPoet.I18n.t('new')}</Link>
|
||||
{MailPoet.I18n.t('pageTitle')} <Link className="page-title-action" to="/new" data-automation-id="new_email">{MailPoet.I18n.t('new')}</Link>
|
||||
</h1>
|
||||
|
||||
<ListingTabs tab="notification" />
|
||||
@ -119,7 +117,7 @@ const NewsletterListNotificationHistory = React.createClass({
|
||||
base_url="notification/history/:parent_id"
|
||||
onRenderItem={this.renderItem}
|
||||
columns={columns}
|
||||
item_actions={newsletter_actions}
|
||||
item_actions={newsletterActions}
|
||||
auto_refresh={true}
|
||||
sort_by="sent_at"
|
||||
sort_order="desc"
|
||||
|
@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { confirmAlert } from 'react-confirm-alert';
|
||||
import classNames from 'classnames';
|
||||
import MailPoet from 'mailpoet';
|
||||
import Hooks from 'wp-js-hooks';
|
||||
@ -13,11 +14,11 @@ import {
|
||||
MailerMixin,
|
||||
} from 'newsletters/listings/mixins.jsx';
|
||||
|
||||
const mailpoet_tracking_enabled = (!!(window['mailpoet_tracking_enabled']));
|
||||
const mailpoetTrackingEnabled = (!!(window.mailpoet_tracking_enabled));
|
||||
|
||||
const messages = {
|
||||
onTrash: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -32,7 +33,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onDelete: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -47,7 +48,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onRestore: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -80,7 +81,7 @@ const columns = [
|
||||
{
|
||||
name: 'statistics',
|
||||
label: MailPoet.I18n.t('statistics'),
|
||||
display: mailpoet_tracking_enabled,
|
||||
display: mailpoetTrackingEnabled,
|
||||
},
|
||||
{
|
||||
name: 'sent_at',
|
||||
@ -89,7 +90,7 @@ const columns = [
|
||||
},
|
||||
];
|
||||
|
||||
const bulk_actions = [
|
||||
const bulkActions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('moveToTrash'),
|
||||
@ -98,17 +99,27 @@ const bulk_actions = [
|
||||
];
|
||||
|
||||
const confirmEdit = (newsletter) => {
|
||||
const redirectToEditing = () => {
|
||||
window.location.href = `?page=mailpoet-newsletter-editor&id=${newsletter.id}`;
|
||||
};
|
||||
if (
|
||||
!newsletter.queue
|
||||
|| newsletter.status != 'sending'
|
||||
|| newsletter.status !== 'sending'
|
||||
|| newsletter.queue.status !== null
|
||||
|| window.confirm(MailPoet.I18n.t('confirmEdit'))
|
||||
) {
|
||||
window.location.href = `?page=mailpoet-newsletter-editor&id=${newsletter.id}`;
|
||||
redirectToEditing();
|
||||
} else {
|
||||
confirmAlert({
|
||||
title: MailPoet.I18n.t('confirmTitle'),
|
||||
message: MailPoet.I18n.t('confirmEdit'),
|
||||
confirmLabel: MailPoet.I18n.t('confirmLabel'),
|
||||
cancelLabel: MailPoet.I18n.t('cancelLabel'),
|
||||
onConfirm: redirectToEditing,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let newsletter_actions = [
|
||||
let newsletterActions = [
|
||||
{
|
||||
name: 'view',
|
||||
link: function (newsletter) {
|
||||
@ -145,7 +156,7 @@ let newsletter_actions = [
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
@ -158,7 +169,7 @@ let newsletter_actions = [
|
||||
];
|
||||
|
||||
Hooks.addFilter('mailpoet_newsletters_listings_standard_actions', StatisticsMixin.addStatsCTAAction);
|
||||
newsletter_actions = Hooks.applyFilters('mailpoet_newsletters_listings_standard_actions', newsletter_actions);
|
||||
newsletterActions = Hooks.applyFilters('mailpoet_newsletters_listings_standard_actions', newsletterActions);
|
||||
|
||||
const NewsletterListStandard = React.createClass({
|
||||
mixins: [QueueMixin, StatisticsMixin, MailerMixin],
|
||||
@ -169,9 +180,7 @@ const NewsletterListStandard = React.createClass({
|
||||
'has-row-actions'
|
||||
);
|
||||
|
||||
const segments = newsletter.segments.map((segment) => {
|
||||
return segment.name;
|
||||
}).join(', ');
|
||||
const segments = newsletter.segments.map(segment => segment.name).join(', ');
|
||||
|
||||
return (
|
||||
<div>
|
||||
@ -191,7 +200,7 @@ const NewsletterListStandard = React.createClass({
|
||||
<td className="column" data-colname={MailPoet.I18n.t('lists')}>
|
||||
{ segments }
|
||||
</td>
|
||||
{ (mailpoet_tracking_enabled === true) ? (
|
||||
{ (mailpoetTrackingEnabled === true) ? (
|
||||
<td className="column" data-colname={MailPoet.I18n.t('statistics')}>
|
||||
{ this.renderStatistics(newsletter, undefined, meta.current_time) }
|
||||
</td>
|
||||
@ -211,6 +220,7 @@ const NewsletterListStandard = React.createClass({
|
||||
onClick={() => MailPoet.trackEvent('Emails > Add New',
|
||||
{ 'MailPoet Free version': window.mailpoet_version }
|
||||
)}
|
||||
data-automation-id="new_email"
|
||||
>
|
||||
{MailPoet.I18n.t('new')}
|
||||
</Link>
|
||||
@ -227,8 +237,8 @@ const NewsletterListStandard = React.createClass({
|
||||
base_url="standard"
|
||||
onRenderItem={this.renderItem}
|
||||
columns={columns}
|
||||
bulk_actions={bulk_actions}
|
||||
item_actions={newsletter_actions}
|
||||
bulk_actions={bulkActions}
|
||||
item_actions={newsletterActions}
|
||||
messages={messages}
|
||||
auto_refresh={true}
|
||||
sort_by="sent_at"
|
||||
|
@ -35,7 +35,7 @@ const ListingTabs = React.createClass({
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={'tab-' + index}
|
||||
key={`tab-${index}`}
|
||||
className={tabClasses}
|
||||
to={tab.link}
|
||||
onClick={() => MailPoet.trackEvent(`Tab Emails > ${tab.name} clicked`,
|
||||
@ -46,7 +46,7 @@ const ListingTabs = React.createClass({
|
||||
});
|
||||
|
||||
return (
|
||||
<h2 className="nav-tab-wrapper">
|
||||
<h2 className="nav-tab-wrapper" data-automation-id="newsletters_listing_tabs">
|
||||
{ tabs }
|
||||
</h2>
|
||||
);
|
||||
|
@ -11,13 +11,13 @@ import MailPoet from 'mailpoet';
|
||||
import _ from 'underscore';
|
||||
import Hooks from 'wp-js-hooks';
|
||||
|
||||
const mailpoet_roles = window.mailpoet_roles || {};
|
||||
const mailpoet_segments = window.mailpoet_segments || {};
|
||||
const mailpoet_tracking_enabled = (!!(window['mailpoet_tracking_enabled']));
|
||||
const mailpoetRoles = window.mailpoet_roles || {};
|
||||
const mailpoetSegments = window.mailpoet_segments || {};
|
||||
const mailpoetTrackingEnabled = (!!(window.mailpoet_tracking_enabled));
|
||||
|
||||
const messages = {
|
||||
onTrash: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -32,7 +32,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onDelete: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -47,7 +47,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onRestore: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -81,7 +81,7 @@ const columns = [
|
||||
{
|
||||
name: 'statistics',
|
||||
label: MailPoet.I18n.t('statistics'),
|
||||
display: mailpoet_tracking_enabled,
|
||||
display: mailpoetTrackingEnabled,
|
||||
},
|
||||
{
|
||||
name: 'updated_at',
|
||||
@ -90,7 +90,7 @@ const columns = [
|
||||
},
|
||||
];
|
||||
|
||||
const bulk_actions = [
|
||||
const bulkActions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('moveToTrash'),
|
||||
@ -98,7 +98,7 @@ const bulk_actions = [
|
||||
},
|
||||
];
|
||||
|
||||
let newsletter_actions = [
|
||||
let newsletterActions = [
|
||||
{
|
||||
name: 'view',
|
||||
link: function (newsletter) {
|
||||
@ -125,7 +125,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);
|
||||
newsletterActions = Hooks.applyFilters('mailpoet_newsletters_listings_welcome_notification_actions', newsletterActions);
|
||||
|
||||
const NewsletterListWelcome = React.createClass({
|
||||
mixins: [StatisticsMixin, MailerMixin],
|
||||
@ -139,7 +139,7 @@ const NewsletterListWelcome = React.createClass({
|
||||
endpoint: 'newsletters',
|
||||
action: 'setStatus',
|
||||
data: {
|
||||
id: ~~(e.target.getAttribute('data-id')),
|
||||
id: Number(e.target.getAttribute('data-id')),
|
||||
status: e.target.value,
|
||||
},
|
||||
}).done((response) => {
|
||||
@ -156,7 +156,7 @@ const NewsletterListWelcome = React.createClass({
|
||||
});
|
||||
},
|
||||
renderStatus: function (newsletter) {
|
||||
const total_sent = (
|
||||
const totalSent = (
|
||||
MailPoet.I18n.t('sentToXSubscribers')
|
||||
.replace('%$1d', newsletter.total_sent.toLocaleString())
|
||||
);
|
||||
@ -173,13 +173,14 @@ const NewsletterListWelcome = React.createClass({
|
||||
<option value="draft">{ MailPoet.I18n.t('inactive') }</option>
|
||||
</select>
|
||||
</p>
|
||||
<p>{ total_sent }</p>
|
||||
<p>{ totalSent }</p>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
renderSettings: function (newsletter) {
|
||||
let sendingEvent;
|
||||
let sendingDelay;
|
||||
let segment;
|
||||
|
||||
// set sending event
|
||||
switch (newsletter.options.event) {
|
||||
@ -189,16 +190,17 @@ const NewsletterListWelcome = React.createClass({
|
||||
sendingEvent = MailPoet.I18n.t('welcomeEventWPUserAnyRole');
|
||||
} else {
|
||||
sendingEvent = MailPoet.I18n.t('welcomeEventWPUserWithRole').replace(
|
||||
'%$1s', mailpoet_roles[newsletter.options.role]
|
||||
'%$1s', mailpoetRoles[newsletter.options.role]
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'segment':
|
||||
default:
|
||||
// get segment
|
||||
const segment = _.find(mailpoet_segments, (segment) => {
|
||||
return (~~(segment.id) === ~~(newsletter.options.segment));
|
||||
});
|
||||
segment = _.find(
|
||||
mailpoetSegments,
|
||||
seg => (Number(seg.id) === Number(newsletter.options.segment))
|
||||
);
|
||||
|
||||
if (segment === undefined) {
|
||||
return (
|
||||
@ -206,11 +208,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;
|
||||
}
|
||||
|
||||
@ -235,8 +237,12 @@ const NewsletterListWelcome = React.createClass({
|
||||
'%$1d', newsletter.options.afterTimeNumber
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
sendingDelay = 'Invalid sending delay';
|
||||
break;
|
||||
}
|
||||
sendingEvent += ' [' + sendingDelay + ']';
|
||||
sendingEvent += ` [${sendingDelay}]`;
|
||||
}
|
||||
// add a "period" at the end if we do have a sendingEvent
|
||||
sendingEvent += '.';
|
||||
@ -272,7 +278,7 @@ const NewsletterListWelcome = React.createClass({
|
||||
<td className="column" data-colname={MailPoet.I18n.t('settings')}>
|
||||
{ this.renderSettings(newsletter) }
|
||||
</td>
|
||||
{ (mailpoet_tracking_enabled === true) ? (
|
||||
{ (mailpoetTrackingEnabled === true) ? (
|
||||
<td className="column" data-colname={MailPoet.I18n.t('statistics')}>
|
||||
{ this.renderStatistics(
|
||||
newsletter,
|
||||
@ -290,7 +296,7 @@ const NewsletterListWelcome = React.createClass({
|
||||
return (
|
||||
<div>
|
||||
<h1 className="title">
|
||||
{ MailPoet.I18n.t('pageTitle') } <Link className="page-title-action" to="/new">{ MailPoet.I18n.t('new') }</Link>
|
||||
{ MailPoet.I18n.t('pageTitle') } <Link className="page-title-action" to="/new" data-automation-id="new_email">{ MailPoet.I18n.t('new') }</Link>
|
||||
</h1>
|
||||
|
||||
<ListingTabs tab="welcome" />
|
||||
@ -304,8 +310,8 @@ const NewsletterListWelcome = React.createClass({
|
||||
base_url="welcome"
|
||||
onRenderItem={this.renderItem}
|
||||
columns={columns}
|
||||
bulk_actions={bulk_actions}
|
||||
item_actions={newsletter_actions}
|
||||
bulk_actions={bulkActions}
|
||||
item_actions={newsletterActions}
|
||||
messages={messages}
|
||||
auto_refresh={true}
|
||||
sort_by="updated_at"
|
||||
|
@ -27,17 +27,18 @@ const App = React.createClass({
|
||||
const container = document.getElementById('newsletters_container');
|
||||
|
||||
if (container) {
|
||||
let extra_routes = [];
|
||||
extra_routes = Hooks.applyFilters('mailpoet_newsletters_before_router', extra_routes);
|
||||
let extraRoutes = [];
|
||||
extraRoutes = Hooks.applyFilters('mailpoet_newsletters_before_router', extraRoutes);
|
||||
|
||||
const mailpoet_listing = ReactDOM.render((
|
||||
const mailpoetListing = ReactDOM.render((
|
||||
<Router history={history}>
|
||||
<Route path="/" component={App}>
|
||||
<IndexRedirect to="standard" />
|
||||
{/* 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} />
|
||||
@ -49,10 +50,10 @@ if (container) {
|
||||
{/* Sending options */}
|
||||
<Route path="send/:id" component={NewsletterSend} />
|
||||
{/* Extra routes */}
|
||||
{ extra_routes.map(rt => <Route key={rt.path} path={rt.path} component={rt.component} />) }
|
||||
{ extraRoutes.map(rt => <Route key={rt.path} path={rt.path} component={rt.component} />) }
|
||||
</Route>
|
||||
</Router>
|
||||
), container);
|
||||
|
||||
window.mailpoet_listing = mailpoet_listing;
|
||||
window.mailpoet_listing = mailpoetListing;
|
||||
}
|
||||
|
@ -4,14 +4,14 @@ import MailPoet from 'mailpoet';
|
||||
const timeFormat = window.mailpoet_time_format || 'H:i';
|
||||
|
||||
// welcome emails
|
||||
const _timeDelayValues = {
|
||||
const timeDelayValues = {
|
||||
immediate: MailPoet.I18n.t('delayImmediately'),
|
||||
hours: MailPoet.I18n.t('delayHoursAfter'),
|
||||
days: MailPoet.I18n.t('delayDaysAfter'),
|
||||
weeks: MailPoet.I18n.t('delayWeeksAfter'),
|
||||
};
|
||||
|
||||
const _intervalValues = {
|
||||
const intervalValues = {
|
||||
daily: MailPoet.I18n.t('daily'),
|
||||
weekly: MailPoet.I18n.t('weekly'),
|
||||
monthly: MailPoet.I18n.t('monthly'),
|
||||
@ -24,18 +24,16 @@ const SECONDS_IN_DAY = 86400;
|
||||
const TIME_STEP_SECONDS = 3600;
|
||||
const numberOfTimeSteps = SECONDS_IN_DAY / TIME_STEP_SECONDS;
|
||||
|
||||
const _timeOfDayValues = _.object(_.map(
|
||||
_.times(numberOfTimeSteps, (step) => {
|
||||
return step * TIME_STEP_SECONDS;
|
||||
}), (seconds) => {
|
||||
const date = new Date(null);
|
||||
date.setSeconds(seconds);
|
||||
const timeLabel = MailPoet.Date.format(date, { format: timeFormat, offset: 0 });
|
||||
return [seconds, timeLabel];
|
||||
})
|
||||
const timeOfDayValues = _.object(_.map(
|
||||
_.times(numberOfTimeSteps, step => step * TIME_STEP_SECONDS), (seconds) => {
|
||||
const date = new Date(null);
|
||||
date.setSeconds(seconds);
|
||||
const timeLabel = MailPoet.Date.format(date, { format: timeFormat, offset: 0 });
|
||||
return [seconds, timeLabel];
|
||||
})
|
||||
);
|
||||
|
||||
const _weekDayValues = {
|
||||
const weekDayValues = {
|
||||
0: MailPoet.I18n.t('sunday'),
|
||||
1: MailPoet.I18n.t('monday'),
|
||||
2: MailPoet.I18n.t('tuesday'),
|
||||
@ -46,37 +44,35 @@ const _weekDayValues = {
|
||||
};
|
||||
|
||||
const NUMBER_OF_DAYS_IN_MONTH = 28;
|
||||
const _monthDayValues = _.object(
|
||||
const monthDayValues = _.object(
|
||||
_.map(
|
||||
_.times(NUMBER_OF_DAYS_IN_MONTH, (day) => {
|
||||
return day;
|
||||
}), (day) => {
|
||||
const labels = {
|
||||
0: MailPoet.I18n.t('first'),
|
||||
1: MailPoet.I18n.t('second'),
|
||||
2: MailPoet.I18n.t('third'),
|
||||
};
|
||||
let label;
|
||||
if (labels[day] !== undefined) {
|
||||
label = labels[day];
|
||||
} else {
|
||||
label = MailPoet.I18n.t('nth').replace('%$1d', day + 1);
|
||||
_.times(NUMBER_OF_DAYS_IN_MONTH, day => day), (day) => {
|
||||
const labels = {
|
||||
0: MailPoet.I18n.t('first'),
|
||||
1: MailPoet.I18n.t('second'),
|
||||
2: MailPoet.I18n.t('third'),
|
||||
};
|
||||
let label;
|
||||
if (labels[day] !== undefined) {
|
||||
label = labels[day];
|
||||
} else {
|
||||
label = MailPoet.I18n.t('nth').replace('%$1d', day + 1);
|
||||
}
|
||||
return [day + 1, label];
|
||||
}
|
||||
return [day + 1, label];
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
const _nthWeekDayValues = {
|
||||
const nthWeekDayValues = {
|
||||
1: MailPoet.I18n.t('first'),
|
||||
2: MailPoet.I18n.t('second'),
|
||||
3: MailPoet.I18n.t('third'),
|
||||
L: MailPoet.I18n.t('last'),
|
||||
};
|
||||
|
||||
export { _timeDelayValues as timeDelayValues };
|
||||
export { _intervalValues as intervalValues };
|
||||
export { _timeOfDayValues as timeOfDayValues };
|
||||
export { _weekDayValues as weekDayValues };
|
||||
export { _monthDayValues as monthDayValues };
|
||||
export { _nthWeekDayValues as nthWeekDayValues };
|
||||
export { timeDelayValues };
|
||||
export { intervalValues };
|
||||
export { timeOfDayValues };
|
||||
export { weekDayValues };
|
||||
export { monthDayValues };
|
||||
export { nthWeekDayValues };
|
||||
|
@ -25,7 +25,6 @@ define(
|
||||
HelpTooltip,
|
||||
jQuery
|
||||
) => {
|
||||
|
||||
const NewsletterSend = React.createClass({
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired,
|
||||
@ -95,7 +94,7 @@ define(
|
||||
if (!this.isValid()) {
|
||||
jQuery('#mailpoet_newsletter').parsley().validate();
|
||||
} else {
|
||||
this._save(e).done(() => {
|
||||
this.saveNewsletter(e).done(() => {
|
||||
this.setState({ loading: true });
|
||||
}).done((response) => {
|
||||
switch (response.data.type) {
|
||||
@ -109,21 +108,21 @@ define(
|
||||
id: this.props.params.id,
|
||||
status: 'active',
|
||||
},
|
||||
}).done((response) => {
|
||||
}).done((response2) => {
|
||||
// redirect to listing based on newsletter type
|
||||
this.context.router.push(`/${this.state.item.type || ''}`);
|
||||
const opts = this.state.item.options;
|
||||
// display success message depending on newsletter type
|
||||
if (response.data.type === 'welcome') {
|
||||
if (response2.data.type === 'welcome') {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('welcomeEmailActivated')
|
||||
);
|
||||
MailPoet.trackEvent('Emails > Welcome email activated', {
|
||||
'MailPoet Free version': window.mailpoet_version,
|
||||
'List type': opts.event,
|
||||
Delay: opts.afterTimeNumber + ' ' + opts.afterTimeType,
|
||||
Delay: `${opts.afterTimeNumber} ${opts.afterTimeType}`,
|
||||
});
|
||||
} else if (response.data.type === 'notification') {
|
||||
} else if (response2.data.type === 'notification') {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('postNotificationActivated')
|
||||
);
|
||||
@ -132,7 +131,7 @@ define(
|
||||
Frequency: opts.intervalType,
|
||||
});
|
||||
}
|
||||
}).fail(this._showError);
|
||||
}).fail(this.showError);
|
||||
default:
|
||||
return MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
@ -141,11 +140,11 @@ define(
|
||||
data: {
|
||||
newsletter_id: this.props.params.id,
|
||||
},
|
||||
}).done((response) => {
|
||||
}).done((response2) => {
|
||||
// redirect to listing based on newsletter type
|
||||
this.context.router.push(`/${this.state.item.type || ''}`);
|
||||
|
||||
if (response.data.status === 'scheduled') {
|
||||
if (response2.data.status === 'scheduled') {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('newsletterHasBeenScheduled')
|
||||
);
|
||||
@ -162,10 +161,10 @@ define(
|
||||
'MailPoet Free version': window.mailpoet_version,
|
||||
});
|
||||
}
|
||||
}).fail(this._showError);
|
||||
}).fail(this.showError);
|
||||
}
|
||||
})
|
||||
.fail(this._showError)
|
||||
.fail(this.showError)
|
||||
.always(() => {
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
@ -177,7 +176,7 @@ define(
|
||||
if (!this.isValid()) {
|
||||
jQuery('#mailpoet_newsletter').parsley().validate();
|
||||
} else {
|
||||
this._save(e).done(() => {
|
||||
this.saveNewsletter(e).done(() => {
|
||||
this.setState({ loading: true });
|
||||
}).done(() => {
|
||||
MailPoet.Ajax.post({
|
||||
@ -195,13 +194,13 @@ define(
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
})
|
||||
.fail(this._showError)
|
||||
.fail(this.showError)
|
||||
.always(() => {
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
@ -211,27 +210,27 @@ define(
|
||||
handleSave: function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
this._save(e).done(() => {
|
||||
this.saveNewsletter(e).done(() => {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('newsletterUpdated')
|
||||
);
|
||||
}).done(() => {
|
||||
this.context.router.push(`/${this.state.item.type || ''}`);
|
||||
}).fail(this._showError);
|
||||
}).fail(this.showError);
|
||||
},
|
||||
handleRedirectToDesign: function (e) {
|
||||
e.preventDefault();
|
||||
const redirectTo = e.target.href;
|
||||
|
||||
this._save(e).done(() => {
|
||||
this.saveNewsletter(e).done(() => {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('newsletterUpdated')
|
||||
);
|
||||
}).done(() => {
|
||||
window.location = redirectTo;
|
||||
}).fail(this._showError);
|
||||
}).fail(this.showError);
|
||||
},
|
||||
_save: function () {
|
||||
saveNewsletter: function () {
|
||||
const data = this.state.item;
|
||||
data.queue = undefined;
|
||||
this.setState({ loading: true });
|
||||
@ -255,10 +254,10 @@ define(
|
||||
this.setState({ loading: false });
|
||||
});
|
||||
},
|
||||
_showError: (response) => {
|
||||
showError: (response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
@ -275,12 +274,12 @@ define(
|
||||
return true;
|
||||
},
|
||||
render: function () {
|
||||
const isPaused = this.state.item.status == 'sending'
|
||||
const isPaused = this.state.item.status === 'sending'
|
||||
&& this.state.item.queue
|
||||
&& this.state.item.queue.status == 'paused';
|
||||
&& this.state.item.queue.status === 'paused';
|
||||
const fields = this.state.fields.map((field) => {
|
||||
const newField = field;
|
||||
if (field.name == 'segments' || field.name == 'options') {
|
||||
if (field.name === 'segments' || field.name === 'options') {
|
||||
newField.disabled = isPaused;
|
||||
}
|
||||
return newField;
|
||||
@ -325,13 +324,13 @@ define(
|
||||
{MailPoet.I18n.t('orSimply')}
|
||||
<a
|
||||
href={
|
||||
'?page=mailpoet-newsletter-editor&id=' + this.props.params.id
|
||||
`?page=mailpoet-newsletter-editor&id=${this.props.params.id}`
|
||||
}
|
||||
onClick={this.handleRedirectToDesign}>
|
||||
{MailPoet.I18n.t('goBackToDesign')}
|
||||
</a>.
|
||||
</p>
|
||||
{ !isPaused && sendButtonOptions['disabled'] && sendButtonOptions['disabled'] === 'disabled' && (
|
||||
{ !isPaused && sendButtonOptions.disabled && sendButtonOptions.disabled === 'disabled' && (
|
||||
<HelpTooltip
|
||||
tooltip={MailPoet.I18n.t('helpTooltipSendEmail')}
|
||||
tooltipId="helpTooltipSendEmail"
|
||||
|
@ -11,7 +11,6 @@ define(
|
||||
Scheduling,
|
||||
_
|
||||
) => {
|
||||
|
||||
let fields = [
|
||||
{
|
||||
name: 'subject',
|
||||
@ -40,18 +39,14 @@ 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() + ')';
|
||||
return `${segment.name} (${parseInt(segment.subscribers, 10).toLocaleString()})`;
|
||||
},
|
||||
transformChangedValue: function (segment_ids) {
|
||||
const all_segments = this.state.items;
|
||||
return _.map(segment_ids, (id) => {
|
||||
return _.find(all_segments, (segment) => {
|
||||
return segment.id === id;
|
||||
});
|
||||
});
|
||||
transformChangedValue: function (segmentIds) {
|
||||
const allSegments = this.state.items;
|
||||
return _.map(segmentIds, id => _.find(allSegments, segment => segment.id === id));
|
||||
},
|
||||
validation: {
|
||||
'data-parsley-required': true,
|
||||
|
@ -13,11 +13,10 @@ define(
|
||||
MailPoet,
|
||||
Hooks
|
||||
) => {
|
||||
|
||||
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;
|
||||
@ -169,15 +168,13 @@ define(
|
||||
const TimeSelect = React.createClass({
|
||||
render: function () {
|
||||
const options = Object.keys(timeOfDayItems).map(
|
||||
(value, index) => {
|
||||
return (
|
||||
<option
|
||||
key={'option-' + index}
|
||||
value={value}>
|
||||
{ timeOfDayItems[value] }
|
||||
</option>
|
||||
);
|
||||
}
|
||||
(value, index) => (
|
||||
<option
|
||||
key={`option-${index}`}
|
||||
value={value}>
|
||||
{ timeOfDayItems[value] }
|
||||
</option>
|
||||
)
|
||||
);
|
||||
|
||||
return (
|
||||
@ -195,16 +192,16 @@ define(
|
||||
});
|
||||
|
||||
const DateTime = React.createClass({
|
||||
_DATE_TIME_SEPARATOR: ' ',
|
||||
DATE_TIME_SEPARATOR: ' ',
|
||||
getInitialState: function () {
|
||||
return this._buildStateFromProps(this.props);
|
||||
return this.buildStateFromProps(this.props);
|
||||
},
|
||||
componentWillReceiveProps: function (nextProps) {
|
||||
this.setState(this._buildStateFromProps(nextProps));
|
||||
this.setState(this.buildStateFromProps(nextProps));
|
||||
},
|
||||
_buildStateFromProps: function (props) {
|
||||
buildStateFromProps: function (props) {
|
||||
const value = props.value || defaultDateTime;
|
||||
const [date, time] = value.split(this._DATE_TIME_SEPARATOR);
|
||||
const [date, time] = value.split(this.DATE_TIME_SEPARATOR);
|
||||
return {
|
||||
date: date,
|
||||
time: time,
|
||||
@ -220,7 +217,7 @@ define(
|
||||
},
|
||||
propagateChange: function () {
|
||||
if (this.props.onChange) {
|
||||
return this.props.onChange({
|
||||
this.props.onChange({
|
||||
target: {
|
||||
name: this.props.name || '',
|
||||
value: this.getDateTime(),
|
||||
@ -229,7 +226,7 @@ define(
|
||||
}
|
||||
},
|
||||
getDateTime: function () {
|
||||
return [this.state.date, this.state.time].join(this._DATE_TIME_SEPARATOR);
|
||||
return [this.state.date, this.state.time].join(this.DATE_TIME_SEPARATOR);
|
||||
},
|
||||
render: function () {
|
||||
return (
|
||||
@ -254,7 +251,7 @@ define(
|
||||
});
|
||||
|
||||
const StandardScheduling = React.createClass({
|
||||
_getCurrentValue: function () {
|
||||
getCurrentValue: function () {
|
||||
return _.defaults(
|
||||
this.props.item[this.props.field.name] || {},
|
||||
{
|
||||
@ -264,7 +261,7 @@ define(
|
||||
);
|
||||
},
|
||||
handleValueChange: function (event) {
|
||||
const oldValue = this._getCurrentValue();
|
||||
const oldValue = this.getCurrentValue();
|
||||
const newValue = {};
|
||||
newValue[event.target.name] = event.target.value;
|
||||
|
||||
@ -281,7 +278,7 @@ define(
|
||||
return this.handleValueChange(changeEvent);
|
||||
},
|
||||
isScheduled: function () {
|
||||
return this._getCurrentValue().isScheduled === '1';
|
||||
return this.getCurrentValue().isScheduled === '1';
|
||||
},
|
||||
getDateValidation: function () {
|
||||
return {
|
||||
@ -298,7 +295,7 @@ define(
|
||||
<span id="mailpoet_scheduling">
|
||||
<DateTime
|
||||
name="scheduledAt"
|
||||
value={this._getCurrentValue().scheduledAt}
|
||||
value={this.getCurrentValue().scheduledAt}
|
||||
onChange={this.handleValueChange}
|
||||
disabled={this.props.field.disabled}
|
||||
dateValidation={this.getDateValidation()} />
|
||||
@ -348,18 +345,14 @@ 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() + ')';
|
||||
return `${segment.name} (${parseInt(segment.subscribers, 10).toLocaleString()})`;
|
||||
},
|
||||
transformChangedValue: function (segment_ids) {
|
||||
const all_segments = this.state.items;
|
||||
return _.map(segment_ids, (id) => {
|
||||
return _.find(all_segments, (segment) => {
|
||||
return segment.id === id;
|
||||
});
|
||||
});
|
||||
transformChangedValue: function (segmentIds) {
|
||||
const allSegments = this.state.items;
|
||||
return _.map(segmentIds, id => _.find(allSegments, segment => segment.id === id));
|
||||
},
|
||||
validation: {
|
||||
'data-parsley-required': true,
|
||||
@ -437,7 +430,7 @@ define(
|
||||
|
||||
if (newsletterOptions.status === 'sent'
|
||||
|| newsletterOptions.status === 'sending') {
|
||||
options['disabled'] = 'disabled';
|
||||
options.disabled = 'disabled';
|
||||
}
|
||||
|
||||
return options;
|
||||
|
@ -9,8 +9,6 @@ define(
|
||||
Hooks,
|
||||
Scheduling
|
||||
) => {
|
||||
|
||||
|
||||
let fields = [
|
||||
{
|
||||
name: 'subject',
|
||||
|
@ -1,295 +1,286 @@
|
||||
define(
|
||||
[
|
||||
'react',
|
||||
'underscore',
|
||||
'mailpoet',
|
||||
'react-router',
|
||||
'classnames',
|
||||
'newsletters/breadcrumb.jsx',
|
||||
'help-tooltip.jsx',
|
||||
],
|
||||
(
|
||||
React,
|
||||
_,
|
||||
MailPoet,
|
||||
Router,
|
||||
classNames,
|
||||
Breadcrumb,
|
||||
HelpTooltip
|
||||
) => {
|
||||
import React from 'react';
|
||||
import _ from 'underscore';
|
||||
import MailPoet from 'mailpoet';
|
||||
import { confirmAlert } from 'react-confirm-alert';
|
||||
import classNames from 'classnames';
|
||||
import Breadcrumb from 'newsletters/breadcrumb.jsx';
|
||||
import HelpTooltip from 'help-tooltip.jsx';
|
||||
|
||||
const ImportTemplate = React.createClass({
|
||||
saveTemplate: function (saveTemplate) {
|
||||
const template = saveTemplate;
|
||||
const ImportTemplate = React.createClass({
|
||||
saveTemplate: function (saveTemplate) {
|
||||
const template = saveTemplate;
|
||||
|
||||
// Stringify to enable transmission of primitive non-string value types
|
||||
if (!_.isUndefined(template.body)) {
|
||||
template.body = JSON.stringify(template.body);
|
||||
}
|
||||
// Stringify to enable transmission of primitive non-string value types
|
||||
if (!_.isUndefined(template.body)) {
|
||||
template.body = JSON.stringify(template.body);
|
||||
}
|
||||
|
||||
MailPoet.Modal.loading(true);
|
||||
MailPoet.Modal.loading(true);
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'save',
|
||||
data: template,
|
||||
}).always(() => {
|
||||
MailPoet.Modal.loading(false);
|
||||
}).done((response) => {
|
||||
this.props.onImport(response.data);
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
handleSubmit: function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (_.size(this.refs.templateFile.files) <= 0) return false;
|
||||
|
||||
|
||||
const file = _.first(this.refs.templateFile.files);
|
||||
const reader = new FileReader();
|
||||
const saveTemplate = this.saveTemplate;
|
||||
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
saveTemplate(JSON.parse(e.target.result));
|
||||
MailPoet.trackEvent('Emails > Template imported', {
|
||||
'MailPoet Free version': window.mailpoet_version,
|
||||
});
|
||||
} catch (err) {
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('templateFileMalformedError'));
|
||||
}
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
},
|
||||
render: function () {
|
||||
return (
|
||||
<div>
|
||||
<h2>{MailPoet.I18n.t('importTemplateTitle')} <HelpTooltip
|
||||
tooltip={MailPoet.I18n.t('helpTooltipTemplateUpload')}
|
||||
place="right"
|
||||
className="tooltip-help-import-template"
|
||||
/></h2>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input type="file" placeholder={MailPoet.I18n.t('selectJsonFileToUpload')} ref="templateFile" />
|
||||
<p className="submit">
|
||||
<input
|
||||
className="button button-primary"
|
||||
type="submit"
|
||||
value={MailPoet.I18n.t('upload')} />
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'save',
|
||||
data: template,
|
||||
}).always(() => {
|
||||
MailPoet.Modal.loading(false);
|
||||
}).done((response) => {
|
||||
this.props.onImport(response.data);
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
handleSubmit: function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const NewsletterTemplates = React.createClass({
|
||||
getInitialState: function () {
|
||||
return {
|
||||
loading: false,
|
||||
templates: [],
|
||||
};
|
||||
},
|
||||
componentDidMount: function () {
|
||||
this.getTemplates();
|
||||
},
|
||||
getTemplates: function () {
|
||||
this.setState({ loading: true });
|
||||
if (_.size(this.refs.templateFile.files) <= 0) return false;
|
||||
|
||||
MailPoet.Modal.loading(true);
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'getAll',
|
||||
}).always(() => {
|
||||
MailPoet.Modal.loading(false);
|
||||
}).done((response) => {
|
||||
if (this.isMounted()) {
|
||||
if (response.data.length === 0) {
|
||||
response.data = [
|
||||
{
|
||||
name:
|
||||
MailPoet.I18n.t('mailpoetGuideTemplateTitle'),
|
||||
description:
|
||||
MailPoet.I18n.t('mailpoetGuideTemplateDescription'),
|
||||
readonly: '1',
|
||||
},
|
||||
];
|
||||
}
|
||||
this.setState({
|
||||
templates: response.data,
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
handleSelectTemplate: function (template) {
|
||||
let body = template.body;
|
||||
const file = _.first(this.refs.templateFile.files);
|
||||
const reader = new FileReader();
|
||||
const saveTemplate = this.saveTemplate;
|
||||
|
||||
// Stringify to enable transmission of primitive non-string value types
|
||||
if (!_.isUndefined(body)) {
|
||||
body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
MailPoet.trackEvent('Emails > Template selected', {
|
||||
reader.onload = (evt) => {
|
||||
try {
|
||||
saveTemplate(JSON.parse(evt.target.result));
|
||||
MailPoet.trackEvent('Emails > Template imported', {
|
||||
'MailPoet Free version': window.mailpoet_version,
|
||||
'Email name': template.name,
|
||||
});
|
||||
} catch (err) {
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('templateFileMalformedError'));
|
||||
}
|
||||
};
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletters',
|
||||
action: 'save',
|
||||
data: {
|
||||
id: this.props.params.id,
|
||||
body: body,
|
||||
},
|
||||
}).done((response) => {
|
||||
// TODO: Move this URL elsewhere
|
||||
window.location = 'admin.php?page=mailpoet-newsletter-editor&id=' + response.data.id;
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
handleDeleteTemplate: function (template) {
|
||||
this.setState({ loading: true });
|
||||
if (
|
||||
window.confirm(
|
||||
(
|
||||
MailPoet.I18n.t('confirmTemplateDeletion')
|
||||
).replace('%$1s', template.name)
|
||||
)
|
||||
) {
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'delete',
|
||||
data: {
|
||||
id: template.id,
|
||||
reader.readAsText(file);
|
||||
return true;
|
||||
},
|
||||
render: function () {
|
||||
return (
|
||||
<div>
|
||||
<h2>{MailPoet.I18n.t('importTemplateTitle')} <HelpTooltip
|
||||
tooltip={MailPoet.I18n.t('helpTooltipTemplateUpload')}
|
||||
place="right"
|
||||
className="tooltip-help-import-template"
|
||||
/></h2>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<input type="file" placeholder={MailPoet.I18n.t('selectJsonFileToUpload')} ref="templateFile" />
|
||||
<p className="submit">
|
||||
<input
|
||||
className="button button-primary"
|
||||
type="submit"
|
||||
value={MailPoet.I18n.t('upload')} />
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const NewsletterTemplates = React.createClass({
|
||||
getInitialState: function () {
|
||||
return {
|
||||
loading: false,
|
||||
templates: [],
|
||||
};
|
||||
},
|
||||
componentDidMount: function () {
|
||||
this.getTemplates();
|
||||
},
|
||||
getTemplates: function () {
|
||||
this.setState({ loading: true });
|
||||
|
||||
MailPoet.Modal.loading(true);
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'getAll',
|
||||
}).always(() => {
|
||||
MailPoet.Modal.loading(false);
|
||||
}).done((response) => {
|
||||
if (this.isMounted()) {
|
||||
if (response.data.length === 0) {
|
||||
response.data = [
|
||||
{
|
||||
name:
|
||||
MailPoet.I18n.t('mailpoetGuideTemplateTitle'),
|
||||
description:
|
||||
MailPoet.I18n.t('mailpoetGuideTemplateDescription'),
|
||||
readonly: '1',
|
||||
},
|
||||
}).done(() => {
|
||||
this.getTemplates();
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.setState({ loading: false });
|
||||
];
|
||||
}
|
||||
},
|
||||
handleShowTemplate: function (template) {
|
||||
MailPoet.Modal.popup({
|
||||
title: template.name,
|
||||
template: '<div class="mailpoet_boxes_preview" style="background-color: {{ body.globalStyles.body.backgroundColor }}"><img src="{{ thumbnail }}" /></div>',
|
||||
data: template,
|
||||
this.setState({
|
||||
templates: response.data,
|
||||
loading: false,
|
||||
});
|
||||
},
|
||||
handleTemplateImport: function () {
|
||||
this.getTemplates();
|
||||
},
|
||||
render: function () {
|
||||
const templates = this.state.templates.map((template, index) => {
|
||||
const deleteLink = (
|
||||
<div className="mailpoet_delete">
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={this.handleDeleteTemplate.bind(null, template)}
|
||||
>
|
||||
{MailPoet.I18n.t('delete')}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
let thumbnail = '';
|
||||
|
||||
if (typeof template.thumbnail === 'string'
|
||||
&& template.thumbnail.length > 0) {
|
||||
thumbnail = (
|
||||
<a href="javascript:;" onClick={this.handleShowTemplate.bind(null, template)}>
|
||||
<img src={template.thumbnail} />
|
||||
<div className="mailpoet_overlay"></div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={'template-' + index}>
|
||||
<div className="mailpoet_thumbnail">
|
||||
{ thumbnail }
|
||||
</div>
|
||||
|
||||
<div className="mailpoet_description">
|
||||
<h3>{ template.name }</h3>
|
||||
<p>{ template.description }</p>
|
||||
</div>
|
||||
|
||||
<div className="mailpoet_actions">
|
||||
<a
|
||||
className="button button-secondary"
|
||||
onClick={this.handleShowTemplate.bind(null, template)}
|
||||
>
|
||||
{MailPoet.I18n.t('preview')}
|
||||
</a>
|
||||
|
||||
<a
|
||||
className="button button-primary"
|
||||
onClick={this.handleSelectTemplate.bind(null, template)}
|
||||
>
|
||||
{MailPoet.I18n.t('select')}
|
||||
</a>
|
||||
</div>
|
||||
{ (template.readonly === '1') ? false : deleteLink }
|
||||
</li>
|
||||
);
|
||||
});
|
||||
|
||||
const boxClasses = classNames(
|
||||
'mailpoet_boxes',
|
||||
'clearfix',
|
||||
{ mailpoet_boxes_loading: this.state.loading }
|
||||
}
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
handleSelectTemplate: function (template) {
|
||||
let body = template.body;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{MailPoet.I18n.t('selectTemplateTitle')}</h1>
|
||||
// Stringify to enable transmission of primitive non-string value types
|
||||
if (!_.isUndefined(body)) {
|
||||
body = JSON.stringify(body);
|
||||
}
|
||||
|
||||
<Breadcrumb step="template" />
|
||||
|
||||
<ul className={boxClasses}>
|
||||
{ templates }
|
||||
</ul>
|
||||
|
||||
<ImportTemplate onImport={this.handleTemplateImport} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
MailPoet.trackEvent('Emails > Template selected', {
|
||||
'MailPoet Free version': window.mailpoet_version,
|
||||
'Email name': template.name,
|
||||
});
|
||||
|
||||
return NewsletterTemplates;
|
||||
}
|
||||
);
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletters',
|
||||
action: 'save',
|
||||
data: {
|
||||
id: this.props.params.id,
|
||||
body: body,
|
||||
},
|
||||
}).done((response) => {
|
||||
// TODO: Move this URL elsewhere
|
||||
window.location = `admin.php?page=mailpoet-newsletter-editor&id=${response.data.id}`;
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
handleDeleteTemplate: function (template) {
|
||||
this.setState({ loading: true });
|
||||
const onConfirm = () => {
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'newsletterTemplates',
|
||||
action: 'delete',
|
||||
data: {
|
||||
id: template.id,
|
||||
},
|
||||
}).done(() => {
|
||||
this.getTemplates();
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
});
|
||||
};
|
||||
const onCancel = () => {
|
||||
this.setState({ loading: false });
|
||||
};
|
||||
confirmAlert({
|
||||
title: MailPoet.I18n.t('confirmTitle'),
|
||||
message: MailPoet.I18n.t('confirmTemplateDeletion').replace('%$1s', template.name),
|
||||
confirmLabel: MailPoet.I18n.t('confirmLabel'),
|
||||
cancelLabel: MailPoet.I18n.t('cancelLabel'),
|
||||
onConfirm: onConfirm,
|
||||
onCancel: onCancel,
|
||||
});
|
||||
},
|
||||
handleShowTemplate: function (template) {
|
||||
MailPoet.Modal.popup({
|
||||
title: template.name,
|
||||
template: '<div class="mailpoet_boxes_preview" style="background-color: {{ body.globalStyles.body.backgroundColor }}"><img src="{{ thumbnail }}" /></div>',
|
||||
data: template,
|
||||
});
|
||||
},
|
||||
handleTemplateImport: function () {
|
||||
this.getTemplates();
|
||||
},
|
||||
render: function () {
|
||||
const templates = this.state.templates.map((template, index) => {
|
||||
const deleteLink = (
|
||||
<div className="mailpoet_delete">
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={this.handleDeleteTemplate.bind(null, template)}
|
||||
>
|
||||
{MailPoet.I18n.t('delete')}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
let thumbnail = '';
|
||||
|
||||
if (typeof template.thumbnail === 'string'
|
||||
&& template.thumbnail.length > 0) {
|
||||
thumbnail = (
|
||||
<a href="javascript:;" onClick={this.handleShowTemplate.bind(null, template)}>
|
||||
<img src={template.thumbnail} />
|
||||
<div className="mailpoet_overlay"></div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={`template-${index}`}>
|
||||
<div className="mailpoet_thumbnail">
|
||||
{ thumbnail }
|
||||
</div>
|
||||
|
||||
<div className="mailpoet_description">
|
||||
<h3>{ template.name }</h3>
|
||||
<p>{ template.description }</p>
|
||||
</div>
|
||||
|
||||
<div className="mailpoet_actions">
|
||||
<a
|
||||
className="button button-secondary"
|
||||
onClick={this.handleShowTemplate.bind(null, template)}
|
||||
>
|
||||
{MailPoet.I18n.t('preview')}
|
||||
</a>
|
||||
|
||||
<a
|
||||
className="button button-primary"
|
||||
data-automation-id={`select_template_${index}`}
|
||||
onClick={this.handleSelectTemplate.bind(null, template)}
|
||||
>
|
||||
{MailPoet.I18n.t('select')}
|
||||
</a>
|
||||
</div>
|
||||
{ (template.readonly === '1') ? false : deleteLink }
|
||||
</li>
|
||||
);
|
||||
});
|
||||
|
||||
const boxClasses = classNames(
|
||||
'mailpoet_boxes',
|
||||
'clearfix',
|
||||
{ mailpoet_boxes_loading: this.state.loading }
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{MailPoet.I18n.t('selectTemplateTitle')}</h1>
|
||||
|
||||
<Breadcrumb step="template" />
|
||||
|
||||
<ul className={boxClasses}>
|
||||
{ templates }
|
||||
</ul>
|
||||
|
||||
<ImportTemplate onImport={this.handleTemplateImport} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export default NewsletterTemplates;
|
||||
|
@ -44,7 +44,7 @@ define(
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
@ -58,7 +58,7 @@ define(
|
||||
description: MailPoet.I18n.t('regularNewsletterTypeDescription'),
|
||||
action: function () {
|
||||
return (
|
||||
<a className="button button-primary" onClick={this.createNewsletter.bind(null, 'standard')}>
|
||||
<a className="button button-primary" data-automation-id="create_standard" onClick={this.createNewsletter.bind(null, 'standard')}>
|
||||
{MailPoet.I18n.t('create')}
|
||||
</a>
|
||||
);
|
||||
@ -68,7 +68,7 @@ define(
|
||||
id: 'welcome',
|
||||
title: MailPoet.I18n.t('welcomeNewsletterTypeTitle'),
|
||||
description: MailPoet.I18n.t('welcomeNewsletterTypeDescription'),
|
||||
action: function () {
|
||||
action: (function () {
|
||||
return (
|
||||
<div>
|
||||
<a href="?page=mailpoet-premium" target="_blank">
|
||||
@ -76,7 +76,7 @@ define(
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
}(),
|
||||
}()),
|
||||
},
|
||||
{
|
||||
id: 'notification',
|
||||
@ -84,7 +84,7 @@ define(
|
||||
description: MailPoet.I18n.t('postNotificationNewsletterTypeDescription'),
|
||||
action: function () {
|
||||
return (
|
||||
<a className="button button-primary" onClick={this.setupNewsletter.bind(null, 'notification')}>
|
||||
<a className="button button-primary" data-automation-id="create_notification" onClick={this.setupNewsletter.bind(null, 'notification')}>
|
||||
{MailPoet.I18n.t('setUp')}
|
||||
</a>
|
||||
);
|
||||
@ -101,24 +101,22 @@ define(
|
||||
<Breadcrumb step="type" />
|
||||
|
||||
<ul className="mailpoet_boxes clearfix">
|
||||
{types.map((type, index) => {
|
||||
return (
|
||||
<li key={index} data-type={type.id}>
|
||||
<div>
|
||||
<div className="mailpoet_thumbnail"></div>
|
||||
{types.map((type, index) => (
|
||||
<li key={index} data-type={type.id}>
|
||||
<div>
|
||||
<div className="mailpoet_thumbnail"></div>
|
||||
|
||||
<div className="mailpoet_description">
|
||||
<h3>{type.title}</h3>
|
||||
<p>{type.description}</p>
|
||||
</div>
|
||||
|
||||
<div className="mailpoet_actions">
|
||||
{type.action}
|
||||
</div>
|
||||
<div className="mailpoet_description">
|
||||
<h3>{type.title}</h3>
|
||||
<p>{type.description}</p>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}, this)}
|
||||
|
||||
<div className="mailpoet_actions">
|
||||
{type.action}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
), this)}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
@ -15,7 +15,6 @@ define(
|
||||
Scheduling,
|
||||
Breadcrumb
|
||||
) => {
|
||||
|
||||
const field = {
|
||||
name: 'options',
|
||||
type: 'reactComponent',
|
||||
@ -56,7 +55,7 @@ define(
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
|
@ -35,11 +35,11 @@ const nthWeekDayField = {
|
||||
};
|
||||
|
||||
const NotificationScheduling = React.createClass({
|
||||
_getCurrentValue: function () {
|
||||
getCurrentValue: function () {
|
||||
return (this.props.item[this.props.field.name] || {});
|
||||
},
|
||||
handleValueChange: function (name, value) {
|
||||
const oldValue = this._getCurrentValue();
|
||||
const oldValue = this.getCurrentValue();
|
||||
const newValue = {};
|
||||
|
||||
newValue[name] = value;
|
||||
@ -82,7 +82,7 @@ const NotificationScheduling = React.createClass({
|
||||
);
|
||||
},
|
||||
render: function () {
|
||||
const value = this._getCurrentValue();
|
||||
const value = this.getCurrentValue();
|
||||
let timeOfDaySelection;
|
||||
let weekDaySelection;
|
||||
let monthDaySelection;
|
||||
@ -92,7 +92,7 @@ const NotificationScheduling = React.createClass({
|
||||
timeOfDaySelection = (
|
||||
<Select
|
||||
field={timeOfDayField}
|
||||
item={this._getCurrentValue()}
|
||||
item={this.getCurrentValue()}
|
||||
onValueChange={this.handleTimeOfDayChange} />
|
||||
);
|
||||
}
|
||||
@ -101,7 +101,7 @@ const NotificationScheduling = React.createClass({
|
||||
weekDaySelection = (
|
||||
<Select
|
||||
field={weekDayField}
|
||||
item={this._getCurrentValue()}
|
||||
item={this.getCurrentValue()}
|
||||
onValueChange={this.handleWeekDayChange} />
|
||||
);
|
||||
}
|
||||
@ -110,7 +110,7 @@ const NotificationScheduling = React.createClass({
|
||||
monthDaySelection = (
|
||||
<Select
|
||||
field={monthDayField}
|
||||
item={this._getCurrentValue()}
|
||||
item={this.getCurrentValue()}
|
||||
onValueChange={this.handleMonthDayChange} />
|
||||
);
|
||||
}
|
||||
@ -119,7 +119,7 @@ const NotificationScheduling = React.createClass({
|
||||
nthWeekDaySelection = (
|
||||
<Select
|
||||
field={nthWeekDayField}
|
||||
item={this._getCurrentValue()}
|
||||
item={this.getCurrentValue()}
|
||||
onValueChange={this.handleNthWeekDayChange} />
|
||||
);
|
||||
}
|
||||
@ -128,7 +128,7 @@ const NotificationScheduling = React.createClass({
|
||||
<div>
|
||||
<Select
|
||||
field={intervalField}
|
||||
item={this._getCurrentValue()}
|
||||
item={this.getCurrentValue()}
|
||||
onValueChange={this.handleIntervalChange} />
|
||||
|
||||
{nthWeekDaySelection}
|
||||
|
@ -11,7 +11,6 @@ define(
|
||||
MailPoet,
|
||||
Breadcrumb
|
||||
) => {
|
||||
|
||||
const NewsletterStandard = React.createClass({
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired,
|
||||
@ -33,7 +32,7 @@ define(
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
|
@ -8,9 +8,7 @@ import { timeDelayValues } from 'newsletters/scheduling/common.jsx';
|
||||
const availableRoles = window.mailpoet_roles || {};
|
||||
const availableSegments = _.filter(
|
||||
window.mailpoet_segments || [],
|
||||
(segment) => {
|
||||
return segment.type === 'default';
|
||||
}
|
||||
segment => segment.type === 'default'
|
||||
);
|
||||
|
||||
const events = {
|
||||
@ -24,7 +22,7 @@ const events = {
|
||||
const availableSegmentValues = _.object(_.map(
|
||||
availableSegments,
|
||||
(segment) => {
|
||||
const name = segment.name + ' (' + parseInt(segment.subscribers, 10).toLocaleString() + ')';
|
||||
const name = `${segment.name} (${parseInt(segment.subscribers, 10).toLocaleString()})`;
|
||||
return [segment.id, name];
|
||||
}
|
||||
));
|
||||
@ -53,11 +51,11 @@ const WelcomeScheduling = React.createClass({
|
||||
contextTypes: {
|
||||
router: React.PropTypes.object.isRequired,
|
||||
},
|
||||
_getCurrentValue: function () {
|
||||
getCurrentValue: function () {
|
||||
return (this.props.item[this.props.field.name] || {});
|
||||
},
|
||||
handleValueChange: function (name, value) {
|
||||
const oldValue = this._getCurrentValue();
|
||||
const oldValue = this.getCurrentValue();
|
||||
const newValue = {};
|
||||
|
||||
newValue[name] = value;
|
||||
@ -113,7 +111,7 @@ const WelcomeScheduling = React.createClass({
|
||||
}).fail((response) => {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
@ -123,7 +121,7 @@ const WelcomeScheduling = React.createClass({
|
||||
this.context.router.push(`/template/${newsletterId}`);
|
||||
},
|
||||
render: function () {
|
||||
const value = this._getCurrentValue();
|
||||
const value = this.getCurrentValue();
|
||||
let roleSegmentSelection;
|
||||
let timeNumber;
|
||||
|
||||
@ -131,14 +129,14 @@ const WelcomeScheduling = React.createClass({
|
||||
roleSegmentSelection = (
|
||||
<Select
|
||||
field={roleField}
|
||||
item={this._getCurrentValue()}
|
||||
item={this.getCurrentValue()}
|
||||
onValueChange={this.handleRoleChange} />
|
||||
);
|
||||
} else {
|
||||
roleSegmentSelection = (
|
||||
<Select
|
||||
field={segmentField}
|
||||
item={this._getCurrentValue()}
|
||||
item={this.getCurrentValue()}
|
||||
onValueChange={this.handleSegmentChange} />
|
||||
);
|
||||
}
|
||||
@ -146,7 +144,7 @@ const WelcomeScheduling = React.createClass({
|
||||
timeNumber = (
|
||||
<Text
|
||||
field={afterTimeNumberField}
|
||||
item={this._getCurrentValue()}
|
||||
item={this.getCurrentValue()}
|
||||
onValueChange={this.handleAfterTimeNumberChange} />
|
||||
);
|
||||
}
|
||||
@ -155,7 +153,7 @@ const WelcomeScheduling = React.createClass({
|
||||
<div>
|
||||
<Select
|
||||
field={events}
|
||||
item={this._getCurrentValue()}
|
||||
item={this.getCurrentValue()}
|
||||
onValueChange={this.handleEventChange} />
|
||||
|
||||
{ roleSegmentSelection }
|
||||
@ -164,7 +162,7 @@ const WelcomeScheduling = React.createClass({
|
||||
|
||||
<Select
|
||||
field={afterTimeTypeField}
|
||||
item={this._getCurrentValue()}
|
||||
item={this.getCurrentValue()}
|
||||
onValueChange={this.handleAfterTimeTypeChange} />
|
||||
</div>
|
||||
);
|
||||
|
@ -115,9 +115,8 @@ define('notice', ['mailpoet', 'jquery'], function (mp, jQuery) {
|
||||
formatMessage: function (message) {
|
||||
if (Array.isArray(message)) {
|
||||
return message.join('<br />');
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
return message;
|
||||
},
|
||||
show: function (options) {
|
||||
// initialize
|
||||
|
@ -18,5 +18,4 @@ define('num',
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
@ -30,54 +30,54 @@ function (
|
||||
});
|
||||
|
||||
form.parsley().on('form:submit', function (parsley) {
|
||||
var form_data = form.mailpoetSerializeObject() || {};
|
||||
var formData = form.mailpoetSerializeObject() || {};
|
||||
// check if we're on the same domain
|
||||
if (isSameDomain(window.MailPoetForm.ajax_url) === false) {
|
||||
// non ajax post request
|
||||
return true;
|
||||
} else {
|
||||
}
|
||||
// ajax request
|
||||
MailPoet.Ajax.post({
|
||||
url: window.MailPoetForm.ajax_url,
|
||||
token: form_data.token,
|
||||
api_version: form_data.api_version,
|
||||
endpoint: 'subscribers',
|
||||
action: 'subscribe',
|
||||
data: form_data.data
|
||||
}).fail(function (response) {
|
||||
form.find('.mailpoet_validate_error').html(
|
||||
MailPoet.Ajax.post({
|
||||
url: window.MailPoetForm.ajax_url,
|
||||
token: formData.token,
|
||||
api_version: formData.api_version,
|
||||
endpoint: 'subscribers',
|
||||
action: 'subscribe',
|
||||
data: formData.data
|
||||
}).fail(function (response) {
|
||||
form.find('.mailpoet_validate_error').html(
|
||||
response.errors.map(function (error) {
|
||||
return error.message;
|
||||
}).join('<br />')
|
||||
).show();
|
||||
}).done(function (response) {
|
||||
}).done(function (response) {
|
||||
// successfully subscribed
|
||||
if (
|
||||
if (
|
||||
response.meta !== undefined
|
||||
&& response.meta.redirect_url !== undefined
|
||||
) {
|
||||
// go to page
|
||||
window.location.href = response.meta.redirect_url;
|
||||
} else {
|
||||
window.location.href = response.meta.redirect_url;
|
||||
} else {
|
||||
// display success message
|
||||
form.find('.mailpoet_validate_success').show();
|
||||
}
|
||||
form.find('.mailpoet_validate_success').show();
|
||||
}
|
||||
|
||||
// reset form
|
||||
form.trigger('reset');
|
||||
form.trigger('reset');
|
||||
// reset validation
|
||||
parsley.reset();
|
||||
parsley.reset();
|
||||
|
||||
// resize iframe
|
||||
if (
|
||||
if (
|
||||
window.frameElement !== null
|
||||
&& MailPoet !== undefined
|
||||
&& MailPoet['Iframe']
|
||||
) {
|
||||
MailPoet.Iframe.autoSize(window.frameElement);
|
||||
}
|
||||
});
|
||||
}
|
||||
MailPoet.Iframe.autoSize(window.frameElement);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
@ -11,7 +11,6 @@ define(
|
||||
MailPoet,
|
||||
Form
|
||||
) => {
|
||||
|
||||
const fields = [
|
||||
{
|
||||
name: 'name',
|
||||
|
@ -40,7 +40,7 @@ const columns = [
|
||||
|
||||
const messages = {
|
||||
onTrash: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -55,7 +55,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onDelete: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -70,7 +70,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onRestore: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -86,7 +86,7 @@ const messages = {
|
||||
},
|
||||
};
|
||||
|
||||
const bulk_actions = [
|
||||
const bulkActions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: MailPoet.I18n.t('moveToTrash'),
|
||||
@ -94,7 +94,7 @@ const bulk_actions = [
|
||||
},
|
||||
];
|
||||
|
||||
const item_actions = [
|
||||
const itemActions = [
|
||||
{
|
||||
name: 'edit',
|
||||
link: function (item) {
|
||||
@ -109,26 +109,24 @@ const item_actions = [
|
||||
{
|
||||
name: 'duplicate_segment',
|
||||
label: MailPoet.I18n.t('duplicate'),
|
||||
onClick: (item, refresh) => {
|
||||
return MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'segments',
|
||||
action: 'duplicate',
|
||||
data: {
|
||||
id: item.id,
|
||||
},
|
||||
}).done((response) => {
|
||||
MailPoet.Notice.success(
|
||||
onClick: (item, refresh) => MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'segments',
|
||||
action: 'duplicate',
|
||||
data: {
|
||||
id: item.id,
|
||||
},
|
||||
}).done((response) => {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('listDuplicated').replace('%$1s', response.data.name)
|
||||
);
|
||||
refresh();
|
||||
}).fail((response) => {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
refresh();
|
||||
}).fail((response) => {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
});
|
||||
},
|
||||
}),
|
||||
display: function (segment) {
|
||||
return (segment.type !== 'wp_users');
|
||||
},
|
||||
@ -166,7 +164,7 @@ const item_actions = [
|
||||
MailPoet.Modal.loading(false);
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map((error) => { return error.message; }),
|
||||
response.errors.map(error => error.message),
|
||||
{ scroll: true }
|
||||
);
|
||||
}
|
||||
@ -200,20 +198,20 @@ const SegmentList = React.createClass({
|
||||
'has-row-actions'
|
||||
);
|
||||
|
||||
const subscribed = ~~(segment.subscribers_count.subscribed || 0);
|
||||
const unconfirmed = ~~(segment.subscribers_count.unconfirmed || 0);
|
||||
const unsubscribed = ~~(segment.subscribers_count.unsubscribed || 0);
|
||||
const bounced = ~~(segment.subscribers_count.bounced || 0);
|
||||
const subscribed = Number(segment.subscribers_count.subscribed || 0);
|
||||
const unconfirmed = Number(segment.subscribers_count.unconfirmed || 0);
|
||||
const unsubscribed = Number(segment.subscribers_count.unsubscribed || 0);
|
||||
const bounced = Number(segment.subscribers_count.bounced || 0);
|
||||
|
||||
let segment_name;
|
||||
let segmentName;
|
||||
|
||||
if (segment.type === 'wp_users') {
|
||||
// the WP users segment is not editable so just display its name
|
||||
segment_name = (
|
||||
segmentName = (
|
||||
<span className="row-title">{ segment.name }</span>
|
||||
);
|
||||
} else {
|
||||
segment_name = (
|
||||
segmentName = (
|
||||
<Link
|
||||
className="row-title"
|
||||
to={`/edit/${segment.id}`}
|
||||
@ -225,7 +223,7 @@ const SegmentList = React.createClass({
|
||||
<div>
|
||||
<td className={rowClasses}>
|
||||
<strong>
|
||||
{ segment_name }
|
||||
{ segmentName }
|
||||
</strong>
|
||||
{ actions }
|
||||
</td>
|
||||
@ -266,8 +264,8 @@ const SegmentList = React.createClass({
|
||||
endpoint="segments"
|
||||
onRenderItem={this.renderItem}
|
||||
columns={columns}
|
||||
bulk_actions={bulk_actions}
|
||||
item_actions={item_actions}
|
||||
bulk_actions={bulkActions}
|
||||
item_actions={itemActions}
|
||||
sort_by="name"
|
||||
sort_order="asc"
|
||||
/>
|
||||
|
@ -19,7 +19,7 @@ define(
|
||||
label: MailPoet.I18n.t('email'),
|
||||
type: 'text',
|
||||
disabled: function (subscriber) {
|
||||
return ~~(subscriber.wp_user_id > 0);
|
||||
return Number(subscriber.wp_user_id > 0);
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -27,7 +27,7 @@ define(
|
||||
label: MailPoet.I18n.t('firstname'),
|
||||
type: 'text',
|
||||
disabled: function (subscriber) {
|
||||
return ~~(subscriber.wp_user_id > 0);
|
||||
return Number(subscriber.wp_user_id > 0);
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -35,7 +35,7 @@ define(
|
||||
label: MailPoet.I18n.t('lastname'),
|
||||
type: 'text',
|
||||
disabled: function (subscriber) {
|
||||
return ~~(subscriber.wp_user_id > 0);
|
||||
return Number(subscriber.wp_user_id > 0);
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -49,7 +49,7 @@ define(
|
||||
bounced: MailPoet.I18n.t('bounced'),
|
||||
},
|
||||
filter: function (subscriber, value) {
|
||||
if (~~(subscriber.wp_user_id) > 0 && value === 'unconfirmed') {
|
||||
if (Number(subscriber.wp_user_id) > 0 && value === 'unconfirmed') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@ -68,34 +68,32 @@ define(
|
||||
return null;
|
||||
}
|
||||
|
||||
return subscriber.subscriptions.map((subscription) => {
|
||||
if (subscription.status === 'subscribed') {
|
||||
return subscription.segment_id;
|
||||
}
|
||||
});
|
||||
return subscriber.subscriptions
|
||||
.filter(subscription => subscription.status === 'subscribed')
|
||||
.map(subscription => subscription.segment_id);
|
||||
},
|
||||
filter: function (segment) {
|
||||
return !!(!segment.deleted_at && segment.type === 'default');
|
||||
return (!segment.deleted_at && segment.type === 'default');
|
||||
},
|
||||
getLabel: function (segment) {
|
||||
return segment.name + ' (' + segment.subscribers + ')';
|
||||
return `${segment.name} (${segment.subscribers})`;
|
||||
},
|
||||
getSearchLabel: function (segment, subscriber) {
|
||||
let label = '';
|
||||
|
||||
if (subscriber.subscriptions !== undefined) {
|
||||
subscriber.subscriptions.map((subscription) => {
|
||||
subscriber.subscriptions.forEach((subscription) => {
|
||||
if (segment.id === subscription.segment_id) {
|
||||
label = segment.name;
|
||||
|
||||
if (subscription.status === 'unsubscribed') {
|
||||
const unsubscribed_at = MailPoet.Date
|
||||
const unsubscribedAt = MailPoet.Date
|
||||
.format(subscription.updated_at);
|
||||
label += ' (%$1s)'.replace(
|
||||
'%$1s',
|
||||
MailPoet.I18n.t('unsubscribedOn').replace(
|
||||
'%$1s',
|
||||
unsubscribed_at
|
||||
unsubscribedAt
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -107,23 +105,23 @@ define(
|
||||
},
|
||||
];
|
||||
|
||||
const custom_fields = window.mailpoet_custom_fields || [];
|
||||
custom_fields.map((custom_field) => {
|
||||
const customFields = window.mailpoet_custom_fields || [];
|
||||
customFields.forEach((customField) => {
|
||||
const field = {
|
||||
name: 'cf_' + custom_field.id,
|
||||
label: custom_field.name,
|
||||
type: custom_field.type,
|
||||
name: `cf_${customField.id}`,
|
||||
label: customField.name,
|
||||
type: customField.type,
|
||||
};
|
||||
if (custom_field.params) {
|
||||
field.params = custom_field.params;
|
||||
if (customField.params) {
|
||||
field.params = customField.params;
|
||||
}
|
||||
|
||||
if (custom_field.params.values) {
|
||||
field.values = custom_field.params.values;
|
||||
if (customField.params.values) {
|
||||
field.values = customField.params.values;
|
||||
}
|
||||
|
||||
// add placeholders for selects (date, select)
|
||||
switch (custom_field.type) {
|
||||
switch (customField.type) {
|
||||
case 'date':
|
||||
field.year_placeholder = MailPoet.I18n.t('year');
|
||||
field.month_placeholder = MailPoet.I18n.t('month');
|
||||
@ -133,6 +131,10 @@ define(
|
||||
case 'select':
|
||||
field.placeholder = '-';
|
||||
break;
|
||||
|
||||
default:
|
||||
field.placeholder = '';
|
||||
break;
|
||||
}
|
||||
|
||||
fields.push(field);
|
||||
@ -151,7 +153,7 @@ define(
|
||||
};
|
||||
|
||||
const beforeFormContent = function (subscriber) {
|
||||
if (~~(subscriber.wp_user_id) > 0) {
|
||||
if (Number(subscriber.wp_user_id) > 0) {
|
||||
return (
|
||||
<p className="description">
|
||||
{ ReactStringReplace(
|
||||
@ -168,6 +170,7 @@ define(
|
||||
</p>
|
||||
);
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const afterFormContent = function () {
|
||||
|
@ -21,15 +21,25 @@ define(
|
||||
var groupBySegmentOptionElement;
|
||||
var nextStepButton;
|
||||
var renderSegmentsAndFields;
|
||||
var subscribers_export_template;
|
||||
var subscribersExportTemplate;
|
||||
if (!window.exportData.segments) {
|
||||
return;
|
||||
}
|
||||
subscribers_export_template =
|
||||
subscribersExportTemplate =
|
||||
Handlebars.compile(jQuery('#mailpoet_subscribers_export_template').html());
|
||||
|
||||
// render template
|
||||
jQuery('#mailpoet_subscribers_export > div.inside').html(subscribers_export_template(window.exportData));
|
||||
jQuery('#mailpoet_subscribers_export > div.inside').html(subscribersExportTemplate(window.exportData));
|
||||
|
||||
function toggleNextStepButton(condition) {
|
||||
var disabled = 'button-disabled';
|
||||
if (condition === 'on') {
|
||||
nextStepButton.removeClass(disabled);
|
||||
}
|
||||
else {
|
||||
nextStepButton.addClass(disabled);
|
||||
}
|
||||
}
|
||||
|
||||
// define reusable variables
|
||||
segmentsContainerElement = jQuery('#export_lists');
|
||||
@ -128,16 +138,6 @@ define(
|
||||
segmentsContainerElement.val(selectedSegments).trigger('change');
|
||||
});
|
||||
|
||||
function toggleNextStepButton(condition) {
|
||||
var disabled = 'button-disabled';
|
||||
if (condition === 'on') {
|
||||
nextStepButton.removeClass(disabled);
|
||||
}
|
||||
else {
|
||||
nextStepButton.addClass(disabled);
|
||||
}
|
||||
}
|
||||
|
||||
nextStepButton.click(function () {
|
||||
var exportFormat;
|
||||
if (jQuery(this).hasClass('button-disabled')) {
|
||||
|
@ -99,8 +99,8 @@ define(
|
||||
|
||||
// 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'));
|
||||
var availableMethods = jQuery(':radio[name="select_method"]');
|
||||
var selectedMethod = availableMethods.index(availableMethods.filter(':checked'));
|
||||
MailPoet.Notice.hide();
|
||||
// hide all methods
|
||||
currentStepE.find('.inside')
|
||||
@ -108,7 +108,7 @@ define(
|
||||
.hide();
|
||||
// show selected method
|
||||
currentStepE.find('.inside')
|
||||
.children('div[id^="method_"]:eq(' + selected_method + ')')
|
||||
.children('div[id^="method_"]:eq(' + selectedMethod + ')')
|
||||
.show()
|
||||
.find('table')
|
||||
.show();
|
||||
@ -117,6 +117,196 @@ define(
|
||||
// start step 1
|
||||
showCurrentStep();
|
||||
|
||||
function toggleNextStepButton(element, condition) {
|
||||
var disabled = 'button-disabled';
|
||||
if (condition === 'on') {
|
||||
element.closest('table a').removeClass(disabled);
|
||||
return;
|
||||
}
|
||||
element.closest('table a').addClass(disabled);
|
||||
}
|
||||
|
||||
function parseCSV(isFile) {
|
||||
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,
|
||||
delimiter: advancedOptionDelimiter,
|
||||
newline: advancedOptionNewline,
|
||||
comments: advancedOptionComments,
|
||||
error: function () {
|
||||
MailPoet.Notice.hide();
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('dataProcessingError'));
|
||||
},
|
||||
complete: function (CSV) {
|
||||
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();
|
||||
});
|
||||
rowColumnCount = rowData.length;
|
||||
// set the number of row elements based on the first non-empty row
|
||||
if (columnCount === null) {
|
||||
columnCount = rowColumnCount;
|
||||
}
|
||||
// Process the row with the following assumptions:
|
||||
// 1. Each row should contain the same number of elements
|
||||
// 2. There should be at least 1 valid (as per HTML5 e-mail regex)
|
||||
// e-mail address on each row EXCEPT when the header option is set to true
|
||||
// 3. Duplicate addresses are skipped
|
||||
if (rowColumnCount === columnCount) {
|
||||
// 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 (column in rowData) {
|
||||
emailAddress = detectAndCleanupEmail(rowData[column]);
|
||||
if (emailColumnPosition === null
|
||||
&& window.emailRegex.test(emailAddress)) {
|
||||
emailColumnPosition = column;
|
||||
parsedEmails[emailAddress] = true; // add current e-mail to an object index
|
||||
rowData[column] = emailAddress;
|
||||
processedSubscribers[emailAddress] = rowData;
|
||||
}
|
||||
}
|
||||
if (emailColumnPosition === null
|
||||
&& advancedOptionHeader
|
||||
&& parseInt(rowCount) === 0) {
|
||||
isHeaderFound = true;
|
||||
processedSubscribers[0] = rowData;
|
||||
}
|
||||
}
|
||||
else if (rowData[emailColumnPosition] !== '') {
|
||||
email = detectAndCleanupEmail(rowData[emailColumnPosition]);
|
||||
if (_.has(parsedEmails, email)) {
|
||||
duplicateEmails.push(email);
|
||||
}
|
||||
else if (!window.emailRegex.test(email)) {
|
||||
invalidEmails.push(rowData[emailColumnPosition]);
|
||||
}
|
||||
// if we haven't yet processed this e-mail and it passed
|
||||
// the regex test, then process the row
|
||||
else {
|
||||
parsedEmails[email] = true;
|
||||
rowData[emailColumnPosition] = email;
|
||||
processedSubscribers[email] = rowData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// reindex array to avoid non-numeric indices
|
||||
processedSubscribers = _.values(processedSubscribers);
|
||||
// if the header options is set, there should be at least
|
||||
// 2 data rows, otherwise at least 1 data row
|
||||
if (processedSubscribers &&
|
||||
(isHeaderFound && processedSubscribers.length >= 2) ||
|
||||
(!isHeaderFound && processedSubscribers.length >= 1)
|
||||
) {
|
||||
// since we assume that the header line is always present, we need
|
||||
// to detect the header by checking if it contains a valid e-mail address
|
||||
window.importData.step1 = {
|
||||
header: (!window.emailRegex.test(
|
||||
processedSubscribers[0][emailColumnPosition])
|
||||
) ? processedSubscribers.shift() : null,
|
||||
subscribers: processedSubscribers,
|
||||
subscribersCount: processedSubscribers.length,
|
||||
duplicate: duplicateEmails,
|
||||
invalid: invalidEmails
|
||||
};
|
||||
MailPoet.trackEvent('Subscribers import started', {
|
||||
source: isFile ? 'file upload' : 'pasted data',
|
||||
'MailPoet Free version': window.mailpoet_version
|
||||
});
|
||||
router.navigate('step2', { trigger: true });
|
||||
}
|
||||
else {
|
||||
MailPoet.Modal.loading(false);
|
||||
errorNotice = MailPoet.I18n.t('noValidRecords');
|
||||
errorNotice = errorNotice.replace('[link]', MailPoet.I18n.t('csvKBLink'));
|
||||
errorNotice = errorNotice.replace('[/link]', '</a>');
|
||||
MailPoet.Notice.error(errorNotice);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function displayMailChimpLists(data) {
|
||||
var listSelectElement = mailChimpListsContainerElement.find('select');
|
||||
if (listSelectElement.data('select2')) {
|
||||
listSelectElement.select2('data', data);
|
||||
listSelectElement.trigger('change');
|
||||
}
|
||||
else {
|
||||
listSelectElement
|
||||
.select2({
|
||||
data: data,
|
||||
width: '20em',
|
||||
templateResult: function (item) {
|
||||
return item.name;
|
||||
},
|
||||
templateSelection: function (item) {
|
||||
return item.name;
|
||||
}
|
||||
})
|
||||
.change(function () {
|
||||
if (jQuery(this).val() !== null) {
|
||||
toggleNextStepButton(mailChimpProcessButtonElement, 'on');
|
||||
}
|
||||
else {
|
||||
toggleNextStepButton(mailChimpProcessButtonElement, 'off');
|
||||
}
|
||||
})
|
||||
.trigger('change');
|
||||
}
|
||||
mailChimpListsContainerElement.show();
|
||||
}
|
||||
|
||||
/*
|
||||
* Paste
|
||||
*/
|
||||
@ -261,196 +451,6 @@ define(
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function displayMailChimpLists(data) {
|
||||
var listSelectElement = mailChimpListsContainerElement.find('select');
|
||||
if (listSelectElement.data('select2')) {
|
||||
listSelectElement.select2('data', data);
|
||||
listSelectElement.trigger('change');
|
||||
}
|
||||
else {
|
||||
listSelectElement
|
||||
.select2({
|
||||
data: data,
|
||||
width: '20em',
|
||||
templateResult: function (item) {
|
||||
return item.name;
|
||||
},
|
||||
templateSelection: function (item) {
|
||||
return item.name;
|
||||
}
|
||||
})
|
||||
.change(function () {
|
||||
if (jQuery(this).val() !== null) {
|
||||
toggleNextStepButton(mailChimpProcessButtonElement, 'on');
|
||||
}
|
||||
else {
|
||||
toggleNextStepButton(mailChimpProcessButtonElement, 'off');
|
||||
}
|
||||
})
|
||||
.trigger('change');
|
||||
}
|
||||
mailChimpListsContainerElement.show();
|
||||
}
|
||||
|
||||
function toggleNextStepButton(element, condition) {
|
||||
var disabled = 'button-disabled';
|
||||
if (condition === 'on') {
|
||||
element.closest('table a').removeClass(disabled);
|
||||
return;
|
||||
}
|
||||
element.closest('table a').addClass(disabled);
|
||||
}
|
||||
|
||||
function parseCSV(isFile) {
|
||||
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,
|
||||
delimiter: advancedOptionDelimiter,
|
||||
newline: advancedOptionNewline,
|
||||
comments: advancedOptionComments,
|
||||
error: function () {
|
||||
MailPoet.Notice.hide();
|
||||
MailPoet.Notice.error(MailPoet.I18n.t('dataProcessingError'));
|
||||
},
|
||||
complete: function (CSV) {
|
||||
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();
|
||||
});
|
||||
rowColumnCount = rowData.length;
|
||||
// set the number of row elements based on the first non-empty row
|
||||
if (columnCount === null) {
|
||||
columnCount = rowColumnCount;
|
||||
}
|
||||
// Process the row with the following assumptions:
|
||||
// 1. Each row should contain the same number of elements
|
||||
// 2. There should be at least 1 valid (as per HTML5 e-mail regex)
|
||||
// e-mail address on each row EXCEPT when the header option is set to true
|
||||
// 3. Duplicate addresses are skipped
|
||||
if (rowColumnCount === columnCount) {
|
||||
// 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 (column in rowData) {
|
||||
emailAddress = detectAndCleanupEmail(rowData[column]);
|
||||
if (emailColumnPosition === null
|
||||
&& window.emailRegex.test(emailAddress)) {
|
||||
emailColumnPosition = column;
|
||||
parsedEmails[emailAddress] = true; // add current e-mail to an object index
|
||||
rowData[column] = emailAddress;
|
||||
processedSubscribers[emailAddress] = rowData;
|
||||
}
|
||||
}
|
||||
if (emailColumnPosition === null
|
||||
&& advancedOptionHeader
|
||||
&& parseInt(rowCount) === 0) {
|
||||
isHeaderFound = true;
|
||||
processedSubscribers[0] = rowData;
|
||||
}
|
||||
}
|
||||
else if (rowData[emailColumnPosition] !== '') {
|
||||
email = detectAndCleanupEmail(rowData[emailColumnPosition]);
|
||||
if (_.has(parsedEmails, email)) {
|
||||
duplicateEmails.push(email);
|
||||
}
|
||||
else if (!window.emailRegex.test(email)) {
|
||||
invalidEmails.push(rowData[emailColumnPosition]);
|
||||
}
|
||||
// if we haven't yet processed this e-mail and it passed
|
||||
// the regex test, then process the row
|
||||
else {
|
||||
parsedEmails[email] = true;
|
||||
rowData[emailColumnPosition] = email;
|
||||
processedSubscribers[email] = rowData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// reindex array to avoid non-numeric indices
|
||||
processedSubscribers = _.values(processedSubscribers);
|
||||
// if the header options is set, there should be at least
|
||||
// 2 data rows, otherwise at least 1 data row
|
||||
if (processedSubscribers &&
|
||||
(isHeaderFound && processedSubscribers.length >= 2) ||
|
||||
(!isHeaderFound && processedSubscribers.length >= 1)
|
||||
) {
|
||||
// since we assume that the header line is always present, we need
|
||||
// to detect the header by checking if it contains a valid e-mail address
|
||||
window.importData.step1 = {
|
||||
header: (!window.emailRegex.test(
|
||||
processedSubscribers[0][emailColumnPosition])
|
||||
) ? processedSubscribers.shift() : null,
|
||||
subscribers: processedSubscribers,
|
||||
subscribersCount: processedSubscribers.length,
|
||||
duplicate: duplicateEmails,
|
||||
invalid: invalidEmails
|
||||
};
|
||||
MailPoet.trackEvent('Subscribers import started', {
|
||||
source: isFile ? 'file upload' : 'pasted data',
|
||||
'MailPoet Free version': window.mailpoet_version
|
||||
});
|
||||
router.navigate('step2', { trigger: true });
|
||||
}
|
||||
else {
|
||||
MailPoet.Modal.loading(false);
|
||||
errorNotice = MailPoet.I18n.t('noValidRecords');
|
||||
errorNotice = errorNotice.replace('[link]', MailPoet.I18n.t('csvKBLink'));
|
||||
errorNotice = errorNotice.replace('[/link]', '</a>');
|
||||
MailPoet.Notice.error(errorNotice);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
router.on('route:step2', function () {
|
||||
@ -465,7 +465,7 @@ define(
|
||||
var filler;
|
||||
var fillerArray;
|
||||
var fillerPosition;
|
||||
var import_results;
|
||||
var importResults;
|
||||
var duplicates;
|
||||
var email;
|
||||
if (typeof (window.importData.step1) === 'undefined') {
|
||||
@ -492,6 +492,15 @@ define(
|
||||
|
||||
showCurrentStep();
|
||||
|
||||
function toggleNextStepButton(condition) {
|
||||
var disabled = 'button-disabled';
|
||||
if (condition === 'on') {
|
||||
nextStepButton.removeClass(disabled);
|
||||
return;
|
||||
}
|
||||
nextStepButton.addClass(disabled);
|
||||
}
|
||||
|
||||
// hide previous statistics/import results
|
||||
jQuery('#subscribers_data_parse_results:visible').html('');
|
||||
jQuery('#subscribers_data_import_results:visible').hide();
|
||||
@ -501,8 +510,8 @@ define(
|
||||
// count repeating e-mails inside duplicate array and present them in
|
||||
// 'email (xN)' format
|
||||
duplicates = {};
|
||||
subscribers.duplicate.forEach(function (email) {
|
||||
duplicates[email] = (duplicates[email] || 0) + 1;
|
||||
subscribers.duplicate.forEach(function (subscriberEmail) {
|
||||
duplicates[subscriberEmail] = (duplicates[subscriberEmail] || 0) + 1;
|
||||
});
|
||||
subscribers.duplicate = [];
|
||||
for (email in duplicates) {
|
||||
@ -514,7 +523,7 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
import_results = {
|
||||
importResults = {
|
||||
notice: MailPoet.I18n.t('importNoticeSkipped').replace(
|
||||
'%1$s',
|
||||
'<strong>' + (subscribers.invalid.length + subscribers.duplicate.length) + '</strong>'
|
||||
@ -531,7 +540,7 @@ define(
|
||||
: null
|
||||
};
|
||||
jQuery('#subscribers_data_parse_results').html(
|
||||
subscribersDataParseResultsTemplate(import_results)
|
||||
subscribersDataParseResultsTemplate(importResults)
|
||||
);
|
||||
}
|
||||
|
||||
@ -619,22 +628,22 @@ define(
|
||||
description: segmentDescription
|
||||
}
|
||||
}).done(function (response) {
|
||||
var selected_values;
|
||||
var selectedValues;
|
||||
window.mailpoetSegments.push({
|
||||
id: response.data.id,
|
||||
name: response.data.name,
|
||||
subscriberCount: 0
|
||||
});
|
||||
|
||||
selected_values = segmentSelectElement.val();
|
||||
if (selected_values === null) {
|
||||
selected_values = [response.data.id];
|
||||
selectedValues = segmentSelectElement.val();
|
||||
if (selectedValues === null) {
|
||||
selectedValues = [response.data.id];
|
||||
} else {
|
||||
selected_values.push(response.data.id);
|
||||
selectedValues.push(response.data.id);
|
||||
}
|
||||
|
||||
enableSegmentSelection(window.mailpoetSegments);
|
||||
segmentSelectElement.val(selected_values).trigger('change');
|
||||
segmentSelectElement.val(selectedValues).trigger('change');
|
||||
jQuery('.mailpoet_segments:hidden').show();
|
||||
jQuery('.mailpoet_no_segments:visible').hide();
|
||||
MailPoet.Modal.close();
|
||||
@ -662,7 +671,7 @@ define(
|
||||
// autodetect column types
|
||||
Handlebars.registerHelper(
|
||||
'show_and_match_columns',
|
||||
function (subscribers, options) {
|
||||
function (helperSubscribers, options) {
|
||||
var displayedColumns = [];
|
||||
var displayedColumnsIds = [];
|
||||
var i;
|
||||
@ -671,14 +680,14 @@ define(
|
||||
var headerName;
|
||||
var headerNameMatch;
|
||||
// go through all elements of the first row in subscribers data
|
||||
for (i in subscribers.subscribers[0]) {
|
||||
columnData = subscribers.subscribers[0][i];
|
||||
for (i in helperSubscribers.subscribers[0]) {
|
||||
columnData = helperSubscribers.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) {
|
||||
headerName = subscribers.header[i];
|
||||
} else if (helperSubscribers.header) {
|
||||
headerName = helperSubscribers.header[i];
|
||||
headerNameMatch = window.mailpoetColumns.map(function (el) {
|
||||
return el.name;
|
||||
}).indexOf(headerName);
|
||||
@ -729,9 +738,8 @@ define(
|
||||
// if we're on the last line, show the total count of subscribers data
|
||||
else if (index === (subscribers.subscribers.length - 1)) {
|
||||
return subscribers.subscribersCount.toLocaleString();
|
||||
} else {
|
||||
return index + 1;
|
||||
}
|
||||
return index + 1;
|
||||
});
|
||||
|
||||
// reduce subscribers object if the total length is greater than the
|
||||
@ -743,127 +751,14 @@ define(
|
||||
);
|
||||
}
|
||||
|
||||
// render template
|
||||
jQuery('#subscribers_data > table').html(subscribersDataTemplate(subscribers));
|
||||
|
||||
// filter displayed data
|
||||
jQuery('select.mailpoet_subscribers_column_data_match')
|
||||
.select2({
|
||||
data: window.mailpoetColumnsSelect2,
|
||||
width: '15em',
|
||||
templateResult: function (item) {
|
||||
return item.name;
|
||||
},
|
||||
templateSelection: function (item) {
|
||||
return item.name;
|
||||
}
|
||||
})
|
||||
.on('select2:selecting', function (selectEvent) {
|
||||
var selectElement = this;
|
||||
var selectedOptionId = selectEvent.params.args.data.id;
|
||||
// CREATE CUSTOM FIELD
|
||||
if (selectedOptionId === 'create') {
|
||||
selectEvent.preventDefault();
|
||||
jQuery(selectElement).select2('close');
|
||||
MailPoet.Modal.popup({
|
||||
title: MailPoet.I18n.t('addNewField'),
|
||||
template: jQuery('#form_template_field_form').html()
|
||||
});
|
||||
jQuery('#form_field_new').parsley().on('form:submit', function () {
|
||||
// get data
|
||||
var data = jQuery(this.$element).mailpoetSerializeObject();
|
||||
|
||||
// save custom field
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'customFields',
|
||||
action: 'save',
|
||||
data: data
|
||||
}).done(function (response) {
|
||||
var new_column_data = {
|
||||
id: response.data.id,
|
||||
name: response.data.name,
|
||||
type: response.data.type,
|
||||
params: response.data.params,
|
||||
custom: true
|
||||
};
|
||||
// if this is the first custom column, create an "optgroup"
|
||||
if (window.mailpoetColumnsSelect2.length === 2) {
|
||||
window.mailpoetColumnsSelect2.push({
|
||||
name: MailPoet.I18n.t('userColumns'),
|
||||
children: []
|
||||
});
|
||||
}
|
||||
window.mailpoetColumnsSelect2[2].children.push(new_column_data);
|
||||
window.mailpoetColumns.push(new_column_data);
|
||||
jQuery('select.mailpoet_subscribers_column_data_match')
|
||||
.each(function () {
|
||||
jQuery(this)
|
||||
.html('')
|
||||
.select2('destroy')
|
||||
.select2({
|
||||
data: window.mailpoetColumnsSelect2,
|
||||
width: '15em',
|
||||
templateResult: function (item) {
|
||||
return item.name;
|
||||
},
|
||||
templateSelection: function (item) {
|
||||
return item.name;
|
||||
}
|
||||
});
|
||||
});
|
||||
jQuery(selectElement).data('column-id', new_column_data.id);
|
||||
jQuery(selectElement).data('validation-rule', false);
|
||||
filterSubscribers();
|
||||
// close popup
|
||||
MailPoet.Modal.close();
|
||||
}).fail(function (response) {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(function (error) { return error.message; }),
|
||||
{ positionAfter: '#field_name' }
|
||||
);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
// CHANGE COLUMN
|
||||
else {
|
||||
// check for duplicate values in all select options
|
||||
jQuery('select.mailpoet_subscribers_column_data_match')
|
||||
.each(function () {
|
||||
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') {
|
||||
if (confirm(MailPoet.I18n.t('selectedValueAlreadyMatched') + ' ' + MailPoet.I18n.t('confirmCorrespondingColumn'))) {
|
||||
jQuery(element).data('column-id', 'ignore');
|
||||
}
|
||||
else {
|
||||
selectEvent.preventDefault();
|
||||
jQuery(selectElement).select2('close');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.on('select2:select', function (selectEvent) {
|
||||
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();
|
||||
'[data-id="notice_invalidEmail"], [data-id="notice_invalidDate"]')
|
||||
.remove();
|
||||
displayedColumns = jQuery.map(
|
||||
jQuery('.mailpoet_subscribers_column_data_match'), function (element, elementIndex) {
|
||||
var columnId = jQuery(element).data('column-id');
|
||||
@ -977,25 +872,129 @@ define(
|
||||
});
|
||||
// refresh table with susbcribers' data
|
||||
jQuery('#subscribers_data > table > tbody')
|
||||
.html(subscribersDataTemplatePartial(subscribersClone));
|
||||
.html(subscribersDataTemplatePartial(subscribersClone));
|
||||
|
||||
if (preventNextStep) {
|
||||
toggleNextStepButton('off');
|
||||
}
|
||||
else if (!jQuery('.mailpoet_notice.error:visible').length
|
||||
&& segmentSelectElement.val()) {
|
||||
&& segmentSelectElement.val()) {
|
||||
toggleNextStepButton('on');
|
||||
}
|
||||
}
|
||||
|
||||
function toggleNextStepButton(condition) {
|
||||
var disabled = 'button-disabled';
|
||||
if (condition === 'on') {
|
||||
nextStepButton.removeClass(disabled);
|
||||
return;
|
||||
}
|
||||
nextStepButton.addClass(disabled);
|
||||
}
|
||||
// render template
|
||||
jQuery('#subscribers_data > table').html(subscribersDataTemplate(subscribers));
|
||||
|
||||
// filter displayed data
|
||||
jQuery('select.mailpoet_subscribers_column_data_match')
|
||||
.select2({
|
||||
data: window.mailpoetColumnsSelect2,
|
||||
width: '15em',
|
||||
templateResult: function (item) {
|
||||
return item.name;
|
||||
},
|
||||
templateSelection: function (item) {
|
||||
return item.name;
|
||||
}
|
||||
})
|
||||
.on('select2:selecting', function (selectEvent) {
|
||||
var selectElement = this;
|
||||
var selectedOptionId = selectEvent.params.args.data.id;
|
||||
// CREATE CUSTOM FIELD
|
||||
if (selectedOptionId === 'create') {
|
||||
selectEvent.preventDefault();
|
||||
jQuery(selectElement).select2('close');
|
||||
MailPoet.Modal.popup({
|
||||
title: MailPoet.I18n.t('addNewField'),
|
||||
template: jQuery('#form_template_field_form').html()
|
||||
});
|
||||
jQuery('#form_field_new').parsley().on('form:submit', function () {
|
||||
// get data
|
||||
var data = jQuery(this.$element).mailpoetSerializeObject();
|
||||
|
||||
// save custom field
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'customFields',
|
||||
action: 'save',
|
||||
data: data
|
||||
}).done(function (response) {
|
||||
var newColumnData = {
|
||||
id: response.data.id,
|
||||
name: response.data.name,
|
||||
type: response.data.type,
|
||||
params: response.data.params,
|
||||
custom: true
|
||||
};
|
||||
// if this is the first custom column, create an "optgroup"
|
||||
if (window.mailpoetColumnsSelect2.length === 2) {
|
||||
window.mailpoetColumnsSelect2.push({
|
||||
name: MailPoet.I18n.t('userColumns'),
|
||||
children: []
|
||||
});
|
||||
}
|
||||
window.mailpoetColumnsSelect2[2].children.push(newColumnData);
|
||||
window.mailpoetColumns.push(newColumnData);
|
||||
jQuery('select.mailpoet_subscribers_column_data_match')
|
||||
.each(function () {
|
||||
jQuery(this)
|
||||
.html('')
|
||||
.select2('destroy')
|
||||
.select2({
|
||||
data: window.mailpoetColumnsSelect2,
|
||||
width: '15em',
|
||||
templateResult: function (item) {
|
||||
return item.name;
|
||||
},
|
||||
templateSelection: function (item) {
|
||||
return item.name;
|
||||
}
|
||||
});
|
||||
});
|
||||
jQuery(selectElement).data('column-id', newColumnData.id);
|
||||
jQuery(selectElement).data('validation-rule', false);
|
||||
filterSubscribers();
|
||||
// close popup
|
||||
MailPoet.Modal.close();
|
||||
}).fail(function (response) {
|
||||
if (response.errors.length > 0) {
|
||||
MailPoet.Notice.error(
|
||||
response.errors.map(function (error) { return error.message; }),
|
||||
{ positionAfter: '#field_name' }
|
||||
);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
// CHANGE COLUMN
|
||||
else {
|
||||
// check for duplicate values in all select options
|
||||
jQuery('select.mailpoet_subscribers_column_data_match')
|
||||
.each(function () {
|
||||
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') {
|
||||
if (confirm(MailPoet.I18n.t('selectedValueAlreadyMatched') + ' ' + MailPoet.I18n.t('confirmCorrespondingColumn'))) {
|
||||
jQuery(element).data('column-id', 'ignore');
|
||||
}
|
||||
else {
|
||||
selectEvent.preventDefault();
|
||||
jQuery(selectElement).select2('close');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.on('select2:select', function (selectEvent) {
|
||||
var selectElement = this;
|
||||
var selectedOptionId = selectEvent.params.data.id;
|
||||
jQuery(selectElement).data('column-id', selectedOptionId);
|
||||
filterSubscribers();
|
||||
});
|
||||
|
||||
previousStepButton.off().on('click', function () {
|
||||
router.navigate('step1', { trigger: true });
|
||||
@ -1007,22 +1006,21 @@ define(
|
||||
var batchNumber = 0;
|
||||
var batchSize = 2000;
|
||||
var timestamp = Date.now() / 1000;
|
||||
var subscribers = [];
|
||||
var importResults = {
|
||||
var clickImportResults = {
|
||||
created: 0,
|
||||
updated: 0,
|
||||
errors: [],
|
||||
segments: []
|
||||
};
|
||||
var subscribers;
|
||||
var clickSubscribers;
|
||||
var splitSubscribers;
|
||||
|
||||
if (jQuery(this).hasClass('button-disabled')) {
|
||||
return;
|
||||
}
|
||||
MailPoet.Modal.loading(true);
|
||||
splitSubscribers = function (subscribers, size) {
|
||||
return subscribers.reduce(function (res, item, index) {
|
||||
splitSubscribers = function (localSubscribers, size) {
|
||||
return localSubscribers.reduce(function (res, item, index) {
|
||||
if (index % size === 0) {
|
||||
res.push([]);
|
||||
}
|
||||
@ -1030,7 +1028,7 @@ define(
|
||||
return res;
|
||||
}, []);
|
||||
};
|
||||
subscribers = splitSubscribers(window.importData.step1.subscribers, batchSize);
|
||||
clickSubscribers = splitSubscribers(window.importData.step1.subscribers, batchSize);
|
||||
|
||||
_.each(jQuery('select.mailpoet_subscribers_column_data_match'),
|
||||
function (column, columnIndex) {
|
||||
@ -1042,26 +1040,26 @@ define(
|
||||
columns[columnId] = { index: columnIndex, validation_rule: validationRule };
|
||||
});
|
||||
|
||||
_.each(subscribers, function () {
|
||||
queue.add(function (queue) {
|
||||
queue.pause();
|
||||
_.each(clickSubscribers, function () {
|
||||
queue.add(function (addQueue) {
|
||||
addQueue.pause();
|
||||
MailPoet.Ajax.post({
|
||||
api_version: window.mailpoet_api_version,
|
||||
endpoint: 'ImportExport',
|
||||
action: 'processImport',
|
||||
data: JSON.stringify({
|
||||
columns: columns,
|
||||
subscribers: subscribers[batchNumber],
|
||||
subscribers: clickSubscribers[batchNumber],
|
||||
timestamp: timestamp,
|
||||
segments: segmentSelectElement.val(),
|
||||
updateSubscribers: (jQuery(':radio[name="subscriber_update_option"]:checked').val() === 'yes')
|
||||
})
|
||||
}).done(function (response) {
|
||||
importResults.created += response.data.created;
|
||||
importResults.updated += response.data.updated;
|
||||
importResults.segments = response.data.segments;
|
||||
importResults.added_to_segment_with_welcome_notification = response.data.added_to_segment_with_welcome_notification;
|
||||
queue.run();
|
||||
clickImportResults.created += response.data.created;
|
||||
clickImportResults.updated += response.data.updated;
|
||||
clickImportResults.segments = response.data.segments;
|
||||
clickImportResults.added_to_segment_with_welcome_notification = response.data.added_to_segment_with_welcome_notification;
|
||||
addQueue.run();
|
||||
}).fail(function (response) {
|
||||
MailPoet.Modal.loading(false);
|
||||
if (response.errors.length > 0) {
|
||||
@ -1079,17 +1077,17 @@ define(
|
||||
|
||||
queue.onComplete(function () {
|
||||
MailPoet.Modal.loading(false);
|
||||
if (importResults.errors.length > 0 && !importResults.updated && !importResults.created) {
|
||||
MailPoet.Notice.error(_.flatten(importResults.errors)
|
||||
if (clickImportResults.errors.length > 0 && !clickImportResults.updated && !clickImportResults.created) {
|
||||
MailPoet.Notice.error(_.flatten(clickImportResults.errors)
|
||||
);
|
||||
}
|
||||
else {
|
||||
window.mailpoetSegments = importResults.segments;
|
||||
importResults.segments = _.map(segmentSelectElement.select2('data'),
|
||||
window.mailpoetSegments = clickImportResults.segments;
|
||||
clickImportResults.segments = _.map(segmentSelectElement.select2('data'),
|
||||
function (data) {
|
||||
return data.name;
|
||||
});
|
||||
window.importData.step2 = importResults;
|
||||
window.importData.step2 = clickImportResults;
|
||||
enableSegmentSelection(window.mailpoetSegments);
|
||||
router.navigate('step3', { trigger: true });
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ const columns = [
|
||||
|
||||
const messages = {
|
||||
onTrash: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -53,7 +53,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onDelete: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -68,7 +68,7 @@ const messages = {
|
||||
MailPoet.Notice.success(message);
|
||||
},
|
||||
onRestore: (response) => {
|
||||
const count = ~~response.meta.count;
|
||||
const count = Number(response.meta.count);
|
||||
let message = null;
|
||||
|
||||
if (count === 1) {
|
||||
@ -100,7 +100,7 @@ const messages = {
|
||||
},
|
||||
};
|
||||
|
||||
const bulk_actions = [
|
||||
const bulkActions = [
|
||||
{
|
||||
name: 'moveToList',
|
||||
label: MailPoet.I18n.t('moveToList'),
|
||||
@ -122,13 +122,13 @@ const bulk_actions = [
|
||||
},
|
||||
getData: function () {
|
||||
return {
|
||||
segment_id: ~~(jQuery('#move_to_segment').val()),
|
||||
segment_id: Number(jQuery('#move_to_segment').val()),
|
||||
};
|
||||
},
|
||||
onSuccess: function (response) {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('multipleSubscribersMovedToList')
|
||||
.replace('%$1d', (~~(response.meta.count)).toLocaleString())
|
||||
.replace('%$1d', (Number(response.meta.count)).toLocaleString())
|
||||
.replace('%$2s', response.meta.segment)
|
||||
);
|
||||
},
|
||||
@ -154,13 +154,13 @@ const bulk_actions = [
|
||||
},
|
||||
getData: function () {
|
||||
return {
|
||||
segment_id: ~~(jQuery('#add_to_segment').val()),
|
||||
segment_id: Number(jQuery('#add_to_segment').val()),
|
||||
};
|
||||
},
|
||||
onSuccess: function (response) {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('multipleSubscribersAddedToList')
|
||||
.replace('%$1d', (~~response.meta.count).toLocaleString())
|
||||
.replace('%$1d', (Number(response.meta.count)).toLocaleString())
|
||||
.replace('%$2s', response.meta.segment)
|
||||
);
|
||||
},
|
||||
@ -186,13 +186,13 @@ const bulk_actions = [
|
||||
},
|
||||
getData: function () {
|
||||
return {
|
||||
segment_id: ~~(jQuery('#remove_from_segment').val()),
|
||||
segment_id: Number(jQuery('#remove_from_segment').val()),
|
||||
};
|
||||
},
|
||||
onSuccess: function (response) {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('multipleSubscribersRemovedFromList')
|
||||
.replace('%$1d', (~~response.meta.count).toLocaleString())
|
||||
.replace('%$1d', (Number(response.meta.count)).toLocaleString())
|
||||
.replace('%$2s', response.meta.segment)
|
||||
);
|
||||
},
|
||||
@ -203,7 +203,7 @@ const bulk_actions = [
|
||||
onSuccess: function (response) {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('multipleSubscribersRemovedFromAllLists')
|
||||
.replace('%$1d', (~~response.meta.count).toLocaleString())
|
||||
.replace('%$1d', (Number(response.meta.count)).toLocaleString())
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -213,7 +213,7 @@ const bulk_actions = [
|
||||
onSuccess: function (response) {
|
||||
MailPoet.Notice.success(
|
||||
MailPoet.I18n.t('multipleConfirmationEmailsSent')
|
||||
.replace('%$1d', (~~response.meta.count).toLocaleString())
|
||||
.replace('%$1d', (Number(response.meta.count)).toLocaleString())
|
||||
);
|
||||
},
|
||||
},
|
||||
@ -224,7 +224,7 @@ const bulk_actions = [
|
||||
},
|
||||
];
|
||||
|
||||
const item_actions = [
|
||||
const itemActions = [
|
||||
{
|
||||
name: 'edit',
|
||||
label: MailPoet.I18n.t('edit'),
|
||||
@ -237,23 +237,23 @@ const item_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
display: function (subscriber) {
|
||||
return !!(~~subscriber.wp_user_id === 0);
|
||||
return Number(subscriber.wp_user_id) === 0;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const SubscriberList = React.createClass({
|
||||
getSegmentFromId: function (segment_id) {
|
||||
getSegmentFromId: function (segmentId) {
|
||||
let result = false;
|
||||
window.mailpoet_segments.map((segment) => {
|
||||
if (segment.id === segment_id) {
|
||||
window.mailpoet_segments.forEach((segment) => {
|
||||
if (segment.id === segmentId) {
|
||||
result = segment;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
renderItem: function (subscriber, actions) {
|
||||
const row_classes = classNames(
|
||||
const rowClasses = classNames(
|
||||
'manage-column',
|
||||
'column-primary',
|
||||
'has-row-actions',
|
||||
@ -278,32 +278,36 @@ const SubscriberList = React.createClass({
|
||||
case 'bounced':
|
||||
status = MailPoet.I18n.t('bounced');
|
||||
break;
|
||||
|
||||
default:
|
||||
status = 'Invalid';
|
||||
break;
|
||||
}
|
||||
|
||||
let segments = false;
|
||||
|
||||
// Subscriptions
|
||||
if (subscriber.subscriptions.length > 0) {
|
||||
const subscribed_segments = [];
|
||||
const subscribedSegments = [];
|
||||
|
||||
subscriber.subscriptions.map((subscription) => {
|
||||
subscriber.subscriptions.forEach((subscription) => {
|
||||
const segment = this.getSegmentFromId(subscription.segment_id);
|
||||
if (segment === false) return;
|
||||
if (subscription.status === 'subscribed') {
|
||||
subscribed_segments.push(segment.name);
|
||||
subscribedSegments.push(segment.name);
|
||||
}
|
||||
});
|
||||
|
||||
segments = (
|
||||
<span>
|
||||
{ subscribed_segments.join(', ') }
|
||||
{ subscribedSegments.join(', ') }
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<td className={row_classes}>
|
||||
<td className={rowClasses}>
|
||||
<strong>
|
||||
<Link
|
||||
className="row-title"
|
||||
@ -356,8 +360,8 @@ const SubscriberList = React.createClass({
|
||||
endpoint="subscribers"
|
||||
onRenderItem={this.renderItem}
|
||||
columns={columns}
|
||||
bulk_actions={bulk_actions}
|
||||
item_actions={item_actions}
|
||||
bulk_actions={bulkActions}
|
||||
item_actions={itemActions}
|
||||
messages={messages}
|
||||
sort_by={'created_at'}
|
||||
sort_order={'desc'}
|
||||
|
@ -16,10 +16,12 @@
|
||||
"nesbot/carbon": "^1.21",
|
||||
"soundasleep/html2text": "^0.3.4",
|
||||
"sabberworm/php-css-parser": "^8.1",
|
||||
"symfony/polyfill-xml": "^1.3"
|
||||
"symfony/polyfill-xml": "^1.3",
|
||||
"symfony/polyfill-mbstring": "1.6.0",
|
||||
"sensiolabs/security-checker": "^4.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/aspect-mock": "^2.0",
|
||||
"codeception/aspect-mock": "2.0.1",
|
||||
"codeception/codeception": "2.3.5",
|
||||
"codeception/verify": "^0.3.3",
|
||||
"consolidation/robo": "^1.0.5",
|
||||
|
1351
composer.lock
generated
1351
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,7 @@ services:
|
||||
codeception:
|
||||
build: .
|
||||
depends_on:
|
||||
- mailhog
|
||||
- wordpress
|
||||
volumes:
|
||||
- ./:/project
|
||||
@ -11,6 +12,12 @@ services:
|
||||
- ./:/wp-core/wp-content/plugins/mailpoet
|
||||
entrypoint: /docker-entrypoint.sh
|
||||
|
||||
mailhog:
|
||||
image: mailhog/mailhog
|
||||
ports:
|
||||
- 1025:1025
|
||||
- 8025:8025
|
||||
|
||||
wordpress:
|
||||
build: ./tests/wordpressDockerfile
|
||||
image: wordpress:latest
|
||||
@ -25,8 +32,6 @@ services:
|
||||
- 8080:80
|
||||
environment:
|
||||
WORDPRESS_DB_PASSWORD: wordpress
|
||||
WORDPRESS_TABLE_PREFIX: mp_
|
||||
|
||||
mysql:
|
||||
image: mysql:5.6
|
||||
environment:
|
||||
@ -42,7 +47,7 @@ services:
|
||||
- /dev/shm:/dev/shm
|
||||
image: selenium/standalone-chrome-debug
|
||||
ports:
|
||||
- '4444'
|
||||
- '5900:5900'
|
||||
- 4444
|
||||
- 5900:5900
|
||||
volumes:
|
||||
wp-core:
|
||||
wp-core:
|
@ -28,9 +28,11 @@ if ! $(wp-su core is-installed); then
|
||||
echo "Configuring WordPress"
|
||||
# The development version of Gravity Flow requires SCRIPT_DEBUG
|
||||
wp-su core config --dbhost=mysql --dbname=wordpress --dbuser=wordpress --dbpass=wordpress --extra-php="define( 'SCRIPT_DEBUG', true );" --force
|
||||
|
||||
fi
|
||||
|
||||
# Change default table prefix
|
||||
sed -i "s/\$table_prefix = 'wp_';/\$table_prefix = 'mp_';/" ./wp-config.php
|
||||
|
||||
# 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 ...
|
||||
|
@ -10,7 +10,9 @@ use MailPoet\Form\Util\FieldNameObfuscator;
|
||||
use MailPoet\Models\Form;
|
||||
use MailPoet\Models\StatisticsForms;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Segments\SubscribersListings;
|
||||
use MailPoet\Subscription\Throttling as SubscriptionThrottling;
|
||||
use MailPoet\WP\Hooks;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -40,12 +42,15 @@ class Subscribers extends APIEndpoint {
|
||||
}
|
||||
|
||||
function listing($data = array()) {
|
||||
$listing = new Listing\Handler(
|
||||
'\MailPoet\Models\Subscriber',
|
||||
$data
|
||||
);
|
||||
|
||||
$listing_data = $listing->get();
|
||||
if(!isset($data['filter']['segment'])) {
|
||||
$listing = new Listing\Handler('\MailPoet\Models\Subscriber', $data);
|
||||
|
||||
$listing_data = $listing->get();
|
||||
} else {
|
||||
$listings = new SubscribersListings();
|
||||
$listing_data = $listings->getListingsInSegment($data);
|
||||
}
|
||||
|
||||
$data = array();
|
||||
foreach($listing_data['items'] as $subscriber) {
|
||||
@ -54,6 +59,10 @@ class Subscribers extends APIEndpoint {
|
||||
->asArray();
|
||||
}
|
||||
|
||||
$listing_data['filters']['segment'] = Hooks::applyFilters(
|
||||
'mailpoet_subscribers_listings_filters_segments',
|
||||
$listing_data['filters']['segment']
|
||||
);
|
||||
return $this->successResponse($data, array(
|
||||
'count' => $listing_data['count'],
|
||||
'filters' => $listing_data['filters'],
|
||||
|
@ -42,8 +42,12 @@ class API {
|
||||
}
|
||||
|
||||
function subscribeToLists($subscriber_id, array $segments_ids) {
|
||||
$subscriber = Subscriber::findOne($subscriber_id);
|
||||
if(empty($segments_ids)) {
|
||||
throw new \Exception(__('At least one segment ID is required.', 'mailpoet'));
|
||||
}
|
||||
|
||||
// throw exception when subscriber does not exist
|
||||
$subscriber = Subscriber::findOne($subscriber_id);
|
||||
if(!$subscriber) {
|
||||
throw new \Exception(__('This subscriber does not exist.', 'mailpoet'));
|
||||
}
|
||||
@ -51,7 +55,8 @@ class API {
|
||||
// throw exception when none of the segments exist
|
||||
$found_segments = Segment::whereIn('id', $segments_ids)->findMany();
|
||||
if(!$found_segments) {
|
||||
throw new \Exception(__('These lists do not exist.', 'mailpoet'));
|
||||
$exception = _n('This list does not exist.', 'These lists do not exist.', count($segments_ids), 'mailpoet');
|
||||
throw new \Exception($exception);
|
||||
}
|
||||
|
||||
// throw exception when trying to subscribe to a WP Users segment
|
||||
@ -66,13 +71,62 @@ class API {
|
||||
// throw an exception when one or more segments do not exist
|
||||
if(count($found_segments_ids) !== count($segments_ids)) {
|
||||
$missing_ids = array_values(array_diff($segments_ids, $found_segments_ids));
|
||||
throw new \Exception(__(sprintf('Lists with ID %s do not exist.', implode(', ', $missing_ids)), 'mailpoet'));
|
||||
$exception = sprintf(
|
||||
_n('List with ID %s does not exist.', 'Lists with IDs %s do not exist.', count($missing_ids), 'mailpoet'),
|
||||
implode(', ', $missing_ids)
|
||||
);
|
||||
throw new \Exception(sprintf($exception, implode(', ', $missing_ids)));
|
||||
}
|
||||
|
||||
SubscriberSegment::subscribeToSegments($subscriber, $found_segments_ids);
|
||||
return $subscriber->withCustomFields()->withSubscriptions()->asArray();
|
||||
}
|
||||
|
||||
function unsubscribeFromList($subscriber_id, $segment_id) {
|
||||
return $this->unsubscribeFromLists($subscriber_id, array($segment_id));
|
||||
}
|
||||
|
||||
function unsubscribeFromLists($subscriber_id, array $segments_ids) {
|
||||
if(empty($segments_ids)) {
|
||||
throw new \Exception(__('At least one segment ID is required.', 'mailpoet'));
|
||||
}
|
||||
|
||||
// throw exception when subscriber does not exist
|
||||
$subscriber = Subscriber::findOne($subscriber_id);
|
||||
if(!$subscriber) {
|
||||
throw new \Exception(__('This subscriber does not exist.', 'mailpoet'));
|
||||
}
|
||||
|
||||
// throw exception when none of the segments exist
|
||||
$found_segments = Segment::whereIn('id', $segments_ids)->findMany();
|
||||
if(!$found_segments) {
|
||||
$exception = _n('This list does not exist.', 'These lists do not exist.', count($segments_ids), 'mailpoet');
|
||||
throw new \Exception($exception);
|
||||
}
|
||||
|
||||
// throw exception when trying to subscribe to a WP Users segment
|
||||
$found_segments_ids = array();
|
||||
foreach($found_segments as $segment) {
|
||||
if($segment->type === Segment::TYPE_WP_USERS) {
|
||||
throw new \Exception(__(sprintf("Can't subscribe to a WordPress Users list with ID %d.", $segment->id), 'mailpoet'));
|
||||
}
|
||||
$found_segments_ids[] = $segment->id;
|
||||
}
|
||||
|
||||
// throw an exception when one or more segments do not exist
|
||||
if(count($found_segments_ids) !== count($segments_ids)) {
|
||||
$missing_ids = array_values(array_diff($segments_ids, $found_segments_ids));
|
||||
$exception = sprintf(
|
||||
_n('List with ID %s does not exist.', 'Lists with IDs %s do not exist.', count($missing_ids), 'mailpoet'),
|
||||
implode(', ', $missing_ids)
|
||||
);
|
||||
throw new \Exception($exception);
|
||||
}
|
||||
|
||||
SubscriberSegment::unsubscribeFromSegments($subscriber, $found_segments_ids);
|
||||
return $subscriber->withCustomFields()->withSubscriptions()->asArray();
|
||||
}
|
||||
|
||||
function getLists() {
|
||||
return Segment::whereNotEqual('type', Segment::TYPE_WP_USERS)->findArray();
|
||||
}
|
||||
@ -133,6 +187,46 @@ 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();
|
||||
}
|
||||
|
||||
function getSubscriber($subscriber_email) {
|
||||
$subscriber = Subscriber::findOne($subscriber_email);
|
||||
// throw exception when subscriber does not exist
|
||||
if(!$subscriber) {
|
||||
throw new \Exception(__('This subscriber does not exist.', 'mailpoet'));
|
||||
}
|
||||
return $subscriber->withCustomFields()->withSubscriptions()->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'];
|
||||
@ -22,6 +22,15 @@ 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);
|
||||
}
|
||||
}
|
||||
|
@ -115,8 +115,7 @@ class Initializer {
|
||||
}
|
||||
|
||||
function setupWidget() {
|
||||
$widget = new Widget($this->renderer);
|
||||
$widget->init();
|
||||
register_widget('\MailPoet\Form\Widget');
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
@ -232,6 +231,7 @@ class Initializer {
|
||||
$this->setupHooks();
|
||||
$this->setupJSONAPI();
|
||||
$this->setupRouter();
|
||||
$this->setupUserLocale();
|
||||
} catch(\Exception $e) {
|
||||
$this->handleFailedInitialization($e);
|
||||
}
|
||||
@ -247,6 +247,13 @@ class Initializer {
|
||||
$router->init();
|
||||
}
|
||||
|
||||
function setupUserLocale() {
|
||||
if(get_user_locale() === get_locale()) return;
|
||||
unload_textdomain(Env::$plugin_name);
|
||||
$localizer = new Localizer();
|
||||
$localizer->init();
|
||||
}
|
||||
|
||||
function setupPages() {
|
||||
$pages = new \MailPoet\Settings\Pages();
|
||||
$pages->init();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user