Completed
Push — develop ( 58200e...206f78 )
by Zack
13:53 queued 11:35
created

oEmbed::init()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 0
dl 0
loc 12
ccs 0
cts 8
cp 0
crap 12
rs 9.8666
c 0
b 0
f 0
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 4
	public static function render( $matches, $attr, $url, $rawattr ) {
0 ignored issues
show
Unused Code introduced by
The parameter $rawattr is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
83
84 4
		$result = self::parse_matches( $matches, $url );
85
86 4
		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 4
		list( $view, $entry ) = $result;
92
93 4
		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 4
		} 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 4
		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 3
	private static function parse_matches( $matches, $url ) {
113
		// If not using permalinks, re-assign values for matching groups
114 3
		if ( ! empty( $matches['entry_slug2'] ) ) {
115 2
			$matches['is_cpt'] = $matches['is_cpt2'];
116 2
			$matches['slug'] = $matches['slug2'];
117 2
			$matches['entry_slug'] = $matches['entry_slug2'];
118 2
			unset( $matches['is_cpt2'], $matches['slug2'], $matches['entry_slug2'] );
119
		}
120
121 3
		if ( empty( $matches['entry_slug'] ) ) {
122
			gravityview()->log->error( 'Entry slug not parsed by regex.', array( 'data' => $matches ) );
123
			return null;
124
		} else {
125 3
			$entry_id = $matches['entry_slug'];
126
		}
127
128 3
		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 3
		$view = null;
134
135 3
		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 3
		if ( ! $view ) {
141
142
			// If the slug doesn't work, maybe using Plain permalinks and not the slug, only ID
143 3
			if( is_numeric( $matches['slug'] ) ) {
144
				$view = \GV\View::by_id( $matches['slug'] );
145
			}
146
147 3
			if( ! $view ) {
148 3
				$view = \GV\View::from_post( get_page_by_path( $matches['slug'], OBJECT, 'gravityview' ) );
149
			}
150
		}
151
152 3
		if ( ! $view ) {
153 1
			gravityview()->log->error( 'Could not detect View from URL {url}', array( 'url' => $url, 'data' => $matches ) );
154 1
			return null;
155
		}
156
157 3
		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
171
		$image = \GravityView_Admin::get_floaty();
172
173
		$embed_heading = sprintf( esc_html__( 'Embed Entry %d', 'gravityview' ), $entry->ID );
174
175
		$embed_text = sprintf( esc_html__( 'This entry will be displayed as it is configured in View %d', 'gravityview' ), $view->ID );
0 ignored issues
show
Documentation introduced by
The property ID does not exist on object<GV\View>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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.', 'gravityview' );
195
		$message = esc_html__( 'Styles don\'t get loaded when being previewed, so the content below will look strange. Don\'t be concerned!', '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 3
	private static function render_frontend( $view, $entry ) {
208
		/** Private, pending, draft, etc. */
209 3
		$public_states = get_post_stati( array( 'public' => true ) );
210 3
		if ( ! in_array( $view->post_status, $public_states ) && ! \GVCommon::has_cap( 'read_gravityview', $view->ID ) ) {
0 ignored issues
show
Documentation introduced by
The property post_status does not exist on object<GV\View>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property ID does not exist on object<GV\View>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
211 1
			gravityview()->log->notice( 'The current user cannot access this View #{view_id}', array( 'view_id' => $view->ID ) );
0 ignored issues
show
Documentation introduced by
The property ID does not exist on object<GV\View>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
212 1
			return __( 'You are not allowed to view this content.', 'gravityview' );
213
		}
214
215 3
		if ( $entry && 'active' !== $entry['status'] ) {
216 1
			gravityview()->log->notice( 'Entry ID #{entry_id} is not active', array( 'entry_id' => $entry->ID ) );
217 1
			return __( 'You are not allowed to view this content.', 'gravityview' );
218
		}
219
220 3
		if ( $view->settings->get( 'show_only_approved' ) ) {
221 1
			if ( ! \GravityView_Entry_Approval_Status::is_approved( gform_get_meta( $entry->ID, \GravityView_Entry_Approval::meta_key ) )  ) {
222 1
				gravityview()->log->error( 'Entry ID #{entry_id} is not approved for viewing', array( 'entry_id' => $entry->ID ) );
223 1
				return __( 'You are not allowed to view this content.', 'gravityview' );
224
			}
225
		}
226
227
		/**
228
		 * When this is embedded inside a view we should not display the widgets.
229
		 */
230 3
		$request = gravityview()->request;
231 3
		$is_reembedded = false; // Assume not embedded unless detected otherwise.
232 3
		if ( in_array( get_class( $request ), array( 'GV\Frontend_Request', 'GV\Mock_Request' ) ) ) {
233 3
			if ( ( $_view = $request->is_view() ) && $_view->ID !== $view->ID ) {
0 ignored issues
show
Documentation introduced by
The property ID does not exist on object<GV\View>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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 3
		if ( $is_reembedded || $entry ) {
243 3
			$view->widgets = new \GV\Widget_Collection();
244
		}
245
246 3
		if ( $request->is_edit_entry() ) {
247
			/**
248
			 * Based on code in our unit-tests.
249
			 * Mocks old context, etc.
250
			 */
251
			$loader = \GravityView_Edit_Entry::getInstance();
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
257
			\GravityView_View::$instance = null;
0 ignored issues
show
Bug introduced by
The property instance cannot be accessed from this context as it is declared private in class GravityView_View.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
258
			\GravityView_View_Data::$instance = null;
0 ignored issues
show
Bug introduced by
The property instance cannot be accessed from this context as it is declared private in class GravityView_View_Data.

This check looks for access to properties that are not accessible from the current context.

If you need to make a property accessible to another context you can either raise its visibility level or provide an accessible getter in the defining class.

Loading history...
259
260
			$data = \GravityView_View_Data::getInstance( get_post( $view->ID ) );
0 ignored issues
show
Documentation introduced by
The property ID does not exist on object<GV\View>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
261
			$template = \GravityView_View::getInstance( array(
262
				'form' => $form,
263
				'form_id' => $form['id'],
264
				'view_id' => $view->ID,
0 ignored issues
show
Documentation introduced by
The property ID does not exist on object<GV\View>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
265
				'entries' => array( $entry->as_entry() ),
266
				'atts' => \GVCommon::get_template_settings( $view->ID ),
0 ignored issues
show
Documentation introduced by
The property ID does not exist on object<GV\View>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
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 3
			add_filter( 'gravityview/template/links/back/url', '__return_false' );
274
275 3
			$renderer = new \GV\Entry_Renderer();
276 3
			$output = $renderer->render( $entry, $view, gravityview()->request );
277 3
			$output = sprintf( '<div class="gravityview-oembed gravityview-oembed-entry gravityview-oembed-entry-%d">%s</div>', $entry->ID, $output );
278
279 3
			remove_filter( 'gravityview/template/links/back/url', '__return_false' );
280
		}
281
282 3
		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 ) {
0 ignored issues
show
Unused Code introduced by
The parameter $args is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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
}
334