Completed
Push — develop ( 507546...65eb42 )
by Zack
05:37
created

includes/class-oembed.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * GravityView oEmbed handling
4
 *
5
 * @package   GravityView
6
 * @license   GPL2+
7
 * @author    Katz Web Services, Inc.
8
 * @link      http://gravityview.co
9
 * @copyright Copyright 2014, Katz Web Services, Inc.
10
 * @since 1.6
11
 */
12
13
if ( ! defined( 'ABSPATH' ) ) {
14
	die;
15
}
16
17
/**
18
 * Register oEmbed handlers for embedding GravityView data and render that data
19
 *
20
 * @since 1.6
21
 */
22
class GravityView_oEmbed {
23
24
	protected $output = array();
25
	protected $entry_id = NULL;
26
	protected $view_id = NULL;
27
	protected $is_full_oembed_preview = false;
28
29
	static $instance = NULL;
30
31
	private function __construct() {}
32
33
	private function initialize() {
34
35
		add_action( 'init', array( $this, 'register_handler' ) );
36
37
	}
38
39
	/**
40
	 * @return GravityView_oEmbed
41
	 * @since 1.6
42
	 */
43
	static function getInstance() {
44
45
		if( empty( self::$instance ) ) {
46
			self::$instance = new self;
47
48
			self::$instance->initialize();
49
		}
50
51
		return self::$instance;
52
	}
53
54
	/**
55
	 * Register the oEmbed handler
56
	 *
57
	 * @since 1.6
58
	 * @uses get_handler_regex
59
	 */
60
	function register_handler() {
61
62
		wp_embed_register_handler( 'gravityview_entry', $this->get_handler_regex(), array( $this, 'render_handler' ), 20000 );
63
64
	}
65
66
	/**
67
	 * Generate the Regular expression that matches embedded entries.
68
	 *
69
	 * Generates different regex if using permalinks and if not using permalinks
70
	 *
71
	 * @since 1.6
72
	 *
73
	 * @return string Regex code
74
	 */
75
	private function get_handler_regex() {
76
77
		if ( function_exists( 'gravityview' ) ) {
78
			$entry_var_name = \GV\Entry::get_endpoint_name();
79
		} else {
80
			/** Deprecated. Use \GV\Entry::get_endpoint_name instead. */
81
			$entry_var_name = GravityView_Post_Types::get_entry_var_name();
82
		}
83
84
		/**
85
		 * @filter `gravityview_slug` Modify the url part for a View. [Read the doc](http://docs.gravityview.co/article/62-changing-the-view-slug)
86
		 * @param string $rewrite_slug The slug shown in the URL
87
		 */
88
		$rewrite_slug = apply_filters( 'gravityview_slug', 'view' );
89
90
		// Only support embeds for current site
91
		$prefix = trailingslashit( home_url() );
92
93
		// Using permalinks
94
		$using_permalinks = $prefix . "(?P<is_cpt>{$rewrite_slug})?/?(?P<slug>.+?)/{$entry_var_name}/(?P<entry_slug>.+?)/?\$";
95
96
		// Not using permalinks
97
		$not_using_permalinks = $prefix . "(?:index.php)?\?(?P<is_cpt2>[^=]+)=(?P<slug2>[^&]+)&entry=(?P<entry_slug2>[^&]+)\$";
98
99
		// Catch either
100
		$match_regex = "(?:{$using_permalinks}|{$not_using_permalinks})";
101
102
		return '#'.$match_regex.'#i';
103
	}
104
105
	/**
106
	 * Get the post ID from an URL
107
	 *
108
	 * This is necessary because url_to_postid() doesn't work with permalinks off for custom post types
109
	 *
110
	 * @uses url_to_postid()
111
	 * @since 1.6
112
	 *
113
	 * @param string $url URL to get the post ID from
114
	 * @param string $slug The name of a post, used as backup way of checking for post ID
115
	 * @return int 0 if not found; int of URL post ID otherwise
116
	 */
117
	private function get_postid_from_url_and_slug( $url = '', $slug = '' ) {
118
119
		$post_id = url_to_postid( $url );
120
121
		if( empty( $post_id ) ) {
122
123
			$args = array(
124
				'post_status' => 'publish',
125
				'name' => $slug,
126
				'post_type' => array('any', 'gravityview'),
127
			);
128
129
			$posts = get_posts( $args );
130
131
			if( !empty( $posts ) ) {
132
				$post_id = $posts[0]->ID;
133
			}
134
		}
135
136
		return $post_id;
137
	}
138
139
	/**
140
	 * Get the entry id for the current oEmbedded entry
141
	 *
142
	 * @since 1.6
143
	 *
144
	 * @return int|null
145
	 */
146
	public function get_entry_id() {
147
		return $this->entry_id;
148
	}
149
150
	/**
151
	 *
152
	 *
153
	 * @since 1.6
154
	 * @see GravityView_oEmbed::add_providers() for the regex
155
	 *
156
	 * @param array $matches The regex matches from the provided regex when calling wp_embed_register_handler()
157
	 * @param array $attr Embed attributes.
158
	 * @param string $url The original URL that was matched by the regex.
159
	 * @param array $rawattr The original unmodified attributes.
160
	 * @return string The embed HTML.
161
	 */
162
	public function render_handler( $matches, $attr, $url, $rawattr ) {
163
164
		// If not using permalinks, re-assign values for matching groups
165
		if( !empty( $matches['entry_slug2'] ) ) {
166
			$matches['is_cpt'] = $matches['is_cpt2'];
167
			$matches['slug'] = $matches['slug2'];
168
			$matches['entry_slug'] = $matches['entry_slug2'];
169
			unset( $matches['is_cpt2'], $matches['slug2'], $matches['entry_slug2'] );
170
		}
171
172
		// No Entry was found
173
		if( empty( $matches['entry_slug'] ) ) {
174
175
			do_action('gravityview_log_error', 'GravityView_oEmbed[render_handler] $entry_slug not parsed by regex.', $matches );
176
177
			return '';
178
		}
179
180
		$return = '';
181
182
		// Setup the data used
183
		$this->set_vars( $matches, $attr, $url, $rawattr );
184
185
		if( is_admin() && !$this->is_full_oembed_preview ) {
186
			$return = $this->render_admin( $matches, $attr, $url, $rawattr );
187
		} else {
188
189
			if( $this->is_full_oembed_preview ) {
190
				$return .= $this->generate_preview_notice();
191
			}
192
193
			$return .= $this->render_frontend( $matches, $attr, $url, $rawattr );
194
		}
195
196
		return $return;
197
	}
198
199
200
	/**
201
	 * Generate a warning to users when previewing oEmbed in the Add Media modal
202
	 *
203
	 * @return string HTML notice
204
	 */
205
	private function generate_preview_notice() {
206
		$floaty = GravityView_Admin::get_floaty();
207
		$title = esc_html__( 'This will look better when it is embedded.', 'gravityview' );
208
		$message = esc_html__('Styles don\'t get loaded when being previewed, so the content below will look strange. Don\'t be concerned!', 'gravityview');
209
		return '<div class="updated notice">'. $floaty. '<h3>'.$title.'</h3><p>'.$message.'</p><br style="clear:both;" /></div>';
210
	}
211
212
	/**
213
	 * Set entry_id and view_id from the data sent to render_handler
214
	 *
215
	 * @var $entry_id
216
	 * @var $view_id
217
	 *
218
	 * @see render_handler
219
	 */
220
	private function set_vars( $matches, $attr, $url, $rawattr ) {
221
222
		$this->entry_id = $matches['entry_slug'];
223
224
		$post_id = $this->get_postid_from_url_and_slug( $url, $matches['slug'] );
225
226
		// The URL didn't have the View Custom Post Type structure.
227
		if( empty( $matches['is_cpt'] ) || $matches['is_cpt'] !== 'gravityview' ) {
228
229
			do_action('gravityview_log_debug', 'GravityView_oEmbed[render_handler] Embedding an entry inside a post or page', $matches );
230
231
			if ( function_exists( 'gravityview' ) && $post = get_post( $post_id ) ) {
232
				$views = \GV\View_Collection::from_post( $post );
233
				$views = $views->all();
234
				if ( ! empty( $views ) )
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
235
					$this->view_id = $views[0]->ID;
236
			} else {
237
				/** Deprecated. */
238
				$this->view_id = GravityView_View_Data::getInstance()->maybe_get_view_id( $post_id );
0 ignored issues
show
Deprecated Code introduced by
The method GravityView_View_Data::maybe_get_view_id() has been deprecated.

This method has been deprecated.

Loading history...
239
			}
240
241
		} else {
242
243
			$this->view_id = $post_id;
244
245
		}
246
247
		// The inline content has $_POST['type'] set to "embed", while the "Add Media" modal doesn't set that.
248
		$this->is_full_oembed_preview = ( isset( $_POST['action'] ) && $_POST['action'] === 'parse-embed' && !isset( $_POST['type'] ) );
249
	}
250
251
	/**
252
	 * Display a nice placeholder in the admin for the entry
253
	 *
254
	 * @param array $matches The regex matches from the provided regex when calling wp_embed_register_handler()
255
	 * @param array $attr Embed attributes.
256
	 * @param string $url The original URL that was matched by the regex.
257
	 * @param array $rawattr The original unmodified attributes.
258
	 * @return string The embed HTML.
259
	 */
260
	private function render_admin( $matches, $attr, $url, $rawattr ) {
261
		global $wp_version;
262
263
		// Floaty the astronaut
264
		$image = GravityView_Admin::get_floaty();
265
266
		$embed_heading = sprintf( esc_html__('Embed Entry %d', 'gravityview'), $this->entry_id );
267
268
		$embed_text = sprintf( esc_html__('This entry will be displayed as it is configured in View %d', 'gravityview'), $this->view_id );
269
270
		return '
271
		<div class="loading-placeholder" style="background-color:#e6f0f5;">
272
			<h3 style="margin:0; padding:0; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, Oxygen-Sans, Ubuntu, Cantarell, \'Helvetica Neue\', sans-serif;">'.$image.$embed_heading.'</h3>
273
			<p style="margin:0; padding:0; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, Oxygen-Sans, Ubuntu, Cantarell, \'Helvetica Neue\', sans-serif;">
274
				'.$embed_text.'
275
			</p>
276
			<br style="clear: both;">
277
		</div>';
278
279
	}
280
281
	private function generate_entry_output() {
282
283
		// Tell get_gravityview() to display a single entry
284
		add_filter( 'gravityview/is_single_entry', array( $this, 'set_single_entry_id' ) );
285
286
		ob_start();
287
288
		// Print the entry as configured in View
289
		the_gravityview( $this->view_id );
290
291
		$view_html = ob_get_clean();
292
293
		// Clean up the filter
294
		remove_filter( 'gravityview/is_single_entry', array( $this, 'set_single_entry_id' ) );
295
296
		// Strip the new lines that are generated--when editing an entry in particular, scripts are printed that
297
		// then are passed through wpautop() and everything looks terrible.
298
		$view_html = str_replace( "\n", ' ', $view_html );
299
300
		return $view_html;
301
	}
302
303
	/**
304
	 * Tell get_gravityview() to display a single entry
305
	 *
306
	 * REQUIRED FOR THE VIEW TO OUTPUT A SINGLE ENTRY
307
	 *
308
	 * @param bool|int $is_single_entry Existing single entry. False, because GV thinks we're in a post or page.
309
	 *
310
	 * @return int The current entry ID
311
	 */
312
	public function set_single_entry_id( $is_single_entry = false ) {
313
314
		return $this->entry_id;
315
	}
316
317
	/**
318
	 * GravityView embed entry handler
319
	 *
320
	 * @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}.
321
	 * @param array $attr Embed attributes.
322
	 * @param string $url The original URL that was matched by the regex.
323
	 * @param array $rawattr The original unmodified attributes.
324
	 * @return string The embed HTML.
325
	 */
326
	private function render_frontend( $matches, $attr, $url, $rawattr ) {
327
328
		// If it's already been parsed, don't re-output it.
329
		if( !empty( $this->output[ $this->entry_id ] ) ) {
330
			return $this->output[ $this->entry_id ];
331
		}
332
333
		$entry_output = $this->generate_entry_output();
334
335
		// Wrap a container div around the output to allow for custom styling
336
		$output = sprintf('<div class="gravityview-oembed gravityview-oembed-entry gravityview-oembed-entry-'.$this->entry_id.'">%s</div>', $entry_output );
337
338
		/**
339
		 * @filter `gravityview/oembed/entry` Filter the output of the oEmbed entry embed
340
		 * @param string $output HTML of the embedded entry, with wrapper div
341
		 * @param GravityView_oEmbed $object The current GravityView_oEmbed instance
342
		 * @param array $atts Other passed parameters and info. \n
343
		 *  @var string $entry_output HTML of just the View output, without the wrapper \n
344
		 *  @var array  $matches Capture group matches from the regex \n
345
		 *  @var array $attr Embed attributes. \n
346
		 *  @var string $url The original URL that was matched by the regex. \n
347
		 *  @var array $rawattr The original unmodified attributes.
348
		 */
349
		$output = apply_filters('gravityview/oembed/entry', $output, $this, compact( $entry_output, $matches, $attr, $url, $rawattr ) );
350
351
		unset( $entry_output );
352
353
		$this->output[ $this->entry_id ] = $output;
354
355
		return $this->output[ $this->entry_id ];
356
357
	}
358
359
}
360
361
GravityView_oEmbed::getInstance();