Completed
Push — master ( 397f5b...27d97b )
by Zack
27:24 queued 14:56
created

oEmbed::render_provider_request()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 26
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 18
nc 3
nop 0
dl 0
loc 26
ccs 0
cts 17
cp 0
crap 20
rs 8.5806
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 12 and the first side effect is on line 6.

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.

Loading history...
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 {
0 ignored issues
show
Coding Style introduced by
Class name "oEmbed" is not in camel caps format
Loading history...
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'] ) ) {
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
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'];
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
48
		} else {
49
			header( 'HTTP/1.0 404 Not Found' );
50
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method render_provider_request() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
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 ) {
0 ignored issues
show
introduced by
Found "!= 2". Use Yoda Condition checks, you must
Loading history...
57
			header( 'HTTP/1.0 404 Not Found' );
58
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method render_provider_request() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
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;
0 ignored issues
show
Coding Style Compatibility introduced by
The method render_provider_request() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
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 ) {
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 3
		$result = self::parse_matches( $matches, $url );
85
86 3
		if ( ! $result || count( $result ) != 2 ) {
0 ignored issues
show
introduced by
Found "!= 2". Use Yoda Condition checks, you must
Loading history...
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 );
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...
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');
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
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 ) ) {
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...
198 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...
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 ) {
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...
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>[^&]+)\$";
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal (?:index.php)?\?(?P<is_c...?P<entry_slug2>[^&]+)\$ does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
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 ) {
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...
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 ) {
0 ignored issues
show
introduced by
Found "!= 2". Use Yoda Condition checks, you must
Loading history...
286
			return $result;
287
		}
288
289
		list( $view, $entry ) = $view_entry;
290
291
		return self::render_frontend( $view, $entry );
292
	}
293
}
294