374 lines
12 KiB
PHP
374 lines
12 KiB
PHP
<?php
|
|
|
|
namespace FluentCrm\App\Hooks\Handlers;
|
|
|
|
use FluentCrm\App\Models\CampaignEmail;
|
|
use FluentCrm\App\Models\CampaignUrlMetric;
|
|
use FluentCrm\App\Models\Company;
|
|
use FluentCrm\App\Models\CompanyNote;
|
|
use FluentCrm\App\Models\FunnelMetric;
|
|
use FluentCrm\App\Models\FunnelSubscriber;
|
|
use FluentCrm\App\Models\Subscriber;
|
|
use FluentCrm\App\Models\SubscriberMeta;
|
|
use FluentCrm\App\Models\SubscriberNote;
|
|
use FluentCrm\App\Models\SubscriberPivot;
|
|
use FluentCrm\App\Services\BlockParser;
|
|
use FluentCrm\App\Services\Helper;
|
|
use FluentCrm\App\Models\Meta;
|
|
|
|
|
|
/**
|
|
* Cleanup Class
|
|
*
|
|
* Used to handle cleanup related assets for subscribers, campaigns and automations.
|
|
*
|
|
* @package FluentCrm\App\Hooks
|
|
*
|
|
* @version 1.0.0
|
|
*/
|
|
class Cleanup
|
|
{
|
|
/**
|
|
* Cleanup related data of a subscriber.
|
|
*
|
|
* @param array $subscriberIds
|
|
*/
|
|
public function deleteSubscribersAssets($subscriberIds)
|
|
{
|
|
CampaignEmail::whereIn('subscriber_id', $subscriberIds)->delete();
|
|
CampaignUrlMetric::whereIn('subscriber_id', $subscriberIds)->delete();
|
|
SubscriberMeta::whereIn('subscriber_id', $subscriberIds)->delete();
|
|
SubscriberNote::whereIn('subscriber_id', $subscriberIds)->delete();
|
|
SubscriberPivot::whereIn('subscriber_id', $subscriberIds)->delete();
|
|
FunnelMetric::whereIn('subscriber_id', $subscriberIds)->delete();
|
|
FunnelSubscriber::whereIn('subscriber_id', $subscriberIds)->delete();
|
|
|
|
if (defined('FLUENTCAMPAIGN_DIR_FILE')) {
|
|
\FluentCampaign\App\Models\SequenceTracker::whereIn('subscriber_id', $subscriberIds)->delete();
|
|
}
|
|
|
|
if (Helper::isExperimentalEnabled('company_module')) {
|
|
Company::whereIn('owner_id', $subscriberIds)
|
|
->update([
|
|
'owner_id' => NULL
|
|
]);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Cleanup related data of a campaign.
|
|
*
|
|
* @param int $campaignId
|
|
*/
|
|
public function deleteCampaignAssets($campaignId)
|
|
{
|
|
// Idempotent backstop — Campaign::deleteCampaignData() already removes
|
|
// these in the normal delete flow, but we keep this here so any
|
|
// future caller that fires fluent_crm/campaign_deleted without
|
|
// running deleteCampaignData() first still gets a clean teardown.
|
|
CampaignEmail::where('campaign_id', $campaignId)->delete();
|
|
CampaignUrlMetric::where('campaign_id', $campaignId)->delete();
|
|
}
|
|
|
|
/**
|
|
* Cleanup related data of a list.
|
|
*
|
|
* @param int $listId
|
|
*/
|
|
public function deleteListAssets($listId)
|
|
{
|
|
SubscriberPivot::where('object_type', 'FluentCrm\App\Models\Lists')->where('object_id', $listId)->delete();
|
|
}
|
|
|
|
/**
|
|
* Cleanup related data of a tag.
|
|
*
|
|
* @param int $listId
|
|
*/
|
|
public function deleteTagAssets($listId)
|
|
{
|
|
SubscriberPivot::where('object_type', 'FluentCrm\App\Models\Tag')->where('object_id', $listId)->delete();
|
|
}
|
|
|
|
/**
|
|
* Cancel Future Emails.
|
|
*
|
|
* @param \FluentCrm\App\Models\Subscriber $subscriber
|
|
*/
|
|
public function handleUnsubscribe($subscriber)
|
|
{
|
|
// Per-statement try/catch: a row-lock deadlock against the mailer
|
|
// workers on the CampaignEmail update should not also block the
|
|
// FunnelSubscriber / SequenceTracker cancellations. The next status
|
|
// transition (or a manual retry) will reconcile any rows we miss.
|
|
try {
|
|
CampaignEmail::where('subscriber_id', $subscriber->id)
|
|
->whereIn('status', ['pending', 'scheduled', 'draft', 'processing', 'scheduling'])
|
|
->update([
|
|
'status' => 'cancelled'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Helper::debugLog('handleUnsubscribe', 'CampaignEmail cancel deferred: ' . $e->getMessage(), 'extended');
|
|
}
|
|
|
|
try {
|
|
FunnelSubscriber::where('subscriber_id', $subscriber->id)
|
|
->where('status', 'active')
|
|
->whereDoesntHave('funnel', function ($query) {
|
|
$query->where('trigger_name', 'fluent_crm/subscriber_status_changed');
|
|
})
|
|
->update([
|
|
'status' => 'cancelled'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Helper::debugLog('handleUnsubscribe', 'FunnelSubscriber cancel deferred: ' . $e->getMessage(), 'extended');
|
|
}
|
|
|
|
if (defined('FLUENTCAMPAIGN')) {
|
|
try {
|
|
\FluentCampaign\App\Models\SequenceTracker::where('subscriber_id', $subscriber->id)
|
|
->where('status', 'active')
|
|
->update([
|
|
'status' => 'cancelled'
|
|
]);
|
|
} catch (\Exception $e) {
|
|
Helper::debugLog('handleUnsubscribe', 'SequenceTracker cancel deferred: ' . $e->getMessage(), 'extended');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Change the future emails email_address of a provided contact.
|
|
*
|
|
* @param \FluentCrm\App\Models\Subscriber $subscriber
|
|
*/
|
|
public function handleContactEmailChanged($subscriber)
|
|
{
|
|
CampaignEmail::where('subscriber_id', $subscriber->id)
|
|
->whereIn('status', ['draft', 'scheduled'])
|
|
->update([
|
|
'email_address' => $subscriber->email
|
|
]);
|
|
}
|
|
|
|
|
|
/**
|
|
* @param $userId int
|
|
* @param $resign int|null
|
|
* @param $deletedUser \WP_User
|
|
* @return bool
|
|
*/
|
|
public function handleUserDelete($userId, $resign, $deletedUser)
|
|
{
|
|
$settings = Helper::getComplianceSettings();
|
|
if ($settings['delete_contact_on_user'] !== 'yes') {
|
|
return false;
|
|
}
|
|
|
|
$subscriber = Subscriber::where('user_id', $userId)->first();
|
|
|
|
if (!$subscriber && $deletedUser) {
|
|
$subscriber = Subscriber::where('email', $deletedUser->user_email)->first();
|
|
}
|
|
|
|
if (!$subscriber) {
|
|
return false;
|
|
}
|
|
|
|
// delete the subscriber now;
|
|
Helper::deleteContacts([$subscriber->id]);
|
|
|
|
return true;
|
|
}
|
|
|
|
public function attachCrmExporter($exporters)
|
|
{
|
|
$settings = Helper::getComplianceSettings();
|
|
if ($settings['personal_data_export'] !== 'yes') {
|
|
return $exporters;
|
|
}
|
|
|
|
$exporters['fluent-crm'] = [
|
|
'exporter_friendly_name' => __('FluentCRM Data', 'fluent-crm'),
|
|
'callback' => [$this, 'exportPersonalDataWP'],
|
|
];
|
|
|
|
return $exporters;
|
|
|
|
}
|
|
|
|
public function exportPersonalDataWP($user_email, $page = 1)
|
|
{
|
|
$subscriber = Subscriber::where('email', $user_email)->first();
|
|
|
|
if (!$subscriber) {
|
|
return [
|
|
'data' => [],
|
|
'done' => true
|
|
];
|
|
}
|
|
|
|
$customerFields = $subscriber->custom_fields();
|
|
$mainFields = $subscriber->toArray();
|
|
|
|
$data = [
|
|
'group_id' => 'fluent-crm-contact',
|
|
'group_label' => __('FluentCRM Data', 'fluent-crm'),
|
|
'item_id' => 'crm-contact',
|
|
'data' => []
|
|
];
|
|
|
|
foreach ($mainFields as $fieldKey => $fieldValue) {
|
|
if ($fieldValue) {
|
|
$data['data'][] = [
|
|
'name' => $fieldKey,
|
|
'value' => $fieldValue
|
|
];
|
|
}
|
|
}
|
|
|
|
foreach ($customerFields as $fieldKey => $customerField) {
|
|
$data['data'][] = [
|
|
'name' => $fieldKey,
|
|
'value' => $customerField
|
|
];
|
|
}
|
|
|
|
return [
|
|
'data' => [$data],
|
|
'done' => true,
|
|
];
|
|
}
|
|
|
|
public function handleCompanyDelete($id)
|
|
{
|
|
/*
|
|
* Remove Company ID from all connected subscribers
|
|
*/
|
|
Subscriber::where('company_id', $id)->update([
|
|
'company_id' => NULL
|
|
]);
|
|
|
|
fluentCrmDb()->table('fc_subscriber_pivot')
|
|
->where('object_id', $id)
|
|
->where('object_type', 'FluentCrm\App\Models\Company')
|
|
->delete();
|
|
|
|
// Delete company notes
|
|
CompanyNote::where('subscriber_id', $id)->delete();
|
|
}
|
|
|
|
public function handleUserPasswordChanged($user)
|
|
{
|
|
$contact = Subscriber::where('email', $user->user_email)
|
|
->first();
|
|
|
|
if (!$contact) {
|
|
return false;
|
|
}
|
|
|
|
$exist = SubscriberMeta::where('subscriber_id', $contact->id)
|
|
->where('key', '_secure_managed_hash')
|
|
->first();
|
|
|
|
if (!$exist) {
|
|
return false;
|
|
}
|
|
|
|
$hash = md5(wp_generate_uuid4() . '_' . $contact->id . '_' . '_' . time() . '__' . $contact->id);
|
|
$exist->value = $hash;
|
|
$exist->updated_at = current_time('mysql');
|
|
$exist->save();
|
|
|
|
return true;
|
|
}
|
|
|
|
public function archiveCampaignAssets($campaign)
|
|
{
|
|
if ($campaign->type != 'campaign' || fluentcrm_get_campaign_meta($campaign->id, '_cached_email_body', true)) {
|
|
return;
|
|
}
|
|
|
|
// We will create email body and then cache it for future use
|
|
$rawTemplates = [
|
|
'raw_html',
|
|
'visual_builder',
|
|
'raw_classic'
|
|
];
|
|
|
|
if (in_array($campaign->design_template, $rawTemplates)) {
|
|
$emailBody = $campaign->email_body;
|
|
} else {
|
|
$emailBody = (new BlockParser())->parse($campaign->email_body);
|
|
}
|
|
|
|
fluentcrm_update_campaign_meta($campaign->id, '_cached_email_body', $emailBody);
|
|
return true;
|
|
}
|
|
|
|
public static function maybeRemoveOldScheuledActionLogs()
|
|
{
|
|
$group_slug = 'fluent-crm';
|
|
$days_old = 7;
|
|
|
|
global $wpdb;
|
|
|
|
// Get the timestamp for 7 days ago
|
|
$cutoff_date = gmdate('Y-m-d H:i:s', strtotime("-{$days_old} days"));
|
|
|
|
// Get the group ID
|
|
$group_id = $wpdb->get_var($wpdb->prepare(
|
|
"SELECT group_id FROM {$wpdb->prefix}actionscheduler_groups WHERE slug = %s",
|
|
$group_slug
|
|
));
|
|
|
|
if (!$group_id) {
|
|
return false; // Group not found
|
|
}
|
|
|
|
// Delete old actions and their associated logs
|
|
$deleted = $wpdb->query($wpdb->prepare("
|
|
DELETE a, l
|
|
FROM {$wpdb->prefix}actionscheduler_actions a
|
|
LEFT JOIN {$wpdb->prefix}actionscheduler_logs l ON a.action_id = l.action_id
|
|
WHERE a.group_id = %d
|
|
AND a.status IN ('complete', 'failed')
|
|
AND a.scheduled_date_gmt < %s", $group_id, $cutoff_date));
|
|
|
|
// Clean up orphaned claims
|
|
$wpdb->query("
|
|
DELETE c
|
|
FROM {$wpdb->prefix}actionscheduler_claims c
|
|
LEFT JOIN {$wpdb->prefix}actionscheduler_actions a ON c.claim_id = a.claim_id
|
|
WHERE a.action_id IS NULL");
|
|
|
|
return $deleted;
|
|
}
|
|
|
|
public function SyncSubscriberDeleteSettings($fromKey, $value)
|
|
{
|
|
if ($fromKey == 'compliance_settings') {
|
|
$option = Meta::where('key', 'user_syncing_settings')
|
|
->where('object_type', 'option')
|
|
->first();
|
|
|
|
if ($option) {
|
|
$settings = $option->value;
|
|
|
|
if ($settings['delete_contact_on_user_delete'] != $value) {
|
|
$settings['delete_contact_on_user_delete'] = $value;
|
|
$option->value = $settings;
|
|
$option->save();
|
|
}
|
|
}
|
|
} else {
|
|
$complianceSettings = get_option('_fluentcrm_compliance_settings');
|
|
if ($complianceSettings) {
|
|
$complianceSettings['delete_contact_on_user'] = $value;
|
|
update_option('_fluentcrm_compliance_settings', $complianceSettings, 'no');
|
|
}
|
|
}
|
|
}
|
|
}
|