PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 6.1
Jetpack – WP Security, Backup, Speed, & Growth v6.1
16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 All 501 releases
jetpack / modules / publicize.php

publicize.php in Jetpack – WP Security, Backup, Speed, & Growth 6.1, at modules/publicize.php

313 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 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
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>" );
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 ) {
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;
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