Automattic /
jetpack
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * Module Name: Publicize |
||
| 4 | * Module Description: Automated social marketing. |
||
| 5 | * Sort Order: 10 |
||
| 6 | * Recommendation Order: 7 |
||
| 7 | * First Introduced: 2.0 |
||
| 8 | * Requires Connection: Yes |
||
| 9 | * Auto Activate: Yes |
||
| 10 | * Module Tags: Social, Recommended |
||
| 11 | * Feature: Engagement |
||
| 12 | * Additional Search Queries: facebook, twitter, google+, googleplus, google, path, tumblr, linkedin, social, tweet, connections, sharing |
||
| 13 | */ |
||
| 14 | |||
| 15 | class Jetpack_Publicize { |
||
| 16 | |||
| 17 | public $in_jetpack = true; |
||
| 18 | |||
| 19 | function __construct() { |
||
| 20 | global $publicize_ui; |
||
| 21 | |||
| 22 | $this->in_jetpack = ( class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'enable_module_configurable' ) ) ? true : false; |
||
| 23 | |||
| 24 | if ( $this->in_jetpack && method_exists( 'Jetpack', 'module_configuration_load' ) ) { |
||
| 25 | Jetpack::enable_module_configurable( __FILE__ ); |
||
| 26 | Jetpack::module_configuration_load( __FILE__, array( $this, 'jetpack_configuration_load' ) ); |
||
| 27 | } |
||
| 28 | |||
| 29 | require_once dirname( __FILE__ ) . '/publicize/publicize.php'; |
||
| 30 | |||
| 31 | if ( $this->in_jetpack ) |
||
| 32 | require_once dirname( __FILE__ ) . '/publicize/publicize-jetpack.php'; |
||
| 33 | else { |
||
| 34 | require_once dirname( dirname( __FILE__ ) ) . '/mu-plugins/keyring/keyring.php'; |
||
| 35 | require_once dirname( __FILE__ ) . '/publicize/publicize-wpcom.php'; |
||
| 36 | } |
||
| 37 | |||
| 38 | require_once dirname( __FILE__ ) . '/publicize/ui.php'; |
||
| 39 | $publicize_ui = new Publicize_UI(); |
||
| 40 | $publicize_ui->in_jetpack = $this->in_jetpack; |
||
| 41 | |||
| 42 | // Jetpack specific checks / hooks |
||
| 43 | if ( $this->in_jetpack) { |
||
| 44 | // if sharedaddy isn't active, the sharing menu hasn't been added yet |
||
| 45 | $active = Jetpack::get_active_modules(); |
||
| 46 | View Code Duplication | if ( in_array( 'publicize', $active ) && !in_array( 'sharedaddy', $active ) ) |
|
| 47 | add_action( 'admin_menu', array( &$publicize_ui, 'sharing_menu' ) ); |
||
| 48 | } |
||
| 49 | } |
||
| 50 | |||
| 51 | function jetpack_configuration_load() { |
||
| 52 | wp_safe_redirect( menu_page_url( 'sharing', false ) ); |
||
| 53 | exit; |
||
| 54 | } |
||
| 55 | } |
||
| 56 | |||
| 57 | global $publicize_ui; |
||
| 58 | new Jetpack_Publicize; |
||
| 59 | |||
| 60 | /** |
||
| 61 | * Helper functions for shared use in the services files |
||
| 62 | */ |
||
| 63 | class Publicize_Util { |
||
| 64 | /** |
||
| 65 | * Truncates a string to be shorter than or equal to the length specified |
||
| 66 | * Attempts to truncate on word boundaries |
||
| 67 | * |
||
| 68 | * @param string $string |
||
| 69 | * @param int $length |
||
| 70 | * @return string |
||
| 71 | */ |
||
| 72 | public static function crop_str( $string, $length = 256 ) { |
||
| 73 | $string = Publicize_Util::sanitize_message( $string ); |
||
| 74 | $length = absint( $length ); |
||
| 75 | |||
| 76 | if ( mb_strlen( $string, 'UTF-8' ) <= $length ) { |
||
| 77 | return $string; |
||
| 78 | } |
||
| 79 | |||
| 80 | // @see wp_trim_words() |
||
| 81 | if ( 'characters' == _x( 'words', 'word count: words or characters?', 'jetpack' ) ) { |
||
| 82 | return trim( mb_substr( $string, 0, $length - 1, 'UTF-8' ) ) . "\xE2\x80\xA6"; // ellipsis |
||
| 83 | } |
||
| 84 | |||
| 85 | $words = explode( ' ', $string ); |
||
| 86 | |||
| 87 | $return = ''; |
||
| 88 | while ( strlen( $word = array_shift( $words ) ) ) { |
||
| 89 | $new_return = $return ? "$return $word" : $word; |
||
| 90 | $new_return_length = mb_strlen( $new_return, 'UTF-8' ); |
||
| 91 | if ( $new_return_length < $length - 1 ) { |
||
| 92 | $return = $new_return; |
||
| 93 | continue; |
||
| 94 | } elseif ( $new_return_length == $length - 1 ) { |
||
| 95 | $return = $new_return; |
||
| 96 | break; |
||
| 97 | } |
||
| 98 | |||
| 99 | if ( !$return ) { |
||
| 100 | $return = mb_substr( $new_return, 0, $length - 1, 'UTF-8' ); |
||
| 101 | } |
||
| 102 | |||
| 103 | break; |
||
| 104 | } |
||
| 105 | |||
| 106 | return "$return\xE2\x80\xA6"; // ellipsis |
||
| 107 | } |
||
| 108 | |||
| 109 | |||
| 110 | /** |
||
| 111 | * Returns an array of DOMNodes that are comments (including recursing child nodes) |
||
| 112 | * |
||
| 113 | * @param DOMNode $node |
||
| 114 | * @return array |
||
| 115 | */ |
||
| 116 | |||
| 117 | function get_comment_nodes( $node ) { |
||
| 118 | $comment_nodes = array(); |
||
| 119 | foreach ( $node->childNodes as $child ) { |
||
| 120 | |||
| 121 | if ( XML_COMMENT_NODE === $child->nodeType ) { |
||
| 122 | $comment_nodes[] = $child; |
||
| 123 | } |
||
| 124 | |||
| 125 | if ( $child->hasChildNodes() ) { |
||
| 126 | $child_comment_nodes = self::get_comment_nodes( $child ); |
||
| 127 | $comment_nodes = array_merge( $comment_nodes, $child_comment_nodes ); |
||
| 128 | } |
||
| 129 | } |
||
| 130 | |||
| 131 | return $comment_nodes; |
||
| 132 | } |
||
| 133 | |||
| 134 | /** |
||
| 135 | * Truncates HTML so that its textContent (text without markup) is shorter than or equal to the length specified. |
||
| 136 | * The length of the returned string may be larger than the specified length due to the markup. |
||
| 137 | * Attempts to truncate on word boundaries. |
||
| 138 | * |
||
| 139 | * @param string $string |
||
| 140 | * @param int $length |
||
| 141 | * @param array $allowed_tags KSES input |
||
| 142 | * @return string |
||
| 143 | */ |
||
| 144 | function crop_html( $string, $length = 256, $allowed_tags = array() ) { |
||
| 145 | $tags = $GLOBALS['allowedtags']; // Markup allowed in comments... |
||
| 146 | |||
| 147 | $tags['img'] = array( // ... plus images ... |
||
| 148 | 'alt' => true, |
||
| 149 | 'height' => true, |
||
| 150 | 'src' => true, |
||
| 151 | 'width' => true, |
||
| 152 | ); |
||
| 153 | |||
| 154 | // ... and some other basics |
||
| 155 | $tags['p'] = array(); |
||
| 156 | $tags['ul'] = array(); |
||
| 157 | $tags['ol'] = array(); |
||
| 158 | $tags['li'] = array(); |
||
| 159 | $tags['br'] = array(); |
||
| 160 | |||
| 161 | $tags = array_merge( $tags, $allowed_tags ); |
||
| 162 | |||
| 163 | // Clean up, then KSES to really lock it down |
||
| 164 | $string = trim( (string) $string ); |
||
| 165 | $string = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $string ); |
||
| 166 | $string = wp_kses( $string, $tags ); |
||
| 167 | |||
| 168 | $string = mb_convert_encoding( $string, 'HTML-ENTITIES', 'UTF-8' ); |
||
| 169 | $dom = new DOMDocument( '1.0', 'UTF-8' ); |
||
| 170 | @$dom->loadHTML( "<html><body>$string</body></html>" ); // suppress parser warning |
||
|
0 ignored issues
–
show
|
|||
| 171 | |||
| 172 | // Strip comment nodes, if any |
||
| 173 | $comment_nodes = self::get_comment_nodes( $dom->documentElement ); |
||
| 174 | foreach ( $comment_nodes as &$comment_node ) { |
||
| 175 | $comment_node->parentNode->removeChild( $comment_node ); |
||
| 176 | } |
||
| 177 | if ( $comment_nodes ) { |
||
| 178 | // Update the $string (some return paths work from just $string) |
||
| 179 | $string = $dom->saveHTML(); |
||
| 180 | $string = preg_replace( '/^<!DOCTYPE.+?>/', '', $string ); |
||
| 181 | $string = str_replace( array('<html>', '</html>', '<body>', '</body>' ), array( '', '', '', '' ), $string ); |
||
| 182 | $string = trim( $string ); |
||
| 183 | } |
||
| 184 | |||
| 185 | // Find the body |
||
| 186 | $body = false; |
||
| 187 | foreach ( $dom->childNodes as $child ) { |
||
| 188 | if ( XML_ELEMENT_NODE === $child->nodeType && 'html' === strtolower( $child->tagName ) ) { |
||
| 189 | $body = $child->firstChild; |
||
| 190 | break; |
||
| 191 | } |
||
| 192 | } |
||
| 193 | |||
| 194 | if ( !$body ) { |
||
| 195 | return self::crop_str( $string, $length ); |
||
| 196 | } |
||
| 197 | |||
| 198 | // If the text (without the markup) is shorter than $length, just return |
||
| 199 | if ( mb_strlen( $body->textContent, 'UTF-8' ) <= $length ) { |
||
| 200 | return $string; |
||
| 201 | } |
||
| 202 | |||
| 203 | $node = false; |
||
| 204 | do { |
||
| 205 | $node = self::remove_innermost_last_child( $body, $node_removed_from ); |
||
| 206 | $new_string_length = mb_strlen( $body->textContent, 'UTF-8' ); |
||
| 207 | } while ( $new_string_length > $length ); |
||
| 208 | |||
| 209 | $new_string = $dom->saveHTML( $body ); |
||
| 210 | $new_string = mb_substr( $new_string, 6, -7, 'UTF-8' ); // 6: <body>, 7: </body> |
||
| 211 | |||
| 212 | if ( !$node ) { |
||
| 213 | return $new_string ? $new_string : self::crop_str( $string, $length ); |
||
| 214 | } |
||
| 215 | |||
| 216 | $append_string_length = $length - $new_string_length; |
||
| 217 | |||
| 218 | if ( !$append_string_length ) { |
||
| 219 | return $new_string; |
||
| 220 | } |
||
| 221 | |||
| 222 | if ( $append_string_length > 1 && XML_TEXT_NODE === $node->nodeType ) { // 1: ellipsis |
||
| 223 | $append_string = self::crop_str( $node->textContent, $append_string_length ); // includes ellipsis |
||
| 224 | $append_node = $dom->createTextNode( $append_string ); |
||
| 225 | $node_removed_from->appendChild( $append_node ); |
||
| 226 | $new_string = $dom->saveHTML( $body ); |
||
| 227 | $new_string = mb_substr( $new_string, 6, -7, 'UTF-8' ); |
||
| 228 | } elseif ( $append_string_length > 9 && XML_ELEMENT_NODE === $node->nodeType && 'p' == strtolower( $node->nodeName ) ) { // 9: '<p>X{\xE2\x80\xA6}</p>' |
||
| 229 | $new_string .= '<p>' . self::crop_str( $node->textContent, $append_string_length - 8 ) . '</p>'; |
||
| 230 | } |
||
| 231 | |||
| 232 | // Clean up any empty Paragraphs that might have occurred after removing their children |
||
| 233 | return trim( preg_replace( '#<p>\s*</p>#i', '', $new_string ) ); |
||
| 234 | } |
||
| 235 | |||
| 236 | function remove_innermost_last_child( $node, &$node_removed_from ) { |
||
| 237 | $node_removed_from = $node; |
||
| 238 | |||
| 239 | if ( !$node->lastChild ) { |
||
| 240 | return false; |
||
| 241 | } |
||
| 242 | |||
| 243 | if ( $node->lastChild->hasChildNodes() ) { |
||
| 244 | return self::remove_innermost_last_child( $node->lastChild, $node_removed_from ); |
||
| 245 | } |
||
| 246 | |||
| 247 | $innermost_last_child = $node->lastChild; |
||
| 248 | $node->removeChild( $innermost_last_child ); |
||
| 249 | |||
| 250 | return $innermost_last_child; |
||
| 251 | } |
||
| 252 | |||
| 253 | function bump_stats_extras_publicize_url( $bin, $post_id ) { |
||
| 254 | static $done = array(); |
||
| 255 | if ( isset( $done[$post_id] ) ) { |
||
| 256 | return; |
||
| 257 | } |
||
| 258 | $done[$post_id] = true; |
||
| 259 | |||
| 260 | /** This action is documented in modules/widgets/social-media-icons.php */ |
||
| 261 | do_action( 'jetpack_bump_stats_extras', 'publicize_url', $bin ); |
||
| 262 | } |
||
| 263 | |||
| 264 | public static function build_sprintf( $args ) { |
||
| 265 | $search = array(); |
||
| 266 | $replace = array(); |
||
| 267 | foreach ( $args as $k => $arg ) { |
||
| 268 | if ( 0 == $k ) { |
||
| 269 | $string = $arg; |
||
| 270 | continue; |
||
| 271 | } |
||
| 272 | $search[] = "%$arg%"; |
||
| 273 | $replace[] = "%$k\$s"; |
||
| 274 | } |
||
| 275 | return str_replace( $search, $replace, $string ); |
||
| 276 | } |
||
| 277 | |||
| 278 | public static function sanitize_message( $message ) { |
||
| 279 | $message = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $message ); |
||
| 280 | $message = wp_kses( $message, array() ); |
||
| 281 | $message = preg_replace('/[\r\n\t ]+/', ' ', $message); |
||
| 282 | $message = trim( $message ); |
||
| 283 | $message = htmlspecialchars_decode( $message, ENT_QUOTES ); |
||
| 284 | return $message; |
||
| 285 | } |
||
| 286 | } |
||
| 287 | |||
| 288 | if( ! ( defined( 'IS_WPCOM' ) && IS_WPCOM ) && ! function_exists( 'publicize_init' ) ) { |
||
| 289 | /** |
||
| 290 | * Helper for grabbing a Publicize object from the "front-end" (non-admin) of |
||
| 291 | * a site. Normally Publicize is only loaded in wp-admin, so there's a little |
||
| 292 | * set up that you might need to do if you want to use it on the front end. |
||
| 293 | * Just call this function and it returns a Publicize object. |
||
| 294 | * |
||
| 295 | * @return Publicize Object |
||
| 296 | */ |
||
| 297 | function publicize_init() { |
||
| 298 | global $publicize; |
||
| 299 | |||
| 300 | if ( ! class_exists( 'Publicize' ) ) { |
||
| 301 | require_once dirname( __FILE__ ) . '/publicize/publicize.php'; |
||
| 302 | } |
||
| 303 | |||
| 304 | return $publicize; |
||
| 305 | } |
||
| 306 | |||
| 307 | } |
||
| 308 |
If you suppress an error, we recommend checking for the error condition explicitly: