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
|
|
|
add_action( 'init', array( $this, 'add_provider' ) ); |
37
|
|
|
|
38
|
|
|
if ( ! empty( $_GET['gv_oembed_provider'] ) && ! empty( $_GET['url'] ) ) { |
|
|
|
|
39
|
|
|
add_action( 'template_redirect', array( $this, 'render_provider_request' ) ); |
40
|
|
|
} |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @return GravityView_oEmbed |
45
|
|
|
* @since 1.6 |
46
|
|
|
*/ |
47
|
|
|
static function getInstance() { |
|
|
|
|
48
|
|
|
|
49
|
|
|
if( empty( self::$instance ) ) { |
50
|
|
|
self::$instance = new self; |
51
|
|
|
|
52
|
|
|
self::$instance->initialize(); |
53
|
|
|
} |
54
|
|
|
|
55
|
|
|
return self::$instance; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Register the oEmbed handler |
60
|
|
|
* |
61
|
|
|
* @since 1.6 |
62
|
|
|
* @uses get_handler_regex |
63
|
|
|
*/ |
64
|
|
|
function register_handler() { |
|
|
|
|
65
|
|
|
|
66
|
|
|
wp_embed_register_handler( 'gravityview_entry', $this->get_handler_regex(), array( $this, 'render_handler' ), 20000 ); |
67
|
|
|
|
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* Become an oEmbed provider for GravityView. |
72
|
|
|
* |
73
|
|
|
* @since 1.21.5.3 |
74
|
|
|
* |
75
|
|
|
* @return void |
76
|
|
|
*/ |
77
|
|
|
function add_provider() { |
|
|
|
|
78
|
|
|
wp_oembed_add_provider( $this->get_handler_regex(), add_query_arg( 'gv_oembed_provider', '1', site_url() ), true ); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* Output a response as a provider for an entry oEmbed URL. |
83
|
|
|
* |
84
|
|
|
* For now we only output the JSON format and don't care about the size (width, height). |
85
|
|
|
* Our only current use-case is for it to provide output to the Add Media / From URL box |
86
|
|
|
* in WordPress 4.8. |
87
|
|
|
* |
88
|
|
|
* @since 1.21.5.3 |
89
|
|
|
* |
90
|
|
|
* @return void |
91
|
|
|
*/ |
92
|
|
|
function render_provider_request() { |
|
|
|
|
93
|
|
|
if ( ! empty( $_GET['url'] ) ) { |
94
|
|
|
$url = $_GET['url']; |
|
|
|
|
95
|
|
|
} else { |
96
|
|
|
header( 'HTTP/1.0 404 Not Found' ); |
97
|
|
|
exit; |
|
|
|
|
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
preg_match( $this->get_handler_regex(), $url, $matches ); |
101
|
|
|
|
102
|
|
|
// If not using permalinks, re-assign values for matching groups |
103
|
|
|
if ( ! empty( $matches['entry_slug2'] ) ) { |
104
|
|
|
$matches['is_cpt'] = $matches['is_cpt2']; |
105
|
|
|
$matches['slug'] = $matches['slug2']; |
106
|
|
|
$matches['entry_slug'] = $matches['entry_slug2']; |
107
|
|
|
unset( $matches['is_cpt2'], $matches['slug2'], $matches['entry_slug2'] ); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
// No Entry was found |
111
|
|
|
if ( empty( $matches['entry_slug'] ) ) { |
112
|
|
|
do_action('gravityview_log_error', 'GravityView_oEmbed[render_handler] $entry_slug not parsed by regex.', $matches ); |
|
|
|
|
113
|
|
|
header( 'HTTP/1.0 404 Not Found' ); |
114
|
|
|
exit; |
|
|
|
|
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
// Setup the data used |
118
|
|
|
$this->set_vars( $matches, null, $url, null ); |
119
|
|
|
|
120
|
|
|
echo json_encode( array( |
121
|
|
|
'version' => '1.0', |
122
|
|
|
'provider_name' => 'gravityview', |
123
|
|
|
'provider_url' => add_query_arg( 'gv_oembed_provider', '1', site_url() ), |
124
|
|
|
'html' => $this->generate_preview_notice() . $this->render_frontend( null, null, null, null ), |
125
|
|
|
) ); |
126
|
|
|
exit; |
|
|
|
|
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Generate the Regular expression that matches embedded entries. |
131
|
|
|
* |
132
|
|
|
* Generates different regex if using permalinks and if not using permalinks |
133
|
|
|
* |
134
|
|
|
* @since 1.6 |
135
|
|
|
* |
136
|
|
|
* @return string Regex code |
137
|
|
|
*/ |
138
|
|
|
private function get_handler_regex() { |
139
|
|
|
|
140
|
|
|
if ( defined( 'GRAVITYVIEW_FUTURE_CORE_LOADED' ) ) { |
141
|
|
|
$entry_var_name = \GV\Entry::get_endpoint_name(); |
142
|
|
|
} else { |
143
|
|
|
/** Deprecated. Use \GV\Entry::get_endpoint_name instead. */ |
144
|
|
|
$entry_var_name = GravityView_Post_Types::get_entry_var_name(); |
|
|
|
|
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* @filter `gravityview_slug` Modify the url part for a View. [Read the doc](http://docs.gravityview.co/article/62-changing-the-view-slug) |
149
|
|
|
* @param string $rewrite_slug The slug shown in the URL |
150
|
|
|
*/ |
151
|
|
|
$rewrite_slug = apply_filters( 'gravityview_slug', 'view' ); |
152
|
|
|
|
153
|
|
|
// Only support embeds for current site |
154
|
|
|
$prefix = trailingslashit( home_url() ); |
155
|
|
|
|
156
|
|
|
// Using permalinks |
157
|
|
|
$using_permalinks = $prefix . "(?P<is_cpt>{$rewrite_slug})?/?(?P<slug>.+?)/{$entry_var_name}/(?P<entry_slug>.+?)/?\$"; |
158
|
|
|
|
159
|
|
|
// Not using permalinks |
160
|
|
|
$not_using_permalinks = $prefix . "(?:index.php)?\?(?P<is_cpt2>[^=]+)=(?P<slug2>[^&]+)&entry=(?P<entry_slug2>[^&]+)\$"; |
|
|
|
|
161
|
|
|
|
162
|
|
|
// Catch either |
163
|
|
|
$match_regex = "(?:{$using_permalinks}|{$not_using_permalinks})"; |
164
|
|
|
|
165
|
|
|
return '#'.$match_regex.'#i'; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Get the post ID from an URL |
170
|
|
|
* |
171
|
|
|
* This is necessary because url_to_postid() doesn't work with permalinks off for custom post types |
172
|
|
|
* |
173
|
|
|
* @uses url_to_postid() |
174
|
|
|
* @since 1.6 |
175
|
|
|
* |
176
|
|
|
* @param string $url URL to get the post ID from |
177
|
|
|
* @param string $slug The name of a post, used as backup way of checking for post ID |
178
|
|
|
* @return int 0 if not found; int of URL post ID otherwise |
179
|
|
|
*/ |
180
|
|
|
private function get_postid_from_url_and_slug( $url = '', $slug = '' ) { |
181
|
|
|
|
182
|
|
|
$post_id = url_to_postid( $url ); |
183
|
|
|
|
184
|
|
|
if( empty( $post_id ) ) { |
185
|
|
|
|
186
|
|
|
$args = array( |
187
|
|
|
'post_status' => 'publish', |
188
|
|
|
'name' => $slug, |
189
|
|
|
'post_type' => array('any', 'gravityview'), |
|
|
|
|
190
|
|
|
); |
191
|
|
|
|
192
|
|
|
$posts = get_posts( $args ); |
193
|
|
|
|
194
|
|
|
if( !empty( $posts ) ) { |
|
|
|
|
195
|
|
|
$post_id = $posts[0]->ID; |
196
|
|
|
} |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
return $post_id; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* Get the entry id for the current oEmbedded entry |
204
|
|
|
* |
205
|
|
|
* @since 1.6 |
206
|
|
|
* |
207
|
|
|
* @return int|null |
208
|
|
|
*/ |
209
|
|
|
public function get_entry_id() { |
210
|
|
|
return $this->entry_id; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* |
215
|
|
|
* |
216
|
|
|
* @since 1.6 |
217
|
|
|
* @see GravityView_oEmbed::add_providers() for the regex |
218
|
|
|
* |
219
|
|
|
* @param array $matches The regex matches from the provided regex when calling wp_embed_register_handler() |
220
|
|
|
* @param array $attr Embed attributes. |
221
|
|
|
* @param string $url The original URL that was matched by the regex. |
222
|
|
|
* @param array $rawattr The original unmodified attributes. |
223
|
|
|
* @return string The embed HTML. |
224
|
|
|
*/ |
225
|
|
|
public function render_handler( $matches, $attr, $url, $rawattr ) { |
226
|
|
|
|
227
|
|
|
// If not using permalinks, re-assign values for matching groups |
228
|
|
|
if( !empty( $matches['entry_slug2'] ) ) { |
|
|
|
|
229
|
|
|
$matches['is_cpt'] = $matches['is_cpt2']; |
230
|
|
|
$matches['slug'] = $matches['slug2']; |
231
|
|
|
$matches['entry_slug'] = $matches['entry_slug2']; |
232
|
|
|
unset( $matches['is_cpt2'], $matches['slug2'], $matches['entry_slug2'] ); |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
// No Entry was found |
236
|
|
|
if( empty( $matches['entry_slug'] ) ) { |
237
|
|
|
|
238
|
|
|
do_action('gravityview_log_error', 'GravityView_oEmbed[render_handler] $entry_slug not parsed by regex.', $matches ); |
|
|
|
|
239
|
|
|
|
240
|
|
|
return ''; |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
$return = ''; |
244
|
|
|
|
245
|
|
|
// Setup the data used |
246
|
|
|
$this->set_vars( $matches, $attr, $url, $rawattr ); |
247
|
|
|
|
248
|
|
|
if( is_admin() && !$this->is_full_oembed_preview ) { |
|
|
|
|
249
|
|
|
$return = $this->render_admin( $matches, $attr, $url, $rawattr ); |
250
|
|
|
} else { |
251
|
|
|
|
252
|
|
|
if( $this->is_full_oembed_preview ) { |
253
|
|
|
$return .= $this->generate_preview_notice(); |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
$return .= $this->render_frontend( $matches, $attr, $url, $rawattr ); |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
return $return; |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* Generate a warning to users when previewing oEmbed in the Add Media modal |
265
|
|
|
* |
266
|
|
|
* @return string HTML notice |
267
|
|
|
*/ |
268
|
|
|
private function generate_preview_notice() { |
269
|
|
|
$floaty = GravityView_Admin::get_floaty(); |
270
|
|
|
$title = esc_html__( 'This will look better when it is embedded.', 'gravityview' ); |
271
|
|
|
$message = esc_html__('Styles don\'t get loaded when being previewed, so the content below will look strange. Don\'t be concerned!', 'gravityview'); |
|
|
|
|
272
|
|
|
return '<div class="updated notice">'. $floaty. '<h3>'.$title.'</h3><p>'.$message.'</p><br style="clear:both;" /></div>'; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* Set entry_id and view_id from the data sent to render_handler |
277
|
|
|
* |
278
|
|
|
* @var $entry_id |
279
|
|
|
* @var $view_id |
280
|
|
|
* |
281
|
|
|
* @see render_handler |
282
|
|
|
*/ |
283
|
1 |
|
private function set_vars( $matches, $attr, $url, $rawattr ) { |
|
|
|
|
284
|
|
|
|
285
|
1 |
|
$this->entry_id = $matches['entry_slug']; |
286
|
|
|
|
287
|
1 |
|
$post_id = $this->get_postid_from_url_and_slug( $url, $matches['slug'] ); |
288
|
|
|
|
289
|
|
|
// The URL didn't have the View Custom Post Type structure. |
290
|
1 |
|
if( empty( $matches['is_cpt'] ) || $matches['is_cpt'] !== 'gravityview' ) { |
|
|
|
|
291
|
|
|
|
292
|
1 |
|
do_action('gravityview_log_debug', 'GravityView_oEmbed[render_handler] Embedding an entry inside a post or page', $matches ); |
|
|
|
|
293
|
|
|
|
294
|
1 |
|
if ( defined( 'GRAVITYVIEW_FUTURE_CORE_LOADED' ) && $post = get_post( $post_id ) ) { |
295
|
1 |
|
$views = \GV\View_Collection::from_post( $post ); |
296
|
1 |
|
$views = $views->all(); |
297
|
1 |
|
if ( ! empty( $views ) ) { |
298
|
|
|
/** maybe_get_view_id has a side-effect that adds retrieved views to the global scope */ |
299
|
1 |
|
foreach ( $views as $view ) { |
300
|
1 |
|
if ( \GV\View::exists( $view->ID ) && ! gravityview()->views->contains( $view->ID ) ) { |
|
|
|
|
301
|
1 |
|
gravityview()->views->add( $view ); |
|
|
|
|
302
|
|
|
} |
303
|
|
|
} |
304
|
|
|
|
305
|
1 |
|
$this->view_id = $views[0]->ID; |
306
|
|
|
} |
307
|
|
|
} else { |
308
|
|
|
/** Deprecated. */ |
309
|
1 |
|
$this->view_id = GravityView_View_Data::getInstance()->maybe_get_view_id( $post_id ); |
|
|
|
|
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
} else { |
313
|
|
|
|
314
|
|
|
$this->view_id = $post_id; |
315
|
|
|
|
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
// The inline content has $_POST['type'] set to "embed", while the "Add Media" modal doesn't set that. |
319
|
1 |
|
$this->is_full_oembed_preview = ( isset( $_POST['action'] ) && $_POST['action'] === 'parse-embed' && !isset( $_POST['type'] ) ); |
|
|
|
|
320
|
1 |
|
} |
321
|
|
|
|
322
|
|
|
/** |
323
|
|
|
* Display a nice placeholder in the admin for the entry |
324
|
|
|
* |
325
|
|
|
* @param array $matches The regex matches from the provided regex when calling wp_embed_register_handler() |
326
|
|
|
* @param array $attr Embed attributes. |
327
|
|
|
* @param string $url The original URL that was matched by the regex. |
328
|
|
|
* @param array $rawattr The original unmodified attributes. |
329
|
|
|
* @return string The embed HTML. |
330
|
|
|
*/ |
331
|
|
|
private function render_admin( $matches, $attr, $url, $rawattr ) { |
|
|
|
|
332
|
|
|
global $wp_version; |
|
|
|
|
333
|
|
|
|
334
|
|
|
// Floaty the astronaut |
335
|
|
|
$image = GravityView_Admin::get_floaty(); |
336
|
|
|
|
337
|
|
|
$embed_heading = sprintf( esc_html__('Embed Entry %d', 'gravityview'), $this->entry_id ); |
|
|
|
|
338
|
|
|
|
339
|
|
|
$embed_text = sprintf( esc_html__('This entry will be displayed as it is configured in View %d', 'gravityview'), $this->view_id ); |
|
|
|
|
340
|
|
|
|
341
|
|
|
return ' |
342
|
|
|
<div class="loading-placeholder" style="background-color:#e6f0f5;"> |
343
|
|
|
<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> |
344
|
|
|
<p style="margin:0; padding:0; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, Oxygen-Sans, Ubuntu, Cantarell, \'Helvetica Neue\', sans-serif;"> |
345
|
|
|
'.$embed_text.' |
346
|
|
|
</p> |
347
|
|
|
<br style="clear: both;"> |
348
|
|
|
</div>'; |
349
|
|
|
|
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
private function generate_entry_output() { |
353
|
|
|
|
354
|
|
|
// Tell get_gravityview() to display a single entry |
355
|
|
|
add_filter( 'gravityview/is_single_entry', array( $this, 'set_single_entry_id' ) ); |
356
|
|
|
|
357
|
|
|
ob_start(); |
358
|
|
|
|
359
|
|
|
// Print the entry as configured in View |
360
|
|
|
the_gravityview( $this->view_id ); |
361
|
|
|
|
362
|
|
|
$view_html = ob_get_clean(); |
363
|
|
|
|
364
|
|
|
// Clean up the filter |
365
|
|
|
remove_filter( 'gravityview/is_single_entry', array( $this, 'set_single_entry_id' ) ); |
366
|
|
|
|
367
|
|
|
// Strip the new lines that are generated--when editing an entry in particular, scripts are printed that |
368
|
|
|
// then are passed through wpautop() and everything looks terrible. |
369
|
|
|
$view_html = str_replace( "\n", ' ', $view_html ); |
370
|
|
|
|
371
|
|
|
return $view_html; |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
/** |
375
|
|
|
* Tell get_gravityview() to display a single entry |
376
|
|
|
* |
377
|
|
|
* REQUIRED FOR THE VIEW TO OUTPUT A SINGLE ENTRY |
378
|
|
|
* |
379
|
|
|
* @param bool|int $is_single_entry Existing single entry. False, because GV thinks we're in a post or page. |
380
|
|
|
* |
381
|
|
|
* @return int The current entry ID |
382
|
|
|
*/ |
383
|
|
|
public function set_single_entry_id( $is_single_entry = false ) { |
|
|
|
|
384
|
|
|
|
385
|
|
|
return $this->entry_id; |
386
|
|
|
} |
387
|
|
|
|
388
|
|
|
/** |
389
|
|
|
* GravityView embed entry handler |
390
|
|
|
* |
391
|
|
|
* @param array $matches The regex matches from the provided regex when calling {@link wp_embed_register_handler()}. |
392
|
|
|
* @param array $attr Embed attributes. |
393
|
|
|
* @param string $url The original URL that was matched by the regex. |
394
|
|
|
* @param array $rawattr The original unmodified attributes. |
395
|
|
|
* @return string The embed HTML. |
396
|
|
|
*/ |
397
|
|
|
private function render_frontend( $matches, $attr, $url, $rawattr ) { |
398
|
|
|
|
399
|
|
|
// If it's already been parsed, don't re-output it. |
400
|
|
|
if( !empty( $this->output[ $this->entry_id ] ) ) { |
|
|
|
|
401
|
|
|
return $this->output[ $this->entry_id ]; |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
$entry_output = $this->generate_entry_output(); |
405
|
|
|
|
406
|
|
|
// Wrap a container div around the output to allow for custom styling |
407
|
|
|
$output = sprintf('<div class="gravityview-oembed gravityview-oembed-entry gravityview-oembed-entry-'.$this->entry_id.'">%s</div>', $entry_output ); |
|
|
|
|
408
|
|
|
|
409
|
|
|
/** |
410
|
|
|
* @filter `gravityview/oembed/entry` Filter the output of the oEmbed entry embed |
411
|
|
|
* @param string $output HTML of the embedded entry, with wrapper div |
412
|
|
|
* @param GravityView_oEmbed $object The current GravityView_oEmbed instance |
413
|
|
|
* @param array $atts Other passed parameters and info. \n |
414
|
|
|
* @var string $entry_output HTML of just the View output, without the wrapper \n |
415
|
|
|
* @var array $matches Capture group matches from the regex \n |
416
|
|
|
* @var array $attr Embed attributes. \n |
417
|
|
|
* @var string $url The original URL that was matched by the regex. \n |
418
|
|
|
* @var array $rawattr The original unmodified attributes. |
419
|
|
|
*/ |
420
|
|
|
$output = apply_filters('gravityview/oembed/entry', $output, $this, compact( $entry_output, $matches, $attr, $url, $rawattr ) ); |
|
|
|
|
421
|
|
|
|
422
|
|
|
unset( $entry_output ); |
423
|
|
|
|
424
|
|
|
$this->output[ $this->entry_id ] = $output; |
425
|
|
|
|
426
|
|
|
return $this->output[ $this->entry_id ]; |
427
|
|
|
|
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
} |
431
|
|
|
|
432
|
|
|
GravityView_oEmbed::getInstance(); |
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.