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(
'%s',
$url,
JVB()->email()->image($image[0], get_post_meta($img, '_wp_attachment_image_alt', true))
);
$t = sprintf(
'%s',
$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(
'%s',
$url,
JVB()->email()->image($image[0], get_post_meta($img, '_wp_attachment_image_alt', true))
);
$t = sprintf(
'%s',
$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);
}
}