1
|
|
|
<?php |
|
|
|
|
2
|
|
|
namespace GV; |
3
|
|
|
|
4
|
|
|
/** If this file is called directly, abort. */ |
5
|
|
|
if ( ! defined( 'GRAVITYVIEW_DIR' ) ) { |
6
|
|
|
die(); |
7
|
|
|
} |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* oEmbed functionality for GravityView |
11
|
|
|
*/ |
12
|
|
|
class oEmbed { |
|
|
|
|
13
|
|
|
public static $provider_url = ''; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Initialize. |
17
|
|
|
* |
18
|
|
|
* Register the oEmbed handler and the provider. |
19
|
|
|
* Fire off the provider handler if detected. |
20
|
|
|
* |
21
|
|
|
* @return void |
22
|
|
|
*/ |
23
|
|
|
public static function init() { |
24
|
|
|
self::$provider_url = add_query_arg( 'gv_oembed_provider', '1', site_url() ); |
25
|
|
|
|
26
|
|
|
wp_embed_register_handler( 'gravityview_entry', self::get_entry_regex(), array( __CLASS__, 'render' ), 20000 ); |
27
|
|
|
wp_oembed_add_provider( self::get_entry_regex(), self::$provider_url, true ); |
28
|
|
|
|
29
|
|
|
if ( ! empty( $_GET['gv_oembed_provider'] ) && ! empty( $_GET['url'] ) ) { |
|
|
|
|
30
|
|
|
add_action( 'template_redirect', array( __CLASS__, 'render_provider_request' ) ); |
31
|
|
|
} |
32
|
|
|
|
33
|
|
|
add_action( 'pre_oembed_result', array( __CLASS__, 'pre_oembed_result' ), 11, 3 ); |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Output a response as a provider for an entry oEmbed URL. |
38
|
|
|
* |
39
|
|
|
* For now we only output the JSON format and don't care about the size (width, height). |
40
|
|
|
* Our only current use-case is for it to provide output to the Add Media / From URL box |
41
|
|
|
* in WordPress 4.8. |
42
|
|
|
* |
43
|
|
|
* @return void |
44
|
|
|
*/ |
45
|
|
|
public static function render_provider_request() { |
46
|
|
|
if ( ! empty( $_GET['url'] ) ) { |
47
|
|
|
$url = $_GET['url']; |
|
|
|
|
48
|
|
|
} else { |
49
|
|
|
header( 'HTTP/1.0 404 Not Found' ); |
50
|
|
|
exit; |
|
|
|
|
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
/** Parse the URL to an entry and a view */ |
54
|
|
|
preg_match( self::get_entry_regex(), $url, $matches ); |
55
|
|
|
$result = self::parse_matches( $matches, $url ); |
56
|
|
|
if ( ! $result || count( $result ) != 2 ) { |
|
|
|
|
57
|
|
|
header( 'HTTP/1.0 404 Not Found' ); |
58
|
|
|
exit; |
|
|
|
|
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
list( $view, $entry ) = $result; |
62
|
|
|
|
63
|
|
|
echo json_encode( array( |
64
|
|
|
'version' => '1.0', |
65
|
|
|
'provider_name' => 'gravityview', |
66
|
|
|
'provider_url' => self::$provider_url, |
67
|
|
|
'html' => self::render_preview_notice() . self::render_frontend( $view, $entry ), |
68
|
|
|
) ); |
69
|
|
|
exit; |
|
|
|
|
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Output the embed HTML. |
74
|
|
|
* |
75
|
|
|
* @param array $matches The regex matches from the provided regex when calling wp_embed_register_handler() |
76
|
|
|
* @param array $attr Embed attributes. |
77
|
|
|
* @param string $url The original URL that was matched by the regex. |
78
|
|
|
* @param array $rawattr The original unmodified attributes. |
79
|
|
|
* |
80
|
|
|
* @return string The embed HTML. |
81
|
|
|
*/ |
82
|
3 |
|
public static function render( $matches, $attr, $url, $rawattr ) { |
|
|
|
|
83
|
|
|
|
84
|
3 |
|
$result = self::parse_matches( $matches, $url ); |
85
|
|
|
|
86
|
3 |
|
if ( ! $result || count( $result ) != 2 ) { |
|
|
|
|
87
|
1 |
|
gravityview()->log->notice( 'View or entry could not be parsed in oEmbed url {url}', array( 'url' => $url, 'matches' => $matches ) ); |
88
|
1 |
|
return __( 'You are not allowed to view this content.', 'gravityview' ); |
89
|
|
|
} |
90
|
|
|
|
91
|
3 |
|
list( $view, $entry ) = $result; |
92
|
|
|
|
93
|
3 |
|
if ( Request::is_ajax() && ! Request::is_add_oembed_preview() ) { |
94
|
|
|
/** Render a nice placeholder in the Visual mode. */ |
95
|
|
|
return self::render_admin( $view, $entry ); |
96
|
3 |
|
} else if ( Request::is_add_oembed_preview() ) { |
97
|
|
|
/** Prepend a preview notice in Add Media / From URL screen */ |
98
|
|
|
return self::render_preview_notice() . self::render_frontend( $view, $entry ); |
99
|
|
|
} |
100
|
|
|
|
101
|
3 |
|
return self::render_frontend( $view, $entry ); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Parse oEmbed regex matches and return View and Entry. |
106
|
|
|
* |
107
|
|
|
* @param array $matches The regex matches. |
108
|
|
|
* @param string $url The URL of the embed. |
109
|
|
|
* |
110
|
|
|
* @return array (\GV\View, \GV\Entry) |
111
|
|
|
*/ |
112
|
2 |
|
private static function parse_matches( $matches, $url ) { |
113
|
|
|
// If not using permalinks, re-assign values for matching groups |
114
|
2 |
|
if ( ! empty( $matches['entry_slug2'] ) ) { |
115
|
1 |
|
$matches['is_cpt'] = $matches['is_cpt2']; |
116
|
1 |
|
$matches['slug'] = $matches['slug2']; |
117
|
1 |
|
$matches['entry_slug'] = $matches['entry_slug2']; |
118
|
1 |
|
unset( $matches['is_cpt2'], $matches['slug2'], $matches['entry_slug2'] ); |
119
|
|
|
} |
120
|
|
|
|
121
|
2 |
|
if ( empty( $matches['entry_slug'] ) ) { |
122
|
|
|
gravityview()->log->error( 'Entry slug not parsed by regex.', array( 'data' => $matches ) ); |
123
|
|
|
return null; |
124
|
|
|
} else { |
125
|
2 |
|
$entry_id = $matches['entry_slug']; |
126
|
|
|
} |
127
|
|
|
|
128
|
2 |
|
if ( ! $entry = \GV\GF_Entry::by_id( $entry_id ) ) { |
129
|
|
|
gravityview()->log->error( 'Invalid entry ID {entry_id}', array( 'entry_id' => $entry_id ) ); |
130
|
|
|
return null; |
131
|
|
|
} |
132
|
|
|
|
133
|
2 |
|
if ( ! $view_id = url_to_postid( $url ) ) { |
134
|
2 |
|
$view = \GV\View::from_post( get_page_by_path( $matches['slug'], OBJECT, 'gravityview' ) ); |
135
|
|
|
} else { |
136
|
|
|
$view = \GV\View::by_id( $view_id ); |
137
|
|
|
} |
138
|
|
|
|
139
|
2 |
|
if ( ! $view ) { |
140
|
1 |
|
gravityview()->log->error( 'Could not detect view from URL {url}', array( 'url' => $url ) ); |
141
|
1 |
|
return null; |
142
|
|
|
} |
143
|
|
|
|
144
|
2 |
|
return array( $view, $entry ); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Display a nice placeholder in the admin for the entry. |
149
|
|
|
* |
150
|
|
|
* @param \GV\View $view The View. |
151
|
|
|
* @param \GV\Entry $entry The Entry. |
152
|
|
|
* |
153
|
|
|
* @return string A placeholder, with Mr. Floaty :) |
154
|
|
|
*/ |
155
|
|
|
private static function render_admin( $view, $entry ) { |
156
|
|
|
|
157
|
|
|
// Floaty the astronaut |
158
|
|
|
$image = \GravityView_Admin::get_floaty(); |
159
|
|
|
|
160
|
|
|
$embed_heading = sprintf( esc_html__( 'Embed Entry %d', 'gravityview' ), $entry->ID ); |
161
|
|
|
|
162
|
|
|
$embed_text = sprintf( esc_html__( 'This entry will be displayed as it is configured in View %d', 'gravityview' ), $view->ID ); |
|
|
|
|
163
|
|
|
|
164
|
|
|
return ' |
165
|
|
|
<div class="loading-placeholder" style="background-color:#e6f0f5;"> |
166
|
|
|
<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> |
167
|
|
|
<p style="margin:0; padding:0; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, Oxygen-Sans, Ubuntu, Cantarell, \'Helvetica Neue\', sans-serif;"> |
168
|
|
|
'.$embed_text.' |
169
|
|
|
</p> |
170
|
|
|
<br style="clear: both;"> |
171
|
|
|
</div>'; |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* Generate a warning to users when previewing oEmbed in the Add Media modal. |
176
|
|
|
* |
177
|
|
|
* @return string HTML notice |
178
|
|
|
*/ |
179
|
|
|
private static function render_preview_notice() { |
180
|
|
|
$floaty = \GravityView_Admin::get_floaty(); |
181
|
|
|
$title = esc_html__( 'This will look better when it is embedded.', 'gravityview' ); |
182
|
|
|
$message = esc_html__( 'Styles don\'t get loaded when being previewed, so the content below will look strange. Don\'t be concerned!', 'gravityview'); |
|
|
|
|
183
|
|
|
return '<div class="updated notice">'.$floaty.'<h3>'.$title.'</h3><p>'.$message.'</p><br style="clear:both;" /></div>'; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Render the entry as an oEmbed. |
188
|
|
|
* |
189
|
|
|
* @param \GV\View $view The View. |
190
|
|
|
* @param \GV\Entry $entry The Entry. |
191
|
|
|
* |
192
|
|
|
* @return string The rendered oEmbed. |
193
|
|
|
*/ |
194
|
2 |
|
private static function render_frontend( $view, $entry ) { |
195
|
|
|
/** Private, pending, draft, etc. */ |
196
|
2 |
|
$public_states = get_post_stati( array( 'public' => true ) ); |
197
|
2 |
|
if ( ! in_array( $view->post_status, $public_states ) && ! \GVCommon::has_cap( 'read_gravityview', $view->ID ) ) { |
|
|
|
|
198
|
1 |
|
gravityview()->log->notice( 'The current user cannot access this View #{view_id}', array( 'view_id' => $view->ID ) ); |
|
|
|
|
199
|
1 |
|
return __( 'You are not allowed to view this content.', 'gravityview' ); |
200
|
|
|
} |
201
|
|
|
|
202
|
2 |
|
if ( $entry && 'active' !== $entry['status'] ) { |
203
|
1 |
|
gravityview()->log->notice( 'Entry ID #{entry_id} is not active', array( 'entry_id' => $entry->ID ) ); |
204
|
1 |
|
return __( 'You are not allowed to view this content.', 'gravityview' ); |
205
|
|
|
} |
206
|
|
|
|
207
|
2 |
|
if ( $view->settings->get( 'show_only_approved' ) ) { |
208
|
1 |
|
if ( ! \GravityView_Entry_Approval_Status::is_approved( gform_get_meta( $entry->ID, \GravityView_Entry_Approval::meta_key ) ) ) { |
209
|
1 |
|
gravityview()->log->error( 'Entry ID #{entry_id} is not approved for viewing', array( 'entry_id' => $entry->ID ) ); |
210
|
1 |
|
return __( 'You are not allowed to view this content.', 'gravityview' ); |
211
|
|
|
} |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
/** |
215
|
|
|
* When this is embedded inside a view we should not display the widgets. |
216
|
|
|
*/ |
217
|
2 |
|
$request = gravityview()->request; |
218
|
2 |
|
$is_reembedded = false; // Assume not embedded unless detected otherwise. |
219
|
2 |
|
if ( in_array( get_class( $request ), array( 'GV\Frontend_Request', 'GV\Mock_Request' ) ) ) { |
220
|
2 |
|
if ( ( $_view = $request->is_view() ) && $_view->ID !== $view->ID ) { |
|
|
|
|
221
|
|
|
$is_reembedded = true; |
222
|
|
|
} |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Remove Widgets on a nested embedded View. |
227
|
|
|
* Also, don't show widgets if we're embedding an entry |
228
|
|
|
*/ |
229
|
2 |
|
if ( $is_reembedded || $entry ) { |
230
|
2 |
|
$view->widgets = new \GV\Widget_Collection(); |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
/** Remove the back link. */ |
234
|
2 |
|
add_filter( 'gravityview/template/links/back/url', '__return_false' ); |
235
|
|
|
|
236
|
2 |
|
$renderer = new \GV\Entry_Renderer(); |
237
|
2 |
|
$output = $renderer->render( $entry, $view, gravityview()->request ); |
238
|
2 |
|
$output = sprintf( '<div class="gravityview-oembed gravityview-oembed-entry gravityview-oembed-entry-%d">%s</div>', $entry->ID, $output ); |
239
|
|
|
|
240
|
2 |
|
remove_filter( 'gravityview/template/links/back/url', '__return_false' ); |
241
|
|
|
|
242
|
2 |
|
return $output; |
243
|
|
|
} |
244
|
|
|
|
245
|
|
|
/** |
246
|
|
|
* Generate the Regular expression that matches embedded entries. |
247
|
|
|
* |
248
|
|
|
* Generates different regex if using permalinks and if not using permalinks |
249
|
|
|
* |
250
|
|
|
* @return string Regex code |
251
|
|
|
*/ |
252
|
|
|
private static function get_entry_regex() { |
253
|
|
|
$entry_var_name = \GV\Entry::get_endpoint_name(); |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* @filter `gravityview_slug` Modify the url part for a View. [Read the doc](https://docs.gravityview.co/article/62-changing-the-view-slug) |
257
|
|
|
* @param string $rewrite_slug The slug shown in the URL |
258
|
|
|
*/ |
259
|
|
|
$rewrite_slug = apply_filters( 'gravityview_slug', 'view' ); |
260
|
|
|
|
261
|
|
|
// Only support embeds for current site |
262
|
|
|
$prefix = trailingslashit( home_url() ); |
263
|
|
|
|
264
|
|
|
// Using permalinks |
265
|
|
|
$using_permalinks = $prefix . "(?P<is_cpt>{$rewrite_slug})?/?(?P<slug>.+?)/{$entry_var_name}/(?P<entry_slug>.+?)/?\$"; |
266
|
|
|
|
267
|
|
|
// Not using permalinks |
268
|
|
|
$not_using_permalinks = $prefix . "(?:index.php)?\?(?P<is_cpt2>[^=]+)=(?P<slug2>[^&]+)&entry=(?P<entry_slug2>[^&]+)\$"; |
|
|
|
|
269
|
|
|
|
270
|
|
|
// Catch either |
271
|
|
|
$match_regex = "(?:{$using_permalinks}|{$not_using_permalinks})"; |
272
|
|
|
|
273
|
|
|
return '#'.$match_regex.'#i'; |
274
|
|
|
} |
275
|
|
|
|
276
|
|
|
/** |
277
|
|
|
* Internal oEmbed output, shortcircuit without proxying to the provider. |
278
|
|
|
*/ |
279
|
|
|
public static function pre_oembed_result( $result, $url, $args ) { |
|
|
|
|
280
|
|
|
if ( ! preg_match( self::get_entry_regex(), $url, $matches ) ) { |
281
|
|
|
return $result; |
282
|
|
|
} |
283
|
|
|
|
284
|
|
|
$view_entry = self::parse_matches( $matches, $url ); |
285
|
|
|
if ( ! $view_entry || count( $view_entry ) != 2 ) { |
|
|
|
|
286
|
|
|
return $result; |
287
|
|
|
} |
288
|
|
|
|
289
|
|
|
list( $view, $entry ) = $view_entry; |
290
|
|
|
|
291
|
|
|
return self::render_frontend( $view, $entry ); |
292
|
|
|
} |
293
|
|
|
} |
294
|
|
|
|
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.