GravityView  2.17
The best, easiest way to display Gravity Forms entries on your website.
class-gv-oembed.php
Go to the documentation of this file.
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  public static function render( $matches, $attr, $url, $rawattr ) {
83 
84  $result = self::parse_matches( $matches, $url );
85 
86  if ( ! $result || count( $result ) != 2 ) {
87  gravityview()->log->notice( 'View or entry could not be parsed in oEmbed url {url}', array( 'url' => $url, 'matches' => $matches ) );
88  return __( 'You are not allowed to view this content.', 'gk-gravityview' );
89  }
90 
91  list( $view, $entry ) = $result;
92 
93  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  } 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  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  private static function parse_matches( $matches, $url ) {
113  // If not using permalinks, re-assign values for matching groups
114  if ( ! empty( $matches['entry_slug2'] ) ) {
115  $matches['is_cpt'] = $matches['is_cpt2'];
116  $matches['slug'] = $matches['slug2'];
117  $matches['entry_slug'] = $matches['entry_slug2'];
118  unset( $matches['is_cpt2'], $matches['slug2'], $matches['entry_slug2'] );
119  }
120 
121  if ( empty( $matches['entry_slug'] ) ) {
122  gravityview()->log->error( 'Entry slug not parsed by regex.', array( 'data' => $matches ) );
123  return null;
124  } else {
125  $entry_id = $matches['entry_slug'];
126  }
127 
128  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  $view = null;
134 
135  if ( $view_id = url_to_postid( $url ) ) {
136  $view = \GV\View::by_id( $view_id );
137  }
138 
139  // Maybe it failed to find a GravityView CPT
140  if ( ! $view ) {
141 
142  // If the slug doesn't work, maybe using Plain permalinks and not the slug, only ID
143  if( is_numeric( $matches['slug'] ) ) {
144  $view = \GV\View::by_id( $matches['slug'] );
145  }
146 
147  if( ! $view ) {
148  $view = \GV\View::from_post( get_page_by_path( $matches['slug'], OBJECT, 'gravityview' ) );
149  }
150  }
151 
152  if ( ! $view ) {
153  gravityview()->log->error( 'Could not detect View from URL {url}', array( 'url' => $url, 'data' => $matches ) );
154  return null;
155  }
156 
157  return array( $view, $entry );
158  }
159 
160  /**
161  * Display a nice placeholder in the admin for the entry.
162  *
163  * @param \GV\View $view The View.
164  * @param \GV\Entry $entry The Entry.
165  *
166  * @return string A placeholder, with Mr. Floaty :)
167  */
168  private static function render_admin( $view, $entry ) {
169 
170  // Floaty the astronaut
172 
173  $embed_heading = sprintf( esc_html__( 'Embed Entry %d', 'gk-gravityview' ), $entry->ID );
174 
175  $embed_text = sprintf( esc_html__( 'This entry will be displayed as it is configured in View %d', 'gk-gravityview' ), $view->ID );
176 
177  return '
178  <div class="loading-placeholder" style="background-color:#e6f0f5;">
179  <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>
180  <p style="margin:0; padding:0; font-family: -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, Oxygen-Sans, Ubuntu, Cantarell, \'Helvetica Neue\', sans-serif;">
181  '.$embed_text.'
182  </p>
183  <br style="clear: both;">
184  </div>';
185  }
186 
187  /**
188  * Generate a warning to users when previewing oEmbed in the Add Media modal.
189  *
190  * @return string HTML notice
191  */
192  private static function render_preview_notice() {
193  $floaty = \GravityView_Admin::get_floaty();
194  $title = esc_html__( 'This will look better when it is embedded.', 'gk-gravityview' );
195  $message = esc_html__( 'Styles don\'t get loaded when being previewed, so the content below will look strange. Don\'t be concerned!', 'gk-gravityview');
196  return '<div class="updated notice">'.$floaty.'<h3>'.$title.'</h3><p>'.$message.'</p><br style="clear:both;" /></div>';
197  }
198 
199  /**
200  * Render the entry as an oEmbed.
201  *
202  * @param \GV\View $view The View.
203  * @param \GV\Entry $entry The Entry.
204  *
205  * @return string The rendered oEmbed.
206  */
207  private static function render_frontend( $view, $entry ) {
208  /** Private, pending, draft, etc. */
209  $public_states = get_post_stati( array( 'public' => true ) );
210  if ( ! in_array( $view->post_status, $public_states ) && ! \GVCommon::has_cap( 'read_gravityview', $view->ID ) ) {
211  gravityview()->log->notice( 'The current user cannot access this View #{view_id}', array( 'view_id' => $view->ID ) );
212  return __( 'You are not allowed to view this content.', 'gk-gravityview' );
213  }
214 
215  if ( $entry && 'active' !== $entry['status'] ) {
216  gravityview()->log->notice( 'Entry ID #{entry_id} is not active', array( 'entry_id' => $entry->ID ) );
217  return __( 'You are not allowed to view this content.', 'gk-gravityview' );
218  }
219 
220  if ( $view->settings->get( 'show_only_approved' ) ) {
222  gravityview()->log->error( 'Entry ID #{entry_id} is not approved for viewing', array( 'entry_id' => $entry->ID ) );
223  return __( 'You are not allowed to view this content.', 'gk-gravityview' );
224  }
225  }
226 
227  /**
228  * When this is embedded inside a view we should not display the widgets.
229  */
230  $request = gravityview()->request;
231  $is_reembedded = false; // Assume not embedded unless detected otherwise.
232  if ( in_array( get_class( $request ), array( 'GV\Frontend_Request', 'GV\Mock_Request' ) ) ) {
233  if ( ( $_view = $request->is_view() ) && $_view->ID !== $view->ID ) {
234  $is_reembedded = true;
235  }
236  }
237 
238  /**
239  * Remove Widgets on a nested embedded View.
240  * Also, don't show widgets if we're embedding an entry
241  */
242  if ( $is_reembedded || $entry ) {
243  $view->widgets = new \GV\Widget_Collection();
244  }
245 
246  if ( $request->is_edit_entry() ) {
247  /**
248  * Based on code in our unit-tests.
249  * Mocks old context, etc.
250  */
252  $render = $loader->instances['render'];
253 
254  $form = \GFAPI::get_form( $entry['form_id'] );
255 
256  // @todo We really need to rewrite Edit Entry soon
259 
260  $data = \GravityView_View_Data::getInstance( get_post( $view->ID ) );
262  'form' => $form,
263  'form_id' => $form['id'],
264  'view_id' => $view->ID,
265  'entries' => array( $entry->as_entry() ),
266  'atts' => \GVCommon::get_template_settings( $view->ID ),
267  ) );
268 
269  ob_start() && $render->init( $data, \GV\Entry::by_id( $entry['id'] ), $view );
270  $output = ob_get_clean(); // Render :)
271  } else {
272  /** Remove the back link. */
273  add_filter( 'gravityview/template/links/back/url', '__return_false' );
274 
275  $renderer = new \GV\Entry_Renderer();
276  $output = $renderer->render( $entry, $view, gravityview()->request );
277  $output = sprintf( '<div class="gravityview-oembed gravityview-oembed-entry gravityview-oembed-entry-%d">%s</div>', $entry->ID, $output );
278 
279  remove_filter( 'gravityview/template/links/back/url', '__return_false' );
280  }
281 
282  return $output;
283  }
284 
285  /**
286  * Generate the Regular expression that matches embedded entries.
287  *
288  * Generates different regex if using permalinks and if not using permalinks
289  *
290  * @return string Regex code
291  */
292  private static function get_entry_regex() {
293  $entry_var_name = \GV\Entry::get_endpoint_name();
294 
295  /**
296  * @filter `gravityview_slug` Modify the url part for a View. [Read the doc](https://docs.gravityview.co/article/62-changing-the-view-slug)
297  * @param string $rewrite_slug The slug shown in the URL
298  */
299  $rewrite_slug = apply_filters( 'gravityview_slug', 'view' );
300 
301  // Only support embeds for current site
302  $prefix = trailingslashit( home_url() );
303 
304  // Using permalinks
305  $using_permalinks = $prefix . "(?P<is_cpt>{$rewrite_slug})?/?(?P<slug>.+?)/{$entry_var_name}/(?P<entry_slug>.+?)/?\$";
306 
307  // Not using permalinks
308  $not_using_permalinks = $prefix . "(?:index.php)?\?(?P<is_cpt2>[^=]+)=(?P<slug2>[^&]+)&entry=(?P<entry_slug2>[^&]+)\$";
309 
310  // Catch either
311  $match_regex = "(?:{$using_permalinks}|{$not_using_permalinks})";
312 
313  return '#'.$match_regex.'#i';
314  }
315 
316  /**
317  * Internal oEmbed output, shortcircuit without proxying to the provider.
318  */
319  public static function pre_oembed_result( $result, $url, $args ) {
320  if ( ! preg_match( self::get_entry_regex(), $url, $matches ) ) {
321  return $result;
322  }
323 
324  $view_entry = self::parse_matches( $matches, $url );
325  if ( ! $view_entry || count( $view_entry ) != 2 ) {
326  return $result;
327  }
328 
329  list( $view, $entry ) = $view_entry;
330 
331  return self::render_frontend( $view, $entry );
332  }
333 }
$url
Definition: post_image.php:25
$image
Definition: post_image.php:98
static init()
Initialize.
static render_preview_notice()
Generate a warning to users when previewing oEmbed in the Add Media modal.
static render( $matches, $attr, $url, $rawattr)
Output the embed HTML.
static getInstance( $passed_post=NULL)
static getInstance( $passed_post=NULL)
Definition: class-data.php:122
if(! isset( $gravityview)||empty( $gravityview->template)) $template
The entry loop for the list output.
static render_provider_request()
Output a response as a provider for an entry oEmbed URL.
if(gravityview() ->plugin->is_GF_25()) $form
static get_template_settings( $post_id)
Get all the settings for a View.
static render_admin( $view, $entry)
Display a nice placeholder in the admin for the entry.
static get_endpoint_name()
Return the endpoint name for a single Entry.
static render_frontend( $view, $entry)
Render the entry as an oEmbed.
static by_id( $post_id)
Construct a instance from a post ID.
static get_entry_regex()
Generate the Regular expression that matches embedded entries.
static pre_oembed_result( $result, $url, $args)
Internal oEmbed output, shortcircuit without proxying to the provider.
static parse_matches( $matches, $url)
Parse oEmbed regex matches and return View and Entry.
static get_floaty()
Get an image of our intrepid explorer friend.
gravityview()
The main GravityView wrapper function.
static is_approved( $status)
static has_cap( $caps='', $object_id=null, $user_id=null)
Alias of GravityView_Roles_Capabilities::has_cap()
$entry
Definition: notes.php:27
If this file is called directly, abort.
static from_post( $post)
Construct a instance from a .
const meta_key
$title
static getInstance()