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 { |
||
|
0 ignored issues
–
show
|
|||
| 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 | |||
| 171 | // The @ is not enough to suppress errors when dealing with libxml, |
||
| 172 | // we have to tell it directly how we want to handle errors. |
||
| 173 | libxml_use_internal_errors( true ); |
||
| 174 | @$dom->loadHTML( "<html><body>$string</body></html>" ); |
||
|
0 ignored issues
–
show
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.
If you suppress an error, we recommend checking for the error condition explicitly: // For example instead of
@mkdir($dir);
// Better use
if (@mkdir($dir) === false) {
throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
|
|||
| 175 | libxml_use_internal_errors( false ); |
||
| 176 | |||
| 177 | // Strip comment nodes, if any |
||
| 178 | $comment_nodes = self::get_comment_nodes( $dom->documentElement ); |
||
| 179 | foreach ( $comment_nodes as &$comment_node ) { |
||
| 180 | $comment_node->parentNode->removeChild( $comment_node ); |
||
| 181 | } |
||
| 182 | if ( $comment_nodes ) { |
||
|
0 ignored issues
–
show
The expression
$comment_nodes of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using Loading history...
|
|||
| 183 | // Update the $string (some return paths work from just $string) |
||
| 184 | $string = $dom->saveHTML(); |
||
| 185 | $string = preg_replace( '/^<!DOCTYPE.+?>/', '', $string ); |
||
| 186 | $string = str_replace( array('<html>', '</html>', '<body>', '</body>' ), array( '', '', '', '' ), $string ); |
||
| 187 | $string = trim( $string ); |
||
| 188 | } |
||
| 189 | |||
| 190 | // Find the body |
||
| 191 | $body = false; |
||
| 192 | foreach ( $dom->childNodes as $child ) { |
||
| 193 | if ( XML_ELEMENT_NODE === $child->nodeType && 'html' === strtolower( $child->tagName ) ) { |
||
| 194 | $body = $child->firstChild; |
||
| 195 | break; |
||
| 196 | } |
||
| 197 | } |
||
| 198 | |||
| 199 | if ( !$body ) { |
||
| 200 | return self::crop_str( $string, $length ); |
||
| 201 | } |
||
| 202 | |||
| 203 | // If the text (without the markup) is shorter than $length, just return |
||
| 204 | if ( mb_strlen( $body->textContent, 'UTF-8' ) <= $length ) { |
||
| 205 | return $string; |
||
| 206 | } |
||
| 207 | |||
| 208 | $node = false; |
||
|
0 ignored issues
–
show
$node is not used, you could remove the assignment.
This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently. $myVar = 'Value';
$higher = false;
if (rand(1, 6) > 3) {
$higher = true;
} else {
$higher = false;
}
Both the Loading history...
|
|||
| 209 | do { |
||
| 210 | $node = self::remove_innermost_last_child( $body, $node_removed_from ); |
||
| 211 | $new_string_length = mb_strlen( $body->textContent, 'UTF-8' ); |
||
| 212 | } while ( $new_string_length > $length ); |
||
| 213 | |||
| 214 | $new_string = $dom->saveHTML( $body ); |
||
| 215 | $new_string = mb_substr( $new_string, 6, -7, 'UTF-8' ); // 6: <body>, 7: </body> |
||
| 216 | |||
| 217 | if ( !$node ) { |
||
| 218 | return $new_string ? $new_string : self::crop_str( $string, $length ); |
||
| 219 | } |
||
| 220 | |||
| 221 | $append_string_length = $length - $new_string_length; |
||
| 222 | |||
| 223 | if ( !$append_string_length ) { |
||
| 224 | return $new_string; |
||
| 225 | } |
||
| 226 | |||
| 227 | if ( $append_string_length > 1 && XML_TEXT_NODE === $node->nodeType ) { // 1: ellipsis |
||
| 228 | $append_string = self::crop_str( $node->textContent, $append_string_length ); // includes ellipsis |
||
| 229 | $append_node = $dom->createTextNode( $append_string ); |
||
| 230 | $node_removed_from->appendChild( $append_node ); |
||
| 231 | $new_string = $dom->saveHTML( $body ); |
||
| 232 | $new_string = mb_substr( $new_string, 6, -7, 'UTF-8' ); |
||
| 233 | } elseif ( $append_string_length > 9 && XML_ELEMENT_NODE === $node->nodeType && 'p' == strtolower( $node->nodeName ) ) { // 9: '<p>X{\xE2\x80\xA6}</p>' |
||
| 234 | $new_string .= '<p>' . self::crop_str( $node->textContent, $append_string_length - 8 ) . '</p>'; |
||
| 235 | } |
||
| 236 | |||
| 237 | // Clean up any empty Paragraphs that might have occurred after removing their children |
||
| 238 | return trim( preg_replace( '#<p>\s*</p>#i', '', $new_string ) ); |
||
| 239 | } |
||
| 240 | |||
| 241 | function remove_innermost_last_child( $node, &$node_removed_from ) { |
||
| 242 | $node_removed_from = $node; |
||
| 243 | |||
| 244 | if ( !$node->lastChild ) { |
||
| 245 | return false; |
||
| 246 | } |
||
| 247 | |||
| 248 | if ( $node->lastChild->hasChildNodes() ) { |
||
| 249 | return self::remove_innermost_last_child( $node->lastChild, $node_removed_from ); |
||
| 250 | } |
||
| 251 | |||
| 252 | $innermost_last_child = $node->lastChild; |
||
| 253 | $node->removeChild( $innermost_last_child ); |
||
| 254 | |||
| 255 | return $innermost_last_child; |
||
| 256 | } |
||
| 257 | |||
| 258 | function bump_stats_extras_publicize_url( $bin, $post_id ) { |
||
| 259 | static $done = array(); |
||
| 260 | if ( isset( $done[$post_id] ) ) { |
||
| 261 | return; |
||
| 262 | } |
||
| 263 | $done[$post_id] = true; |
||
| 264 | |||
| 265 | /** This action is documented in modules/widgets/social-media-icons.php */ |
||
| 266 | do_action( 'jetpack_bump_stats_extras', 'publicize_url', $bin ); |
||
| 267 | } |
||
| 268 | |||
| 269 | public static function build_sprintf( $args ) { |
||
| 270 | $search = array(); |
||
| 271 | $replace = array(); |
||
| 272 | foreach ( $args as $k => $arg ) { |
||
| 273 | if ( 0 == $k ) { |
||
| 274 | $string = $arg; |
||
| 275 | continue; |
||
| 276 | } |
||
| 277 | $search[] = "%$arg%"; |
||
| 278 | $replace[] = "%$k\$s"; |
||
| 279 | } |
||
| 280 | return str_replace( $search, $replace, $string ); |
||
| 281 | } |
||
| 282 | |||
| 283 | public static function sanitize_message( $message ) { |
||
| 284 | $message = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $message ); |
||
| 285 | $message = wp_kses( $message, array() ); |
||
| 286 | $message = preg_replace('/[\r\n\t ]+/', ' ', $message); |
||
| 287 | $message = trim( $message ); |
||
| 288 | $message = htmlspecialchars_decode( $message, ENT_QUOTES ); |
||
| 289 | return $message; |
||
| 290 | } |
||
| 291 | } |
||
| 292 | |||
| 293 | if( ! ( defined( 'IS_WPCOM' ) && IS_WPCOM ) && ! function_exists( 'publicize_init' ) ) { |
||
| 294 | /** |
||
| 295 | * Helper for grabbing a Publicize object from the "front-end" (non-admin) of |
||
| 296 | * a site. Normally Publicize is only loaded in wp-admin, so there's a little |
||
| 297 | * set up that you might need to do if you want to use it on the front end. |
||
| 298 | * Just call this function and it returns a Publicize object. |
||
| 299 | * |
||
| 300 | * @return Publicize Object |
||
| 301 | */ |
||
| 302 | function publicize_init() { |
||
| 303 | global $publicize; |
||
| 304 | |||
| 305 | if ( ! class_exists( 'Publicize' ) ) { |
||
| 306 | require_once dirname( __FILE__ ) . '/publicize/publicize.php'; |
||
| 307 | } |
||
| 308 | |||
| 309 | return $publicize; |
||
| 310 | } |
||
| 311 | |||
| 312 | } |
||
| 313 |
Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.