Jetpack by WordPress.com - Version 2.9.4

Version Description

Release Date: May 26, 2016

  • Important security update. Please upgrade immediately.
Download this release

Release Info

Developer samhotchkiss
Plugin Icon 128x128 Jetpack by WordPress.com
Version 2.9.4
Comparing to
See all releases

Code changes from version 2.8.3 to 2.9.4

.travis.yml DELETED
@@ -1,40 +0,0 @@
1
- # Travis CI Configuration File
2
-
3
- # Tell Travis CI we're using PHP
4
- language: php
5
-
6
- # Versions of PHP to test against
7
- php:
8
- - "5.2"
9
- - "5.3"
10
- - "5.4"
11
-
12
- # Specify versions of WordPress to test against
13
- # WP_VERSION = WordPress version number (use "master" for SVN trunk)
14
- # WP_MULTISITE = whether to test multisite (use either "0" or "1")
15
- env:
16
- - WP_VERSION=master WP_MULTISITE=0
17
- - WP_VERSION=3.6 WP_MULTISITE=0
18
- - WP_VERSION=3.5.2 WP_MULTISITE=0
19
- # - WP_VERSION=3.4.2 WP_MULTISITE=0
20
- # - WP_VERSION=3.3.3 WP_MULTISITE=0
21
- # - WP_VERSION=3.2.1 WP_MULTISITE=0
22
- - WP_VERSION=master WP_MULTISITE=1
23
- - WP_VERSION=3.6 WP_MULTISITE=1
24
- - WP_VERSION=3.5.2 WP_MULTISITE=1
25
- # - WP_VERSION=3.4.2 WP_MULTISITE=1
26
- # - WP_VERSION=3.3.3 WP_MULTISITE=1
27
- # - WP_VERSION=3.2.1 WP_MULTISITE=1
28
-
29
- # Grab the setup script and execute
30
- before_script:
31
- - wget https://raw.github.com/tierra/wordpress-plugin-tests/setup/setup.sh
32
- - source setup.sh
33
-
34
- script: phpunit
35
-
36
- # wordpress-plugin-tests specific config
37
- # Tells Travis CI not to run unit tests against the setup branch
38
- branches:
39
- except:
40
- - setup
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3rd-party/3rd-party.php ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /*
4
+ * Placeholder to load 3rd party plugin tweaks until a legit system
5
+ * is architected
6
+ */
7
+
8
+ require_once( 'buddypress.php' );
3rd-party/buddypress.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ add_filter( 'bp_core_pre_avatar_handle_upload', 'blobphoto' );
4
+ function blobphoto( $bool ) {
5
+
6
+ add_filter( 'jetpack_photon_skip_image', '__return_true' );
7
+
8
+ return $bool;
9
+ }
_inc/jetpack.css CHANGED
@@ -40,7 +40,9 @@
40
  .toplevel_page_jetpack ul#adminmenu a.wp-has-current-submenu:after,
41
  .toplevel_page_jetpack ul#adminmenu > li.current > a.current:after,
42
  .jetpack-pagestyles ul#adminmenu a.wp-has-current-submenu:after,
43
- .jetpack-pagestyles ul#adminmenu > li.current > a.current:after {
 
 
44
  border-right-color:#8fad4b;
45
  }
46
 
@@ -65,7 +67,9 @@
65
  }
66
 
67
  .toplevel_page_jetpack.jetpack-connected #wpwrap,
68
- .jetpack-pagestyles.jetpack-connected #wpwrap {
 
 
69
  background: url(images/header-clouds-small.png) 52px 0 repeat-x;
70
  }
71
 
@@ -121,10 +125,18 @@
121
  margin: 30px 0 -30px 0;
122
  }
123
 
 
 
 
 
124
  #jp-header.small {
125
  margin: 20px 0 50px;
126
  }
127
 
 
 
 
 
128
  #jp-header #jp-clouds {
129
  position: relative;
130
  padding-top: 110px;
@@ -192,7 +204,9 @@
192
  }
193
 
194
  .toplevel_page_jetpack.jetpack-connected #wpwrap,
195
- .jetpack-pagestyles.jetpack-connected #wpwrap {
 
 
196
  background-image: url(images/header-clouds-small-2x.png);
197
  background-size: 980px 140px;
198
  }
@@ -229,6 +243,13 @@
229
  top: -35px;
230
  }
231
 
 
 
 
 
 
 
 
232
  /* Retina Logo */
233
  @media only screen and (-moz-min-device-pixel-ratio: 1.5),
234
  only screen and (-o-min-device-pixel-ratio: 3/2),
@@ -240,7 +261,8 @@
240
  background-size: 250px 200px;
241
  }
242
 
243
- #jp-header.small h3 {
 
244
  background-image: url(images/logo-small-2x.png);
245
  background-size: 150px 120px;
246
  }
@@ -355,6 +377,11 @@
355
  overflow: hidden;
356
  }
357
 
 
 
 
 
 
358
  .jetpack-message h4 {
359
  margin: 0 10px 10px 0;
360
  font-size: 18px;
@@ -365,6 +392,10 @@
365
  display: inline-block;
366
  }
367
 
 
 
 
 
368
  .jetpack-message h5 {
369
  margin: 0;
370
  }
@@ -1098,6 +1129,10 @@ p#news-sub {
1098
  margin: 10px 13px 40px 15px;
1099
  }
1100
 
 
 
 
 
1101
  .jp-survey h4 {
1102
  margin: 0;
1103
  font-size: 18px;
@@ -1213,6 +1248,10 @@ p#news-sub {
1213
  cursor: pointer;
1214
  }
1215
 
 
 
 
 
1216
  .jetpack-install-container p a.button-connector:hover,
1217
  .jetpack-install-container p a.button-connector:active {
1218
  background-color: #f0a000;
40
  .toplevel_page_jetpack ul#adminmenu a.wp-has-current-submenu:after,
41
  .toplevel_page_jetpack ul#adminmenu > li.current > a.current:after,
42
  .jetpack-pagestyles ul#adminmenu a.wp-has-current-submenu:after,
43
+ .jetpack-pagestyles ul#adminmenu > li.current > a.current:after,
44
+ .jetpack_page_jetpack-settings.network-admin ul#adminmenu a.wp-has-current-submenu:after,
45
+ .jetpack_page_jetpack-settings.network-admin ul#adminmenu > li.current > a.current:after {
46
  border-right-color:#8fad4b;
47
  }
48
 
67
  }
68
 
69
  .toplevel_page_jetpack.jetpack-connected #wpwrap,
70
+ .jetpack-pagestyles.jetpack-connected #wpwrap,
71
+ .toplevel_page_jetpack.network-admin #wpwrap,
72
+ .jetpack_page_jetpack-settings.network-admin #wpwrap {
73
  background: url(images/header-clouds-small.png) 52px 0 repeat-x;
74
  }
75
 
125
  margin: 30px 0 -30px 0;
126
  }
127
 
128
+ .network-admin #jp-header {
129
+ margin-bottom: -50px;
130
+ }
131
+
132
  #jp-header.small {
133
  margin: 20px 0 50px;
134
  }
135
 
136
+ .network-admin #jp-header.small {
137
+ margin-bottom: 30px;
138
+ }
139
+
140
  #jp-header #jp-clouds {
141
  position: relative;
142
  padding-top: 110px;
204
  }
205
 
206
  .toplevel_page_jetpack.jetpack-connected #wpwrap,
207
+ .jetpack-pagestyles.jetpack-connected #wpwrap,
208
+ .toplevel_page_jetpack.network-admin #wpwrap,
209
+ .jetpack_page_jetpack-settings.network-admin #wpwrap {
210
  background-image: url(images/header-clouds-small-2x.png);
211
  background-size: 980px 140px;
212
  }
243
  top: -35px;
244
  }
245
 
246
+ .network-admin #jp-header h3 {
247
+ background: transparent url(images/logo-small.png) top left no-repeat;
248
+ width: 150px;
249
+ height: 120px;
250
+ left: 18px;
251
+ }
252
+
253
  /* Retina Logo */
