Compare commits

...

154 Commits

Author SHA1 Message Date
1d756e95a7 Bump up release version to 0.0.41 2016-08-23 12:27:14 +03:00
1fb0da9fda Merge pull request #587 from mailpoet/open_stats_fix
Prevents tracking opens/clicks from subscribers who the newsletter was not sent to
2016-08-23 12:17:51 +03:00
a0017b91ee Merge pull request #592 from mailpoet/editor_fixes
Editor fixes
2016-08-22 20:43:24 -04:00
444ab17342 - Updates statistics tracking unit tests 2016-08-22 16:24:33 -04:00
44f3058326 - Adds unit test for tracker router endpoint 2016-08-22 16:24:13 -04:00
ec09fbcb78 - Converts static classes to dynamic 2016-08-22 16:22:33 -04:00
ed352bb1d3 - Passes wp_user_preview parameter to custom link shortcode filter 2016-08-22 09:26:25 -04:00
375bbd2759 - Calls an open tracking class when tracking clicks 2016-08-22 09:26:25 -04:00
9fb9d25132 - Uses model method to get queue 2016-08-22 09:26:25 -04:00
30f79aa589 - Updates unsubscribe tracking logic 2016-08-22 09:26:25 -04:00
69f8daac95 - Updates wp user & preview check condition 2016-08-22 09:26:25 -04:00
03f3a6080c - Returns empty response or an image in all cases 2016-08-22 09:26:25 -04:00
44f84c6cdb - Updates method names 2016-08-22 09:26:25 -04:00
31008a6895 - Updates queue and subscriber check condition 2016-08-22 09:26:25 -04:00
2490d8c919 - Refactors browser preview 2016-08-22 09:26:25 -04:00
5886dbfd25 - Refactors statistics tracking 2016-08-22 09:26:25 -04:00
e48d55f0b1 - Adds new model methods 2016-08-22 09:26:25 -04:00
42339927cf - Extends ORM's isNew() method to work on saved models 2016-08-22 09:26:25 -04:00
b492bcecc0 - Removes requirement of passing newsletter id when tracking clicks
- Extracts common tracking data processing/validation code into the Track
  class
- Refactors Clicks, Opens and View in Browser classes to enforce
  subscriber id and token check
- Allows admin users to preview newsletters without tracking statistics
2016-08-22 09:26:25 -04:00
6ab7debb7b - Fixes code formatting 2016-08-22 09:26:25 -04:00
b76ce6c26f - Adds verification of newsletter-to-queue and subscriber-to-queue
- Prevents tracking open/click rates for subscribers who the newsletter
  was not sent to
2016-08-22 09:26:25 -04:00
6fbc7b1593 Add "View in browser" to happen on the same page, instead of opening new
window
2016-08-19 18:48:29 +03:00
69c8670b01 Add an option to open settings by clicking on the block for image,
button, social, divider and spacer blocks
2016-08-19 14:57:14 +03:00
da44a87415 Change onkeyup to oninput events to correctly detect pasting into
inputs
2016-08-18 16:55:53 +03:00
9fb17d4a6b Fix "Preview in browser" notice to display a proper success message 2016-08-18 15:07:20 +03:00
16dd286f9d Merge pull request #591 from mailpoet/missing_response
missing response in fail and prevent next on MC import
2016-08-18 12:07:05 +03:00
5025f10f9f missing response in fail and prevent next on MC import 2016-08-17 16:15:54 +02:00
1278d9648c Merge pull request #590 from mailpoet/api_uniform_c
Custom fields & Import & Export
2016-08-17 16:18:53 +03:00
289811a595 Updated Import & MailChimp tests 2016-08-17 13:32:29 +02:00
916ae97f73 Updated export unit test 2016-08-17 12:51:50 +02:00
876e386966 converted export 2016-08-17 12:23:15 +02:00
9582e58dda converted import 2016-08-17 12:16:58 +02:00
213bca8050 fixed rendering of date block + fixed validation for dates 2016-08-16 14:41:53 +02:00
dc97d3115e updated Custom Field endpoint + Unit tests + form editor update 2016-08-16 12:40:10 +02:00
90eb443965 Merge pull request #582 from mailpoet/custom_field_fix
Custom field fix
2016-08-16 12:00:28 +02:00
1b40f02715 Bump up release version to 0.0.40 2016-08-16 12:30:53 +03:00
c5a02c6136 - Allows setting empty value for date custom fields 2016-08-14 13:00:28 -04:00
492cd8c96b Merge pull request #583 from mailpoet/editor_fixes
Editor fixes
2016-08-12 16:40:08 +02:00
7f091d7188 - Fixes rebase screwup 2016-08-12 10:38:15 -04:00
1c081623b9 When there's no social icon image URL, revert to "Image not found" image 2016-08-12 17:30:11 +03:00
87332037c2 Do not render images or social icons if image src is not defined 2016-08-12 16:18:44 +03:00
62023397f4 Remove example URLs from social icons, leave placeholders 2016-08-12 15:47:53 +03:00
37ec6dc1a6 Fix handling of images with empty src in newsletter editor 2016-08-12 14:40:21 +03:00
81c277ca93 - Update import to autodetect dates (UI) and convert them to datetime
format (backend)
- Fixes unit test
- Fixes code formatting in Date class
2016-08-12 00:29:57 -04:00
f8fea75130 - Updates date conversion method
- Uses Moment to parse dates in UI
- Updates Custom Field model to utilize date conversion method
- Adds unit test
2016-08-11 21:11:57 -04:00
a4457649f7 Fix saving on last newsletter step to JSON encode newsletter body 2016-08-11 18:58:55 +03:00
1d6a09f010 Modify ALC to ignore posts published before notification newsletter is
created
2016-08-11 16:46:47 +03:00
faec553521 Enable closing of a sidebar section in newsletter editor 2016-08-11 16:46:47 +03:00
37fcf3a234 Fix newsletter template titles to not overlap Delete controls 2016-08-11 16:46:47 +03:00
68a56aada8 Fix default footer to allow toggling bold and italic on sentences 2016-08-11 16:46:47 +03:00
f744305834 - Change default URLs to blank;
- Fix image, button, social icon URL placeholders;
- Remove links to example.org.
2016-08-11 16:46:47 +03:00
7a9402f5b5 Merge pull request #586 from mailpoet/api_uniform_f
updated cron endpoint + cron.jsx
2016-08-11 16:42:31 +03:00
de6d7e0cae updated cron endpoint + cron.jsx 2016-08-11 12:36:17 +02:00
a3c56b84ce Merge pull request #585 from mailpoet/api_uniform_e
Api uniform (NewsletterTemplates)
2016-08-11 12:34:16 +03:00
3d4defd563 Fixed unit tests for newletter templates
- changed null to false for default value of id in nltemplates endpoint
2016-08-10 16:51:04 +02:00
52da08abb2 converted newsletterTemplates endpoint + react 2016-08-10 15:50:07 +02:00
b9637b52e9 Merge pull request #584 from mailpoet/api_uniform_d
Api uniform (Mailer & SendingQueue)
2016-08-10 15:04:17 +03:00
0369a23fe8 Call different actions for standard and automated newsletters on Send step
- converted save and setStatus method of Newsletters endpoint
- updated pause/resume mixin for notification & welcome listings
- use 'newsletter_id' instead of 'id' in SendingQueue endpoint (less confusing)
- added NOT_FOUND constant to APIError
- fixed unit test for Newsletters endpoint save and added test for setStatus
2016-08-10 13:09:35 +02:00
f690e1a095 return sending queue object for standard / newsletter for automated\n- bugfix loading screen not going away\n- fixed not being used in sending queue endpoint 2016-08-10 13:09:35 +02:00
22e8e34213 unify getData() response 2016-08-10 13:09:35 +02:00
12b46736c5 updated sending queue endpoint + react (pause/resume/send) 2016-08-10 13:09:35 +02:00
4950e47297 updated mailer endpoint 2016-08-10 13:09:35 +02:00
d7c5c8c3e7 Bump up release version to 0.0.39 2016-08-09 12:09:38 +03:00
46b0fcf37b - Adds subscriber data validation; specifically for custom fields with
date type
2016-08-08 18:23:11 -04:00
b07c4d0e6e - Adds date validation based on date format 2016-08-08 18:23:11 -04:00
eb107799a7 - Fixes varable name typo
- Declares a new variable during date validate
2016-08-08 18:23:11 -04:00
4eec0a42f9 - Forces date validation to use custom custom field parameter 2016-08-08 18:23:11 -04:00
9a5a3a08c6 - Fixes default date format not being set 2016-08-08 18:23:11 -04:00
151683c632 - Updates Import to use the existing custom field creation code
- Closes #499
2016-08-08 18:23:11 -04:00
fd2103d1aa - Extracts custom field specific templates/code into a separate template
file
2016-08-08 18:23:11 -04:00
7696b6ec5d - Replaces depreciated Notice .error() method with .fail() 2016-08-08 18:23:11 -04:00
d972b96255 - Updates custom field editor to display errors inside the form 2016-08-08 18:23:11 -04:00
35ccfb8bcf Merge pull request #564 from mailpoet/cron_update
Cron update
2016-08-08 16:59:27 +03:00
7ff036b1e9 - Removes depreciated variables
- Updates method names
2016-08-08 09:45:16 -04:00
983d56c29b - Updates default cron trigger method in Populator and Settings 2016-08-08 09:07:01 -04:00
a2528939ba Merge pull request #581 from mailpoet/api_uniform_b
updated ALC endpoint + nl editor + js tests
2016-08-08 15:00:26 +03:00
c136d91dd2 encapsulating in communication component + update js tests 2016-08-08 11:36:29 +02:00
bf00e82596 - Fixes sending limits not being enforced 2016-08-07 11:39:05 -04:00
0e10f6c820 - Fixes merge conflict 2016-08-05 13:03:31 -04:00
c056e95249 - Rebases master
- Updates sending limit logic
2016-08-05 13:03:31 -04:00
1be7fda1cf - Updates daemon request timeouts 2016-08-05 13:03:31 -04:00
0b0c0f5759 - Fixes conditional statement 2016-08-05 13:03:31 -04:00
59a4428965 - Fixes class naming conflict 2016-08-05 13:03:31 -04:00
3f5c36d2d4 - Fixes blocking HTTP request issue
- Simplifies cron supervisor
2016-08-05 13:03:31 -04:00
3cc5812c1d - Removes exception throwing that can disable plugin 2016-08-05 13:03:31 -04:00
3e616201ad - Encasupsulates trigger methods logic into separate classes
- Updates cron router
2016-08-05 13:03:31 -04:00
5558ebad45 - Updates the name of the setting const 2016-08-05 13:03:31 -04:00
63bd093f35 - Renames TaskScheduler to CronTrigger and updates relevant code
- Standardizes setting value const naming convention
2016-08-05 13:03:31 -04:00
ec6559b8be - Removes unused method from Settings model
- Renames method to delete value
2016-08-05 13:03:31 -04:00
3421406dc7 - Removes unused const 2016-08-05 13:03:31 -04:00
a2917c08f6 - Switches methods used to get cron settings 2016-08-05 13:03:31 -04:00
5fa9b5a8dd - Updates method name that returns mailer configuration 2016-08-05 13:03:31 -04:00
41ad86ba1f - Fixes code style 2016-08-05 13:03:31 -04:00
067b3ff3e6 - Updates Mailer class to use default values from Settings 2016-08-05 13:03:31 -04:00
9b9cb1455a - Updates cron/mailer/scheduler code to work with the new sending queue
task scheduler and mailer log
2016-08-05 13:03:31 -04:00
a5569a6a55 - Adds new sendiing queue task responsible for managing task scheduler 2016-08-05 13:03:31 -04:00
71c1026729 - Adds new class responsible for managing mailer log 2016-08-05 13:03:31 -04:00
f102e847bf - Stops cron daemon when settings are changed from MailPoet to WordPress
task scheduler
2016-08-05 13:03:31 -04:00
3158e2c460 - Updates cron router to properly return the daemon status 2016-08-05 13:03:31 -04:00
a438f13bb0 - Modifies cron router/UI to display proper status message when WP task
scheduler is configured and cron is not running
- Updates sending queue worker and related components to stop (delete)
  cron when all processing is done
2016-08-05 13:03:31 -04:00
5ed0a5819c - Updates settings to use task scheduler method names from the newly
introduced config class
2016-08-05 13:03:31 -04:00
6dd3c6acda - Adds new task scheduler configuration class
- Introduces method to start cron on demand when there are
  scheduled newsletters or queues in progress
2016-08-05 13:03:31 -04:00
ca2c1c2e6f Merge pull request #580 from mailpoet/alc_posts
ALC Post filtering
2016-08-05 11:53:55 -04:00
1305a10ee0 updated ALC endpoint + nl editor + js tests 2016-08-05 15:25:54 +02:00
5e36eb818b Fix sending first notification newsletter with ALC posts 2016-08-05 13:40:54 +03:00
cfde82ff5f Declaring class properties of AutomatedLatestContent 2016-08-04 18:32:30 +03:00
af98ade650 Add ALC filter to use posts created after last newsletter was sent 2016-08-04 18:17:49 +03:00
598432466e - Rename wysija-newsetters.pot to mailpoet.pot
- Add pot file regeneration on plugin build
2016-08-04 18:16:43 +03:00
9469ce83f1 Fix creation and update timestamps for notification history newsletters 2016-08-04 18:16:43 +03:00
5624f4c7a0 Add new Robo task to run all QA tasks in one go 2016-08-04 18:16:43 +03:00
82a001dc05 Add timestamp based ALC filter to ignore older posts 2016-08-04 18:16:43 +03:00
a9b424fb79 Merge pull request #579 from mailpoet/notification_history_preview
Enables preview of notification history newsletters
2016-08-04 18:12:50 +03:00
d31af9d71c - Adds additional logic to not exclude sent posts when previewing from
within the newsletter editor
2016-08-04 10:30:53 -04:00
ff7a24590f - Removes unnecessary condition 2016-08-04 09:46:05 -04:00
ea87a7acf8 - Fixes code style
- Removes unnecessary condition
- Closes #576
2016-08-04 09:36:15 -04:00
9d36a17261 - Fixes preview of newsletters with ALC 2016-08-03 21:36:03 -04:00
9c3cb5a509 - Enables preview of notification history newsletters 2016-08-03 20:42:35 -04:00
4dd7f32f3a Merge pull request #578 from mailpoet/api_uniform
Api unification - Step 1
2016-08-03 17:27:09 +03:00
1c6fca7f83 fixed ErrorResponse call in API::setupPublic()
- removed empty constructors in updated endpoints
- added missing keys to Error class (unauthorized, forbidden)
2016-08-03 16:04:45 +02:00
c5b376bd21 satisfy code sniffer rule 2016-08-03 15:08:27 +02:00
5d2800bc25 added API/Error class to hold error keys as constants
- re-added Setting::getAll() to API/Setting::set() in response
- updated settings/setup tests
2016-08-03 15:04:25 +02:00
6675d5a20d added default error messages to errorResponse and badRequest 2016-08-03 14:08:22 +02:00
28c39d301c Added default error response in case no errors were specified
- converted Setup endpoint
- unit tests for Setup endpoint
2016-08-03 12:41:21 +02:00
afa0d3af63 Updated Ajax.js to avoid promise workaround
- Removed get method in ajax.js as it's useless
2016-08-02 18:08:12 +02:00
b05344b1d3 added missing data in deferred.resolve() 2016-08-02 17:18:18 +02:00
2e88d7cce0 Added API/Endpoint abstract class
- (re)Added Endpoints folder to both API and Router
- fixed syntax in namespaces
- xhr.responseJSON is returned to the fail()
- fixed Router endpoints (view in browser, cron,...)
2016-08-02 17:08:43 +02:00
cb558ce2ab Bump release version up to 0.0.38 2016-08-02 16:49:11 +03:00
ed30d8f639 externalize Success/ErrorResponse classes into their own files 2016-08-01 17:22:23 +02:00
9410d4f10a Reorganized new API + added legacy API support + new API
- Updated Settings Router to new standards
- Updated settings.html to reflect API change with better error handling
- Updated Settings API unit tests
2016-08-01 17:00:32 +02:00
354d249e1d Moved current Router files to API
- updated Unit tests to reflect the change
2016-08-01 17:00:32 +02:00
008fdb94c5 Moved lib/API to lib/Router
- renamed lib/API/API.php to lib/Router/Front.php
- updated namespaces in various file to account for namespace change
2016-08-01 17:00:32 +02:00
d0fb94b3f8 Merge pull request #577 from mailpoet/newsletter_templates
Newsletter templates
2016-07-29 16:23:12 +02:00
a451f00ed3 Remove obsolete function 2016-07-29 17:21:14 +03:00
0e0c371b28 Swap Preview and Select buttons in template select page 2016-07-29 17:15:20 +03:00
2e52f3bb92 Add a "Preview" button in template select page 2016-07-29 17:11:30 +03:00
a3a5016278 Allow template titles to be displayed in multiple lines w\o ellipsis 2016-07-29 16:56:07 +03:00
cb5d7cb9a0 Fix incorrect post notification template logo URL 2016-07-29 16:52:55 +03:00
a44d4ed0b5 Add new newsletter sample templates 2016-07-29 16:29:44 +03:00
7bd23288f6 Add newsletter blank 1 column template 2016-07-29 16:28:44 +03:00
08c663759c Merge pull request #574 from mailpoet/copy-edit
Copy edit
2016-07-29 13:34:57 +03:00
c46ee07674 Change form editor page title, swap "New" to "Add New" 2016-07-29 13:30:17 +03:00
18398a3bfb Finish changing segments to lists and columns to fields 2016-07-29 13:30:17 +03:00
88d9315f8b Finish converting Segment to List in language strings 2016-07-29 13:30:17 +03:00
8bc95db0c9 Change items to item(s), fix Twig syntax errors 2016-07-29 13:29:05 +03:00
c05a20cff9 Update 28 July 2016 2016-07-29 13:29:05 +03:00
08cb994252 Merge pull request #573 from mailpoet/text_version_fix
Fixes link rendering in text version of the newsletter
2016-07-28 16:17:13 +03:00
543ad81e28 Merge pull request #575 from mailpoet/archives_page
Newsletter Archives page
2016-07-28 15:23:50 +03:00
641ba04685 Added Newsletter::getArchives() in order to return proper archives
- Archives page: replaced created_at by processed_at as the issue date
2016-07-28 11:52:56 +02:00
8e4d07c658 - Updates regex to not match http/ftp links as shortcodes
- Updates regex to properly replace links in text version of newsletter
2016-07-27 21:57:55 -04:00
420650f37f Merge pull request #572 from mailpoet/error_handling
Display plugin initialization errors to admin
2016-07-27 15:38:57 +02:00
775f7faee4 Declare private properties and swap strings for named constants 2016-07-27 16:35:21 +03:00
13b91ad051 Encapsulate showing WP notices into a separate Notice class 2016-07-27 15:07:09 +03:00
2d3ec13473 Stringify exception to include the stack trace as well 2016-07-27 13:58:44 +03:00
3094cfc076 Prevent plugin from disabling itself on error, display that error to
admin instead
2016-07-26 16:18:33 +03:00
185 changed files with 7993 additions and 5468 deletions

2
.gitignore vendored
View File

@ -12,7 +12,7 @@ npm-debug.log
/views/cache/**
temp
.idea
wysija-newsletters.zip
mailpoet.zip
tests/javascript/testBundles
assets/css/*.css
assets/js/*.js

View File

@ -141,8 +141,13 @@ class RoboFile extends \Robo\Tasks {
$this->_exec('vendor/bin/codecept run -g failed');
}
function qa() {
$this->qaLint();
$this->qaCodeSniffer('all');
}
function qaLint() {
$this->_exec('./tasks/php_lint.sh lib/ tests/');
$this->_exec('./tasks/php_lint.sh lib/ tests/ mailpoet.php');
}
function qaCodeSniffer($severity='errors') {

View File

@ -65,11 +65,10 @@
overflow: hidden
.mailpoet_boxes .mailpoet_description h3
margin: 0 0 1em 0
margin: 0 0 0.7em 0
overflow: hidden
white-space: nowrap
text-overflow: ellipsis
max-width: 220px
max-width: 210px
line-height: 1.4em
.mailpoet_boxes .mailpoet_actions
position: absolute

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -8,19 +8,11 @@ define('ajax', ['mailpoet', 'jquery', 'underscore'], function(MailPoet, jQuery,
endpoint: null,
action: null,
token: null,
data: {},
onSuccess: function(data, textStatus, xhr) {},
onError: function(xhr, textStatus, errorThrown) {}
},
get: function(options) {
return this.request('get', options);
data: {}
},
post: function(options) {
return this.request('post', options);
},
delete: function(options) {
return this.request('delete', options);
},
init: function(options) {
// merge options
this.options = jQuery.extend({}, this.defaults, options);
@ -50,7 +42,7 @@ define('ajax', ['mailpoet', 'jquery', 'underscore'], function(MailPoet, jQuery,
// set request params
var params = this.getParams();
var jqXHR;
var deferred = jQuery.Deferred();
// remove null values from the data object
if (_.isObject(params.data)) {
@ -59,29 +51,22 @@ define('ajax', ['mailpoet', 'jquery', 'underscore'], function(MailPoet, jQuery,
})
}
// make ajax request depending on method
if(method === 'get') {
jqXHR = jQuery.get(
this.options.url,
params,
this.options.onSuccess,
'json'
);
} else {
jqXHR = jQuery.ajax({
url: this.options.url,
type : 'post',
data: params,
dataType: 'json',
success : this.options.onSuccess,
error : this.options.onError
});
}
// ajax request
deferred = jQuery.post(
this.options.url,
params,
null,
'json'
).then(function(data) {
return data;
}, function(xhr) {
return xhr.responseJSON;
});
// clear options
this.options = {};
return jqXHR;
return deferred;
}
};
});

View File

@ -19,16 +19,15 @@ define(
MailPoet.Ajax.post({
endpoint: 'cron',
action: 'getStatus'
})
.done(function(response) {
jQuery('.button-primary')
.removeClass('disabled');
if(response.status !== undefined) {
this.setState(response);
} else {
this.replaceState();
}
}.bind(this));
}).done((response) => {
this.setState({
status: response.data.status
});
}).fail((response) => {
this.setState({
status: false
});
});
},
componentDidMount: function() {
if(this.isMounted()) {
@ -36,56 +35,29 @@ define(
setInterval(this.getCronData, 5000);
}
},
controlCron: function(action) {
if(jQuery('.button-primary').hasClass('disabled')) {
return;
}
jQuery('.button-primary')
.addClass('disabled');
MailPoet.Ajax.post({
endpoint: 'cron',
action: action,
})
.done(function(response) {
if(!response.result) {
MailPoet.Notice.error(MailPoet.I18n.t('daemonControlError'));
}
}.bind(this));
},
render: function() {
if(this.state.status === 'loading') {
return(<div>{MailPoet.I18n.t('loadingDaemonStatus')}</div>);
}
let status;
switch(this.state.status) {
case 'started':
return(
<div>
{MailPoet.I18n.t('cronDaemonIsRunning')}
<br/>
<br/>
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'stop')}>{MailPoet.I18n.t('stop')}</a>
</div>
);
break;
case 'starting':
case false:
case 'stopping':
return(
<div>
{MailPoet.I18n.t('cronDaemonState').replace('%$1s', this.state.status)}
</div>
);
break;
case 'stopped':
return(
<div>
{MailPoet.I18n.t('cronDaemonState').replace('%$1s', this.state.status)}
<br />
<br />
<a href="#" className="button-primary" onClick={this.controlCron.bind(null, 'start')}>{MailPoet.I18n.t('start')}</a>
</div>
);
break;
status = MailPoet.I18n.t('cronDaemonIsNotRunning');
break;
case 'starting':
case 'started':
status = MailPoet.I18n.t('cronDaemonIsRunning');
break;
case 'loading':
status = MailPoet.I18n.t('loadingDaemonStatus');
break;
}
return (
<div>
{ status }
</div>
);
}
});

View File

@ -127,37 +127,12 @@ define([
return;
}
const dateType = this.props.field.params.date_type;
const dateParts = value.split('-');
let year = '';
let month = '';
let day = '';
switch(dateType) {
case 'year_month_day':
year = ~~(dateParts[0]);
month = ~~(dateParts[1]);
day = ~~(dateParts[2]);
break;
case 'year_month':
year = ~~(dateParts[0]);
month = ~~(dateParts[1]);
break;
case 'month':
month = ~~(dateParts[0]);
break;
case 'year':
year = ~~(dateParts[0]);
break;
}
const dateTime = Moment(value);
this.setState({
year: year,
month: month,
day: day
year: dateTime.format('YYYY'),
month: dateTime.format('M'),
day: dateTime.format('D')
});
}
formatValue() {
@ -228,7 +203,7 @@ define([
const fields = dateSelects.map(type => {
switch(type) {
case 'yyyy':
case 'YYYY':
return (<FormFieldDateYear
onValueChange={ this.onValueChange.bind(this) }
ref={ 'year' }
@ -239,7 +214,7 @@ define([
/>);
break;
case 'mm':
case 'MM':
return (<FormFieldDateMonth
onValueChange={ this.onValueChange.bind(this) }
ref={ 'month' }
@ -251,7 +226,7 @@ define([
/>);
break;
case 'dd':
case 'DD':
return (<FormFieldDateDay
onValueChange={ this.onValueChange.bind(this) }
ref={ 'day' }

View File

@ -0,0 +1,31 @@
/**
* Show Settings Behavior
*
* Opens up settings of a BlockView if contents are clicked upon
*/
define([
'backbone.marionette',
'jquery',
'newsletter_editor/behaviors/BehaviorsLookup',
], function(Marionette, jQuery, BehaviorsLookup) {
BehaviorsLookup.ShowSettingsBehavior = Marionette.Behavior.extend({
defaults: {
ignoreFrom: '', // selector
},
events: {
'click .mailpoet_content': 'showSettings',
},
showSettings: function(event) {
if(!this.isIgnoredElement(event.target)) {
this.view.triggerMethod('showSettings');
}
},
isIgnoredElement: function(element) {
return this.options.ignoreFrom
&& this.options.ignoreFrom.length > 0
&& jQuery(element).is(this.options.ignoreFrom);
},
});
});

