'Planning Center',
'tasks' => array( 'create_person' => 'Add Contact (People, Workflow & Group)' )
);
return $actions;
}
add_filter( 'adfoin_settings_tabs', 'adfoin_planningcenter_settings_tab', 10, 1 );
function adfoin_planningcenter_settings_tab( $providers ) { $providers['planningcenter'] = 'Planning Center'; return $providers; }
add_action( 'adfoin_settings_view', 'adfoin_planningcenter_settings_view', 10, 1 );
function adfoin_planningcenter_settings_view( $current_tab ) {
if ( $current_tab !== 'planningcenter' ) return;
$arguments = wp_json_encode( array(
'platform' => 'planningcenter',
'fields' => array(
array( 'key' => 'appId', 'label' => __( 'Client ID', 'advanced-form-integration' ), 'hidden' => true ),
array( 'key' => 'secret', 'label' => __( 'Secret', 'advanced-form-integration' ), 'hidden' => true ),
),
) );
$instructions = __( 'In Planning Center, go to your Personal Access Tokens page', 'advanced-form-integration' ) . ' (api.planningcenteronline.com/oauth/applications). ' . __( 'Create a Personal Access Token and copy the Application ID and Secret.', 'advanced-form-integration' );
echo adfoin_platform_settings_template( __( 'Planning Center', 'advanced-form-integration' ), 'planningcenter', $arguments, $instructions );
}
add_action( 'adfoin_action_fields', 'adfoin_planningcenter_action_fields' );
function adfoin_planningcenter_action_fields() {
?>
'first_name', 'value' => 'First Name', 'description' => '' ),
array( 'key' => 'middle_name','value' => 'Middle Name','description' => '' ),
array( 'key' => 'last_name', 'value' => 'Last Name', 'description' => '' ),
array( 'key' => 'nickname', 'value' => 'Nickname', 'description' => '' ),
array( 'key' => 'email', 'value' => 'Email', 'description' => '' ),
array( 'key' => 'phone', 'value' => 'Phone', 'description' => '' ),
array( 'key' => 'birthdate', 'value' => 'Birthdate', 'description' => 'YYYY-MM-DD' ),
array( 'key' => 'gender', 'value' => 'Gender', 'description' => 'Male / Female' ),
array( 'key' => 'street', 'value' => 'Street', 'description' => '' ),
array( 'key' => 'city', 'value' => 'City', 'description' => '' ),
array( 'key' => 'state', 'value' => 'State', 'description' => '' ),
array( 'key' => 'zip', 'value' => 'Zip', 'description' => '' ),
array( 'key' => 'status', 'value' => 'Status', 'description' => 'active / inactive' ),
array( 'key' => 'membership', 'value' => 'Membership', 'description' => 'Member / Visitor / etc.' ),
);
wp_send_json_success( $fields );
}
function adfoin_planningcenter_credentials_list() {
foreach ( adfoin_read_credentials( 'planningcenter' ) as $option ) {
printf( '', esc_attr( $option['id'] ), esc_html( $option['title'] ) );
}
}
add_action( 'wp_ajax_adfoin_get_planningcenter_workflows', 'adfoin_get_planningcenter_workflows' );
function adfoin_get_planningcenter_workflows() {
adfoin_verify_nonce();
$cred_id = isset( $_POST['credId'] ) ? sanitize_text_field( wp_unslash( $_POST['credId'] ) ) : '';
wp_send_json_success( adfoin_planningcenter_list_all( 'people/v2/workflows', $cred_id ) );
}
add_action( 'wp_ajax_adfoin_get_planningcenter_groups', 'adfoin_get_planningcenter_groups' );
function adfoin_get_planningcenter_groups() {
adfoin_verify_nonce();
$cred_id = isset( $_POST['credId'] ) ? sanitize_text_field( wp_unslash( $_POST['credId'] ) ) : '';
wp_send_json_success( adfoin_planningcenter_list_all( 'groups/v2/groups', $cred_id ) );
}
function adfoin_planningcenter_list_all( $endpoint, $cred_id ) {
$all = array();
$offset = 0;
$per_page = 100;
$has_more = true;
while ( $has_more ) {
$response = adfoin_planningcenter_request( $endpoint . '?per_page=' . $per_page . '&offset=' . $offset . '&order=name', 'GET', array(), array(), $cred_id );
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) >= 400 ) break;
$body = json_decode( wp_remote_retrieve_body( $response ), true );
if ( empty( $body['data'] ) ) break;
foreach ( $body['data'] as $item ) {
$all[ $item['id'] ] = $item['attributes']['name'];
}
$offset += $per_page;
$has_more = count( $body['data'] ) === $per_page;
}
return $all;
}
function adfoin_planningcenter_request( $endpoint, $method = 'POST', $data = array(), $record = array(), $cred_id = '' ) {
$credentials = adfoin_get_credentials_by_id( 'planningcenter', $cred_id );
$app_id = isset( $credentials['appId'] ) ? $credentials['appId'] : '';
$secret = isset( $credentials['secret'] ) ? $credentials['secret'] : '';
if ( ! $app_id || ! $secret ) return;
$url = 'https://api.planningcenteronline.com/' . ltrim( $endpoint, '/' );
$args = array(
'timeout' => 30,
'method' => $method,
'headers' => array(
'Authorization' => 'Basic ' . base64_encode( $app_id . ':' . $secret ),
'Content-Type' => 'application/json',
'Accept' => 'application/json',
),
);
if ( in_array( $method, array( 'POST', 'PUT', 'PATCH' ), true ) ) $args['body'] = wp_json_encode( $data );
$response = wp_remote_request( $url, $args );
if ( $record ) adfoin_add_to_log( $response, $url, $args, $record );
return $response;
}
/**
* Looks up an existing person by exact email match (Planning Center's
* search_name_or_email is a fuzzy match, so results are verified client-side
* against the included Email resources). Returns '' when nothing matches so
* callers fall back to creating a new person.
*/
function adfoin_planningcenter_find_person( $email, $record, $cred_id ) {
$empty = array( 'id' => '', 'email_id' => '', 'phone_id' => '', 'address_id' => '' );
if ( ! $email ) return $empty;
$endpoint = 'people/v2/people?where[search_name_or_email]=' . rawurlencode( $email ) . '&include=emails,phone_numbers,addresses';
$response = adfoin_planningcenter_request( $endpoint, 'GET', array(), $record, $cred_id );
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) >= 400 ) return $empty;
$body = json_decode( wp_remote_retrieve_body( $response ), true );
if ( empty( $body['data'] ) ) return $empty;
$included = array();
foreach ( (array) ( isset( $body['included'] ) ? $body['included'] : array() ) as $inc ) {
$included[ $inc['type'] . ':' . $inc['id'] ] = $inc;
}
foreach ( $body['data'] as $person ) {
$email_refs = isset( $person['relationships']['emails']['data'] ) ? $person['relationships']['emails']['data'] : array();
$matched_email_id = '';
foreach ( $email_refs as $ref ) {
$inc = isset( $included[ 'Email:' . $ref['id'] ] ) ? $included[ 'Email:' . $ref['id'] ] : null;
if ( $inc && isset( $inc['attributes']['address'] ) && strtolower( $inc['attributes']['address'] ) === strtolower( $email ) ) {
$matched_email_id = $ref['id'];
break;
}
}
if ( ! $matched_email_id ) continue;
$phone_id = '';
foreach ( (array) ( isset( $person['relationships']['phone_numbers']['data'] ) ? $person['relationships']['phone_numbers']['data'] : array() ) as $ref ) {
$inc = isset( $included[ 'PhoneNumber:' . $ref['id'] ] ) ? $included[ 'PhoneNumber:' . $ref['id'] ] : null;
if ( $inc && ! empty( $inc['attributes']['primary'] ) ) { $phone_id = $ref['id']; break; }
if ( ! $phone_id ) $phone_id = $ref['id'];
}
$address_id = '';
foreach ( (array) ( isset( $person['relationships']['addresses']['data'] ) ? $person['relationships']['addresses']['data'] : array() ) as $ref ) {
$inc = isset( $included[ 'Address:' . $ref['id'] ] ) ? $included[ 'Address:' . $ref['id'] ] : null;
if ( $inc && ! empty( $inc['attributes']['primary'] ) ) { $address_id = $ref['id']; break; }
if ( ! $address_id ) $address_id = $ref['id'];
}
return array( 'id' => $person['id'], 'email_id' => $matched_email_id, 'phone_id' => $phone_id, 'address_id' => $address_id );
}
return $empty;
}
/**
* Creates a new person, or updates the existing one matched by email so
* repeat submissions don't create duplicate People records.
*/
function adfoin_planningcenter_upsert_person( $fields, $record, $cred_id ) {
$attrs = array();
foreach ( array( 'first_name', 'middle_name', 'last_name', 'nickname', 'birthdate', 'gender', 'status', 'membership', 'grade' ) as $k ) {
if ( ! empty( $fields[ $k ] ) ) $attrs[ $k ] = $fields[ $k ];
}
$existing = adfoin_planningcenter_find_person( isset( $fields['email'] ) ? $fields['email'] : '', $record, $cred_id );
$person_id = $existing['id'];
if ( $person_id ) {
$body = array( 'data' => array( 'type' => 'Person', 'id' => $person_id, 'attributes' => $attrs ) );
$response = adfoin_planningcenter_request( "people/v2/people/{$person_id}", 'PATCH', $body, $record, $cred_id );
} else {
$body = array( 'data' => array( 'type' => 'Person', 'attributes' => $attrs ) );
$response = adfoin_planningcenter_request( 'people/v2/people', 'POST', $body, $record, $cred_id );
if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) >= 400 ) return array( 'response' => $response, 'person_id' => '' );
$resp_body = json_decode( wp_remote_retrieve_body( $response ), true );
$person_id = isset( $resp_body['data']['id'] ) ? $resp_body['data']['id'] : '';
}
if ( ! $person_id ) return array( 'response' => $response, 'person_id' => '' );
if ( ! empty( $fields['email'] ) && ! $existing['email_id'] ) {
adfoin_planningcenter_request( "people/v2/people/{$person_id}/emails", 'POST', array(
// Planning Center rejects Email records with a 422 ("can't be
// blank") unless `location` is set; the API has no default.
'data' => array( 'type' => 'Email', 'attributes' => array( 'address' => $fields['email'], 'location' => 'Home', 'primary' => true ) ),
), $record, $cred_id );
}
if ( ! empty( $fields['phone'] ) ) {
$phone_body = array( 'data' => array( 'type' => 'PhoneNumber', 'attributes' => array( 'number' => $fields['phone'], 'location' => 'Home', 'primary' => true ) ) );
if ( $existing['phone_id'] ) {
$phone_body['data']['id'] = $existing['phone_id'];
adfoin_planningcenter_request( "people/v2/people/{$person_id}/phone_numbers/{$existing['phone_id']}", 'PATCH', $phone_body, $record, $cred_id );
} else {
adfoin_planningcenter_request( "people/v2/people/{$person_id}/phone_numbers", 'POST', $phone_body, $record, $cred_id );
}
}
if ( ! empty( $fields['street'] ) || ! empty( $fields['city'] ) || ! empty( $fields['zip'] ) ) {
$addr_attrs = array( 'location' => 'Home' );
foreach ( array( 'street' => 'street_line_1', 'city' => 'city', 'state' => 'state', 'zip' => 'zip' ) as $local => $remote ) {
if ( ! empty( $fields[ $local ] ) ) $addr_attrs[ $remote ] = $fields[ $local ];
}
$addr_body = array( 'data' => array( 'type' => 'Address', 'attributes' => $addr_attrs ) );
if ( $existing['address_id'] ) {
$addr_body['data']['id'] = $existing['address_id'];
adfoin_planningcenter_request( "people/v2/people/{$person_id}/addresses/{$existing['address_id']}", 'PATCH', $addr_body, $record, $cred_id );
} else {
adfoin_planningcenter_request( "people/v2/people/{$person_id}/addresses", 'POST', $addr_body, $record, $cred_id );
}
}
return array( 'response' => $response, 'person_id' => $person_id );
}
function adfoin_planningcenter_add_to_workflow( $person_id, $workflow_id, $record, $cred_id ) {
if ( ! $person_id || ! $workflow_id ) return;
$body = array(
'data' => array(
'type' => 'WorkflowCard',
'relationships' => array(
'person' => array( 'data' => array( 'type' => 'Person', 'id' => $person_id ) ),
),
),
);
adfoin_planningcenter_request( "people/v2/workflows/{$workflow_id}/cards", 'POST', $body, $record, $cred_id );
}
function adfoin_planningcenter_add_to_group( $person_id, $group_id, $record, $cred_id ) {
if ( ! $person_id || ! $group_id ) return;
$body = array(
'data' => array(
'type' => 'Membership',
'attributes' => array( 'role' => 'member' ),
'relationships' => array(
'person' => array( 'data' => array( 'type' => 'Person', 'id' => $person_id ) ),
),
),
);
adfoin_planningcenter_request( "groups/v2/groups/{$group_id}/memberships", 'POST', $body, $record, $cred_id );
}
add_action( 'adfoin_planningcenter_job_queue', 'adfoin_planningcenter_job_queue', 10, 1 );
function adfoin_planningcenter_job_queue( $data ) {
adfoin_planningcenter_send_data( $data['record'], $data['posted_data'] );
}
function adfoin_planningcenter_send_data( $record, $posted_data ) {
$record_data = json_decode( $record['data'], true );
if ( isset( $record_data['action_data']['cl'] ) && adfoin_check_conditional_logic( $record_data['action_data']['cl'], $posted_data ) ) return;
$data = isset( $record_data['field_data'] ) ? $record_data['field_data'] : $record_data;
$cred_id = isset( $data['credId'] ) ? $data['credId'] : ( isset( $record['cred_id'] ) ? $record['cred_id'] : '' );
if ( $record['task'] !== 'create_person' ) return;
$fields = array();
foreach ( $data as $k => $v ) {
if ( in_array( $k, array( 'credId', 'workflowId', 'groupId' ), true ) ) continue;
$parsed = adfoin_get_parsed_values( $v, $posted_data );
if ( $parsed !== '' && $parsed !== null ) $fields[ $k ] = $parsed;
}
$result = adfoin_planningcenter_upsert_person( $fields, $record, $cred_id );
$person_id = $result['person_id'];
if ( ! $person_id ) return;
if ( ! empty( $data['workflowId'] ) ) {
adfoin_planningcenter_add_to_workflow( $person_id, $data['workflowId'], $record, $cred_id );
}
if ( ! empty( $data['groupId'] ) ) {
adfoin_planningcenter_add_to_group( $person_id, $data['groupId'], $record, $cred_id );
}
}