Skip to content

Commit

Permalink
Merge branch 'main' into try/11734-format
Browse files Browse the repository at this point in the history
  • Loading branch information
swissspidy authored Jun 27, 2022
2 parents 73074f1 + 851d9fd commit 85297ca
Show file tree
Hide file tree
Showing 17 changed files with 266 additions and 223 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/tests-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,14 @@ jobs:
env:
COMPOSE_INTERACTIVE_NO_CLI: true

- name: Upload artifacts
uses: actions/upload-artifact@v3
if: always()
with:
name: failures-artifacts
path: build/e2e-artifacts
if-no-files-found: ignore

percy:
name: Percy
runs-on: ubuntu-latest
Expand Down
42 changes: 22 additions & 20 deletions docs/quick-actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,33 @@ Quick actions are not limited to just the above functionality. In the future it

The actions that are displayed dynamically change depending on which element type is selected. These are outlined below:

|Selected Element Type|Available actions|
|--|--|
|Nothing selected|- Change background color<br/>- Insert media<br/>- Insert text|
|Background Image|- Replace background<br/>- Add animation<br/>- Clear filters and animation|
|Foreground Image|- Replace media<br/>- Add animation<br/>- Add link<br/>- Clear animation|
|Video|- Replace media<br/>- Add animation<br/>- Add link<br/>- Add captions<br/>- Clear filters and animation|
|Shape|- Change color<br/>- Add animation<br/>- Add link<br/>- Clear filters and animation|
|Text|- Change color<br/>- Edit text<br/>- Add animation<br/>- Add link<br/>- Clear animation|
| Selected Element Type | Available actions |
|-----------------------|-----------------------------------------------------------------------------------------------------------|
| Nothing selected | - Change background color<br/>- Insert media <br/>- Insert text |
| Background Image | - Replace background<br/>- Add animation* <br/>- Clear filters and animation |
| Foreground Image | - Replace media<br/>- Add animation* <br/>- Add link<br/>- Clear animation |
| Video | - Replace media<br/>- Add animation* <br/>- Add link<br/>- Add captions<br/>- Clear filters and animation |
| Shape | - Change color<br/>- Add animation* <br/>- Add link<br/>- Clear filters and animation |
| Text | - Change color<br/>- Edit text<br/>- Add animation*<br/>- Add link<br/>- Clear animation |

### Action functionality

Reference this table when needing to know what a quick action will do when clicked.

|Quick Action|Result|
|--|--|
|Add animation|Open and highlight the `animation` panel in the `design pane`. Focus the effect chooser dropdown.|
|Add caption|Open and highlight the `captions and subtitles` panel in the `design pane`. Focus the input.|
|Add link|Open and highlight the `link` panel in the `design pane`. Focus the input.|
|Change background color|Open and highlight the `page background` panel in the `design pane`. Focus the input.|
|Clear animation/filters and animation|Remove all filters and/or animations as specified. Display a snackbar to the user indicating that they were removed. The snackbar will contain an action button that when clicked will re-apply the styles that were removed.|
|Edit text|Open and highlight the `text` panel in the `design pane`. Focus the first input.|
|Insert media|Open and highlight the `media pane` in the `library`. Focus the media tab button.|
|Insert text|Open and highlight the `text pane` in the `library`. Focus the text tab button.|
|Replace background/media|Open and highlight one of the media panes in the `library`. The media pane opened will depend on the type of the element selected. Focus the relevant tab button.|
|Trim video|Enter video trim mode similar to button in design panel.|
| Quick Action | Result |
|---------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Add animation* | Open and highlight the `animation` panel in the `design pane`. Focus the effect chooser dropdown. |
| Add caption | Open and highlight the `captions and subtitles` panel in the `design pane`. Focus the input. |
| Add link | Open and highlight the `link` panel in the `design pane`. Focus the input. |
| Change background color | Open and highlight the `page background` panel in the `design pane`. Focus the input. |
| Clear animation/filters and animation | Remove all filters and/or animations as specified. Display a snackbar to the user indicating that they were removed. The snackbar will contain an action button that when clicked will re-apply the styles that were removed. |
| Edit text | Open and highlight the `text` panel in the `design pane`. Focus the first input. |
| Insert media | Open and highlight the `media pane` in the `library`. Focus the media tab button. |
| Insert text | Open and highlight the `text pane` in the `library`. Focus the text tab button. |
| Replace background/media | Open and highlight one of the media panes in the `library`. The media pane opened will depend on the type of the element selected. Focus the relevant tab button. |
| Trim video | Enter video trim mode similar to button in design panel. |

> *_Add animation_ action is not available on the first page of a story.
### Visibility

