PluginProbe
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) / 1.8.12.3
Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) v1.8.12.3
1.8.13.1 1.8.13 1.8.12.3 1.8.12.2 1.8.12.1 1.8.12 1.8.11.3 1.8.11.2 1.8.11.1 1.8.11 1.6.6 1.6.60 1.6.7 1.6.8 1.6.9 1.7.0 1.7.0.1 1.7.0.11 1.7.0.12 1.7.0.14 1.7.0.2 1.7.0.3 1.7.0.5 1.7.0.6 1.7.0.7 All 212 releases
charitable / includes / admin / campaign-builder / class-campaign-builder-preview.php

class-campaign-builder-preview.php in Charitable – Donation & Fundraising Platform (Donation Forms, Recurring Donations & Fundraising Campaigns) 1.8.12.3, at includes/admin/campaign-builder/class-campaign-builder-preview.php

455 lines 13.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class that sets up the Campaign builder preview abilities.
4 *
5 * @package Charitable
6 * @author David Bisset
7 * @copyright Copyright (c) 2023, WP Charitable LLC
8 * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9 * @since 1.8.0
10 * @version 1.8.0
11 */
12
13 // Exit if accessed directly.
14 if ( ! defined( 'ABSPATH' ) ) {
15 exit;
16 }
17
18 if ( ! class_exists( 'Campaign_Builder_Preview' ) ) :
19
20 /**
21 * Campaign preview.
22 *
23 * @since 1.8.0
24 * @version 1.8.8.6
25 */
26 class Campaign_Builder_Preview { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedClassFound -- Class name is used by other code. Changing it would break existing functionality.
27
28 /**
29 * One is the loneliest number that you'll ever do.
30 *
31 * @since 1.8.0
32 *
33 * @var object
34 */
35 private static $instance;
36
37 /**
38 * Campaign data.
39 *
40 * @since 1.8.0
41 *
42 * @var array
43 */
44 public $campaign_data;
45
46 /**
47 * Constructor.
48 *
49 * @since 1.8.0
50 */
51 public function __construct() {
52
53 if ( ! $this->is_preview_page() ) {
54 return;
55 }
56
57 $this->hooks();
58 }
59
60 /**
61 * Main Instance.
62 *
63 * @since 1.8.0
64 *
65 * @return Charitable_Builder
66 */
67 public static function get_instance() {
68 if ( ! isset( self::$instance ) && ! ( self::$instance instanceof Campaign_Builder_Preview ) ) {
69
70 self::$instance = new Campaign_Builder_Preview();
71
72 }
73
74 return self::$instance;
75 }
76
77 /**
78 * Init
79 *
80 * @since 1.8.0
81 *
82 * @return void
83 */
84 public function init() {
85 }
86
87 /**
88 * Check if current page request meets requirements for campaign preview page.
89 *
90 * @since 1.8.0
91 *
92 * @return bool
93 */
94 public function is_preview_page() {
95
96 // Only proceed for the campaign preview page.
97 // phpcs:ignore WordPress.Security.NonceVerification.Recommended
98 if ( empty( $_GET['charitable_campaign_preview'] ) ) {
99 return false;
100 }
101
102 // Check for logged-in user with correct capabilities.
103 if ( ! is_user_logged_in() ) {
104 return false;
105 }
106
107 $campaign_id = isset( $_GET['p'] ) ? absint( $_GET['p'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
108 $campaign_id = ( 0 === $campaign_id && ! empty( $_GET['charitable_campaign_preview'] ) ) ? absint( $_GET['charitable_campaign_preview'] ) : $campaign_id; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
109
110 if ( 0 === $campaign_id && ! empty( $_GET['charitable_campaign_preview'] ) && 1 === intval( $_GET['charitable_campaign_preview'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
111 global $post;
112 if ( ! empty( $post->post_id ) ) {
113 $campaign_id = absint( $post->post_id );
114 }
115 }
116
117 // check if this custom post type actually exists.
118 global $wpdb;
119 if ( ! $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE ID=%s AND post_status != 'trash'", array( $campaign_id ) ) ) ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
120 return false;
121 }
122
123 // check permissions.
124 if ( ! charitable_current_user_can( 'edit_post', $campaign_id ) ) { // this was 'view_campaign_single'.
125 return false;
126 }
127
128 // Fetch campaign details.
129 $this->campaign_data = get_post_meta( $campaign_id, 'campaign_settings_v2', true );
130
131 // Check valid campaign was found.
132 if ( empty( $this->campaign_data ) || ( empty( $this->campaign_data['id'] ) && empty( absint( $_GET['charitable_campaign_preview'] ) ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
133 return false;
134 }
135
136 return true;
137 }
138
139 /**
140 * Hooks.
141 *
142 * @since 1.8.0
143 */
144 public function hooks() {
145
146 add_action( 'pre_get_posts', [ $this, 'pre_get_posts' ] );
147 add_filter( 'the_title', [ $this, 'the_title' ], 100, 1 );
148 add_filter( 'the_content', [ $this, 'the_content' ], 999 );
149 add_filter( 'get_the_excerpt', [ $this, 'the_content' ], 999 );
150 add_filter( 'home_template_hierarchy', [ $this, 'force_page_template_hierarchy' ], 999 );
151 add_filter( 'frontpage_template_hierarchy', [ $this, 'force_page_template_hierarchy' ], 999 );
152 add_filter( 'post_thumbnail_html', '__return_empty_string' );
153 add_filter( 'charitable_campaign_can_receive_donations', [ $this, 'enable_campaign_form' ], 10, 2 );
154 }
155
156 /**
157 * The campaign donation form should be enabled for a campaign preview page, so on a preview page it's "ok" for a campaign to recieve a donation.
158 *
159 * @param boolean $can Whether the campaign form accepts donations.
160 * @param mixed $the_object Charitable_Campaign.
161 *
162 * @return boolean Whether the campaign form can be enabled for the given object.
163 */
164 public function enable_campaign_form( $can, $the_object ) { // phpcs:ignore
165 return true;
166 }
167
168 /**
169 * Modify query, limit to one post.
170 *
171 * @since 1.8.0
172 *
173 * @param \WP_Query $query The WP_Query instance.
174 */
175 public function pre_get_posts( $query ) {
176
177 if ( is_admin() || ! $query->is_main_query() ) {
178 return;
179 }
180
181 $query->set( 'page_id', '' );
182 $query->set( 'post_type', 'campaign' );
183 $query->set( 'post__in', empty( $this->campaign_data['id'] ) ? [] : [ (int) $this->campaign_data['id'] ] );
184 $query->set( 'posts_per_page', 1 );
185 }
186
187 /**
188 * Customize campaign preview page title.
189 *
190 * @since 1.5.1
191 *
192 * @param string $title Page title.
193 *
194 * @return string
195 */
196 public function the_title( $title ) {
197
198 if ( in_the_loop() ) {
199 $title = sprintf( /* translators: %s - campaign title. */
200 esc_html__( '%s Preview', 'charitable' ),
201 ! empty( $this->campaign_data['settings']['campaign_title'] ) ? sanitize_text_field( $this->campaign_data['settings']['campaign_title'] ) : esc_html__( 'Campaign', 'charitable' )
202 );
203 }
204
205 return $title;
206 }
207
208 /**
209 * Customize campaign preview page content.
210 *
211 * @since 1.8.0
212 * @version 1.8.12.3 Detach from the content filters while the campaign renders, to prevent an infinite the_content loop that exhausts memory.
213 *
214 * @return string
215 */
216 public function the_content() {
217
218 if ( ! isset( $this->campaign_data['id'] ) ) {
219 return '';
220 }
221
222 if ( ! charitable_current_user_can( 'edit_posts', $this->campaign_data['id'] ) ) {
223 return '';
224 }
225
226 $links = [];
227
228 if ( charitable_current_user_can( 'edit_posts', $this->campaign_data['id'] ) ) {
229 $links[] = [
230 'url' => esc_url(
231 add_query_arg(
232 [
233 'page' => 'charitable-campaign-builder',
234 'view' => 'design',
235 'campaign_id' => absint( $this->campaign_data['id'] ),
236 ],
237 admin_url( 'admin.php' )
238 )
239 ),
240 'text' => esc_html__( 'Edit Campaign', 'charitable' ),
241 ];
242 }
243
244 if ( charitable_current_user_can( 'read', $this->campaign_data['id'] ) ) {
245 $links[] = [
246 'url' => esc_url(
247 add_query_arg(
248 [
249 'post_type' => 'donation',
250 'campaign_id' => absint( $this->campaign_data['id'] ),
251 ],
252 admin_url( 'edit.php' )
253 )
254 ),
255 'text' => esc_html__( 'View Donations', 'charitable' ),
256 ];
257 }
258
259 $links[] = [
260 'url' => esc_url(
261 charitable_utm_link( 'https://www.wpcharitable.com/documentation/', 'Campaign Preview', 'Documentation' )
262 ),
263 'text' => esc_html__( 'Documentation', 'charitable' ),
264 ];
265
266 if ( ! empty( $_GET['new_window'] ) ) { // phpcs:ignore
267 $links[] = [
268 'url' => 'javascript:window.close();',
269 'text' => esc_html__( 'Close this window', 'charitable' ),
270 ];
271 }
272
273 $content = '<div class="charitable-preview-messages"><p>';
274
275 if ( 'publish' === get_post_status( $this->campaign_data['id'] ) ) :
276
277 if ( ! empty( $_GET['charitable_campaign_preview'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
278
279 $content .= '<p>';
280
281 $content .= sprintf(
282 wp_kses(
283 /* translators: %s - Charitable doc link. */
284 __( 'You are viewing a preview of a <a href="%s" target="_blank">published campaign</a>. If this does not match your campaign page in the campaign builder, save your changes and then refresh this page. Your current theme can also effect the look of this page.', 'charitable' ),
285 [
286 'a' => [
287 'href' => [],
288 'target' => [],
289 'rel' => [],
290 ],
291 ]
292 ),
293 esc_url( get_permalink( $this->campaign_data['id'] ) )
294 );
295
296 $content .= '</p>';
297
298 $content .= '<p>' . esc_html__( 'Some functionality and elements (like donation buttons, forms, social sharing, etc.) are disabled.', 'charitable' ) . '</p>';
299
300 } else {
301
302 $content .= '<p>';
303
304 $content .= sprintf(
305 wp_kses(
306 /* translators: %s - Charitable doc link. */
307 __( 'This is a preview of the latest saved revision of your campaign page. If this does not match your campaign page in the campaign builder, save your changes and then refresh this page. Your current theme can also effect the look of this page.', 'charitable' ),
308 [
309 'a' => [
310 'href' => [],
311 'target' => [],
312 'rel' => [],
313 ],
314 ]
315 ),
316 esc_url( get_permalink( $this->campaign_data['id'] ) )
317 );
318
319 $content .= '</p>';
320
321 $content .= '<p>' . esc_html__( 'Some functionality and elements (like donation buttons, forms, social sharing, etc.) are disabled.', 'charitable' ) . '</p>';
322
323 }
324
325 array_unshift(
326 $links,
327 [
328 'url' => esc_url(
329 get_permalink( $this->campaign_data['id'] )
330 ),
331 'text' => esc_html__( 'View Live Campaign', 'charitable' ),
332 ]
333 );
334
335 elseif ( 'draft' === get_post_status( $this->campaign_data['id'] ) ) :
336
337 $content .= '<p>';
338
339 $content .= sprintf(
340 wp_kses(
341 /* translators: %s - Charitable doc link. */
342 __( 'This is a preview of the latest saved revision of your campaign page. If this does not match your campaign page in the campaign builder, save your changes and then refresh this page. Your current theme can also effect the look of this page.', 'charitable' ),
343 [
344 'a' => [
345 'href' => [],
346 'target' => [],
347 'rel' => [],
348 ],
349 ]
350 ),
351 esc_url( get_permalink( $this->campaign_data['id'] ) )
352 );
353
354 $content .= '</p>';
355
356 $content .= '<p>' . esc_html__( 'Some functionality and elements (like donation buttons, forms, social sharing, etc.) are disabled.', 'charitable' ) . '</p>';
357
358 endif;
359
360 if ( ! empty( $links ) ) {
361 $content .= '<span class="charitable-preview-notice-links">';
362
363 foreach ( $links as $key => $link ) {
364 $content .= '<a href="' . $link['url'] . '">' . $link['text'] . '</a>';
365 $l = array_keys( $links );
366
367 if ( end( $l ) !== $key ) {
368 $content .= ' <span style="display:inline-block;margin:0 6px;opacity: 0.5">|</span> ';
369 }
370 }
371
372 $content .= '</span>';
373 }
374 $content .= '</p>';
375
376 $content .= '</div>';
377
378 // if there is no campaign ID in the campaign_data, this MIGHT be an initial preview on a new campaign which means it should be in the URL.
379 $campaign_id = 0 === intval( $this->campaign_data['id'] ) && ! empty( $_GET['charitable_campaign_preview'] ) ? absint( $_GET['charitable_campaign_preview'] ) : absint( $this->campaign_data['id'] ); // phpcs:ignore
380
381 // Detach this handler from the content filters before rendering the campaign.
382 // The campaign render re-applies the_content (via the campaign's get_content), and
383 // with certain content-processing plugins active that re-application would re-enter
384 // this method and render the campaign again, looping until PHP's memory limit is
385 // exhausted (HelpScout #11576). The campaign endpoint already guards its own render
386 // the same way. The finally re-adds the filters unconditionally, so even if the
387 // render throws they are never left detached for the rest of the request.
388 remove_filter( 'the_content', [ $this, 'the_content' ], 999 );
389 remove_filter( 'get_the_excerpt', [ $this, 'the_content' ], 999 );
390
391 try {
392 $content .= do_shortcode( '[campaign version="2" id=' . $campaign_id . ']' );
393 } finally {
394 add_filter( 'the_content', [ $this, 'the_content' ], 999 );
395 add_filter( 'get_the_excerpt', [ $this, 'the_content' ], 999 );
396 }
397
398 return $content;
399 }
400
401 /**
402 * Force page template types.
403 *
404 * @since 1.8.0
405 *
406 * @param array $templates A list of template candidates, in descending order of priority.
407 *
408 * @return array
409 */
410 public function force_page_template_hierarchy( $templates ) { // phpcs:ignore
411
412 return [ 'page.php', 'single.php', 'index.php' ];
413 }
414
415 /**
416 * Adjust value of the {page_title} smart tag.
417 *
418 * @since 1.7.7
419 *
420 * @param string $content Content.
421 * @param array $campaign_data Campaign data.
422 * @param array $fields List of fields.
423 * @param string $entry_id Entry ID.
424 * @param object $smart_tag_object The smart tag object or the Generic object for those cases when class unregistered.
425 *
426 * @return string
427 */
428 public function smart_tags_process_page_title_value( $content, $campaign_data, $fields, $entry_id, $smart_tag_object ) { // phpcs:ignore
429
430 return sprintf( /* translators: %s - campaign title. */
431 esc_html__( '%s Preview', 'charitable' ),
432 ! empty( $campaign_data['settings']['campaign_title'] ) ? sanitize_text_field( $campaign_data['settings']['campaign_title'] ) : esc_html__( 'Campaign', 'charitable' )
433 );
434 }
435
436 /**
437 * Force page template types.
438 *
439 * @since 1.5.1
440 * @deprecated 1.8.0
441 *
442 * @return string
443 */
444 public function template_include() {
445
446 _deprecated_function( __METHOD__, '1.8.0 of the Charitable plugin' );
447
448 return locate_template( [ 'page.php', 'single.php', 'index.php' ] );
449 }
450 }
451
452 endif;
453
454 new Campaign_Builder_Preview();
455