diff --git a/assets/js/src/listing/listing.jsx b/assets/js/src/listing/listing.jsx index 7f8b1d85e1..d9ef512579 100644 --- a/assets/js/src/listing/listing.jsx +++ b/assets/js/src/listing/listing.jsx @@ -298,7 +298,8 @@ const Listing = React.createClass({ filters: {}, filter: {}, selected_ids: [], - selection: false + selection: false, + meta: {} }; }, getParam: function(param) { @@ -463,15 +464,21 @@ const Listing = React.createClass({ items: response.data || [], filters: response.meta.filters || {}, groups: response.meta.groups || [], - count: response.meta.count || 0 + count: response.meta.count || 0, + meta: _.omit(response.meta, ['filters', 'groups', 'count']) }, () => { // if viewing an empty trash if (this.state.group === 'trash' && response.meta.count === 0) { // redirect to default group this.handleGroup('all'); } + + // trigger afterGetItems callback if specified + if (this.props.afterGetItems !== undefined) { + this.props.afterGetItems(this.state); + } }); - }).fail(function(response) { + }).fail((response) => { if (response.errors.length > 0) { MailPoet.Notice.error( response.errors.map(function(error) { return error.message; }), @@ -711,7 +718,7 @@ const Listing = React.createClass({ }.bind(this)); }, handleRenderItem: function(item, actions) { - const render = this.props.onRenderItem(item, actions); + const render = this.props.onRenderItem(item, actions, this.state.meta); return render.props.children; }, handleRefreshItems: function() { diff --git a/assets/js/src/newsletters/listings/mixins.jsx b/assets/js/src/newsletters/listings/mixins.jsx index 3406e203f3..c7d687a4f7 100644 --- a/assets/js/src/newsletters/listings/mixins.jsx +++ b/assets/js/src/newsletters/listings/mixins.jsx @@ -1,7 +1,9 @@ import React from 'react' +import ReactDOM from 'react-dom' import MailPoet from 'mailpoet' import classNames from 'classnames' import jQuery from 'jquery' +import ListingNotices from 'newsletters/listings/notices.jsx' const _QueueMixin = { pauseSending: function(newsletter) { @@ -180,22 +182,23 @@ const _StatisticsMixin = { } const _MailerMixin = { - resumeSending: function() { - MailPoet.Ajax.post({ - endpoint: 'mailer', - action: 'resumeSending' - }).done(function() { - jQuery('.mailpoet_sending_status.error').remove(); - MailPoet.Notice.success(MailPoet.I18n.t('mailerSendingResumedNotice')); - // TODO: refresh listings to update the newsletter queue status - }).fail((response) => { - if (response.errors.length > 0) { - MailPoet.Notice.error( - response.errors.map(function(error) { return error.message; }), - { scroll: true } - ); - } - }); + checkMailerStatus: function(state) { + if (state.meta.mta_log.error) { + MailPoet.Notice.error( + '', + { static: true, id: 'mailpoet_mailer_error' } + ); + + ReactDOM.render(( + + ), jQuery('[data-id="mailpoet_mailer_error"]')[0]); + + } else { + MailPoet.Notice.hide('mailpoet_mailer_error'); + } } } diff --git a/assets/js/src/newsletters/listings/notices.jsx b/assets/js/src/newsletters/listings/notices.jsx index 64591a18aa..ed08bfa230 100644 --- a/assets/js/src/newsletters/listings/notices.jsx +++ b/assets/js/src/newsletters/listings/notices.jsx @@ -1,37 +1,47 @@ import React from "react"; import MailPoet from "mailpoet"; -import { MailerMixin } from 'newsletters/listings/mixins.jsx' - const ListingNotices = React.createClass({ - mixins: [MailerMixin], - render() { - // display sending error - if (this.props.mailer_log.error) { - let mailer_error_notice = null; - if (this.props.mailer_log.error.operation === 'send') { - mailer_error_notice = - MailPoet.I18n.t('mailerSendErrorNotice') - .replace('%$1s', this.props.mailer_config.method) - .replace('%$2s', this.props.mailer_log.error.error_message); - } else { - mailer_error_notice = - MailPoet.I18n.t('mailerConnectionErrorNotice') - .replace('%$1s', this.props.mailer_log.error.error_message); + resumeSending() { + MailPoet.Ajax.post({ + endpoint: 'mailer', + action: 'resumeSending' + }).done(function() { + MailPoet.Notice.hide('mailpoet_mailer_error'); + MailPoet.Notice.success(MailPoet.I18n.t('mailerSendingResumedNotice')); + }).fail((response) => { + if (response.errors.length > 0) { + MailPoet.Notice.error( + response.errors.map(function(error) { return error.message; }), + { scroll: true } + ); } - return ( -
-

{ mailer_error_notice }

-

{ MailPoet.I18n.t('mailerResumeSendingNotice') }

-

- { MailPoet.I18n.t('mailerResumeSendingButton') } -

-
- ) + }); + }, + render() { + let mailer_error_notice; + if (this.props.mta_log.error.operation === 'send') { + mailer_error_notice = + MailPoet.I18n.t('mailerSendErrorNotice') + .replace('%$1s', this.props.mta_method) + .replace('%$2s', this.props.mta_log.error.error_message); + } else { + mailer_error_notice = + MailPoet.I18n.t('mailerConnectionErrorNotice') + .replace('%$1s', this.props.mta_log.error.error_message); } - return null; + return ( +
+

{ mailer_error_notice }

+

{ MailPoet.I18n.t('mailerResumeSendingNotice') }

+

+ { MailPoet.I18n.t('mailerResumeSendingButton') } +

+
+ ); } }); diff --git a/assets/js/src/newsletters/listings/notification.jsx b/assets/js/src/newsletters/listings/notification.jsx index a52882f88e..85918c536c 100644 --- a/assets/js/src/newsletters/listings/notification.jsx +++ b/assets/js/src/newsletters/listings/notification.jsx @@ -4,7 +4,8 @@ import { createHashHistory } from 'history' import Listing from 'listing/listing.jsx' import ListingTabs from 'newsletters/listings/tabs.jsx' -import ListingNotices from 'newsletters/listings/notices.jsx' + +import { MailerMixin } from 'newsletters/listings/mixins.jsx' import classNames from 'classnames' import jQuery from 'jquery' @@ -18,8 +19,6 @@ import { } from 'newsletters/scheduling/common.jsx' const mailpoet_settings = window.mailpoet_settings || {}; -const mailpoet_mailer_log = mailpoet_settings.mta_log || {}; -const mailpoet_mailer_config = mailpoet_settings.mta || {}; const messages = { onTrash: (response) => { @@ -158,6 +157,7 @@ const newsletter_actions = [ ]; const NewsletterListNotification = React.createClass({ + mixins: [ MailerMixin ], updateStatus: function(e) { // make the event persist so that we can still override the selected value // in the ajax callback @@ -316,8 +316,6 @@ const NewsletterListNotification = React.createClass({ {MailPoet.I18n.t('pageTitle')} {MailPoet.I18n.t('new')} - - ); diff --git a/assets/js/src/newsletters/listings/notification_history.jsx b/assets/js/src/newsletters/listings/notification_history.jsx index 64ea560415..eaced5c474 100644 --- a/assets/js/src/newsletters/listings/notification_history.jsx +++ b/assets/js/src/newsletters/listings/notification_history.jsx @@ -6,14 +6,15 @@ import MailPoet from 'mailpoet' import Listing from 'listing/listing.jsx' import ListingTabs from 'newsletters/listings/tabs.jsx' -import ListingNotices from 'newsletters/listings/notices.jsx' -import { QueueMixin, StatisticsMixin } from 'newsletters/listings/mixins.jsx' +import { + QueueMixin, + StatisticsMixin, + MailerMixin +} from 'newsletters/listings/mixins.jsx' const mailpoet_tracking_enabled = (!!(window['mailpoet_tracking_enabled'])); const mailpoet_settings = window.mailpoet_settings || {}; -const mailpoet_mailer_log = mailpoet_settings.mta_log || {}; -const mailpoet_mailer_config = mailpoet_settings.mta || {}; const columns = [ { @@ -53,7 +54,7 @@ const newsletter_actions = [ ]; const NewsletterListNotificationHistory = React.createClass({ - mixins: [QueueMixin, StatisticsMixin], + mixins: [ QueueMixin, StatisticsMixin, MailerMixin ], renderItem: function(newsletter, actions) { const rowClasses = classNames( 'manage-column', @@ -102,8 +103,6 @@ const NewsletterListNotificationHistory = React.createClass({ {MailPoet.I18n.t('pageTitle')} {MailPoet.I18n.t('new')} - - ); diff --git a/assets/js/src/newsletters/listings/standard.jsx b/assets/js/src/newsletters/listings/standard.jsx index 33467636ba..08e976df2f 100644 --- a/assets/js/src/newsletters/listings/standard.jsx +++ b/assets/js/src/newsletters/listings/standard.jsx @@ -6,14 +6,15 @@ import MailPoet from 'mailpoet' import Listing from 'listing/listing.jsx' import ListingTabs from 'newsletters/listings/tabs.jsx' -import ListingNotices from 'newsletters/listings/notices.jsx' -import { QueueMixin, StatisticsMixin } from 'newsletters/listings/mixins.jsx' +import { + QueueMixin, + StatisticsMixin, + MailerMixin +} from 'newsletters/listings/mixins.jsx' const mailpoet_tracking_enabled = (!!(window['mailpoet_tracking_enabled'])); const mailpoet_settings = window.mailpoet_settings || {}; -const mailpoet_mailer_log = mailpoet_settings.mta_log || {}; -const mailpoet_mailer_config = mailpoet_settings.mta || {}; const messages = { onTrash: (response) => { @@ -151,8 +152,8 @@ const newsletter_actions = [ ]; const NewsletterListStandard = React.createClass({ - mixins: [QueueMixin, StatisticsMixin], - renderItem: function(newsletter, actions) { + mixins: [ QueueMixin, StatisticsMixin, MailerMixin ], + renderItem: function(newsletter, actions, meta) { const rowClasses = classNames( 'manage-column', 'column-primary', @@ -175,7 +176,7 @@ const NewsletterListStandard = React.createClass({ { actions } - { this.renderQueueStatus(newsletter, mailpoet_mailer_log) } + { this.renderQueueStatus(newsletter, meta.mta_log) } { segments } @@ -198,8 +199,6 @@ const NewsletterListStandard = React.createClass({ {MailPoet.I18n.t('pageTitle')} {MailPoet.I18n.t('new')} - - ); diff --git a/assets/js/src/newsletters/listings/welcome.jsx b/assets/js/src/newsletters/listings/welcome.jsx index 172336f5ac..e3f382da91 100644 --- a/assets/js/src/newsletters/listings/welcome.jsx +++ b/assets/js/src/newsletters/listings/welcome.jsx @@ -4,7 +4,8 @@ import { createHashHistory } from 'history' import Listing from 'listing/listing.jsx' import ListingTabs from 'newsletters/listings/tabs.jsx' -import ListingNotices from 'newsletters/listings/notices.jsx' + +import { MailerMixin } from 'newsletters/listings/mixins.jsx' import classNames from 'classnames' import jQuery from 'jquery' @@ -15,8 +16,6 @@ const mailpoet_roles = window.mailpoet_roles || {}; const mailpoet_segments = window.mailpoet_segments || {}; const mailpoet_tracking_enabled = (!!(window['mailpoet_tracking_enabled'])); const mailpoet_settings = window.mailpoet_settings || {}; -const mailpoet_mailer_log = mailpoet_settings.mta_log || {}; -const mailpoet_mailer_config = mailpoet_settings.mta || {}; const messages = { onTrash: (response) => { @@ -155,6 +154,7 @@ const newsletter_actions = [ ]; const NewsletterListWelcome = React.createClass({ + mixins: [ MailerMixin ], updateStatus: function(e) { // make the event persist so that we can still override the selected value // in the ajax callback @@ -345,8 +345,6 @@ const NewsletterListWelcome = React.createClass({ { MailPoet.I18n.t('pageTitle') } { MailPoet.I18n.t('new') } - - ); diff --git a/assets/js/src/newsletters/newsletters.jsx b/assets/js/src/newsletters/newsletters.jsx index 0ed787054e..fd82973843 100644 --- a/assets/js/src/newsletters/newsletters.jsx +++ b/assets/js/src/newsletters/newsletters.jsx @@ -27,7 +27,7 @@ const App = React.createClass({ const container = document.getElementById('newsletters_container'); if(container) { - ReactDOM.render(( + const mailpoet_listing = ReactDOM.render(( @@ -49,4 +49,6 @@ if(container) { ), container); + + window.mailpoet_listing = mailpoet_listing; } diff --git a/assets/js/src/notice.js b/assets/js/src/notice.js index 43abfdd3c4..668cbfe496 100644 --- a/assets/js/src/notice.js +++ b/assets/js/src/notice.js @@ -5,14 +5,14 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) { MailPoet Notice: description: Handles notices - version: 0.2 + version: 1.0 author: Jonathan Labreuille company: Wysija dependencies: jQuery Usage: - // success message (static: false) + // success message (static: false) MailPoet.Notice.success('Yatta!'); // error message (static: false) @@ -21,199 +21,206 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) { // system message (static: true) MailPoet.Notice.system('You need to updated ASAP!'); - Examples: - - MailPoet.Notice.success('- success #1 -'); - setTimeout(function() { - MailPoet.Notice.success('- success #2 -'); - setTimeout(function() { - MailPoet.Notice.error('- error -'); - setTimeout(function() { - MailPoet.Notice.system('- system -'); - - setTimeout(function() { - MailPoet.Notice.hide(); - }, 2500); - }, 300); - }, 400); - }, 500); - ==================================================================================================*/ MailPoet.Notice = { - version: 0.2, - // default options - defaults: { - type: 'success', - message: '', - static: false, - hideClose: false, - id: null, - positionAfter: false, - scroll: false, - timeout: 5000, - onOpen: null, - onClose: null - }, - options: {}, - init: function(options) { - // set options - this.options = jQuery.extend({}, this.defaults, options); + version: 1.0, + // default options + defaults: { + type: 'success', + message: '', + static: false, + hideClose: false, + id: null, + positionAfter: false, + scroll: false, + timeout: 5000, + onOpen: null, + onClose: null + }, + options: {}, + init: function(options) { + // set options + this.options = jQuery.extend({}, this.defaults, options); - // clone element - this.element = jQuery('#mailpoet_notice_'+this.options.type).clone(); + return this; + }, + createNotice: function() { + // clone element + this.element = jQuery('#mailpoet_notice_'+this.options.type).clone(); - // add data-id to the element - if (this.options.id) this.element.attr('data-id', 'notice_' + this.options.id); - - // remove id from clone - this.element.removeAttr('id'); - - // insert notice after its parent - var positionAfter; - if (typeof this.options.positionAfter === 'object') { - positionAfter = this.options.positionAfter; - } else if (typeof this.options.positionAfter === 'string') { - positionAfter = jQuery(this.options.positionAfter); - } else { - positionAfter = jQuery('#mailpoet_notice_'+this.options.type); - } - positionAfter.after(this.element); - - // setup onClose callback - var onClose = null; - if(this.options.onClose !== null) { - onClose = this.options.onClose; - } - - // listen to remove event - jQuery(this.element).on('close', function() { - jQuery(this).fadeOut(200, function() { - // on close callback - if(onClose !== null) { - onClose(); - } - // remove notice - jQuery(this).remove(); - }); - }.bind(this.element)); - - // listen to message event - jQuery(this.element).on('message', function(e, message) { - MailPoet.Notice.setMessage(message); - }.bind(this.element)); - - return this; - }, - isHTML: function(str) { - var a = document.createElement('div'); - a.innerHTML = str; - for(var c = a.childNodes, i = c.length; i--;) { - if(c[i].nodeType == 1) return true; - } - return false; - }, - setMessage: function(message) { - // if it's not an html message, let's sugar coat the message with a fancy

- if(this.isHTML(message) === false) { - message = '

'+message+'

'; - } - // set message - return this.element.html(message); - }, - show: function(options) { - // initialize - this.init(options); - - // show notice - this.showNotice(); - - // return this; - }, - showNotice: function() { - // set message - this.setMessage(this.options.message); - - // position notice - this.element.insertAfter(jQuery('h2.title')); - - // set class name - switch(this.options.type) { - case 'success': - this.element.addClass('updated'); - break; - case 'system': - this.element.addClass('update-nag'); - break; - case 'error': - this.element.addClass('error'); - break; - } - - // make the notice appear - this.element.fadeIn(200); - - // if scroll option is enabled, scroll to the notice - if(this.options.scroll === true) { - this.element.get(0).scrollIntoView(false); - } - - // if the notice is not static, it has to disappear after a timeout - if(this.options.static === false) { - this.element.delay(this.options.timeout).trigger('close'); - } else if (this.options.hideClose === false) { - this.element.append(''); - this.element.find('.mailpoet_notice_close').on('click', function() { - jQuery(this).trigger('close'); - }); - } - - // call onOpen callback - if(this.options.onOpen !== null) { - this.options.onOpen(this.element); - } - }, - hide: function(all) { - if(all !== undefined && all === true) { - jQuery('.mailpoet_notice:not([id])').trigger('close'); - } else if (all !== undefined && jQuery.isArray(all)) { - for (var id in all) { - jQuery('[data-id="notice_' + all[id] + '"]') - .trigger('close'); - } - } if (all !== undefined) { - jQuery('[data-id="notice_' + all + '"]') - .trigger('close'); - } else { - jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])') - .trigger('close'); - } - }, - error: function(message, options) { - this.show(jQuery.extend({}, { - type: 'error', - message: '

'+this.formatMessage(message)+'

' - }, options)); - }, - success: function(message, options) { - this.show(jQuery.extend({}, { - type: 'success', - message: '

'+this.formatMessage(message)+'

' - }, options)); - }, - system: function(message, options) { - this.show(jQuery.extend({}, { - type: 'system', - static: true, - message: '

'+this.formatMessage(message)+'

' - }, options)); - }, - formatMessage: function(message) { - if(Array.isArray(message)) { - return message.join('
'); - } else { - return message; - } + // add data-id to the element + if (this.options.id) { + this.element.attr( + 'data-id', + this.options.id + ); } + + // remove id from clone + this.element.removeAttr('id'); + + // insert notice after its parent + var positionAfter; + if (typeof this.options.positionAfter === 'object') { + positionAfter = this.options.positionAfter; + } else if (typeof this.options.positionAfter === 'string') { + positionAfter = jQuery(this.options.positionAfter); + } else { + positionAfter = jQuery('#mailpoet_notice_'+this.options.type); + } + positionAfter.after(this.element); + + // setup onClose callback + var onClose = null; + if (this.options.onClose !== null) { + onClose = this.options.onClose; + } + + // listen to remove event + jQuery(this.element).on('close', function() { + jQuery(this).fadeOut(200, function() { + // on close callback + if (onClose !== null) { + onClose(); + } + // remove notice + jQuery(this).remove(); + }); + }.bind(this.element)); + + // listen to message event + jQuery(this.element).on('setMessage', function(e, message) { + MailPoet.Notice.setMessage(message); + }.bind(this.element)); + + return this; + }, + updateNotice: function() { + // update notice's message + jQuery('[data-id="'+this.options.id+'"').first().trigger( + 'setMessage', this.options.message + ); + }, + isHTML: function(str) { + var a = document.createElement('div'); + a.innerHTML = str; + for (var c = a.childNodes, i = c.length; i--;) { + if (c[i].nodeType == 1) return true; + } + return false; + }, + setMessage: function(message) { + message = this.formatMessage(message); + + // if it's not an html message + // let's sugar coat the message with a fancy

+ if (this.isHTML(message) === false) { + message = '

'+message+'

'; + } + // set message + return this.element.html(message); + }, + formatMessage: function(message) { + if (Array.isArray(message)) { + return message.join('
'); + } else { + return message; + } + }, + show: function(options) { + // initialize + this.init(options); + + if ( + this.options.id !== null + && + jQuery('[data-id="'+this.options.id+'"').length > 0 + ) { + this.updateNotice(); + } else { + this.createNotice(); + } + this.showNotice(); + }, + showNotice: function() { + // set message + this.setMessage(this.options.message); + + // position notice + this.element.insertAfter(jQuery('h2.title')); + + // set class name + switch (this.options.type) { + case 'success': + this.element.addClass('updated'); + break; + case 'system': + this.element.addClass('update-nag'); + break; + case 'error': + this.element.addClass('error'); + break; + } + + // make the notice appear + this.element.fadeIn(200); + + // if scroll option is enabled, scroll to the notice + if (this.options.scroll === true) { + this.element.get(0).scrollIntoView(false); + } + + // if the notice is not static, it has to disappear after a timeout + if (this.options.static === false) { + this.element.delay(this.options.timeout).trigger('close'); + } else if (this.options.hideClose === false) { + this.element.append(''); + this.element.find('.mailpoet_notice_close').on('click', function() { + jQuery(this).trigger('close'); + }); + } + + // call onOpen callback + if (this.options.onOpen !== null) { + this.options.onOpen(this.element); + } + }, + hide: function(all) { + if (all !== undefined && all === true) { + // all notices + jQuery('.mailpoet_notice:not([id])').trigger('close'); + } else if (all !== undefined && jQuery.isArray(all)) { + // array of ids + for (var id in all) { + jQuery('[data-id="' + all[id] + '"]').trigger('close'); + } + } if (all !== undefined) { + // single id + jQuery('[data-id="' + all + '"]').trigger('close'); + } else { + jQuery('.mailpoet_notice.updated:not([id]), .mailpoet_notice.error:not([id])') + .trigger('close'); + } + }, + error: function(message, options) { + this.show(jQuery.extend({}, { + type: 'error', + message: message + }, options)); + }, + success: function(message, options) { + this.show(jQuery.extend({}, { + type: 'success', + message: message + }, options)); + }, + system: function(message, options) { + this.show(jQuery.extend({}, { + type: 'system', + static: true, + message: message + }, options)); + } }; }); diff --git a/lib/API/Endpoints/Newsletters.php b/lib/API/Endpoints/Newsletters.php index 2f5284b634..7d5eee8add 100644 --- a/lib/API/Endpoints/Newsletters.php +++ b/lib/API/Endpoints/Newsletters.php @@ -346,7 +346,9 @@ class Newsletters extends APIEndpoint { return $this->successResponse($data, array( 'count' => $listing_data['count'], 'filters' => $listing_data['filters'], - 'groups' => $listing_data['groups'] + 'groups' => $listing_data['groups'], + 'mta_log' => Setting::getValue('mta_log'), + 'mta_method' => Setting::getValue('mta.method') )); }