* * * 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, ' $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; }