| |
| <?php
|
|
|
|
|
|
|
|
|
|
|
| defined( 'ABSPATH' ) || exit;
|
|
|
|
|
| add_action( 'admin_bar_menu', function( $wp_admin_bar ) {
|
| if ( ! current_user_can( 'manage_options' ) ) return;
|
|
|
| $wp_admin_bar->add_node( [
|
| 'id' => 'gf-flow-sync-test',
|
| 'title' => '🔄 Sync GF Workflow Steps',
|
| 'href' => admin_url( 'admin.php?gf_flow_sync_test=1' ),
|
| ] );
|
| }, 999 );
|
|
|
|
|
| add_action( 'admin_init', function() {
|
| if ( ! isset( $_GET['gf_flow_sync_test'] ) || ! current_user_can( 'manage_options' ) ) return;
|
|
|
| if ( ! class_exists( 'GFAPI' ) || ! class_exists( 'Gravity_Flow_API' ) ) {
|
| wp_die( 'Gravity Forms and/or Gravity Flow not active!' );
|
| }
|
|
|
|
|
| gf_flow_cleanup_orphaned_steps();
|
|
|
|
|
| $forms = GFAPI::get_forms();
|
| $count = 0;
|
|
|
| foreach ( $forms as $form ) {
|
| if ( gf_flow_mirror_steps_to_cpt( $form ) ) {
|
| $count++;
|
| }
|
| }
|
|
|
| wp_redirect( admin_url( 'edit.php?post_type=gf_workflow_step&synced=' . $count ) );
|
| exit;
|
| } );
|
|
|
|
|
| add_action( 'admin_notices', function() {
|
| if ( isset( $_GET['synced'] ) && $_GET['post_type'] === 'gf_workflow_step' ) {
|
| $count = intval( $_GET['synced'] );
|
| echo '<div class="notice notice-success"><p>✅ Synced workflows from ' . $count . ' forms to CPT!</p></div>';
|
| }
|
| } );
|
|
|
|
|
|
|
|
|
| function gf_flow_cleanup_orphaned_steps() {
|
| if ( ! class_exists( 'GFAPI' ) ) {
|
| return;
|
| }
|
|
|
|
|
| $forms = GFAPI::get_forms();
|
| $active_form_ids = array_map( function( $form ) {
|
| return absint( $form['id'] );
|
| }, $forms );
|
|
|
| error_log( 'GF_FLOW_SYNC: Active form IDs: ' . implode( ', ', $active_form_ids ) );
|
|
|
|
|
| $all_step_posts = new WP_Query( [
|
| 'post_type' => 'gf_workflow_step',
|
| 'posts_per_page' => -1,
|
| 'fields' => 'ids',
|
| ] );
|
|
|
| $deleted_count = 0;
|
|
|
| foreach ( $all_step_posts->posts as $post_id ) {
|
| $form_id = intval( get_post_meta( $post_id, 'form_id', true ) );
|
|
|
|
|
| if ( $form_id && ! in_array( $form_id, $active_form_ids, true ) ) {
|
| wp_delete_post( $post_id, true );
|
| $deleted_count++;
|
| error_log( "GF_FLOW_SYNC: Deleted orphaned step from non-existent form {$form_id} (post {$post_id})" );
|
| }
|
| }
|
|
|
| if ( $deleted_count > 0 ) {
|
| error_log( "GF_FLOW_SYNC: Cleaned up {$deleted_count} orphaned workflow steps" );
|
| }
|
| }
|
|
|
|
|
|
|
|
|
| function gf_flow_mirror_steps_to_cpt( $form ) {
|
|
|
| if ( ! post_type_exists( 'gf_workflow_step' ) ) {
|
| error_log( 'GF_FLOW_SYNC: CPT gf_workflow_step does not exist!' );
|
| return false;
|
| }
|
|
|
|
|
| if ( empty( $form ) || ! is_array( $form ) || empty( $form['id'] ) ) {
|
| error_log( 'GF_FLOW_SYNC: Invalid form data' );
|
| return false;
|
| }
|
|
|
|
|
| if ( ! class_exists( 'Gravity_Flow_API' ) ) {
|
| error_log( 'GF_FLOW_SYNC: Gravity Flow API not available' );
|
| return false;
|
| }
|
|
|
| $form_id = absint( $form['id'] );
|
| $form_title = isset( $form['title'] ) ? sanitize_text_field( $form['title'] ) : 'Form ' . $form_id;
|
|
|
|
|
| $gravity_flow_api = new Gravity_Flow_API( $form_id );
|
| $steps = $gravity_flow_api->get_steps();
|
|
|
| if ( empty( $steps ) ) {
|
| error_log( "GF_FLOW_SYNC: No workflow steps found for form {$form_id}" );
|
|
|
|
|
| $existing_query = new WP_Query( [
|
| 'post_type' => 'gf_workflow_step',
|
| 'posts_per_page' => -1,
|
| 'fields' => 'ids',
|
| 'meta_query' => [
|
| [
|
| 'key' => 'form_id',
|
| 'value' => $form_id,
|
| ]
|
| ]
|
| ] );
|
|
|
| foreach ( $existing_query->posts as $post_id ) {
|
| wp_delete_post( $post_id, true );
|
| }
|
|
|
| return false;
|
| }
|
|
|
| error_log( "GF_FLOW_SYNC: Processing " . count( $steps ) . " steps for form {$form_id} - {$form_title}" );
|
|
|
|
|
| $existing_query = new WP_Query( [
|
| 'post_type' => 'gf_workflow_step',
|
| 'posts_per_page' => -1,
|
| 'fields' => 'ids',
|
| 'meta_query' => [
|
| [
|
| 'key' => 'form_id',
|
| 'value' => $form_id,
|
| ]
|
| ]
|
| ] );
|
|
|
| $post_map = [];
|
| foreach ( $existing_query->posts as $post_id ) {
|
| $step_id = get_post_meta( $post_id, 'step_id', true );
|
| if ( $step_id ) {
|
| $post_map[ $step_id ] = $post_id;
|
| }
|
| }
|
|
|
| $processed_steps = [];
|
|
|
|
|
| $order = 0;
|
| foreach ( $steps as $step ) {
|
| $order++;
|
|
|
| if ( ! is_object( $step ) ) {
|
| continue;
|
| }
|
|
|
| $step_id = $step->get_id();
|
| $processed_steps[] = $step_id;
|
|
|
| $step_name = $step->get_name();
|
| $step_type = $step->get_type();
|
| $slug = sanitize_title( "gf-flow-{$form_id}-{$step_id}" );
|
|
|
|
|
| $assignees = [];
|
| if ( method_exists( $step, 'get_assignees' ) ) {
|
| $assignees = $step->get_assignees();
|
| }
|
| $assignees_text = gf_flow_format_assignees( $assignees );
|
|
|
|
|
| $routing_text = '';
|
| if ( method_exists( $step, 'get_routing' ) ) {
|
| $routing = $step->get_routing();
|
| if ( ! empty( $routing ) ) {
|
| $routing_text = wp_json_encode( $routing );
|
| }
|
| }
|
|
|
|
|
| $is_scheduled = false;
|
| $schedule_type = '';
|
| $schedule_value = '';
|
|
|
| if ( method_exists( $step, 'scheduled' ) && $step->scheduled ) {
|
| $is_scheduled = true;
|
| $schedule_type = isset( $step->schedule_type ) ? $step->schedule_type : 'delay';
|
|
|
| if ( $schedule_type === 'delay' ) {
|
| $delay_value = isset( $step->delay_value ) ? $step->delay_value : '';
|
| $delay_unit = isset( $step->delay_unit ) ? $step->delay_unit : 'hours';
|
| $schedule_value = $delay_value . ' ' . $delay_unit;
|
| } else {
|
| $schedule_value = isset( $step->schedule_date ) ? $step->schedule_date : '';
|
| }
|
| }
|
|
|
|
|
| $has_expiration = false;
|
| $expiration_value = 0;
|
|
|
| if ( method_exists( $step, 'expiration_enabled' ) && $step->expiration_enabled ) {
|
| $has_expiration = true;
|
| $expiration_value = isset( $step->expiration ) ? intval( $step->expiration ) : 0;
|
| }
|
|
|
|
|
| $settings = $step->get_settings();
|
| $settings_json = wp_json_encode( $settings );
|
|
|
|
|
| $has_conditional = false;
|
| $conditional_json = '';
|
|
|
| if ( isset( $settings['conditional_logic_enabled'] ) && $settings['conditional_logic_enabled'] ) {
|
| $has_conditional = true;
|
| if ( isset( $settings['conditional_logic'] ) ) {
|
| $conditional_json = wp_json_encode( $settings['conditional_logic'] );
|
| }
|
| }
|
|
|
|
|
| $outcomes = [];
|
| if ( method_exists( $step, 'get_outcomes' ) ) {
|
| $outcomes = $step->get_outcomes();
|
| }
|
| $outcomes_text = implode( ', ', array_keys( $outcomes ) );
|
|
|
|
|
| $next_steps = [];
|
| if ( method_exists( $step, 'get_next_step_id' ) ) {
|
| foreach ( $outcomes as $outcome_key => $outcome_label ) {
|
| $next_id = $step->get_next_step_id( $outcome_key );
|
| if ( $next_id ) {
|
| $next_steps[] = "{$outcome_key} → Step {$next_id}";
|
| }
|
| }
|
| }
|
| $next_steps_text = implode( "\n", $next_steps );
|
|
|
| $post_data = [
|
| 'post_type' => 'gf_workflow_step',
|
| 'post_status' => 'publish',
|
| 'post_title' => $step_name . ' (Form ' . $form_id . ')',
|
| 'post_name' => $slug,
|
| ];
|
|
|
| $meta_data = [
|
| 'form_id' => $form_id,
|
| 'form_title' => $form_title,
|
| 'step_id' => $step_id,
|
| 'step_name' => $step_name,
|
| 'step_order' => $order,
|
| 'step_type' => $step_type,
|
| 'is_active' => $step->is_active() ? 1 : 0,
|
| 'assignees' => $assignees_text,
|
| 'routing_logic' => $routing_text,
|
| 'is_scheduled' => $is_scheduled ? 1 : 0,
|
| 'schedule_type' => $schedule_type,
|
| 'schedule_value' => $schedule_value,
|
| 'has_expiration' => $has_expiration ? 1 : 0,
|
| 'expiration_value' => $expiration_value,
|
| 'settings_json' => $settings_json,
|
| 'has_conditional_logic' => $has_conditional ? 1 : 0,
|
| 'conditional_logic_json' => $conditional_json,
|
| 'step_outcomes' => $outcomes_text,
|
| 'next_steps' => $next_steps_text,
|
| ];
|
|
|
| if ( isset( $post_map[ $step_id ] ) ) {
|
|
|
| $post_data['ID'] = $post_map[ $step_id ];
|
| $post_id = wp_update_post( $post_data );
|
| error_log( "GF_FLOW_SYNC: Updated step {$step_id} (post {$post_id})" );
|
| } else {
|
|
|
| $post_id = wp_insert_post( $post_data );
|
| error_log( "GF_FLOW_SYNC: Created step {$step_id} (post {$post_id})" );
|
| }
|
|
|
|
|
| if ( $post_id && ! is_wp_error( $post_id ) ) {
|
| foreach ( $meta_data as $key => $value ) {
|
| update_post_meta( $post_id, $key, $value );
|
| }
|
| }
|
| }
|
|
|
|
|
| foreach ( $post_map as $step_id => $post_id ) {
|
| if ( ! in_array( $step_id, $processed_steps, true ) ) {
|
| wp_delete_post( $post_id, true );
|
| error_log( "GF_FLOW_SYNC: Deleted orphaned step {$step_id}" );
|
| }
|
| }
|
|
|
| error_log( "GF_FLOW_SYNC: Completed form {$form_id}" );
|
| return true;
|
| }
|
|
|
|
|
|
|
|
|
| function gf_flow_format_assignees( $assignees ) {
|
| if ( empty( $assignees ) || ! is_array( $assignees ) ) {
|
| return '';
|
| }
|
|
|
| $formatted = [];
|
|
|
| foreach ( $assignees as $assignee ) {
|
| if ( is_array( $assignee ) ) {
|
| $type = isset( $assignee['type'] ) ? $assignee['type'] : '';
|
| $value = isset( $assignee['value'] ) ? $assignee['value'] : '';
|
|
|
| if ( $type && $value ) {
|
| $formatted[] = "{$type}: {$value}";
|
| }
|
| } elseif ( is_string( $assignee ) ) {
|
| $formatted[] = $assignee;
|
| }
|
| }
|
|
|
| return implode( "\n", $formatted );
|
| }
|
|
|
|
|
| add_action( 'gform_after_save_form', function( $form, $is_new ) {
|
| error_log( 'GF_FLOW_SYNC: gform_after_save_form fired' );
|
|
|
|
|
| add_action( 'shutdown', function() use ( $form ) {
|
| gf_flow_mirror_steps_to_cpt( $form );
|
| } );
|
| }, 30, 2 );
|
|
|
|
|
| add_action( 'gform_before_delete_form', function( $form_id ) {
|
| error_log( "GF_FLOW_SYNC: Form {$form_id} is being deleted - removing all workflow steps" );
|
|
|
| $query = new WP_Query( [
|
| 'post_type' => 'gf_workflow_step',
|
| 'posts_per_page' => -1,
|
| 'fields' => 'ids',
|
| 'meta_query' => [
|
| [
|
| 'key' => 'form_id',
|
| 'value' => absint( $form_id ),
|
| ]
|
| ]
|
| ] );
|
|
|
| $deleted_count = 0;
|
| foreach ( $query->posts as $post_id ) {
|
| wp_delete_post( $post_id, true );
|
| $deleted_count++;
|
| }
|
|
|
| error_log( "GF_FLOW_SYNC: Deleted {$deleted_count} workflow steps from form {$form_id}" );
|
| }, 10, 1 );
|
|
|
|
|
| add_action( 'gravityflow_step_saved', function( $step_id, $form_id, $step_settings ) {
|
| error_log( "GF_FLOW_SYNC: Step {$step_id} saved in form {$form_id}" );
|
|
|
| $form = GFAPI::get_form( $form_id );
|
| if ( $form ) {
|
| gf_flow_mirror_steps_to_cpt( $form );
|
| }
|
| }, 10, 3 );
|
|
|
|
|
| add_action( 'load-edit.php', function() {
|
| if ( ! isset( $_GET['post_type'] ) || $_GET['post_type'] !== 'gf_workflow_step' ) {
|
| return;
|
| }
|
|
|
| if ( get_transient( 'gf_flow_sync_cleanup_done' ) ) {
|
| return;
|
| }
|
|
|
| gf_flow_cleanup_orphaned_steps();
|
| set_transient( 'gf_flow_sync_cleanup_done', true, HOUR_IN_SECONDS );
|
| } );
|
|
|
|
|
| add_action( 'init', function() {
|
| if ( ! class_exists( 'Gravity_Flow' ) ) {
|
| error_log( 'GF_FLOW_SYNC: Gravity Flow not detected!' );
|
| } else {
|
| error_log( 'GF_FLOW_SYNC: Gravity Flow detected' );
|
| }
|
| }, 999 );
|
| |
| |
Comments