Working on Campaign Stats page
authorSteve Sutton <steve@gaslightmedia.com>
Fri, 23 Aug 2019 13:08:10 +0000 (09:08 -0400)
committerSteve Sutton <steve@gaslightmedia.com>
Fri, 23 Aug 2019 13:08:10 +0000 (09:08 -0400)
Adding campaign stats
Adding filter for message (subject)

classes/data/dataEmailLogs.php [new file with mode: 0644]
classes/data/dataEmailMessages.php
models/admin/ajax/ajaxMessagePreview.php
models/admin/ajax/trackMessagePixel.php
models/admin/messages/index.php
setup/routes.php [new file with mode: 0644]
views/admin/header.html
views/admin/messages/list.html
views/admin/messages/stats.html

diff --git a/classes/data/dataEmailLogs.php b/classes/data/dataEmailLogs.php
new file mode 100644 (file)
index 0000000..fb01174
--- /dev/null
@@ -0,0 +1,184 @@
+<?php
+/**
+ * GLM Member-DB WordPress Add-On Plugin
+ * Data Class Email Messages
+ *
+ * PHP version 5.3
+ *
+ * @category Data
+ * @package  GLM Member-DB
+ * @author   Chuck Scott <cscott@gaslightmedia.com>
+ * @license  http://www.gaslightmedia.com Gaslightmedia
+ * @release  SVN: $Id: dataEvents.php,v 1.0 2011/01/25 19:31:47 cscott Exp $
+ */
+
+/**
+ * GlmDataEmailMessages class
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package GLM Member DB
+ * @author  Chuck Scott <cscott@gaslightmedia.com>
+ * @license http://www.gaslightmedia.com Gaslightmedia
+ *          @release SVN: $Id: dataMembers.php,v 1.0 2011/01/25 19:31:47 cscott
+ *          Exp $
+ */
+class GlmDataEmailLogs extends GlmDataAbstract
+{
+
+    /**
+     * WordPress Database Object
+     *
+     * @var $wpdb
+     * @access public
+     */
+    public $wpdb;
+    /**
+     * Plugin Configuration Data
+     *
+     * @var $config
+     * @access public
+     */
+    public $config;
+    /**
+     * Data Table Name
+     *
+     * @var $table
+     * @access public
+     */
+    public $table;
+    /**
+     * Field definitions
+     *
+     * 'type' is type of field as defined by the application
+     * text Regular text field
+     * pointer Pointer to an entry in another table
+     * 'filters' is the filter name for a particular filter ID in PHP filter
+     * functions
+     * See PHP filter_id()
+     *
+     * 'use' is when to use the field
+     * l = List
+     * g = Get
+     * n = New
+     * i = Insert
+     * e = Edit
+     * u = Update
+     * d = Delete
+     * a = All
+     *
+     * @var $ini
+     * @access public
+     */
+    public $fields = false;
+
+    public $postOpens = false;
+
+    /**
+     * Constructor
+     *
+     * @param object $d database connection
+     * @param array $config Configuration array
+     * @param bool $limitedEdit Flag to say indicate limited edit requested
+     *
+     * @return void
+     * @access public
+     */
+    public function __construct($wpdb, $config, $limitedEdit = false)
+    {
+
+        // If this class is not being extended along with existing $wpdb and $config
+        if (!$this->wpdb) {
+
+            // Save WordPress Database object
+            $this->wpdb = $wpdb;
+
+            // Save plugin configuration object
+            $this->config = $config;
+
+        }
+
+        /*
+         * Table Name
+         */
+        $this->table = GLM_MEMBERS_MESSAGES_PLUGIN_DB_PREFIX . 'email_logs';
+
+        /*
+         * Table Data Fields
+         */
+
+        $this->fields = [
+
+            'id' => [
+                'field' => 'id',
+                'type' => 'integer',
+                'view_only' => true,
+                'use' => 'a'
+            ],
+
+            // process id
+            'process_id' => [
+                'field'    => 'process_id',
+                'type'     => 'text',
+                'required' => false,
+                'use'      => 'a'
+            ],
+
+            // send date
+            'send_date' => [
+                'field'    => 'send_date',
+                'type'     => 'date',
+                'required' => false,
+                'use'      => 'a'
+            ],
+
+            // Message id
+            'message_id' => [
+                'field'      => 'message_id',
+                'type'       => 'pointer',
+                'p_table'    => GLM_MEMBERS_MESSAGES_PLUGIN_DB_PREFIX . 'email_messages',
+                'p_field'    => 'subject',
+                'p_orderby'  => 'subject',
+                'force_list' => false,
+                'required'   => false,
+                'use'        => 'a'
+            ],
+
+        ];
+
+    }
+
+    /**
+     * Entry Post Processing Call-Back Method
+     *
+     * Perform post-processing for all result entries.
+     *
+     * In this case we're using it to append an array of category
+     * data to each member result and also sort by member name.
+     *
+     * @param array $r Array of field result data for a single entry
+     * @param string $a Action being performed (l, i, g, ...)
+     *
+     * @return object Class object
+     *
+     */
+    public function entryPostProcessing($r, $a)
+    {
+        if ( $this->postOpens ) {
+            $opens = $this->wpdb->get_var(
+                $this->wpdb->prepare(
+                    "SELECT count(*)
+                       FROM " . GLM_MEMBERS_MESSAGES_PLUGIN_DB_PREFIX . "email_logs
+                      WHERE process_id = %s
+                        AND read_date IS NOT NULL",
+                    $r['process_id']
+                )
+            );
+            $r['opens'] = ( $opens ) ? $opens : '0';
+        }
+        return $r;
+    }
+
+
+}
index b8e6913..2788f2b 100644 (file)
@@ -125,7 +125,7 @@ class GlmDataEmailMessages extends GlmDataAbstract
                 'use'   => 'a',
             ),
 
