*
*
* Document:
*
*
*
*
* External document:
*
*
* Spreadsheet Form:
*
*
* Spreadsheet Widget:
*
*
*
* Calendar:
*
*
*
* Customized calendar:
*
*
* Generic
*
*
* @package automattic/jetpack
*/
add_filter( 'pre_kses', 'googleapps_embed_to_shortcode' );
add_shortcode( 'googleapps', 'googleapps_shortcode' );
/**
* Reverse iframe embed to shortcode mapping HTML attributes to shortcode attributes.
*
* @since 4.5.0
*
* @param string $content Post content.
*
* @return mixed
*/
function googleapps_embed_to_shortcode( $content ) {
if (
! is_string( $content )
|| false === stripos( $content, '#i';
$regexp_ent = str_replace( '�*58;', '�*58;|*58;', htmlspecialchars( $regexp, ENT_NOQUOTES ) );
$regexp_squot = str_replace( '"', "'", $regexp );
$regexp_ent_squot = str_replace( '"', "'", $regexp_ent );
$regexp_noquot = '!!';
$regexp_ent_noquot = str_replace( '�*58;', '�*58;|*58;', htmlspecialchars( $regexp_noquot, ENT_NOQUOTES ) );
foreach ( compact( 'regexp', 'regexp_ent', 'regexp_squot', 'regexp_ent_squot', 'regexp_noquot', 'regexp_ent_noquot' ) as $reg => $regexp ) {
if ( ! preg_match_all( $regexp, $content, $matches, PREG_SET_ORDER ) ) {
continue;
}
foreach ( $matches as $match ) {
$params = $match[1] . $match[5];
if ( in_array( $reg, array( 'regexp_ent', 'regexp_ent_squot' ), true ) ) {
$params = html_entity_decode( $params, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 );
}
$params = wp_kses_hair( $params, array( 'http' ) );
$width = 0;
$height = 0;
if ( isset( $params['width'] ) ) {
$width = (int) $params['width']['value'];
}
if ( isset( $params['height'] ) ) {
$height = (int) $params['height']['value'];
}
// allow the user to specify width greater than 200 inside text widgets.
if (
$width > 400
// We don't need to check a nonce here. A nonce is already checked "further up" in most code paths.
// In the case where no nonce is ever checked, setting this $_POST parameter doesn't do anything the submitter couldn't already do (set the width/height).
&& isset( $_POST['widget-text'] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing
) {
$width = 200;
$height = 200;
}
$attributes = '';
if ( isset( $params['width'] ) && '100%' === $params['width']['value'] ) {
$width = '100%';
}
if ( $width ) {
$attributes = ' width="' . $width . '"';
}
if ( $height ) {
$attributes .= ' height="' . $height . '"';
}
$domain = 'spreadsheets';
if ( in_array( $match[2], array( 'docs', 'drive', 'www', 'calendar' ), true ) ) {
$domain = $match[2];
}
// Make sure this is actually something that the shortcode supports. If it's not, leave the HTML alone.
if ( ! googleapps_validate_domain_and_dir( $domain, $match[3] ) ) {
continue;
}
/** This action is documented in modules/widgets/social-media-icons.php */
do_action( 'jetpack_bump_stats_extras', 'html_to_shortcode', googleapps_service_name( $domain, $match[3] ) );
$content = str_replace( $match[0], '[googleapps domain="' . $domain . '" dir="' . $match[3] . '" query="' . esc_attr( $match[4] ) . '"' . $attributes . ' /]', $content );
}
}
return $content;
}
/**
* Parse shortcode attributes and output a Google Docs embed.
*
* @since 4.5.0
*
* @param array $atts Shortcode attributes.
*
* @return string
*/
function googleapps_shortcode( $atts ) {
global $content_width;
$attr = shortcode_atts(
array(
'width' => '100%',
'height' => '560',
'domain' => 'docs',
'dir' => 'document',
'query' => '',
'src' => '',
),
$atts
);
if ( is_numeric( $content_width ) && $content_width > 0 && is_numeric( $attr['width'] ) && $attr['width'] > $content_width ) {
$attr['width'] = $content_width;
}
if ( is_numeric( $content_width ) && $content_width > 0 && '560' === $attr['height'] ) {
$attr['height'] = floor( $content_width * 3 / 4 );
}
if ( isset( $atts[0] ) && $atts[0] ) {
$attr['src'] = $atts[0];
}
if ( $attr['src'] && preg_match( '!https?://(docs|drive|spreadsheets\d*|calendar|www)*\.google\.com/([-\w\./]+)\?([^"]+)!', $attr['src'], $matches ) ) {
$attr['domain'] = $matches[1];
$attr['dir'] = $matches[2];
parse_str( htmlspecialchars_decode( $matches[3], ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ), $query_ar );
$query_ar['chrome'] = 'false';
$query_ar['embedded'] = 'true';
$attr['query'] = http_build_query( $query_ar );
}
if ( ! googleapps_validate_domain_and_dir( $attr['domain'], $attr['dir'] ) ) {
return '';
}
$attr['query'] = $attr['dir'] . '?' . $attr['query'];
/** This action is documented in modules/widgets/social-media-icons.php */
do_action( 'jetpack_bump_stats_extras', 'embeds', googleapps_service_name( $attr['domain'], $attr['dir'] ) );
return sprintf(
'',
esc_url( 'https://' . $attr['domain'] . '.google.com/' . $attr['query'] ),
esc_attr( $attr['width'] ),
esc_attr( $attr['height'] )
);
}
/**
* Check that the domain blogs to a Google Apps domain.
*
* @since 4.5.0
*
* @param string $domain Google subdomain.
* @param string $dir Subdirectory of the shared URL.
*
* @return bool
*/
function googleapps_validate_domain_and_dir( $domain, $dir ) {
if ( ! in_array( $domain, array( 'docs', 'drive', 'www', 'spreadsheets', 'calendar' ), true ) ) {
return false;
}
// Calendars.
if ( ( 'www' === $domain || 'calendar' === $domain ) && ! str_starts_with( $dir, 'calendar/' ) ) {
return false;
}
// Docs.
if ( in_array( $domain, array( 'docs', 'drive' ), true ) && ! preg_match( '![-\.\w/]*(presentation/embed|presentation/d/(.*)|present/embed|document/pub|spreadsheets/d/(.*)|document/d/(e/)?[\w-]+/pub|file/d/[\w-]+/preview|viewer|forms/d/(.*)/viewform|spreadsheet/\w+)$!', $dir ) ) {
return false;
}
// Spreadsheets.
if ( 'spreadsheets' === $domain && ! preg_match( '!^([-\.\w/]+/pub|[-\.\w/]*embeddedform)$!', $dir ) ) {
return false;
}
return true;
}
/**
* Get the name of the service we'll be embedding.
*
* @since 4.5.0
*
* @param string $domain Google subdomain.
* @param string $dir Subdirectory of the shared URL.
*
* @return string
*/
function googleapps_service_name( $domain, $dir ) {
switch ( $domain ) {
case 'drive':
case 'docs':
$service_name = ( 'present/embed' === $dir ) ? 'googledocs_presentation' : 'googledocs_document';
break;
case 'spreadsheets':
$service_name = ( 'embeddedform' === $dir ) ? 'googledocs_form' : 'googledocs_spreadsheet';
break;
case 'calendar':
default:
$service_name = 'google_calendar';
}
return $service_name;
}