Publicize_Util

Helper functions for shared use in the services files.

Defined (1)

The class is defined in the following location(s).

/modules/publicize.php  
  1. class Publicize_Util { 
  2. /** 
  3. * Truncates a string to be shorter than or equal to the length specified 
  4. * Attempts to truncate on word boundaries 
  5. * @param string $string 
  6. * @param int $length 
  7. * @return string 
  8. */ 
  9. public static function crop_str( $string, $length = 256 ) { 
  10. $string = Publicize_Util::sanitize_message( $string ); 
  11. $length = absint( $length ); 
  12.  
  13. if ( mb_strlen( $string, 'UTF-8' ) <= $length ) { 
  14. return $string; 
  15.  
  16. // @see wp_trim_words() 
  17. if ( 'characters' == _x( 'words', 'word count: words or characters?', 'jetpack' ) ) { 
  18. return trim( mb_substr( $string, 0, $length - 1, 'UTF-8' ) ) . "\xE2\x80\xA6"; // ellipsis 
  19.  
  20. $words = explode( ' ', $string ); 
  21.  
  22. $return = ''; 
  23. while ( strlen( $word = array_shift( $words ) ) ) { 
  24. $new_return = $return ? "$return $word" : $word; 
  25. $new_return_length = mb_strlen( $new_return, 'UTF-8' ); 
  26. if ( $new_return_length < $length - 1 ) { 
  27. $return = $new_return; 
  28. continue; 
  29. } elseif ( $new_return_length == $length - 1 ) { 
  30. $return = $new_return; 
  31. break; 
  32.  
  33. if ( !$return ) { 
  34. $return = mb_substr( $new_return, 0, $length - 1, 'UTF-8' ); 
  35.  
  36. break; 
  37.  
  38. return "$return\xE2\x80\xA6"; // ellipsis 
  39.  
  40.  
  41. /** 
  42. * Returns an array of DOMNodes that are comments (including recursing child nodes) 
  43. * @param DOMNode $node 
  44. * @return array 
  45. */ 
  46.  
  47. function get_comment_nodes( $node ) { 
  48. $comment_nodes = array(); 
  49. foreach ( $node->childNodes as $child ) { 
  50.  
  51. if ( XML_COMMENT_NODE === $child->nodeType ) { 
  52. $comment_nodes[] = $child; 
  53.  
  54. if ( $child->hasChildNodes() ) { 
  55. $child_comment_nodes = self::get_comment_nodes( $child ); 
  56. $comment_nodes = array_merge( $comment_nodes, $child_comment_nodes ); 
  57.  
  58. return $comment_nodes; 
  59.  
  60. /** 
  61. * Truncates HTML so that its textContent (text without markup) is shorter than or equal to the length specified. 
  62. * The length of the returned string may be larger than the specified length due to the markup. 
  63. * Attempts to truncate on word boundaries. 
  64. * @param string $string 
  65. * @param int $length 
  66. * @param array $allowed_tags KSES input 
  67. * @return string 
  68. */ 
  69. function crop_html( $string, $length = 256, $allowed_tags = array() ) { 
  70. $tags = $GLOBALS['allowedtags']; // Markup allowed in comments... 
  71.  
  72. $tags['img'] = array( // ... plus images ... 
  73. 'alt' => true,  
  74. 'height' => true,  
  75. 'src' => true,  
  76. 'width' => true,  
  77. ); 
  78.  
  79. // ... and some other basics 
  80. $tags['p'] = array(); 
  81. $tags['ul'] = array(); 
  82. $tags['ol'] = array(); 
  83. $tags['li'] = array(); 
  84. $tags['br'] = array(); 
  85.  
  86. $tags = array_merge( $tags, $allowed_tags ); 
  87.  
  88. // Clean up, then KSES to really lock it down 
  89. $string = trim( (string) $string ); 
  90. $string = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $string ); 
  91. $string = wp_kses( $string, $tags ); 
  92.  
  93. $string = mb_convert_encoding( $string, 'HTML-ENTITIES', 'UTF-8' ); 
  94. $dom = new DOMDocument( '1.0', 'UTF-8' ); 
  95. @$dom->loadHTML( "<html><body>$string</body></html>" ); // suppress parser warning 
  96.  
  97. // Strip comment nodes, if any 
  98. $comment_nodes = self::get_comment_nodes( $dom->documentElement ); 
  99. foreach ( $comment_nodes as &$comment_node ) { 
  100. $comment_node->parentNode->removeChild( $comment_node ); 
  101. if ( $comment_nodes ) { 
  102. // Update the $string (some return paths work from just $string) 
  103. $string = $dom->saveHTML(); 
  104. $string = preg_replace( '/^<!DOCTYPE.+?>/', '', $string ); 
  105. $string = str_replace( array('<html>', '</html>', '<body>', '</body>' ), array( '', '', '', '' ), $string ); 
  106. $string = trim( $string ); 
  107.  
  108. // Find the body 
  109. $body = false; 
  110. foreach ( $dom->childNodes as $child ) { 
  111. if ( XML_ELEMENT_NODE === $child->nodeType && 'html' === strtolower( $child->tagName ) ) { 
  112. $body = $child->firstChild; 
  113. break; 
  114.  
  115. if ( !$body ) { 
  116. return self::crop_str( $string, $length ); 
  117.  
  118. // If the text (without the markup) is shorter than $length, just return 
  119. if ( mb_strlen( $body->textContent, 'UTF-8' ) <= $length ) { 
  120. return $string; 
  121.  
  122. $node = false; 
  123. do { 
  124. $node = self::remove_innermost_last_child( $body, $node_removed_from ); 
  125. $new_string_length = mb_strlen( $body->textContent, 'UTF-8' ); 
  126. } while ( $new_string_length > $length ); 
  127.  
  128. $new_string = $dom->saveHTML( $body ); 
  129. $new_string = mb_substr( $new_string, 6, -7, 'UTF-8' ); // 6: <body>, 7: </body> 
  130.  
  131. if ( !$node ) { 
  132. return $new_string ? $new_string : self::crop_str( $string, $length ); 
  133.  
  134. $append_string_length = $length - $new_string_length; 
  135.  
  136. if ( !$append_string_length ) { 
  137. return $new_string; 
  138.  
  139. if ( $append_string_length > 1 && XML_TEXT_NODE === $node->nodeType ) { // 1: ellipsis 
  140. $append_string = self::crop_str( $node->textContent, $append_string_length ); // includes ellipsis 
  141. $append_node = $dom->createTextNode( $append_string ); 
  142. $node_removed_from->appendChild( $append_node ); 
  143. $new_string = $dom->saveHTML( $body ); 
  144. $new_string = mb_substr( $new_string, 6, -7, 'UTF-8' ); 
  145. } elseif ( $append_string_length > 9 && XML_ELEMENT_NODE === $node->nodeType && 'p' == strtolower( $node->nodeName ) ) { // 9: '<p>X{\xE2\x80\xA6}</p>' 
  146. $new_string .= '<p>' . self::crop_str( $node->textContent, $append_string_length - 8 ) . '</p>'; 
  147.  
  148. // Clean up any empty Paragraphs that might have occurred after removing their children 
  149. return trim( preg_replace( '#<p>\s*</p>#i', '', $new_string ) ); 
  150.  
  151. function remove_innermost_last_child( $node, &$node_removed_from ) { 
  152. $node_removed_from = $node; 
  153.  
  154. if ( !$node->lastChild ) { 
  155. return false; 
  156.  
  157. if ( $node->lastChild->hasChildNodes() ) { 
  158. return self::remove_innermost_last_child( $node->lastChild, $node_removed_from ); 
  159.  
  160. $innermost_last_child = $node->lastChild; 
  161. $node->removeChild( $innermost_last_child ); 
  162.  
  163. return $innermost_last_child; 
  164.  
  165. function bump_stats_extras_publicize_url( $bin, $post_id ) { 
  166. static $done = array(); 
  167. if ( isset( $done[$post_id] ) ) { 
  168. return; 
  169. $done[$post_id] = true; 
  170.  
  171. /** This action is documented in modules/widgets/social-media-icons.php */ 
  172. do_action( 'jetpack_bump_stats_extras', 'publicize_url', $bin ); 
  173.  
  174. public static function build_sprintf( $args ) { 
  175. $search = array(); 
  176. $replace = array(); 
  177. foreach ( $args as $k => $arg ) { 
  178. if ( 0 == $k ) { 
  179. $string = $arg; 
  180. continue; 
  181. $search[] = "%$arg%"; 
  182. $replace[] = "%$k\$s"; 
  183. return str_replace( $search, $replace, $string ); 
  184.  
  185. public static function sanitize_message( $message ) { 
  186. $message = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $message ); 
  187. $message = wp_kses( $message, array() ); 
  188. $message = preg_replace('/[\r\n\t ]+/', ' ', $message); 
  189. $message = trim( $message ); 
  190. $message = htmlspecialchars_decode( $message, ENT_QUOTES ); 
  191. return $message;