254
  @media only screen and (-moz-min-device-pixel-ratio: 1.5),
255
  only screen and (-o-min-device-pixel-ratio: 3/2),
261
  background-size: 250px 200px;
262
  }
263
 
264
+ #jp-header.small h3,
265
+ .network-admin #jp-header h3 {
266
  background-image: url(images/logo-small-2x.png);
267
  background-size: 150px 120px;
268
  }
377
  overflow: hidden;
378
  }
379
 
380
+ .jetpack-message h2 {
381
+ color: #fff;
382
+ text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
383
+ }
384
+
385
  .jetpack-message h4 {
386
  margin: 0 10px 10px 0;
387
  font-size: 18px;
392
  display: inline-block;
393
  }
394
 
395
+ .jetpack-message.jp-multisite h4 {
396
+ margin-bottom: 0;
397
+ }
398
+
399
  .jetpack-message h5 {
400
  margin: 0;
401
  }
1129
  margin: 10px 13px 40px 15px;
1130
  }
1131
 
1132
+ .network-admin #jetpack-settings .wrap {
1133
+ margin: 10px 18px 30px 18px;
1134
+ }
1135
+
1136
  .jp-survey h4 {
1137
  margin: 0;
1138
  font-size: 18px;
1248
  cursor: pointer;
1249
  }
1250
 
1251
+ .network-admin .jetpack-install-container p a.button-connector {
1252
+ bottom: 15px;
1253
+ }
1254
+
1255
  .jetpack-install-container p a.button-connector:hover,
