get_pipelines(); if ( $result['success'] ) { $pipelines = $this->normalize_pipelines( $result['pipelines'] ); } } wp_enqueue_script( 'lacrm-editor', plugin_dir_url( LACRM_FORMS_PLUGIN_FILE ) . 'assets/js/lacrm-editor.js', array( 'jquery' ), LACRM_FORMS_VERSION, true ); // Avoid double-localizing if CF7 already did it on the same page. if ( ! wp_script_is( 'lacrm-editor', 'done' ) ) { wp_localize_script( 'lacrm-editor', 'lacrmEditorData', array( 'pipelines' => $pipelines, 'i18n' => array( 'select_pipeline' => __( '— Select Pipeline —', 'aal-connector-for-lacrm' ), 'select_status' => __( '— Select Status —', 'aal-connector-for-lacrm' ), ), ) ); } } /** * Normalize raw custom field data from LACRM API into a consistent structure. * * @param array $raw Raw custom fields array from API. * @return array Normalized: [{id, name, type}] */ /** * Types that are display-only layout elements with no storable value. */ private const CF_DISPLAY_ONLY_TYPES = array( 'Section', 'SectionHeader', 'TextBlock', 'Signature', 'FileLink' ); private function normalize_custom_fields( array $raw ): array { $fields = array(); foreach ( $raw as $cf ) { if ( ! is_array( $cf ) ) { continue; } // Skip LACRM built-in fields (Birthday, Job Title, Website, etc.). $default_flag = $cf['IsDefaultField'] ?? $cf['isDefaultField'] ?? $cf['DefaultField'] ?? $cf['defaultField'] ?? false; if ( filter_var( $default_flag, FILTER_VALIDATE_BOOLEAN ) ) { continue; } $id = (string) ( $cf['CustomFieldId'] ?? $cf['customFieldId'] ?? '' ); $name = (string) ( $cf['Name'] ?? $cf['name'] ?? '' ); $type = (string) ( $cf['Type'] ?? $cf['type'] ?? 'Text' ); if ( $id === '' || $name === '' ) { continue; } // Skip display-only layout types that hold no user-entered value. if ( in_array( $type, self::CF_DISPLAY_ONLY_TYPES, true ) ) { continue; } $fields[] = array( 'id' => $id, 'name' => $name, 'type' => $type ); } return $fields; } /** * Normalize raw pipeline data from LACRM API into a consistent structure. * * @param array $raw Raw pipeline array from API. * @return array Normalized: [{id, name, statuses: [{id, name}]}] */ private function normalize_pipelines( array $raw ): array { $pipelines = array(); foreach ( $raw as $p ) { if ( ! is_array( $p ) ) { continue; } $id = (string) ( $p['PipelineId'] ?? $p['pipelineId'] ?? $p['PipelineID'] ?? $p['pipelineID'] ?? $p['Id'] ?? $p['id'] ?? '' ); $name = (string) ( $p['Name'] ?? $p['name'] ?? $p['PipelineName'] ?? $p['pipelineName'] ?? '' ); if ( $id === '' ) { continue; } $statuses_raw = $p['Statuses'] ?? $p['StatusList'] ?? $p['statuses'] ?? $p['PipelineStatuses'] ?? $p['pipelineStatuses'] ?? array(); $statuses = array(); if ( is_array( $statuses_raw ) ) { foreach ( $statuses_raw as $s ) { if ( ! is_array( $s ) ) { continue; } $sid = (string) ( $s['StatusId'] ?? $s['statusId'] ?? $s['PipelineStatusId'] ?? $s['pipelineStatusId'] ?? $s['Id'] ?? $s['id'] ?? '' ); $sname = (string) ( $s['Name'] ?? $s['name'] ?? $s['StatusName'] ?? $s['statusName'] ?? '' ); if ( $sid !== '' ) { $statuses[] = array( 'id' => $sid, 'name' => $sname ); } } } $pipelines[] = array( 'id' => $id, 'name' => $name, 'statuses' => $statuses ); } return $pipelines; } /** * Get pipeline and status choices for GF select fields. * * @return array{pipelines: array, statuses: array} Ready-to-use GF choices arrays. */ private function get_pipeline_choices(): array { $api_key = lacrm_forms_get_option( 'api_key', '' ); $pipeline_choices = array( array( 'label' => __( '— Select Pipeline —', 'aal-connector-for-lacrm' ), 'value' => '' ) ); $status_choices = array( array( 'label' => __( '— Select Status —', 'aal-connector-for-lacrm' ), 'value' => '' ) ); if ( $api_key !== '' ) { $api = new LACRM_Forms_API( $api_key ); $result = $api->get_pipelines(); if ( $result['success'] ) { $pipelines = $this->normalize_pipelines( $result['pipelines'] ); foreach ( $pipelines as $pl ) { $pipeline_choices[] = array( 'label' => $pl['name'], 'value' => $pl['id'] ); foreach ( $pl['statuses'] as $st ) { // Label includes pipeline name so the flat list is readable without cascade. $status_choices[] = array( 'label' => $pl['name'] . ': ' . $st['name'], 'value' => $st['id'], ); } } } } return array( 'pipelines' => $pipeline_choices, 'statuses' => $status_choices, ); } /** * Feed settings fields (enable sync + field mapping + company + pipeline). * Signature must match GFFeedAddOn::feed_settings_fields(). * * @param int $form_id Form ID. * @param int $feed_id Feed ID (0 for new). * @return array */ public function feed_settings_fields( $form_id = 0, $feed_id = 0 ) { $api_key = lacrm_forms_get_option( 'api_key', '' ); if ( $api_key === '' ) { return array( array( 'title' => __( 'LACRM Feed Settings', 'aal-connector-for-lacrm' ), 'fields' => array( array( 'name' => 'lacrm_setup_required', 'label' => '', 'type' => 'html', 'html' => '
' . wp_kses_post( sprintf( /* translators: %s: plugin settings URL */ __( 'Please save your LACRM API key in plugin settings before configuring Gravity Forms feeds.', 'aal-connector-for-lacrm' ), esc_url( admin_url( 'options-general.php?page=aal-connector-for-lacrm' ) ) ) ) . '
', ), ), ), ); } $pipeline_data = $this->get_pipeline_choices(); $owner_choices = $this->get_owner_choices(); $fields = array(); // ── Section 1: Contact sync ── $fields[] = array( 'title' => __( 'LACRM Feed Settings', 'aal-connector-for-lacrm' ), 'fields' => array( array( 'name' => 'feedName', 'label' => __( 'Feed name', 'aal-connector-for-lacrm' ), 'type' => 'text', 'class' => 'medium', 'tooltip' => __( 'A name for this feed (e.g. "Sync to LACRM").', 'aal-connector-for-lacrm' ), ), array( 'name' => 'enabled', 'label' => __( 'Enable LACRM sync', 'aal-connector-for-lacrm' ), 'type' => 'checkbox', 'choices' => array( array( 'name' => 'enabled', 'label' => __( 'Sync this form to Less Annoying CRM', 'aal-connector-for-lacrm' ), ), ), ), array( 'name' => 'contact_owner', 'label' => __( 'Contact owner (optional)', 'aal-connector-for-lacrm' ), 'type' => 'select', 'choices' => $owner_choices, 'tooltip' => __( 'Override default contact owner for this feed.', 'aal-connector-for-lacrm' ), 'dependency' => array( 'field' => 'enabled', 'values' => array( '1' ), ), ), array( 'name' => 'field_map_first_name', 'label' => __( 'First Name', 'aal-connector-for-lacrm' ), 'type' => 'field_select', 'tooltip' => __( 'Map to LACRM First Name (used in full name).', 'aal-connector-for-lacrm' ), 'dependency' => array( 'field' => 'enabled', 'values' => array( '1' ), ), ), array( 'name' => 'field_map_last_name', 'label' => __( 'Last Name', 'aal-connector-for-lacrm' ), 'type' => 'field_select', 'tooltip' => __( 'Map to LACRM Last Name.', 'aal-connector-for-lacrm' ), 'dependency' => array( 'field' => 'enabled', 'values' => array( '1' ), ), ), array( 'name' => 'field_map_email', 'label' => __( 'Email', 'aal-connector-for-lacrm' ), 'type' => 'field_select', 'tooltip' => __( 'Required for matching/creating contacts.', 'aal-connector-for-lacrm' ), 'dependency' => array( 'field' => 'enabled', 'values' => array( '1' ), ), ), array( 'name' => 'field_map_phone', 'label' => __( 'Phone', 'aal-connector-for-lacrm' ), 'type' => 'field_select', 'dependency' => array( 'field' => 'enabled', 'values' => array( '1' ), ), ), array( 'name' => 'field_map_company', 'label' => __( 'Company', 'aal-connector-for-lacrm' ), 'type' => 'field_select', 'dependency' => array( 'field' => 'enabled', 'values' => array( '1' ), ), ), array( 'name' => 'field_map_notes', 'label' => __( 'Notes', 'aal-connector-for-lacrm' ), 'type' => 'field_select', 'tooltip' => __( 'Stored as Background Info and/or a note on the contact.', 'aal-connector-for-lacrm' ), 'dependency' => array( 'field' => 'enabled', 'values' => array( '1' ), ), ), ), ); // ── Section 2: Company settings ── $fields[] = array( 'title' => __( 'Company Settings', 'aal-connector-for-lacrm' ), 'fields' => array( array( 'name' => 'company_enabled', 'label' => __( 'Create / link Company', 'aal-connector-for-lacrm' ), 'type' => 'checkbox', 'tooltip' => __( 'When the Company field is filled, find or create a Company record in LACRM and link it to the contact.', 'aal-connector-for-lacrm' ), 'choices' => array( array( 'name' => 'company_enabled', 'label' => __( 'Create or link a Company record in LACRM when the Company field is filled', 'aal-connector-for-lacrm' ), ), ), 'dependency' => array( 'field' => 'enabled', 'values' => array( '1' ), ), ), ), ); // ── Section 3: Pipeline settings ── $pipeline_fields = array( array( 'name' => 'pipeline_enabled', 'label' => __( 'Enable Pipeline', 'aal-connector-for-lacrm' ), 'type' => 'checkbox', 'choices' => array( array( 'name' => 'pipeline_enabled', 'label' => __( 'Add contact to a Pipeline in LACRM after form submission', 'aal-connector-for-lacrm' ), ), ), 'dependency' => array( 'field' => 'enabled', 'values' => array( '1' ), ), ), ); if ( count( $pipeline_data['pipelines'] ) > 1 ) { // Pipeline dropdown (JS cascade will filter status based on this). $pipeline_fields[] = array( 'name' => 'pipeline_id', 'label' => __( 'Pipeline', 'aal-connector-for-lacrm' ), 'type' => 'select', 'choices' => $pipeline_data['pipelines'], 'class' => 'lacrm-gf-pipeline-select', 'tooltip' => __( 'Select the pipeline to add the contact to.', 'aal-connector-for-lacrm' ), 'dependency' => array( 'field' => 'pipeline_enabled', 'values' => array( '1' ), ), ); // Status dropdown – shows all statuses; JS cascades based on pipeline selection. $pipeline_fields[] = array( 'name' => 'pipeline_status_id', 'label' => __( 'Status', 'aal-connector-for-lacrm' ), 'type' => 'select', 'choices' => $pipeline_data['statuses'], 'class' => 'lacrm-gf-status-select', 'tooltip' => __( 'Select the initial pipeline status for the contact.', 'aal-connector-for-lacrm' ), 'dependency' => array( 'field' => 'pipeline_enabled', 'values' => array( '1' ), ), ); } else { $pipeline_fields[] = array( 'name' => 'pipeline_no_pipelines', 'label' => __( 'Pipeline', 'aal-connector-for-lacrm' ), 'type' => 'html', 'html' => '' . esc_html__( 'No pipelines found in LACRM. Create a pipeline in LACRM first.', 'aal-connector-for-lacrm' ) . '
', ); } $pipeline_fields[] = array( 'name' => 'pipeline_trigger', 'label' => __( 'When to add', 'aal-connector-for-lacrm' ), 'type' => 'radio', 'choices' => array( array( 'label' => __( 'Only for new contacts', 'aal-connector-for-lacrm' ), 'value' => 'new_only' ), array( 'label' => __( 'Every submission (new and returning contacts)', 'aal-connector-for-lacrm' ), 'value' => 'always' ), ), 'dependency' => array( 'field' => 'pipeline_enabled', 'values' => array( '1' ), ), ); $pipeline_fields[] = array( 'name' => 'pipeline_note_field', 'label' => __( 'Pipeline note (optional)', 'aal-connector-for-lacrm' ), 'type' => 'field_select', 'tooltip' => __( 'Optional: map a form field to include as a note on the pipeline item.', 'aal-connector-for-lacrm' ), 'dependency' => array( 'field' => 'pipeline_enabled', 'values' => array( '1' ), ), ); $fields[] = array( 'title' => __( 'Pipeline Settings', 'aal-connector-for-lacrm' ), 'fields' => $pipeline_fields, ); // ── Section 4: Custom Fields ── $cf_section_fields = array(); $lacrm_custom_fields = array(); $api_key = lacrm_forms_get_option( 'api_key', '' ); if ( $api_key !== '' ) { $api_cf = new LACRM_Forms_API( $api_key ); $lacrm_custom_fields = $this->get_cached_contact_custom_fields( $api_cf ); } if ( empty( $lacrm_custom_fields ) ) { $cf_section_fields[] = array( 'name' => 'no_custom_fields', 'label' => '', 'type' => 'html', 'html' => '' . esc_html__( 'No custom fields found in your LACRM account. Add custom fields in LACRM Settings, then return here.', 'aal-connector-for-lacrm' ) . '
', ); } else { foreach ( $lacrm_custom_fields as $cf ) { $cf_section_fields[] = array( 'name' => 'custom_field_' . $cf['id'], 'label' => $cf['name'], 'type' => 'field_select', 'tooltip' => sprintf( /* translators: %s: custom field name */ __( 'Map a form field to the LACRM custom field "%s".', 'aal-connector-for-lacrm' ), $cf['name'] ), 'dependency' => array( 'field' => 'enabled', 'values' => array( '1' ), ), ); } } $fields[] = array( 'title' => __( 'Custom Fields', 'aal-connector-for-lacrm' ), 'fields' => $cf_section_fields, ); // ── Section 5: Task Settings ── $task_fields = array( array( 'name' => 'task_enabled', 'label' => __( 'Create Task', 'aal-connector-for-lacrm' ), 'type' => 'checkbox', 'choices' => array( array( 'name' => 'task_enabled', 'label' => __( 'Create a task in LACRM after form submission', 'aal-connector-for-lacrm' ), ), ), 'dependency' => array( 'field' => 'enabled', 'values' => array( '1' ), ), ), array( 'name' => 'task_description', 'label' => __( 'Task description', 'aal-connector-for-lacrm' ), 'type' => 'text', 'class' => 'medium', 'tooltip' => __( 'Static task description. If a form field is mapped below, that value is used instead.', 'aal-connector-for-lacrm' ), 'dependency' => array( 'field' => 'task_enabled', 'values' => array( '1' ), ), ), array( 'name' => 'task_description_field', 'label' => __( 'Or map description from field', 'aal-connector-for-lacrm' ), 'type' => 'field_select', 'tooltip' => __( 'If set, the selected form field value is used as the task description (overrides static text above).', 'aal-connector-for-lacrm' ), 'dependency' => array( 'field' => 'task_enabled', 'values' => array( '1' ), ), ), array( 'name' => 'task_due_days', 'label' => __( 'Due in (days)', 'aal-connector-for-lacrm' ), 'type' => 'text', 'class' => 'small', 'tooltip' => __( '0 = due today, 1 = due tomorrow, etc.', 'aal-connector-for-lacrm' ), 'dependency' => array( 'field' => 'task_enabled', 'values' => array( '1' ), ), ), array( 'name' => 'task_assigned_to', 'label' => __( 'Assign to', 'aal-connector-for-lacrm' ), 'type' => 'select', 'choices' => $owner_choices, 'tooltip' => __( 'User to assign the task to. Defaults to the contact owner.', 'aal-connector-for-lacrm' ), 'dependency' => array( 'field' => 'task_enabled', 'values' => array( '1' ), ), ), array( 'name' => 'task_trigger', 'label' => __( 'When to create', 'aal-connector-for-lacrm' ), 'type' => 'radio', 'choices' => array( array( 'label' => __( 'Only for new contacts', 'aal-connector-for-lacrm' ), 'value' => 'new_only' ), array( 'label' => __( 'Every submission (new and returning contacts)', 'aal-connector-for-lacrm' ), 'value' => 'always' ), ), 'dependency' => array( 'field' => 'task_enabled', 'values' => array( '1' ), ), ), ); $fields[] = array( 'title' => __( 'Task Settings', 'aal-connector-for-lacrm' ), 'fields' => $task_fields, ); return $fields; } /** * Get contact custom fields with a short-lived transient cache (5 minutes). * Avoids an extra GetCustomFields API call on every form submission. * * @param LACRM_Forms_API $api API instance. * @return array Normalized: [{id, name, type}] */ private function get_cached_contact_custom_fields( LACRM_Forms_API $api ): array { $transient_key = 'lacrm_cf_contact_defs'; $cached = get_transient( $transient_key ); if ( is_array( $cached ) ) { return $cached; } $result = $api->get_custom_fields(); $fields = $result['success'] ? $this->normalize_custom_fields( $result['fields'] ) : array(); set_transient( $transient_key, $fields, 5 * MINUTE_IN_SECONDS ); return $fields; } /** * Get owner dropdown choices (default + LACRM users). * * @return array */ private function get_owner_choices(): array { $choices = array( array( 'label' => __( 'Use default (from AAL Connector For LACRM settings)', 'aal-connector-for-lacrm' ), 'value' => '', ), ); $api_key = lacrm_forms_get_option( 'api_key', '' ); if ( $api_key !== '' ) { $api = new LACRM_Forms_API( $api_key ); $users = $api->get_users(); if ( $users['success'] && ! empty( $users['users'] ) ) { foreach ( $users['users'] as $user ) { $uid = $user['UserId'] ?? ''; $name = trim( ( $user['FirstName'] ?? '' ) . ' ' . ( $user['LastName'] ?? '' ) ); if ( $name === '' ) { $name = $uid; } $choices[] = array( 'label' => $name, 'value' => $uid, ); } } } return $choices; } /** * Process feed: create/update LACRM contact, optionally company and pipeline, on form submission. * Signature must match GFFeedAddOn::process_feed(). * * @param array $feed Feed object. * @param array $entry Entry object. * @param array $form Form object. */ public function process_feed( $feed, $entry, $form ) { if ( ! is_array( $feed ) || ! is_array( $entry ) || ! is_array( $form ) ) { return; } if ( empty( $feed['meta']['enabled'] ) ) { return; } $api_key = lacrm_forms_get_option( 'api_key', '' ); if ( $api_key === '' ) { LACRM_Forms_Logger::instance()->log( __( 'LACRM API key not set; skipping GF feed.', 'aal-connector-for-lacrm' ), 'error', array( 'form_id' => $form['id'], 'entry_id' => $entry['id'] ) ); return; } $meta = is_array( $feed['meta'] ) ? $feed['meta'] : array(); $first_name = $this->get_field_value( $form, $entry, (string) ( $meta['field_map_first_name'] ?? '' ) ); $last_name = $this->get_field_value( $form, $entry, (string) ( $meta['field_map_last_name'] ?? '' ) ); $email = $this->get_field_value( $form, $entry, (string) ( $meta['field_map_email'] ?? '' ) ); $phone = $this->get_field_value( $form, $entry, (string) ( $meta['field_map_phone'] ?? '' ) ); $company = $this->get_field_value( $form, $entry, (string) ( $meta['field_map_company'] ?? '' ) ); $notes = $this->get_field_value( $form, $entry, (string) ( $meta['field_map_notes'] ?? '' ) ); $email = is_string( $email ) ? sanitize_email( $email ) : ''; if ( ! is_email( $email ) ) { LACRM_Forms_Logger::instance()->log( __( 'No valid email in mapped field; LACRM sync skipped.', 'aal-connector-for-lacrm' ), 'error', array( 'form_id' => $form['id'], 'entry_id' => $entry['id'] ) ); return; } $name = trim( ( is_string( $first_name ) ? $first_name : '' ) . ' ' . ( is_string( $last_name ) ? $last_name : '' ) ); if ( $name === '' ) { $name = $email; } $assigned_to = ! empty( $meta['contact_owner'] ) ? (string) $meta['contact_owner'] : (string) lacrm_forms_get_option( 'default_owner', '' ); if ( $assigned_to === '' ) { LACRM_Forms_Logger::instance()->log( __( 'No contact owner (default or feed); LACRM CreateContact requires AssignedTo.', 'aal-connector-for-lacrm' ), 'error', array( 'form_id' => $form['id'] ) ); return; } $api = new LACRM_Forms_API( $api_key ); $find = $api->find_contact_by_email( $email ); $contact_id = null; $is_new = false; // Build an id → name lookup so custom field values can be sent using the field Name // (LACRM v2 expects custom field values as direct top-level named parameters). $cf_id_to_name = array(); foreach ( $this->get_cached_contact_custom_fields( $api ) as $cf_def ) { if ( ! empty( $cf_def['id'] ) && ! empty( $cf_def['name'] ) ) { $cf_id_to_name[ $cf_def['id'] ] = $cf_def['name']; } } // Collect custom field values from feed meta keys prefixed 'custom_field_{id}'. $custom_fields = array(); // {cf_name => value} — ready for the API call. foreach ( $meta as $key => $value ) { if ( strpos( (string) $key, 'custom_field_' ) === 0 ) { $cf_id = substr( (string) $key, strlen( 'custom_field_' ) ); $cf_name = $cf_id_to_name[ $cf_id ] ?? ''; $field_id = (string) $value; if ( $cf_name !== '' && $field_id !== '' ) { $cf_value = $this->get_field_value( $form, $entry, $field_id ); if ( is_string( $cf_value ) && $cf_value !== '' ) { $custom_fields[ $cf_name ] = $cf_value; } } } } if ( $find['success'] && ! empty( $find['contact_id'] ) ) { $contact_id = $find['contact_id']; $edit = $api->edit_contact( $contact_id, array( 'name' => $name, 'email' => $email, 'phone' => is_string( $phone ) ? $phone : '', 'company_name' => is_string( $company ) ? $company : '', 'background_info' => is_string( $notes ) ? $notes : '', 'assigned_to' => $assigned_to, 'custom_fields' => $custom_fields, ) ); if ( ! $edit['success'] ) { LACRM_Forms_Logger::instance()->log( $edit['error_description'] ?? 'EditContact failed', 'error', array( 'contact_id' => $contact_id ) ); } else { LACRM_Forms_Logger::instance()->log( __( 'LACRM contact updated.', 'aal-connector-for-lacrm' ), 'info', array( 'contact_id' => $contact_id, 'entry_id' => $entry['id'] ) ); } $form_title = isset( $form['title'] ) ? $form['title'] : __( 'Form', 'aal-connector-for-lacrm' ); $resubmit_note = sprintf( /* translators: 1: form title, 2: date/time */ __( 'Form resubmitted: %1$s on %2$s.', 'aal-connector-for-lacrm' ), $form_title, current_time( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ) ); if ( is_string( $notes ) && $notes !== '' ) { $resubmit_note .= "\n" . __( 'Message:', 'aal-connector-for-lacrm' ) . ' ' . $notes; } $api->create_note( $contact_id, $resubmit_note ); } else { $create = $api->create_contact( array( 'assigned_to' => $assigned_to, 'name' => $name, 'email' => $email, 'phone' => is_string( $phone ) ? $phone : '', 'company_name' => is_string( $company ) ? $company : '', 'background_info' => is_string( $notes ) ? $notes : '', 'custom_fields' => $custom_fields, ) ); if ( ! $create['success'] ) { LACRM_Forms_Logger::instance()->log( $create['error_description'] ?? 'CreateContact failed', 'error', array( 'form_id' => $form['id'], 'entry_id' => $entry['id'] ) ); return; } $contact_id = $create['contact_id'] ?? null; $is_new = true; LACRM_Forms_Logger::instance()->log( __( 'LACRM contact created.', 'aal-connector-for-lacrm' ), 'info', array( 'contact_id' => $contact_id, 'entry_id' => $entry['id'] ) ); if ( $contact_id && is_string( $notes ) && $notes !== '' ) { $api->create_note( $contact_id, $notes ); } } if ( ! $contact_id ) { return; } // ── Company sync ── if ( ! empty( $meta['company_enabled'] ) && is_string( $company ) && $company !== '' ) { $this->sync_company( $api, $contact_id, $company, $assigned_to, $form['id'] ); } // ── Pipeline sync ── if ( ! empty( $meta['pipeline_enabled'] ) ) { $pipeline_id = (string) ( $meta['pipeline_id'] ?? '' ); $pipeline_status_id = (string) ( $meta['pipeline_status_id'] ?? '' ); $pipeline_trigger = (string) ( $meta['pipeline_trigger'] ?? 'new_only' ); $note_field_id = (string) ( $meta['pipeline_note_field'] ?? '' ); $pipeline_note = $note_field_id !== '' ? $this->get_field_value( $form, $entry, $note_field_id ) : ''; $should_add = $pipeline_trigger === 'always' || $is_new; if ( $should_add && $pipeline_id !== '' && $pipeline_status_id !== '' ) { $this->sync_pipeline( $api, $contact_id, $pipeline_id, $pipeline_status_id, is_string( $pipeline_note ) ? $pipeline_note : '', $assigned_to, $form['id'] ); } } // ── Task sync ── if ( ! empty( $meta['task_enabled'] ) ) { $task_trig = (string) ( $meta['task_trigger'] ?? 'new_only' ); if ( $task_trig === 'always' || $is_new ) { $this->sync_task( $api, $contact_id, $meta, $form, $entry, $assigned_to, $form['id'] ); } } } /** * Create a task in LACRM linked to the contact. * * @param LACRM_Forms_API $api API instance. * @param string $contact_id LACRM ContactId. * @param array $meta Feed meta settings. * @param array $form GF form object. * @param array $entry GF entry object. * @param string $assigned_to Fallback user ID. * @param int|string $form_id GF form ID (for logging context). */ private function sync_task( LACRM_Forms_API $api, string $contact_id, array $meta, array $form, array $entry, string $assigned_to, $form_id ): void { $description_field_id = (string) ( $meta['task_description_field'] ?? '' ); $description = ''; if ( $description_field_id !== '' ) { $description = $this->get_field_value( $form, $entry, $description_field_id ); $description = is_string( $description ) ? $description : ''; } if ( $description === '' ) { $description = (string) ( $meta['task_description'] ?? '' ); } if ( $description === '' ) { LACRM_Forms_Logger::instance()->log( __( 'LACRM: task skipped — no description configured.', 'aal-connector-for-lacrm' ), 'info', array( 'contact_id' => $contact_id, 'form_id' => $form_id ) ); return; } $due_days = ( isset( $meta['task_due_days'] ) && is_numeric( $meta['task_due_days'] ) ) ? max( 0, (int) $meta['task_due_days'] ) : 1; $due_date = gmdate( 'Y-m-d', strtotime( '+' . $due_days . ' days', (int) current_time( 'timestamp', true ) ) ); $task_owner = ! empty( $meta['task_assigned_to'] ) ? (string) $meta['task_assigned_to'] : $assigned_to; $result = $api->create_task( $contact_id, array( 'description' => $description, 'due_date' => $due_date, 'assigned_to' => $task_owner, ) ); if ( ! $result['success'] ) { LACRM_Forms_Logger::instance()->log( $result['error_description'] ?? 'CreateTask failed', 'error', array( 'contact_id' => $contact_id, 'form_id' => $form_id ) ); } else { LACRM_Forms_Logger::instance()->log( __( 'LACRM: task created (GF).', 'aal-connector-for-lacrm' ), 'info', array( 'contact_id' => $contact_id, 'task_id' => $result['task_id'] ?? '', 'form_id' => $form_id ) ); } } /** * Find or create a Company in LACRM and associate it with the contact. * * @param LACRM_Forms_API $api API instance. * @param string $contact_id LACRM ContactId of the person. * @param string $company Company name from the form. * @param string $assigned_to User ID to assign a newly created company to. * @param int|string $form_id GF form ID (for logging context). */ private function sync_company( LACRM_Forms_API $api, string $contact_id, string $company, string $assigned_to, $form_id ): void { $find = $api->find_company_by_name( $company ); if ( $find['success'] && ! empty( $find['company_id'] ) ) { $company_id = $find['company_id']; LACRM_Forms_Logger::instance()->log( __( 'LACRM: existing company found, linking contact.', 'aal-connector-for-lacrm' ), 'info', array( 'contact_id' => $contact_id, 'company_id' => $company_id, 'form_id' => $form_id ) ); } else { $create = $api->create_company( array( 'assigned_to' => $assigned_to, 'name' => $company, ) ); if ( ! $create['success'] ) { LACRM_Forms_Logger::instance()->log( $create['error_description'] ?? 'CreateCompany failed', 'error', array( 'form_id' => $form_id ) ); return; } $company_id = $create['company_id'] ?? ''; LACRM_Forms_Logger::instance()->log( __( 'LACRM: company created.', 'aal-connector-for-lacrm' ), 'info', array( 'contact_id' => $contact_id, 'company_id' => $company_id, 'form_id' => $form_id ) ); } if ( $company_id === '' ) { return; } $assoc = $api->associate_contacts( $contact_id, $company_id ); if ( ! $assoc['success'] ) { LACRM_Forms_Logger::instance()->log( $assoc['error_description'] ?? 'AssociateContacts failed', 'error', array( 'contact_id' => $contact_id, 'company_id' => $company_id ) ); } else { LACRM_Forms_Logger::instance()->log( __( 'LACRM: contact linked to company.', 'aal-connector-for-lacrm' ), 'info', array( 'contact_id' => $contact_id, 'company_id' => $company_id ) ); } } /** * Attach a contact to an existing LACRM pipeline — skips if already attached. * * Flow: * 1. GetPipelineReport → check whether contact already has an item in this pipeline. * 2. Already in pipeline → log and return (no duplicate created). * 3. Not in pipeline → CreatePipelineItem (attach contact to the pipeline). * * @param LACRM_Forms_API $api LACRM API instance. * @param string $contact_id LACRM ContactId. * @param string $pipeline_id ID of the existing pipeline. * @param string $pipeline_status_id Status ID within the pipeline. * @param string $note Optional note text for the pipeline item. * @param string $assigned_to User ID for pipeline assignment. * @param int|string $form_id GF form ID (for logging context). */ private function sync_pipeline( LACRM_Forms_API $api, string $contact_id, string $pipeline_id, string $pipeline_status_id, string $note, string $assigned_to, $form_id ): void { // ── Step 1: check whether contact is already in this pipeline ── $check = $api->check_contact_in_pipeline( $contact_id, $pipeline_id ); if ( $check['in_pipeline'] ) { LACRM_Forms_Logger::instance()->log( __( 'LACRM: contact already in pipeline — skipped to avoid duplicate.', 'aal-connector-for-lacrm' ), 'info', array( 'contact_id' => $contact_id, 'pipeline_id' => $pipeline_id, 'form_id' => $form_id ) ); return; } // ── Step 2: attach contact to the pipeline ── $result = $api->create_pipeline_item( $contact_id, array( 'pipeline_id' => $pipeline_id, 'status_id' => $pipeline_status_id, 'note' => $note, 'assigned_to' => $assigned_to, ) ); if ( ! $result['success'] ) { LACRM_Forms_Logger::instance()->log( $result['error_description'] ?? 'CreatePipelineItem failed', 'error', array( 'contact_id' => $contact_id, 'pipeline_id' => $pipeline_id, 'form_id' => $form_id ) ); } else { LACRM_Forms_Logger::instance()->log( __( 'LACRM: contact attached to pipeline.', 'aal-connector-for-lacrm' ), 'info', array( 'contact_id' => $contact_id, 'pipeline_item_id' => $result['pipeline_item_id'] ?? '', 'form_id' => $form_id ) ); } } /** * Get field value from entry (supports GF field types). * Signature must match GFAddOn::get_field_value($form, $entry, $field_id). * * @param array $form Form. * @param array $entry Entry. * @param string $field_id Field ID. * @return string */ public function get_field_value( $form, $entry, $field_id ) { $field_id = (string) $field_id; if ( $field_id === '' ) { return ''; } $entry = is_array( $entry ) ? $entry : array(); $value = function_exists( 'rgar' ) ? rgar( $entry, $field_id ) : ( $entry[ $field_id ] ?? '' ); if ( $value === '' || $value === null ) { return ''; } return is_array( $value ) ? implode( ' ', array_filter( $value ) ) : (string) $value; } /** * Feed list columns. * Signature must match GFFeedAddOn::feed_list_columns(). * * @return array */ public function feed_list_columns() { return array( 'feedName' => __( 'Name', 'aal-connector-for-lacrm' ), 'enabled' => __( 'Sync enabled', 'aal-connector-for-lacrm' ), ); } /** * Get column value for feed list (column "enabled"). * * Newer versions of Gravity Forms call get_column_value_*($item) with a single * argument; older versions pass ($item, $column). Making $column optional keeps * the method compatible with both. * * @param array $item Feed item. * @param string|null $column Column key (optional in newer GF versions). * @return string */ public function get_column_value_enabled( $item, $column = '' ) { $item = is_array( $item ) ? $item : array(); return ! empty( $item['meta']['enabled'] ) ? __( 'Yes', 'aal-connector-for-lacrm' ) : __( 'No', 'aal-connector-for-lacrm' ); } }