Skip to content

Commit

Permalink
Merge pull request #157 from alleyinteractive/feature/sendgrid-stats
Browse files Browse the repository at this point in the history
Feature: Add in SendGrid Stats
  • Loading branch information
efuller authored Jun 25, 2024
2 parents bc645b2 + 90c4e46 commit 9ef2766
Show file tree
Hide file tree
Showing 14 changed files with 114 additions and 71 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Contributors: alleyinteractive

Tags: alleyinteractive, wp-newsletter-builder

Stable tag: 0.3.28
Stable tag: 0.3.29

Requires at least: 6.2

Expand Down
2 changes: 1 addition & 1 deletion blocks/button/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function wp_newsletter_builder_button_block_init() {
function wp_newsletter_builder_button_block_init(): void {
// Register the block by passing the location of block.json.
register_block_type(
__DIR__
Expand Down
2 changes: 1 addition & 1 deletion blocks/divider/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function wp_newsletter_builder_divider_block_init() {
function wp_newsletter_builder_divider_block_init(): void {
// Register the block by passing the location of block.json.
register_block_type(
__DIR__
Expand Down
2 changes: 1 addition & 1 deletion blocks/heading/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function wp_newsletter_builder_heading_block_init() {
function wp_newsletter_builder_heading_block_init(): void {
// Register the block by passing the location of block.json.
register_block_type(
__DIR__
Expand Down
2 changes: 1 addition & 1 deletion blocks/list/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function wp_newsletter_builder_list_block_init() {
function wp_newsletter_builder_list_block_init(): void {
// Register the block by passing the location of block.json.
register_block_type(
__DIR__
Expand Down
2 changes: 1 addition & 1 deletion blocks/paragraph/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function wp_newsletter_builder_paragraph_block_init() {
function wp_newsletter_builder_paragraph_block_init(): void {
// Register the block by passing the location of block.json.
register_block_type(
__DIR__
Expand Down
47 changes: 47 additions & 0 deletions hooks/useNewsletterStats/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useCallback, useEffect, useState } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';

interface Stats {
Bounced?: number;
Clicks?: number;
Forwards?: number;
Likes?: number;
Mentions?: number;
Name?: string;
Recipients?: number;
SpamComplaints?: number;
Status?: string;
TotalOpened?: number;
UniqueOpened?: number;
Unsubscribed?: number;
WebVersionTextURL?: string;
WebVersionURL?: string;
WorldviewURL?: string;
}

function useNewsletterStats(newsletterId: string) {
const [stats, setStats] = useState<Stats>({});
const [fetching, setFetching] = useState(false);

const fetchStats = useCallback(async () => {
setFetching(true);
const res = await apiFetch({
path: `/wp-newsletter-builder/v1/status/${newsletterId}`,
});
setStats(res as Stats);
setFetching(false);
}, [newsletterId]);

useEffect(() => {
fetchStats();
}, [fetchStats]);

return {
validStats: stats.Status && stats.Name,
stats,
fetching,
fetchStats,
};
}

export default useNewsletterStats;
3 changes: 0 additions & 3 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ parameters:
# Level 9 is the highest level
level: max

# Disable generics in type hints.
checkGenericClassInNonGenericObjectType: false

paths:
- blocks/
- entries/
Expand Down
2 changes: 1 addition & 1 deletion plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Plugin Name: Newsletter Builder
* Plugin URI: https://github.com/alleyinteractive/wp-newsletter-builder
* Description: Interface to manage email newsletters
* Version: 0.3.28
* Version: 0.3.29
* Author: Alley Interactive
* Author URI: https://github.com/alleyinteractive/wp-newsletter-builder
* Requires at least: 6.2
Expand Down
78 changes: 35 additions & 43 deletions plugins/newsletter-status/newsletterStatusPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,55 @@
import React, { useCallback, useEffect, useState } from '@wordpress/element';
import React from '@wordpress/element';
import { PluginDocumentSettingPanel } from '@wordpress/edit-post';
import { __ } from '@wordpress/i18n';
import { select } from '@wordpress/data';
import apiFetch from '@wordpress/api-fetch';
import { Button } from '@wordpress/components';
import NewsletterSpinner from '@/components/newsletterSpinner';

interface Status {
Bounced?: number;
Clicks?: number;
Forwards?: number;
Likes?: number;
Mentions?: number;
Name?: string;
Recipients?: number;
SpamComplaints?: number;
Status?: string;
TotalOpened?: number;
UniqueOpened?: number;
Unsubscribed?: number;
WebVersionTextURL?: string;
WebVersionURL?: string;
WorldviewURL?: string;
}
import useNewsletterStatus from '@/hooks/useNewsletterStats';

export default function NewsletterStatusPanel() {
// @ts-ignore
const postId = select('core/editor').getCurrentPostId();

const [status, setStatus] = useState<Status>({});
const [fetching, setFetching] = useState(false);

const fetchStatus = useCallback(async () => {
setFetching(true);
const res = await apiFetch({
path: `/wp-newsletter-builder/v1/status/${postId}`,
});
setStatus(res as Status);
setFetching(false);
}, [postId]);

useEffect(() => {
fetchStatus();
}, [fetchStatus]);
const {
stats,
fetching,
fetchStats,
validStats,
} = useNewsletterStatus(postId);

const {
Status: statusString = '',
Name = '',
Recipients = null,
TotalOpened = null,
UniqueOpened = null,
} = status;
Recipients = '',
TotalOpened = '',
UniqueOpened = '',
} = stats;

if (!validStats && !fetching) {
return (
<PluginDocumentSettingPanel
name="rubric-selection"
title={__('Newsletter Stats', 'wp-newsletter-builder')}
>
<p>
{__('Newsletter stats not available. Try clicking the Refresh button.', 'wp-newsletter-builder')}
</p>
<Button
onClick={fetchStats}
variant="secondary"
disabled={fetching}
>
{__('Refresh', 'wp-newsletter-builder')}
</Button>
</PluginDocumentSettingPanel>
);
}

return (
<PluginDocumentSettingPanel
name="rubric-selection"
title={__('Newsletter Status', 'wp-newsletter-builder')}
title={__('Newsletter Stats', 'wp-newsletter-builder')}
>
{status ? (
{validStats ? (
<>
<dl>
<dt>
Expand Down Expand Up @@ -92,7 +84,7 @@ export default function NewsletterStatusPanel() {
</dd>
</dl>
<Button
onClick={fetchStatus}
onClick={fetchStats}
variant="secondary"
disabled={fetching}
>
Expand Down
4 changes: 3 additions & 1 deletion src/class-rest-api-endpoints.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,18 +190,20 @@ public function get_status( WP_REST_Request $request ): array {
'Status' => __( 'Not sent', 'wp-newsletter-builder' ),
];
}

global $newsletter_builder_email_provider;
if ( empty( $newsletter_builder_email_provider ) || ! $newsletter_builder_email_provider instanceof Email_Providers\Email_Provider ) {
return [
'Status' => __( 'No email provider selected for WP Newsletter Builder.', 'wp-newsletter-builder' ),
];
}
$status = $newsletter_builder_email_provider->get_campaign_summary( $campaign_id );
if ( ! empty( $status ) && is_array( $status['response'] ) && 200 === $status['http_status_code'] ) {
if ( ! empty( $status ) && is_array( $status['response'] ) && $status['success'] ) {
$status['response']['Status'] = __( 'Sent' ); // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
wp_cache_set( $cache_key, $status['response'], '', 5 * MINUTE_IN_SECONDS );
return $status['response'];
}

$send_result = get_post_meta( $post_id, 'nb_newsletter_send_result', true );
if ( ! empty( $send_result ) && is_array( $send_result ) ) {
if ( is_string( $send_result['response'] ) && ( 200 > $send_result['http_status_code'] || 300 <= $send_result['http_status_code'] ) ) {
Expand Down
6 changes: 3 additions & 3 deletions src/email-providers/class-campaign-monitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ public function create_campaign( int $newsletter_id, array $list_ids, string $ca
* @param string $campaign_id The campaign id.
* @return array{
* response: mixed,
* http_status_code: int,
* success: boolean,
* }|false The response from the API.
*/
public function send_campaign( string $campaign_id ): array|false {
Expand All @@ -184,8 +184,8 @@ public function send_campaign( string $campaign_id ): array|false {
);

return [
'response' => $result->response,
'http_status_code' => $result->http_status_code,
'response' => $result->response,
'success' => 200 === $result->http_status_code,
];
}

Expand Down
31 changes: 18 additions & 13 deletions src/email-providers/class-sendgrid.php
Original file line number Diff line number Diff line change
Expand Up @@ -241,27 +241,33 @@ public function send_campaign( string $campaign_id ): array|false {
* @param string $campaign_id The campaign id.
* @return array{
* response: mixed,
* http_status_code: int,
* success: boolean,
* }|false The response from the API.
*
* @todo: Get recipients, total opened, unique opened.
*/
public function get_campaign_summary( string $campaign_id ): array|false {
$sg = $this->get_client();
if ( empty( $sg ) ) {
return false;
}
$response = $sg->client->marketing()->singlesends()->_( $campaign_id )->get();
$body = (object) json_decode( $response->body() );
$campaign_response = $sg->client->marketing()->singlesends()->_( $campaign_id )->get();
$stats_response = $sg->client->marketing()->stats()->singlesends()->_( $campaign_id )->get();

if ( 200 !== $stats_response->statusCode() || 200 !== $campaign_response->statusCode() ) {
return false;
}

$campaign_body = (object) json_decode( $campaign_response->body() );
$stats_body = (object) json_decode( $stats_response->body() );

return [
'response' => [
'Status' => $body->status,
'Name' => $body->name,
'Recipients' => 'N/A',
'TotalOpened' => 'N/A',
'UniqueOpened' => 'N/A',
'response' => [
'Status' => $campaign_body?->status ?? '',
'Name' => $campaign_body?->name ?? '',
'Recipients' => $stats_body->results[0]?->stats?->delivered ?? '',
'TotalOpened' => $stats_body->results[0]?->stats?->opens ?? '',
'UniqueOpened' => $stats_body->results[0]?->stats?->unique_opens ?? '',
],
'http_status_code' => $response->statusCode(),
'success' => true,
];
}

Expand Down Expand Up @@ -416,7 +422,6 @@ private function get_content( $post_id ) {

// Capture template output for the new query.
ob_start();
// @phpstan-ignore-next-line
load_template( WP_PLUGIN_DIR . '/wp-newsletter-builder/single-nb_newsletter.php' );
$content = ob_get_clean();

Expand Down
2 changes: 1 addition & 1 deletion src/email-providers/interface-email-provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public function send_campaign( string $campaign_id ): array|false;
* @param string $campaign_id The campaign id.
* @return array{
* response: mixed,
* http_status_code: int,
* success: boolean,
* }|false The response from the API.
*/
public function get_campaign_summary( string $campaign_id ): array|false;
Expand Down

0 comments on commit 9ef2766

Please sign in to comment.