1256
  .jetpack-install-container p a.button-connector:active {
1257
  background-color: #f0a000;
_inc/lib/markdown/gfm.php CHANGED
@@ -6,9 +6,7 @@
6
  * @author Matt Wiebe <wiebe@automattic.com>
7
  * @link https://github.com/evansolomon/wp-github-flavored-markdown-comments
8
  *
9
- * Add a few extras from GitHub's Markdown implementation. Must be used
10
- * in a WordPress environment if the $preserve_shortcodes member is set to true,
11
- * which will be auto-detected initially on __construct()
12
  */
13
 
14
  class WPCom_GHF_Markdown_Parser extends MarkdownExtra_Parser {
@@ -102,6 +100,51 @@ class WPCom_GHF_Markdown_Parser extends MarkdownExtra_Parser {
102
  return $text;
103
  }
104
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  /**
106
  * Called to preserve legacy LaTeX like $latex some-latex-text $
107
  * @param string $text Text in which to preserve LaTeX
@@ -254,10 +297,15 @@ class WPCom_GHF_Markdown_Parser extends MarkdownExtra_Parser {
254
  * Overload to support Viper's [code] shortcode. Because awesome.
255
  */
256
  public function _doFencedCodeBlocks_callback( $matches ) {
257
- // just MarkdownExtra_Parser if we're not going ultra-deluxe, or if
258
- // there wasn't a language class passed
259
- if ( ! $this->use_code_shortcode || empty( $matches[2] ) )
260
  return parent::_doFencedCodeBlocks_callback( $matches );
 
 
 
 
 
 
261
 
262
  $classname =& $matches[2];
263
  $codeblock = preg_replace_callback('/^\n+/', array( $this, '_doFencedCodeBlocks_newlines' ), $matches[4] );
@@ -265,6 +313,7 @@ class WPCom_GHF_Markdown_Parser extends MarkdownExtra_Parser {
265
  if ( $classname{0} == '.' )
266
  $classname = substr( $classname, 1 );
267
 
 
268
  $codeblock = sprintf( $this->shortcode_start, $classname ) . "\n{$codeblock}" . $this->shortcode_end;
269
  return "\n\n" . $this->hashBlock( $codeblock ). "\n\n";
270
  }
6
  * @author Matt Wiebe <wiebe@automattic.com>
7
  * @link https://github.com/evansolomon/wp-github-flavored-markdown-comments
8
  *
9
+ * Add a few extras from GitHub's Markdown implementation. Must be used in a WordPress environment.
 
 
10
  */
11
 
12
  class WPCom_GHF_Markdown_Parser extends MarkdownExtra_Parser {
100
  return $text;
101
  }
102
 
103
+ /**
104
+ * Preserve code block contents by HTML encoding them. Useful before getting to KSES stripping.
105
+ * @param string $text Markdown/HTML content
106
+ * @return string Markdown/HTML content with escaped code blocks
107
+ */
108
+ public function codeblock_preserve( $text ) {
109
+ $text = preg_replace_callback( "/^(`{3})([^`\n]+)?\n([^`~]+)(`{3})/m", array( $this, 'do_codeblock_preserve' ), $text );
110
+ $text = preg_replace_callback( "/^(~{3})([^~\n]+)?\n([^~~]+)(~{3})/m", array( $this, 'do_codeblock_preserve' ), $text );
111
+ return $text;
112
+ }
113
+
114
+ /**
115
+ * Regex callback for code block preservation.
116
+ * @param array $matches Regex matches
117
+ * @return string Codeblock with escaped interior
118
+ */
119
+ public function do_codeblock_preserve( $matches ) {
120
+ $block = stripslashes( $matches[3] );
121
+ $block = esc_html( $block );
122
+ $open = $matches[1] . $matches[2] . "\n";
123
+ return $open . $block . $matches[4];
124
+ }
125
+
126
+ /**
127
+ * Restore previously preserved (i.e. escaped) code block contents.
128
+ * @param string $text Markdown/HTML content with escaped code blocks
129
+ * @return string Markdown/HTML content
130
+ */
131
+ public function codeblock_restore( $text ) {
132
+ $text = preg_replace_callback( "/^(`{3})([^`\n]+)?\n([^`~]+)(`{3})/m", array( $this, 'do_codeblock_restore' ), $text );
133
+ $text = preg_replace_callback( "/^(~{3})([^~\n]+)?\n([^~~]+)(~{3})/m", array( $this, 'do_codeblock_restore' ), $text );
134
+ return $text;
135
+ }
136
+
137
+ /**
138
+ * Regex callback for code block restoration (unescaping).
139
+ * @param array $matches Regex matches
140
+ * @return string Codeblock with unescaped interior
141
+ */
142
+ public function do_codeblock_restore( $matches ) {
143
+ $block = html_entity_decode( $matches[3] );
144
+ $open = $matches[1] . $matches[2] . "\n";
145
+ return $open . $block . $matches[4];
146
+ }
147
+
148
  /**
149
  * Called to preserve legacy LaTeX like $latex some-latex-text $
150
  * @param string $text Text in which to preserve LaTeX
297
  * Overload to support Viper's [code] shortcode. Because awesome.
298
  */
299
  public function _doFencedCodeBlocks_callback( $matches ) {
300
+ // just MarkdownExtra_Parser if we're not going ultra-deluxe
301
+ if ( ! $this->use_code_shortcode ) {
 
302
  return parent::_doFencedCodeBlocks_callback( $matches );
303
+ }
304
+
305
+ // default to a "text" class if one wasn't passed. Helps with encoding issues later.
306
+ if ( empty( $matches[2] ) ) {
307
+ $matches[2] = 'text';
308
+ }
309
 
310
  $classname =& $matches[2];
311
  $codeblock = preg_replace_callback('/^\n+/', array( $this, '_doFencedCodeBlocks_newlines' ), $matches[4] );
313
  if ( $classname{0} == '.' )
314
  $classname = substr( $classname, 1 );
315
 
316
+ $codeblock = esc_html( $codeblock );
317
  $codeblock = sprintf( $this->shortcode_start, $classname ) . "\n{$codeblock}" . $this->shortcode_end;
318
  return "\n\n" . $this->hashBlock( $codeblock ). "\n\n";
319
  }
class.jetpack-client-server.php CHANGED
@@ -9,9 +9,7 @@
9
  class Jetpack_Client_Server {
10
  function authorize() {
11
  $data = stripslashes_deep( $_GET );
12
-
13
  $args = array();
14
-
15
  $redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
16
 
17
  do {
@@ -136,7 +134,7 @@ class Jetpack_Client_Server {
136
  function get_token( $data ) {
137
  $jetpack = Jetpack::init();
138
  $role = $jetpack->translate_current_user_to_role();
139
-
140
  if ( !$role ) {
141
  return new Jetpack_Error( 'role', __( 'An administrator for this blog must set up the Jetpack connection.', 'jetpack' ) );
142
  }
9
  class Jetpack_Client_Server {
10
  function authorize() {
11
  $data = stripslashes_deep( $_GET );
 
12
  $args = array();
 
13
  $redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
14
 
15
  do {
134
  function get_token( $data ) {
135
  $jetpack = Jetpack::init();
136
  $role = $jetpack->translate_current_user_to_role();
137
+
138
  if ( !$role ) {
139
  return new Jetpack_Error( 'role', __( 'An administrator for this blog must set up the Jetpack connection.', 'jetpack' ) );
140
  }
class.jetpack-debugger.php CHANGED
@@ -15,7 +15,7 @@ class Jetpack_Debugger {
15
  }
16
 
17
  public static function jetpack_increase_timeout($time) {
18
- $time = 20; //seconds
19
  return $time;
20
  }
21
 
15
  }
16
 
17
  public static function jetpack_increase_timeout($time) {
18
+ $time = 30; //seconds
19
  return $time;
20
  }
21
 
class.jetpack-network-sites-list-table.php ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if( ! class_exists( 'WP_List_Table' ) ) {
4
+ require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
5
+ }
6
+
7
+ class Jetpack_Network_Sites_List_Table extends WP_List_Table {
8
+
9
+
10
+ public function get_columns() {
11
+ // site name, status, username connected under
12
+ $columns = array(
13
+ 'cb' => '<input type="checkbox" />',
14
+ 'blogname' => __( 'Site Name', 'jetpack' ),
15
+ 'blog_path' => __( 'Path', 'jetpack' ),
16
+ 'connected' => __( 'Connected', 'jetpack' ),
17
+ );
18
+
19
+ return $columns;
20
+ }
21
+
22
+ public function prepare_items() {
23
+ $jpms = Jetpack_Network::init();
24
+
25
+ // Deal with bulk actions if any were requested by the user
26
+ $this->process_bulk_action();
27
+
28
+ $columns = $this->get_columns();
29
+ $hidden = array();
30
+ $sortable = array();
31
+ $this->_column_headers = array( $columns, $hidden, $sortable );
32
+ $this->items = $jpms->wp_get_sites();;
33
+ }
34
+
35
+ public function column_blogname( $item ) {
36
+ // http://jpms/wp-admin/network/site-info.php?id=1
37
+ switch_to_blog( $item->blog_id );
38
+ $jp_url = admin_url( 'admin.php?page=jetpack' );
39
+ restore_current_blog();
40
+
41
+ $actions = array(
42
+ 'edit' => '<a href="' . network_admin_url( 'site-info.php?id=' . $item->blog_id ) . '">' . __( 'Edit', 'jetpack' ) . '</a>',
43
+ 'dashboard' => '<a href="' . get_admin_url( $item->blog_id, '', 'admin' ) . '">Dashboard</a>',
44
+ 'view' => '<a href="' . get_site_url( $item->blog_id, '', 'admin' ) . '">View</a>',
45
+ 'jetpack-' . $item->blog_id => '<a href="' . $jp_url . '">Jetpack</a>',
46
+ );
47
+
48
+ return sprintf('%1$s %2$s', '<strong>' . get_blog_option( $item->blog_id, 'blogname' ) . '</strong>', $this->row_actions($actions) );
49
+ }
50
+
51
+ public function column_blog_path( $item ) {
52
+ return
53
+ '<a href="' .
54
+ get_site_url( $item->blog_id, '', 'admin' ) .
55
+ '">' .
56
+ str_replace( array( 'http://', 'https://' ), '', get_site_url( $item->blog_id, '', 'admin' ) ) .
57
+ '</a>';
58
+ }
59
+
60
+ public function column_connected( $item ) {
61
+ $jpms = Jetpack_Network::init();
62
+ $jp = Jetpack::init();
63
+
64
+ switch_to_blog( $item->blog_id );
65
+ if( $jp->is_active() ) {
66
+ // Build url for disconnecting
67
+ $url = $jpms->get_url( array(
68
+ 'name' => 'subsitedisconnect',
69
+ 'site_id' => $item->blog_id,
70
+
71
+ ) );
72
+ restore_current_blog();
73
+ return '<a href="' . $url . '">Disconnect</a>';
74
+ }
75
+ restore_current_blog();
76
+
77
+ // Build URL for connecting
78
+ $url = $jpms->get_url( array(
79
+ 'name' => 'subsiteregister',
80
+ 'site_id' => $item->blog_id,
81
+ ) );
82
+ return '<a href="' . $url . '">Connect</a>';
83
+ }
84
+
85
+ public function get_bulk_actions() {
86
+ $actions = array(
87
+ 'connect' => 'Connect',
88
+ 'disconnect' => 'Disconnect'
89
+ );
90
+
91
+ return $actions;
92
+ }
93
+
94
+ function column_cb($item) {
95
+ return sprintf(
96
+ '<input type="checkbox" name="bulk[]" value="%s" />', $item->blog_id
97
+ );
98
+ }
99
+
100
+ /**
101
+ * @todo Ensure sites are not in/active before performing action
102
+ */
103
+ public function process_bulk_action() {
104
+ if( !isset( $_POST['bulk'] ) || empty ( $_POST['bulk'] ) )
105
+ return; // Thou shall not pass! There is nothing to do
106
+
107
+ $jpms = Jetpack_Network::init();
108
+
109
+ $action = $this->current_action();
110
+ switch ( $action ) {
111
+
112
+ case 'connect':
113
+ foreach( $_POST['bulk'] AS $k => $site ) {
114
+ $jpms->do_subsiteregister( $site );
115
+ }
116
+ break;
117
+ case 'disconnect':
118
+ foreach( $_POST['bulk'] AS $k => $site ) {
119
+ $jpms->do_subsitedisconnect( $site );
120
+ }
121
+ break;
122
+ }
123
+ }
124
+ } // end h
class.jetpack-network.php ADDED
@@ -0,0 +1,821 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Used to manage Jetpack installation on Multisite Network installs
5
+ *
6
+ * SINGLETON: To use call Jetpack_Network::init()
7
+ *
8
+ * DO NOT USE ANY STATIC METHODS IN THIS CLASS!!!!!!
9
+ *
10
+ * @todo Look through todos in Jetpack_Network
11
+ * @since 2.9
12
+ */
13
+ class Jetpack_Network {
14
+
15
+ /**
16
+ * Holds a static copy of Jetpack_Network for the singleton
17
+ *
18
+ * @since 2.9
19
+ * @var Jetpack_Network
20
+ */
21
+ private static $instance = null;
22
+
23
+ /**
24
+ * Name of the network wide settings
25
+ *
26
+ * @since 2.9
27
+ * @var string
28
+ */
29
+ private $settings_name = 'jetpack-network-settings';
30
+
31
+ /**
32
+ * Defaults for settings found on the Jetpack > Settings page
33
+ *
34
+ * @since 2.9
35
+ * @var array
36
+ */
37
+ private $setting_defaults = array(
38
+ 'auto-connect' => 0,
39
+ 'sub-site-connection-override' => 1,
40
+ // 'manage_auto_activated_modules' => 0,
41
+ );
42
+
43
+ /**
44
+ * Constructor
45
+ *
46
+ * @since 2.9
47
+ */
48
+ private function __construct() {
49
+ require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); // For the is_plugin... check
50
+ /*
51
+ * Sanity check to ensure the install is Multisite and we
52
+ * are in Network Admin
53
+ */
54
+ if (is_multisite() && is_network_admin()) {
55
+ add_action('network_admin_menu', array($this, 'add_network_admin_menu'));
56
+ add_action('network_admin_edit_jetpack-network-settings', array($this, 'save_network_settings_page'), 10, 0);
57
+ add_action( 'admin_init', array ( $this, 'jetpack_sites_list' ) );
58
+ add_filter( 'admin_body_class', array( $this, 'body_class' ) );
59
+ add_filter( 'wpmu_blogs_columns', array( $this, 'add_jetpack_sites_column' ) );
60
+ //add_action( 'manage_sites_custom_column', array( $this, 'render_jetpack_sites_column' ), 10, 2 );
61
+ //add_action( 'manage_blogs_custom_column', array( $this, 'render_jetpack_sites_column' ), 10, 2 );
62
+ }
63
+
64
+ /*
65
+ * Things that should only run on multisite
66
+ */
67
+ if( is_multisite() && is_plugin_active_for_network( 'jetpack/jetpack.php' ) ) {
68
+ add_action( 'wp_before_admin_bar_render', array( $this, 'add_to_menubar' ) );
69
+
70
+
71
+ /*
72
+ * If admin wants to automagically register new sites set the hook here
73
+ *
74
+ * This is a hacky way because xmlrpc is not available on wpmu_new_blog
75
+ */
76
+ if( $this->get_option( 'auto-connect' ) == 1 ) {
77
+ //add_action( 'admin_init', array( $this, 'do_automatically_add_new_site' ) );
78
+ add_action( 'wpmu_new_blog', array( $this, 'do_automatically_add_new_site' ) );
79
+ }
80
+ }
81
+
82
+ // Remove the toggles for 2.9, re-evaluate how they're done and added for a 3.0 release. They don't feel quite right yet.
83
+ // add_filter( 'jetpack_get_default_modules', array( $this, 'set_auto_activated_modules' ) );
84
+ }
85
+
86
+ /**
87
+ * Sets which modules get activated by default on subsite connection.
88
+ * Modules can be set in Network Admin > Jetpack > Settings
89
+ *
90
+ * @since 2.9
91
+ * @param array $modules
92
+ * @return array
93
+ **/
94
+ public function set_auto_activated_modules( $modules ) {
95
+ return $modules;
96
+
97
+ /* Remove the toggles for 2.9, re-evaluate how they're done and added for a 3.0 release. They don't feel quite right yet.
98
+ if( 1 == $this->get_option( 'manage_auto_activated_modules' ) ) {
99
+ return (array) $this->get_option( 'modules' );
100
+ } else {
101
+ return $modules;
102
+ }
103
+ */
104
+ }
105
+
106
+ /**
107
+ * Displays the Jetpack connection status on the Network Admin > Sites
108
+ * page.
109
+ *
110
+ * @param string $column_name
111
+ * @param int $blog_id
112
+ **/
113
+ public function render_jetpack_sites_column( $column_name, $blog_id ) {
114
+ if( 'jetpack_connection' != $column_name )
115
+ return;
116
+
117
+ $jp = Jetpack::init();
118
+
119
+ switch_to_blog( $blog_id );
120
+ if( $jp->is_active() ) {
121
+ // Build url for disconnecting
122
+ $url = $this->get_url( array(
123
+ 'name' => 'subsitedisconnect',
124
+ 'site_id' => $blog_id,
125
+
126
+ ) );
127
+ restore_current_blog();
128
+ echo '<a href="' . $url . '">Disconnect</a>';
129
+ return;
130
+ }
131
+ restore_current_blog();
132
+
133
+ // Build URL for connecting
134
+ $url = $this->get_url( array(
135
+ 'name' => 'subsiteregister',
136
+ 'site_id' => $blog_id,
137
+ ) );
138
+ echo '<a href="' . $url . '">Connect</a>';
139
+ return;
140
+ }
141
+
142
+ /**
143
+ * Add the column for Jetpack connection status to the
144
+ * Network Admin > Sites list
145
+ *
146
+ * @since 2.9
147
+ * @param array $columns
148
+ * @return array
149
+ **/
150
+ public function add_jetpack_sites_column( $columns ) {
151
+ $columns['jetpack_connection'] = __( 'Jetpack' );
152
+ return $columns;
153
+ }
154
+
155
+ /**
156
+ * Registers new sites upon creation
157
+ *
158
+ * @since 2.9
159
+ * @uses wpmu_new_blog
160
+ * @param int $blog_id
161
+ **/
162
+ public function do_automatically_add_new_site( $blog_id ) {
163
+ $this->do_subsiteregister( $blog_id );
164
+ }
165
+
166
+ /**
167
+ * Adds .network-admin class to the body tag
168
+ * Helps distiguish network admin JP styles from regular site JP styles
169
+ *
170
+ * @since 2.9
171
+ */
172
+ public function body_class( $classes ) {
173
+ return 'network-admin';
174
+ }
175
+
176
+ /**
177
+ * Provides access to an instance of Jetpack_Network
178
+ *
179
+ * This is how the Jetpack_Network object should *always* be accessed
180
+ *
181
+ * @since 2.9
182
+ * @return Jetpack_Network
183
+ */
184
+ public static function init() {
185
+ if (!self::$instance || !is_a(self::$instance, 'Jetpack_Network')) {
186
+ self::$instance = new Jetpack_Network;
187
+ }
188
+
189
+ return self::$instance;
190
+ }
191
+
192
+ /**
193
+ * Registers the Multisite admin bar menu item shortcut.
194
+ * This shortcut helps users quickly and easily navigate to the Jetpack Network Admin
195
+ * menu from anywhere in their network.
196
+ *
197
+ * @since 2.9
198
+ */
199
+ public function register_menubar() {
200
+ add_action( 'wp_before_admin_bar_render', array( $this, 'add_to_menubar' ) );
201
+ }
202
+
203
+ /**
204
+ * Runs when Jetpack is deactivated from the network admin plugins menu.
205
+ * Each individual site will need to have Jetpack::disconnect called on it.
206
+ * Site that had Jetpack individually enabled will not be disconnected as
207
+ * on Multisite individually activated plugins are still activated when
208
+ * a plugin is deactivated network wide.
209
+ *
210
+ * @since 2.9
211
+ **/
212
+ public function deactivate() {
213
+ if( !is_network_admin() ) return; // Only fire if in network admin
214
+
215
+ $sites = $this->wp_get_sites();
216
+
217
+ foreach( $sites AS $s ) {
218
+ switch_to_blog( $s->blog_id );
219
+ $plugins = get_option( 'active_plugins' );
220
+
221
+ /*
222
+ * If this plugin was activated in the subsite individually
223
+ * we do not want to call disconnect. Plugins activated
224
+ * individually (before network activation) stay activated
225
+ * when the network deactivation occurs
226
+ */
227
+ if( !in_array( 'jetpack/jetpack.php', $plugins ) ) {
228
+ Jetpack::disconnect();
229
+ }
230
+ }
231
+ restore_current_blog();
232
+ }
233
+
234
+ /**
235
+ * Adds a link to the Jetpack Network Admin page in the netowrk admin menu bar.
236
+ *
237
+ * @since 2.9
238
+ **/
239
+ public function add_to_menubar () {
240
+ global $wp_admin_bar;
241
+ // Don't show for logged out users or single site mode.
242
+ if ( ! is_user_logged_in() || ! is_multisite() )
243
+ return;
244
+
245
+ $wp_admin_bar->add_node( array(
246
+ 'parent' => 'network-admin',
247
+ 'id' => 'network-admin-jetpack',
248
+ 'title' => __( 'Jetpack' ),
249
+ 'href' => $this->get_url( 'network_admin_page' ),
250
+ ) );
251
+ }
252
+
253
+ /**
254
+ * Returns various URL strings. Factory like
255
+ *
256
+ * $args can be a string or an array.
257
+ * If $args is an array there must be an element called name for the switch statement
258
+ *
259
+ * Currently supports:
260
+ * - subsiteregister: Pass array( 'name' => 'subsiteregister', 'site_id' => SITE_ID )
261
+ * - network_admin_page: Provides link to /wp-admin/network/JETPACK
262
+ * - subsitedisconnect: Pass array( 'name' => 'subsitedisconnect', 'site_id' => SITE_ID )
263
+ *
264
+ * @since 2.9
265
+ * @param Mixed $args
266
+ * @return String
267
+ **/
268
+ public function get_url( $args ) {
269
+ $url = null; // Default url value
270
+
271
+ if( is_string( $args ) )
272
+ $name = $args;
273
+ else
274
+ $name = $args['name'];
275
+
276
+
277
+ switch( $name ) {
278
+ case 'subsiteregister':
279
+ if( !isset( $args['site_id'] ) ) {
280
+ break; // If there is not a site id present we cannot go further
281
+ }
282
+ $url = network_admin_url(
283
+ 'admin.php?page=jetpack&action=subsiteregister&site_id='
284
+ . $args['site_id'] );
285
+ break;
286
+
287
+ case 'network_admin_page':
288
+ $url = network_admin_url( 'admin.php?page=jetpack' );
289
+ break;
290
+ case 'subsitedisconnect':
291
+ if( !isset( $args['site_id'] ) ) {
292
+ break; // If there is not a site id present we cannot go further
293
+ }
294
+ $url = network_admin_url(
295
+ 'admin.php?page=jetpack&action=subsitedisconnect&site_id='
296
+ . $args['site_id'] );
297
+ break;
298
+ }
299
+ return $url;
300
+ }
301
+
302
+ /**
303
+ * Adds the Jetpack menu item to the Network Admin area
304
+ *
305
+ * @since 2.9
306
+ */
307
+ public function add_network_admin_menu() {
308
+ add_action( 'admin_print_styles', array( $this, 'network_admin_styles' ) );
309
+
310
+ add_menu_page(__('Jetpack', 'jetpack'), __('Jetpack', 'jetpack'), 'read', 'jetpack', array($this, 'network_admin_page'), 'div', 3);
311
+ add_submenu_page('jetpack', 'Jetpack Sites', 'Sites', 'manage_options', 'jetpack', array($this, 'network_admin_page'));
312
+ add_submenu_page('jetpack', __('Settings', 'jetpack'), __('Settings', 'jetpack'), 'read', 'jetpack-settings', array($this, 'render_network_admin_settings_page'));
313
+
314
+ /**
315
+ * As jetpack_register_genericons is by default fired off a hook,
316
+ * the hook may have already fired by this point.
317
+ * So, let's just trigger it manually.
318
+ */
319
+ require_once( JETPACK__PLUGIN_DIR . '_inc/genericons.php' );
320
+ jetpack_register_genericons();
321
+
322
+ if ( ! wp_style_is( 'jetpack-icons', 'registered' ) )
323
+ wp_register_style( 'jetpack-icons', plugins_url( '_inc/jetpack-icons/jetpack-icons.css', __FILE__ ), false, JETPACK__VERSION );
324
+
325
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
326
+ }
327
+
328
+ /**
329
+ * Adds JP menu icon
330
+ *
331
+ * @since 2.9
332
+ **/
333
+ function admin_menu_css() {
334
+ // Make sure we're working off a clean version.
335
+ include( ABSPATH . WPINC . '/version.php' );
336
+ if ( version_compare( $wp_version, '3.8-alpha', '>=' ) ) {
337
+ wp_enqueue_style( 'jetpack-icons' );
338
+ $css = "
339
+ #toplevel_page_jetpack .wp-menu-image:before {
340
+ font-family: 'Jetpack' !important;
341
+ content: '\\e600';
342
+ }
343
+ #menu-posts-feedback .wp-menu-image:before {
344
+ font-family: dashicons !important;
345
+ content: '\\f175';
346
+ }
347
+ #adminmenu #menu-posts-feedback div.wp-menu-image {
348
+ background: none !important;
349
+ background-repeat: no-repeat;
350
+ }";
351
+ } else {
352
+ $css = "
353
+ #toplevel_page_jetpack .wp-menu-image {
354
+ background: url( " . plugins_url( '_inc/images/menuicon-sprite.png', __FILE__ ) . " ) 0 90% no-repeat;
355
+ }
356
+ /* Retina Jetpack Menu Icon */
357
+ @media only screen and (-moz-min-device-pixel-ratio: 1.5),
358
+ only screen and (-o-min-device-pixel-ratio: 3/2),
359
+ only screen and (-webkit-min-device-pixel-ratio: 1.5),
360
+ only screen and (min-device-pixel-ratio: 1.5) {
361
+ #toplevel_page_jetpack .wp-menu-image {
362
+ background: url( " . plugins_url( '_inc/images/menuicon-sprite-2x.png', __FILE__ ) . " ) 0 90% no-repeat;
363
+ background-size:30px 64px;
364
+ }
365
+ }
366
+ #toplevel_page_jetpack.current .wp-menu-image,
367
+ #toplevel_page_jetpack.wp-has-current-submenu .wp-menu-image,
368
+ #toplevel_page_jetpack:hover .wp-menu-image {
369
+ background-position: top left;
370
+ }";
371
+ }
372
+ wp_add_inline_style( 'wp-admin', $css );
373
+ }
374
+
375
+ /**
376
+ * Provides functionality for the Jetpack > Sites page.
377
+ * Does not do the display!
378
+ *
379
+ * @since 2.9
380
+ */
381
+ public function jetpack_sites_list() {
382
+ $jp = Jetpack::init();
383
+
384
+ if( isset( $_GET['action'] ) ) {
385
+ switch( $_GET['action'] ) {
386
+ case 'subsiteregister':
387
+ /*
388
+ * @todo check_admin_referer( 'jetpack-subsite-register' );
389
+ */
390
+ Jetpack::log( 'subsiteregister' );
391
+
392
+ // If !$_GET['site_id'] stop registration and error
393
+ if( !isset( $_GET['site_id'] ) || empty( $_GET['site_id'] ) ) {
394
+ // Log error to state cookie for display later
395
+ /**
396
+ * @todo Make state messages show on Jetpack NA pages
397
+ **/
398
+ Jetpack::state( 'missing_site_id', 'Site ID must be provided to register a sub-site' );
399
+ break;
400
+ }
401
+
402
+ // Send data to register endpoint and retrieve shadow blog details
403
+ $result = $this->do_subsiteregister();
404
+ $url = $this->get_url( 'network_admin_page' );
405
+ if( is_wp_error( $result ) ) {
406
+ $url = add_query_arg( 'action', 'connection_failed', $url );
407
+ } else {
408
+ $url = add_query_arg( 'action', 'connected', $url );
409
+ }
410
+
411
+ wp_safe_redirect( $url );
412
+ break;
413
+ case 'subsitedisconnect':
414
+ Jetpack::log( 'subsitedisconnect' );
415
+
416
+ if( !isset( $_GET['site_id'] ) || empty( $_GET['site_id'] ) ) {
417
+ Jetpack::state( 'missing_site_id', 'Site ID must be provided to disconnect a sub-site' );
418
+ break;
419
+ }
420
+
421
+ $this->do_subsitedisconnect();
422
+ break;
423
+ case 'connected':
424
+ case 'connection_failed':
425
+ add_action( 'jetpack_notices', array( $this, 'show_jetpack_notice' ) );
426
+ break;
427
+ }
428
+ }
429
+ }
430
+
431
+ public function show_jetpack_notice() {
432
+
433
+ if( isset( $_GET['action'] ) && 'connected' == $_GET['action'] ) {
434
+ $notice = 'Blog successfully connected';
435
+ } else if( isset( $_GET['action'] ) && 'connection_failed' == $_GET['action'] ) {
436
+ $notice = 'Blog connection <strong>failed</strong>';
437
+ }
438
+
439
+ require_once( 'views/admin/network-admin-alert.php' );
440
+ }
441
+
442
+ /**
443
+ * Disconnect functionality for an individual site
444
+ *
445
+ * @since 2.9
446
+ * @see Jetpack_Network::jetpack_sites_list()
447
+ */
448
+ public function do_subsitedisconnect( $site_id = null ) {
449
+ $site_id = ( is_null( $site_id ) ) ? $_GET['site_id']: $site_id;
450
+ switch_to_blog( $site_id );
451
+ Jetpack::disconnect();
452
+ restore_current_blog();
453
+ }
454
+
455
+ /**
456
+ * Registers a subsite with the Jetpack servers
457
+ *
458
+ * @since 2.9
459
+ * @todo Break apart into easier to manage chunks that can be unit tested
460
+ * @see Jetpack_Network::jetpack_sites_list();
461
+ */
462
+ public function do_subsiteregister( $site_id = null ) {
463
+ $jp = Jetpack::init();
464
+
465
+ // Figure out what site we are working on
466
+ $site_id = ( is_null( $site_id ) ) ? $_GET['site_id']: $site_id;
467
+
468
+ // Build secrets to sent to wpcom for verification
469
+ $secrets = $jp->generate_secrets();
470
+
471
+ // Remote query timeout limit
472
+ $timeout = $jp->get_remote_query_timeout_limit();
473
+
474
+ // Get proof the wpcom server can trust you adding this site
475
+ $network_admin_token = '';
476
+
477
+ // The blog id on WordPress.com of the primary network site
478
+ $network_wpcom_blog_id = Jetpack_Options::get_option( 'id' );
479
+
480
+ /*
481
+ * Here we need to switch to the subsite
482
+ * For the registration process we really only hijack how it
483
+ * works for an idividual site and pass in some extra data here
484
+ */
485
+ switch_to_blog( $site_id );
486
+
487
+ // Save the secrets in the subsite so when the wpcom server does a pingback it
488
+ // will be able to validate the connection
489
+ Jetpack_Options::update_option( 'register',
490
+ $secrets[0] . ':' .$secrets[1]. ':' . $secrets[2]
491
+ );
492
+
493
+ // Gra info for gmt offset
494
+ $gmt_offset = get_option( 'gmt_offset' );
495
+ if ( ! $gmt_offset ) {
496
+ $gmt_offset = 0;
497
+ }
498
+
499
+ /*
500
+ * Get the stats_option option from the db.
501
+ * It looks like the server strips this out so maybe it is not necessary?
502
+ * Does it match the Jetpack site with the old stats plugin id?
503
+ *
504
+ * @todo Find out if sending the stats_id is necessary
505
+ */
506
+ $stat_options = get_option( 'stats_options' );
507
+ $stat_id = $stat_options = isset($stats_options['blog_id']) ? $stats_options['blog_id'] : null;
508
+ $args = array(
509
+ 'method' => 'POST',
510
+ 'body' => array(
511
+ 'network_url' => $this->get_url( 'network_admin_page' ),
512
+ 'network_wpcom_blog_id' => $network_wpcom_blog_id,
513
+ 'siteurl' => site_url(),
514
+ 'home' => home_url(),
515
+ 'gmt_offset' => $gmt_offset,
516
+ 'timezone_string' => (string) get_option( 'timezone_string' ),
517
+ 'site_name' => (string) get_option( 'blogname' ),
518
+ 'secret_1' => $secrets[0],
519
+ 'secret_2' => $secrets[1],
520
+ 'site_lang' => get_locale(),
521
+ 'timeout' => $timeout,
522
+ 'stats_id' => $stat_id, // Is this still required?
523
+ 'user_id' => get_current_user_id(),
524
+ ),
525
+ 'headers' => array(
526
+ 'Accept' => 'application/json',
527
+ ),
528
+ 'timeout' => $timeout,
529
+ );
530
+
531
+ // Attempt to retrieve shadow blog details
532
+ $response = Jetpack_Client::_wp_remote_request(
533
+ Jetpack::fix_url_for_bad_hosts( Jetpack::api_url( 'subsiteregister' ) ), $args, true
534
+ );
535
+
536
+ /*
537
+ * $response should either be invalid or contain:
538
+ * - jetpack_id => id
539
+ * - jetpack_secret => blog_token
540
+ * - jetpack_public
541
+ *
542
+ * Store the wpcom site details
543
+ */
544
+ $valid_response = $jp->validate_remote_register_response( $response );
545
+
546
+ if( is_wp_error( $valid_response ) || !$valid_response ) {
547
+ return $valid_response;
548
+ }
549
+
550
+ // Grab the response values to work with
551
+ $code = wp_remote_retrieve_response_code( $response );
552
+ $entity = wp_remote_retrieve_body( $response );
553
+ if ( $entity )
554
+ $json = json_decode( $entity );
555
+ else
556
+ $json = false;
557
+
558
+ if ( empty( $json->jetpack_secret ) || ! is_string( $json->jetpack_secret ) )
559
+ return new Jetpack_Error( 'jetpack_secret', '', $code );
560
+
561
+ if ( isset( $json->jetpack_public ) ) {
562
+ $jetpack_public = (int) $json->jetpack_public;
563
+ } else {
564
+ $jetpack_public = false;
565
+ }
566
+
567
+ Jetpack_Options::update_options(
568
+ array(
569
+ 'id' => (int) $json->jetpack_id,
570
+ 'blog_token' => (string) $json->jetpack_secret,
571
+ 'public' => $jetpack_public,
572
+ )
573
+ );
574
+
575
+ /*
576
+ * Update the subsiteregister method on wpcom so that it also sends back the
577
+ * token in this same request
578
+ */
579
+ $is_master_user = ! Jetpack::is_active();
580
+ Jetpack::update_user_token(
581
+ get_current_user_id(),
582
+ sprintf(
583
+ '%s.%d',
584
+ $json->token->secret,
585
+ get_current_user_id()
586
+ ),
587
+ $is_master_user
588
+ );
589
+
590
+ Jetpack::activate_default_modules();
591
+
592
+ restore_current_blog();
593
+ }
594
+
595
+ /**
596
+ * Add css styles needed for the Network Admin area
597
+ **/
598
+ function network_admin_styles() {
599
+ global $wp_styles;
600
+ wp_enqueue_style( 'jetpack', plugins_url( '_inc/jetpack.css', __FILE__ ), false, JETPACK__VERSION . '-20121016' );
601
+ $wp_styles->add_data( 'jetpack', 'rtl', true );
602
+ }
603
+
604
+ /**
605
+ * Handles the displaying of all sites on the network that are
606
+ * dis/connected to Jetpack
607
+ *
608
+ * @since 2.9
609
+ * @see Jetpack_Network::jetpack_sites_list()
610
+ */
611
+ function network_admin_page() {
612
+ $this->network_admin_page_header();
613
+
614
+ $jp = Jetpack::init();
615
+
616
+ // We should be, but ensure we are on the main blog
617
+ switch_to_blog(1);
618
+ $main_active = $jp->is_active();
619
+ restore_current_blog();
620
+
621
+ /*
622
+ * Ensure the main blog is connected as all other subsite blog
623
+ * connections will feed off this one
624
+ */
625
+ if( !$main_active ) {
626
+ $url = $this->get_url( array(
627
+ 'name' => 'subsiteregister',
628
+ 'site_id' => 1,
629
+ ) );
630
+ $url = $jp->build_connect_url();
631
+ require_once( 'views/admin/must-connect-main-blog.php' );
632
+ return;
633
+ }
634
+
635
+ require_once( 'class.jetpack-network-sites-list-table.php' );
636
+ $myListTable = new Jetpack_Network_Sites_List_Table();
637
+ echo '<div class="wrap"><h2>' . __('Sites', 'jetpack') . '</h2>';
638
+ echo '<form method="post">';
639
+ $myListTable->prepare_items();
640
+ $myListTable->display();
641
+ echo '</form></div>';
642
+
643
+ $this->network_admin_page_footer();
644
+ }
645
+
646
+ /**
647
+ * Stylized JP header formatting
648
+ *
649
+ * @since 2.9
650
+ */
651
+ function network_admin_page_header() {
652
+ global $current_user;
653
+
654
+ $is_connected = Jetpack::is_active();
655
+ $user_token = Jetpack_Data::get_access_token( $current_user->ID );
656
+ $is_user_connected = $user_token && ! is_wp_error( $user_token );
657
+ $is_master_user = $current_user->ID == Jetpack_Options::get_option( 'master_user' );
658
+
659
+ require_once( 'views/admin/network-admin-header.php' );
660
+ }
661
+
662
+ /**
663
+ * Stylized JP footer formatting
664
+ *
665
+ * @since 2.9
666
+ */
667
+ function network_admin_page_footer() {
668
+ require_once( 'views/admin/network-admin-footer.php' );
669
+ }
670
+
671
+ /**
672
+ * Fires when the Jetpack > Settings page is saved.
673
+ *
674
+ * @since 2.9
675
+ */
676
+ public function save_network_settings_page() {
677
+
678
+ /*
679
+ * Fields
680
+ *
681
+ * auto-connect - Checkbox for global Jetpack connection
682
+ * sub-site-connection-override - Allow sub-site admins to (dis)reconnect with their own Jetpack account
683
+ */
684
+ $auto_connect = 0;
685
+ if( isset( $_POST['auto-connect'] ) )
686
+ $auto_connect = 1;
687
+
688
+
689
+ $sub_site_connection_override = 0;
690
+ if( isset( $_POST['sub-site-connection-override'] ) )
691
+ $sub_site_connection_override = 1;
692
+ /* Remove the toggles for 2.9, re-evaluate how they're done and added for a 3.0 release. They don't feel quite right yet.
693
+ $manage_auto_activated_modules = 0;
694
+ if( isset( $_POST['manage_auto_activated_modules'] ) )
695
+ $manage_auto_activated_modules = 1;
696
+
697
+ $modules = array();
698
+ if( isset( $_POST['modules'] ) )
699
+ $modules = $_POST['modules'];
700
+ */
701
+ $data = array(
702
+ 'auto-connect' => $auto_connect,
703
+ 'sub-site-connection-override' => $sub_site_connection_override,
704
+ // 'manage_auto_activated_modules' => $manage_auto_activated_modules,
705
+ // 'modules' => $modules,
706
+ );
707
+
708
+ update_site_option( $this->settings_name, $data );
709
+ wp_safe_redirect(add_query_arg(array('page' => 'jetpack-settings', 'updated' => 'true'), network_admin_url('admin.php')));
710
+ exit();
711
+ }
712
+
713
+ public function render_network_admin_settings_page() {
714
+ $this->network_admin_page_header();
715
+ $options = wp_parse_args( get_site_option( $this->settings_name ), $this->setting_defaults );
716
+
717
+ $modules = array();
718
+ $module_slugs = Jetpack::get_available_modules();
719
+ foreach ( $module_slugs as $slug ) {
720
+ $module = Jetpack::get_module( $slug );
721
+ $module['module'] = $slug;
722
+ $modules[] = $module;
723
+ }
724
+
725
+ usort( $modules, array( 'Jetpack', 'sort_modules' ) );
726
+
727
+ if( !isset( $options['modules'] ) ) {
728
+ $options['modules'] = $modules;
729
+ }
730
+
731
+ require( 'views/admin/network-settings.php' );
732
+ $this->network_admin_page_footer();
733
+ }
734
+
735
+ /**
736
+ * Updates a site wide option
737
+ *
738
+ * @since 2.9
739
+ * @param string $key
740
+ * @param mixed $value
741
+ * @return boolean
742
+ **/
743
+ public function update_option( $key, $value ) {
744
+ $options = get_site_option( $this->settings_name );
745
+ $options[$key] = $value;
746
+ return update_site_option( $this->settings_name, $options );
747
+ }
748
+
749
+ /**
750
+ * Retrieves a site wide option
751
+ *
752
+ * @since 2.9
753
+ * @param string $name - Name of the option in the database
754
+ **/
755
+ public function get_option( $name ) {
756
+ $options = get_site_option( $this->settings_name );
757
+
758
+ $options = wp_parse_args( $options, $this->setting_defaults );
759
+
760
+ if( !isset( $options[$name] ) )
761
+ $options[$name] = null;
762
+
763
+ return $options[$name];
764
+ }
765
+
766
+ /**
767
+ * Return an array of sites on the specified network. If no network is specified,
768
+ * return all sites, regardless of network.
769
+ *
770
+ *
771
+ * @todo REMOVE THIS FUNCTION! This function is moving to core. Use that one in favor of this. WordPress::wp_get_sites(). http://codex.wordpress.org/Function_Reference/wp_get_sites NOTE, This returns an array instead of stdClass. Be sure to update class.network-sites-list-table.php
772
+ * @since 2.9
773
+ * @deprecated 2.4.5
774
+ * @param array|string $args Optional. Specify the status of the sites to return.
775
+ * @return array An array of site data
776
+ */
777
+ public function wp_get_sites($args = array()) {
778
+ global $wpdb;
779
+
780
+ if (wp_is_large_network())
781
+ return;
782
+
783
+ $defaults = array('network_id' => $wpdb->siteid);
784
+
785
+ $args = wp_parse_args($args, $defaults);
786
+
787
+ $query = "SELECT * FROM $wpdb->blogs WHERE 1=1 ";
788
+
789
+ if (isset($args['network_id']) && ( is_array($args['network_id']) || is_numeric($args['network_id']) )) {
790
+ $network_ids = array_map('intval', (array) $args['network_id']);
791
+ $network_ids = implode(',', $network_ids);
792
+ $query .= "AND site_id IN ($network_ids) ";
793
+ }
794
+
795
+ if (isset($args['public']))
796
+ $query .= $wpdb->prepare("AND public = %s ", $args['public']);
797
+
798
+ if (isset($args['archived']))
799
+ $query .= $wpdb->prepare("AND archived = %s ", $args['archived']);
800
+
801
+ if (isset($args['mature']))
802
+ $query .= $wpdb->prepare("AND mature = %s ", $args['mature']);
803
+
804
+ if (isset($args['spam']))
805
+ $query .= $wpdb->prepare("AND spam = %s ", $args['spam']);
806
+
807
+ if (isset($args['deleted']))
808
+ $query .= $wpdb->prepare("AND deleted = %s ", $args['deleted']);
809
+
810
+ $key = 'wp_get_sites:' . md5($query);
811
+
812
+ if (!$site_results = wp_cache_get($key, 'site-id-cache')) {
813
+ $site_results = (array) $wpdb->get_results($query);
814
+ wp_cache_set($key, $site_results, 'site-id-cache');
815
+ }
816
+
817
+ return $site_results;
818
+ }
819
+ }
820
+
821
+ // end class
class.jetpack-options.php CHANGED
@@ -71,6 +71,7 @@ class Jetpack_Options {
71
  * @param mixed $value Option value
72
  */
73
  public static function update_option( $name, $value ) {
 
74
  if ( in_array( $name, self::get_option_names( 'non_compact' ) ) ) {
75
  return update_option( "jetpack_$name", $value );
76
  } else if ( !in_array( $name, self::get_option_names() ) ) {
71
  * @param mixed $value Option value
72
  */
73
  public static function update_option( $name, $value ) {
74
+ do_action( 'pre_update_jetpack_option_' . $name, $name, $value );
75
  if ( in_array( $name, self::get_option_names( 'non_compact' ) ) ) {
76
  return update_option( "jetpack_$name", $value );
77
  } else if ( !in_array( $name, self::get_option_names() ) ) {
class.jetpack-post-images.php CHANGED
@@ -451,30 +451,31 @@ class Jetpack_PostImages {
451
  }
452
 
453
  /**
454
- * Takes an image URL and a pixel dimensions and returns a URL for the
455
- * squared up image if possible.
456
  *
457
  * @param string $src
458
  * @param int $dimension
459
  * @return string Transformed image URL
460
  */
461
- static function square_image_url( $src, $dimension ) {
462
- $dimension = (int) $dimension;
 
463
 
464
  // Umm...
465
- if ( $dimension < 1 ) {
466
  return $src;
467
  }
468
 
469
  // If WPCOM hosted image use native transformations
470
  $img_host = parse_url( $src, PHP_URL_HOST );
471
  if ( '.files.wordpress.com' == substr( $img_host, -20 ) ) {
472
- return add_query_arg( array( 'w' => $dimension, 'h' => $dimension, 'crop' => 1 ), $src );
473
  }
474
 
475
  // Use Photon magic
476
  if( function_exists( 'jetpack_photon_url' ) ) {
477
- return jetpack_photon_url( $src, array( 'resize' => "$dimension,$dimension" ) );
478
  }
479
 
480
  // Arg... no way to resize image using WordPress.com infrastructure!
451
  }
452
 
453
  /**
454
+ * Takes an image URL and pixel dimensions then returns a URL for the
455
+ * resized and croped image.
456
  *
457
  * @param string $src
458
  * @param int $dimension
459
  * @return string Transformed image URL
460
  */
461
+ static function fit_image_url( $src, $width, $height ) {
462
+ $width = (int) $width;
463
+ $height = (int) $height;
464
 
465
  // Umm...
466
+ if ( $width < 1 || $height < 1 ) {
467
  return $src;
468
  }
469
 
470
  // If WPCOM hosted image use native transformations
471
  $img_host = parse_url( $src, PHP_URL_HOST );
472
  if ( '.files.wordpress.com' == substr( $img_host, -20 ) ) {
473
+ return add_query_arg( array( 'w' => $width, 'h' => $height, 'crop' => 1 ), $src );
474
  }
475
 
476
  // Use Photon magic
477
  if( function_exists( 'jetpack_photon_url' ) ) {
478
+ return jetpack_photon_url( $src, array( 'resize' => "$width,$height" ) );
479
  }
480
 
481
  // Arg... no way to resize image using WordPress.com infrastructure!
class.jetpack-sync.php CHANGED
@@ -32,6 +32,7 @@ class Jetpack_Sync {
32
  * post_stati => array( post_status slugs ): The post stati to sync. Default: publish
33
  */
34
  static function sync_posts( $file, array $settings = null ) {
 
35
  $jetpack = Jetpack::init();
36
  $args = func_get_args();
37
  return call_user_func_array( array( $jetpack->sync, 'posts' ), $args );
@@ -46,6 +47,7 @@ class Jetpack_Sync {
46
  * comment_stati => array( comment_status slugs ): The comment stati to sync. Default: approved
47
  */
48
  static function sync_comments( $file, array $settings = null ) {
 
49
  $jetpack = Jetpack::init();
50
  $args = func_get_args();
51
  return call_user_func_array( array( $jetpack->sync, 'comments' ), $args );
@@ -57,6 +59,7 @@ class Jetpack_Sync {
57
  * @param string $option ...
58
  */
59
  static function sync_options( $file, $option /*, $option, ... */ ) {
 
60
  $jetpack = Jetpack::init();
61
  $args = func_get_args();
62
  return call_user_func_array( array( $jetpack->sync, 'options' ), $args );
@@ -761,7 +764,7 @@ class Jetpack_Sync {
761
  $strings = json_encode( array(
762
  'WAITING' => array(
763
  'action' => __( 'Refresh Status', 'jetpack' ),
764
- 'status' => __( 'Indexing posts&hellip;', 'jetpack' ),
765
  ),
766
  'INDEXING' => array(
767
  'action' => __( 'Refresh Status', 'jetpack' ),
32
  * post_stati => array( post_status slugs ): The post stati to sync. Default: publish
33
  */
34
  static function sync_posts( $file, array $settings = null ) {
35
+ if( is_network_admin() ) return;
36
  $jetpack = Jetpack::init();
37
  $args = func_get_args();
38
  return call_user_func_array( array( $jetpack->sync, 'posts' ), $args );
47
  * comment_stati => array( comment_status slugs ): The comment stati to sync. Default: approved
48
  */
49
  static function sync_comments( $file, array $settings = null ) {
50
+ if( is_network_admin() ) return;
51
  $jetpack = Jetpack::init();
52
  $args = func_get_args();
53
  return call_user_func_array( array( $jetpack->sync, 'comments' ), $args );
59
  * @param string $option ...
60
  */
61
  static function sync_options( $file, $option /*, $option, ... */ ) {
62
+ if( is_network_admin() ) return;
63
  $jetpack = Jetpack::init();
64
  $args = func_get_args();
65
  return call_user_func_array( array( $jetpack->sync, 'options' ), $args );
764
  $strings = json_encode( array(
765
  'WAITING' => array(
766
  'action' => __( 'Refresh Status', 'jetpack' ),
767
+ 'status' => __( 'Indexing request queued and waiting&hellip;', 'jetpack' ),
768
  ),
769
  'INDEXING' => array(
770
  'action' => __( 'Refresh Status', 'jetpack' ),
class.jetpack.php CHANGED
@@ -71,42 +71,55 @@ class Jetpack {
71
  */
72
  private $conflicting_plugins = array(
73
  'comments' => array(
74
- 'Intense Debate' => 'intensedebate/intensedebate.php',
75
- 'Disqus' => 'disqus-comment-system/disqus.php',
 
 
 
76
  ),
77
  'contact-form' => array(
78
- 'Contact Form 7' => 'contact-form-7/wp-contact-form-7.php',
79
- 'Gravity Forms' => 'gravityforms/gravityforms.php',
80
- 'Contact Form Plugin' => 'contact-form-plugin/contact_form.php',
81
- 'Easy Contact Forms' => 'easy-contact-forms/easy-contact-forms.php',
82
- 'Fast Secure Contact Form' => 'si-contact-form/si-contact-form.php',
83
  ),
84
  'gplus-authorship' => array(
85
- 'WP SEO by Yoast' => 'wordpress-seo/wp-seo.php',
86
  ),
87
  'minileven' => array(
88
- 'WPtouch' => 'wptouch/wptouch.php',
89
  ),
90
  'latex' => array(
91
- 'LaTeX for WordPress' => 'latex/latex.php',
92
- 'Youngwhans Simple Latex' => 'youngwhans-simple-latex/yw-latex.php',
93
- 'Easy WP LaTeX' => 'easy-wp-latex-lite/easy-wp-latex-lite.php',
94
- 'MathJax-LaTeX' => 'mathjax-latex/mathjax-latex.php',
95
- 'Enable Latex' => 'enable-latex/enable-latex.php',
96
- 'WP QuickLaTeX' => 'wp-quicklatex/wp-quicklatex.php',
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  ),
98
  'sharedaddy' => array(
99
- 'AddThis' => 'addthis/addthis_social_widget.php',
100
- 'Add To Any' => 'add-to-any/add-to-any.php',
101
- 'ShareThis' => 'share-this/sharethis.php',
102
- 'Shareaholic' => 'shareaholic/shareaholic.php',
103
  ),
104
  'widget-visibility' => array(
105
- 'Widget Logic' => 'widget-logic/widget_logic.php',
106
- 'Dynamic Widgets' => 'dynamic-widgets/dynamic-widgets.php',
107
- ),
108
- 'random-redirect' => array(
109
- 'Random Redirect 2' => 'random-redirect-2/random-redirect.php',
110
  ),
111
  );
112
 
@@ -221,6 +234,23 @@ class Jetpack {
221
  * Constructor. Initializes WordPress hooks
222
  */
223
  private function Jetpack() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  $this->sync = new Jetpack_Sync;
225
 
226
  // Modules should do Jetpack_Sync::sync_options( __FILE__, $option, ... ); instead
@@ -397,7 +427,9 @@ class Jetpack {
397
  * This improves the resolution of gravatars and wordpress.com uploads on hi-res and zoomed browsers.
398
  */
399
  function devicepx() {
400
- wp_enqueue_script( 'devicepx', ( is_ssl() ? 'https' : 'http' ) . '://s0.wp.com/wp-content/js/devicepx-jetpack.js', array(), gmdate( 'oW' ), true );
 
 
401
  }
402
 
403
  /*
@@ -659,6 +691,8 @@ class Jetpack {
659
  'zoltonorg-social-plugin/zosp.php', // Zolton.org Social Plugin
660
  'wp-caregiver/wp-caregiver.php', // WP Caregiver
661
  'facebook-revised-open-graph-meta-tag/index.php', // Facebook Revised Open Graph Meta Tag
 
 
662
  );
663
 
664
  foreach ( $conflicting_plugins as $plugin ) {
@@ -1457,9 +1491,13 @@ p {
1457
  * @static
1458