{$key}; } } if (!function_exists('dd')) { /** * Internal function for debugging */ function dd($data) { foreach (func_get_args() as $arg) { echo "
";
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
print_r($arg);
echo "";
}
die;
}
}
if (!function_exists('ddd')) {
/**
* Internal function for debugging
*/
function ddd($data)
{
foreach (func_get_args() as $arg) {
echo "";
print_r($arg);
echo "";
}
}
}
if (!function_exists('fluentCrmMix')) {
/**
* Generate URL for static assets for plugin's assets directory
* Now uses Vite helper for HMR support in dev mode
*
* @param string $path
* @param string $manifestDirectory (deprecated, kept for compatibility)
*
* @return string
*/
function fluentCrmMix($path, $manifestDirectory = '')
{
return \FluentCrm\App\Vite::getEnqueuePath(ltrim($path, '/'));
}
}
/**
* Get Current Date time
*
* @return string Datetime in format Y-m-d H:i:s
*/
function fluentCrmTimestamp($timestamp = null)
{
return current_time('mysql');
}
/**
* Get Actual Timezone string for WP
*
* @source https://www.skyverge.com/blog/down-the-rabbit-hole-wordpress-and-timezones/
* @return string
*/
function fluentCrmGetTimezoneString()
{
// if site timezone string exists, return it
$timezone = get_option('timezone_string');
if ($timezone) {
return $timezone;
}
// get UTC offset, if it isn't set then return UTC
$utcOffset = get_option('gmt_offset', 0);
if ($utcOffset === 0) {
return 'UTC';
}
// Adjust UTC offset from hours to seconds
$utcOffset *= 3600;
// Attempt to guess the timezone string from the UTC offset
$timezone = timezone_name_from_abbr('', $utcOffset, 0);
if ($timezone) {
return $timezone;
}
// Guess timezone string manually
$isDst = gmdate('I');
foreach (timezone_abbreviations_list() as $abbr) {
foreach ($abbr as $city) {
if ($city['dst'] == $isDst && $city['offset'] == $utcOffset) {
$timezoneId = $city['timezone_id'];
$timezone = $timezoneId ?: timezone_name_from_abbr('', $timezoneId, 0);
if ($timezone) return $timezone;
}
}
}
// Fallback
return 'UTC';
}
/*
* Internal Function For debugging only
*/
function fluentCrmMaybeRegisterQueryLoggerIfAvailable($app)
{
if (fluentCrmQueryLoggingEnabled()) {
$app->addAction('init', ['WpQueryLogger', 'init']);
}
}
/*
* Internal Function For debugging only
*/
function fluentCrmQueryLoggingEnabled()
{
if (file_exists(__DIR__ . '/../Hooks/Handlers/WpQueryLogger.php')) {
return defined('SAVEQUERIES') && SAVEQUERIES;
}
}
/*
* Internal Function For debugging only
*/
function fluentCrmEnableQueryLog()
{
if (file_exists(__DIR__ . '/../Hooks/Handlers/WpQueryLogger.php')) {
FluentCrm\App\Hooks\Handlers\WpQueryLogger::enableQueryLog();
}
}
/*
* Internal Function For debugging only
*/
function fluentCrmGetQueryLog($withStack = true)
{
if (file_exists(__DIR__ . '/../Hooks/Handlers/WpQueryLogger.php')) {
return json_encode([
FluentCrm\App\Hooks\Handlers\WpQueryLogger::getQueryLog($withStack)
]);
}
}
/**
* Get meta entry of a FluentCRM entity
* @param int $objectId ID of referenced object
* @param string $objectType Type of referenced object
* @param string $key Key of the reference
* @return object FluentCrm\App\Models\Meta Object of the Meta Table Model
*/
function fluentcrm_get_meta($objectId, $objectType, $key)
{
return Meta::where('object_id', $objectId)
->where('object_type', $objectType)
->where('key', $key)
->first();
}
/**
* Update or create a meta entry of the given entity
* @param int $objectId ID of referenced object
* @param string $objectType Type of referenced object
* @param string $key Key of the reference
* @param mixed $value
* @return \FluentCrm\App\Models\Meta|object
*/
function fluentcrm_update_meta($objectId, $objectType, $key, $value)
{
$model = fluentcrm_get_meta($objectId, $objectType, $key);
if ($model) {
$model->value = $value;
$model->save();
return $model;
}
return Meta::create([
'key' => $key,
'value' => $value,
'object_id' => $objectId,
'object_type' => $objectType
]);
}
/**
* Delete a Object Meta of FluentCRM
* @param int $objectId ID of referenced entity
* @param string $objectType Type of referenced entity
* @param string $key Key of the reference
* @return boolean
*/
function fluentcrm_delete_meta($objectId, $objectType, $key = '')
{
if ($key === '') {
return Meta::where('object_id', $objectId)
->where('object_type', $objectType)
->delete();
}
return Meta::where('object_id', $objectId)
->where('object_type', $objectType)
->where('key', $key)
->delete();
}
/**
* Get FluentCRM Option
* @param $optionName string
* @param mixed $default
* @return mixed|string
*/
function fluentcrm_get_option($optionName, $default = '')
{
$option = Meta::where('key', $optionName)
->where('object_type', 'option')
->first();
if (!$option) {
return $default;
}
return ($option->value) ? $option->value : $default;
}
/**
* Update FluentCRM Option
* @param $optionName
* @param $value
* @return int Created or updated meta entry id
*/
function fluentcrm_update_option($optionName, $value)
{
$option = Meta::where('key', $optionName)
->where('object_type', 'option')
->first();
if ($option) {
$option->value = $value;
$option->save();
if ($optionName == 'user_syncing_settings') {
do_action('fluent_crm/sync_subscriber_delete_setting', 'general_settings', $value['delete_contact_on_user_delete']);
}
return $option->id;
}
$model = Meta::create([
'key' => $optionName,
'value' => $value,
'object_type' => 'option'
]);
return $model->id;
}
/**
* Delete FluentCRM Option
* @param $optionName
* @return boolean
*/
function fluentcrm_delete_option($optionName)
{
return Meta::where('key', $optionName)
->where('object_type', 'option')
->delete();
}
/**
* Get Email Campaign meta value
* @param int $campaignId ID of the campaign
* @param string $key Key of the meta
* @param false $returnValue If true, return the value, otherwise return the meta object
* @return false|object
*/
function fluentcrm_get_campaign_meta($campaignId, $key, $returnValue = false)
{
$item = fluentcrm_get_meta($campaignId, 'FluentCrm\App\Models\Campaign', $key);
if ($returnValue) {
if ($item) {
return $item->value;
}
return false;
}
return $item;
}
/**
* update Email Campaign meta value
* @param int $campaignId ID of the campaign
* @param string $key Key of the meta
* @param mixed $value Value of the meta
* @return \FluentCrm\App\Models\Meta|object
*/
function fluentcrm_update_campaign_meta($campaignId, $key, $value)
{
return fluentcrm_update_meta($campaignId, 'FluentCrm\App\Models\Campaign', $key, $value);
}
/**
* Delete email campaign meta
* @param int $campaignId
* @param string $key Key of the meta
* @return bool
*/
function fluentcrm_delete_campaign_meta($campaignId, $key = '')
{
return fluentcrm_delete_meta($campaignId, 'FluentCrm\App\Models\Campaign', $key);
}
function fluentcrm_get_sms_campaign_meta($campaignId, $key, $returnValue = false)
{
$item = fluentcrm_get_meta($campaignId, 'FluentCampaign\App\Modules\SMS\Models\SMSCampaign', $key);
if ($returnValue) {
if ($item) {
return $item->value;
}
return false;
}
return $item;
}
function fluentcrm_update_sms_campaign_meta($campaignId, $key, $value)
{
return fluentcrm_update_meta($campaignId, 'FluentCampaign\App\Modules\SMS\Models\SMSCampaign', $key, $value);
}
function fluentcrm_delete_sms_campaign_meta($campaignId, $key = '')
{
return fluentcrm_delete_meta($campaignId, 'FluentCampaign\App\Modules\SMS\Models\SMSCampaign', $key);
}
/**
* Get Email Campaign meta value
* @param int $templateId ID of the template
* @param string $key Key of the meta
* @return object
*/
function fluentcrm_get_template_meta($templateId, $key)
{
return fluentcrm_get_meta($templateId, 'FluentCrm\App\Models\Template', $key);
}
/**
* update Email Template meta value
* @param int $templateId id of the template
* @param string $key Key of the meta
* @param mixed $value Value of the meta
* @return \FluentCrm\App\Models\Meta|object
*/
function fluentcrm_update_template_meta($templateId, $key, $value)
{
return fluentcrm_update_meta($templateId, 'FluentCrm\App\Models\Template', $key, $value);
}
/**
* Delete email template meta
* @param int $templateId
* @param string $key key of the meta
* @return bool
*/
function fluentcrm_delete_template_meta($templateId, $key)
{
return fluentcrm_delete_meta($templateId, 'FluentCrm\App\Models\Template', $key);
}
/**
* get meta value of Contact lists
* @param int $listId
* @param string $key key of the meta
* @return object
*/
function fluentcrm_get_list_meta($listId, $key)
{
return fluentcrm_get_meta($listId, 'FluentCrm\App\Models\Lists', $key);
}
/**
* Update meta value of Contact lists
* @param int $listId
* @param string $key
* @param mixed $value
* @return \FluentCrm\App\Models\Meta|object
*/
function fluentcrm_update_list_meta($listId, $key, $value)
{
return fluentcrm_update_meta($listId, 'FluentCrm\App\Models\Lists', $key, $value);
}
/**
* Delete meta value of Contact lists
* @param int $listId
* @param string $key
* @return bool
*/
function fluentcrm_delete_list_meta($listId, $key)
{
return fluentcrm_delete_meta($listId, 'FluentCrm\App\Models\Lists', $key);
}
/**
* get meta value of a Contact
* @param int $subscriberId contact ID
* @param string $key key of the meta
* @param string $deafult default value if meta not found
* @return mixed|string
*/
function fluentcrm_get_subscriber_meta($subscriberId, $key, $deafult = '')
{
$item = SubscriberMeta::where('key', $key)
->where('subscriber_id', $subscriberId)
->first();
if ($item && $item->value) {
return maybe_unserialize($item->value);
}
return $deafult;
}
/**
* update meta value of a Contact
* @param int $subscriberId
* @param string $key
* @param mixed $value
* @return \FluentCrm\App\Models\SubscriberMeta
*/
function fluentcrm_update_subscriber_meta($subscriberId, $key, $value)
{
$value = maybe_serialize($value);
// check if exists
$model = SubscriberMeta::where('key', $key)
->where('subscriber_id', $subscriberId)
->first();
if ($model) {
$model->updated_at = fluentCrmTimestamp();
$model->value = $value;
return $model->save();
}
return SubscriberMeta::create([
'key' => $key,
'created_by' => get_current_user_id(),
'value' => $value,
'subscriber_id' => $subscriberId,
'created_at' => fluentCrmTimestamp()
]);
}
/**
* Delete a meta value of a contact
* @param int $subscriberId
* @param string $key
* @return boolean
*/
function fluentcrm_delete_subscriber_meta($subscriberId, $key)
{
return SubscriberMeta::where('key', $key)
->where('subscriber_id', $subscriberId)
->delete();
}
/**
* Get all subscriber status options.
*
* @return array
*/
function fluentcrm_subscriber_statuses($isOptions = false)
{
/**
* Contact statuses of FluentCRM.
*
* This filter allows modification of the contact statuses used in FluentCRM.
*
* @param array $statuses An array of default contact statuses.
* @return array The filtered array of contact statuses.
*/
$statuses = apply_filters('fluent_crm/contact_statuses', [
'subscribed',
'pending',
'unsubscribed',
'transactional',
'bounced',
'complained',
'spammed'
]);
if (!$isOptions) {
return $statuses;
}
$formattedStatues = [];
$transMaps = [
'subscribed' => __('Subscribed', 'fluent-crm'),
'pending' => __('Pending', 'fluent-crm'),
'unsubscribed' => __('Unsubscribed', 'fluent-crm'),
'transactional' => __('Transactional', 'fluent-crm'),
'bounced' => __('Bounced', 'fluent-crm'),
'complained' => __('Complained', 'fluent-crm'),
'spammed' => __('Spammed', 'fluent-crm'),
];
foreach ($statuses as $status) {
$formattedStatues[] = [
'id' => $status,
'slug' => $status,
'title' => isset($transMaps[$status]) ? $transMaps[$status] : ucfirst($status)
];
}
return $formattedStatues;
}
function fluentcrm_subscriber_sms_statuses($isOptions = false)
{
$core_statuses = [
'sms_subscribed',
'sms_pending',
'sms_unsubscribed',
'sms_bounced'
];
$statuses = apply_filters('fluent_crm/contact_sms_statuses', $core_statuses);
if (!$isOptions) {
return $statuses;
}
$formattedStatues = [];
$transMaps = [
'sms_subscribed' => __('SMS Subscribed', 'fluent-crm'),
'sms_pending' => __('SMS Pending', 'fluent-crm'),
'sms_unsubscribed' => __('SMS Unsubscribed', 'fluent-crm'),
'sms_bounced' => __('SMS Bounced', 'fluent-crm')
];
foreach ($statuses as $status) {
$title = isset($transMaps[$status]) ? $transMaps[$status] : ucwords(str_replace('_', ' ', $status));
$formattedStatues[] = [
'id' => $status,
'slug' => $status,
'title' => $title
];
}
return $formattedStatues;
}
/**
* Get all subscriber editable status options.
*
* @return array
*/
function fluentcrm_subscriber_editable_statuses($isOptions = false)
{
$statuses = fluentcrm_subscriber_statuses();
$unEditableStatuses = ['bounced', 'complained', 'spammed'];
$statuses = array_diff($statuses, $unEditableStatuses);
/**
* Define the editable contact statuses for FluentCRM contacts.
*
* This filter allows modification of the contact statuses that can be edited.
*
* @param array $statuses The current list of contact statuses.
* @return array The modified list of editable contact statuses.
*/
$editableStatuses = apply_filters('fluent_crm/contact_editable_statuses', $statuses);
if (!$isOptions) {
return $editableStatuses;
}
$formattedStatues = [];
$transMaps = [
'subscribed' => __('Subscribed', 'fluent-crm'),
'pending' => __('Pending', 'fluent-crm'),
'unsubscribed' => __('Unsubscribed', 'fluent-crm'),
'transactional' => __('Transactional', 'fluent-crm'),
'bounced' => __('Bounced', 'fluent-crm'),
'complained' => __('Complained', 'fluent-crm'),
'spammed' => __('Spammed', 'fluent-crm')
];
foreach ($editableStatuses as $status) {
$formattedStatues[] = [
'id' => $status,
'slug' => $status,
'title' => isset($transMaps[$status]) ? $transMaps[$status] : ucfirst($status)
];
}
return $formattedStatues;
}
/**
* Get Contact Types
* @return array
*/
function fluentcrm_contact_types($isOptions = false)
{
/**
* Define FluentCRM contact types.
*
* This filter allows modification of the contact types used in FluentCRM.
*
* @param array $types An associative array of contact types.
* @return array Filtered array of contact types.
*/
$types = apply_filters('fluent_crm/contact_types', [
'lead' => __('Lead', 'fluent-crm'),
'customer' => __('Customer', 'fluent-crm')
]);
if (!$isOptions) {
return $types;
}
$formattedTypes = [];
foreach ($types as $type => $label) {
$formattedTypes[] = [
'id' => $type,
'slug' => $type,
'title' => $label
];
}
return $formattedTypes;
}
/**
* Get Contact's Activity Types
*
* @return array
*/
function fluentcrm_activity_types()
{
/**
* Define FluentCRM contact activity types.
*
* This filter allows modification of the contact activity types used in FluentCRM.
*
* @param array $types {
* An associative array of contact activity types.
*
* @type string $note Note activity type.
* @type string $call Call activity type.
* @type string $email Email activity type.
* @type string $meeting Meeting activity type.
* @type string $quote_sent Quote sent activity type.
* @type string $quote_accepted Quote accepted activity type.
* @type string $quote_refused Quote refused activity type.
* @type string $invoice_sent Invoice sent activity type.
* @type string $invoice_part_paid Invoice part paid activity type.
* @type string $invoice_paid Invoice paid activity type.
* @type string $invoice_refunded Invoice refunded activity type.
* @type string $transaction Transaction activity type.
* @type string $feedback Feedback activity type.
* @type string $tweet Tweet activity type.
* @type string $facebook_post Facebook post activity type.
* }
*/
$types = apply_filters('fluent_crm/contact_activity_types', [
'note' => __('Note', 'fluent-crm'),
'call' => __('Call', 'fluent-crm'),
'email' => __('Email', 'fluent-crm'),
'meeting' => __('Meeting', 'fluent-crm'),
'quote_sent' => __('Quote: Sent', 'fluent-crm'),
'quote_accepted' => __('Quote: Accepted', 'fluent-crm'),
'quote_refused' => __('Quote: Refused', 'fluent-crm'),
'invoice_sent' => __('Invoice: Sent', 'fluent-crm'),
'invoice_part_paid' => __('Invoice: Part Paid', 'fluent-crm'),
'invoice_paid' => __('Invoice: Paid', 'fluent-crm'),
'invoice_refunded' => __('Invoice: Refunded', 'fluent-crm'),
'transaction' => __('Transaction', 'fluent-crm'),
'feedback' => __('Feedback', 'fluent-crm'),
'tweet' => __('Tweet', 'fluent-crm'),
'facebook_post' => __('Facebook Post', 'fluent-crm')
]);
$formattedTypes = [];
foreach ($types as $key => $label) {
$formattedTypes[] = [
'id' => $key,
'label' => $label
];
}
return $formattedTypes;
}
/**
* Get Contact's Strict status Types
*
* @return array
*/
function fluentcrm_strict_statues()
{
/**
* Determine FluentCRM Contact(subscriber) strict statuses.
*
* This returns an array of subscriber statuses that are considered strict,
* such as 'unsubscribed', 'bounced', and 'complained'. The list can be modified
* using the 'subscriber_strict_statuses' filter.
*
* @return array The filtered list of subscriber strict statuses.
*/
return apply_filters('subscriber_strict_statuses', [
'unsubscribed',
'bounced',
'complained',
'spammed'
]);
}
/**
* Email Template CPT Slug
* @return string
*/
function fluentcrmTemplateCPTSlug()
{
return 'fc_template';
}
/**
* Email Template CPT Slug
* @return string
*/
function fluentcrmCampaignTemplateCPTSlug()
{
return FLUENTCRM . 'campaigntemplate';
}
/**
* Get the possible csv mimes.
*
* @return array
*/
function fluentcrmCsvMimes()
{
/**
* Filters the list of MIME types allowed for CSV files.
*
* This applies the 'fluencrm_csv_mimes' filter to an array of MIME types
* that are considered valid for CSV files. The default MIME types included are:
* - 'text/csv'
* - 'text/plain'
* - 'application/csv'
* - 'text/comma-separated-values'
* - 'application/excel'
* - 'application/vnd.ms-excel'
* - 'application/vnd.msexcel'
* - 'text/anytext'
* - 'application/octet-stream'
* - 'application/txt'
*
* @return array Filtered list of MIME types allowed for CSV files.
*/
return apply_filters('fluentcrm_csv_mimes', [
'text/csv',
'text/plain',
'application/csv',
'text/comma-separated-values',
'application/excel',
'application/vnd.ms-excel',
'application/vnd.msexcel',
'text/anytext',
'application/octet-stream',
'application/txt'
]);
}
/**
* Get the gravatar from an email.
*
* @param string $email
* @return string
*/
function fluentcrmGravatar($email, $name = '')
{
$complianceSettings = \FluentCrm\App\Services\Helper::getComplianceSettings();
$gravatarEnabled = $complianceSettings['enable_gravatar'] == 'yes' ? true : false;
$fallbackEnabled = $complianceSettings['gravatar_fallback'] == 'yes' ? true : false;
if (!$gravatarEnabled) {
return apply_filters('fluent_crm/default_avatar', FLUENTCRM_PLUGIN_URL . 'assets/images/avatar.png', $email);
}
$hash = md5(strtolower(trim($email)));
$fallback = '';
if ($fallbackEnabled && $name) {
$fallback = '&d=https%3A%2F%2Fui-avatars.com%2Fapi%2F' . urlencode($name) . '/128';
}
/**
* Apply filters to get the avatar URL.
*
* This generates a Gravatar URL based on the provided email hash and applies the 'fluent_crm/get_avatar' filter.
*
* @param string $hash The MD5 hash of the user's email address.
* @param string $fallback The fallback URL to use if the Gravatar is not found.
* @param string $email The user's email address.
* @return string The filtered avatar URL.
*/
return apply_filters('fluent_crm/get_avatar',
"https://www.gravatar.com/avatar/{$hash}?s=128" . $fallback,
$email
);
}
/**
* Get avatar HTML for admin-facing UI and guarantee a safe placeholder.
*
* WordPress get_avatar() can return false when avatars are disabled, which
* breaks consumers that expect markup. In that case we fall back to the
* plugin's avatar URL helper and return a simple image tag.
*
* @param string $email
* @param string $name
* @param int $size
* @return string
*/
function fluentcrmGetAvatarHtml($email, $name = '', $size = 128)
{
$avatarHtml = get_avatar($email, $size);
if ($avatarHtml) {
return $avatarHtml;
}
$avatarUrl = fluentcrmGravatar($email, $name);
return sprintf(
'