-            // Member Type
+            // Template Id
             'template_id' => array (
                 'field'      => 'template_id',
                 'type'       => 'pointer',
index 3ea28cf..bf050a1 100644 (file)
@@ -33,13 +33,7 @@ require_once GLM_MEMBERS_MESSAGES_PLUGIN_PATH . '/models/admin/messages/index.ph
  */
 
 /**
- * This class handles the work of creating new invoices based on.
- * 1) Member Type of member matching a paid invoiceType
- * 2) Member renewal date past
- * 3) Member has Billing Account
- * 4) Member has no active Invoice
- * 5) Renewal date is within the next 30 Days
- *
+ * Ajax previewer for Email messages
  */
 class GlmMembersAdmin_ajax_ajaxMessagePreview extends GlmMembersAdmin_messages_index
 {
index ac055b8..f444592 100644 (file)
  */
 
 /**
- * This class handles the work of creating new invoices based on.
- * 1) Member Type of member matching a paid invoiceType
- * 2) Member renewal date past
- * 3) Member has Billing Account
- * 4) Member has no active Invoice
- * 5) Renewal date is within the next 30 Days
- *
+ * Track the email opens
  */
 class GlmMembersAdmin_ajax_trackMessagePixel
 {
index b561874..2177f94 100644 (file)
@@ -16,6 +16,7 @@
 // Load Members data abstract
 require_once GLM_MEMBERS_MESSAGES_PLUGIN_CLASS_PATH.'/data/dataEmailMessages.php';
 require_once GLM_MEMBERS_MESSAGES_PLUGIN_CLASS_PATH.'/data/dataEmailTemplates.php';
+require_once GLM_MEMBERS_MESSAGES_PLUGIN_CLASS_PATH.'/data/dataEmailLogs.php';
 
 /*
  * This class performs the work for the default action of the "Members" menu
@@ -104,6 +105,7 @@ class GlmMembersAdmin_messages_index extends GlmDataEmailMessages
         $option    = '';
         $option2   = false;
         $viewPath  = 'admin/messages/';
+        // Merge Tags
         $mergeTags = [
             // 'Member Name', '{$member.name}',
             'Contact First Name', '{$contact.fname}',
@@ -117,6 +119,14 @@ class GlmMembersAdmin_messages_index extends GlmDataEmailMessages
             // 'Contact Home Phone', '{$contact.home_phone}',
             // 'Contact Mobile Phone', '{$contact.mobile_phone}',
         ];
+        // For Paging
+        $paging        = true;
+        $start         = 1;
+        $limit         = 20;
+        $numbDisplayed = false;
+        $lastDisplayed = false;
+        $prevStart     = false;
+        $nextStart     = false;
 
         // Setup Foundation 6
         wp_enqueue_style( 'Foundation6', GLM_MEMBERS_PLUGIN_URL . 'css/foundation-6.min.css' );
@@ -150,8 +160,97 @@ class GlmMembersAdmin_messages_index extends GlmDataEmailMessages
         switch ( $option ) {
         case 'stats':
             $view = 'stats';
+            $whereParts = [
+                'process_id IS NOT NULL'
+            ];
+            $where = false;
+            if ( isset( $_REQUEST['filterMessage'] ) && $filterMessage = filter_var( $_REQUEST['filterMessage'], FILTER_SANITIZE_STRING) ) {
+                $whereParts[] = " message_id IN ( SELECT id FROM " . GLM_MEMBERS_MESSAGES_PLUGIN_DB_PREFIX . "email_messages WHERE subject like '%" . $filterMessage . "%')";
+            }
+            // Check if we're doing paging (must be done before getList or getResults)
+            if ( isset( $_REQUEST['pageSelect'] ) ) {
+
+                // If request is for Next
+                if ( $_REQUEST['pageSelect'][0] == 'N' ) {
+                    $newStart = $_REQUEST['nextStart'] - 0;
+
+                // Otherwise it must be Previous
+                } else {
+                    $newStart = $_REQUEST['prevStart'] - 0;
+                }
+
+                if ( $newStart > 0 ) {
+                    $start = $newStart;
+                }
+            }
+            if ( !empty( $whereParts ) ) {
+                $where = implode( ' AND ', $whereParts );
+            }
+            // Get logs
+            $Stats            = new GlmDataEmailLogs( $this->wpdb, $this->config );
+            $Stats->postOpens = true;
+            $statsData        = $Stats->getResults(
+                [
+                    'where'   => $where,
+                    'groupby' => [
+                        'aggregate' => 'count(*) as emails',
+                        'group'     => 'process_id',
+                    ],
+                    'start'   => $start,
+                    'limit'   => $limit,
+                    'stats'   => true,
+                ]
+            );
+            $statCount = $statsData['stats'];
+            // echo '<pre>$statCount: ' . print_r( $statCount, true ) . '</pre>';
+            $Stats->postOpens = false;
+            // Get paging results (after getList or getResults)
+            $numbDisplayed = $statsData['returned'];
+            $lastDisplayed = $statsData['last'];
+            if ( $start == 1 ) {
+                $prevStart = false;
+            } else {
+                $prevStart = $start - $limit;
+                if ( $start < 1 ) {
+                    $start = 1;
+                }
+            }
+            if ( $numbDisplayed == $limit ) {
+                $nextStart = $start + $limit;
+            }
+            if ( $statCount <= $limit ) {
+                $paging = false;
+            } else {
+                $paging = [
+                    'unitName'      => 'Stats',
+                    'start'         => $start,
+                    'limit'         => $limit,
+                    'numbDisplayed' => $numbDisplayed,
+                    'lastDisplayed' => $lastDisplayed,
+                    'prevStart'     => $prevStart,
+                    'nextStart'     => $nextStart,
+                ];
+            }
+            $stats = $statsData['list'];
+            $messageData = $this->wpdb->get_results(
+                "SELECT id,subject
+                   FROM " . GLM_MEMBERS_MESSAGES_PLUGIN_DB_PREFIX . "email_messages
+                  ORDER BY subject"
+            );
+            $messages = array();
+            foreach ( $messageData as $row ) {
+                $messages[$row->id] = $row->subject;
+            }
             $tData = [
-                'stats' => 'test'
+                'stats'    => $stats,
+                'messages' => $messages,
+                'headers'  => [
+                    'Message',
+                    'Date Sent',
+                    'Total Sent',
+                    'Opens',
+                ],
+                'paging'   => $paging,
             ];
             break;
         case 'search':
@@ -466,6 +565,23 @@ class GlmMembersAdmin_messages_index extends GlmDataEmailMessages
             $view = 'list';
 
         default:
+            // Check if we're doing paging (must be done before getList or getResults)
+            if ( isset( $_REQUEST['pageSelect'] ) ) {
+
+                // If request is for Next
+                if ( $_REQUEST['pageSelect'][0] == 'N' ) {
+                    $newStart = $_REQUEST['nextStart'] - 0;
+
+                // Otherwise it must be Previous
+                } else {
+                    $newStart = $_REQUEST['prevStart'] - 0;
+                }
+
+                if ( $newStart > 0 ) {
+                    $start = $newStart;
+                }
+            }
+
             if ( isset( $_REQUEST['filterArchived'] ) && $filterArchived = filter_var( $_REQUEST['filterArchived'], FILTER_VALIDATE_BOOLEAN ) ) {
                 $where = "T.archived = true";
             } else {
@@ -473,12 +589,43 @@ class GlmMembersAdmin_messages_index extends GlmDataEmailMessages
             }
             $this->postStats = true;
             $this->postSent  = true;
-            $messages = $this->getList( $where );
+            $order           = 'last_updated DESC';
+            $messageCount    = $this->getStats( $where );
+            $messageData     = $this->getList( $where, $order, true, 'id', $start, $limit );
+            // Get paging results (after getList or getResults)
+            $numbDisplayed = $messageData['returned'];
+            $lastDisplayed = $messageData['last'];
+            if ( $start == 1 ) {
+                $prevStart = false;
+            } else {
+                $prevStart = $start - $limit;
+                if ( $start < 1 ) {
+                    $start = 1;
+                }
+            }
+            if ( $numbDisplayed == $limit ) {
+                $nextStart = $start + $limit;
+            }
+            if ( $messageCount <= $limit ) {
+                $paging = false;
+            } else {
+                $paging = [
+                    'unitName'      => 'Messages',
+                    'start'         => $start,
+                    'limit'         => $limit,
+                    'numbDisplayed' => $numbDisplayed,
+                    'lastDisplayed' => $lastDisplayed,
+                    'prevStart'     => $prevStart,
+                    'nextStart'     => $nextStart,
+                ];
+            }
+            $messages = $messageData['list'];
             $this->postStats = false;
             $this->postSent  = false;
-            $tData = array(
+            $tData = [
                 'messages' => $messages,
-            );
+                'paging'   => $paging,
+            ];
             break;
         }
 
diff --git a/setup/routes.php b/setup/routes.php
new file mode 100644 (file)
index 0000000..b908e8f
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+
+class Glm_REST_Messages_Controller
+{
+    public function __construct()
+    {
+        $this->namespace     = '/glm/v0';
+        $this->resource_name = 'messages';
+    }
+
+    public function register_routes()
+    {
+        register_rest_route( $this->namespace, '/' . $this->resource_name, array(
+            // Here we register the readable endpoint for collections.
+            array(
+                'methods'   => 'GET',
+                'callback'  => array( $this, 'get_items' ),
+            ),
+        ) );
+        register_rest_route( $this->namespace, '/' . $this->resource_name . '/(?P<id>[\d]+)', array(
+            // Notice how we are registering multiple endpoints the 'schema' equates to an OPTIONS request.
+            array(
+                'methods'   => 'GET',
+                'callback'  => array( $this, 'get_item' ),
+            ),
+        ) );
+    }
+
+    /**
+     * Grabs the five most recent posts and outputs them as a rest response.
+     *
+     * @param WP_REST_Request $request Current request.
+     */
+    public function get_item( $request ) {
+        $id   = (int) $request['id'];
+        $post = get_post( $id );
+
+        if ( empty( $post ) ) {
+            return rest_ensure_response( array() );
+        }
+
+        $response = prepare_item_for_response( $post );
+
+        // Return all of our post response data.
+        return $response;
+    }
+
+}
+
+add_action( 'rest_api_init', function(){
+    $controller = new Glm_REST_Messages_Controller();
+    $controller->register_routes();
+});
index b6e3888..c997176 100644 (file)
@@ -15,7 +15,7 @@
                         <a href="{$thisUrl}?page={$thisPage}&option=list">Email Messages</a>
                     </li>
                     <li class="{if $thisOption == 'stats'}is-active{/if}">
-                        <a href="{$thisUrl}?page={$thisPage}&option=stats">Stats</a>
+                        <a href="{$thisUrl}?page={$thisPage}&option=stats">Campaign Stats</a>
                     </li>
                     {*
                     <li class="{if $thisOption == 'listTemplates' || $thisOption == 'editTemplate'}is-active{/if}">
index 18490de..30b136b 100644 (file)
@@ -1,50 +1,60 @@
+{* Emails Messages List view *}
+
+{* Header *}
 {include file='admin/header.html'}
 <h3>List Messages</h3>
 
 {* Form Start *}
-{$ui = [
-    'id'     => 'messageSearch',
-    'action' => "{$thisUrl}?page={$thisPage}",
-    'method' => 'post',
-    'file'   => false
-]}
-{include file='ui/f6/form-start.html'}
-
-<input type="hidden" name="option" value="list" />
-
-<div class="grid-x grid-margin-x">
-    <fieldset class="fieldset cell small-12 medium-6">
-        <legend>Filter</legend>
-
-        {* Show Archived *}
-        {$ui = [
-            'value'     => $smarty.request.filterArchived|default:'',
-            'field'     => 'filterArchived',
-            'label'     => 'Show Archived',
-            'required'  => false,
-            'errorText' => 'Show Archived is Required',
-            'dataError' => ''
-        ]}
-        {include file='ui/f6/checkbox.html'}
-
-        {* Submit *}
-        {$ui = [
-            'class'  => 'primary',
-            'label'  => 'Submit',
-            'submit' => true,
-            'id'     => '',
-            'cancel' => ""
-        ]}
-        {include file='ui/f6/submit.html'}
-
-    </fieldset>
-</div>
+<form action="{$thisUrl}?page={$thisPage}" method="post" id="messageSearch">
+
+    <input type="hidden" name="option" value="list" />
+    <input type="hidden" name="prevStart" value="{$paging.prevStart}">
+    <input type="hidden" name="nextStart" value="{$paging.nextStart}">
+    <input type="hidden" name="limit" value="{$paging.limit}">
+
+    <div class="grid-x grid-margin-x">
+        <fieldset class="fieldset cell small-12 medium-6">
+            <legend>Filter</legend>
+
+            {* Show Archived *}
+            {$ui = [
+                'value'     => $smarty.request.filterArchived|default:'',
+                'field'     => 'filterArchived',
+                'label'     => 'Show Archived',
+                'required'  => false,
+                'errorText' => 'Show Archived is Required',
+                'dataError' => ''
+            ]}
+            {include file='ui/f6/checkbox.html'}
+
+            {* Submit *}
+            {$ui = [
+                'class'  => 'primary',
+                'label'  => 'Submit',
+                'submit' => true,
+                'id'     => '',
+                'cancel' => ""
+            ]}
+            {include file='ui/f6/submit.html'}
+
+        </fieldset>
+    </div>
 
-{* Form End *}
-{include file='ui/f6/form-end.html'}
 
 <a href="{$thisUrl}?page={$thisPage}&option=editHtmlEmail" class="button primary">Add Message</a>
 
+<br>
+{* Paging *}
+{include file="ui/f6/paging.html"}
+
+{* List Messages *}
 {include file='admin/messages/listMessagesTable.html'}
 
+{* Paging *}
+{include file="ui/f6/paging.html"}
+
+{* Form End *}
+{include file='ui/f6/form-end.html'}
+
+{* Footer *}
 {include file='../../admin/footer.html'}
index 01844de..a189d1b 100644 (file)
@@ -1,6 +1,67 @@
+{* Stats List View *}
+
+{* Header *}
 {include file='admin/header.html'}
-<h3>Stats</h3>
+<h3>Campaign Stats</h3>
+
+{* Form Start *}
+<form method="post" action="{$thisUrl}?page={$thisPage}">
+    <input type="hidden" name="option" value="stats">
+    <input type="hidden" name="prevStart" value="{$paging.prevStart}">
+    <input type="hidden" name="nextStart" value="{$paging.nextStart}">
+    <input type="hidden" name="limit" value="{$paging.limit}">
+
+    <div class="grid-x grid-margin-x">
+        <fieldset class="fieldset cell small-12 medium-6">
+            <legend>Filter</legend>
+
+            {* Message *}
+            {$ui = [
+                'value'       => $smarty.request.filterMessage|default:'',
+                'field'       => 'filterMessage',
+                'label'       => 'Message',
+                'required'    => false,
+                'errorText'   => 'Message is Required',
+                'dataError'   => ''
+            ]}
+            {include file='ui/f6/text.html'}
+
+            {* Submit *}
+            {$ui = [
+                'class'  => 'primary',
+                'label'  => 'Submit',
+                'submit' => true,
+                'id'     => '',
+                'cancel' => ""
+            ]}
+            {include file='ui/f6/submit.html'}
+
+        </fieldset>
+    </div>
+
+    {* Paging *}
+    {include file="ui/f6/paging.html"}
+
+    <table>
+        <tr>
+            <th align="left"> Message </th>
+            <th align="left"> Date Sent </th>
+            <th align="left"> Total Sent </th>
+            <th align="left"> Opens </th>
+        </tr>
+        {foreach $stats as $campaign}
+            <tr>
+                <td> {$campaign.message_id} </td>
+                <td> {$campaign.send_date.date} </td>
+                <td> {$campaign.emails} </td>
+                <td> {$campaign.opens} </td>
+            </tr>
+        {/foreach}
+    </table>
+
+    {* Paging *}
+    {include file="ui/f6/paging.html"}
 
-{$stats}
+</form>
 
 {include file='../../admin/footer.html'}