PluginProbe ʕ •ᴥ•ʔ
JetFormBuilder — Dynamic Blocks Form Builder / 3.6.4.1
JetFormBuilder — Dynamic Blocks Form Builder v3.6.4.1
3.6.4.1 3.6.4 3.6.3.1 3.6.3 3.6.2.2 3.6.2.1 3.6.2 3.6.1.1 3.6.1 3.6.0.1 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.4.1 1.4.2 1.4.3 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.1.0 2.1.1 2.1.10 2.1.11 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.1.9 3.0.0 3.0.0.1 3.0.0.2 3.0.0.3 3.0.1 3.0.1.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.0.1 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9 3.2.0 3.2.1 3.2.2 3.2.3 3.3.0 3.3.1 3.3.2 3.3.3 3.3.3.1 3.3.4 3.3.4.1 3.3.4.2 3.4.0 3.4.1 3.4.2 3.4.3 3.4.4 3.4.5 3.4.5.1 3.4.5.2 3.4.6 3.4.7 3.4.7.1 3.5.0 3.5.1 3.5.1.1 3.5.1.2 3.5.2 3.5.2.1 3.5.3 3.5.4 3.5.5 3.5.6 3.5.6.1 3.5.6.2 3.5.6.3 3.6.0
jetformbuilder / includes / generators / get-from-rest-api.php
jetformbuilder / includes / generators Last commit date
base-v2.php 3 months ago base.php 2 years ago get-from-db.php 3 weeks ago get-from-rest-api.php 3 months ago get-from-users.php 3 weeks ago get-related-posts.php 3 months ago legacy-parser.php 3 months ago num-range-manual.php 3 months ago num-range.php 3 months ago registry.php 3 weeks ago
get-from-rest-api.php
297 lines
1 <?php
2 /**
3 * REST API Generator.
4 *
5 * Generates options by fetching data from an external REST API endpoint.
6 * Supports dot-notation paths, multiple request headers, and URL placeholders
7 * for auto-update (e.g. https://api.example.com/items?category={category_field}).
8 *
9 * @package Jet_Form_Builder\Generators
10 */
11
12 namespace Jet_Form_Builder\Generators;
13
14 // If this file is called directly, abort.
15 if ( ! defined( 'WPINC' ) ) {
16 die;
17 }
18
19 /**
20 * Get_From_Rest_Api class.
21 *
22 * Generates options by fetching from an external REST API.
23 */
24 class Get_From_Rest_Api extends Base_V2 {
25
26 /**
27 * Returns generator ID.
28 *
29 * @return string
30 */
31 public function get_id() {
32 return 'get_from_rest_api';
33 }
34
35 /**
36 * Returns generator name.
37 *
38 * @return string
39 */
40 public function get_name() {
41 return __( 'Get values list from REST API', 'jet-form-builder' );
42 }
43
44 /**
45 * Returns structured settings schema.
46 *
47 * @return array
48 */
49 public function get_settings_schema(): array {
50 return array(
51 'endpoint_url' => array(
52 'type' => 'string',
53 'default' => '',
54 'label' => __( 'API Endpoint URL', 'jet-form-builder' ),
55 'control' => 'text',
56 'placeholder' => 'https://api.example.com/items',
57 'required' => true,
58 'help' => __( 'URL must return a JSON array or object. For auto-update, use {field_name} placeholders to inject values from watched fields.', 'jet-form-builder' ),
59 ),
60 'value_path' => array(
61 'type' => 'string',
62 'default' => 'id',
63 'label' => __( 'Option Value', 'jet-form-builder' ),
64 'control' => 'text',
65 'placeholder' => 'id',
66 'help' => __( 'JSON key for option value. Supports dot notation: data.id', 'jet-form-builder' ),
67 ),
68 'label_path' => array(
69 'type' => 'string',
70 'default' => 'name',
71 'label' => __( 'Option Label', 'jet-form-builder' ),
72 'control' => 'text',
73 'placeholder' => 'name',
74 'help' => __( 'JSON key for option label. Supports dot notation: data.name', 'jet-form-builder' ),
75 ),
76 'items_path' => array(
77 'type' => 'string',
78 'default' => '',
79 'label' => __( 'Items Path (optional)', 'jet-form-builder' ),
80 'control' => 'text',
81 'placeholder' => 'results',
82 'help' => __( 'Path to the array inside the JSON response. Leave empty if the root is already an array.', 'jet-form-builder' ),
83 ),
84 'request_headers' => array(
85 'type' => 'string',
86 'default' => '',
87 'label' => __( 'Request Headers (optional)', 'jet-form-builder' ),
88 'control' => 'textarea',
89 'placeholder' => "Authorization: Bearer your-token\nX-API-Key: abc123",
90 'help' => __( 'One header per line in format "Header-Name: value".', 'jet-form-builder' ),
91 ),
92 'request_timeout' => array(
93 'type' => 'number',
94 'default' => 10,
95 'label' => __( 'Request Timeout (seconds)', 'jet-form-builder' ),
96 'control' => 'number',
97 'min' => 1,
98 'max' => 30,
99 ),
100 );
101 }
102
103 /**
104 * Whether this generator supports auto-update.
105 *
106 * @return bool
107 */
108 public function supports_auto_update(): bool {
109 return true;
110 }
111
112 /**
113 * Returns context field descriptions for auto-update.
114 *
115 * @return array
116 */
117 public function get_auto_update_context_fields(): array {
118 return array(
119 array(
120 'description' => __( 'Use {field_name} placeholders in the Endpoint URL below to inject values from the selected Trigger Fields.', 'jet-form-builder' ),
121 'example' => 'https://api.example.com/products?category={category_field}',
122 ),
123 );
124 }
125
126 /**
127 * Returns the auto-update value type supported by this generator.
128 *
129 * @return string
130 */
131 public function get_auto_update_value_type(): string {
132 return 'scalar';
133 }
134
135 /**
136 * Empty trigger should use the static endpoint configuration.
137 *
138 * @return string
139 */
140 public function get_auto_update_empty_context_policy(): string {
141 return 'fallback_to_static';
142 }
143
144 /**
145 * Generate with context — replaces {field_name} placeholders in the URL.
146 *
147 * @param array $settings Parsed settings.
148 * @param array $context ['field_name' => 'value'] from listened fields.
149 *
150 * @return array
151 */
152 public function generate_with_context( array $settings, array $context = array() ): array {
153 if ( ! empty( $context ) && ! empty( $settings['endpoint_url'] ) ) {
154 foreach ( $context as $field_name => $field_value ) {
155 $settings['endpoint_url'] = str_replace(
156 '{' . $field_name . '}',
157 rawurlencode( (string) $field_value ),
158 $settings['endpoint_url']
159 );
160 }
161 }
162
163 return $this->generate( $settings );
164 }
165
166 /**
167 * Generates options by fetching from the REST API endpoint.
168 *
169 * @param array $args Settings from schema.
170 *
171 * @return array
172 */
173 public function generate( $args ) {
174 $endpoint = trim( $args['endpoint_url'] ?? '' );
175
176 if ( empty( $endpoint ) ) {
177 return array();
178 }
179
180 // Bail if URL still contains unresolved {placeholder} tokens.
181 if ( preg_match( '/\{[^}]+\}/', $endpoint ) ) {
182 return array();
183 }
184
185 if ( ! wp_http_validate_url( $endpoint ) ) {
186 return array();
187 }
188
189 $request_args = array(
190 'timeout' => intval( $args['request_timeout'] ?? 10 ),
191 'headers' => $this->parse_headers( $args['request_headers'] ?? '' ),
192 'reject_unsafe_urls' => true,
193 );
194
195 $response = wp_safe_remote_get( $endpoint, $request_args );
196 if ( is_wp_error( $response ) ) {
197 return array();
198 }
199
200 if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
201 return array();
202 }
203
204 $body = json_decode( wp_remote_retrieve_body( $response ), true );
205
206 if ( ! is_array( $body ) ) {
207 return array();
208 }
209
210 // Navigate to items array if path specified
211 $items_path = trim( $args['items_path'] ?? '' );
212 if ( ! empty( $items_path ) ) {
213 $body = $this->get_by_path( $body, $items_path );
214 }
215
216 if ( ! is_array( $body ) ) {
217 return array();
218 }
219
220 $value_path = $args['value_path'] ?? 'id';
221 $label_path = $args['label_path'] ?? 'name';
222 $result = array();
223
224 foreach ( $body as $item ) {
225 if ( ! is_array( $item ) ) {
226 continue;
227 }
228
229 $value = $this->get_by_path( $item, $value_path );
230
231 if ( null === $value ) {
232 continue;
233 }
234
235 $label = $this->get_by_path( $item, $label_path );
236
237 $result[] = array(
238 'value' => strval( $value ),
239 'label' => strval( $label ?? $value ),
240 );
241 }
242
243 return $result;
244 }
245
246 /**
247 * Parse textarea headers string into associative array.
248 * Format: one "Header-Name: value" per line.
249 *
250 * @param string $raw Raw headers string.
251 *
252 * @return array Parsed headers.
253 */
254 private function parse_headers( string $raw ): array {
255 $headers = array();
256
257 foreach ( explode( "\n", $raw ) as $line ) {
258 $line = trim( $line );
259
260 if ( empty( $line ) ) {
261 continue;
262 }
263
264 $parts = explode( ':', $line, 2 );
265
266 if ( 2 === count( $parts ) ) {
267 $headers[ trim( $parts[0] ) ] = trim( $parts[1] );
268 }
269 }
270
271 return $headers;
272 }
273
274 /**
275 * Navigate to a nested value using dot notation.
276 * Example: get_by_path( $data, 'results.items' ) reads $data['results']['items'].
277 *
278 * @param array $data Array to navigate.
279 * @param string $path Dot-separated path.
280 *
281 * @return mixed|null Value at path or null if not found.
282 */
283 private function get_by_path( array $data, string $path ) {
284 $keys = explode( '.', $path );
285 $current = $data;
286
287 foreach ( $keys as $key ) {
288 if ( ! is_array( $current ) || ! array_key_exists( $key, $current ) ) {
289 return null;
290 }
291 $current = $current[ $key ];
292 }
293
294 return $current;
295 }
296 }
297