PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 2.3.2
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v2.3.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 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.3.2, at includes/class-convertkit-output.php

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