| @@ -1,0 +1,262 @@ | ||
| 1 | +<?php | |
| 2 | +/** | |
| 3 | + * Google Docs and Google Calendar Shortcode | |
| 4 | + * | |
| 5 | + * Presentation: | |
| 6 | + * <iframe src="https://docs.google.com/present/embed?id=dhfhrphh_123drp8s65c&interval=15&autoStart=true&loop=true&size=l" frameborder="0" width="700" height="559"></iframe> | |
| 7 | + * <iframe src="https://docs.google.com/presentation/embed?id=13ItX4jV0SOSdr-ZjHarcpTh9Lr4omfsHAp87jpxv8-0&start=false&loop=false&delayms=3000" frameborder="0" width="960" height="749" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe> | |
| 8 | + * | |
| 9 | + * Document: | |
| 10 | + * <iframe src="https://docs.google.com/document/pub?id=1kDatklacdZ_tZUOpWtt_ONzY97Ldj2zFcuO9LBY2Ln4&embedded=true"></iframe> | |
| 11 | + * <iframe src="https://docs.google.com/document/d/1kDatklacdZ_tZUOpWtt_ONzY97Ldj2zFcuO9LBY2Ln4/pub?embedded=true"></iframe> | |
| 12 | + * <iframe src="https://docs.google.com/document/d/e/2PACX-1vRkpIdasKL-eKXDjJgpEONduUspZTz0YmKaajfie0eJYnzikuyusuG1_V8X8T9XflN9l8A1oCM2sgEA/pub?embedded=true"></iframe> | |
| 13 | + * | |
| 14 | + * External document: | |
| 15 | + * <iframe width=100% height=560px frameborder=0 src=https://docs.google.com/a/pranab.in/viewer?a=v&pid=explorer&chrome=false&embedded=true&srcid=1VTMwdgGiDMt8MCr75-YkQP-4u9WmEp1Qvf6C26KYBgFilxU2qndpd-VHhBIn&hl=en></iframe> | |
| 16 | + * | |
| 17 | + * Spreadsheet Form: | |
| 18 | + * <iframe src="https://spreadsheets.google.com/embeddedform?formkey=dEVOYnMzZG5jMUpGbjFMYjFYNVB3NkE6MQ" width="760" height="710" frameborder="0" marginheight="0" marginwidth="0">Loading...</iframe> | |
| 19 | + * | |
| 20 | + * Spreadsheet Widget: | |
| 21 | + * <iframe width='500' height='300' frameborder='0' src='https://spreadsheets1.google.com/a/petedavies.com/pub?hl=en&hl=en&key=0AjSij7nlnXvKdHNsNjRSWG12YmVfOEFwdlMxQ3J1S1E&single=true&gid=0&output=html&widget=true'></iframe> | |
| 22 | + * <iframe width='500' height='300' frameborder='0' src='https://spreadsheets.google.com/spreadsheet/pub?hl=en&hl=en&key=0AhInIwfvYrIUdGJiTXhtUEhBSFVPUzdRZU5OMDlqdnc&output=html&widget=true'></iframe> | |
| 23 | + * | |
| 24 | + * Calendar: | |
| 25 | + * <iframe src="https://www.google.com/calendar/embed?src=serjant%40gmail.com&ctz=Europe/Sofia" style="border: 0" width="800" height="600" frameborder="0" scrolling="no"></iframe> | |
| 26 | + * <iframe src="http://www.google.com/calendar/hosted/belcastro.com/embed?src=n8nr8sd6v9hnus3nmlk7ed1238%40group.calendar.google.com&ctz=Europe/Zurich" style="border: 0" width="800" height="600" frameborder="0" scrolling="no"></iframe> | |
| 27 | + * | |
| 28 | + * Customized calendar: | |
| 29 | + * <iframe src="https://www.google.com/calendar/embed?title=asdf&showTitle=0&showNav=0&showDate=0&showPrint=0&showTabs=0&showCalendars=0& | |
| 30 | + * showTz=0&mode=AGENDA&height=300&wkst=2&hl=fi&bgcolor=%23ffcccc&src=m52gdmbgelo3itf00u1v44g0ns%40group.calendar.google.com&color=%234E5D6C& | |
| 31 | + * src=serjant%40gmail.com&color=%235229A3&ctz=Europe%2FRiga" style=" border:solid 1px #777 " width="500" height="300" frameborder="0" scrolling="no"></iframe> | |
| 32 | + * | |
| 33 | + * Generic | |
| 34 | + * <iframe src="https://docs.google.com/file/d/0B0SIdZW7iu-zX1RWREJpMXVHZVU/preview" width="640" height="480"></iframe> | |
| 35 | + * | |
| 36 | + * @package automattic/jetpack | |
| 37 | + */ | |
| 38 | + | |
| 39 | +if ( ! defined( 'ABSPATH' ) ) { | |
| 40 | + exit( 0 ); | |
| 41 | +} | |
| 42 | + | |
| 43 | +if ( jetpack_shortcodes_should_hook_pre_kses() ) { | |
| 44 | + add_filter( 'pre_kses', 'googleapps_embed_to_shortcode' ); | |
| 45 | +} | |
| 46 | + | |
| 47 | +add_shortcode( 'googleapps', 'googleapps_shortcode' ); | |
| 48 | + | |
| 49 | +/** | |
| 50 | + * Reverse iframe embed to shortcode mapping HTML attributes to shortcode attributes. | |
| 51 | + * | |
| 52 | + * @since 4.5.0 | |
| 53 | + * | |
| 54 | + * @param string $content Post content. | |
| 55 | + * | |
| 56 | + * @return mixed | |
| 57 | + */ | |
| 58 | +function googleapps_embed_to_shortcode( $content ) { | |
| 59 | + if ( | |
| 60 | + ! is_string( $content ) | |
| 61 | + || false === stripos( $content, '<iframe' ) | |
| 62 | + && false === stripos( $content, '.google.com' ) | |
| 63 | + ) { | |
| 64 | + return $content; | |
| 65 | + } | |
| 66 | + | |
| 67 | + $regexp = '#<iframe((?:\s+\w+="[^"]*")*?)\s*src="https?://(docs|drive|spreadsheets\d*|calendar|www)*\.google\.com/(?!maps)([-\w\./]+)(?:\?)?([^"]+)?"\s*((?:\s+\w+="[^"]*")*?)>.*?</iframe>#i'; | |
| 68 | + $regexp_ent = str_replace( '&#0*58;', '&#0*58;|�*58;', htmlspecialchars( $regexp, ENT_NOQUOTES ) ); | |
| 69 | + $regexp_squot = str_replace( '"', "'", $regexp ); | |
| 70 | + $regexp_ent_squot = str_replace( '"', "'", $regexp_ent ); | |
| 71 | + $regexp_noquot = '!<iframe(.*?)src=https://(docs|drive)\.google\.com/[-\.\w/]*?(viewer)\?(.*?)>(.*?)</iframe>!'; | |
| 72 | + $regexp_ent_noquot = str_replace( '&#0*58;', '&#0*58;|�*58;', htmlspecialchars( $regexp_noquot, ENT_NOQUOTES ) ); | |
| 73 | + | |
| 74 | + foreach ( compact( 'regexp', 'regexp_ent', 'regexp_squot', 'regexp_ent_squot', 'regexp_noquot', 'regexp_ent_noquot' ) as $reg => $regexp ) { | |
| 75 | + if ( ! preg_match_all( $regexp, $content, $matches, PREG_SET_ORDER ) ) { | |
| 76 | + continue; | |
| 77 | + } | |
| 78 | + | |
| 79 | + foreach ( $matches as $match ) { | |
| 80 | + $params = $match[1] . $match[5]; | |
| 81 | + if ( in_array( $reg, array( 'regexp_ent', 'regexp_ent_squot' ), true ) ) { | |
| 82 | + $params = html_entity_decode( $params, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ); | |
| 83 | + } | |
| 84 | + | |
| 85 | + $params = wp_kses_hair( $params, array( 'http' ) ); | |
| 86 | + | |
| 87 | + $width = 0; | |
| 88 | + $height = 0; | |
| 89 | + | |
| 90 | + if ( isset( $params['width'] ) ) { | |
| 91 | + $width = (int) $params['width']['value']; | |
| 92 | + } | |
| 93 | + | |
| 94 | + if ( isset( $params['height'] ) ) { | |
| 95 | + $height = (int) $params['height']['value']; | |
| 96 | + } | |
| 97 | + | |
| 98 | + // allow the user to specify width greater than 200 inside text widgets. | |
| 99 | + if ( | |
| 100 | + $width > 400 | |
| 101 | + // We don't need to check a nonce here. A nonce is already checked "further up" in most code paths. | |
| 102 | + // 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). | |
| 103 | + && isset( $_POST['widget-text'] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing | |
| 104 | + ) { | |
| 105 | + $width = 200; | |
| 106 | + $height = 200; | |
| 107 | + } | |
| 108 | + | |
| 109 | + $attributes = ''; | |
| 110 | + if ( isset( $params['width'] ) && '100%' === $params['width']['value'] ) { | |
| 111 | + $width = '100%'; | |
| 112 | + } | |
| 113 | + | |
| 114 | + if ( $width ) { | |
| 115 | + $attributes = ' width="' . $width . '"'; | |
| 116 | + } | |
| 117 | + | |
| 118 | + if ( $height ) { | |
| 119 | + $attributes .= ' height="' . $height . '"'; | |
| 120 | + } | |
| 121 | + | |
| 122 | + $domain = 'spreadsheets'; | |
| 123 | + if ( in_array( $match[2], array( 'docs', 'drive', 'www', 'calendar' ), true ) ) { | |
| 124 | + $domain = $match[2]; | |
| 125 | + } | |
| 126 | + | |
| 127 | + // Make sure this is actually something that the shortcode supports. If it's not, leave the HTML alone. | |
| 128 | + if ( ! googleapps_validate_domain_and_dir( $domain, $match[3] ) ) { | |
| 129 | + continue; | |
| 130 | + } | |
| 131 | + | |
| 132 | + /** This action is documented in modules/widgets/social-media-icons.php */ | |
| 133 | + do_action( 'jetpack_bump_stats_extras', 'html_to_shortcode', googleapps_service_name( $domain, $match[3] ) ); | |
| 134 | + | |
| 135 | + $content = str_replace( $match[0], '[googleapps domain="' . $domain . '" dir="' . $match[3] . '" query="' . esc_attr( $match[4] ) . '"' . $attributes . ' /]', $content ); | |
| 136 | + } | |
| 137 | + } | |
| 138 | + | |
| 139 | + return $content; | |
| 140 | +} | |
| 141 | + | |
| 142 | +/** | |
| 143 | + * Parse shortcode attributes and output a Google Docs embed. | |
| 144 | + * | |
| 145 | + * @since 4.5.0 | |
| 146 | + * | |
| 147 | + * @param array $atts Shortcode attributes. | |
| 148 | + * | |
| 149 | + * @return string | |
| 150 | + */ | |
| 151 | +function googleapps_shortcode( $atts ) { | |
| 152 | + global $content_width; | |
| 153 | + | |
| 154 | + $attr = shortcode_atts( | |
| 155 | + array( | |
| 156 | + 'width' => '100%', | |
| 157 | + 'height' => '560', | |
| 158 | + 'domain' => 'docs', | |
| 159 | + 'dir' => 'document', | |
| 160 | + 'query' => '', | |
| 161 | + 'src' => '', | |
| 162 | + ), | |
| 163 | + $atts | |
| 164 | + ); | |
| 165 | + | |
| 166 | + if ( is_numeric( $content_width ) && $content_width > 0 && is_numeric( $attr['width'] ) && $attr['width'] > $content_width ) { | |
| 167 | + $attr['width'] = $content_width; | |
| 168 | + } | |
| 169 | + | |
| 170 | + if ( is_numeric( $content_width ) && $content_width > 0 && '560' === $attr['height'] ) { | |
| 171 | + $attr['height'] = floor( $content_width * 3 / 4 ); | |
| 172 | + } | |
| 173 | + | |
| 174 | + if ( isset( $atts[0] ) && $atts[0] ) { | |
| 175 | + $attr['src'] = $atts[0]; | |
| 176 | + } | |
| 177 | + | |
| 178 | + if ( $attr['src'] && preg_match( '!https?://(docs|drive|spreadsheets\d*|calendar|www)*\.google\.com/([-\w\./]+)\?([^"]+)!', $attr['src'], $matches ) ) { | |
| 179 | + $attr['domain'] = $matches[1]; | |
| 180 | + $attr['dir'] = $matches[2]; | |
| 181 | + parse_str( htmlspecialchars_decode( $matches[3], ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401 ), $query_ar ); | |
| 182 | + $query_ar['chrome'] = 'false'; | |
| 183 | + $query_ar['embedded'] = 'true'; | |
| 184 | + $attr['query'] = http_build_query( $query_ar ); | |
| 185 | + } | |
| 186 | + | |
| 187 | + if ( ! googleapps_validate_domain_and_dir( $attr['domain'], $attr['dir'] ) ) { | |
| 188 | + return '<!-- Unsupported URL -->'; | |
| 189 | + } | |
| 190 | + | |
| 191 | + $attr['query'] = $attr['dir'] . '?' . $attr['query']; | |
| 192 | + | |
| 193 | + /** This action is documented in modules/widgets/social-media-icons.php */ | |
| 194 | + do_action( 'jetpack_bump_stats_extras', 'embeds', googleapps_service_name( $attr['domain'], $attr['dir'] ) ); | |
| 195 | + | |
| 196 | + return sprintf( | |
| 197 | + '<iframe src="%s" frameborder="0" width="%s" height="%s" marginheight="0" marginwidth="0" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>', | |
| 198 | + esc_url( 'https://' . $attr['domain'] . '.google.com/' . $attr['query'] ), | |
| 199 | + esc_attr( $attr['width'] ), | |
| 200 | + esc_attr( $attr['height'] ) | |
| 201 | + ); | |
| 202 | +} | |
| 203 | + | |
| 204 | +/** | |
| 205 | + * Check that the domain blogs to a Google Apps domain. | |
| 206 | + * | |
| 207 | + * @since 4.5.0 | |
| 208 | + * | |
| 209 | + * @param string $domain Google subdomain. | |
| 210 | + * @param string $dir Subdirectory of the shared URL. | |
| 211 | + * | |
| 212 | + * @return bool | |
| 213 | + */ | |
| 214 | +function googleapps_validate_domain_and_dir( $domain, $dir ) { | |
| 215 | + if ( ! in_array( $domain, array( 'docs', 'drive', 'www', 'spreadsheets', 'calendar' ), true ) ) { | |
| 216 | + return false; | |
| 217 | + } | |
| 218 | + | |
| 219 | + // Calendars. | |
| 220 | + if ( ( 'www' === $domain || 'calendar' === $domain ) && ! str_starts_with( $dir, 'calendar/' ) ) { | |
| 221 | + return false; | |
| 222 | + } | |
| 223 | + | |
| 224 | + // Docs. | |
| 225 | + 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 ) ) { | |
| 226 | + return false; | |
| 227 | + } | |
| 228 | + | |
| 229 | + // Spreadsheets. | |
| 230 | + if ( 'spreadsheets' === $domain && ! preg_match( '!^([-\.\w/]+/pub|[-\.\w/]*embeddedform)$!', $dir ) ) { | |
| 231 | + return false; | |
| 232 | + } | |
| 233 | + | |
| 234 | + return true; | |
| 235 | +} | |
| 236 | + | |
| 237 | +/** | |
| 238 | + * Get the name of the service we'll be embedding. | |
| 239 | + * | |
| 240 | + * @since 4.5.0 | |
| 241 | + * | |
| 242 | + * @param string $domain Google subdomain. | |
| 243 | + * @param string $dir Subdirectory of the shared URL. | |
| 244 | + * | |
| 245 | + * @return string | |
| 246 | + */ | |
| 247 | +function googleapps_service_name( $domain, $dir ) { | |
| 248 | + switch ( $domain ) { | |
| 249 | + case 'drive': | |
| 250 | + case 'docs': | |
| 251 | + $service_name = ( 'present/embed' === $dir ) ? 'googledocs_presentation' : 'googledocs_document'; | |
| 252 | + break; | |
| 253 | + case 'spreadsheets': | |
| 254 | + $service_name = ( 'embeddedform' === $dir ) ? 'googledocs_form' : 'googledocs_spreadsheet'; | |
| 255 | + break; | |
| 256 | + case 'calendar': | |
| 257 | + default: | |
| 258 | + $service_name = 'google_calendar'; | |
| 259 | + } | |
| 260 | + | |
| 261 | + return $service_name; | |
| 262 | +} | |