summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYury German <blueknight@gentoo.org>2019-05-22 01:01:36 -0400
committerYury German <blueknight@gentoo.org>2019-05-22 01:01:36 -0400
commit0914c92da22824025992c368c745546e41fbeb84 (patch)
tree965f6adf3b725e56d559fe4a93eff02281499dcc /plugins/jetpack/json-endpoints/jetpack
parentDeleting plugins for update (diff)
downloadblogs-gentoo-0914c92da22824025992c368c745546e41fbeb84.tar.gz
blogs-gentoo-0914c92da22824025992c368c745546e41fbeb84.tar.bz2
blogs-gentoo-0914c92da22824025992c368c745546e41fbeb84.zip
Adding Plugins
Updating the following akismet.4.1.2, google-authenticator.0.52, jetpack.7.3.1 Signed-off-by: Yury German <blueknight@gentoo.org>
Diffstat (limited to 'plugins/jetpack/json-endpoints/jetpack')
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-check-capabilities-endpoint.php26
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-core-endpoint.php20
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-core-modify-endpoint.php75
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-cron-endpoint.php252
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-endpoint.php128
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-comment-backup-endpoint.php52
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-database-object-backup-endpoint.php97
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-option-backup-endpoint.php35
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-post-backup-endpoint.php31
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-term-backup-endpoint.php32
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-user-backup-endpoint.php32
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-jps-woocommerce-connect-endpoint.php58
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-log-endpoint.php16
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-maybe-auto-update-endpoint.php32
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-endpoint.php125
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-get-endpoint.php6
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-list-endpoint.php13
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-modify-endpoint.php62
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-delete-endpoint.php78
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-endpoint.php321
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-get-endpoint.php28
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-install-endpoint.php96
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-list-endpoint.php36
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-modify-endpoint.php420
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-modify-v1-2-endpoint.php191
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-new-endpoint.php136
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-sync-endpoint.php322
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-active-endpoint.php50
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-delete-endpoint.php60
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-endpoint.php178
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-get-endpoint.php6
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-install-endpoint.php173
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-list-endpoint.php13
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-modify-endpoint.php130
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-new-endpoint.php83
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-translations-endpoint.php20
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-translations-modify-endpoint.php29
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-updates-status-endpoint.php34
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-connect-endpoint.php30
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-create-endpoint.php72
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-get-option-endpoint.php41
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-update-option-endpoint.php31
-rw-r--r--plugins/jetpack/json-endpoints/jetpack/json-api-jetpack-endpoints.php1234
43 files changed, 4904 insertions, 0 deletions
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-check-capabilities-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-check-capabilities-endpoint.php
new file mode 100644
index 00000000..c86cddec
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-check-capabilities-endpoint.php
@@ -0,0 +1,26 @@
+<?php
+
+class Jetpack_JSON_API_Check_Capabilities_Endpoint extends Jetpack_JSON_API_Modules_Endpoint {
+ // GET /sites/%s/me/capability
+ // The unused $object parameter is for making the method signature compatible with its parent class method.
+ public function callback( $path = '', $_blog_id = 0, $object = null ) {
+ // Check minimum capability and blog membership first
+ if ( is_wp_error( $error = $this->validate_call( $_blog_id, 'read', false ) ) ) {
+ return $error;
+ }
+
+ $args = $this->input();
+
+ if ( ! isset( $args['capability'] ) || empty( $args['capability'] ) ) {
+ return new WP_Error( 'missing_capability', __( 'You are required to specify a capability to check.', 'jetpack' ), 400 );
+ }
+
+ $capability = $args['capability'];
+ if ( is_array( $capability ) ) {
+ $results = array_map( 'current_user_can', $capability );
+ return array_combine( $capability, $results );
+ } else {
+ return current_user_can( $capability );
+ }
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-core-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-core-endpoint.php
new file mode 100644
index 00000000..f63a6cd7
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-core-endpoint.php
@@ -0,0 +1,20 @@
+<?php
+
+class Jetpack_JSON_API_Core_Endpoint extends Jetpack_JSON_API_Endpoint {
+ // POST /sites/%s/core
+ // POST /sites/%s/core/update
+ protected $needed_capabilities = 'manage_options';
+ protected $new_version;
+ protected $log;
+
+ public function result() {
+ global $wp_version;
+
+ return array(
+ 'version' => ( empty( $this->new_version ) ) ? $wp_version : $this->new_version,
+ 'autoupdate' => Jetpack_Options::get_option( 'autoupdate_core', false ),
+ 'log' => $this->log,
+ );
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-core-modify-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-core-modify-endpoint.php
new file mode 100644
index 00000000..8f707ad4
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-core-modify-endpoint.php
@@ -0,0 +1,75 @@
+<?php
+
+class Jetpack_JSON_API_Core_Modify_Endpoint extends Jetpack_JSON_API_Core_Endpoint {
+ // POST /sites/%s/core
+ // POST /sites/%s/core/update
+ protected $needed_capabilities = 'update_core';
+ protected $action = 'default_action';
+ protected $new_version;
+ protected $log;
+
+ public function default_action() {
+ $args = $this->input();
+
+ if ( isset( $args['autoupdate'] ) && is_bool( $args['autoupdate'] ) ) {
+ Jetpack_Options::update_option( 'autoupdate_core', $args['autoupdate'] );
+ }
+
+ return true;
+ }
+
+ protected function update() {
+ $args = $this->input();
+ $version = isset( $args['version'] ) ? $args['version'] : false;
+ $locale = isset( $args['locale'] ) ? $args['locale'] : get_locale();
+
+ include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+
+ delete_site_transient( 'update_core' );
+ wp_version_check( array(), true );
+
+ if ( $version ) {
+ $update = find_core_update( $version, $locale );
+ } else {
+ $update = $this->find_latest_update_offer();
+ }
+
+ /**
+ * Pre-upgrade action
+ *
+ * @since 3.9.3
+ *
+ * @param object|array $update as returned by find_core_update() or find_core_auto_update()
+ */
+ do_action('jetpack_pre_core_upgrade', $update);
+
+ $skin = new Automatic_Upgrader_Skin();
+ $upgrader = new Core_Upgrader( $skin );
+
+ $this->new_version = $upgrader->upgrade( $update );
+
+ $this->log = $upgrader->skin->get_upgrade_messages();
+
+ if ( is_wp_error( $this->new_version ) ) {
+ return $this->new_version;
+ }
+
+ return $this->new_version;
+ }
+
+ protected function find_latest_update_offer() {
+ // Select the latest update.
+ // Remove filters to bypass automattic updates.
+ add_filter( 'request_filesystem_credentials', '__return_true' );
+ add_filter( 'automatic_updates_is_vcs_checkout', '__return_false' );
+ add_filter( 'allow_major_auto_core_updates', '__return_true' );
+ add_filter( 'send_core_update_notification_email', '__return_false' );
+ $update = find_core_auto_update();
+ remove_filter( 'request_filesystem_credentials', '__return_true' );
+ remove_filter( 'automatic_updates_is_vcs_checkout', '__return_false' );
+ remove_filter( 'allow_major_auto_core_updates', '__return_true' );
+ remove_filter( 'send_core_update_notification_email', '__return_false' );
+ return $update;
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-cron-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-cron-endpoint.php
new file mode 100644
index 00000000..9638c3eb
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-cron-endpoint.php
@@ -0,0 +1,252 @@
+<?php
+
+// GET /sites/%s/cron
+class Jetpack_JSON_API_Cron_Endpoint extends Jetpack_JSON_API_Endpoint {
+ protected $needed_capabilities = 'manage_options';
+
+ protected function validate_call( $_blog_id, $capability, $check_manage_active = true ) {
+ return parent::validate_call( $_blog_id, $capability, false );
+ }
+
+ protected function result() {
+ return array(
+ 'cron_array' => _get_cron_array(),
+ 'current_timestamp' => time()
+ );
+ }
+
+ protected function sanitize_hook( $hook ) {
+ return preg_replace( '/[^A-Za-z0-9-_]/', '', $hook );
+ }
+
+ protected function resolve_arguments() {
+ $args = $this->input();
+ return isset( $args['arguments'] ) ? json_decode( $args['arguments'] ) : array();
+ }
+
+ protected function is_cron_locked( $gmt_time ) {
+ // The cron lock: a unix timestamp from when the cron was spawned.
+ $doing_cron_transient = $this->get_cron_lock();
+ if ( $doing_cron_transient && ( $doing_cron_transient + WP_CRON_LOCK_TIMEOUT > $gmt_time ) ) {
+ return new WP_Error( 'cron-is-locked', 'Current there is a cron already happening.', 403 );
+ }
+ return $doing_cron_transient;
+ }
+
+ protected function maybe_unlock_cron( $doing_wp_cron ) {
+ if ( $this->get_cron_lock() == $doing_wp_cron ) {
+ delete_transient( 'doing_cron' );
+ }
+ }
+
+ protected function lock_cron() {
+ $lock = sprintf( '%.22F', microtime( true ) );
+ set_transient( 'doing_cron', $lock );
+ return $lock;
+ }
+
+ protected function get_schedules( $hook, $args ) {
+ $crons = _get_cron_array();
+ $key = md5(serialize($args));
+ if ( empty( $crons ) )
+ return array();
+ $found = array();
+ foreach ( $crons as $timestamp => $cron ) {
+ if ( isset( $cron[$hook][$key] ) )
+ $found[] = $timestamp;
+ }
+
+ return $found;
+ }
+
+ /**
+ * This function is based on the one found in wp-cron.php with a similar name
+ * @return int
+ */
+ protected function get_cron_lock() {
+ global $wpdb;
+
+ $value = 0;
+ if ( wp_using_ext_object_cache() ) {
+ /*
+ * Skip local cache and force re-fetch of doing_cron transient
+ * in case another process updated the cache.
+ */
+ $value = wp_cache_get( 'doing_cron', 'transient', true );
+ } else {
+ $row = $wpdb->get_row( $wpdb->prepare( "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", '_transient_doing_cron' ) );
+ if ( is_object( $row ) ) {
+ $value = $row->option_value;
+ }
+ }
+ return $value;
+ }
+}
+
+// POST /sites/%s/cron
+class Jetpack_JSON_API_Cron_Post_Endpoint extends Jetpack_JSON_API_Cron_Endpoint {
+
+ protected function result() {
+ define( 'DOING_CRON', true );
+ set_time_limit( 0 );
+ $args = $this->input();
+
+ if ( false === $crons = _get_cron_array() ) {
+ return new WP_Error( 'no-cron-event', 'Currently there are no cron events', 400 );
+ }
+
+ $timestamps_to_run = array_keys( $crons );
+ $gmt_time = microtime( true );
+
+ if ( isset( $timestamps_to_run[0] ) && $timestamps_to_run[0] > $gmt_time ) {
+ return new WP_Error( 'no-cron-event', 'Currently there are no cron events ready to be run', 400 );
+ }
+
+ $locked = $this->is_cron_locked( $gmt_time );
+ if ( is_wp_error( $locked ) ) {
+ return $locked;
+ }
+
+ $lock = $this->lock_cron();
+ $processed_events = array();
+
+ foreach ( $crons as $timestamp => $cronhooks ) {
+ if ( $timestamp > $gmt_time && ! isset( $args[ 'hook' ] ) ) {
+ break;
+ }
+
+ foreach ( $cronhooks as $hook => $hook_data ) {
+ if ( isset( $args[ 'hook' ] ) && ! in_array( $hook, $args['hook'] ) ) {
+ continue;
+ }
+
+ foreach ( $hook_data as $hash => $hook_item ) {
+
+ $schedule = $hook_item['schedule'];
+ $arguments = $hook_item['args'];
+
+ if ( $schedule != false ) {
+ wp_reschedule_event( $timestamp, $schedule, $hook, $arguments );
+ }
+
+ wp_unschedule_event( $timestamp, $hook, $arguments );
+
+ do_action_ref_array( $hook, $arguments );
+ $processed_events[] = array( $hook => $arguments );
+
+ // If the hook ran too long and another cron process stole the lock,
+ // or if we things are taking longer then 20 seconds then quit.
+ if ( ( $this->get_cron_lock() != $lock ) || ( $gmt_time + 20 > microtime( true ) ) ) {
+ $this->maybe_unlock_cron( $lock );
+ return array( 'success' => $processed_events );
+ }
+
+ }
+ }
+ }
+
+ $this->maybe_unlock_cron( $lock );
+ return array( 'success' => $processed_events );
+ }
+}
+
+// POST /sites/%s/cron/schedule
+class Jetpack_JSON_API_Cron_Schedule_Endpoint extends Jetpack_JSON_API_Cron_Endpoint {
+
+ protected function result() {
+ $args = $this->input();
+ if ( ! isset( $args['timestamp'] ) ) {
+ return new WP_Error( 'missing_argument', 'Please provide the timestamp argument', 400 );
+ }
+
+ if ( ! is_int( $args['timestamp'] ) || $args['timestamp'] < time() ) {
+ return new WP_Error( 'timestamp-invalid', 'Please provide timestamp that is an integer and set in the future', 400 );
+ }
+
+ if ( ! isset( $args['hook'] ) ) {
+ return new WP_Error( 'missing_argument', 'Please provide the hook argument', 400 );
+ }
+
+ $hook = $this->sanitize_hook( $args['hook'] );
+
+ $locked = $this->is_cron_locked( microtime( true ) );
+ if ( is_wp_error( $locked ) ) {
+ return $locked;
+ }
+
+ $arguments = $this->resolve_arguments();
+ $next_scheduled = $this->get_schedules( $hook, $arguments );
+
+ if ( isset( $args['recurrence'] ) ) {
+ $schedules = wp_get_schedules();
+ if ( ! isset( $schedules[ $args['recurrence'] ] ) ) {
+ return new WP_Error( 'invalid-recurrence', 'Please provide a valid recurrence argument', 400 );
+ }
+
+ if ( count( $next_scheduled ) > 0 ) {
+ return new WP_Error( 'event-already-scheduled', 'This event is ready scheduled', 400 );
+ }
+ $lock = $this->lock_cron();
+ wp_schedule_event( $args['timestamp'], $args['recurrence'], $hook, $arguments );
+ $this->maybe_unlock_cron( $lock );
+ return array( 'success' => true );
+ }
+
+ foreach( $next_scheduled as $scheduled_time ) {
+ if ( abs( $scheduled_time - $args['timestamp'] ) <= 10 * MINUTE_IN_SECONDS ) {
+ return new WP_Error( 'event-already-scheduled', 'This event is ready scheduled', 400 );
+ }
+ }
+ $lock = $this->lock_cron();
+ $next = wp_schedule_single_event( $args['timestamp'], $hook, $arguments );
+ $this->maybe_unlock_cron( $lock );
+ /**
+ * Note: Before WP 5.1, the return value was either `false` or `null`.
+ * With 5.1 and later, the return value is now `false` or `true`.
+ * We need to account for both.
+ */
+ return array( 'success' => false !== $next );
+ }
+}
+
+// POST /sites/%s/cron/unschedule
+class Jetpack_JSON_API_Cron_Unschedule_Endpoint extends Jetpack_JSON_API_Cron_Endpoint {
+
+ protected function result() {
+ $args = $this->input();
+
+ if ( !isset( $args['hook'] ) ) {
+ return new WP_Error( 'missing_argument', 'Please provide the hook argument', 400 );
+ }
+
+ $hook = $this->sanitize_hook( $args['hook'] );
+
+ $locked = $this->is_cron_locked( microtime( true ) );
+ if ( is_wp_error( $locked ) ) {
+ return $locked;
+ }
+
+ $crons = _get_cron_array();
+ if ( empty( $crons ) ) {
+ return new WP_Error( 'cron-not-present', 'Unable to unschedule an event, no events in the cron', 400 );
+ }
+
+ $arguments = $this->resolve_arguments();
+
+ if ( isset( $args['timestamp'] ) ) {
+ $next_schedulded = $this->get_schedules( $hook, $arguments );
+ if ( in_array( $args['timestamp'], $next_schedulded ) ) {
+ return new WP_Error( 'event-not-present', 'Unable to unschedule the event, the event doesn\'t exist', 400 );
+ }
+
+ $lock = $this->lock_cron();
+ wp_unschedule_event( $args['timestamp'], $hook, $arguments );
+ $this->maybe_unlock_cron( $lock );
+ return array( 'success' => true );
+ }
+ $lock = $this->lock_cron();
+ wp_clear_scheduled_hook( $hook, $arguments );
+ $this->maybe_unlock_cron( $lock );
+ return array( 'success' => true );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-endpoint.php
new file mode 100644
index 00000000..8a349015
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-endpoint.php
@@ -0,0 +1,128 @@
+<?php
+
+include JETPACK__PLUGIN_DIR . '/modules/module-info.php';
+
+/**
+ * Base class for Jetpack Endpoints, has the validate_call helper function.
+ */
+abstract class Jetpack_JSON_API_Endpoint extends WPCOM_JSON_API_Endpoint {
+
+ protected $needed_capabilities;
+ protected $expected_actions = array();
+ protected $action;
+
+
+ public function callback( $path = '', $blog_id = 0, $object = null ) {
+ if ( is_wp_error( $error = $this->validate_call( $blog_id, $this->needed_capabilities ) ) ) {
+ return $error;
+ }
+
+ if ( is_wp_error( $error = $this->validate_input( $object ) ) ) {
+ return $error;
+ }
+
+ if ( ! empty( $this->action ) ) {
+ if( is_wp_error( $error = call_user_func( array( $this, $this->action ) ) ) ) {
+ return $error;
+ }
+ }
+
+ return $this->result();
+ }
+
+ abstract protected function result();
+
+ protected function validate_input( $object ) {
+ $args = $this->input();
+
+ if( isset( $args['action'] ) && $args['action'] == 'update' ) {
+ $this->action = 'update';
+ }
+
+ if ( preg_match( "/\/update\/?$/", $this->path ) ) {
+ $this->action = 'update';
+
+ } elseif( preg_match( "/\/install\/?$/", $this->path ) ) {
+ $this->action = 'install';
+
+ } elseif( ! empty( $args['action'] ) ) {
+ if( ! in_array( $args['action'], $this->expected_actions ) ) {
+ return new WP_Error( 'invalid_action', __( 'You must specify a valid action', 'jetpack' ) );
+ }
+ $this->action = $args['action'];
+ }
+ return true;
+ }
+
+ /**
+ * Switches to the blog and checks current user capabilities.
+ * @return bool|WP_Error a WP_Error object or true if things are good.
+ */
+ protected function validate_call( $_blog_id, $capability, $check_validation = true ) {
+ $blog_id = $this->api->switch_to_blog_and_validate_user( $this->api->get_blog_id( $_blog_id ) );
+ if ( is_wp_error( $blog_id ) ) {
+ return $blog_id;
+ }
+
+ if ( is_wp_error( $error = $this->check_capability( $capability ) ) ) {
+ return $error;
+ }
+
+ if (
+ $check_validation &&
+ 'GET' !== $this->method &&
+ /**
+ * Filter to disallow JSON API requests to the site.
+ * Setting to false disallows you to manage your site remotely from WordPress.com
+ * and disallows plugin auto-updates.
+ *
+ * @since 7.3.0
+ *
+ * @param bool $check_validation Whether to allow API requests to manage the site
+ */
+ ! apply_filters( 'jetpack_json_manage_api_enabled', $check_validation )
+ ) {
+ return new WP_Error( 'unauthorized_full_access', __( 'Full management mode is off for this site.', 'jetpack' ), 403 );
+ }
+
+ return true;
+ }
+
+ /**
+ * @param $capability
+ *
+ * @return bool|WP_Error
+ */
+ protected function check_capability( $capability ) {
+ if ( is_array( $capability ) ) {
+ // the idea is that the we can pass in an array of capabilitie that the user needs to have before we allowing them to do something
+ $capabilities = ( isset( $capability['capabilities'] ) ? $capability['capabilities'] : $capability );
+
+ // We can pass in the number of conditions we must pass by default it is all.
+ $must_pass = ( isset( $capability['must_pass'] ) && is_int( $capability['must_pass'] ) ? $capability['must_pass'] : count( $capabilities ) );
+
+ $failed = array(); // store the failed capabilities
+ $passed = 0; //
+
+ foreach ( $capabilities as $cap ) {
+ if ( current_user_can( $cap ) ) {
+ $passed ++;
+ } else {
+ $failed[] = $cap;
+ }
+ }
+ // Check that must have conditions is less then
+ if ( $passed < $must_pass ) {
+ return new WP_Error( 'unauthorized', sprintf( __( 'This user is not authorized to %s on this blog.', 'jetpack' ), implode( ', ', $failed ), 403 ) );
+ }
+
+ } else {
+ if ( !current_user_can( $capability ) ) {
+ return new WP_Error( 'unauthorized', sprintf( __( 'This user is not authorized to %s on this blog.', 'jetpack' ), $capability ), 403 );
+ }
+ }
+
+ return true;
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-comment-backup-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-comment-backup-endpoint.php
new file mode 100644
index 00000000..0e4bc256
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-comment-backup-endpoint.php
@@ -0,0 +1,52 @@
+<?php
+
+class Jetpack_JSON_API_Get_Comment_Backup_Endpoint extends Jetpack_JSON_API_Endpoint {
+ // /sites/%s/comments/%d/backup -> $blog_id, $comment_id
+
+ protected $needed_capabilities = array(); // This endpoint is only accessible using a site token
+ protected $comment_id;
+
+ function validate_input( $comment_id ) {
+ if ( empty( $comment_id ) || ! is_numeric( $comment_id ) ) {
+ return new WP_Error( 'comment_id_not_specified', __( 'You must specify a Comment ID', 'jetpack' ), 400 );
+ }
+
+ $this->comment_id = intval( $comment_id );
+
+ return true;
+ }
+
+ protected function result() {
+ $comment = get_comment( $this->comment_id );
+ if ( empty( $comment ) ) {
+ return new WP_Error( 'comment_not_found', __( 'Comment not found', 'jetpack' ), 404 );
+ }
+
+ $allowed_keys = array(
+ 'comment_ID',
+ 'comment_post_ID',
+ 'comment_author',
+ 'comment_author_email',
+ 'comment_author_url',
+ 'comment_author_IP',
+ 'comment_date',
+ 'comment_date_gmt',
+ 'comment_content',
+ 'comment_karma',
+ 'comment_approved',
+ 'comment_agent',
+ 'comment_type',
+ 'comment_parent',
+ 'user_id',
+ );
+
+ $comment = array_intersect_key( $comment->to_array(), array_flip( $allowed_keys ) );
+ $comment_meta = get_comment_meta( $comment['comment_ID'] );
+
+ return array(
+ 'comment' => $comment,
+ 'meta' => is_array( $comment_meta ) ? $comment_meta : array(),
+ );
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-database-object-backup-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-database-object-backup-endpoint.php
new file mode 100644
index 00000000..b7134730
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-database-object-backup-endpoint.php
@@ -0,0 +1,97 @@
+<?php
+
+class Jetpack_JSON_API_Get_Database_Object_Backup_Endpoint extends Jetpack_JSON_API_Endpoint {
+ // /sites/%s/database-object/backup -> $blog_id
+
+ protected $needed_capabilities = array(); // This endpoint is only accessible using a site token
+ protected $object_type;
+ protected $object_id;
+
+ // Full list of database objects that can be retrieved via this endpoint.
+ protected $object_types = array(
+ 'woocommerce_attribute' => array(
+ 'table' => 'woocommerce_attribute_taxonomies',
+ 'id_field' => 'attribute_id',
+ ),
+
+ 'woocommerce_downloadable_product_permission' => array(
+ 'table' => 'woocommerce_downloadable_product_permissions',
+ 'id_field' => 'permission_id',
+ ),
+
+ 'woocommerce_order_item' => array(
+ 'table' => 'woocommerce_order_items',
+ 'id_field' => 'order_item_id',
+ 'meta_type' => 'order_item',
+ ),
+
+ 'woocommerce_payment_token' => array(
+ 'table' => 'woocommerce_payment_tokens',
+ 'id_field' => 'token_id',
+ 'meta_type' => 'payment_token',
+ ),
+
+ 'woocommerce_tax_rate' => array(
+ 'table' => 'woocommerce_tax_rates',
+ 'id_field' => 'tax_rate_id',
+ 'child_table' => 'woocommerce_tax_rate_locations',
+ 'child_id_field' => 'tax_rate_id',
+ ),
+
+ 'woocommerce_webhook' => array(
+ 'table' => 'wc_webhooks',
+ 'id_field' => 'webhook_id',
+ ),
+ );
+
+ function validate_input( $object ) {
+ $query_args = $this->query_args();
+
+ if ( empty( $query_args['object_type'] ) || empty( $query_args['object_id'] ) ) {
+ return new WP_Error( 'invalid_args', __( 'You must specify both an object type and id to fetch', 'jetpack' ), 400 );
+ }
+
+ if ( empty( $this->object_types[ $query_args['object_type'] ] ) ) {
+ return new WP_Error( 'invalid_args', __( 'Specified object_type not recognized', 'jetpack' ), 400 );
+ }
+
+ $this->object_type = $this->object_types[ $query_args['object_type'] ];
+ $this->object_id = $query_args['object_id'];
+
+ return true;
+ }
+
+ protected function result() {
+ global $wpdb;
+
+ $table = $wpdb->prefix . $this->object_type['table'];
+ $id_field = $this->object_type['id_field'];
+
+ // Fetch the requested object
+ $query = $wpdb->prepare( 'select * from `' . $table . '` where `' . $id_field . '` = %d', $this->object_id );
+ $object = $wpdb->get_row( $query );
+
+ if ( empty( $object ) ) {
+ return new WP_Error( 'object_not_found', __( 'Object not found', 'jetpack' ), 404 );
+ }
+
+ $result = array( 'object' => $object );
+
+ // Fetch associated metadata (if this object type has any)
+ if ( ! empty( $this->object_type['meta_type'] ) ) {
+ $result['meta'] = get_metadata( $this->object_type['meta_type'], $this->object_id );
+ }
+
+ // If there is a child linked table (eg: woocommerce_tax_rate_locations), fetch linked records
+ if ( ! empty( $this->object_type['child_table'] ) ) {
+ $child_table = $wpdb->prefix . $this->object_type['child_table'];
+ $child_id_field = $this->object_type['child_id_field'];
+
+ $query = $wpdb->prepare( 'select * from `' . $child_table . '` where `' . $child_id_field . '` = %d', $this->object_id );
+ $result[ 'children' ] = $wpdb->get_results( $query );
+ }
+
+ return $result;
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-option-backup-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-option-backup-endpoint.php
new file mode 100644
index 00000000..a5d8d3a7
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-option-backup-endpoint.php
@@ -0,0 +1,35 @@
+<?php
+
+class Jetpack_JSON_API_Get_Option_Backup_Endpoint extends Jetpack_JSON_API_Endpoint {
+ // /sites/%s/options/backup -> $blog_id
+
+ protected $needed_capabilities = array(); // This endpoint is only accessible using a site token
+ protected $option_names;
+
+ function validate_input( $object ) {
+ $query_args = $this->query_args();
+
+ if ( empty( $query_args['name'] ) ) {
+ return new WP_Error( 'option_name_not_specified', __( 'You must specify an option name', 'jetpack' ), 400 );
+ }
+
+ if ( is_array( $query_args['name'] ) ) {
+ $this->option_names = $query_args['name'];
+ } else {
+ $this->option_names = array( $query_args['name'] );
+ }
+
+ return true;
+ }
+
+ protected function result() {
+ $options = array_map( array( $this, 'get_option_row' ), $this->option_names );
+ return array( 'options' => $options );
+ }
+
+ private function get_option_row( $name ) {
+ global $wpdb;
+ return $wpdb->get_row( $wpdb->prepare( "select * from `{$wpdb->options}` where option_name = %s", $name ) );
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-post-backup-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-post-backup-endpoint.php
new file mode 100644
index 00000000..903a16ac
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-post-backup-endpoint.php
@@ -0,0 +1,31 @@
+<?php
+
+class Jetpack_JSON_API_Get_Post_Backup_Endpoint extends Jetpack_JSON_API_Endpoint {
+ // /sites/%s/posts/%d/backup -> $blog_id, $post_id
+
+ protected $needed_capabilities = array(); // This endpoint is only accessible using a site token
+ protected $post_id;
+
+ function validate_input( $post_id ) {
+ if ( empty( $post_id ) || ! is_numeric( $post_id ) ) {
+ return new WP_Error( 'post_id_not_specified', __( 'You must specify a Post ID', 'jetpack' ), 400 );
+ }
+
+ $this->post_id = intval( $post_id );
+
+ return true;
+ }
+
+ protected function result() {
+ $post = get_post( $this->post_id );
+ if ( empty( $post ) ) {
+ return new WP_Error( 'post_not_found', __( 'Post not found', 'jetpack' ), 404 );
+ }
+
+ return array(
+ 'post' => (array)$post,
+ 'meta' => get_post_meta( $post->ID ),
+ );
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-term-backup-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-term-backup-endpoint.php
new file mode 100644
index 00000000..40d0ab97
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-term-backup-endpoint.php
@@ -0,0 +1,32 @@
+<?php
+
+class Jetpack_JSON_API_Get_Term_Backup_Endpoint extends Jetpack_JSON_API_Endpoint {
+ // /sites/%s/terms/%d/backup -> $blog_id, $term_id
+
+ protected $needed_capabilities = array(); // This endpoint is only accessible using a site token
+ protected $term_id;
+
+ function validate_input( $term_id ) {
+ if ( empty( $term_id ) || ! is_numeric( $term_id ) ) {
+ return new WP_Error( 'term_id_not_specified', __( 'You must specify a Term ID', 'jetpack' ), 400 );
+ }
+
+ $this->term_id = intval( $term_id );
+
+ return true;
+ }
+
+ protected function result() {
+ $term = get_term( $this->term_id );
+ if ( empty( $term ) ) {
+ return new WP_Error( 'term_not_found', __( 'Term not found', 'jetpack' ), 404 );
+ }
+
+ return array(
+ 'term' => (array) $term,
+ 'meta' => get_term_meta( $this->term_id ),
+ );
+ }
+
+}
+
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-user-backup-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-user-backup-endpoint.php
new file mode 100644
index 00000000..22ca195d
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-get-user-backup-endpoint.php
@@ -0,0 +1,32 @@
+<?php
+
+class Jetpack_JSON_API_Get_User_Backup_Endpoint extends Jetpack_JSON_API_Endpoint {
+ // /sites/%s/users/%d/backup -> $blog_id, $user_id
+
+ protected $needed_capabilities = array(); // This endpoint is only accessible using a site token
+ protected $user_id;
+
+ function validate_input( $user_id ) {
+ if ( empty( $user_id ) || ! is_numeric( $user_id ) ) {
+ return new WP_Error( 'user_id_not_specified', __( 'You must specify a User ID', 'jetpack' ), 400 );
+ }
+
+ $this->user_id = intval( $user_id );
+
+ return true;
+ }
+
+ protected function result() {
+ $user = get_user_by( 'id', $this->user_id );
+ if ( empty( $user ) ) {
+ return new WP_Error( 'user_not_found', __( 'User not found', 'jetpack' ), 404 );
+ }
+
+ return array(
+ 'user' => $user->to_array(),
+ 'meta' => get_user_meta( $user->ID ),
+ );
+ }
+
+}
+
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-jps-woocommerce-connect-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-jps-woocommerce-connect-endpoint.php
new file mode 100644
index 00000000..75a3b04d
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-jps-woocommerce-connect-endpoint.php
@@ -0,0 +1,58 @@
+<?php
+
+class Jetpack_JSON_API_JPS_WooCommerce_Connect_Endpoint extends Jetpack_JSON_API_Endpoint {
+
+ protected $needed_capabilities = 'manage_options';
+
+ function result() {
+ $input = $this->input();
+ $helper_data = get_option( 'woocommerce_helper_data', array() );
+
+ if ( ! empty( $helper_data['auth'] ) ) {
+ return new WP_Error(
+ 'already_configured',
+ __( 'WooCommerce auth data is already set.', 'jetpack' )
+ );
+ }
+
+ // Only update the auth field for `woocommerce_helper_data` instead of blowing out the entire option.
+ $helper_data['auth'] = array(
+ 'user_id' => $input['user_id'],
+ 'site_id' => $input['site_id'],
+ 'updated' => time(),
+ 'access_token' => $input['access_token'],
+ 'access_token_secret' => $input['access_token_secret'],
+ );
+
+ $updated = update_option(
+ 'woocommerce_helper_data',
+ $helper_data
+ );
+
+ return array(
+ 'success' => $updated,
+ );
+ }
+
+ function validate_input( $object ) {
+ $input = $this->input();
+
+ if ( empty( $input['access_token'] ) ) {
+ return new WP_Error( 'input_error', __( 'access_token is required', 'jetpack' ) );
+ }
+
+ if ( empty( $input['access_token_secret'] ) ) {
+ return new WP_Error( 'input_error', __( 'access_token_secret is required', 'jetpack' ) );
+ }
+
+ if ( empty( $input['user_id'] ) ) {
+ return new WP_Error( 'input_error', __( 'user_id is required', 'jetpack' ) );
+ }
+
+ if ( empty( $input['site_id'] ) ) {
+ return new WP_Error( 'input_error', __( 'site_id is required', 'jetpack' ) );
+ }
+
+ return parent::validate_input( $object );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-log-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-log-endpoint.php
new file mode 100644
index 00000000..f1d0f4da
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-log-endpoint.php
@@ -0,0 +1,16 @@
+<?php
+
+class Jetpack_JSON_API_Jetpack_Log_Endpoint extends Jetpack_JSON_API_Endpoint {
+ // GET /sites/%s/jetpack-log
+ protected $needed_capabilities = 'manage_options';
+
+ protected function result() {
+ $args = $this->input();
+ $event = ( isset( $args['event'] ) && is_string( $args['event'] ) ) ? $code : false;
+ $num = ( isset( $args['num'] ) ) ? intval( $num ) : false;
+
+ return array(
+ 'log' => Jetpack::get_log( $event, $num )
+ );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-maybe-auto-update-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-maybe-auto-update-endpoint.php
new file mode 100644
index 00000000..5b368760
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-maybe-auto-update-endpoint.php
@@ -0,0 +1,32 @@
+<?php
+
+class Jetpack_JSON_API_Maybe_Auto_Update_Endpoint extends Jetpack_JSON_API_Endpoint {
+ // POST /sites/%s/maybe_auto_update
+ protected $needed_capabilities = array( 'update_core', 'update_plugins', 'update_themes' );
+
+ protected $update_results = array();
+
+ protected function result() {
+ add_action( 'automatic_updates_complete', array( $this, 'get_update_results' ), 100, 1 );
+
+ wp_maybe_auto_update();
+
+ $result['log'] = $this->update_results;
+
+ if ( empty( $result['log'] ) ) {
+ $possible_reasons_for_failure = Jetpack_Autoupdate::get_possible_failures();
+
+ if ( $possible_reasons_for_failure ) {
+ $result['log']['error'] = $possible_reasons_for_failure;
+ }
+
+ }
+
+ return $result;
+ }
+
+ public function get_update_results( $results ) {
+ $this->update_results = $results;
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-endpoint.php
new file mode 100644
index 00000000..2f56f1ee
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-endpoint.php
@@ -0,0 +1,125 @@
+<?php
+
+/**
+ * Base class for working with Jetpack Modules.
+ */
+abstract class Jetpack_JSON_API_Modules_Endpoint extends Jetpack_JSON_API_Endpoint {
+
+ protected $modules = array();
+
+ protected $bulk = true;
+
+ static $_response_format = array(
+ 'id' => '(string) The module\'s ID',
+ 'active' => '(boolean) The module\'s status.',
+ 'name' => '(string) The module\'s name.',
+ 'description' => '(safehtml) The module\'s description.',
+ 'sort' => '(int) The module\'s display order.',
+ 'introduced' => '(string) The Jetpack version when the module was introduced.',
+ 'changed' => '(string) The Jetpack version when the module was changed.',
+ 'free' => '(boolean) The module\'s Free or Paid status.',
+ 'module_tags' => '(array) The module\'s tags.',
+ 'override' => '(string) The module\'s override. Empty if no override, otherwise \'active\' or \'inactive\'',
+ );
+
+ protected function result() {
+
+ $modules = $this->get_modules();
+
+ if ( ! $this->bulk && ! empty( $modules ) ) {
+ return array_pop( $modules );
+ }
+
+ return array( 'modules' => $modules );
+
+ }
+
+ /**
+ * Walks through either the submitted modules or list of themes and creates the global array
+ * @param $theme
+ *
+ * @return bool
+ */
+ protected function validate_input( $module) {
+ $args = $this->input();
+ // lets set what modules were requested, and validate them
+ if ( ! isset( $module ) || empty( $module ) ) {
+
+ if ( ! $args['modules'] || empty( $args['modules'] ) ) {
+ return new WP_Error( 'missing_module', __( 'You are required to specify a module.', 'jetpack' ), 400 );
+ }
+ if ( is_array( $args['modules'] ) ) {
+ $this->modules = $args['modules'];
+ } else {
+ $this->modules[] = $args['modules'];
+ }
+ } else {
+ $this->modules[] = urldecode( $module );
+ $this->bulk = false;
+ }
+
+ if ( is_wp_error( $error = $this->validate_modules() ) ) {
+ return $error;
+ }
+
+ return parent::validate_input( $module );
+ }
+
+ /**
+ * Walks through submitted themes to make sure they are valid
+ * @return bool|WP_Error
+ */
+ protected function validate_modules() {
+ foreach ( $this->modules as $module ) {
+ if ( ! Jetpack::is_module( $module ) ) {
+ return new WP_Error( 'unknown_jetpack_module', sprintf( __( 'Module not found: `%s`.', 'jetpack' ), $module ), 404 );
+ }
+ }
+ return true;
+ }
+
+ protected static function format_module( $module_slug ) {
+ $module_data = Jetpack::get_module( $module_slug );
+
+ $module = array();
+ $module['id'] = $module_slug;
+ $module['active'] = Jetpack::is_module_active( $module_slug );
+ $module['name'] = $module_data['name'];
+ $module['short_description'] = $module_data['description'];
+ $module['sort'] = $module_data['sort'];
+ $module['introduced'] = $module_data['introduced'];
+ $module['changed'] = $module_data['changed'];
+ $module['free'] = $module_data['free'];
+ $module['module_tags'] = $module_data['module_tags'];
+
+ $overrides_instance = Jetpack_Modules_Overrides::instance();
+ $module['override'] = $overrides_instance->get_module_override( $module_slug );
+
+ // Fetch the HTML formatted long description
+ ob_start();
+ /** This action is documented in class.jetpack-modules-list-table.php */
+ do_action( 'jetpack_module_more_info_' . $module_slug );
+ $module['description'] = ob_get_clean();
+
+ return $module;
+ }
+
+ /**
+ * Format a list of modules for public display, using the supplied offset and limit args
+ * @uses WPCOM_JSON_API_Endpoint::query_args()
+ * @return array Public API modules objects
+ */
+ protected function get_modules() {
+ $modules = array_values( $this->modules );
+ // do offset & limit - we've already returned a 400 error if they're bad numbers
+ $args = $this->query_args();
+
+ if ( isset( $args['offset'] ) )
+ $modules = array_slice( $modules, (int) $args['offset'] );
+ if ( isset( $args['limit'] ) )
+ $modules = array_slice( $modules, 0, (int) $args['limit'] );
+
+ return array_map( array( $this, 'format_module' ), $modules );
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-get-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-get-endpoint.php
new file mode 100644
index 00000000..28a70dba
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-get-endpoint.php
@@ -0,0 +1,6 @@
+<?php
+
+class Jetpack_JSON_API_Modules_Get_Endpoint extends Jetpack_JSON_API_Modules_Endpoint {
+ // GET /sites/%s/jetpack/modules/%s
+ protected $needed_capabilities = 'jetpack_manage_modules';
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-list-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-list-endpoint.php
new file mode 100644
index 00000000..2ed4dbdd
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-list-endpoint.php
@@ -0,0 +1,13 @@
+<?php
+
+class Jetpack_JSON_API_Modules_List_Endpoint extends Jetpack_JSON_API_Modules_Endpoint {
+ // GET /sites/%s/jetpack/modules
+
+ protected $needed_capabilities = 'jetpack_manage_modules';
+
+ public function validate_input( $module ) {
+ $this->modules = Jetpack::get_available_modules();
+ return true;
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-modify-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-modify-endpoint.php
new file mode 100644
index 00000000..e1562f50
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-modules-modify-endpoint.php
@@ -0,0 +1,62 @@
+<?php
+
+class Jetpack_JSON_API_Modules_Modify_Endpoint extends Jetpack_JSON_API_Modules_Endpoint {
+ // POST /sites/%s/jetpack/modules/%s/activate
+ // POST /sites/%s/jetpack/modules/%s
+ // POST /sites/%s/jetpack/modules
+
+ protected $needed_capabilities = 'activate_plugins';
+ protected $action = 'default_action';
+
+ public function default_action() {
+ $args = $this->input();
+ if ( isset( $args['active'] ) && is_bool( $args['active'] ) ) {
+ if ( $args['active'] ) {
+ return $this->activate_module();
+ } else {
+ return $this->deactivate_module();
+ }
+ }
+
+ return true;
+ }
+
+ protected function activate_module() {
+ foreach ( $this->modules as $module ) {
+ if ( Jetpack::is_module_active( $module ) ) {
+ $error = $this->log[ $module ][] = __( 'The Jetpack Module is already activated.', 'jetpack' );
+ continue;
+ }
+ $result = Jetpack::activate_module( $module, false, false );
+ if ( false === $result || ! Jetpack::is_module_active( $module ) ) {
+ $error = $this->log[ $module ][] = __( 'There was an error while activating the module.', 'jetpack' );
+ }
+ }
+
+ if ( ! $this->bulk && isset( $error ) ) {
+ return new WP_Error( 'activation_error', $error, 400 );
+ }
+
+ return true;
+ }
+
+ protected function deactivate_module() {
+ foreach ( $this->modules as $module ) {
+ if ( ! Jetpack::is_module_active( $module ) ) {
+ $error = $this->log[ $module ][] = __( 'The Jetpack Module is already deactivated.', 'jetpack' );
+ continue;
+ }
+ $result = Jetpack::deactivate_module( $module );
+ if ( false === $result || Jetpack::is_module_active( $module ) ) {
+ $error = $this->log[ $module ][] = __( 'There was an error while deactivating the module.', 'jetpack' );
+ }
+ }
+
+ if ( ! $this->bulk && isset( $error ) ) {
+ return new WP_Error( 'deactivation_error', $error, 400 );
+ }
+
+ return true;
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-delete-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-delete-endpoint.php
new file mode 100644
index 00000000..3748d621
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-delete-endpoint.php
@@ -0,0 +1,78 @@
+<?php
+// POST /sites/%s/plugins/%s/delete
+new Jetpack_JSON_API_Plugins_Delete_Endpoint(
+ array(
+ 'description' => 'Delete/Uninstall a plugin from your jetpack blog',
+ 'group' => '__do_not_document',
+ 'stat' => 'plugins:1:delete',
+ 'min_version' => '1',
+ 'max_version' => '1.1',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/plugins/%s/delete',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$plugin' => '(int|string) The plugin slug to delete',
+ ),
+ 'response_format' => Jetpack_JSON_API_Plugins_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/plugins/akismet%2Fakismet/delete'
+ )
+);
+// v1.2
+new Jetpack_JSON_API_Plugins_Delete_Endpoint(
+ array(
+ 'description' => 'Delete/Uninstall a plugin from your jetpack blog',
+ 'group' => '__do_not_document',
+ 'stat' => 'plugins:1:delete',
+ 'min_version' => '1.2',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/plugins/%s/delete',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$plugin' => '(int|string) The plugin slug to delete',
+ ),
+ 'response_format' => Jetpack_JSON_API_Plugins_Endpoint::$_response_format_v1_2,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.2/sites/example.wordpress.org/plugins/akismet%2Fakismet/delete'
+ )
+);
+
+class Jetpack_JSON_API_Plugins_Delete_Endpoint extends Jetpack_JSON_API_Plugins_Endpoint {
+
+ // POST /sites/%s/plugins/%s/delete
+ protected $needed_capabilities = 'delete_plugins';
+ protected $action = 'delete';
+
+ protected function delete() {
+
+ foreach ( $this->plugins as $plugin ) {
+
+ if ( Jetpack::is_plugin_active( $plugin ) ) {
+ $error = $this->log[ $plugin ][] = __( 'You cannot delete a plugin while it is active on the main site.', 'jetpack' );
+ continue;
+ }
+
+ $result = delete_plugins( array( $plugin ) );
+ if ( is_wp_error( $result ) ) {
+ $error = $this->log[ $plugin ][] = $result->get_error_message();
+ } else {
+ $this->log[ $plugin ][] = 'Plugin deleted';
+ }
+ }
+
+ if ( ! $this->bulk && isset( $error ) ) {
+ return new WP_Error( 'delete_plugin_error', $error, 400 );
+ }
+
+ return true;
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-endpoint.php
new file mode 100644
index 00000000..1df4fe66
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-endpoint.php
@@ -0,0 +1,321 @@
+<?php
+
+/**
+ * Base class for working with plugins.
+ */
+abstract class Jetpack_JSON_API_Plugins_Endpoint extends Jetpack_JSON_API_Endpoint {
+
+ protected $plugins = array();
+
+ protected $network_wide = false;
+
+ protected $bulk = true;
+ protected $log;
+
+ static $_response_format = array(
+ 'id' => '(safehtml) The plugin\'s ID',
+ 'slug' => '(safehtml) The plugin\'s .org slug',
+ 'active' => '(boolean) The plugin status.',
+ 'update' => '(object) The plugin update info.',
+ 'name' => '(safehtml) The name of the plugin.',
+ 'plugin_url' => '(url) Link to the plugin\'s web site.',
+ 'version' => '(safehtml) The plugin version number.',
+ 'description' => '(safehtml) Description of what the plugin does and/or notes from the author',
+ 'author' => '(safehtml) The author\'s name',
+ 'author_url' => '(url) The authors web site address',
+ 'network' => '(boolean) Whether the plugin can only be activated network wide.',
+ 'autoupdate' => '(boolean) Whether the plugin is automatically updated',
+ 'autoupdate_translation' => '(boolean) Whether the plugin is automatically updating translations',
+ 'next_autoupdate' => '(string) Y-m-d H:i:s for next scheduled update event',
+ 'log' => '(array:safehtml) An array of update log strings.',
+ 'uninstallable' => '(boolean) Whether the plugin is unistallable.',
+ 'action_links' => '(array) An array of action links that the plugin uses.',
+ );
+
+ static $_response_format_v1_2 = array(
+ 'slug' => '(safehtml) The plugin\'s .org slug',
+ 'active' => '(boolean) The plugin status.',
+ 'update' => '(object) The plugin update info.',
+ 'name' => '(safehtml) The plugin\'s ID',
+ 'display_name' => '(safehtml) The name of the plugin.',
+ 'version' => '(safehtml) The plugin version number.',
+ 'description' => '(safehtml) Description of what the plugin does and/or notes from the author',
+ 'author' => '(safehtml) The author\'s name',
+ 'author_url' => '(url) The authors web site address',
+ 'plugin_url' => '(url) Link to the plugin\'s web site.',
+ 'network' => '(boolean) Whether the plugin can only be activated network wide.',
+ 'autoupdate' => '(boolean) Whether the plugin is automatically updated',
+ 'autoupdate_translation' => '(boolean) Whether the plugin is automatically updating translations',
+ 'uninstallable' => '(boolean) Whether the plugin is unistallable.',
+ 'action_links' => '(array) An array of action links that the plugin uses.',
+ 'log' => '(array:safehtml) An array of update log strings.',
+ );
+
+ protected function result() {
+
+ $plugins = $this->get_plugins();
+
+ if ( ! $this->bulk && ! empty( $plugins ) ) {
+ return array_pop( $plugins );
+ }
+
+ return array( 'plugins' => $plugins );
+
+ }
+
+ protected function validate_input( $plugin ) {
+
+ if ( is_wp_error( $error = parent::validate_input( $plugin ) ) ) {
+ return $error;
+ }
+
+ if ( is_wp_error( $error = $this->validate_network_wide() ) ) {
+ return $error;
+ }
+
+ $args = $this->input();
+ // find out what plugin, or plugins we are dealing with
+ // validate the requested plugins
+ if ( ! isset( $plugin ) || empty( $plugin ) ) {
+ if ( ! $args['plugins'] || empty( $args['plugins'] ) ) {
+ return new WP_Error( 'missing_plugin', __( 'You are required to specify a plugin.', 'jetpack' ), 400 );
+ }
+ if ( is_array( $args['plugins'] ) ) {
+ $this->plugins = $args['plugins'];
+ } else {
+ $this->plugins[] = $args['plugins'];
+ }
+ } else {
+ $this->bulk = false;
+ $this->plugins[] = urldecode( $plugin );
+ }
+
+ if ( is_wp_error( $error = $this->validate_plugins() ) ) {
+ return $error;
+ };
+
+ return true;
+ }
+
+ /**
+ * Walks through submitted plugins to make sure they are valid
+ * @return bool|WP_Error
+ */
+ protected function validate_plugins() {
+ if ( empty( $this->plugins ) || ! is_array( $this->plugins ) ) {
+ return new WP_Error( 'missing_plugins', __( 'No plugins found.', 'jetpack' ));
+ }
+ foreach( $this->plugins as $index => $plugin ) {
+ if ( ! preg_match( "/\.php$/", $plugin ) ) {
+ $plugin = $plugin . '.php';
+ $this->plugins[ $index ] = $plugin;
+ }
+ $valid = $this->validate_plugin( urldecode( $plugin ) ) ;
+ if ( is_wp_error( $valid ) ) {
+ return $valid;
+ }
+ }
+
+ return true;
+ }
+
+ protected function format_plugin( $plugin_file, $plugin_data ) {
+ if ( version_compare( $this->min_version, '1.2', '>=' ) ) {
+ return $this->format_plugin_v1_2( $plugin_file, $plugin_data );
+ }
+ $plugin = array();
+ $plugin['id'] = preg_replace("/(.+)\.php$/", "$1", $plugin_file );
+ $plugin['slug'] = Jetpack_Autoupdate::get_plugin_slug( $plugin_file );
+ $plugin['active'] = Jetpack::is_plugin_active( $plugin_file );
+ $plugin['name'] = $plugin_data['Name'];
+ $plugin['plugin_url'] = $plugin_data['PluginURI'];
+ $plugin['version'] = $plugin_data['Version'];
+ $plugin['description'] = $plugin_data['Description'];
+ $plugin['author'] = $plugin_data['Author'];
+ $plugin['author_url'] = $plugin_data['AuthorURI'];
+ $plugin['network'] = $plugin_data['Network'];
+ $plugin['update'] = $this->get_plugin_updates( $plugin_file );
+ $plugin['next_autoupdate'] = date( 'Y-m-d H:i:s', wp_next_scheduled( 'wp_maybe_auto_update' ) );
+ $action_link = $this->get_plugin_action_links( $plugin_file );
+ if ( ! empty( $action_link ) ) {
+ $plugin['action_links'] = $action_link;
+ }
+
+ $autoupdate = in_array( $plugin_file, Jetpack_Options::get_option( 'autoupdate_plugins', array() ) );
+ $plugin['autoupdate'] = $autoupdate;
+
+ $autoupdate_translation = in_array( $plugin_file, Jetpack_Options::get_option( 'autoupdate_plugins_translations', array() ) );
+ $plugin['autoupdate_translation'] = $autoupdate || $autoupdate_translation || Jetpack_Options::get_option( 'autoupdate_translations', false );
+
+ $plugin['uninstallable'] = is_uninstallable_plugin( $plugin_file );
+
+ if ( ! empty ( $this->log[ $plugin_file ] ) ) {
+ $plugin['log'] = $this->log[ $plugin_file ];
+ }
+ return $plugin;
+ }
+
+ protected function format_plugin_v1_2( $plugin_file, $plugin_data ) {
+ $plugin = array();
+ $plugin['slug'] = Jetpack_Autoupdate::get_plugin_slug( $plugin_file );
+ $plugin['active'] = Jetpack::is_plugin_active( $plugin_file );
+ $plugin['name'] = preg_replace("/(.+)\.php$/", "$1", $plugin_file );
+ $plugin['display_name'] = $plugin_data['Name'];
+ $plugin['plugin_url'] = $plugin_data['PluginURI'];
+ $plugin['version'] = $plugin_data['Version'];
+ $plugin['description'] = $plugin_data['Description'];
+ $plugin['author'] = $plugin_data['Author'];
+ $plugin['author_url'] = $plugin_data['AuthorURI'];
+ $plugin['network'] = $plugin_data['Network'];
+ $plugin['update'] = $this->get_plugin_updates( $plugin_file );
+ $action_link = $this->get_plugin_action_links( $plugin_file );
+ if ( ! empty( $action_link ) ) {
+ $plugin['action_links'] = $action_link;
+ }
+
+ $autoupdate = $this->plugin_has_autoupdates_enabled( $plugin_file );
+ $plugin['autoupdate'] = $autoupdate;
+
+ $autoupdate_translation = $this->plugin_has_translations_autoupdates_enabled( $plugin_file );
+ $plugin['autoupdate_translation'] = $autoupdate || $autoupdate_translation || Jetpack_Options::get_option( 'autoupdate_translations', false );
+ $plugin['uninstallable'] = is_uninstallable_plugin( $plugin_file );
+
+ if ( ! empty ( $this->log[ $plugin_file ] ) ) {
+ $plugin['log'] = $this->log[ $plugin_file ];
+ }
+
+ return $plugin;
+ }
+
+ protected function plugin_has_autoupdates_enabled( $plugin_file ) {
+ return (bool) in_array( $plugin_file, Jetpack_Options::get_option( 'autoupdate_plugins', array() ) );
+ }
+
+ protected function plugin_has_translations_autoupdates_enabled( $plugin_file ) {
+ return (bool) in_array( $plugin_file, Jetpack_Options::get_option( 'autoupdate_plugins_translations', array() ) );
+ }
+
+
+ protected function get_file_mod_capabilities() {
+ $reasons_can_not_autoupdate = array();
+ $reasons_can_not_modify_files = array();
+
+ $has_file_system_write_access = Jetpack_Sync_Functions::file_system_write_access();
+ if ( ! $has_file_system_write_access ) {
+ $reasons_can_not_modify_files['has_no_file_system_write_access'] = __( 'The file permissions on this host prevent editing files.', 'jetpack' );
+ }
+
+ $disallow_file_mods = Jetpack_Constants::get_constant('DISALLOW_FILE_MODS' );
+ if ( $disallow_file_mods ) {
+ $reasons_can_not_modify_files['disallow_file_mods'] = __( 'File modifications are explicitly disabled by a site administrator.', 'jetpack' );
+ }
+
+ $automatic_updater_disabled = Jetpack_Constants::get_constant( 'AUTOMATIC_UPDATER_DISABLED' );
+ if ( $automatic_updater_disabled ) {
+ $reasons_can_not_autoupdate['automatic_updater_disabled'] = __( 'Any autoupdates are explicitly disabled by a site administrator.', 'jetpack' );
+ }
+
+ if ( is_multisite() ) {
+ // is it the main network ? is really is multi network
+ if ( Jetpack::is_multi_network() ) {
+ $reasons_can_not_modify_files['is_multi_network'] = __( 'Multi network install are not supported.', 'jetpack' );
+ }
+ // Is the site the main site here.
+ if ( ! is_main_site() ) {
+ $reasons_can_not_modify_files['is_sub_site'] = __( 'The site is not the main network site', 'jetpack' );
+ }
+ }
+
+ $file_mod_capabilities = array(
+ 'modify_files' => (bool) empty( $reasons_can_not_modify_files ), // install, remove, update
+ 'autoupdate_files' => (bool) empty( $reasons_can_not_modify_files ) && empty( $reasons_can_not_autoupdate ), // enable autoupdates
+ );
+
+ if ( ! empty( $reasons_can_not_modify_files ) ) {
+ $file_mod_capabilities['reasons_modify_files_unavailable'] = $reasons_can_not_modify_files;
+ }
+
+ if ( ! $file_mod_capabilities['autoupdate_files'] ) {
+ $file_mod_capabilities['reasons_autoupdate_unavailable'] = array_merge( $reasons_can_not_autoupdate, $reasons_can_not_modify_files );
+ }
+ return $file_mod_capabilities;
+ }
+
+ protected function get_plugins() {
+ $plugins = array();
+ /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
+ $installed_plugins = apply_filters( 'all_plugins', get_plugins() );
+ foreach( $this->plugins as $plugin ) {
+ if ( ! isset( $installed_plugins[ $plugin ] ) )
+ continue;
+ $plugins[] = $this->format_plugin( $plugin, $installed_plugins[ $plugin ] );
+ }
+ $args = $this->query_args();
+
+ if ( isset( $args['offset'] ) ) {
+ $plugins = array_slice( $plugins, (int) $args['offset'] );
+ }
+ if ( isset( $args['limit'] ) ) {
+ $plugins = array_slice( $plugins, 0, (int) $args['limit'] );
+ }
+
+ return $plugins;
+ }
+
+ protected function validate_network_wide() {
+ $args = $this->input();
+
+ if ( isset( $args['network_wide'] ) && $args['network_wide'] ) {
+ $this->network_wide = true;
+ }
+
+ if ( $this->network_wide && ! current_user_can( 'manage_network_plugins' ) ) {
+ return new WP_Error( 'unauthorized', __( 'This user is not authorized to manage plugins network wide.', 'jetpack' ), 403 );
+ }
+
+ return true;
+ }
+
+
+ protected function validate_plugin( $plugin ) {
+ if ( ! isset( $plugin) || empty( $plugin ) ) {
+ return new WP_Error( 'missing_plugin', __( 'You are required to specify a plugin to activate.', 'jetpack' ), 400 );
+ }
+
+ if ( is_wp_error( $error = validate_plugin( $plugin ) ) ) {
+ return new WP_Error( 'unknown_plugin', $error->get_error_messages() , 404 );
+ }
+
+ return true;
+ }
+
+ protected function get_plugin_updates( $plugin_file ) {
+ $plugin_updates = get_plugin_updates();
+ if ( isset( $plugin_updates[ $plugin_file ] ) ) {
+ $update = $plugin_updates[ $plugin_file ]->update;
+ $cleaned_update = array();
+ foreach( (array) $update as $update_key => $update_value ) {
+ switch ( $update_key ) {
+ case 'id':
+ case 'slug':
+ case 'plugin':
+ case 'new_version':
+ case 'tested':
+ $cleaned_update[ $update_key ] = wp_kses( $update_value, array() );
+ break;
+ case 'url':
+ case 'package':
+ $cleaned_update[ $update_key ] = esc_url( $update_value );
+ break;
+ }
+ }
+ return (object) $cleaned_update;
+ }
+ return null;
+ }
+
+ protected function get_plugin_action_links( $plugin_file ) {
+ require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-functions.php';
+ return Jetpack_Sync_Functions::get_plugins_action_links( $plugin_file );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-get-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-get-endpoint.php
new file mode 100644
index 00000000..276fca49
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-get-endpoint.php
@@ -0,0 +1,28 @@
+<?php
+
+new Jetpack_JSON_API_Plugins_Get_Endpoint(
+ array(
+ 'description' => 'Get the Plugin data.',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/plugins/%s/',
+ 'min_version' => '1',
+ 'max_version' => '1.1',
+ 'stat' => 'plugins:1',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$plugin' => '(string) The plugin ID',
+ ),
+ 'response_format' => Jetpack_JSON_API_Plugins_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/plugins/hello-dolly%20hello'
+ )
+);
+// no v1.2 version since it is .com only
+class Jetpack_JSON_API_Plugins_Get_Endpoint extends Jetpack_JSON_API_Plugins_Endpoint {
+ // GET /sites/%s/plugins/%s
+ protected $needed_capabilities = 'activate_plugins';
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-install-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-install-endpoint.php
new file mode 100644
index 00000000..bbd6de19
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-install-endpoint.php
@@ -0,0 +1,96 @@
+<?php
+
+include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+include_once ABSPATH . 'wp-admin/includes/file.php';
+// POST /sites/%s/plugins/%s/install
+new Jetpack_JSON_API_Plugins_Install_Endpoint(
+ array(
+ 'description' => 'Install a plugin to your jetpack blog',
+ 'group' => '__do_not_document',
+ 'stat' => 'plugins:1:install',
+ 'min_version' => '1',
+ 'max_version' => '1.1',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/plugins/%s/install',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$plugin' => '(int|string) The plugin slug to install',
+ ),
+ 'response_format' => Jetpack_JSON_API_Plugins_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/plugins/akismet/install'
+ )
+);
+
+new Jetpack_JSON_API_Plugins_Install_Endpoint(
+ array(
+ 'description' => 'Install a plugin to your jetpack blog',
+ 'group' => '__do_not_document',
+ 'stat' => 'plugins:1:install',
+ 'min_version' => '1.2',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/plugins/%s/install',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$plugin' => '(int|string) The plugin slug to install',
+ ),
+ 'response_format' => Jetpack_JSON_API_Plugins_Endpoint::$_response_format_v1_2,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.2/sites/example.wordpress.org/plugins/akismet/install'
+ )
+);
+
+class Jetpack_JSON_API_Plugins_Install_Endpoint extends Jetpack_JSON_API_Plugins_Endpoint {
+
+ // POST /sites/%s/plugins/%s/install
+ protected $needed_capabilities = 'install_plugins';
+ protected $action = 'install';
+
+ protected function install() {
+ jetpack_require_lib( 'plugins' );
+ $result = '';
+ foreach ( $this->plugins as $index => $slug ) {
+ $result = Jetpack_Plugins::install_plugin( $slug );
+ if ( is_wp_error( $result ) ) {
+ $this->log[ $slug ][] = $result->get_error_message();
+ if ( ! $this->bulk ) {
+ return $result;
+ }
+ }
+ }
+
+ if ( is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ // No errors, install worked. Now replace the slug with the actual plugin id
+ $this->plugins[$index] = Jetpack_Plugins::get_plugin_id_by_slug( $slug );
+
+ return true;
+ }
+
+ protected function validate_plugins() {
+ if ( empty( $this->plugins ) || ! is_array( $this->plugins ) ) {
+ return new WP_Error( 'missing_plugins', __( 'No plugins found.', 'jetpack' ) );
+ }
+
+ jetpack_require_lib( 'plugins' );
+ foreach ( $this->plugins as $index => $slug ) {
+ // make sure it is not already installed
+ if ( Jetpack_Plugins::get_plugin_id_by_slug( $slug ) ) {
+ return new WP_Error( 'plugin_already_installed', __( 'The plugin is already installed', 'jetpack' ) );
+ }
+
+ }
+
+ return true;
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-list-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-list-endpoint.php
new file mode 100644
index 00000000..ff8004d0
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-list-endpoint.php
@@ -0,0 +1,36 @@
+<?php
+
+new Jetpack_JSON_API_Plugins_List_Endpoint(
+ array(
+ 'description' => 'Get installed Plugins on your blog',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/plugins',
+ 'stat' => 'plugins',
+ 'min_version' => '1',
+ 'max_version' => '1.1',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => array(
+ 'plugins' => '(plugin) An array of plugin objects.',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/plugins'
+ )
+);
+// No v1.2 versions since they are .com only
+class Jetpack_JSON_API_Plugins_List_Endpoint extends Jetpack_JSON_API_Plugins_Endpoint {
+ // GET /sites/%s/plugins
+ protected $needed_capabilities = 'activate_plugins';
+ public function validate_input( $plugin ) {
+ wp_update_plugins();
+ $this->plugins = array_keys( get_plugins() );
+ return true;
+ }
+}
+
+
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-modify-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-modify-endpoint.php
new file mode 100644
index 00000000..49cf43dc
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-modify-endpoint.php
@@ -0,0 +1,420 @@
+<?php
+new Jetpack_JSON_API_Plugins_Modify_Endpoint(
+ array(
+ 'description' => 'Activate/Deactivate a Plugin on your Jetpack Site, or set automatic updates',
+ 'min_version' => '1',
+ 'max_version' => '1.1',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/plugins/%s',
+ 'stat' => 'plugins:1:modify',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$plugin' => '(string) The plugin ID',
+ ),
+ 'request_format' => array(
+ 'action' => '(string) Possible values are \'update\'',
+ 'autoupdate' => '(bool) Whether or not to automatically update the plugin',
+ 'active' => '(bool) Activate or deactivate the plugin',
+ 'network_wide' => '(bool) Do action network wide (default value: false)',
+ ),
+ 'query_parameters' => array(
+ 'autoupdate' => '(bool=false) If the update is happening as a result of autoupdate event',
+ ),
+ 'response_format' => Jetpack_JSON_API_Plugins_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ 'body' => array(
+ 'action' => 'update',
+ )
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/plugins/hello-dolly%20hello'
+ )
+);
+
+new Jetpack_JSON_API_Plugins_Modify_Endpoint(
+ array(
+ 'description' => 'Activate/Deactivate a list of plugins on your Jetpack Site, or set automatic updates',
+ 'min_version' => '1',
+ 'max_version' => '1.1',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/plugins',
+ 'stat' => 'plugins:modify',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ ),
+ 'request_format' => array(
+ 'action' => '(string) Possible values are \'update\'',
+ 'autoupdate' => '(bool) Whether or not to automatically update the plugin',
+ 'active' => '(bool) Activate or deactivate the plugin',
+ 'network_wide' => '(bool) Do action network wide (default value: false)',
+ 'plugins' => '(array) A list of plugin ids to modify',
+ ),
+ 'query_parameters' => array(
+ 'autoupdate' => '(bool=false) If the update is happening as a result of autoupdate event',
+ ),
+ 'response_format' => array(
+ 'plugins' => '(array:plugin) An array of plugin objects.',
+ 'updated' => '(array) A list of plugin ids that were updated. Only present if action is update.',
+ 'not_updated' => '(array) A list of plugin ids that were not updated. Only present if action is update.',
+ 'log' => '(array) Update log. Only present if action is update.',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ 'body' => array(
+ 'active' => true,
+ 'plugins' => array(
+ 'jetpack/jetpack',
+ 'akismet/akismet',
+ ),
+ )
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/plugins'
+ )
+);
+
+new Jetpack_JSON_API_Plugins_Modify_Endpoint(
+ array(
+ 'description' => 'Update a Plugin on your Jetpack Site',
+ 'min_version' => '1',
+ 'max_version' => '1.1',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/plugins/%s/update/',
+ 'stat' => 'plugins:1:update',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$plugin' => '(string) The plugin ID',
+ ),
+ 'query_parameters' => array(
+ 'autoupdate' => '(bool=false) If the update is happening as a result of autoupdate event',
+ ),
+ 'response_format' => Jetpack_JSON_API_Plugins_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/plugins/hello-dolly%20hello/update'
+ )
+);
+
+class Jetpack_JSON_API_Plugins_Modify_Endpoint extends Jetpack_JSON_API_Plugins_Endpoint {
+ // POST /sites/%s/plugins/%s
+ // POST /sites/%s/plugins
+ protected $slug = null;
+ protected $needed_capabilities = 'activate_plugins';
+ protected $action = 'default_action';
+ protected $expected_actions = array( 'update', 'install', 'delete', 'update_translations' );
+
+ public function callback( $path = '', $blog_id = 0, $object = null ) {
+ Jetpack_JSON_API_Endpoint::validate_input( $object );
+ switch ( $this->action ) {
+ case 'delete':
+ $this->needed_capabilities = 'delete_plugins';
+ case 'update_translations':
+ case 'update' :
+ $this->needed_capabilities = 'update_plugins';
+ break;
+ case 'install' :
+ $this->needed_capabilities = 'install_plugins';
+ break;
+ }
+
+ if ( isset( $args['autoupdate'] ) || isset( $args['autoupdate_translations'] ) ) {
+ $this->needed_capabilities = 'update_plugins';
+ }
+
+ return parent::callback( $path, $blog_id, $object );
+ }
+
+ public function default_action() {
+ $args = $this->input();
+
+ if ( isset( $args['autoupdate'] ) && is_bool( $args['autoupdate'] ) ) {
+ if ( $args['autoupdate'] ) {
+ $this->autoupdate_on();
+ } else {
+ $this->autoupdate_off();
+ }
+ }
+
+ if ( isset( $args['active'] ) && is_bool( $args['active'] ) ) {
+ if ( $args['active'] ) {
+ // We don't have to check for activate_plugins permissions since we assume that the user has those
+ // Since we set them via $needed_capabilities.
+ return $this->activate();
+ } else {
+ if ( $this->current_user_can( 'deactivate_plugins' ) ) {
+ return $this->deactivate();
+ } else {
+ return new WP_Error( 'unauthorized_error', __( 'Plugin deactivation is not allowed', 'jetpack' ), '403' );
+ }
+ }
+ }
+
+ if ( isset( $args['autoupdate_translations'] ) && is_bool( $args['autoupdate_translations'] ) ) {
+ if ( $args['autoupdate_translations'] ) {
+ $this->autoupdate_translations_on();
+ } else {
+ $this->autoupdate_translations_off();
+ }
+ }
+
+ return true;
+ }
+
+ protected function autoupdate_on() {
+ $autoupdate_plugins = Jetpack_Options::get_option( 'autoupdate_plugins', array() );
+ $autoupdate_plugins = array_unique( array_merge( $autoupdate_plugins, $this->plugins ) );
+ Jetpack_Options::update_option( 'autoupdate_plugins', $autoupdate_plugins );
+ }
+
+ protected function autoupdate_off() {
+ $autoupdate_plugins = Jetpack_Options::get_option( 'autoupdate_plugins', array() );
+ $autoupdate_plugins = array_diff( $autoupdate_plugins, $this->plugins );
+ Jetpack_Options::update_option( 'autoupdate_plugins', $autoupdate_plugins );
+ }
+
+ protected function autoupdate_translations_on() {
+ $autoupdate_plugins = Jetpack_Options::get_option( 'autoupdate_plugins_translations', array() );
+ $autoupdate_plugins = array_unique( array_merge( $autoupdate_plugins, $this->plugins ) );
+ Jetpack_Options::update_option( 'autoupdate_plugins_translations', $autoupdate_plugins );
+ }
+
+ protected function autoupdate_translations_off() {
+ $autoupdate_plugins = Jetpack_Options::get_option( 'autoupdate_plugins_translations', array() );
+ $autoupdate_plugins = array_diff( $autoupdate_plugins, $this->plugins );
+ Jetpack_Options::update_option( 'autoupdate_plugins_translations', $autoupdate_plugins );
+ }
+
+ protected function activate() {
+ $permission_error = false;
+ foreach ( $this->plugins as $plugin ) {
+
+ if ( ! $this->current_user_can( 'activate_plugin', $plugin ) ) {
+ $this->log[$plugin]['error'] = __( 'Sorry, you are not allowed to activate this plugin.' );
+ $has_errors = true;
+ $permission_error = true;
+ continue;
+ }
+
+ if ( ( ! $this->network_wide && Jetpack::is_plugin_active( $plugin ) ) || is_plugin_active_for_network( $plugin ) ) {
+ $this->log[$plugin]['error'] = __( 'The Plugin is already active.', 'jetpack' );
+ $has_errors = true;
+ continue;
+ }
+
+ if ( ! $this->network_wide && is_network_only_plugin( $plugin ) && is_multisite() ) {
+ $this->log[$plugin]['error'] = __( 'Plugin can only be Network Activated', 'jetpack' );
+ $has_errors = true;
+ continue;
+ }
+
+ $result = activate_plugin( $plugin, '', $this->network_wide );
+
+ if ( is_wp_error( $result ) ) {
+ $this->log[$plugin]['error'] = $result->get_error_messages();
+ $has_errors = true;
+ continue;
+ }
+
+ $success = Jetpack::is_plugin_active( $plugin );
+ if ( $success && $this->network_wide ) {
+ $success &= is_plugin_active_for_network( $plugin );
+ }
+
+ if ( ! $success ) {
+ $this->log[$plugin]['error'] = $result->get_error_messages;
+ $has_errors = true;
+ continue;
+ }
+ $this->log[$plugin][] = __( 'Plugin activated.', 'jetpack' );
+ }
+
+ if ( ! $this->bulk && isset( $has_errors ) ) {
+ $plugin = $this->plugins[0];
+ if ( $permission_error ) {
+ return new WP_Error( 'unauthorized_error', $this->log[$plugin]['error'], 403 );
+ }
+
+ return new WP_Error( 'activation_error', $this->log[$plugin]['error'] );
+ }
+ }
+
+ protected function current_user_can( $capability, $plugin = null ) {
+ if ( $plugin ) {
+ return current_user_can( $capability, $plugin );
+ }
+
+ return current_user_can( $capability );
+ }
+
+ protected function deactivate() {
+ $permission_error = false;
+ foreach ( $this->plugins as $plugin ) {
+ if ( ! $this->current_user_can( 'deactivate_plugin', $plugin ) ) {
+ $error = $this->log[$plugin]['error'] = __( 'Sorry, you are not allowed to deactivate this plugin.', 'jetpack' );
+ $permission_error = true;
+ continue;
+ }
+
+ if ( ! Jetpack::is_plugin_active( $plugin ) ) {
+ $error = $this->log[$plugin]['error'] = __( 'The Plugin is already deactivated.', 'jetpack' );
+ continue;
+ }
+
+ deactivate_plugins( $plugin, false, $this->network_wide );
+
+ $success = ! Jetpack::is_plugin_active( $plugin );
+ if ( $success && $this->network_wide ) {
+ $success &= ! is_plugin_active_for_network( $plugin );
+ }
+
+ if ( ! $success ) {
+ $error = $this->log[$plugin]['error'] = __( 'There was an error deactivating your plugin', 'jetpack' );
+ continue;
+ }
+ $this->log[$plugin][] = __( 'Plugin deactivated.', 'jetpack' );
+ }
+ if ( ! $this->bulk && isset( $error ) ) {
+ if ( $permission_error ) {
+ return new WP_Error( 'unauthorized_error', $error, 403 );
+ }
+
+ return new WP_Error( 'deactivation_error', $error );
+ }
+ }
+
+ protected function update() {
+ $query_args = $this->query_args();
+ if ( isset( $query_args['autoupdate'] ) && $query_args['autoupdate'] ) {
+ Jetpack_Constants::set_constant( 'JETPACK_PLUGIN_AUTOUPDATE', true );
+ }
+ wp_clean_plugins_cache();
+ ob_start();
+ wp_update_plugins(); // Check for Plugin updates
+ ob_end_clean();
+
+ $update_plugins = get_site_transient( 'update_plugins' );
+
+ if ( isset( $update_plugins->response ) ) {
+ $plugin_updates_needed = array_keys( $update_plugins->response );
+ } else {
+ $plugin_updates_needed = array();
+ }
+
+ $update_attempted = false;
+
+ include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+
+ // unhook this functions that output things before we send our response header.
+ remove_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 );
+ remove_action( 'upgrader_process_complete', 'wp_version_check' );
+ remove_action( 'upgrader_process_complete', 'wp_update_themes' );
+
+ $result = false;
+
+ foreach ( $this->plugins as $plugin ) {
+
+ if ( ! in_array( $plugin, $plugin_updates_needed ) ) {
+ $this->log[$plugin][] = __( 'No update needed', 'jetpack' );
+ continue;
+ }
+
+ /**
+ * Pre-upgrade action
+ *
+ * @since 3.9.3
+ *
+ * @param array $plugin Plugin data
+ * @param array $plugin Array of plugin objects
+ * @param bool $updated_attempted false for the first update, true subsequently
+ */
+ do_action( 'jetpack_pre_plugin_upgrade', $plugin, $this->plugins, $update_attempted );
+
+ $update_attempted = true;
+
+ // Object created inside the for loop to clean the messages for each plugin
+ $skin = new WP_Ajax_Upgrader_Skin();
+ // The Automatic_Upgrader_Skin skin shouldn't output anything.
+ $upgrader = new Plugin_Upgrader( $skin );
+ $upgrader->init();
+ // This avoids the plugin to be deactivated.
+ // Using bulk upgrade puts the site into maintenance mode during the upgrades
+ $result = $upgrader->bulk_upgrade( array( $plugin ) );
+ $errors = $upgrader->skin->get_errors();
+ $this->log[$plugin] = $upgrader->skin->get_upgrade_messages();
+
+ if ( is_wp_error( $errors ) && $errors->get_error_code() ) {
+ return $errors;
+ }
+ }
+
+ if ( ! $this->bulk && ! $result && $update_attempted ) {
+ return new WP_Error( 'update_fail', __( 'There was an error updating your plugin', 'jetpack' ), 400 );
+ }
+
+ return $this->default_action();
+ }
+
+ function update_translations() {
+ include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+
+ // Clear the cache.
+ wp_clean_plugins_cache();
+ ob_start();
+ wp_update_plugins(); // Check for Plugin updates
+ ob_end_clean();
+
+ $available_updates = get_site_transient( 'update_plugins' );
+ if ( ! isset( $available_updates->translations ) || empty( $available_updates->translations ) ) {
+ return new WP_Error( 'nothing_to_translate' );
+ }
+
+ $update_attempted = false;
+ $result = false;
+ foreach ( $this->plugins as $plugin ) {
+ $this->slug = Jetpack_Autoupdate::get_plugin_slug( $plugin );
+ $translation = array_filter( $available_updates->translations, array( $this, 'get_translation' ) );
+
+ if ( empty( $translation ) ) {
+ $this->log[$plugin][] = __( 'No update needed', 'jetpack' );
+ continue;
+ }
+
+ /**
+ * Pre-upgrade action
+ *
+ * @since 4.4.0
+ *
+ * @param array $plugin Plugin data
+ * @param array $plugin Array of plugin objects
+ * @param bool $update_attempted false for the first update, true subsequently
+ */
+ do_action( 'jetpack_pre_plugin_upgrade_translations', $plugin, $this->plugins, $update_attempted );
+
+ $update_attempted = true;
+
+ $skin = new Automatic_Upgrader_Skin();
+ $upgrader = new Language_Pack_Upgrader( $skin );
+ $upgrader->init();
+
+ $result = $upgrader->upgrade( (object) $translation[0] );
+
+ $this->log[$plugin] = $upgrader->skin->get_upgrade_messages();
+ }
+
+ if ( ! $this->bulk && ! $result ) {
+ return new WP_Error( 'update_fail', __( 'There was an error updating your plugin', 'jetpack' ), 400 );
+ }
+
+ return true;
+ }
+
+ protected function get_translation( $translation ) {
+ return ( $translation['slug'] === $this->slug );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-modify-v1-2-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-modify-v1-2-endpoint.php
new file mode 100644
index 00000000..23940da0
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-modify-v1-2-endpoint.php
@@ -0,0 +1,191 @@
+<?php
+new Jetpack_JSON_API_Plugins_Modify_v1_2_Endpoint(
+ array(
+ 'description' => 'Activate/Deactivate a Plugin on your Jetpack Site, or set automatic updates',
+ 'min_version' => '1.2',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/plugins/%s',
+ 'stat' => 'plugins:1:modify',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$plugin' => '(string) The plugin ID',
+ ),
+ 'request_format' => array(
+ 'action' => '(string) Possible values are \'update\'',
+ 'autoupdate' => '(bool) Whether or not to automatically update the plugin',
+ 'active' => '(bool) Activate or deactivate the plugin',
+ 'network_wide' => '(bool) Do action network wide (default value: false)',
+ ),
+ 'query_parameters' => array(
+ 'autoupdate' => '(bool=false) If the update is happening as a result of autoupdate event',
+ ),
+ 'response_format' => Jetpack_JSON_API_Plugins_Endpoint::$_response_format_v1_2,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ 'body' => array(
+ 'action' => 'update',
+ )
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.2/sites/example.wordpress.org/plugins/hello-dolly%20hello'
+ )
+);
+
+new Jetpack_JSON_API_Plugins_Modify_v1_2_Endpoint(
+ array(
+ 'description' => 'Activate/Deactivate a list of plugins on your Jetpack Site, or set automatic updates',
+ 'min_version' => '1.2',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/plugins',
+ 'stat' => 'plugins:modify',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ ),
+ 'request_format' => array(
+ 'action' => '(string) Possible values are \'update\'',
+ 'autoupdate' => '(bool) Whether or not to automatically update the plugin',
+ 'active' => '(bool) Activate or deactivate the plugin',
+ 'network_wide' => '(bool) Do action network wide (default value: false)',
+ 'plugins' => '(array) A list of plugin ids to modify',
+ ),
+ 'query_parameters' => array(
+ 'autoupdate' => '(bool=false) If the update is happening as a result of autoupdate event',
+ ),
+ 'response_format' => array(
+ 'plugins' => '(array:plugin_v1_2) An array of plugin objects.',
+ 'updated' => '(array) A list of plugin ids that were updated. Only present if action is update.',
+ 'not_updated' => '(array) A list of plugin ids that were not updated. Only present if action is update.',
+ 'log' => '(array) Update log. Only present if action is update.',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ 'body' => array(
+ 'active' => true,
+ 'plugins' => array(
+ 'jetpack/jetpack',
+ 'akismet/akismet',
+ ),
+ )
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.2/sites/example.wordpress.org/plugins'
+ )
+);
+
+new Jetpack_JSON_API_Plugins_Modify_v1_2_Endpoint(
+ array(
+ 'description' => 'Update a Plugin on your Jetpack Site',
+ 'min_version' => '1.2',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/plugins/%s/update/',
+ 'stat' => 'plugins:1:update',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$plugin' => '(string) The plugin ID',
+ ),
+ 'query_parameters' => array(
+ 'autoupdate' => '(bool=false) If the update is happening as a result of autoupdate event',
+ ),
+ 'response_format' => Jetpack_JSON_API_Plugins_Endpoint::$_response_format_v1_2,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.2/sites/example.wordpress.org/plugins/hello-dolly%20hello/update'
+ )
+);
+
+class Jetpack_JSON_API_Plugins_Modify_v1_2_Endpoint extends Jetpack_JSON_API_Plugins_Modify_Endpoint {
+
+ protected function activate() {
+ $permission_error = false;
+ $has_errors = false;
+ foreach ( $this->plugins as $plugin ) {
+
+ if ( ! $this->current_user_can( 'activate_plugin', $plugin ) ) {
+ $this->log[$plugin]['error'] = __( 'Sorry, you are not allowed to activate this plugin.' );
+ $has_errors = true;
+ $permission_error = true;
+ continue;
+ }
+
+ if ( ( ! $this->network_wide && Jetpack::is_plugin_active( $plugin ) ) || is_plugin_active_for_network( $plugin ) ) {
+ continue;
+ }
+
+ if ( ! $this->network_wide && is_network_only_plugin( $plugin ) && is_multisite() ) {
+ $this->log[$plugin]['error'] = __( 'Plugin can only be Network Activated', 'jetpack' );
+ $has_errors = true;
+ continue;
+ }
+
+ $result = activate_plugin( $plugin, '', $this->network_wide );
+
+ if ( is_wp_error( $result ) ) {
+ $this->log[$plugin]['error'] = $result->get_error_messages();
+ $has_errors = true;
+ continue;
+ }
+
+ $success = Jetpack::is_plugin_active( $plugin );
+ if ( $success && $this->network_wide ) {
+ $success &= is_plugin_active_for_network( $plugin );
+ }
+
+ if ( ! $success ) {
+ $this->log[$plugin]['error'] = $result->get_error_messages;
+ $has_errors = true;
+ continue;
+ }
+ $this->log[$plugin][] = __( 'Plugin activated.', 'jetpack' );
+ }
+
+ if ( ! $this->bulk && $has_errors ) {
+ $plugin = $this->plugins[0];
+ if ( $permission_error ) {
+ return new WP_Error( 'unauthorized_error', $this->log[$plugin]['error'], 403 );
+ }
+
+ return new WP_Error( 'activation_error', $this->log[$plugin]['error'] );
+ }
+ }
+
+
+ protected function deactivate() {
+ $permission_error = false;
+ foreach ( $this->plugins as $plugin ) {
+ if ( ! $this->current_user_can( 'deactivate_plugin', $plugin ) ) {
+ $error = $this->log[$plugin]['error'] = __( 'Sorry, you are not allowed to deactivate this plugin.', 'jetpack' );
+ $permission_error = true;
+ continue;
+ }
+
+ if ( ! Jetpack::is_plugin_active( $plugin ) ) {
+ continue;
+ }
+
+ deactivate_plugins( $plugin, false, $this->network_wide );
+
+ $success = ! Jetpack::is_plugin_active( $plugin );
+ if ( $success && $this->network_wide ) {
+ $success &= ! is_plugin_active_for_network( $plugin );
+ }
+
+ if ( ! $success ) {
+ $error = $this->log[$plugin]['error'] = __( 'There was an error deactivating your plugin', 'jetpack' );
+ continue;
+ }
+ $this->log[$plugin][] = __( 'Plugin deactivated.', 'jetpack' );
+ }
+ if ( ! $this->bulk && isset( $error ) ) {
+ if ( $permission_error ) {
+ return new WP_Error( 'unauthorized_error', $error, 403 );
+ }
+
+ return new WP_Error( 'deactivation_error', $error );
+ }
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-new-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-new-endpoint.php
new file mode 100644
index 00000000..286a625b
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-plugins-new-endpoint.php
@@ -0,0 +1,136 @@
+<?php
+
+include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+include_once ABSPATH . 'wp-admin/includes/file.php';
+
+
+// POST /sites/%s/plugins/new
+new Jetpack_JSON_API_Plugins_New_Endpoint(
+ array(
+ 'description' => 'Install a plugin to a Jetpack site by uploading a zip file',
+ 'group' => '__do_not_document',
+ 'stat' => 'plugins:new',
+ 'min_version' => '1',
+ 'max_version' => '1.1',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/plugins/new',
+ 'path_labels' => array(
+ '$site' => '(int|string) Site ID or domain',
+ ),
+ 'request_format' => array(
+ 'zip' => '(zip) Plugin package zip file. multipart/form-data encoded. ',
+ ),
+ 'response_format' => Jetpack_JSON_API_Plugins_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/plugins/new'
+ )
+);
+
+
+new Jetpack_JSON_API_Plugins_New_Endpoint(
+ array(
+ 'description' => 'Install a plugin to a Jetpack site by uploading a zip file',
+ 'group' => '__do_not_document',
+ 'stat' => 'plugins:new',
+ 'min_version' => '1.2',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/plugins/new',
+ 'path_labels' => array(
+ '$site' => '(int|string) Site ID or domain',
+ ),
+ 'request_format' => array(
+ 'zip' => '(zip) Plugin package zip file. multipart/form-data encoded. ',
+ ),
+ 'response_format' => Jetpack_JSON_API_Plugins_Endpoint::$_response_format_v1_2,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.2/sites/example.wordpress.org/plugins/new'
+ )
+);
+
+class Jetpack_JSON_API_Plugins_New_Endpoint extends Jetpack_JSON_API_Plugins_Endpoint {
+
+ // POST /sites/%s/plugins/new
+ protected $needed_capabilities = 'install_plugins';
+ protected $action = 'install';
+
+ protected function validate_call( $_blog_id, $capability, $check_manage_active = true ) {
+ $validate = parent::validate_call( $_blog_id, $capability, $check_manage_active );
+ if ( is_wp_error( $validate ) ) {
+
+ // Lets delete the attachment... if the user doesn't have the right permissions to do things.
+ $args = $this->input();
+ if ( isset( $args['zip'][0]['id'] ) ) {
+ wp_delete_attachment( $args['zip'][0]['id'], true );
+ }
+ }
+
+ return $validate;
+ }
+
+ // no need to try to validate the plugin since we didn't pass one in.
+ protected function validate_input( $plugin ) {
+ $this->bulk = false;
+ $this->plugins = array();
+ }
+
+ function install() {
+ $args = $this->input();
+
+ if ( isset( $args['zip'][0]['id'] ) ) {
+ $plugin_attachment_id = $args['zip'][0]['id'];
+ $local_file = get_attached_file( $plugin_attachment_id );
+ if ( ! $local_file ) {
+ return new WP_Error( 'local-file-does-not-exist' );
+ }
+ jetpack_require_lib( 'class.jetpack-automatic-install-skin' );
+ $skin = new Jetpack_Automatic_Install_Skin();
+ $upgrader = new Plugin_Upgrader( $skin );
+
+ $pre_install_plugin_list = get_plugins();
+ $result = $upgrader->install( $local_file );
+
+ // clean up.
+ wp_delete_attachment( $plugin_attachment_id, true );
+
+ if ( is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ $after_install_plugin_list = get_plugins();
+ $plugin = array_values( array_diff( array_keys( $after_install_plugin_list ), array_keys( $pre_install_plugin_list ) ) );
+
+ if ( ! $result ) {
+ $error_code = $upgrader->skin->get_main_error_code();
+ $message = $upgrader->skin->get_main_error_message();
+ if ( empty( $message ) ) {
+ $message = __( 'An unknown error occurred during installation', 'jetpack' );
+ }
+
+ if ( 'download_failed' === $error_code ) {
+ $error_code = 'no_package';
+ }
+
+ return new WP_Error( $error_code, $message, 400 );
+ }
+
+ if ( empty( $plugin ) ) {
+ return new WP_Error( 'plugin_already_installed' );
+ }
+
+ $this->plugins = $plugin;
+ $this->log[ $plugin[0] ] = $upgrader->skin->get_upgrade_messages();
+
+ return true;
+ }
+
+ return new WP_Error( 'no_plugin_installed' );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-sync-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-sync-endpoint.php
new file mode 100644
index 00000000..72dcd52c
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-sync-endpoint.php
@@ -0,0 +1,322 @@
+<?php
+
+// POST /sites/%s/sync
+class Jetpack_JSON_API_Sync_Endpoint extends Jetpack_JSON_API_Endpoint {
+ protected $needed_capabilities = 'manage_options';
+
+ protected function validate_call( $_blog_id, $capability, $check_manage_active = true ) {
+ return parent::validate_call( $_blog_id, $capability, false );
+ }
+
+ protected function result() {
+ $args = $this->input();
+ $modules = null;
+
+ // convert list of modules in comma-delimited format into an array
+ // of "$modulename => true"
+ if ( isset( $args['modules'] ) && ! empty( $args['modules'] ) ) {
+ $modules = array_map( '__return_true', array_flip( array_map( 'trim', explode( ',', $args['modules'] ) ) ) );
+ }
+
+ foreach ( array( 'posts', 'comments', 'users' ) as $module_name ) {
+ if ( 'users' === $module_name && isset( $args[ $module_name ] ) && 'initial' === $args[ $module_name ] ) {
+ $modules[ 'users' ] = 'initial';
+ } elseif ( isset( $args[ $module_name ] ) ) {
+ $ids = explode( ',', $args[ $module_name ] );
+ if ( count( $ids ) > 0 ) {
+ $modules[ $module_name ] = $ids;
+ }
+ }
+ }
+
+ if ( empty( $modules ) ) {
+ $modules = null;
+ }
+ return array( 'scheduled' => Jetpack_Sync_Actions::do_full_sync( $modules ) );
+ }
+
+ protected function validate_queue( $query ) {
+ if ( ! isset( $query ) ) {
+ return new WP_Error( 'invalid_queue', 'Queue name is required', 400 );
+ }
+
+ if ( ! in_array( $query, array( 'sync', 'full_sync' ) ) ) {
+ return new WP_Error( 'invalid_queue', 'Queue name should be sync or full_sync', 400 );
+ }
+ return $query;
+ }
+}
+
+// GET /sites/%s/sync/status
+class Jetpack_JSON_API_Sync_Status_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
+ protected function result() {
+ return Jetpack_Sync_Actions::get_sync_status();
+ }
+}
+
+// GET /sites/%s/data-check
+class Jetpack_JSON_API_Sync_Check_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
+ protected function result() {
+ require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-wp-replicastore.php';
+ $store = new Jetpack_Sync_WP_Replicastore();
+ return $store->checksum_all();
+ }
+}
+
+// GET /sites/%s/data-histogram
+class Jetpack_JSON_API_Sync_Histogram_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
+ protected function result() {
+ $args = $this->query_args();
+
+ if ( isset( $args['columns'] ) ) {
+ $columns = array_map( 'trim', explode( ',', $args['columns'] ) );
+ } else {
+ $columns = null; // go with defaults
+ }
+
+ require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-wp-replicastore.php';
+ $store = new Jetpack_Sync_WP_Replicastore();
+
+ if ( ! isset( $args['strip_non_ascii'] ) ) {
+ $args['strip_non_ascii'] = true;
+ }
+ $histogram = $store->checksum_histogram( $args['object_type'], $args['buckets'], $args['start_id'], $args['end_id'], $columns, $args['strip_non_ascii'], $args['shared_salt'] );
+
+ return array( 'histogram' => $histogram, 'type' => $store->get_checksum_type() );
+ }
+}
+
+// POST /sites/%s/sync/settings
+class Jetpack_JSON_API_Sync_Modify_Settings_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
+ protected function result() {
+ $args = $this->input();
+
+ require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-settings.php';
+
+ $sync_settings = Jetpack_Sync_Settings::get_settings();
+
+ foreach ( $args as $key => $value ) {
+ if ( $value !== false ) {
+ if ( is_numeric( $value ) ) {
+ $value = (int) $value;
+ }
+
+ // special case for sending empty arrays - a string with value 'empty'
+ if ( $value === 'empty' ) {
+ $value = array();
+ }
+
+ $sync_settings[ $key ] = $value;
+ }
+ }
+
+ Jetpack_Sync_Settings::update_settings( $sync_settings );
+
+ // re-fetch so we see what's really being stored
+ return Jetpack_Sync_Settings::get_settings();
+ }
+}
+
+// GET /sites/%s/sync/settings
+class Jetpack_JSON_API_Sync_Get_Settings_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
+ protected function result() {
+ require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-settings.php';
+
+ return Jetpack_Sync_Settings::get_settings();
+ }
+}
+
+// GET /sites/%s/sync/object
+class Jetpack_JSON_API_Sync_Object extends Jetpack_JSON_API_Sync_Endpoint {
+ protected function result() {
+ $args = $this->query_args();
+
+ $module_name = $args['module_name'];
+
+ require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-modules.php';
+
+ if ( ! $sync_module = Jetpack_Sync_Modules::get_module( $module_name ) ) {
+ return new WP_Error( 'invalid_module', 'You specified an invalid sync module' );
+ }
+
+ $object_type = $args['object_type'];
+ $object_ids = $args['object_ids'];
+
+ require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-sender.php';
+ $codec = Jetpack_Sync_Sender::get_instance()->get_codec();
+
+ Jetpack_Sync_Settings::set_is_syncing( true );
+ $objects = $codec->encode( $sync_module->get_objects_by_id( $object_type, $object_ids ) );
+ Jetpack_Sync_Settings::set_is_syncing( false );
+
+ return array(
+ 'objects' => $objects,
+ 'codec' => $codec->name(),
+ );
+ }
+}
+
+class Jetpack_JSON_API_Sync_Now_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
+ protected function result() {
+ $args = $this->input();
+ $queue_name = $this->validate_queue( $args['queue'] );
+
+ if ( is_wp_error( $queue_name ) ){
+ return $queue_name;
+ }
+
+ require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-sender.php';
+
+ $sender = Jetpack_Sync_Sender::get_instance();
+ $response = $sender->do_sync_for_queue( new Jetpack_Sync_Queue( $args['queue'] ) );
+
+ return array(
+ 'response' => $response
+ );
+ }
+}
+
+class Jetpack_JSON_API_Sync_Checkout_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
+ protected function result() {
+ $args = $this->input();
+ $queue_name = $this->validate_queue( $args['queue'] );
+
+ if ( is_wp_error( $queue_name ) ){
+ return $queue_name;
+ }
+
+ if ( $args[ 'number_of_items' ] < 1 || $args[ 'number_of_items' ] > 100 ) {
+ return new WP_Error( 'invalid_number_of_items', 'Number of items needs to be an integer that is larger than 0 and less then 100', 400 );
+ }
+
+ require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-queue.php';
+ $queue = new Jetpack_Sync_Queue( $queue_name );
+
+ if ( 0 === $queue->size() ) {
+ return new WP_Error( 'queue_size', 'The queue is empty and there is nothing to send', 400 );
+ }
+
+ require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-sender.php';
+ $sender = Jetpack_Sync_Sender::get_instance();
+
+ // try to give ourselves as much time as possible
+ set_time_limit( 0 );
+
+ // let's delete the checkin state
+ if ( $args['force'] ) {
+ $queue->unlock();
+ }
+
+ $buffer = $this->get_buffer( $queue, $args[ 'number_of_items' ] );
+
+ // Check that the $buffer is not checkout out already
+ if ( is_wp_error( $buffer ) ) {
+ return new WP_Error( 'buffer_open', "We couldn't get the buffer it is currently checked out", 400 );
+ }
+
+ if ( ! is_object( $buffer ) ) {
+ return new WP_Error( 'buffer_non-object', 'Buffer is not an object', 400 );
+ }
+
+ Jetpack_Sync_Settings::set_is_syncing( true );
+ list( $items_to_send, $skipped_items_ids, $items ) = $sender->get_items_to_send( $buffer, $args['encode'] );
+ Jetpack_Sync_Settings::set_is_syncing( false );
+
+ return array(
+ 'buffer_id' => $buffer->id,
+ 'items' => $items_to_send,
+ 'skipped_items' => $skipped_items_ids,
+ 'codec' => $args['encode'] ? $sender->get_codec()->name() : null,
+ 'sent_timestamp' => time(),
+ );
+ }
+
+ protected function get_buffer( $queue, $number_of_items ) {
+ $start = time();
+ $max_duration = 5; // this will try to get the buffer
+
+ $buffer = $queue->checkout( $number_of_items );
+ $duration = time() - $start;
+
+ while( is_wp_error( $buffer ) && $duration < $max_duration ) {
+ sleep( 2 );
+ $duration = time() - $start;
+ $buffer = $queue->checkout( $number_of_items );
+ }
+
+ if ( $buffer === false ) {
+ return new WP_Error( 'queue_size', 'The queue is empty and there is nothing to send', 400 );
+ }
+
+ return $buffer;
+ }
+}
+
+class Jetpack_JSON_API_Sync_Close_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
+ protected function result() {
+ $request_body = $this->input();
+ $queue_name = $this->validate_queue( $request_body['queue'] );
+
+ if ( is_wp_error( $queue_name ) ) {
+ return $queue_name;
+ }
+ require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-queue.php';
+
+ if ( ! isset( $request_body['buffer_id'] ) ) {
+ return new WP_Error( 'missing_buffer_id', 'Please provide a buffer id', 400 );
+ }
+
+ if ( ! isset( $request_body['item_ids'] ) || ! is_array( $request_body['item_ids'] ) ) {
+ return new WP_Error( 'missing_item_ids', 'Please provide a list of item ids in the item_ids argument', 400 );
+ }
+
+ //Limit to A-Z,a-z,0-9,_,-
+ $request_body ['buffer_id'] = preg_replace( '/[^A-Za-z0-9]/', '', $request_body['buffer_id'] );
+ $request_body['item_ids'] = array_filter( array_map( array( 'Jetpack_JSON_API_Sync_Close_Endpoint', 'sanitize_item_ids' ), $request_body['item_ids'] ) );
+
+ $buffer = new Jetpack_Sync_Queue_Buffer( $request_body['buffer_id'], $request_body['item_ids'] );
+ $queue = new Jetpack_Sync_Queue( $queue_name );
+
+ $response = $queue->close( $buffer, $request_body['item_ids'] );
+
+ if ( is_wp_error( $response ) ) {
+ return $response;
+ }
+
+ return array(
+ 'success' => $response
+ );
+ }
+
+ protected static function sanitize_item_ids( $item ) {
+ // lets not delete any options that don't start with jpsq_sync-
+ if ( substr( $item, 0, 5 ) !== 'jpsq_' ) {
+ return null;
+ }
+ //Limit to A-Z,a-z,0-9,_,-,.
+ return preg_replace( '/[^A-Za-z0-9-_.]/', '', $item );
+ }
+}
+
+class Jetpack_JSON_API_Sync_Unlock_Endpoint extends Jetpack_JSON_API_Sync_Endpoint {
+ protected function result() {
+ $args = $this->input();
+
+ if ( ! isset( $args['queue'] ) ) {
+ return new WP_Error( 'invalid_queue', 'Queue name is required', 400 );
+ }
+
+ if ( ! in_array( $args['queue'], array( 'sync', 'full_sync' ) ) ) {
+ return new WP_Error( 'invalid_queue', 'Queue name should be sync or full_sync', 400 );
+ }
+
+ require_once JETPACK__PLUGIN_DIR . 'sync/class.jetpack-sync-queue.php';
+ $queue = new Jetpack_Sync_Queue( $args['queue'] );
+
+ // False means that there was no lock to delete.
+ $response = $queue->unlock();
+ return array(
+ 'success' => $response
+ );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-active-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-active-endpoint.php
new file mode 100644
index 00000000..db23c52f
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-active-endpoint.php
@@ -0,0 +1,50 @@
+<?php
+
+class Jetpack_JSON_API_Themes_Active_Endpoint extends Jetpack_JSON_API_Themes_Endpoint {
+ // GET /sites/%s/themes/mine => current theme
+ // POST /sites/%s/themes/mine => switch theme
+ // The unused $object parameter is for making the method signature compatible with its parent class method.
+ public function callback( $path = '', $blog_id = 0, $object = null ) {
+
+ if ( is_wp_error( $error = $this->validate_call( $blog_id, 'switch_themes', true ) ) ) {
+ return $error;
+ }
+
+ if ( 'POST' === $this->api->method )
+ return $this->switch_theme();
+ else
+ return $this->get_current_theme();
+ }
+
+ protected function switch_theme() {
+ $args = $this->input();
+
+ if ( ! isset( $args['theme'] ) || empty( $args['theme'] ) ) {
+ return new WP_Error( 'missing_theme', __( 'You are required to specify a theme to switch to.', 'jetpack' ), 400 );
+ }
+
+ $theme_slug = $args['theme'];
+
+ if ( ! $theme_slug ) {
+ return new WP_Error( 'theme_not_found', __( 'Theme is empty.', 'jetpack' ), 404 );
+ }
+
+ $theme = wp_get_theme( $theme_slug );
+
+ if ( ! $theme->exists() ) {
+ return new WP_Error( 'theme_not_found', __( 'The specified theme was not found.', 'jetpack' ), 404 );
+ }
+
+ if ( ! $theme->is_allowed() ) {
+ return new WP_Error( 'theme_not_found', __( 'You are not allowed to switch to this theme', 'jetpack' ), 403 );
+ }
+
+ switch_theme( $theme_slug );
+
+ return $this->get_current_theme();
+ }
+
+ protected function get_current_theme() {
+ return $this->format_theme( wp_get_theme() );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-delete-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-delete-endpoint.php
new file mode 100644
index 00000000..97bcc58d
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-delete-endpoint.php
@@ -0,0 +1,60 @@
+<?php
+
+class Jetpack_JSON_API_Themes_Delete_Endpoint extends Jetpack_JSON_API_Themes_Endpoint {
+
+ // POST /sites/%s/plugins/%s/delete
+ protected $needed_capabilities = 'delete_themes';
+ protected $action = 'delete';
+
+ protected function delete() {
+
+ foreach( $this->themes as $theme ) {
+
+ // Don't delete an active child theme
+ if ( is_child_theme() && $theme == get_stylesheet() ) {
+ $error = $this->log[ $theme ]['error'] = 'You cannot delete a theme while it is active on the main site.';
+ continue;
+ }
+
+ if( $theme == get_template() ) {
+ $error = $this->log[ $theme ]['error'] = 'You cannot delete a theme while it is active on the main site.';
+ continue;
+ }
+
+ /**
+ * Filters whether to use an alternative process for deleting a WordPress.com theme.
+ * The alternative process can be executed during the filter.
+ *
+ * The filter can also return an instance of WP_Error; in which case the endpoint response will
+ * contain this error.
+ *
+ * @module json-api
+ *
+ * @since 4.4.2
+ *
+ * @param bool $use_alternative_delete_method Whether to use the alternative method of deleting
+ * a WPCom theme.
+ * @param string $theme_slug Theme name (slug). If it is a WPCom theme,
+ * it should be suffixed with `-wpcom`.
+ */
+ $result = apply_filters( 'jetpack_wpcom_theme_delete', false, $theme );
+
+ if ( ! $result ) {
+ $result = delete_theme( $theme );
+ }
+
+ if ( is_wp_error( $result ) ) {
+ $error = $this->log[ $theme ]['error'] = $result->get_error_messages();
+ } else {
+ $this->log[ $theme ][] = 'Theme deleted';
+ }
+ }
+
+ if( ! $this->bulk && isset( $error ) ) {
+ return new WP_Error( 'delete_theme_error', $error, 400 );
+ }
+
+ return true;
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-endpoint.php
new file mode 100644
index 00000000..2e32dccd
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-endpoint.php
@@ -0,0 +1,178 @@
+<?php
+
+
+// THEMES
+
+/**
+ * Base class for working with themes, has useful helper functions.
+ */
+abstract class Jetpack_JSON_API_Themes_Endpoint extends Jetpack_JSON_API_Endpoint {
+
+ protected $themes = array();
+
+ protected $bulk = true;
+ protected $log;
+ protected $current_theme_id;
+
+ static $_response_format = array(
+ 'id' => '(string) The theme\'s ID.',
+ 'screenshot' => '(string) A theme screenshot URL',
+ 'name' => '(string) The name of the theme.',
+ 'theme_uri' => '(string) The URI of the theme\'s webpage.',
+ 'description' => '(string) A description of the theme.',
+ 'author' => '(string) The author of the theme.',
+ 'author_uri' => '(string) The website of the theme author.',
+ 'tags' => '(array) Tags indicating styles and features of the theme.',
+ 'log' => '(array) An array of log strings',
+ 'autoupdate' => '(bool) Whether the theme is automatically updated',
+ 'autoupdate_translation' => '(bool) Whether the theme is automatically updating translations',
+ );
+
+ protected function result() {
+
+ $themes = $this->get_themes();
+
+ if ( ! $this->bulk && ! empty( $themes ) ) {
+ return array_pop( $themes );
+ }
+
+ return array( 'themes' => $themes );
+
+ }
+
+ /**
+ * Walks through either the submitted theme or list of themes and creates the global array
+ * @param $theme
+ *
+ * @return bool
+ */
+ protected function validate_input( $theme ) {
+ $args = $this->input();
+ // lets set what themes were requested, and validate them
+ if ( ! isset( $theme ) || empty( $theme ) ) {
+
+ if ( ! $args['themes'] || empty( $args['themes'] ) ) {
+ return new WP_Error( 'missing_theme', __( 'You are required to specify a theme to update.', 'jetpack' ), 400 );
+ }
+ if ( is_array( $args['themes'] ) ) {
+ $this->themes = $args['themes'];
+ } else {
+ $this->themes[] = $args['themes'];
+ }
+ } else {
+ $this->themes[] = urldecode( $theme );
+ $this->bulk = false;
+ }
+
+ if ( is_wp_error( $error = $this->validate_themes() ) ) {
+ return $error;
+ }
+
+ return parent::validate_input( $theme );
+ }
+
+ /**
+ * Walks through submitted themes to make sure they are valid
+ * @return bool|WP_Error
+ */
+ protected function validate_themes() {
+ foreach ( $this->themes as $theme ) {
+ if ( is_wp_error( $error = wp_get_theme( $theme )->errors() ) ) {
+ return new WP_Error( 'unknown_theme', $error->get_error_messages() , 404 );
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Format a theme for the public API
+ * @param object $theme WP_Theme object
+ * @return array Named array of theme info used by the API
+ */
+ protected function format_theme( $theme ) {
+
+ if ( ! ( $theme instanceof WP_Theme ) ) {
+ $theme = wp_get_theme( $theme );
+ }
+
+ $fields = array(
+ 'name' => 'Name',
+ 'theme_uri' => 'ThemeURI',
+ 'description' => 'Description',
+ 'author' => 'Author',
+ 'author_uri' => 'AuthorURI',
+ 'tags' => 'Tags',
+ 'version' => 'Version'
+ );
+
+ $id = $theme->get_stylesheet();
+ $formatted_theme = array(
+ 'id' => $id,
+ 'screenshot' => jetpack_photon_url( $theme->get_screenshot(), array(), 'network_path' ),
+ 'active' => $id === $this->current_theme_id,
+ );
+
+ foreach( $fields as $key => $field ) {
+ $formatted_theme[ $key ] = $theme->get( $field );
+ }
+
+ $update_themes = get_site_transient( 'update_themes' );
+ $formatted_theme['update'] = ( isset( $update_themes->response[ $id ] ) ) ? $update_themes->response[ $id ] : null;
+
+ $autoupdate = in_array( $id, Jetpack_Options::get_option( 'autoupdate_themes', array() ) );
+ $formatted_theme['autoupdate'] = $autoupdate;
+
+ $autoupdate_translation = in_array( $id, Jetpack_Options::get_option( 'autoupdate_themes_translations', array() ) );
+ $formatted_theme['autoupdate_translation'] = $autoupdate || $autoupdate_translation || Jetpack_Options::get_option( 'autoupdate_translations', false );
+
+ if ( isset( $this->log[ $id ] ) ) {
+ $formatted_theme['log'] = $this->log[ $id ];
+ }
+
+ /**
+ * Filter the array of theme information that will be returned per theme by the Jetpack theme APIs.
+ *
+ * @module json-api
+ *
+ * @since 4.7.0
+ *
+ * @param array $formatted_theme The theme info array.
+ */
+ return apply_filters( 'jetpack_format_theme_details', $formatted_theme );
+ }
+
+ /**
+ * Checks the query_args our collection endpoint was passed to ensure that it's in the proper bounds.
+ * @return bool|WP_Error a WP_Error object if the args are out of bounds, true if things are good.
+ */
+ protected function check_query_args() {
+ $args = $this->query_args();
+ if ( $args['offset'] < 0 )
+ return new WP_Error( 'invalid_offset', __( 'Offset must be greater than or equal to 0.', 'jetpack' ), 400 );
+ if ( $args['limit'] < 0 )
+ return new WP_Error( 'invalid_limit', __( 'Limit must be greater than or equal to 0.', 'jetpack' ), 400 );
+ return true;
+ }
+
+ /**
+ * Format a list of themes for public display, using the supplied offset and limit args
+ * @uses WPCOM_JSON_API_Endpoint::query_args()
+ * @return array Public API theme objects
+ */
+ protected function get_themes() {
+ // ditch keys
+ $themes = array_values( $this->themes );
+ // do offset & limit - we've already returned a 400 error if they're bad numbers
+ $args = $this->query_args();
+
+ if ( isset( $args['offset'] ) )
+ $themes = array_slice( $themes, (int) $args['offset'] );
+ if ( isset( $args['limit'] ) )
+ $themes = array_slice( $themes, 0, (int) $args['limit'] );
+
+ $this->current_theme_id = wp_get_theme()->get_stylesheet();
+
+ return array_map( array( $this, 'format_theme' ), $themes );
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-get-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-get-endpoint.php
new file mode 100644
index 00000000..cfc352af
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-get-endpoint.php
@@ -0,0 +1,6 @@
+<?php
+
+class Jetpack_JSON_API_Themes_Get_Endpoint extends Jetpack_JSON_API_Themes_Endpoint {
+ // GET /sites/%s/themes/%s
+ protected $needed_capabilities = 'switch_themes';
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-install-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-install-endpoint.php
new file mode 100644
index 00000000..c3cec3d3
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-install-endpoint.php
@@ -0,0 +1,173 @@
+<?php
+
+include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+include_once ABSPATH . 'wp-admin/includes/file.php';
+
+class Jetpack_JSON_API_Themes_Install_Endpoint extends Jetpack_JSON_API_Themes_Endpoint {
+
+ // POST /sites/%s/themes/%s/install
+ protected $needed_capabilities = 'install_themes';
+ protected $action = 'install';
+ protected $download_links = array();
+
+ protected function install() {
+
+ foreach ( $this->themes as $theme ) {
+
+ /**
+ * Filters whether to use an alternative process for installing a WordPress.com theme.
+ * The alternative process can be executed during the filter.
+ *
+ * The filter can also return an instance of WP_Error; in which case the endpoint response will
+ * contain this error.
+ *
+ * @module json-api
+ *
+ * @since 4.4.2
+ *
+ * @param bool $use_alternative_install_method Whether to use the alternative method of installing
+ * a WPCom theme.
+ * @param string $theme_slug Theme name (slug). If it is a WPCom theme,
+ * it should be suffixed with `-wpcom`.
+ */
+ $result = apply_filters( 'jetpack_wpcom_theme_install', false, $theme );
+
+ $skin = null;
+ $upgrader = null;
+ $link = null;
+
+ // If the alternative install method was not used, use the standard method.
+ if ( ! $result ) {
+ jetpack_require_lib( 'class.jetpack-automatic-install-skin' );
+ $skin = new Jetpack_Automatic_Install_Skin();
+ $upgrader = new Theme_Upgrader( $skin );
+
+ $link = $this->download_links[ $theme ];
+ $result = $upgrader->install( $link );
+ }
+
+ if ( file_exists( $link ) ) {
+ // Delete if link was tmp local file
+ unlink( $link );
+ }
+
+ if ( ! $this->bulk && is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ if ( ! $result ) {
+ $error = $this->log[ $theme ]['error'] = __( 'An unknown error occurred during installation', 'jetpack' );
+ }
+
+ elseif ( ! self::is_installed_theme( $theme ) ) {
+ $error = $this->log[ $theme ]['error'] = __( 'There was an error installing your theme', 'jetpack' );
+ }
+
+ elseif ( $upgrader ) {
+ $this->log[ $theme ][] = $upgrader->skin->get_upgrade_messages();
+ }
+ }
+
+ if ( ! $this->bulk && isset( $error ) ) {
+ return new WP_Error( 'install_error', $error, 400 );
+ }
+
+ return true;
+ }
+
+ protected function validate_themes() {
+ if ( empty( $this->themes ) || ! is_array( $this->themes ) ) {
+ return new WP_Error( 'missing_themes', __( 'No themes found.', 'jetpack' ) );
+ }
+ foreach( $this->themes as $index => $theme ) {
+
+ if ( self::is_installed_theme( $theme ) ) {
+ return new WP_Error( 'theme_already_installed', __( 'The theme is already installed', 'jetpack' ) );
+ }
+
+ /**
+ * Filters whether to skip the standard method of downloading and validating a WordPress.com
+ * theme. An alternative method of WPCom theme download and validation can be
+ * executed during the filter.
+ *
+ * The filter can also return an instance of WP_Error; in which case the endpoint response will
+ * contain this error.
+ *
+ * @module json-api
+ *
+ * @since 4.4.2
+ *
+ * @param bool $skip_download_filter_result Whether to skip the standard method of downloading
+ * and validating a WPCom theme.
+ * @param string $theme_slug Theme name (slug). If it is a WPCom theme,
+ * it should be suffixed with `-wpcom`.
+ */
+ $skip_download_filter_result = apply_filters( 'jetpack_wpcom_theme_skip_download', false, $theme );
+
+ if ( is_wp_error( $skip_download_filter_result ) ) {
+ return $skip_download_filter_result;
+ } elseif ( $skip_download_filter_result ) {
+ continue;
+ }
+
+ if ( wp_endswith( $theme, '-wpcom' ) ) {
+ $file = self::download_wpcom_theme_to_file( $theme );
+
+ if ( is_wp_error( $file ) ) {
+ return $file;
+ }
+
+ $this->download_links[ $theme ] = $file;
+ continue;
+ }
+
+ $params = (object) array( 'slug' => $theme );
+ $url = 'https://api.wordpress.org/themes/info/1.0/';
+ $args = array(
+ 'body' => array(
+ 'action' => 'theme_information',
+ 'request' => serialize( $params ),
+ )
+ );
+ $response = wp_remote_post( $url, $args );
+ $theme_data = unserialize( $response['body'] );
+ if ( is_wp_error( $theme_data ) ) {
+ return $theme_data;
+ }
+
+ if ( ! is_object( $theme_data ) && !isset( $theme_data->download_link ) ) {
+ return new WP_Error( 'theme_not_found', __( 'This theme does not exist', 'jetpack' ) , 404 );
+ }
+
+ $this->download_links[ $theme ] = $theme_data->download_link;
+
+ }
+ return true;
+ }
+
+ protected static function is_installed_theme( $theme ) {
+ $wp_theme = wp_get_theme( $theme );
+ return $wp_theme->exists();
+ }
+
+ protected static function download_wpcom_theme_to_file( $theme ) {
+ $wpcom_theme_slug = preg_replace( '/-wpcom$/', '', $theme );
+
+ $file = wp_tempnam( 'theme' );
+ if ( ! $file ) {
+ return new WP_Error( 'problem_creating_theme_file', __( 'Problem creating file for theme download', 'jetpack' ) );
+ }
+
+ $url = "themes/download/$theme.zip";
+ $args = array( 'stream' => true, 'filename' => $file );
+ $result = Jetpack_Client::wpcom_json_api_request_as_blog( $url, '1.1', $args );
+
+ $response = $result[ 'response' ];
+ if ( $response[ 'code' ] !== 200 ) {
+ unlink( $file );
+ return new WP_Error( 'problem_fetching_theme', __( 'Problem downloading theme', 'jetpack' ) );
+ }
+
+ return $file;
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-list-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-list-endpoint.php
new file mode 100644
index 00000000..526cf4d7
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-list-endpoint.php
@@ -0,0 +1,13 @@
+<?php
+
+class Jetpack_JSON_API_Themes_List_Endpoint extends Jetpack_JSON_API_Themes_Endpoint {
+ // GET /sites/%s/themes
+
+ protected $needed_capabilities = 'switch_themes';
+
+ public function validate_input( $theme ) {
+ $this->themes = wp_get_themes( array( 'allowed' => true ) );
+ return true;
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-modify-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-modify-endpoint.php
new file mode 100644
index 00000000..072bfc5c
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-modify-endpoint.php
@@ -0,0 +1,130 @@
+<?php
+
+class Jetpack_JSON_API_Themes_Modify_Endpoint extends Jetpack_JSON_API_Themes_Endpoint {
+ // POST /sites/%s/themes/%s
+ // POST /sites/%s/themes
+
+ protected $needed_capabilities = 'update_themes';
+ protected $action = 'default_action';
+ protected $expected_actions = array( 'update', 'update_translations' );
+
+ public function default_action() {
+ $args = $this->input();
+ if ( isset( $args['autoupdate'] ) && is_bool( $args['autoupdate'] ) ) {
+ if ( $args['autoupdate'] ) {
+ $this->autoupdate_on();
+ } else {
+ $this->autoupdate_off();
+ }
+ }
+ if ( isset( $args['autoupdate_translations'] ) && is_bool( $args['autoupdate_translations'] ) ) {
+ if ( $args['autoupdate_translations'] ) {
+ $this->autoupdate_translations_on();
+ } else {
+ $this->autoupdate_translations_off();
+ }
+ }
+
+ return true;
+ }
+
+ function autoupdate_on() {
+ $autoupdate_themes = Jetpack_Options::get_option( 'autoupdate_themes', array() );
+ $autoupdate_themes = array_unique( array_merge( $autoupdate_themes, $this->themes ) );
+ Jetpack_Options::update_option( 'autoupdate_themes', $autoupdate_themes );
+ }
+
+ function autoupdate_off() {
+ $autoupdate_themes = Jetpack_Options::get_option( 'autoupdate_themes', array() );
+ $autoupdate_themes = array_diff( $autoupdate_themes, $this->themes );
+ Jetpack_Options::update_option( 'autoupdate_themes', $autoupdate_themes );
+ }
+
+ function autoupdate_translations_on() {
+ $autoupdate_themes_translations = Jetpack_Options::get_option( 'autoupdate_themes_translations', array() );
+ $autoupdate_themes_translations = array_unique( array_merge( $autoupdate_themes_translations, $this->themes ) );
+ Jetpack_Options::update_option( 'autoupdate_themes_translations', $autoupdate_themes_translations );
+ }
+
+ function autoupdate_translations_off() {
+ $autoupdate_themes_translations = Jetpack_Options::get_option( 'autoupdate_themes_translations', array() );
+ $autoupdate_themes_translations = array_diff( $autoupdate_themes_translations, $this->themes );
+ Jetpack_Options::update_option( 'autoupdate_themes_translations', $autoupdate_themes_translations );
+ }
+
+ function update() {
+ include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+
+ // Clear the cache.
+ wp_update_themes();
+
+ foreach ( $this->themes as $theme ) {
+ /**
+ * Pre-upgrade action
+ *
+ * @since 3.9.3
+ *
+ * @param object $theme WP_Theme object
+ * @param array $themes Array of theme objects
+ */
+ do_action('jetpack_pre_theme_upgrade', $theme, $this->themes);
+ // Objects created inside the for loop to clean the messages for each theme
+ $skin = new Automatic_Upgrader_Skin();
+ $upgrader = new Theme_Upgrader( $skin );
+ $upgrader->init();
+ $result = $upgrader->upgrade( $theme );
+ $this->log[ $theme ][] = $upgrader->skin->get_upgrade_messages();
+ }
+
+ if ( ! $this->bulk && ! $result ) {
+ return new WP_Error( 'update_fail', __( 'There was an error updating your theme', 'jetpack' ), 400 );
+ }
+
+ return true;
+ }
+
+ function update_translations() {
+ include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+
+ // Clear the cache.
+ wp_update_themes();
+
+ $available_themes_updates = get_site_transient( 'update_themes' );
+
+ if ( ! isset( $available_themes_updates->translations ) || empty( $available_themes_updates->translations ) ) {
+ return new WP_Error( 'nothing_to_translate' );
+ }
+
+ foreach( $available_themes_updates->translations as $translation ) {
+ $theme = $translation['slug'] ;
+ if ( ! in_array( $translation['slug'], $this->themes ) ) {
+ $this->log[ $theme ][] = __( 'No update needed', 'jetpack' );
+ continue;
+ }
+
+ /**
+ * Pre-upgrade action
+ *
+ * @since 4.4.0
+ *
+ * @param object $theme WP_Theme object
+ * @param array $themes Array of theme objects
+ */
+ do_action( 'jetpack_pre_theme_upgrade_translations', $theme, $this->themes );
+ // Objects created inside the for loop to clean the messages for each theme
+ $skin = new Automatic_Upgrader_Skin();
+ $upgrader = new Language_Pack_Upgrader( $skin );
+ $upgrader->init();
+
+ $result = $upgrader->upgrade( (object) $translation );
+ $this->log[ $theme ] = $upgrader->skin->get_upgrade_messages();
+ }
+
+ if ( ! $this->bulk && ! $result ) {
+ return new WP_Error( 'update_fail', __( 'There was an error updating your theme', 'jetpack' ), 400 );
+ }
+
+ return true;
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-new-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-new-endpoint.php
new file mode 100644
index 00000000..75768183
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-themes-new-endpoint.php
@@ -0,0 +1,83 @@
+<?php
+
+include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+include_once ABSPATH . 'wp-admin/includes/file.php';
+
+class Jetpack_JSON_API_Themes_New_Endpoint extends Jetpack_JSON_API_Themes_Endpoint {
+
+ // POST /sites/%s/themes/%s/install
+ protected $needed_capabilities = 'install_themes';
+ protected $action = 'install';
+ protected $download_links = array();
+
+ protected function validate_call( $_blog_id, $capability, $check_manage_active = true ) {
+ $validate = parent::validate_call( $_blog_id, $capability, $check_manage_active );
+ if ( is_wp_error( $validate ) ) {
+ // Lets delete the attachment... if the user doesn't have the right permissions to do things.
+ $args = $this->input();
+ if ( isset( $args['zip'][0]['id'] ) ) {
+ wp_delete_attachment( $args['zip'][0]['id'], true );
+ }
+ }
+
+ return $validate;
+ }
+
+ protected function validate_input( $theme ) {
+ $this->bulk = false;
+ $this->themes = array();
+ }
+
+ function install() {
+ $args = $this->input();
+
+ if ( isset( $args['zip'][0]['id'] ) ) {
+ $attachment_id = $args['zip'][0]['id'];
+ $local_file = get_attached_file( $attachment_id );
+ if ( ! $local_file ) {
+ return new WP_Error( 'local-file-does-not-exist' );
+ }
+ jetpack_require_lib( 'class.jetpack-automatic-install-skin' );
+ $skin = new Jetpack_Automatic_Install_Skin();
+ $upgrader = new Theme_Upgrader( $skin );
+
+ $pre_install_list = wp_get_themes();
+ $result = $upgrader->install( $local_file );
+
+ // clean up.
+ wp_delete_attachment( $attachment_id, true );
+
+ if ( is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ $after_install_list = wp_get_themes();
+ $plugin = array_values( array_diff( array_keys( $after_install_list ), array_keys( $pre_install_list ) ) );
+
+ if ( ! $result ) {
+ $error_code = $upgrader->skin->get_main_error_code();
+ $message = $upgrader->skin->get_main_error_message();
+ if ( empty( $message ) ) {
+ $message = __( 'An unknown error occurred during installation', 'jetpack' );
+ }
+
+ if ( 'download_failed' === $error_code ) {
+ $error_code = 'no_package';
+ }
+
+ return new WP_Error( $error_code, $message, 400 );
+ }
+
+ if ( empty( $plugin ) ) {
+ return new WP_Error( 'theme_already_installed' );
+ }
+
+ $this->themes = $plugin;
+ $this->log[ $plugin[0] ] = $upgrader->skin->get_upgrade_messages();
+
+ return true;
+ }
+
+ return new WP_Error( 'no_theme_installed' );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-translations-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-translations-endpoint.php
new file mode 100644
index 00000000..65b6e725
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-translations-endpoint.php
@@ -0,0 +1,20 @@
+<?php
+
+// Translations
+class Jetpack_JSON_API_Translations_Endpoint extends Jetpack_JSON_API_Endpoint {
+ // GET /sites/%s/translations
+ // POST /sites/%s/translations
+ // POST /sites/%s/translations/update
+ protected $needed_capabilities = array( 'update_core', 'update_plugins', 'update_themes' );
+ protected $log;
+ protected $success;
+
+ public function result() {
+ return array(
+ 'translations' => wp_get_translation_updates(),
+ 'autoupdate' => Jetpack_Options::get_option( 'autoupdate_translations', false ),
+ 'log' => $this->log,
+ 'success' => $this->success,
+ );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-translations-modify-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-translations-modify-endpoint.php
new file mode 100644
index 00000000..fd5f6a56
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-translations-modify-endpoint.php
@@ -0,0 +1,29 @@
+<?php
+
+class Jetpack_JSON_API_Translations_Modify_Endpoint extends Jetpack_JSON_API_Translations_Endpoint {
+ // POST /sites/%s/translations
+ // POST /sites/%s/translations/update
+ protected $action = 'default_action';
+ protected $new_version;
+ protected $log;
+
+ public function default_action() {
+ $args = $this->input();
+
+ if ( isset( $args['autoupdate'] ) && is_bool( $args['autoupdate'] ) ) {
+ Jetpack_Options::update_option( 'autoupdate_translations', $args['autoupdate'] );
+ }
+
+ return true;
+ }
+
+ protected function update() {
+ include_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+
+ $upgrader = new Language_Pack_Upgrader( new Automatic_Upgrader_Skin() );
+ $result = $upgrader->bulk_upgrade();
+
+ $this->log = $upgrader->skin->get_upgrade_messages();
+ $this->success = ( ! is_wp_error( $result ) ) ? (bool) $result : false;
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-updates-status-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-updates-status-endpoint.php
new file mode 100644
index 00000000..48f9ae9d
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-updates-status-endpoint.php
@@ -0,0 +1,34 @@
+<?php
+
+class Jetpack_JSON_API_Updates_Status extends Jetpack_JSON_API_Endpoint {
+ // GET /sites/%s/updates
+ protected $needed_capabilities = 'manage_options';
+
+ protected function result() {
+
+ wp_update_themes();
+ wp_update_plugins();
+
+ $update_data = wp_get_update_data();
+ if ( ! isset( $update_data['counts'] ) ) {
+ return new WP_Error( 'get_update_data_error', __( 'There was an error while getting the update data for this site.', 'jetpack' ), 500 );
+ }
+
+ $result = $update_data['counts'];
+
+ include( ABSPATH . WPINC . '/version.php' ); // $wp_version;
+ $result['wp_version'] = isset( $wp_version ) ? $wp_version : null;
+
+ if ( ! empty( $result['wordpress'] ) ) {
+ $cur = get_preferred_from_update_core();
+ if ( isset( $cur->response ) && $cur->response === 'upgrade' ) {
+ $result['wp_update_version'] = $cur->current;
+ }
+ }
+
+ $result['jp_version'] = JETPACK__VERSION;
+
+ return $result;
+
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-connect-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-connect-endpoint.php
new file mode 100644
index 00000000..b30597de
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-connect-endpoint.php
@@ -0,0 +1,30 @@
+<?php
+
+class Jetpack_JSON_API_User_Connect_Endpoint extends Jetpack_JSON_API_Endpoint {
+
+ protected $needed_capabilities = 'create_users';
+
+ private $user_id;
+ private $user_token;
+
+ function result() {
+ Jetpack::update_user_token( $this->user_id, sprintf( '%s.%d', $this->user_token, $this->user_id ), false );
+ return array( 'success' => Jetpack::is_user_connected( $this->user_id ) );
+ }
+
+ function validate_input( $user_id ) {
+ $input = $this->input();
+ if ( ! isset( $user_id ) ) {
+ return new WP_Error( 'input_error', __( 'user_id is required', 'jetpack' ) );
+ }
+ $this->user_id = $user_id;
+ if ( Jetpack::is_user_connected( $this->user_id ) ) {
+ return new WP_Error( 'user_already_connected', __( 'The user is already connected', 'jetpack' ) );
+ }
+ if ( ! isset( $input['user_token'] ) ) {
+ return new WP_Error( 'input_error', __( 'user_token is required', 'jetpack' ) );
+ }
+ $this->user_token = sanitize_text_field( $input[ 'user_token'] );
+ return parent::validate_input( $user_id );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-create-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-create-endpoint.php
new file mode 100644
index 00000000..bd71249b
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.jetpack-json-api-user-create-endpoint.php
@@ -0,0 +1,72 @@
+<?php
+
+class Jetpack_JSON_API_User_Create_Endpoint extends Jetpack_JSON_API_Endpoint {
+
+ protected $needed_capabilities = 'create_users';
+
+ private $user_data;
+
+ function result() {
+ return $this->create_or_get_user();
+ }
+
+ function validate_input( $object ) {
+ $this->user_data = $this->input();
+
+ if ( empty( $this->user_data ) ) {
+ return new WP_Error( 'input_error', __( 'user_data is required', 'jetpack' ) );
+ }
+ if ( ! isset( $this->user_data[ 'email' ] ) ) {
+ return new WP_Error( 'input_error', __( 'user email is required', 'jetpack' ) );
+ }
+ if ( ! isset( $this->user_data[ 'login' ] ) ) {
+ return new WP_Error( 'input_error', __( 'user login is required', 'jetpack' ) );
+ }
+ return parent::validate_input( $object );
+ }
+
+ function create_or_get_user() {
+ require_once JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php';
+ // Check for an existing user
+ $user = get_user_by( 'email', $this->user_data['email'] );
+ $roles = (array) $this->user_data['roles'];
+ $role = array_pop( $roles );
+
+ $query_args = $this->query_args();
+ if ( isset( $query_args['invite_accepted'] ) && $query_args['invite_accepted'] ) {
+ Jetpack_Constants::set_constant( 'JETPACK_INVITE_ACCEPTED', true );
+ }
+
+ if ( ! $user ) {
+ // We modify the input here to mimick the same call structure of the update user endpoint.
+ $this->user_data = (object) $this->user_data;
+ $this->user_data->role = $role;
+ $this->user_data->url = isset( $this->user_data->URL ) ? $this->user_data->URL : '';
+ $this->user_data->display_name = $this->user_data->name;
+ $this->user_data->description = '';
+ $user = Jetpack_SSO_Helpers::generate_user( $this->user_data );
+ }
+
+ if ( is_multisite() ) {
+ add_user_to_blog( get_current_blog_id(), $user->ID, $role );
+ }
+
+ if ( ! $user ) {
+
+ return false;
+ }
+
+ return $this->get_user( $user->ID );
+ }
+
+ public function get_user( $user_id ) {
+ $the_user = $this->get_author( $user_id, true );
+ if ( $the_user && ! is_wp_error( $the_user ) ) {
+ $userdata = get_userdata( $user_id );
+ $the_user->roles = ! is_wp_error( $userdata ) ? $userdata->roles : array();
+ }
+
+ return $the_user;
+ }
+
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-get-option-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-get-option-endpoint.php
new file mode 100644
index 00000000..3a76256f
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-get-option-endpoint.php
@@ -0,0 +1,41 @@
+<?php
+
+class WPCOM_JSON_API_Get_Option_Endpoint extends Jetpack_JSON_API_Endpoint {
+
+ protected $needed_capabilities = 'manage_options';
+
+ public $option_name;
+ public $site_option;
+
+ function result() {
+ if ( $this->site_option ) {
+ return array( 'option_value' => get_site_option( $this->option_name ) );
+ }
+ return array( 'option_value' => get_option( $this->option_name ) );
+ }
+
+ function validate_input( $object ) {
+ $query_args = $this->query_args();
+ $this->option_name = isset( $query_args['option_name'] ) ? $query_args['option_name'] : false;
+ if ( ! $this->option_name ) {
+ return new WP_Error( 'option_name_not_set', __( 'You must specify an option_name', 'jetpack' ) );
+ }
+ $this->site_option = isset( $query_args['site_option'] ) ? $query_args['site_option'] : false;
+
+ require_once JETPACK__PLUGIN_DIR . '/sync/class.jetpack-sync-defaults.php';
+ /**
+ * Filter the list of options that are manageable via the JSON API.
+ *
+ * @module json-api
+ *
+ * @since 3.8.2
+ *
+ * @param array The default list of site options.
+ * @param bool Is the option a site option.
+ */
+ if ( ! in_array( $this->option_name, apply_filters( 'jetpack_options_whitelist', Jetpack_Sync_Defaults::$default_options_whitelist, $this->site_option ) ) ) {
+ return new WP_Error( 'option_name_not_in_whitelist', __( 'You must specify a whitelisted option_name', 'jetpack' ) );
+ }
+ return true;
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-update-option-endpoint.php b/plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-update-option-endpoint.php
new file mode 100644
index 00000000..20f8895c
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/class.wpcom-json-api-update-option-endpoint.php
@@ -0,0 +1,31 @@
+<?php
+
+class WPCOM_JSON_API_Update_Option_Endpoint extends WPCOM_JSON_API_Get_Option_Endpoint {
+ public $option_value;
+
+ function result() {
+ if ( $this->site_option ) {
+ update_site_option( $this->option_name, $this->option_value );
+ } else {
+ update_option( $this->option_name, $this->option_value );
+ }
+ return parent::result();
+ }
+
+ function validate_input( $object ) {
+ $input = $this->input();
+ $query_args = $this->query_args();
+ if ( ! isset( $input['option_value'] ) || is_array( $input['option_value'] ) ) {
+ return new WP_Error( 'option_value_not_set', __( 'You must specify an option_value', 'jetpack' ) );
+ }
+ if ( $query_args['is_array'] ) {
+ // When converted back from JSON, the value is an object.
+ // Cast it to an array for options that expect arrays.
+ $this->option_value = (array) $input['option_value'];
+ } else {
+ $this->option_value = $input['option_value'];
+ }
+
+ return parent::validate_input( $object );
+ }
+}
diff --git a/plugins/jetpack/json-endpoints/jetpack/json-api-jetpack-endpoints.php b/plugins/jetpack/json-endpoints/jetpack/json-api-jetpack-endpoints.php
new file mode 100644
index 00000000..a7fd2acd
--- /dev/null
+++ b/plugins/jetpack/json-endpoints/jetpack/json-api-jetpack-endpoints.php
@@ -0,0 +1,1234 @@
+<?php
+
+$json_jetpack_endpoints_dir = dirname( __FILE__ ) . '/';
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-endpoint.php' );
+
+// THEMES
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-themes-endpoint.php' );
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-themes-active-endpoint.php' );
+
+new Jetpack_JSON_API_Themes_Active_Endpoint( array(
+ 'description' => 'Get the active theme of your blog',
+ 'stat' => 'themes:mine',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/themes/mine',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => Jetpack_JSON_API_Themes_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/themes/mine'
+) );
+
+new Jetpack_JSON_API_Themes_Active_Endpoint( array(
+ 'description' => 'Change the active theme of your blog',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/themes/mine',
+ 'stat' => 'themes:mine',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'query_parameters' => array(
+ 'context' => false
+ ),
+ 'request_format' => array(
+ 'theme' => '(string) The ID of the theme that should be activated'
+ ),
+ 'response_format' => Jetpack_JSON_API_Themes_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ 'body' => array(
+ 'theme' => 'twentytwelve'
+ )
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/themes/mine'
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-themes-list-endpoint.php' );
+
+new Jetpack_JSON_API_Themes_List_Endpoint( array(
+ 'description' => 'Get WordPress.com Themes allowed on your blog',
+ 'group' => '__do_not_document',
+ 'stat' => 'themes',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/themes',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => array(
+ 'found' => '(int) The total number of themes found.',
+ 'themes' => '(array) An array of theme objects.',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/themes'
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-themes-get-endpoint.php' );
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-themes-new-endpoint.php' );
+
+// POST /sites/%s/themes/%new
+new Jetpack_JSON_API_Themes_New_Endpoint( array(
+ 'description' => 'Install a theme to your jetpack blog',
+ 'group' => '__do_not_document',
+ 'stat' => 'themes:new',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/themes/new',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ ),
+ 'request_format' => array(
+ 'zip' => '(zip) Theme package zip file. multipart/form-data encoded. ',
+ ),
+ 'response_format' => Jetpack_JSON_API_Themes_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/themes/new'
+) );
+
+
+
+new Jetpack_JSON_API_Themes_Get_Endpoint( array(
+ 'description' => 'Get a single theme on a jetpack blog',
+ 'group' => '__do_not_document',
+ 'stat' => 'themes:get:1',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/themes/%s',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$theme' => '(string) The theme slug',
+ ),
+ 'response_format' => Jetpack_JSON_API_Themes_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/themes/twentyfourteen'
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-themes-modify-endpoint.php' );
+new Jetpack_JSON_API_Themes_Modify_Endpoint( array(
+ 'description' => 'Modify a single theme on a jetpack blog',
+ 'group' => '__do_not_document',
+ 'stat' => 'themes:modify:1',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/themes/%s',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$theme' => '(string) The theme slug',
+ ),
+ 'request_format' => array(
+ 'action' => '(string) Only possible value is \'update\'. More to follow.',
+ 'autoupdate' => '(bool) Whether or not to automatically update the theme.',
+ ),
+ 'response_format' => Jetpack_JSON_API_Themes_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ 'body' => array(
+ 'action' => 'update',
+ )
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/themes/twentyfourteen'
+) );
+
+new Jetpack_JSON_API_Themes_Modify_Endpoint( array(
+ 'description' => 'Modify a list of themes on a jetpack blog',
+ 'group' => '__do_not_document',
+ 'stat' => 'themes:modify',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/themes',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ ),
+ 'request_format' => array(
+ 'action' => '(string) Only possible value is \'update\'. More to follow.',
+ 'autoupdate' => '(bool) Whether or not to automatically update the theme.',
+ 'themes' => '(array) A list of theme slugs',
+ ),
+ 'response_format' => array(
+ 'themes' => '(array:theme) A list of theme objects',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ 'body' => array(
+ 'action' => 'autoupdate_on',
+ 'themes' => array(
+ 'twentytwelve',
+ 'twentyfourteen',
+ ),
+ )
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/themes'
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-themes-install-endpoint.php' );
+// POST /sites/%s/themes/%s/install
+new Jetpack_JSON_API_Themes_Install_Endpoint( array(
+ 'description' => 'Install a theme to your jetpack blog',
+ 'group' => '__do_not_document',
+ 'stat' => 'themes:1:install',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/themes/%s/install',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$theme' => '(int|string) The theme slug to install',
+ ),
+ 'response_format' => Jetpack_JSON_API_Themes_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/themes/twentyfourteen/install'
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-themes-delete-endpoint.php' );
+// POST /sites/%s/themes/%s/delete
+new Jetpack_JSON_API_Themes_Delete_Endpoint( array(
+ 'description' => 'Delete/Uninstall a theme from your jetpack blog',
+ 'group' => '__do_not_document',
+ 'stat' => 'themes:1:delete',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/themes/%s/delete',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$theme' => '(string) The slug of the theme to delete',
+ ),
+ 'response_format' => Jetpack_JSON_API_Themes_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/themes/twentyfourteen/delete'
+) );
+
+
+// PLUGINS
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-plugins-endpoint.php' );
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-plugins-get-endpoint.php' );
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-plugins-list-endpoint.php' );
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-plugins-new-endpoint.php' );
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-plugins-install-endpoint.php' );
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-plugins-delete-endpoint.php' );
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-plugins-modify-endpoint.php' );
+
+// PLUGINS V1.2
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-plugins-modify-v1-2-endpoint.php' );
+
+// Jetpack Modules
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-modules-endpoint.php' );
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-modules-get-endpoint.php' );
+
+new Jetpack_JSON_API_Modules_Get_Endpoint( array(
+ 'description' => 'Get the info about a Jetpack Module on your Jetpack Site',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/jetpack/modules/%s/',
+ 'stat' => 'jetpack:modules:1',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$module' => '(string) The module name',
+ ),
+ 'response_format' => Jetpack_JSON_API_Modules_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/jetpack/modules/stats'
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-modules-modify-endpoint.php' );
+
+new Jetpack_JSON_API_Modules_Modify_Endpoint( array(
+ 'description' => 'Modify the status of a Jetpack Module on your Jetpack Site',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/jetpack/modules/%s/',
+ 'stat' => 'jetpack:modules:1',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$module' => '(string) The module name',
+ ),
+ 'request_format' => array(
+ 'active' => '(bool) The module activation status',
+ ),
+ 'response_format' => Jetpack_JSON_API_Modules_Endpoint::$_response_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ 'body' => array(
+ 'active' => true,
+ )
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/jetpack/modules/stats'
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-modules-list-endpoint.php' );
+
+new Jetpack_JSON_API_Modules_List_Endpoint( array(
+ 'description' => 'Get the list of available Jetpack modules on your site',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/jetpack/modules',
+ 'stat' => 'jetpack:modules',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => array(
+ 'found' => '(int) The total number of modules found.',
+ 'modules' => '(array) An array of module objects.',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/jetpack/modules'
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-updates-status-endpoint.php' );
+
+new Jetpack_JSON_API_Updates_Status( array(
+ 'description' => 'Get counts for available updates',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/updates',
+ 'stat' => 'updates',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => array(
+ 'plugins' => '(int) The total number of plugins updates.',
+ 'themes' => '(int) The total number of themes updates.',
+ 'wordpress' => '(int) The total number of core updates.',
+ 'translations' => '(int) The total number of translation updates.',
+ 'total' => '(int) The total number of updates.',
+ 'wp_version' => '(safehtml) The wp_version string.',
+ 'wp_update_version' => '(safehtml) The wp_version to update string.',
+ 'jp_version' => '(safehtml) The site Jetpack version.',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/updates'
+) );
+
+
+// Jetpack Extras
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-check-capabilities-endpoint.php' );
+
+new Jetpack_JSON_API_Check_Capabilities_Endpoint( array(
+ 'description' => 'Check if the current user has a certain capability over a Jetpack site',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/me/capability',
+ 'stat' => 'me:capabulity',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => '(bool) True if the user has the queried capability.',
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ 'body' => array(
+ 'capability' => 'A single capability or an array of capabilities'
+ )
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/me/capability'
+) );
+
+
+// CORE
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-core-endpoint.php' );
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-core-modify-endpoint.php' );
+
+new Jetpack_JSON_API_Core_Endpoint( array(
+ 'description' => 'Gets info about a Jetpack blog\'s core installation',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/core',
+ 'stat' => 'core',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => array(
+ 'version' => '(string) The current version',
+ 'autoupdate' => '(bool) Whether or not we automatically update core'
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/core'
+) );
+
+new Jetpack_JSON_API_Core_Modify_Endpoint( array(
+ 'description' => 'Update WordPress installation on a Jetpack blog',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/core/update',
+ 'stat' => 'core:update',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => array(
+ 'version' => '(string) The core version to update',
+ ),
+ 'response_format' => array(
+ 'version' => '(string) The core version after the upgrade has run.',
+ 'log' => '(array:safehtml) An array of log strings.',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/core/update'
+) );
+
+new Jetpack_JSON_API_Core_Endpoint( array(
+ 'description' => 'Toggle automatic core updates for a Jetpack blog',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/core',
+ 'stat' => 'core',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => array(
+ 'autoupdate' => '(bool) Whether or not we automatically update core',
+ ),
+ 'response_format' => array(
+ 'version' => '(string) The current version',
+ 'autoupdate' => '(bool) Whether or not we automatically update core'
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ 'body' => array(
+ 'autoupdate' => true,
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/core'
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-sync-endpoint.php' );
+
+// POST /sites/%s/sync
+new Jetpack_JSON_API_Sync_Endpoint( array(
+ 'description' => 'Force sync of all options and constants',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/sync',
+ 'stat' => 'sync',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => array(
+ 'modules' => '(string) Comma-delimited set of sync modules to use (default: all of them)',
+ 'posts' => '(string) Comma-delimited list of post IDs to sync',
+ 'comments' => '(string) Comma-delimited list of comment IDs to sync',
+ 'users' => '(string) Comma-delimited list of user IDs to sync',
+ ),
+ 'response_format' => array(
+ 'scheduled' => '(bool) Whether or not the synchronisation was started'
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/sync'
+) );
+
+// GET /sites/%s/sync/status
+new Jetpack_JSON_API_Sync_Status_Endpoint( array(
+ 'description' => 'Status of the current full sync or the previous full sync',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/sync/status',
+ 'stat' => 'sync-status',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => array(
+ 'started' => '(int|null) The unix timestamp when the last sync started',
+ 'queue_finished' => '(int|null) The unix timestamp when the enqueuing was done for the last sync',
+ 'send_started' => '(int|null) The unix timestamp when the last sent process started',
+ 'finished' => '(int|null) The unix timestamp when the last sync finished',
+ 'total' => '(array) Count of actions that could be sent',
+ 'queue' => '(array) Count of actions that have been added to the queue',
+ 'sent' => '(array) Count of actions that have been sent',
+ 'config' => '(array) Configuration of the last full sync',
+ 'queue_size' => '(int) Number of items in the sync queue',
+ 'queue_lag' => '(float) Time delay of the oldest item in the sync queue',
+ 'queue_next_sync' => '(float) Time in seconds before trying to sync again',
+ 'full_queue_size' => '(int) Number of items in the full sync queue',
+ 'full_queue_lag' => '(float) Time delay of the oldest item in the full sync queue',
+ 'full_queue_next_sync' => '(float) Time in seconds before trying to sync the full sync queue again',
+ 'cron_size' => '(int) Size of the current cron array',
+ 'next_cron' => '(int) The number of seconds till the next item in cron.',
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/sync/status'
+) );
+
+
+// GET /sites/%s/data-checksums
+new Jetpack_JSON_API_Sync_Check_Endpoint( array(
+ 'description' => 'Check that cacheable data on the site is in sync with wordpress.com',
+ 'group' => '__do_not_document',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/data-checksums',
+ 'stat' => 'data-checksums',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => array(
+ 'posts' => '(string) Posts checksum',
+ 'comments' => '(string) Comments checksum',
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/data-checksums'
+) );
+
+// GET /sites/%s/data-histogram
+new Jetpack_JSON_API_Sync_Histogram_Endpoint( array(
+ 'description' => 'Get a histogram of checksums for certain synced data',
+ 'group' => '__do_not_document',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/data-histogram',
+ 'stat' => 'data-histogram',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'query_parameters' => array(
+ 'object_type' => '(string=posts) The type of object to checksum - posts, comments or options',
+ 'buckets' => '(int=10) The number of buckets for the checksums',
+ 'start_id' => '(int=0) Starting ID for the range',
+ 'end_id' => '(int=null) Ending ID for the range',
+ 'columns' => '(string) Columns to checksum',
+ 'strip_non_ascii' => '(bool=true) Strip non-ascii characters from all columns',
+ 'shared_salt' => '(string) Salt to reduce the collision and improve validation',
+ ),
+ 'response_format' => array(
+ 'histogram' => '(array) Associative array of histograms by ID range, e.g. "500-999" => "abcd1234"',
+ 'type' => '(string) Type of checksum algorithm',
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/data-histogram'
+) );
+
+$sync_settings_response = array(
+ 'dequeue_max_bytes' => '(int|bool=false) Maximum bytes to read from queue in a single request',
+ 'sync_wait_time' => '(int|bool=false) Wait time between requests in seconds if sync threshold exceeded',
+ 'sync_wait_threshold' => '(int|bool=false) If a request to WPCOM exceeds this duration, wait sync_wait_time seconds before sending again',
+ 'upload_max_bytes' => '(int|bool=false) Maximum bytes to send in a single request',
+ 'upload_max_rows' => '(int|bool=false) Maximum rows to send in a single request',
+ 'max_queue_size' => '(int|bool=false) Maximum queue size that that the queue is allowed to expand to in DB rows to prevent the DB from filling up. Needs to also meet the max_queue_lag limit.',
+ 'max_queue_lag' => '(int|bool=false) Maximum queue lag in seconds used to prevent the DB from filling up. Needs to also meet the max_queue_size limit.',
+ 'queue_max_writes_sec' => '(int|bool=false) Maximum writes per second to allow to the queue during full sync.',
+ 'post_types_blacklist' => '(array|string|bool=false) List of post types to exclude from sync. Send "empty" to unset.',
+ 'post_meta_whitelist' => '(array|string|bool=false) List of post meta to be included in sync. Send "empty" to unset.',
+ 'comment_meta_whitelist' => '(array|string|bool=false) List of comment meta to be included in sync. Send "empty" to unset.',
+ 'disable' => '(int|bool=false) Set to 1 or true to disable sync entirely.',
+ 'render_filtered_content' => '(int|bool=true) Set to 1 or true to render filtered content.',
+ 'max_enqueue_full_sync' => '(int|bool=false) Maximum number of rows to enqueue during each full sync process',
+ 'max_queue_size_full_sync' => '(int|bool=false) Maximum queue size that full sync is allowed to use',
+ 'sync_via_cron' => '(int|bool=false) Set to 1 or true to avoid using cron for sync.',
+ 'cron_sync_time_limit' => '(int|bool=false) Limit cron jobs to number of seconds',
+ 'enqueue_wait_time' => '(int|bool=false) Wait time in seconds between attempting to continue a full sync, via requests',
+);
+
+// GET /sites/%s/sync/settings
+new Jetpack_JSON_API_Sync_Get_Settings_Endpoint( array(
+ 'description' => 'Update sync settings',
+ 'method' => 'GET',
+ 'group' => '__do_not_document',
+ 'path' => '/sites/%s/sync/settings',
+ 'stat' => 'write-sync-settings',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => $sync_settings_response,
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/sync/settings'
+) );
+
+// POST /sites/%s/sync/settings
+new Jetpack_JSON_API_Sync_Modify_Settings_Endpoint( array(
+ 'description' => 'Update sync settings',
+ 'method' => 'POST',
+ 'group' => '__do_not_document',
+ 'path' => '/sites/%s/sync/settings',
+ 'stat' => 'write-sync-settings',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => $sync_settings_response,
+ 'response_format' => $sync_settings_response,
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/sync/settings'
+) );
+
+// GET /sites/%s/sync/object
+new Jetpack_JSON_API_Sync_Object( array(
+ 'description' => 'Get an object by ID from one of the sync modules, in the format it would be synced in',
+ 'group' => '__do_not_document',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/sync/object',
+ 'stat' => 'sync-object',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'query_parameters' => array(
+ 'module_name' => '(string) The sync module ID, e.g. "posts"',
+ 'object_type' => '(string) An identified for the object type, e.g. "post"',
+ 'object_ids' => '(array) The IDs of the objects',
+ ),
+ 'response_format' => array(
+ 'objects' => '(string) The encoded objects',
+ 'codec' => '(string) The codec used to encode the objects, deflate-json-array or simple'
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/sync/object?module_name=posts&object_type=post&object_ids[]=1&object_ids[]=2&object_ids[]=3'
+) );
+
+// POST /sites/%s/sync/now
+new Jetpack_JSON_API_Sync_Now_Endpoint( array(
+ 'description' => 'Force immediate sync of top items on a queue',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/sync/now',
+ 'stat' => 'sync-now',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => array(
+ 'queue' => '(string) sync or full_sync',
+ ),
+ 'response_format' => array(
+ 'response' => '(array) The response from the server'
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/sync/now?queue=full_sync'
+) );
+
+
+// POST /sites/%s/sync/unlock
+new Jetpack_JSON_API_Sync_Unlock_Endpoint( array(
+ 'description' => 'Unlock the queue in case it gets locked by a process.',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/sync/unlock',
+ 'group' => '__do_not_document',
+ 'stat' => 'sync-unlock',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => array(
+ 'queue' => '(string) sync or full_sync',
+ ),
+ 'response_format' => array(
+ 'success' => '(bool) Unlocking the queue successful?'
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/sync/unlock'
+) );
+
+// POST /sites/%s/sync/checkout
+new Jetpack_JSON_API_Sync_Checkout_Endpoint( array(
+ 'description' => 'Locks the queue and returns items and the buffer ID.',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/sync/checkout',
+ 'group' => '__do_not_document',
+ 'stat' => 'sync-checkout',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => array(
+ 'queue' => '(string) sync or full_sync',
+ 'number_of_items' => '(int=10) Maximum number of items from the queue to be returned',
+ 'encode' => '(bool=true) Use the default encode method',
+ 'force' => '(bool=false) Force unlock the queue',
+ ),
+ 'response_format' => array(
+ 'buffer_id' => '(string) Buffer ID that we are using',
+ 'items' => '(array) Items from the queue that are ready to be processed by the sync server',
+ 'skipped_items' => '(array) Skipped item ids',
+ 'codec' => '(string) The name of the codec used to encode the data',
+ 'sent_timestamp' => '(int) Current timestamp of the server',
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/sync/checkout'
+) );
+
+// POST /sites/%s/sync/close
+new Jetpack_JSON_API_Sync_Close_Endpoint( array(
+ 'description' => 'Closes the buffer and delete the processed items from the queue.',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/sync/close',
+ 'group' => '__do_not_document',
+ 'stat' => 'sync-close',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => array(
+ 'item_ids' => '(array) Item IDs to delete from the queue.',
+ 'queue' => '(string) sync or full_sync',
+ 'buffer_id' => '(string) buffer ID that was opened during the checkout step.',
+ ),
+ 'response_format' => array(
+ 'success' => '(bool) Closed the buffer successfully?'
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/sync/close'
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-log-endpoint.php' );
+
+new Jetpack_JSON_API_Jetpack_Log_Endpoint( array(
+ 'description' => 'Get the Jetpack log',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/jetpack-log',
+ 'stat' => 'log',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => array(
+ 'event' => '(string) The event to filter by, by default all entries are returned',
+ 'num' => '(int) The number of entries to get, by default all entries are returned'
+ ),
+ 'response_format' => array(
+ 'log' => '(array) An array of jetpack log entries'
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/jetpack-log'
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-maybe-auto-update-endpoint.php' );
+
+new Jetpack_JSON_API_Maybe_Auto_Update_Endpoint( array(
+ 'description' => 'Maybe Auto Update Core, Plugins, Themes and Languages',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/maybe-auto-update',
+ 'stat' => 'maybe-auto-update',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => array(
+ 'log' => '(array) Results of running the update job'
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/maybe-auto-update'
+
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-translations-endpoint.php' );
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-translations-modify-endpoint.php' );
+
+new Jetpack_JSON_API_Translations_Endpoint( array(
+ 'description' => 'Gets info about a Jetpack blog\'s core installation',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/translations',
+ 'stat' => 'translations',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => array(
+ 'translations' => '(array) A list of translations that are available',
+ 'autoupdate' => '(bool) Whether or not we automatically update translations',
+ 'log' => '(array:safehtml) An array of log strings.',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/translations'
+) );
+
+new Jetpack_JSON_API_Translations_Modify_Endpoint( array(
+ 'description' => 'Toggle automatic core updates for a Jetpack blog',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/translations',
+ 'stat' => 'translations',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => array(
+ 'autoupdate' => '(bool) Whether or not we automatically update translations',
+ ),
+ 'response_format' => array(
+ 'translations' => '(array) A list of translations that are available',
+ 'autoupdate' => '(bool) Whether or not we automatically update translations',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ 'body' => array(
+ 'autoupdate' => true,
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/translations'
+) );
+
+new Jetpack_JSON_API_Translations_Modify_Endpoint( array(
+ 'description' => 'Update All Translations installation on a Jetpack blog',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/translations/update',
+ 'stat' => 'translations:update',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => array(
+ 'log' => '(array:safehtml) An array of log strings.',
+ 'success' => '(bool) Was the operation successful'
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/translations/update'
+) );
+
+// Options
+require_once( $json_jetpack_endpoints_dir . 'class.wpcom-json-api-get-option-endpoint.php' );
+
+new WPCOM_JSON_API_Get_Option_Endpoint( array (
+ 'method' => 'GET',
+ 'description' => 'Fetches an option.',
+ 'group' => '__do_not_document',
+ 'stat' => 'option',
+ 'path' => '/sites/%s/option',
+ 'path_labels' => array(
+ '$site' => '(int|string) Site ID or domain',
+ ),
+ 'query_parameters' => array(
+ 'option_name' => '(string) The name of the option to fetch.',
+ 'site_option' => '(bool=false) True if the option is a site option.',
+ ),
+ 'response_format' => array(
+ 'option_value' => '(string|object) The value of the option.',
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/82974409/option?option_name=blogname',
+ 'example_request_data' => array(
+ 'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ),
+ ),
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.wpcom-json-api-update-option-endpoint.php' );
+
+new WPCOM_JSON_API_Update_Option_Endpoint( array (
+ 'method' => 'POST',
+ 'description' => 'Updates an option.',
+ 'group' => '__do_not_document',
+ 'stat' => 'option:update',
+ 'path' => '/sites/%s/option',
+ 'path_labels' => array(
+ '$site' => '(int|string) Site ID or domain',
+ ),
+ 'query_parameters' => array(
+ 'option_name' => '(string) The name of the option to fetch.',
+ 'site_option' => '(bool=false) True if the option is a site option.',
+ 'is_array' => '(bool=false) True if the value should be converted to an array before saving.',
+ ),
+ 'request_format' => array(
+ 'option_value' => '(string|object) The new value of the option.',
+ ),
+ 'response_format' => array(
+ 'option_value' => '(string|object) The value of the updated option.',
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/82974409/option',
+ 'example_request_data' => array(
+ 'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ),
+ 'body' => array(
+ 'option_value' => 'My new blog name'
+ ),
+ ),
+) );
+
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-cron-endpoint.php' );
+
+// GET /sites/%s/cron
+new Jetpack_JSON_API_Cron_Endpoint( array(
+ 'description' => 'Fetches the cron array',
+ 'group' => '__do_not_document',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/cron',
+ 'stat' => 'cron-get',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'response_format' => array(
+ 'cron_array' => '(array) The cron array',
+ 'current_timestamp' => '(int) Current server timestamp'
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/cron',
+ 'example_request_data' => array(
+ 'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ),
+ ),
+) );
+
+// POST /sites/%s/cron
+new Jetpack_JSON_API_Cron_Post_Endpoint( array(
+ 'description' => 'Process items in the cron',
+ 'group' => '__do_not_document',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/cron',
+ 'stat' => 'cron-run',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => array(
+ 'hooks' => '(array) List of hooks to run if they have been scheduled (optional)',
+ ),
+ 'response_format' => array(
+ 'success' => '(array) Of processed hooks with their arguments'
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/cron',
+ 'example_request_data' => array(
+ 'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ),
+ 'body' => array(
+ 'hooks' => array( 'jetpack_sync_cron' )
+ ),
+ ),
+) );
+
+// POST /sites/%s/cron/schedule
+new Jetpack_JSON_API_Cron_Schedule_Endpoint( array(
+ 'description' => 'Schedule one or a recurring hook to fire at a particular time',
+ 'group' => '__do_not_document',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/cron/schedule',
+ 'stat' => 'cron-schedule',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => array(
+ 'hook' => '(string) Hook name that should run when the event is scheduled',
+ 'timestamp' => '(int) Timestamp when the event should take place, has to be in the future',
+ 'arguments' => '(string) JSON Object of arguments that the hook will use (optional)',
+ 'recurrence' => '(string) How often the event should take place. If empty only one event will be scheduled. Possible values 1min, hourly, twicedaily, daily (optional) '
+ ),
+ 'response_format' => array(
+ 'success' => '(bool) Was the event scheduled?'
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/cron/schedule',
+ 'example_request_data' => array(
+ 'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ),
+ 'body' => array(
+ 'hook' => 'jetpack_sync_cron',
+ 'arguments' => '[]',
+ 'recurrence'=> '1min',
+ 'timestamp' => 1476385523
+ ),
+ ),
+) );
+
+// POST /sites/%s/cron/unschedule
+new Jetpack_JSON_API_Cron_Unschedule_Endpoint( array(
+ 'description' => 'Unschedule one or all events with a particular hook and arguments',
+ 'group' => '__do_not_document',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/cron/unschedule',
+ 'stat' => 'cron-unschedule',
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain'
+ ),
+ 'request_format' => array(
+ 'hook' => '(string) Name of the hook that should be unscheduled',
+ 'timestamp' => '(int) Timestamp of the hook that you want to unschedule. This will unschedule only 1 event. (optional)',
+ 'arguments' => '(string) JSON Object of arguments that the hook has been scheduled with (optional)',
+ ),
+ 'response_format' => array(
+ 'success' => '(bool) Was the event unscheduled?'
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1.1/sites/example.wordpress.org/cron/unschedule',
+ 'example_request_data' => array(
+ 'headers' => array( 'authorization' => 'Bearer YOUR_API_TOKEN' ),
+ 'body' => array(
+ 'hook' => 'jetpack_sync_cron',
+ 'arguments' => '[]',
+ 'timestamp' => 1476385523
+ ),
+ ),
+) );
+
+// BACKUPS
+
+// GET /sites/%s/database-object/backup
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-get-database-object-backup-endpoint.php' );
+new Jetpack_JSON_API_Get_Database_Object_Backup_Endpoint( array(
+ 'description' => 'Fetch a backup of a database object, along with all of its metadata',
+ 'group' => '__do_not_document',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/database-object/backup',
+ 'stat' => 'database-objects:1:backup',
+ 'allow_jetpack_site_auth' => true,
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ ),
+ 'query_parameters' => array(
+ 'object_type' => '(string) Type of object to fetch from the database',
+ 'object_id' => '(int) ID of the database object to fetch',
+ ),
+ 'response_format' => array(
+ 'object' => '(array) Database object row',
+ 'meta' => '(array) Associative array of key/value metadata associated with the row',
+ 'children' => '(array) Where appropriate, child records associated with the object. eg: Woocommerce tax rate locations',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/database-object/backup'
+) );
+
+// GET /sites/%s/comments/%d/backup
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-get-comment-backup-endpoint.php' );
+new Jetpack_JSON_API_Get_Comment_Backup_Endpoint( array(
+ 'description' => 'Fetch a backup of a comment, along with all of its metadata',
+ 'group' => '__do_not_document',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/comments/%d/backup',
+ 'stat' => 'comments:1:backup',
+ 'allow_jetpack_site_auth' => true,
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$post' => '(int) The comment ID',
+ ),
+ 'response_format' => array(
+ 'comment' => '(array) Comment table row',
+ 'meta' => '(array) Associative array of key/value commentmeta data',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/comments/1/backup'
+) );
+
+// GET /sites/%s/options/backup
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-get-option-backup-endpoint.php' );
+new Jetpack_JSON_API_Get_Option_Backup_Endpoint( array(
+ 'description' => 'Fetch a backup of an option',
+ 'group' => '__do_not_document',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/options/backup',
+ 'stat' => 'options:backup',
+ 'allow_jetpack_site_auth' => true,
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ ),
+ 'query_parameters' => array(
+ 'name' => '(string|array) One or more option names to include in the backup',
+ ),
+ 'response_format' => array(
+ 'options' => '(array) Associative array of option_name => option_value entries',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ )
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/options/backup'
+) );
+
+// GET /sites/%s/posts/%d/backup
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-get-post-backup-endpoint.php' );
+new Jetpack_JSON_API_Get_Post_Backup_Endpoint( array(
+ 'description' => 'Fetch a backup of a post, along with all of its metadata',
+ 'group' => '__do_not_document',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/posts/%d/backup',
+ 'stat' => 'posts:1:backup',
+ 'allow_jetpack_site_auth' => true,
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$post' => '(int) The post ID',
+ ),
+ 'response_format' => array(
+ 'post' => '(array) Post table row',
+ 'meta' => '(array) Associative array of key/value postmeta data',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/posts/1/backup'
+) );
+
+// GET /sites/%s/terms/%d/backup
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-get-term-backup-endpoint.php' );
+new Jetpack_JSON_API_Get_Term_Backup_Endpoint( array(
+ 'description' => 'Fetch a backup of a term, along with all of its metadata',
+ 'group' => '__do_not_document',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/terms/%d/backup',
+ 'stat' => 'terms:1:backup',
+ 'allow_jetpack_site_auth' => true,
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$term' => '(int) The term ID',
+ ),
+ 'response_format' => array(
+ 'term' => '(array) Term table row',
+ 'meta' => '(array) Metadata associated with the term',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/terms/1/backup'
+) );
+
+// GET /sites/%s/users/%d/backup
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-get-user-backup-endpoint.php' );
+new Jetpack_JSON_API_Get_User_Backup_Endpoint( array(
+ 'description' => 'Fetch a backup of a user, along with all of its metadata',
+ 'group' => '__do_not_document',
+ 'method' => 'GET',
+ 'path' => '/sites/%s/users/%d/backup',
+ 'stat' => 'users:1:backup',
+ 'allow_jetpack_site_auth' => true,
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$user' => '(int) The user ID',
+ ),
+ 'response_format' => array(
+ 'user' => '(array) User table row',
+ 'meta' => '(array) Associative array of key/value usermeta data',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ ),
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/users/1/backup'
+) );
+
+// USERS
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-user-connect-endpoint.php' );
+
+// POST /sites/%s/users/%d/connect
+new Jetpack_JSON_API_User_Connect_Endpoint( array(
+ 'description' => 'Creates or returns a new user given profile data',
+ 'group' => '__do_not_document',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/users/%d/connect',
+ 'stat' => 'users:connect',
+ 'allow_jetpack_site_auth' => true,
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ '$user_id' => '(int) The site user ID to connect',
+ ),
+ 'request_format' => array(
+ 'user_token' => '(string) The user token',
+ ),
+ 'response_format' => array(
+ 'success' => '(bool) Was the user connected',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN',
+ ),
+ 'body' => array(
+ 'user_token' => 'XDH55jndskjf3klh3',
+ )
+ ),
+ 'example_response' => '{
+ "success" => true
+ }',
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/users/6/connect'
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-user-create-endpoint.php' );
+
+// POST /sites/%s/users/create
+new Jetpack_JSON_API_User_Create_Endpoint( array(
+ 'description' => 'Creates or returns a new user given profile data',
+ 'group' => '__do_not_document',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/users/create',
+ 'stat' => 'users:create',
+ 'allow_jetpack_site_auth' => true,
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ ),
+ 'query_parameters' => array(
+ 'invite_accepted' => '(bool=false) If the user is being created in the invite context',
+ ),
+ 'request_format' => WPCOM_JSON_API_Site_User_Endpoint::$user_format,
+ 'response_format' => WPCOM_JSON_API_Site_User_Endpoint::$user_format,
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN'
+ ),
+ 'body' => array(
+ 'roles' => array(
+ array(
+ 'administrator',
+ )
+ ),
+ 'first_name' => 'John',
+ 'last_name' => 'Doe',
+ 'email' => 'john.doe@example.wordpress.org',
+ )
+ ),
+ 'example_response' => '{
+ "ID": 18342963,
+ "login": "binarysmash"
+ "email": false,
+ "name": "binarysmash",
+ "URL": "http:\/\/binarysmash.wordpress.com",
+ "avatar_URL": "http:\/\/0.gravatar.com\/avatar\/a178ebb1731d432338e6bb0158720fcc?s=96&d=identicon&r=G",
+ "profile_URL": "http:\/\/en.gravatar.com\/binarysmash",
+ "roles": [ "administrator" ]
+ }',
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/users/create'
+
+) );
+
+require_once( $json_jetpack_endpoints_dir . 'class.jetpack-json-api-jps-woocommerce-connect-endpoint.php' );
+
+// POST /sites/%s/jps/woo-connect
+new Jetpack_JSON_API_JPS_WooCommerce_Connect_Endpoint( array(
+ 'description' => 'Attempts to connect the WooCommerce plugin for this site to WooCommerce.com.',
+ 'group' => '__do_not_document',
+ 'method' => 'POST',
+ 'path' => '/sites/%s/jps/woo-connect',
+ 'stat' => 'jps:woo-connect',
+ 'allow_jetpack_site_auth' => true,
+ 'path_labels' => array(
+ '$site' => '(int|string) The site ID, The site domain',
+ ),
+ 'request_format' => array(
+ 'access_token' => '(string) The access token for WooCommerce to connect to WooCommerce.com',
+ 'access_token_secret' => '(string) The access token secret for WooCommerce to connect to WooCommerce.com',
+ 'user_id' => '(int) The user\'s ID after registering for a host plan',
+ 'site_id' => '(int) The site\'s ID after registering for a host plan',
+ ),
+ 'response_format' => array(
+ 'success' => '(bool) Setting access token and access token secret successful?',
+ ),
+ 'example_request_data' => array(
+ 'headers' => array(
+ 'authorization' => 'Bearer YOUR_API_TOKEN',
+ ),
+ 'body' => array(
+ 'access_token' => '123456789',
+ 'access_token_secret' => 'abcdefghiklmnop',
+ 'user_id' => 1,
+ 'site_id' => 2,
+ ),
+ ),
+ 'example_response' => '{ "success": true }',
+ 'example_request' => 'https://public-api.wordpress.com/rest/v1/sites/example.wordpress.org/jps/woo-connect'
+) );