Expand Down
127 changes: 79 additions & 48 deletions includes/REST_API/Hotlinking_Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,11 @@ public function parse_url( WP_REST_Request $request ) {
$raw_url = $request['url'];
$raw_url = untrailingslashit( $raw_url );

$url = $this->validate_url( $raw_url );
$url_or_ip = $this->validate_url( $raw_url );

$host = wp_parse_url( $raw_url, PHP_URL_HOST );

if ( ! $url || ! $host ) {
if ( ! $url_or_ip || ! $host ) {
return new WP_Error( 'rest_invalid_url', __( 'Invalid URL', 'web-stories' ), [ 'status' => 400 ] );
}

Expand All @@ -230,11 +230,11 @@ public function parse_url( WP_REST_Request $request ) {
*
* @since 1.11.0
*
* @param int $time Time to live (in seconds). Default is 1 day.
* @param string $url The attempted URL.
* @param int $time Time to live (in seconds). Default is 1 day.
* @param string $url The attempted URL.
*/
$cache_ttl = apply_filters( 'web_stories_hotlinking_url_data_cache_ttl', DAY_IN_SECONDS, $url );
$cache_key = 'web_stories_url_data_' . md5( $url );
$cache_ttl = apply_filters( 'web_stories_hotlinking_url_data_cache_ttl', DAY_IN_SECONDS, $raw_url );
$cache_key = 'web_stories_url_data_' . md5( $raw_url );

$data = get_transient( $cache_key );
if ( \is_string( $data ) && ! empty( $data ) ) {
Expand All @@ -252,15 +252,21 @@ public function parse_url( WP_REST_Request $request ) {
}
}

$callback = $this->get_curl_resolve_callback( $raw_url, $url_or_ip );
add_action( 'http_api_curl', $callback );

$response = wp_safe_remote_head(
$url,
$raw_url,
[
'redirection' => 0, // No redirects allowed.
'headers' => [
'Host' => $host,
],
]
);

remove_action( 'http_api_curl', $callback );

if ( is_wp_error( $response ) && 'http_request_failed' === $response->get_error_code() ) {
return new WP_Error( 'rest_invalid_url', __( 'Invalid URL', 'web-stories' ), [ 'status' => 404 ] );
}
Expand All @@ -277,12 +283,7 @@ public function parse_url( WP_REST_Request $request ) {
}
$file_size = (int) $headers['content-length'];

/**
* The URL's path.
*
* @var string|false|null $path
*/
$path = wp_parse_url( $url, PHP_URL_PATH );
$path = wp_parse_url( $raw_url, PHP_URL_PATH );

if ( ! \is_string( $path ) ) {
return new WP_Error( 'rest_invalid_url', __( 'Invalid URL', 'web-stories' ), [ 'status' => 404 ] );
Expand Down Expand Up @@ -339,11 +340,11 @@ public function proxy_url( WP_REST_Request $request ) {
$raw_url = $request['url'];
$raw_url = untrailingslashit( $raw_url );

$url = $this->validate_url( $raw_url );
$url_or_ip = $this->validate_url( $raw_url );

$host = wp_parse_url( $raw_url, PHP_URL_HOST );

if ( ! $url || ! $host ) {
if ( ! $url_or_ip || ! $host ) {
return new WP_Error( 'rest_invalid_url', __( 'Invalid URL', 'web-stories' ), [ 'status' => 400 ] );
}

Expand All @@ -368,8 +369,11 @@ public function proxy_url( WP_REST_Request $request ) {
'redirection' => 0, // No redirects allowed.
];

$callback = $this->get_curl_resolve_callback( $raw_url, $url_or_ip );
add_action( 'http_api_curl', $callback );

$http = _wp_http_get_object();
$transport = $http->_get_first_available_transport( $args, $url );
$transport = $http->_get_first_available_transport( $args, $raw_url );

// When cURL is available, we might be able to use it together with fopen().
if ( 'WP_Http_Curl' === $transport ) {
Expand All @@ -384,17 +388,16 @@ public function proxy_url( WP_REST_Request $request ) {

if ( $stream_handle ) {
$this->stream_handle = $stream_handle;
$this->proxy_url_curl( $url, $args );
$this->proxy_url_curl( $raw_url, $args );
exit;
}
exit;
}

// If either cURL is not available or fopen() did not succeed, use whatever WP gives us,
// using good old wp_remote
// Fall back to using whatever else is set up on the site, presumably WP_Http_Streams
// or just cURL but without .
// If either cURL is not available or fopen() did not succeed,
// fall back to using whatever else is set up on the site,
// presumably WP_Http_Streams or still WP_Http_Curl but without streams.
unset( $args['blocking'] );
$this->proxy_url_fallback( $url, $args );
$this->proxy_url_fallback( $raw_url, $args );

exit;
}
Expand Down Expand Up @@ -578,7 +581,11 @@ public function get_item_schema(): array {
*/
public function parse_url_permissions_check() {
if ( ! $this->story_post_type->has_cap( 'edit_posts' ) ) {
return new WP_Error( 'rest_forbidden', __( 'Sorry, you are not allowed to insert external media.', 'web-stories' ), [ 'status' => rest_authorization_required_code() ] );
return new WP_Error(
'rest_forbidden',
__( 'Sorry, you are not allowed to insert external media.', 'web-stories' ),
[ 'status' => rest_authorization_required_code() ]
);
}

return true;
Expand Down Expand Up @@ -620,7 +627,7 @@ public function validate_callback( $value ) {
* @since 1.22.0
*
* @param string $url Request URL.
* @return string|false URL or false on failure.
* @return string|false Original URL, resolved IP address, or false on failure.
*/
private function validate_url( string $url ) {
if ( '' === $url || is_numeric( $url ) ) {
Expand All @@ -633,12 +640,6 @@ private function validate_url( string $url ) {
return false;
}

/**
* URL parts.
*
* @var array|false $parsed_url
* @phpstan-var URLParts|false $parsed_url
*/
$parsed_url = wp_parse_url( $url );
if ( ! $parsed_url || ! isset( $parsed_url['host'], $parsed_url['scheme'] ) ) {
return false;
Expand All @@ -659,12 +660,6 @@ private function validate_url( string $url ) {
*/
$home_url = get_option( 'home' );

/**
* URL parts of home URL.
*
* @var array|false $parsed_home
* @phpstan-var URLParts|false $parsed_home
*/
$parsed_home = wp_parse_url( $home_url );

if ( ! $parsed_home ) {
Expand Down Expand Up @@ -695,15 +690,8 @@ private function validate_url( string $url ) {
return false;
}

// Construct host using IP address to avoid DNS rebinding issues.
$validated_url = sprintf(
'%1$s://%2$s%3$s%4$s%5$s',
$parsed_url['scheme'],
$ip,
isset( $parsed_url['port'] ) ? ":${parsed_url['port']}" : '',
$parsed_url['path'] ?? '',
isset( $parsed_url['query'] ) ? "?${parsed_url['query']}" : ''
);
// Use resolved IP address to avoid DNS rebinding issues.
$validated_url = $ip;
}

/** This filter is documented in wp-includes/http.php */
Expand All @@ -722,14 +710,57 @@ private function validate_url( string $url ) {
return false;
}

/**
* Returns a callback to modify the cURL configuration before the request is executed.
*
* @since 1.22.1
*
* @param string $url URL.
* @param string $url_or_ip URL or IP address.
*/
public function get_curl_resolve_callback( string $url, string $url_or_ip ): callable {
/**
* CURL configuration callback.
*
* @param resource $handle The cURL handle returned by curl_init() (passed by reference).
*/
return static function( $handle ) use ( $url, $url_or_ip ): void {
// Just some safeguard in case cURL is not really available,
// despite this method being run in the context of WP_Http_Curl.
if ( ! function_exists( 'curl_setopt' ) ) {
return;
}

if ( $url === $url_or_ip ) {
return;
}

$host = wp_parse_url( $url, PHP_URL_HOST );
$scheme = wp_parse_url( $url, PHP_URL_SCHEME ) ?? 'http';
$port = wp_parse_url( $url, PHP_URL_PORT ) ?? 'http' === $scheme ? 80 : 443;

// phpcs:disable WordPress.WP.AlternativeFunctions.curl_curl_setopt

curl_setopt(
$handle,
CURLOPT_RESOLVE,
[
"$host:$port:$url_or_ip",
]
);

// phpcs:enable WordPress.WP.AlternativeFunctions.curl_curl_setopt
};
}

/**
* Modifies the cURL configuration before the request is executed.
*
* @since 1.15.0
*
* @param resource $handle The cURL handle returned by curl_init() (passed by reference).
* @param resource $handle The cURL handle returned by {@see curl_init()} (passed by reference).
*/
public function modify_curl_configuration( &$handle ): void {
public function modify_curl_configuration( $handle ): void {
// Just some safeguard in case cURL is not really available,
// despite this method being run in the context of WP_Http_Curl.
if ( ! function_exists( 'curl_setopt' ) ) {
Expand Down
1 change: 1 addition & 0 deletions jest-puppeteer.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ module.exports = {
product: PUPPETEER_PRODUCT,
args: ['--window-size=1600,1000'], // Same as in percy.config.yml.
},
exitOnPageError: false,
};
Loading

0 comments on commit 85297ca

Please sign in to comment.