__( 'Clickup', 'advanced-form-integration' ),
'tasks' => array(
'create_task' => __( 'Create Task', 'advanced-form-integration' )
)
);
return $actions;
}
add_filter( 'adfoin_settings_tabs', 'adfoin_clickup_settings_tab', 10, 1 );
function adfoin_clickup_settings_tab( $providers ) {
$providers['clickup'] = __( 'Clickup', 'advanced-form-integration' );
return $providers;
}
add_action( 'adfoin_settings_view', 'adfoin_clickup_settings_view', 10, 1 );
function adfoin_clickup_settings_view( $current_tab ) {
if( $current_tab != 'clickup' ) {
return;
}
if ( ! class_exists( 'ADFOIN_Account_Manager' ) ) {
require_once plugin_dir_path( __FILE__ ) . '../../includes/class-adfoin-account-manager.php';
}
$fields = array(
array(
'name' => 'apiToken',
'label' => __( 'API Token', 'advanced-form-integration' ),
'type' => 'text',
'required' => true,
'mask' => true,
'placeholder' => __( 'Enter your API Token', 'advanced-form-integration' ),
'show_in_table' => true
)
);
$instructions = sprintf(
'
%s
',
__('Go to My Settings > Apps. Generate API Token and copy it.', 'advanced-form-integration')
);
ADFOIN_Account_Manager::render_settings_view( 'clickup', __( 'ClickUp', 'advanced-form-integration' ), $fields, $instructions );
}
add_action( 'wp_ajax_adfoin_get_clickup_credentials', 'adfoin_get_clickup_credentials', 10, 0 );
/*
* Get ClickUp credentials
*/
function adfoin_get_clickup_credentials() {
if ( ! class_exists( 'ADFOIN_Account_Manager' ) ) {
require_once plugin_dir_path( __FILE__ ) . '../../includes/class-adfoin-account-manager.php';
}
ADFOIN_Account_Manager::ajax_get_credentials_list( 'clickup' );
}
add_action( 'wp_ajax_adfoin_save_clickup_credentials', 'adfoin_save_clickup_credentials', 10, 0 );
/*
* Save ClickUp credentials
*/
function adfoin_save_clickup_credentials() {
if ( ! class_exists( 'ADFOIN_Account_Manager' ) ) {
require_once plugin_dir_path( __FILE__ ) . '../../includes/class-adfoin-account-manager.php';
}
ADFOIN_Account_Manager::ajax_save_credentials( 'clickup', array( 'apiToken' ) );
}
/*
* ClickUp Credentials List
*/
function adfoin_clickup_get_credentials_list() {
$credentials = adfoin_read_credentials( 'clickup' );
foreach ($credentials as $option) {
printf('', esc_attr($option['id']), esc_html($option['title']));
}
}
// Legacy single-account import: surfaces old `adfoin_clickup_*` options
// as a Legacy Account record when the new credentials store is empty.
add_action( 'plugins_loaded', function() {
if ( class_exists( 'ADFOIN_Account_Manager' ) ) {
ADFOIN_Account_Manager::register_legacy_option_importer( 'clickup', array(
'apiToken' => 'adfoin_clickup_api_token',
) );
}
}, 20 );
// Deprecated - kept for backward compatibility
add_action( 'admin_post_adfoin_save_clickup_api_token', 'adfoin_save_clickup_api_token', 10, 0 );
function adfoin_save_clickup_api_token() {
// Security Check
// Authorization check
adfoin_require_manage_options();
if (! wp_verify_nonce( $_POST['_nonce'], 'adfoin_clickup_settings' ) ) {
die( __( 'Security check Failed', 'advanced-form-integration' ) );
}
$api_token = sanitize_text_field( wp_unslash( $_POST["adfoin_clickup_api_token"] ) );
// Save tokens
update_option( "adfoin_clickup_api_token", $api_token );
advanced_form_integration_redirect( "admin.php?page=advanced-form-integration-settings&tab=clickup" );
}
add_action( 'adfoin_action_fields', 'adfoin_clickup_action_fields' );
function adfoin_clickup_action_fields() {
?>
ClickUp.', 'advanced-form-integration' ) );
if ( $record ) {
adfoin_add_to_log( $error, 'https://api.clickup.com/api/v2/' . $endpoint, array( 'method' => $method, 'body' => $data ), $record );
}
return $error;
}
$base_url = 'https://api.clickup.com/api/v2/';
$url = $base_url . $endpoint;
$args = array(
'timeout' => 30,
'method' => $method,
'headers' => array(
'Content-Type' => 'application/json',
'Authorization' => $api_token,
),
);
if ('POST' == $method || 'PUT' == $method) {
$args['body'] = wp_json_encode($data);
}
$response = wp_remote_request($url, $args);
// ClickUp returns HTTP 429 with X-RateLimit-Reset (Unix timestamp) when
// the per-token rate limit is exceeded. Retry once after waiting for the
// reset (clamped to <=30s). Falls back to Retry-After if present.
if ( 429 == (int) wp_remote_retrieve_response_code( $response ) ) {
$reset = wp_remote_retrieve_header( $response, 'x-ratelimit-reset' );
$wait = 2;
if ( $reset && is_numeric( $reset ) ) {
$wait = (int) $reset - time();
} else {
$retry_after = wp_remote_retrieve_header( $response, 'retry-after' );
if ( $retry_after && is_numeric( $retry_after ) ) {
$wait = (int) $retry_after;
}
}
if ( $wait < 1 ) {
$wait = 1;
}
if ( $wait > 30 ) {
$wait = 30;
}
sleep( $wait );
$response = wp_remote_request( $url, $args );
}
if ($record) {
// Notes (e.g. an ignored date) are shown in the log message of a successful request.
if ( $log_note && ! is_wp_error( $response ) && (int) wp_remote_retrieve_response_code( $response ) < 300 ) {
$response['response']['message'] = 'OK (' . $log_note . ')';
}
adfoin_add_to_log( $response, $url, adfoin_clickup_args_for_log( $args ), $record );
}
return $response;
}
function adfoin_clickup_args_for_log( $args ) {
if ( isset( $args['headers']['Authorization'] ) ) {
$args['headers']['Authorization'] = substr( (string) $args['headers']['Authorization'], 0, 6 ) . '****';
}
return $args;
}
function adfoin_clickup_log_skipped( $record, $message ) {
adfoin_add_to_log( new WP_Error( 'clickup_not_sent', $message ), 'https://api.clickup.com/api/v2/', array( 'method' => 'POST' ), $record );
}
/*
* Converts a form value to a ClickUp timestamp (ms). Accepts anything
* strtotime() reads, in the site timezone, or a Unix timestamp.
* Returns array( ms, has_time ) or null when the value can't be read.
*/
function adfoin_clickup_parse_date( $value ) {
$value = trim( (string) $value );
if ( '' === $value ) {
return null;
}
if ( ctype_digit( $value ) && strlen( $value ) >= 10 ) {
$ms = strlen( $value ) >= 13 ? (int) $value : (int) $value * 1000;
return array( $ms, true );
}
$date = date_create( $value, wp_timezone() );
if ( ! $date ) {
return null;
}
return array( (int) $date->format( 'U' ) * 1000, (bool) preg_match( '/\d{1,2}:\d{2}/', $value ) );
}
/*
* Sets "_date" (and "_date_time" when a time was given) on
* $target. A value that can't be read is skipped and noted in $notes.
*/
function adfoin_clickup_set_date( &$target, $prefix, $value, $label, &$notes ) {
if ( '' === trim( (string) $value ) ) {
return;
}
$parsed = adfoin_clickup_parse_date( $value );
if ( ! $parsed ) {
$notes[] = sprintf( __( '%1$s "%2$s" not recognised, left empty', 'advanced-form-integration' ), $label, $value );
return;
}
$target[ $prefix . '_date' ] = $parsed[0];
if ( $parsed[1] ) {
$target[ $prefix . '_date_time' ] = true;
}
}
/*
* Splits a comma-separated value, trimming spaces and dropping empties.
*/
function adfoin_clickup_split( $value ) {
return array_values( array_filter( array_map( 'trim', explode( ',', (string) $value ) ), 'strlen' ) );
}
add_action( 'wp_ajax_adfoin_get_clickup_workspaces', 'adfoin_get_clickup_workspaces', 10, 0 );
/*
* Get Clickup Workspaces
*/
function adfoin_get_clickup_workspaces() {
// Security Check (capability + nonce, with isset/unslash/sanitize)
adfoin_verify_nonce();
$cred_id = isset( $_POST['credId'] ) ? sanitize_text_field( wp_unslash( $_POST['credId'] ) ) : '';
$return = adfoin_clickup_request( 'team', 'GET', array(), array(), $cred_id );
if( !is_wp_error( $return ) ) {
$body = json_decode( wp_remote_retrieve_body( $return ) );
if( ! isset( $body->teams ) || ! is_array( $body->teams ) ) {
wp_send_json_error();
}
$workspaces = wp_list_pluck( $body->teams, 'name', 'id' );
wp_send_json_success( $workspaces );
} else {
wp_send_json_error();
}
}
add_action( 'wp_ajax_adfoin_get_clickup_spaces', 'adfoin_get_clickup_spaces', 20, 0 );
/*
* Get Clickup spaces
*/
function adfoin_get_clickup_spaces() {
// Security Check (capability + nonce, with isset/unslash/sanitize)
adfoin_verify_nonce();
$cred_id = isset( $_POST['credId'] ) ? sanitize_text_field( wp_unslash( $_POST['credId'] ) ) : '';
$workspace_id = isset( $_POST['workspaceId'] ) ? sanitize_text_field( wp_unslash( $_POST['workspaceId'] ) ) : '';
$return = adfoin_clickup_request( 'team/' . $workspace_id . '/space', 'GET', array(), array(), $cred_id );
if( !is_wp_error( $return ) ) {
$body = json_decode( wp_remote_retrieve_body( $return ) );
if( ! isset( $body->spaces ) || ! is_array( $body->spaces ) ) {
wp_send_json_error();
}
$spaces = wp_list_pluck( $body->spaces, 'name', 'id' );
wp_send_json_success( $spaces );
} else {
wp_send_json_error();
}
}
add_action( 'wp_ajax_adfoin_get_clickup_folders', 'adfoin_get_clickup_folders', 20, 0 );
/*
* Get Clickup folders
*/
function adfoin_get_clickup_folders() {
// Security Check (capability + nonce, with isset/unslash/sanitize)
adfoin_verify_nonce();
$cred_id = isset( $_POST['credId'] ) ? sanitize_text_field( wp_unslash( $_POST['credId'] ) ) : '';
$space_id = isset( $_POST['spaceId'] ) ? sanitize_text_field( wp_unslash( $_POST['spaceId'] ) ) : '';
$return = adfoin_clickup_request( 'space/' . $space_id . '/folder', 'GET', array(), array(), $cred_id );
if( !is_wp_error( $return ) ) {
$body = json_decode( wp_remote_retrieve_body( $return ) );
if( ! isset( $body->folders ) || ! is_array( $body->folders ) ) {
wp_send_json_error();
}
$folders = wp_list_pluck( $body->folders, 'name', 'id' );
wp_send_json_success( $folders );
} else {
wp_send_json_error();
}
}
add_action( 'wp_ajax_adfoin_get_clickup_lists', 'adfoin_get_clickup_lists', 20, 0 );
/*
* Get Clickup lists
*/
function adfoin_get_clickup_lists() {
// Security Check (capability + nonce, with isset/unslash/sanitize)
adfoin_verify_nonce();
$cred_id = isset( $_POST['credId'] ) ? sanitize_text_field( wp_unslash( $_POST['credId'] ) ) : '';
$space_id = isset( $_POST['spaceId'] ) && $_POST['spaceId'] ? sanitize_text_field( wp_unslash( $_POST['spaceId'] ) ) : '';
$folder_id = isset( $_POST['folderId'] ) && $_POST['folderId'] ? sanitize_text_field( wp_unslash( $_POST['folderId'] ) ) : '';
// Folder takes precedence — both shouldn't be set, but if neither is, bail
// cleanly instead of running is_wp_error() on an uninitialized variable.
$return = null;
if( $folder_id ) {
$return = adfoin_clickup_request( 'folder/' . $folder_id . '/list', 'GET', array(), array(), $cred_id );
} elseif( $space_id ) {
$return = adfoin_clickup_request( 'space/' . $space_id . '/list', 'GET', array(), array(), $cred_id );
}
if( null === $return ) {
wp_send_json_error();
}
if( !is_wp_error( $return ) ) {
$body = json_decode( wp_remote_retrieve_body( $return ) );
if( ! isset( $body->lists ) || ! is_array( $body->lists ) ) {
wp_send_json_error();
}
$lists = wp_list_pluck( $body->lists, 'name', 'id' );
wp_send_json_success( $lists );
} else {
wp_send_json_error();
}
}
add_action( 'adfoin_clickup_job_queue', 'adfoin_clickup_job_queue', 10, 1 );
function adfoin_clickup_job_queue( $data ) {
adfoin_clickup_send_data( $data['record'], $data['posted_data'] );
}
/*
* Handles sending data to ClickUp API
*/
function adfoin_clickup_send_data( $record, $posted_data ) {
$record_data = json_decode( $record['data'], true );
if ( adfoin_check_conditional_logic( $record_data['action_data']['cl'] ?? array(), $posted_data ) ) {
return;
}
$data = $record_data['field_data'];
$task = $record['task'];
$cred_id = isset( $data['credId'] ) ? $data['credId'] : '';
$workspace_id = empty( $data['workspaceId'] ) ? '' : $data['workspaceId'];
$space_id = empty( $data['spaceId'] ) ? '' : $data['spaceId'];
$folder_id = empty( $data['folderId'] ) ? '' : $data['folderId'];
$list_id = empty( $data['listId'] ) ? '' : $data['listId'];
$name = empty( $data['name'] ) ? '' : adfoin_get_parsed_values( $data['name'], $posted_data );
$description = empty( $data['description'] ) ? '' : adfoin_get_parsed_values( $data['description'], $posted_data );
$start_date = empty( $data['startDate'] ) ? '' : adfoin_get_parsed_values( $data['startDate'], $posted_data );
$due_date = empty( $data['dueDate'] ) ? '' : adfoin_get_parsed_values( $data['dueDate'], $posted_data );
$due_on_x = empty( $data["dueOnX"] ) ? "" : adfoin_get_parsed_values( $data["dueOnX"], $posted_data );
$priority_id = empty( $data['priorityId'] ) ? '' : adfoin_get_parsed_values( $data['priorityId'], $posted_data );
$assignees = empty( $data['assignees'] ) ? '' : adfoin_get_parsed_values( $data['assignees'], $posted_data );
// Backward compatibility: if no cred_id, use first available credential
if( empty( $cred_id ) ) {
$all_credentials = adfoin_read_credentials( 'clickup' );
if( !empty( $all_credentials ) ) {
$cred_id = $all_credentials[0]['id'];
}
}
if( $task == 'create_task' ) {
if ( ! $list_id ) {
adfoin_clickup_log_skipped( $record, __( 'Not sent: no ClickUp list is selected in the integration.', 'advanced-form-integration' ) );
return;
}
$notes = array();
$task_data = array(
'name' => $name,
'description' => $description,
);
adfoin_clickup_set_date( $task_data, 'start', $start_date, __( 'Start Date', 'advanced-form-integration' ), $notes );
adfoin_clickup_set_date( $task_data, 'due', $due_date, __( 'Due Date', 'advanced-form-integration' ), $notes );
if( $due_on_x ) {
$after_days = (int) $due_on_x;
if( $after_days ) {
$task_data['due_date'] = (int) date_create( '+' . $after_days . ' days', wp_timezone() )->format( 'U' ) * 1000;
unset( $task_data['due_date_time'] );
}
}
if( $priority_id ) {
$task_data['priority'] = (int) $priority_id;
}
if( $assignees ) {
$assignee_ids = adfoin_clickup_assignees( $workspace_id, $assignees, $cred_id, $notes );
if( $assignee_ids ) {
$task_data['assignees'] = $assignee_ids;
}
}
$response = adfoin_clickup_request( 'list/' . $list_id . '/task', 'POST', $task_data, $record, $cred_id, implode( '; ', $notes ) );
}
return;
}
function adfoin_get_clickup_assignee_ids( $workspace_id, $emails, $cred_id = '', &$not_found = null ) {
$assignee_ids = array();
$wanted = array_map( 'strtolower', adfoin_clickup_split( $emails ) );
$not_found = $wanted;
if ( ! $wanted || ! $workspace_id ) {
return $assignee_ids;
}
$space_data = adfoin_clickup_request( 'team/' . $workspace_id, 'GET', array(), array(), $cred_id );
$body = json_decode( wp_remote_retrieve_body( $space_data ), true );
foreach ( (array) ( $body['team']['members'] ?? array() ) as $member ) {
$email = strtolower( trim( $member['user']['email'] ?? '' ) );
if ( $email && in_array( $email, $wanted, true ) ) {
$assignee_ids[] = $member['user']['id'];
$not_found = array_diff( $not_found, array( $email ) );
}
}
$not_found = array_values( $not_found );
return $assignee_ids;
}
/*
* Resolves assignee emails and notes the ones that aren't workspace members.
*/
function adfoin_clickup_assignees( $workspace_id, $emails, $cred_id, &$notes ) {
$not_found = array();
$ids = adfoin_get_clickup_assignee_ids( $workspace_id, $emails, $cred_id, $not_found );
if ( $not_found ) {
$notes[] = sprintf( __( 'not a workspace member, not assigned: %s', 'advanced-form-integration' ), implode( ', ', $not_found ) );
}
return $ids;
}