<?php
|
namespace JVBase\managers\Notifications;
|
|
use JVBase\managers\Cache;
|
use JVBase\managers\CustomTable;
|
use JVBase\meta\Meta;
|
use JVBase\registrar\Registrar;
|
use WP_Error;
|
use WP_Query;
|
|
if (!defined('ABSPATH')) {
|
exit; // Exit if accessed directly
|
}
|
/**
|
* EmailDigests
|
* Prepares the email digest for daily/weekly/monthly email summaries
|
**/
|
class EmailDigests
|
{
|
protected string $campaign;
|
protected Cache $terms;
|
protected Cache $users;
|
protected CustomTable $userIndex;
|
protected CustomTable $termIndex;
|
public function __construct()
|
{
|
$this->registerCron();
|
$this->registerTable();
|
|
add_filter(BASE . 'handle_bulk_operation', [ $this, 'processOperation' ], 10, 3);
|
}
|
|
protected function registerCron():void
|
{
|
add_action(BASE . 'notification_digest_daily', [ $this, 'runDailyDigests' ]);
|
add_action(BASE . 'notification_digest_weekly', [ $this, 'runWeeklyDigests' ]);
|
add_action(BASE . 'notification_digest_monthly', [ $this, 'runMonthlyDigests' ]);
|
}
|
|
protected function registerTable():void
|
{
|
$this->registerUserIndex();
|
$this->registerTermIndex();
|
}
|
protected function registerUserIndex():void
|
{
|
$table = CustomTable::for('user_notification_email_digest');
|
// $types = implode(',',array_map(function($item) { return "'{$item}'"; }, Registrar::getFeatured('favouritable')));
|
$table->setColumns([
|
'id' => 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
|
'user_id' => "{$table->getUserIDType()} NOT NULL",
|
'frequency' => "ENUM('daily', 'weekly', 'monthly') NOT NULL",
|
'content_ids' => 'JSON DEFAULT NULL',
|
'output' => 'TEXT DEFAULT NULL',
|
'total_sent' => 'int unsigned DEFAULT 0',
|
'created_at' => 'datetime DEFAULT CURRENT_TIMESTAMP',
|
]);
|
|
$table->setKeys([
|
['key' => 'PRIMARY', 'value' => '(`id`)'],
|
['key' => 'UNIQUE', 'value' => '`user_frequency` (`user_id`, `frequency`)'],
|
'user_id (`user_id`)'
|
]);
|
$base = BASE;
|
$table->setConstraints([
|
"CONSTRAINT `{$base}digest_user` FOREIGN KEY (`user_id`)
|
REFERENCES `{$table->getUserTable()}` (`ID`) ON DELETE CASCADE"
|
]);
|
|
$table->defineTable();
|
$this->userIndex = $table;
|
}
|
protected function registerTermIndex():void
|
{
|
$table = CustomTable::for('user_notification_email_digest');
|
$types = implode(',',array_map(function($item) { return "'{$item}'"; }, Registrar::getFeatured('favouritable', 'term')));
|
$table->setColumns([
|
'id' => 'bigint(20) unsigned NOT NULL AUTO_INCREMENT',
|
'term_id' => "{$table->getTermIDType()} NOT NULL",
|
'type' => "ENUM({$types}) NOT NULL",
|
'frequency' => "ENUM('daily', 'weekly', 'monthly') NOT NULL",
|
'content_ids' => 'JSON DEFAULT NULL',
|
'output' => 'TEXT DEFAULT NULL',
|
'total_sent' => 'int unsigned DEFAULT 0',
|
'created_at' => 'datetime DEFAULT CURRENT_TIMESTAMP',
|
]);
|
|
$table->setKeys([
|
['key' => 'PRIMARY', 'value' => '(`id`)'],
|
['key' => 'UNIQUE', 'value' => '`term_frequency` (`term_id`, `type`, `frequency`)'],
|
'term_id (`term_id`)'
|
]);
|
$base = BASE;
|
$table->setConstraints([
|
"CONSTRAINT `{$base}digest_term` FOREIGN KEY (`term_id`)
|
REFERENCES `{$table->getTermTable()}` (`term_id`) ON DELETE CASCADE"
|
]);
|
|
$table->defineTable();
|
$this->termIndex = $table;
|
}
|
|
public function runDailyDigests():void
|
{
|
$this->campaign = 'daily_digest_' .date('Y-m-d');
|
$this->processDigest('daily');
|
}
|
public function runWeeklyDigests():void
|
{
|
$this->campaign = 'weekly_digest_'.date('Y-m-d');
|
$this->processDigest('weekly');
|
}
|
|
public function runMonthlyDigests():void
|
{
|
$this->campaign = 'monthly_digest_'.date('Y-m-d');
|
$this->processDigest('monthly');
|
}
|
|
protected function processDigest(string $frequency):void
|
{
|
$users = $this->getUsers($frequency);
|
if (empty($users)){
|
return;
|
}
|
|
JVB()->queue()->add(
|
'email_notification_digest',
|
0,
|
[
|
'frequency' => $frequency,
|
'users' => $users,
|
],
|
[
|
'chunk_key' => 'users',
|
'chunk_size'=> 20,
|
'operation_id'=> 'notification_digest_'.date('Y_m_d_His')
|
]
|
);
|
}
|
|
public function getUsers(string $frequency):array
|
{
|
if (!in_array($frequency, ['never', 'daily', 'weekly', 'monthly'])) {
|
return [];
|
}
|
return JVB()->notification()->getUsersByFrequency($frequency);
|
}
|
|
public function processOperation(WP_Error|array $result, object $operation, array $data):WP_Error|array
|
{
|
if ($operation->type !== 'email_notification_digest') {
|
return $result;
|
}
|
|
$results = [];
|
foreach ($data['user'] as $userID) {
|
$result = $this->generateUserDigest($userID, $data['frequency']);
|
if ($result) {
|
$results[$userID] = $result;
|
}
|
usleep(50);
|
}
|
|
return [
|
'success' => true,
|
'result' => $results
|
];
|
}
|
protected function getSinceDate(string $frequency):string
|
{
|
return match ($frequency) {
|
'weekly' => '-1 week',
|
'monthly' => '-1 month',
|
default => '-1 day',
|
};
|
}
|
|
protected function generateUserDigest(int $userID, string $frequency):string|false
|
{
|
$subscription = JVB()->notification()->getUserSubscriptions($userID, $frequency);
|
|
//TODO
|
$notifications = JVB()->notification()->getUserNotifications($userID);
|
|
if (empty($subscription) && empty($notifications)) {
|
return false;
|
}
|
$content = '';
|
foreach ($subscription as $item) {
|
$temp = match ($item['item_type']) {
|
array_merge(['user'], Registrar::getFeatured('favouritable', 'user')) => $this->getUserUpdates($item['item_id'], $frequency),
|
Registrar::getFeatured('favouritable', 'term') => $this->getTermUpdates($item['item_id'], $item['item_type'], $frequency),
|
default => false,
|
};
|
if ($temp) {
|
$content .= $temp;
|
}
|
}
|
if (empty($content)) {
|
return false;
|
}
|
return $content;
|
}
|
|
protected function getUserUpdates(int $userID, string $frequency):string|false
|
{
|
$frequency = strtolower($frequency);
|
$frequency = in_array($frequency, ['daily', 'weekly', 'monthly']) ? $frequency : 'daily';
|
$user = get_userdata($userID);
|
if (!$user || is_wp_error($user)) {
|
return false;
|
}
|
$since = $this->getSinceDate($frequency);
|
|
$entry = $this->userIndex->get([
|
'user_id' => $userID,
|
'frequency' => $frequency
|
]);
|
if ($entry) {
|
return $entry->output;
|
}
|
$new = [
|
'user_id' => $userID,
|
'frequency' => $frequency,
|
];
|
//Didn't do it yet, create it here
|
$IDs = Content::forUser($userID, $since);
|
$new['content_ids'] = json_encode($IDs);
|
$new['output'] = $this->contentForUser($userID, $IDs);
|
|
$this->userIndex->insert($new);
|
return $new['output'];
|
}
|
|
protected function contentForUser(int $userID, array $IDs):string
|
{
|
if (empty($IDs)) {
|
return '';
|
}
|
$user = get_userdata($userID);
|
if (!$user || is_wp_error($user)) {
|
return '';
|
}
|
$role = jvbUserRole($userID);
|
$registrar = Registrar::getInstance($role);
|
if (!$registrar) {
|
return '';
|
}
|
if ($registrar->profile_link) {
|
$meta = Meta::forPost(jvbUserProfileLink($userID));
|
$title = $meta->get('post_title');
|
$goTo = get_the_permalink(jvbUserProfileLink($userID));
|
}else {
|
$meta = Meta::forUser($userID);
|
$title = $meta->get('display_name');
|
$goTo = get_author_posts_url($userID);
|
}
|
|
$output = [];
|
|
$count = count($IDs);
|
$max = (min($count, 5));
|
$canIncrease = $count > $max;
|
for ($i = 0; $i <=$max; $i++) {
|
$ID = $IDs[$i];
|
$m = Meta::forPost($ID);
|
$img = $m->get('post_thumbnail');
|
if (empty($img)) {
|
if ($canIncrease){
|
$max++;
|
}
|
continue;
|
}
|
$image = wp_get_attachment_image_src($img, 'medium');
|
if (!$image) {
|
if ($canIncrease){
|
$max++;
|
}
|
continue;
|
}
|
$url = get_the_permalink($ID);
|
$image = sprintf(
|
'<a href="%s">%s</a>',
|
$url,
|
JVB()->email()->image($image[0], get_post_meta($img, '_wp_attachment_image_alt', true))
|
);
|
$t = sprintf(
|
'<a href="%s">%s</a>',
|
$url,
|
$m->get('post_title')
|
);
|
$output[] = JVB()->email()->card($image, $t);
|
}
|
|
$final = JVB()->email()->button($goTo, 'Want to See More?');
|
return JVB()->email()->grid($output, 3, 'New from '.$title,'',$final);
|
}
|
|
protected function getTermUpdates(int $termID, string $type, string $frequency):string|false
|
{
|
$frequency = strtolower($frequency);
|
$frequency = in_array($frequency, ['daily', 'weekly', 'monthly']) ? $frequency : 'daily';
|
$taxonomy = jvbCheckBase($type);
|
$term = get_term($termID, $taxonomy);
|
if (!$term || is_wp_error($term)) {
|
return false;
|
}
|
$since = $this->getSinceDate($frequency);
|
|
$entry = $this->termIndex->get([
|
'term_id' => $termID,
|
'type' => $type,
|
'frequency' => $frequency
|
]);
|
if ($entry) {
|
return $entry->output;
|
}
|
$new = [
|
'term_id' => $termID,
|
'type' => $type,
|
'frequency' => $frequency
|
];
|
//Didn't do it yet, create it here
|
$IDs = Content::forTerm($termID, $taxonomy, $since);
|
$new['content_ids'] = json_encode($IDs);
|
$new['output'] = $this->contentForTerm($termID, $type, $IDs);
|
|
$this->termIndex->insert($new);
|
return $new['output'];
|
}
|
|
protected function contentForTerm(int $termID, string $tax, array $IDs):string
|
{
|
if (empty($IDs)) {
|
return '';
|
}
|
$taxonomy = jvbCheckBase($tax);
|
$term = get_term($termID, $taxonomy);
|
if (!$term || is_wp_error($term)) {
|
return '';
|
}
|
$registrar = Registrar::getInstance($tax);
|
if (!$registrar) {
|
return '';
|
}
|
$meta = Meta::forTerm($termID);
|
$title = $meta->get('name');
|
|
$goTo = get_term_link($termID,$taxonomy);
|
|
$output = [];
|
|
$count = count($IDs);
|
$max = (min($count, 5));
|
$canIncrease = $count > $max;
|
for ($i = 0; $i <=$max; $i++) {
|
$ID = $IDs[$i];
|
$m = Meta::forPost($ID);
|
$img = $m->get('post_thumbnail');
|
if (empty($img)) {
|
if ($canIncrease){
|
$max++;
|
}
|
continue;
|
}
|
$image = wp_get_attachment_image_src($img, 'medium');
|
if (!$image) {
|
if ($canIncrease){
|
$max++;
|
}
|
continue;
|
}
|
$url = get_the_permalink($ID);
|
$image = sprintf(
|
'<a href="%s">%s</a>',
|
$url,
|
JVB()->email()->image($image[0], get_post_meta($img, '_wp_attachment_image_alt', true))
|
);
|
$t = sprintf(
|
'<a href="%s">%s</a>',
|
$url,
|
$m->get('post_title')
|
);
|
$output[] = JVB()->email()->card($image, $t);
|
}
|
|
$final = JVB()->email()->button($goTo, 'See More');
|
return JVB()->email()->grid($output, 3, 'New in '.$title, '', $final);
|
}
|
}
|