View File

@ -180,17 +180,17 @@ define([
"change .mailpoet_automated_latest_content_title_format": 'changeTitleFormat',
"change .mailpoet_automated_latest_content_title_as_links": _.partial(this.changeBoolField, 'titleIsLink'),
"change .mailpoet_automated_latest_content_show_divider": _.partial(this.changeBoolField, 'showDivider'),
"keyup .mailpoet_automated_latest_content_show_amount": _.partial(this.changeField, "amount"),
"input .mailpoet_automated_latest_content_show_amount": _.partial(this.changeField, "amount"),
"change .mailpoet_automated_latest_content_content_type": _.partial(this.changeField, "contentType"),
"change .mailpoet_automated_latest_content_include_or_exclude": _.partial(this.changeField, "inclusionType"),
"change .mailpoet_automated_latest_content_title_alignment": _.partial(this.changeField, "titleAlignment"),
"change .mailpoet_automated_latest_content_image_full_width": _.partial(this.changeBoolField, "imageFullWidth"),
"change .mailpoet_automated_latest_content_featured_image_position": _.partial(this.changeField, "featuredImagePosition"),
"change .mailpoet_automated_latest_content_show_author": _.partial(this.changeField, "showAuthor"),
"keyup .mailpoet_automated_latest_content_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"),
"input .mailpoet_automated_latest_content_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"),
"change .mailpoet_automated_latest_content_show_categories": _.partial(this.changeField, "showCategories"),
"keyup .mailpoet_automated_latest_content_categories": _.partial(this.changeField, "categoriesPrecededBy"),
"keyup .mailpoet_automated_latest_content_read_more_text": _.partial(this.changeField, "readMoreText"),
"input .mailpoet_automated_latest_content_categories": _.partial(this.changeField, "categoriesPrecededBy"),
"input .mailpoet_automated_latest_content_read_more_text": _.partial(this.changeField, "readMoreText"),
"change .mailpoet_automated_latest_content_sort_by": _.partial(this.changeField, "sortBy"),
"click .mailpoet_done_editing": "close",
};
@ -217,8 +217,10 @@ define([
};
},
transport: function(options, success, failure) {
var taxonomies,
promise = CommunicationComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
var taxonomies;
var promise = CommunicationComponent.getTaxonomies(
that.model.get('contentType')
).then(function(tax) {
taxonomies = tax;
// Fetch available terms based on the list of taxonomies already fetched
var promise = CommunicationComponent.getTerms({
@ -227,7 +229,7 @@ define([
}).then(function(terms) {
return {
taxonomies: taxonomies,
terms: terms,
terms: terms
};
});
return promise;

View File

@ -19,7 +19,7 @@ define([
return this._getDefaults({
type: 'button',
text: 'Button',
url: 'http://google.com',
url: '',
styles: {
block: {
backgroundColor: '#ff0000',
@ -44,6 +44,9 @@ define([
className: "mailpoet_block mailpoet_button_block mailpoet_droppable_block",
getTemplate: function() { return templates.buttonBlock; },
onDragSubstituteBy: function() { return Module.ButtonWidgetView; },
behaviors: _.extend({}, base.BlockView.prototype.behaviors, {
ShowSettingsBehavior: {},
}),
initialize: function() {
base.BlockView.prototype.initialize.apply(this, arguments);
@ -65,8 +68,8 @@ define([
getTemplate: function() { return templates.buttonBlockSettings; },
events: function() {
return {
"keyup .mailpoet_field_button_text": _.partial(this.changeField, "text"),
"keyup .mailpoet_field_button_url": _.partial(this.changeField, "url"),
"input .mailpoet_field_button_text": _.partial(this.changeField, "text"),
"input .mailpoet_field_button_url": _.partial(this.changeField, "url"),
"change .mailpoet_field_button_alignment": _.partial(this.changeField, "styles.block.textAlign"),
"change .mailpoet_field_button_font_color": _.partial(this.changeColorField, "styles.block.fontColor"),
"change .mailpoet_field_button_font_family": _.partial(this.changeField, "styles.block.fontFamily"),
@ -77,23 +80,19 @@ define([
"input .mailpoet_field_button_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"change .mailpoet_field_button_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"change .mailpoet_field_button_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"keyup .mailpoet_field_button_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"input .mailpoet_field_button_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"input .mailpoet_field_button_border_radius": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_radius_input', _.partial(this.changePixelField, "styles.block.borderRadius").bind(this)),
"change .mailpoet_field_button_border_radius": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_radius_input', _.partial(this.changePixelField, "styles.block.borderRadius").bind(this)),
"change .mailpoet_field_button_border_radius_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_radius', _.partial(this.changePixelField, "styles.block.borderRadius").bind(this)),
"keyup .mailpoet_field_button_border_radius_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_radius', _.partial(this.changePixelField, "styles.block.borderRadius").bind(this)),
"input .mailpoet_field_button_border_radius_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_radius', _.partial(this.changePixelField, "styles.block.borderRadius").bind(this)),
"input .mailpoet_field_button_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_width_input', _.partial(this.changePixelField, "styles.block.width").bind(this)),
"change .mailpoet_field_button_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_width_input', _.partial(this.changePixelField, "styles.block.width").bind(this)),
"change .mailpoet_field_button_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_width', _.partial(this.changePixelField, "styles.block.width").bind(this)),
"keyup .mailpoet_field_button_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_width', _.partial(this.changePixelField, "styles.block.width").bind(this)),
"input .mailpoet_field_button_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_width', _.partial(this.changePixelField, "styles.block.width").bind(this)),
"input .mailpoet_field_button_line_height": _.partial(this.updateValueAndCall, '.mailpoet_field_button_line_height_input', _.partial(this.changePixelField, "styles.block.lineHeight").bind(this)),
"change .mailpoet_field_button_line_height": _.partial(this.updateValueAndCall, '.mailpoet_field_button_line_height_input', _.partial(this.changePixelField, "styles.block.lineHeight").bind(this)),
"change .mailpoet_field_button_line_height_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_line_height', _.partial(this.changePixelField, "styles.block.lineHeight").bind(this)),
"keyup .mailpoet_field_button_line_height_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_line_height', _.partial(this.changePixelField, "styles.block.lineHeight").bind(this)),
"input .mailpoet_field_button_line_height_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_line_height', _.partial(this.changePixelField, "styles.block.lineHeight").bind(this)),
"click .mailpoet_field_button_replace_all_styles": "applyToAll",
"click .mailpoet_done_editing": "close",

View File

@ -43,6 +43,9 @@ define([
minLength: 0, // TODO: Move this number to editor configuration
modelField: 'styles.block.padding',
},
ShowSettingsBehavior: {
ignoreFrom: '.mailpoet_resize_handle'
},
}, base.BlockView.prototype.behaviors),
onDragSubstituteBy: function() { return Module.DividerWidgetView; },
initialize: function() {
@ -88,8 +91,7 @@ define([
"input .mailpoet_field_divider_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_divider_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"change .mailpoet_field_divider_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_divider_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"change .mailpoet_field_divider_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_divider_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"keyup .mailpoet_field_divider_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_divider_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"input .mailpoet_field_divider_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_divider_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"change .mailpoet_field_divider_border_color": _.partial(this.changeColorField, "styles.block.borderColor"),
"change .mailpoet_field_divider_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"),

View File

@ -17,8 +17,8 @@ define([
defaults: function() {
return this._getDefaults({
type: 'image',
link: 'http://example.org',
src: 'no-image.png',
link: '',
src: '',
alt: 'An image of...',
fullWidth: true, // true | false
width: '64px',
@ -41,6 +41,9 @@ define([
imageMissingSrc: App.getConfig().get('urls.imageMissing'),
}, base.BlockView.prototype.templateHelpers.apply(this));
},
behaviors: _.extend({}, base.BlockView.prototype.behaviors, {
ShowSettingsBehavior: {},
}),
onRender: function() {
this.toolsView = new Module.ImageBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView);
@ -61,9 +64,9 @@ define([
getTemplate: function() { return templates.imageBlockSettings; },
events: function() {
return {
"keyup .mailpoet_field_image_link": _.partial(this.changeField, "link"),
"keyup .mailpoet_field_image_address": _.partial(this.changeField, "src"),
"keyup .mailpoet_field_image_alt_text": _.partial(this.changeField, "alt"),
"input .mailpoet_field_image_link": _.partial(this.changeField, "link"),
"input .mailpoet_field_image_address": _.partial(this.changeField, "src"),
"input .mailpoet_field_image_alt_text": _.partial(this.changeField, "alt"),
"change .mailpoet_field_image_full_width": _.partial(this.changeBoolCheckboxField, "fullWidth"),
"change .mailpoet_field_image_alignment": _.partial(this.changeField, "styles.block.textAlign"),
"click .mailpoet_field_image_select_another_image": "showMediaManager",

View File

@ -273,7 +273,7 @@ define([
return {
'change .mailpoet_settings_posts_content_type': _.partial(this.changeField, 'contentType'),
'change .mailpoet_posts_post_status': _.partial(this.changeField, 'postStatus'),
'keyup .mailpoet_posts_search_term': _.partial(this.changeField, 'search'),
'input .mailpoet_posts_search_term': _.partial(this.changeField, 'search'),
};
},
constructor: function() {
@ -302,8 +302,10 @@ define([
};
},
transport: function(options, success, failure) {
var taxonomies,
promise = CommunicationComponent.getTaxonomies(that.model.get('contentType')).then(function(tax) {
var taxonomies;
var promise = CommunicationComponent.getTaxonomies(
that.model.get('contentType')
).then(function(tax) {
taxonomies = tax;
// Fetch available terms based on the list of taxonomies already fetched
var promise = CommunicationComponent.getTerms({
@ -312,7 +314,7 @@ define([
}).then(function(terms) {
return {
taxonomies: taxonomies,
terms: terms,
terms: terms
};
});
return promise;
@ -412,17 +414,17 @@ define([
"change .mailpoet_posts_title_format": 'changeTitleFormat',
"change .mailpoet_posts_title_as_links": _.partial(this.changeBoolField, 'titleIsLink'),
"change .mailpoet_posts_show_divider": _.partial(this.changeBoolField, 'showDivider'),
"keyup .mailpoet_posts_show_amount": _.partial(this.changeField, "amount"),
"input .mailpoet_posts_show_amount": _.partial(this.changeField, "amount"),
"change .mailpoet_posts_content_type": _.partial(this.changeField, "contentType"),
"change .mailpoet_posts_include_or_exclude": _.partial(this.changeField, "inclusionType"),
"change .mailpoet_posts_title_alignment": _.partial(this.changeField, "titleAlignment"),
"change .mailpoet_posts_image_full_width": _.partial(this.changeBoolField, "imageFullWidth"),
"change .mailpoet_posts_featured_image_position": _.partial(this.changeField, "featuredImagePosition"),
"change .mailpoet_posts_show_author": _.partial(this.changeField, "showAuthor"),
"keyup .mailpoet_posts_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"),
"input .mailpoet_posts_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"),
"change .mailpoet_posts_show_categories": _.partial(this.changeField, "showCategories"),
"keyup .mailpoet_posts_categories": _.partial(this.changeField, "categoriesPrecededBy"),
"keyup .mailpoet_posts_read_more_text": _.partial(this.changeField, "readMoreText"),
"input .mailpoet_posts_categories": _.partial(this.changeField, "categoriesPrecededBy"),
"input .mailpoet_posts_read_more_text": _.partial(this.changeField, "readMoreText"),
"change .mailpoet_posts_sort_by": _.partial(this.changeField, "sortBy"),
};
},

View File

@ -93,6 +93,7 @@ define([
return {
model: this.model.toJSON(),
allIconSets: allIconSets.toJSON(),
imageMissingSrc: App.getConfig().get('urls.imageMissing'),
};
},
});
@ -140,6 +141,7 @@ define([
},
},
HighlightEditingBehavior: {},
ShowSettingsBehavior: {},
},
onDragSubstituteBy: function() { return Module.SocialWidgetView; },
constructor: function() {
@ -148,6 +150,7 @@ define([
Marionette.CompositeView.apply(this, arguments);
},
initialize: function() {
this.on('showSettings', this.showSettings, this);
this.on('dom:refresh', this.showBlock, this);
this._isFirstRender = true;
},
@ -175,6 +178,9 @@ define([
this.$(this.ui.tools).hide();
_event.stopPropagation();
},
showSettings: function(options) {
this.toolsView.triggerMethod('showSettings', options);
},
getDropFunc: function() {
return function() {
return this.model.clone();
@ -274,9 +280,9 @@ define([
return {
"click .mailpoet_delete_block": "deleteIcon",
"change .mailpoet_social_icon_field_type": _.partial(this.changeField, "iconType"),
"keyup .mailpoet_social_icon_field_image": _.partial(this.changeField, "image"),
"keyup .mailpoet_social_icon_field_link": this.changeLink,
"keyup .mailpoet_social_icon_field_text": _.partial(this.changeField, "text"),
"input .mailpoet_social_icon_field_image": _.partial(this.changeField, "image"),
"input .mailpoet_social_icon_field_link": this.changeLink,
"input .mailpoet_social_icon_field_text": _.partial(this.changeField, "text"),
};
},
modelEvents: {
@ -383,7 +389,7 @@ define([
{
type: 'socialIcon',
iconType: 'facebook',
link: 'http://example.com',
link: 'http://www.facebook.com',
image: App.getAvailableStyles().get('socialIconSets.default.facebook'),
height: '32px',
width: '32px',
@ -392,7 +398,7 @@ define([
{
type: 'socialIcon',
iconType: 'twitter',
link: 'http://example.com',
link: 'http://www.twitter.com',
image: App.getAvailableStyles().get('socialIconSets.default.twitter'),
height: '32px',
width: '32px',

View File

@ -36,6 +36,9 @@ define([
minLength: 20, // TODO: Move this number to editor configuration
modelField: 'styles.block.height',
},
ShowSettingsBehavior: {
ignoreFrom: '.mailpoet_resize_handle'
},
}, base.BlockView.prototype.behaviors),
modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'),
onDragSubstituteBy: function() { return Module.SpacerWidgetView; },

View File

@ -27,8 +27,8 @@ define([
return Module._cachedQuery({
action: 'getPostTypes',
options: {},
}).then(function(types) {
return _.values(types);
}).then(function(response) {
return _.values(response.data);
});
};
@ -36,36 +36,46 @@ define([
return Module._cachedQuery({
action: 'getTaxonomies',
options: {
postType: postType,
},
postType: postType
}
}).then(function(response) {
return response.data;
});
};
Module.getTerms = function(options) {
return Module._cachedQuery({
action: 'getTerms',
options: options,
options: options
}).then(function(response) {
return response.data;
});
};
Module.getPosts = function(options) {
return Module._cachedQuery({
action: 'getPosts',
options: options,
options: options
}).then(function(response) {
return response.data;
});
};
Module.getTransformedPosts = function(options) {
return Module._cachedQuery({
action: 'getTransformedPosts',
options: options,
options: options
}).then(function(response) {
return response.data;
});
};
Module.getBulkTransformedPosts = function(options) {
return Module._query({
action: 'getBulkTransformedPosts',
options: options,
options: options
}).then(function(response) {
return response.data;
});
};

View File

@ -104,7 +104,7 @@ define([
return MailPoet.Ajax.post({
endpoint: 'newsletterTemplates',
action: 'save',
data: data,
data: data
});
});

View File

@ -65,10 +65,6 @@ define([
var $openRegion = this.$el.find('.mailpoet_sidebar_region:not(.closed)'),
$targetRegion = this.$el.find(event.target).closest('.mailpoet_sidebar_region');
if ($openRegion.get(0) === $targetRegion.get(0)) {
return;
}
$openRegion.find('.mailpoet_region_content').velocity(
'slideUp',
{
@ -79,16 +75,19 @@ define([
}.bind(this)
}
);
$targetRegion.find('.mailpoet_region_content').velocity(
'slideDown',
{
duration: 250,
easing: "easeIn",
complete: function() {
$targetRegion.removeClass('closed');
},
}
);
if ($openRegion.get(0) !== $targetRegion.get(0)) {
$targetRegion.find('.mailpoet_region_content').velocity(
'slideDown',
{
duration: 250,
easing: "easeIn",
complete: function() {
$targetRegion.removeClass('closed');
},
}
);
}
},
},
initialize: function(options) {
@ -233,6 +232,12 @@ define([
'click .mailpoet_show_preview': 'showPreview',
'click #mailpoet_send_preview': 'sendPreview',
},
onBeforeDestroy: function() {
if (this.previewView) {
this.previewView.destroy();
this.previewView = null;
}
},
showPreview: function() {
var json = App.toJSON();
@ -247,18 +252,32 @@ define([
endpoint: 'newsletters',
action: 'showPreview',
data: json,
}).done(function(response){
MailPoet.Modal.loading(false);
}).done(function(response) {
if (response.result === true) {
window.open(response.data.url, '_blank')
this.previewView = new Module.NewsletterPreviewView({
previewUrl: response.data.url
});
var view = this.previewView.render();
MailPoet.Modal.popup({
template: '',
element: this.previewView.$el,
title: MailPoet.I18n.t('newsletterPreview'),
onCancel: function() {
this.previewView.destroy();
this.previewView = null;
}.bind(this)
});
} else {
MailPoet.Notice.error(response.errors);
}
MailPoet.Notice.error(response.errors);
}).fail(function(error) {
MailPoet.Modal.loading(false);
}.bind(this)).fail(function(error) {
MailPoet.Notice.error(
MailPoet.I18n.t('newsletterPreviewFailed')
);
}).always(function() {
MailPoet.Modal.loading(false);
});
},
sendPreview: function() {
@ -309,6 +328,22 @@ define([
},
});
Module.NewsletterPreviewView = Marionette.ItemView.extend({
getTemplate: function() { return templates.newsletterPreview; },
initialize: function(options) {
this.previewUrl = options.previewUrl;
this.width = App.getConfig().get('newsletterPreview.width');
this.height = App.getConfig().get('newsletterPreview.height')
},
templateHelpers: function() {
return {
previewUrl: this.previewUrl,
width: this.width,
height: this.height,
};
}
});
App.on('before:start', function(options) {
App.registerWidget = Module.registerWidget;
App.getWidgets = Module.getWidgets;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -8,20 +8,38 @@ const _QueueMixin = {
MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'pause',
data: newsletter.id
data: {
newsletter_id: newsletter.id
}
}).done(function() {
jQuery('#resume_'+newsletter.id).show();
jQuery('#pause_'+newsletter.id).hide();
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map(function(error) { return error.message; }),
{ scroll: true }
);
}
});
},
resumeSending: function(newsletter) {
MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'resume',
data: newsletter.id
data: {
newsletter_id: newsletter.id
}
}).done(function() {
jQuery('#pause_'+newsletter.id).show();
jQuery('#resume_'+newsletter.id).hide();
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map(function(error) { return error.message; }),
{ scroll: true }
);
}
});
},
renderQueueStatus: function(newsletter) {

View File

@ -158,20 +158,18 @@ const NewsletterListNotification = React.createClass({
id: ~~(e.target.getAttribute('data-id')),
status: e.target.value
}
}).done(function(response) {
if (response.result === false) {
MailPoet.Notice.error(MailPoet.I18n.t('postNotificationActivationFailed'));
// reset value to actual newsletter's status
e.target.value = response.status;
} else {
if (response.status === 'active') {
MailPoet.Notice.success(MailPoet.I18n.t('postNotificationActivated'));
}
// force refresh of listing so that groups are updated
this.forceUpdate();
}).done((response) => {
if (response.data.status === 'active') {
MailPoet.Notice.success(MailPoet.I18n.t('postNotificationActivated'));
}
}.bind(this));
// force refresh of listing so that groups are updated
this.forceUpdate();
}).fail((response) => {
MailPoet.Notice.error(MailPoet.I18n.t('postNotificationActivationFailed'));
// reset value to actual newsletter's status
e.target.value = response.status;
});
},
renderStatus: function(newsletter) {
return (

View File

@ -156,20 +156,18 @@ const NewsletterListWelcome = React.createClass({
id: ~~(e.target.getAttribute('data-id')),
status: e.target.value
}
}).done(function(response) {
if (response.result === false) {
MailPoet.Notice.error(MailPoet.I18n.t('welcomeEmailActivationFailed'));
// reset value to actual newsletter's status
e.target.value = response.status;
} else {
if (response.status === 'active') {
MailPoet.Notice.success(MailPoet.I18n.t('welcomeEmailActivated'));
}
// force refresh of listing so that groups are updated
this.forceUpdate();
}).done((response) => {
if (response.data.status === 'active') {
MailPoet.Notice.success(MailPoet.I18n.t('welcomeEmailActivated'));
}
}.bind(this));
// force refresh of listing so that groups are updated
this.forceUpdate();
}).fail((response) => {
MailPoet.Notice.error(MailPoet.I18n.t('welcomeEmailActivationFailed'));
// reset value to actual newsletter's status
e.target.value = response.status;
});
},
renderStatus: function(newsletter) {
let total_sent;

View File

@ -90,78 +90,110 @@ define(
if(!this.isValid()) {
jQuery('#mailpoet_newsletter').parsley().validate();
} else {
this.setState({ loading: true });
MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'save',
data: this.state.item,
}).then((response) => {
if (response.result === true) {
return MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'add',
data: _.extend({}, this.state.item, {
newsletter_id: this.props.params.id,
}),
});
} else {
return response;
}
this._save(e).done(() => {
this.setState({ loading: true });
}).done((response) => {
this.setState({ loading: false });
switch (response.data.type) {
case 'notification':
case 'welcome':
return MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'setStatus',
data: {
id: this.props.params.id,
status: 'active'
}
}).done((response) => {
// redirect to listing based on newsletter type
this.context.router.push(`/${ this.state.item.type || '' }`);
if(response.result === true) {
this.context.router.push(`/${ this.state.item.type || '' }`);
MailPoet.Notice.success(response.data.message);
} else {
if(response.errors) {
MailPoet.Notice.error(response.errors);
} else {
MailPoet.Notice.error(
MailPoet.I18n.t('newsletterSendingError').replace("%$1s", '?page=mailpoet-settings')
);
}
// display success message depending on newsletter type
if (response.data.type === 'welcome') {
MailPoet.Notice.success(
MailPoet.I18n.t('welcomeEmailActivated')
);
} else if (response.data.type === 'notification') {
MailPoet.Notice.success(
MailPoet.I18n.t('postNotificationActivated')
);
}
}).fail(this._showError);
default:
return MailPoet.Ajax.post({
endpoint: 'sendingQueue',
action: 'add',
data: {
newsletter_id: this.props.params.id
}
}).done((response) => {
// redirect to listing based on newsletter type
this.context.router.push(`/${ this.state.item.type || '' }`);
if (response.data.status === 'scheduled') {
MailPoet.Notice.success(
MailPoet.I18n.t('newsletterHasBeenScheduled')
);
} else {
MailPoet.Notice.success(
MailPoet.I18n.t('newsletterBeingSent')
);
}
}).fail(this._showError);
}
}).fail(this._showError).always(() => {
this.setState({ loading: false });
});
}
return false;
},
handleSave: function(e) {
e.preventDefault();
this._save(e).done(() => {
this._save(e).done((response) => {
MailPoet.Notice.success(
MailPoet.I18n.t('newsletterUpdated')
);
}).done(() => {
this.context.router.push(`/${ this.state.item.type || '' }`);
});
}).fail(this._showError);
},
handleRedirectToDesign: function(e) {
e.preventDefault();
var redirectTo = e.target.href;
this._save(e).done(() => {
this._save(e).done((response) => {
MailPoet.Notice.success(
MailPoet.I18n.t('newsletterUpdated')
);
}).done(() => {
window.location = redirectTo;
});
}).fail(this._showError);
},
_save: function(e) {
var data = this.state.item;
this.setState({ loading: true });
// Ensure that body is JSON encoded
if (!_.isUndefined(data.body)) {
data.body = JSON.stringify(data.body);
}
return MailPoet.Ajax.post({
endpoint: 'newsletters',
action: 'save',
data: this.state.item,
}).done((response) => {
data: data,
}).always(() => {
this.setState({ loading: false });
if(response.result === true) {
MailPoet.Notice.success(
MailPoet.I18n.t('newsletterUpdated')
);
} else {
if(response.errors) {
MailPoet.Notice.error(response.errors);
}
}
});
},
_showError: (response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map(function(error) { return error.message; }),
{ scroll: true }
);
}
},
handleFormChange: function(e) {
var item = this.state.item,
field = e.target.name;

View File

@ -30,33 +30,35 @@ define(
endpoint: 'newsletterTemplates',
action: 'save',
data: template
}).done(function(response) {
}).always(function() {
MailPoet.Modal.loading(false);
if(response.result === true) {
this.props.onImport(template);
} else {
response.map(function(error) {
MailPoet.Notice.error(error);
});
}).done((response) => {
this.props.onImport(response.data);
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map(function(error) { return error.message; }),
{ scroll: true }
);
}
}.bind(this));
});
},
handleSubmit: function(e) {
e.preventDefault();
if (_.size(this.refs.templateFile.files) <= 0) return false;
var file = _.first(this.refs.templateFile.files),
reader = new FileReader(),
saveTemplate = this.saveTemplate;
var file = _.first(this.refs.templateFile.files);
var reader = new FileReader();
var saveTemplate = this.saveTemplate;
reader.onload = function(e) {
reader.onload = (e) => {
try {
saveTemplate(JSON.parse(e.target.result));
} catch (err) {
MailPoet.Notice.error(MailPoet.I18n.t('templateFileMalformedError'));
}
}.bind(this);
};
reader.readAsText(file);
},
@ -97,12 +99,12 @@ define(
MailPoet.Ajax.post({
endpoint: 'newsletterTemplates',
action: 'getAll',
}).done(function(response) {
}).always(() => {
MailPoet.Modal.loading(false);
if(this.isMounted()) {
if(response.length === 0) {
response = [
}).done((response) => {
if (this.isMounted()) {
if (response.data.length === 0) {
response.data = [
{
name:
MailPoet.I18n.t('mailpoetGuideTemplateTitle'),
@ -110,14 +112,14 @@ define(
MailPoet.I18n.t('mailpoetGuideTemplateDescription'),
readonly: "1"
}
]
];
}
this.setState({
templates: response,
templates: response.data,
loading: false
});
}
}.bind(this));
});
},
handleSelectTemplate: function(template) {
var body = template.body;
@ -134,19 +136,17 @@ define(
id: this.props.params.id,
body: body
}
}).done(function(response) {
if(response.result === true) {
// TODO: Move this URL elsewhere
window.location = 'admin.php?page=mailpoet-newsletter-editor&id=' + this.props.params.id;
} else {
response.errors.map(function(error) {
MailPoet.Notice.error(error);
});
}).done((response) => {
// TODO: Move this URL elsewhere
window.location = 'admin.php?page=mailpoet-newsletter-editor&id=' + response.data.id;
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map(function(error) { return error.message; }),
{ scroll: true }
);
}
}.bind(this));
},
handlePreviewTemplate: function(template) {
console.log('preview template #'+template.id);
});
},
handleDeleteTemplate: function(template) {
this.setState({ loading: true });
@ -160,10 +160,12 @@ define(
MailPoet.Ajax.post({
endpoint: 'newsletterTemplates',
action: 'delete',
data: template.id
}).done(function(response) {
data: {
id: template.id
}
}).done((response) => {
this.getTemplates();
}.bind(this));
});
} else {
this.setState({ loading: false });
}
@ -172,7 +174,7 @@ define(
MailPoet.Modal.popup({
title: template.name,
template: '<div class="mailpoet_boxes_preview" style="background-color: {{ body.globalStyles.body.backgroundColor }}"><img src="{{ thumbnail }}" /></div>',
data: template,
data: template
});
},
handleTemplateImport: function() {
@ -213,20 +215,19 @@ define(
</div>
<div className="mailpoet_actions">
<a
className="button button-secondary"
onClick={ this.handleShowTemplate.bind(null, template) }
>
{MailPoet.I18n.t('preview')}
</a>
&nbsp;
<a
className="button button-primary"
onClick={ this.handleSelectTemplate.bind(null, template) }
>
{MailPoet.I18n.t('select')}
</a>
&nbsp;
<a
style={ { display: 'none' }}
className="button button-secondary"
onClick={ this.handlePreviewTemplate.bind(null, template) }
>
{MailPoet.I18n.t('preview')}
</a>
</div>
{ (template.readonly === "1") ? false : deleteLink }
</li>

View File

@ -133,8 +133,7 @@ define(
return;
}
MailPoet.Modal.loading(true);
MailPoet.Ajax
.post({
MailPoet.Ajax.post({
endpoint: 'ImportExport',
action: 'processExport',
data: JSON.stringify({
@ -144,25 +143,22 @@ define(
'segments': (exportData.segments) ? segmentsContainerElement.val() : false,
'subscriber_fields': subscriberFieldsContainerElement.val()
})
})
.done(function (response) {
}).always(function(response) {
MailPoet.Modal.loading(false);
if (response.result === false) {
MailPoet.Notice.error(response.errors);
} else {
resultMessage = MailPoet.I18n.t('exportMessage')
.replace('%1$s', '<strong>' + parseInt(response.data.totalExported).toLocaleString() + '</strong>')
.replace('[link]', '<a href="' + response.data.exportFileURL + '" target="_blank" >')
.replace('[/link]', '</a>');
jQuery('#export_result_notice').html('<p>' + resultMessage + '</p>').show();
window.location.href = response.data.exportFileURL;
}).done(function(response) {
resultMessage = MailPoet.I18n.t('exportMessage')
.replace('%1$s', '<strong>' + parseInt(response.data.totalExported).toLocaleString() + '</strong>')
.replace('[link]', '<a href="' + response.data.exportFileURL + '" target="_blank" >')
.replace('[/link]', '</a>');
jQuery('#export_result_notice').html('<p>' + resultMessage + '</p>').show();
window.location.href = response.data.exportFileURL;
}).fail(function(response) {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map(function(error) { return error.message; }),
{ scroll: true }
);
}
})
.error(function (error) {
MailPoet.Modal.loading(false);
MailPoet.Notice.error(
MailPoet.I18n.t('serverError') + error.statusText.toLowerCase() + '.'
);
});
});
});

View File

@ -51,6 +51,12 @@ define(
* STEP 1 (upload or copy/paste)
*/
router.on('route:step1', function () {
// set or reset temporary validation rule on all columns
mailpoetColumns = jQuery.map(mailpoetColumns, function (column, columnIndex) {
column.validation_rule = false;
return column;
});
if (typeof (importData.step1) !== 'undefined') {
showCurrentStep();
return;
@ -185,68 +191,59 @@ define(
mailChimpKeyVerifyButtonElement.click(function () {
MailPoet.Modal.loading(true);
MailPoet.Ajax.post({
endpoint: 'ImportExport',
endpoint: 'importExport',
action: 'getMailChimpLists',
data: {api_key: mailChimpKeyInputElement.val()}
}).done(function (response) {
if (response.result === false) {
MailPoet.Notice.hide();
MailPoet.Notice.error(response.errors);
jQuery('.mailpoet_mailchimp-key-status')
.removeClass()
.addClass('mailpoet_mailchimp-key-status mailpoet_mailchimp-error');
data: {
api_key: mailChimpKeyInputElement.val()
}
}).always(function() {
MailPoet.Modal.loading(false);
}).done(function(response) {
jQuery('.mailpoet_mailchimp-key-status')
.html('')
.removeClass()
.addClass('mailpoet_mailchimp-key-status mailpoet_mailchimp-ok');
if (response.data.length === 0) {
jQuery('.mailpoet_mailchimp-key-status').html(MailPoet.I18n.t('noMailChimpLists'));
mailChimpListsContainerElement.hide();
toggleNextStepButton(mailChimpProcessButtonElement, 'off');
} else {
jQuery('.mailpoet_mailchimp-key-status')
.html('')
.removeClass()
.addClass('mailpoet_mailchimp-key-status mailpoet_mailchimp-ok');
if (!response.data) {
jQuery('.mailpoet_mailchimp-key-status').html(MailPoet.I18n.t('noMailChimpLists'));
mailChimpListsContainerElement.hide();
toggleNextStepButton(mailChimpProcessButtonElement, 'off');
} else {
displayMailChimpLists(response.data);
}
displayMailChimpLists(response.data);
}
}).fail(function(response) {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map(function(error) { return error.message; }),
{ scroll: true }
);
}
MailPoet.Modal.loading(false);
}).error(function (error) {
MailPoet.Modal.loading(false);
MailPoet.Notice.error(
MailPoet.I18n.t('serverError') + error.statusText.toLowerCase() + '.'
);
});
MailPoet.Modal.loading(false);
});
mailChimpProcessButtonElement.click(function () {
if (mailChimpProcessButtonElement.closest('table a').hasClass('disabled')) {
if (mailChimpProcessButtonElement.closest('table a').hasClass('button-disabled')) {
return;
}
MailPoet.Modal.loading(true);
MailPoet.Ajax.post({
endpoint: 'ImportExport',
endpoint: 'importExport',
action: 'getMailChimpSubscribers',
data: {
api_key: mailChimpKeyInputElement.val(),
lists: mailChimpListsContainerElement.find('select').val()
}
}).done(function (response) {
if (response.result === true) {
importData.step1 = response.data;
router.navigate('step2', {trigger: true});
}
else {
MailPoet.Notice.hide();
MailPoet.Notice.error(response.errors);
}
}).always(function(response) {
MailPoet.Modal.loading(false);
}).error(function () {
MailPoet.Modal.loading(false);
MailPoet.Notice.error(
MailPoet.I18n.t('serverError') + result.statusText.toLowerCase() + '.'
);
}).done(function(response) {
importData.step1 = response.data;
router.navigate('step2', {trigger: true});
}).fail(function(response) {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map(function(error) { return error.message; }),
{ scroll: true }
);
}
});
});
@ -455,7 +452,7 @@ define(
null,
new Array(subscribers.subscribers[0].length)
).map(String.prototype.valueOf, filler),
fillterPosition;
fillerPosition;
showCurrentStep();
@ -565,79 +562,54 @@ define(
})
}
jQuery('.mailpoet_create_segment').click(function () {
jQuery('.mailpoet_create_segment').click(function() {
MailPoet.Modal.popup({
title: MailPoet.I18n.t('addNewList'),
template: jQuery('#new_segment_template').html()
})
jQuery('#new_segment_name').keypress(function (e) {
jQuery('#new_segment_name').keypress(function(e) {
if (e.which == 13) {
jQuery('#new_segment_process').click();
}
});
jQuery('#new_segment_process').click(function () {
var segmentName = jQuery('#new_segment_name').val().trim(),
segmentDescription = jQuery('#new_segment_description').val().trim(),
isDuplicateListName = ( jQuery.map(mailpoetSegments, function (el) {
if (el.name.toLowerCase() === segmentName.toLowerCase()) {
return true;
}
}).length && segmentName) ? true : false;
if (segmentName === '') {
jQuery('.mailpoet_validation_error[data-error="segment_name_required"]:hidden').show();
} else {
jQuery('.mailpoet_validation_error[data-error="segment_name_required"]:visible').hide();
}
if (isDuplicateListName) {
jQuery('.mailpoet_validation_error[data-error="segment_name_not_unique"]:hidden').show();
} else {
jQuery('.mailpoet_validation_error[data-error="segment_name_not_unique"]:visible').hide();
}
if (segmentName && !isDuplicateListName) {
jQuery('.mailpoet_validation_error[data-error="segment_name_required"]:visible').hide();
MailPoet.Ajax
.post({
endpoint: 'ImportExport',
action: 'addSegment',
data: {
name: segmentName,
description: segmentDescription
}
})
.done(function (response) {
if (response.result === true) {
mailpoetSegments.push({
'id': response.segment.id,
'name': response.segment.name
});
var segmentName = jQuery('#new_segment_name').val().trim();
var segmentDescription = jQuery('#new_segment_description').val().trim();
var selected_values = segmentSelectElement.val();
if (selected_values === null) {
selected_values = [response.segment.id]
} else {
selected_values.push(response.segment.id);
}
MailPoet.Ajax.post({
endpoint: 'ImportExport',
action: 'addSegment',
data: {
name: segmentName,
description: segmentDescription
}
}).done(function(response) {
mailpoetSegments.push({
'id': response.data.id,
'name': response.data.name
});
enableSegmentSelection(mailpoetSegments);
segmentSelectElement.val(selected_values).trigger('change');
jQuery('.mailpoet_segments:hidden').show();
jQuery('.mailpoet_no_segments:visible').hide();
MailPoet.Modal.close();
}
else {
MailPoet.Modal.close();
MailPoet.Notice.error(
MailPoet.I18n.t('segmentCreateError') + response.message + '.'
);
}
})
.error(function (error) {
MailPoet.Modal.close();
MailPoet.Notice.error(
MailPoet.I18n.t('serverError') + error.statusText.toLowerCase() + '.'
);
});
}
var selected_values = segmentSelectElement.val();
if (selected_values === null) {
selected_values = [response.data.id]
} else {
selected_values.push(response.data.id);
}
enableSegmentSelection(mailpoetSegments);
segmentSelectElement.val(selected_values).trigger('change');
jQuery('.mailpoet_segments:hidden').show();
jQuery('.mailpoet_no_segments:visible').hide();
MailPoet.Modal.close();
}).fail(function(response) {
if (response.errors.length > 0) {
MailPoet.Notice.hide();
MailPoet.Notice.error(
response.errors.map(function(error) { return error.message; }),
{ positionAfter: '#new_segment_name' }
);
}
});
});
jQuery('#new_segment_cancel').click(function () {
MailPoet.Modal.close();
@ -707,7 +679,7 @@ define(
// display filler data (e.g., ellipsis) if we've reached the maximum number of rows and
// subscribers count is greater than the maximum number of rows we're displaying
if (index === maxRowsToShow && subscribers.subscribersCount > (maxRowsToShow + 1)) {
fillterPosition = index;
fillerPosition = index;
return filler;
}
// if we're on the last line, show the total count of subscribers data
@ -745,114 +717,70 @@ define(
.on('select2:selecting', function (selectEvent) {
var selectElement = this,
selectedOptionId = selectEvent.params.args.data.id;
// CREATE CUSTOM FIELD
if (selectedOptionId === 'create') {
selectEvent.preventDefault();
jQuery(selectElement).select2('close');
MailPoet.Modal.popup({
title: MailPoet.I18n.t('addNewColumn'),
template: jQuery('#new_column_template').html()
title: MailPoet.I18n.t('addNewField'),
template: jQuery('#form_template_field_form').html()
});
jQuery('#new_column_name').keypress(function (e) {
if (e.which == 13) {
jQuery('#new_column_process').click();
}
});
jQuery('#new_column_process').click(function () {
var name = jQuery('#new_column_name').val().trim(),
type = jQuery('#new_column_type').val().trim(),
columnNames = mailpoetColumns.map(function (el) {
return el.name.toLowerCase();
jQuery('#form_field_new').parsley().on('form:submit', function(parsley) {
// get data
var data = jQuery(this.$element).serializeObject();
// save custom field
MailPoet.Ajax.post({
endpoint: 'customFields',
action: 'save',
data: data
}).done(function(response) {
var new_column_data = {
'id': response.data.id,
'name': response.data.name,
'type': response.data.type,
'params': response.data.params,
'custom': true
};
// if this is the first custom column, create an "optgroup"
if (mailpoetColumnsSelect2.length === 2) {
mailpoetColumnsSelect2.push({
'name': MailPoet.I18n.t('userColumns'),
'children': []
});
isDuplicateColumnName =
(name && columnNames.indexOf(name.toLowerCase()) > -1)
? true
: false;
if (name === '') {
jQuery('.mailpoet_validation_error[data-error="name_required"]')
.show();
} else {
jQuery('.mailpoet_validation_error[data-error="name_required"]')
.hide();
}
if (type === '') {
jQuery('.mailpoet_validation_error[data-error="type_required"]')
.show();
} else {
jQuery('.mailpoet_validation_error[data-error="type_required"]')
.hide();
}
if (isDuplicateColumnName) {
jQuery('.mailpoet_validation_error[data-error="name_not_unique"]')
.show();
} else {
jQuery('.mailpoet_validation_error[data-error="name_not_unique"]')
.hide();
}
// create new field
if (name && type && !isDuplicateColumnName) {
MailPoet.Modal
.close()
.loading(true);
MailPoet.Ajax
.post({
endpoint: 'ImportExport',
action: 'addCustomField',
data: {
name: name,
type: type
}
})
.done(function (response) {
if (response.result === true) {
var new_column_data = {
'id': response.customField.id,
'name': name,
'type': type,
'custom': true,
};
// if this is the first custom column, create an "optgroup"
if (mailpoetColumnsSelect2.length === 2) {
mailpoetColumnsSelect2.push({
'name': MailPoet.I18n.t('userColumns'),
'children': []
});
}
mailpoetColumnsSelect2[2].children.push(new_column_data);
mailpoetColumns.push(new_column_data);
jQuery('select.mailpoet_subscribers_column_data_match')
.each(function () {
jQuery(this)
.html('')
.select2('destroy')
.select2({
data: mailpoetColumnsSelect2,
width: '15em',
templateResult: function (item) {
return item.name;
},
templateSelection: function (item) {
return item.name;
}
mailpoetColumnsSelect2[2].children.push(new_column_data);
mailpoetColumns.push(new_column_data);
jQuery('select.mailpoet_subscribers_column_data_match')
.each(function () {
jQuery(this)
.html('')
.select2('destroy')
.select2({
data: mailpoetColumnsSelect2,
width: '15em',
templateResult: function (item) {
return item.name;
},
templateSelection: function (item) {
return item.name;
}
})
});
jQuery(selectElement).data('column-id', new_column_data.id);
filterSubscribers();
}
else {
MailPoet.Notice.error(MailPoet.I18n.t('customFieldCreateError'));
}
MailPoet.Modal.loading(false);
})
.error(function (error) {
MailPoet.Modal.loading(false);
MailPoet.Notice.error(
MailPoet.I18n.t('serverError') + error.statusText.toLowerCase() + '.'
);
});
}
});
jQuery('#new_column_cancel').click(function () {
MailPoet.Modal.close();
})
});
jQuery(selectElement).data('column-id', new_column_data.id);
jQuery(selectElement).data('validation-rule', false);
filterSubscribers();
// close popup
MailPoet.Modal.close();
}).fail(function(response) {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map(function(error) { return error.message; }),
{ positionAfter: '#field_name' }
);
}
});
return false;
});
}
// CHANGE COLUMN
@ -890,22 +818,22 @@ define(
.remove();
var subscribersClone = jQuery.extend(true, {}, subscribers),
preventNextStep = false,
displayedColumnsIds = jQuery.map(
jQuery('.mailpoet_subscribers_column_data_match'), function (data) {
var columnId = jQuery(data).data('column-id');
jQuery(data).val(columnId).trigger('change');
return columnId;
displayedColumns = jQuery.map(
jQuery('.mailpoet_subscribers_column_data_match'), function (element, elementIndex) {
var columnId = jQuery(element).data('column-id');
var validationRule = jQuery(element).data('validation-rule');
jQuery(element).val(columnId).trigger('change');
return { id: columnId, index: elementIndex, validationRule: validationRule, element: element };
});
// iterate through the object of mailpoet columns
jQuery.map(mailpoetColumns, function (column) {
jQuery.map(mailpoetColumns, function (column, columnIndex) {
// check if the column id matches the selected id of one of the
// subscriber's data columns
var matchedColumn = jQuery.inArray(column.id, displayedColumnsIds);
// EMAIL filter: if the last value in the column doesn't have a valid
var matchedColumn = _.find(displayedColumns, function(data) { return data.id === column.id; });
// EMAIL filter: if the first value in the column doesn't have a valid
// email, hide the next button
if (column.id === "email") {
if (!emailRegex.test(subscribersClone.subscribers[0][matchedColumn])) {
if (column.id === 'email') {
if (!emailRegex.test(subscribersClone.subscribers[0][matchedColumn.index])) {
preventNextStep = true;
if (!jQuery('[data-id="notice_invalidEmail"]').length) {
MailPoet.Notice.error(MailPoet.I18n.t('columnContainsInvalidElement'), {
@ -921,35 +849,63 @@ define(
}
}
// DATE filter: if column type is date, check if we can recognize it
if (column.type === 'date' && matchedColumn !== -1) {
jQuery.map(subscribersClone.subscribers, function (data, position) {
var rowData = data[matchedColumn];
if (position !== fillterPosition) {
// check if date exists
if (rowData.trim() === '') {
data[matchedColumn] =
'<span class="mailpoet_data_match mailpoet_import_error" title="'
+ MailPoet.I18n.t('noDateFieldMatch') + '">'
+ MailPoet.I18n.t('emptyDate')
+ '</span>';
preventNextStep = true;
return;
}
// check if date is valid and is before today
if (Moment(rowData).isValid() && Moment(rowData).isBefore(Moment())) {
data[matchedColumn] +=
'<span class="mailpoet_data_match" title="'
+ MailPoet.I18n.t('verifyDateMatch') + '">'
+ MailPoet.Date.format(rowData) + '</span>';
}
else {
data[matchedColumn] +=
'<span class="mailpoet_data_match mailpoet_import_error" title="'
+ MailPoet.I18n.t('noDateFieldMatch') + '">'
+ MailPoet.I18n.t('dateMatchError') + '</span>';
preventNextStep = true;
if (column.type === 'date' && matchedColumn) {
var allowedDateFormats = [
Moment.ISO_8601,
'YYYY/MM/DD',
'MM/DD/YYYY',
'DD/MM/YYYY',
'YYYY/MM/DD',
'YYYY/DD/MM',
'MM/YYYY',
'YYYY/MM',
'YYYY'
];
var firstRowData = subscribersClone.subscribers[0][matchedColumn.index];
var validationRule = false;
// check if date exists
if (firstRowData.trim() === '') {
subscribersClone.subscribers[0][matchedColumn.index] =
'<span class="mailpoet_data_match mailpoet_import_error" title="'
+ MailPoet.I18n.t('noDateFieldMatch') + '">'
+ MailPoet.I18n.t('emptyFirstRowDate')
+ '</span>';
preventNextStep = true;
}
else {
for (var format in allowedDateFormats) {
var testedFormat = allowedDateFormats[format]
if (Moment(firstRowData, testedFormat, true).isValid()) {
var validationRule = (typeof(testedFormat) === 'function') ?
'datetime' :
testedFormat
// set validation on the column element
jQuery(matchedColumn.element).data('validation-rule', validationRule);
break;
}
if (validationRule === 'datetime') validationRule = Moment.ISO_8601;
}
}
jQuery.map(subscribersClone.subscribers, function (data, index) {
var rowData = data[matchedColumn.index];
if (index === fillerPosition || rowData.trim() === '') return;
var date = Moment(rowData, testedFormat, true);
// validate date
if (date.isValid()) {
data[matchedColumn.index] +=
'<span class="mailpoet_data_match" title="'
+ MailPoet.I18n.t('verifyDateMatch') + '">'
+ MailPoet.Date.format(date)
+ '</span>';
}
else {
data[matchedColumn.index] +=
'<span class="mailpoet_data_match mailpoet_import_error" title="'
+ MailPoet.I18n.t('noDateFieldMatch') + '">'
+ MailPoet.I18n.t('dateMatchError')
+ '</span>';
preventNextStep = true;
};
});
if (preventNextStep && !jQuery('.mailpoet_invalidDate').length) {
MailPoet.Notice.error(MailPoet.I18n.t('columnContainsInvalidDate'), {
@ -983,11 +939,11 @@ define(
nextStepButton.addClass(disabled);
}
previousStepButton.off().click(function () {
router.navigate('step1', {trigger: true});
previousStepButton.off().on('click', function () {
router.navigate('step1', { trigger: true });
});
nextStepButton.off().click(function () {
nextStepButton.off().on('click', function () {
if (jQuery(this).hasClass('button-disabled')) {
return;
}
@ -1019,44 +975,41 @@ define(
_.each(jQuery('select.mailpoet_subscribers_column_data_match'),
function (column, columnIndex) {
var columnId = jQuery(column).data('column-id');
var validationRule = jQuery(column).data('validation-rule');
if (columnId === 'ignore') {
return;
}
columns[columnId] = columnIndex;
columns[columnId] = { index: columnIndex, validation_rule: validationRule };
});
_.each(subscribers, function () {
queue.add(function (queue) {
queue.add(function(queue) {
queue.pause();
MailPoet.Ajax
.post({
endpoint: 'ImportExport',
action: 'processImport',
data: JSON.stringify({
columns: columns,
subscribers: subscribers[batchNumber],
timestamp: timestamp,
segments: segmentSelectElement.val(),
updateSubscribers: (jQuery(':radio[name="subscriber_update_option"]:checked').val() === 'yes') ? true : false
})
MailPoet.Ajax.post({
endpoint: 'ImportExport',
action: 'processImport',
data: JSON.stringify({
columns: columns,
subscribers: subscribers[batchNumber],
timestamp: timestamp,
segments: segmentSelectElement.val(),
updateSubscribers: (jQuery(':radio[name="subscriber_update_option"]:checked').val() === 'yes') ? true : false
})
.done(function (response) {
if (response.result === false) {
importResults.errors.push(response.errors);
} else {
importResults.created = response.data.created;
importResults.updated = response.data.updated;
importResults.segments = response.data.segments;
importResults.added_to_segment_with_welcome_notification = response.data.added_to_segment_with_welcome_notification;
}
queue.run();
})
.error(function (error) {
importResults.errors.push(
MailPoet.I18n.t('serverError') + error.statusText.toLowerCase() + '.'
}).done(function(response) {
importResults.created = response.data.created;
importResults.updated = response.data.updated;
importResults.segments = response.data.segments;
importResults.added_to_segment_with_welcome_notification = response.data.added_to_segment_with_welcome_notification;
queue.run();
}).fail(function(response) {
MailPoet.Modal.loading(false);
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map(function(error) { return error.message; }),
{ scroll: true }
);
queue.run();
});
}
});
batchNumber++;
})
});

View File

@ -16,6 +16,9 @@ npm install
# Production libraries.
./composer.phar install --no-dev
# Translations
./do makepot
# Copy release folders.
cp -Rf lang $plugin_name
cp -RfL assets $plugin_name

264
composer.lock generated
View File

@ -407,12 +407,12 @@
"source": {
"type": "git",
"url": "https://github.com/mailpoet/html2text.git",
"reference": "6e6c48d07a542f4b4ae24341cc6ab92335c32922"
"reference": "5ba872ab9a96c7b74fdaffbb42c78ee8dc12745a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mailpoet/html2text/zipball/6e6c48d07a542f4b4ae24341cc6ab92335c32922",
"reference": "6e6c48d07a542f4b4ae24341cc6ab92335c32922",
"url": "https://api.github.com/repos/mailpoet/html2text/zipball/5ba872ab9a96c7b74fdaffbb42c78ee8dc12745a",
"reference": "5ba872ab9a96c7b74fdaffbb42c78ee8dc12745a",
"shasum": ""
},
"require": {
@ -452,7 +452,7 @@
"email": "support@jevon.org",
"source": "https://github.com/mailpoet/html2text/tree/master"
},
"time": "2016-06-13 18:24:35"
"time": "2016-07-28 01:09:53"
},
{
"name": "sunra/php-simple-html-dom-parser",
@ -499,23 +499,23 @@
},
{
"name": "swiftmailer/swiftmailer",
"version": "v5.4.2",
"version": "v5.4.3",
"source": {
"type": "git",
"url": "https://github.com/swiftmailer/swiftmailer.git",
"reference": "d8db871a54619458a805229a057ea2af33c753e8"
"reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/d8db871a54619458a805229a057ea2af33c753e8",
"reference": "d8db871a54619458a805229a057ea2af33c753e8",
"url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/4cc92842069c2bbc1f28daaaf1d2576ec4dfe153",
"reference": "4cc92842069c2bbc1f28daaaf1d2576ec4dfe153",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"mockery/mockery": "~0.9.1,<0.9.4"
"mockery/mockery": "~0.9.1"
},
"type": "library",
"extra": {
@ -548,7 +548,7 @@
"mail",
"mailer"
],
"time": "2016-05-01 08:45:47"
"time": "2016-07-08 11:51:25"
},
{
"name": "symfony/polyfill-mbstring",
@ -611,16 +611,16 @@
},
{
"name": "symfony/translation",
"version": "v2.8.7",
"version": "v2.8.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/translation.git",
"reference": "8a1648d2e165ba87c759ba57d7f4c13d95fdf4a1"
"reference": "00334ef0b9317e5d7c7641a2b56671a1df23b7a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/translation/zipball/8a1648d2e165ba87c759ba57d7f4c13d95fdf4a1",
"reference": "8a1648d2e165ba87c759ba57d7f4c13d95fdf4a1",
"url": "https://api.github.com/repos/symfony/translation/zipball/00334ef0b9317e5d7c7641a2b56671a1df23b7a0",
"reference": "00334ef0b9317e5d7c7641a2b56671a1df23b7a0",
"shasum": ""
},
"require": {
@ -671,7 +671,7 @@
],
"description": "Symfony Translation Component",
"homepage": "https://symfony.com",
"time": "2016-06-06 11:11:27"
"time": "2016-06-29 05:29:29"
},
{
"name": "tburry/pquery",
@ -848,16 +848,16 @@
},
{
"name": "codeception/codeception",
"version": "2.2.2",
"version": "2.2.3",
"source": {
"type": "git",
"url": "https://github.com/Codeception/Codeception.git",
"reference": "8d80bb4ec7470e8df5de0e4c401785bc3fa1f4f6"
"reference": "34c268ae5872105c0c218296487650ab08e3991b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Codeception/Codeception/zipball/8d80bb4ec7470e8df5de0e4c401785bc3fa1f4f6",
"reference": "8d80bb4ec7470e8df5de0e4c401785bc3fa1f4f6",
"url": "https://api.github.com/repos/Codeception/Codeception/zipball/34c268ae5872105c0c218296487650ab08e3991b",
"reference": "34c268ae5872105c0c218296487650ab08e3991b",
"shasum": ""
},
"require": {
@ -935,20 +935,20 @@
"functional testing",
"unit testing"
],
"time": "2016-06-29 00:59:28"
"time": "2016-07-24 19:31:22"
},
{
"name": "codeception/verify",
"version": "0.3.0",
"version": "0.3.1",
"source": {
"type": "git",
"url": "https://github.com/Codeception/Verify.git",
"reference": "d3721cfc668d96b41acbda3ecd01d3499381db64"
"reference": "dc19c8722f3756341e5b0ce0590f09fda1d63719"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Codeception/Verify/zipball/d3721cfc668d96b41acbda3ecd01d3499381db64",
"reference": "d3721cfc668d96b41acbda3ecd01d3499381db64",
"url": "https://api.github.com/repos/Codeception/Verify/zipball/dc19c8722f3756341e5b0ce0590f09fda1d63719",
"reference": "dc19c8722f3756341e5b0ce0590f09fda1d63719",
"shasum": ""
},
"require-dev": {
@ -968,7 +968,7 @@
}
],
"description": "BDD assertion library for PHPUnit",
"time": "2015-11-26 23:23:25"
"time": "2016-07-13 09:34:15"
},
{
"name": "codegyre/robo",
@ -1125,27 +1125,27 @@
},
{
"name": "guzzlehttp/guzzle",
"version": "6.2.0",
"version": "6.2.1",
"source": {
"type": "git",
"url": "https://github.com/guzzle/guzzle.git",
"reference": "d094e337976dff9d8e2424e8485872194e768662"
"reference": "3f808fba627f2c5b69e2501217bf31af349c1427"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/d094e337976dff9d8e2424e8485872194e768662",
"reference": "d094e337976dff9d8e2424e8485872194e768662",
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/3f808fba627f2c5b69e2501217bf31af349c1427",
"reference": "3f808fba627f2c5b69e2501217bf31af349c1427",
"shasum": ""
},
"require": {
"guzzlehttp/promises": "~1.0",
"guzzlehttp/psr7": "~1.1",
"php": ">=5.5.0"
"guzzlehttp/promises": "^1.0",
"guzzlehttp/psr7": "^1.3.1",
"php": ">=5.5"
},
"require-dev": {
"ext-curl": "*",
"phpunit/phpunit": "~4.0",
"psr/log": "~1.0"
"phpunit/phpunit": "^4.0",
"psr/log": "^1.0"
},
"type": "library",
"extra": {
@ -1183,7 +1183,7 @@
"rest",
"web service"
],
"time": "2016-03-21 20:02:09"
"time": "2016-07-15 17:22:37"
},
{
"name": "guzzlehttp/promises",
@ -1605,16 +1605,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "4.0.0",
"version": "4.0.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "900370c81280cc0d942ffbc5912d80464eaee7e9"
"reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/900370c81280cc0d942ffbc5912d80464eaee7e9",
"reference": "900370c81280cc0d942ffbc5912d80464eaee7e9",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/5f3f7e736d6319d5f1fc402aff8b026da26709a3",
"reference": "5f3f7e736d6319d5f1fc402aff8b026da26709a3",
"shasum": ""
},
"require": {
@ -1623,7 +1623,7 @@
"phpunit/php-text-template": "~1.2",
"phpunit/php-token-stream": "^1.4.2",
"sebastian/code-unit-reverse-lookup": "~1.0",
"sebastian/environment": "^1.3.2",
"sebastian/environment": "^1.3.2 || ^2.0",
"sebastian/version": "~1.0|~2.0"
},
"require-dev": {
@ -1664,7 +1664,7 @@
"testing",
"xunit"
],
"time": "2016-06-03 05:03:56"
"time": "2016-07-26 14:39:29"
},
{
"name": "phpunit/php-file-iterator",
@ -1849,16 +1849,16 @@
},
{
"name": "phpunit/phpunit",
"version": "5.4.6",
"version": "5.4.8",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "2f1fc94b77ea6418bd6a06c64a1dac0645fbce59"
"reference": "3132365e1430c091f208e120b8845d39c25f20e6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2f1fc94b77ea6418bd6a06c64a1dac0645fbce59",
"reference": "2f1fc94b77ea6418bd6a06c64a1dac0645fbce59",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3132365e1430c091f208e120b8845d39c25f20e6",
"reference": "3132365e1430c091f208e120b8845d39c25f20e6",
"shasum": ""
},
"require": {
@ -1870,7 +1870,7 @@
"myclabs/deep-copy": "~1.3",
"php": "^5.6 || ^7.0",
"phpspec/prophecy": "^1.3.1",
"phpunit/php-code-coverage": "^4.0",
"phpunit/php-code-coverage": "^4.0.1",
"phpunit/php-file-iterator": "~1.4",
"phpunit/php-text-template": "~1.2",
"phpunit/php-timer": "^1.0.6",
@ -1923,7 +1923,7 @@
"testing",
"xunit"
],
"time": "2016-06-16 06:01:15"
"time": "2016-07-26 14:48:00"
},
{
"name": "phpunit/phpunit-mock-objects",
@ -2599,16 +2599,16 @@
},
{
"name": "squizlabs/php_codesniffer",
"version": "2.6.1",
"version": "2.6.2",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "fb72ed32f8418db5e7770be1653e62e0d6f5dd3d"
"reference": "4edb770cb853def6e60c93abb088ad5ac2010c83"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/fb72ed32f8418db5e7770be1653e62e0d6f5dd3d",
"reference": "fb72ed32f8418db5e7770be1653e62e0d6f5dd3d",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/4edb770cb853def6e60c93abb088ad5ac2010c83",
"reference": "4edb770cb853def6e60c93abb088ad5ac2010c83",
"shasum": ""
},
"require": {
@ -2673,20 +2673,20 @@
"phpcs",
"standards"
],
"time": "2016-05-30 22:24:32"
"time": "2016-07-13 23:29:13"
},
{
"name": "symfony/browser-kit",
"version": "v3.1.1",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/browser-kit.git",
"reference": "b645a9b23d6c0eeba5ac823fa87bf010db9aff22"
"reference": "dcf41ed026b0499254385b5c88f03247b2ba010b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/b645a9b23d6c0eeba5ac823fa87bf010db9aff22",
"reference": "b645a9b23d6c0eeba5ac823fa87bf010db9aff22",
"url": "https://api.github.com/repos/symfony/browser-kit/zipball/dcf41ed026b0499254385b5c88f03247b2ba010b",
"reference": "dcf41ed026b0499254385b5c88f03247b2ba010b",
"shasum": ""
},
"require": {
@ -2730,20 +2730,20 @@
],
"description": "Symfony BrowserKit Component",
"homepage": "https://symfony.com",
"time": "2016-03-04 07:56:56"
"time": "2016-06-29 05:41:56"
},
{
"name": "symfony/config",
"version": "v2.8.7",
"version": "v2.8.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/config.git",
"reference": "a2edd59c2163c65747fc3f35d132b5a39266bd05"
"reference": "0926e69411eba491803dbafb9f1f233e2ced58d0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/config/zipball/a2edd59c2163c65747fc3f35d132b5a39266bd05",
"reference": "a2edd59c2163c65747fc3f35d132b5a39266bd05",
"url": "https://api.github.com/repos/symfony/config/zipball/0926e69411eba491803dbafb9f1f233e2ced58d0",
"reference": "0926e69411eba491803dbafb9f1f233e2ced58d0",
"shasum": ""
},
"require": {
@ -2783,20 +2783,20 @@
],
"description": "Symfony Config Component",
"homepage": "https://symfony.com",
"time": "2016-06-06 11:11:27"
"time": "2016-06-29 05:31:50"
},
{
"name": "symfony/console",
"version": "v3.1.1",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "64a4d43b045f07055bb197650159769604cb2a92"
"reference": "747154aa69b0f83cd02fc9aa554836dee417631a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/64a4d43b045f07055bb197650159769604cb2a92",
"reference": "64a4d43b045f07055bb197650159769604cb2a92",
"url": "https://api.github.com/repos/symfony/console/zipball/747154aa69b0f83cd02fc9aa554836dee417631a",
"reference": "747154aa69b0f83cd02fc9aa554836dee417631a",
"shasum": ""
},
"require": {
@ -2843,20 +2843,20 @@
],
"description": "Symfony Console Component",
"homepage": "https://symfony.com",
"time": "2016-06-14 11:18:07"
"time": "2016-06-29 07:02:31"
},
{
"name": "symfony/css-selector",
"version": "v3.1.1",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
"reference": "c526d7b3cb4fe1673c6a34e13be2ff63f519df99"
"reference": "2851e1932d77ce727776154d659b232d061e816a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/c526d7b3cb4fe1673c6a34e13be2ff63f519df99",
"reference": "c526d7b3cb4fe1673c6a34e13be2ff63f519df99",
"url": "https://api.github.com/repos/symfony/css-selector/zipball/2851e1932d77ce727776154d659b232d061e816a",
"reference": "2851e1932d77ce727776154d659b232d061e816a",
"shasum": ""
},
"require": {
@ -2896,20 +2896,20 @@
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
"time": "2016-06-06 11:42:41"
"time": "2016-06-29 05:41:56"
},
{
"name": "symfony/dom-crawler",
"version": "v3.1.1",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/dom-crawler.git",
"reference": "12aa63fd41b060d2bee9a34623d29eda70bc8fe3"
"reference": "99ec4a23330fcd0c8667095f3ef7aa204ffd9dc0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/12aa63fd41b060d2bee9a34623d29eda70bc8fe3",
"reference": "12aa63fd41b060d2bee9a34623d29eda70bc8fe3",
"url": "https://api.github.com/repos/symfony/dom-crawler/zipball/99ec4a23330fcd0c8667095f3ef7aa204ffd9dc0",
"reference": "99ec4a23330fcd0c8667095f3ef7aa204ffd9dc0",
"shasum": ""
},
"require": {
@ -2952,20 +2952,20 @@
],
"description": "Symfony DomCrawler Component",
"homepage": "https://symfony.com",
"time": "2016-05-13 15:49:09"
"time": "2016-06-29 05:41:56"
},
{
"name": "symfony/event-dispatcher",
"version": "v2.8.7",
"version": "v2.8.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/event-dispatcher.git",
"reference": "2a6b8713f8bdb582058cfda463527f195b066110"
"reference": "b180b70439dca70049b6b9b7e21d75e6e5d7aca9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2a6b8713f8bdb582058cfda463527f195b066110",
"reference": "2a6b8713f8bdb582058cfda463527f195b066110",
"url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b180b70439dca70049b6b9b7e21d75e6e5d7aca9",
"reference": "b180b70439dca70049b6b9b7e21d75e6e5d7aca9",
"shasum": ""
},
"require": {
@ -3012,20 +3012,20 @@
],
"description": "Symfony EventDispatcher Component",
"homepage": "https://symfony.com",
"time": "2016-06-06 11:11:27"
"time": "2016-06-29 05:29:29"
},
{
"name": "symfony/filesystem",
"version": "v2.8.7",
"version": "v2.8.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "dee379131dceed90a429e951546b33edfe7dccbb"
"reference": "7258ddd6f987053f21fa43d03430580ba54e6096"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/dee379131dceed90a429e951546b33edfe7dccbb",
"reference": "dee379131dceed90a429e951546b33edfe7dccbb",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/7258ddd6f987053f21fa43d03430580ba54e6096",
"reference": "7258ddd6f987053f21fa43d03430580ba54e6096",
"shasum": ""
},
"require": {
@ -3061,20 +3061,20 @@
],
"description": "Symfony Filesystem Component",
"homepage": "https://symfony.com",
"time": "2016-04-12 18:01:21"
"time": "2016-06-29 05:31:50"
},
{
"name": "symfony/finder",
"version": "v3.1.1",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "40d17ed287bf51a2f884c4619ce8ff2a1c5cd219"
"reference": "8201978de88a9fa0923e18601bb17f1df9c721e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/40d17ed287bf51a2f884c4619ce8ff2a1c5cd219",
"reference": "40d17ed287bf51a2f884c4619ce8ff2a1c5cd219",
"url": "https://api.github.com/repos/symfony/finder/zipball/8201978de88a9fa0923e18601bb17f1df9c721e7",
"reference": "8201978de88a9fa0923e18601bb17f1df9c721e7",
"shasum": ""
},
"require": {
@ -3110,20 +3110,20 @@
],
"description": "Symfony Finder Component",
"homepage": "https://symfony.com",
"time": "2016-05-13 18:06:41"
"time": "2016-06-29 05:41:56"
},
{
"name": "symfony/form",
"version": "v2.8.7",
"version": "v2.8.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/form.git",
"reference": "edfb109ac1ebb755ad0ce964f32e43ddbddae7bb"
"reference": "fd24c42112a3c434a083c498b9550331bb96eaa3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/form/zipball/edfb109ac1ebb755ad0ce964f32e43ddbddae7bb",
"reference": "edfb109ac1ebb755ad0ce964f32e43ddbddae7bb",
"url": "https://api.github.com/repos/symfony/form/zipball/fd24c42112a3c434a083c498b9550331bb96eaa3",
"reference": "fd24c42112a3c434a083c498b9550331bb96eaa3",
"shasum": ""
},
"require": {
@ -3184,20 +3184,20 @@
],
"description": "Symfony Form Component",
"homepage": "https://symfony.com",
"time": "2016-06-06 11:11:27"
"time": "2016-06-29 05:29:29"
},
{
"name": "symfony/intl",
"version": "v3.0.7",
"version": "v3.0.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/intl.git",
"reference": "96ce72abb3538c4256585ce278cf30ceb1e86f16"
"reference": "330c95b92989c1a515794bb87aabfe7492148f61"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/intl/zipball/96ce72abb3538c4256585ce278cf30ceb1e86f16",
"reference": "96ce72abb3538c4256585ce278cf30ceb1e86f16",
"url": "https://api.github.com/repos/symfony/intl/zipball/330c95b92989c1a515794bb87aabfe7492148f61",
"reference": "330c95b92989c1a515794bb87aabfe7492148f61",
"shasum": ""
},
"require": {
@ -3259,20 +3259,20 @@
"l10n",
"localization"
],
"time": "2016-05-13 18:03:36"
"time": "2016-06-29 05:40:45"
},
{
"name": "symfony/options-resolver",
"version": "v2.8.7",
"version": "v2.8.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "ddad0be20d6f2cb266cded8c349e046731df09b8"
"reference": "b6f982782a0624d37b5416c2c96fb99ce5ab74d5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/ddad0be20d6f2cb266cded8c349e046731df09b8",
"reference": "ddad0be20d6f2cb266cded8c349e046731df09b8",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/b6f982782a0624d37b5416c2c96fb99ce5ab74d5",
"reference": "b6f982782a0624d37b5416c2c96fb99ce5ab74d5",
"shasum": ""
},
"require": {
@ -3313,7 +3313,7 @@
"configuration",
"options"
],
"time": "2016-05-24 10:00:02"
"time": "2016-06-29 05:29:29"
},
{
"name": "symfony/polyfill-intl-icu",
@ -3375,16 +3375,16 @@
},
{
"name": "symfony/process",
"version": "v3.1.1",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "6350e63ed9c232da50e00f00a7e0330f066387a2"
"reference": "5c11a1a4d4016662eeaf0f8757958c7de069f9a0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/6350e63ed9c232da50e00f00a7e0330f066387a2",
"reference": "6350e63ed9c232da50e00f00a7e0330f066387a2",
"url": "https://api.github.com/repos/symfony/process/zipball/5c11a1a4d4016662eeaf0f8757958c7de069f9a0",
"reference": "5c11a1a4d4016662eeaf0f8757958c7de069f9a0",
"shasum": ""
},
"require": {
@ -3420,20 +3420,20 @@
],
"description": "Symfony Process Component",
"homepage": "https://symfony.com",
"time": "2016-06-06 11:42:41"
"time": "2016-06-29 05:42:25"
},
{
"name": "symfony/property-access",
"version": "v3.0.7",
"version": "v3.0.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/property-access.git",
"reference": "6a958de831366005603432ef438c22cd93fad1ef"
"reference": "dedc1109b7c4f0454dffb00b7caa449a39a3c5dd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/property-access/zipball/6a958de831366005603432ef438c22cd93fad1ef",
"reference": "6a958de831366005603432ef438c22cd93fad1ef",
"url": "https://api.github.com/repos/symfony/property-access/zipball/dedc1109b7c4f0454dffb00b7caa449a39a3c5dd",
"reference": "dedc1109b7c4f0454dffb00b7caa449a39a3c5dd",
"shasum": ""
},
"require": {
@ -3480,20 +3480,20 @@
"property path",
"reflection"
],
"time": "2016-05-29 09:50:11"
"time": "2016-06-29 05:40:00"
},
{
"name": "symfony/routing",
"version": "v2.8.7",
"version": "v2.8.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/routing.git",
"reference": "4cbc81aa378869445fbd2d18c8c8b4a056c632a0"
"reference": "cdd298b1d45b9882de0905856e89171bf487c6d5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/routing/zipball/4cbc81aa378869445fbd2d18c8c8b4a056c632a0",
"reference": "4cbc81aa378869445fbd2d18c8c8b4a056c632a0",
"url": "https://api.github.com/repos/symfony/routing/zipball/cdd298b1d45b9882de0905856e89171bf487c6d5",
"reference": "cdd298b1d45b9882de0905856e89171bf487c6d5",
"shasum": ""
},
"require": {
@ -3555,20 +3555,20 @@
"uri",
"url"
],
"time": "2016-05-30 06:57:11"
"time": "2016-06-29 05:29:29"
},
{
"name": "symfony/twig-bridge",
"version": "v2.8.7",
"version": "v2.8.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/twig-bridge.git",
"reference": "360f3330e1df67edde1bb5d72fe5aa7c1bc74b9d"
"reference": "b2db0187b7b805bb349ffe2714fbc846124c71ba"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/360f3330e1df67edde1bb5d72fe5aa7c1bc74b9d",
"reference": "360f3330e1df67edde1bb5d72fe5aa7c1bc74b9d",
"url": "https://api.github.com/repos/symfony/twig-bridge/zipball/b2db0187b7b805bb349ffe2714fbc846124c71ba",
"reference": "b2db0187b7b805bb349ffe2714fbc846124c71ba",
"shasum": ""
},
"require": {
@ -3636,20 +3636,20 @@
],
"description": "Symfony Twig Bridge",
"homepage": "https://symfony.com",
"time": "2016-06-06 11:11:27"
"time": "2016-06-29 05:29:29"
},
{
"name": "symfony/yaml",
"version": "v3.1.1",
"version": "v3.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "c5a7e7fc273c758b92b85dcb9c46149ccda89623"
"reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/c5a7e7fc273c758b92b85dcb9c46149ccda89623",
"reference": "c5a7e7fc273c758b92b85dcb9c46149ccda89623",
"url": "https://api.github.com/repos/symfony/yaml/zipball/2884c26ce4c1d61aebf423a8b912950fe7c764de",
"reference": "2884c26ce4c1d61aebf423a8b912950fe7c764de",
"shasum": ""
},
"require": {
@ -3685,7 +3685,7 @@
],
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"time": "2016-06-14 11:18:07"
"time": "2016-06-29 05:41:56"
},
{
"name": "twig/extensions",

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,199 +0,0 @@
# Copyright (C) 2015
# This file is distributed under the same license as the package.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: http://support.mailpoet.com/\n"
"POT-Creation-Date: 2015-07-29 16:59:53+00:00\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2015-07-29 19:00+0100\n"
"Last-Translator: MailPoet i18n (https://www.transifex.com/organization/wysija)\n"
"Language-Team: MailPoet i18n <https://www.transifex.com/organization/wysija>\n"
"X-Generator: Poedit 1.7.4\n"
"X-Poedit-KeywordsList: __;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-Basepath: ../\n"
"X-Textdomain-Support: yes\n"
"Language: en\n"
"X-Poedit-SearchPath-0: .\n"
#: lib/form/renderer.php:91
msgid "Year, month, day"
msgstr ""
#: lib/form/renderer.php:92
msgid "Year, month"
msgstr ""
#: lib/form/renderer.php:93
msgid "Month (January, February,...)"
msgstr ""
#: lib/form/renderer.php:94 lib/form/renderer.php:519
msgid "Year"
msgstr ""
#: lib/form/renderer.php:108
msgid "January"
msgstr ""
#: lib/form/renderer.php:109
msgid "February"
msgstr ""
#: lib/form/renderer.php:110
msgid "March"
msgstr ""
#: lib/form/renderer.php:111
msgid "April"
msgstr ""
#: lib/form/renderer.php:112
msgid "May"
msgstr ""
#: lib/form/renderer.php:113
msgid "June"
msgstr ""
#: lib/form/renderer.php:114
msgid "July"
msgstr ""
#: lib/form/renderer.php:115
msgid "August"
msgstr ""
#: lib/form/renderer.php:116
msgid "September"
msgstr ""
#: lib/form/renderer.php:117
msgid "October"
msgstr ""
#: lib/form/renderer.php:118
msgid "November"
msgstr ""
#: lib/form/renderer.php:119
msgid "December"
msgstr ""
#: lib/form/renderer.php:509
msgid "Day"
msgstr ""
#: lib/form/renderer.php:514
msgid "Month"
msgstr ""
#: lib/form/widget.php:11
msgid "MailPoet Subscription Form"
msgstr ""
#: lib/form/widget.php:13
msgid "Newsletter subscription form"
msgstr ""
#: lib/form/widget.php:36
msgid "Subscribe to our Newsletter"
msgstr ""
#: lib/form/widget.php:56
msgid "Title:"
msgstr ""
#: lib/form/widget.php:76
msgid "Create a new form"
msgstr ""
#: lib/form/widget.php:109
msgid "Newsletter"
msgstr ""
#: views/form/editor.html:4 views/form/editor.html:26
msgid "Save"
msgstr ""
#: views/form/editor.html:5
msgid "Translatable string with a link %shere%s"
msgstr ""
#: views/form/editor.html:6
msgid "deleted one message"
msgid_plural "deleted %d messages"
msgstr[0] ""
msgstr[1] ""
#: views/form/editor.html:34
msgid "Settings"
msgstr ""
#: views/form/editor.html:42
msgid "This form adds subscribers to these lists:"
msgstr ""
#: views/form/editor.html:44
msgid "Choose a list"
msgstr ""
#: views/form/editor.html:57
msgid "You have to select at least 1 list"
msgstr ""
#: views/form/editor.html:63
msgid "After submit..."
msgstr ""
#: views/form/editor.html:65
msgid "Show message"
msgstr ""
#: views/form/editor.html:69
msgid "Go to page"
msgstr ""
#: views/form/editor.html:76
msgid "Check your inbox now to confirm your subscription."
msgstr ""
#: views/form/editor.html:98
msgid "Shortcodes"
msgstr ""
#: views/form/editor.html:103
msgid "You can easily add this form to your theme's in the"
msgstr ""
#: views/form/editor.html:103
msgid "Widgets areas"
msgstr ""
#: views/form/editor.html:126
msgid "[link_html]HTML[/link_html]"
msgstr ""
#: views/form/editor.html:138
msgid "Fields"
msgstr ""
#: views/form/editor.html:144
msgid "Add New Field"
msgstr ""
#: views/form/editor.html:153
msgid "Styles"
msgstr ""
#: views/form/editor.html:157
msgid "Preview"
msgstr ""
#: views/index.html:4
msgid "monvier"
msgstr ""

Binary file not shown.

View File

@ -1,215 +0,0 @@
# Copyright (C) 2015
# This file is distributed under the same license as the package.
msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: http://support.mailpoet.com/\n"
"POT-Creation-Date: 2015-07-30 14:08:29+00:00\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"PO-Revision-Date: 2015-07-30 16:19+0100\n"
"Last-Translator: MailPoet i18n (https://www.transifex.com/organization/wysija)\n"
"Language-Team: MailPoet i18n <https://www.transifex.com/organization/wysija>\n"
"X-Generator: Poedit 1.7.4\n"
"X-Poedit-KeywordsList: __;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"X-Poedit-SourceCharset: UTF-8\n"
"X-Poedit-Basepath: ../\n"
"X-Textdomain-Support: yes\n"
"Language: fr\n"
"X-Poedit-SearchPath-0: .\n"
#: lib/config/initializer.php:259
msgid "New form"
msgstr ""
#: lib/config/initializer.php:264
msgid "Check your inbox or spam folder now to confirm your subscription."
msgstr ""
#: lib/config/initializer.php:270 lib/config/initializer.php:275
msgid "Email"
msgstr ""
#: lib/config/initializer.php:280
msgid "Submit"
msgstr ""
#: lib/config/initializer.php:285
msgid "Subscribe!"
msgstr ""
#: lib/form/renderer.php:91
msgid "Year, month, day"
msgstr ""
#: lib/form/renderer.php:92
msgid "Year, month"
msgstr ""
#: lib/form/renderer.php:93
msgid "Month (January, February,...)"
msgstr ""
#: lib/form/renderer.php:94 lib/form/renderer.php:519
msgid "Year"
msgstr ""
#: lib/form/renderer.php:108
msgid "January"
msgstr ""
#: lib/form/renderer.php:109
msgid "February"
msgstr ""
#: lib/form/renderer.php:110
msgid "March"
msgstr ""
#: lib/form/renderer.php:111
msgid "April"
msgstr ""
#: lib/form/renderer.php:112
msgid "May"
msgstr ""
#: lib/form/renderer.php:113
msgid "June"
msgstr ""
#: lib/form/renderer.php:114
msgid "July"
msgstr ""
#: lib/form/renderer.php:115
msgid "August"
msgstr ""
#: lib/form/renderer.php:116
msgid "September"
msgstr ""
#: lib/form/renderer.php:117
msgid "October"
msgstr ""
#: lib/form/renderer.php:118
msgid "November"
msgstr ""
#: lib/form/renderer.php:119
msgid "December"
msgstr ""
#: lib/form/renderer.php:509
msgid "Day"
msgstr ""
#: lib/form/renderer.php:514
msgid "Month"
msgstr ""
#: lib/form/widget.php:11
msgid "MailPoet Subscription Form"
msgstr ""
#: lib/form/widget.php:13
msgid "Newsletter subscription form"
msgstr ""
#: lib/form/widget.php:36
msgid "Subscribe to our Newsletter"
msgstr ""
#: lib/form/widget.php:56
msgid "Title:"
msgstr ""
#: lib/form/widget.php:76
msgid "Create a new form"
msgstr ""
#: lib/form/widget.php:109
msgid "Newsletter"
msgstr ""
#: views/form/editor.html:8
msgid "Edit name' "
msgstr ""
#: views/form/editor.html:23
msgid "Save"
msgstr "Sauvegarder"
#: views/form/editor.html:31
msgid "Settings"
msgstr "Réglages"
#: views/form/editor.html:39
msgid "This form adds subscribers to these lists:"
msgstr ""
#: views/form/editor.html:41
msgid "Choose a list"
msgstr ""
#: views/form/editor.html:46
msgid "You have to select at least 1 list"
msgstr ""
#: views/form/editor.html:52
msgid "After submit..."
msgstr "Après l'inscription"
#: views/form/editor.html:54
msgid "Show message"
msgstr "Afficher le message"
#: views/form/editor.html:58
msgid "Go to page"
msgstr "Aller à la page"
#: views/form/editor.html:65
msgid "Check your inbox now to confirm your subscription."
msgstr ""
#: views/form/editor.html:85
msgid "Shortcodes"
msgstr ""
#: views/form/editor.html:90
msgid "You can easily add this form to your theme's in the %sWidgets area%s"
msgstr ""
#: views/form/editor.html:96
msgid "%sHTML%s"
msgstr ""
#: views/form/editor.html:119
msgid "Fields"
msgstr ""
#: views/form/editor.html:125
msgid "Add New Field"
msgstr ""
#: views/form/editor.html:134
msgid "Styles"
msgstr ""
#: views/form/editor.html:138
msgid "Preview"
msgstr ""
#: views/index.html:5
msgid "Translatable string with a link %shere%s"
msgstr ""
#: views/index.html:6 views/index.html:7
msgid "deleted one message"
msgid_plural "deleted %d messages"
msgstr[0] ""
msgstr[1] ""

View File

@ -1,90 +1,135 @@
<?php
namespace MailPoet\API;
use MailPoet\Util\Helpers;
use \MailPoet\Util\Security;
if(!defined('ABSPATH')) exit;
class API {
public $api_request;
public $endpoint;
public $action;
public $data;
const NAME = 'mailpoet_api';
const ENDPOINT_NAMESPACE = '\MailPoet\API\Endpoints\\';
const RESPONSE_ERROR = 404;
function __construct($api_data = false) {
$api_data = ($api_data) ? $api_data : $_GET;
$this->api_request = isset($api_data[self::NAME]);
$this->endpoint = isset($api_data['endpoint']) ?
Helpers::underscoreToCamelCase($api_data['endpoint']) :
false;
$this->action = isset($api_data['action']) ?
Helpers::underscoreToCamelCase($api_data['action']) :
false;
$this->data = isset($api_data['data']) ?
self::decodeRequestData($api_data['data']) :
false;
function __construct() {
}
function init() {
$endpoint = self::ENDPOINT_NAMESPACE . ucfirst($this->endpoint);
if(!$this->api_request) return;
if(!$this->endpoint || !class_exists($endpoint)) {
self::terminateRequest(self::RESPONSE_ERROR, __('Invalid API endpoint.'));
}
$this->callEndpoint(
$endpoint,
$this->action,
$this->data
// security token
add_action(
'admin_head',
array($this, 'setToken')
);
// Admin API (Ajax only)
add_action(
'wp_ajax_mailpoet',
array($this, 'setupAdmin')
);
// Public API (Ajax)
add_action(
'wp_ajax_nopriv_mailpoet',
array($this, 'setupPublic')
);
// Public API (Post)
add_action(
'admin_post_nopriv_mailpoet',
array($this, 'setupPublic')
);
}
function callEndpoint($endpoint, $action, $data) {
if(!method_exists($endpoint, $action)) {
self::terminateRequest(self::RESPONSE_ERROR, __('Invalid API action.'));
function setupAdmin() {
if($this->checkToken() === false) {
(new ErrorResponse(
array(
Error::UNAUTHORIZED => __('You need to specify a valid API token.')
),
array(),
Response::STATUS_UNAUTHORIZED
))->send();
}
call_user_func(
array(
$endpoint,
$action
),
$data
if($this->checkPermissions() === false) {
(new ErrorResponse(
array(
Error::FORBIDDEN => __('You do not have the required permissions.')
),
array(),
Response::STATUS_FORBIDDEN
))->send();
}
$this->processRoute();
}
function setupPublic() {
if($this->checkToken() === false) {
(new ErrorResponse(
array(
Error::UNAUTHORIZED => __('You need to specify a valid API token.')
),
array(),
Response::STATUS_UNAUTHORIZED
))->send();
}
$this->processRoute();
}
function processRoute() {
$class = ucfirst($_POST['endpoint']);
$endpoint = __NAMESPACE__ . "\\Endpoints\\" . $class;
$method = $_POST['method'];
$doing_ajax = (bool)(defined('DOING_AJAX') && DOING_AJAX);
if($doing_ajax) {
$data = isset($_POST['data']) ? stripslashes_deep($_POST['data']) : array();
} else {
$data = $_POST;
}
if(is_array($data) && !empty($data)) {
// filter out reserved keywords from data
$reserved_keywords = array(
'token',
'endpoint',
'method',
'mailpoet_redirect'
);
$data = array_diff_key($data, array_flip($reserved_keywords));
}
try {
$endpoint = new $endpoint();
$response = $endpoint->$method($data);
// TODO: remove this condition once the API unification is complete
if(is_object($response)) {
$response->send();
} else {
// LEGACY API
wp_send_json($response);
}
} catch(\Exception $e) {
(new ErrorResponse(
array($e->getCode() => $e->getMessage())
))->send();
}
}
function setToken() {
$global = '<script type="text/javascript">';
$global .= 'var mailpoet_token = "'.Security::generateToken().'";';
$global .= '</script>';
echo $global;
}
function checkPermissions() {
return current_user_can('manage_options');
}
function checkToken() {
return (
isset($_POST['token'])
&&
wp_verify_nonce($_POST['token'], 'mailpoet_token')
);
}
static function decodeRequestData($data) {
$data = base64_decode($data);
if(is_serialized($data)) {
$data = unserialize($data);
}
if(!is_array($data)) {
$data = array();
}
return $data;
}
static function encodeRequestData($data) {
return rtrim(base64_encode(serialize($data)), '=');
}
static function buildRequest($endpoint, $action, $data) {
$data = self::encodeRequestData($data);
$params = array(
self::NAME => '',
'endpoint' => $endpoint,
'action' => $action,
'data' => $data
);
return add_query_arg($params, home_url());
}
static function terminateRequest($code, $message) {
status_header($code, $message);
exit;
}
}

33
lib/API/Endpoint.php Normal file
View File

@ -0,0 +1,33 @@
<?php
namespace MailPoet\API;
if(!defined('ABSPATH')) exit;
abstract class Endpoint {
function successResponse(
$data = array(), $meta = array(), $status = Response::STATUS_OK
) {
return new SuccessResponse($data, $meta, $status);
}
function errorResponse(
$errors = array(), $meta = array(), $status = Response::STATUS_NOT_FOUND
) {
if(empty($errors)) {
$errors = array(
Error::UNKNOWN => __('An unknown error occurred.')
);
}
return new ErrorResponse($errors, $meta, $status);
}
function badRequest($errors = array(), $meta = array()) {
if(empty($errors)) {
$errors = array(
Error::BAD_REQUEST => __('Invalid request parameters.')
);
}
return new ErrorResponse($errors, $meta, Response::STATUS_BAD_REQUEST);
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace MailPoet\API\Endpoints;
use \MailPoet\API\Endpoint as APIEndpoint;
use \MailPoet\API\Error as APIError;
if(!defined('ABSPATH')) exit;
class AutomatedLatestContent extends APIEndpoint {
public $ALC;
function __construct() {
$this->ALC = new \MailPoet\Newsletter\AutomatedLatestContent();
}
function getPostTypes() {
return $this->successResponse(
get_post_types(array(), 'objects')
);
}
function getTaxonomies($data = array()) {
$post_type = (isset($data['postType'])) ? $data['postType'] : 'post';
return $this->successResponse(
get_object_taxonomies($post_type, 'objects')
);
}
function getTerms($data = array()) {
$taxonomies = (isset($data['taxonomies'])) ? $data['taxonomies'] : array();
$search = (isset($data['search'])) ? $data['search'] : '';
$limit = (isset($data['limit'])) ? (int)$data['limit'] : 10;
$page = (isset($data['page'])) ? (int)$data['page'] : 1;
return $this->successResponse(
get_terms(
$taxonomies,
array(
'hide_empty' => false,
'search' => $search,
'number' => $limit,
'offset' => $limit * ($page - 1)
)
)
);
}
function getPosts($data = array()) {
return $this->successResponse(
$this->ALC->getPosts($data)
);
}
function getTransformedPosts($data = array()) {
$posts = $this->ALC->getPosts($data);
return $this->successResponse(
$this->ALC->transformPosts($data, $posts)
);
}
function getBulkTransformedPosts($data = array()) {
$alc = new \MailPoet\Newsletter\AutomatedLatestContent();
$used_posts = array();
$rendered_posts = array();
foreach($data['blocks'] as $block) {
$posts = $alc->getPosts($block, $used_posts);
$rendered_posts[] = $alc->transformPosts($block, $posts);
foreach($posts as $post) {
$used_posts[] = $post->ID;
}
}
return $this->successResponse($rendered_posts);
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace MailPoet\API\Endpoints;
use \MailPoet\API\Endpoint as APIEndpoint;
use \MailPoet\API\Error as APIError;
use MailPoet\Cron\CronHelper;
use MailPoet\Models\Setting;
if(!defined('ABSPATH')) exit;
class Cron extends APIEndpoint {
function getStatus() {
$daemon = Setting::getValue(CronHelper::DAEMON_SETTING, false);
if($daemon === false) {
return $this->errorResponse(array(
APIError::NOT_FOUND => __('Cron daemon is not running.')
));
} else {
return $this->successResponse($daemon);
}
}
}

View File

@ -0,0 +1,57 @@
<?php
namespace MailPoet\API\Endpoints;
use \MailPoet\API\Endpoint as APIEndpoint;
use \MailPoet\API\Error as APIError;
use \MailPoet\Models\CustomField;
if(!defined('ABSPATH')) exit;
class CustomFields extends APIEndpoint {
function getAll() {
$collection = CustomField::findMany();
$custom_fields = array_map(function($custom_field) {
return $custom_field->asArray();
}, $collection);
return $this->successResponse($custom_fields);
}
function delete($data = array()) {
$id = (isset($data['id']) ? (int)$data['id'] : null);
$custom_field = CustomField::findOne($id);
if($custom_field === false) {
return $this->errorResponse(array(
APIError::NOT_FOUND => __('This custom field does not exist.')
));
} else {
$custom_field->delete();
return $this->successResponse($custom_field->asArray());
}
}
function save($data = array()) {
$custom_field = CustomField::createOrUpdate($data);
$errors = $custom_field->getErrors();
if(!empty($errors)) {
return $this->badRequest($errors);
} else {
return $this->successResponse(
CustomField::findOne($custom_field->id)->asArray()
);
}
}
function get($data = array()) {
$id = (isset($data['id']) ? (int)$data['id'] : null);
$custom_field = CustomField::findOne($id);
if($custom_field === false) {
return $this->errorResponse(array(
APIError::NOT_FOUND => __('This custom field does not exist.')
));
} else {
return $this->successResponse($custom_field->asArray());
}
}
}

View File

@ -1,5 +1,6 @@
<?php
namespace MailPoet\Router;
namespace MailPoet\API\Endpoints;
use \MailPoet\Models\Form;
use \MailPoet\Models\StatisticsForms;
use \MailPoet\Form\Renderer as FormRenderer;

View File

@ -0,0 +1,77 @@
<?php
namespace MailPoet\API\Endpoints;
use \MailPoet\API\Endpoint as APIEndpoint;
use \MailPoet\API\Error as APIError;
use MailPoet\Subscribers\ImportExport\Import\MailChimp;
use MailPoet\Models\CustomField;
use MailPoet\Models\Segment;
if(!defined('ABSPATH')) exit;
class ImportExport extends APIEndpoint {
function getMailChimpLists($data) {
try {
$mailChimp = new MailChimp($data['api_key']);
$lists = $mailChimp->getLists();
return $this->successResponse($lists);
} catch(\Exception $e) {
return $this->errorResponse(array(
$e->getCode() => $e->getMessage()
));
}
}
function getMailChimpSubscribers($data) {
try {
$mailChimp = new MailChimp($data['api_key']);
$subscribers = $mailChimp->getSubscribers($data['lists']);
return $this->successResponse($subscribers);
} catch(\Exception $e) {
return $this->errorResponse(array(
$e->getCode() => $e->getMessage()
));
}
}
function addSegment($data) {
$segment = Segment::createOrUpdate($data);
$errors = $segment->getErrors();
if(!empty($errors)) {
return $this->errorResponse($errors);
} else {
return $this->successResponse(
Segment::findOne($segment->id)->asArray()
);
}
}
function processImport($data) {
try {
$import = new \MailPoet\Subscribers\ImportExport\Import\Import(
json_decode($data, true)
);
$process = $import->process();
return $this->successResponse($process);
} catch(\Exception $e) {
return $this->errorResponse(array(
$e->getCode() => $e->getMessage()
));
}
}
function processExport($data) {
try {
$export = new \MailPoet\Subscribers\ImportExport\Export\Export(
json_decode($data, true)
);
$process = $export->process();
return $this->successResponse($process);
} catch(\Exception $e) {
return $this->errorResponse(array(
$e->getCode() => $e->getMessage()
));
}
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace MailPoet\API\Endpoints;
use MailPoet\API\Endpoint as APIEndpoint;
use MailPoet\API\Error as APIError;
if(!defined('ABSPATH')) exit;
class Mailer extends APIEndpoint {
function send($data = array()) {
try {
$mailer = new \MailPoet\Mailer\Mailer(
(isset($data['mailer'])) ? $data['mailer'] : false,
(isset($data['sender'])) ? $data['sender'] : false,
(isset($data['reply_to'])) ? $data['reply_to'] : false
);
$result = $mailer->send($data['newsletter'], $data['subscriber']);
} catch(\Exception $e) {
return $this->errorResponse(array(
$e->getCode() => $e->getMessage()
));
}
if($result === false) {
return $this->errorResponse(array(
APIError::BAD_REQUEST => __("The email could not be sent. Please check your settings.")
));
} else {
return $this->successResponse(null);
}
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace MailPoet\API\Endpoints;
use \MailPoet\API\Endpoint as APIEndpoint;
use \MailPoet\API\Error as APIError;
use MailPoet\Models\NewsletterTemplate;
if(!defined('ABSPATH')) exit;
class NewsletterTemplates extends APIEndpoint {
function get($data = array()) {
$id = (isset($data['id']) ? (int)$data['id'] : false);
$template = NewsletterTemplate::findOne($id);
if($template === false) {
return $this->errorResponse(array(
APIError::NOT_FOUND => __('This template does not exist.')
));
} else {
return $this->successResponse(
$template->asArray()
);
}
}
function getAll() {
$collection = NewsletterTemplate::findMany();
$templates = array_map(function($item) {
return $item->asArray();
}, $collection);
return $this->successResponse($templates);
}
function save($data = array()) {
$template = NewsletterTemplate::createOrUpdate($data);
$errors = $template->getErrors();
if(!empty($errors)) {
return $this->errorResponse($errors);
} else {
return $this->successResponse(
NewsletterTemplate::findOne($template->id)->asArray()
);
}
}
function delete($data = array()) {
$id = (isset($data['id']) ? (int)$data['id'] : false);
$template = NewsletterTemplate::findOne($id);
if($template === false) {
return $this->errorResponse(array(
APIError::NOT_FOUND => __('This template does not exist.')
));
} else {
$template->delete();
return $this->successResponse(null, array('count' => 1));
}
}
}

View File

@ -1,8 +1,11 @@
<?php
namespace MailPoet\Router;
namespace MailPoet\API\Endpoints;
use MailPoet\API\Endpoint as APIEndpoint;
use MailPoet\API\Error as APIError;
use MailPoet\Listing;
use MailPoet\Models\Newsletter;
use MailPoet\Models\SendingQueue;
use MailPoet\Models\Setting;
use MailPoet\Models\NewsletterTemplate;
use MailPoet\Models\NewsletterSegment;
@ -18,10 +21,7 @@ if(!defined('ABSPATH')) exit;
require_once(ABSPATH . 'wp-includes/pluggable.php');
class Newsletters {
function __construct() {
}
class Newsletters extends APIEndpoint {
function get($id = false) {
$newsletter = Newsletter::findOne($id);
if($newsletter === false) {
@ -55,10 +55,7 @@ class Newsletters {
$errors = $newsletter->getErrors();
if(!empty($errors)) {
return array(
'result' => false,
'errors' => $errors
);
return $this->badRequest($errors);
} else {
if(!empty($segment_ids)) {
NewsletterSegment::where('newsletter_id', $newsletter->id)
@ -92,32 +89,39 @@ class Newsletters {
}
}
return array(
'result' => true
return $this->successResponse(
Newsletter::findOne($newsletter->id)->asArray()
);
}
}
function setStatus($data = array()) {
$id = (isset($data['id'])) ? (int)$data['id'] : null;
$id = (isset($data['id'])) ? (int)$data['id'] : false;
$newsletter = Newsletter::findOne($id);
$status = (isset($data['status']) ? $data['status'] : null);
$result = false;
$errors = array();
if($newsletter !== false && $status !== null) {
$newsletter->setStatus($status);
$result = (
$newsletter->getErrors() === false && $newsletter->status === $status
);
if(!$status) {
return $this->badRequest(array(
APIError::BAD_REQUEST => __('You need to specify a status.')
));
}
return array(
'result' => $result,
'status' => $newsletter->status
);
if($newsletter === false) {
return $this->errorResponse(array(
APIError::NOT_FOUND => __('This newsletter does not exist.')
));
}
$newsletter->setStatus($status);
$errors = $newsletter->getErrors();
if(!empty($errors)) {
return $this->errorResponse($errors);
} else {
return $this->successResponse(
$newsletter->asArray()
);
}
}
function restore($id) {
@ -178,11 +182,10 @@ class Newsletters {
}
$newsletter->body = $data['body'];
$newsletter->save();
$wp_user = wp_get_current_user();
$subscriber = Subscriber::where('email', $wp_user->data->user_email)
->findOne();
$subscriber = ($subscriber) ? $subscriber->asArray() : $subscriber;
$preview_url = NewsletterUrl::getViewInBrowserUrl($data, $subscriber);
$subscriber = Subscriber::getCurrentWPUser();
$preview_url = NewsletterUrl::getViewInBrowserUrl(
$data, $subscriber, $queue = false, $preview = true
);
return array(
'result' => true,
'data' => array('url' => $preview_url)
@ -190,7 +193,7 @@ class Newsletters {
}
function sendPreview($data = array()) {
$id = (isset($data['id'])) ? (int)$data['id'] : null;
$id = (isset($data['id'])) ? (int)$data['id'] : false;
$newsletter = Newsletter::findOne($id);
if($newsletter === false) {
@ -207,15 +210,14 @@ class Newsletters {
$newsletter = $newsletter->asArray();
$renderer = new Renderer($newsletter);
$renderer = new Renderer($newsletter, $preview = true);
$rendered_newsletter = $renderer->render();
$divider = '***MailPoet***';
$data_for_shortcodes =
array_merge(array($newsletter['subject']), $rendered_newsletter);
$body = implode($divider, $data_for_shortcodes);
$wp_user = wp_get_current_user();
$subscriber = Subscriber::findOne($wp_user->data->user_email);
$subscriber = Subscriber::getCurrentWPUser();
$subscriber = ($subscriber) ? $subscriber->asArray() : false;
$shortcodes = new \MailPoet\Newsletter\Shortcodes\Shortcodes(
@ -251,8 +253,10 @@ class Newsletters {
$data
);
$listing_data = $listing->get();
$subscriber = Subscriber::getCurrentWPUser();
foreach($listing_data['items'] as $key => $newsletter) {
$queue = false;
if($newsletter->type === Newsletter::TYPE_STANDARD) {
$newsletter
@ -276,8 +280,15 @@ class Newsletters {
->withStatistics();
}
if($newsletter->status === Newsletter::STATUS_SENT ||
$newsletter->status === Newsletter::STATUS_SENDING
) {
$queue = $newsletter->getQueue();
}
// get preview url
$newsletter->preview_url = NewsletterUrl::getViewInBrowserUrl($newsletter);
$newsletter->preview_url = NewsletterUrl::getViewInBrowserUrl(
$newsletter, $subscriber, $queue, $preview = true);
// convert object to array
$listing_data['items'][$key] = $newsletter->asArray();

View File

@ -1,5 +1,6 @@
<?php
namespace MailPoet\Router;
namespace MailPoet\API\Endpoints;
use \MailPoet\Models\Segment;
use \MailPoet\Models\SubscriberSegment;
use \MailPoet\Models\SegmentFilter;

View File

@ -1,5 +1,7 @@
<?php
namespace MailPoet\Router;
namespace MailPoet\API\Endpoints;
use MailPoet\API\Endpoint as APIEndpoint;
use MailPoet\API\Error as APIError;
use MailPoet\Mailer\Mailer;
use MailPoet\Models\Newsletter;
@ -14,63 +16,41 @@ use MailPoet\Util\Helpers;
if(!defined('ABSPATH')) exit;
class SendingQueue {
function add($data) {
class SendingQueue extends APIEndpoint {
function add($data = array()) {
$newsletter_id = (isset($data['newsletter_id'])
? (int)$data['newsletter_id']
: false
);
// check that the newsletter exists
$newsletter = Newsletter::filter('filterWithOptions')
->findOne($newsletter_id);
if($newsletter === false) {
return $this->errorResponse(array(
APIError::NOT_FOUND => __('This newsletter does not exist.')
));
}
// check that the sending method has been configured properly
try {
new Mailer(false);
} catch(\Exception $e) {
return array(
'result' => false,
'errors' => array($e->getMessage())
);
}
// check that the newsletter exists
$newsletter = Newsletter::filter('filterWithOptions')
->findOne($data['newsletter_id']);
if($newsletter === false) {
return array(
'result' => false,
'errors' => array(__('This newsletter does not exist'))
);
}
if($newsletter->type === Newsletter::TYPE_WELCOME ||
$newsletter->type === Newsletter::TYPE_NOTIFICATION
) {
// set newsletter status to active
$result = $newsletter->setStatus(Newsletter::STATUS_ACTIVE);
$errors = $result->getErrors();
if(!empty($errors)) {
return array(
'result' => false,
'errors' => $errors
);
} else {
$message = ($newsletter->type === Newsletter::TYPE_WELCOME) ?
__('Your welcome email has been activated') :
__('Your post notification has been activated');
return array(
'result' => true,
'data' => array(
'message' => $message
)
);
}
return $this->errorResponse(array(
$e->getCode() => $e->getMessage()
));
}
// add newsletter to the sending queue
$queue = SendingQueueModel::whereNull('status')
->where('newsletter_id', $newsletter->id)
->findOne();
->where('newsletter_id', $newsletter->id)
->findOne();
if(!empty($queue)) {
return array(
'result' => false,
'errors' => array(__('This newsletter is already being sent'))
);
return $this->errorResponse(array(
APIError::NOT_FOUND => __('This newsletter is already being sent.')
));
}
$queue = SendingQueueModel::where('newsletter_id', $newsletter->id)
@ -92,8 +72,6 @@ class SendingQueue {
);
$queue->subscribers = null;
$queue->count_total = $queue->count_to_process = 0;
$message = __('The newsletter has been scheduled');
} else {
$segments = $newsletter->segments()->findArray();
$segment_ids = array_map(function($segment) {
@ -104,10 +82,9 @@ class SendingQueue {
$subscribers = Helpers::arrayColumn($subscribers, 'subscriber_id');
$subscribers = array_unique($subscribers);
if(!count($subscribers)) {
return array(
'result' => false,
'errors' => array(__('There are no subscribers'))
);
return $this->errorResponse(array(
APIError::UNKNOWN => __('There are no subscribers in that list!')
));
}
$queue->status = null;
$queue->scheduled_at = null;
@ -120,58 +97,69 @@ class SendingQueue {
// set newsletter status
$newsletter->setStatus(Newsletter::STATUS_SENDING);
$message = __('The newsletter is being sent...');
}
$queue->save();
$errors = $queue->getErrors();
if(!empty($errors)) {
return array(
'result' => false,
'errors' => $errors
);
return $this->errorResponse($errors);
} else {
return array(
'result' => true,
'data' => array(
'message' => $message
)
return $this->successResponse(
$newsletter->getQueue()->asArray()
);
}
}
function pause($newsletter_id) {
function pause($data = array()) {
$newsletter_id = (isset($data['newsletter_id'])
? (int)$data['newsletter_id']
: false
);
$newsletter = Newsletter::findOne($newsletter_id);
$result = false;
if($newsletter !== false) {
if($newsletter === false) {
return $this->errorResponse(array(
APIError::NOT_FOUND => __('This newsletter does not exist.')
));
} else {
$queue = $newsletter->getQueue();
if($queue !== false) {
$result = $queue->pause();
if($queue === false) {
return $this->errorResponse(array(
APIError::UNKNOWN => __('This newsletter has not been sent yet.')
));
} else {
$queue->pause();
return $this->successResponse(
$newsletter->getQueue()->asArray()
);
}
}
return array(
'result' => $result
);
}
function resume($newsletter_id) {
function resume($data = array()) {
$newsletter_id = (isset($data['newsletter_id'])
? (int)$data['newsletter_id']
: false
);
$newsletter = Newsletter::findOne($newsletter_id);
$result = false;
if($newsletter !== false) {
if($newsletter === false) {
return $this->errorResponse(array(
APIError::NOT_FOUND => __('This newsletter does not exist.')
));
} else {
$queue = $newsletter->getQueue();
if($queue !== false) {
$result = $queue->resume();
if($queue === false) {
return $this->errorResponse(array(
APIError::UNKNOWN => __('This newsletter has not been sent yet.')
));
} else {
$queue->resume();
return $this->successResponse(
$newsletter->getQueue()->asArray()
);
}
}
return array(
'result' => $result
);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace MailPoet\API\Endpoints;
use \MailPoet\API\Endpoint as APIEndpoint;
use \MailPoet\API\Error as APIError;
use \MailPoet\Models\Setting;
if(!defined('ABSPATH')) exit;
class Settings extends APIEndpoint {
function get() {
return $this->successResponse(Setting::getAll());
}
function set($settings = array()) {
if(empty($settings)) {
return $this->badRequest(array(
APIError::BAD_REQUEST =>
__("You have not specified any settings to be saved.")
));
} else {
foreach($settings as $name => $value) {
Setting::setValue($name, $value);
}
return $this->successResponse(Setting::getAll());
}
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace MailPoet\API\Endpoints;
use \MailPoet\API\Endpoint as APIEndpoint;
use \MailPoet\Config\Activator;
if(!defined('ABSPATH')) exit;
class Setup extends APIEndpoint {
function reset() {
try {
$activator = new Activator();
$activator->deactivate();
$activator->activate();
return $this->successResponse();
} catch(\Exception $e) {
return $this->errorResponse(array(
$e->getCode() => $e->getMessage()
));
}
}
}

View File

@ -1,5 +1,5 @@
<?php
namespace MailPoet\Router;
namespace MailPoet\API\Endpoints;
use MailPoet\Listing;
use MailPoet\Models\Subscriber;

View File

@ -1,23 +0,0 @@
<?php
namespace MailPoet\API\Endpoints;
use MailPoet\Statistics\Track\Clicks;
use MailPoet\Statistics\Track\Opens;
if(!defined('ABSPATH')) exit;
class Track {
const ENDPOINT = 'track';
const ACTION_CLICK = 'click';
const ACTION_OPEN = 'open';
static function click($data) {
$clicks = new Clicks($data);
$clicks->track();
}
static function open($data) {
$opens = new Opens($data);
$opens->track();
}
}

16
lib/API/Error.php Normal file
View File

@ -0,0 +1,16 @@
<?php
namespace MailPoet\API;
if(!defined('ABSPATH')) exit;
final class Error {
const UNKNOWN = 'unknown';
const BAD_REQUEST = 'bad_request';
const UNAUTHORIZED = 'unauthorized';
const FORBIDDEN = 'forbidden';
const NOT_FOUND = 'not_found';
private function __construct() {
}
}

34
lib/API/ErrorResponse.php Normal file
View File

@ -0,0 +1,34 @@
<?php
namespace MailPoet\API;
if(!defined('ABSPATH')) exit;
class ErrorResponse extends Response {
public $errors;
function __construct($errors = array(), $meta = array(), $status = self::STATUS_NOT_FOUND) {
parent::__construct($status, $meta);
$this->errors = $this->formatErrors($errors);
}
function getData() {
if(empty($this->errors)) {
return null;
} else {
return array(
'errors' => $this->errors
);
}
}
function formatErrors($errors = array()) {
$formatted_errors = array();
foreach($errors as $error => $message) {
$formatted_errors[] = array(
'error' => $error,
'message' => $message
);
}
return $formatted_errors;
}
}

42
lib/API/Response.php Normal file
View File

@ -0,0 +1,42 @@
<?php
namespace MailPoet\API;
if(!defined('ABSPATH')) exit;
abstract class Response {
const STATUS_OK = 200;
const STATUS_BAD_REQUEST = 400;
const STATUS_UNAUTHORIZED = 401;
const STATUS_FORBIDDEN = 403;
const STATUS_NOT_FOUND = 404;
public $status;
public $meta;
function __construct($status, $meta = array()) {
$this->status = $status;
$this->meta = $meta;
}
function send() {
status_header($this->status);
$data = $this->getData();
$response = array();
if(!empty($this->meta)) {
$response['meta'] = $this->meta;
}
if($data !== null) {
$response = array_merge($response, $data);
}
if(empty($response)) {
die();
} else {
wp_send_json($response);
}
}
abstract function getData();
}

View File

@ -0,0 +1,21 @@
<?php
namespace MailPoet\API;
if(!defined('ABSPATH')) exit;
class SuccessResponse extends Response {
public $data;
function __construct($data = array(), $meta = array(), $status = self::STATUS_OK) {
parent::__construct($status, $meta);
$this->data = $data;
}
function getData() {
if($this->data === null) return null;
return array(
'data' => $this->data
);
}
}

View File

@ -2,14 +2,19 @@
namespace MailPoet\Config;
use MailPoet\Models;
use MailPoet\Cron\Supervisor;
use MailPoet\Cron\CronTrigger;
use MailPoet\Router;
use MailPoet\API;
use MailPoet\WP\Notice as WPNotice;
if(!defined('ABSPATH')) exit;
require_once(ABSPATH . 'wp-admin/includes/plugin.php');
class Initializer {
protected $plugin_initialized = false;
function __construct($params = array(
'file' => '',
'version' => '1.0.0'
@ -28,30 +33,6 @@ class Initializer {
add_action('widgets_init', array($this, 'setupWidget'));
}
function setup() {
try {
$this->setupRenderer();
$this->setupLocalizer();
$this->setupMenu();
$this->setupAnalytics();
$this->setupChangelog();
$this->setupShortcodes();
$this->setupHooks();
$this->setupImages();
$this->runQueueSupervisor();
} catch(\Exception $e) {
// if anything goes wrong during init
// automatically deactivate the plugin
deactivate_plugins(Env::$file);
}
}
function onInit() {
$this->setupRouter();
$this->setupAPI();
$this->setupPages();
}
function setupDB() {
\ORM::configure(Env::$db_source_name);
\ORM::configure('username', Env::$db_username);
@ -116,6 +97,51 @@ class Initializer {
$populator->up();
}
function setup() {
try {
$this->setupRenderer();
$this->setupLocalizer();
$this->setupMenu();
$this->setupAnalytics();
$this->setupChangelog();
$this->setupShortcodes();
$this->setupHooks();
$this->setupImages();
$this->setupCronTrigger();
$this->plugin_initialized = true;
} catch(\Exception $e) {
$this->handleFailedInitialization($e);
}
}
function onInit() {
if(!$this->plugin_initialized) {
return;
}
try {
$this->setupAPI();
$this->setupFrontRouter();
$this->setupPages();
} catch(\Exception $e) {
$this->handleFailedInitialization($e);
}
}
function setupWidget() {
if(!$this->plugin_initialized) {
return;
}
try {
$widget = new Widget($this->renderer);
$widget->init();
} catch(\Exception $e) {
$this->handleFailedInitialization($e);
}
}
function setupRenderer() {
$renderer = new Renderer();
$this->renderer = $renderer->init();
@ -131,19 +157,9 @@ class Initializer {
$menu->init();
}
function setupRouter() {
$router = new Router\Router();
$router->init();
}
function setupWidget() {
$widget = new Widget($this->renderer);
$widget->init();
}
function setupAnalytics() {
$widget = new Analytics();
$widget->init();
$analytics = new Analytics();
$analytics->init();
}
function setupChangelog() {
@ -167,21 +183,25 @@ class Initializer {
}
function setupAPI() {
$API = new \MailPoet\API\API();
$API->init();
$api = new API\API();
$api->init();
}
function runQueueSupervisor() {
if(php_sapi_name() === 'cli') return;
try {
$supervisor = new Supervisor();
$supervisor->checkDaemon();
} catch(\Exception $e) {
// Prevent Daemon exceptions from breaking out and breaking UI
}
function setupFrontRouter() {
$router = new Router\Front();
$router->init();
}
function setupCronTrigger() {
$cron_trigger = new CronTrigger();
$cron_trigger->init();
}
function setupImages() {
add_image_size('mailpoet_newsletter_max', 1320);
}
function handleFailedInitialization($message) {
return WPNotice::displayError($message);
}
}

View File

@ -1,6 +1,7 @@
<?php
namespace MailPoet\Config;
use MailPoet\Cron\CronTrigger;
use MailPoet\Form\Block;
use MailPoet\Form\Renderer as FormRenderer;
use MailPoet\Models\CustomField;
@ -105,8 +106,8 @@ class Menu {
$segments_page = add_submenu_page(
$main_page_slug,
$this->setPageTitle(__('Segments')),
__('Segments'),
$this->setPageTitle(__('Lists')),
__('Lists'),
'manage_options',
'mailpoet-segments',
array($this, 'segments')
@ -169,8 +170,8 @@ class Menu {
add_submenu_page(
true,
$this->setPageTitle(__('Form')),
__('Form editor'),
$this->setPageTitle(__('Form Editor')),
__('Form Editor'),
'manage_options',
'mailpoet-form-editor',
array($this, 'formEditor')
@ -255,6 +256,7 @@ class Menu {
$data = array(
'settings' => $settings,
'segments' => Segment::getPublic()->findArray(),
'cron_trigger' => CronTrigger::getAvailableMethods(),
'pages' => Pages::getAll(),
'flags' => $flags,
'current_user' => wp_get_current_user(),
@ -377,7 +379,12 @@ class Menu {
function import() {
$import = new ImportExportFactory('import');
$data = $import->bootstrap();
$data['sub_menu'] = 'mailpoet-subscribers';
$data = array_merge($data, array(
'date_types' => Block\Date::getDateTypes(),
'date_formats' => Block\Date::getDateFormats(),
'month_names' => Block\Date::getMonthNames(),
'sub_menu' => 'mailpoet-subscribers'
));
echo $this->renderer->render('subscribers/importExport/import.html', $data);
}

View File

@ -2,9 +2,15 @@
namespace MailPoet\Config;
use MailPoet\Config\PopulatorData\Templates\FranksRoastHouseTemplate;
use MailPoet\Config\PopulatorData\Templates\BlankTemplate;
use MailPoet\Config\PopulatorData\Templates\WelcomeTemplate;
use MailPoet\Config\PopulatorData\Templates\PostNotificationsBlankTemplate;
use MailPoet\Config\PopulatorData\Templates\NewsletterBlank1Column;
use MailPoet\Config\PopulatorData\Templates\NewsletterBlank12Column;
use MailPoet\Config\PopulatorData\Templates\NewsletterBlank121Column;
use MailPoet\Config\PopulatorData\Templates\NewsletterBlank13Column;
use MailPoet\Config\PopulatorData\Templates\PostNotificationsBlank1Column;
use MailPoet\Config\PopulatorData\Templates\WelcomeBlank1Column;
use MailPoet\Config\PopulatorData\Templates\WelcomeBlank12Column;
use MailPoet\Config\PopulatorData\Templates\SimpleText;
use MailPoet\Cron\CronTrigger;
use \MailPoet\Models\Segment;
use \MailPoet\Segments\WP;
use \MailPoet\Models\Setting;
@ -69,10 +75,10 @@ class Populator {
private function createDefaultSettings() {
$current_user = wp_get_current_user();
if(!Setting::getValue('task_scheduler')) {
if(!Setting::getValue(CronTrigger::SETTING_NAME)) {
// disable task scheduler (cron) be default
Setting::setValue('task_scheduler', array(
'method' => 'WordPress'
Setting::setValue(CronTrigger::SETTING_NAME, array(
'method' => CronTrigger::DEFAULT_METHOD
));
}
@ -180,9 +186,14 @@ class Populator {
private function newsletterTemplates() {
return array(
(new FranksRoastHouseTemplate(Env::$assets_url))->get(),
(new BlankTemplate(Env::$assets_url))->get(),
(new WelcomeTemplate(Env::$assets_url))->get(),
(new PostNotificationsBlankTemplate(Env::$assets_url))->get(),
(new NewsletterBlank1Column(Env::$assets_url))->get(),
(new NewsletterBlank12Column(Env::$assets_url))->get(),
(new NewsletterBlank121Column(Env::$assets_url))->get(),
(new NewsletterBlank13Column(Env::$assets_url))->get(),
(new PostNotificationsBlank1Column(Env::$assets_url))->get(),
(new WelcomeBlank1Column(Env::$assets_url))->get(),
(new WelcomeBlank12Column(Env::$assets_url))->get(),
(new SimpleText(Env::$assets_url))->get(),
);
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -67,17 +67,16 @@ class Shortcodes {
}
function getArchive($params) {
$segment_ids = array();
if(!empty($params['segments'])) {
$segment_ids = array_map(function($segment_id) {
return (int)trim($segment_id);
}, explode(',', $params['segments']));
}
$newsletters = array();
$html = '';
// TODO: needs more advanced newsletters in order to finish
$newsletters = Newsletter::limit(10)->orderByDesc('created_at')->findMany();
$newsletters = Newsletter::getArchives($segment_ids);
if(empty($newsletters)) {
return apply_filters(
@ -109,7 +108,7 @@ class Shortcodes {
function renderArchiveDate($newsletter) {
return date_i18n(
get_option('date_format'),
strtotime($newsletter->created_at)
strtotime($newsletter->processed_at)
);
}

View File

@ -1,9 +1,9 @@
<?php
namespace MailPoet\Cron;
use MailPoet\API\API;
use MailPoet\API\Endpoints\Queue as QueueAPI;
use MailPoet\Models\Setting;
use MailPoet\Router\Endpoints\Queue as QueueEndpoint;
use MailPoet\Router\Front as FrontRouter;
use MailPoet\Util\Security;
if(!defined('ABSPATH')) exit;
@ -12,6 +12,7 @@ class CronHelper {
const DAEMON_EXECUTION_LIMIT = 20;
const DAEMON_EXECUTION_TIMEOUT = 35;
const DAEMON_REQUEST_TIMEOUT = 2;
const DAEMON_SETTING = 'cron_daemon';
static function createDaemon($token) {
$daemon = array(
@ -22,30 +23,46 @@ class CronHelper {
return $daemon;
}
static function restartDaemon($token) {
return self::createDaemon($token);
}
static function getDaemon() {
return Setting::getValue('cron_daemon');
return Setting::getValue(self::DAEMON_SETTING);
}
static function saveDaemon($daemon) {
$daemon['updated_at'] = time();
return Setting::setValue(
'cron_daemon',
self::DAEMON_SETTING,
$daemon
);
}
static function stopDaemon() {
$daemon = self::getDaemon();
$daemon['status'] = Daemon::STATUS_STOPPED;
return self::saveDaemon($daemon);
}
static function deleteDaemon() {
return Setting::deleteValue(self::DAEMON_SETTING);
}
static function createToken() {
return Security::generateRandomString();
}
static function accessDaemon($token, $timeout = self::DAEMON_REQUEST_TIMEOUT) {
$data = array('token' => $token);
$url = API::buildRequest(
QueueAPI::ENDPOINT,
QueueAPI::ACTION_RUN,
$url = FrontRouter::buildRequest(
QueueEndpoint::ENDPOINT,
QueueEndpoint::ACTION_RUN,
$data
);
$args = array(
'blocking' => false,
'sslverify' => false,
'timeout' => $timeout,
'user-agent' => 'MailPoet (www.mailpoet.com) Cron'
);
@ -70,7 +87,7 @@ class CronHelper {
throw new \Exception(__('Site URL is unreachable.'));
}
static function checkExecutionTimer($timer) {
static function enforceExecutionLimit($timer) {
$elapsed_time = microtime(true) - $timer;
if($elapsed_time >= self::DAEMON_EXECUTION_LIMIT) {
throw new \Exception(__('Maximum execution time has been reached.'));

41
lib/Cron/CronTrigger.php Normal file
View File

@ -0,0 +1,41 @@
<?php
namespace MailPoet\Cron;
use MailPoet\Models\Setting;
if(!defined('ABSPATH')) exit;
class CronTrigger {
public $current_method;
public static $available_methods = array(
'mailpoet' => 'MailPoet',
'wordpress' => 'WordPress'
);
const DEFAULT_METHOD = 'WordPress';
const SETTING_NAME = 'cron_trigger';
function __construct() {
$this->current_method = self::getCurrentMethod();
}
function init() {
try {
// configure cron trigger only outside of cli environment
if(php_sapi_name() === 'cli') return;
$trigger_class = __NAMESPACE__ . '\Triggers\\' . $this->current_method;
return (class_exists($trigger_class)) ?
$trigger_class::run() :
false;
} catch(\Exception $e) {
// cron exceptions should not prevent the rest of the site from loading
}
}
static function getAvailableMethods() {
return self::$available_methods;
}
static function getCurrentMethod() {
return Setting::getValue(self::SETTING_NAME . '.method');
}
}

View File

@ -78,15 +78,16 @@ class Daemon {
}
}
function abortWithError($message) {
exit('[mailpoet_cron_error:' . base64_encode($message) . ']');
}
function callSelf() {
CronHelper::accessDaemon($this->token, self::REQUEST_TIMEOUT);
$this->terminateRequest();
}
function abortWithError($message) {
status_header(404, $message);
exit;
}
function terminateRequest() {
exit;
}

View File

@ -6,87 +6,35 @@ if(!defined('ABSPATH')) exit;
class Supervisor {
public $daemon;
public $token;
public $force_run;
function __construct($force_run = false) {
$this->daemon = CronHelper::getDaemon();
function __construct() {
$this->token = CronHelper::createToken();
$this->force_run = $force_run;
$this->daemon = $this->getDaemon();
}
function checkDaemon() {
$daemon = $this->daemon;
if(!$daemon) {
$daemon = CronHelper::createDaemon($this->token);
return $this->runDaemon($daemon);
$execution_timeout_exceeded =
(time() - (int)$daemon['updated_at']) > CronHelper::DAEMON_EXECUTION_TIMEOUT;
if($execution_timeout_exceeded) {
CronHelper::restartDaemon($this->token);
return $this->runDaemon();
}
// if the daemon is stopped, return its status and do nothing
if(!$this->force_run &&
isset($daemon['status']) &&
$daemon['status'] === Daemon::STATUS_STOPPED
) {
return $this->formatDaemonStatusMessage($daemon['status']);
}
$elapsed_time = time() - (int)$daemon['updated_at'];
// if it's been less than 40 seconds since last execution and we're not
// force-running the daemon, return its status and do nothing
if($elapsed_time < CronHelper::DAEMON_EXECUTION_TIMEOUT && !$this->force_run) {
return $this->formatDaemonStatusMessage($daemon['status']);
} elseif($elapsed_time < CronHelper::DAEMON_EXECUTION_TIMEOUT &&
$this->force_run &&
in_array($daemon['status'], array(
Daemon::STATUS_STOPPING,
Daemon::STATUS_STARTING
))
) {
// if it's been less than 40 seconds since last execution, we are
// force-running the daemon and it's either being started or stopped,
// return its status and do nothing
return $this->formatDaemonStatusMessage($daemon['status']);
}
// re-create (restart) daemon
CronHelper::createDaemon($this->token);
return $this->runDaemon();
return $daemon;
}
function runDaemon() {
$request = CronHelper::accessDaemon($this->token);
preg_match('/\[(mailpoet_cron_error:.*?)\]/i', $request, $status);
CronHelper::accessDaemon($this->token);
$daemon = CronHelper::getDaemon();
if(!empty($status) || !$daemon) {
if(!$daemon) {
$message = __('Daemon failed to run.');
} else {
list(, $message) = explode(':', $status[0]);
$message = base64_decode($message);
}
return $this->formatResultMessage(
false,
$message
);
}
return $this->formatDaemonStatusMessage($daemon['status']);
return $daemon;
}
private function formatDaemonStatusMessage($status) {
return $this->formatResultMessage(
true,
sprintf(
__('Daemon is currently %s.'),
__($status)
)
);
}
private function formatResultMessage($result, $message) {
$formattedResult = array(
'result' => $result
);
if(!$result) {
$formattedResult['errors'] = array($message);
} else {
$formattedResult['message'] = $message;
function getDaemon() {
$daemon = CronHelper::getDaemon();
if(!$daemon) {
CronHelper::createDaemon($this->token);
return $this->runDaemon();
}
return $formattedResult;
return $daemon;
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace MailPoet\Cron\Triggers;
use MailPoet\Cron\Supervisor;
if(!defined('ABSPATH')) exit;
class MailPoet {
static function run() {
$supervisor = new Supervisor();
return $supervisor->checkDaemon();
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace MailPoet\Cron\Triggers;
use MailPoet\Cron\CronHelper;
use MailPoet\Cron\Workers\Scheduler as SchedulerWorker;
use MailPoet\Cron\Workers\SendingQueue\SendingQueue as SendingQueueWorker;
use MailPoet\Mailer\MailerLog;
if(!defined('ABSPATH')) exit;
class WordPress {
static function run() {
return (self::checkExecutionRequirements()) ?
MailPoet::run() :
self::cleanup();
}
static function checkExecutionRequirements() {
$scheduled_queues = SchedulerWorker::getScheduledQueues();
$running_queues = SendingQueueWorker::getRunningQueues();
$sending_limit_reached = MailerLog::isSendingLimitReached();
return (($scheduled_queues || $running_queues) && !$sending_limit_reached);
}
static function cleanup() {
$cron_daemon = CronHelper::getDaemon();
if($cron_daemon) {
CronHelper::deleteDaemon();
}
}
}

View File

@ -20,13 +20,12 @@ class Scheduler {
function __construct($timer = false) {
$this->timer = ($timer) ? $timer : microtime(true);
CronHelper::checkExecutionTimer($this->timer);
// abort if execution limit is reached
CronHelper::enforceExecutionLimit($this->timer);
}
function process() {
$scheduled_queues = SendingQueue::where('status', 'scheduled')
->whereLte('scheduled_at', Carbon::createFromTimestamp(current_time('timestamp')))
->findMany();
$scheduled_queues = self::getScheduledQueues();
if(!count($scheduled_queues)) return;
foreach($scheduled_queues as $i => $queue) {
$newsletter = Newsletter::filter('filterWithOptions')
@ -40,7 +39,7 @@ class Scheduler {
} elseif($newsletter->type === 'standard') {
$this->processScheduledStandardNewsletter($newsletter, $queue);
}
CronHelper::checkExecutionTimer($this->timer);
CronHelper::enforceExecutionLimit($this->timer);
}
}
@ -185,4 +184,10 @@ class Scheduler {
$notification_history :
false;
}
static function getScheduledQueues() {
return SendingQueue::where('status', 'scheduled')
->whereLte('scheduled_at', Carbon::createFromTimestamp(current_time('timestamp')))
->findMany();
}
}

View File

@ -5,7 +5,7 @@ use MailPoet\Cron\CronHelper;
use MailPoet\Cron\Workers\SendingQueue\Tasks\Mailer as MailerTask;
use MailPoet\Cron\Workers\SendingQueue\Tasks\Newsletter as NewsletterTask;
use MailPoet\Cron\Workers\SendingQueue\Tasks\Subscribers as SubscribersTask;
use MailPoet\Models\Newsletter as NewsletterModel;
use MailPoet\Mailer\MailerLog;
use MailPoet\Models\SendingQueue as SendingQueueModel;
use MailPoet\Models\StatisticsNewsletters as StatisticsNewslettersModel;
use MailPoet\Models\Subscriber as SubscriberModel;
@ -23,11 +23,14 @@ class SendingQueue {
$this->mailer_task = new MailerTask();
$this->newsletter_task = new NewsletterTask();
$this->timer = ($timer) ? $timer : microtime(true);
// abort if execution or sending limit are reached
CronHelper::enforceExecutionLimit($this->timer);
}
function process() {
$this->mailer_task->checkSendingLimit();
foreach($this->getQueues() as $queue) {
foreach(self::getRunningQueues() as $queue) {
// abort if sending limit is reached
MailerLog::enforceSendingLimit();
// get and pre-process newsletter (render, replace shortcodes/links, etc.)
$newsletter = $this->newsletter_task->getAndPreProcess($queue->asArray());
if(!$newsletter) {
@ -67,6 +70,11 @@ class SendingQueue {
$newsletter,
$found_subscribers
);
if($queue->status === SendingQueueModel::STATUS_COMPLETED) {
$this->newsletter_task->markNewsletterAsSent($queue->newsletter_id);
}
// abort if execution limit is reached
CronHelper::enforceExecutionLimit($this->timer);
}
}
}
@ -141,6 +149,7 @@ class SendingQueue {
$prepared_subscribers_ids,
$queue->subscribers
);
$queue = $this->updateQueue($queue);
} else {
// update processed/to process list
$queue->subscribers = SubscribersTask::updateProcessedList(
@ -149,19 +158,18 @@ class SendingQueue {
);
// log statistics
StatisticsNewslettersModel::createMultiple($statistics);
// keep track of sent items
$this->mailer_task->updateMailerLog();
$subscribers_to_process_count = count($queue->subscribers['to_process']);
// update the sent count
$this->mailer_task->updateSentCount();
$queue = $this->updateQueue($queue);
// enforce sending limit if there are still subscribers left to process
if($queue->count_to_process) {
MailerLog::enforceSendingLimit();
}
}
$queue = $this->updateQueue($queue);
if($subscribers_to_process_count) {
$this->mailer_task->checkSendingLimit();
}
CronHelper::checkExecutionTimer($this->timer);
return $queue;
}
function getQueues() {
static function getRunningQueues() {
return SendingQueueModel::orderByDesc('priority')
->whereNull('deleted_at')
->whereNull('status')
@ -178,13 +186,6 @@ class SendingQueue {
if(!$queue->count_to_process) {
$queue->processed_at = current_time('mysql');
$queue->status = SendingQueueModel::STATUS_COMPLETED;
// if it's a standard or post notificaiton newsletter, update its status to sent
$newsletter = NewsletterModel::findOne($queue->newsletter_id);
if($newsletter->type === NewsletterModel::TYPE_STANDARD ||
$newsletter->type === NewsletterModel::TYPE_NOTIFICATION_HISTORY
) {
$newsletter->setStatus(NewsletterModel::STATUS_SENT);
}
}
return $queue->save();
}

View File

@ -2,18 +2,14 @@
namespace MailPoet\Cron\Workers\SendingQueue\Tasks;
use MailPoet\Mailer\Mailer as MailerFactory;
use MailPoet\Models\Setting;
use MailPoet\Mailer\MailerLog;
if(!defined('ABSPATH')) exit;
class Mailer {
public $mta_config;
public $mta_log;
public $mailer;
function __construct() {
$this->mta_config = $this->getMailerConfig();
$this->mta_log = $this->getMailerLog();
$this->mailer = $this->configureMailer();
}
@ -40,33 +36,16 @@ class Mailer {
return $this->mailer;
}
function getMailerConfig() {
$mta_config = Setting::getValue('mta');
if(!$mta_config) {
throw new \Exception(__('Mailer is not configured'));
}
return $mta_config;
}
function getMailerLog() {
$mta_log = Setting::getValue('mta_log');
if(!$mta_log) {
$mta_log = array(
'sent' => 0,
'started' => time()
);
Setting::setValue('mta_log', $mta_log);
}
return $mta_log;
return MailerLog::getMailerLog();
}
function updateMailerLog() {
$this->mta_log['sent']++;
Setting::setValue('mta_log', $this->mta_log);
function updateSentCount() {
return MailerLog::incrementSentCount();
}
function getProcessingMethod() {
return ($this->mta_config['method'] === 'MailPoet') ?
return ($this->mailer->mailer_config['method'] === MailerFactory::METHOD_MAILPOET) ?
'bulk' :
'individual';
}
@ -81,23 +60,4 @@ class Mailer {
$prepared_subscribers
);
}
function checkSendingLimit() {
if($this->mta_config['method'] === 'MailPoet') return;
$frequency_interval = (int)$this->mta_config['frequency']['interval'] * 60;
$frequency_limit = (int)$this->mta_config['frequency']['emails'];
$elapsed_time = time() - (int)$this->mta_log['started'];
if($this->mta_log['sent'] === $frequency_limit &&
$elapsed_time <= $frequency_interval
) {
throw new \Exception(__('Sending frequency limit has been reached'));
}
if($elapsed_time > $frequency_interval) {
$this->mta_log = array(
'sent' => 0,
'started' => time()
);
Setting::setValue('mta_log', $this->mta_log);
}
}
}

View File

@ -62,7 +62,7 @@ class Newsletter {
return $newsletter;
}
function render($newsletter) {
function render(array $newsletter) {
$renderer = new Renderer($newsletter);
$newsletter['rendered_body'] = $renderer->render();
return $newsletter;
@ -88,7 +88,6 @@ class Newsletter {
);
if($this->tracking_enabled) {
$prepared_newsletter = NewsletterLinks::replaceSubscriberData(
$newsletter['id'],
$subscriber['id'],
$queue['id'],
$prepared_newsletter
@ -103,4 +102,12 @@ class Newsletter {
)
);
}
function markNewsletterAsSent($newsletter_id) {
$newsletter = NewsletterModel::findOne($newsletter_id);
// if it's a standard newsletter, update its status
if($newsletter->type === NewsletterModel::TYPE_STANDARD) {
$newsletter->setStatus(NewsletterModel::STATUS_SENT);
}
}
}

View File

@ -1,7 +1,7 @@
<?php
namespace MailPoet\Cron\Workers\SendingQueue\Tasks;
use MailPoet\Models\Newsletter;
use MailPoet\Models\Newsletter as NewsletterModel;
use MailPoet\Models\NewsletterPost;
if(!defined('ABSPATH')) exit;
@ -19,7 +19,7 @@ class Posts {
if(!count($matched_posts_ids)) {
return $newsletter;
}
$newsletter_id = ($newsletter['type'] === Newsletter::TYPE_NOTIFICATION_HISTORY) ?
$newsletter_id = ($newsletter['type'] === NewsletterModel::TYPE_NOTIFICATION_HISTORY) ?
$newsletter['parent_id'] :
$newsletter['id'];
foreach($matched_posts_ids as $post_id) {

View File

@ -2,7 +2,7 @@
namespace MailPoet\Form\Block;
abstract class Base {
protected static function getInputValidation($block) {
protected static function getInputValidation($block, $extra_rules = array()) {
$rules = array();
if($block['id'] === 'email') {
@ -37,8 +37,15 @@ abstract class Base {
$rules['required-message'] = __('Please select at least one option');
}
if($block['type'] === 'date') {
$rules['group'] = 'custom_field_'.$block['id'];
$rules['errors-container'] = '.mailpoet_error_'.$block['id'];
}
$validation = array();
$rules = array_merge($rules, $extra_rules);
if(!empty($rules)) {
$rules = array_unique($rules);
foreach($rules as $rule => $value) {

View File

@ -1,6 +1,8 @@
<?php
namespace MailPoet\Form\Block;
use Carbon\Carbon;
class Date extends Base {
static function render($block) {
@ -17,7 +19,6 @@ class Date extends Base {
$html = '';
$field_name = static::getFieldName($block);
$field_validation = static::getInputValidation($block);
$date_formats = static::getDateFormats();
@ -65,27 +66,38 @@ class Date extends Base {
}
foreach($date_selectors as $date_selector) {
if($date_selector === 'dd') {
if($date_selector === 'DD') {
$block['selected'] = $day;
$html .= '<select class="mailpoet_date_day" ';
$html .= static::getInputValidation($block, array(
'required-message' => __('Please select a day')
));
$html .= 'name="'.$field_name.'[day]" placeholder="'.__('Day').'">';
$html .= static::getDays($block);
$html .= '</select>';
} else if($date_selector === 'mm') {
} else if($date_selector === 'MM') {
$block['selected'] = $month;
$html .= '<select class="mailpoet_date_month" ';
$html .= static::getInputValidation($block, array(
'required-message' => __('Please select a month')
));
$html .= 'name="'.$field_name.'[month]" placeholder="'.__('Month').'">';
$html .= static::getMonths($block);
$html .= '</select>';
} else if($date_selector === 'yyyy') {
} else if($date_selector === 'YYYY') {
$block['selected'] = $year;
$html .= '<select class="mailpoet_date_year" ';
$html .= static::getInputValidation($block, array(
'required-message' => __('Please select a year')
));
$html .= 'name="'.$field_name.'[year]" placeholder="'.__('Year').'">';
$html .= static::getYears($block);
$html .= '</select>';
}
}
$html .= '<span class="mailpoet_error_'.$block['id'].'"></span>';
return $html;
}
@ -100,10 +112,10 @@ class Date extends Base {
static function getDateFormats() {
return array(
'year_month_day' => array('mm/dd/yyyy', 'dd/mm/yyyy', 'yyyy/mm/dd'),
'year_month' => array('mm/yyyy', 'yyyy/mm'),
'year' => array('yyyy'),
'month' => array('mm')
'year_month_day' => array('MM/DD/YYYY', 'DD/MM/YYYY', 'YYYY/MM/DD'),
'year_month' => array('MM/YYYY', 'YYYY/MM'),
'year' => array('YYYY'),
'month' => array('MM')
);
}
static function getMonthNames() {
@ -192,4 +204,85 @@ class Date extends Base {
return $html;
}
static function convertDateToDatetime($date, $date_format) {
$datetime = false;
if($date_format === 'datetime') {
$datetime = $date;
} else {
$parsed_date = explode('/', $date);
$parsed_date_format = explode('/', $date_format);
$year_position = array_search('YYYY', $parsed_date_format);
$month_position = array_search('MM', $parsed_date_format);
$day_position = array_search('DD', $parsed_date_format);
if(count($parsed_date) === 3) {
// create date from any combination of month, day and year
$parsed_date = array(
'year' => $parsed_date[$year_position],
'month' => $parsed_date[$month_position],
'day' => $parsed_date[$day_position]
);
} else if(count($parsed_date) === 2) {
// create date from any combination of month and year
$parsed_date = array(
'year' => $parsed_date[$year_position],
'month' => $parsed_date[$month_position],
'day' => '01'
);
} else if($date_format === 'MM' && count($parsed_date) === 1) {
// create date from month
if((int)$parsed_date[$month_position] === 0) {
$datetime = '';
$parsed_date = false;
} else {
$parsed_date = array(
'month' => $parsed_date[$month_position],
'day' => '01',
'year' => date('Y')
);
}
} else if($date_format === 'YYYY' && count($parsed_date) === 1) {
// create date from year
if((int)$parsed_date[$year_position] === 0) {
$datetime = '';
$parsed_date = false;
} else {
$parsed_date = array(
'year' => $parsed_date[$year_position],
'month' => '01',
'day' => '01'
);
}
} else {
$parsed_date = false;
}
if($parsed_date) {
$year = $parsed_date['year'];
$month = $parsed_date['month'];
$day = $parsed_date['day'];
// if all date parts are set to 0, date value is empty
if((int)$year === 0 && (int)$month === 0 && (int)$day === 0) {
$datetime = '';
} else {
if((int)$year === 0) $year = date('Y');
if((int)$month === 0) $month = date('m');
if((int)$day === 0) $day = date('d');
$datetime = sprintf(
'%s-%s-%s 00:00:00',
$year,
$month,
$day
);
}
}
}
if($datetime !== false && !empty($datetime)) {
try {
$datetime = Carbon::parse($datetime)->toDateTimeString();
} catch(\Exception $e) {
$datetime = false;
}
}
return $datetime;
}
}

View File

@ -8,13 +8,22 @@ require_once(ABSPATH . 'wp-includes/pluggable.php');
if(!defined('ABSPATH')) exit;
class Mailer {
public $mailer;
public $mailer_config;
public $sender;
public $reply_to;
public $mailer_instance;
const MAILER_CONFIG = 'mta';
const SENDING_LIMIT_INTERVAL_MULTIPLIER = 60;
const METHOD_MAILPOET = 'MailPoet';
const METHOD_MAILGUN = 'MailGun';
const METHOD_ELASTICEMAIL = 'ElasticEmail';
const METHOD_AMAZONSES = 'AmazonSES';
const METHOD_SENDGRID = 'SendGrid';
const METHOD_PHPMAIL = 'PHPMail';
const METHOD_SMTP = 'SMTP';
function __construct($mailer = false, $sender = false, $reply_to = false) {
$this->mailer = $this->getMailer($mailer);
$this->mailer_config = self::getMailerConfig($mailer);
$this->sender = $this->getSender($sender);
$this->reply_to = $this->getReplyTo($reply_to);
$this->mailer_instance = $this->buildMailer();
@ -26,59 +35,59 @@ class Mailer {
}
function buildMailer() {
switch($this->mailer['method']) {
case 'AmazonSES':
$mailer_instance = new $this->mailer['class'](
$this->mailer['region'],
$this->mailer['access_key'],
$this->mailer['secret_key'],
switch($this->mailer_config['method']) {
case self::METHOD_AMAZONSES:
$mailer_instance = new $this->mailer_config['class'](
$this->mailer_config['region'],
$this->mailer_config['access_key'],
$this->mailer_config['secret_key'],
$this->sender,
$this->reply_to
);
break;
case 'ElasticEmail':
$mailer_instance = new $this->mailer['class'](
$this->mailer['api_key'],
case self::METHOD_ELASTICEMAIL:
$mailer_instance = new $this->mailer_config['class'](
$this->mailer_config['api_key'],
$this->sender,
$this->reply_to
);
break;
case 'MailGun':
$mailer_instance = new $this->mailer['class'](
$this->mailer['domain'],
$this->mailer['api_key'],
case self::METHOD_MAILGUN:
$mailer_instance = new $this->mailer_config['class'](
$this->mailer_config['domain'],
$this->mailer_config['api_key'],
$this->sender,
$this->reply_to
);
break;
case 'MailPoet':
$mailer_instance = new $this->mailer['class'](
$this->mailer['mailpoet_api_key'],
case self::METHOD_MAILPOET:
$mailer_instance = new $this->mailer_config['class'](
$this->mailer_config['mailpoet_api_key'],
$this->sender,
$this->reply_to
);
break;
case 'SendGrid':
$mailer_instance = new $this->mailer['class'](
$this->mailer['api_key'],
case self::METHOD_SENDGRID:
$mailer_instance = new $this->mailer_config['class'](
$this->mailer_config['api_key'],
$this->sender,
$this->reply_to
);
break;
case 'PHPMail':
$mailer_instance = new $this->mailer['class'](
case self::METHOD_PHPMAIL:
$mailer_instance = new $this->mailer_config['class'](
$this->sender,
$this->reply_to
);
break;
case 'SMTP':
$mailer_instance = new $this->mailer['class'](
$this->mailer['host'],
$this->mailer['port'],
$this->mailer['authentication'],
$this->mailer['login'],
$this->mailer['password'],
$this->mailer['encryption'],
case self::METHOD_SMTP:
$mailer_instance = new $this->mailer_config['class'](
$this->mailer_config['host'],
$this->mailer_config['port'],
$this->mailer_config['authentication'],
$this->mailer_config['login'],
$this->mailer_config['password'],
$this->mailer_config['encryption'],
$this->sender,
$this->reply_to
);
@ -89,12 +98,20 @@ class Mailer {
return $mailer_instance;
}
function getMailer($mailer = false) {
static function getMailerConfig($mailer = false) {
if(!$mailer) {
$mailer = Setting::getValue('mta');
$mailer = Setting::getValue(self::MAILER_CONFIG);
if(!$mailer || !isset($mailer['method'])) throw new \Exception(__('Mailer is not configured'));
}
if(empty($mailer['frequency'])) {
$default_settings = Setting::getDefaults();
$mailer['frequency'] = $default_settings['mta']['frequency'];
}
// add additional variables to the mailer object
$mailer['class'] = 'MailPoet\\Mailer\\Methods\\' . $mailer['method'];
$mailer['frequency_interval'] =
(int)$mailer['frequency']['interval'] * self::SENDING_LIMIT_INTERVAL_MULTIPLIER;
$mailer['frequency_limit'] = (int)$mailer['frequency']['emails'];
return $mailer;
}

60
lib/Mailer/MailerLog.php Normal file
View File

@ -0,0 +1,60 @@
<?php
namespace MailPoet\Mailer;
use MailPoet\Models\Setting;
if(!defined('ABSPATH')) exit;
class MailerLog {
const SETTING_NAME = 'mta_log';
static function getMailerLog() {
$mailer_log = Setting::getValue(self::SETTING_NAME);
if(!$mailer_log) {
$mailer_log = self::createMailerLog();
}
return $mailer_log;
}
static function createMailerLog() {
$mailer_log = array(
'sent' => 0,
'started' => time()
);
Setting::setValue(self::SETTING_NAME, $mailer_log);
return $mailer_log;
}
static function resetMailerLog() {
return self::createMailerLog();
}
static function updateMailerLog($mailer_log) {
Setting::setValue(self::SETTING_NAME, $mailer_log);
return $mailer_log;
}
static function incrementSentCount($mailer_log = false) {
$mailer_log = ($mailer_log) ? $mailer_log : self::getMailerLog();
(int)$mailer_log['sent']++;
return self::updateMailerLog($mailer_log);
}
static function isSendingLimitReached() {
$mailer_config = Mailer::getMailerConfig();
$mailer_log = self::getMailerLog();
$elapsed_time = time() - (int)$mailer_log['started'];
if($mailer_log['sent'] === $mailer_config['frequency_limit']) {
if($elapsed_time <= $mailer_config['frequency_interval']) return true;
// reset mailer log if enough time has passed since the limit was reached
self::resetMailerLog();
}
return false;
}
static function enforceSendingLimit() {
if(self::isSendingLimitReached()) {
throw new \Exception(__('Sending frequency limit has been reached'));
}
}
}

View File

@ -1,6 +1,8 @@
<?php
namespace MailPoet\Models;
use MailPoet\Form\Block\Date;
if(!defined('ABSPATH')) exit;
class CustomField extends Model {
@ -43,28 +45,27 @@ class CustomField extends Model {
// format custom field data depending on type
if(is_array($value) && $this->type === 'date' ) {
$custom_field_data = $this->asArray();
$date_format = $custom_field_data['params']['date_format'];
$date_type = (isset($custom_field_data['params']['date_type'])
? $custom_field_data['params']['date_type']
: 'year_month_day'
);
$date_parts = explode('_', $date_type);
switch($date_type) {
case 'year_month_day':
$value = sprintf(
'%04d-%02d-%02d',
$value['year'],
'%s/%s/%s',
$value['month'],
$value['day']
$value['day'],
$value['year']
);
break;
case 'year_month':
$value = sprintf(
'%04d-%02d',
$value['year'],
$value['month']
'%s/%s',
$value['month'],
$value['year']
);
break;
@ -73,12 +74,23 @@ class CustomField extends Model {
$value = '';
} else {
$value = sprintf(
'%02d',
'%s',
$value['month']
);
}
break;
case 'day':
if((int)$value['day'] === 0) {
$value = '';
} else {
$value = sprintf(
'%s',
$value['day']
);
}
break;
case 'year':
if((int)$value['year'] === 0) {
$value = '';
@ -90,6 +102,10 @@ class CustomField extends Model {
}
break;
}
if(!empty($value)) {
$value = Date::convertDateToDatetime($value, $date_format);
}
}
return $value;

View File

@ -5,6 +5,7 @@ if(!defined('ABSPATH')) exit;
class Model extends \Sudzy\ValidModel {
protected $_errors;
protected $_new_record;
function __construct() {
$this->_errors = array();
@ -36,6 +37,7 @@ class Model extends \Sudzy\ValidModel {
function save() {
$this->setTimestamp();
$this->_new_record = $this->isNew();
try {
parent::save();
} catch(\Sudzy\ValidationException $e) {
@ -63,6 +65,12 @@ class Model extends \Sudzy\ValidModel {
return $this;
}
function isNew() {
return (isset($this->_new_record)) ?
$this->_new_record :
parent::isNew();
}
function trash() {
return $this->set_expr('deleted_at', 'NOW()')->save();
}

View File

@ -126,6 +126,11 @@ class Newsletter extends Model {
$notification_history = self::create();
$notification_history->hydrate($data);
// reset timestamps
$notification_history->set_expr('created_at', 'NOW()');
$notification_history->set_expr('updated_at', 'NOW()');
$notification_history->set_expr('deleted_at', 'NULL');
$notification_history->save();
if($notification_history->getErrors() === false) {
@ -611,4 +616,31 @@ class Newsletter extends Model {
->whereIn('options.value', $segments)
->findMany();
}
static function getArchives($segment_ids = array()) {
$orm = self::table_alias('newsletters')
->distinct()->select('newsletters.*')
->whereIn('newsletters.type', array(
self::TYPE_STANDARD,
self::TYPE_NOTIFICATION_HISTORY
))
->join(
MP_SENDING_QUEUES_TABLE,
'queues.newsletter_id = newsletters.id',
'queues'
)
->where('queues.status', SendingQueue::STATUS_COMPLETED)
->select('queues.processed_at')
->orderByDesc('queues.processed_at');
if(!empty($segment_ids)) {
$orm->join(
MP_NEWSLETTER_SEGMENT_TABLE,
'newsletter_segments.newsletter_id = newsletters.id',
'newsletter_segments'
)
->whereIn('newsletter_segments.segment_id', $segment_ids);
}
return $orm->findMany();
}
}

View File

@ -5,4 +5,9 @@ if(!defined('ABSPATH')) exit;
class NewsletterLink extends Model {
public static $_table = MP_NEWSLETTER_LINKS_TABLE;
static function getByHash($hash) {
return parent::where('hash', $hash)
->findOne();
}
}

View File

@ -5,4 +5,10 @@ if(!defined('ABSPATH')) exit;
class NewsletterPost extends Model {
public static $_table = MP_NEWSLETTER_POSTS_TABLE;
static function getNewestNewsletterPost($newsletter_id) {
return self::where('newsletter_id', $newsletter_id)
->orderByDesc('created_at')
->findOne();
}
}

View File

@ -59,6 +59,15 @@ class SendingQueue extends Model {
return $subscribers;
}
function getRenderedNewsletterBody() {
return json_decode($this->newsletter_rendered_body, true);
}
function isSubscriberProcessed($subscriber_id) {
$subscribers = $this->getSubscribers();
return in_array($subscriber_id, $subscribers['processed']);
}
function asArray() {
$model = parent::asArray();
$model['subscribers'] = (is_serialized($this->subscribers))

View File

@ -1,6 +1,8 @@
<?php
namespace MailPoet\Models;
use MailPoet\Cron\CronTrigger;
if(!defined('ABSPATH')) exit;
class Setting extends Model {
@ -38,6 +40,9 @@ class Setting extends Model {
'interval' => self::DEFAULT_SENDING_FREQUENCY_INTERVAL
)
),
CronTrigger::SETTING_NAME => array(
'method' => CronTrigger::DEFAULT_METHOD
),
'signup_confirmation' => array(
'enabled' => true,
'subject' => sprintf(__('Confirm your subscription to %1$s'), get_option('blogname')),
@ -157,4 +162,9 @@ class Setting extends Model {
return $setting->save();
}
public static function deleteValue($value) {
$value = self::where('name', $value)->findOne();
return ($value) ? $value->delete() : false;
}
}

View File

@ -5,4 +5,23 @@ if(!defined('ABSPATH')) exit;
class StatisticsClicks extends Model {
public static $_table = MP_STATISTICS_CLICKS_TABLE;
static function createOrUpdateClickCount($link_id, $subscriber_id, $newsletter_id, $queue_id) {
$statistics = self::where('link_id', $link_id)
->where('subscriber_id', $subscriber_id)
->where('newsletter_id', $newsletter_id)
->where('queue_id', $queue_id)
->findOne();
if(!$statistics) {
$statistics = self::create();
$statistics->link_id = $link_id;
$statistics->subscriber_id = $subscriber_id;
$statistics->newsletter_id = $newsletter_id;
$statistics->queue_id = $queue_id;
$statistics->count = 1;
} else {
$statistics->count++;
}
return $statistics->save();
}
}

View File

@ -5,4 +5,19 @@ if(!defined('ABSPATH')) exit;
class StatisticsOpens extends Model {
public static $_table = MP_STATISTICS_OPENS_TABLE;
static function getOrCreate($subscriber_id, $newsletter_id, $queue_id) {
$statistics = self::where('subscriber_id', $subscriber_id)
->where('newsletter_id', $newsletter_id)
->where('queue_id', $queue_id)
->findOne();
if(!$statistics) {
$statistics = self::create();
$statistics->subscriber_id = $subscriber_id;
$statistics->newsletter_id = $newsletter_id;
$statistics->queue_id = $queue_id;
$statistics->save();
}
return $statistics;
}
}

View File

@ -5,4 +5,19 @@ if(!defined('ABSPATH')) exit;
class StatisticsUnsubscribes extends Model {
public static $_table = MP_STATISTICS_UNSUBSCRIBES_TABLE;
static function getOrCreate($subscriber_id, $newsletter_id, $queue_id) {
$statistics = self::where('subscriber_id', $subscriber_id)
->where('newsletter_id', $newsletter_id)
->where('queue_id', $queue_id)
->findOne();
if(!$statistics) {
$statistics = self::create();
$statistics->subscriber_id = $subscriber_id;
$statistics->newsletter_id = $newsletter_id;
$statistics->queue_id = $queue_id;
$statistics->save();
}
return $statistics;
}
}

View File

@ -66,6 +66,11 @@ class Subscriber extends Model {
return ($this->wp_user_id !== null);
}
static function getCurrentWPUser() {
$wp_user = wp_get_current_user();
return self::where('wp_user_id', $wp_user->ID)->findOne();
}
function sendConfirmationEmail() {
if($this->status === self::STATUS_UNCONFIRMED) {
$signup_confirmation = Setting::getValue('signup_confirmation');
@ -212,13 +217,13 @@ class Subscriber extends Model {
$segments = Segment::orderByAsc('name')->findMany();
$segment_list = array();
$segment_list[] = array(
'label' => __('All segments'),
'label' => __('All Lists'),
'value' => ''
);
$subscribers_without_segment = self::filter('withoutSegments')->count();
$subscribers_without_segment_label = sprintf(
__('Subscribers without a segment (%s)'),
__('Subscribers without a list (%s)'),
number_format($subscribers_without_segment)
);

View File

@ -9,8 +9,12 @@ if(!defined('ABSPATH')) exit;
class AutomatedLatestContent {
const DEFAULT_POSTS_PER_PAGE = 10;
function __construct($newsletter_id = false) {
private $newsletter_id;
private $newer_than_timestamp;
function __construct($newsletter_id = false, $newer_than_timestamp = false) {
$this->newsletter_id = $newsletter_id;
$this->newer_than_timestamp = $newer_than_timestamp;
$this->_attachSentPostsFilter();
}
@ -59,6 +63,15 @@ class AutomatedLatestContent {
// the query.
$parameters['suppress_filters'] = false;
if($this->newer_than_timestamp) {
$parameters['date_query'] = array(
array(
'column' => 'post_date',
'after' => $this->newer_than_timestamp
)
);
}
return get_posts($parameters);
}

View File

@ -1,8 +1,9 @@
<?php
namespace MailPoet\Newsletter\Links;
use MailPoet\API\API;
use MailPoet\API\Endpoints\Track as TrackAPI;
use MailPoet\Models\Subscriber;
use MailPoet\Router\Front as FrontRouter;
use MailPoet\Router\Endpoints\Track as TrackEndpoint;
use MailPoet\Models\NewsletterLink;
use MailPoet\Newsletter\Shortcodes\Shortcodes;
use MailPoet\Util\Security;
@ -80,7 +81,7 @@ class Links {
// i.e., <a href="http://google.com">(http://google.com)</a> => [(http://google.com)](http://tracked_link)
$regex_escaped_extracted_link = preg_quote($extracted_link['link'], '/');
$content = preg_replace(
'/\[(' . $regex_escaped_extracted_link . ')\](\(' . $regex_escaped_extracted_link . '\))/',
'/\[(.*?)\](\(' . $regex_escaped_extracted_link . '\))/',
'[$1](' . $tracked_link . ')',
$content
);
@ -92,10 +93,10 @@ class Links {
}
static function replaceSubscriberData(
$newsletter_id,
$subscriber_id,
$queue_id,
$content
$content,
$preview = false
) {
// match data tags
$regex = sprintf(
@ -103,6 +104,7 @@ class Links {
preg_quote(self::DATA_TAG_CLICK),
preg_quote(self::DATA_TAG_OPEN)
);
$subscriber = Subscriber::findOne($subscriber_id);
preg_match_all($regex, $content, $matches);
foreach($matches[1] as $index => $match) {
$hash = null;
@ -110,17 +112,18 @@ class Links {
list(, $hash) = explode('-', $match);
}
$data = array(
'newsletter' => $newsletter_id,
'subscriber' => $subscriber_id,
'queue' => $queue_id,
'hash' => $hash
'subscriber_id' => $subscriber->id,
'subscriber_token' => Subscriber::generateToken($subscriber->email),
'queue_id' => $queue_id,
'link_hash' => $hash,
'preview' => $preview
);
$API_action = ($matches[2][$index] === self::DATA_TAG_CLICK) ?
TrackAPI::ACTION_CLICK :
TrackAPI::ACTION_OPEN;
$link = API::buildRequest(
TrackAPI::ENDPOINT,
$API_action,
$router_action = ($matches[2][$index] === self::DATA_TAG_CLICK) ?
TrackEndpoint::ACTION_CLICK :
TrackEndpoint::ACTION_OPEN;
$link = FrontRouter::buildRequest(
TrackEndpoint::ENDPOINT,
$router_action,
$data
);
$content = str_replace($match, $link, $content);
@ -130,7 +133,7 @@ class Links {
static function save(array $links, $newsletter_id, $queue_id) {
foreach($links as $link) {
if(empty($link['hash'] || empty($link['url']))) continue;
if(empty($link['hash']) || empty($link['url'])) continue;
$newsletter_link = NewsletterLink::create();
$newsletter_link->newsletter_id = $newsletter_id;
$newsletter_link->queue_id = $queue_id;

View File

@ -6,6 +6,10 @@ use MailPoet\Newsletter\Renderer\StylesHelper;
class Image {
static function render($element, $column_count) {
if(empty($element['src'])) {
return '';
}
$element['width'] = (int)$element['width'];
$element['height'] = (int)$element['height'];
$element = self::adjustImageDimensions($element, $column_count);

View File

@ -2,19 +2,38 @@
namespace MailPoet\Newsletter\Renderer\Blocks;
use MailPoet\Models\Newsletter;
use MailPoet\Models\NewsletterPost;
use MailPoet\Newsletter\Renderer\StylesHelper;
class Renderer {
public $newsletter;
public $posts;
public $ALC;
function __construct(array $newsletter, $posts = false) {
function __construct(array $newsletter, $preview) {
$this->newsletter = $newsletter;
$this->posts = array();
$newsletter_id = ($newsletter['type'] === Newsletter::TYPE_NOTIFICATION_HISTORY) ?
$newsletter['parent_id'] :
$newsletter['id'];
$this->ALC = new \MailPoet\Newsletter\AutomatedLatestContent($newsletter_id);
if($newsletter['type'] === Newsletter::TYPE_NOTIFICATION_HISTORY) {
$newsletter_id = $newsletter['parent_id'];
$last_post = NewsletterPost::getNewestNewsletterPost($newsletter_id);
if($last_post) {
$newer_than_timestamp = $last_post->created_at;
} else {
$parent = Newsletter::findOne($newsletter_id);
$newer_than_timestamp = $parent->created_at;
}
} else if($preview) {
$newsletter_id = false;
$newer_than_timestamp = false;
} else {
$newsletter_id = $newsletter['id'];
$newer_than_timestamp = false;
}
$this->ALC = new \MailPoet\Newsletter\AutomatedLatestContent(
$newsletter_id,
$newer_than_timestamp
);
}
function render($data, $column_count) {

View File

@ -6,11 +6,17 @@ class Social {
$icons_block = '';
if(is_array($element['icons'])) {
foreach($element['icons'] as $index => $icon) {
if(empty($icon['image'])) {
continue;
}
$icons_block .= '
<a href="' . $icon['link'] . '" style="text-decoration:none!important;">
<img src="' . $icon['image'] . '" width="' . (int)$icon['width'] . '" height="' . (int)$icon['height'] . '" style="width:' . $icon['width'] . ';height:' . $icon['width'] . ';-ms-interpolation-mode:bicubic;border:0;display:inline;outline:none;" alt="' . $icon['iconType'] . '">
</a>';
}
}
if(!empty($icons_block)) {
$template = '
<tr>
<td class="mailpoet_padded_side mailpoet_padded_bottom" valign="top" align="center">

Some files were not shown because too many files have changed in this diff Show More