mailchimp.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. <?php
  2. /**
  3. * MailChimp Subscriber Popup Form shortcode
  4. *
  5. * Example:
  6. * [mailchimp_subscriber_popup baseUrl="mc.us11.list-manage.com" uuid="1ca7856462585a934b8674c71" lid="2d24f1898b"]
  7. *
  8. * Embed code example:
  9. * <script type="text/javascript" src="//downloads.mailchimp.com/js/signup-forms/popup/embed.js" data-dojo-config="usePlainJson: true, isDebug: false"></script><script type="text/javascript">require(["mojo/signup-forms/Loader"], function(L) { L.start({"baseUrl":"mc.us11.list-manage.com","uuid":"1ca7856462585a934b8674c71","lid":"2d24f1898b"}) })</script>
  10. *
  11. */
  12. /**
  13. * Register [mailchimp_subscriber_popup] shortcode and add a filter to 'pre_kses' queue to reverse MailChimp embed to shortcode.
  14. *
  15. * @since 4.5.0
  16. */
  17. function jetpack_mailchimp_subscriber_popup() {
  18. add_shortcode( 'mailchimp_subscriber_popup', array(
  19. 'MailChimp_Subscriber_Popup',
  20. 'shortcode'
  21. ) );
  22. add_filter( 'pre_kses', array(
  23. 'MailChimp_Subscriber_Popup',
  24. 'reversal'
  25. ) );
  26. }
  27. if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
  28. add_action( 'init', 'jetpack_mailchimp_subscriber_popup' );
  29. } else {
  30. jetpack_mailchimp_subscriber_popup();
  31. }
  32. /**
  33. * Class MailChimp_Subscriber_Popup
  34. *
  35. * @since 4.5.0
  36. */
  37. class MailChimp_Subscriber_Popup {
  38. /**
  39. * Regular expressions to reverse script tags to shortcodes.
  40. *
  41. * @var array
  42. */
  43. static $reversal_regexes = array(
  44. /* raw examplejs */
  45. '/<script type="text\/javascript" src="(https?:)?\/\/downloads\.mailchimp\.com\/js\/signup-forms\/popup\/embed\.js" data-dojo-config="([^"]*?)"><\/script><script type="text\/javascript">require\(\["mojo\/signup-forms\/Loader"\]\, function\(L\) { L\.start\({([^}]*?)}\) }\)<\/script>/s',
  46. /* visual editor */
  47. '/&lt;script type="text\/javascript" src="(https?:)?\/\/downloads\.mailchimp\.com\/js\/signup-forms\/popup\/embed\.js" data-dojo-config="([^"]*?)"&gt;&lt;\/script&gt;&lt;script type="text\/javascript"&gt;require\(\["mojo\/signup-forms\/Loader"]\, function\(L\) { L\.start\({([^}]*?)}\) }\)&lt;\/script&gt;/s',
  48. );
  49. /**
  50. * Allowed configuration attributes. Used in reversal when checking allowed attributes.
  51. *
  52. * @var array
  53. */
  54. static $allowed_config = array(
  55. 'usePlainJson' => 'true',
  56. 'isDebug' => 'false',
  57. );
  58. /**
  59. * Allowed JS variables. Used in reversal to whitelist variables.
  60. *
  61. * @var array
  62. */
  63. static $allowed_js_vars = array(
  64. 'baseUrl',
  65. 'uuid',
  66. 'lid',
  67. );
  68. /**
  69. * Runs the whole reversal.
  70. *
  71. * @since 4.5.0
  72. *
  73. * @param string $content Post Content
  74. *
  75. * @return string Content with embeds replaced
  76. */
  77. static function reversal( $content ) {
  78. // Bail without the js src
  79. if ( ! is_string( $content ) || false === stripos( $content, 'downloads.mailchimp.com/js/signup-forms/popup/embed.js' ) ) {
  80. return $content;
  81. }
  82. require_once( ABSPATH . WPINC . '/class-json.php' );
  83. $wp_json = new Services_JSON();
  84. // loop through our rules and find valid embeds
  85. foreach ( self::$reversal_regexes as $regex ) {
  86. if ( ! preg_match_all( $regex, $content, $matches ) ) {
  87. continue;
  88. }
  89. foreach ( $matches[3] as $index => $js_vars ) {
  90. // the regex rule for a specific embed
  91. $replace_regex = sprintf( '#\s*%s\s*#', preg_quote( $matches[0][$index], '#' ) );
  92. $attrs = $wp_json->decode( '{' . $js_vars . '}' );
  93. if ( $matches[2][$index] ) {
  94. $config_attrs = $wp_json->decode( '{' . $matches[2][$index] . '}' );
  95. foreach ( $config_attrs as $key => $value ) {
  96. $attrs->$key = ( 1 == $value ) ? 'true' : 'false';
  97. }
  98. }
  99. $shortcode = self::build_shortcode_from_reversal_attrs( $attrs );
  100. $content = preg_replace( $replace_regex, "\n\n$shortcode\n\n", $content );
  101. /** This action is documented in modules/widgets/social-media-icons.php */
  102. do_action( 'jetpack_bump_stats_extras', 'html_to_shortcode', 'mailchimp_subscriber_popup' );
  103. }
  104. }
  105. return $content;
  106. }
  107. /**
  108. * Builds the actual shortcode based on passed in attributes.
  109. *
  110. * @since 4.5.0
  111. *
  112. * @param array $attrs A valid list of attributes (gets matched against self::$allowed_config and self::$allowed_js_vars)
  113. *
  114. * @return string
  115. */
  116. static function build_shortcode_from_reversal_attrs( $attrs ) {
  117. $shortcode = '[mailchimp_subscriber_popup ';
  118. foreach ( $attrs as $key => $value ) {
  119. // skip unsupported keys
  120. if ( ! in_array( $key, array_keys( self::$allowed_config ) ) && ! in_array( $key, self::$allowed_js_vars ) ) {
  121. continue;
  122. }
  123. $value = esc_attr( $value );
  124. $shortcode .= "$key='$value' ";
  125. }
  126. return trim( $shortcode ) . ']';
  127. }
  128. /**
  129. * Parses the shortcode back out to embedded information.
  130. *
  131. * @since 4.5.0
  132. *
  133. * @param array $lcase_attrs
  134. *
  135. * @return string
  136. */
  137. static function shortcode( $lcase_attrs ) {
  138. static $displayed_once = false;
  139. // Limit to one form per page load
  140. if ( $displayed_once ) {
  141. return '';
  142. }
  143. if ( empty( $lcase_attrs ) ) {
  144. return '<!-- Missing MailChimp baseUrl, uuid or lid -->';
  145. }
  146. $defaults = array_fill_keys( self::$allowed_js_vars, '' );
  147. $defaults = array_merge( $defaults, self::$allowed_config );
  148. // Convert $attrs back to proper casing since they come through in all lowercase
  149. $attrs = array();
  150. foreach ( $defaults as $key => $value ) {
  151. if ( array_key_exists( strtolower( $key ), $lcase_attrs ) ) {
  152. $attrs[ $key ] = $lcase_attrs[ strtolower( $key ) ];
  153. }
  154. }
  155. $attrs = array_map( 'esc_js', array_filter( shortcode_atts( $defaults, $attrs ) ) );
  156. // Split config & js vars
  157. $config_vars = $js_vars = array();
  158. foreach ( $attrs as $key => $value ) {
  159. if ( in_array( $key, self::$allowed_js_vars ) ) {
  160. $js_vars[ $key ] = $value;
  161. } else {
  162. $config_vars[] = "$key: $value";
  163. }
  164. }
  165. // If one of these parameters is missing we can't render the form so exist.
  166. if ( empty( $js_vars['baseUrl'] ) || empty( $js_vars['uuid'] ) || empty( $js_vars['lid'] ) ) {
  167. return '<!-- Missing MailChimp baseUrl, uuid or lid -->';
  168. }
  169. /** This action is already documented in modules/widgets/gravatar-profile.php */
  170. do_action( 'jetpack_stats_extra', 'mailchimp_subscriber_popup', 'view' );
  171. $displayed_once = true;
  172. return "\n\n" . '<script type="text/javascript" data-dojo-config="' . esc_attr( implode( ', ', $config_vars ) ) . '">jQuery.getScript( "//downloads.mailchimp.com/js/signup-forms/popup/embed.js", function( data, textStatus, jqxhr ) { require(["mojo/signup-forms/Loader"], function(L) { L.start(' . wp_json_encode( $js_vars ) . ') }); window.define.amd = undefined; } );</script>' . "\n\n";
  173. }
  174. }