get('settings_keys', []); $returnSettings = []; foreach ($keys as $key) { if ($key == 'email_settings') { $returnSettings[$key] = Helper::getGlobalEmailSettings(); } else if (isset($existingSettings[$key])) { if ($key == 'business_settings') { $existingSettings[$key] = fluentcrmGetGlobalSettings('business_settings'); } $returnSettings[$key] = $existingSettings[$key]; } } return $returnSettings; } public function save(Request $request) { $settings = (array)$request->get('settings', []); $existingSettings = get_option('fluentcrm-global-settings'); if (!$existingSettings) { $existingSettings = []; } foreach ($settings as $settingsKey => $setting) { $existingSettings[$settingsKey] = $setting; if ($settingsKey == 'email_settings') { $emailFooter = Arr::get($setting, 'email_footer'); if (!Helper::hasComplianceText($emailFooter)) { return $this->sendError([ 'message' => __('##crm.manage_subscription_url## or ##crm.unsubscribe_url## string is required for compliance. Please include unsubscription or manage subscription link', 'fluent-crm') ]); } } } update_option( 'fluentcrm-global-settings', $existingSettings ); return $this->sendSuccess([ 'message' => __('Settings Updated', 'fluent-crm') ]); } public function getDoubleOptinSettings(Request $request) { //check if list id comes $listId = $request->get('list_id', null); $doubleOptinSettings = null; //if list id sent then it is double optin setup of a list if ($listId) { $meta = fluentcrm_get_list_meta($listId, 'double_optin_settings'); $doubleOptinSettings = $meta ? $meta->value : null; } //if no double optin setup of list found or this is global if (!$doubleOptinSettings) { $doubleOptinSettings = Helper::getDoubleOptinSettings(); if (empty($doubleOptinSettings['tag_based_redirect'])) { $doubleOptinSettings['tag_based_redirect'] = 'no'; $doubleOptinSettings['tag_redirects'] = [ [ 'field_key' => [], 'field_value' => '' ] ]; } } $data = [ 'settings' => $doubleOptinSettings ]; if ($listId) { $globalDoubleOptin = fluentcrm_get_list_meta($listId, 'global_double_optin'); $data['global_double_optin'] = $globalDoubleOptin ? $globalDoubleOptin->value : 'yes'; } if (in_array('settings_fields', $request->get('with', []))) { $designTemplates = Helper::getEmailDesignTemplates(); $designTemplates = Arr::only($designTemplates, ['simple', 'plain', 'classic', 'raw_classic']); $data['settings_fields'] = [ 'design_template' => [ 'type' => 'image-radio', 'label' => __('Design Template', 'fluent-crm'), 'help' => __('Email Design Template for this double-optin email', 'fluent-crm'), 'options' => $designTemplates ], 'email_subject' => [ 'type' => 'input-text-popper', 'placeholder' => __('Optin Email Subject', 'fluent-crm'), 'label' => __('Email Subject', 'fluent-crm'), 'help' => __('Your double-optin email subject', 'fluent-crm') ], 'email_pre_header' => [ 'type' => 'input-text-popper', 'placeholder' => __('Optin Email Pre Header', 'fluent-crm'), 'label' => __('Email Pre Header', 'fluent-crm'), 'help' => __('Your double-optin email pre header', 'fluent-crm') ], 'email_body' => [ 'type' => 'wp-editor', 'placeholder' => __('Double-Optin Email Body', 'fluent-crm'), 'label' => __('Email Body', 'fluent-crm'), 'help' => __('Provide Email Body for the double-optin', 'fluent-crm'), 'inline_help' => __('Use #activate_link# for plain url or {{crm.activate_button|Confirm Subscription}} for default button', 'fluent-crm') ], 'confirmation_html_viewer' => [ 'type' => 'html-viewer', 'heading' => __('After Confirmation Actions', 'fluent-crm'), 'info' => __('Please provide details after a contact confirms double opt-in from email', 'fluent-crm') ], 'after_confirmation_type' => [ 'type' => 'input-radio', 'label' => __('After Confirmation Type', 'fluent-crm'), 'help' => __('Please select what will happen once a contact confirms double opt-in', 'fluent-crm'), 'options' => [ [ 'id' => 'message', 'label' => __('Show Message', 'fluent-crm') ], [ 'id' => 'redirect', 'label' => __('Redirect to an URL', 'fluent-crm') ] ] ], 'after_confirm_message' => [ 'type' => 'wp-editor', 'placeholder' => __('After Confirmation Message', 'fluent-crm'), 'label' => __('After Confirmation Message', 'fluent-crm'), 'help' => __('This message will be shown after a subscriber confirm subscription', 'fluent-crm'), 'dependency' => [ 'depends_on' => 'after_confirmation_type', 'operator' => '=', 'value' => 'message' ] ], 'after_conf_redirect_url' => [ 'type' => 'input-text-popper', 'placeholder' => __('Redirect URL', 'fluent-crm'), 'label' => __('Redirect URL', 'fluent-crm'), 'help' => __('Please provide redirect URL after confirmation', 'fluent-crm'), 'dependency' => [ 'depends_on' => 'after_confirmation_type', 'operator' => '=', 'value' => 'redirect' ] ], 'tag_based_redirect' => [ 'type' => 'inline-checkbox', 'checkbox_label' => (defined('FLUENTCAMPAIGN')) ? __('Enable Tag based double optin redirect', 'fluent-crm') : __('Enable Tag based double optin redirect (Require FluentCRM Pro)', 'fluent-crm'), 'true_label' => 'yes', 'false_label' => 'no', 'disabled' => !defined('FLUENTCAMPAIGN') ], 'tag_redirects' => [ 'label' => __('Configure your redirect URLs based on tags (Will redirect to the provided URL if any selected tag matches the contact)', 'fluent-crm'), 'type' => 'form-many-drop-down-mapper', 'local_label' => __('Targeted Tags', 'fluent-crm'), 'local_placeholder' => __('Select Tags', 'fluent-crm'), 'remote_label' => __('Redirect URL (After Double Optin Confirmation)', 'fluent-crm'), 'field_option_selector' => [ 'option_key' => 'tags', 'is_multiple' => true ], 'remote_field_type' => 'input-text-popper', 'remote_field' => [ 'placeholder' => __('Redirect URL', 'fluent-crm') ], 'help' => __('User will be redirected to the URL which matches based on the tags at first match', 'fluent-crm'), 'dependency' => [ 'depends_on' => 'tag_based_redirect', 'operator' => '=', 'value' => 'yes' ], 'manage_serial' => true ] ]; } return $this->sendSuccess($data); } public function saveDoubleOptinSettings(Request $request) { $settings = wp_unslash($request->get('settings')); $listId = $request->get('list_id'); $globalDoubleOptin = sanitize_text_field($request->get('global_double_optin')); $this->validate($settings, [ 'email_subject' => 'required', 'email_body' => 'required', 'after_confirm_message' => 'required' ], [ 'email_subject.required' => __('Email Subject is required', 'fluent-crm'), 'email_body.required' => __('Email Body is required', 'fluent-crm'), 'after_confirm_message.required' => __('After Confirmation Message is required', 'fluent-crm') ]); // let's check if message contains #activate_link# or {{crm.activate_button $emailBody = $settings['email_body']; if ( strpos($emailBody, '#activate_link#') === false && strpos($emailBody, '{{crm.activate_button') === false ) { return $this->sendError([ 'message' => __('Email Body need to contains activation link', 'fluent-crm') ]); } if ($listId) { fluentcrm_update_list_meta($listId, 'double_optin_settings', $settings); if ($globalDoubleOptin) { fluentcrm_update_list_meta($listId, 'global_double_optin', $globalDoubleOptin); } } else { fluentcrm_update_option('double_optin_settings', $settings); } return $this->sendSuccess([ 'message' => __('Double Opt-in settings has been updated', 'fluent-crm') ] ); } public function TestRequestResolver(Request $request) { return [ 'message' => __('Valid', 'fluent-crm'), 'params' => $request->all() ]; } public function resetDB(Request $request) { if (!current_user_can('manage_options')) { return $this->sendError([ 'message' => __('Sorry, You do not have admin permission to reset database', 'fluent-crm') ]); } if (!defined('FLUENTCRM_IS_DEV_FEATURES') || !FLUENTCRM_IS_DEV_FEATURES) { return $this->sendError([ 'message' => __('Development mode is not activated. So you cannot use this feature. You can define "FLUENTCRM_IS_DEV_FEATURES" in your wp-config to enable this feature', 'fluent-crm') ]); } $tables = [ 'fc_campaign_emails', 'fc_campaigns', 'fc_campaign_url_metrics', 'fc_funnel_metrics', 'fc_funnels', 'fc_funnel_sequences', 'fc_funnel_subscribers', 'fc_lists', 'fc_meta', 'fc_subscriber_meta', 'fc_subscriber_notes', 'fc_subscriber_pivot', 'fc_subscribers', 'fc_tags', 'fc_url_stores' ]; if (defined('FLUENTCAMPAIGN_PLUGIN_URL')) { $tables[] = 'fc_sequence_tracker'; } global $wpdb; foreach ($tables as $table) { // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared $wpdb->query("DROP TABLE IF EXISTS " . $wpdb->prefix . $table); } // All tables are delete now let's run the migration (new ActivationHandler)->handle(false); if (defined('FLUENTCAMPAIGN_PLUGIN_URL')) { \FluentCampaign\App\Migration\Migrate::run(false); } $options = [ '_fluentcrm_commerce_modules' ]; foreach ($options as $option) { delete_option($option); } return [ 'message' => __('All FluentCRM Database Tables have been reset', 'fluent-crm'), 'tables' => $tables ]; } public function getBounceConfigs() { $securityCode = fluentcrm_get_option('_fc_bounce_key'); if (!$securityCode) { $securityCode = 'fcrm_' . substr(md5(wp_generate_uuid4()), 0, 14); // first 14 digit fluentcrm_update_option('_fc_bounce_key', $securityCode); } $bounceSettings = [ 'ses' => [ 'label' => __('Amazon SES', 'fluent-crm'), 'webhook_url' => site_url('index.php?fluentcrm=1&route=bounce_handler&provider=ses&verify_key=' . $securityCode), 'doc_url' => 'https://fluentcrm.com/docs/bounce-handler-with-amazon-ses/', 'input_title' => __('Amazon SES Bounce Handler URL', 'fluent-crm'), 'input_info' => __('Please use this bounce handler url in your Amazon SES + SNS settings', 'fluent-crm') ], 'tosend' => [ 'label' => __('ToSend', 'fluent-crm'), 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/tosend/handle/' . $securityCode), 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-tosend/', 'input_title' => __('ToSend Bounce Handler Webhook URL', 'fluent-crm'), 'input_info' => __('Please paste this URL into your ToSend\'s Webhook settings to enable Bounce Handling with FluentCRM. Select both Bounce and Complaint events.', 'fluent-crm') ], 'mailgun' => [ 'label' => __('Mailgun', 'fluent-crm'), 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/mailgun/handle/' . $securityCode), 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-mailgun/', 'input_title' => __('Mailgun Bounce Handler Webhook URL', 'fluent-crm'), 'input_info' => __('Please paste this URL into your Mailgun\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') ], 'pepipost' => [ 'label' => __('PepiPost', 'fluent-crm'), 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/pepipost/handle/' . $securityCode), 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-pepipost/', 'input_title' => __('PepiPost Bounce Handler Webhook URL', 'fluent-crm'), 'input_info' => __('Please paste this URL into your PepiPost\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') ], 'postmark' => [ 'label' => __('PostMark', 'fluent-crm'), 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/postmark/handle/' . $securityCode), 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-postmark/', 'input_title' => __('PostMark Bounce Handler Webhook URL', 'fluent-crm'), 'input_info' => __('Please paste this URL into your PostMark\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') ], 'sendgrid' => [ 'label' => __('SendGrid', 'fluent-crm'), 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/sendgrid/handle/' . $securityCode), 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-sendgrid/', 'input_title' => __('SendGrid Bounce Handler Webhook URL', 'fluent-crm'), 'input_info' => __('Please paste this URL into your SendGrid\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') ], 'sparkpost' => [ 'label' => __('SparkPost', 'fluent-crm'), 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/sparkpost/handle/' . $securityCode), 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-sparkpost/', 'input_title' => __('SparkPost Bounce Handler Webhook URL', 'fluent-crm'), 'input_info' => __('Please paste this URL into your SparkPost\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') ], 'elasticemail' => [ 'label' => __('Elastic Email', 'fluent-crm'), 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/elasticemail/handle/' . $securityCode), 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-elastic-email/', 'input_title' => __('Elastic Email Bounce Handler Webhook URL', 'fluent-crm'), 'input_info' => __('Please paste this URL into your Elastic Email\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') ], 'postalserver' => [ 'label' => __('Postal Server', 'fluent-crm'), 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/postalserver/handle/' . $securityCode), 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-postal-server/', 'input_title' => __('Postal Server Bounce Handler Webhook URL', 'fluent-crm'), 'input_info' => __('Please paste this URL into your Postal Server\'s Webhook settings to enable Bounce Handling with FluentCRM. Please select only MessageBounced & MessageDeliveryFailed event', 'fluent-crm') ], 'smtp2go' => [ 'label' => __('SMTP2Go', 'fluent-crm'), 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/smtp2go/handle/' . $securityCode), 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-smtp2go/', 'input_title' => __('SMTP2Go Bounce Handler Webhook URL', 'fluent-crm'), 'input_info' => __('Please paste this URL into your SMTP2Go\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') ], 'brevo' => [ 'label' => __('Brevo (ex Sendinblue)', 'fluent-crm'), 'webhook_url' => get_rest_url(null, 'fluent-crm/v2/public/bounce_handler/brevo/handle/' . $securityCode), 'doc_url' => 'https://fluentcrm.com/docs/bounce-handling-with-brevo/', 'input_title' => __('Brevo Bounce Handler Webhook URL', 'fluent-crm'), 'input_info' => __('Please paste this URL into your Brevo\'s Webhook settings to enable Bounce Handling with FluentCRM', 'fluent-crm') ], ]; $data = [ /** * Determine FluentCRM Bounce Handler settings. * * This filter allows modification of the bounce handler settings. * * @param array $bounceSettings The current bounce settings. * @param string $securityCode The security code for the bounce handler. * @since 2.5.95 * */ 'bounce_settings' => apply_filters('fluent_crm/bounce_handlers', $bounceSettings, $securityCode) ]; if (defined('FLUENTMAIL')) { $smtpSettings = get_option('fluentmail-settings', []); if (!$smtpSettings || !count($smtpSettings['connections'])) { $data['fluentsmtp_info'] = [ 'configured' => false ]; } else { $data['fluentsmtp_info'] = [ 'configured' => true, 'verified_senders' => array_keys($smtpSettings['mappings']) ]; } $data['fluentsmtp_info']['config_url'] = admin_url('options-general.php?page=fluent-mail#/connections'); } else { $data['fluentsmtp_info'] = false; } return $data; } public function getAutoSubscribeSettings(Request $request) { $autoSubscribeService = new AutoSubscribe(); $roleBasedTaggingClass = new RoleBasedTagging(); $data = [ 'registration_setting' => $autoSubscribeService->getRegistrationSettings(), 'comment_settings' => $autoSubscribeService->getCommentSettings(), 'user_syncing_settings' => $autoSubscribeService->getUserSyncSettings(), 'role_based_tagging_settings' => $roleBasedTaggingClass->getSettings(true), 'date_time_settings' => [ 'classic_date_time' => Arr::get(Helper::getExperimentalSettings(), 'classic_date_time', 'no') ] ]; $with = $request->get('with', []); if (in_array('fields', $with)) { $data['registration_fields'] = $autoSubscribeService->getRegistrationFields(); $data['comment_fields'] = $autoSubscribeService->getCommentFields(); $data['user_syncing_fields'] = $autoSubscribeService->getUserSyncFields(); $data['role_based_tagging_settings_fields'] = $roleBasedTaggingClass->getFields(); } if (defined('WC_PLUGIN_FILE')) { $data['woo_checkout_fields'] = $autoSubscribeService->getWooCheckoutFields(); $data['woo_checkout_settings'] = $autoSubscribeService->getWooCheckoutSettings(); } if (defined('FLUENTCART_VERSION')) { $data['fluent_cart_checkout_fields'] = $autoSubscribeService->getFluentCartCheckoutFields(); $data['fluent_cart_checkout_settings'] = $autoSubscribeService->getFluentCartCheckoutSettings(); } return $data; } public function saveAutoSubscribeSettings(Request $request) { $registrationSettings = $request->get('registration_setting', []); $commentSettings = $request->get('comment_settings', []); $userSyncSettings = $request->get('user_syncing_settings', []); $dateTimeSettings = $request->get('date_time_settings', []); fluentcrm_update_option('user_registration_subscribe_settings', $registrationSettings); fluentcrm_update_option('comment_form_subscribe_settings', $commentSettings); fluentcrm_update_option('user_syncing_settings', $userSyncSettings); if (is_array($dateTimeSettings) && array_key_exists('classic_date_time', $dateTimeSettings)) { $experimentalSettings = Helper::getExperimentalSettings(); $experimentalSettings['classic_date_time'] = sanitize_text_field($dateTimeSettings['classic_date_time']) === 'yes' ? 'yes' : 'no'; update_option('_fluentcrm_experimental_settings', $experimentalSettings, 'yes'); } if (defined('FLUENTCAMPAIGN_PLUGIN_VERSION')) { $roleBasedSettings = $request->get('role_based_tagging_settings', []); fluentcrm_update_option('role_based_tagging_settings', $roleBasedSettings); } if (defined('WC_PLUGIN_FILE') && defined('FLUENTCAMPAIGN_DIR_FILE')) { $wooCheckoutSettings = $request->get('woo_checkout_settings'); fluentcrm_update_option('woo_checkout_form_subscribe_settings', $wooCheckoutSettings); fluentCrmSetCache('woo_checkout_form_subscribe_settings', $wooCheckoutSettings, 86400); } if (defined('FLUENTCART_VERSION')) { $fluentCartCheckoutSettings = (new AutoSubscribe())->sanitizeFluentCartCheckoutSettings( $request->get('fluent_cart_checkout_settings', []) ); fluentcrm_update_option('fluent_cart_checkout_form_subscribe_settings', $fluentCartCheckoutSettings); } return [ 'message' => __('Settings has been updated', 'fluent-crm') ]; } public function getCronStatus() { $events = []; $nextRun = Helper::getNextMinuteTaskTimeStamp(); $events[] = (object)array( 'hook' => 'fluentcrm_scheduled_every_minute_tasks', 'is_overdue' => (time() - $nextRun) > 30, 'human_name' => __('Scheduled Email Sending Tasks', 'fluent-crm'), 'next_run' => human_time_diff($nextRun, time()), 'interval' => 60 ); $nextFiverMinutesRun = wp_next_scheduled('fluentcrm_scheduled_five_minute_tasks'); $events[] = (object)array( 'hook' => 'fluentcrm_scheduled_hourly_tasks', 'is_overdue' => ($nextFiverMinutesRun - time()) < -60, 'human_name' => __('Scheduled Email Processing', 'fluent-crm'), 'next_run' => human_time_diff($nextFiverMinutesRun, time()), 'interval' => 300 ); $nextHourlyRun = wp_next_scheduled('fluentcrm_scheduled_hourly_tasks'); $events[] = (object)array( 'hook' => 'fluentcrm_scheduled_hourly_tasks', 'is_overdue' => ($nextHourlyRun - time()) < -120, 'human_name' => __('Scheduled Automation Tasks', 'fluent-crm'), 'next_run' => human_time_diff($nextHourlyRun, time()), 'interval' => 3600 ); return [ 'cron_events' => $events, 'server' => [ 'memory_limit' => intval(fluentCrmGetMemoryLimit() / 1048576) . 'MB', 'usage_percent' => fluentCrmGetMemoryUsagePercentage(), 'max_execution_time' => fluentCrmMaxRunTime() . ' seconds', 'has_server_cron' => defined('DISABLE_WP_CRON') && DISABLE_WP_CRON ] ]; } public function runCron(Request $request) { $hookName = $request->get('hook'); $hookNames = [ 'fluentcrm_scheduled_every_minute_tasks' => __('Scheduled Email Sending', 'fluent-crm'), 'fluentcrm_scheduled_hourly_tasks' => __('Scheduled Automation Tasks', 'fluent-crm'), 'fluentcrm_scheduled_five_minute_tasks' => __('Scheduled Email Processing', 'fluent-crm') ]; if (!isset($hookNames[$hookName])) { return $this->sendError([ 'message' => __('The provided hook name is not valid', 'fluent-crm') ]); } do_action($hookName); return [ 'message' => __('Selected CRON Event successfully ran', 'fluent-crm') ]; } /** * Return the health of the critical DB indexes for the settings UI. * * Cheap by default: serves the cached snapshot unless ?fresh=1 is passed * (e.g. to re-verify right after a repair). * * @param Request $request * @return array */ public function getDbIndexHealth(Request $request) { $fromDb = $request->getSafe('fresh', 'sanitize_text_field') === '1'; return $this->sendSuccess([ 'indexes' => array_values(DbPerformanceService::getIndexHealth($fromDb)) ]); } /** * Repair any missing/broken critical DB indexes. * * Runs inline (the ALTERs are idempotent and prefer a non-blocking online * build) behind a short transient lock so concurrent admin tabs/users can't * trigger overlapping ALTERs on the same tables. If a repair is already in * flight we report that rather than stacking a second one. * * @param Request $request * @return array */ public function repairDbIndexes(Request $request) { // Re-check from the live DB so a stale "ok" cache can't make us skip a // genuinely needed repair (and a stale "broken" cache can't force a // pointless ALTER pass). if (!DbPerformanceService::hasBrokenIndex(true)) { return $this->sendSuccess([ 'message' => __('All database indexes are healthy.', 'fluent-crm'), 'indexes' => array_values(DbPerformanceService::getIndexHealth(false)) ]); } $lockKey = 'fc_db_index_repairing'; if (get_transient($lockKey)) { // Not an error — another tab/request is already repairing. Report it // as a benign "pending" success so the client doesn't surface a // false failure notice while the other request finishes the work. return $this->sendSuccess([ 'pending' => true, 'message' => __('A database index repair is already running.', 'fluent-crm'), 'indexes' => array_values(DbPerformanceService::getIndexHealth(false)) ]); } set_transient($lockKey, 1, 5 * MINUTE_IN_SECONDS); try { $result = DbPerformanceService::repairBrokenIndexes(); } finally { delete_transient($lockKey); } if ($result['failed']) { return $this->sendError([ 'message' => __('Some database indexes could not be repaired. Please check your database user privileges or contact your host.', 'fluent-crm'), 'failed' => $result['failed'], 'indexes' => array_values($result['health']) ], 422); } return $this->sendSuccess([ 'message' => __('Database indexes repaired successfully.', 'fluent-crm'), 'repaired' => $result['repaired'], 'indexes' => array_values($result['health']) ]); } public function getOldLogDetails(Request $request) { $data = $request->all(); $this->validate($data, [ 'days_before' => 'required|numeric|min:7', 'selected_logs' => 'array|required' ]); $selectedLogs = $data['selected_logs']; $daysBefore = $data['days_before']; $refDate = gmdate('Y-m-d 00:00:01', time() - $daysBefore * 86400); $dataCounters = []; if (in_array('emails', $selectedLogs)) { $dataCounters[] = [ 'title' => __('Email History Logs', 'fluent-crm'), 'count' => CampaignEmail::where('created_at', '<', $refDate) ->where('status', 'sent') ->count() ]; } if (in_array('email_clicks', $selectedLogs)) { $dataCounters[] = [ 'title' => __('Email clicks', 'fluent-crm'), 'count' => CampaignUrlMetric::where('type', 'click') ->where('created_at', '<', $refDate) ->count() ]; } if (in_array('email_open', $selectedLogs)) { $dataCounters[] = [ 'title' => __('Email Opens', 'fluent-crm'), 'count' => CampaignUrlMetric::where('type', 'open') ->where('created_at', '<', $refDate) ->count() ]; } if (in_array('system_logs', $selectedLogs)) { $dataCounters[] = [ 'title' => __('System Logs', 'fluent-crm'), 'count' => SystemLog::where('created_at', '<', $refDate) ->count() ]; } if (in_array('activity_logs', $selectedLogs)) { $dataCounters[] = [ 'title' => __('Activity Logs', 'fluent-crm'), 'count' => ActivityLog::where('created_at', '<', $refDate) ->count() ]; } return [ 'log_counts' => $dataCounters ]; } public function removeOldLogs(Request $request) { $data = $request->all(); $this->validate($data, [ 'days_before' => 'required|numeric|min:7', 'selected_logs' => 'array|required' ]); $selectedLogs = $data['selected_logs']; $daysBefore = $data['days_before']; $perChunk = 10000; // Deleting 10,000 per chunk $hasMore = false; $refDate = gmdate('Y-m-d 00:00:01', time() - $daysBefore * 86400); if (in_array('emails', $selectedLogs)) { $campaignIds = CampaignEmail::where('created_at', '<', $refDate) ->where('status', 'sent') ->groupBy('campaign_id') ->pluck('campaign_id'); foreach ($campaignIds->toArray() as $campaignId) { fluentcrm_update_campaign_meta($campaignId, '_data_trunked', 'yes'); } CampaignEmail::where('created_at', '<', $refDate) ->where('status', 'sent') ->limit($perChunk) ->delete(); $hasMore = CampaignEmail::where('created_at', '<', $refDate) ->where('status', 'sent') ->exists(); } $urlMetricsTypes = []; if (in_array('email_clicks', $selectedLogs)) { $urlMetricsTypes[] = 'click'; } if (in_array('email_open', $selectedLogs)) { $urlMetricsTypes[] = 'open'; } if ($urlMetricsTypes) { CampaignUrlMetric::whereIn('type', $urlMetricsTypes) ->where('created_at', '<', $refDate) ->limit($perChunk) ->delete(); if (!$hasMore) { $hasMore = CampaignUrlMetric::whereIn('type', $urlMetricsTypes) ->where('created_at', '<', $refDate) ->exists(); } } if (in_array('system_logs', $selectedLogs)) { SystemLog::where('created_at', '<', $refDate) ->limit($perChunk) ->delete(); if (!$hasMore) { $hasMore = SystemLog::where('created_at', '<', $refDate) ->exists(); } } if (in_array('activity_logs', $selectedLogs)) { ActivityLog::where('created_at', '<', $refDate) ->limit($perChunk) ->delete(); if (!$hasMore) { $hasMore = ActivityLog::where('created_at', '<', $refDate) ->exists(); } } return [ /* translators: %d is the number of days; used to indicate how old the deleted logs were. */ 'message' => sprintf(__('Logs older than %d days have been deleted successfully', 'fluent-crm'), $daysBefore), 'has_more' => $hasMore ]; } public function deleteRestKey(Request $request) { $data = $request->all(); $this->validate($data, [ 'user_id' => 'required', 'uuid' => 'required' ]); $data['user_id'] = intval($data['user_id']); $data['uuid'] = sanitize_text_field($data['uuid']); if (!get_user_meta($data['user_id'], '_fcrm_has_role', true)) { return $this->sendError([ 'message' => __('Sorry, the provided user does not have FluentCRM access', 'fluent-crm') ]); } if (!current_user_can('manage_options')) { return $this->sendError([ 'message' => __('Sorry, You do not have permission to delete REST API', 'fluent-crm') ]); } $deleted = \WP_Application_Passwords::delete_application_password($data['user_id'], $data['uuid']); if ($deleted) { $applicationUsers = fluentcrm_get_option('_rest_api_users', []); foreach ($applicationUsers[$data['user_id']] as $index => $uuid) { if ($uuid == $data['uuid']) { array_splice($applicationUsers[$data['user_id']], $index, 1); } } fluentcrm_update_option('_rest_api_users', $applicationUsers); } if (!$deleted) { return $this->sendError([ 'message' => __('Something is wrong', 'fluent-crm') ]); } return [ 'message' => __('API Key has been successfully deleted', 'fluent-crm') ]; } public function getRestKeys(Request $request) { $query = new \WP_User_Query(array( 'meta_key' => '_fcrm_has_role', 'meta_value' => 1, 'meta_compare' => '=', 'number' => 200 )); $managers = []; foreach ($query->get_results() as $user) { if (user_can($user, 'manage_options')) { continue; } $managers[] = [ 'id' => $user->ID, 'full_name' => $user->first_name . ' - ' . $user->last_name, 'email' => $user->user_email ]; } $applicationUsers = fluentcrm_get_option('_rest_api_users', []); $restApps = []; if ($applicationUsers) { $userIds = array_keys($applicationUsers); $restUsers = get_users([ 'include' => $userIds, 'number' => 20 ]); foreach ($restUsers as $restUser) { $applicationUUIDs = $applicationUsers[$restUser->ID]; $passwords = get_user_meta($restUser->ID, '_application_passwords', true); $crmApps = []; foreach ($passwords as $password) { if (in_array($password['uuid'], $applicationUUIDs)) { $crmApps[] = [ 'uuid' => $password['uuid'], 'name' => $password['name'], 'created' => gmdate('Y-m-d H:i:s', $password['created']) ]; } } if (!$crmApps) { continue; } $restApps[] = [ 'id' => $restUser->ID, 'first_name' => $restUser->first_name, 'last_name' => $restUser->last_name, 'email' => $restUser->user_email, 'api_keys' => $crmApps, 'manage_url' => admin_url('user-edit.php?user_id=' . $restUser->ID . '#application-passwords-section') ]; } } return [ 'managers' => $managers, 'rest_keys' => $restApps ]; } public function createRestKey(Request $request) { $data = $request->all(); $this->validate($data, [ 'api_name' => 'required', 'api_user_id' => 'required' ]); $data['api_user_id'] = intval($data['api_user_id']); $data['api_name'] = sanitize_text_field($data['api_name']); // check if the provided user has FluentCRM Access if (!get_user_meta($data['api_user_id'], '_fcrm_has_role', true)) { return $this->sendError([ 'message' => __('Sorry, the provided user does not have FluentCRM access', 'fluent-crm') ]); } if (!current_user_can('manage_options')) { return $this->sendError([ 'message' => __('Sorry, You do not have permission to create REST API', 'fluent-crm') ]); } $user = get_user_by('ID', $data['api_user_id']); if (is_wp_error($user)) { return $this->sendError([ 'message' => __('Sorry, the provided user does not have FluentCRM access', 'fluent-crm') ]); } $prepared = (object)[ 'name' => $data['api_name'] ]; $created = \WP_Application_Passwords::create_new_application_password($user->ID, wp_slash((array)$prepared)); if (is_wp_error($created)) { return $this->sendError([ 'message' => $created->get_error_message() ]); } $password = $created[0]; $item = \WP_Application_Passwords::get_user_application_password($user->ID, $created[1]['uuid']); $item['info'] = [ 'api_password' => \WP_Application_Passwords::chunk_password($password), 'api_username' => $user->user_login, ]; $uuid = $item['uuid']; $applicationUsers = fluentcrm_get_option('_rest_api_users', []); if (!isset($applicationUsers[$user->ID])) { $applicationUsers[$user->ID] = []; } $applicationUsers[$user->ID][] = $uuid; fluentcrm_update_option('_rest_api_users', $applicationUsers); return [ 'item' => $item, 'message' => __('API Key has been successfully created', 'fluent-crm') ]; } public function getIntegrations(Request $request) { $withFields = in_array('fields', $request->get('with', [])); /** * Determine the deep integration providers for FluentCRM. * * This filter allows modification of the deep integration providers used in FluentCRM such as Woocommerce, Easy Digital Downloads, etc. * * @param array An array of deep integration providers. * @param bool $withFields Whether to include fields in the integration providers. * @since 2.5.1 * */ $deepIntegrationProviders = apply_filters('fluentcrm_deep_integration_providers', [], $withFields); return [ 'integrations' => $deepIntegrationProviders ]; } public function saveIntegration(Request $request) { $provider = $request->get('provider'); $action = $request->get('action'); $data = $request->all(); if ($action == 'sync') { /** * Determine whether to allow deep integration sync for a specific provider. * * This filter allows you to modify the result of the deep integration sync for a given provider. * * @param mixed The result of the integration sync. Default false. Expected to be a boolean. * @param array $data The data to be synced. * @since 2.5.1 * */ $result = apply_filters('fluentcrm_deep_integration_sync_' . $provider, false, $data); } else { /** * Determine the result of saving deep integration settings for a specific provider. * * The dynamic portion of the hook name, `$provider`, refers to the specific integration provider. * * @param mixed The result of the save operation. Default false. Expected to be a boolean. * @param array $data The data being saved. * @since 2.5.1 * */ $result = apply_filters('fluentcrm_deep_integration_save_' . $provider, false, $data); } if ($result) { if (is_wp_error($result)) { return $this->sendError([ 'message' => $result->get_error_message() ]); } return $result; } return $this->sendError([ 'message' => __('Sorry, the provided provider does not exist', 'fluent-crm') ]); } public function getComplianceSettings(Request $request) { return [ 'settings' => Helper::getComplianceSettings() ]; } public function updateComplianceSettings(Request $request) { $data = Arr::only($request->all(), array_keys(Helper::getComplianceSettings())); $validValues = [ 'yes', 'no', 'anonymous' ]; foreach ($data as $key => $datum) { if (!in_array($datum, $validValues)) { $data[$key] = ''; } else { $data[$key] = $datum; } } update_option('_fluentcrm_compliance_settings', $data, 'no'); do_action('fluent_crm/sync_subscriber_delete_setting', 'compliance_settings', $data['delete_contact_on_user']); return [ 'message' => __('Settings has been successfully updated', 'fluent-crm'), 'settings' => $data ]; } public function getExperimentalSettings(Request $request) { return [ 'settings' => Helper::getExperimentalSettings() ]; } public function updateExperimentalSettings(Request $request) { $data = Arr::only($request->all(), array_keys(Helper::getExperimentalSettings())); foreach ($data as $key => $datum) { if ($key === 'campaign_ids' && is_array($datum)) { $data[$key] = array_map('intval', $datum); } elseif ($key === 'frontend_portal_page_id') { $data[$key] = absint($datum); } elseif ($key === 'frontend_portal_slug') { $data[$key] = sanitize_title($datum); } elseif ($key === 'frontend_portal_render_type') { $renderType = sanitize_text_field($datum); $data[$key] = in_array($renderType, ['standalone', 'shortcode'], true) ? $renderType : 'standalone'; } else { $data[$key] = sanitize_text_field($datum); } } if (Arr::get($data, 'frontend_portal') === 'yes' && empty($data['frontend_portal_slug'])) { $data['frontend_portal_slug'] = 'fluentcrm'; } if (Arr::get($data, 'company_module') == 'yes') { require_once(FLUENTCRM_PLUGIN_PATH . 'database/migrations/CompaniesMigrator.php'); \FluentCrmMigrations\CompaniesMigrator::migrate(); } if (Arr::get($data, 'event_tracking') == 'yes') { require_once(FLUENTCRM_PLUGIN_PATH . 'database/migrations/SubscriberEventTracking.php'); \FluentCrmMigrations\SubscriberEventTracking::migrate(); } if (Arr::get($data, 'activity_log') == 'yes') { require_once(FLUENTCRM_PLUGIN_PATH . 'database/migrations/ActivityLogsMigrator.php'); \FluentCrmMigrations\ActivityLogsMigrator::migrate(); } update_option('_fluentcrm_experimental_settings', $data, 'yes'); return [ 'message' => __('Settings has been updated', 'fluent-crm') ]; } public function getCampaigns(Request $request) { $campaigns = Campaign::orderBy('id', 'DESC')->get(); return [ 'campaigns' => $campaigns ]; } }