PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.1.0
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.1.0
3.4.6 3.4.5 3.4.4 3.4.3 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 All 199 releases
← All changes | includes/class-convertkit-resource-forms.php +479 -26 2.2.5 → 3.1.0 View file →
@@ -11,9 +11,9 @@
11 11 * ConvertKit Forms data stored locally from the API.
12 12 *
13 13 * @since 1.9.6
14 14 */
15 -class ConvertKit_Resource_Forms extends ConvertKit_Resource {
15 +class ConvertKit_Resource_Forms extends ConvertKit_Resource_V4 {
16 16
17 17 /**
18 18 * Holds the Settings Key that stores site wide ConvertKit settings
19 19 *
@@ -36,14 +36,16 @@
36 36 * @param bool|string $context Context.
37 37 */
38 38 public function __construct( $context = false ) {
39 39
40 - // Initialize the API if the API Key and Secret have been defined in the Plugin Settings.
40 + // Initialize the API if the Access Token has been defined in the Plugin Settings.
41 41 $settings = new ConvertKit_Settings();
42 - if ( $settings->has_api_key_and_secret() ) {
43 - $this->api = new ConvertKit_API(
44 - $settings->get_api_key(),
45 - $settings->get_api_secret(),
42 + if ( $settings->has_access_and_refresh_token() ) {
43 + $this->api = new ConvertKit_API_V4(
44 + CONVERTKIT_OAUTH_CLIENT_ID,
45 + CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI,
46 + $settings->get_access_token(),
47 + $settings->get_refresh_token(),
46 48 $settings->debug_enabled(),
47 49 $context
48 50 );
49 51 }
@@ -53,8 +55,42 @@
53 55
54 56 }
55 57
56 58 /**
59 + * Returns all inline forms based on the sort order.
60 + *
61 + * @since 2.7.3
62 + *
63 + * @return bool|array
64 + */
65 + public function get_inline() {
66 +
67 + // If the ConvertKit WordPress Libraries are < 1.3.6 (e.g. loaded by an outdated
68 + // addon), or a WordPress site updates this Plugin before other ConvertKit Plugins,
69 + // get_by() won't be available and will cause an E_ERROR, crashing the site.
70 + // @see https://wordpress.org/support/topic/error-1795/.
71 + if ( ! method_exists( $this, 'get_by' ) ) { // @phpstan-ignore-line Older WordPress Libraries won't have this function.
72 + return false;
73 + }
74 +
75 + return $this->get_by( 'format', array( 'inline' ) );
76 +
77 + }
78 +
79 + /**
80 + * Returns whether any inline forms exist in the options table.
81 + *
82 + * @since 2.7.3
83 + *
84 + * @return bool
85 + */
86 + public function inline_exist() {
87 +
88 + return (bool) $this->get_inline();
89 +
90 + }
91 +
92 + /**
57 93 * Returns all non-inline forms based on the sort order.
58 94 *
59 95 * @since 2.2.4
60 96 *
@@ -65,9 +101,9 @@
65 101 // If the ConvertKit WordPress Libraries are < 1.3.6 (e.g. loaded by an outdated
66 102 // addon), or a WordPress site updates this Plugin before other ConvertKit Plugins,
67 103 // get_by() won't be available and will cause an E_ERROR, crashing the site.
68 104 // @see https://wordpress.org/support/topic/error-1795/.
69 - if ( ! method_exists( $this, 'get_by' ) ) {
105 + if ( ! method_exists( $this, 'get_by' ) ) { // @phpstan-ignore-line Older WordPress Libraries won't have this function.
70 106 return false;
71 107 }
72 108
73 109 return $this->get_by( 'format', array( 'modal', 'slide in', 'sticky bar' ) );
@@ -73,9 +109,8 @@
73 109 return $this->get_by( 'format', array( 'modal', 'slide in', 'sticky bar' ) );
74 110
75 111 }
76 112
77 -
78 113 /**
79 114 * Returns whether any non-inline forms exist in the options table.
80 115 *
81 116 * @since 2.2.4
@@ -92,8 +127,347 @@
92 127
93 128 }
94 129
95 130 /**
131 + * Determines if the given Form ID is a legacy Form or Landing Page.
132 + *
133 + * @since 2.5.0
134 + *
135 + * @param int $id Form or Landing Page ID.
136 + */
137 + public function is_legacy( $id ) {
138 +
139 + // Get Form.
140 + $form = $this->get_by_id( (int) $id );
141 +
142 + // Return false if no Form exists.
143 + if ( ! $form ) {
144 + return false;
145 + }
146 +
147 + // If the `format` key exists, this is not a legacy Form.
148 + if ( array_key_exists( 'format', $form ) ) {
149 + return false;
150 + }
151 +
152 + return true;
153 +
154 + }
155 +
156 + /**
157 + * Returns a <select> field populated with all forms, based on the given parameters.
158 + *
159 + * @since 2.3.9
160 + *
161 + * @param string $name Name.
162 + * @param string $id ID.
163 + * @param bool|array $css_classes <select> CSS class(es).
164 + * @param string $selected_option <option> value to mark as selected.
165 + * @param bool|array $prepend_options <option> elements to prepend before resources.
166 + * @param bool|array $attributes <select> attributes.
167 + * @param bool|string|array $description Description.
168 + * @return string HTML Select Field
169 + */
170 + public function get_select_field_all( $name, $id, $css_classes, $selected_option, $prepend_options = false, $attributes = false, $description = false ) {
171 +
172 + return $this->get_select_field(
173 + $this->get(),
174 + $name,
175 + $id,
176 + $css_classes,
177 + $selected_option,
178 + $prepend_options,
179 + $attributes,
180 + $description
181 + );
182 +
183 + }
184 +
185 + /**
186 + * Outputs a <select> field populated with all forms, based on the given parameters.
187 + *
188 + * @since 2.8.5
189 + *
190 + * @param string $name Name.
191 + * @param string $id ID.
192 + * @param bool|array $css_classes <select> CSS class(es).
193 + * @param string $selected_option <option> value to mark as selected.
194 + * @param bool|array $prepend_options <option> elements to prepend before resources.
195 + * @param bool|array $attributes <select> attributes.
196 + * @param bool|string|array $description Description.
197 + */
198 + public function output_select_field_all( $name, $id, $css_classes, $selected_option, $prepend_options = false, $attributes = false, $description = false ) {
199 +
200 + $this->output_select_field(
201 + $this->get(),
202 + $name,
203 + $id,
204 + $css_classes,
205 + $selected_option,
206 + $prepend_options,
207 + $attributes,
208 + $description
209 + );
210 +
211 + }
212 +
213 + /**
214 + * Returns a <select> field populated with all non-inline forms, based on the given parameters.
215 + *
216 + * @since 2.3.9
217 + *
218 + * @param string $name Name.
219 + * @param string $id ID.
220 + * @param bool|array $css_classes <select> CSS class(es).
221 + * @param array $selected_options <option> values to mark as selected.
222 + * @param bool|array $prepend_options <option> elements to prepend before resources.
223 + * @param bool|array $attributes <select> attributes.
224 + * @param bool|string|array $description Description.
225 + * @return string HTML Select Field
226 + */
227 + public function get_select_field_non_inline( $name, $id, $css_classes, $selected_options, $prepend_options = false, $attributes = false, $description = false ) {
228 +
229 + return $this->get_multi_select_field(
230 + $this->get_non_inline(),
231 + $name,
232 + $id,
233 + $css_classes,
234 + $selected_options,
235 + $prepend_options,
236 + $attributes,
237 + $description
238 + );
239 +
240 + }
241 +
242 + /**
243 + * Outputs a <select> field populated with all non-inline forms, based on the given parameters.
244 + *
245 + * @since 2.3.9
246 + *
247 + * @param string $name Name.
248 + * @param string $id ID.
249 + * @param bool|array $css_classes <select> CSS class(es).
250 + * @param array $selected_options <option> values to mark as selected.
251 + * @param bool|array $prepend_options <option> elements to prepend before resources.
252 + * @param bool|array $attributes <select> attributes.
253 + * @param bool|string|array $description Description.
254 + */
255 + public function output_select_field_non_inline( $name, $id, $css_classes, $selected_options, $prepend_options = false, $attributes = false, $description = false ) {
256 +
257 + echo wp_kses(
258 + $this->get_select_field_non_inline(
259 + $name,
260 + $id,
261 + $css_classes,
262 + $selected_options,
263 + $prepend_options,
264 + $attributes,
265 + $description
266 + ),
267 + convertkit_kses_allowed_html()
268 + );
269 +
270 + }
271 +
272 + /**
273 + * Returns a <select> field populated with the resources, based on the given parameters.
274 + *
275 + * @since 2.3.9
276 + *
277 + * @param array $forms Forms.
278 + * @param string $name Name.
279 + * @param string $id ID.
280 + * @param bool|array $css_classes <select> CSS class(es).
281 + * @param string $selected_option <option> value to mark as selected.
282 + * @param bool|array $prepend_options <option> elements to prepend before resources.
283 + * @param bool|array $attributes <select> attributes.
284 + * @param bool|string|array $description Description.
285 + * @return string HTML Select Field
286 + */
287 + private function get_select_field( $forms, $name, $id, $css_classes, $selected_option, $prepend_options = false, $attributes = false, $description = false ) {
288 +
289 + $html = sprintf(
290 + '<select name="%s" id="%s" class="%s"',
291 + esc_attr( $name ),
292 + esc_attr( $id ),
293 + esc_attr( ( is_array( $css_classes ) ? implode( ' ', $css_classes ) : '' ) )
294 + );
295 +
296 + // Append any attributes.
297 + if ( $attributes ) {
298 + foreach ( $attributes as $key => $value ) {
299 + $html .= sprintf(
300 + ' %s="%s"',
301 + esc_attr( $key ),
302 + esc_attr( $value )
303 + );
304 + }
305 + }
306 +
307 + // Close select tag.
308 + $html .= '>';
309 +
310 + // If any prepended options exist, add them now.
311 + if ( $prepend_options ) {
312 + foreach ( $prepend_options as $value => $label ) {
313 + $html .= sprintf(
314 + '<option value="%s" data-preserve-on-refresh="1"%s>%s</option>',
315 + esc_attr( $value ),
316 + selected( $selected_option, $value, false ),
317 + esc_attr( $label )
318 + );
319 + }
320 + }
321 +
322 + // Iterate through resources, if they exist, building <option> elements.
323 + if ( $forms ) {
324 + foreach ( $forms as $form ) {
325 + // Legacy forms don't include a `format` key, so define them as inline.
326 + $html .= sprintf(
327 + '<option value="%s"%s>%s [%s]</option>',
328 + esc_attr( $form['id'] ),
329 + selected( $selected_option, $form['id'], false ),
330 + esc_attr( $form['name'] ),
331 + ( ! empty( $form['format'] ) ? esc_attr( $form['format'] ) : 'inline' )
332 + );
333 + }
334 + }
335 +
336 + // Close select.
337 + $html .= '</select>';
338 +
339 + // If no description is provided, return the select field now.
340 + if ( ! $description ) {
341 + return $html;
342 + }
343 +
344 + // Append description before returning field.
345 + if ( ! is_array( $description ) ) {
346 + return $html . '<p class="description">' . $description . '</p>';
347 + }
348 +
349 + // Return description lines in a paragraph, using breaklines for each description entry in the array.
350 + return $html . '<p class="description">' . implode( '<br />', $description ) . '</p>';
351 +
352 + }
353 +
354 + /**
355 + * Outputs a <select> field populated with the resources, based on the given parameters.
356 + *
357 + * @since 2.8.5
358 + *
359 + * @param array $forms Forms.
360 + * @param string $name Name.
361 + * @param string $id ID.
362 + * @param bool|array $css_classes <select> CSS class(es).
363 + * @param string $selected_option <option> value to mark as selected.
364 + * @param bool|array $prepend_options <option> elements to prepend before resources.
365 + * @param bool|array $attributes <select> attributes.
366 + * @param bool|string|array $description Description.
367 + */
368 + private function output_select_field( $forms, $name, $id, $css_classes, $selected_option, $prepend_options = false, $attributes = false, $description = false ) {
369 +
370 + echo wp_kses(
371 + $this->get_select_field(
372 + $forms,
373 + $name,
374 + $id,
375 + $css_classes,
376 + $selected_option,
377 + $prepend_options,
378 + $attributes,
379 + $description
380 + ),
381 + convertkit_kses_allowed_html()
382 + );
383 +
384 + }
385 +
386 + /**
387 + * Returns a <select> field populated with the resources, based on the given parameters,
388 + * that supports multiple selection.
389 + *
390 + * @since 2.6.9
391 + *
392 + * @param array $forms Forms.
393 + * @param string $name Name.
394 + * @param string $id ID.
395 + * @param bool|array $css_classes <select> CSS class(es).
396 + * @param array $selected_options <option> values to mark as selected.
397 + * @param bool|array $prepend_options <option> elements to prepend before resources.
398 + * @param bool|array $attributes <select> attributes.
399 + * @param bool|string|array $description Description.
400 + * @return string HTML Select Field
401 + */
402 + private function get_multi_select_field( $forms, $name, $id, $css_classes, $selected_options = array(), $prepend_options = false, $attributes = false, $description = false ) {
403 +
404 + $html = sprintf(
405 + '<select name="%s[]" id="%s" class="%s" multiple',
406 + esc_attr( $name ),
407 + esc_attr( $id ),
408 + esc_attr( ( is_array( $css_classes ) ? implode( ' ', $css_classes ) : '' ) )
409 + );
410 +
411 + // Append any attributes.
412 + if ( $attributes ) {
413 + foreach ( $attributes as $key => $value ) {
414 + $html .= sprintf(
415 + ' %s="%s"',
416 + esc_attr( $key ),
417 + esc_attr( $value )
418 + );
419 + }
420 + }
421 +
422 + // Close select tag.
423 + $html .= '>';
424 +
425 + // If any prepended options exist, add them now.
426 + if ( $prepend_options ) {
427 + foreach ( $prepend_options as $value => $label ) {
428 + $html .= sprintf(
429 + '<option value="%s" data-preserve-on-refresh="1"%s>%s</option>',
430 + esc_attr( $value ),
431 + ( in_array( $value, $selected_options, true ) ? ' selected' : '' ),
432 + esc_attr( $label )
433 + );
434 + }
435 + }
436 +
437 + // Iterate through resources, if they exist, building <option> elements.
438 + if ( $forms ) {
439 + foreach ( $forms as $form ) {
440 + // Legacy forms don't include a `format` key, so define them as inline.
441 + $html .= sprintf(
442 + '<option value="%s"%s>%s [%s]</option>',
443 + esc_attr( $form['id'] ),
444 + ( in_array( $form['id'], $selected_options, true ) ? ' selected' : '' ),
445 + esc_attr( $form['name'] ),
446 + ( ! empty( $form['format'] ) ? esc_attr( $form['format'] ) : 'inline' )
447 + );
448 + }
449 + }
450 +
451 + // Close select.
452 + $html .= '</select>';
453 +
454 + // If no description is provided, return the select field now.
455 + if ( ! $description ) {
456 + return $html;
457 + }
458 +
459 + // Append description before returning field.
460 + if ( ! is_array( $description ) ) {
461 + return $html . '<p class="description">' . $description . '</p>';
462 + }
463 +
464 + // Return description lines in a paragraph, using breaklines for each description entry in the array.
465 + return $html . '<p class="description">' . implode( '<br />', $description ) . '</p>';
466 +
467 + }
468 +
469 + /**
96 470 * Returns the HTML/JS markup for the given Form ID.
97 471 *
98 472 * Legacy Forms will return HTML.
99 473 * Current Forms will return a <script> embed string.
@@ -99,12 +473,13 @@
99 473 * Current Forms will return a <script> embed string.
100 474 *
101 475 * @since 1.9.6
102 476 *
103 - * @param int $id Form ID.
477 + * @param int $id Form ID.
478 + * @param int $post_id Post ID that requested the Form.
104 479 * @return WP_Error|string
105 480 */
106 - public function get_html( $id ) {
481 + public function get_html( $id, $post_id = 0 ) {
107 482
108 483 // Cast ID to integer.
109 484 $id = absint( $id );
110 485
@@ -118,33 +493,42 @@
118 493 return new WP_Error(
119 494 'convertkit_resource_forms_get_html',
120 495 sprintf(
121 496 /* translators: ConvertKit Form ID */
122 - __( 'ConvertKit Form ID %s does not exist on ConvertKit.', 'convertkit' ),
497 + __( 'Kit Form ID %s does not exist on Kit.', 'convertkit' ),
123 498 $id
124 499 )
125 500 );
126 501 }
127 502
503 + // Initialize Settings.
504 + $settings = new ConvertKit_Settings();
505 +
128 506 // If no uid is present in the Form API data, this is a legacy form that's served by directly fetching the HTML
129 - // from forms.convertkit.com.
507 + // from forms.kit.com.
130 508 if ( ! isset( $this->resources[ $id ]['uid'] ) ) {
131 - // Initialize Settings.
132 - $settings = new ConvertKit_Settings();
133 -
134 - // Bail if no API Key is specified in the Plugin Settings.
135 - if ( ! $settings->has_api_key() ) {
509 + // Bail if no Access Token is specified in the Plugin Settings.
510 + if ( ! $settings->has_access_token() ) {
136 511 return new WP_Error(
137 512 'convertkit_resource_forms_get_html',
138 - __( 'ConvertKit Legacy Form could not be fetched as no API Key specified in Plugin Settings', 'convertkit' )
513 + __( 'Kit Legacy Form could not be fetched as no Access Token specified in Plugin Settings', 'convertkit' )
139 514 );
140 515 }
141 516
142 517 // Initialize the API.
143 - $api = new ConvertKit_API( $settings->get_api_key(), $settings->get_api_secret(), $settings->debug_enabled(), 'output_form' );
518 + $api = new ConvertKit_API_V4(
519 + CONVERTKIT_OAUTH_CLIENT_ID,
520 + CONVERTKIT_OAUTH_CLIENT_REDIRECT_URI,
521 + $settings->get_access_token(),
522 + $settings->get_refresh_token(),
523 + $settings->debug_enabled(),
524 + 'output_form'
525 + );
144 526
145 527 // Return Legacy Form HTML.
146 - return $api->get_form_html( $id );
528 + // We now call get_html() with the `embed_url` property, instead of get_form_html() with the `id` property,
529 + // because `embed_url` includes the API Key.
530 + return $api->get_html( $this->resources[ $id ]['embed_url'] );
147 531 }
148 532
149 533 // If the form's format is not an inline form, add the inline script before the closing </body> tag.
150 534 // This prevents a modal form's overlay being constrained by the WordPress Theme's styles,
@@ -152,27 +536,96 @@
152 536 // displaying twice.
153 537 if ( $this->resources[ $id ]['format'] !== 'inline' ) {
154 538 add_filter(
155 539 'convertkit_output_scripts_footer',
156 - function( $scripts ) use ( $id ) {
540 + function ( $scripts ) use ( $id, $post_id, $settings ) {
157 541
158 - $scripts[] = array(
159 - 'async' => true,
160 - 'data-uid' => $this->resources[ $id ]['uid'],
161 - 'src' => $this->resources[ $id ]['embed_js'],
542 + // Build script.
543 + $script = array(
544 + 'async' => true,
545 + 'data-uid' => $this->resources[ $id ]['uid'],
546 + 'src' => $this->resources[ $id ]['embed_js'],
547 + 'data-kit-limit-per-session' => $settings->non_inline_form_limit_per_session() ? '1' : '0',
162 548 );
163 549
550 + // If debugging is enabled, add the post ID to the script.
551 + if ( $settings->debug_enabled() ) {
552 + $script['data-kit-source-post-id'] = $post_id;
553 + }
554 +
555 + // Add the script to the scripts array.
556 + $scripts[] = $script;
557 +
164 558 return $scripts;
165 559
166 560 }
167 561 );
168 562
563 + // Sanity check we're not in the WordPress Admin interface.
564 + // Some third party REST API Plugins seem to load frontend Posts, which would result in a wp_die() as
565 + // the output Plugin class (rightly) isn't initialized in the backend.
566 + if ( is_admin() ) {
567 + return '';
568 + }
569 +
570 + // Don't output the global non-inline form, if defined, because
571 + // a non-inline form was specified at either Post/Page default level, Post/Page level
572 + // or Post Category level.
573 + // This prevents multiple non-inline forms loading.
574 + remove_action( 'wp_footer', array( WP_ConvertKit()->get_class( 'output' ), 'output_global_non_inline_form' ), 1 );
575 +
169 576 // Don't return a script for output, as it'll be output in the site's footer.
170 577 return '';
171 578 }
172 579
173 580 // If here, return Form <script> embed now, as we want the inline form to display at this specific point of the content.
174 - return '<script async data-uid="' . esc_attr( $this->resources[ $id ]['uid'] ) . '" src="' . esc_url( $this->resources[ $id ]['embed_js'] ) . '"></script>';
581 +
582 + // Define script key-value pairs.
583 + $script = array(
584 + 'async' => true,
585 + 'data-uid' => $this->resources[ $id ]['uid'],
586 + 'src' => $this->resources[ $id ]['embed_js'],
587 + );
588 +
589 + // If debugging is enabled, add the post ID to the script.
590 + if ( $settings->debug_enabled() ) {
591 + $script['data-kit-source-post-id'] = $post_id;
592 + }
593 +
594 + /**
595 + * Filter the form <script> key/value pairs immediately before the script is output.
596 + *
597 + * @since 2.4.5
598 + *
599 + * @param array $script Form script key/value pairs to output as <script> tag.
600 + */
601 + $script = apply_filters( 'convertkit_resource_forms_output_script', $script );
602 +
603 + // Build script output.
604 + $output = '<script';
605 + foreach ( $script as $attribute => $value ) {
606 + // If the value is true, just output the attribute.
607 + if ( $value === true ) {
608 + $output .= ' ' . esc_attr( $attribute );
609 + continue;
610 + }
611 +
612 + // Sanitize attribute and value.
613 + $attribute = esc_attr( $attribute );
614 + $value = ( $attribute === 'src' ? esc_url( $value ) : esc_attr( $value ) );
615 +
616 + // Output the attribute and value.
617 + $output .= ' ' . $attribute;
618 +
619 + // Output the value, if it's not a blank string.
620 + if ( strlen( $value ) > 0 ) {
621 + $output .= '="' . $value . '"';
622 + }
623 + }
624 + $output .= '></script>';
625 +
626 + // Return script output.
627 + return $output;
175 628
176 629 }
177 630
178 631 }