Add detailed stats page support in Free [PREMIUM-1]

This commit is contained in:
Alexey Stoletniy
2017-04-04 15:37:16 +03:00
parent afedc409f5
commit fbc0a3ad8d
8 changed files with 82 additions and 28 deletions

View File

@ -209,8 +209,8 @@ const ListingItems = React.createClass({
className="colspanchange">
{
(this.props.loading === true)
? MailPoet.I18n.t('loadingItems')
: MailPoet.I18n.t('noItemsFound')
? (this.props.messages.onLoadingItems || MailPoet.I18n.t('loadingItems'))
: (this.props.messages.onNoItemsFound || MailPoet.I18n.t('noItemsFound'))
}
</td>
</tr>
@ -793,6 +793,12 @@ const Listing = React.createClass({
groups = false;
}
// messages
let messages = {};
if (this.props.messages !== undefined) {
messages = this.props.messages;
}
return (
<div>
{ groups }
@ -846,6 +852,7 @@ const Listing = React.createClass({
count={ this.state.count }
limit={ this.state.limit }
item_actions={ item_actions }
messages={ messages }
items={ items } />
<tfoot>

View File

@ -1,9 +1,11 @@
import React from 'react'
import ReactDOM from 'react-dom'
import ReactStringReplace from 'react-string-replace'
import { Link } from 'react-router'
import MailPoet from 'mailpoet'
import classNames from 'classnames'
import jQuery from 'jquery'
import Hooks from 'wp-js-hooks'
const _QueueMixin = {
pauseSending: function(newsletter) {
@ -146,6 +148,9 @@ const _StatisticsMixin = {
&& newsletter.queue
&& newsletter.queue.status !== 'scheduled'
) {
let params = {};
params = Hooks.applyFilters('mailpoet_newsletters_listing_stats_before', params, newsletter);
const total_sent = ~~(newsletter.queue.count_processed);
let percentage_clicked = 0;
@ -153,22 +158,32 @@ const _StatisticsMixin = {
let percentage_unsubscribed = 0;
if (total_sent > 0) {
percentage_clicked = Math.round(
(~~(newsletter.statistics.clicked) * 100) / total_sent
);
percentage_opened = Math.round(
(~~(newsletter.statistics.opened) * 100) / total_sent
);
percentage_unsubscribed = Math.round(
(~~(newsletter.statistics.unsubscribed) * 100) / total_sent
);
percentage_clicked = (newsletter.statistics.clicked * 100) / total_sent;
percentage_opened = (newsletter.statistics.opened * 100) / total_sent;
percentage_unsubscribed = (newsletter.statistics.unsubscribed * 100) / total_sent;
}
return (
// format to 1 decimal place
percentage_clicked = percentage_clicked.toFixed(1);
percentage_opened = percentage_opened.toFixed(1);
percentage_unsubscribed = percentage_unsubscribed.toFixed(1);
const content = (
<span>
{ percentage_opened }%, { percentage_clicked }%, { percentage_unsubscribed }%
</span>
);
if (total_sent > 0 && params.link) {
return (
<Link
key={ `stats-${newsletter.id}` }
to={ params.link }
>{ content }</Link>
);
}
return content;
} else {
return (
<span>{MailPoet.I18n.t('notSentYet')}</span>

View File

@ -11,6 +11,7 @@ import classNames from 'classnames'
import jQuery from 'jquery'
import MailPoet from 'mailpoet'
import _ from 'underscore'
import Hooks from 'wp-js-hooks'
const mailpoet_roles = window.mailpoet_roles || {};
const mailpoet_segments = window.mailpoet_segments || {};
@ -281,24 +282,37 @@ const NewsletterListWelcome = React.createClass({
return;
}
let params = {};
params = Hooks.applyFilters('mailpoet_newsletters_listing_stats_before', params, newsletter);
if (newsletter.total_sent > 0 && newsletter.statistics) {
const total_sent = ~~(newsletter.total_sent);
const percentage_clicked = Math.round(
(~~(newsletter.statistics.clicked) * 100) / total_sent
);
const percentage_opened = Math.round(
(~~(newsletter.statistics.opened) * 100) / total_sent
);
const percentage_unsubscribed = Math.round(
(~~(newsletter.statistics.unsubscribed) * 100) / total_sent
);
let percentage_clicked = (newsletter.statistics.clicked * 100) / total_sent;
let percentage_opened = (newsletter.statistics.opened * 100) / total_sent;
let percentage_unsubscribed = (newsletter.statistics.unsubscribed * 100) / total_sent;
return (
// format to 1 decimal place
percentage_clicked = percentage_clicked.toFixed(1);
percentage_opened = percentage_opened.toFixed(1);
percentage_unsubscribed = percentage_unsubscribed.toFixed(1);
const content = (
<span>
{ percentage_opened }%, { percentage_clicked }%, { percentage_unsubscribed }%
</span>
);
if (params.link) {
return (
<Link
key={ `stats-${newsletter.id}` }
to={ params.link }
>{ content }</Link>
);
}
return content;
} else {
return (
<span>{MailPoet.I18n.t('notSentYet')}</span>

View File

@ -2,6 +2,7 @@ import React from 'react'
import ReactDOM from 'react-dom'
import { Router, Route, IndexRedirect, Link, useRouterHistory } from 'react-router'
import { createHashHistory } from 'history'
import Hooks from 'wp-js-hooks'
import NewsletterTypes from 'newsletters/types.jsx'
import NewsletterTemplates from 'newsletters/templates.jsx'
@ -27,6 +28,9 @@ const App = React.createClass({
const container = document.getElementById('newsletters_container');
if(container) {
let extra_routes = [];
extra_routes = Hooks.applyFilters('mailpoet_newsletters_before_router', extra_routes);
const mailpoet_listing = ReactDOM.render((
<Router history={ history }>
<Route path="/" component={ App }>
@ -46,6 +50,8 @@ if(container) {
<Route name="template" path="template/:id" component={ NewsletterTemplates } />
{/* Sending options */}
<Route path="send/:id" component={ NewsletterSend } />
{/* Extra routes */}
{ extra_routes.map(rt => <Route key={rt.path} path={rt.path} component={rt.component} />) }
</Route>
</Router>
), container);

View File

@ -300,7 +300,8 @@ class Migrator {
'subscriber_id mediumint(9) NOT NULL,',
'queue_id mediumint(9) NOT NULL,',
'sent_at TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id)',
'PRIMARY KEY (id),',
'KEY newsletter_id (newsletter_id)',
);
return $this->sqlify(__FUNCTION__, $attributes);
}
@ -316,6 +317,7 @@ class Migrator {
'created_at TIMESTAMP NULL,',
'updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),',
'KEY newsletter_id (newsletter_id),',
'KEY queue_id (queue_id)',
);
return $this->sqlify(__FUNCTION__, $attributes);
@ -329,6 +331,7 @@ class Migrator {
'queue_id mediumint(9) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),',
'KEY newsletter_id (newsletter_id),',
'KEY queue_id (queue_id)',
);
return $this->sqlify(__FUNCTION__, $attributes);
@ -342,6 +345,7 @@ class Migrator {
'queue_id mediumint(9) NOT NULL,',
'created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,',
'PRIMARY KEY (id),',
'KEY newsletter_id (newsletter_id),',
'KEY queue_id (queue_id)',
);
return $this->sqlify(__FUNCTION__, $attributes);

View File

@ -264,7 +264,8 @@ class Newsletter extends Model {
'status',
'count_processed',
'count_total',
'scheduled_at'
'scheduled_at',
'created_at'
));
if($queue === false) {
$this->queue = false;

View File

@ -3,7 +3,6 @@
use Codeception\Util\Stub;
use MailPoet\Models\Newsletter;
use MailPoet\Models\SendingQueue;
use MailPoet\Models\StatisticsClicks;
use MailPoet\Models\StatisticsOpens;
use MailPoet\Models\Subscriber;
use MailPoet\Statistics\Track\Opens;
@ -90,6 +89,5 @@ class OpensTest extends MailPoetTest {
ORM::raw_execute('TRUNCATE ' . Subscriber::$_table);
ORM::raw_execute('TRUNCATE ' . SendingQueue::$_table);
ORM::raw_execute('TRUNCATE ' . StatisticsOpens::$_table);
ORM::raw_execute('TRUNCATE ' . StatisticsClicks::$_table);
}
}

View File

@ -65,7 +65,11 @@ baseConfig = {
include: require.resolve('react'),
loader: 'expose-loader?' + globalPrefix + '.React',
},
{
{
include: require.resolve('react-router'),
loader: 'expose-loader?' + globalPrefix + '.ReactRouter',
},
{
include: require.resolve('react-string-replace'),
loader: 'expose-loader?' + globalPrefix + '.ReactStringReplace',
},
@ -73,6 +77,10 @@ baseConfig = {
test: /wp-js-hooks/i,
loader: 'expose-loader?' + globalPrefix + '.Hooks!exports-loader?wp.hooks',
},
{
test: /listing.jsx/i,
loader: 'expose-loader?' + globalPrefix + '.Listing!babel-loader',
},
{
include: /Blob.js$/,
loader: 'exports-loader?window.Blob',
@ -124,7 +132,8 @@ config.push(_.extend({}, baseConfig, {
'react',
'react-dom',
'react-router',
'react-string-replace'
'react-string-replace',
'listing/listing.jsx'
],
admin: [
'subscribers/subscribers.jsx',