Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
daff3d5016 | |||
f13a4dd4f3 | |||
04238f3339 | |||
bc62474f3c | |||
98005a2a6f | |||
b7fe8dc6d6 | |||
436faea591 | |||
4208b148b4 | |||
5ce5f0bf8a | |||
18518de397 | |||
68f747211a | |||
ebd26ddd98 | |||
924aa0439f | |||
3124d6a61b | |||
149d031b52 | |||
fa96c4697d | |||
d46c9d5412 | |||
9425ac1593 | |||
4e32479609 | |||
7d95b38dc4 | |||
17c83c5bd4 | |||
23bb2f111f | |||
4655b2c64c | |||
84fede11b8 | |||
f37488fc0b | |||
20e2e03982 | |||
a5d96f1534 | |||
a516eb1a95 | |||
6dd8270bec | |||
981cbd579f | |||
52a0aae10f |
67
Vagrantfile
vendored
67
Vagrantfile
vendored
@ -1,67 +0,0 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
Vagrant.configure(2) do |config|
|
||||
config.vm.provider "virtualbox" do |v|
|
||||
v.name = "phoenix"
|
||||
v.memory = "2048"
|
||||
end
|
||||
|
||||
config.vm.define :web do |web|
|
||||
web.vm.box = "ubuntu/trusty64"
|
||||
web.vm.hostname = "phoenix"
|
||||
web.vm.network "forwarded_port", guest: 80, host: 8080
|
||||
web.vm.synced_folder(
|
||||
".",
|
||||
"/var/www/html/wp-content/plugins/wysija-newsletters",
|
||||
create: true,
|
||||
owner: "vagrant",
|
||||
group: "www-data"
|
||||
)
|
||||
|
||||
web.vm.provision "shell", inline: <<-SHELL
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y apache2 curl zip sendmail git build-essential
|
||||
|
||||
sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password password root'
|
||||
sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password_again password root'
|
||||
sudo apt-get install -y mysql-server-5.5
|
||||
|
||||
sudo apt-get install -y php5 libapache2-mod-php5 php5-curl php5-gd php5-mcrypt php5-readline mysql-server-5.5 php5-mysql php-apc
|
||||
sudo sed -i "s/error_reporting = .*/error_reporting = E_ALL/" /etc/php5/apache2/php.ini
|
||||
sudo sed -i "s/display_errors = .*/display_errors = On/" /etc/php5/apache2/php.ini
|
||||
|
||||
cd /var/www/html
|
||||
|
||||
sudo wget https://github.com/calvinlough/sqlbuddy/raw/gh-pages/sqlbuddy.zip -O /var/www/html/sqlbuddy.zip
|
||||
sudo rm index.html
|
||||
unzip sqlbuddy.zip
|
||||
|
||||
sudo curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
|
||||
sudo chmod +x wp-cli.phar
|
||||
sudo mv wp-cli.phar /usr/local/bin/wp
|
||||
sudo wp core download --allow-root
|
||||
mysql -uroot -proot -e "create database wordpress"
|
||||
sudo wp core config --allow-root --dbname=wordpress --dbuser=root --dbpass=root
|
||||
sudo wp core install --allow-root --url="http://localhost:8080" --title=WordPress --admin_user=admin --admin_password=password --admin_email=test@mailpoet-container.com
|
||||
|
||||
sudo sed -i "s/upload_max_filesize = .*/upload_max_filesize = 32M/" /etc/php5/apache2/php.ini
|
||||
sudo sed -i "s/post_max_size = .*/post_max_size = 32M/" /etc/php5/apache2/php.ini
|
||||
sudo chown -hR vagrant:www-data /var/www/html/
|
||||
sudo a2enmod rewrite > /dev/null 2>&1
|
||||
|
||||
cd /var/www/html/wp-content/plugins/wysija-newsletters
|
||||
|
||||
curl -sS https://getcomposer.org/installer | php
|
||||
|
||||
sudo add-apt-repository -y ppa:chris-lea/node.js
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y nodejs
|
||||
|
||||
sudo sed -i "s/export APACHE_RUN_USER.*/export APACHE_RUN_USER=vagrant/" /etc/apache2/envvars
|
||||
sudo chown -R vagrant:www-data /var/lock/apache2
|
||||
|
||||
sudo service apache2 restart
|
||||
SHELL
|
||||
end
|
||||
end
|
@ -18,6 +18,38 @@
|
||||
height: 150px
|
||||
margin-right: 15px
|
||||
float: left
|
||||
overflow: hidden
|
||||
position: relative
|
||||
|
||||
img
|
||||
min-width: 150px
|
||||
min-height: 150px
|
||||
height: auto
|
||||
width: 110%
|
||||
position: relative
|
||||
top: 0
|
||||
left: 50%
|
||||
transform: translate(-50%, 0%)
|
||||
|
||||
.mailpoet_overlay
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
background-color: rgba(255, 255, 255, 0.0)
|
||||
|
||||
&:hover
|
||||
background-color: rgba(255, 255, 255, 0.7)
|
||||
|
||||
&::after
|
||||
content: " "
|
||||
position: absolute
|
||||
top: 0
|
||||
left: 0
|
||||
bottom: 0
|
||||
right: 0
|
||||
background: url(../img/preview_magnifying_glass.svg) no-repeat center center
|
||||
|
||||
.mailpoet_boxes .mailpoet_description
|
||||
float:left
|
||||
|
12
assets/img/preview_magnifying_glass.svg
Normal file
12
assets/img/preview_magnifying_glass.svg
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="47.002px" height="38.003px" viewBox="0 0 47.002 38.003" enable-background="new 0 0 47.002 38.003" xml:space="preserve">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" fill="#565656" d="M46.328,36.365c-1.188,1.725-3.553,2.158-5.273,0.962L25.479,26.52
|
||||
c-1.104-0.763-1.674-2.007-1.631-3.257c-2.006,2.157-4.642,3.606-7.594,4.145c-3.626,0.663-7.288-0.13-10.311-2.227
|
||||
c-3.024-2.098-5.054-5.253-5.714-8.887c-0.661-3.636,0.127-7.31,2.221-10.344c4.325-6.264,12.927-7.834,19.177-3.5
|
||||
c5.672,3.938,7.486,11.412,4.537,17.443c1.152-0.486,2.519-0.392,3.627,0.377l15.58,10.808C47.09,32.274,47.52,34.641,46.328,36.365
|
||||
z M23.235,12.09c-0.459-2.534-1.874-4.734-3.982-6.196C14.897,2.87,8.896,3.963,5.878,8.331c-3.014,4.373-1.922,10.388,2.435,13.408
|
||||
c4.356,3.025,10.356,1.932,13.374-2.438C23.146,17.187,23.696,14.625,23.235,12.09z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/img/sample_template/coffee-grain.jpg
Normal file
BIN
assets/img/sample_template/coffee-grain.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 178 KiB |
BIN
assets/img/sample_template/header-v2.jpg
Normal file
BIN
assets/img/sample_template/header-v2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
BIN
assets/img/sample_template/map-v2.jpg
Normal file
BIN
assets/img/sample_template/map-v2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 130 KiB |
BIN
assets/img/sample_template/sandwich.jpg
Normal file
BIN
assets/img/sample_template/sandwich.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
@ -105,6 +105,9 @@ const item_actions = [
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'trash'
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -79,27 +79,48 @@ define(
|
||||
|
||||
if(custom_actions.length > 0) {
|
||||
item_actions = custom_actions.map(function(action, index) {
|
||||
if(action.refresh) {
|
||||
if(action.display !== undefined) {
|
||||
if(action.display(this.props.item) === false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(action.name === 'trash') {
|
||||
return (
|
||||
<span key={ 'action-'+index } className="trash">
|
||||
{(index > 0) ? ' | ' : ''}
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={ this.handleTrashItem.bind(
|
||||
null,
|
||||
this.props.item.id
|
||||
) }>
|
||||
Trash
|
||||
</a>
|
||||
</span>
|
||||
);
|
||||
} else if(action.refresh) {
|
||||
return (
|
||||
<span
|
||||
onClick={ this.props.onRefreshItems }
|
||||
key={ 'action-'+index } className={ action.name }>
|
||||
{(index > 0) ? ' | ' : ''}
|
||||
{ action.link(this.props.item) }
|
||||
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
|
||||
</span>
|
||||
);
|
||||
} else if(action.link) {
|
||||
return (
|
||||
<span
|
||||
key={ 'action-'+index } className={ action.name }>
|
||||
{(index > 0) ? ' | ' : ''}
|
||||
{ action.link(this.props.item) }
|
||||
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
|
||||
</span>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<span
|
||||
key={ 'action-'+index } className={ action.name }>
|
||||
{(index > 0) ? ' | ' : ''}
|
||||
<a href="javascript:;" onClick={
|
||||
(action.onClick !== undefined)
|
||||
? action.onClick.bind(null,
|
||||
@ -108,7 +129,6 @@ define(
|
||||
)
|
||||
: false
|
||||
}>{ action.label }</a>
|
||||
{(index < (custom_actions.length - 1)) ? ' | ' : ''}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@ -158,17 +178,6 @@ define(
|
||||
<div>
|
||||
<div className="row-actions">
|
||||
{ item_actions }
|
||||
{ ' | ' }
|
||||
<span className="trash">
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={ this.handleTrashItem.bind(
|
||||
null,
|
||||
this.props.item.id
|
||||
) }>
|
||||
Trash
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
|
||||
@ -637,7 +646,7 @@ define(
|
||||
// bulk actions
|
||||
var bulk_actions = this.props.bulk_actions || [];
|
||||
|
||||
if(this.state.group === 'trash') {
|
||||
if(this.state.group === 'trash' && bulk_actions.length > 0) {
|
||||
bulk_actions = [
|
||||
{
|
||||
name: 'restore',
|
||||
|
@ -6,8 +6,9 @@ define([
|
||||
'backbone.marionette',
|
||||
'jquery',
|
||||
'blob',
|
||||
'filesaver'
|
||||
], function(App, MailPoet, Notice, Backbone, Marionette, jQuery, Blob, FileSaver) {
|
||||
'filesaver',
|
||||
'html2canvas'
|
||||
], function(App, MailPoet, Notice, Backbone, Marionette, jQuery, Blob, FileSaver, html2canvas) {
|
||||
|
||||
"use strict";
|
||||
|
||||
@ -56,16 +57,26 @@ define([
|
||||
});
|
||||
};
|
||||
|
||||
Module.exportTemplate = function(options) {
|
||||
var data = _.extend(options || {}, {
|
||||
body: App.getBody(),
|
||||
});
|
||||
var blob = new Blob(
|
||||
[JSON.stringify(data)],
|
||||
{ type: 'application/json;charset=utf-8' }
|
||||
);
|
||||
Module.getThumbnail = function(element, options) {
|
||||
return html2canvas(element, options || {});
|
||||
};
|
||||
|
||||
FileSaver.saveAs(blob, 'template.json');
|
||||
Module.exportTemplate = function(options) {
|
||||
var that = this;
|
||||
return Module.getThumbnail(
|
||||
jQuery('#mailpoet_editor_content > .mailpoet_block').get(0)
|
||||
).then(function(thumbnail) {
|
||||
var data = _.extend(options || {}, {
|
||||
thumbnail: thumbnail.toDataURL('image/jpeg'),
|
||||
body: App.getBody(),
|
||||
});
|
||||
var blob = new Blob(
|
||||
[JSON.stringify(data)],
|
||||
{ type: 'application/json;charset=utf-8' }
|
||||
);
|
||||
|
||||
FileSaver.saveAs(blob, 'template.json');
|
||||
});
|
||||
};
|
||||
|
||||
Module.SaveView = Marionette.LayoutView.extend({
|
||||
|
@ -56,7 +56,7 @@ define([
|
||||
};
|
||||
|
||||
Module.getTransformedPosts = function(options) {
|
||||
return Module._query({
|
||||
return Module._cachedQuery({
|
||||
action: 'getTransformedPosts',
|
||||
options: options,
|
||||
});
|
||||
|
@ -112,6 +112,9 @@ define(
|
||||
</a>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'trash'
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -150,6 +150,13 @@ define(
|
||||
this.setState({ loading: false });
|
||||
}
|
||||
},
|
||||
handleShowTemplate: function(template) {
|
||||
MailPoet.Modal.popup({
|
||||
title: template.name,
|
||||
template: '<img src="{{ thumbnail }}" />',
|
||||
data: template,
|
||||
});
|
||||
},
|
||||
handleTemplateImport: function() {
|
||||
this.getTemplates();
|
||||
},
|
||||
@ -164,11 +171,22 @@ define(
|
||||
Delete
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
), thumbnail = '';
|
||||
|
||||
if (typeof template.thumbnail === 'string'
|
||||
&& template.thumbnail.length > 0) {
|
||||
thumbnail = (
|
||||
<a href="javascript:;" onClick={this.handleShowTemplate.bind(null, template)}>
|
||||
<img src={ template.thumbnail } />
|
||||
<div className="mailpoet_overlay"></div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={ 'template-'+index }>
|
||||
<div className="mailpoet_thumbnail">
|
||||
{ thumbnail }
|
||||
</div>
|
||||
|
||||
<div className="mailpoet_description">
|
||||
|
75
assets/js/src/queue.jsx
Normal file
75
assets/js/src/queue.jsx
Normal file
@ -0,0 +1,75 @@
|
||||
define(
|
||||
[
|
||||
'react',
|
||||
'react-dom',
|
||||
'mailpoet',
|
||||
'classnames'
|
||||
],
|
||||
function (
|
||||
React,
|
||||
ReactDOM,
|
||||
MailPoet,
|
||||
classNames
|
||||
) {
|
||||
var QueueDaemonControl = React.createClass({
|
||||
getInitialState: function () {
|
||||
return (queueDaemon) ? {
|
||||
status: queueDaemon.status,
|
||||
timeSinceStart: queueDaemon.time_since_start,
|
||||
timeSinceUpdate: queueDaemon.time_since_update,
|
||||
counter: queueDaemon.counter
|
||||
} : null;
|
||||
},
|
||||
getDaemonData: function () {
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: 'queue',
|
||||
action: 'getQueueStatus'
|
||||
}).done(function (response) {
|
||||
this.setState({
|
||||
status: response.status,
|
||||
timeSinceStart: response.time_since_start,
|
||||
timeSinceUpdate: response.time_since_update,
|
||||
counter: response.counter,
|
||||
});
|
||||
}.bind(this));
|
||||
},
|
||||
componentDidMount: function () {
|
||||
this.getDaemonData;
|
||||
setInterval(this.getDaemonData, 5000);
|
||||
},
|
||||
render: function () {
|
||||
if (!this.state) {
|
||||
return (
|
||||
<div className="QueueControl">
|
||||
Woops, daemon is not running ;\
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
Queue is currently <b>{this.state.status}</b>.
|
||||
<br/>
|
||||
<br/>
|
||||
It was started
|
||||
<b> {this.state.timeSinceStart} </b> and was last executed
|
||||
<b> {this.state.timeSinceUpdate} </b> for a total of
|
||||
<b> {this.state.counter} </b> times (once every 30 seconds, unless it was interrupted and restarted).
|
||||
<br />
|
||||
</div>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
let container = document.getElementById('queue_container');
|
||||
if (container) {
|
||||
ReactDOM.render(
|
||||
<QueueDaemonControl />,
|
||||
container
|
||||
)
|
||||
}
|
||||
}
|
||||
);
|
@ -121,6 +121,30 @@ const item_actions = [
|
||||
);
|
||||
refresh();
|
||||
});
|
||||
},
|
||||
display: function(segment) {
|
||||
return (segment.type !== 'wp_users');
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'synchronize_segment',
|
||||
label: 'Update',
|
||||
className: 'update',
|
||||
onClick: function(item, refresh) {
|
||||
return MailPoet.Ajax.post({
|
||||
endpoint: 'segments',
|
||||
action: 'synchronize'
|
||||
}).done(function(response) {
|
||||
if(response === true) {
|
||||
MailPoet.Notice.success(
|
||||
('List "%$1s" has been synchronized.').replace('%$1s', item.name)
|
||||
);
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
},
|
||||
display: function(segment) {
|
||||
return (segment.type === 'wp_users');
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -130,15 +154,16 @@ const item_actions = [
|
||||
<a href={ item.subscribers_url }>View subscribers</a>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'trash',
|
||||
display: function(segment) {
|
||||
return (segment.type !== 'wp_users');
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const bulk_actions = [
|
||||
{
|
||||
name: 'trash',
|
||||
label: 'Trash',
|
||||
onSuccess: messages.onTrash
|
||||
}
|
||||
];
|
||||
|
||||
const SegmentList = React.createClass({
|
||||
@ -148,7 +173,6 @@ const SegmentList = React.createClass({
|
||||
'column-primary',
|
||||
'has-row-actions'
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<td className={ rowClasses }>
|
||||
|
@ -20,6 +20,7 @@ define(
|
||||
return;
|
||||
}
|
||||
jQuery(document).ready(function () {
|
||||
jQuery('input[name="select_method"]').attr('checked', false);
|
||||
// configure router
|
||||
router = new (Backbone.Router.extend({
|
||||
routes: {
|
||||
@ -299,11 +300,11 @@ define(
|
||||
var test,
|
||||
cleanEmail =
|
||||
email
|
||||
// left/right trim spaces, punctuation (e.g., " 'email@email.com'; ")
|
||||
// right trim non-printable characters (e.g., "email@email.com<6F>")
|
||||
// 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
|
||||
// remove spaces (e.g., "email @ email . com")
|
||||
// remove urlencoded characters
|
||||
.replace(/\s+|%\d+|,+/g, '')
|
||||
.toLowerCase();
|
||||
|
||||
@ -1000,7 +1001,6 @@ define(
|
||||
}
|
||||
|
||||
function toggleNextStepButton(condition) {
|
||||
console.log(condition);
|
||||
var disabled = 'button-disabled';
|
||||
if (condition === 'on') {
|
||||
nextStepButton.removeClass(disabled);
|
@ -103,7 +103,9 @@ const bulk_actions = [
|
||||
id: 'move_to_segment',
|
||||
endpoint: 'segments',
|
||||
filter: function(segment) {
|
||||
return !!(!segment.deleted_at);
|
||||
return !!(
|
||||
!segment.deleted_at && segment.type === 'default'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -132,7 +134,9 @@ const bulk_actions = [
|
||||
id: 'add_to_segment',
|
||||
endpoint: 'segments',
|
||||
filter: function(segment) {
|
||||
return !!(!segment.deleted_at);
|
||||
return !!(
|
||||
!segment.deleted_at && segment.type === 'default'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@ -159,7 +163,12 @@ const bulk_actions = [
|
||||
onSelect: function() {
|
||||
let field = {
|
||||
id: 'remove_from_segment',
|
||||
endpoint: 'segments'
|
||||
endpoint: 'segments',
|
||||
filter: function(segment) {
|
||||
return !!(
|
||||
segment.type === 'default'
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
@ -206,6 +215,21 @@ const bulk_actions = [
|
||||
}
|
||||
];
|
||||
|
||||
const item_actions = [
|
||||
{
|
||||
name: 'edit',
|
||||
label: 'Edit',
|
||||
link: function(item) {
|
||||
return (
|
||||
<Link to={ `/edit/${item.id}` }>Edit</Link>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'trash'
|
||||
}
|
||||
];
|
||||
|
||||
const SubscriberList = React.createClass({
|
||||
renderItem: function(subscriber, actions) {
|
||||
let row_classes = classNames(
|
||||
@ -295,6 +319,7 @@ const SubscriberList = React.createClass({
|
||||
onRenderItem={ this.renderItem }
|
||||
columns={ columns }
|
||||
bulk_actions={ bulk_actions }
|
||||
item_actions={ item_actions }
|
||||
messages={ messages }
|
||||
onGetItems={ this.onGetItems }
|
||||
/>
|
||||
|
@ -8,7 +8,9 @@
|
||||
"tburry/pquery": "*",
|
||||
"j4mie/paris": "1.5.4",
|
||||
"swiftmailer/swiftmailer": "^5.4",
|
||||
"phpseclib/phpseclib": "*"
|
||||
"phpseclib/phpseclib": "*",
|
||||
"mtdowling/cron-expression": "^1.0",
|
||||
"nesbot/carbon": "^1.21"
|
||||
},
|
||||
"require-dev": {
|
||||
"codeception/codeception": "*",
|
||||
|
@ -4,36 +4,37 @@ namespace MailPoet\Config;
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Env {
|
||||
public static $version;
|
||||
public static $plugin_name;
|
||||
public static $plugin_url;
|
||||
public static $file;
|
||||
public static $path;
|
||||
public static $views_path;
|
||||
public static $assets_path;
|
||||
public static $assets_url;
|
||||
public static $temp_name;
|
||||
public static $temp_path;
|
||||
public static $languages_path;
|
||||
public static $lib_path;
|
||||
public static $plugin_prefix;
|
||||
public static $db_prefix;
|
||||
public static $db_source_name;
|
||||
public static $db_host;
|
||||
public static $db_socket;
|
||||
public static $db_port;
|
||||
public static $db_name;
|
||||
public static $db_username;
|
||||
public static $db_password;
|
||||
public static $db_charset;
|
||||
static $version;
|
||||
static $plugin_name;
|
||||
static $plugin_url;
|
||||
static $plugin_path;
|
||||
static $file;
|
||||
static $path;
|
||||
static $views_path;
|
||||
static $assets_path;
|
||||
static $assets_url;
|
||||
static $temp_name;
|
||||
static $temp_path;
|
||||
static $languages_path;
|
||||
static $lib_path;
|
||||
static $plugin_prefix;
|
||||
static $db_prefix;
|
||||
static $db_source_name;
|
||||
static $db_host;
|
||||
static $db_socket;
|
||||
static $db_port;
|
||||
static $db_name;
|
||||
static $db_username;
|
||||
static $db_password;
|
||||
static $db_charset;
|
||||
|
||||
public static function init($file, $version) {
|
||||
static function init($file, $version) {
|
||||
global $wpdb;
|
||||
self::$version = $version;
|
||||
self::$file = $file;
|
||||
self::$path = dirname(self::$file);
|
||||
self::$plugin_name = 'mailpoet';
|
||||
self::$plugin_url = plugins_url() . '/' . basename(Env::$path);
|
||||
self::$plugin_url = plugin_dir_url(__FILE__);
|
||||
self::$views_path = self::$path . '/views';
|
||||
self::$assets_path = self::$path . '/assets';
|
||||
self::$assets_url = plugins_url('/assets', $file);
|
||||
@ -46,11 +47,12 @@ class Env {
|
||||
self::$db_host = DB_HOST;
|
||||
self::$db_port = 3306;
|
||||
self::$db_socket = false;
|
||||
if (preg_match('/(?=:\d+$)/', DB_HOST)) {
|
||||
if(preg_match('/(?=:\d+$)/', DB_HOST)) {
|
||||
list(self::$db_host, self::$db_port) = explode(':', DB_HOST);
|
||||
}
|
||||
else if (preg_match('/:/', DB_HOST)) {
|
||||
self::$db_socket = true;
|
||||
} else {
|
||||
if(preg_match('/:/', DB_HOST)) {
|
||||
self::$db_socket = true;
|
||||
}
|
||||
}
|
||||
self::$db_name = DB_NAME;
|
||||
self::$db_username = DB_USER;
|
||||
@ -59,7 +61,7 @@ class Env {
|
||||
self::$db_source_name = self::dbSourceName(self::$db_host, self::$db_socket, self::$db_port);
|
||||
}
|
||||
|
||||
private static function dbSourceName($host, $socket,$port) {
|
||||
private static function dbSourceName($host, $socket, $port) {
|
||||
$source_name = array(
|
||||
(!$socket) ? 'mysql:host=' : 'mysql:unix_socket=',
|
||||
$host,
|
||||
@ -72,4 +74,14 @@ class Env {
|
||||
);
|
||||
return implode('', $source_name);
|
||||
}
|
||||
|
||||
static function isPluginActivated() {
|
||||
$activatesPlugins = get_option('active_plugins');
|
||||
$isActivated =
|
||||
in_array(
|
||||
sprintf('%s/%s.php', basename(self::$path), self::$plugin_name),
|
||||
$activatesPlugins
|
||||
);
|
||||
return ($isActivated) ? true : false;
|
||||
}
|
||||
}
|
42
lib/Config/Hooks.php
Normal file
42
lib/Config/Hooks.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
|
||||
class Hooks {
|
||||
function __construct() {
|
||||
}
|
||||
|
||||
function init() {
|
||||
// WP Users synchronization
|
||||
add_action(
|
||||
'user_register',
|
||||
'\MailPoet\Segments\WP::synchronizeUser',
|
||||
1
|
||||
);
|
||||
add_action(
|
||||
'added_existing_user',
|
||||
'\MailPoet\Segments\WP::synchronizeUser',
|
||||
1
|
||||
);
|
||||
add_action(
|
||||
'profile_update',
|
||||
'\MailPoet\Segments\WP::synchronizeUser',
|
||||
1
|
||||
);
|
||||
add_action(
|
||||
'delete_user',
|
||||
'\MailPoet\Segments\WP::synchronizeUser',
|
||||
1
|
||||
);
|
||||
// multisite
|
||||
add_action(
|
||||
'deleted_user',
|
||||
'\MailPoet\Segments\WP::synchronizeUser',
|
||||
1
|
||||
);
|
||||
add_action(
|
||||
'remove_user_from_blog',
|
||||
'\MailPoet\Segments\WP::synchronizeUser',
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Models;
|
||||
use MailPoet\Queue\Supervisor;
|
||||
use MailPoet\Router;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
@ -25,6 +26,9 @@ class Initializer {
|
||||
$this->setupAnalytics();
|
||||
$this->setupPermissions();
|
||||
$this->setupChangelog();
|
||||
$this->setupPublicAPI();
|
||||
$this->runQueueSupervisor();
|
||||
$this->setupHooks();
|
||||
}
|
||||
|
||||
function setupDB() {
|
||||
@ -33,7 +37,8 @@ class Initializer {
|
||||
\ORM::configure('password', Env::$db_password);
|
||||
\ORM::configure('logging', WP_DEBUG);
|
||||
\ORM::configure('driver_options', array(
|
||||
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8'
|
||||
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8',
|
||||
\PDO::MYSQL_ATTR_INIT_COMMAND => 'SET TIME_ZONE = "+00:00"'
|
||||
));
|
||||
|
||||
$subscribers = Env::$db_prefix . 'subscribers';
|
||||
@ -41,6 +46,8 @@ class Initializer {
|
||||
$newsletters = Env::$db_prefix . 'newsletters';
|
||||
$newsletter_templates = Env::$db_prefix . 'newsletter_templates';
|
||||
$segments = Env::$db_prefix . 'segments';
|
||||
$filters = Env::$db_prefix . 'filters';
|
||||
$segment_filter = Env::$db_prefix . 'segment_filter';
|
||||
$forms = Env::$db_prefix . 'forms';
|
||||
$subscriber_segment = Env::$db_prefix . 'subscriber_segment';
|
||||
$newsletter_segment = Env::$db_prefix . 'newsletter_segment';
|
||||
@ -48,11 +55,15 @@ class Initializer {
|
||||
$subscriber_custom_field = Env::$db_prefix . 'subscriber_custom_field';
|
||||
$newsletter_option_fields = Env::$db_prefix . 'newsletter_option_fields';
|
||||
$newsletter_option = Env::$db_prefix . 'newsletter_option';
|
||||
$queues = Env::$db_prefix . 'queues';
|
||||
$newsletter_statistics = Env::$db_prefix . 'newsletter_statistics';
|
||||
|
||||
define('MP_SUBSCRIBERS_TABLE', $subscribers);
|
||||
define('MP_SETTINGS_TABLE', $settings);
|
||||
define('MP_NEWSLETTERS_TABLE', $newsletters);
|
||||
define('MP_SEGMENTS_TABLE', $segments);
|
||||
define('MP_FILTERS_TABLE', $filters);
|
||||
define('MP_SEGMENT_FILTER_TABLE', $segment_filter);
|
||||
define('MP_FORMS_TABLE', $forms);
|
||||
define('MP_SUBSCRIBER_SEGMENT_TABLE', $subscriber_segment);
|
||||
define('MP_NEWSLETTER_TEMPLATES_TABLE', $newsletter_templates);
|
||||
@ -61,6 +72,8 @@ class Initializer {
|
||||
define('MP_SUBSCRIBER_CUSTOM_FIELD_TABLE', $subscriber_custom_field);
|
||||
define('MP_NEWSLETTER_OPTION_FIELDS_TABLE', $newsletter_option_fields);
|
||||
define('MP_NEWSLETTER_OPTION_TABLE', $newsletter_option);
|
||||
define('MP_QUEUES_TABLE', $queues);
|
||||
define('MP_NEWSLETTER_STATISTICS_TABLE', $newsletter_statistics);
|
||||
}
|
||||
|
||||
function setupActivator() {
|
||||
@ -110,4 +123,21 @@ class Initializer {
|
||||
$changelog = new Changelog();
|
||||
$changelog->init();
|
||||
}
|
||||
}
|
||||
|
||||
function setupHooks() {
|
||||
$hooks = new Hooks();
|
||||
$hooks->init();
|
||||
}
|
||||
|
||||
function setupPublicAPI() {
|
||||
$publicAPI = new PublicAPI();
|
||||
$publicAPI->init();
|
||||
}
|
||||
|
||||
function runQueueSupervisor() {
|
||||
try {
|
||||
$supervisor = new Supervisor();
|
||||
$supervisor->checkDaemon();
|
||||
} catch (\Exception $e) {}
|
||||
}
|
||||
}
|
@ -1,17 +1,17 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Form\Block;
|
||||
use MailPoet\Form\Renderer as FormRenderer;
|
||||
use MailPoet\Models\Form;
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Settings\Charsets;
|
||||
use MailPoet\Settings\Hosts;
|
||||
use MailPoet\Settings\Pages;
|
||||
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
|
||||
use \MailPoet\Models\Segment;
|
||||
use \MailPoet\Models\Setting;
|
||||
use \MailPoet\Models\Form;
|
||||
use \MailPoet\Form\Block;
|
||||
use \MailPoet\Form\Renderer as FormRenderer;
|
||||
use \MailPoet\Settings\Hosts;
|
||||
use \MailPoet\Settings\Pages;
|
||||
use \MailPoet\Settings\Charsets;
|
||||
use \MailPoet\Util\Permissions;
|
||||
use \MailPoet\Util\DKIM;
|
||||
use MailPoet\Util\DKIM;
|
||||
use MailPoet\Util\Permissions;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -24,7 +24,10 @@ class Menu {
|
||||
function init() {
|
||||
add_action(
|
||||
'admin_menu',
|
||||
array($this, 'setup')
|
||||
array(
|
||||
$this,
|
||||
'setup'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -34,7 +37,10 @@ class Menu {
|
||||
'MailPoet',
|
||||
'manage_options',
|
||||
'mailpoet',
|
||||
array($this, 'home'),
|
||||
array(
|
||||
$this,
|
||||
'home'
|
||||
),
|
||||
$this->assets_url . '/img/menu_icon.png',
|
||||
30
|
||||
);
|
||||
@ -44,7 +50,10 @@ class Menu {
|
||||
__('Newsletters'),
|
||||
'manage_options',
|
||||
'mailpoet-newsletters',
|
||||
array($this, 'newsletters')
|
||||
array(
|
||||
$this,
|
||||
'newsletters'
|
||||
)
|
||||
);
|
||||
add_submenu_page(
|
||||
'mailpoet',
|
||||
@ -52,7 +61,10 @@ class Menu {
|
||||
__('Forms'),
|
||||
'manage_options',
|
||||
'mailpoet-forms',
|
||||
array($this, 'forms')
|
||||
array(
|
||||
$this,
|
||||
'forms'
|
||||
)
|
||||
);
|
||||
add_submenu_page(
|
||||
'mailpoet',
|
||||
@ -60,7 +72,10 @@ class Menu {
|
||||
__('Subscribers'),
|
||||
'manage_options',
|
||||
'mailpoet-subscribers',
|
||||
array($this, 'subscribers')
|
||||
array(
|
||||
$this,
|
||||
'subscribers'
|
||||
)
|
||||
);
|
||||
add_submenu_page(
|
||||
'mailpoet',
|
||||
@ -68,7 +83,10 @@ class Menu {
|
||||
__('Segments'),
|
||||
'manage_options',
|
||||
'mailpoet-segments',
|
||||
array($this, 'segments')
|
||||
array(
|
||||
$this,
|
||||
'segments'
|
||||
)
|
||||
);
|
||||
add_submenu_page(
|
||||
'mailpoet',
|
||||
@ -76,7 +94,10 @@ class Menu {
|
||||
__('Settings'),
|
||||
'manage_options',
|
||||
'mailpoet-settings',
|
||||
array($this, 'settings')
|
||||
array(
|
||||
$this,
|
||||
'settings'
|
||||
)
|
||||
);
|
||||
add_submenu_page(
|
||||
null,
|
||||
@ -84,7 +105,10 @@ class Menu {
|
||||
__('Import'),
|
||||
'manage_options',
|
||||
'mailpoet-import',
|
||||
array($this, 'import')
|
||||
array(
|
||||
$this,
|
||||
'import'
|
||||
)
|
||||
);
|
||||
add_submenu_page(
|
||||
null,
|
||||
@ -92,7 +116,10 @@ class Menu {
|
||||
__('Export'),
|
||||
'manage_options',
|
||||
'mailpoet-export',
|
||||
array($this, 'export')
|
||||
array(
|
||||
$this,
|
||||
'export'
|
||||
)
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
@ -101,7 +128,10 @@ class Menu {
|
||||
__('Welcome'),
|
||||
'manage_options',
|
||||
'mailpoet-welcome',
|
||||
array($this, 'welcome')
|
||||
array(
|
||||
$this,
|
||||
'welcome'
|
||||
)
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
@ -110,7 +140,10 @@ class Menu {
|
||||
__('Update'),
|
||||
'manage_options',
|
||||
'mailpoet-update',
|
||||
array($this, 'update')
|
||||
array(
|
||||
$this,
|
||||
'update'
|
||||
)
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
@ -119,7 +152,10 @@ class Menu {
|
||||
__('Form editor'),
|
||||
'manage_options',
|
||||
'mailpoet-form-editor',
|
||||
array($this, 'formEditor')
|
||||
array(
|
||||
$this,
|
||||
'formEditor'
|
||||
)
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
@ -128,7 +164,22 @@ class Menu {
|
||||
__('Newsletter editor'),
|
||||
'manage_options',
|
||||
'mailpoet-newsletter-editor',
|
||||
array($this, 'newletterEditor')
|
||||
array(
|
||||
$this,
|
||||
'newletterEditor'
|
||||
)
|
||||
);
|
||||
|
||||
add_submenu_page(
|
||||
'mailpoet',
|
||||
__('Queue'),
|
||||
__('Queue'),
|
||||
'manage_options',
|
||||
'mailpoet-queue',
|
||||
array(
|
||||
$this,
|
||||
'queue'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -142,8 +193,8 @@ class Menu {
|
||||
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
|
||||
$redirect_url =
|
||||
(!empty($_GET['mailpoet_redirect']))
|
||||
? urldecode($_GET['mailpoet_redirect'])
|
||||
: wp_get_referer();
|
||||
? urldecode($_GET['mailpoet_redirect'])
|
||||
: wp_get_referer();
|
||||
|
||||
if(
|
||||
$redirect_url === $current_url
|
||||
@ -166,8 +217,8 @@ class Menu {
|
||||
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
|
||||
$redirect_url =
|
||||
(!empty($_GET['mailpoet_redirect']))
|
||||
? urldecode($_GET['mailpoet_redirect'])
|
||||
: wp_get_referer();
|
||||
? urldecode($_GET['mailpoet_redirect'])
|
||||
: wp_get_referer();
|
||||
|
||||
if(
|
||||
$redirect_url === $current_url
|
||||
@ -206,7 +257,8 @@ class Menu {
|
||||
|
||||
$data = array(
|
||||
'settings' => $settings,
|
||||
'segments' => Segment::getPublished()->findArray(),
|
||||
'segments' => Segment::getPublished()
|
||||
->findArray(),
|
||||
'pages' => Pages::getAll(),
|
||||
'flags' => $this->_getFlags(),
|
||||
'charsets' => Charsets::getAll(),
|
||||
@ -234,11 +286,14 @@ class Menu {
|
||||
|
||||
// check if users can register
|
||||
$flags['registration_enabled'] =
|
||||
!(in_array($registration, array('none', 'blog')));
|
||||
!(in_array($registration, array(
|
||||
'none',
|
||||
'blog'
|
||||
)));
|
||||
} else {
|
||||
// check if users can register
|
||||
$flags['registration_enabled'] =
|
||||
(bool)get_option('users_can_register', false);
|
||||
(bool) get_option('users_can_register', false);
|
||||
}
|
||||
|
||||
return $flags;
|
||||
@ -249,7 +304,7 @@ class Menu {
|
||||
|
||||
$data['segments'] = Segment::findArray();
|
||||
|
||||
echo $this->renderer->render('subscribers.html', $data);
|
||||
echo $this->renderer->render('subscribers/subscribers.html', $data);
|
||||
}
|
||||
|
||||
function segments() {
|
||||
@ -272,7 +327,7 @@ class Menu {
|
||||
$data['segments'] = Segment::findArray();
|
||||
$settings = Setting::findArray();
|
||||
$data['settings'] = array();
|
||||
foreach($settings as $setting) {
|
||||
foreach ($settings as $setting) {
|
||||
$data['settings'][$setting['name']] = $setting['value'];
|
||||
}
|
||||
$data['roles'] = $wp_roles->get_names();
|
||||
@ -290,17 +345,17 @@ class Menu {
|
||||
function import() {
|
||||
$import = new BootStrapMenu('import');
|
||||
$data = $import->bootstrap();
|
||||
echo $this->renderer->render('import.html', $data);
|
||||
echo $this->renderer->render('subscribers/importExport/import.html', $data);
|
||||
}
|
||||
|
||||
function export() {
|
||||
$export = new BootStrapMenu('export');
|
||||
$data = $export->bootstrap();
|
||||
echo $this->renderer->render('export.html', $data);
|
||||
echo $this->renderer->render('subscribers/importExport/export.html', $data);
|
||||
}
|
||||
|
||||
function formEditor() {
|
||||
$id = (isset($_GET['id']) ? (int)$_GET['id'] : 0);
|
||||
$id = (isset($_GET['id']) ? (int) $_GET['id'] : 0);
|
||||
$form = Form::findOne($id);
|
||||
if($form !== false) {
|
||||
$form = $form->asArray();
|
||||
@ -309,7 +364,8 @@ class Menu {
|
||||
$data = array(
|
||||
'form' => $form,
|
||||
'pages' => Pages::getAll(),
|
||||
'segments' => Segment::getPublished()->findArray(),
|
||||
'segments' => Segment::getPublished()
|
||||
->findArray(),
|
||||
'styles' => FormRenderer::getStyles($form),
|
||||
'date_types' => Block\Date::getDateTypes(),
|
||||
'date_formats' => Block\Date::getDateFormats()
|
||||
@ -317,4 +373,10 @@ class Menu {
|
||||
|
||||
echo $this->renderer->render('form/editor.html', $data);
|
||||
}
|
||||
|
||||
function queue() {
|
||||
$daemon = new \MailPoet\Queue\BootStrapMenu();
|
||||
$data['daemon'] = json_encode($daemon->bootstrap());
|
||||
echo $this->renderer->render('queue.html', $data);
|
||||
}
|
||||
}
|
@ -21,6 +21,8 @@ class Migrator {
|
||||
'subscriber_custom_field',
|
||||
'newsletter_option_fields',
|
||||
'newsletter_option',
|
||||
'queues',
|
||||
'newsletter_statistics',
|
||||
'forms'
|
||||
);
|
||||
}
|
||||
@ -50,6 +52,7 @@ class Migrator {
|
||||
function subscribers() {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'wp_user_id bigint(20) NULL,',
|
||||
'first_name tinytext NOT NULL,',
|
||||
'last_name tinytext NOT NULL,',
|
||||
'email varchar(150) NOT NULL,',
|
||||
@ -97,6 +100,7 @@ class Migrator {
|
||||
'name varchar(250) NOT NULL,',
|
||||
'description varchar(250) NOT NULL,',
|
||||
'body longtext,',
|
||||
'thumbnail longtext,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id)'
|
||||
@ -106,14 +110,15 @@ class Migrator {
|
||||
|
||||
function segments() {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'name varchar(90) NOT NULL,',
|
||||
'description varchar(250) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id),',
|
||||
'UNIQUE KEY name (name)'
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'name varchar(90) NOT NULL,',
|
||||
'type varchar(90) NOT NULL DEFAULT "default",',
|
||||
'description varchar(250) NOT NULL,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id),',
|
||||
'UNIQUE KEY name (name)'
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
@ -123,6 +128,7 @@ class Migrator {
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'subscriber_id mediumint(9) NOT NULL,',
|
||||
'segment_id mediumint(9) NOT NULL,',
|
||||
'status varchar(12) NOT NULL DEFAULT "subscribed",',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'PRIMARY KEY (id),',
|
||||
@ -199,6 +205,41 @@ class Migrator {
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
|
||||
function queues() {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'newsletter_id mediumint(9) NOT NULL,',
|
||||
'subscribers longtext,',
|
||||
'status varchar(12) NOT NULL,',
|
||||
'priority mediumint(9) NOT NULL DEFAULT 0,',
|
||||
'count_total mediumint(9) NOT NULL DEFAULT 0,',
|
||||
'count_processed mediumint(9) NOT NULL DEFAULT 0,',
|
||||
'count_to_process mediumint(9) NOT NULL DEFAULT 0,',
|
||||
'count_failed mediumint(9) NOT NULL DEFAULT 0,',
|
||||
'processed_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
|
||||
'PRIMARY KEY (id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
|
||||
function newsletter_statistics() {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
'newsletter_id mediumint(9) NOT NULL,',
|
||||
'subscriber_id mediumint(9) NOT NULL,',
|
||||
'queue_id mediumint(9) NOT NULL,',
|
||||
'sent_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'created_at TIMESTAMP NOT NULL DEFAULT 0,',
|
||||
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
|
||||
'deleted_at TIMESTAMP NULL DEFAULT NULL,',
|
||||
'PRIMARY KEY (id)',
|
||||
);
|
||||
return $this->sqlify(__FUNCTION__, $attributes);
|
||||
}
|
||||
|
||||
function forms() {
|
||||
$attributes = array(
|
||||
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
||||
@ -224,4 +265,4 @@ class Migrator {
|
||||
|
||||
return implode("\n", $sql);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Config\PopulatorData\Templates\SampleTemplate;
|
||||
|
||||
if (!defined('ABSPATH')) exit;
|
||||
|
||||
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
|
||||
@ -10,6 +12,7 @@ class Populator {
|
||||
$this->prefix = Env::$db_prefix;
|
||||
$this->models = array(
|
||||
'newsletter_option_fields',
|
||||
'newsletter_templates',
|
||||
);
|
||||
}
|
||||
|
||||
@ -84,6 +87,12 @@ class Populator {
|
||||
);
|
||||
}
|
||||
|
||||
private function newsletter_templates() {
|
||||
return array(
|
||||
(new SampleTemplate(Env::$assets_url))->get(),
|
||||
);
|
||||
}
|
||||
|
||||
private function populate($model) {
|
||||
$rows = $this->$model();
|
||||
$table = $this->prefix . $model;
|
||||
|
379
lib/Config/PopulatorData/Templates/SampleTemplate.php
Normal file
379
lib/Config/PopulatorData/Templates/SampleTemplate.php
Normal file
File diff suppressed because one or more lines are too long
45
lib/Config/PublicAPI.php
Normal file
45
lib/Config/PublicAPI.php
Normal file
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
namespace MailPoet\Config;
|
||||
|
||||
use MailPoet\Queue\Daemon;
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class PublicAPI {
|
||||
function __construct() {
|
||||
# http://example.com/?mailpoet-api§ion=&action=&payload=
|
||||
$this->api = isset($_GET['mailpoet-api']) ? true : false;
|
||||
$this->section = isset($_GET['section']) ? $_GET['section'] : false;
|
||||
$this->action = isset($_GET['action']) ?
|
||||
Helpers::underscoreToCamelCase($_GET['action']) :
|
||||
false;
|
||||
$this->payload = isset($_GET['payload']) ?
|
||||
json_decode(urldecode($_GET['payload']), true) :
|
||||
false;
|
||||
}
|
||||
|
||||
function init() {
|
||||
if(!$this->api && !$this->section) return;
|
||||
$this->_checkAndCallMethod($this, $this->section, $terminate = true);
|
||||
}
|
||||
|
||||
function queue() {
|
||||
$queue = new Daemon($this->payload);
|
||||
$this->_checkAndCallMethod($queue, $this->action);
|
||||
}
|
||||
|
||||
private function _checkAndCallMethod($class, $method, $terminate = false) {
|
||||
if(!method_exists($class, $method)) {
|
||||
if(!$terminate) return;
|
||||
header('HTTP/1.0 404 Not Found');
|
||||
exit;
|
||||
}
|
||||
call_user_func(
|
||||
array(
|
||||
$class,
|
||||
$method
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
50
lib/Models/Filter.php
Normal file
50
lib/Models/Filter.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
namespace MailPoet\Models;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Filter extends Model {
|
||||
static $_table = MP_FILTERS_TABLE;
|
||||
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
|
||||
$this->addValidations('name', array(
|
||||
'required' => __('You need to specify a name.')
|
||||
));
|
||||
}
|
||||
|
||||
function delete() {
|
||||
// delete all relations to subscribers
|
||||
SegmentFilter::where('filter_id', $this->id)->deleteMany();
|
||||
return parent::delete();
|
||||
}
|
||||
|
||||
function segments() {
|
||||
return $this->has_many_through(
|
||||
__NAMESPACE__.'\Segment',
|
||||
__NAMESPACE__.'\SegmentFilter',
|
||||
'filter_id',
|
||||
'segment_id'
|
||||
);
|
||||
}
|
||||
|
||||
static function createOrUpdate($data = array()) {
|
||||
$filter = false;
|
||||
|
||||
if(isset($data['id']) && (int)$data['id'] > 0) {
|
||||
$filter = self::findOne((int)$data['id']);
|
||||
}
|
||||
|
||||
if($filter === false) {
|
||||
$filter = self::create();
|
||||
$filter->hydrate($data);
|
||||
} else {
|
||||
unset($data['id']);
|
||||
$filter->set($data);
|
||||
}
|
||||
|
||||
$filter->save();
|
||||
return $filter;
|
||||
}
|
||||
}
|
12
lib/Models/NewsletterStatistics.php
Normal file
12
lib/Models/NewsletterStatistics.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace MailPoet\Models;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class NewsletterStatistics extends Model {
|
||||
public static $_table = MP_NEWSLETTER_STATISTICS_TABLE;
|
||||
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
12
lib/Models/Queue.php
Normal file
12
lib/Models/Queue.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace MailPoet\Models;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Queue extends Model {
|
||||
public static $_table = MP_QUEUES_TABLE;
|
||||
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
@ -38,6 +38,15 @@ class Segment extends Model {
|
||||
);
|
||||
}
|
||||
|
||||
function segmentFilters() {
|
||||
return $this->has_many_through(
|
||||
__NAMESPACE__.'\Filter',
|
||||
__NAMESPACE__.'\SegmentFilter',
|
||||
'segment_id',
|
||||
'filter_id'
|
||||
);
|
||||
}
|
||||
|
||||
function duplicate($data = array()) {
|
||||
$duplicate = parent::duplicate($data);
|
||||
|
||||
@ -67,6 +76,23 @@ class Segment extends Model {
|
||||
->delete();
|
||||
}
|
||||
|
||||
static function getWPUsers() {
|
||||
$segment = self::where('type', 'wp_users')->findOne();
|
||||
|
||||
if($segment === false) {
|
||||
// create the wp users list
|
||||
$segment = self::create();
|
||||
$segment->hydrate(array(
|
||||
'name' => __('WordPress Users'),
|
||||
'type' => 'wp_users'
|
||||
));
|
||||
$segment->save();
|
||||
return self::findOne($segment->id());
|
||||
}
|
||||
|
||||
return $segment;
|
||||
}
|
||||
|
||||
static function search($orm, $search = '') {
|
||||
return $orm->whereLike('name', '%'.$search.'%');
|
||||
}
|
||||
@ -102,8 +128,13 @@ class Segment extends Model {
|
||||
->left_outer_join(
|
||||
MP_SUBSCRIBER_SEGMENT_TABLE,
|
||||
array(self::$_table.'.id', '=', MP_SUBSCRIBER_SEGMENT_TABLE.'.segment_id'))
|
||||
->left_outer_join(
|
||||
MP_SUBSCRIBERS_TABLE,
|
||||
array(MP_SUBSCRIBER_SEGMENT_TABLE.'.subscriber_id', '=', MP_SUBSCRIBERS_TABLE.'.id'))
|
||||
->whereNull(MP_SUBSCRIBERS_TABLE.'.deleted_at')
|
||||
->group_by(self::$_table.'.id')
|
||||
->group_by(self::$_table.'.name')
|
||||
->where(self::$_table.'.type', 'default')
|
||||
->findArray();
|
||||
}
|
||||
|
||||
@ -114,16 +145,18 @@ class Segment extends Model {
|
||||
'LEFT JOIN ' . self::$_table . ' segments ON segments.id = relation.segment_id ' .
|
||||
'LEFT JOIN ' . MP_SUBSCRIBERS_TABLE . ' subscribers ON subscribers.id = relation.subscriber_id ' .
|
||||
(($withConfirmedSubscribers) ?
|
||||
'WHERE subscribers.status = 1 ' :
|
||||
'WHERE subscribers.status = "subscribed" ' :
|
||||
'WHERE relation.segment_id IS NOT NULL ') .
|
||||
'AND subscribers.deleted_at IS NULL ' .
|
||||
'GROUP BY segments.id) ' .
|
||||
'UNION ALL ' .
|
||||
'(SELECT 0 as id, "' . __('Not In List') . '" as name, COUNT(*) as subscribers ' .
|
||||
'FROM ' . MP_SUBSCRIBERS_TABLE . ' subscribers ' .
|
||||
'LEFT JOIN ' . MP_SUBSCRIBER_SEGMENT_TABLE . ' relation on relation.subscriber_id = subscribers.id ' .
|
||||
(($withConfirmedSubscribers) ?
|
||||
'WHERE relation.subscriber_id is NULL AND subscribers.status = 1 ' :
|
||||
'WHERE relation.subscriber_id is NULL AND subscribers.status = "subscribed" ' :
|
||||
'WHERE relation.subscriber_id is NULL ') .
|
||||
'AND subscribers.deleted_at IS NULL ' .
|
||||
'HAVING subscribers) ' .
|
||||
'ORDER BY name'
|
||||
)->findArray();
|
||||
@ -147,4 +180,8 @@ class Segment extends Model {
|
||||
$segment->save();
|
||||
return $segment;
|
||||
}
|
||||
|
||||
static function getPublic() {
|
||||
return self::getPublished()->where('type', 'default');
|
||||
}
|
||||
}
|
12
lib/Models/SegmentFilter.php
Normal file
12
lib/Models/SegmentFilter.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
namespace MailPoet\Models;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class SegmentFilter extends Model {
|
||||
public static $_table = MP_SEGMENT_FILTER_TABLE;
|
||||
|
||||
function __construct() {
|
||||
parent::__construct();
|
||||
}
|
||||
}
|
@ -324,7 +324,7 @@ class Subscriber extends Model {
|
||||
->whereNull('deleted_at')
|
||||
->where('status', 'unconfirmed');
|
||||
}
|
||||
|
||||
|
||||
static function createMultiple($columns, $values) {
|
||||
return self::rawExecute(
|
||||
'INSERT INTO `' . self::$_table . '` ' .
|
||||
|
36
lib/Queue/BootStrapMenu.php
Normal file
36
lib/Queue/BootStrapMenu.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace MailPoet\Queue;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use MailPoet\Models\Queue;
|
||||
use MailPoet\Models\Setting;
|
||||
|
||||
class BootStrapMenu {
|
||||
function __construct() {
|
||||
$this->daemon = Setting::where('name', 'daemon')
|
||||
->findOne();
|
||||
}
|
||||
|
||||
function bootStrap() {
|
||||
$queues = Queue::findMany();
|
||||
return ($this->daemon) ?
|
||||
array_merge(
|
||||
array(
|
||||
'time_since_start' =>
|
||||
Carbon::createFromFormat(
|
||||
'Y-m-d H:i:s',
|
||||
$this->daemon->created_at,
|
||||
'UTC'
|
||||
)->diffForHumans(),
|
||||
'time_since_update' =>
|
||||
Carbon::createFromFormat(
|
||||
'Y-m-d H:i:s',
|
||||
$this->daemon->updated_at,
|
||||
'UTC'
|
||||
)->diffForHumans()
|
||||
),
|
||||
json_decode($this->daemon->value, true)
|
||||
) :
|
||||
"false";
|
||||
}
|
||||
}
|
128
lib/Queue/Daemon.php
Normal file
128
lib/Queue/Daemon.php
Normal file
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
namespace MailPoet\Queue;
|
||||
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Util\Security;
|
||||
|
||||
require_once(ABSPATH . 'wp-includes/pluggable.php');
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Daemon {
|
||||
function __construct($payload = array()) {
|
||||
set_time_limit(0);
|
||||
ignore_user_abort();
|
||||
list ($this->daemon, $this->daemonData) = $this->getDaemon();
|
||||
$this->refreshedToken = $this->refreshToken();
|
||||
$this->payload = $payload;
|
||||
$this->timer = microtime(true);
|
||||
}
|
||||
|
||||
function start() {
|
||||
if(!isset($this->payload['session'])) {
|
||||
$this->abortWithError('missing session ID');
|
||||
}
|
||||
$this->manageSession('start');
|
||||
$daemon = $this->daemon;
|
||||
$daemonData = $this->daemonData;
|
||||
if(!$daemon) {
|
||||
$daemon = Setting::create();
|
||||
$daemon->name = 'daemon';
|
||||
$daemon->value = json_encode(array('status' => 'stopped'));
|
||||
$daemon->save();
|
||||
}
|
||||
if($daemonData['status'] !== 'started') {
|
||||
$_SESSION['daemon'] = 'started';
|
||||
$daemonData = array(
|
||||
'status' => 'started',
|
||||
'token' => $this->refreshedToken,
|
||||
'counter' => ($daemonData['status'] === 'paused') ?
|
||||
$daemonData['counter'] :
|
||||
0
|
||||
);
|
||||
$_SESSION['daemon'] = array('result' => true);
|
||||
$this->manageSession('end');
|
||||
$daemon->value = json_encode($daemonData);
|
||||
$daemon->save();
|
||||
$this->callSelf();
|
||||
} else {
|
||||
$_SESSION['daemon'] = array(
|
||||
'result' => false,
|
||||
'error' => 'already started'
|
||||
);
|
||||
}
|
||||
$this->manageSession('end');
|
||||
}
|
||||
|
||||
function run() {
|
||||
if(!$this->daemon || $this->daemonData['status'] !== 'started') {
|
||||
$this->abortWithError('not running');
|
||||
}
|
||||
if(!isset($this->payload['token']) ||
|
||||
$this->payload['token'] !== $this->daemonData['token']
|
||||
) {
|
||||
$this->abortWithError('invalid token');
|
||||
}
|
||||
|
||||
$worker = new Worker();
|
||||
$worker->process();
|
||||
$elapsedTime = microtime(true) - $this->timer;
|
||||
if ($elapsedTime < 30) {
|
||||
sleep(30 - $elapsedTime);
|
||||
}
|
||||
|
||||
// after each execution, read daemon in case it's status was modified
|
||||
list($daemon, $daemonData) = $this->getDaemon();
|
||||
$daemonData['counter']++;
|
||||
$daemonData['token'] = $this->refreshedToken;
|
||||
$daemon->value = json_encode($daemonData);
|
||||
$daemon->save();
|
||||
$this->callSelf();
|
||||
}
|
||||
|
||||
function getDaemon() {
|
||||
$daemon = Setting::where('name', 'daemon')
|
||||
->findOne();
|
||||
return array(
|
||||
($daemon) ? $daemon : null,
|
||||
($daemon) ? json_decode($daemon->value, true) : null
|
||||
);
|
||||
}
|
||||
|
||||
function refreshToken() {
|
||||
return Security::generateRandomString(5);
|
||||
}
|
||||
|
||||
function manageSession($action) {
|
||||
switch ($action) {
|
||||
case 'start':
|
||||
if(session_id()) {
|
||||
session_write_close();
|
||||
}
|
||||
session_id($this->payload['session']);
|
||||
session_start();
|
||||
break;
|
||||
case 'end':
|
||||
session_write_close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function callSelf() {
|
||||
$payload = json_encode(array('token' => $this->refreshedToken));
|
||||
Supervisor::getRemoteUrl(
|
||||
'/?mailpoet-api§ion=queue&action=run&payload=' . urlencode($payload)
|
||||
|
||||
);
|
||||
exit;
|
||||
}
|
||||
|
||||
function abortWithError($error) {
|
||||
wp_send_json(
|
||||
array(
|
||||
'result' => false,
|
||||
'error' => $error
|
||||
));
|
||||
exit;
|
||||
}
|
||||
}
|
89
lib/Queue/Supervisor.php
Normal file
89
lib/Queue/Supervisor.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
namespace MailPoet\Queue;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use MailPoet\Config\Env;
|
||||
use MailPoet\Models\Setting;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Supervisor {
|
||||
function __construct($forceStart = false) {
|
||||
$this->forceStart = $forceStart;
|
||||
if(!Env::isPluginActivated()) {
|
||||
throw new \Exception('Database has not been configured.');
|
||||
}
|
||||
list ($this->daemon, $this->daemonData) = $this->getDaemon();
|
||||
}
|
||||
|
||||
function checkDaemon() {
|
||||
if(!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])) return;
|
||||
if(!$this->daemon) {
|
||||
return $this->startDaemon();
|
||||
} else {
|
||||
if(!$this->forceStart && ($this->daemonData['status'] === 'paused' ||
|
||||
$this->daemonData['status'] === 'stopped'
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
$currentTime = Carbon::now('UTC');
|
||||
$lastUpdateTime = Carbon::createFromFormat(
|
||||
'Y-m-d H:i:s',
|
||||
$this->daemon->updated_at, 'UTC'
|
||||
);
|
||||
$timeSinceLastStart = $currentTime->diffInSeconds($lastUpdateTime);
|
||||
if($timeSinceLastStart < 50) return;
|
||||
$this->daemonData['status'] = 'paused';
|
||||
$this->daemon->value = json_encode($this->daemonData);
|
||||
$this->daemon->save();
|
||||
return $this->startDaemon();
|
||||
}
|
||||
}
|
||||
|
||||
function startDaemon() {
|
||||
if(!session_id()) session_start();
|
||||
$sessionId = session_id();
|
||||
session_write_close();
|
||||
$_SESSION['daemon'] = null;
|
||||
$payload = json_encode(array('session' => $sessionId));
|
||||
self::getRemoteUrl(
|
||||
'/?mailpoet-api§ion=queue&action=start&payload=' . urlencode($payload)
|
||||
);
|
||||
session_start();
|
||||
$daemonStatus = $_SESSION['daemon'];
|
||||
unset($_SESSION['daemon']);
|
||||
session_write_close();
|
||||
return $daemonStatus;
|
||||
}
|
||||
|
||||
function getDaemon() {
|
||||
$daemon = Setting::where('name', 'daemon')
|
||||
->findOne();
|
||||
$daemonData = ($daemon) ? json_decode($daemon->value, true) : false;
|
||||
return array(
|
||||
$daemon,
|
||||
$daemonData
|
||||
);
|
||||
}
|
||||
|
||||
static function getRemoteUrl($url) {
|
||||
$args = array(
|
||||
'timeout' => 1,
|
||||
'user-agent' => 'MailPoet (www.mailpoet.com)'
|
||||
);
|
||||
wp_remote_get(
|
||||
self::getSiteUrl() . $url,
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
static function getSiteUrl() {
|
||||
if(preg_match('!:\d+/!', site_url())) return site_url();
|
||||
preg_match('!http://(?P<host>.*?):(?P<port>\d+)!', site_url(), $server);
|
||||
$fp = @fsockopen($server['host'], $server['port'], $errno, $errstr, 1);
|
||||
return ($fp) ?
|
||||
site_url() :
|
||||
preg_replace('/(?=:\d+):\d+/', '$1', site_url());
|
||||
}
|
||||
}
|
62
lib/Queue/Worker.php
Normal file
62
lib/Queue/Worker.php
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
namespace MailPoet\Queue;
|
||||
|
||||
use MailPoet\Models\Newsletter;
|
||||
use MailPoet\Models\NewsletterStatistics;
|
||||
use MailPoet\Models\Queue;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Worker {
|
||||
function __construct($timer = false) {
|
||||
$this->timer = $timer;
|
||||
$this->timer = microtime(true);
|
||||
}
|
||||
|
||||
function process() {
|
||||
$queues =
|
||||
Queue::orderByDesc('priority')
|
||||
->whereNotIn('status', array(
|
||||
'paused',
|
||||
'completed'
|
||||
))
|
||||
->findResultSet();
|
||||
foreach ($queues as $queue) {
|
||||
$newsletter = Newsletter::findOne($queue->newsletter_id)
|
||||
->asArray();
|
||||
$subscribers = json_decode($queue->subscribers, true);
|
||||
if(!isset($subscribers['failed'])) $subscribers['failed'] = array();
|
||||
if(!isset($subscribers['processed'])) $subscribers['processed'] = array();
|
||||
$subscribersToProcess = $subscribers['to_process'];
|
||||
foreach ($subscribersToProcess as $subscriber) {
|
||||
$elapsedTime = microtime(true) - $this->timer;
|
||||
if($elapsedTime >= 28) break;
|
||||
// TODO: hook up to mailer
|
||||
sleep(1);
|
||||
$newsletterStatistics = NewsletterStatistics::create();
|
||||
$newsletterStatistics->subscriber_id = $subscriber;
|
||||
$newsletterStatistics->newsletter_id = $newsletter['id'];
|
||||
$newsletterStatistics->queue_id = $queue->id;
|
||||
$newsletterStatistics->save();
|
||||
$subscribers['processed'][] = $subscriber;
|
||||
$subscribers['to_process'] = array_values(
|
||||
array_diff(
|
||||
$subscribers['to_process'],
|
||||
$subscribers['processed']
|
||||
)
|
||||
);
|
||||
$queue->count_processed = count($subscribers['processed']);
|
||||
$queue->count_to_process = count($subscribers['to_process']);
|
||||
$queue->count_failed = count($subscribers['failed']);
|
||||
$queue->count_total =
|
||||
$queue->count_processed + $queue->count_to_process + $queue->count_failed;
|
||||
if(!$queue->count_to_process) {
|
||||
$queue->processed_at = date('Y-m-d H:i:s');
|
||||
$queue->status = 'completed';
|
||||
}
|
||||
$queue->subscribers = json_encode($subscribers);
|
||||
$queue->save();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
51
lib/Router/Queue.php
Normal file
51
lib/Router/Queue.php
Normal file
@ -0,0 +1,51 @@
|
||||
<?php
|
||||
namespace MailPoet\Router;
|
||||
|
||||
use MailPoet\Models\Setting;
|
||||
use MailPoet\Queue\Daemon;
|
||||
use MailPoet\Queue\Supervisor;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
class Queue {
|
||||
function start() {
|
||||
$supervisor = new Supervisor();
|
||||
wp_send_json(
|
||||
array(
|
||||
'result' => ($supervisor->checkDaemon($forceStart = true)) ?
|
||||
true :
|
||||
false
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function update($data) {
|
||||
switch ($data['action']) {
|
||||
case 'stop':
|
||||
$status = 'stopped';
|
||||
break;
|
||||
default:
|
||||
$status = 'paused';
|
||||
break;
|
||||
}
|
||||
$daemon = new Daemon();
|
||||
if(!$daemon->daemon || $daemon->daemonData['status'] !== 'started') {
|
||||
$result = false;
|
||||
} else {
|
||||
$daemon->daemonData['status'] = $status;
|
||||
$daemon->daemon->value = json_encode($daemon->daemonData);
|
||||
$result = $daemon->daemon->save();
|
||||
}
|
||||
wp_send_json(
|
||||
array(
|
||||
'result' => $result
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function getQueueStatus() {
|
||||
$daemon = new \MailPoet\Queue\BootStrapMenu();
|
||||
wp_send_json($daemon->bootStrap());
|
||||
|
||||
}
|
||||
}
|
@ -2,7 +2,9 @@
|
||||
namespace MailPoet\Router;
|
||||
use \MailPoet\Models\Segment;
|
||||
use \MailPoet\Models\SubscriberSegment;
|
||||
use \MailPoet\Models\SegmentFilter;
|
||||
use \MailPoet\Listing;
|
||||
use \MailPoet\Segments\WP;
|
||||
|
||||
if(!defined('ABSPATH')) exit;
|
||||
|
||||
@ -42,15 +44,15 @@ class Segments {
|
||||
'subscribers'
|
||||
)
|
||||
->select_expr(
|
||||
'SUM(CASE status WHEN "subscribed" THEN 1 ELSE 0 END)',
|
||||
'SUM(CASE subscribers.status WHEN "subscribed" THEN 1 ELSE 0 END)',
|
||||
'subscribed'
|
||||
)
|
||||
->select_expr(
|
||||
'SUM(CASE status WHEN "unsubscribed" THEN 1 ELSE 0 END)',
|
||||
'SUM(CASE subscribers.status WHEN "unsubscribed" THEN 1 ELSE 0 END)',
|
||||
'unsubscribed'
|
||||
)
|
||||
->select_expr(
|
||||
'SUM(CASE status WHEN "unconfirmed" THEN 1 ELSE 0 END)',
|
||||
'SUM(CASE subscribers.status WHEN "unconfirmed" THEN 1 ELSE 0 END)',
|
||||
'unconfirmed'
|
||||
)
|
||||
->findOne()->asArray();
|
||||
@ -135,6 +137,12 @@ class Segments {
|
||||
wp_send_json($result);
|
||||
}
|
||||
|
||||
function synchronize() {
|
||||
$result = WP::synchronizeUsers();
|
||||
|
||||
wp_send_json($result);
|
||||
}
|
||||
|
||||
function bulkAction($data = array()) {
|
||||
$bulk_action = new Listing\BulkAction(
|
||||
'\MailPoet\Models\Segment',
|
||||
|
88
lib/Segments/WP.php
Normal file
88
lib/Segments/WP.php
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
namespace MailPoet\Segments;
|
||||
use \MailPoet\Models\Subscriber;
|
||||
use \MailPoet\Models\Segment;
|
||||
|
||||
class WP {
|
||||
static function synchronizeUser($wp_user_id) {
|
||||
$wpUser = \get_userdata($wp_user_id);
|
||||
if($wpUser === false) return;
|
||||
|
||||
$subscriber = Subscriber::where('wp_user_id', $wpUser->ID)
|
||||
->findOne();
|
||||
|
||||
switch(current_filter()) {
|
||||
case 'delete_user':
|
||||
case 'deleted_user':
|
||||
case 'remove_user_from_blog':
|
||||
if($subscriber !== false && $subscriber->id()) {
|
||||
$subscriber->delete();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'user_register':
|
||||
case 'added_existing_user':
|
||||
case 'profile_update':
|
||||
default:
|
||||
// get first name & last name
|
||||
$first_name = $wpUser->first_name;
|
||||
$last_name = $wpUser->last_name;
|
||||
if(empty($wpUser->first_name) && empty($wpUser->last_name)) {
|
||||
$first_name = $wpUser->display_name;
|
||||
}
|
||||
|
||||
// subscriber data
|
||||
$data = array(
|
||||
'wp_user_id'=> $wpUser->ID,
|
||||
'email' => $wpUser->user_email,
|
||||
'first_name' => $first_name,
|
||||
'last_name' => $last_name,
|
||||
'status' => 'subscribed'
|
||||
);
|
||||
|
||||
if($subscriber !== false) {
|
||||
$data['id'] = $subscriber->id();
|
||||
}
|
||||
$subscriber = Subscriber::createOrUpdate($data);
|
||||
|
||||
if($subscriber !== false && $subscriber->id()) {
|
||||
$segment = Segment::getWPUsers();
|
||||
$segment->addSubscriber($subscriber->id());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static function synchronizeUsers() {
|
||||
// get wordpress users list
|
||||
$segment = Segment::getWPUsers();
|
||||
|
||||
// count WP users
|
||||
$users_count = \count_users()['total_users'];
|
||||
$linked_subscribers_count = $segment->subscribers()->count();
|
||||
|
||||
if($users_count !== $linked_subscribers_count) {
|
||||
$linked_subscribers = Subscriber::select('wp_user_id')
|
||||
->whereNotNull('wp_user_id')
|
||||
->findArray();
|
||||
|
||||
$exclude_ids = array();
|
||||
if(!empty($linked_subscribers)) {
|
||||
$exclude_ids = array_map(function($subscriber) {
|
||||
return $subscriber['wp_user_id'];
|
||||
}, $linked_subscribers);
|
||||
}
|
||||
|
||||
$users = \get_users(array(
|
||||
'count_total' => false,
|
||||
'fields' => 'ID',
|
||||
'exclude' => $exclude_ids
|
||||
));
|
||||
|
||||
foreach($users as $user) {
|
||||
static::synchronizeUser($user);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
@ -9,7 +9,6 @@ use MailPoet\Models\SubscriberSegment;
|
||||
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
|
||||
use MailPoet\Util\Helpers;
|
||||
use MailPoet\Util\XLSXWriter;
|
||||
use Symfony\Component\Console\Helper\Helper;
|
||||
|
||||
class Export {
|
||||
public function __construct($data) {
|
||||
@ -93,7 +92,12 @@ class Export {
|
||||
);
|
||||
$lastSegment = $subscriber['segment_name'];
|
||||
}
|
||||
$writer->writeSheet(array_merge($headerRow, $rows), 'MailPoet');
|
||||
$writer->writeSheet(
|
||||
array_merge($headerRow, $rows),
|
||||
($this->groupBySegmentOption) ?
|
||||
ucwords($subscriber['segment_name']) :
|
||||
'MailPoet'
|
||||
);
|
||||
$writer->writeToFile($this->exportFile);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
@ -155,6 +159,8 @@ class Export {
|
||||
$subscribers =
|
||||
$subscribers->where(Subscriber::$_table . '.status', 'subscribed');
|
||||
}
|
||||
$subscribers = $subscribers->whereNull(Subscriber::$_table . '.deleted_at');
|
||||
|
||||
return $subscribers->findArray();
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
<?php
|
||||
namespace MailPoet\Subscribers\ImportExport\Import;
|
||||
|
||||
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Models\SubscriberCustomField;
|
||||
use MailPoet\Models\SubscriberSegment;
|
||||
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
|
||||
use MailPoet\Util\Helpers;
|
||||
|
||||
class Import {
|
||||
@ -27,7 +27,9 @@ class Import {
|
||||
$subscriberFields = $this->subscriberFields;
|
||||
$subscriberCustomFields = $this->subscriberCustomFields;
|
||||
$subscribersData = $this->subscribersData;
|
||||
$subscribersData = $this->filterSubscriberStatus($subscribersData);
|
||||
list ($subscribersData, $subscriberFields) =
|
||||
$this->filterSubscriberStatus($subscribersData, $subscriberFields);
|
||||
$this->deleteExistingTrashedSubscribers($subscribersData);
|
||||
list($subscribersData, $subscriberFields) = $this->extendSubscribersAndFields(
|
||||
$subscribersData, $subscriberFields
|
||||
);
|
||||
@ -48,7 +50,7 @@ class Import {
|
||||
$updatedSubscribers =
|
||||
$this->createOrUpdateSubscribers(
|
||||
'update',
|
||||
$existingSubscribers,
|
||||
$existingSubscribers,
|
||||
$subscriberFields,
|
||||
$subscriberCustomFields
|
||||
);
|
||||
@ -84,6 +86,7 @@ class Import {
|
||||
array_map(function ($subscriberEmails) {
|
||||
return Subscriber::selectMany(array('email'))
|
||||
->whereIn('email', $subscriberEmails)
|
||||
->whereNull('deleted_at')
|
||||
->findArray();
|
||||
}, array_chunk($subscribersData['email'], 200))
|
||||
);
|
||||
@ -131,6 +134,25 @@ class Import {
|
||||
);
|
||||
}
|
||||
|
||||
function deleteExistingTrashedSubscribers($subscribersData) {
|
||||
$existingTrashedRecords = array_filter(
|
||||
array_map(function ($subscriberEmails) {
|
||||
return Subscriber::selectMany(array('id'))
|
||||
->whereIn('email', $subscriberEmails)
|
||||
->whereNotNull('deleted_at')
|
||||
->findArray();
|
||||
}, array_chunk($subscribersData['email'], 200))
|
||||
);
|
||||
if(!$existingTrashedRecords) return;
|
||||
$existingTrashedRecords = Helpers::flattenArray($existingTrashedRecords);
|
||||
foreach (array_chunk($existingTrashedRecords, 200) as $subscriberIds) {
|
||||
Subscriber::whereIn('id', $subscriberIds)
|
||||
->deleteMany();
|
||||
SubscriberSegment::whereIn('subscriber_id', $subscriberIds)
|
||||
->deleteMany();
|
||||
}
|
||||
}
|
||||
|
||||
function extendSubscribersAndFields($subscribersData, $subscriberFields) {
|
||||
$subscribersData['created_at'] = $this->filterSubscriberCreatedAtDate();
|
||||
$subscriberFields[] = 'created_at';
|
||||
@ -164,8 +186,16 @@ class Import {
|
||||
return array_fill(0, $this->subscribersCount, $this->currentTime);
|
||||
}
|
||||
|
||||
function filterSubscriberStatus($subscribersData) {
|
||||
if(!in_array('status', $this->subscriberFields)) return $subscribersData;
|
||||
function filterSubscriberStatus($subscribersData, $subscriberFields) {
|
||||
if(!in_array('status', $subscriberFields)) {
|
||||
$subscribersData['status'] =
|
||||
array_fill(0, count($subscribersData['email']), 'subscribed');
|
||||
$subscriberFields[] = 'status';
|
||||
return array(
|
||||
$subscribersData,
|
||||
$subscriberFields
|
||||
);
|
||||
}
|
||||
$statuses = array(
|
||||
'subscribed' => array(
|
||||
'subscribed',
|
||||
@ -198,7 +228,10 @@ class Import {
|
||||
}
|
||||
return 'subscribed'; // make "subscribed" a default status
|
||||
}, $subscribersData['status']);
|
||||
return $subscribersData;
|
||||
return array(
|
||||
$subscribersData,
|
||||
$subscriberFields
|
||||
);
|
||||
}
|
||||
|
||||
function createOrUpdateSubscribers(
|
||||
|
@ -76,12 +76,18 @@ class Helpers {
|
||||
static function getMaxPostSize($bytes = false) {
|
||||
$maxPostSize = ini_get('post_max_size');
|
||||
if(!$bytes) return $maxPostSize;
|
||||
switch (substr ($maxPostSize, -1))
|
||||
{
|
||||
case 'M': case 'm': return (int)$maxPostSize * 1048576;
|
||||
case 'K': case 'k': return (int)$maxPostSize * 1024;
|
||||
case 'G': case 'g': return (int)$maxPostSize * 1073741824;
|
||||
default: return $maxPostSize;
|
||||
switch (substr($maxPostSize, -1)) {
|
||||
case 'M':
|
||||
case 'm':
|
||||
return (int) $maxPostSize * 1048576;
|
||||
case 'K':
|
||||
case 'k':
|
||||
return (int) $maxPostSize * 1024;
|
||||
case 'G':
|
||||
case 'g':
|
||||
return (int) $maxPostSize * 1073741824;
|
||||
default:
|
||||
return $maxPostSize;
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,4 +170,18 @@ class Helpers {
|
||||
}
|
||||
return $resultArray;
|
||||
}
|
||||
|
||||
static function underscoreToCamelCase($str, $capitalise_first_char = false) {
|
||||
if($capitalise_first_char) {
|
||||
$str[0] = strtoupper($str[0]);
|
||||
}
|
||||
$func = create_function('$c', 'return strtoupper($c[1]);');
|
||||
return preg_replace_callback('/_([a-z])/', $func, $str);
|
||||
}
|
||||
|
||||
static function camelCaseToUnderscore($str) {
|
||||
$str[0] = strtolower($str[0]);
|
||||
$func = create_function('$c', 'return "_" . strtolower($c[1]);');
|
||||
return preg_replace_callback('/([A-Z])/', $func, $str);
|
||||
}
|
||||
}
|
@ -1,8 +1,14 @@
|
||||
<?php
|
||||
namespace MailPoet\Util;
|
||||
|
||||
require_once(ABSPATH . 'wp-includes/pluggable.php');
|
||||
|
||||
class Security {
|
||||
static function generateToken() {
|
||||
return wp_create_nonce('mailpoet_token');
|
||||
}
|
||||
|
||||
static function generateRandomString($length) {
|
||||
return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, $length);
|
||||
}
|
||||
}
|
@ -5,7 +5,8 @@
|
||||
},
|
||||
"napa": {
|
||||
"blob": "eligrey/Blob.js.git",
|
||||
"filesaver": "eligrey/FileSaver.js.git"
|
||||
"filesaver": "eligrey/FileSaver.js.git",
|
||||
"sticky-kit": "leafo/sticky-kit.git"
|
||||
},
|
||||
"dependencies": {
|
||||
"backbone": "1.2.3",
|
||||
|
@ -1,10 +1,10 @@
|
||||
<?php
|
||||
|
||||
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
|
||||
use MailPoet\Models\CustomField;
|
||||
use MailPoet\Models\Segment;
|
||||
use MailPoet\Models\Subscriber;
|
||||
use MailPoet\Models\SubscriberSegment;
|
||||
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
|
||||
|
||||
class BootStrapMenuCest {
|
||||
function _before() {
|
||||
@ -22,13 +22,13 @@ class BootStrapMenuCest {
|
||||
array(
|
||||
'first_name' => 'John',
|
||||
'last_name' => 'Mailer',
|
||||
'status' => 0,
|
||||
'status' => 'unconfirmed',
|
||||
'email' => 'john@mailpoet.com'
|
||||
),
|
||||
array(
|
||||
'first_name' => 'Mike',
|
||||
'last_name' => 'Smith',
|
||||
'status' => 1,
|
||||
'status' => 'subscribed',
|
||||
'email' => 'mike@maipoet.com'
|
||||
)
|
||||
);
|
||||
@ -52,6 +52,28 @@ class BootStrapMenuCest {
|
||||
expect($segments[1]['subscriberCount'])->equals(1);
|
||||
}
|
||||
|
||||
function itCanGetSegmentsForImportWithoutTrashedSubscribers() {
|
||||
$this->_createSegmentsAndSubscribers();
|
||||
$segments = $this->bootStrapImportMenu->getSegments();
|
||||
expect(count($segments))->equals(2);
|
||||
$subscriber = Subscriber::findOne(1);
|
||||
$subscriber->deleted_at = date('Y-m-d H:i:s');
|
||||
$subscriber->save();
|
||||
$segments = $this->bootStrapImportMenu->getSegments();
|
||||
expect(count($segments))->equals(1);
|
||||
}
|
||||
|
||||
function itCanGetSegmentsForExportWithoutTrashedSubscribers() {
|
||||
$this->_createSegmentsAndSubscribers();
|
||||
$segments = $this->bootStrapExportMenu->getSegments();
|
||||
expect(count($segments))->equals(2);
|
||||
$subscriber = Subscriber::findOne(1);
|
||||
$subscriber->deleted_at = date('Y-m-d H:i:s');
|
||||
$subscriber->save();
|
||||
$segments = $this->bootStrapExportMenu->getSegments();
|
||||
expect(count($segments))->equals(1);
|
||||
}
|
||||
|
||||
function itCanGetSegmentsForExport() {
|
||||
$this->_createSegmentsAndSubscribers();
|
||||
$segments = $this->bootStrapExportMenu->getSegments();
|
||||
@ -270,13 +292,9 @@ class BootStrapMenuCest {
|
||||
}
|
||||
|
||||
function _after() {
|
||||
ORM::forTable(Subscriber::$_table)
|
||||
->deleteMany();
|
||||
ORM::forTable(CustomField::$_table)
|
||||
->deleteMany();
|
||||
ORM::forTable(Segment::$_table)
|
||||
->deleteMany();
|
||||
ORM::forTable(SubscriberSegment::$_table)
|
||||
->deleteMany();
|
||||
ORM::raw_execute('TRUNCATE ' . Subscriber::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . Segment::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . SubscriberSegment::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . CustomField::$_table);
|
||||
}
|
||||
}
|
@ -105,28 +105,39 @@ class ImportCest {
|
||||
expect($fields)->equals(array(39));
|
||||
}
|
||||
|
||||
function itCanFilterSubscriberState() {
|
||||
$data = array(
|
||||
function itCanFilterSubscriberStatus() {
|
||||
$subscibersData = $this->subscribersData;
|
||||
$subscriberFields = $this->subscriberFields;
|
||||
list($subscibersData, $subsciberFields) =
|
||||
$this->import->filterSubscriberStatus($subscibersData, $subscriberFields);
|
||||
// subscribers' status was set to "subscribed" & status column was added
|
||||
// to subscribers fields
|
||||
expect(array_pop($subsciberFields))->equals('status');
|
||||
expect($subscibersData['status'][0])->equals('subscribed');
|
||||
expect(count($subscibersData['status']))->equals(2);
|
||||
$subscriberFields[] = 'status';
|
||||
$subscibersData = array(
|
||||
'status' => array(
|
||||
//subscribed
|
||||
#subscribed
|
||||
'subscribed',
|
||||
'confirmed',
|
||||
1,
|
||||
'1',
|
||||
'true',
|
||||
//unconfirmed
|
||||
#unconfirmed
|
||||
'unconfirmed',
|
||||
0,
|
||||
"0",
|
||||
//unsubscribed
|
||||
#unsubscribed
|
||||
'unsubscribed',
|
||||
-1,
|
||||
'-1',
|
||||
'false'
|
||||
),
|
||||
);
|
||||
$statuses = $this->import->filterSubscriberStatus($data);
|
||||
expect($statuses)->equals(
|
||||
list($subscibersData, $subsciberFields) =
|
||||
$this->import->filterSubscriberStatus($subscibersData, $subscriberFields);
|
||||
expect($subscibersData)->equals(
|
||||
array(
|
||||
'status' => array(
|
||||
'subscribed',
|
||||
@ -170,6 +181,41 @@ class ImportCest {
|
||||
->equals($subscribersData['first_name'][1]);
|
||||
}
|
||||
|
||||
function itCanDeleteTrashedSubscribers() {
|
||||
$subscribersData = $this->subscribersData;
|
||||
$subscriberFields = $this->subscriberFields;
|
||||
$subscribersData['deleted_at'] = array(
|
||||
null,
|
||||
date('Y-m-d H:i:s')
|
||||
);
|
||||
$subscriberFields[] = 'deleted_at';
|
||||
$this->import->createOrUpdateSubscribers(
|
||||
'create',
|
||||
$subscribersData,
|
||||
$subscriberFields,
|
||||
false
|
||||
);
|
||||
$dbSubscribers = Helpers::arrayColumn(
|
||||
Subscriber::select('id')
|
||||
->findArray(),
|
||||
'id'
|
||||
);
|
||||
expect(count($dbSubscribers))->equals(2);
|
||||
$this->import->addSubscribersToSegments(
|
||||
$dbSubscribers,
|
||||
$this->segments
|
||||
);
|
||||
$subscribersSegments = SubscriberSegment::findArray();
|
||||
expect(count($subscribersSegments))->equals(4);
|
||||
$this->import->deleteExistingTrashedSubscribers(
|
||||
$subscribersData
|
||||
);
|
||||
$subscribersSegments = SubscriberSegment::findArray();
|
||||
$dbSubscribers = Subscriber::findArray();
|
||||
expect(count($subscribersSegments))->equals(2);
|
||||
expect(count($dbSubscribers))->equals(1);
|
||||
}
|
||||
|
||||
function itCanCreateOrUpdateCustomFields() {
|
||||
$subscribersData = $this->subscribersData;
|
||||
$this->import->createOrUpdateSubscribers(
|
||||
@ -231,6 +277,22 @@ class ImportCest {
|
||||
expect(count($subscribersSegments))->equals(4);
|
||||
}
|
||||
|
||||
function itCanDeleteExistingTrashedSubscribers() {
|
||||
$subscribersData = $this->subscribersData;
|
||||
$subscriberFields = $this->subscriberFields;
|
||||
$subscriberFields[] = 'deleted_at';
|
||||
$subscribersData['deleted_at'] = array(
|
||||
null,
|
||||
date('Y-m-d H:i:s')
|
||||
);
|
||||
$this->import->createOrUpdateSubscribers(
|
||||
'create',
|
||||
$subscribersData,
|
||||
$subscriberFields,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
function itCanProcess() {
|
||||
$import = clone($this->import);
|
||||
$result = $import->process();
|
||||
@ -253,11 +315,8 @@ class ImportCest {
|
||||
}
|
||||
|
||||
function _after() {
|
||||
ORM::forTable(Subscriber::$_table)
|
||||
->deleteMany();
|
||||
ORM::forTable(SubscriberCustomField::$_table)
|
||||
->deleteMany();
|
||||
ORM::forTable(SubscriberSegment::$_table)
|
||||
->deleteMany();
|
||||
ORM::raw_execute('TRUNCATE ' . Subscriber::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . SubscriberSegment::$_table);
|
||||
ORM::raw_execute('TRUNCATE ' . SubscriberCustomField::$_table);
|
||||
}
|
||||
}
|
@ -4,9 +4,15 @@
|
||||
<h2 class="title">
|
||||
<span id="mailpoet_form_name"><%= form.name %></span>
|
||||
<input id="mailpoet_form_name_input" type="text" value="" style="display:none;" />
|
||||
<span>
|
||||
<a id="mailpoet_form_edit_name" class="button" href="javascript:;"><%= __('Edit name' ) %></a>
|
||||
</span>
|
||||
<a
|
||||
id="mailpoet_form_edit_name"
|
||||
class="add-new-h2"
|
||||
href="javascript:;"
|
||||
><%= __('Edit name' ) %></a>
|
||||
<a
|
||||
href="<%= admin_url('admin.php?page=mailpoet-forms') | raw %>"
|
||||
class="add-new-h2"
|
||||
><%= __('List of forms' ) %></a>
|
||||
</h2>
|
||||
<% endblock %>
|
||||
|
||||
@ -42,7 +48,7 @@
|
||||
<div id="mailpoet_settings_segment_selection">
|
||||
<!-- Form settings: list selection -->
|
||||
<p>
|
||||
<strong><%= __('This form adds subscribers to these lists:') %></strong>
|
||||
<strong><%= __('This form adds subscribers to these lists:') %></strong>
|
||||
</p>
|
||||
<select
|
||||
id="mailpoet_form_segments"
|
||||
|
@ -57,12 +57,10 @@
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Text') %></div>
|
||||
</div>
|
||||
<div class="mailpoet_form_field">
|
||||
<label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="background-color" class="mailpoet_field_button_background_color mailpoet_color" value="{{ model.styles.block.backgroundColor }}" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Background') %></div>
|
||||
</label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="background-color" class="mailpoet_field_button_background_color mailpoet_color" value="{{ model.styles.block.backgroundColor }}" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Background') %></div>
|
||||
</div>
|
||||
<div class="mailpoet_form_field">
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
|
@ -16,20 +16,16 @@
|
||||
</label>
|
||||
</div>
|
||||
<div class="mailpoet_form_field">
|
||||
<label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="divider-color" class="mailpoet_field_divider_border_color mailpoet_color" value="{{ model.styles.block.borderColor }}" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Divider color') %></div>
|
||||
</label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="divider-color" class="mailpoet_field_divider_border_color mailpoet_color" value="{{ model.styles.block.borderColor }}" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Divider color') %></div>
|
||||
</div>
|
||||
<div class="mailpoet_form_field">
|
||||
<label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="background-color" class="mailpoet_field_divider_background_color mailpoet_color" value="{{ model.styles.block.backgroundColor }}" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Background') %></div>
|
||||
</label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="background-color" class="mailpoet_field_divider_background_color mailpoet_color" value="{{ model.styles.block.backgroundColor }}" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Background') %></div>
|
||||
</div>
|
||||
{{#ifCond renderOptions.hideApplyToAll '!==' true}}
|
||||
<div class="mailpoet_form_field">
|
||||
|
@ -1,29 +1,25 @@
|
||||
<h3><%= __('Footer') %></h3>
|
||||
<div class="mailpoet_form_field">
|
||||
<label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="font-color" id="mailpoet_field_footer_text_color" class="mailpoet_field_footer_text_color mailpoet_color" value="{{ model.styles.text.fontColor }}" />
|
||||
<select id="mailpoet_field_footer_text_font_family" name="font-family" class="mailpoet_select mailpoet_select_medium mailpoet_field_footer_text_font_family mailpoet_font_family">
|
||||
{{#each availableStyles.fonts}}
|
||||
<option value="{{ this }}" {{#ifCond this '==' ../model.styles.text.fontFamily}}SELECTED{{/ifCond}}>{{ this }}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<select id="mailpoet_field_footer_text_size" name="font-size" class="mailpoet_select mailpoet_select_small mailpoet_field_footer_text_size mailpoet_font_size">
|
||||
{{#each availableStyles.textSizes}}
|
||||
<option value="{{ this }}" {{#ifCond this '==' ../model.styles.text.fontSize}}SELECTED{{/ifCond}}>{{ this }}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Text') %></div>
|
||||
</label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="font-color" id="mailpoet_field_footer_text_color" class="mailpoet_field_footer_text_color mailpoet_color" value="{{ model.styles.text.fontColor }}" />
|
||||
<select id="mailpoet_field_footer_text_font_family" name="font-family" class="mailpoet_select mailpoet_select_medium mailpoet_field_footer_text_font_family mailpoet_font_family">
|
||||
{{#each availableStyles.fonts}}
|
||||
<option value="{{ this }}" {{#ifCond this '==' ../model.styles.text.fontFamily}}SELECTED{{/ifCond}}>{{ this }}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<select id="mailpoet_field_footer_text_size" name="font-size" class="mailpoet_select mailpoet_select_small mailpoet_field_footer_text_size mailpoet_font_size">
|
||||
{{#each availableStyles.textSizes}}
|
||||
<option value="{{ this }}" {{#ifCond this '==' ../model.styles.text.fontSize}}SELECTED{{/ifCond}}>{{ this }}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Text') %></div>
|
||||
</div>
|
||||
<div class="mailpoet_form_field">
|
||||
<label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" class="mailpoet_color" size="6" maxlength="6" name="link-color" value="{{ model.styles.link.fontColor }}" id="mailpoet_field_footer_link_color" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Links') %></div>
|
||||
</label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" class="mailpoet_color" size="6" maxlength="6" name="link-color" value="{{ model.styles.link.fontColor }}" id="mailpoet_field_footer_link_color" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Links') %></div>
|
||||
<label>
|
||||
<div class="mailpoet_form_field_checkbox_option mailpoet_option_offset_left_small">
|
||||
<input type="checkbox" name="underline" value="underline" id="mailpoet_field_footer_link_underline" {{#ifCond model.styles.link.textDecoration '==' 'underline'}}CHECKED{{/ifCond}}/> <%= __('Underline') %>
|
||||
@ -32,12 +28,10 @@
|
||||
</div>
|
||||
|
||||
<div class="mailpoet_form_field">
|
||||
<label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="background-color" class="mailpoet_field_footer_background_color mailpoet_color" value="{{ model.styles.block.backgroundColor }}" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Background') %></div>
|
||||
</label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="background-color" class="mailpoet_field_footer_background_color mailpoet_color" value="{{ model.styles.block.backgroundColor }}" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Background') %></div>
|
||||
</div>
|
||||
|
||||
<div class="mailpoet_form_field">
|
||||
|
@ -1,29 +1,25 @@
|
||||
<h3><%= __('Header') %></h3>
|
||||
<div class="mailpoet_form_field">
|
||||
<label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="font-color" id="mailpoet_field_header_text_color" class="mailpoet_field_header_text_color mailpoet_color" value="{{ model.styles.text.fontColor }}" />
|
||||
<select id="mailpoet_field_header_text_font_family" name="font-family" class="mailpoet_select mailpoet_select_medium mailpoet_field_header_text_font_family mailpoet_font_family">
|
||||
{{#each availableStyles.fonts}}
|
||||
<option value="{{ this }}" {{#ifCond this '==' ../model.styles.text.fontFamily}}SELECTED{{/ifCond}}>{{ this }}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<select id="mailpoet_field_header_text_size" name="font-size" class="mailpoet_select mailpoet_select_small mailpoet_field_header_text_size mailpoet_font_size">
|
||||
{{#each availableStyles.textSizes}}
|
||||
<option value="{{ this }}" {{#ifCond this '==' ../model.styles.text.fontSize}}SELECTED{{/ifCond}}>{{ this }}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Text') %></div>
|
||||
</label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="font-color" id="mailpoet_field_header_text_color" class="mailpoet_field_header_text_color mailpoet_color" value="{{ model.styles.text.fontColor }}" />
|
||||
<select id="mailpoet_field_header_text_font_family" name="font-family" class="mailpoet_select mailpoet_select_medium mailpoet_field_header_text_font_family mailpoet_font_family">
|
||||
{{#each availableStyles.fonts}}
|
||||
<option value="{{ this }}" {{#ifCond this '==' ../model.styles.text.fontFamily}}SELECTED{{/ifCond}}>{{ this }}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
<select id="mailpoet_field_header_text_size" name="font-size" class="mailpoet_select mailpoet_select_small mailpoet_field_header_text_size mailpoet_font_size">
|
||||
{{#each availableStyles.textSizes}}
|
||||
<option value="{{ this }}" {{#ifCond this '==' ../model.styles.text.fontSize}}SELECTED{{/ifCond}}>{{ this }}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Text') %></div>
|
||||
</div>
|
||||
<div class="mailpoet_form_field">
|
||||
<label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" class="mailpoet_color" size="6" maxlength="6" name="link-color" value="{{ model.styles.link.fontColor }}" id="mailpoet_field_header_link_color" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Links') %></div>
|
||||
</label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" class="mailpoet_color" size="6" maxlength="6" name="link-color" value="{{ model.styles.link.fontColor }}" id="mailpoet_field_header_link_color" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Links') %></div>
|
||||
<label>
|
||||
<div class="mailpoet_form_field_checkbox_option mailpoet_option_offset_left_small">
|
||||
<input type="checkbox" name="underline" value="underline" id="mailpoet_field_header_link_underline" {{#ifCond model.styles.link.textDecoration '==' 'underline'}}CHECKED{{/ifCond}}/> <%= __('Underline') %>
|
||||
@ -32,12 +28,10 @@
|
||||
</div>
|
||||
|
||||
<div class="mailpoet_form_field">
|
||||
<label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="background-color" class="mailpoet_field_header_background_color mailpoet_color" value="{{ model.styles.block.backgroundColor }}" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Background') %></div>
|
||||
</label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="background-color" class="mailpoet_field_header_background_color mailpoet_color" value="{{ model.styles.block.backgroundColor }}" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Background') %></div>
|
||||
</div>
|
||||
|
||||
<div class="mailpoet_form_field">
|
||||
|
@ -1,11 +1,9 @@
|
||||
<h3><%= __('Spacer') %></h3>
|
||||
<div class="mailpoet_form_field">
|
||||
<label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="background-color" class="mailpoet_field_spacer_background_color mailpoet_color" value="{{ styles.block.backgroundColor }}" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Background') %></div>
|
||||
</label>
|
||||
<div class="mailpoet_form_field_input_option">
|
||||
<input type="text" name="background-color" class="mailpoet_field_spacer_background_color mailpoet_color" value="{{ styles.block.backgroundColor }}" />
|
||||
</div>
|
||||
<div class="mailpoet_form_field_title mailpoet_form_field_title_inline"><%= __('Background') %></div>
|
||||
</div>
|
||||
|
||||
<div class="mailpoet_form_field">
|
||||
|
@ -15,7 +15,7 @@
|
||||
{{#each availableStyles.textSizes}}
|
||||
<option value="{{ this }}" {{#ifCond this '==' ../model.text.fontSize}}SELECTED{{/ifCond}}>{{ this }}</option>
|
||||
{{/each}}
|
||||
</select> <label for="mailpoet_text_font_color"><%= __('Text') %></label>
|
||||
</select> <%= __('Text') %>
|
||||
</div>
|
||||
<div class="mailpoet_form_field">
|
||||
<span>
|
||||
@ -30,7 +30,7 @@
|
||||
{{#each availableStyles.headingSizes}}
|
||||
<option value="{{ this }}" {{#ifCond this '==' ../model.h1.fontSize}}SELECTED{{/ifCond}}>{{ this }}</option>
|
||||
{{/each}}
|
||||
</select> <label for="mailpoet_h1_font_color"><%= __('Heading 1') %></label>
|
||||
</select> <%= __('Heading 1') %>
|
||||
</div>
|
||||
<div class="mailpoet_form_field">
|
||||
<span>
|
||||
@ -45,7 +45,7 @@
|
||||
{{#each availableStyles.headingSizes}}
|
||||
<option value="{{ this }}" {{#ifCond this '==' ../model.h2.fontSize}}SELECTED{{/ifCond}}>{{ this }}</option>
|
||||
{{/each}}
|
||||
</select> <label for="mailpoet_h2_font_color"><%= __('Heading 2') %></label>
|
||||
</select> <%= __('Heading 2') %>
|
||||
</div>
|
||||
<div class="mailpoet_form_field">
|
||||
<span>
|
||||
@ -60,23 +60,23 @@
|
||||
{{#each availableStyles.headingSizes}}
|
||||
<option value="{{ this }}" {{#ifCond this '==' ../model.h3.fontSize}}SELECTED{{/ifCond}}>{{ this }}</option>
|
||||
{{/each}}
|
||||
</select> <label for="mailpoet_h3_font_color"><%= __('Heading 3') %></label>
|
||||
</select> <%= __('Heading 3') %>
|
||||
</div>
|
||||
<div class="mailpoet_form_field">
|
||||
<span>
|
||||
<span><input type="text" class="mailpoet_color" size="6" maxlength="6" name="link-color" value="{{ model.link.fontColor }}" id="mailpoet_a_font_color"></span>
|
||||
</span><label for="mailpoet_a_font_color"><%= __('Links') %></label> <input type="checkbox" name="underline" value="underline" id="mailpoet_a_font_underline" {{#ifCond model.link.textDecoration '==' 'underline'}}CHECKED{{/ifCond}} class="mailpoet_option_offset_left_small"/> <%= __('Underline') %>
|
||||
</span><%= __('Links') %> <label><input type="checkbox" name="underline" value="underline" id="mailpoet_a_font_underline" {{#ifCond model.link.textDecoration '==' 'underline'}}CHECKED{{/ifCond}} class="mailpoet_option_offset_left_small"/> <%= __('Underline') %></label>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="mailpoet_form_field">
|
||||
<span>
|
||||
<span><input type="text" class="mailpoet_color" size="6" maxlength="6" name="newsletter-color" value="{{ model.wrapper.backgroundColor }}" id="mailpoet_newsletter_background_color"></span>
|
||||
</span><label for="mailpoet_newsletter_background_color"><%= __('Newsletter') %></label>
|
||||
</span><%= __('Newsletter') %>
|
||||
</div>
|
||||
<div class="mailpoet_form_field">
|
||||
<span>
|
||||
<span><input type="text" class="mailpoet_color" size="6" maxlength="6" name="background-color" value="{{ model.body.backgroundColor }}" id="mailpoet_background_color"></span>
|
||||
</span><label for="mailpoet_background_color"><%= __('Background') %></label>
|
||||
</span><%= __('Background') %>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
10
views/queue.html
Normal file
10
views/queue.html
Normal file
@ -0,0 +1,10 @@
|
||||
<% extends 'layout.html' %>
|
||||
|
||||
<% block content %>
|
||||
<div id="queue_container"></div>
|
||||
<script type="text/javascript">
|
||||
</script>
|
||||
<script>
|
||||
var queueDaemon = <%= daemon|raw %>
|
||||
</script>
|
||||
<% endblock %>
|
@ -123,7 +123,7 @@
|
||||
exportData = {
|
||||
segments: segments.length || null,
|
||||
segmentsWithConfirmedSubscribers: segmentsWithConfirmedSubscribers.length || null,
|
||||
exportConfirmedOption: false,
|
||||
exportConfirmedOption: true,
|
||||
groupBySegmentOption: (segments.length > 1 || segmentsWithConfirmedSubscribers.length > 1) ? true : null
|
||||
};
|
||||
</script>
|
@ -1,5 +1,4 @@
|
||||
<% extends 'layout.html' %>
|
||||
|
||||
<% block content %>
|
||||
<div id="mailpoet_subscribers_import" class="wrap">
|
||||
<h2 class="title">
|
||||
@ -7,11 +6,11 @@
|
||||
<a class="add-new-h2" href="?page=mailpoet-subscribers#/"><%= __('Back to list') %></a>
|
||||
</h2>
|
||||
<!-- STEP 1: method selection -->
|
||||
<% include 'import/step1.html' %>
|
||||
<% include 'subscribers/importExport/import/step1.html' %>
|
||||
<!-- STEP 2: subscriber data manipulation -->
|
||||
<% include 'import/step2.html' %>
|
||||
<% include 'subscribers/importExport/import/step2.html' %>
|
||||
<!-- STEP 3: results -->
|
||||
<% include 'import/step3.html' %>
|
||||
<% include 'subscribers/importExport/import/step3.html' %>
|
||||
</div>
|
||||
|
||||
<%= stylesheet('importExport.css') %>
|
||||
@ -64,6 +63,7 @@
|
||||
<script type="text/javascript">
|
||||
var
|
||||
maxPostSize = '<%= maxPostSize %>',
|
||||
maxPostSizeBytes = '<%= maxPostSizeBytes %>',
|
||||
importData = {},
|
||||
mailpoetColumnsSelect2 = <%= subscriberFieldsSelect2|raw %>,
|
||||
mailpoetColumns = <%= subscriberFields|raw %>,
|
@ -29,7 +29,8 @@ baseConfig = {
|
||||
'blob$': 'blob/Blob.js',
|
||||
'filesaver$': 'filesaver/FileSaver.js',
|
||||
'papaparse': 'papaparse/papaparse.min.js',
|
||||
'helpscout': 'helpscout.js'
|
||||
'helpscout': 'helpscout.js',
|
||||
'html2canvas': 'html2canvas/dist/html2canvas.js'
|
||||
},
|
||||
},
|
||||
node: {
|
||||
@ -73,6 +74,10 @@ baseConfig = {
|
||||
include: /helpscout.js$/,
|
||||
loader: 'exports-loader?window.HS',
|
||||
},
|
||||
{
|
||||
include: /html2canvas.js$/,
|
||||
loader: 'expose-loader?html2canvas',
|
||||
},
|
||||
]
|
||||
}
|
||||
};
|
||||
@ -95,9 +100,10 @@ config.push(_.extend({}, baseConfig, {
|
||||
'segments/segments.jsx',
|
||||
'forms/forms.jsx',
|
||||
'settings/tabs.js',
|
||||
'import/import.js',
|
||||
'export/export.js',
|
||||
'helpscout'
|
||||
'subscribers/importExport/import.js',
|
||||
'subscribers/importExport/export.js',
|
||||
'helpscout',
|
||||
'queue.jsx'
|
||||
],
|
||||
form_editor: [
|
||||
'form_editor/form_editor.js',
|
||||
|
Reference in New Issue
Block a user