PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 2.4.1
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v2.4.1
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 2.2.9 2.3.0 2.3.1 2.3.2 2.3.3 All 194 releases
convertkit / includes / class-convertkit-output.php

class-convertkit-output.php in Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages 2.4.1, at includes/class-convertkit-output.php

521 lines 14.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * ConvertKit Output class.
4 *
5 * @package ConvertKit
6 * @author ConvertKit
7 */
8
9 /**
10 * Outputs Forms and Landing Pages on the frontend web site, based on
11 * the Post and Plugin's configuration.
12 *
13 * @since 1.9.6
14 */
15 class ConvertKit_Output {
16
17 /**
18 * Holds the ConvertKit Subscriber ID.
19 *
20 * @since 2.0.6
21 *
22 * @var int|string
23 */
24 private $subscriber_id = 0;
25
26 /**
27 * Holds the ConvertKit Plugin Settings class
28 *
29 * @since 1.9.6
30 *
31 * @var bool|ConvertKit_Settings
32 */
33 private $settings = false;
34
35 /**
36 * Holds the ConvertKit Post Settings class
37 *
38 * @since 1.9.6
39 *
40 * @var bool|ConvertKit_Post
41 */
42 private $post_settings = false;
43
44 /**
45 * Holds the available ConvertKit Forms
46 *
47 * @since 1.9.6
48 *
49 * @var bool|ConvertKit_Resource_Forms
50 */
51 private $forms = false;
52
53 /**
54 * Holds the available ConvertKit Landing Pages
55 *
56 * @since 1.9.6
57 *
58 * @var bool|ConvertKit_Resource_Landing_Pages
59 */
60 private $landing_pages = false;
61
62 /**
63 * Constructor. Registers actions and filters to output ConvertKit Forms and Landing Pages
64 * on the frontend web site.
65 *
66 * @since 1.9.6
67 */
68 public function __construct() {
69
70 add_action( 'init', array( $this, 'get_subscriber_id_from_request' ), 1 );
71 add_action( 'template_redirect', array( $this, 'output_form' ) );
72 add_action( 'template_redirect', array( $this, 'page_takeover' ) );
73 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
74 add_filter( 'the_content', array( $this, 'append_form_to_content' ) );
75 add_action( 'wp_footer', array( $this, 'output_global_non_inline_form' ), 1 );
76 add_action( 'wp_footer', array( $this, 'output_scripts_footer' ) );
77
78 }
79
80 /**
81 * Runs the `convertkit_output_output_form` action for singular Post Types that don't use the_content()
82 * or apply_filters( 'the_content' ) to output a ConvertKit Form.
83 *
84 * @since 1.9.6
85 */
86 public function output_form() {
87
88 /**
89 * Outputs a ConvertKit Form on singular Post Types that don't use the_content()
90 * or apply_filters( 'the_content' ).
91 *
92 * @since 1.9.6
93 *
94 * @return string Post Content with Form Appended, if applicable
95 */
96 do_action( 'convertkit_output_output_form' );
97
98 }
99
100 /**
101 * Outputs a ConvertKit Landing Page if configured, replacing all output for the singular Post Type.
102 *
103 * @since 1.9.6
104 */
105 public function page_takeover() {
106
107 $queried_object = get_queried_object();
108
109 // Bail if the queried object cannot be inspected.
110 if ( ! isset( $queried_object->post_type ) ) {
111 return;
112 }
113
114 // Get Post ID.
115 $post_id = $queried_object->ID;
116
117 // Bail if the queried object isn't a supported Post Type for Landing Pages.
118 if ( $queried_object->post_type !== 'page' ) {
119 return;
120 }
121
122 // Get ConvertKit Post's Settings, if they have not yet been loaded.
123 if ( ! $this->post_settings ) {
124 $this->post_settings = new ConvertKit_Post( $post_id );
125 }
126
127 // Get Landing Page ID.
128 $landing_page_id = $this->post_settings->get_landing_page();
129
130 /**
131 * Define the ConvertKit Landing Page ID to display for the given Post ID,
132 * overriding the Post settings.
133 *
134 * Return false to not display any ConvertKit Landing Page.
135 *
136 * @since 1.9.6
137 *
138 * @param int $landing_page_id Landing Page ID
139 * @param int $post_id Post ID
140 */
141 $landing_page_id = apply_filters( 'convertkit_output_page_takeover_landing_page_id', $landing_page_id, $post_id );
142
143 // Bail if no Landing Page is configured to be output.
144 if ( empty( $landing_page_id ) ) {
145 return;
146 }
147
148 // Get available ConvertKit Landing Pages, if they have not yet been loaded.
149 if ( ! $this->landing_pages ) {
150 $this->landing_pages = new ConvertKit_Resource_Landing_Pages( 'output_landing_page' );
151 }
152
153 // Get Landing Page.
154 $landing_page = $this->landing_pages->get_html( $this->post_settings->get_landing_page() );
155
156 // Bail if an error occured.
157 if ( is_wp_error( $landing_page ) ) {
158 return;
159 }
160
161 // Replace the favicon with the WordPress site's favicon, if specified.
162 $landing_page = $this->landing_pages->replace_favicon( $landing_page );
163
164 // Output Landing Page.
165 // Output is supplied from ConvertKit's API, which is already sanitized.
166 echo $landing_page; // phpcs:ignore WordPress.Security.EscapeOutput
167 exit;
168
169 }
170
171 /**
172 * Appends a form to the singular Page, Post or Custom Post Type's Content.
173 *
174 * @param string $content Post Content.
175 * @return string Post Content with Form Appended, if applicable
176 */
177 public function append_form_to_content( $content ) {
178
179 // Bail if not a singular Post Type supported by ConvertKit.
180 if ( ! is_singular( convertkit_get_supported_post_types() ) ) {
181 return $content;
182 }
183
184 // Get Post ID and ConvertKit Form ID for the Post.
185 $post_id = get_the_ID();
186 $form_id = $this->get_post_form_id( $post_id );
187
188 /**
189 * Define the ConvertKit Form ID to display for the given Post ID,
190 * overriding the Post, Category or Plugin settings.
191 *
192 * Return false to not display any ConvertKit Form.
193 *
194 * @since 1.9.6
195 *
196 * @param bool|int $form_id Form ID
197 * @param int $post_id Post ID
198 */
199 $form_id = apply_filters( 'convertkit_output_append_form_to_content_form_id', $form_id, $post_id );
200
201 // Return the Post Content, unedited, if the Form ID is false or zero.
202 if ( ! $form_id ) {
203 return $content;
204 }
205
206 // Get available ConvertKit Forms, if they have not yet been loaded.
207 if ( ! $this->forms ) {
208 $this->forms = new ConvertKit_Resource_Forms( 'output_form' );
209 }
210
211 // Get Form HTML.
212 $form = $this->forms->get_html( $form_id );
213
214 // If an error occured, it could be because the specified Form ID for the Post either:
215 // - belongs to another ConvertKit account (i.e. API credentials were changed in the Plugin, but this Post's specified Form was not changed), or
216 // - the form was deleted from the ConvertKit account.
217 // Attempt to fallback to the default form for this Post Type.
218 if ( is_wp_error( $form ) ) {
219 if ( $this->settings->debug_enabled() ) {
220 $content .= '<!-- ConvertKit append_form_to_content(): ' . $form->get_error_message() . ' Attempting fallback to Default Form. -->';
221 }
222
223 // Get Default Form ID for this Post's Type.
224 $form_id = $this->settings->get_default_form( get_post_type( $post_id ) );
225
226 // If no Default Form is specified, just return the Post Content, unedited.
227 if ( ! $form_id ) {
228 if ( $this->settings->debug_enabled() ) {
229 $content .= '<!-- ConvertKit append_form_to_content(): No Default Form exists as a fallback. -->';
230 }
231
232 return $content;
233 }
234
235 // Get Form HTML.
236 $form = $this->forms->get_html( $form_id );
237
238 // If an error occured again, the default form doesn't exist in this ConvertKit account.
239 // Just return the Post Content, unedited.
240 if ( is_wp_error( $form ) ) {
241 if ( $this->settings->debug_enabled() ) {
242 $content .= '<!-- ConvertKit append_form_to_content(): Default Form: ' . $form->get_error_message() . ' -->';
243 }
244
245 return $content;
246 }
247 }
248
249 // If here, we have a ConvertKit Form.
250 // Append form to Post's Content.
251 $content = $content .= $form;
252
253 /**
254 * Filter the Post's Content, which includes a ConvertKit Form, immediately before it is output.
255 *
256 * @since 1.9.6
257 *
258 * @param string $content Post Content
259 * @param string $form ConvertKit Form HTML
260 * @param int $post_id Post ID
261 * @param int $form_id ConvertKit Form ID
262 */
263 $content = apply_filters( 'convertkit_frontend_append_form', $content, $form, $post_id, $form_id );
264
265 return $content;
266
267 }
268
269 /**
270 * Returns the Post, Category or Plugin ConvertKit Form ID for the given Post.
271 *
272 * If the Post specifies a form to use, returns that Form ID.
273 * If the Post uses the 'Default' setting, and an assigned Category has a Form ID, uses the Category's Form ID.
274 * Otherwise falls back to the Plugin's Default Form ID (if any).
275 *
276 * @since 1.9.6
277 *
278 * @param int $post_id Post ID.
279 * @return bool|string|int false|'default'|Form ID
280 */
281 private function get_post_form_id( $post_id ) {
282
283 // Get Settings, if they have not yet been loaded.
284 if ( ! $this->settings ) {
285 $this->settings = new ConvertKit_Settings();
286 }
287
288 // Get ConvertKit Post's Settings, if they have not yet been loaded.
289 if ( ! $this->post_settings ) {
290 $this->post_settings = new ConvertKit_Post( $post_id );
291 }
292
293 // If the Post specifies a Form to use, return its ID now.
294 if ( $this->post_settings->has_form() ) {
295 return $this->post_settings->get_form();
296 }
297
298 // If the Post specifies that no Form should be used, return false.
299 if ( $this->post_settings->uses_no_form() ) {
300 return false;
301 }
302
303 // Sanity check that the Post uses the Default Form setting, which should be the case
304 // because the above conditions were not met.
305 if ( ! $this->post_settings->uses_default_form() ) {
306 return false;
307 }
308
309 // Get Post's Categories.
310 $categories = wp_get_post_categories(
311 $post_id,
312 array(
313 'fields' => 'ids',
314 )
315 );
316
317 // If no Categories exist, use the Default Form.
318 if ( ! is_array( $categories ) || ! count( $categories ) ) {
319 // Get Post Type.
320 return $this->settings->get_default_form( get_post_type( $post_id ) );
321 }
322
323 /**
324 * Iterate through Categories in reverse order.
325 * This honors the behaviour < 1.9.6, which states that if multiple Categories each have a Form.
326 * assigned, the last Category with a Form in the wp_get_post_categories() call will be used.
327 */
328 $categories = array_reverse( $categories );
329 foreach ( $categories as $term_id ) {
330 // Load Term Settings.
331 $term_settings = new ConvertKit_Term( $term_id );
332
333 // If a Form ID exists, return it now.
334 if ( $term_settings->has_form() ) {
335 return $term_settings->get_form();
336 }
337 }
338
339 // If here, use the Plugin's Default Form.
340 return $this->settings->get_default_form( get_post_type( $post_id ) );
341
342 }
343
344 /**
345 * Enqueue scripts.
346 *
347 * @since 1.9.6
348 */
349 public function enqueue_scripts() {
350
351 // Get Post.
352 $post = get_post();
353
354 // Bail if no Post could be fetched.
355 if ( ! $post ) {
356 return;
357 }
358
359 // Get ConvertKit Settings and Post's Settings.
360 $settings = new ConvertKit_Settings();
361 $convertkit_post = new ConvertKit_Post( $post->ID );
362
363 // Register scripts that we might use.
364 wp_register_script(
365 'convertkit-js',
366 CONVERTKIT_PLUGIN_URL . 'resources/frontend/js/convertkit.js',
367 array( 'jquery' ),
368 CONVERTKIT_PLUGIN_VERSION,
369 true
370 );
371 wp_localize_script(
372 'convertkit-js',
373 'convertkit',
374 array(
375 'ajaxurl' => admin_url( 'admin-ajax.php' ),
376 'debug' => $settings->debug_enabled(),
377 'nonce' => wp_create_nonce( 'convertkit' ),
378 'subscriber_id' => $this->subscriber_id,
379 'tag' => ( ( is_singular() && $convertkit_post->has_tag() ) ? $convertkit_post->get_tag() : false ),
380 'post_id' => $post->ID,
381 )
382 );
383
384 // Bail if the no scripts setting is enabled.
385 if ( $settings->scripts_disabled() ) {
386 return;
387 }
388
389 // Enqueue.
390 wp_enqueue_script( 'convertkit-js' );
391
392 }
393
394 /**
395 * Gets the subscriber ID from the request (either the cookie or the URL).
396 *
397 * @since 1.9.6
398 */
399 public function get_subscriber_id_from_request() {
400
401 // Use ConvertKit_Subscriber class to fetch and validate the subscriber ID.
402 $subscriber = new ConvertKit_Subscriber();
403 $subscriber_id = $subscriber->get_subscriber_id();
404
405 // If an error occured, the subscriber ID in the request/cookie is not a valid subscriber.
406 if ( is_wp_error( $subscriber_id ) ) {
407 return;
408 }
409
410 $this->subscriber_id = $subscriber_id;
411
412 }
413
414 /**
415 * Outputs a non-inline form if defined in the Plugin's settings >
416 * Default Non-Inline Form (Global) setting.
417 *
418 * @since 2.3.3
419 */
420 public function output_global_non_inline_form() {
421
422 // Get Settings, if they have not yet been loaded.
423 if ( ! $this->settings ) {
424 $this->settings = new ConvertKit_Settings();
425 }
426
427 // Bail if no non-inline form setting is specified.
428 if ( ! $this->settings->has_non_inline_form() ) {
429 return;
430 }
431
432 // Get form.
433 $convertkit_forms = new ConvertKit_Resource_Forms();
434 $form = $convertkit_forms->get_by_id( (int) $this->settings->get_non_inline_form() );
435
436 // Bail if the Form doesn't exist (this shouldn't happen, but you never know).
437 if ( ! $form ) {
438 return;
439 }
440
441 // Add the form to the scripts array so it is included in the output.
442 add_filter(
443 'convertkit_output_scripts_footer',
444 function ( $scripts ) use ( $form ) {
445
446 $scripts[] = array(
447 'async' => true,
448 'data-uid' => $form['uid'],
449 'src' => $form['embed_js'],
450 );
451
452 return $scripts;
453
454 }
455 );
456
457 }
458
459 /**
460 * Outputs any JS <script> tags registered with the convertkit_output_scripts_footer
461 * filter
462 *
463 * @since 2.1.4
464 */
465 public function output_scripts_footer() {
466
467 // Define array of scripts.
468 $scripts = array();
469
470 /**
471 * Define an array of scripts to output in the footer of the WordPress site.
472 *
473 * @since 2.1.4
474 *
475 * @param array $scripts Scripts.
476 */
477 $scripts = apply_filters( 'convertkit_output_scripts_footer', $scripts );
478
479 // Bail if no scripts exist.
480 if ( ! count( $scripts ) ) {
481 return;
482 }
483
484 // Define array to store <script> outputs.
485 $output_scripts = array();
486
487 // Iterate through scripts, building the <script> tag for each.
488 foreach ( $scripts as $script ) {
489 $output = '<script';
490
491 foreach ( $script as $attribute => $value ) {
492 // If the value is true, just output the attribute.
493 if ( $value === true ) {
494 $output .= ' ' . esc_attr( $attribute );
495 continue;
496 }
497
498 // Output the attribute and value.
499 $output .= ' ' . esc_attr( $attribute ) . '="' . esc_attr( $value ) . '"';
500 }
501 $output .= '></script>';
502
503 // Add to array.
504 $output_scripts[] = $output;
505 }
506
507 // Remove duplicate scripts.
508 // This prevents the same non-inline form displaying twice. For example, if a modal form is specified both
509 // in the Page's settings and the Form block, the user would see the same modal form displayed twice
510 // because the script would be output twice.
511 $output_scripts = array_unique( $output_scripts );
512
513 // Output scripts.
514 foreach ( $output_scripts as $output_script ) {
515 echo $output_script . "\n"; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
516 }
517
518 }
519
520 }
521