__('Monday.com', 'advanced-form-integration'), 'tasks' => [ 'create_item' => __('Create Item', 'advanced-form-integration'), ] ]; return $actions; } add_filter('adfoin_settings_tabs', 'adfoin_monday_settings_tab', 10, 1); function adfoin_monday_settings_tab($providers) { $providers['monday'] = __('Monday.com', 'advanced-form-integration'); return $providers; } add_action('adfoin_settings_view', 'adfoin_monday_settings_view', 10, 1); function adfoin_monday_settings_view($current_tab) { if ($current_tab != 'monday') { return; } $title = __('Monday.com', 'advanced-form-integration'); $key = 'monday'; $arguments = wp_json_encode([ 'platform' => $key, 'fields' => [ [ 'key' => 'accessToken', 'label' => __('Access Token', 'advanced-form-integration'), 'hidden' => true ] ] ]); $instructions = __('
Go to Profile > Developers > My access tokens and copy the access token.
', 'advanced-form-integration'); echo adfoin_platform_settings_template($title, $key, $arguments, $instructions); } add_action('wp_ajax_adfoin_get_monday_credentials', 'adfoin_get_monday_credentials', 10, 0); function adfoin_get_monday_credentials() { adfoin_verify_nonce(); $all_credentials = adfoin_read_credentials('monday'); wp_send_json_success($all_credentials); } add_action('wp_ajax_adfoin_save_monday_credentials', 'adfoin_save_monday_credentials', 10, 0); function adfoin_save_monday_credentials() { adfoin_verify_nonce(); $platform = sanitize_text_field( wp_unslash( $_POST['platform'] ) ); if ('monday' == $platform) { $data = adfoin_array_map_recursive('sanitize_text_field', $_POST['data']); adfoin_save_credentials($platform, $data); } wp_send_json_success(); } function adfoin_monday_credentials_list() { $html = ''; $credentials = adfoin_read_credentials('monday'); foreach ($credentials as $option) { $html .= ''; } echo $html; } add_action('adfoin_action_fields', 'adfoin_monday_action_fields'); function adfoin_monday_action_fields() { ?> $column['id'], 'value' => $column['title']]; } } wp_send_json_success($columns_array); } add_action('wp_ajax_adfoin_get_monday_boards', 'adfoin_monday_get_boards'); function adfoin_monday_get_boards() { adfoin_verify_nonce(); $cred_id = sanitize_text_field( wp_unslash( $_POST['credId'] ) ); // Fetch only public and private boards (exclude shareable, docs, etc.) $query = 'query { boards (limit: 1000) { id name } }'; $response = adfoin_monday_request('boards', 'POST', $query, [], $cred_id); if (is_wp_error($response)) { wp_send_json_error( __( 'Error fetching boards', 'advanced-form-integration' ) ); return; } $body = json_decode(wp_remote_retrieve_body($response), true); if (empty($body) || isset($body['errors'])) { wp_send_json_error( __( 'Error in response body', 'advanced-form-integration' ) ); return; } $boards = $body['data']['boards'] ?? []; $boards_array = []; if ($boards) { foreach ($boards as $board) { $boards_array[$board['id']] = $board['name']; } } wp_send_json_success($boards_array); } add_action( 'wp_ajax_adfoin_get_monday_groups', 'adfoin_monday_get_groups' ); function adfoin_monday_get_groups() { adfoin_verify_nonce(); $cred_id = sanitize_text_field( wp_unslash( $_POST['credId'] ) ); $board_id = sanitize_text_field( wp_unslash( $_POST['boardId'] ) ); $query = 'query { boards (ids: ' . $board_id . ') { id groups { id title } } }'; $response = adfoin_monday_request('boards', 'POST', $query, [], $cred_id); if (is_wp_error($response)) { wp_send_json_error( __( 'Error fetching groups', 'advanced-form-integration' ) ); return; } $body = json_decode(wp_remote_retrieve_body($response), true); if (empty($body) || isset($body['errors'])) { wp_send_json_error( __( 'Error in response body', 'advanced-form-integration' ) ); return; } $groups = $body['data']['boards'][0]['groups'] ?? []; $groups_array = []; if ($groups) { foreach ($groups as $group) { $groups_array[$group['id']] = $group['title']; } } wp_send_json_success($groups_array); } function adfoin_monday_request($endpoint, $method = 'POST', $data ='', $record = [], $cred_id = '') { $credentials = adfoin_get_credentials_by_id('monday', $cred_id); $access_token = isset($credentials['accessToken']) ? $credentials['accessToken'] : ''; $base_url = 'https://api.monday.com/v2'; $args = [ 'timeout' => 30, 'method' => $method, 'headers' => [ 'Content-Type' => 'application/json', 'Authorization' => $access_token, ], ]; if (!empty($data)) { $args['body'] = wp_json_encode(['query' => $data]); } $response = wp_remote_request($base_url, $args); if( $record ) { adfoin_add_to_log($response, $base_url, $args, $record); } return $response; } add_action('adfoin_monday_job_queue', 'adfoin_monday_job_queue', 10, 1); function adfoin_monday_job_queue($data) { adfoin_monday_send_data($data['record'], $data['posted_data']); } if ( ! function_exists( 'adfoin_monday_prepare_column_values' ) ) { /** * Prepare Monday.com column values with proper structures per column type heuristics. * - Email columns: key starts with 'email' OR contains 'email' -> { email, text } * - Single select columns: key contains 'single_select' -> { label } * - Remove empty values * * @param array $data Raw field mapping data after removing meta keys. * @param array $posted_data Form submission data. * @return array */ function adfoin_monday_prepare_column_values( $data, $posted_data ) { $column_values = []; foreach ( $data as $key => $value ) { $parsed = adfoin_get_parsed_values( $value, $posted_data ); if ( $parsed === '' || $parsed === null ) { continue; // Skip empties } $lower_key = strtolower( $key ); // Email column (heuristic: id contains 'email') if ( strpos( $lower_key, 'email' ) !== false ) { $column_values[ $key ] = [ 'email' => $parsed, 'text' => $parsed, ]; continue; } // Single select column (heuristic) if ( strpos( $lower_key, 'single_select' ) !== false ) { $column_values[ $key ] = [ 'label' => $parsed ]; continue; } // Default: raw value $column_values[ $key ] = $parsed; } // Remove reserved / conflicting keys unset( $column_values['name'] ); return $column_values; } } if ( ! function_exists( 'adfoin_monday_graphql_string_literal' ) ) { /** * Escape a raw string for safe embedding inside a double-quoted GraphQL * string literal (GraphQL string escaping follows the same rules as JSON's). * * addslashes() is not safe here: besides backslashes and double quotes it * also escapes apostrophes as \', which is not a valid escape sequence in * GraphQL (or JSON) strings. Monday.com's parser then rejects the whole * mutation with a PARSING_ERROR whenever a field contains an apostrophe * (e.g. "He's incredibly fluffy."). * * @param string $value Raw value, or an already JSON-encoded string (e.g. column_values). * @return string Escaped text, WITHOUT surrounding quotes. */ function adfoin_monday_graphql_string_literal( $value ) { $value = (string) $value; $json = wp_json_encode( $value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); if ( ! is_string( $json ) || strlen( $json ) < 2 ) { // Encoding failed (e.g. irrecoverably malformed UTF-8). Fall back to // escaping just the two characters that must not appear raw inside a // GraphQL string literal, rather than dropping the value entirely. return str_replace( array( '\\', '"' ), array( '\\\\', '\\"' ), $value ); } // wp_json_encode() of a scalar string always wraps it in exactly one // pair of double quotes with no extra whitespace, so stripping the // first and last character leaves just the escaped inner content. return substr( $json, 1, -1 ); } } function adfoin_monday_send_data($record, $posted_data) { $record_data = json_decode($record['data'], true); if (adfoin_check_conditional_logic($record_data['action_data']['cl'] ?? [], $posted_data)) return; $data = $record_data['field_data']; $task = $record['task']; $cred_id = empty($data['credId']) ? '' : $data['credId']; if ($task == 'create_item') { $board_id = (int) $data['boardId']; $group_id = $data['groupId']; $item_name = empty($data['name']) ? '' : adfoin_get_parsed_values($data['name'], $posted_data); unset($data['credId'], $data['boardId'], $data['groupId']); $column_values_array = adfoin_monday_prepare_column_values( $data, $posted_data ); $column_values_json = wp_json_encode( $column_values_array, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ); // Escape for embedding as GraphQL string literals (same escaping rules // as JSON). See adfoin_monday_graphql_string_literal() docblock for why // addslashes() must not be used here. $column_values_literal = adfoin_monday_graphql_string_literal($column_values_json); $item_name_literal = adfoin_monday_graphql_string_literal($item_name); // Build GraphQL mutation without \n escape sequences (actual newlines fine) $query = "mutation CreateItem {\n" . " create_item (\n" . " board_id: $board_id,\n" . " group_id: \"$group_id\",\n" . " item_name: \"$item_name_literal\",\n" . " column_values: \"$column_values_literal\"\n" . " ) {\n" . " id\n" . " }\n" . "}"; // Allow overriding the generated mutation for advanced/edge cases. $query = apply_filters( 'adfoin_monday_create_item_mutation', $query, $data, $posted_data, $record ); adfoin_monday_request('create_item', 'POST', $query, $record, $cred_id); return; } }