summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/jetpack/jetpack_vendor/automattic')
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/CHANGELOG.md101
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/src/class-a8c-mc-stats.php183
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/CHANGELOG.md251
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/src/class-abtest.php101
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/CHANGELOG.md34
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/class-admin-menu.php174
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/css/jetpack-icon.css14
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.eotbin0 -> 1654 bytes
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.svg33
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.ttfbin0 -> 1480 bytes
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.woffbin0 -> 1056 bytes
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/CHANGELOG.md200
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/actions.php19
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/src/class-assets.php704
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/src/class-semver.php122
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/CHANGELOG.md114
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/actions.php32
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/class-helper-script-manager.php347
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/class-package-version.php31
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/class-rest-controller.php526
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/CHANGELOG.md110
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/src/class-blocks.php291
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/CHANGELOG.md119
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/functions.php32
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-client.php89
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-sync-actions.php362
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-sync-modules.php28
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-sync-settings.php229
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpacktracking.php47
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/lib/tracks/client.php41
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-config/CHANGELOG.md113
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-config/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-config/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-config/src/class-config.php293
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/CHANGELOG.md186
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/babel.config.js10
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/images/disconnect-confirm-dc9fe8f5c68cfd1320e0.jpgbin0 -> 25037 bytes
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/images/disconnect-thanks-5873bfac56a9bd7322cd.jpgbin0 -> 55604 bytes
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.asset.php1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.css1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.js4
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.js.LICENSE.txt28
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.rtl.css1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/src/class-admin.php107
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/src/class-initial-state.php53
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/CHANGELOG.md571
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php157
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php74
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-signature.php404
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-xmlrpc-server.php866
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-client.php488
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-error-handler.php690
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-initial-state.php49
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-manager.php2484
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-nonce-handler.php213
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-package-version-tracker.php112
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-package-version.php30
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-plugin-storage.php269
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-plugin.php118
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-rest-authentication.php220
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-rest-connector.php860
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-secrets.php281
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-server-sandbox.php132
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-tokens.php595
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-urls.php187
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-utils.php87
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-webhooks.php127
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-xmlrpc-async-call.php105
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-xmlrpc-connector.php83
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/interface-manager.php17
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/CHANGELOG.md144
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/src/class-constants.php124
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/CHANGELOG.md116
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/src/class-device-detection.php211
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/src/class-user-agent-info.php1579
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-error/CHANGELOG.md112
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-error/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-error/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-error/src/class-error.php15
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/CHANGELOG.md123
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/src/class-heartbeat.php254
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/CHANGELOG.md144
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/babel.config.js10
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.asset.php1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.css1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.js8
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.js.LICENSE.txt5
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.rtl.css1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/_inc/admin-bar.scss28
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/_inc/admin.jsx58
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/_inc/style.scss4
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/class-identity-crisis.php1219
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/class-rest-endpoints.php188
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/class-ui.php151
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/CHANGELOG.md431
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.asset.php1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.css1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.js1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.rtl.css1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-jitm.php312
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-post-connection-jitm.php607
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-pre-connection-jitm.php171
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-rest-api-endpoints.php92
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/js/jetpack-jitm.js267
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/CHANGELOG.md189
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/intersection-observer.asset.php1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/intersection-observer.js1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/intersection-observer.src.js1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/lazy-images.asset.php1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/lazy-images.js1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/src/images/1x1.trans.gifbin0 -> 42 bytes
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/src/js/lazy-images.js219
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/src/lazy-images.php516
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/webpack.config.js43
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/CHANGELOG.md184
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/src/class-licensing.php272
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/CHANGELOG.md133
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/src/class-logo.php91
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/.babelrc9
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/CHANGELOG.md50
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/_inc/admin.jsx25
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/_inc/components/my-jetpack-screen/index.jsx63
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/_inc/components/my-jetpack-screen/style.scss1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/babel.config.js10
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/images/disconnect-confirm-dc9fe8f5c68cfd1320e0.jpgbin0 -> 25037 bytes
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/images/disconnect-thanks-5873bfac56a9bd7322cd.jpgbin0 -> 55604 bytes
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.asset.php1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.css1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.js2
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.js.LICENSE.txt5
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.rtl.css1
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-initializer.php101
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/webpack.config.js60
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-options/CHANGELOG.md178
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-options/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-options/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-options/legacy/class-jetpack-options.php689
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/CHANGELOG.md130
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/src/class-partner-coupon.php324
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/src/class-partner.php199
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/CHANGELOG.md63
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/src/class-password-checker.php1313
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/CHANGELOG.md143
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/src/class-redirect.php79
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/CHANGELOG.md130
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/src/class-roles.php81
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/CHANGELOG.md51
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-helper.php908
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-module-control.php217
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-options.php88
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-plan.php140
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-rest-controller.php250
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-settings.php59
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-template-tags.php335
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/button/index.jsx52
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/card/compact.jsx23
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/card/index.jsx141
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/form-toggle/compact.jsx26
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/form-toggle/index.jsx126
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/index.jsx55
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/store/actions.js61
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/store/reducer.js22
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/store/selectors.js5
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/notice/index.jsx136
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/notice/notice-action.jsx48
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/actions/index.js14
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/actions/jetpack-settings.js73
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/actions/site-plan.js16
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/controls.js55
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/index.js18
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/index.js23
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/jetpack-settings.js22
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/site-data.js5
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/site-plan.js18
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/user-data.js5
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/resolvers.js48
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/index.js18
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/jetpack-settings.js10
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/site-data.js12
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/site-plan.js12
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/user-data.js5
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/wpes/class-query-builder.php430
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/wpes/class-query-parser.php703
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-status/CHANGELOG.md177
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-status/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-status/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-status/src/class-host.php40
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-status/src/class-status.php288
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/CHANGELOG.md641
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-actions.php942
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-defaults.php1272
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-functions.php631
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-health.php190
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-json-deflate-array-codec.php93
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-listener.php487
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-lock.php77
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-main.php103
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-modules.php160
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-package-version.php30
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-queue-buffer.php78
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-queue.php744
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-replicastore.php1457
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-rest-endpoints.php804
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-rest-sender.php144
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-sender.php916
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-server.php195
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-settings.php568
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-simple-codec.php63
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-users.php152
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-utils.php65
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/interface-codec.php44
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/interface-replicastore.php566
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-attachments.php98
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-callables.php635
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-comments.php495
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-constants.php339
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php467
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync.php730
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-import.php220
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-menus.php146
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-meta.php112
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-module.php604
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-network-options.php252
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-options.php481
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-plugins.php420
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-posts.php771
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-protect.php53
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-stats.php68
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-term-relationships.php244
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-terms.php314
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-themes.php877
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-updates.php585
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-users.php871
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-woocommerce.php613
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-wp-super-cache.php156
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/replicastore/class-table-checksum-usermeta.php208
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/replicastore/class-table-checksum-users.php184
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/replicastore/class-table-checksum.php826
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/CHANGELOG.md223
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/src/class-terms-of-service.php112
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/CHANGELOG.md253
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/LICENSE.txt357
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/SECURITY.md38
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-client.php230
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-event.php189
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/src/class-tracking.php325
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/src/js/tracks-ajax.js62
-rw-r--r--plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/src/js/tracks-callables.js79
298 files changed, 65289 insertions, 0 deletions
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/CHANGELOG.md
new file mode 100644
index 00000000..15ab7fa5
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/CHANGELOG.md
@@ -0,0 +1,101 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.4.11] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+
+## [1.4.10] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.4.9] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.4.8] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.4.7] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.4.6] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.4.5] - 2021-08-30
+### Changed
+- Run composer update on test-php command instead of phpunit
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- updated annotations versions
+
+## [1.4.4] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.4.3] - 2021-04-08
+### Changed
+- Packaging and build changes, no change to the package itself.
+
+## [1.4.2] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.4.1] - 2021-02-05
+
+- CI: Make tests more generic
+
+## [1.4.0] - 2021-01-20
+
+- Add mirror-repo information to all current composer packages
+
+## [1.3.0] - 2020-12-17
+
+- Coverage Update whitelist for backend tests
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.2.0] - 2020-09-17
+
+## [1.1.1] - 2020-09-17
+
+- a8c-mc-stats: Do not distribute test files
+
+## [1.1.0] - 2020-08-13
+
+- CI: Try collect js coverage
+
+## 1.0.0 - 2020-07-27
+
+- Creates the MC Stats package
+
+[1.4.11]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.10...v1.4.11
+[1.4.10]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.9...v1.4.10
+[1.4.9]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.8...v1.4.9
+[1.4.8]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.7...v1.4.8
+[1.4.7]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.6...v1.4.7
+[1.4.6]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.5...v1.4.6
+[1.4.5]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.4...v1.4.5
+[1.4.4]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.3...v1.4.4
+[1.4.3]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.2...v1.4.3
+[1.4.2]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.1...v1.4.2
+[1.4.1]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.1.1...v1.2.0
+[1.1.1]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-a8c-mc-stats/compare/v1.0.0...v1.1.0
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/src/class-a8c-mc-stats.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/src/class-a8c-mc-stats.php
new file mode 100644
index 00000000..88635fbb
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-a8c-mc-stats/src/class-a8c-mc-stats.php
@@ -0,0 +1,183 @@
+<?php
+/**
+ * Jetpack MC Stats package.
+ *
+ * @package automattic/jetpack-mc-stats
+ */
+
+namespace Automattic\Jetpack;
+
+/**
+ * Class MC Stats, used to record stats using https://pixel.wp.com/g.gif
+ */
+class A8c_Mc_Stats {
+
+ /**
+ * Holds the stats to be processed
+ *
+ * @var array
+ */
+ private $stats = array();
+
+ /**
+ * Indicates whether to use the transparent pixel (b.gif) instead of the regular smiley (g.gif)
+ *
+ * @var boolean
+ */
+ public $use_transparent_pixel = true;
+
+ /**
+ * Class Constructor
+ *
+ * @param boolean $use_transparent_pixel Use the transparent pixel instead of the smiley.
+ */
+ public function __construct( $use_transparent_pixel = true ) {
+ $this->use_transparent_pixel = $use_transparent_pixel;
+ }
+
+ /**
+ * Store a stat for later output.
+ *
+ * @param string $group The stat group.
+ * @param string $name The stat name to bump.
+ *
+ * @return boolean true if stat successfully added
+ */
+ public function add( $group, $name ) {
+
+ if ( ! \is_string( $group ) || ! \is_string( $name ) ) {
+ return false;
+ }
+
+ if ( ! isset( $this->stats[ $group ] ) ) {
+ $this->stats[ $group ] = array();
+ }
+
+ if ( \in_array( $name, $this->stats[ $group ], true ) ) {
+ return false;
+ }
+
+ $this->stats[ $group ][] = $name;
+
+ return true;
+ }
+
+ /**
+ * Gets current stats stored to be processed
+ *
+ * @return array $stats
+ */
+ public function get_current_stats() {
+ return $this->stats;
+ }
+
+ /**
+ * Return the stats from a group in an array ready to be added as parameters in a query string
+ *
+ * @param string $group_name The name of the group to retrieve.
+ * @return array Array with one item, where the key is the prefixed group and the value are all stats concatenated with a comma. If group not found, an empty array will be returned
+ */
+ public function get_group_query_args( $group_name ) {
+ $stats = $this->get_current_stats();
+ if ( isset( $stats[ $group_name ] ) && ! empty( $stats[ $group_name ] ) ) {
+ return array( "x_jetpack-{$group_name}" => implode( ',', $stats[ $group_name ] ) );
+ }
+ return array();
+ }
+
+ /**
+ * Gets a list of trac URLs for every stored URL
+ *
+ * @return array An array of URLs
+ */
+ public function get_stats_urls() {
+
+ $urls = array();
+
+ foreach ( $this->get_current_stats() as $group => $stat ) {
+ $group_query_string = $this->get_group_query_args( $group );
+ $urls[] = $this->build_stats_url( $group_query_string );
+ }
+
+ return $urls;
+
+ }
+
+ /**
+ * Outputs the tracking pixels for the current stats and empty the stored stats from the object
+ *
+ * @return void
+ */
+ public function do_stats() {
+ $urls = $this->get_stats_urls();
+ foreach ( $urls as $url ) {
+ echo '<img src="' . esc_url( $url ) . '" width="1" height="1" style="display:none;" />';
+ }
+ $this->stats = array();
+ }
+
+ /**
+ * Pings the stats server for the current stats and empty the stored stats from the object
+ *
+ * @return void
+ */
+ public function do_server_side_stats() {
+ $urls = $this->get_stats_urls();
+ foreach ( $urls as $url ) {
+ $this->do_server_side_stat( $url );
+ }
+ $this->stats = array();
+ }
+
+ /**
+ * Runs stats code for a one-off, server-side.
+ *
+ * @param string $url string The URL to be pinged. Should include `x_jetpack-{$group}={$stats}` or whatever we want to store.
+ *
+ * @return bool If it worked.
+ */
+ public function do_server_side_stat( $url ) {
+ $response = wp_remote_get( esc_url_raw( $url ) );
+ if ( is_wp_error( $response ) ) {
+ return false;
+ }
+
+ if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Builds the stats url.
+ *
+ * @param array $args array|string The arguments to append to the URL.
+ *
+ * @return string The URL to be pinged.
+ */
+ public function build_stats_url( $args ) {
+ $defaults = array(
+ 'v' => 'wpcom2',
+ 'rand' => md5( wp_rand( 0, 999 ) . time() ),
+ );
+ $args = wp_parse_args( $args, $defaults );
+ $gifname = true === $this->use_transparent_pixel ? 'b.gif' : 'g.gif';
+
+ /**
+ * Filter the URL used as the Stats tracking pixel.
+ *
+ * @since-jetpack 2.3.2
+ * @since 1.0.0
+ *
+ * @param string $url Base URL used as the Stats tracking pixel.
+ */
+ $base_url = apply_filters(
+ 'jetpack_stats_base_url',
+ 'https://pixel.wp.com/' . $gifname
+ );
+ $url = add_query_arg( $args, $base_url );
+ return $url;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/CHANGELOG.md
new file mode 100644
index 00000000..cdd96d24
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/CHANGELOG.md
@@ -0,0 +1,251 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.9.18] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+
+## [1.9.17] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.9.16] - 2021-11-30
+### Changed
+- Updated package dependencies.
+
+## [1.9.15] - 2021-11-23
+### Changed
+- Updated package dependencies.
+
+## [1.9.14] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.9.13] - 2021-10-19
+### Changed
+- Updated package dependencies.
+
+## [1.9.12] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.9.11] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.9.10] - 2021-08-31
+### Changed
+- Run composer update on test-php command instead of phpunit
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+
+## [1.9.9] - 2021-07-27
+### Changed
+- Updated package dependencies.
+
+## [1.9.8] - 2021-06-29
+### Changed
+- Updated package dependencies.
+
+## [1.9.7] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.9.6] - 2021-04-27
+### Changed
+- Updated package dependencies.
+
+## [1.9.5] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.9.4] - 2021-02-23
+
+- CI: Make tests more generic
+
+## [1.9.3] - 2021-02-08
+
+- Update dependencies to latest stable
+
+## [1.9.2] - 2021-01-28
+
+- Update dependencies to latest stable
+
+## [1.9.1] - 2021-01-26
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.9.0] - 2021-01-05
+
+- Coverage Update whitelist for backend tests
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+- Tests: Try CodeCov coverage app
+- Add .gitignore to export-ignore for dist releases
+
+## [1.8.4] - 2020-11-24
+
+- Version packages for release
+
+## [1.8.3] - 2020-11-24
+
+- Fix remaining phpcs warnings in most of requirelist
+- Updated PHPCS: Packages and Debugger
+
+## [1.8.2] - 2020-11-05
+
+- Update dependencies to latest stable
+
+## [1.8.1] - 2020-10-29
+
+- Update dependencies to latest stable
+
+## [1.8.0] - 2020-10-27
+
+- Updated dependencies to latest stable
+
+## [1.7.4] - 2020-10-14
+
+- Update dependencies to latest stable
+
+## [1.7.3] - 2020-10-09
+
+- Update dependencies to latest stable
+
+## [1.7.2] - 2020-10-06
+
+- Update dependencies to latest stable
+
+## [1.7.1] - 2020-10-01
+
+- Update dependencies to latest stable
+
+## [1.7.0] - 2020-09-29
+
+- Update dependencies to latest stable
+
+## [1.6.1] - 2020-09-09
+
+- Update dependencies to latest stable
+
+## [1.6.0] - 2020-08-26
+
+- CI: Try collect js coverage
+- Docker: Add package testing shortcut
+
+## [1.5.2] - 2020-08-10
+
+- Update dependencies to latest stable
+
+## [1.5.1] - 2020-08-10
+
+- Update dependencies to latest stable
+
+## [1.5.0] - 2020-07-28
+
+- Package Unit tests: update test file names to make sure they runs in Travis
+
+## [1.4.2] - 2020-07-06
+
+- Update dependencies to latest stable
+
+## [1.4.1] - 2020-07-01
+
+- Update dependencies to latest stable
+
+## [1.4.0] - 2020-06-30
+
+- PHPCS: Clean up the packages
+- PHPCS Updates after WPCS 2.3
+
+## [1.3.1] - 2020-06-01
+
+- Update dependencies to latest stable
+
+## [1.3.0] - 2020-05-26
+
+- Update dependencies to latest stable
+
+## [1.2.0] - 2020-04-28
+
+- Update dependencies to latest stable
+
+## [1.1.0] - 2020-03-31
+
+- Update dependencies to latest stable
+
+## [1.0.4] - 2019-12-04
+
+## [1.0.3] - 2019-12-04
+
+- Updating dependencies for 'abtest'
+
+## [1.0.2] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## [1.0.1] - 2019-10-28
+
+- Packages: Add gitattributes files to all packages that need th…
+
+## 1.0.0 - 2019-09-14
+
+- Packages: Introduce a simple A/B test package
+
+[1.9.18]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.17...v1.9.18
+[1.9.17]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.16...v1.9.17
+[1.9.16]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.15...v1.9.16
+[1.9.15]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.14...v1.9.15
+[1.9.14]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.13...v1.9.14
+[1.9.13]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.12...v1.9.13
+[1.9.12]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.11...v1.9.12
+[1.9.11]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.10...v1.9.11
+[1.9.10]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.9...v1.9.10
+[1.9.9]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.8...v1.9.9
+[1.9.8]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.7...v1.9.8
+[1.9.7]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.6...v1.9.7
+[1.9.6]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.5...v1.9.6
+[1.9.5]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.4...v1.9.5
+[1.9.4]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.3...v1.9.4
+[1.9.3]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.2...v1.9.3
+[1.9.2]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.1...v1.9.2
+[1.9.1]: https://github.com/Automattic/jetpack-abtest/compare/v1.9.0...v1.9.1
+[1.9.0]: https://github.com/Automattic/jetpack-abtest/compare/v1.8.4...v1.9.0
+[1.8.4]: https://github.com/Automattic/jetpack-abtest/compare/v1.8.3...v1.8.4
+[1.8.3]: https://github.com/Automattic/jetpack-abtest/compare/v1.8.2...v1.8.3
+[1.8.2]: https://github.com/Automattic/jetpack-abtest/compare/v1.8.1...v1.8.2
+[1.8.1]: https://github.com/Automattic/jetpack-abtest/compare/v1.8.0...v1.8.1
+[1.8.0]: https://github.com/Automattic/jetpack-abtest/compare/v1.7.4...v1.8.0
+[1.7.4]: https://github.com/Automattic/jetpack-abtest/compare/v1.7.3...v1.7.4
+[1.7.3]: https://github.com/Automattic/jetpack-abtest/compare/v1.7.2...v1.7.3
+[1.7.2]: https://github.com/Automattic/jetpack-abtest/compare/v1.7.1...v1.7.2
+[1.7.1]: https://github.com/Automattic/jetpack-abtest/compare/v1.7.0...v1.7.1
+[1.7.0]: https://github.com/Automattic/jetpack-abtest/compare/v1.6.1...v1.7.0
+[1.6.1]: https://github.com/Automattic/jetpack-abtest/compare/v1.6.0...v1.6.1
+[1.6.0]: https://github.com/Automattic/jetpack-abtest/compare/v1.5.2...v1.6.0
+[1.5.2]: https://github.com/Automattic/jetpack-abtest/compare/v1.5.1...v1.5.2
+[1.5.1]: https://github.com/Automattic/jetpack-abtest/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/Automattic/jetpack-abtest/compare/v1.4.2...v1.5.0
+[1.4.2]: https://github.com/Automattic/jetpack-abtest/compare/v1.4.1...v1.4.2
+[1.4.1]: https://github.com/Automattic/jetpack-abtest/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/Automattic/jetpack-abtest/compare/v1.3.1...v1.4.0
+[1.3.1]: https://github.com/Automattic/jetpack-abtest/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/Automattic/jetpack-abtest/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-abtest/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/Automattic/jetpack-abtest/compare/v1.0.4...v1.1.0
+[1.0.4]: https://github.com/Automattic/jetpack-abtest/compare/v1.0.3...v1.0.4
+[1.0.3]: https://github.com/Automattic/jetpack-abtest/compare/v1.0.2...v1.0.3
+[1.0.2]: https://github.com/Automattic/jetpack-abtest/compare/v1.0.1...v1.0.2
+[1.0.1]: https://github.com/Automattic/jetpack-abtest/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/src/class-abtest.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/src/class-abtest.php
new file mode 100644
index 00000000..b69b4cae
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-abtest/src/class-abtest.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * A class that interacts with WP.com A/B tests.
+ *
+ * @package automattic/jetpack-abtest
+ */
+
+namespace Automattic\Jetpack;
+
+use Automattic\Jetpack\Connection\Client;
+
+/**
+ * This class provides an interface to the WP.com A/B tests.
+ */
+class Abtest {
+ /**
+ * A variable to hold the tests we fetched, and their variations for the current user.
+ *
+ * @access private
+ *
+ * @var array
+ */
+ private $tests = array();
+
+ /**
+ * Retrieve the test variation for a provided A/B test.
+ *
+ * @access public
+ *
+ * @param string $test_name Name of the A/B test.
+ * @return mixed|null A/B test variation, or null on failure.
+ */
+ public function get_variation( $test_name ) {
+ $variation = $this->fetch_variation( $test_name );
+
+ // If there was an error retrieving a variation, conceal the error for the consumer.
+ if ( is_wp_error( $variation ) ) {
+ return null;
+ }
+
+ return $variation;
+ }
+
+ /**
+ * Fetch and cache the test variation for a provided A/B test from WP.com.
+ *
+ * @access protected
+ *
+ * @param string $test_name Name of the A/B test.
+ * @return mixed|Automattic\Jetpack\Error A/B test variation, or Automattic\Jetpack\Error on failure.
+ */
+ protected function fetch_variation( $test_name ) {
+ // Make sure test name exists.
+ if ( ! $test_name ) {
+ return new Error( 'test_name_not_provided', 'A/B test name has not been provided.' );
+ }
+
+ // Make sure test name is a valid one.
+ if ( ! preg_match( '/^[A-Za-z0-9_]+$/', $test_name ) ) {
+ return new Error( 'invalid_test_name', 'Invalid A/B test name.' );
+ }
+
+ // Return cached test variations.
+ if ( isset( $this->tests[ $test_name ] ) ) {
+ return $this->tests[ $test_name ];
+ }
+
+ // Make the request to the WP.com API.
+ $response = $this->request_variation( $test_name );
+
+ // Bail if there was an error or malformed response.
+ if ( is_wp_error( $response ) || ! is_array( $response ) || ! isset( $response['body'] ) ) {
+ return new Error( 'failed_to_fetch_data', 'Unable to fetch the requested data.' );
+ }
+
+ // Decode the results.
+ $results = json_decode( $response['body'], true );
+
+ // Bail if there were no results or there is no test variation returned.
+ if ( ! is_array( $results ) || empty( $results['variation'] ) ) {
+ return new Error( 'unexpected_data_format', 'Data was not returned in the expected format.' );
+ }
+
+ // Store the variation in our internal cache.
+ $this->tests[ $test_name ] = $results['variation'];
+
+ return $results['variation'];
+ }
+
+ /**
+ * Perform the request for a variation of a provided A/B test from WP.com.
+ *
+ * @access protected
+ *
+ * @param string $test_name Name of the A/B test.
+ * @return mixed|Automattic\Jetpack\Error A/B test variation, or Automattic\Jetpack\Error on failure.
+ */
+ protected function request_variation( $test_name ) {
+ return Client::wpcom_json_api_request_as_blog( sprintf( '/abtest/%s', $test_name ), '2', array(), null, 'wpcom' );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/CHANGELOG.md
new file mode 100644
index 00000000..6215e5c5
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/CHANGELOG.md
@@ -0,0 +1,34 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [0.2.1] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+
+## [0.2.0] - 2021-12-14
+### Added
+- New method to get the top level menu item
+
+## [0.1.1] - 2021-11-17
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## 0.1.0 - 2021-10-13
+### Added
+- Created the package.
+
+### Changed
+- Updated package dependencies.
+
+### Fixed
+- Fixing menu visibility issues.
+
+[0.2.1]: https://github.com/Automattic/jetpack-admin-ui/compare/0.2.0...0.2.1
+[0.2.0]: https://github.com/Automattic/jetpack-admin-ui/compare/0.1.1...0.2.0
+[0.1.1]: https://github.com/Automattic/jetpack-admin-ui/compare/0.1.0...0.1.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/class-admin-menu.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/class-admin-menu.php
new file mode 100644
index 00000000..140b40f6
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/class-admin-menu.php
@@ -0,0 +1,174 @@
+<?php
+/**
+ * Admin Menu Registration
+ *
+ * @package automattic/jetpack-admin-ui
+ */
+
+namespace Automattic\Jetpack\Admin_UI;
+
+/**
+ * This class offers a wrapper to add_submenu_page and makes sure stand-alone plugin's menu items are always added under the Jetpack top level menu.
+ * If the Jetpack top level was not previously registered by other plugin, it will be registered here.
+ */
+class Admin_Menu {
+
+ const PACKAGE_VERSION = '0.2.1';
+
+ /**
+ * Whether this class has been initialized
+ *
+ * @var boolean
+ */
+ private static $initialized = false;
+
+ /**
+ * List of menu items enqueued to be added
+ *
+ * @var array
+ */
+ private static $menu_items = array();
+
+ /**
+ * Initialize the class and set up the main hook
+ *
+ * @return void
+ */
+ public static function init() {
+ if ( ! self::$initialized ) {
+ self::$initialized = true;
+ add_action( 'admin_menu', array( __CLASS__, 'admin_menu_hook_callback' ), 1000 ); // Jetpack uses 998.
+ }
+ }
+
+ /**
+ * Enqueue styles for the top level menu
+ *
+ * @return void
+ */
+ public static function enqueue_style() {
+ wp_enqueue_style(
+ 'jetpack-admin-ui',
+ plugin_dir_url( __FILE__ ) . 'css/jetpack-icon.css',
+ array(),
+ self::PACKAGE_VERSION
+ );
+ }
+
+ /**
+ * Callback to the admin_menu hook that will register the enqueued menu items
+ *
+ * @return void
+ */
+ public static function admin_menu_hook_callback() {
+ $can_see_toplevel_menu = true;
+ $jetpack_plugin_present = class_exists( 'Jetpack_React_Page' );
+
+ if ( ! $jetpack_plugin_present ) {
+ add_action( 'admin_print_scripts', array( __CLASS__, 'enqueue_style' ) );
+ add_menu_page(
+ 'Jetpack',
+ 'Jetpack',
+ 'read',
+ 'jetpack',
+ '__return_null',
+ 'div',
+ 3
+ );
+
+ // If Jetpack plugin is not present, user will only be able to see this menu if they have enough capability to at least one of the sub menus being added.
+ $can_see_toplevel_menu = false;
+ }
+
+ foreach ( self::$menu_items as $menu_item ) {
+ if ( ! current_user_can( $menu_item['capability'] ) ) {
+ continue;
+ }
+
+ $can_see_toplevel_menu = true;
+
+ add_submenu_page(
+ 'jetpack',
+ $menu_item['page_title'],
+ $menu_item['menu_title'],
+ $menu_item['capability'],
+ $menu_item['menu_slug'],
+ $menu_item['function'],
+ $menu_item['position']
+ );
+ }
+
+ if ( ! $jetpack_plugin_present ) {
+ remove_submenu_page( 'jetpack', 'jetpack' );
+ }
+
+ if ( ! $can_see_toplevel_menu ) {
+ remove_menu_page( 'jetpack' );
+ }
+ }
+
+ /**
+ * Adds a new submenu to the Jetpack Top level menu
+ *
+ * The parameters this method accepts are the same as @see add_submenu_page. This class will
+ * aggreagate all menu items registered by stand-alone plugins and make sure they all go under the same
+ * Jetpack top level menu. It will also handle the top level menu registration in case the Jetpack plugin is not present.
+ *
+ * @param string $page_title The text to be displayed in the title tags of the page when the menu
+ * is selected.
+ * @param string $menu_title The text to be used for the menu.
+ * @param string $capability The capability required for this menu to be displayed to the user.
+ * @param string $menu_slug The slug name to refer to this menu by. Should be unique for this menu
+ * and only include lowercase alphanumeric, dashes, and underscores characters
+ * to be compatible with sanitize_key().
+ * @param callable $function The function to be called to output the content for this page.
+ * @param int $position The position in the menu order this item should appear.
+ *
+ * @return string The resulting page's hook_suffix
+ */
+ public static function add_menu( $page_title, $menu_title, $capability, $menu_slug, $function, $position = null ) {
+ self::init();
+ self::$menu_items[] = compact( 'page_title', 'menu_title', 'capability', 'menu_slug', 'function', 'position' );
+
+ /**
+ * Let's return the page hook so consumers can use.
+ * We know all pages will be under Jetpack top level menu page, so we can hardcode the first part of the string.
+ * Using get_plugin_page_hookname here won't work because the top level page is not registered yet.
+ */
+ return 'jetpack_page_' . $menu_slug;
+ }
+
+ /**
+ * Gets the slug for the first item under the Jetpack top level menu
+ *
+ * @return string|null
+ */
+ public static function get_top_level_menu_item_slug() {
+ global $submenu;
+ if ( ! empty( $submenu['jetpack'] ) ) {
+ $item = reset( $submenu['jetpack'] );
+ if ( isset( $item[2] ) ) {
+ return $item[2];
+ }
+ }
+ }
+
+ /**
+ * Gets the URL for the first item under the Jetpack top level menu
+ *
+ * @param string $fallback If Jetpack menu is not there or no children is found, return this fallback instead. Default to admin_url().
+ * @return string
+ */
+ public static function get_top_level_menu_item_url( $fallback = false ) {
+ $slug = self::get_top_level_menu_item_slug();
+
+ if ( $slug ) {
+ $url = menu_page_url( $slug, false );
+ return $url;
+ }
+
+ $url = $fallback ? $fallback : admin_url();
+ return $url;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/css/jetpack-icon.css b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/css/jetpack-icon.css
new file mode 100644
index 00000000..77d428e6
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/css/jetpack-icon.css
@@ -0,0 +1,14 @@
+@font-face {
+ font-family: 'jetpack';
+ src: url( '../fonts/jetpack/jetpack.eot' );
+ src: url( '../fonts/jetpack/jetpack.eot?#iefix' ) format( 'embedded-opentype' ),
+ url( '../fonts/jetpack/jetpack.woff' ) format( 'woff' ),
+ url( '../fonts/jetpack/jetpack.ttf' ) format( 'truetype' );
+ font-weight: normal;
+ font-style: normal;
+}
+
+li.toplevel_page_jetpack .wp-menu-image:before {
+ font-family: 'jetpack' !important;
+ content: '\f100';
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.eot b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.eot
new file mode 100644
index 00000000..f4becaf9
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.eot
Binary files differ
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.svg b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.svg
new file mode 100644
index 00000000..57a10c6e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.svg
@@ -0,0 +1,33 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<!--
+2017-12-4: Created with FontForge (http://fontforge.org)
+-->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
+<metadata>
+Created by FontForge 20170925 at Mon Dec 4 15:18:51 2017
+ By Michael Arestad
+
+</metadata>
+<defs>
+<font id="jetpack" horiz-adv-x="200" >
+ <font-face
+ font-family="jetpack"
+ font-weight="400"
+ font-stretch="normal"
+ units-per-em="512"
+ panose-1="2 0 5 3 0 0 0 0 0 0"
+ ascent="448"
+ descent="-64"
+ bbox="51.2002 -12.7998 460.8 396.8"
+ underline-thickness="25.6"
+ underline-position="-51.2"
+ unicode-range="U+0020-F100"
+ />
+ <missing-glyph />
+ <glyph glyph-name="space" unicode=" "
+ />
+ <glyph glyph-name="jetpack-logo__icon" unicode="&#xf100;" horiz-adv-x="512"
+d="M256 396.8c112.64 0 204.8 -92.1602 204.8 -204.8s-92.1602 -204.8 -204.8 -204.8s-204.8 92.1602 -204.8 204.8s92.1602 204.8 204.8 204.8zM230.4 166.4v179.199l-102.4 -179.199h102.4zM281.6 38.4004l102.4 179.199h-102.4v-179.199z" />
+ </font>
+</defs></svg>
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.ttf b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.ttf
new file mode 100644
index 00000000..47936c43
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.ttf
Binary files differ
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.woff b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.woff
new file mode 100644
index 00000000..347be333
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-admin-ui/src/fonts/jetpack/jetpack.woff
Binary files differ
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/CHANGELOG.md
new file mode 100644
index 00000000..422b57f5
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/CHANGELOG.md
@@ -0,0 +1,200 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.16.1] - 2022-01-05
+### Fixed
+- Don't issue a "doing it wrong" warning for registering aliases during plugin activation.
+
+## [1.16.0] - 2022-01-04
+### Added
+- Document use of jetpack-assets, jetpack-composer-plugin, and i18n-loader-webpack-plugin together.
+
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+- Updated package textdomain from `jetpack` to `jetpack-assets`.
+
+## [1.15.0] - 2021-12-20
+### Added
+- Add `alias_textdomain()`.
+
+## [1.14.0] - 2021-12-14
+### Added
+- Generate `wp-jp-i18n-state` script.
+
+## [1.13.1] - 2021-11-22
+### Fixed
+- Call `_doing_it_wrong` correctly.
+
+## [1.13.0] - 2021-11-22
+### Added
+- Have `Assets::register_script()` accept a textdomain for `wp_set_script_translations` (and complain if no textdomain is passed when `wp-i18n` is depended on).
+
+### Changed
+- Updated package dependencies
+
+### Fixed
+- Added missing option doc for `Assets::register_script()`.
+
+## [1.12.0] - 2021-11-15
+### Added
+- Add `Assets::register_script()` for easier loading of Webpack-built scripts.
+
+## [1.11.10] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.11.9] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.11.8] - 2021-10-06
+### Changed
+- Updated package dependencies
+
+## [1.11.7] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.11.6] - 2021-08-30
+### Changed
+- Run composer update on test-php command instead of phpunit
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- update annotations versions
+
+## [1.11.5] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.11.4] - 2021-04-08
+### Changed
+- Packaging and build changes, no change to the package itself.
+
+## [1.11.3] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.11.2] - 2021-02-23
+
+- CI: Make tests more generic
+
+## [1.11.1] - 2021-01-26
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.11.0] - 2021-01-05
+
+- Update dependency brain/monkey to v2.6.0
+
+## [1.10.0] - 2020-12-08
+
+- Assets: introduce new method to process static resources
+- Assets: Use defer for script tags
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.9.1] - 2020-11-24
+
+- Update dependency brain/monkey to v2.5.0
+- Updated PHPCS: Packages and Debugger
+
+## [1.9.0] - 2020-10-27
+
+- Instagram oEmbed: Simplify
+
+## [1.8.0] - 2020-09-29
+
+- Consolidate the Lazy Images package to rely on the Assets package
+
+## [1.7.0] - 2020-08-25
+
+- Packages: Update filenames after #16810
+- CI: Try collect js coverage
+- Docker: Add package testing shortcut
+
+## [1.6.0] - 2020-07-28
+
+- Various: Use wp_resource_hints
+
+## [1.5.0] - 2020-06-30
+
+- PHPCS: Clean up the packages
+- WooCommerce Analytics: avoid 404 error when enqueuing script
+
+## [1.4.0] - 2020-05-26
+
+- Add Jetpack Scan threat notifications
+
+## [1.3.0] - 2020-04-28
+
+- Update dependencies to latest stable
+
+## [1.2.0] - 2020-03-31
+
+- Update dependencies to latest stable
+
+## [1.1.1] - 2020-01-27
+
+- Pin dependency brain/monkey to 2.4.0
+
+## [1.1.0] - 2020-01-14
+
+- Packages: Various improvements for wp.com or self-contained consumers
+
+## [1.0.3] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## [1.0.1] - 2019-10-28
+
+- PHPCS: JITM and Assets packages
+- Packages: Add gitattributes files to all packages that need th…
+
+## 1.0.0 - 2019-09-14
+
+- Statically access asset tools
+
+[1.16.1]: https://github.com/Automattic/jetpack-assets/compare/v1.16.0...v1.16.1
+[1.16.0]: https://github.com/Automattic/jetpack-assets/compare/v1.15.0...v1.16.0
+[1.15.0]: https://github.com/Automattic/jetpack-assets/compare/v1.14.0...v1.15.0
+[1.14.0]: https://github.com/Automattic/jetpack-assets/compare/v1.13.1...v1.14.0
+[1.13.1]: https://github.com/Automattic/jetpack-assets/compare/v1.13.0...v1.13.1
+[1.13.0]: https://github.com/Automattic/jetpack-assets/compare/v1.12.0...v1.13.0
+[1.12.0]: https://github.com/Automattic/jetpack-assets/compare/v1.11.10...v1.12.0
+[1.11.10]: https://github.com/Automattic/jetpack-assets/compare/v1.11.9...v1.11.10
+[1.11.9]: https://github.com/Automattic/jetpack-assets/compare/v1.11.8...v1.11.9
+[1.11.8]: https://github.com/Automattic/jetpack-assets/compare/v1.11.7...v1.11.8
+[1.11.7]: https://github.com/Automattic/jetpack-assets/compare/v1.11.6...v1.11.7
+[1.11.6]: https://github.com/Automattic/jetpack-assets/compare/v1.11.5...v1.11.6
+[1.11.5]: https://github.com/Automattic/jetpack-assets/compare/v1.11.4...v1.11.5
+[1.11.4]: https://github.com/Automattic/jetpack-assets/compare/v1.11.3...v1.11.4
+[1.11.3]: https://github.com/Automattic/jetpack-assets/compare/v1.11.2...v1.11.3
+[1.11.2]: https://github.com/Automattic/jetpack-assets/compare/v1.11.1...v1.11.2
+[1.11.1]: https://github.com/Automattic/jetpack-assets/compare/v1.11.0...v1.11.1
+[1.11.0]: https://github.com/Automattic/jetpack-assets/compare/v1.10.0...v1.11.0
+[1.10.0]: https://github.com/Automattic/jetpack-assets/compare/v1.9.1...v1.10.0
+[1.9.1]: https://github.com/Automattic/jetpack-assets/compare/v1.9.0...v1.9.1
+[1.9.0]: https://github.com/Automattic/jetpack-assets/compare/v1.8.0...v1.9.0
+[1.8.0]: https://github.com/Automattic/jetpack-assets/compare/v1.7.0...v1.8.0
+[1.7.0]: https://github.com/Automattic/jetpack-assets/compare/v1.6.0...v1.7.0
+[1.6.0]: https://github.com/Automattic/jetpack-assets/compare/v1.5.0...v1.6.0
+[1.5.0]: https://github.com/Automattic/jetpack-assets/compare/v1.4.0...v1.5.0
+[1.4.0]: https://github.com/Automattic/jetpack-assets/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/Automattic/jetpack-assets/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-assets/compare/v1.1.1...v1.2.0
+[1.1.1]: https://github.com/Automattic/jetpack-assets/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-assets/compare/v1.0.3...v1.1.0
+[1.0.3]: https://github.com/Automattic/jetpack-assets/compare/v1.0.1...v1.0.3
+[1.0.1]: https://github.com/Automattic/jetpack-assets/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/actions.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/actions.php
new file mode 100644
index 00000000..1c8becda
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/actions.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Action Hooks for Jetpack Assets module.
+ *
+ * @package automattic/jetpack-assets
+ */
+
+// If WordPress's plugin API is available already, use it. If not,
+// drop data into `$wp_filter` for `WP_Hook::build_preinitialized_hooks()`.
+if ( function_exists( 'add_action' ) ) {
+ add_action( 'wp_default_scripts', array( Automattic\Jetpack\Assets::class, 'wp_default_scripts_hook' ) );
+} else {
+ global $wp_filter;
+ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $wp_filter['wp_default_scripts'][10][] = array(
+ 'accepted_args' => 1,
+ 'function' => array( Automattic\Jetpack\Assets::class, 'wp_default_scripts_hook' ),
+ );
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/src/class-assets.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/src/class-assets.php
new file mode 100644
index 00000000..dd130a7b
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/src/class-assets.php
@@ -0,0 +1,704 @@
+<?php
+/**
+ * Jetpack Assets package.
+ *
+ * @package automattic/jetpack-assets
+ */
+
+namespace Automattic\Jetpack;
+
+use Automattic\Jetpack\Assets\Semver;
+use Automattic\Jetpack\Constants as Jetpack_Constants;
+use InvalidArgumentException;
+
+/**
+ * Class Assets
+ */
+class Assets {
+ /**
+ * Holds all the scripts handles that should be loaded in a deferred fashion.
+ *
+ * @var array
+ */
+ private $defer_script_handles = array();
+
+ /**
+ * The singleton instance of this class.
+ *
+ * @var Assets
+ */
+ protected static $instance;
+
+ /**
+ * The registered textdomain mappings.
+ *
+ * @var array `array( mapped_domain => array( string target_domain, string target_type, string semver ) )`.
+ */
+ private static $domain_map = array();
+
+ /**
+ * Constructor.
+ *
+ * Static-only class, so nothing here.
+ */
+ private function __construct() {}
+
+ // ////////////////////
+ // region Async script loading
+
+ /**
+ * Get the singleton instance of the class.
+ *
+ * @return Assets
+ */
+ public static function instance() {
+ if ( ! isset( self::$instance ) ) {
+ self::$instance = new Assets();
+ self::$instance->init_hooks();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Initalize the hooks as needed.
+ */
+ private function init_hooks() {
+ /*
+ * Load some scripts asynchronously.
+ */
+ add_filter( 'script_loader_tag', array( $this, 'script_add_async' ), 10, 2 );
+ }
+
+ /**
+ * A public method for adding the async script.
+ *
+ * @param string $script_handle Script handle.
+ */
+ public function add_async_script( $script_handle ) {
+ $this->defer_script_handles[] = $script_handle;
+ }
+
+ /**
+ * Add an async attribute to scripts that can be loaded deferred.
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script
+ *
+ * @param string $tag The <script> tag for the enqueued script.
+ * @param string $handle The script's registered handle.
+ */
+ public function script_add_async( $tag, $handle ) {
+ if ( empty( $this->defer_script_handles ) ) {
+ return $tag;
+ }
+
+ if ( in_array( $handle, $this->defer_script_handles, true ) ) {
+ return preg_replace( '/^<script /i', '<script defer ', $tag );
+ }
+
+ return $tag;
+ }
+
+ /**
+ * A helper function that lets you enqueue scripts in an async fashion.
+ *
+ * @param string $handle Name of the script. Should be unique.
+ * @param string $min_path Minimized script path.
+ * @param string $non_min_path Full Script path.
+ * @param array $deps Array of script dependencies.
+ * @param bool $ver The script version.
+ * @param bool $in_footer Should the script be included in the footer.
+ */
+ public static function enqueue_async_script( $handle, $min_path, $non_min_path, $deps = array(), $ver = false, $in_footer = true ) {
+ $assets_instance = self::instance();
+ $assets_instance->add_async_script( $handle );
+ wp_enqueue_script( $handle, self::get_file_url_for_environment( $min_path, $non_min_path ), $deps, $ver, $in_footer );
+ }
+
+ // endregion .
+
+ // ////////////////////
+ // region Utils
+
+ /**
+ * Given a minified path, and a non-minified path, will return
+ * a minified or non-minified file URL based on whether SCRIPT_DEBUG is set and truthy.
+ *
+ * If $package_path is provided, then the minified or non-minified file URL will be generated
+ * relative to the root package directory.
+ *
+ * Both `$min_base` and `$non_min_base` can be either full URLs, or are expected to be relative to the
+ * root Jetpack directory.
+ *
+ * @param string $min_path minified path.
+ * @param string $non_min_path non-minified path.
+ * @param string $package_path Optional. A full path to a file inside a package directory
+ * The URL will be relative to its directory. Default empty.
+ * Typically this is done by passing __FILE__ as the argument.
+ *
+ * @return string The URL to the file
+ * @since 1.0.3
+ * @since-jetpack 5.6.0
+ */
+ public static function get_file_url_for_environment( $min_path, $non_min_path, $package_path = '' ) {
+ $path = ( Jetpack_Constants::is_defined( 'SCRIPT_DEBUG' ) && Jetpack_Constants::get_constant( 'SCRIPT_DEBUG' ) )
+ ? $non_min_path
+ : $min_path;
+
+ /*
+ * If the path is actually a full URL, keep that.
+ * We look for a host value, since enqueues are sometimes without a scheme.
+ */
+ $file_parts = wp_parse_url( $path );
+ if ( ! empty( $file_parts['host'] ) ) {
+ $url = $path;
+ } else {
+ $plugin_path = empty( $package_path ) ? Jetpack_Constants::get_constant( 'JETPACK__PLUGIN_FILE' ) : $package_path;
+
+ $url = plugins_url( $path, $plugin_path );
+ }
+
+ /**
+ * Filters the URL for a file passed through the get_file_url_for_environment function.
+ *
+ * @since 1.0.3
+ *
+ * @package assets
+ *
+ * @param string $url The URL to the file.
+ * @param string $min_path The minified path.
+ * @param string $non_min_path The non-minified path.
+ */
+ return apply_filters( 'jetpack_get_file_for_environment', $url, $min_path, $non_min_path );
+ }
+
+ /**
+ * Passes an array of URLs to wp_resource_hints.
+ *
+ * @since 1.5.0
+ *
+ * @param string|array $urls URLs to hint.
+ * @param string $type One of the supported resource types: dns-prefetch (default), preconnect, prefetch, or prerender.
+ */
+ public static function add_resource_hint( $urls, $type = 'dns-prefetch' ) {
+ add_filter(
+ 'wp_resource_hints',
+ function ( $hints, $resource_type ) use ( $urls, $type ) {
+ if ( $resource_type === $type ) {
+ // Type casting to array required since the function accepts a single string.
+ foreach ( (array) $urls as $url ) {
+ $hints[] = $url;
+ }
+ }
+ return $hints;
+ },
+ 10,
+ 2
+ );
+ }
+
+ /**
+ * Serve a WordPress.com static resource via a randomized wp.com subdomain.
+ *
+ * @since 1.9.0
+ *
+ * @param string $url WordPress.com static resource URL.
+ *
+ * @return string $url
+ */
+ public static function staticize_subdomain( $url ) {
+ // Extract hostname from URL.
+ $host = wp_parse_url( $url, PHP_URL_HOST );
+
+ // Explode hostname on '.'.
+ $exploded_host = explode( '.', $host );
+
+ // Retrieve the name and TLD.
+ if ( count( $exploded_host ) > 1 ) {
+ $name = $exploded_host[ count( $exploded_host ) - 2 ];
+ $tld = $exploded_host[ count( $exploded_host ) - 1 ];
+ // Rebuild domain excluding subdomains.
+ $domain = $name . '.' . $tld;
+ } else {
+ $domain = $host;
+ }
+ // Array of Automattic domains.
+ $domains_allowed = array( 'wordpress.com', 'wp.com' );
+
+ // Return $url if not an Automattic domain.
+ if ( ! in_array( $domain, $domains_allowed, true ) ) {
+ return $url;
+ }
+
+ if ( \is_ssl() ) {
+ return preg_replace( '|https?://[^/]++/|', 'https://s-ssl.wordpress.com/', $url );
+ }
+
+ /*
+ * Generate a random subdomain id by taking the modulus of the crc32 value of the URL.
+ * Valid values are 0, 1, and 2.
+ */
+ $static_counter = abs( crc32( basename( $url ) ) % 3 );
+
+ return preg_replace( '|://[^/]+?/|', "://s$static_counter.wp.com/", $url );
+ }
+
+ /**
+ * Resolve '.' and '..' components in a path or URL.
+ *
+ * @since 1.12.0
+ * @param string $path Path or URL.
+ * @return string Normalized path or URL.
+ */
+ public static function normalize_path( $path ) {
+ $parts = wp_parse_url( $path );
+ if ( ! isset( $parts['path'] ) ) {
+ return $path;
+ }
+
+ $ret = '';
+ $ret .= isset( $parts['scheme'] ) ? $parts['scheme'] . '://' : '';
+ if ( isset( $parts['user'] ) || isset( $parts['pass'] ) ) {
+ $ret .= isset( $parts['user'] ) ? $parts['user'] : '';
+ $ret .= isset( $parts['pass'] ) ? ':' . $parts['pass'] : '';
+ $ret .= '@';
+ }
+ $ret .= isset( $parts['host'] ) ? $parts['host'] : '';
+ $ret .= isset( $parts['port'] ) ? ':' . $parts['port'] : '';
+
+ $pp = explode( '/', $parts['path'] );
+ if ( '' === $pp[0] ) {
+ $ret .= '/';
+ array_shift( $pp );
+ }
+ $i = 0;
+ while ( $i < count( $pp ) ) { // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found
+ if ( '' === $pp[ $i ] || '.' === $pp[ $i ] || 0 === $i && '..' === $pp[ $i ] ) {
+ array_splice( $pp, $i, 1 );
+ } elseif ( '..' === $pp[ $i ] ) {
+ array_splice( $pp, --$i, 2 );
+ } else {
+ $i++;
+ }
+ }
+ $ret .= join( '/', $pp );
+
+ $ret .= isset( $parts['query'] ) ? '?' . $parts['query'] : '';
+ $ret .= isset( $parts['fragment'] ) ? '#' . $parts['fragment'] : '';
+
+ return $ret;
+ }
+
+ // endregion .
+
+ // ////////////////////
+ // region Webpack-built script registration
+
+ /**
+ * Register a Webpack-built script.
+ *
+ * Our Webpack-built scripts tend to need a bunch of boilerplate:
+ * - A call to `Assets::get_file_url_for_environment()` for possible debugging.
+ * - A call to `wp_register_style()` for extracted CSS, possibly with detection of RTL.
+ * - Loading of dependencies and version provided by `@wordpress/dependency-extraction-webpack-plugin`.
+ * - Avoiding WPCom's broken minifier.
+ *
+ * This wrapper handles all of that.
+ *
+ * @since 1.12.0
+ * @param string $handle Name of the script. Should be unique across both scripts and styles.
+ * @param string $path Minimized script path.
+ * @param string $relative_to File that `$path` is relative to. Pass `__FILE__`.
+ * @param array $options Additional options:
+ * - `asset_path`: (string|null) `.asset.php` to load. Default is to base it on `$path`.
+ * - `async`: (bool) Set true to register the script as async, like `Assets::enqueue_async_script()`
+ * - `css_dependencies`: (string[]) Additional style dependencies to queue.
+ * - `css_path`: (string|null) `.css` to load. Default is to base it on `$path`.
+ * - `dependencies`: (string[]) Additional script dependencies to queue.
+ * - `enqueue`: (bool) Set true to enqueue the script immediately.
+ * - `in_footer`: (bool) Set true to register script for the footer.
+ * - `media`: (string) Media for the css file. Default 'all'.
+ * - `minify`: (bool|null) Set true to pass `minify=true` in the query string, or `null` to suppress the normal `minify=false`.
+ * - `nonmin_path`: (string) Non-minified script path.
+ * - `textdomain`: (string) Text domain for the script. Required if the script depends on wp-i18n.
+ * - `version`: (string) Override the version from the `asset_path` file.
+ * @throws \InvalidArgumentException If arguments are invalid.
+ */
+ public static function register_script( $handle, $path, $relative_to, array $options = array() ) {
+ if ( substr( $path, -3 ) !== '.js' ) {
+ throw new \InvalidArgumentException( '$path must end in ".js"' );
+ }
+
+ $dir = dirname( $relative_to );
+ $base = substr( $path, 0, -3 );
+ $options += array(
+ 'asset_path' => "$base.asset.php",
+ 'async' => false,
+ 'css_dependencies' => array(),
+ 'css_path' => "$base.css",
+ 'dependencies' => array(),
+ 'enqueue' => false,
+ 'in_footer' => false,
+ 'media' => 'all',
+ 'minify' => false,
+ 'textdomain' => null,
+ );
+
+ if ( $options['css_path'] && substr( $options['css_path'], -4 ) !== '.css' ) {
+ throw new \InvalidArgumentException( '$options[\'css_path\'] must end in ".css"' );
+ }
+
+ if ( isset( $options['nonmin_path'] ) ) {
+ $url = self::get_file_url_for_environment( $path, $options['nonmin_path'], $relative_to );
+ } else {
+ $url = plugins_url( $path, $relative_to );
+ }
+ $url = self::normalize_path( $url );
+ if ( null !== $options['minify'] ) {
+ $url = add_query_arg( 'minify', $options['minify'] ? 'true' : 'false', $url );
+ }
+
+ if ( $options['asset_path'] && file_exists( "$dir/{$options['asset_path']}" ) ) {
+ $asset = require "$dir/{$options['asset_path']}";
+ $options['dependencies'] = array_merge( $asset['dependencies'], $options['dependencies'] );
+ $options['css_dependencies'] = array_merge(
+ array_filter(
+ $asset['dependencies'],
+ function ( $d ) {
+ return wp_style_is( $d, 'registered' );
+ }
+ ),
+ $options['css_dependencies']
+ );
+ $ver = isset( $options['version'] ) ? $options['version'] : $asset['version'];
+ } else {
+ $ver = isset( $options['version'] ) ? $options['version'] : filemtime( "$dir/$path" );
+ }
+
+ wp_register_script( $handle, $url, $options['dependencies'], $ver, $options['in_footer'] );
+ if ( $options['async'] ) {
+ self::instance()->add_async_script( $handle );
+ }
+ if ( $options['textdomain'] ) {
+ // phpcs:ignore Jetpack.Functions.I18n.DomainNotLiteral
+ wp_set_script_translations( $handle, $options['textdomain'] );
+ } elseif ( in_array( 'wp-i18n', $options['dependencies'], true ) ) {
+ _doing_it_wrong(
+ __METHOD__,
+ /* translators: %s is the script handle. */
+ esc_html( sprintf( __( 'Script "%s" depends on wp-i18n but does not specify "textdomain"', 'jetpack-assets' ), $handle ) ),
+ ''
+ );
+ }
+
+ if ( $options['css_path'] && file_exists( "$dir/{$options['css_path']}" ) ) {
+ $csspath = $options['css_path'];
+ if ( is_rtl() ) {
+ $rtlcsspath = substr( $csspath, 0, -4 ) . '.rtl.css';
+ if ( file_exists( "$dir/$rtlcsspath" ) ) {
+ $csspath = $rtlcsspath;
+ }
+ }
+
+ $url = self::normalize_path( plugins_url( $csspath, $relative_to ) );
+ if ( null !== $options['minify'] ) {
+ $url = add_query_arg( 'minify', $options['minify'] ? 'true' : 'false', $url );
+ }
+ wp_register_style( $handle, $url, $options['css_dependencies'], $ver, $options['media'] );
+ wp_script_add_data( $handle, 'Jetpack::Assets::hascss', true );
+ } else {
+ wp_script_add_data( $handle, 'Jetpack::Assets::hascss', false );
+ }
+
+ if ( $options['enqueue'] ) {
+ self::enqueue_script( $handle );
+ }
+ }
+
+ /**
+ * Enqueue a script registered with `Assets::register_script`.
+ *
+ * @since 1.12.0
+ * @param string $handle Name of the script. Should be unique across both scripts and styles.
+ */
+ public static function enqueue_script( $handle ) {
+ wp_enqueue_script( $handle );
+ if ( wp_scripts()->get_data( $handle, 'Jetpack::Assets::hascss' ) ) {
+ wp_enqueue_style( $handle );
+ }
+ }
+
+ /**
+ * 'wp_default_scripts' action handler.
+ *
+ * This registers the `wp-jp-i18n-state` script for use by Webpack bundles built with
+ * `@automattic/i18n-loader-webpack-plugin`.
+ *
+ * @since 1.14.0
+ * @param \WP_Scripts $wp_scripts WP_Scripts instance.
+ */
+ public static function wp_default_scripts_hook( $wp_scripts ) {
+ $data = array(
+ 'baseUrl' => false,
+ 'locale' => determine_locale(),
+ 'domainMap' => array(),
+ );
+
+ $lang_dir = Jetpack_Constants::get_constant( 'WP_LANG_DIR' );
+ $abspath = Jetpack_Constants::get_constant( 'ABSPATH' );
+
+ if ( strpos( $lang_dir, $abspath ) === 0 ) {
+ $data['baseUrl'] = site_url( substr( trailingslashit( $lang_dir ), strlen( untrailingslashit( $abspath ) ) ) );
+ }
+
+ foreach ( self::$domain_map as $from => list( $to, $type ) ) {
+ $data['domainMap'][ $from ] = ( 'core' === $type ? '' : "{$type}/" ) . $to;
+ }
+
+ /**
+ * Filters the i18n state data for use by Webpack bundles built with
+ * `@automattic/i18n-loader-webpack-plugin`.
+ *
+ * @since 1.14.0
+ * @package assets
+ * @param array $data The state data to generate. Expected fields are:
+ * - `baseUrl`: (string|false) The URL to the languages directory. False if no URL could be determined.
+ * - `locale`: (string) The locale for the page.
+ * - `domainMap`: (string[]) A mapping from Composer package textdomains to the corresponding
+ * `plugins/textdomain` or `themes/textdomain` (or core `textdomain`, but that's unlikely).
+ */
+ $data = apply_filters( 'jetpack_i18n_state', $data );
+
+ if ( ! is_array( $data ) ||
+ ! isset( $data['baseUrl'] ) || ! ( is_string( $data['baseUrl'] ) || false === $data['baseUrl'] ) ||
+ ! isset( $data['locale'] ) || ! is_string( $data['locale'] ) ||
+ ! isset( $data['domainMap'] ) || ! is_array( $data['domainMap'] )
+ ) {
+ $js = 'console.warn( "I18n state deleted by jetpack_i18n_state hook" );';
+ } elseif ( ! $data['baseUrl'] ) {
+ $js = 'console.warn( "Failed to determine languages base URL. Is WP_LANG_DIR in the WordPress root?" );';
+ } else {
+ $data['domainMap'] = (object) $data['domainMap']; // Ensure it becomes a json object.
+ $js = 'wp.jpI18nState = ' . wp_json_encode( $data, JSON_UNESCAPED_SLASHES ) . ';';
+ }
+
+ // Depend on wp-i18n to ensure global `wp` exists and because anything needing this will need that too.
+ $wp_scripts->add( 'wp-jp-i18n-state', null, array( 'wp-i18n' ) );
+ $wp_scripts->add_inline_script( 'wp-jp-i18n-state', $js, 'before' );
+ }
+
+ // endregion .
+
+ // ////////////////////
+ // region Textdomain aliasing
+
+ /**
+ * Register a textdomain alias.
+ *
+ * Composer packages included in plugins will likely not use the textdomain of the plugin, while
+ * WordPress's i18n infrastructure will include the translations in the plugin's domain. This
+ * allows for mapping the package's domain to the plugin's.
+ *
+ * Since multiple plugins may use the same package, we include the package's version here so
+ * as to choose the most recent translations (which are most likely to match the package
+ * selected by jetpack-autoloader).
+ *
+ * @since 1.15.0
+ * @param string $from Domain to alias.
+ * @param string $to Domain to alias it to.
+ * @param string $totype What is the target of the alias: 'plugins', 'themes', or 'core'.
+ * @param string $ver Version of the `$from` domain.
+ * @throws InvalidArgumentException If arguments are invalid.
+ */
+ public static function alias_textdomain( $from, $to, $totype, $ver ) {
+ if ( ! in_array( $totype, array( 'plugins', 'themes', 'core' ), true ) ) {
+ throw new InvalidArgumentException( 'Type must be "plugins", "themes", or "core"' );
+ }
+
+ if (
+ did_action( 'wp_default_scripts' ) &&
+ // Don't complain during plugin activation.
+ ! defined( 'WP_SANDBOX_SCRAPING' )
+ ) {
+ _doing_it_wrong(
+ __METHOD__,
+ sprintf(
+ /* translators: 1: wp_default_scripts. 2: Name of the domain being aliased. */
+ esc_html__( 'Textdomain aliases should be registered before the %1$s hook. This notice was triggered by the %2$s domain.', 'jetpack-assets' ),
+ '<code>wp_default_scripts</code>',
+ '<code>' . esc_html( $from ) . '</code>'
+ ),
+ ''
+ );
+ }
+
+ if ( empty( self::$domain_map[ $from ] ) ) {
+ self::init_domain_map_hooks( $from, array() === self::$domain_map );
+ self::$domain_map[ $from ] = array( $to, $totype, $ver );
+ } elseif ( Semver::compare( $ver, self::$domain_map[ $from ][2] ) > 0 ) {
+ self::$domain_map[ $from ] = array( $to, $totype, $ver );
+ }
+ }
+
+ /**
+ * Register textdomain aliases from a mapping file.
+ *
+ * The mapping file is simply a PHP file that returns an array
+ * with the following properties:
+ * - 'domain': String, `$to`
+ * - 'type': String, `$totype`
+ * - 'packages': Array, mapping `$from` to `$ver`.
+ *
+ * @since 1.15.0
+ * @param string $file Mapping file.
+ */
+ public static function alias_textdomains_from_file( $file ) {
+ $data = require $file;
+ foreach ( $data['packages'] as $from => $ver ) {
+ self::alias_textdomain( $from, $data['domain'], $data['type'], $ver );
+ }
+ }
+
+ /**
+ * Register the hooks for textdomain aliasing.
+ *
+ * @param string $domain Domain to alias.
+ * @param bool $firstcall If this is the first call.
+ */
+ private static function init_domain_map_hooks( $domain, $firstcall ) {
+ // If WordPress's plugin API is available already, use it. If not,
+ // drop data into `$wp_filter` for `WP_Hook::build_preinitialized_hooks()`.
+ if ( function_exists( 'add_filter' ) ) {
+ $add_filter = 'add_filter';
+ } else {
+ $add_filter = function ( $hook_name, $callback, $priority = 10, $accepted_args = 1 ) {
+ global $wp_filter;
+ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $wp_filter[ $hook_name ][ $priority ][] = array(
+ 'accepted_args' => $accepted_args,
+ 'function' => $callback,
+ );
+ };
+ }
+
+ $add_filter( "gettext_{$domain}", array( self::class, 'filter_gettext' ), 10, 3 );
+ $add_filter( "ngettext_{$domain}", array( self::class, 'filter_ngettext' ), 10, 5 );
+ $add_filter( "gettext_with_context_{$domain}", array( self::class, 'filter_gettext_with_context' ), 10, 4 );
+ $add_filter( "ngettext_with_context_{$domain}", array( self::class, 'filter_ngettext_with_context' ), 10, 6 );
+ if ( $firstcall ) {
+ $add_filter( 'load_script_translation_file', array( self::class, 'filter_load_script_translation_file' ), 10, 3 );
+ }
+ }
+
+ /**
+ * Filter for `gettext`.
+ *
+ * @since 1.15.0
+ * @param string $translation Translated text.
+ * @param string $text Text to translate.
+ * @param string $domain Text domain.
+ * @return string Translated text.
+ */
+ public static function filter_gettext( $translation, $text, $domain ) {
+ if ( $translation === $text ) {
+ // phpcs:ignore WordPress.WP.I18n
+ $newtext = __( $text, self::$domain_map[ $domain ][0] );
+ if ( $newtext !== $text ) {
+ return $newtext;
+ }
+ }
+ return $translation;
+ }
+
+ /**
+ * Filter for `ngettext`.
+ *
+ * @since 1.15.0
+ * @param string $translation Translated text.
+ * @param string $single The text to be used if the number is singular.
+ * @param string $plural The text to be used if the number is plural.
+ * @param string $number The number to compare against to use either the singular or plural form.
+ * @param string $domain Text domain.
+ * @return string Translated text.
+ */
+ public static function filter_ngettext( $translation, $single, $plural, $number, $domain ) {
+ if ( $translation === $single || $translation === $plural ) {
+ // phpcs:ignore WordPress.WP.I18n
+ $translation = _n( $single, $plural, $number, self::$domain_map[ $domain ][0] );
+ }
+ return $translation;
+ }
+
+ /**
+ * Filter for `gettext_with_context`.
+ *
+ * @since 1.15.0
+ * @param string $translation Translated text.
+ * @param string $text Text to translate.
+ * @param string $context Context information for the translators.
+ * @param string $domain Text domain.
+ * @return string Translated text.
+ */
+ public static function filter_gettext_with_context( $translation, $text, $context, $domain ) {
+ if ( $translation === $text ) {
+ // phpcs:ignore WordPress.WP.I18n
+ $translation = _x( $text, $context, self::$domain_map[ $domain ][0] );
+ }
+ return $translation;
+ }
+
+ /**
+ * Filter for `ngettext_with_context`.
+ *
+ * @since 1.15.0
+ * @param string $translation Translated text.
+ * @param string $single The text to be used if the number is singular.
+ * @param string $plural The text to be used if the number is plural.
+ * @param string $number The number to compare against to use either the singular or plural form.
+ * @param string $context Context information for the translators.
+ * @param string $domain Text domain.
+ * @return string Translated text.
+ */
+ public static function filter_ngettext_with_context( $translation, $single, $plural, $number, $context, $domain ) {
+ if ( $translation === $single || $translation === $plural ) {
+ // phpcs:ignore WordPress.WP.I18n
+ $translation = _nx( $single, $plural, $number, $context, self::$domain_map[ $domain ][0] );
+ }
+ return $translation;
+ }
+
+ /**
+ * Filter for `load_script_translation_file`.
+ *
+ * @since 1.15.0
+ * @param string|false $file Path to the translation file to load. False if there isn't one.
+ * @param string $handle Name of the script to register a translation domain to.
+ * @param string $domain The text domain.
+ */
+ public static function filter_load_script_translation_file( $file, $handle, $domain ) {
+ if ( false !== $file && isset( self::$domain_map[ $domain ] ) && ! is_readable( $file ) ) {
+ // Determine the part of the filename after the domain.
+ $suffix = basename( $file );
+ $l = strlen( $domain );
+ if ( substr( $suffix, 0, $l ) !== $domain || '-' !== $suffix[ $l ] ) {
+ return $file;
+ }
+ $suffix = substr( $suffix, $l );
+ $lang_dir = Jetpack_Constants::get_constant( 'WP_LANG_DIR' );
+
+ // Look for replacement files.
+ list( $newdomain, $type ) = self::$domain_map[ $domain ];
+ $newfile = $lang_dir . ( 'core' === $type ? '/' : "/{$type}/" ) . $newdomain . $suffix;
+ if ( is_readable( $newfile ) ) {
+ return $newfile;
+ }
+ }
+ return $file;
+ }
+
+ // endregion .
+
+}
+
+// Enable section folding in vim:
+// vim: foldmarker=//\ region,//\ endregion foldmethod=marker
+// .
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/src/class-semver.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/src/class-semver.php
new file mode 100644
index 00000000..5a5c6591
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-assets/src/class-semver.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * Simple semver version handling.
+ *
+ * We use this instead of something like `composer/semver` to avoid
+ * plugins needing to include yet-another dependency package. The
+ * amount of code we need here is pretty small.
+ *
+ * We use this instead of PHP's `version_compare()` because that doesn't
+ * handle prerelease versions in the way anyone other than PHP devs would
+ * expect, and silently breaks on various unexpected input.
+ *
+ * @package automattic/jetpack-assets
+ */
+
+namespace Automattic\Jetpack\Assets;
+
+use InvalidArgumentException;
+
+/**
+ * Simple semver version handling.
+ */
+class Semver {
+ /**
+ * Parse a semver version.
+ *
+ * @param string $version Version.
+ * @return array With components:
+ * - major: (int) Major version.
+ * - minor: (int) Minor version.
+ * - patch: (int) Patch version.
+ * - version: (string) Major.minor.patch.
+ * - prerelease: (string|null) Pre-release string.
+ * - buildinfo: (string|null) Build metadata string.
+ * @throws InvalidArgumentException If the version number is not in a recognized format.
+ */
+ public static function parse( $version ) {
+ // This is slightly looser than the official version from semver.org, in that leading zeros are allowed.
+ if ( ! preg_match( '/^(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<prerelease>(?:[0-9a-zA-Z-]+)(?:\.(?:[0-9a-zA-Z-]+))*))?(?:\+(?P<buildinfo>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/', $version, $m ) ) {
+ throw new InvalidArgumentException( "Version number \"$version\" is not in a recognized format." );
+ }
+ $info = array(
+ 'major' => (int) $m['major'],
+ 'minor' => (int) $m['minor'],
+ 'patch' => (int) $m['patch'],
+ 'version' => sprintf( '%d.%d.%d', $m['major'], $m['minor'], $m['patch'] ),
+ 'prerelease' => isset( $m['prerelease'] ) && '' !== $m['prerelease'] ? $m['prerelease'] : null,
+ 'buildinfo' => isset( $m['buildinfo'] ) && '' !== $m['buildinfo'] ? $m['buildinfo'] : null,
+ );
+
+ if ( null !== $info['prerelease'] ) {
+ $sep = '';
+ $prerelease = '';
+ foreach ( explode( '.', $info['prerelease'] ) as $part ) {
+ if ( ctype_digit( $part ) ) {
+ $part = (int) $part;
+ }
+ $prerelease .= $sep . $part;
+ $sep = '.';
+ }
+ $info['prerelease'] = $prerelease;
+ }
+
+ return $info;
+ }
+
+ /**
+ * Compare two version numbers.
+ *
+ * @param string $a First version.
+ * @param string $b Second version.
+ * @return int Less than, equal to, or greater than 0 depending on whether `$a` is less than, equal to, or greater than `$b`.
+ * @throws InvalidArgumentException If the version numbers are not in a recognized format.
+ */
+ public static function compare( $a, $b ) {
+ $aa = self::parse( $a );
+ $bb = self::parse( $b );
+ if ( $aa['major'] !== $bb['major'] ) {
+ return $aa['major'] - $bb['major'];
+ }
+ if ( $aa['minor'] !== $bb['minor'] ) {
+ return $aa['minor'] - $bb['minor'];
+ }
+ if ( $aa['patch'] !== $bb['patch'] ) {
+ return $aa['patch'] - $bb['patch'];
+ }
+
+ if ( null === $aa['prerelease'] ) {
+ return null === $bb['prerelease'] ? 0 : 1;
+ }
+ if ( null === $bb['prerelease'] ) {
+ return -1;
+ }
+
+ $aaa = explode( '.', $aa['prerelease'] );
+ $bbb = explode( '.', $bb['prerelease'] );
+ $al = count( $aaa );
+ $bl = count( $bbb );
+ for ( $i = 0; $i < $al && $i < $bl; $i++ ) {
+ $a = $aaa[ $i ];
+ $b = $bbb[ $i ];
+ if ( ctype_digit( $a ) ) {
+ if ( ctype_digit( $b ) ) {
+ if ( (int) $a !== (int) $b ) {
+ return $a - $b;
+ }
+ } else {
+ return -1;
+ }
+ } elseif ( ctype_digit( $b ) ) {
+ return 1;
+ } else {
+ $tmp = strcmp( $a, $b );
+ if ( 0 !== $tmp ) {
+ return $tmp;
+ }
+ }
+ }
+ return $al - $bl;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/CHANGELOG.md
new file mode 100644
index 00000000..571fa485
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/CHANGELOG.md
@@ -0,0 +1,114 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.2.0] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+- Updated package textdomain from `jetpack` to `jetpack-backup-pkg`.
+
+## [1.1.11] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.1.10] - 2021-11-30
+### Changed
+- Updated package dependencies.
+
+## [1.1.9] - 2021-11-23
+### Changed
+- Updated package dependencies.
+
+## [1.1.8] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.1.7] - 2021-10-26
+### Changed
+- Updated package dependencies.
+
+## [1.1.6] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.1.5] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.1.4] - 2021-09-28
+### Fixed
+- Register WP hooks even if WP isn't loaded yet.
+
+## [1.1.3] - 2021-08-31
+### Changed
+- Bump changelogger version
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- Updated package dependencies.
+
+## [1.1.2] - 2021-08-12
+### Added
+- Add package version tracking.
+
+## [1.1.1] - 2021-07-27
+### Added
+- Add a package version constant.
+
+### Changed
+- Updated package dependencies.
+
+## [1.1.0] - 2021-06-29
+### Added
+- Add backup-helper-script endpoints under the jetpack/v4 namespace.
+- Add backup real time endpoints.
+
+## [1.0.6] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.0.5] - 2021-04-27
+### Changed
+- Updated package dependencies.
+
+## [1.0.4] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+## [1.0.3] - 2021-01-19
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.0.2] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## 1.0.0 - 2019-10-29
+
+- Add API endpoints and Jetpack Backup package for managing Help…
+
+[1.2.0]: https://github.com/Automattic/jetpack-backup/compare/v1.1.11...v1.2.0
+[1.1.11]: https://github.com/Automattic/jetpack-backup/compare/v1.1.10...v1.1.11
+[1.1.10]: https://github.com/Automattic/jetpack-backup/compare/v1.1.9...v1.1.10
+[1.1.9]: https://github.com/Automattic/jetpack-backup/compare/v1.1.8...v1.1.9
+[1.1.8]: https://github.com/Automattic/jetpack-backup/compare/v1.1.7...v1.1.8
+[1.1.7]: https://github.com/Automattic/jetpack-backup/compare/v1.1.6...v1.1.7
+[1.1.6]: https://github.com/Automattic/jetpack-backup/compare/v1.1.5...v1.1.6
+[1.1.5]: https://github.com/Automattic/jetpack-backup/compare/v1.1.4...v1.1.5
+[1.1.4]: https://github.com/Automattic/jetpack-backup/compare/v1.1.3...v1.1.4
+[1.1.3]: https://github.com/Automattic/jetpack-backup/compare/v1.1.2...v1.1.3
+[1.1.2]: https://github.com/Automattic/jetpack-backup/compare/v1.1.1...v1.1.2
+[1.1.1]: https://github.com/Automattic/jetpack-backup/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-backup/compare/v1.0.6...v1.1.0
+[1.0.6]: https://github.com/Automattic/jetpack-backup/compare/v1.0.5...v1.0.6
+[1.0.5]: https://github.com/Automattic/jetpack-backup/compare/v1.0.4...v1.0.5
+[1.0.4]: https://github.com/Automattic/jetpack-backup/compare/v1.0.3...v1.0.4
+[1.0.3]: https://github.com/Automattic/jetpack-backup/compare/v1.0.2...v1.0.3
+[1.0.2]: https://github.com/Automattic/jetpack-backup/compare/v1.0.0...v1.0.2
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/actions.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/actions.php
new file mode 100644
index 00000000..b9ae6aa5
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/actions.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Action Hooks for Jetpack Backup module.
+ *
+ * @package automattic/jetpack-backup
+ */
+
+// If WordPress's plugin API is available already, use it. If not,
+// drop data into `$wp_filter` for `WP_Hook::build_preinitialized_hooks()`.
+if ( function_exists( 'add_filter' ) ) {
+ $add_filter = 'add_filter';
+ $add_action = 'add_action';
+} else {
+ $add_filter = function ( $name, $cb, $priority = 10, $accepted_args = 1 ) {
+ global $wp_filter;
+ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $wp_filter[ $name ][ $priority ][] = array(
+ 'accepted_args' => $accepted_args,
+ 'function' => $cb,
+ );
+ };
+ $add_action = $add_filter;
+}
+
+// Clean up expired Helper Scripts from a scheduled event.
+$add_action( 'jetpack_backup_cleanup_helper_scripts', array( 'Automattic\\Jetpack\\Backup\\Helper_Script_Manager', 'cleanup_expired_helper_scripts' ) );
+
+// Register REST routes.
+$add_action( 'rest_api_init', array( 'Automattic\\Jetpack\\Backup\\REST_Controller', 'register_rest_routes' ) );
+
+// Set up package version hook.
+$add_filter( 'jetpack_package_versions', 'Automattic\\Jetpack\\Backup\\Package_Version::send_package_version_to_tracker' );
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/class-helper-script-manager.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/class-helper-script-manager.php
new file mode 100644
index 00000000..58c54eb4
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/class-helper-script-manager.php
@@ -0,0 +1,347 @@
+<?php
+/**
+ * The Jetpack Backup Helper Script Manager class.
+ *
+ * @package automattic/jetpack-backup
+ */
+
+namespace Automattic\Jetpack\Backup;
+
+/**
+ * Helper_Script_Manager manages installation, deletion and cleanup of Helper Scripts
+ * to assist with backing up Jetpack Sites.
+ */
+class Helper_Script_Manager {
+
+ const TEMP_DIRECTORY = 'jetpack-temp';
+ const HELPER_HEADER = "<?php /* Jetpack Backup Helper Script */\n";
+ const EXPIRY_TIME = 8 * 3600; // 8 hours
+ const MAX_FILESIZE = 1024 * 1024; // 1 MiB
+
+ const README_LINES = array(
+ 'These files have been put on your server by Jetpack to assist with backups and restores of your site content. They are cleaned up automatically when we no longer need them.',
+ 'If you no longer have Jetpack connected to your site, you can delete them manually.',
+ 'If you have questions or need assistance, please contact Jetpack Support at https://jetpack.com/support/',
+ 'If you like to build amazing things with WordPress, you should visit automattic.com/jobs and apply to join the fun – mention this file when you apply!;',
+ );
+
+ const INDEX_FILE = '<?php // Silence is golden';
+
+ /**
+ * Installs a Helper Script, and returns its filesystem path and access url.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $script_body Helper Script file contents.
+ * @return array|WP_Error Either an array containing the path and url of the helper script, or an error.
+ */
+ public static function install_helper_script( $script_body ) {
+ // Check that the script body contains the correct header.
+ if ( strncmp( $script_body, self::HELPER_HEADER, strlen( self::HELPER_HEADER ) ) !== 0 ) {
+ return new \WP_Error( 'invalid_helper', 'Invalid Helper Script header' );
+ }
+
+ // Refuse to install a Helper Script that is too large.
+ if ( strlen( $script_body ) > self::MAX_FILESIZE ) {
+ return new \WP_Error( 'invalid_helper', 'Invalid Helper Script size' );
+ }
+
+ // Replace '[wp_path]' in the Helper Script with the WordPress installation location. Allows the Helper Script to find WordPress.
+ $script_body = str_replace( '[wp_path]', addslashes( ABSPATH ), $script_body );
+
+ // Create a jetpack-temp directory for the Helper Script.
+ $temp_directory = self::create_temp_directory();
+ if ( \is_wp_error( $temp_directory ) ) {
+ return $temp_directory;
+ }
+
+ // Generate a random filename, avoid clashes.
+ $max_attempts = 5;
+ for ( $attempt = 0; $attempt < $max_attempts; $attempt++ ) {
+ $file_key = wp_generate_password( 10, false );
+ $file_name = 'jp-helper-' . $file_key . '.php';
+ $file_path = trailingslashit( $temp_directory['path'] ) . $file_name;
+
+ if ( ! file_exists( $file_path ) ) {
+ // Attempt to write helper script.
+ if ( ! self::put_contents( $file_path, $script_body ) ) {
+ if ( file_exists( $file_path ) ) {
+ unlink( $file_path );
+ }
+
+ continue;
+ }
+
+ // Always schedule a cleanup run shortly after EXPIRY_TIME.
+ \wp_schedule_single_event( time() + self::EXPIRY_TIME + 60, 'jetpack_backup_cleanup_helper_scripts' );
+
+ // Success! Figure out the URL and return the path and URL.
+ return array(
+ 'path' => $file_path,
+ 'url' => trailingslashit( $temp_directory['url'] ) . $file_name,
+ );
+ }
+ }
+
+ return new \WP_Error( 'install_faied', 'Failed to install Helper Script' );
+ }
+
+ /**
+ * Given a path, verify it looks like a helper script and then delete it if so.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $path Path to Helper Script to delete.
+ * @return boolean True if the file is deleted (or does not exist).
+ */
+ public static function delete_helper_script( $path ) {
+ if ( ! file_exists( $path ) ) {
+ return true;
+ }
+
+ // Check this file looks like a JPR helper script.
+ if ( ! self::verify_file_header( $path, self::HELPER_HEADER ) ) {
+ return false;
+ }
+
+ return unlink( $path );
+ }
+
+ /**
+ * Search for Helper Scripts that are suspiciously old, and clean them out.
+ *
+ * @access public
+ * @static
+ */
+ public static function cleanup_expired_helper_scripts() {
+ self::cleanup_helper_scripts( time() - self::EXPIRY_TIME );
+ }
+
+ /**
+ * Search for and delete all Helper Scripts. Used during uninstallation.
+ *
+ * @access public
+ * @static
+ */
+ public static function delete_all_helper_scripts() {
+ self::cleanup_helper_scripts( null );
+ }
+
+ /**
+ * Search for and delete Helper Scripts. If an $expiry_time is specified, only delete Helper Scripts
+ * with an mtime older than $expiry_time. Otherwise, delete them all.
+ *
+ * @access public
+ * @static
+ *
+ * @param int|null $expiry_time If specified, only delete scripts older than $expiry_time.
+ */
+ public static function cleanup_helper_scripts( $expiry_time = null ) {
+ foreach ( self::get_install_locations() as $directory => $url ) {
+ $temp_dir = trailingslashit( $directory ) . self::TEMP_DIRECTORY;
+
+ if ( is_dir( $temp_dir ) ) {
+ // Find expired helper scripts and delete them.
+ $helper_scripts = glob( trailingslashit( $temp_dir ) . 'jp-helper-*.php' );
+ if ( is_array( $helper_scripts ) ) {
+ foreach ( $helper_scripts as $filename ) {
+ if ( null === $expiry_time || filemtime( $filename ) < $expiry_time ) {
+ self::delete_helper_script( $filename );
+ }
+ }
+ }
+
+ // Delete the directory if it's empty now.
+ self::delete_empty_helper_directory( $temp_dir );
+ }
+ }
+ }
+
+ /**
+ * Delete a helper script directory if it's empty
+ *
+ * @access public
+ * @static
+ *
+ * @param string $dir Path to Helper Script directory.
+ * @return boolean True if the directory is deleted
+ */
+ private static function delete_empty_helper_directory( $dir ) {
+ if ( ! is_dir( $dir ) ) {
+ return false;
+ }
+
+ // Tally the files in the target directory, and reject if there are too many.
+ $glob_path = trailingslashit( $dir ) . '*';
+ $dir_contents = glob( $glob_path );
+ if ( count( $dir_contents ) > 2 ) {
+ return false;
+ }
+
+ // Check that the only remaining files are a README and index.php generated by this system.
+ $allowed_files = array(
+ 'README' => self::README_LINES[0],
+ 'index.php' => self::INDEX_FILE,
+ );
+
+ foreach ( $dir_contents as $path ) {
+ $basename = basename( $path );
+ if ( ! isset( $allowed_files[ $basename ] ) ) {
+ return false;
+ }
+
+ // Verify the file starts with the expected contents.
+ if ( ! self::verify_file_header( $path, $allowed_files[ $basename ] ) ) {
+ return false;
+ }
+
+ if ( ! unlink( $path ) ) {
+ return false;
+ }
+ }
+
+ // If the directory is now empty, delete it.
+ if ( count( glob( $glob_path ) ) === 0 ) {
+ return rmdir( $dir );
+ }
+
+ return false;
+ }
+
+ /**
+ * Find an appropriate location for a jetpack-temp folder, and create one
+ *
+ * @access public
+ * @static
+ *
+ * @return WP_Error|array Array containing the url and path of the temp directory if successful, WP_Error if not.
+ */
+ private static function create_temp_directory() {
+ foreach ( self::get_install_locations() as $directory => $url ) {
+ // Check if the install location is writeable.
+ if ( ! is_writeable( $directory ) ) {
+ continue;
+ }
+
+ // Create if one doesn't already exist.
+ $temp_dir = trailingslashit( $directory ) . self::TEMP_DIRECTORY;
+ if ( ! is_dir( $temp_dir ) ) {
+ if ( ! mkdir( $temp_dir ) ) {
+ continue;
+ }
+
+ // Temp directory created. Drop a README and index.php file in there.
+ self::write_supplementary_temp_files( $temp_dir );
+ }
+
+ return array(
+ 'path' => trailingslashit( $directory ) . self::TEMP_DIRECTORY,
+ 'url' => trailingslashit( $url ) . self::TEMP_DIRECTORY,
+ );
+ }
+
+ return new \WP_Error( 'temp_directory', 'Failed to create jetpack-temp directory' );
+ }
+
+ /**
+ * Write out an index.php file and a README file for a new jetpack-temp directory.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $dir Path to Helper Script directory.
+ */
+ private static function write_supplementary_temp_files( $dir ) {
+ $readme_path = trailingslashit( $dir ) . 'README';
+ self::put_contents( $readme_path, implode( "\n\n", self::README_LINES ) );
+
+ $index_path = trailingslashit( $dir ) . 'index.php';
+ self::put_contents( $index_path, self::INDEX_FILE );
+ }
+
+ /**
+ * Write a file to the specified location with the specified contents.
+ *
+ * @access private
+ * @static
+ *
+ * @param string $file_path Path to write to.
+ * @param string $contents File contents to write.
+ * @return boolean True if successfully written.
+ */
+ private static function put_contents( $file_path, $contents ) {
+ global $wp_filesystem;
+
+ if ( ! function_exists( '\\WP_Filesystem' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/file.php';
+ }
+
+ if ( ! \WP_Filesystem() ) {
+ return false;
+ }
+
+ return $wp_filesystem->put_contents( $file_path, $contents );
+ }
+
+ /**
+ * Checks that a file exists, is readable, and has the expected header.
+ *
+ * @access private
+ * @static
+ *
+ * @param string $file_path File to verify.
+ * @param string $expected_header Header that the file should have.
+ * @return boolean True if the file exists, is readable, and the header matches.
+ */
+ private static function verify_file_header( $file_path, $expected_header ) {
+ global $wp_filesystem;
+
+ if ( ! function_exists( '\\WP_Filesystem' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/file.php';
+ }
+
+ if ( ! \WP_Filesystem() ) {
+ return false;
+ }
+
+ // Verify the file exists and is readable.
+ if ( ! $wp_filesystem->exists( $file_path ) || ! $wp_filesystem->is_readable( $file_path ) ) {
+ return false;
+ }
+
+ // Verify that the file isn't too big or small.
+ $file_size = $wp_filesystem->size( $file_path );
+ if ( $file_size < strlen( $expected_header ) || $file_size > self::MAX_FILESIZE ) {
+ return false;
+ }
+
+ // Read the file and verify its header.
+ $contents = $wp_filesystem->get_contents( $file_path );
+ return ( strncmp( $contents, $expected_header, strlen( $expected_header ) ) === 0 );
+ }
+
+ /**
+ * Gets an associative array of possible places to install a jetpack-temp directory, along with the URL to access each.
+ *
+ * @access private
+ * @static
+ *
+ * @return array Array, with keys specifying the full path of install locations, and values with the equivalent URL.
+ */
+ public static function get_install_locations() {
+ // Include WordPress root and wp-content.
+ $install_locations = array(
+ \ABSPATH => \get_site_url(),
+ \WP_CONTENT_DIR => \WP_CONTENT_URL,
+ );
+
+ // Include uploads folder.
+ $upload_dir_info = \wp_upload_dir();
+ $install_locations[ $upload_dir_info['basedir'] ] = $upload_dir_info['baseurl'];
+
+ return $install_locations;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/class-package-version.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/class-package-version.php
new file mode 100644
index 00000000..b38415bf
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/class-package-version.php
@@ -0,0 +1,31 @@
+<?php
+/**
+ * The Package_Version class.
+ *
+ * @package automattic/jetpack-backup
+ */
+
+namespace Automattic\Jetpack\Backup;
+
+/**
+ * The Package_Version class.
+ */
+class Package_Version {
+
+ const PACKAGE_VERSION = '1.2.0';
+
+ const PACKAGE_SLUG = 'backup';
+
+ /**
+ * Adds the package slug and version to the package version tracker's data.
+ *
+ * @param array $package_versions The package version array.
+ *
+ * @return array The packge version array.
+ */
+ public static function send_package_version_to_tracker( $package_versions ) {
+ $package_versions[ self::PACKAGE_SLUG ] = self::PACKAGE_VERSION;
+ return $package_versions;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/class-rest-controller.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/class-rest-controller.php
new file mode 100644
index 00000000..087aba84
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-backup/src/class-rest-controller.php
@@ -0,0 +1,526 @@
+<?php
+/**
+ * The Backup Rest Controller class.
+ * Registers the REST routes for Backup.
+ *
+ * @package automattic/jetpack-backup
+ */
+
+namespace Automattic\Jetpack\Backup;
+
+use Automattic\Jetpack\Connection\Rest_Authentication;
+use Automattic\Jetpack\Sync\Actions as Sync_Actions;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Server;
+
+/**
+ * Registers the REST routes for Backup.
+ */
+class REST_Controller {
+ /**
+ * Registers the REST routes for Backup.
+ *
+ * @access public
+ * @static
+ */
+ public static function register_rest_routes() {
+ // Install a Helper Script to assist Jetpack Backup fetch data.
+ register_rest_route(
+ 'jetpack/v4',
+ '/backup-helper-script',
+ array(
+ 'methods' => WP_REST_Server::CREATABLE,
+ 'callback' => __CLASS__ . '::install_backup_helper_script',
+ 'permission_callback' => __CLASS__ . '::backup_permissions_callback',
+ 'args' => array(
+ 'helper' => array(
+ 'description' => __( 'base64 encoded Backup Helper Script body.', 'jetpack-backup-pkg' ),
+ 'type' => 'string',
+ 'required' => true,
+ ),
+ ),
+ )
+ );
+
+ // Delete a Backup Helper Script.
+ register_rest_route(
+ 'jetpack/v4',
+ '/backup-helper-script',
+ array(
+ 'methods' => WP_REST_Server::DELETABLE,
+ 'callback' => __CLASS__ . '::delete_backup_helper_script',
+ 'permission_callback' => __CLASS__ . '::backup_permissions_callback',
+ 'args' => array(
+ 'path' => array(
+ 'description' => __( 'Path to Backup Helper Script', 'jetpack-backup-pkg' ),
+ 'type' => 'string',
+ 'required' => true,
+ ),
+ ),
+ )
+ );
+
+ // Fetch a backup of a database object, along with all of its metadata.
+ register_rest_route(
+ 'jetpack/v4',
+ '/database-object/backup',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::fetch_database_object_backup',
+ 'permission_callback' => __CLASS__ . '::backup_permissions_callback',
+ 'args' => array(
+ 'object_type' => array(
+ 'description' => __( 'Type of object to fetch from the database', 'jetpack-backup-pkg' ),
+ 'required' => true,
+ 'validate_callback' => function ( $value ) {
+ if ( ! is_string( $value ) ) {
+ return new WP_Error(
+ 'rest_invalid_param',
+ __( 'The object_type argument must be a non-empty string.', 'jetpack-backup-pkg' ),
+ array( 'status' => 400 )
+ );
+ }
+
+ $allowed_object_types = array_keys( self::get_allowed_object_types() );
+
+ if ( ! in_array( $value, $allowed_object_types, true ) ) {
+ return new WP_Error(
+ 'rest_invalid_param',
+ sprintf(
+ /* translators: %s: comma-separated list of allowed object types */
+ __( 'The object_type argument should be one of %s', 'jetpack-backup-pkg' ),
+ implode( ', ', $allowed_object_types )
+ ),
+ array( 'status' => 400 )
+ );
+ }
+
+ return true;
+ },
+ ),
+ 'object_id' => array(
+ 'description' => __( 'ID of the database object to fetch', 'jetpack-backup-pkg' ),
+ 'type' => 'integer',
+ 'required' => true,
+ ),
+ ),
+ )
+ );
+
+ // Fetch a backup of an option.
+ register_rest_route(
+ 'jetpack/v4',
+ '/options/backup',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::fetch_options_backup',
+ 'permission_callback' => __CLASS__ . '::backup_permissions_callback',
+ 'args' => array(
+ 'name' => array(
+ 'description' => __( 'One or more option names to include in the backup', 'jetpack-backup-pkg' ),
+ 'validate_callback' => function ( $value ) {
+ $is_valid = is_array( $value ) || is_string( $value );
+ if ( ! $is_valid ) {
+ return new WP_Error( 'rest_invalid_param', __( 'The name argument should be an option name or an array of option names', 'jetpack-backup-pkg' ), array( 'status' => 400 ) );
+ }
+
+ return true;
+ },
+ 'required' => true,
+ ),
+ ),
+ )
+ );
+
+ // Fetch a backup of a comment, along with all of its metadata.
+ register_rest_route(
+ 'jetpack/v4',
+ '/comments/(?P<id>\d+)/backup',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::fetch_comment_backup',
+ 'permission_callback' => __CLASS__ . '::backup_permissions_callback',
+ )
+ );
+
+ // Fetch a backup of a post, along with all of its metadata.
+ register_rest_route(
+ 'jetpack/v4',
+ '/posts/(?P<id>\d+)/backup',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::fetch_post_backup',
+ 'permission_callback' => __CLASS__ . '::backup_permissions_callback',
+ )
+ );
+
+ // Fetch a backup of a term, along with all of its metadata.
+ register_rest_route(
+ 'jetpack/v4',
+ '/terms/(?P<id>\d+)/backup',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::fetch_term_backup',
+ 'permission_callback' => __CLASS__ . '::backup_permissions_callback',
+ )
+ );
+
+ // Fetch a backup of a user, along with all of its metadata.
+ register_rest_route(
+ 'jetpack/v4',
+ '/users/(?P<id>\d+)/backup',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::fetch_user_backup',
+ 'permission_callback' => __CLASS__ . '::backup_permissions_callback',
+ )
+ );
+ }
+
+ /**
+ * The Backup endpoints should only be available via site-level authentication.
+ * This means that the corresponding endpoints can only be accessible from WPCOM.
+ *
+ * @access public
+ * @static
+ *
+ * @return bool|WP_Error True if a blog token was used to sign the request, WP_Error otherwise.
+ */
+ public static function backup_permissions_callback() {
+ if ( Rest_Authentication::is_signed_with_blog_token() ) {
+ return true;
+ }
+
+ $error_msg = esc_html__(
+ 'You are not allowed to perform this action.',
+ 'jetpack-backup-pkg'
+ );
+
+ return new WP_Error( 'rest_forbidden', $error_msg, array( 'status' => rest_authorization_required_code() ) );
+ }
+
+ /**
+ * Install the Backup Helper Script.
+ *
+ * @access public
+ * @static
+ *
+ * @param WP_REST_Request $request The request sent to the WP REST API.
+ * @return array|WP_Error Returns the result of Helper Script installation. Returns one of:
+ * - WP_Error on failure, or
+ * - An array with installation info on success:
+ * 'path' (string) The sinstallation path.
+ * 'url' (string) The access url.
+ * 'abspath' (string) The abspath.
+ */
+ public static function install_backup_helper_script( $request ) {
+ $helper_script = $request->get_param( 'helper' );
+
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
+ $helper_script = base64_decode( $helper_script );
+ if ( ! $helper_script ) {
+ return new WP_Error( 'invalid_args', __( 'Helper script body must be base64 encoded', 'jetpack-backup-pkg' ), 400 );
+ }
+
+ $installation_info = Helper_Script_Manager::install_helper_script( $helper_script );
+ Helper_Script_Manager::cleanup_expired_helper_scripts();
+
+ // Include ABSPATH with successful result.
+ if ( ! is_wp_error( $installation_info ) ) {
+ $installation_info['abspath'] = ABSPATH;
+ }
+
+ return rest_ensure_response( $installation_info );
+ }
+
+ /**
+ * Delete a Backup Helper Script.
+ *
+ * @access public
+ * @static
+ *
+ * @param WP_REST_Request $request The request sent to the WP REST API.
+ * @return array An array with 'success' key indicating the result of the delete operation.
+ */
+ public static function delete_backup_helper_script( $request ) {
+ $path_to_helper_script = $request->get_param( 'path' );
+
+ $deleted = Helper_Script_Manager::delete_helper_script( $path_to_helper_script );
+ Helper_Script_Manager::cleanup_expired_helper_scripts();
+
+ return rest_ensure_response(
+ array(
+ 'success' => $deleted,
+ )
+ );
+ }
+
+ /**
+ * Fetch a backup of a database object, along with all of its metadata.
+ *
+ * @access public
+ * @static
+ *
+ * @param WP_REST_Request $request The request sent to the WP REST API.
+ * @return array
+ */
+ public static function fetch_database_object_backup( $request ) {
+ global $wpdb;
+
+ // Disable Sync as this is a read-only operation and triggered by sync activity.
+ Sync_Actions::mark_sync_read_only();
+
+ $allowed_object_types = self::get_allowed_object_types();
+ // Safe to do this as we have already validated the object_type key exists in self::get_allowed_object_types().
+ $object_type = $allowed_object_types[ $request->get_param( 'object_type' ) ];
+ $object_id = $request->get_param( 'object_id' );
+ $table = $wpdb->prefix . $object_type['table'];
+ $id_field = $object_type['id_field'];
+
+ // Fetch the requested object.
+ $object = $wpdb->get_row(
+ $wpdb->prepare(
+ 'SELECT * FROM `' . $table . '` WHERE `' . $id_field . '` = %d', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $object_id
+ )
+ );
+
+ if ( empty( $object ) ) {
+ return new WP_Error( 'object_not_found', __( 'Object not found', 'jetpack-backup-pkg' ), array( 'status' => 404 ) );
+ }
+
+ $result = array( 'object' => $object );
+
+ // Fetch associated metadata (if this object type has any).
+ if ( ! empty( $object_type['meta_type'] ) ) {
+ $result['meta'] = get_metadata( $object_type['meta_type'], $object_id );
+ }
+
+ // If there is a child linked table (eg: woocommerce_tax_rate_locations), fetch linked records.
+ if ( ! empty( $object_type['child_table'] ) ) {
+ $child_table = $wpdb->prefix . $object_type['child_table'];
+ $child_id_field = $object_type['child_id_field'];
+
+ $result['children'] = $wpdb->get_results(
+ $wpdb->prepare(
+ 'SELECT * FROM `' . $child_table . '` where `' . $child_id_field . '` = %d', // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $object_id
+ )
+ );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Fetch a backup of an option.
+ *
+ * @access public
+ * @static
+ *
+ * @param WP_REST_Request $request The request sent to the WP REST API.
+ * @return array
+ */
+ public static function fetch_options_backup( $request ) {
+ // Disable Sync as this is a read-only operation and triggered by sync activity.
+ Sync_Actions::mark_sync_read_only();
+
+ $option_names = (array) $request->get_param( 'name' );
+
+ $options = array_map( 'self::get_option_row', $option_names );
+ return array( 'options' => $options );
+ }
+
+ /**
+ * Fetch a backup of a comment, along with all of its metadata.
+ *
+ * @access public
+ * @static
+ *
+ * @param WP_REST_Request $request The request sent to the WP REST API.
+ * @return array
+ */
+ public static function fetch_comment_backup( $request ) {
+ // Disable Sync as this is a read-only operation and triggered by sync activity.
+ Sync_Actions::mark_sync_read_only();
+
+ $comment_id = $request['id'];
+ $comment = get_comment( $comment_id );
+
+ if ( empty( $comment ) ) {
+ return new WP_Error( 'comment_not_found', __( 'Comment not found', 'jetpack-backup-pkg' ), array( 'status' => 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(),
+ );
+ }
+
+ /**
+ * Fetch a backup of a post, along with all of its metadata.
+ *
+ * @access public
+ * @static
+ *
+ * @param WP_REST_Request $request The request sent to the WP REST API.
+ * @return array
+ */
+ public static function fetch_post_backup( $request ) {
+ global $wpdb;
+
+ // Disable Sync as this is a read-only operation and triggered by sync activity.
+ Sync_Actions::mark_sync_read_only();
+
+ $post_id = $request['id'];
+ $post = get_post( $post_id );
+
+ if ( empty( $post ) ) {
+ return new WP_Error( 'post_not_found', __( 'Post not found', 'jetpack-backup-pkg' ), array( 'status' => 404 ) );
+ }
+
+ // Fetch terms associated with this post object.
+ $terms = $wpdb->get_results(
+ $wpdb->prepare(
+ "SELECT term_taxonomy_id, term_order FROM {$wpdb->term_relationships} WHERE object_id = %d;",
+ $post->ID
+ )
+ );
+
+ return array(
+ 'post' => (array) $post,
+ 'meta' => get_post_meta( $post->ID ),
+ 'terms' => (array) $terms,
+ );
+ }
+
+ /**
+ * Fetch a backup of a term, along with all of its metadata.
+ *
+ * @access public
+ * @static
+ *
+ * @param WP_REST_Request $request The request sent to the WP REST API.
+ * @return array
+ */
+ public static function fetch_term_backup( $request ) {
+ // Disable Sync as this is a read-only operation and triggered by sync activity.
+ Sync_Actions::mark_sync_read_only();
+
+ $term_id = $request['id'];
+ $term = get_term( $term_id );
+
+ if ( empty( $term ) ) {
+ return new WP_Error( 'term_not_found', __( 'Term not found', 'jetpack-backup-pkg' ), array( 'status' => 404 ) );
+ }
+
+ return array(
+ 'term' => (array) $term,
+ 'meta' => get_term_meta( $term_id ),
+ );
+ }
+
+ /**
+ * Fetch a backup of a user, along with all of its metadata.
+ *
+ * @access public
+ * @static
+ *
+ * @param WP_REST_Request $request The request sent to the WP REST API.
+ * @return array
+ */
+ public static function fetch_user_backup( $request ) {
+ // Disable Sync as this is a read-only operation and triggered by sync activity.
+ Sync_Actions::mark_sync_read_only();
+
+ $user_id = $request['id'];
+ $user = get_user_by( 'id', $user_id );
+
+ if ( empty( $user ) ) {
+ return new WP_Error( 'user_not_found', __( 'User not found', 'jetpack-backup-pkg' ), array( 'status' => 404 ) );
+ }
+
+ return array(
+ 'user' => $user->to_array(),
+ 'meta' => get_user_meta( $user->ID ),
+ );
+ }
+
+ /**
+ * Get allowed object types for the '/database-object/backup' endpoint.
+ *
+ * @access private
+ * @static
+ *
+ * @return array
+ */
+ private static function get_allowed_object_types() {
+ return 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',
+ ),
+ );
+ }
+
+ /**
+ * Fetch option row by option name.
+ *
+ * @access private
+ * @static
+ *
+ * @param string $name The option name.
+ * @return object|null Database query result as object format specified or null on failure.
+ */
+ private static 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/jetpack_vendor/automattic/jetpack-blocks/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/CHANGELOG.md
new file mode 100644
index 00000000..83a23092
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/CHANGELOG.md
@@ -0,0 +1,110 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.4.9] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+
+## [1.4.8] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.4.7] - 2021-11-23
+### Changed
+- Updated package dependencies
+
+## [1.4.6] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.4.5] - 2021-10-19
+### Changed
+- Updated package dependencies.
+
+## [1.4.4] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.4.3] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.4.2] - 2021-08-31
+### Changed
+- Run composer update on test-php command instead of phpunit.
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- Update annotations versions.
+
+## [1.4.1] - 2021-06-29
+### Changed
+- Update docs to replace yarn with pnpm.
+
+## [1.4.0] - 2021-05-12
+### Added
+- Add helper method to determine if the current theme is an FSE/Site editor theme.
+- Adds an attribute to paid blocks to support hiding nested upgrade nudges on the frontend.
+
+### Changed
+- Updated package dependencies.
+
+## [1.3.0] - 2021-03-22
+### Added
+- Composer alias for dev-master, to improve dependencies
+- Enable GitHub action for auto-tagging releases from monorepo pushes.
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Add editor style dependency when registering Jetpack blocks to ensure support for the new site editor.
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.2.2] - 2021-02-05
+
+- CI: Make tests more generic
+
+## [1.2.1] - 2021-01-20
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.2.0] - 2020-12-07
+
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.1.1] - 2020-11-13
+
+- Codecoverage: fix reports
+- Updated PHPCS: Packages and Debugger
+
+## [1.1.0] - 2020-09-25
+
+- Blocks: add block registration to package
+
+## 1.0.0 - 2020-09-17
+
+- Blocks: introduce new package for block management
+
+[1.4.9]: https://github.com/Automattic/jetpack-blocks/compare/v1.4.8...v1.4.9
+[1.4.8]: https://github.com/Automattic/jetpack-blocks/compare/v1.4.7...v1.4.8
+[1.4.7]: https://github.com/Automattic/jetpack-blocks/compare/v1.4.6...v1.4.7
+[1.4.6]: https://github.com/Automattic/jetpack-blocks/compare/v1.4.5...v1.4.6
+[1.4.5]: https://github.com/Automattic/jetpack-blocks/compare/v1.4.4...v1.4.5
+[1.4.4]: https://github.com/Automattic/jetpack-blocks/compare/v1.4.3...v1.4.4
+[1.4.3]: https://github.com/Automattic/jetpack-blocks/compare/v1.4.2...v1.4.3
+[1.4.2]: https://github.com/Automattic/jetpack-blocks/compare/v1.4.1...v1.4.2
+[1.4.1]: https://github.com/Automattic/jetpack-blocks/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/Automattic/jetpack-blocks/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/Automattic/jetpack-blocks/compare/v1.2.2...v1.3.0
+[1.2.2]: https://github.com/Automattic/jetpack-blocks/compare/v1.2.1...v1.2.2
+[1.2.1]: https://github.com/Automattic/jetpack-blocks/compare/v1.2.0...v1.2.1
+[1.2.0]: https://github.com/Automattic/jetpack-blocks/compare/v1.1.1...v1.2.0
+[1.1.1]: https://github.com/Automattic/jetpack-blocks/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-blocks/compare/v1.0.0...v1.1.0
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/src/class-blocks.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/src/class-blocks.php
new file mode 100644
index 00000000..2ddf6f3d
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-blocks/src/class-blocks.php
@@ -0,0 +1,291 @@
+<?php
+/** Blocks package.
+ *
+ * @since 1.1.0
+ *
+ * This package lifts elements from Jetpack's Jetpack_Gutenberg class.
+ * It is now an standalone package reusable outside Jetpack.
+ *
+ * @package automattic/jetpack-blocks
+ */
+
+namespace Automattic\Jetpack;
+
+use Jetpack_Gutenberg;
+
+/**
+ * Register and manage blocks within a plugin. Used to manage block registration, enqueues, and more.
+ *
+ * @since 1.1.0
+ */
+class Blocks {
+ /**
+ * Wrapper function to safely register a Gutenberg block type
+ *
+ * @see register_block_type
+ * @see Automattic\Jetpack\Blocks::is_gutenberg_version_available
+ *
+ * @since 1.1.0
+ *
+ * @param string $slug Slug of the block.
+ * @param array $args {
+ * Arguments that are passed into register_block_type.
+ * See register_block_type for full list of arguments.
+ * Can also include 2 extra arguments not currently supported by register_block_type.
+ *
+ * @type array $version_requirements Array containing required Gutenberg version and, if known, the WordPress version that was released with this minimum version.
+ * @type bool $plan_check Should we check for a specific plan before registering the block.
+ * }
+ *
+ * @return WP_Block_Type|false The registered block type on success, or false on failure.
+ */
+ public static function jetpack_register_block( $slug, $args = array() ) {
+ if ( 0 !== strpos( $slug, 'jetpack/' ) && ! strpos( $slug, '/' ) ) {
+ _doing_it_wrong( 'jetpack_register_block', 'Prefix the block with jetpack/ ', 'Jetpack 9.0.0' );
+ $slug = 'jetpack/' . $slug;
+ }
+
+ if (
+ isset( $args['version_requirements'] )
+ && ! self::is_gutenberg_version_available( $args['version_requirements'], $slug )
+ ) {
+ return false;
+ }
+
+ // Checking whether block is registered to ensure it isn't registered twice.
+ if ( self::is_registered( $slug ) ) {
+ return false;
+ }
+
+ $feature_name = self::remove_extension_prefix( $slug );
+
+ // This is only useful in Jetpack.
+ if ( ! self::is_standalone_block() ) {
+ // If the block is dynamic, and a Jetpack block, wrap the render_callback to check availability.
+ if ( ! empty( $args['plan_check'] ) ) {
+ // Set up attributes.
+ if ( ! isset( $args['attributes'] ) ) {
+ $args['attributes'] = array();
+ }
+ $args['attributes'] = array_merge(
+ $args['attributes'],
+ array(
+ // Indicates that this block should display an upgrade nudge on the frontend when applicable.
+ 'shouldDisplayFrontendBanner' => array(
+ 'type' => 'boolean',
+ 'default' => true,
+ ),
+ )
+ );
+ if ( isset( $args['render_callback'] ) ) {
+ $args['render_callback'] = Jetpack_Gutenberg::get_render_callback_with_availability_check( $feature_name, $args['render_callback'] );
+ }
+ $method_name = 'set_availability_for_plan';
+ } else {
+ $method_name = 'set_extension_available';
+ }
+
+ add_action(
+ 'jetpack_register_gutenberg_extensions',
+ function () use ( $feature_name, $method_name ) {
+ call_user_func( array( 'Jetpack_Gutenberg', $method_name ), $feature_name );
+ }
+ );
+
+ // Ensure editor styles are registered so that the site editor knows about the
+ // editor style dependency when copying styles to the editor iframe.
+ if ( ! isset( $args['editor_style'] ) ) {
+ $args['editor_style'] = 'jetpack-blocks-editor';
+ }
+ }
+
+ return register_block_type( $slug, $args );
+ }
+
+ /**
+ * Check if an extension/block is already registered
+ *
+ * @since 1.1.0
+ *
+ * @param string $slug Name of extension/block to check.
+ *
+ * @return bool
+ */
+ public static function is_registered( $slug ) {
+ return \WP_Block_Type_Registry::get_instance()->is_registered( $slug );
+ }
+
+ /**
+ * Remove the 'jetpack/' or jetpack-' prefix from an extension name
+ *
+ * @since 1.1.0
+ *
+ * @param string $extension_name The extension name.
+ *
+ * @return string The unprefixed extension name.
+ */
+ public static function remove_extension_prefix( $extension_name ) {
+ if ( 0 === strpos( $extension_name, 'jetpack/' ) || 0 === strpos( $extension_name, 'jetpack-' ) ) {
+ return substr( $extension_name, strlen( 'jetpack/' ) );
+ }
+ return $extension_name;
+ }
+
+ /**
+ * Check to see if a minimum version of Gutenberg is available. Because a Gutenberg version is not available in
+ * php if the Gutenberg plugin is not installed, if we know which minimum WP release has the required version we can
+ * optionally fall back to that.
+ *
+ * @since 1.1.0
+ *
+ * @param array $version_requirements {
+ * An array containing the required Gutenberg version and, if known, the WordPress version that was released with this minimum version.
+ *
+ * @type string $gutenberg Gutenberg version.
+ * @type string $wp Optional. WordPress version.
+ * }
+ * @param string $slug The slug of the block or plugin that has the Gutenberg version requirement.
+ *
+ * @return boolean True if the version of Gutenberg required by the block or plugin is available.
+ */
+ public static function is_gutenberg_version_available( $version_requirements, $slug ) {
+ global $wp_version;
+
+ // Bail if we don't at least have the Gutenberg version requirement, the WP version is optional.
+ if ( empty( $version_requirements['gutenberg'] ) ) {
+ return false;
+ }
+
+ // If running a local dev build of Gutenberg plugin GUTENBERG_DEVELOPMENT_MODE is set so assume correct version.
+ if ( defined( 'GUTENBERG_DEVELOPMENT_MODE' ) && GUTENBERG_DEVELOPMENT_MODE ) {
+ return true;
+ }
+
+ $version_available = false;
+
+ // If running a production build of the Gutenberg plugin then GUTENBERG_VERSION is set, otherwise if WP version
+ // with required version of Gutenberg is known check that.
+ if ( defined( 'GUTENBERG_VERSION' ) ) {
+ $version_available = version_compare( GUTENBERG_VERSION, $version_requirements['gutenberg'], '>=' );
+ } elseif ( ! empty( $version_requirements['wp'] ) ) {
+ $version_available = version_compare( $wp_version, $version_requirements['wp'], '>=' );
+ }
+
+ if (
+ ! $version_available
+ && ! self::is_standalone_block() // This is only useful in Jetpack.
+ ) {
+ Jetpack_Gutenberg::set_extension_unavailable(
+ $slug,
+ 'incorrect_gutenberg_version',
+ array(
+ 'required_feature' => $slug,
+ 'required_version' => $version_requirements,
+ 'current_version' => array(
+ 'wp' => $wp_version,
+ 'gutenberg' => defined( 'GUTENBERG_VERSION' ) ? GUTENBERG_VERSION : null,
+ ),
+ )
+ );
+ }
+
+ return $version_available;
+ }
+
+ /**
+ * Get CSS classes for a block.
+ *
+ * @since 1.1.0
+ *
+ * @param string $slug Block slug.
+ * @param array $attr Block attributes.
+ * @param array $extra Potential extra classes you may want to provide.
+ *
+ * @return string $classes List of CSS classes for a block.
+ */
+ public static function classes( $slug, $attr, $extra = array() ) {
+ if ( empty( $slug ) ) {
+ return '';
+ }
+
+ // Basic block name class.
+ $classes = array(
+ 'wp-block-jetpack-' . $slug,
+ );
+
+ // Add alignment if provided.
+ if (
+ ! empty( $attr['align'] )
+ && in_array( $attr['align'], array( 'left', 'center', 'right', 'wide', 'full' ), true )
+ ) {
+ $classes[] = 'align' . $attr['align'];
+ }
+
+ // Add custom classes if provided in the block editor.
+ if ( ! empty( $attr['className'] ) ) {
+ $classes[] = $attr['className'];
+ }
+
+ // Add any extra classes.
+ if ( is_array( $extra ) && ! empty( $extra ) ) {
+ $classes = array_merge( $classes, array_filter( $extra ) );
+ }
+
+ return implode( ' ', $classes );
+ }
+
+ /**
+ * Does the page return AMP content.
+ *
+ * @since 1.1.0
+ *
+ * @return bool $is_amp_request Are we on an AMP view.
+ */
+ public static function is_amp_request() {
+ $is_amp_request = ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() );
+
+ /** This filter is documented in 3rd-party/class.jetpack-amp-support.php */
+ return apply_filters( 'jetpack_is_amp_request', $is_amp_request );
+ }
+
+ /**
+ * Is the current theme an FSE/Site Editor theme.
+ *
+ * @since 1.4.0
+ *
+ * @return bool True if the current theme is an FSE/Site Editor theme.
+ */
+ public static function is_fse_theme() {
+ $is_fse_theme = function_exists( 'gutenberg_is_fse_theme' ) && gutenberg_is_fse_theme();
+
+ /**
+ * Returns true if the current theme is an FSE/Site Editor theme.
+ *
+ * @since 1.4.0
+ *
+ * @param boolean $is_fse_theme Is the theme an FSE theme.
+ */
+ return apply_filters( 'jetpack_is_fse_theme', $is_fse_theme );
+ }
+
+ /**
+ * Check whether or the block being registered is a standalone block,
+ * running in a context outside of the Jetpack plugin.
+ *
+ * @since 1.3.0
+ *
+ * @return bool
+ */
+ public static function is_standalone_block() {
+ $is_standalone_block = ! class_exists( Jetpack_Gutenberg::class );
+
+ /**
+ * Returns true if the block is not being registered within a Jetpack plugin context.
+ *
+ * @since 1.3.0
+ *
+ * @param boolean $is_standalone_block Is the block running standalone versus as part of the Jetpack plugin.
+ */
+ return apply_filters( 'jetpack_is_standalone_block', $is_standalone_block );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/CHANGELOG.md
new file mode 100644
index 00000000..76993fe5
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/CHANGELOG.md
@@ -0,0 +1,119 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.6.8] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.6.7] - 2021-10-19
+### Changed
+- Updated package dependencies.
+
+## [1.6.6] - 2021-09-28
+### Fixed
+- Register WP hooks even if WP isn't loaded yet.
+
+## [1.6.5] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.6.4] - 2021-04-27
+### Changed
+- Updated package dependencies.
+
+## [1.6.3] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+## [1.6.2] - 2021-02-05
+
+- CI: Make tests more generic
+
+## [1.6.1] - 2021-01-19
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.6.0] - 2020-12-07
+
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.5.1] - 2020-10-28
+
+- Updated PHPCS: Packages and Debugger
+
+## [1.5.0] - 2020-10-01
+
+- API: Remove the constant `JETPACK_CLIENT__HTTPS`.
+
+## [1.4.0] - 2020-08-19
+
+- Compat Package: Fix method declaration compatibility
+
+## [1.3.0] - 2020-06-10
+
+- Various: Update use of whitelist/blacklist
+
+## [1.2.0] - 2020-04-28
+
+- Correct inline documentation "Array" type
+- Compat: use require_once instead of jetpack_require_lib()
+
+## [1.1.0] - 2020-03-10
+
+- Sync Package: Add readme skeleton (#14945)
+
+## [1.0.5] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## [1.0.4] - 2019-11-08
+
+- Move fix_url_for_bad_hosts from Jetpack class to Connection pa…
+
+## [1.0.3] - 2019-10-29
+
+- PHPCS: Rest of the packages
+- Update Prettier and reformat files
+
+## [1.0.2] - 2019-09-20
+
+- Docs: Unify usage of @package phpdoc tags
+- Janitorial: Remove the leading backslash from namespaces
+
+## [1.0.1] - 2019-09-14
+
+- Sync: Add return for deprecated Jetpack_Sync_Settings functions with expected return value
+
+## 1.0.0 - 2019-09-14
+
+- Jetpack 7.5: Back compatibility package
+
+[1.6.8]: https://github.com/Automattic/jetpack-compat/compare/v1.6.7...v1.6.8
+[1.6.7]: https://github.com/Automattic/jetpack-compat/compare/v1.6.6...v1.6.7
+[1.6.6]: https://github.com/Automattic/jetpack-compat/compare/v1.6.5...v1.6.6
+[1.6.5]: https://github.com/Automattic/jetpack-compat/compare/v1.6.4...v1.6.5
+[1.6.4]: https://github.com/Automattic/jetpack-compat/compare/v1.6.3...v1.6.4
+[1.6.3]: https://github.com/Automattic/jetpack-compat/compare/v1.6.2...v1.6.3
+[1.6.2]: https://github.com/Automattic/jetpack-compat/compare/v1.6.1...v1.6.2
+[1.6.1]: https://github.com/Automattic/jetpack-compat/compare/v1.6.0...v1.6.1
+[1.6.0]: https://github.com/Automattic/jetpack-compat/compare/v1.5.1...v1.6.0
+[1.5.1]: https://github.com/Automattic/jetpack-compat/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/Automattic/jetpack-compat/compare/v1.4.0...v1.5.0
+[1.4.0]: https://github.com/Automattic/jetpack-compat/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/Automattic/jetpack-compat/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-compat/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/Automattic/jetpack-compat/compare/v1.0.5...v1.1.0
+[1.0.5]: https://github.com/Automattic/jetpack-compat/compare/v1.0.4...v1.0.5
+[1.0.4]: https://github.com/Automattic/jetpack-compat/compare/v1.0.3...v1.0.4
+[1.0.3]: https://github.com/Automattic/jetpack-compat/compare/v1.0.2...v1.0.3
+[1.0.2]: https://github.com/Automattic/jetpack-compat/compare/v1.0.1...v1.0.2
+[1.0.1]: https://github.com/Automattic/jetpack-compat/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/functions.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/functions.php
new file mode 100644
index 00000000..42f73915
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/functions.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Legacy global scope functions.
+ *
+ * @package automattic/jetpack-compat
+ */
+
+// If WordPress's plugin API is available already, use it. If not,
+// drop data into `$wp_filter` for `WP_Hook::build_preinitialized_hooks()`.
+if ( function_exists( 'add_filter' ) ) {
+ $add_filter = 'add_filter';
+ $add_action = 'add_action';
+} else {
+ $add_filter = function ( $name, $cb, $priority = 10, $accepted_args = 1 ) {
+ global $wp_filter;
+ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $wp_filter[ $name ][ $priority ][] = array(
+ 'accepted_args' => $accepted_args,
+ 'function' => $cb,
+ );
+ };
+ $add_action = $add_filter;
+}
+
+/**
+ * Load necessary functions.
+ */
+function jetpack_compat_require_defined_functions() {
+ require_once __DIR__ . '/lib/tracks/client.php';
+}
+
+$add_action( 'plugins_loaded', 'jetpack_compat_require_defined_functions' );
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-client.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-client.php
new file mode 100644
index 00000000..6e0a8941
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-client.php
@@ -0,0 +1,89 @@
+<?php
+/**
+ * Jetpack Client
+ *
+ * Deprecated methods for Jetpack to act as client with wpcom, provided for back-compatibility.
+ *
+ * @category Connection
+ * @package automattic/jetpack-compat
+ */
+
+use Automattic\Jetpack\Connection\Client;
+
+/**
+ * Class Jetpack_Client
+ *
+ * @deprecated Use Automattic\Jetpack\Connection\Client
+ */
+class Jetpack_Client {
+
+ /**
+ * Jetpack API version.
+ *
+ * @deprecated use Automattic\Jetpack\Connection\Client::WPCOM_JSON_API_VERSION
+ */
+ const WPCOM_JSON_API_VERSION = '1.1';
+
+ /**
+ * Perform remote request.
+ *
+ * @deprecated use Automattic\Jetpack\Connection\Client::remote_request
+ *
+ * @param array $args Arguments.
+ * @param null $body Request body.
+ *
+ * @return array|WP_Error
+ */
+ public static function remote_request( $args, $body = null ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Connection\Client' );
+ return Client::remote_request( $args, $body );
+ }
+
+ /**
+ * Request to wpcom using the blog id.
+ *
+ * @deprecated use Automattic\Jetpack\Connection\Client::wpcom_json_api_request_as_blog
+ *
+ * @param string $path Endpoint path.
+ * @param string $version Endpoint version.
+ * @param array $args Arguments.
+ * @param null $body Request body.
+ * @param string $base_api_path Endpoint base prefix.
+ *
+ * @return array|WP_Error
+ */
+ public static function wpcom_json_api_request_as_blog(
+ $path,
+ $version = self::WPCOM_JSON_API_VERSION,
+ $args = array(),
+ $body = null,
+ $base_api_path = 'rest'
+ ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Connection\Client' );
+ return Client::wpcom_json_api_request_as_blog( $path, $version, $args, $body, $base_api_path );
+ }
+
+ /**
+ * Wrapper for wp_remote_request(). Turns off SSL verification for certain SSL errors.
+ * This is suboptimal, but many, many, many hosts have misconfigured SSL.
+ *
+ * @deprecated use Automattic\Jetpack\Connection\Client::_wp_remote_request
+ *
+ * When Jetpack is registered, the jetpack_fallback_no_verify_ssl_certs option is set to the current time if:
+ * 1. a certificate error is found AND
+ * 2. not verifying the certificate works around the problem.
+ *
+ * The option is checked on each request.
+ *
+ * @internal
+ *
+ * @param String $url the request URL.
+ * @param array $args request arguments.
+ * @param Boolean $set_fallback whether to allow flagging this request to use a fallback certficate override.
+ * @return array|WP_Error WP HTTP response on success
+ */
+ public static function _wp_remote_request( $url, $args, $set_fallback = false ) { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Connection\Client' );
+ return Client::_wp_remote_request( $url, $args, $set_fallback );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-sync-actions.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-sync-actions.php
new file mode 100644
index 00000000..096b4cd5
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-sync-actions.php
@@ -0,0 +1,362 @@
+<?php
+/**
+ * A compatibility shim for the sync actions class.
+ *
+ * @package automattic/jetpack-compat
+ */
+
+use Automattic\Jetpack\Sync\Actions;
+
+/**
+ * Class Jetpack_Sync_Actions
+ *
+ * @deprecated Use Automattic\Jetpack\Sync\Actions
+ */
+class Jetpack_Sync_Actions extends Automattic\Jetpack\Sync\Actions {
+
+ /**
+ * Initializes the class.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::init
+ */
+ public static function init() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::init();
+ }
+
+ /**
+ * Adds a shutdown sender callback.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::add_sender_shutdown
+ */
+ public static function add_sender_shutdown() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::add_sender_shutdown();
+ }
+
+ /**
+ * Returns false or true based on whether this class should initialize the sender
+ * in current circumstances.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::should_initialize_sender
+ *
+ * @return Boolean should the object initialize sender?
+ */
+ public static function should_initialize_sender() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::should_initialize_sender();
+ }
+
+ /**
+ * Returns false or true based on whether sync is allowed.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::sync_allowed
+ *
+ * @return Boolean is sync allowed?
+ */
+ public static function sync_allowed() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::sync_allowed();
+ }
+
+ /**
+ * Returns false or true based on whether sync via cron is allowed.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::sync_via_cron_allowed
+ *
+ * @return Boolean is sync via cron allowed?
+ */
+ public static function sync_via_cron_allowed() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::sync_via_cron_allowed();
+ }
+
+ /**
+ * Filters a boolean value that determines whether blocklisted posts should be prevented
+ * from being publicized.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::prevent_publicize_blacklisted_posts
+ *
+ * @param Boolean $should_publicize initial setting value.
+ * @param WP_Post $post the post object.
+ * @return Boolean whether to prevent publicizing.
+ */
+ public static function prevent_publicize_blacklisted_posts( $should_publicize, $post ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::prevent_publicize_blacklisted_posts( $should_publicize, $post );
+ }
+
+ /**
+ * Set the importing flag to true.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::set_is_importing_true
+ */
+ public static function set_is_importing_true() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::set_is_importing_true();
+ }
+
+ /**
+ * Send the sync data.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::send_data
+ *
+ * @param Mixed $data the sync data.
+ * @param String $codec_name the codec slug.
+ * @param Integer $sent_timestamp the current server timestamp.
+ * @param Integer $queue_id the queue identifier.
+ * @param Integer $checkout_duration time spent retrieving items.
+ * @param Integer $preprocess_duration Time spent converting items into data.
+ * @param Integer $queue_size The current size of the sync queue.
+ * @param string $buffer_id The ID of the Queue buffer checked out for processing.
+ *
+ * @return WP_Response the response object.
+ */
+ public static function send_data( $data, $codec_name, $sent_timestamp, $queue_id, $checkout_duration, $preprocess_duration, $queue_size = null, $buffer_id = null ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::send_data( $data, $codec_name, $sent_timestamp, $queue_id, $checkout_duration, $preprocess_duration, $queue_size, $buffer_id );
+ }
+
+ /**
+ * Commence initial sync.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::do_initial_sync
+ */
+ public static function do_initial_sync() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::do_initial_sync();
+ }
+
+ /**
+ * Commence full sync.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::do_full_sync
+ *
+ * @param array $modules the modules list.
+ * @return Boolean whether the sync was initialized.
+ */
+ public static function do_full_sync( $modules = null ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::do_full_sync( $modules );
+ }
+
+ /**
+ * Schedule cron sessions.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::jetpack_cron_schedule
+ *
+ * @param array $schedules the schedules to add.
+ */
+ public static function jetpack_cron_schedule( $schedules ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::jetpack_cron_schedule( $schedules );
+ }
+
+ /**
+ * Commence cron sync.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::do_cron_sync
+ */
+ public static function do_cron_sync() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::do_cron_sync();
+ }
+
+ /**
+ * Commence cron full sync.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::do_cron_full_sync
+ */
+ public static function do_cron_full_sync() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::do_cron_full_sync();
+ }
+
+ /**
+ * Commence cron sync of a specific type of object.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::do_cron_sync_by_type
+ *
+ * @param array $type the type of object to sync.
+ */
+ public static function do_cron_sync_by_type( $type ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::do_cron_sync_by_type();
+ }
+
+ /**
+ * Initalize the listener of the object.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::initialize_listener
+ */
+ public static function initialize_listener() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::initialize_listener();
+ }
+
+ /**
+ * Initalize the sender of the object.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::initialize_sender
+ */
+ public static function initialize_sender() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::initialize_sender();
+ }
+
+ /**
+ * Initalize the woocommerce listeners.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::initialize_woocommerce
+ */
+ public static function initialize_woocommerce() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::initialize_woocommerce();
+ }
+
+ /**
+ * Add the woocommerce sync module.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::add_woocommerce_sync_module
+ *
+ * @param array $sync_modules an array of modules.
+ */
+ public static function add_woocommerce_sync_module( $sync_modules ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::add_woocommerce_sync_module( $sync_modules );
+ }
+
+ /**
+ * Initalize the WP Super Cache listener.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::initialize_wp_super_cache
+ */
+ public static function initialize_wp_super_cache() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::initialize_wp_super_cache();
+ }
+
+ /**
+ * Add the WP Super Cache sync module.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::add_wp_super_cache_sync_module
+ *
+ * @param array $sync_modules the list to be amended.
+ */
+ public static function add_wp_super_cache_sync_module( $sync_modules ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::add_wp_super_cache_sync_module( $sync_modules );
+ }
+
+ /**
+ * Sanitizes the filtered sync cron schedule.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::sanitize_filtered_sync_cron_schedule
+ *
+ * @param String $schedule the cron schedule to sanitize.
+ * @return String sanitized cron schedule.
+ */
+ public static function sanitize_filtered_sync_cron_schedule( $schedule ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::sanitize_filtered_sync_cron_schedule( $schedule );
+ }
+
+ /**
+ * Returns the time offset for a the start schedule.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::get_start_time_offset
+ *
+ * @param String $schedule the schedule string.
+ * @param String $hook hook slug.
+ * @return Integer start time offset.
+ */
+ public static function get_start_time_offset( $schedule = '', $hook = '' ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::get_start_time_offset( $schedule, $hook );
+ }
+
+ /**
+ * If needed, schedule a cron sync.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::maybe_schedule_sync_cron
+ *
+ * @param String $schedule the schedule string.
+ * @param String $hook hook slug.
+ */
+ public static function maybe_schedule_sync_cron( $schedule, $hook ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::maybe_schedule_sync_cron( $schedule, $hook );
+ }
+
+ /**
+ * Clears cron jobs scheduled for sync.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::clear_sync_cron_jobs
+ */
+ public static function clear_sync_cron_jobs() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::clear_sync_cron_jobs();
+ }
+
+ /**
+ * Initialize cron jobs for sync.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::init_sync_cron_jobs
+ */
+ public static function init_sync_cron_jobs() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::init_sync_cron_jobs();
+ }
+
+ /**
+ * Cleans up schedules on plugin upgrade.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::cleanup_on_upgrade
+ *
+ * @param String $new_version the new version.
+ * @param String $old_version the old version.
+ */
+ public static function cleanup_on_upgrade( $new_version = null, $old_version = null ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::cleanup_on_upgrade( $new_version, $old_version );
+ }
+
+ /**
+ * Clears cron jobs scheduled for sync.
+ *
+ * @deprecated Automattic\Jetpack\Sync\Actions::get_sync_status
+ *
+ * @param array $fields sync fields to get status of.
+ */
+ public static function get_sync_status( $fields = null ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Actions' );
+
+ return Actions::get_sync_status( $fields );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-sync-modules.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-sync-modules.php
new file mode 100644
index 00000000..69bc8af5
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-sync-modules.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * A compatibility shim for the sync modules class.
+ *
+ * @package automattic/jetpack-compat
+ */
+
+use Automattic\Jetpack\Sync\Modules;
+
+/**
+ * Class Jetpack_Sync_Modules
+ *
+ * @deprecated Use Automattic\Jetpack\Sync\Modules
+ */
+class Jetpack_Sync_Modules {
+
+ /**
+ * Returns the sync module object.
+ *
+ * @param String $module_name the module name.
+ * @return Automattic\Jetpack\Sync\Modules\Module the module object.
+ */
+ public static function get_module( $module_name ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Modules' );
+
+ return Modules::get_module( $module_name );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-sync-settings.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-sync-settings.php
new file mode 100644
index 00000000..e31f79d9
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpack-sync-settings.php
@@ -0,0 +1,229 @@
+<?php
+/**
+ * Legacy/deprecated Sync Setting getter and setter.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+use Automattic\Jetpack\Sync\Settings;
+
+/**
+ * Class Jetpack_Sync_Settings
+ *
+ * @deprecated Use Automattic\Jetpack\Sync\Settings
+ */
+class Jetpack_Sync_Settings {
+
+ /**
+ * Return all settings
+ *
+ * @deprecated See Automattic/Jetpack/Sync/Settings
+ *
+ * @return array All Sync Settings.
+ */
+ public static function get_settings() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ return Settings::get_settings();
+ }
+
+ /**
+ * Return a single setting.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ *
+ * @param string $setting Setting to return.
+ *
+ * @return mixed Value of setting.
+ */
+ public static function get_setting( $setting ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ return Settings::get_setting( $setting );
+ }
+
+ /**
+ * Update a sync setting
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ *
+ * @param mixed $new_settings New setting to set.
+ */
+ public static function update_settings( $new_settings ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ Settings::update_settings( $new_settings );
+ }
+
+ /**
+ * Return is_network_setting result.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ *
+ * @param string $setting Setting to check.
+ *
+ * @return bool
+ */
+ public static function is_network_setting( $setting ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ return Settings::is_network_setting( $setting );
+ }
+
+ /**
+ * Return blocklisted post types SQL.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ */
+ public static function get_blacklisted_post_types_sql() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ return Settings::get_blacklisted_post_types_sql();
+ }
+
+ /**
+ * Return allowed post meta SQL.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ *
+ * @return string
+ */
+ public static function get_whitelisted_post_meta_sql() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ return Settings::get_whitelisted_post_meta_sql();
+ }
+
+ /**
+ * Return allowed comment meta SQL
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ */
+ public static function get_whitelisted_comment_meta_sql() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ return Settings::get_whitelisted_comment_meta_sql();
+ }
+
+ /**
+ * Return get_comments_filter_sql
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ */
+ public static function get_comments_filter_sql() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ return Settings::get_comments_filter_sql();
+ }
+
+ /**
+ * Result data.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ */
+ public static function reset_data() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ Settings::reset_data();
+ }
+
+ /**
+ * Set importing status.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ *
+ * @param mixed $is_importing Value to set.
+ */
+ public static function set_importing( $is_importing ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ Settings::set_importing( $is_importing );
+ }
+
+ /**
+ * Return is_importing status.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ *
+ * @return bool
+ */
+ public static function is_importing() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ return Settings::is_importing();
+ }
+
+ /**
+ * Return is_sync_enabled status.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ *
+ * @return bool
+ */
+ public static function is_sync_enabled() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ return Settings::is_sync_enabled();
+ }
+
+ /**
+ * Set cron status.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ *
+ * @param mixed $is_doing_cron Value to set.
+ */
+ public static function set_doing_cron( $is_doing_cron ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ Settings::set_doing_cron( $is_doing_cron );
+ }
+
+ /**
+ * Return is_doing_cron status.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ *
+ * @return bool
+ */
+ public static function is_doing_cron() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ return Settings::is_doing_cron();
+ }
+
+ /**
+ * Return is_syncing status.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ *
+ * @return bool
+ */
+ public static function is_syncing() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ return Settings::is_syncing();
+ }
+
+ /**
+ * Set "is syncing" status.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ *
+ * @param mixed $is_syncing Is syncing value.
+ */
+ public static function set_is_syncing( $is_syncing ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ Settings::set_is_syncing( $is_syncing );
+ }
+
+ /**
+ * Return is_sending status.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ *
+ * @return bool
+ */
+ public static function is_sending() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ return Settings::is_sending();
+ }
+
+ /**
+ * Set "is sending" status.
+ *
+ * @deprecated See Automattic\Jetpack\Sync\Settings
+ *
+ * @param mixed $is_sending Is sending value.
+ */
+ public static function set_is_sending( $is_sending ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Sync\Settings' );
+ Settings::set_is_sending( $is_sending );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpacktracking.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpacktracking.php
new file mode 100644
index 00000000..abfe4f62
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/legacy/class-jetpacktracking.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * Legacy and deprecated Jetpack Tracking class.
+ *
+ * @package automattic/jetpack-compat
+ */
+
+use Automattic\Jetpack\Tracking;
+
+/**
+ * Legacy class JetpackTracking
+ *
+ * @deprecated See Automattic\Jetpack\Tracking
+ */
+class JetpackTracking {
+
+ /**
+ * Enqueue tracks scripts.
+ *
+ * @deprecated See Automattic\Jetpack\Tracking
+ */
+ public static function enqueue_tracks_scripts() {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Tracking' );
+
+ $tracking = new Tracking();
+ return $tracking->enqueue_tracks_scripts();
+ }
+
+ /**
+ * Record user event.
+ *
+ * @deprecated See Automattic\Jetpack\Tracking
+ *
+ * @param mixed $event_type Event type.
+ * @param array $data Event data.
+ * @param mixed $user User who did the event.
+ *
+ * @return bool
+ */
+ public static function record_user_event( $event_type, $data = array(), $user = null ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Tracking' );
+
+ $tracking = new Tracking();
+ return $tracking->record_user_event( $event_type, $data, $user );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/lib/tracks/client.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/lib/tracks/client.php
new file mode 100644
index 00000000..41056897
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-compat/lib/tracks/client.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Deprecated Tracks client.
+ *
+ * @package automattic/jetpack-compat
+ */
+
+/**
+ * Get tracks identity for an user.
+ *
+ * @deprecated 7.5.0 use Automattic\Jetpack\Tracking->tracks_get_identity instead
+ *
+ * @param int $user_id User id.
+ *
+ * @return mixed tracks identity.
+ */
+function jetpack_tracks_get_identity( $user_id ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Tracking->tracks_get_identity' );
+
+ $tracking = new Automattic\Jetpack\Tracking( 'jetpack', Jetpack::connection() );
+ return $tracking->tracks_get_identity( $user_id );
+}
+
+/**
+ * Record Jetpack Tracks Event
+ *
+ * @deprecated 7.5.0 use Automattic\Jetpack\Tracking->tracks_record_event instead
+ *
+ * @param object $user User acting.
+ * @param string $event_name Event name.
+ * @param array $properties Properties.
+ * @param string|bool $event_timestamp_millis Timestamp.
+ *
+ * @return bool
+ */
+function jetpack_tracks_record_event( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
+ _deprecated_function( __METHOD__, 'jetpack-7.5', 'Automattic\Jetpack\Tracking->tracks_record_event' );
+
+ $tracking = new Automattic\Jetpack\Tracking( 'jetpack', Jetpack::connection() );
+ return $tracking->tracks_record_event( $user, $event_name, $properties, $event_timestamp_millis );
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-config/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-config/CHANGELOG.md
new file mode 100644
index 00000000..2774b4b4
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-config/CHANGELOG.md
@@ -0,0 +1,113 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.6.0] - 2022-01-04
+### Added
+- Accept options for the IDC package.
+
+### Changed
+- Updated package textdomain from `jetpack` to `jetpack-config`.
+
+## [1.5.4] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.5.3] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.5.2] - 2021-10-12
+### Added
+- Add support for the identity-crisis package
+
+## [1.5.1] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.5.0] - 2021-09-22
+### Added
+- Allow for enabling and initializing new Post_List package from Config package.
+
+## [1.4.7] - 2021-08-31
+### Changed
+- updates annotations versions.
+
+## [1.4.6] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.4.5] - 2021-04-27
+### Changed
+- Updated package dependencies.
+
+## [1.4.4] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+## [1.4.3] - 2021-01-19
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.4.2] - 2020-10-28
+
+- Updated PHPCS: Packages and Debugger
+- Config: remove tos and tracking features
+- Config: add info about the package dependencies to the package docs
+
+## [1.4.1] - 2020-09-15
+
+- Config: remove tos and tracking features
+
+## [1.4.0] - 2020-08-26
+
+- Config: Remove composer dependencies
+- Config: Add connection status check
+
+## [1.3.0] - 2020-06-26
+
+- Config: check for both JITM namespaces
+
+## [1.2.0] - 2020-05-20
+
+- Store the list of active plugins that uses connection in an option
+- Implement pre-connection JITMs
+- Connection Package: Handle disconnections gracefully
+
+## [1.1.0] - 2020-01-23
+
+- Moved JITM initialization to plugins_loaded.
+
+## [1.0.1] - 2020-01-20
+
+- Move connection manager related logic to after plugins_loaded.
+
+## 1.0.0 - 2020-01-14
+
+- Trying to add deterministic initialization.
+
+[1.6.0]: https://github.com/Automattic/jetpack-config/compare/v1.5.4...v1.6.0
+[1.5.4]: https://github.com/Automattic/jetpack-config/compare/v1.5.3...v1.5.4
+[1.5.3]: https://github.com/Automattic/jetpack-config/compare/v1.5.2...v1.5.3
+[1.5.2]: https://github.com/Automattic/jetpack-config/compare/v1.5.1...v1.5.2
+[1.5.1]: https://github.com/Automattic/jetpack-config/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/Automattic/jetpack-config/compare/v1.4.7...v1.5.0
+[1.4.7]: https://github.com/Automattic/jetpack-config/compare/v1.4.6...v1.4.7
+[1.4.6]: https://github.com/Automattic/jetpack-config/compare/v1.4.5...v1.4.6
+[1.4.5]: https://github.com/Automattic/jetpack-config/compare/v1.4.4...v1.4.5
+[1.4.4]: https://github.com/Automattic/jetpack-config/compare/v1.4.3...v1.4.4
+[1.4.3]: https://github.com/Automattic/jetpack-config/compare/v1.4.2...v1.4.3
+[1.4.2]: https://github.com/Automattic/jetpack-config/compare/v1.4.1...v1.4.2
+[1.4.1]: https://github.com/Automattic/jetpack-config/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/Automattic/jetpack-config/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/Automattic/jetpack-config/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-config/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/Automattic/jetpack-config/compare/v1.0.1...v1.1.0
+[1.0.1]: https://github.com/Automattic/jetpack-config/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-config/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-config/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-config/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-config/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-config/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-config/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-config/src/class-config.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-config/src/class-config.php
new file mode 100644
index 00000000..8d275d68
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-config/src/class-config.php
@@ -0,0 +1,293 @@
+<?php
+/**
+ * The base Jetpack configuration class file.
+ *
+ * @package automattic/jetpack-config
+ */
+
+namespace Automattic\Jetpack;
+
+/*
+ * The Config package does not require the composer packages that
+ * contain the package classes shown below. The consumer plugin
+ * must require the corresponding packages to use these features.
+ */
+use Automattic\Jetpack\Connection\Manager;
+use Automattic\Jetpack\Connection\Plugin;
+use Automattic\Jetpack\JITM as JITM;
+use Automattic\Jetpack\JITMS\JITM as JITMS_JITM;
+use Automattic\Jetpack\Post_List\Post_List as Post_List;
+use Automattic\Jetpack\Sync\Main as Sync_Main;
+
+/**
+ * The configuration class.
+ */
+class Config {
+
+ const FEATURE_ENSURED = 1;
+ const FEATURE_NOT_AVAILABLE = 0;
+ const FEATURE_ALREADY_ENSURED = -1;
+
+ /**
+ * The initial setting values.
+ *
+ * @var Array
+ */
+ protected $config = array(
+ 'jitm' => false,
+ 'connection' => false,
+ 'sync' => false,
+ 'post_list' => false,
+ 'identity_crisis' => false,
+ );
+
+ /**
+ * Initialization options stored here.
+ *
+ * @var array
+ */
+ protected $feature_options = array();
+
+ /**
+ * Creates the configuration class instance.
+ */
+ public function __construct() {
+ /**
+ * Adding the config handler to run on priority 2 because the class itself is
+ * being constructed on priority 1.
+ */
+ add_action( 'plugins_loaded', array( $this, 'on_plugins_loaded' ), 2 );
+
+ }
+
+ /**
+ * Require a feature to be initialized. It's up to the package consumer to actually add
+ * the package to their composer project. Declaring a requirement using this method
+ * instructs the class to initialize it.
+ *
+ * @param String $feature the feature slug.
+ * @param array $options Additional options, optional.
+ */
+ public function ensure( $feature, array $options = array() ) {
+ $this->config[ $feature ] = true;
+
+ $this->set_feature_options( $feature, $options );
+ }
+
+ /**
+ * Runs on plugins_loaded hook priority with priority 2.
+ *
+ * @action plugins_loaded
+ */
+ public function on_plugins_loaded() {
+ if ( $this->config['connection'] ) {
+ $this->ensure_class( 'Automattic\Jetpack\Connection\Manager' )
+ && $this->ensure_feature( 'connection' );
+ }
+
+ if ( $this->config['sync'] ) {
+ $this->ensure_class( 'Automattic\Jetpack\Sync\Main' )
+ && $this->ensure_feature( 'sync' );
+ }
+
+ if ( $this->config['jitm'] ) {
+ // Check for the JITM class in both namespaces. The namespace was changed in jetpack-jitm v1.6.
+ ( $this->ensure_class( 'Automattic\Jetpack\JITMS\JITM', false )
+ || $this->ensure_class( 'Automattic\Jetpack\JITM' ) )
+ && $this->ensure_feature( 'jitm' );
+ }
+
+ if ( $this->config['post_list'] ) {
+ $this->ensure_class( 'Automattic\Jetpack\Post_List\Post_List' )
+ && $this->ensure_feature( 'post_list' );
+ }
+
+ if ( $this->config['identity_crisis'] ) {
+ $this->ensure_class( 'Automattic\Jetpack\Identity_Crisis' )
+ && $this->ensure_feature( 'identity_crisis' );
+ }
+ }
+
+ /**
+ * Returns true if the required class is available and alerts the user if it's not available
+ * in case the site is in debug mode.
+ *
+ * @param String $classname a fully qualified class name.
+ * @param Boolean $log_notice whether the E_USER_NOTICE should be generated if the class is not found.
+ *
+ * @return Boolean whether the class is available.
+ */
+ protected function ensure_class( $classname, $log_notice = true ) {
+ $available = class_exists( $classname );
+
+ if ( $log_notice && ! $available && defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+ trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
+ sprintf(
+ /* translators: %1$s is a PHP class name. */
+ esc_html__(
+ 'Unable to load class %1$s. Please add the package that contains it using composer and make sure you are requiring the Jetpack autoloader',
+ 'jetpack-config'
+ ),
+ esc_html( $classname )
+ ),
+ E_USER_NOTICE
+ );
+ }
+
+ return $available;
+ }
+
+ /**
+ * Ensures a feature is enabled, sets it up if it hasn't already been set up.
+ * Run the options method (if exists) every time the method is called.
+ *
+ * @param String $feature slug of the feature.
+ * @return Integer either FEATURE_ENSURED, FEATURE_ALREADY_ENSURED or FEATURE_NOT_AVAILABLE constants.
+ */
+ protected function ensure_feature( $feature ) {
+ $method = 'enable_' . $feature;
+ if ( ! method_exists( $this, $method ) ) {
+ return self::FEATURE_NOT_AVAILABLE;
+ }
+
+ $method_options = 'ensure_options_' . $feature;
+ if ( method_exists( $this, $method_options ) ) {
+ $this->{ $method_options }();
+ }
+
+ if ( did_action( 'jetpack_feature_' . $feature . '_enabled' ) ) {
+ return self::FEATURE_ALREADY_ENSURED;
+ }
+
+ $this->{ $method }();
+
+ /**
+ * Fires when a specific Jetpack package feature is initalized using the Config package.
+ *
+ * @since 1.1.0
+ */
+ do_action( 'jetpack_feature_' . $feature . '_enabled' );
+
+ return self::FEATURE_ENSURED;
+ }
+
+ /**
+ * Enables the JITM feature.
+ */
+ protected function enable_jitm() {
+ if ( class_exists( 'Automattic\Jetpack\JITMS\JITM' ) ) {
+ JITMS_JITM::configure();
+ } else {
+ // Provides compatibility with jetpack-jitm <v1.6.
+ JITM::configure();
+ }
+
+ return true;
+ }
+
+ /**
+ * Enables the Post_List feature.
+ */
+ protected function enable_post_list() {
+ Post_List::configure();
+
+ return true;
+ }
+
+ /**
+ * Enables the Sync feature.
+ */
+ protected function enable_sync() {
+ Sync_Main::configure();
+
+ return true;
+ }
+
+ /**
+ * Enables the Connection feature.
+ */
+ protected function enable_connection() {
+ Manager::configure();
+
+ return true;
+ }
+
+ /**
+ * Enables the identity-crisis feature.
+ */
+ protected function enable_identity_crisis() {
+ Identity_Crisis::init();
+ }
+
+ /**
+ * Setup the Connection options.
+ */
+ protected function ensure_options_connection() {
+ $options = $this->get_feature_options( 'connection' );
+
+ if ( ! empty( $options['slug'] ) ) {
+ // The `slug` and `name` are removed from the options because they need to be passed as arguments.
+ $slug = $options['slug'];
+ unset( $options['slug'] );
+
+ $name = $slug;
+ if ( ! empty( $options['name'] ) ) {
+ $name = $options['name'];
+ unset( $options['name'] );
+ }
+
+ ( new Plugin( $slug ) )->add( $name, $options );
+ }
+
+ return true;
+ }
+
+ /**
+ * Setup the Identity Crisis options.
+ *
+ * @return bool
+ */
+ protected function ensure_options_identity_crisis() {
+ $options = $this->get_feature_options( 'identity_crisis' );
+
+ if ( is_array( $options ) && count( $options ) ) {
+ add_filter(
+ 'jetpack_idc_consumers',
+ function ( $consumers ) use ( $options ) {
+ $consumers[] = $options;
+ return $consumers;
+ }
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Temporary save initialization options for a feature.
+ *
+ * @param string $feature The feature slug.
+ * @param array $options The options.
+ *
+ * @return bool
+ */
+ protected function set_feature_options( $feature, array $options ) {
+ if ( $options ) {
+ $this->feature_options[ $feature ] = $options;
+ }
+
+ return true;
+ }
+
+ /**
+ * Get initialization options for a feature from the temporary storage.
+ *
+ * @param string $feature The feature slug.
+ *
+ * @return array
+ */
+ protected function get_feature_options( $feature ) {
+ return empty( $this->feature_options[ $feature ] ) ? array() : $this->feature_options[ $feature ];
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/CHANGELOG.md
new file mode 100644
index 00000000..b6b82ab7
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/CHANGELOG.md
@@ -0,0 +1,186 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [2.3.0] - 2022-01-04
+### Changed
+- Drop isRegistered and isUserConnected params from ConnectionStatusCard component
+- Replaced IDC screen rendering with site-wide IDC screen.
+- Updated package dependencies
+- Updated package textdomain from `jetpack` to `jetpack-connection-ui`.
+
+## [2.2.0] - 2021-12-14
+### Changed
+- Allow non-adimin access to the Connection Manager for proper IDC screen review.
+
+### Fixed
+- Build minimized JS for the production build.
+
+## [2.1.4] - 2021-12-07
+### Added
+- Pass tracking data into the RNA IDC package.
+
+### Changed
+- Updated package dependencies.
+
+## [2.1.3] - 2021-11-30
+### Changed
+- Remove now-redundant `output.filename` from Webpack config.
+
+## [2.1.2] - 2021-11-23
+### Changed
+- Updated package dependencies.
+
+## [2.1.1] - 2021-11-17
+### Changed
+- Updated package dependencies.
+
+## [2.1.0] - 2021-11-16
+### Added
+- Use monorepo `validate-es` script to validate Webpack builds.
+
+### Changed
+- Replace the withConnectionStatus HOC with withSelect HOC.
+- Updated package dependencies
+
+### Removed
+- Remove use of `gulp` in build, all it was doing was wrapping `webpack`.
+
+## [2.0.0] - 2021-11-09
+### Added
+- Initialize IDC package, properly display the RNA IDC screen.
+
+### Changed
+- Updated package dependencies.
+- Update webpack build config. Removes IE 11 support in the JavaScript.
+
+## [1.6.0] - 2021-11-02
+### Changed
+- Updated package dependencies
+- Use ConnectScreenRequiredPlan instead of ConnectScreen.
+
+## [1.5.3] - 2021-10-26
+### Added
+- Add the redirect URI for RNA IDC "Start Fresh" functionality.
+
+### Changed
+- Updated package dependencies.
+
+## [1.5.2] - 2021-10-19
+### Changed
+- Bump the RNA API version.
+
+## [1.5.1] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.5.0] - 2021-10-12
+### Added
+- Initialize REST API in the IDC package.
+- Temporarily force the IDC screen to appear for debugging purposes.
+
+### Changed
+- Use the "withConnectionStatus" HOC for ConnectScreen component.
+
+## [1.4.1] - 2021-09-28
+### Changed
+- Allow Node ^14.17.6 to be used in this project. This shouldn't change the behavior of the code itself.
+- Updated package dependencies.
+
+## [1.4.0] - 2021-08-31
+### Changed
+- Bump connection package version to 0.5.2-alpha.
+- Updated package dependencies.
+- Updated Package versions in annotations.
+- Update to latest webpack, webpack-cli and calypso-build.
+- Use Node 16.7.0 in tooling. This shouldn't change the behavior of the code itself.
+- Use the "withConnectionStatus" HOC for ConnectScreen component.
+
+### Removed
+- Removed unused method
+
+## [1.3.1] - 2021-08-12
+### Changed
+- Updated package dependencies
+- Update jest dependency to fix jetpack search tests
+
+## [1.3.0] - 2021-07-27
+### Added
+- Integrate ConnectionStatusCard component.
+- Integrate DisconnectDialog RNA component.
+- Integrate the RNA connection screen component.
+
+## [1.2.0] - 2021-06-29
+### Added
+- Add the Jetpack logo to the header.
+
+### Changed
+- Adjust the RNA Connection usage because 'Main' component has been removed to 'ConnectButton'
+- Clean up the code that used to be required for now removed In-Place Connection flow.
+- Updated package dependencies.
+- Update node version requirement to 14.16.1
+
+## [1.1.1] - 2021-06-15
+### Changed
+- Remove the 'authorizeUrl' RNA Connection parameter as it's no longer needed.
+- Update docs to replace yarn with pnpm.
+
+### Fixed
+- Remove dependency on @wordpress/url as it caused dependency issues in build test flows.
+- Use `absoluteRuntime` in babel JS build to avoid module not found errors.
+
+## [1.1.0] - 2021-05-25
+### Added
+- Integrate the connection flow using RNA Connection package.
+
+### Fixed
+- Add docblock for `jetpack_on_connection_ui_init` hook.
+- Fixing the Connection UI initialization logical error
+- Initialize the main connection-ui Admin class only once since it may be called multiple times.
+
+## [1.0.2] - 2021-04-27
+### Added
+- Add React initial state.
+
+### Changed
+- Updated package dependencies.
+
+## [1.0.1] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Connection UI: remove .github directory from production package
+- Pin dependencies
+- Update Node to match latest LTS 12
+- Update package dependencies.
+
+## 1.0.0 - 2021-02-23
+
+- Connection UI: Building the Framework
+
+[2.3.0]: https://github.com/Automattic/jetpack-connection-ui/compare/v2.2.0...v2.3.0
+[2.2.0]: https://github.com/Automattic/jetpack-connection-ui/compare/v2.1.4...v2.2.0
+[2.1.4]: https://github.com/Automattic/jetpack-connection-ui/compare/v2.1.3...v2.1.4
+[2.1.3]: https://github.com/Automattic/jetpack-connection-ui/compare/v2.1.2...v2.1.3
+[2.1.2]: https://github.com/Automattic/jetpack-connection-ui/compare/v2.1.1...v2.1.2
+[2.1.1]: https://github.com/Automattic/jetpack-connection-ui/compare/v2.1.0...v2.1.1
+[2.1.0]: https://github.com/Automattic/jetpack-connection-ui/compare/v2.0.0...v2.1.0
+[2.0.0]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.6.0...v2.0.0
+[1.6.0]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.5.3...v1.6.0
+[1.5.3]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.5.2...v1.5.3
+[1.5.2]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.5.1...v1.5.2
+[1.5.1]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.4.1...v1.5.0
+[1.4.1]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.3.1...v1.4.0
+[1.3.1]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.1.1...v1.2.0
+[1.1.1]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.0.2...v1.1.0
+[1.0.2]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.0.1...v1.0.2
+[1.0.1]: https://github.com/Automattic/jetpack-connection-ui/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/babel.config.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/babel.config.js
new file mode 100644
index 00000000..0773ed0d
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/babel.config.js
@@ -0,0 +1,10 @@
+const config = {
+ presets: [
+ [
+ '@automattic/jetpack-webpack-config/babel/preset',
+ { pluginReplaceTextdomain: { textdomain: 'jetpack-connection-ui' } },
+ ],
+ ],
+};
+
+module.exports = config;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/images/disconnect-confirm-dc9fe8f5c68cfd1320e0.jpg b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/images/disconnect-confirm-dc9fe8f5c68cfd1320e0.jpg
new file mode 100644
index 00000000..13f5651c
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/images/disconnect-confirm-dc9fe8f5c68cfd1320e0.jpg
Binary files differ
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/images/disconnect-thanks-5873bfac56a9bd7322cd.jpg b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/images/disconnect-thanks-5873bfac56a9bd7322cd.jpg
new file mode 100644
index 00000000..a191ec9e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/images/disconnect-thanks-5873bfac56a9bd7322cd.jpg
Binary files differ
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.asset.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.asset.php
new file mode 100644
index 00000000..6486fa68
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-url'), 'version' => 'cf7c4416c6aebd1c4977dbe9905998f7'); \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.css b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.css
new file mode 100644
index 00000000..60f69f98
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.css
@@ -0,0 +1 @@
+#jetpack-connection-ui-container .jetpack-cui__header h1{font-size:4.6em;line-height:1.2em;text-align:left}#jetpack-connection-ui-container{margin:40px}#jetpack-connection-ui-container p{font-size:1.23em;line-height:1.5em}.jp-connection-status-card h3{color:var(--jp-black);font-size:36px;font-weight:400;line-height:40px;margin:0}.jp-connection-status-card a,.jp-connection-status-card a:active,.jp-connection-status-card a:hover{color:var(--jp-black)}.jp-connection-status-card p{color:var(--jp-black);margin:16px 0}.jp-connection-status-card a,.jp-connection-status-card li,.jp-connection-status-card p{font-size:16px;line-height:24px}.jp-connection-status-card--status{align-items:center;display:flex;margin:24px 0 24px -6px}.jp-connection-status-card--cloud{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDIiIGhlaWdodD0iNDIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PG1hc2sgaWQ9ImEiIG1hc2stdHlwZT0iYWxwaGEiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjciIHk9IjkiIHdpZHRoPSIyOCIgaGVpZ2h0PSIyMSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zMC4zMzMgMTcuNjk3di0uMDhjMC00LjI1My0zLjc2LTcuNy04LjQtNy43LTMuOTE2IDAtNy4yMDYgMi40NTctOC4xMzYgNS43OGE2LjM4NiA2LjM4NiAwIDAgMC0uMjY0LS4wMDVDOS45MjUgMTUuNjkyIDcgMTguNzA4IDcgMjIuNDI5czIuOTI1IDYuNzM4IDYuNTMzIDYuNzM4SDI5LjRjMy4wOTMgMCA1LjYtMi41ODYgNS42LTUuNzc1IDAtMi44NjEtMi4wMTgtNS4yMzctNC42NjYtNS42OTVaIiBmaWxsPSIjZmZmIi8+PC9tYXNrPjxnIG1hc2s9InVybCgjYSkiPjxwYXRoIGQ9Im0zMC4zMzMgMTcuNjk3LTIuNjI1LS4wMy0uMDI1IDIuMjM1IDIuMjAyLjM4MS40NDgtMi41ODZabS0xNi41MzctMi0uMTA3IDIuNjIzIDIuMDc1LjA4NS41Ni0yLTIuNTI4LS43MDhabS0uMjYzIDEzLjQ3djIuNjI1aC4wMDhsLS4wMDgtMi42MjVabS4wNCAwdi0yLjYyNWgtLjAwOWwuMDA4IDIuNjI1Wm0xNS43OTUgMCAuMDE1LTIuNjI1aC0uMDE1djIuNjI1Wm0zLjU5LTExLjQ0MXYtLjExaC01LjI1di4wNTJsNS4yNS4wNThabTAtLjExYzAtNS45MTItNS4xNTUtMTAuMzI0LTExLjAyNS0xMC4zMjR2NS4yNWMzLjQxIDAgNS43NzUgMi40ODIgNS43NzUgNS4wNzVoNS4yNVpNMjEuOTMzIDcuMjkzYy00Ljk4NCAwLTkuMzg4IDMuMTM5LTEwLjY2NCA3LjY5OGw1LjA1NSAxLjQxNWMuNTg1LTIuMDg5IDIuNzYyLTMuODYzIDUuNjEtMy44NjN2LTUuMjVabS04LjAzIDUuNzgyYTkuMTExIDkuMTExIDAgMCAwLS4zNy0uMDA3djUuMjVjLjA1MyAwIC4xMDUuMDAxLjE1Ni4wMDNsLjIxNS01LjI0NlptLS4zNy0uMDA3Yy01LjEzMyAwLTkuMTU4IDQuMjY4LTkuMTU4IDkuMzYyaDUuMjVjMC0yLjM0OCAxLjgyNS00LjExMiAzLjkwOC00LjExMnYtNS4yNVptLTkuMTU4IDkuMzYyYzAgNS4wOTUgNC4wMjUgOS4zNjMgOS4xNTggOS4zNjN2LTUuMjVjLTIuMDgzIDAtMy45MDgtMS43NjUtMy45MDgtNC4xMTNoLTUuMjVabTkuMTY2IDkuMzYzaC4wNGwtLjAxNy01LjI1aC0uMDM5bC4wMTYgNS4yNVptLjAzMSAwaDE1Ljc5NnYtNS4yNUgxMy41NzJ2NS4yNVptMTUuODI3LTUuMjVoLS4wMTZsLS4wMyA1LjI1aC4wNDZ2LTUuMjVabTIuOTc1LTMuMTVjMCAxLjgxNi0xLjQwNyAzLjE1LTIuOTc1IDMuMTV2NS4yNWM0LjYxOCAwIDguMjI1LTMuODM4IDguMjI1LTguNGgtNS4yNVptLTIuNDg5LTMuMTA5YzEuMzU1LjIzNSAyLjQ5IDEuNDg4IDIuNDkgMy4xMDloNS4yNWMwLTQuMTAyLTIuOTAyLTcuNi02Ljg0NC04LjI4MmwtLjg5NiA1LjE3M1oiIGZpbGw9IiMxRTFFMUUiLz48L2c+PC9zdmc+);height:42px;margin-right:4px;width:42px}.jp-connection-status-card--jetpack-logo{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE2IDMyYzguODM3IDAgMTYtNy4xNjMgMTYtMTZTMjQuODM3IDAgMTYgMCAwIDcuMTYzIDAgMTZzNy4xNjMgMTYgMTYgMTZaIiBmaWxsPSIjMDAwIi8+PHBhdGggZD0iTTE2Ljc5NCAxMy4zMTN2MTUuNTExbDgtMTUuNTEyaC04Wk0xNS4xNzUgMTguNjU3VjMuMTc2bC03Ljk2OSAxNS40OGg3Ljk3WiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==);height:32px;margin-left:11px;width:32px}.jp-connection-status-card--btn-connect-user{background:var(--jp-black)!important;border-radius:4px;font-size:var(--font-body-small);height:40px}.jp-connection-status-card--avatar{background-color:var(--jp-white);background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='32' height='32' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='16' cy='16' r='16' fill='%23fff'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M4.498 27.123C6.038 24.165 10.916 21.5 16 21.5c5.084 0 9.963 2.665 11.502 5.623a15.952 15.952 0 0 1-11.257 4.875L16 32l-.245-.002a15.952 15.952 0 0 1-11.257-4.875zM16 8a6 6 0 1 1 0 12 6 6 0 0 1 0-12z' fill='%23A2AAB2'/%3E%3C/svg%3E");background-repeat:no-repeat;background-size:contain;border:0;border-radius:20px;height:32px;margin-left:-10px;width:32px}.jp-connection-status-card--line{border-top:2px solid var(--jp-black);height:0;width:67px}.jp-connection-status-card--line.jp-connection-status-card--site-only{border-top-style:dashed}.jp-connection-status-card--list{list-style-type:none;margin:16px 0}.jp-connection-status-card--list li{color:var(--jp-black);margin:0 0 8px -3px;padding-left:25px}.jp-connection-status-card--list-item-success{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTE4LjkzNSA2LTguOSAxMS45Ny01LjE2Mi0zLjgzNyIgc3Ryb2tlPSIjMUUxRTFFIiBzdHJva2Utd2lkdGg9IjEuNSIvPjwvc3ZnPg==) no-repeat 0 0}.jp-connection-status-card--list-item-error{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDIwYTggOCAwIDEgMCAwLTE2IDggOCAwIDAgMCAwIDE2WiIgc3Ryb2tlPSIjRDYzNjM5IiBzdHJva2Utd2lkdGg9IjEuNSIvPjxwYXRoIGQ9Ik0xMyA3aC0ydjZoMlY3Wk0xMyAxNWgtMnYyaDJ2LTJaIiBmaWxsPSIjRDYzNjM5Ii8+PC9zdmc+) no-repeat 0 0;color:var(--jp-red)!important}.jp-connection__disconnect-dialog h1{font-size:var(--font-title-small);font-weight:600;line-height:1.2;margin-top:0}.jp-connection__disconnect-dialog h2{font-size:var(--font-title-small);font-weight:400;line-height:1.2;margin:0}.jp-connection__disconnect-dialog p{font-size:var(--font-body);margin-top:0}.jp-connection__disconnect-dialog__large-text,.jp-connection__disconnect-dialog p.jp-connection__disconnect-dialog__large-text{font-size:1.25rem}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link,.jp-connection__disconnect-dialog__link{color:var(--jp-black);font-size:var(--font-body);font:inherit;height:auto;padding:0;text-decoration:underline}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link:hover,.jp-connection__disconnect-dialog__link:hover{color:var(--jp-black);text-decoration-thickness:var(--jp-underline-thickness)}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link:focus,.jp-connection__disconnect-dialog__link:focus{box-shadow:none!important;color:var(--jp-black)}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link--bold,.jp-connection__disconnect-dialog__link--bold{font-weight:700}.jp-connection__disconnect-dialog .components-button{border-radius:4px;font-size:var(--font-body-small);height:40px}.jp-connection__disconnect-dialog .components-modal__content{display:flex;flex-direction:column;flex-grow:1;margin:0;padding:0}.jp-connection__disconnect-dialog .components-modal__content:before,.jp-connection__disconnect-dialog .components-modal__header{display:none}.jp-connection__disconnect-dialog .jp-row{align-items:center;width:calc(100% - 48px)}.jp-connection__disconnect-dialog__content{align-items:center;background:var(--jp-white-off);border-radius:4px;display:flex;flex-direction:column;flex-grow:1;justify-content:center;margin:0;padding:2rem 1rem;text-align:center}.jp-connection__disconnect-dialog__actions{background:var(--jp-white);border-top:1px solid var(--jp-gray);bottom:0;padding:2rem 0;position:sticky}.jp-connection__disconnect-dialog__actions p{margin-bottom:0}.jp-connection__disconnect-dialog__actions:before{background:linear-gradient(to bottom,transparent,var(--jp-white-off));bottom:calc(100% + 1px);content:"";display:block;height:80px;left:0;position:absolute;width:100%}.jp-connection__disconnect-dialog__btn-dismiss,.jp-connection__disconnect-dialog__btn-dismiss.components-button{background:var(--jp-black)!important;margin-right:10px}.jp-connection__disconnect-dialog__btn-disconnect{background:var(--jp-red)!important}.jp-connection__disconnect-dialog__btn-back-to-wp{background:var(--jp-black)!important}.jp-connection__disconnect-dialog__button-wrap{text-align:left}@media(min-width:960px){.jp-connection__disconnect-dialog__button-wrap{text-align:center}}.jp-connection__disconnect-dialog__error{color:var(--jp-red)}.jp-connection__disconnect-dialog__survey{margin-bottom:1.5rem;max-width:100%}.jp-connection__disconnect-dialog__step-copy{margin:0 auto;max-width:800px}.jp-connection__disconnect-dialog__step-copy--narrow{max-width:600px}@media(max-height:900px){.jp-connection__disconnect-dialog__content .jp-components__decorative-card{display:none}}@media(min-width:600px){.jp-connection__disconnect-dialog,.jp-connection__disconnect-dialog.components-modal__frame{max-width:calc(100% - 32px);width:100%}.jp-connection__disconnect-dialog__actions,.jp-connection__disconnect-dialog__content{padding:2rem}}@media(min-width:960px){.jp-connection__disconnect-dialog,.jp-connection__disconnect-dialog.components-modal__frame{display:flex;flex-direction:column;height:900px;width:1200px}.jp-connection__disconnect-dialog h1{font-size:var(--font-title-large)}.jp-connection__disconnect-dialog__large-text,.jp-connection__disconnect-dialog p.jp-connection__disconnect-dialog__large-text{font-size:1.5rem}.jp-connection__disconnect-dialog__content{padding:80px}.jp-connection__disconnect-dialog__actions{padding:2rem 3rem}.jp-row{margin-left:0}}.jp-connection__disconnect-card{background-color:var(--jp-white);border:none;border-radius:3px;box-shadow:0 0 15px var(--jp-gray-off);margin:0 auto 1rem;max-width:100%;padding:1rem 2rem;text-align:left;width:800px}.jp-connection__disconnect-card__group{margin-bottom:1rem;max-width:100%}.jp-connection__disconnect-card__card-content{display:block;font-size:.875rem}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-content{align-items:center;display:flex;justify-content:space-between}}.jp-connection__disconnect-card .jp-connection__disconnect-card__card-headline,.jp-connection__disconnect-card__card-headline{flex-shrink:0;font-size:1.25rem;font-weight:600;margin-bottom:0;margin-top:0}@media only screen and (min-width:782px){.jp-connection__disconnect-card .jp-connection__disconnect-card__card-headline,.jp-connection__disconnect-card__card-headline{font-size:1.5rem;margin-right:1.5rem}}@media only screen and (max-width:782px){.jp-connection__disconnect-card .jp-connection__disconnect-card__card-headline+.jp-disconnect-card__card-stat-block,.jp-connection__disconnect-card__card-headline+.jp-disconnect-card__card-stat-block{margin-top:.5rem}}.jp-connection__disconnect-card__card-stat-block{align-items:baseline;display:flex;flex-grow:1}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-stat-block{flex-direction:row-reverse}}.jp-connection__disconnect-card__card-description{flex-grow:1}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-description{text-align:right}}.jp-connection__disconnect-card__card-stat{font-size:1rem;font-weight:600;margin-right:.5rem}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-stat{font-size:1.5rem;margin-left:1rem;margin-right:0}}.jp-components__decorative-card{border-radius:8px;box-shadow:0 0 15px var(--jp-gray);display:flex;height:280px;margin:0 auto 3rem;max-width:100%;overflow:hidden;position:relative;width:360px}.jp-components__decorative-card__content,.jp-components__decorative-card__image{width:50%}.jp-components__decorative-card__image{background:var(--jp-gray);background-size:cover;position:relative}.jp-components__decorative-card__image:before{background-image:url('data:image/svg+xml;uf8,<svg width="38" height="8" viewBox="0 0 38 8" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 7C1 7 2.37087 1 6.89831 1C11.4257 1 14.3709 7 18.8983 7C23.4257 7 26.7777 1 31.3051 1C35.912 1 37 7 37 7" stroke="white" stroke-width="1.5" stroke-linejoin="round"/></svg>');content:"";display:block;height:8px;left:24px;position:absolute;top:24px;width:38px}.jp-components__decorative-card__content{background:#fff;padding:2rem}.jp-components__decorative-card__icon-container{background:var(--jp-red);border-radius:50px;height:80px;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);width:80px}.jp-components__decorative-card__icon{background-position:50%,50%;background-repeat:no-repeat;height:40px;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);width:40px}.jp-components__decorative-card__icon--unlink{background-image:url('data:image/svg+xml;uf8,<svg width="34" height="37" viewBox="0 0 34 37" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M22.3335 10.001H25.0002C29.4184 10.001 33.0002 13.5827 33.0002 18.001V19.7788C33.0002 24.197 29.4184 27.7788 25.0002 27.7788H22.3335" stroke="white" stroke-width="1.5" stroke-linecap="square"/> <path d="M11.6675 27.7783L9.00082 27.7783C4.58254 27.7783 1.00081 24.1966 1.00081 19.7783L1.00081 18.0005C1.00081 13.5823 4.58253 10.0005 9.00081 10.0005L11.6675 10.0005" stroke="white" stroke-width="1.5" stroke-linecap="square"/> <path d="M10.9998 19.167L16.9998 19.167" stroke="white" stroke-width="1.5"/> <path d="M8.99951 35.998L24.9995 0.998048" stroke="white"/> </svg>')}.jp-components__decorative-card__lines,.jp-components__decorative-card__lines:after,.jp-components__decorative-card__lines:before{background:#e9eff5;border-radius:6px;display:block;height:12px;position:relative;width:100%}.jp-components__decorative-card__lines:after,.jp-components__decorative-card__lines:before{content:"";top:calc(100% + 16px)}.jp-components__decorative-card__lines:after{top:calc(100% + 32px);width:75%}.jp-components__decorative-card--vertical{flex-direction:column}.jp-components__decorative-card--vertical .jp-components__decorative-card__content,.jp-components__decorative-card--vertical .jp-components__decorative-card__image{height:50%;width:100%}.jp-components__decorative-card--vertical .jp-components__decorative-card__lines{margin-left:auto;margin-right:auto;max-width:135px}.jp-components__decorative-card--vertical .jp-components__decorative-card__lines:after,.jp-components__decorative-card--vertical .jp-components__decorative-card__lines:before{margin-left:auto;margin-right:auto}.jp-connect__disconnect-survey-card{border:2px solid transparent;border-radius:4px;box-shadow:0 0 15px var(--jp-gray-off);margin-left:auto;margin-right:auto;max-width:100%;padding:1rem;position:relative;text-align:left;width:800px}.jp-connect__disconnect-survey-card--selected{background:var(--jp-gray-off);border-color:var(--jp-black)}.jp-connect__disconnect-survey-card:after{border-right:2px solid var(--jp-black);border-top:2px solid var(--jp-black);content:"";display:block;height:5px;position:absolute;right:1.5rem;top:50%;transform:translateY(-50%) rotate(45deg);width:5px}.jp-connect__disconnect-survey-card:hover{cursor:pointer}.jp-connect__disconnect-survey-card:focus:not(.jp-disconnect-survey-card--selected),.jp-connect__disconnect-survey-card:hover:not(.jp-disconnect-survey-card--selected){border-color:var(--jp-black-80)}.jp-connect__disconnect-survey-card__answer{align-items:center;display:flex;font-weight:700;margin:0}input.jp-connect__disconnect-survey-card__input{-webkit-appearance:none;background-color:transparent;border:none;color:var(--jp-black-80);flex-grow:1;max-width:calc(100% - 40px);padding-right:40px}@media(min-width:1080px){.jp-connection__connect-screen-layout__left{width:70%}.jp-connection__connect-screen-required-plan{background:linear-gradient(90deg,#fff 70%,#f9f9f6 0);position:relative}}.jp-connection__connect-screen-required-plan__loading{display:none}@media(min-width:1080px){.jp-connection__connect-screen-required-plan__pricing-card{left:62%;position:absolute;top:14%}}.jp-connection__connect-screen-required-plan__pricing-card .components-button{align-items:center;background:var(--jp-black)!important;border-radius:var(--jp-border-radius);color:var(--jp-white)!important;font-size:18px;font-weight:500;height:auto;justify-content:center;margin:24px 0 32px;padding:14px 24px;width:100%}.jp-connection__connect-screen-required-plan__with-subscription{margin-top:38px}.jp-connection__connect-screen-required-plan__with-subscription .jp-action-button{display:inline}.jp-connection__connect-screen-required-plan__with-subscription .jp-action-button--button{background:inherit!important;color:var(--jp-black)!important;display:inline;font-size:var(--font-title-small);font:inherit;height:auto;line-height:20px;min-width:0;padding:0;text-decoration:underline;width:auto}.jp-connection__connect-screen-required-plan__with-subscription .jp-action-button--button:hover{background:inherit;text-decoration-thickness:var(--jp-underline-thickness)}.jp-connection__connect-screen-required-plan__with-subscription .jp-action-button--button:focus{background:inherit;box-shadow:none!important}.jp-connection__connect-screen-required-plan__with-subscription .jp-components-spinner__inner,.jp-connection__connect-screen-required-plan__with-subscription .jp-components-spinner__outer{border-right-color:var(--jp-black);border-top-color:var(--jp-black)}.jp-action-button--button{background:#000}.jp-action-button--button,.jp-action-button--button.components-button{border-radius:4px;display:block;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:14px;font-style:normal;font-weight:600;height:40px;line-height:18px;min-width:264px;text-align:center}.jp-action-button__error{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDIwYTggOCAwIDEgMCAwLTE2IDggOCAwIDAgMCAwIDE2WiIgc3Ryb2tlPSIjRDYzNjM5IiBzdHJva2Utd2lkdGg9IjEuNSIvPjxwYXRoIGQ9Ik0xMyA3aC0ydjZoMlY3Wk0xMyAxNWgtMnYyaDJ2LTJaIiBmaWxsPSIjRDYzNjM5Ii8+PC9zdmc+) no-repeat 0 0;color:var(--jp-red)!important;line-height:25px!important;padding-left:25px}@keyframes rotate-spinner{to{transform:rotate(1turn)}}.jp-components-spinner{align-items:center;display:flex}.jp-components-spinner__inner,.jp-components-spinner__outer{animation:3s linear infinite;animation-name:rotate-spinner;border:.1em solid transparent;border-radius:50%;box-sizing:border-box;margin:auto}.jp-components-spinner__outer{border-top-color:#fff}.jp-components-spinner__inner{border-right-color:#fff;border-top-color:#fff;height:100%;opacity:.4;width:100%}.jp-connection__connect-screen-layout{background:var(--jp-white);border-radius:4px;box-shadow:0 0 40px rgba(0,0,0,.08)}.jp-connection__connect-screen-layout__loading{display:none}.jp-connection__connect-screen-layout__left,.jp-connection__connect-screen-layout__right{box-sizing:border-box}.jp-connection__connect-screen-layout__left{padding:25px}@media(min-width:600px){.jp-connection__connect-screen-layout__left{padding:64px 96px}}.jp-connection__connect-screen-layout__left .jetpack-logo{margin-bottom:24px}.jp-connection__connect-screen-layout__left h2{color:var(--jp-black);font-size:36px;font-style:normal;font-weight:700;line-height:40px;margin-bottom:0;margin-top:32px}.jp-connection__connect-screen-layout__left h3{color:var(--jp-black);font-size:24px;font-style:normal;font-weight:500;line-height:32px;margin-bottom:0;margin-top:32px}.jp-connection__connect-screen-layout__left li,.jp-connection__connect-screen-layout__left p{font-size:16px;font-style:normal;font-weight:400;line-height:24px}.jp-connection__connect-screen-layout__left p{color:#101517;margin:16px 0}.jp-connection__connect-screen-layout__left a{color:var(--jp-black);font-size:var(--font-body);font:inherit;height:auto;padding:0;text-decoration:underline}.jp-connection__connect-screen-layout__left a:hover{color:var(--jp-black);text-decoration-thickness:var(--jp-underline-thickness)}.jp-connection__connect-screen-layout__left a:focus{box-shadow:none!important;color:var(--jp-black)}.jp-connection__connect-screen-layout__left ul{list-style-type:none;padding:0}.jp-connection__connect-screen-layout__left ul li{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAANlBMVEVHcEwFnwUInggGnggGnggHnAcAnwUFnQcAnwcGnwkFnQgGnQgFnwcGnQYFnQcFnAcGnQkDnwdhiL0pAAAAEnRSTlMAMF//f2Aw7yBQ3+9gcIBgcED+HDbkAAAAZklEQVR4Ae3LNwICARDDQC0+cv7/Y8mwV9odSfWIcf/+VegnGkIvDaGXKvTTn/Gz+Uf5xTL0K1XotS7fs5H6GHvvaO8d7c3j7rdgHne/A/PYt/cO+R42oYdN6OEQetiFHo4A//6dAXqtBEkmtWutAAAAAElFTkSuQmCC) no-repeat;background-size:24px;color:var(--jp-black);margin-bottom:9px;padding-left:30px}.jp-connection__connect-screen-layout__right{padding:64px 0}.jp-connection__connect-screen-layout__right img{max-width:100%}.jp-connection__connect-screen-layout__two-columns{display:flex;flex-wrap:wrap}.jp-connection__connect-screen-layout__two-columns .jp-connection__connect-screen-layout__left{flex-basis:100%;flex-grow:1}@media(min-width:1080px){.jp-connection__connect-screen-layout__two-columns .jp-connection__connect-screen-layout__left{flex-basis:52%}}.jp-connection__connect-screen-layout__two-columns .jp-connection__connect-screen-layout__right{background:#f9f9f6;display:none;flex-basis:47%;flex-grow:1}@media(min-width:1080px){.jp-connection__connect-screen-layout__two-columns .jp-connection__connect-screen-layout__right{display:block}}:root{--font-title-large:36px;--font-title-small:24px;--font-body:16px;--font-label:12px;--jp-black:#000;--jp-black-80:#2c3338;--jp-white:#fff;--jp-white-off:#f9f9f6;--jp-gray:#dcdcde;--jp-gray-0:#f6f7f7;--jp-gray-20:#a7aaad;--jp-gray-40:#787c82;--jp-gray-50:#646970;--jp-gray-60:#50575e;--jp-gray-80:#8a2424;--jp-gray-off:#e2e2df;--jp-red-0:#f7ebec;--jp-red-50:#d63638;--jp-red-60:#b32d2e;--jp-red-80:#8a2424;--jp-red:#d63639;--jp-pink:#c9356e;--jp-green-0:#f0f2eb;--jp-green-5:#d0e6b8;--jp-green-10:#9dd977;--jp-green-20:#64ca43;--jp-green-30:#2fb41f;--jp-green-40:#069e08;--jp-green-50:#008710;--jp-green-60:#007117;--jp-green-70:#005b18;--jp-green-80:#004515;--jp-green-90:#003010;--jp-green-100:#001c09;--jp-green:#069e08;--jp-green-primary:var( --jp-green-40 );--jp-green-secondary:var( --jp-green-30 );--jp-border-radius:4px;--jp-menu-border-height:1px;--jp-underline-thickness:2px}*{box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;margin:0;min-height:100%;padding:0}.jp-wrap{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.jp-row{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.jp-row{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.jp-row{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.sm-col-span-1{grid-column-end:span 1}.sm-col-span-2{grid-column-end:span 2}.sm-col-span-3{grid-column-end:span 3}.sm-col-span-4{grid-column-end:span 4}@media(min-width:600px){.md-col-span-1{grid-column-end:span 1}.md-col-span-2{grid-column-end:span 2}.md-col-span-3{grid-column-end:span 3}.md-col-span-4{grid-column-end:span 4}.md-col-span-5{grid-column-end:span 5}.md-col-span-6{grid-column-end:span 6}.md-col-span-7{grid-column-end:span 7}.md-col-span-8{grid-column-end:span 8}}@media(min-width:960px){.lg-col-span-1{grid-column-end:span 1}.lg-col-span-2{grid-column-end:span 2}.lg-col-span-3{grid-column-end:span 3}.lg-col-span-4{grid-column-end:span 4}.lg-col-span-5{grid-column-end:span 5}.lg-col-span-6{grid-column-end:span 6}.lg-col-span-7{grid-column-end:span 7}.lg-col-span-8{grid-column-end:span 8}.lg-col-span-9{grid-column-end:span 9}.lg-col-span-10{grid-column-end:span 10}.lg-col-span-11{grid-column-end:span 11}.lg-col-span-12{grid-column-end:span 12}}@media(max-width:960px){.md-col-span-0{display:none}}@media(max-width:600px){.sm-col-span-0{display:none}}.jp-cut{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);margin:32px 0;padding:16px 64px 16px 24px;position:relative;text-decoration:none}.jp-cut,.jp-cut span{display:block}.jp-cut span:last-of-type{font-weight:600}.jp-cut:focus span:last-of-type,.jp-cut:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.jp-cut:focus:after,.jp-cut:hover:after{transform:translateY(-50%) translateX(8px)}.jp-cut:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;position:absolute;right:24px;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.jp-components__pricing-card{background:var(--jp-white);border-radius:var(--jp-border-radius);box-shadow:0 10px 40px rgba(0,0,0,.08);max-width:384px;padding:24px 24px 32px;width:-moz-fit-content;width:fit-content}@media screen and (min-width:600px){.jp-components__pricing-card{padding:32px 32px 44px}}.jp-components__pricing-card__icon img{height:32px;width:32px}.jp-components__pricing-card__title{color:#101517;font-size:32px;line-height:38px;margin:16px 0 24px}.jp-components__pricing-card__pricing{display:flex;flex-wrap:wrap}.jp-components__pricing-card__price-after,.jp-components__pricing-card__price-before{display:inline-block;font-size:54px;font-weight:700;line-height:40px;margin-bottom:8px;padding:0 2px}.jp-components__pricing-card__price-before{color:var(--jp-gray-20);margin-right:16px;position:relative}.jp-components__pricing-card__price-strikethrough{background:var(--jp-pink);border-radius:1.5px;height:3px;left:0;position:absolute;top:20px;width:100%}.jp-components__pricing-card__price-after{color:var(--jp-black)}.jp-components__pricing-card__currency{font-size:var(--font-title-small);font-weight:400;line-height:20px;vertical-align:super}.jp-components__pricing-card__price-details{align-self:flex-end;color:var(--jp-gray-50);font-size:14px;font-weight:400;letter-spacing:-.02em;line-height:17px;margin-bottom:8px}.jp-components__pricing-card__price-decimal{font-size:var(--font-label);line-height:14px;vertical-align:top}.jp-components__pricing-card__button{align-items:center;background:var(--jp-black);border-radius:var(--jp-border-radius);color:var(--jp-white)!important;font-size:18px;height:auto;justify-content:center;margin:24px 0 32px;padding:14px 24px;width:100%}.jp-components__pricing-card__info{color:var(--jp-gray-60);font-size:var(--font-label);letter-spacing:-.02em;line-height:20px} \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.js
new file mode 100644
index 00000000..c8a04c5d
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.js
@@ -0,0 +1,4 @@
+/*! For license information please see index.js.LICENSE.txt */
+!function(){var e={4728:function(e,t,n){"use strict";n.d(t,{X:function(){return o}});var i={AED:{symbol:"د.إ.‏",grouping:",",decimal:".",precision:2},AFN:{symbol:"؋",grouping:",",decimal:".",precision:2},ALL:{symbol:"Lek",grouping:".",decimal:",",precision:2},AMD:{symbol:"֏",grouping:",",decimal:".",precision:2},ANG:{symbol:"ƒ",grouping:",",decimal:".",precision:2},AOA:{symbol:"Kz",grouping:",",decimal:".",precision:2},ARS:{symbol:"$",grouping:".",decimal:",",precision:2},AUD:{symbol:"A$",grouping:",",decimal:".",precision:2},AWG:{symbol:"ƒ",grouping:",",decimal:".",precision:2},AZN:{symbol:"₼",grouping:" ",decimal:",",precision:2},BAM:{symbol:"КМ",grouping:".",decimal:",",precision:2},BBD:{symbol:"Bds$",grouping:",",decimal:".",precision:2},BDT:{symbol:"৳",grouping:",",decimal:".",precision:0},BGN:{symbol:"лв.",grouping:" ",decimal:",",precision:2},BHD:{symbol:"د.ب.‏",grouping:",",decimal:".",precision:3},BIF:{symbol:"FBu",grouping:",",decimal:".",precision:0},BMD:{symbol:"$",grouping:",",decimal:".",precision:2},BND:{symbol:"$",grouping:".",decimal:",",precision:0},BOB:{symbol:"Bs",grouping:".",decimal:",",precision:2},BRL:{symbol:"R$",grouping:".",decimal:",",precision:2},BSD:{symbol:"$",grouping:",",decimal:".",precision:2},BTC:{symbol:"Ƀ",grouping:",",decimal:".",precision:2},BTN:{symbol:"Nu.",grouping:",",decimal:".",precision:1},BWP:{symbol:"P",grouping:",",decimal:".",precision:2},BYR:{symbol:"р.",grouping:" ",decimal:",",precision:2},BZD:{symbol:"BZ$",grouping:",",decimal:".",precision:2},CAD:{symbol:"C$",grouping:",",decimal:".",precision:2},CDF:{symbol:"FC",grouping:",",decimal:".",precision:2},CHF:{symbol:"CHF",grouping:"'",decimal:".",precision:2},CLP:{symbol:"$",grouping:".",decimal:",",precision:2},CNY:{symbol:"¥",grouping:",",decimal:".",precision:2},COP:{symbol:"$",grouping:".",decimal:",",precision:2},CRC:{symbol:"₡",grouping:".",decimal:",",precision:2},CUC:{symbol:"CUC",grouping:",",decimal:".",precision:2},CUP:{symbol:"$MN",grouping:",",decimal:".",precision:2},CVE:{symbol:"$",grouping:",",decimal:".",precision:2},CZK:{symbol:"Kč",grouping:" ",decimal:",",precision:2},DJF:{symbol:"Fdj",grouping:",",decimal:".",precision:0},DKK:{symbol:"kr.",grouping:"",decimal:",",precision:2},DOP:{symbol:"RD$",grouping:",",decimal:".",precision:2},DZD:{symbol:"د.ج.‏",grouping:",",decimal:".",precision:2},EGP:{symbol:"ج.م.‏",grouping:",",decimal:".",precision:2},ERN:{symbol:"Nfk",grouping:",",decimal:".",precision:2},ETB:{symbol:"ETB",grouping:",",decimal:".",precision:2},EUR:{symbol:"€",grouping:".",decimal:",",precision:2},FJD:{symbol:"FJ$",grouping:",",decimal:".",precision:2},FKP:{symbol:"£",grouping:",",decimal:".",precision:2},GBP:{symbol:"£",grouping:",",decimal:".",precision:2},GEL:{symbol:"Lari",grouping:" ",decimal:",",precision:2},GHS:{symbol:"₵",grouping:",",decimal:".",precision:2},GIP:{symbol:"£",grouping:",",decimal:".",precision:2},GMD:{symbol:"D",grouping:",",decimal:".",precision:2},GNF:{symbol:"FG",grouping:",",decimal:".",precision:0},GTQ:{symbol:"Q",grouping:",",decimal:".",precision:2},GYD:{symbol:"G$",grouping:",",decimal:".",precision:2},HKD:{symbol:"HK$",grouping:",",decimal:".",precision:2},HNL:{symbol:"L.",grouping:",",decimal:".",precision:2},HRK:{symbol:"kn",grouping:".",decimal:",",precision:2},HTG:{symbol:"G",grouping:",",decimal:".",precision:2},HUF:{symbol:"Ft",grouping:".",decimal:",",precision:0},IDR:{symbol:"Rp",grouping:".",decimal:",",precision:0},ILS:{symbol:"₪",grouping:",",decimal:".",precision:2},INR:{symbol:"₹",grouping:",",decimal:".",precision:2},IQD:{symbol:"د.ع.‏",grouping:",",decimal:".",precision:2},IRR:{symbol:"﷼",grouping:",",decimal:"/",precision:2},ISK:{symbol:"kr.",grouping:".",decimal:",",precision:0},JMD:{symbol:"J$",grouping:",",decimal:".",precision:2},JOD:{symbol:"د.ا.‏",grouping:",",decimal:".",precision:3},JPY:{symbol:"¥",grouping:",",decimal:".",precision:0},KES:{symbol:"S",grouping:",",decimal:".",precision:2},KGS:{symbol:"сом",grouping:" ",decimal:"-",precision:2},KHR:{symbol:"៛",grouping:",",decimal:".",precision:0},KMF:{symbol:"CF",grouping:",",decimal:".",precision:2},KPW:{symbol:"₩",grouping:",",decimal:".",precision:0},KRW:{symbol:"₩",grouping:",",decimal:".",precision:0},KWD:{symbol:"د.ك.‏",grouping:",",decimal:".",precision:3},KYD:{symbol:"$",grouping:",",decimal:".",precision:2},KZT:{symbol:"₸",grouping:" ",decimal:"-",precision:2},LAK:{symbol:"₭",grouping:",",decimal:".",precision:0},LBP:{symbol:"ل.ل.‏",grouping:",",decimal:".",precision:2},LKR:{symbol:"₨",grouping:",",decimal:".",precision:0},LRD:{symbol:"L$",grouping:",",decimal:".",precision:2},LSL:{symbol:"M",grouping:",",decimal:".",precision:2},LYD:{symbol:"د.ل.‏",grouping:",",decimal:".",precision:3},MAD:{symbol:"د.م.‏",grouping:",",decimal:".",precision:2},MDL:{symbol:"lei",grouping:",",decimal:".",precision:2},MGA:{symbol:"Ar",grouping:",",decimal:".",precision:0},MKD:{symbol:"ден.",grouping:".",decimal:",",precision:2},MMK:{symbol:"K",grouping:",",decimal:".",precision:2},MNT:{symbol:"₮",grouping:" ",decimal:",",precision:2},MOP:{symbol:"MOP$",grouping:",",decimal:".",precision:2},MRO:{symbol:"UM",grouping:",",decimal:".",precision:2},MTL:{symbol:"₤",grouping:",",decimal:".",precision:2},MUR:{symbol:"₨",grouping:",",decimal:".",precision:2},MVR:{symbol:"MVR",grouping:",",decimal:".",precision:1},MWK:{symbol:"MK",grouping:",",decimal:".",precision:2},MXN:{symbol:"MX$",grouping:",",decimal:".",precision:2},MYR:{symbol:"RM",grouping:",",decimal:".",precision:2},MZN:{symbol:"MT",grouping:",",decimal:".",precision:0},NAD:{symbol:"N$",grouping:",",decimal:".",precision:2},NGN:{symbol:"₦",grouping:",",decimal:".",precision:2},NIO:{symbol:"C$",grouping:",",decimal:".",precision:2},NOK:{symbol:"kr",grouping:" ",decimal:",",precision:2},NPR:{symbol:"₨",grouping:",",decimal:".",precision:2},NZD:{symbol:"NZ$",grouping:",",decimal:".",precision:2},OMR:{symbol:"﷼",grouping:",",decimal:".",precision:3},PAB:{symbol:"B/.",grouping:",",decimal:".",precision:2},PEN:{symbol:"S/.",grouping:",",decimal:".",precision:2},PGK:{symbol:"K",grouping:",",decimal:".",precision:2},PHP:{symbol:"₱",grouping:",",decimal:".",precision:2},PKR:{symbol:"₨",grouping:",",decimal:".",precision:2},PLN:{symbol:"zł",grouping:" ",decimal:",",precision:2},PYG:{symbol:"₲",grouping:".",decimal:",",precision:2},QAR:{symbol:"﷼",grouping:",",decimal:".",precision:2},RON:{symbol:"lei",grouping:".",decimal:",",precision:2},RSD:{symbol:"Дин.",grouping:".",decimal:",",precision:2},RUB:{symbol:"₽",grouping:" ",decimal:",",precision:2},RWF:{symbol:"RWF",grouping:" ",decimal:",",precision:2},SAR:{symbol:"﷼",grouping:",",decimal:".",precision:2},SBD:{symbol:"S$",grouping:",",decimal:".",precision:2},SCR:{symbol:"₨",grouping:",",decimal:".",precision:2},SDD:{symbol:"LSd",grouping:",",decimal:".",precision:2},SDG:{symbol:"£‏",grouping:",",decimal:".",precision:2},SEK:{symbol:"kr",grouping:",",decimal:".",precision:2},SGD:{symbol:"S$",grouping:",",decimal:".",precision:2},SHP:{symbol:"£",grouping:",",decimal:".",precision:2},SLL:{symbol:"Le",grouping:",",decimal:".",precision:2},SOS:{symbol:"S",grouping:",",decimal:".",precision:2},SRD:{symbol:"$",grouping:",",decimal:".",precision:2},STD:{symbol:"Db",grouping:",",decimal:".",precision:2},SVC:{symbol:"₡",grouping:",",decimal:".",precision:2},SYP:{symbol:"£",grouping:",",decimal:".",precision:2},SZL:{symbol:"E",grouping:",",decimal:".",precision:2},THB:{symbol:"฿",grouping:",",decimal:".",precision:2},TJS:{symbol:"TJS",grouping:" ",decimal:";",precision:2},TMT:{symbol:"m",grouping:" ",decimal:",",precision:0},TND:{symbol:"د.ت.‏",grouping:",",decimal:".",precision:3},TOP:{symbol:"T$",grouping:",",decimal:".",precision:2},TRY:{symbol:"TL",grouping:".",decimal:",",precision:2},TTD:{symbol:"TT$",grouping:",",decimal:".",precision:2},TVD:{symbol:"$T",grouping:",",decimal:".",precision:2},TWD:{symbol:"NT$",grouping:",",decimal:".",precision:2},TZS:{symbol:"TSh",grouping:",",decimal:".",precision:2},UAH:{symbol:"₴",grouping:" ",decimal:",",precision:2},UGX:{symbol:"USh",grouping:",",decimal:".",precision:2},USD:{symbol:"$",grouping:",",decimal:".",precision:2},UYU:{symbol:"$U",grouping:".",decimal:",",precision:2},UZS:{symbol:"сўм",grouping:" ",decimal:",",precision:2},VEB:{symbol:"Bs.",grouping:",",decimal:".",precision:2},VEF:{symbol:"Bs. F.",grouping:".",decimal:",",precision:2},VND:{symbol:"₫",grouping:".",decimal:",",precision:1},VUV:{symbol:"VT",grouping:",",decimal:".",precision:0},WST:{symbol:"WS$",grouping:",",decimal:".",precision:2},XAF:{symbol:"F",grouping:",",decimal:".",precision:2},XCD:{symbol:"$",grouping:",",decimal:".",precision:2},XOF:{symbol:"F",grouping:" ",decimal:",",precision:2},XPF:{symbol:"F",grouping:",",decimal:".",precision:2},YER:{symbol:"﷼",grouping:",",decimal:".",precision:2},ZAR:{symbol:"R",grouping:" ",decimal:",",precision:2},ZMW:{symbol:"ZK",grouping:",",decimal:".",precision:2},WON:{symbol:"₩",grouping:",",decimal:".",precision:2}};function o(e){return i[e]||{symbol:"$",grouping:",",decimal:".",precision:2}}},6078:function(e,t,n){"use strict";n.d(t,{LR:function(){return c}});var i=n(2141),o=n(3807),r=n(4728);function c(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},c=(0,r.X)(t);if(!c||isNaN(e))return null;var s=(0,i.Z)({},c,n),a=s.decimal,u=s.grouping,l=s.precision,p=s.symbol,d=e<0?"-":"",g=Math.abs(e),m=Math.floor(g),f=(0,o.Y4)(m,{decimals:0,thousandsSep:u,decPoint:a}),h=l>0?(0,o.Y4)(g-m,{decimals:l,thousandsSep:u,decPoint:a}).slice(1):"";return{sign:d,symbol:p,integer:f,fraction:h}}},8772:function(e,t,n){"use strict";n.d(t,{Z:function(){return c}});var i=n(9196),o=n(5592);function r(e,t){let n,o,c=[];for(let i=0;i<e.length;i++){const r=e[i];if("string"!==r.type){if(void 0===t[r.value])throw new Error(`Invalid interpolation, missing component node: \`${r.value}\``);if("object"!=typeof t[r.value])throw new Error(`Invalid interpolation, component node must be a ReactElement or null: \`${r.value}\``);if("componentClose"===r.type)throw new Error(`Missing opening component token: \`${r.value}\``);if("componentOpen"===r.type){n=t[r.value],o=i;break}c.push(t[r.value])}else c.push(r.value)}if(n){const s=function(e,t){const n=t[e];let i=0;for(let o=e+1;o<t.length;o++){const e=t[o];if(e.value===n.value){if("componentOpen"===e.type){i++;continue}if("componentClose"===e.type){if(0===i)return o;i--}}}throw new Error("Missing closing component token `"+n.value+"`")}(o,e),a=r(e.slice(o+1,s),t),u=(0,i.cloneElement)(n,{},a);if(c.push(u),s<e.length-1){const n=r(e.slice(s+1),t);c=c.concat(n)}}return c=c.filter(Boolean),0===c.length?null:1===c.length?c[0]:(0,i.createElement)(i.Fragment,null,...c)}function c(e){const{mixedString:t,components:n,throwErrors:i}=e;if(!n)return t;if("object"!=typeof n){if(i)throw new Error(`Interpolation Error: unable to process \`${t}\` because components is not an object`);return t}const c=(0,o.Z)(t);try{return r(c,n)}catch(e){if(i)throw new Error(`Interpolation Error: unable to process \`${t}\` because of error \`${e.message}\``);return t}}},5592:function(e,t,n){"use strict";function i(e){return e.startsWith("{{/")?{type:"componentClose",value:e.replace(/\W/g,"")}:e.endsWith("/}}")?{type:"componentSelfClosing",value:e.replace(/\W/g,"")}:e.startsWith("{{")?{type:"componentOpen",value:e.replace(/\W/g,"")}:{type:"string",value:e}}function o(e){return e.split(/(\{\{\/?\s*\w+\s*\/?\}\})/g).map(i)}n.d(t,{Z:function(){return o}})},7538:function(e){e.exports=function(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e},e.exports.default=e.exports,e.exports.__esModule=!0},9183:function(e){function t(){return e.exports=t=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},e.exports.default=e.exports,e.exports.__esModule=!0,t.apply(this,arguments)}e.exports=t,e.exports.default=e.exports,e.exports.__esModule=!0},8172:function(e,t,n){"use strict";n.d(t,{Z:function(){return r}});var i=n(1354),o=n(4803);function r(e){var t=(0,i.Z)(e);return function(e){return(0,o.Z)(t,e)}}},4803:function(e,t,n){"use strict";n.d(t,{Z:function(){return o}});var i={"!":function(e){return!e},"*":function(e,t){return e*t},"/":function(e,t){return e/t},"%":function(e,t){return e%t},"+":function(e,t){return e+t},"-":function(e,t){return e-t},"<":function(e,t){return e<t},"<=":function(e,t){return e<=t},">":function(e,t){return e>t},">=":function(e,t){return e>=t},"==":function(e,t){return e===t},"!=":function(e,t){return e!==t},"&&":function(e,t){return e&&t},"||":function(e,t){return e||t},"?:":function(e,t,n){if(e)throw t;return n}};function o(e,t){var n,o,r,c,s,a,u=[];for(n=0;n<e.length;n++){if(s=e[n],c=i[s]){for(o=c.length,r=Array(o);o--;)r[o]=u.pop();try{a=c.apply(null,r)}catch(e){return e}}else a=t.hasOwnProperty(s)?t[s]:+s;u.push(a)}return u[0]}},7478:function(e,t,n){"use strict";n.d(t,{Z:function(){return o}});var i=n(8172);function o(e){var t=(0,i.Z)(e);return function(e){return+t({n:e})}}},1354:function(e,t,n){"use strict";var i,o,r,c;function s(e){for(var t,n,s,a,u=[],l=[];t=e.match(c);){for(n=t[0],(s=e.substr(0,t.index).trim())&&u.push(s);a=l.pop();){if(r[n]){if(r[n][0]===a){n=r[n][1]||n;break}}else if(o.indexOf(a)>=0||i[a]<i[n]){l.push(a);break}u.push(a)}r[n]||l.push(n),e=e.substr(t.index+n.length)}return(e=e.trim())&&u.push(e),u.concat(l.reverse())}n.d(t,{Z:function(){return s}}),i={"(":9,"!":8,"*":7,"/":7,"%":7,"+":6,"-":6,"<":5,"<=":5,">":5,">=":5,"==":4,"!=":4,"&&":3,"||":2,"?":1,"?:":1},o=["(","?"],r={")":["("],":":["?","?:"]},c=/<=|>=|==|!=|&&|\|\||\?:|\(|!|\*|\/|%|\+|-|<|>|\?|\)|:/},702:function(e,t,n){"use strict";n.d(t,{Z:function(){return o}});var i=/%(((\d+)\$)|(\(([$_a-zA-Z][$_a-zA-Z0-9]*)\)))?[ +0#-]*\d*(\.(\d+|\*))?(ll|[lhqL])?([cduxXefgsp%])/g;function o(e,t){var n;if(!Array.isArray(t))for(t=new Array(arguments.length-1),n=1;n<arguments.length;n++)t[n-1]=arguments[n];return n=1,e.replace(i,(function(){var e,i,o,r,c;return e=arguments[3],i=arguments[5],"%"===(r=arguments[9])?"%":("*"===(o=arguments[7])&&(o=t[n-1],n++),void 0!==i?t[0]&&"object"==typeof t[0]&&t[0].hasOwnProperty(i)&&(c=t[0][i]):(void 0===e&&(e=n),n++,c=t[e-1]),"f"===r?c=parseFloat(c)||0:"d"===r&&(c=parseInt(c)||0),void 0!==o&&("f"===r?c=c.toFixed(o):"s"===r&&(c=c.substr(0,o))),null!=c?c:"")}))}},9105:function(e,t){var n;!function(){"use strict";var i={}.hasOwnProperty;function o(){for(var e=[],t=0;t<arguments.length;t++){var n=arguments[t];if(n){var r=typeof n;if("string"===r||"number"===r)e.push(n);else if(Array.isArray(n)){if(n.length){var c=o.apply(null,n);c&&e.push(c)}}else if("object"===r)if(n.toString===Object.prototype.toString)for(var s in n)i.call(n,s)&&n[s]&&e.push(s);else e.push(n.toString())}}return e.join(" ")}e.exports?(o.default=o,e.exports=o):void 0===(n=function(){return o}.apply(t,[]))||(e.exports=n)}()},5771:function(e,t,n){t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const n="color: "+this.color;t.splice(1,0,n,"color: inherit");let i=0,o=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{"%%"!==e&&(i++,"%c"===e&&(o=i))})),t.splice(o,0,n)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}!e&&"undefined"!=typeof process&&"env"in process&&(e=process.env.DEBUG);return e},t.useColors=function(){if("undefined"!=typeof window&&window.process&&("renderer"===window.process.type||window.process.__nwjs))return!0;if("undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;return"undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)},t.storage=function(){try{return localStorage}catch(e){}}(),t.destroy=(()=>{let e=!1;return()=>{e||(e=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=n(1244)(t);const{formatters:i}=e.exports;i.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},1244:function(e,t,n){e.exports=function(e){function t(e){let n,o,r,c=null;function s(...e){if(!s.enabled)return;const i=s,o=Number(new Date),r=o-(n||o);i.diff=r,i.prev=n,i.curr=o,n=o,e[0]=t.coerce(e[0]),"string"!=typeof e[0]&&e.unshift("%O");let c=0;e[0]=e[0].replace(/%([a-zA-Z%])/g,((n,o)=>{if("%%"===n)return"%";c++;const r=t.formatters[o];if("function"==typeof r){const t=e[c];n=r.call(i,t),e.splice(c,1),c--}return n})),t.formatArgs.call(i,e);(i.log||t.log).apply(i,e)}return s.namespace=e,s.useColors=t.useColors(),s.color=t.selectColor(e),s.extend=i,s.destroy=t.destroy,Object.defineProperty(s,"enabled",{enumerable:!0,configurable:!1,get:()=>null!==c?c:(o!==t.namespaces&&(o=t.namespaces,r=t.enabled(e)),r),set:e=>{c=e}}),"function"==typeof t.init&&t.init(s),s}function i(e,n){const i=t(this.namespace+(void 0===n?":":n)+e);return i.log=this.log,i}function o(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return t.debug=t,t.default=t,t.coerce=function(e){if(e instanceof Error)return e.stack||e.message;return e},t.disable=function(){const e=[...t.names.map(o),...t.skips.map(o).map((e=>"-"+e))].join(",");return t.enable(""),e},t.enable=function(e){let n;t.save(e),t.namespaces=e,t.names=[],t.skips=[];const i=("string"==typeof e?e:"").split(/[\s,]+/),o=i.length;for(n=0;n<o;n++)i[n]&&("-"===(e=i[n].replace(/\*/g,".*?"))[0]?t.skips.push(new RegExp("^"+e.substr(1)+"$")):t.names.push(new RegExp("^"+e+"$")))},t.enabled=function(e){if("*"===e[e.length-1])return!0;let n,i;for(n=0,i=t.skips.length;n<i;n++)if(t.skips[n].test(e))return!1;for(n=0,i=t.names.length;n<i;n++)if(t.names[n].test(e))return!0;return!1},t.humanize=n(2002),t.destroy=function(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")},Object.keys(e).forEach((n=>{t[n]=e[n]})),t.names=[],t.skips=[],t.formatters={},t.selectColor=function(e){let n=0;for(let t=0;t<e.length;t++)n=(n<<5)-n+e.charCodeAt(t),n|=0;return t.colors[Math.abs(n)%t.colors.length]},t.enable(t.load()),t}},2571:function(e){"use strict";var t,n="object"==typeof Reflect?Reflect:null,i=n&&"function"==typeof n.apply?n.apply:function(e,t,n){return Function.prototype.apply.call(e,t,n)};t=n&&"function"==typeof n.ownKeys?n.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var o=Number.isNaN||function(e){return e!=e};function r(){r.init.call(this)}e.exports=r,e.exports.once=function(e,t){return new Promise((function(n,i){function o(n){e.removeListener(t,r),i(n)}function r(){"function"==typeof e.removeListener&&e.removeListener("error",o),n([].slice.call(arguments))}f(e,t,r,{once:!0}),"error"!==t&&function(e,t,n){"function"==typeof e.on&&f(e,"error",t,n)}(e,o,{once:!0})}))},r.EventEmitter=r,r.prototype._events=void 0,r.prototype._eventsCount=0,r.prototype._maxListeners=void 0;var c=10;function s(e){if("function"!=typeof e)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof e)}function a(e){return void 0===e._maxListeners?r.defaultMaxListeners:e._maxListeners}function u(e,t,n,i){var o,r,c,u;if(s(n),void 0===(r=e._events)?(r=e._events=Object.create(null),e._eventsCount=0):(void 0!==r.newListener&&(e.emit("newListener",t,n.listener?n.listener:n),r=e._events),c=r[t]),void 0===c)c=r[t]=n,++e._eventsCount;else if("function"==typeof c?c=r[t]=i?[n,c]:[c,n]:i?c.unshift(n):c.push(n),(o=a(e))>0&&c.length>o&&!c.warned){c.warned=!0;var l=new Error("Possible EventEmitter memory leak detected. "+c.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");l.name="MaxListenersExceededWarning",l.emitter=e,l.type=t,l.count=c.length,u=l,console&&console.warn&&console.warn(u)}return e}function l(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function p(e,t,n){var i={fired:!1,wrapFn:void 0,target:e,type:t,listener:n},o=l.bind(i);return o.listener=n,i.wrapFn=o,o}function d(e,t,n){var i=e._events;if(void 0===i)return[];var o=i[t];return void 0===o?[]:"function"==typeof o?n?[o.listener||o]:[o]:n?function(e){for(var t=new Array(e.length),n=0;n<t.length;++n)t[n]=e[n].listener||e[n];return t}(o):m(o,o.length)}function g(e){var t=this._events;if(void 0!==t){var n=t[e];if("function"==typeof n)return 1;if(void 0!==n)return n.length}return 0}function m(e,t){for(var n=new Array(t),i=0;i<t;++i)n[i]=e[i];return n}function f(e,t,n,i){if("function"==typeof e.on)i.once?e.once(t,n):e.on(t,n);else{if("function"!=typeof e.addEventListener)throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type '+typeof e);e.addEventListener(t,(function o(r){i.once&&e.removeEventListener(t,o),n(r)}))}}Object.defineProperty(r,"defaultMaxListeners",{enumerable:!0,get:function(){return c},set:function(e){if("number"!=typeof e||e<0||o(e))throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received '+e+".");c=e}}),r.init=function(){void 0!==this._events&&this._events!==Object.getPrototypeOf(this)._events||(this._events=Object.create(null),this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},r.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||o(e))throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received '+e+".");return this._maxListeners=e,this},r.prototype.getMaxListeners=function(){return a(this)},r.prototype.emit=function(e){for(var t=[],n=1;n<arguments.length;n++)t.push(arguments[n]);var o="error"===e,r=this._events;if(void 0!==r)o=o&&void 0===r.error;else if(!o)return!1;if(o){var c;if(t.length>0&&(c=t[0]),c instanceof Error)throw c;var s=new Error("Unhandled error."+(c?" ("+c.message+")":""));throw s.context=c,s}var a=r[e];if(void 0===a)return!1;if("function"==typeof a)i(a,this,t);else{var u=a.length,l=m(a,u);for(n=0;n<u;++n)i(l[n],this,t)}return!0},r.prototype.addListener=function(e,t){return u(this,e,t,!1)},r.prototype.on=r.prototype.addListener,r.prototype.prependListener=function(e,t){return u(this,e,t,!0)},r.prototype.once=function(e,t){return s(t),this.on(e,p(this,e,t)),this},r.prototype.prependOnceListener=function(e,t){return s(t),this.prependListener(e,p(this,e,t)),this},r.prototype.removeListener=function(e,t){var n,i,o,r,c;if(s(t),void 0===(i=this._events))return this;if(void 0===(n=i[e]))return this;if(n===t||n.listener===t)0==--this._eventsCount?this._events=Object.create(null):(delete i[e],i.removeListener&&this.emit("removeListener",e,n.listener||t));else if("function"!=typeof n){for(o=-1,r=n.length-1;r>=0;r--)if(n[r]===t||n[r].listener===t){c=n[r].listener,o=r;break}if(o<0)return this;0===o?n.shift():function(e,t){for(;t+1<e.length;t++)e[t]=e[t+1];e.pop()}(n,o),1===n.length&&(i[e]=n[0]),void 0!==i.removeListener&&this.emit("removeListener",e,c||t)}return this},r.prototype.off=r.prototype.removeListener,r.prototype.removeAllListeners=function(e){var t,n,i;if(void 0===(n=this._events))return this;if(void 0===n.removeListener)return 0===arguments.length?(this._events=Object.create(null),this._eventsCount=0):void 0!==n[e]&&(0==--this._eventsCount?this._events=Object.create(null):delete n[e]),this;if(0===arguments.length){var o,r=Object.keys(n);for(i=0;i<r.length;++i)"removeListener"!==(o=r[i])&&this.removeAllListeners(o);return this.removeAllListeners("removeListener"),this._events=Object.create(null),this._eventsCount=0,this}if("function"==typeof(t=n[e]))this.removeListener(e,t);else if(void 0!==t)for(i=t.length-1;i>=0;i--)this.removeListener(e,t[i]);return this},r.prototype.listeners=function(e){return d(this,e,!0)},r.prototype.rawListeners=function(e){return d(this,e,!1)},r.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):g.call(e,t)},r.prototype.listenerCount=g,r.prototype.eventNames=function(){return this._eventsCount>0?t(this._events):[]}},5949:function(e,t,n){"use strict";var i=n(3566),o=n(8282);function r(){this.pending=null,this.pendingTotal=0,this.blockSize=this.constructor.blockSize,this.outSize=this.constructor.outSize,this.hmacStrength=this.constructor.hmacStrength,this.padLength=this.constructor.padLength/8,this.endian="big",this._delta8=this.blockSize/8,this._delta32=this.blockSize/32}t.BlockHash=r,r.prototype.update=function(e,t){if(e=i.toArray(e,t),this.pending?this.pending=this.pending.concat(e):this.pending=e,this.pendingTotal+=e.length,this.pending.length>=this._delta8){var n=(e=this.pending).length%this._delta8;this.pending=e.slice(e.length-n,e.length),0===this.pending.length&&(this.pending=null),e=i.join32(e,0,e.length-n,this.endian);for(var o=0;o<e.length;o+=this._delta32)this._update(e,o,o+this._delta32)}return this},r.prototype.digest=function(e){return this.update(this._pad()),o(null===this.pending),this._digest(e)},r.prototype._pad=function(){var e=this.pendingTotal,t=this._delta8,n=t-(e+this.padLength)%t,i=new Array(n+this.padLength);i[0]=128;for(var o=1;o<n;o++)i[o]=0;if(e<<=3,"big"===this.endian){for(var r=8;r<this.padLength;r++)i[o++]=0;i[o++]=0,i[o++]=0,i[o++]=0,i[o++]=0,i[o++]=e>>>24&255,i[o++]=e>>>16&255,i[o++]=e>>>8&255,i[o++]=255&e}else for(i[o++]=255&e,i[o++]=e>>>8&255,i[o++]=e>>>16&255,i[o++]=e>>>24&255,i[o++]=0,i[o++]=0,i[o++]=0,i[o++]=0,r=8;r<this.padLength;r++)i[o++]=0;return i}},9016:function(e,t,n){"use strict";var i=n(3566),o=n(5949),r=n(7574),c=i.rotl32,s=i.sum32,a=i.sum32_5,u=r.ft_1,l=o.BlockHash,p=[1518500249,1859775393,2400959708,3395469782];function d(){if(!(this instanceof d))return new d;l.call(this),this.h=[1732584193,4023233417,2562383102,271733878,3285377520],this.W=new Array(80)}i.inherits(d,l),e.exports=d,d.blockSize=512,d.outSize=160,d.hmacStrength=80,d.padLength=64,d.prototype._update=function(e,t){for(var n=this.W,i=0;i<16;i++)n[i]=e[t+i];for(;i<n.length;i++)n[i]=c(n[i-3]^n[i-8]^n[i-14]^n[i-16],1);var o=this.h[0],r=this.h[1],l=this.h[2],d=this.h[3],g=this.h[4];for(i=0;i<n.length;i++){var m=~~(i/20),f=a(c(o,5),u(m,r,l,d),g,n[i],p[m]);g=d,d=l,l=c(r,30),r=o,o=f}this.h[0]=s(this.h[0],o),this.h[1]=s(this.h[1],r),this.h[2]=s(this.h[2],l),this.h[3]=s(this.h[3],d),this.h[4]=s(this.h[4],g)},d.prototype._digest=function(e){return"hex"===e?i.toHex32(this.h,"big"):i.split32(this.h,"big")}},7574:function(e,t,n){"use strict";var i=n(3566).rotr32;function o(e,t,n){return e&t^~e&n}function r(e,t,n){return e&t^e&n^t&n}function c(e,t,n){return e^t^n}t.ft_1=function(e,t,n,i){return 0===e?o(t,n,i):1===e||3===e?c(t,n,i):2===e?r(t,n,i):void 0},t.ch32=o,t.maj32=r,t.p32=c,t.s0_256=function(e){return i(e,2)^i(e,13)^i(e,22)},t.s1_256=function(e){return i(e,6)^i(e,11)^i(e,25)},t.g0_256=function(e){return i(e,7)^i(e,18)^e>>>3},t.g1_256=function(e){return i(e,17)^i(e,19)^e>>>10}},3566:function(e,t,n){"use strict";var i=n(8282),o=n(9503);function r(e,t){return 55296==(64512&e.charCodeAt(t))&&(!(t<0||t+1>=e.length)&&56320==(64512&e.charCodeAt(t+1)))}function c(e){return(e>>>24|e>>>8&65280|e<<8&16711680|(255&e)<<24)>>>0}function s(e){return 1===e.length?"0"+e:e}function a(e){return 7===e.length?"0"+e:6===e.length?"00"+e:5===e.length?"000"+e:4===e.length?"0000"+e:3===e.length?"00000"+e:2===e.length?"000000"+e:1===e.length?"0000000"+e:e}t.inherits=o,t.toArray=function(e,t){if(Array.isArray(e))return e.slice();if(!e)return[];var n=[];if("string"==typeof e)if(t){if("hex"===t)for((e=e.replace(/[^a-z0-9]+/gi,"")).length%2!=0&&(e="0"+e),o=0;o<e.length;o+=2)n.push(parseInt(e[o]+e[o+1],16))}else for(var i=0,o=0;o<e.length;o++){var c=e.charCodeAt(o);c<128?n[i++]=c:c<2048?(n[i++]=c>>6|192,n[i++]=63&c|128):r(e,o)?(c=65536+((1023&c)<<10)+(1023&e.charCodeAt(++o)),n[i++]=c>>18|240,n[i++]=c>>12&63|128,n[i++]=c>>6&63|128,n[i++]=63&c|128):(n[i++]=c>>12|224,n[i++]=c>>6&63|128,n[i++]=63&c|128)}else for(o=0;o<e.length;o++)n[o]=0|e[o];return n},t.toHex=function(e){for(var t="",n=0;n<e.length;n++)t+=s(e[n].toString(16));return t},t.htonl=c,t.toHex32=function(e,t){for(var n="",i=0;i<e.length;i++){var o=e[i];"little"===t&&(o=c(o)),n+=a(o.toString(16))}return n},t.zero2=s,t.zero8=a,t.join32=function(e,t,n,o){var r=n-t;i(r%4==0);for(var c=new Array(r/4),s=0,a=t;s<c.length;s++,a+=4){var u;u="big"===o?e[a]<<24|e[a+1]<<16|e[a+2]<<8|e[a+3]:e[a+3]<<24|e[a+2]<<16|e[a+1]<<8|e[a],c[s]=u>>>0}return c},t.split32=function(e,t){for(var n=new Array(4*e.length),i=0,o=0;i<e.length;i++,o+=4){var r=e[i];"big"===t?(n[o]=r>>>24,n[o+1]=r>>>16&255,n[o+2]=r>>>8&255,n[o+3]=255&r):(n[o+3]=r>>>24,n[o+2]=r>>>16&255,n[o+1]=r>>>8&255,n[o]=255&r)}return n},t.rotr32=function(e,t){return e>>>t|e<<32-t},t.rotl32=function(e,t){return e<<t|e>>>32-t},t.sum32=function(e,t){return e+t>>>0},t.sum32_3=function(e,t,n){return e+t+n>>>0},t.sum32_4=function(e,t,n,i){return e+t+n+i>>>0},t.sum32_5=function(e,t,n,i,o){return e+t+n+i+o>>>0},t.sum64=function(e,t,n,i){var o=e[t],r=i+e[t+1]>>>0,c=(r<i?1:0)+n+o;e[t]=c>>>0,e[t+1]=r},t.sum64_hi=function(e,t,n,i){return(t+i>>>0<t?1:0)+e+n>>>0},t.sum64_lo=function(e,t,n,i){return t+i>>>0},t.sum64_4_hi=function(e,t,n,i,o,r,c,s){var a=0,u=t;return a+=(u=u+i>>>0)<t?1:0,a+=(u=u+r>>>0)<r?1:0,e+n+o+c+(a+=(u=u+s>>>0)<s?1:0)>>>0},t.sum64_4_lo=function(e,t,n,i,o,r,c,s){return t+i+r+s>>>0},t.sum64_5_hi=function(e,t,n,i,o,r,c,s,a,u){var l=0,p=t;return l+=(p=p+i>>>0)<t?1:0,l+=(p=p+r>>>0)<r?1:0,l+=(p=p+s>>>0)<s?1:0,e+n+o+c+a+(l+=(p=p+u>>>0)<u?1:0)>>>0},t.sum64_5_lo=function(e,t,n,i,o,r,c,s,a,u){return t+i+r+s+u>>>0},t.rotr64_hi=function(e,t,n){return(t<<32-n|e>>>n)>>>0},t.rotr64_lo=function(e,t,n){return(e<<32-n|t>>>n)>>>0},t.shr64_hi=function(e,t,n){return e>>>n},t.shr64_lo=function(e,t,n){return(e<<32-n|t>>>n)>>>0}},8617:function(e,t,n){"use strict";var i=n(9128),o=n(8086),r=n(5771),c=n.n(r),s=n(8772),a=n(8090),u=n(5565),l=n.n(u),p=n(9016),d=n.n(p),g=n(2571),m=n(702),f=n(2846),h=c()("i18n-calypso"),y="number_format_decimals",v="number_format_thousands_sep",b="messages",_=[function(e){return e}],k={};function w(){N.throwErrors&&"undefined"!=typeof window&&window.console&&window.console.warn&&window.console.warn.apply(window.console,arguments)}function j(e){return Array.prototype.slice.call(e)}function C(e){var t=e[0];("string"!=typeof t||e.length>3||e.length>2&&"object"==typeof e[1]&&"object"==typeof e[2])&&w("Deprecated Invocation: `translate()` accepts ( string, [string], [object] ). These arguments passed:",j(e),". See https://github.com/Automattic/i18n-calypso#translate-method"),2===e.length&&"string"==typeof t&&"string"==typeof e[1]&&w("Invalid Invocation: `translate()` requires an options object for plural translations, but passed:",j(e));for(var n={},i=0;i<e.length;i++)"object"==typeof e[i]&&(n=e[i]);if("string"==typeof t?n.original=t:"object"==typeof n.original&&(n.plural=n.original.plural,n.count=n.original.count,n.original=n.original.single),"string"==typeof e[1]&&(n.plural=e[1]),void 0===n.original)throw new Error("Translate called without a `string` value as first argument.");return n}function E(e,t){return e.dcnpgettext(b,t.context,t.original,t.plural,t.count)}function S(e,t){for(var n=_.length-1;n>=0;n--){var i=_[n](Object.assign({},t)),o=i.context?i.context+""+i.original:i.original;if(e.state.locale[o])return E(e.state.tannin,i)}return null}function N(){if(!(this instanceof N))return new N;this.defaultLocaleSlug="en",this.defaultPluralForms=function(e){return 1===e?0:1},this.state={numberFormatSettings:{},tannin:void 0,locale:void 0,localeSlug:void 0,textDirection:void 0,translations:l()({max:100})},this.componentUpdateHooks=[],this.translateHooks=[],this.stateObserver=new g.EventEmitter,this.stateObserver.setMaxListeners(0),this.configure()}N.throwErrors=!1,N.prototype.on=function(){var e;(e=this.stateObserver).on.apply(e,arguments)},N.prototype.off=function(){var e;(e=this.stateObserver).off.apply(e,arguments)},N.prototype.emit=function(){var e;(e=this.stateObserver).emit.apply(e,arguments)},N.prototype.numberFormat=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n="number"==typeof t?t:t.decimals||0,i=t.decPoint||this.state.numberFormatSettings.decimal_point||".",o=t.thousandsSep||this.state.numberFormatSettings.thousands_sep||",";return(0,f.Z)(e,n,i,o)},N.prototype.configure=function(e){Object.assign(this,e||{}),this.setLocale()},N.prototype.setLocale=function(e){var t,n,i;if(e&&e[""]&&e[""]["key-hash"]){var r=e[""]["key-hash"],c=function(e,t){var n=!1===t?"":String(t);if(void 0!==k[n+e])return k[n+e];var i=d()().update(e).digest("hex");return k[n+e]=t?i.substr(0,t):i},s=function(e){return function(t){return t.context?(t.original=c(t.context+String.fromCharCode(4)+t.original,e),delete t.context):t.original=c(t.original,e),t}};if("sha1"===r.substr(0,4))if(4===r.length)_.push(s(!1));else{var u=r.substr(5).indexOf("-");if(u<0){var l=Number(r.substr(5));_.push(s(l))}else for(var p=Number(r.substr(5,u)),g=Number(r.substr(6+u)),m=p;m<=g;m++)_.push(s(m))}}if(e&&e[""].localeSlug)if(e[""].localeSlug===this.state.localeSlug){if(e===this.state.locale)return;Object.assign(this.state.locale,e)}else this.state.locale=Object.assign({},e);else this.state.locale={"":{localeSlug:this.defaultLocaleSlug,plural_forms:this.defaultPluralForms}};this.state.localeSlug=this.state.locale[""].localeSlug,this.state.textDirection=(null===(t=this.state.locale["text directionltr"])||void 0===t?void 0:t[0])||(null===(n=this.state.locale[""])||void 0===n||null===(i=n.momentjs_locale)||void 0===i?void 0:i.textDirection),this.state.tannin=new a.Z((0,o.Z)({},b,this.state.locale)),this.state.numberFormatSettings.decimal_point=E(this.state.tannin,C([y])),this.state.numberFormatSettings.thousands_sep=E(this.state.tannin,C([v])),this.state.numberFormatSettings.decimal_point===y&&(this.state.numberFormatSettings.decimal_point="."),this.state.numberFormatSettings.thousands_sep===v&&(this.state.numberFormatSettings.thousands_sep=","),this.stateObserver.emit("change")},N.prototype.getLocale=function(){return this.state.locale},N.prototype.getLocaleSlug=function(){return this.state.localeSlug},N.prototype.isRtl=function(){return"rtl"===this.state.textDirection},N.prototype.addTranslations=function(e){for(var t in e)""!==t&&(this.state.tannin.data.messages[t]=e[t]);this.stateObserver.emit("change")},N.prototype.hasTranslation=function(){return!!S(this,C(arguments))},N.prototype.translate=function(){var e=C(arguments),t=S(this,e);if(t||(t=E(this.state.tannin,e)),e.args){var n=Array.isArray(e.args)?e.args.slice(0):[e.args];n.unshift(t);try{t=m.Z.apply(void 0,(0,i.Z)(n))}catch(e){if(!window||!window.console)return;var o=this.throwErrors?"error":"warn";"string"!=typeof e?window.console[o](e):window.console[o]("i18n sprintf error:",n)}}return e.components&&(t=(0,s.Z)({mixedString:t,components:e.components,throwErrors:this.throwErrors})),this.translateHooks.forEach((function(n){t=n(t,e)})),t},N.prototype.reRenderTranslations=function(){h("Re-rendering all translations due to external request"),this.stateObserver.emit("change")},N.prototype.registerComponentUpdateHook=function(e){this.componentUpdateHooks.push(e)},N.prototype.registerTranslateHook=function(e){this.translateHooks.push(e)},t.Z=N},3807:function(e,t,n){"use strict";n.d(t,{Y4:function(){return a}});var i=n(8617),o=n(2928),r=n(975),c=n(4531),s=new i.Z,a=s.numberFormat.bind(s),u=(s.translate.bind(s),s.configure.bind(s),s.setLocale.bind(s),s.getLocale.bind(s),s.getLocaleSlug.bind(s),s.addTranslations.bind(s),s.reRenderTranslations.bind(s),s.registerComponentUpdateHook.bind(s),s.registerTranslateHook.bind(s),s.state,s.stateObserver,s.on.bind(s),s.off.bind(s),s.emit.bind(s),(0,o.Z)(s),(0,r.Z)(s),(0,c.Z)(s));u.useRtl,u.withRtl},2928:function(e,t,n){"use strict";n.d(t,{Z:function(){return d}});var i=n(9591),o=n(1687),r=n(3772),c=n(5200),s=n(2104),a=n(9057),u=n(8086),l=n(9196),p=n.n(l);function d(e){var t={numberFormat:e.numberFormat.bind(e),translate:e.translate.bind(e)};return function(n){var l,d,g=n.displayName||n.name||"";return d=l=function(l){(0,s.Z)(g,l);var d=(0,a.Z)(g);function g(){var e;(0,o.Z)(this,g);for(var t=arguments.length,n=new Array(t),i=0;i<t;i++)n[i]=arguments[i];return e=d.call.apply(d,[this].concat(n)),(0,u.Z)((0,c.Z)(e),"boundForceUpdate",e.forceUpdate.bind((0,c.Z)(e))),e}return(0,r.Z)(g,[{key:"componentDidMount",value:function(){e.on("change",this.boundForceUpdate)}},{key:"componentWillUnmount",value:function(){e.off("change",this.boundForceUpdate)}},{key:"render",value:function(){var o=(0,i.Z)({locale:e.getLocaleSlug()},this.props,{},t);return p().createElement(n,o)}}]),g}(p().Component),(0,u.Z)(l,"displayName","Localized("+g+")"),d}}},2846:function(e,t,n){"use strict";function i(e,t,n,i){e=(e+"").replace(/[^0-9+\-Ee.]/g,"");var o=isFinite(+e)?+e:0,r=isFinite(+t)?Math.abs(t):0,c=void 0===i?",":i,s=void 0===n?".":n,a="";return a=(r?function(e,t){var n=Math.pow(10,t);return""+(Math.round(e*n)/n).toFixed(t)}(o,r):""+Math.round(o)).split("."),a[0].length>3&&(a[0]=a[0].replace(/\B(?=(?:\d{3})+(?!\d))/g,c)),(a[1]||"").length<r&&(a[1]=a[1]||"",a[1]+=new Array(r-a[1].length+1).join("0")),a.join(s)}n.d(t,{Z:function(){return i}})},4531:function(e,t,n){"use strict";n.d(t,{Z:function(){return a}});var i=n(5672),o=n(9196),r=n.n(o),c=n(36),s=n(4333);function a(e){var t={getCurrentValue:function(){return e.isRtl()},subscribe:function(t){return e.on("change",t),function(){return e.off("change",t)}}};function n(){return(0,c.useSubscription)(t)}var a=(0,s.createHigherOrderComponent)((function(e){return(0,o.forwardRef)((function(t,o){var c=n();return r().createElement(e,(0,i.Z)({},t,{isRtl:c,ref:o}))}))}),"WithRTL");return{useRtl:n,withRtl:a}}},975:function(e,t,n){"use strict";n.d(t,{Z:function(){return c}});var i=n(572),o=n(9196),r=n.n(o);function c(e){function t(){var t=e.translate.bind(e);return Object.defineProperty(t,"localeSlug",{get:e.getLocaleSlug.bind(e)}),t}return function(){var n=r().useState(t),o=(0,i.Z)(n,2),c=o[0],s=o[1];return r().useEffect((function(){var n=function(){return s(t)};return e.on("change",n),function(){return e.off("change",n)}}),[]),c}}},9503:function(e){"function"==typeof Object.create?e.exports=function(e,t){t&&(e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}))}:e.exports=function(e,t){if(t){e.super_=t;var n=function(){};n.prototype=t.prototype,e.prototype=new n,e.prototype.constructor=e}}},5565:function(e,t,n){var i=n(2571),o=n(9503);function r(e){if(!(this instanceof r))return new r(e);"number"==typeof e&&(e={max:e}),e||(e={}),i.EventEmitter.call(this),this.cache={},this.head=this.tail=null,this.length=0,this.max=e.max||1e3,this.maxAge=e.maxAge||0}e.exports=r,o(r,i.EventEmitter),Object.defineProperty(r.prototype,"keys",{get:function(){return Object.keys(this.cache)}}),r.prototype.clear=function(){this.cache={},this.head=this.tail=null,this.length=0},r.prototype.remove=function(e){if("string"!=typeof e&&(e=""+e),this.cache.hasOwnProperty(e)){var t=this.cache[e];return delete this.cache[e],this._unlink(e,t.prev,t.next),t.value}},r.prototype._unlink=function(e,t,n){this.length--,0===this.length?this.head=this.tail=null:this.head===e?(this.head=t,this.cache[this.head].next=null):this.tail===e?(this.tail=n,this.cache[this.tail].prev=null):(this.cache[t].next=n,this.cache[n].prev=t)},r.prototype.peek=function(e){if(this.cache.hasOwnProperty(e)){var t=this.cache[e];if(this._checkAge(e,t))return t.value}},r.prototype.set=function(e,t){var n;if("string"!=typeof e&&(e=""+e),this.cache.hasOwnProperty(e)){if((n=this.cache[e]).value=t,this.maxAge&&(n.modified=Date.now()),e===this.head)return t;this._unlink(e,n.prev,n.next)}else n={value:t,modified:0,next:null,prev:null},this.maxAge&&(n.modified=Date.now()),this.cache[e]=n,this.length===this.max&&this.evict();return this.length++,n.next=null,n.prev=this.head,this.head&&(this.cache[this.head].next=e),this.head=e,this.tail||(this.tail=e),t},r.prototype._checkAge=function(e,t){return!(this.maxAge&&Date.now()-t.modified>this.maxAge)||(this.remove(e),this.emit("evict",{key:e,value:t.value}),!1)},r.prototype.get=function(e){if("string"!=typeof e&&(e=""+e),this.cache.hasOwnProperty(e)){var t=this.cache[e];if(this._checkAge(e,t))return this.head!==e&&(e===this.tail?(this.tail=t.next,this.cache[this.tail].prev=null):this.cache[t.prev].next=t.next,this.cache[t.next].prev=t.prev,this.cache[this.head].next=e,t.prev=this.head,t.next=null,this.head=e),t.value}},r.prototype.evict=function(){if(this.tail){var e=this.tail,t=this.remove(this.tail);this.emit("evict",{key:e,value:t})}}},4125:function(){},8776:function(){},7394:function(){},6212:function(){},4959:function(){},2961:function(){},404:function(){},1294:function(){},1545:function(){},843:function(){},8282:function(e){function t(e,t){if(!e)throw new Error(t||"Assertion failed")}e.exports=t,t.equal=function(e,t,n){if(e!=t)throw new Error(n||"Assertion failed: "+e+" != "+t)}},2002:function(e){var t=1e3,n=60*t,i=60*n,o=24*i,r=7*o,c=365.25*o;function s(e,t,n,i){var o=t>=1.5*n;return Math.round(e/n)+" "+i+(o?"s":"")}e.exports=function(e,a){a=a||{};var u=typeof e;if("string"===u&&e.length>0)return function(e){if((e=String(e)).length>100)return;var s=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!s)return;var a=parseFloat(s[1]);switch((s[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return a*c;case"weeks":case"week":case"w":return a*r;case"days":case"day":case"d":return a*o;case"hours":case"hour":case"hrs":case"hr":case"h":return a*i;case"minutes":case"minute":case"mins":case"min":case"m":return a*n;case"seconds":case"second":case"secs":case"sec":case"s":return a*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return a;default:return}}(e);if("number"===u&&isFinite(e))return a.long?function(e){var r=Math.abs(e);if(r>=o)return s(e,r,o,"day");if(r>=i)return s(e,r,i,"hour");if(r>=n)return s(e,r,n,"minute");if(r>=t)return s(e,r,t,"second");return e+" ms"}(e):function(e){var r=Math.abs(e);if(r>=o)return Math.round(e/o)+"d";if(r>=i)return Math.round(e/i)+"h";if(r>=n)return Math.round(e/n)+"m";if(r>=t)return Math.round(e/t)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},1625:function(e){"use strict";var t=Object.getOwnPropertySymbols,n=Object.prototype.hasOwnProperty,i=Object.prototype.propertyIsEnumerable;function o(e){if(null==e)throw new TypeError("Object.assign cannot be called with null or undefined");return Object(e)}e.exports=function(){try{if(!Object.assign)return!1;var e=new String("abc");if(e[5]="de","5"===Object.getOwnPropertyNames(e)[0])return!1;for(var t={},n=0;n<10;n++)t["_"+String.fromCharCode(n)]=n;if("0123456789"!==Object.getOwnPropertyNames(t).map((function(e){return t[e]})).join(""))return!1;var i={};return"abcdefghijklmnopqrst".split("").forEach((function(e){i[e]=e})),"abcdefghijklmnopqrst"===Object.keys(Object.assign({},i)).join("")}catch(e){return!1}}()?Object.assign:function(e,r){for(var c,s,a=o(e),u=1;u<arguments.length;u++){for(var l in c=Object(arguments[u]))n.call(c,l)&&(a[l]=c[l]);if(t){s=t(c);for(var p=0;p<s.length;p++)i.call(c,s[p])&&(a[s[p]]=c[s[p]])}}return a}},9587:function(e,t,n){"use strict";var i=n(5843);function o(){}function r(){}r.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,r,c){if(c!==i){var s=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw s.name="Invariant Violation",s}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:r,resetWarningCache:o};return n.PropTypes=n,n}},1268:function(e,t,n){e.exports=n(9587)()},5843:function(e){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},8090:function(e,t,n){"use strict";n.d(t,{Z:function(){return r}});var i=n(7478),o={contextDelimiter:"",onMissingKey:null};function r(e,t){var n;for(n in this.data=e,this.pluralForms={},this.options={},o)this.options[n]=void 0!==t&&n in t?t[n]:o[n]}r.prototype.getPluralForm=function(e,t){var n,o,r,c=this.pluralForms[e];return c||("function"!=typeof(r=(n=this.data[e][""])["Plural-Forms"]||n["plural-forms"]||n.plural_forms)&&(o=function(e){var t,n,i;for(t=e.split(";"),n=0;n<t.length;n++)if(0===(i=t[n].trim()).indexOf("plural="))return i.substr(7)}(n["Plural-Forms"]||n["plural-forms"]||n.plural_forms),r=(0,i.Z)(o)),c=this.pluralForms[e]=r),c(t)},r.prototype.dcnpgettext=function(e,t,n,i,o){var r,c,s;return r=void 0===o?0:this.getPluralForm(e,o),c=n,t&&(c=t+this.options.contextDelimiter+n),(s=this.data[e][c])&&s[r]?s[r]:(this.options.onMissingKey&&this.options.onMissingKey(n,e),0===r?n:i)}},816:function(e,t,n){"use strict";var i=n(5771),o=n.n(i),r=n(2819);const c=o()("dops:analytics");let s,a;window._tkq=window._tkq||[],window.ga=window.ga||function(){(window.ga.q=window.ga.q||[]).push(arguments)},window.ga.l=+new Date;const u={initialize:function(e,t,n){u.setUser(e,t),u.setSuperProps(n),u.identifyUser()},setGoogleAnalyticsEnabled:function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;this.googleAnalyticsEnabled=e,this.googleAnalyticsKey=t},setMcAnalyticsEnabled:function(e){this.mcAnalyticsEnabled=e},setUser:function(e,t){a={ID:e,username:t}},setSuperProps:function(e){s=e},mc:{bumpStat:function(e,t){const n=function(e,t){let n="";if("object"==typeof e){for(const t in e)n+="&x_"+encodeURIComponent(t)+"="+encodeURIComponent(e[t]);c("Bumping stats %o",e)}else n="&x_"+encodeURIComponent(e)+"="+encodeURIComponent(t),c('Bumping stat "%s" in group "%s"',t,e);return n}(e,t);this.mcAnalyticsEnabled&&((new Image).src=document.location.protocol+"//pixel.wp.com/g.gif?v=wpcom-no-pv"+n+"&t="+Math.random())},bumpStatWithPageView:function(e,t){const n=function(e,t){let n="";if("object"==typeof e){for(const t in e)n+="&"+encodeURIComponent(t)+"="+encodeURIComponent(e[t]);c("Built stats %o",e)}else n="&"+encodeURIComponent(e)+"="+encodeURIComponent(t),c('Built stat "%s" in group "%s"',t,e);return n}(e,t);this.mcAnalyticsEnabled&&((new Image).src=document.location.protocol+"//pixel.wp.com/g.gif?v=wpcom"+n+"&t="+Math.random())}},pageView:{record:function(e,t){u.tracks.recordPageView(e),u.ga.recordPageView(e,t)}},purchase:{record:function(e,t,n,i,o,r,c){u.ga.recordPurchase(e,t,n,i,o,r,c)}},tracks:{recordEvent:function(e,t){t=t||{},0===e.indexOf("akismet_")||0===e.indexOf("jetpack_")?(s&&(c("- Super Props: %o",s),t=(0,r.assign)(t,s)),c('Record event "%s" called with props %s',e,JSON.stringify(t)),window._tkq.push(["recordEvent",e,t])):c('- Event name must be prefixed by "akismet_" or "jetpack_"')},recordJetpackClick:function(e){const t="object"==typeof e?e:{target:e};u.tracks.recordEvent("jetpack_wpa_click",t)},recordPageView:function(e){u.tracks.recordEvent("akismet_page_view",{path:e})},setOptOut:function(e){c("Pushing setOptOut: %o",e),window._tkq.push(["setOptOut",e])}},ga:{initialized:!1,initialize:function(){let e={};u.ga.initialized||(a&&(e={userId:"u-"+a.ID}),window.ga("create",this.googleAnalyticsKey,"auto",e),u.ga.initialized=!0)},recordPageView:function(e,t){u.ga.initialize(),c("Recording Page View ~ [URL: "+e+"] [Title: "+t+"]"),this.googleAnalyticsEnabled&&(window.ga("set","page",e),window.ga("send",{hitType:"pageview",page:e,title:t}))},recordEvent:function(e,t,n,i){u.ga.initialize();let o="Recording Event ~ [Category: "+e+"] [Action: "+t+"]";void 0!==n&&(o+=" [Option Label: "+n+"]"),void 0!==i&&(o+=" [Option Value: "+i+"]"),c(o),this.googleAnalyticsEnabled&&window.ga("send","event",e,t,n,i)},recordPurchase:function(e,t,n,i,o,r,c){window.ga("require","ecommerce"),window.ga("ecommerce:addTransaction",{id:e,revenue:i,currency:c}),window.ga("ecommerce:addItem",{id:e,name:t,sku:n,price:o,quantity:r}),window.ga("ecommerce:send")}},identifyUser:function(){a&&window._tkq.push(["identifyUser",a.ID,a.username])},setProperties:function(e){window._tkq.push(["setProperties",e])},clearedIdentity:function(){window._tkq.push(["clearIdentity"])}};t.Z=u},9570:function(e,t,n){"use strict";var i=n(2819),o=n(6483),r=n(6251);function c(e){class t extends Error{constructor(){super(...arguments),this.name=e}}return t}const s=c("JsonParseError"),a=c("JsonParseAfterRedirectError"),u=c("Api404Error"),l=c("Api404AfterRedirectError"),p=c("FetchNetworkError");const d=new function(e,t){let n=e,c={"X-WP-Nonce":t},s={credentials:"same-origin",headers:c},a={method:"post",credentials:"same-origin",headers:(0,i.assign)({},c,{"Content-type":"application/json"})},u=function(e){const t=e.split("?"),n=t.length>1?t[1]:"",i=n.length?n.split("&"):[];return i.push("_cacheBuster="+(new Date).getTime()),t[0]+"?"+i.join("&")};const l={setApiRoot(e){n=e},setApiNonce(e){c={"X-WP-Nonce":e},s={credentials:"same-origin",headers:c},a={method:"post",credentials:"same-origin",headers:(0,i.assign)({},c,{"Content-type":"application/json"})}},setCacheBusterCallback:e=>{u=e},registerSite:(e,t)=>{const i={registration_nonce:e,no_iframe:!0};return(0,r.jetpackConfigHas)("consumer_slug")&&(i.plugin_slug=(0,r.jetpackConfigGet)("consumer_slug")),null!==t&&(i.redirect_uri=t),d(`${n}jetpack/v4/connection/register`,a,{body:JSON.stringify(i)}).then(g).then(m)},fetchAuthorizationUrl:e=>p((0,o.addQueryArgs)(`${n}jetpack/v4/connection/authorize_url`,{no_iframe:"1",redirect_uri:e}),s).then(g).then(m),fetchSiteConnectionData:()=>p(`${n}jetpack/v4/connection/data`,s).then(m),fetchSiteConnectionStatus:()=>p(`${n}jetpack/v4/connection`,s).then(m),fetchSiteConnectionTest:()=>p(`${n}jetpack/v4/connection/test`,s).then(g).then(m),fetchUserConnectionData:()=>p(`${n}jetpack/v4/connection/data`,s).then(m),fetchUserTrackingSettings:()=>p(`${n}jetpack/v4/tracking/settings`,s).then(g).then(m),updateUserTrackingSettings:e=>d(`${n}jetpack/v4/tracking/settings`,a,{body:JSON.stringify(e)}).then(g).then(m),disconnectSite:()=>d(`${n}jetpack/v4/connection`,a,{body:JSON.stringify({isActive:!1})}).then(g).then(m),fetchConnectUrl:()=>p(`${n}jetpack/v4/connection/url`,s).then(g).then(m),unlinkUser:()=>d(`${n}jetpack/v4/connection/user`,a,{body:JSON.stringify({linked:!1})}).then(g).then(m),reconnect:()=>d(`${n}jetpack/v4/connection/reconnect`,a).then(g).then(m),fetchConnectedPlugins:()=>p(`${n}jetpack/v4/connection/plugins`,s).then(g).then(m),setHasSeenWCConnectionModal:()=>d(`${n}jetpack/v4/seen-wc-connection-modal`,a).then(g).then(m),fetchModules:()=>p(`${n}jetpack/v4/module/all`,s).then(g).then(m),fetchModule:e=>p(`${n}jetpack/v4/module/${e}`,s).then(g).then(m),activateModule:e=>d(`${n}jetpack/v4/module/${e}/active`,a,{body:JSON.stringify({active:!0})}).then(g).then(m),deactivateModule:e=>d(`${n}jetpack/v4/module/${e}/active`,a,{body:JSON.stringify({active:!1})}),updateModuleOptions:(e,t)=>d(`${n}jetpack/v4/module/${e}`,a,{body:JSON.stringify(t)}).then(g).then(m),updateSettings:e=>d(`${n}jetpack/v4/settings`,a,{body:JSON.stringify(e)}).then(g).then(m),getProtectCount:()=>p(`${n}jetpack/v4/module/protect/data`,s).then(g).then(m),resetOptions:e=>d(`${n}jetpack/v4/options/${e}`,a,{body:JSON.stringify({reset:!0})}).then(g).then(m),activateVaultPress:()=>d(`${n}jetpack/v4/plugins`,a,{body:JSON.stringify({slug:"vaultpress",status:"active"})}).then(g).then(m),getVaultPressData:()=>p(`${n}jetpack/v4/module/vaultpress/data`,s).then(g).then(m),installPlugin:(e,t)=>{const i={slug:e,status:"active"};return t&&(i.source=t),d(`${n}jetpack/v4/plugins`,a,{body:JSON.stringify(i)}).then(g).then(m)},activateAkismet:()=>d(`${n}jetpack/v4/plugins`,a,{body:JSON.stringify({slug:"akismet",status:"active"})}).then(g).then(m),getAkismetData:()=>p(`${n}jetpack/v4/module/akismet/data`,s).then(g).then(m),checkAkismetKey:()=>p(`${n}jetpack/v4/module/akismet/key/check`,s).then(g).then(m),checkAkismetKeyTyped:e=>d(`${n}jetpack/v4/module/akismet/key/check`,a,{body:JSON.stringify({api_key:e})}).then(g).then(m),fetchStatsData:e=>p(function(e){let t=`${n}jetpack/v4/module/stats/data`;-1!==t.indexOf("?")?t+=`&range=${encodeURIComponent(e)}`:t+=`?range=${encodeURIComponent(e)}`;return t}(e),s).then(g).then(m).then(f),getPluginUpdates:()=>p(`${n}jetpack/v4/updates/plugins`,s).then(g).then(m),getPlans:()=>p(`${n}jetpack/v4/plans`,s).then(g).then(m),fetchSettings:()=>p(`${n}jetpack/v4/settings`,s).then(g).then(m),updateSetting:e=>d(`${n}jetpack/v4/settings`,a,{body:JSON.stringify(e)}).then(g).then(m),fetchSiteData:()=>p(`${n}jetpack/v4/site`,s).then(g).then(m).then((e=>JSON.parse(e.data))),fetchSiteFeatures:()=>p(`${n}jetpack/v4/site/features`,s).then(g).then(m).then((e=>JSON.parse(e.data))),fetchSiteProducts:()=>p(`${n}jetpack/v4/site/products`,s).then(g).then(m),fetchSitePurchases:()=>p(`${n}jetpack/v4/site/purchases`,s).then(g).then(m).then((e=>JSON.parse(e.data))),fetchSiteBenefits:()=>p(`${n}jetpack/v4/site/benefits`,s).then(g).then(m).then((e=>JSON.parse(e.data))),fetchSetupQuestionnaire:()=>p(`${n}jetpack/v4/setup/questionnaire`,s).then(g).then(m),fetchRecommendationsData:()=>p(`${n}jetpack/v4/recommendations/data`,s).then(g).then(m),fetchRecommendationsProductSuggestions:()=>p(`${n}jetpack/v4/recommendations/product-suggestions`,s).then(g).then(m),fetchRecommendationsUpsell:()=>p(`${n}jetpack/v4/recommendations/upsell`,s).then(g).then(m),saveRecommendationsData:e=>d(`${n}jetpack/v4/recommendations/data`,a,{body:JSON.stringify({data:e})}).then(g),fetchProducts:()=>p(`${n}jetpack/v4/products`,s).then(g).then(m),fetchRewindStatus:()=>p(`${n}jetpack/v4/rewind`,s).then(g).then(m).then((e=>JSON.parse(e.data))),fetchScanStatus:()=>p(`${n}jetpack/v4/scan`,s).then(g).then(m).then((e=>JSON.parse(e.data))),dismissJetpackNotice:e=>d(`${n}jetpack/v4/notice/${e}`,a,{body:JSON.stringify({dismissed:!0})}).then(g).then(m),fetchPluginsData:()=>p(`${n}jetpack/v4/plugins`,s).then(g).then(m),fetchVerifySiteGoogleStatus:e=>p(null!==e?`${n}jetpack/v4/verify-site/google/${e}`:`${n}jetpack/v4/verify-site/google`,s).then(g).then(m),verifySiteGoogle:e=>d(`${n}jetpack/v4/verify-site/google`,a,{body:JSON.stringify({keyring_id:e})}).then(g).then(m),sendMobileLoginEmail:()=>d(`${n}jetpack/v4/mobile/send-login-email`,a).then(g).then(m),submitSurvey:e=>d(`${n}jetpack/v4/marketing/survey`,a,{body:JSON.stringify(e)}).then(g).then(m),saveSetupQuestionnaire:e=>d(`${n}jetpack/v4/setup/questionnaire`,a,{body:JSON.stringify(e)}).then(g).then(m),updateLicensingError:e=>d(`${n}jetpack/v4/licensing/error`,a,{body:JSON.stringify(e)}).then(g).then(m),updateLicenseKey:e=>d(`${n}jetpack/v4/licensing/set-license`,a,{body:JSON.stringify({license:e})}).then(g).then(m),getUserLicensesCounts:()=>p(`${n}jetpack/v4/licensing/user/counts`,s).then(g).then(m),updateLicensingActivationNoticeDismiss:e=>d(`${n}jetpack/v4/licensing/user/activation-notice-dismiss`,a,{body:JSON.stringify({last_detached_count:e})}).then(g).then(m),updateRecommendationsStep:e=>d(`${n}jetpack/v4/recommendations/step`,a,{body:JSON.stringify({step:e})}).then(g),confirmIDCSafeMode:()=>d(`${n}jetpack/v4/identity-crisis/confirm-safe-mode`,a).then(g),startIDCFresh:e=>d(`${n}jetpack/v4/identity-crisis/start-fresh`,a,{body:JSON.stringify({redirect_uri:e})}).then(g).then(m),migrateIDC:()=>d(`${n}jetpack/v4/identity-crisis/migrate`,a).then(g),attachLicenses:e=>d(`${n}jetpack/v4/licensing/attach-licenses`,a,{body:JSON.stringify({licenses:e})}).then(g).then(m),fetchSearchPlanInfo:()=>p(`${n}jetpack/v4/search/plan`,s).then(g).then(m),fetchSearchSettings:()=>p(`${n}jetpack/v4/search/settings`,s).then(g).then(m),updateSearchSettings:e=>d(`${n}jetpack/v4/search/settings`,a,{body:JSON.stringify(e)}).then(g).then(m)};function p(e,t){return fetch(u(e),t)}function d(e,t,n){return fetch(e,(0,i.assign)({},t,n)).catch(h)}function f(e){return e.general&&void 0===e.general.response||e.week&&void 0===e.week.response||e.month&&void 0===e.month.response?e:{}}(0,i.assign)(this,l)};function g(e){return e.status>=200&&e.status<300?e:404===e.status?new Promise((()=>{throw e.redirected?new l(e.redirected):new u})):e.json().catch((e=>f(e))).then((t=>{const n=new Error(`${t.message} (Status ${e.status})`);throw n.response=t,n.name="ApiError",n}))}function m(e){return e.json().catch((t=>f(t,e.redirected,e.url)))}function f(e,t,n){throw t?new a(n):new s}function h(){throw new p}t.ZP=d},1583:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(5736),c=n(5609),s=n(1268),a=n.n(s),u=(n(4125),n(7262));const __=r.__,l=e=>{const{label:t,onClick:n,isLoading:i,displayError:r,errorMessage:s}=e;return o().createElement("div",{className:"jp-action-button"},o().createElement(c.Button,{className:"jp-action-button--button",label:t,onClick:n,isPrimary:!0,disabled:i},i?o().createElement(u.Z,null):t),r&&o().createElement("p",{className:"jp-action-button__error"},s))};l.propTypes={label:a().string.isRequired,onClick:a().func,isLoading:a().bool,displayError:a().bool,errorMessage:a().string},l.defaultProps={isLoading:!1,displayError:!1,errorMessage:__("An error occurred. Please try again.","jetpack-connection-ui")},t.Z=l},9697:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r);n(8776);const s=e=>{const{format:t,icon:n,imageUrl:i}=e;return o().createElement("div",{className:"jp-components__decorative-card "+(t?"jp-components__decorative-card--"+t:"")},o().createElement("div",{className:"jp-components__decorative-card__image",style:{backgroundImage:i?`url( ${i} )`:""}}),o().createElement("div",{className:"jp-components__decorative-card__content"},o().createElement("div",{className:"jp-components__decorative-card__lines"})),(()=>{if(n)return o().createElement("div",{className:"jp-components__decorative-card__icon-container"},o().createElement("span",{className:"jp-components__decorative-card__icon jp-components__decorative-card__icon--"+n}))})())};s.propTypes={format:c().oneOf(["horizontal","vertical"]),icon:c().oneOf(["unlink"]),imageUrl:c().string},s.defaultProps={format:"horizontal"},t.Z=s},1546:function(e,t,n){"use strict";var i=n(9183),o=n.n(i),r=n(7538),c=n.n(r),s=n(1268),a=n.n(s),u=n(9196),l=n.n(u),p=n(9105),d=n.n(p),g=n(5736);const __=g.__;class m extends l().Component{render(){const{logoColor:e,showText:t,className:n,...i}=this.props,r=t?"0 0 118 32":"0 0 32 32";return l().createElement("svg",o()({xmlns:"http://www.w3.org/2000/svg",x:"0px",y:"0px",viewBox:r,className:d()("jetpack-logo",n),"aria-labelledby":"jetpack-logo-title"},i),l().createElement("title",{id:"jetpack-logo-title"},__("Jetpack Logo","jetpack-connection-ui")),l().createElement("path",{fill:e,d:"M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M15,19H7l8-16V19z M17,29V13h8L17,29z"}),t&&l().createElement(u.Fragment,null,l().createElement("path",{d:"M41.3,26.6c-0.5-0.7-0.9-1.4-1.3-2.1c2.3-1.4,3-2.5,3-4.6V8h-3V6h6v13.4C46,22.8,45,24.8,41.3,26.6z"}),l().createElement("path",{d:"M65,18.4c0,1.1,0.8,1.3,1.4,1.3c0.5,0,2-0.2,2.6-0.4v2.1c-0.9,0.3-2.5,0.5-3.7,0.5c-1.5,0-3.2-0.5-3.2-3.1V12H60v-2h2.1V7.1 H65V10h4v2h-4V18.4z"}),l().createElement("path",{d:"M71,10h3v1.3c1.1-0.8,1.9-1.3,3.3-1.3c2.5,0,4.5,1.8,4.5,5.6s-2.2,6.3-5.8,6.3c-0.9,0-1.3-0.1-2-0.3V28h-3V10z M76.5,12.3 c-0.8,0-1.6,0.4-2.5,1.2v5.9c0.6,0.1,0.9,0.2,1.8,0.2c2,0,3.2-1.3,3.2-3.9C79,13.4,78.1,12.3,76.5,12.3z"}),l().createElement("path",{d:"M93,22h-3v-1.5c-0.9,0.7-1.9,1.5-3.5,1.5c-1.5,0-3.1-1.1-3.1-3.2c0-2.9,2.5-3.4,4.2-3.7l2.4-0.3v-0.3c0-1.5-0.5-2.3-2-2.3 c-0.7,0-2.3,0.5-3.7,1.1L84,11c1.2-0.4,3-1,4.4-1c2.7,0,4.6,1.4,4.6,4.7L93,22z M90,16.4l-2.2,0.4c-0.7,0.1-1.4,0.5-1.4,1.6 c0,0.9,0.5,1.4,1.3,1.4s1.5-0.5,2.3-1V16.4z"}),l().createElement("path",{d:"M104.5,21.3c-1.1,0.4-2.2,0.6-3.5,0.6c-4.2,0-5.9-2.4-5.9-5.9c0-3.7,2.3-6,6.1-6c1.4,0,2.3,0.2,3.2,0.5V13 c-0.8-0.3-2-0.6-3.2-0.6c-1.7,0-3.2,0.9-3.2,3.6c0,2.9,1.5,3.8,3.3,3.8c0.9,0,1.9-0.2,3.2-0.7V21.3z"}),l().createElement("path",{d:"M110,15.2c0.2-0.3,0.2-0.8,3.8-5.2h3.7l-4.6,5.7l5,6.3h-3.7l-4.2-5.8V22h-3V6h3V15.2z"}),l().createElement("path",{d:"M58.5,21.3c-1.5,0.5-2.7,0.6-4.2,0.6c-3.6,0-5.8-1.8-5.8-6c0-3.1,1.9-5.9,5.5-5.9s4.9,2.5,4.9,4.9c0,0.8,0,1.5-0.1,2h-7.3 c0.1,2.5,1.5,2.8,3.6,2.8c1.1,0,2.2-0.3,3.4-0.7C58.5,19,58.5,21.3,58.5,21.3z M56,15c0-1.4-0.5-2.9-2-2.9c-1.4,0-2.3,1.3-2.4,2.9 C51.6,15,56,15,56,15z"})))}}c()(m,"propTypes",{className:a().string,width:a().number,height:a().number,showText:a().bool,logoColor:a().string}),c()(m,"defaultProps",{className:"",height:32,showText:!0,logoColor:"#069e08"}),t.Z=m},2678:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r),s=n(5609),a=n(6078),u=n(5736);n(7394);const __=u.__,l=e=>-1===e.fraction.indexOf("00"),p=e=>{const t=(0,a.LR)(e.priceBefore,e.currencyCode),n=(0,a.LR)(e.priceAfter,e.currencyCode);return o().createElement("div",{className:"jp-components__pricing-card"},e.icon&&o().createElement("div",{className:"jp-components__pricing-card__icon"},o().createElement("img",{src:e.icon,alt:(0,u.sprintf)(
+/* translators: placeholder is a product name */
+__("Icon for the product %s","jetpack-connection-ui"),e.title)})),o().createElement("h1",{className:"jp-components__pricing-card__title"},e.title),o().createElement("div",{className:"jp-components__pricing-card__pricing"},e.priceBefore!==e.priceAfter&&o().createElement("div",{className:"jp-components__pricing-card__price-before"},o().createElement("span",{className:"jp-components__pricing-card__currency"},t.symbol),o().createElement("span",{className:"jp-components__pricing-card__price"},t.integer),l(t)&&o().createElement("span",{className:"jp-components__pricing-card__price-decimal"}," ",t.fraction),o().createElement("div",{className:"jp-components__pricing-card__price-strikethrough"})),o().createElement("div",{className:"jp-components__pricing-card__price-after"},o().createElement("span",{className:"jp-components__pricing-card__currency"},n.symbol),o().createElement("span",{className:"jp-components__pricing-card__price"},n.integer),l(n)&&o().createElement("span",{className:"jp-components__pricing-card__price-decimal"},n.fraction)),o().createElement("span",{className:"jp-components__pricing-card__price-details"},e.priceDetails)),e.children&&o().createElement("div",{className:"jp-components__pricing-card__extra-content-wrapper"},e.children),e.ctaText&&o().createElement("div",{className:"jp-components__pricing-card__cta"},o().createElement(s.Button,{className:"jp-components__pricing-card__button",label:e.ctaText,onClick:e.onCtaClick},e.ctaText)),e.infoText&&o().createElement("div",{className:"jp-components__pricing-card__info"},e.infoText))};p.propTypes={title:c().string.isRequired,icon:c().string,priceBefore:c().number.isRequired,priceAfter:c().number.isRequired,priceDetails:c().string,currencyCode:c().string,ctaText:c().string,onCtaClick:c().func,infoText:c().oneOfType([c().string,c().object])},p.defaultProps={currencyCode:"USD",priceDetails:__("/month, paid yearly","jetpack-connection-ui")},t.Z=p},7262:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r);n(6212);const s=e=>{const t=e.className+" jp-components-spinner",n={width:e.size,height:e.size,fontSize:e.size,borderTopColor:e.color},i={borderTopColor:e.color,borderRightColor:e.color};return o().createElement("div",{className:t},o().createElement("div",{className:"jp-components-spinner__outer",style:n},o().createElement("div",{className:"jp-components-spinner__inner",style:i})))};s.propTypes={color:c().string,className:c().string,size:c().number},s.defaultProps={color:"#FFFFFF",className:"",size:20},t.Z=s},1415:function(e,t,n){"use strict";function i(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const n={};let i;if("undefined"!=typeof window&&(i=window.Initial_State?.calypsoEnv),0===e.search("https://")){const t=new URL(e);e=`https://${t.host}${t.pathname}`,n.url=encodeURIComponent(e)}else n.source=encodeURIComponent(e);Object.keys(t).map((e=>{n[e]=encodeURIComponent(t[e])})),!Object.keys(n).includes("site")&&"undefined"!=typeof jetpack_redirects&&jetpack_redirects.hasOwnProperty("currentSiteRawUrl")&&(n.site=jetpack_redirects.currentSiteRawUrl),i&&(n.calypso_env=i);const o=Object.keys(n).map((e=>e+"="+n[e])).join("&");return"https://jetpack.com/redirect/?"+o}n.d(t,{Z:function(){return i}})},6251:function(e,t,n){let i={};try{i=n(Object(function(){var e=new Error("Cannot find module 'jetpackConfig'");throw e.code="MODULE_NOT_FOUND",e}()))}catch{console.error("jetpackConfig is missing in your webpack config file. See @automattic/jetpack-config"),i={missingConfig:!0}}const o=e=>i.hasOwnProperty(e);e.exports={jetpackConfigHas:o,jetpackConfigGet:e=>{if(!o(e))throw'This app requires the "'+e+'" Jetpack Config to be defined in your webpack configuration file. See details in @automattic/jetpack-config package docs.';return i[e]}}},4254:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r);const s=e=>{const{images:t,assetBaseUrl:n}=e;if(!t.length)return null;const i=t.map(((e,t)=>o().createElement(o().Fragment,{key:t},o().createElement("img",{src:n+e,alt:""}))));return o().createElement("div",{className:"jp-connection__connect-screen__image-slider"},i)};s.propTypes={images:c().arrayOf(c().string).isRequired,assetBaseUrl:c().string},s.defaultProps={assetBaseUrl:""},t.Z=s},7865:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r),s=n(1546),a=n(4254);n(4959);const u=e=>{const{title:t,children:n,className:i,assetBaseUrl:r,images:c}=e,u=c?.length;return o().createElement("div",{className:"jp-connection__connect-screen-layout"+(u?" jp-connection__connect-screen-layout__two-columns":"")+(i?" "+i:"")},o().createElement("div",{className:"jp-connection__connect-screen-layout__left"},o().createElement(s.Z,null),o().createElement("h2",null,t),n),u?o().createElement("div",{className:"jp-connection__connect-screen-layout__right"},o().createElement(a.Z,{images:c,assetBaseUrl:r})):null)};u.propTypes={title:c().string,className:c().string,images:c().arrayOf(c().string),assetBaseUrl:c().string},t.Z=u},1213:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r),s=n(9818),a=n(5736),u=n(5731),l=n(2199),p=n(1631);const __=a.__,d=e=>{const{title:t,autoTrigger:n,buttonLabel:i,apiRoot:r,apiNonce:c,registrationNonce:a,from:d,redirectUri:g,children:m,priceBefore:f,priceAfter:h,pricingIcon:y,pricingTitle:v,pricingCurrencyCode:b}=e,{handleRegisterSite:_,isRegistered:k,isUserConnected:w,siteIsRegistering:j,userIsConnecting:C,registrationError:E}=(0,p.Z)({registrationNonce:a,redirectUri:g,apiRoot:r,apiNonce:c,autoTrigger:n,from:d}),S=!k||!w,N=(0,s.useSelect)((e=>e(l.t).getConnectionStatusIsFetching())),O=Boolean(E),P=j||C;return o().createElement(u.Z,{title:t,buttonLabel:i,priceBefore:f,priceAfter:h,pricingIcon:y,pricingTitle:v,pricingCurrencyCode:b,isLoading:N,handleButtonClick:_,showConnectButton:S,displayButtonError:O,buttonIsLoading:P},m)};d.propTypes={title:c().string,buttonLabel:c().string,apiRoot:c().string.isRequired,apiNonce:c().string.isRequired,registrationNonce:c().string.isRequired,from:c().string,redirectUri:c().string.isRequired,autoTrigger:c().bool,pricingTitle:c().string.isRequired,icon:c().string,priceBefore:c().number.isRequired,priceAfter:c().number.isRequired,pricingCurrencyCode:c().string},d.defaultProps={title:__("Over 5 million WordPress sites are faster and more secure","jetpack-connection-ui"),buttonLabel:__("Set up Jetpack","jetpack-connection-ui"),pricingCurrencyCode:"USD",autoTrigger:!1},t.Z=d},5731:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r),s=n(5736),a=n(1415),u=n(1583),l=n(2678),p=n(9307),d=n(7865);n(2961);const __=s.__,g=e=>{const{title:t,buttonLabel:n,children:i,priceBefore:r,priceAfter:c,pricingIcon:s,pricingTitle:g,pricingCurrencyCode:m,isLoading:f,handleButtonClick:h,showConnectButton:y,displayButtonError:v,buttonIsLoading:b}=e,_=(0,p.createInterpolateElement)(__("By clicking the button above, you agree to our <tosLink>Terms of Service</tosLink> and to <shareDetailsLink>share details</shareDetailsLink> with WordPress.com.","jetpack-connection-ui"),{tosLink:o().createElement("a",{href:(0,a.Z)("wpcom-tos"),rel:"noopener noreferrer",target:"_blank"}),shareDetailsLink:o().createElement("a",{href:(0,a.Z)("jetpack-support-what-data-does-jetpack-sync"),rel:"noopener noreferrer",target:"_blank"})}),k=(0,p.createInterpolateElement)(__("Already have a subscription? <connectButton/> to get started.","jetpack-connection-ui"),{connectButton:o().createElement(u.Z,{label:__("Log in","jetpack-connection-ui"),onClick:h,isLoading:b})});return o().createElement(d.Z,{title:t,className:"jp-connection__connect-screen-required-plan"+(f?" jp-connection__connect-screen-required-plan__loading":"")},o().createElement("div",{className:"jp-connection__connect-screen-required-plan__content"},i,o().createElement("div",{className:"jp-connection__connect-screen-required-plan__pricing-card"},o().createElement(l.Z,{title:g,icon:s,priceBefore:r,currencyCode:m,priceAfter:c,infoText:y?_:""},y&&o().createElement(u.Z,{label:n,onClick:h,displayError:v,isLoading:b}))),y&&o().createElement("div",{className:"jp-connection__connect-screen-required-plan__with-subscription"},k)))};g.propTypes={pricingTitle:c().string.isRequired,priceBefore:c().number.isRequired,priceAfter:c().number.isRequired,pricingCurrencyCode:c().string,title:c().string,buttonLabel:c().string,pricingIcon:c().string,isLoading:c().bool,handleButtonClick:c().func,showConnectButton:c().bool,displayButtonError:c().bool,buttonIsLoading:c().bool},g.defaultProps={pricingCurrencyCode:"USD",showConnectButton:!0,isLoading:!1,buttonIsLoading:!1,displayButtonError:!1,handleButtonClick:()=>{}},t.Z=g},9565:function(e,t,n){"use strict";var i=n(9196),o=n(1268),r=n.n(o),c=n(9570);const s=e=>{const{redirectFunc:t,connectUrl:n,redirectUri:o,from:r}=e,[s,a]=(0,i.useState)(null);return n&&n!==s&&a(n),(0,i.useEffect)((()=>{s||c.ZP.fetchAuthorizationUrl(o).then((e=>a(e.authorizeUrl))).catch((e=>{throw e}))}),[]),s?(t(s+(r?(s.includes("?")?"&":"?")+"from="+encodeURIComponent(r):"")),null):null};s.propTypes={connectUrl:r().string,redirectUri:r().string.isRequired,from:r().string,redirectFunc:r().func},s.defaultProps={redirectFunc:e=>window.location.assign(e),redirectUri:null},t.Z=s},3593:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r),s=n(5736),a=n(2332);const __=s.__,u=e=>{const{connectedPlugins:t,disconnectingPlugin:n}=e,r=(0,i.useMemo)((()=>{if(t){return Object.keys(t).map((e=>Object.assign({slug:e},t[e]))).filter((e=>n!==e.slug))}return[]}),[t,n]);return t&&r.length>0?o().createElement(o().Fragment,null,o().createElement("div",{className:"jp-connection__disconnect-dialog__step-copy"},o().createElement("p",{className:"jp-connection__disconnect-dialog__large-text"},__("Jetpack is powering other plugins on your site. If you disconnect, these plugins will no longer work.","jetpack-connection-ui"))),o().createElement("div",{className:"jp-connection__disconnect-card__group"},r.map((e=>o().createElement(a.Z,{title:e.name}))))):null};u.PropTypes={connectedPlugins:c().object,disconnectingPlugin:c().string},t.Z=u},7132:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(5736),c=n(5609),s=n(1268),a=n.n(s),u=n(9570),l=n(9818),p=n(9565),d=n(5628),g=n(2199),m=n(1631);n(404);const __=r.__,f=e=>{const{apiRoot:t,apiNonce:n,redirectUri:r,title:s,connectionInfoText:a,onDisconnected:f,connectedPlugins:h,connectedSiteId:y,context:v}=e,{isRegistered:b,isUserConnected:_}=(0,m.Z)({apiRoot:t,apiNonce:n}),[k,w]=(0,i.useState)(!1),[j,C]=(0,i.useState)({}),[E,S]=(0,i.useState)(!1),N=(0,l.useSelect)((e=>e(g.t).getUserIsConnecting()),[]),{setConnectionStatus:O,setUserIsConnecting:P}=(0,l.useDispatch)(g.t),Z=(0,i.useRef)();(0,i.useEffect)((()=>{u.ZP.setApiRoot(t),u.ZP.setApiNonce(n)}),[t,n]),(0,i.useEffect)((()=>{w(!0),u.ZP.fetchSiteConnectionData().then((e=>{w(!1),C(e.currentUser?.wpcomUser);const t=e.currentUser?.wpcomUser?.avatar;t&&(Z.current.style.backgroundImage=`url('${t}')`)})).catch((e=>{throw w(!1),e}))}),[w,C]);const R=(0,i.useCallback)((e=>{e&&e.preventDefault(),S(!0)}),[S]),x=(0,i.useCallback)((e=>{e&&e.preventDefault(),S(!1)}),[S]),F=(0,i.useCallback)((e=>{e&&e.preventDefault(),O({isActive:!1,isRegistered:!1,isUserConnected:!1}),f&&"[object Function]"==={}.toString.call(f)&&f()}),[f,O]);return b?o().createElement("div",{className:"jp-connection-status-card"},o().createElement("h3",null,s),o().createElement("p",null,a),o().createElement("div",{className:"jp-connection-status-card--status"},o().createElement("div",{className:"jp-connection-status-card--cloud"}),o().createElement("div",{className:"jp-connection-status-card--line"+(_?"":" jp-connection-status-card--site-only")}),o().createElement("div",{className:"jp-connection-status-card--jetpack-logo"}),o().createElement("div",{className:"jp-connection-status-card--avatar",ref:Z})),o().createElement("ul",{className:"jp-connection-status-card--list"},o().createElement("li",{className:"jp-connection-status-card--list-item-success"},__("Site connected.","jetpack-connection-ui")," ",o().createElement(c.Button,{variant:"link",onClick:R,className:"jp-connection__disconnect-dialog__link"},__("Disconnect","jetpack-connection-ui")),o().createElement(d.Z,{apiRoot:t,apiNonce:n,onDisconnected:F,connectedPlugins:h,connectedSiteId:y,connectedUser:j,isOpen:E,onClose:x,context:v})),_&&!k&&o().createElement("li",{className:"jp-connection-status-card--list-item-success"},__("Logged in as","jetpack-connection-ui")," ",j?.display_name),!_&&!k&&o().createElement("li",{className:"jp-connection-status-card--list-item-error"},__("Your WordPress.com account is not connected.","jetpack-connection-ui"))),!_&&!k&&o().createElement(c.Button,{isPrimary:!0,disabled:N,onClick:P,className:"jp-connection-status-card--btn-connect-user"},__("Connect your WordPress.com account","jetpack-connection-ui")),N&&o().createElement(p.Z,{redirectUri:r})):null};f.propTypes={apiRoot:a().string.isRequired,apiNonce:a().string.isRequired,redirectUri:a().string.isRequired,connectedPlugins:a().object,connectedSiteId:a().number,title:a().string,connectionInfoText:a().string,onDisconnected:a().func,context:a().string},f.defaultProps={title:__("Connection","jetpack-connection-ui"),connectionInfoText:__("Leverages the Jetpack Cloud for more features on your side.","jetpack-connection-ui")},t.Z=f},2332:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r);n(1294);const s=e=>{const{title:t,value:n,description:i}=e;return o().createElement("div",{className:"jp-connection__disconnect-card card"},o().createElement("div",{className:"jp-connection__disconnect-card__card-content"},o().createElement("p",{className:"jp-connection__disconnect-card__card-headline"},t),(n||i)&&o().createElement("div",{className:"jp-connection__disconnect-card__card-stat-block"},o().createElement("span",{className:"jp-connection__disconnect-card__card-stat"},n),o().createElement("div",{className:"jp-connection__disconnect-card__card-description"},i))))};s.propTypes={title:c().string,value:c().string|c().number,description:c().number},t.Z=s},5628:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r),s=n(5736),a=n(5609),u=n(9570),l=n(816),p=n(6251),d=(n(1545),n(163)),g=n(1496),m=n(4130),f=n(5700);const __=s.__,h=e=>{const[t,n]=(0,i.useState)(!1),[r,c]=(0,i.useState)(!1),[s,h]=(0,i.useState)(!1),[y,v]=(0,i.useState)(!1),[b,_]=(0,i.useState)(!1),[k,w]=(0,i.useState)(!1),{apiRoot:j,apiNonce:C,connectedPlugins:E,title:S,pluginScreenDisconnectCallback:N,onDisconnected:O,onError:P,disconnectStepComponent:Z,context:R,connectedUser:x,connectedSiteId:F,isOpen:T,onClose:I}=e;let A="";(0,p.jetpackConfigHas)("consumer_slug")&&(A=(0,p.jetpackConfigGet)("consumer_slug"));const L=(0,i.useMemo)((()=>({context:R,plugin:A})),[R,A]);(0,i.useEffect)((()=>{u.ZP.setApiRoot(j),u.ZP.setApiNonce(C)}),[j,C]),(0,i.useEffect)((()=>{x&&x.ID&&x.login&&l.Z.initialize(x.ID,x.login)}),[x,x.ID,x.login]),(0,i.useEffect)((()=>{T&&l.Z.tracks.recordEvent("jetpack_disconnect_dialog_open",L)}),[T,L]),(0,i.useEffect)((()=>{T&&(r?!r||y||b?y&&!b?l.Z.tracks.recordEvent("jetpack_disconnect_dialog_step",Object.assign({},{step:"survey"},L)):b&&l.Z.tracks.recordEvent("jetpack_disconnect_dialog_step",Object.assign({},{step:"thank_you"},L)):l.Z.tracks.recordEvent("jetpack_disconnect_dialog_step",Object.assign({},{step:"disconnect_confirm"},L)):l.Z.tracks.recordEvent("jetpack_disconnect_dialog_step",Object.assign({},{step:"disconnect"},L)))}),[T,r,y,b,L]);const U=(0,i.useCallback)((()=>{u.ZP.disconnectSite().then((()=>{n(!1),c(!0)})).catch((e=>{n(!1),h(e),P&&P(e)}))}),[n,c,h,P]),D=(0,i.useCallback)(((e,t)=>{w(!0),fetch("https://public-api.wordpress.com/wpcom/v2/marketing/feedback-survey",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(e)}).then((e=>e.json())).then((e=>{if(!0!==e.success)throw new Error("Survey endpoint returned error code "+e.code);l.Z.tracks.recordEvent("jetpack_disconnect_survey_submit",t),_(!0),w(!1)})).catch((e=>{l.Z.tracks.recordEvent("jetpack_disconnect_survey_error",Object.assign({},{error:e.message},t)),_(!0),w(!1)}))}),[w,_]),$=(0,i.useCallback)((e=>{e&&e.preventDefault(),h(!1),n(!0),"plugins"!==R?U():N&&N(e)}),[h,n,N,R,U]),B=(0,i.useCallback)((()=>x.ID&&F),[x,F]),M=(0,i.useCallback)(((e,t,n)=>{if(n&&n.preventDefault(),!B())return void _(!0);const i={site_id:F,user_id:x.ID,survey_id:"jetpack-plugin-disconnect",survey_responses:{"why-cancel":{response:e,text:t||null}}},o=Object.assign({},L,{disconnect_reason:e});D(i,o)}),[D,_,B,F,x,L]),z=(0,i.useCallback)((e=>{e&&e.preventDefault(),O&&O(),I()}),[O,I]),J=(0,i.useCallback)((e=>{e&&e.preventDefault(),v(!0)}),[v]);return o().createElement(o().Fragment,null,T&&o().createElement(a.Modal,{title:"",contentLabel:S,aria:{labelledby:"jp-connection__disconnect-dialog__heading"},onRequestClose:I,shouldCloseOnClickOutside:!1,shouldCloseOnEsc:!1,isDismissible:!1,className:"jp-connection__disconnect-dialog"+(r?" jp-connection__disconnect-dialog__success":"")},r?!r||y||b?y&&!b?o().createElement(m.Z,{isSubmittingFeedback:k,onFeedBackProvided:M,onExit:z}):b?o().createElement(f.Z,{onExit:z}):void 0:o().createElement(g.Z,{canProvideFeedback:B(),onProvideFeedback:J,onExit:z}):o().createElement(d.Z,{title:S,connectedPlugins:E,disconnectStepComponent:Z,isDisconnecting:t,closeModal:I,onDisconnect:$,disconnectError:s,context:R,disconnectingPlugin:A})))};h.propTypes={apiRoot:c().string.isRequired,apiNonce:c().string.isRequired,title:c().string,onDisconnected:c().func,onError:c().func,context:c().string,connectedPlugins:c().object,pluginScreenDisconnectCallback:c().func,disconnectStepComponent:c().element,connectedUser:c().object,connectedSiteId:c().number,isOpen:c().bool,onClose:c().func},h.defaultProps={title:__("Are you sure you want to disconnect?","jetpack-connection-ui"),context:"jetpack-dashboard",connectedUser:{}},t.Z=h},1496:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r),s=n(9307),a=n(5736),u=n(5609),l=n(9697),p=n(2042);const __=a.__,d=e=>{const{onExit:t,canProvideFeedback:n,onProvideFeedback:i}=e;return o().createElement("div",{className:"jp-connection__disconnect-dialog__content"},o().createElement(l.Z,{icon:"unlink",imageUrl:p}),o().createElement("div",{className:"jp-connection__disconnect-dialog__step-copy jp-connection__disconnect-dialog__step-copy--narrow"},o().createElement("h1",null,(0,s.createInterpolateElement)(__("Jetpack has been <br/>successfully disconnected.","jetpack-connection-ui"),{br:o().createElement("br",null)})),n&&o().createElement(o().Fragment,null,o().createElement("p",null,__("We’re sorry to see you go. Here at Jetpack, we’re always striving to provide the best experience for our customers. Please take our short survey (2 minutes, promise).","jetpack-connection-ui")),o().createElement("p",null,o().createElement(u.Button,{isPrimary:!0,onClick:i,className:"jp-connection__disconnect-dialog__btn-back-to-wp"},__("Help us improve","jetpack-connection-ui"))),o().createElement("a",{className:"jp-connection__disconnect-dialog__link jp-connection__disconnect-dialog__link--bold",href:"#",onClick:t},__("No thank you","jetpack-connection-ui"))),!n&&o().createElement(o().Fragment,null,o().createElement("p",null,o().createElement(u.Button,{isPrimary:!0,onClick:t,className:"jp-connection__disconnect-dialog__btn-back-to-wp"},__("Back to my website","jetpack-connection-ui"))))))};d.PropTypes={onExit:c().func,onProvideFeedback:c().func,canProvideFeedback:c().bool},t.Z=d},163:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(9307),c=n(5736),s=n(1415),a=n(5609),u=n(1268),l=n.n(u),p=n(3593);const __=c.__,d=e=>{const{title:t,isDisconnecting:n,onDisconnect:i,disconnectError:c,disconnectStepComponent:u,connectedPlugins:l,disconnectingPlugin:d,closeModal:g,context:m}=e;return o().createElement(o().Fragment,null,o().createElement("div",{className:"jp-connection__disconnect-dialog__content"},o().createElement("h1",{id:"jp-connection__disconnect-dialog__heading"},t),o().createElement(p.Z,{connectedPlugins:l,disconnectingPlugin:d}),u,(()=>{if(!l&&!u)return o().createElement("div",{className:"jp-connection__disconnect-dialog__step-copy"},o().createElement("p",{className:"jp-connection__disconnect-dialog__large-text"},__("Jetpack is currently powering multiple products on your site.","jetpack-connection-ui"),o().createElement("br",null),__("Once you disconnect Jetpack, these will no longer work.","jetpack-connection-ui")))})()),o().createElement("div",{className:"jp-connection__disconnect-dialog__actions"},o().createElement("div",{className:"jp-row"},o().createElement("div",{className:"lg-col-span-7 md-col-span-8 sm-col-span-4"},o().createElement("p",null,(0,r.createInterpolateElement)(__("<strong>Need help?</strong> Learn more about the <jpConnectionInfoLink>Jetpack connection</jpConnectionInfoLink> or <jpSupportLink>contact Jetpack support</jpSupportLink>.","jetpack-connection-ui"),{strong:o().createElement("strong",null),jpConnectionInfoLink:o().createElement("a",{href:(0,s.Z)("why-the-wordpress-com-connection-is-important-for-jetpack"),rel:"noopener noreferrer",target:"_blank",className:"jp-connection__disconnect-dialog__link"}),jpSupportLink:o().createElement("a",{href:(0,s.Z)("jetpack-support"),rel:"noopener noreferrer",target:"_blank",className:"jp-connection__disconnect-dialog__link"})}))),o().createElement("div",{className:"jp-connection__disconnect-dialog__button-wrap lg-col-span-5 md-col-span-8 sm-col-span-4"},o().createElement(a.Button,{isPrimary:!0,disabled:n,onClick:g,className:"jp-connection__disconnect-dialog__btn-dismiss"},__("Stay connected","jetpack-connection-ui")),(()=>{let e=__("Disconnect","jetpack-connection-ui");return n?e=__("Disconnecting…","jetpack-connection-ui"):"plugins"===m&&(e=__("Disconnect and Deactivate","jetpack-connection-ui")),o().createElement(a.Button,{isPrimary:!0,disabled:n,onClick:i,className:"jp-connection__disconnect-dialog__btn-disconnect"},e)})())),c&&o().createElement("p",{className:"jp-connection__disconnect-dialog__error"},c)))};d.propTypes={title:l().string,isDisconnecting:l().bool,onDisconnect:l().func,disconnectError:l().bool,disconnectStepComponent:l().elementType,connectedPlugins:l().array,disconnectingPlugin:l().string,closeModal:l().func,context:l().string},t.Z=d},4130:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r),s=n(5736),a=(n(843),n(1194));const __=s.__,u=e=>{const{onExit:t,onFeedBackProvided:n,isSubmittingFeedback:i}=e;return o().createElement("div",{className:"jp-connection__disconnect-dialog__content"},o().createElement("h1",null,__("Before you go, help us improve Jetpack","jetpack-connection-ui")),o().createElement("p",{className:"jp-connection__disconnect-dialog__large-text"},__("Let us know what didn‘t work for you","jetpack-connection-ui")),o().createElement(a.Z,{onSubmit:n,isSubmittingFeedback:i}),o().createElement("a",{className:"jp-connection__disconnect-dialog__link jp-connection__disconnect-dialog__link--bold",href:"#",onClick:t},__("Skip for now","jetpack-connection-ui")))};u.PropTypes={onExit:c().func,onFeedBackProvided:c().func,isSubmittingFeedback:c().bool},t.Z=u},5700:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r),s=n(9697),a=n(5736),u=n(5609),l=n(9307),p=n(724);const __=a.__,d=e=>{const{onExit:t}=e;return o().createElement("div",{className:"jp-connection__disconnect-dialog__content"},o().createElement(s.Z,{format:"vertical",imageUrl:p}),o().createElement("div",{className:"jp-connection__disconnect-dialog__copy"},o().createElement("h1",null,__("Thank you!","jetpack-connection-ui")),o().createElement("p",{className:"jp-connection__disconnect-dialog__large-text"},(0,l.createInterpolateElement)(__("Your answer has been submitted. <br/>Thanks for your input on how we can improve Jetpack.","jetpack-connection-ui"),{br:o().createElement("br",null)})),o().createElement(u.Button,{isPrimary:!0,onClick:t,className:"jp-connection__disconnect-dialog__btn-back-to-wp"},__("Back to my website","jetpack-connection-ui"))))};d.PropTypes={onExit:c().func,assetBaseUrl:c().string},t.Z=d},1194:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(1268),c=n.n(r),s=n(5609),a=n(5736),u=n(7215);const __=a.__,l=e=>{const{onSubmit:t,isSubmittingFeedback:n}=e,[r,c]=(0,i.useState)(),[a,l]=(0,i.useState)(),p=[{id:"troubleshooting",answerText:__("Troubleshooting - I'll be reconnecting afterwards.","jetpack-connection-ui")},{id:"not-working",answerText:__("I can't get it to work.","jetpack-connection-ui")},{id:"slowed-down-site",answerText:__("It slowed down my site.","jetpack-connection-ui")},{id:"buggy",answerText:__("It's buggy.","jetpack-connection-ui")},{id:"what-does-it-do",answerText:__("I don't know what it does.","jetpack-connection-ui")}],d="another-reason",g=(0,i.useCallback)((()=>{t(r,r===d?a:"")}),[t,d,a,r]),m=(0,i.useCallback)((e=>{const t=e.target.value;e.stopPropagation(),l(t)}),[l]),f=e=>e===r?"jp-connect__disconnect-survey-card--selected":"",h=(0,i.useCallback)(((e,t)=>{switch(t.key){case"Enter":case"Space":case"Spacebar":case" ":c(e)}}),[c]);return o().createElement(o().Fragment,null,o().createElement("div",{className:"jp-connection__disconnect-dialog__survey"},p.map((e=>o().createElement(u.Z,{id:e.id,onClick:c,onKeyDown:h,className:"card jp-connect__disconnect-survey-card "+f(e.id)},o().createElement("p",{className:"jp-connect__disconnect-survey-card__answer"},e.answerText)))),o().createElement(u.Z,{id:d,onClick:c,onKeyDown:h,className:"card jp-connect__disconnect-survey-card "+f(d)},o().createElement("p",{className:"jp-connect__disconnect-survey-card__answer"},__("Other:","jetpack-connection-ui")," ",o().createElement("input",{placeholder:__("share your experience","jetpack-connection-ui"),className:"jp-connect__disconnect-survey-card__input",type:"text",value:a,onChange:m,maxLength:1e3})))),o().createElement("p",null,o().createElement(s.Button,{disabled:!r||n,isPrimary:!0,onClick:g,className:"jp-connection__disconnect-dialog__btn-back-to-wp"},n?__("Submitting…","jetpack-connection-ui"):__("Submit Feedback","jetpack-connection-ui",0))))};l.PropTypes={onSubmit:c().func,isSubmittingFeedback:c().bool},t.Z=l},7215:function(e,t,n){"use strict";var i=n(9196),o=n.n(i);n(843);t.Z=e=>{const{id:t,onClick:n,onKeyDown:r,children:c,className:s}=e,a=(0,i.useCallback)((()=>{n(t)}),[t,n]),u=(0,i.useCallback)((e=>{r(t,e)}),[t,r]);return o().createElement("div",{tabIndex:"0",role:"button",onClick:a,onKeyDown:u,className:"card jp-connect__disconnect-survey-card "+s},c)}},1631:function(e,t,n){"use strict";var i=n(9196),o=n(9818),r=n(9570),c=n(2199);t.Z=e=>{let{registrationNonce:t,redirectUri:n,apiRoot:s,apiNonce:a,autoTrigger:u,from:l}=e;const{registerSite:p,connectUser:d}=(0,o.useDispatch)(c.t),g=(0,o.useSelect)((e=>e(c.t).getRegistrationError())),{siteIsRegistering:m,userIsConnecting:f,isRegistered:h,isUserConnected:y}=(0,o.useSelect)((e=>({siteIsRegistering:e(c.t).getSiteIsRegistering(),userIsConnecting:e(c.t).getUserIsConnecting(),...e(c.t).getConnectionStatus()}))),v=()=>d({from:l}),b=e=>{e&&e.preventDefault(),h?v():p({registrationNonce:t,redirectUri:n}).then((()=>{v()}))};return(0,i.useEffect)((()=>{r.ZP.setApiRoot(s),r.ZP.setApiNonce(a)}),[s,a]),(0,i.useEffect)((()=>{!u||m||f||b()}),[]),{handleRegisterSite:b,handleConnectUser:v,isRegistered:h,isUserConnected:y,siteIsRegistering:m,userIsConnecting:f,registrationError:g}}},6973:function(e,t,n){"use strict";n.d(t,{i6:function(){return i},LI:function(){return o},r7:function(){return r},N4:function(){return c},qV:function(){return s},T1:function(){return a},TS:function(){return u},ZP:function(){return y}});const i="SET_CONNECTION_STATUS",o="SET_CONNECTION_STATUS_IS_FETCHING",r="SET_SITE_IS_REGISTERING",c="SET_USER_IS_CONNECTING",s="SET_REGISTRATION_ERROR",a="CLEAR_REGISTRATION_ERROR",u="SET_AUTHORIZATION_URL",l="CONNECT_USER",p=e=>({type:i,connectionStatus:e}),d=e=>({type:r,isRegistering:e}),g=e=>({type:c,isConnecting:e}),m=e=>({type:s,registrationError:e}),f=()=>({type:a}),h=e=>({type:u,authorizationUrl:e});const y={setConnectionStatus:p,setConnectionStatusIsFetching:e=>({type:o,isFetching:e}),fetchConnectionStatus:()=>({type:"FETCH_CONNECTION_STATUS"}),fetchAuthorizationUrl:e=>({type:"FETCH_AUTHORIZATION_URL",redirectUri:e}),setSiteIsRegistering:d,setUserIsConnecting:g,setRegistrationError:m,clearRegistrationError:f,setAuthorizationUrl:h,registerSite:function*(e){let{registrationNonce:t,redirectUri:n}=e;yield f(),yield d(!0);try{const e=yield{type:"REGISTER_SITE",registrationNonce:t,redirectUri:n};return yield p({isRegistered:!0}),yield h(e.authorizeUrl),yield d(!1),Promise.resolve(e)}catch(e){return yield m(e),yield d(!1),Promise.reject(e)}},connectUser:function*(){let{from:e,redirectFunc:t}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};yield g(!0),yield{type:l,from:e,redirectFunc:t}}}},4213:function(e,t,n){"use strict";var i=n(9570),o=n(9818),r=n(2199);const c=(0,o.createRegistryControl)((e=>{let{resolveSelect:t}=e;return function(){let{from:e,redirectFunc:n}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return new Promise(((i,o)=>{t(r.t).getAuthorizationUrl().then((t=>{const o=n||(e=>window.location.assign(e)),r=new URL(t);e&&r.searchParams.set("from",encodeURIComponent(e));const c=r.toString();o(c),i(c)})).catch((e=>{o(e)}))}))}}));t.Z={FETCH_CONNECTION_STATUS:()=>new Promise(((e,t)=>{i.ZP.fetchSiteConnectionStatus().then((t=>e(t))).catch((e=>t(e)))})),FETCH_AUTHORIZATION_URL:e=>{let{redirectUri:t}=e;return i.ZP.fetchAuthorizationUrl(t)},REGISTER_SITE:e=>{let{registrationNonce:t,redirectUri:n}=e;return i.ZP.registerSite(t,n)},CONNECT_USER:c}},1147:function(e,t,n){"use strict";var i=n(9818),o=n(6973);const r=(0,i.combineReducers)({connectionStatus:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1?arguments[1]:void 0;return t.type===o.i6?{...e,...t.connectionStatus}:e},connectionStatusIsFetching:function(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=arguments.length>1?arguments[1]:void 0;return t.type===o.LI?t.isFetching:e},siteIsRegistering:function(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=arguments.length>1?arguments[1]:void 0;return t.type===o.r7?t.isRegistering:e},userIsConnecting:function(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=arguments.length>1?arguments[1]:void 0;return t.type===o.N4?t.isConnecting:e},registrationError:(e,t)=>{switch(t.type){case o.T1:return!1;case o.qV:return t.registrationError;default:return e}},authorizationUrl:(e,t)=>t.type===o.TS?t.authorizationUrl:e});t.Z=r},8310:function(e,t,n){"use strict";var i=n(9818),o=n(6973),r=n(2199);const c={*getConnectionStatus(){yield o.ZP.setConnectionStatusIsFetching(!0);const e=yield o.ZP.fetchConnectionStatus();return yield o.ZP.setConnectionStatusIsFetching(!1),o.ZP.setConnectionStatus(e)},getAuthorizationUrl:{isFulfilled:function(e){const t=Boolean(e.authorizationUrl);for(var n=arguments.length,o=new Array(n>1?n-1:0),c=1;c<n;c++)o[c-1]=arguments[c];const s=(0,i.select)(r.t).hasFinishedResolution("getAuthorizationUrl",o);return t&&!s&&(0,i.dispatch)(r.t).finishResolution("getAuthorizationUrl",o),t},*fulfill(e){const t=yield o.ZP.fetchAuthorizationUrl(e);yield o.ZP.setAuthorizationUrl(t.authorizeUrl)}}};t.Z={...c}},387:function(e,t){"use strict";const n={getConnectionStatus:e=>e.connectionStatus||{},getConnectionStatusIsFetching:e=>e.connectionStatusIsFetching||!1,getSiteIsRegistering:e=>e.siteIsRegistering||!1,getUserIsConnecting:e=>e.userIsConnecting||!1,getRegistrationError:e=>e.registrationError||!1,getAuthorizationUrl:e=>e.authorizationUrl||!1};t.Z=n},5333:function(e,t,n){"use strict";var i=n(7538),o=n.n(i),r=n(9818);class c{static mayBeInit(e,t){null===c.store&&(c.store=(0,r.createReduxStore)(e,t),(0,r.register)(c.store),c.resolveResolvers(e,t.initialState))}static resolveResolvers(e,t){t.connectionStatus&&t.connectionStatus.hasOwnProperty("isRegistered")&&(0,r.dispatch)(e).finishResolution("getConnectionStatus",[])}}o()(c,"store",null),t.Z=c},2199:function(e,t,n){"use strict";n.d(t,{t:function(){return u}});var i=n(1147),o=n(6973),r=n(387),c=n(5333),s=n(8310),a=n(4213);const u="jetpack-connection";c.Z.mayBeInit(u,{reducer:i.Z,actions:o.ZP,selectors:r.Z,resolvers:s.Z,controls:a.Z,initialState:window.JP_CONNECTION_INITIAL_STATE||{}})},8509:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(9818),c=n(5736),s=n(9570),a=n(7132),u=n(1213),l=n(2199),p=n(682),d=n(8547);const __=c.__;t.Z=(0,r.withSelect)((e=>({connectionStatus:e(l.t).getConnectionStatus()})))((e=>{const t=(0,r.useSelect)((e=>e(p.t).getAPINonce()),[]),n=(0,r.useSelect)((e=>e(p.t).getAPIRoot()),[]),c=(0,r.useSelect)((e=>e(p.t).getRegistrationNonce()),[]),{hasIDC:l,canManageConnection:g,isSafeModeConfirmed:m}=(0,r.useSelect)((e=>e(p.t).getIDCData()),[]),{connectionStatus:f}=e;return(0,i.useEffect)((()=>{s.ZP.setApiRoot(n),s.ZP.setApiNonce(t)}),[n,t]),o().createElement(o().Fragment,null,(!l||m)&&o().createElement(d.Z,null),(!l||m)&&g&&f.isRegistered&&o().createElement(a.Z,{apiRoot:n,apiNonce:t,redirectUri:"tools.php?page=wpcom-connection-manager"}),(!l||m)&&g&&!f.isRegistered&&o().createElement(u.Z,{connectionStatus:f,apiRoot:n,apiNonce:t,registrationNonce:c,from:"connection-ui",redirectUri:"tools.php?page=wpcom-connection-manager",pricingIcon:"data:image/svg+xml,%3Csvg width='32' height='32' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m21.092 15.164.019-1.703v-.039c0-1.975-1.803-3.866-4.4-3.866-2.17 0-3.828 1.351-4.274 2.943l-.426 1.524-1.581-.065a2.92 2.92 0 0 0-.12-.002c-1.586 0-2.977 1.344-2.977 3.133 0 1.787 1.388 3.13 2.973 3.133H22.399c1.194 0 2.267-1.016 2.267-2.4 0-1.235-.865-2.19-1.897-2.368l-1.677-.29Zm-10.58-3.204a4.944 4.944 0 0 0-.201-.004c-2.75 0-4.978 2.298-4.978 5.133s2.229 5.133 4.978 5.133h12.088c2.357 0 4.267-1.97 4.267-4.4 0-2.18-1.538-3.99-3.556-4.339v-.06c0-3.24-2.865-5.867-6.4-5.867-2.983 0-5.49 1.871-6.199 4.404Z' fill='%23000'/%3E%3C/svg%3E",priceBefore:9,priceAfter:4.5,pricingTitle:__("Jetpack Backup","jetpack-connection-ui"),buttonLabel:__("Get Jetpack Backup","jetpack-connection-ui")},o().createElement("p",null,__("Secure and speed up your site for free with Jetpack's powerful WordPress tools.","jetpack-connection-ui")),o().createElement("ul",null,o().createElement("li",null,__("Measure your impact with beautiful stats","jetpack-connection-ui")),o().createElement("li",null,__("Speed up your site with optimized images","jetpack-connection-ui")),o().createElement("li",null,__("Protect your site against bot attacks","jetpack-connection-ui")),o().createElement("li",null,__("Get notifications if your site goes offline","jetpack-connection-ui")),o().createElement("li",null,__("Enhance your site with dozens of other features","jetpack-connection-ui")))),(!l||m)&&!g&&o().createElement("p",null,"You need to be an admin to access this page."))}))},8547:function(e,t,n){"use strict";var i=n(9196),o=n.n(i),r=n(5736);const __=r.__;t.Z=()=>o().createElement("div",{className:"jetpack-cui__header"},o().createElement("h1",null,__("Connection Manager","jetpack-connection-ui")))},5472:function(e,t){"use strict";t.Z=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return e}},3379:function(e,t){"use strict";t.Z=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return e}},2310:function(e,t){"use strict";t.Z=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return e}},4665:function(e,t,n){"use strict";var i=n(9818),o=n(5472),r=n(3379),c=n(2310);const s=(0,i.combineReducers)({API:o.Z,assets:r.Z,IDC:c.Z});t.Z=s},6931:function(e,t){"use strict";t.Z={getAPIRoot:e=>e.API.WP_API_root||null,getAPINonce:e=>e.API.WP_API_nonce||null,getRegistrationNonce:e=>e.API.registrationNonce||null}},4447:function(e,t){"use strict";t.Z={getAssetBuildUrl:e=>e.assets.buildUrl||null}},9546:function(e,t){"use strict";t.Z={getIDCData:e=>e.IDC||{}}},246:function(e,t,n){"use strict";var i=n(6931),o=n(4447),r=n(9546);const c={...i.Z,...o.Z,...r.Z};t.Z=c},682:function(e,t,n){"use strict";n.d(t,{t:function(){return r},i:function(){return c}});var i=n(4665),o=n(246);const r="jetpack-connection-ui",c={reducer:i.Z,selectors:o.Z,initialState:window.CUI_INITIAL_STATE||{}}},1043:function(e,t,n){"use strict";var i=n(1625),o=n(9196);t.useSubscription=function(e){var t=e.getCurrentValue,n=e.subscribe,r=o.useState((function(){return{getCurrentValue:t,subscribe:n,value:t()}}));e=r[0];var c=r[1];return r=e.value,e.getCurrentValue===t&&e.subscribe===n||(r=t(),c({getCurrentValue:t,subscribe:n,value:r})),o.useDebugValue(r),o.useEffect((function(){function e(){if(!o){var e=t();c((function(o){return o.getCurrentValue!==t||o.subscribe!==n||o.value===e?o:i({},o,{value:e})}))}}var o=!1,r=n(e);return e(),function(){o=!0,r()}}),[t,n]),r}},36:function(e,t,n){"use strict";e.exports=n(1043)},2042:function(e,t,n){"use strict";e.exports=n.p+"images/disconnect-confirm-dc9fe8f5c68cfd1320e0.jpg"},724:function(e,t,n){"use strict";e.exports=n.p+"images/disconnect-thanks-5873bfac56a9bd7322cd.jpg"},9196:function(e){"use strict";e.exports=window.React},1850:function(e){"use strict";e.exports=window.ReactDOM},2819:function(e){"use strict";e.exports=window.lodash},5609:function(e){"use strict";e.exports=window.wp.components},4333:function(e){"use strict";e.exports=window.wp.compose},9818:function(e){"use strict";e.exports=window.wp.data},9307:function(e){"use strict";e.exports=window.wp.element},5736:function(e){"use strict";e.exports=window.wp.i18n},6483:function(e){"use strict";e.exports=window.wp.url},8134:function(e,t,n){"use strict";function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,i=new Array(t);n<t;n++)i[n]=e[n];return i}n.d(t,{Z:function(){return i}})},4316:function(e,t,n){"use strict";function i(e){if(Array.isArray(e))return e}n.d(t,{Z:function(){return i}})},7024:function(e,t,n){"use strict";n.d(t,{Z:function(){return o}});var i=n(8134);function o(e){if(Array.isArray(e))return(0,i.Z)(e)}},5200:function(e,t,n){"use strict";function i(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}n.d(t,{Z:function(){return i}})},1687:function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}n.d(t,{Z:function(){return i}})},3772:function(e,t,n){"use strict";function i(e,t){for(var n=0;n<t.length;n++){var i=t[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(e,i.key,i)}}function o(e,t,n){return t&&i(e.prototype,t),n&&i(e,n),e}n.d(t,{Z:function(){return o}})},9057:function(e,t,n){"use strict";n.d(t,{Z:function(){return c}});var i=n(2406),o=n(8189),r=n(4621);function c(e){var t=(0,o.Z)();return function(){var n,o=(0,i.Z)(e);if(t){var c=(0,i.Z)(this).constructor;n=Reflect.construct(o,arguments,c)}else n=o.apply(this,arguments);return(0,r.Z)(this,n)}}},8086:function(e,t,n){"use strict";function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}n.d(t,{Z:function(){return i}})},5672:function(e,t,n){"use strict";function i(){return i=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)Object.prototype.hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},i.apply(this,arguments)}n.d(t,{Z:function(){return i}})},2406:function(e,t,n){"use strict";function i(e){return i=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)},i(e)}n.d(t,{Z:function(){return i}})},2104:function(e,t,n){"use strict";n.d(t,{Z:function(){return o}});var i=n(3051);function o(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&(0,i.Z)(e,t)}},8189:function(e,t,n){"use strict";function i(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}n.d(t,{Z:function(){return i}})},613:function(e,t,n){"use strict";function i(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}n.d(t,{Z:function(){return i}})},120:function(e,t,n){"use strict";function i(e,t){var n=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=n){var i,o,r=[],_n=!0,c=!1;try{for(n=n.call(e);!(_n=(i=n.next()).done)&&(r.push(i.value),!t||r.length!==t);_n=!0);}catch(e){c=!0,o=e}finally{try{_n||null==n.return||n.return()}finally{if(c)throw o}}return r}}n.d(t,{Z:function(){return i}})},1722:function(e,t,n){"use strict";function i(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}n.d(t,{Z:function(){return i}})},6083:function(e,t,n){"use strict";function i(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}n.d(t,{Z:function(){return i}})},2141:function(e,t,n){"use strict";n.d(t,{Z:function(){return o}});var i=n(8086);function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?Object(arguments[t]):{},o=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&o.push.apply(o,Object.getOwnPropertySymbols(n).filter((function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),o.forEach((function(t){(0,i.Z)(e,t,n[t])}))}return e}},9591:function(e,t,n){"use strict";n.d(t,{Z:function(){return r}});var i=n(8086);function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);t&&(i=i.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,i)}return n}function r(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?o(Object(n),!0).forEach((function(t){(0,i.Z)(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):o(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}},4621:function(e,t,n){"use strict";n.d(t,{Z:function(){return r}});var i=n(9504),o=n(5200);function r(e,t){if(t&&("object"===(0,i.Z)(t)||"function"==typeof t))return t;if(void 0!==t)throw new TypeError("Derived constructors may only return object or undefined");return(0,o.Z)(e)}},3051:function(e,t,n){"use strict";function i(e,t){return i=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},i(e,t)}n.d(t,{Z:function(){return i}})},572:function(e,t,n){"use strict";n.d(t,{Z:function(){return s}});var i=n(4316),o=n(120),r=n(3611),c=n(1722);function s(e,t){return(0,i.Z)(e)||(0,o.Z)(e,t)||(0,r.Z)(e,t)||(0,c.Z)()}},9128:function(e,t,n){"use strict";n.d(t,{Z:function(){return s}});var i=n(7024),o=n(613),r=n(3611),c=n(6083);function s(e){return(0,i.Z)(e)||(0,o.Z)(e)||(0,r.Z)(e)||(0,c.Z)()}},9504:function(e,t,n){"use strict";function i(e){return i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},i(e)}n.d(t,{Z:function(){return i}})},3611:function(e,t,n){"use strict";n.d(t,{Z:function(){return o}});var i=n(8134);function o(e,t){if(e){if("string"==typeof e)return(0,i.Z)(e,t);var n=Object.prototype.toString.call(e).slice(8,-1);return"Object"===n&&e.constructor&&(n=e.constructor.name),"Map"===n||"Set"===n?Array.from(e):"Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?(0,i.Z)(e,t):void 0}}}},t={};function n(i){var o=t[i];if(void 0!==o)return o.exports;var r=t[i]={exports:{}};return e[i](r,r.exports,n),r.exports}n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,{a:t}),t},n.d=function(e,t){for(var i in t)n.o(t,i)&&!n.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},function(){var e;n.g.importScripts&&(e=n.g.location+"");var t=n.g.document;if(!e&&t&&(t.currentScript&&(e=t.currentScript.src),!e)){var i=t.getElementsByTagName("script");i.length&&(e=i[i.length-1].src)}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),n.p=e}(),function(){"use strict";var e=n(1850),t=n.n(e),i=n(9196),o=n.n(i),r=n(9818),c=n(8509),s=n(682);const a=(0,r.createReduxStore)(s.t,s.i);(0,r.register)(a),function(){const e=document.getElementById("jetpack-connection-ui-container");null!==e&&t().render(o().createElement(c.Z,null),e)}()}()}(); \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.js.LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.js.LICENSE.txt
new file mode 100644
index 00000000..00db07be
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.js.LICENSE.txt
@@ -0,0 +1,28 @@
+/*
+ * Exposes number format capability
+ *
+ * @copyright Copyright (c) 2013 Kevin van Zonneveld (http://kvz.io) and Contributors (http://phpjs.org/authors).
+ * @license See CREDITS.md
+ * @see https://github.com/kvz/phpjs/blob/ffe1356af23a6f2512c84c954dd4e828e92579fa/functions/strings/number_format.js
+ */
+
+/*
+object-assign
+(c) Sindre Sorhus
+@license MIT
+*/
+
+/*!
+ Copyright (c) 2018 Jed Watson.
+ Licensed under the MIT License (MIT), see
+ http://jedwatson.github.io/classnames
+*/
+
+/** @license React vundefined
+ * use-subscription.production.min.js
+ *
+ * Copyright (c) Facebook, Inc. and its affiliates.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.rtl.css b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.rtl.css
new file mode 100644
index 00000000..5924baa9
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/build/index.rtl.css
@@ -0,0 +1 @@
+#jetpack-connection-ui-container .jetpack-cui__header h1{font-size:4.6em;line-height:1.2em;text-align:right}#jetpack-connection-ui-container{margin:40px}#jetpack-connection-ui-container p{font-size:1.23em;line-height:1.5em}.jp-connection-status-card h3{color:var(--jp-black);font-size:36px;font-weight:400;line-height:40px;margin:0}.jp-connection-status-card a,.jp-connection-status-card a:active,.jp-connection-status-card a:hover{color:var(--jp-black)}.jp-connection-status-card p{color:var(--jp-black);margin:16px 0}.jp-connection-status-card a,.jp-connection-status-card li,.jp-connection-status-card p{font-size:16px;line-height:24px}.jp-connection-status-card--status{align-items:center;display:flex;margin:24px -6px 24px 0}.jp-connection-status-card--cloud{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDIiIGhlaWdodD0iNDIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PG1hc2sgaWQ9ImEiIG1hc2stdHlwZT0iYWxwaGEiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjciIHk9IjkiIHdpZHRoPSIyOCIgaGVpZ2h0PSIyMSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zMC4zMzMgMTcuNjk3di0uMDhjMC00LjI1My0zLjc2LTcuNy04LjQtNy43LTMuOTE2IDAtNy4yMDYgMi40NTctOC4xMzYgNS43OGE2LjM4NiA2LjM4NiAwIDAgMC0uMjY0LS4wMDVDOS45MjUgMTUuNjkyIDcgMTguNzA4IDcgMjIuNDI5czIuOTI1IDYuNzM4IDYuNTMzIDYuNzM4SDI5LjRjMy4wOTMgMCA1LjYtMi41ODYgNS42LTUuNzc1IDAtMi44NjEtMi4wMTgtNS4yMzctNC42NjYtNS42OTVaIiBmaWxsPSIjZmZmIi8+PC9tYXNrPjxnIG1hc2s9InVybCgjYSkiPjxwYXRoIGQ9Im0zMC4zMzMgMTcuNjk3LTIuNjI1LS4wMy0uMDI1IDIuMjM1IDIuMjAyLjM4MS40NDgtMi41ODZabS0xNi41MzctMi0uMTA3IDIuNjIzIDIuMDc1LjA4NS41Ni0yLTIuNTI4LS43MDhabS0uMjYzIDEzLjQ3djIuNjI1aC4wMDhsLS4wMDgtMi42MjVabS4wNCAwdi0yLjYyNWgtLjAwOWwuMDA4IDIuNjI1Wm0xNS43OTUgMCAuMDE1LTIuNjI1aC0uMDE1djIuNjI1Wm0zLjU5LTExLjQ0MXYtLjExaC01LjI1di4wNTJsNS4yNS4wNThabTAtLjExYzAtNS45MTItNS4xNTUtMTAuMzI0LTExLjAyNS0xMC4zMjR2NS4yNWMzLjQxIDAgNS43NzUgMi40ODIgNS43NzUgNS4wNzVoNS4yNVpNMjEuOTMzIDcuMjkzYy00Ljk4NCAwLTkuMzg4IDMuMTM5LTEwLjY2NCA3LjY5OGw1LjA1NSAxLjQxNWMuNTg1LTIuMDg5IDIuNzYyLTMuODYzIDUuNjEtMy44NjN2LTUuMjVabS04LjAzIDUuNzgyYTkuMTExIDkuMTExIDAgMCAwLS4zNy0uMDA3djUuMjVjLjA1MyAwIC4xMDUuMDAxLjE1Ni4wMDNsLjIxNS01LjI0NlptLS4zNy0uMDA3Yy01LjEzMyAwLTkuMTU4IDQuMjY4LTkuMTU4IDkuMzYyaDUuMjVjMC0yLjM0OCAxLjgyNS00LjExMiAzLjkwOC00LjExMnYtNS4yNVptLTkuMTU4IDkuMzYyYzAgNS4wOTUgNC4wMjUgOS4zNjMgOS4xNTggOS4zNjN2LTUuMjVjLTIuMDgzIDAtMy45MDgtMS43NjUtMy45MDgtNC4xMTNoLTUuMjVabTkuMTY2IDkuMzYzaC4wNGwtLjAxNy01LjI1aC0uMDM5bC4wMTYgNS4yNVptLjAzMSAwaDE1Ljc5NnYtNS4yNUgxMy41NzJ2NS4yNVptMTUuODI3LTUuMjVoLS4wMTZsLS4wMyA1LjI1aC4wNDZ2LTUuMjVabTIuOTc1LTMuMTVjMCAxLjgxNi0xLjQwNyAzLjE1LTIuOTc1IDMuMTV2NS4yNWM0LjYxOCAwIDguMjI1LTMuODM4IDguMjI1LTguNGgtNS4yNVptLTIuNDg5LTMuMTA5YzEuMzU1LjIzNSAyLjQ5IDEuNDg4IDIuNDkgMy4xMDloNS4yNWMwLTQuMTAyLTIuOTAyLTcuNi02Ljg0NC04LjI4MmwtLjg5NiA1LjE3M1oiIGZpbGw9IiMxRTFFMUUiLz48L2c+PC9zdmc+);height:42px;margin-left:4px;width:42px}.jp-connection-status-card--jetpack-logo{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE2IDMyYzguODM3IDAgMTYtNy4xNjMgMTYtMTZTMjQuODM3IDAgMTYgMCAwIDcuMTYzIDAgMTZzNy4xNjMgMTYgMTYgMTZaIiBmaWxsPSIjMDAwIi8+PHBhdGggZD0iTTE2Ljc5NCAxMy4zMTN2MTUuNTExbDgtMTUuNTEyaC04Wk0xNS4xNzUgMTguNjU3VjMuMTc2bC03Ljk2OSAxNS40OGg3Ljk3WiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==);height:32px;margin-right:11px;width:32px}.jp-connection-status-card--btn-connect-user{background:var(--jp-black)!important;border-radius:4px;font-size:var(--font-body-small);height:40px}.jp-connection-status-card--avatar{background-color:var(--jp-white);background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='32' height='32' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='16' cy='16' r='16' fill='%23fff'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M4.498 27.123C6.038 24.165 10.916 21.5 16 21.5c5.084 0 9.963 2.665 11.502 5.623a15.952 15.952 0 0 1-11.257 4.875L16 32l-.245-.002a15.952 15.952 0 0 1-11.257-4.875zM16 8a6 6 0 1 1 0 12 6 6 0 0 1 0-12z' fill='%23A2AAB2'/%3E%3C/svg%3E");background-repeat:no-repeat;background-size:contain;border:0;border-radius:20px;height:32px;margin-right:-10px;width:32px}.jp-connection-status-card--line{border-top:2px solid var(--jp-black);height:0;width:67px}.jp-connection-status-card--line.jp-connection-status-card--site-only{border-top-style:dashed}.jp-connection-status-card--list{list-style-type:none;margin:16px 0}.jp-connection-status-card--list li{color:var(--jp-black);margin:0 -3px 8px 0;padding-right:25px}.jp-connection-status-card--list-item-success{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTE4LjkzNSA2LTguOSAxMS45Ny01LjE2Mi0zLjgzNyIgc3Ryb2tlPSIjMUUxRTFFIiBzdHJva2Utd2lkdGg9IjEuNSIvPjwvc3ZnPg==) no-repeat 100% 0}.jp-connection-status-card--list-item-error{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDIwYTggOCAwIDEgMCAwLTE2IDggOCAwIDAgMCAwIDE2WiIgc3Ryb2tlPSIjRDYzNjM5IiBzdHJva2Utd2lkdGg9IjEuNSIvPjxwYXRoIGQ9Ik0xMyA3aC0ydjZoMlY3Wk0xMyAxNWgtMnYyaDJ2LTJaIiBmaWxsPSIjRDYzNjM5Ii8+PC9zdmc+) no-repeat 100% 0;color:var(--jp-red)!important}.jp-connection__disconnect-dialog h1{font-size:var(--font-title-small);font-weight:600;line-height:1.2;margin-top:0}.jp-connection__disconnect-dialog h2{font-size:var(--font-title-small);font-weight:400;line-height:1.2;margin:0}.jp-connection__disconnect-dialog p{font-size:var(--font-body);margin-top:0}.jp-connection__disconnect-dialog__large-text,.jp-connection__disconnect-dialog p.jp-connection__disconnect-dialog__large-text{font-size:1.25rem}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link,.jp-connection__disconnect-dialog__link{color:var(--jp-black);font-size:var(--font-body);font:inherit;height:auto;padding:0;text-decoration:underline}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link:hover,.jp-connection__disconnect-dialog__link:hover{color:var(--jp-black);text-decoration-thickness:var(--jp-underline-thickness)}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link:focus,.jp-connection__disconnect-dialog__link:focus{box-shadow:none!important;color:var(--jp-black)}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link--bold,.jp-connection__disconnect-dialog__link--bold{font-weight:700}.jp-connection__disconnect-dialog .components-button{border-radius:4px;font-size:var(--font-body-small);height:40px}.jp-connection__disconnect-dialog .components-modal__content{display:flex;flex-direction:column;flex-grow:1;margin:0;padding:0}.jp-connection__disconnect-dialog .components-modal__content:before,.jp-connection__disconnect-dialog .components-modal__header{display:none}.jp-connection__disconnect-dialog .jp-row{align-items:center;width:calc(100% - 48px)}.jp-connection__disconnect-dialog__content{align-items:center;background:var(--jp-white-off);border-radius:4px;display:flex;flex-direction:column;flex-grow:1;justify-content:center;margin:0;padding:2rem 1rem;text-align:center}.jp-connection__disconnect-dialog__actions{background:var(--jp-white);border-top:1px solid var(--jp-gray);bottom:0;padding:2rem 0;position:sticky}.jp-connection__disconnect-dialog__actions p{margin-bottom:0}.jp-connection__disconnect-dialog__actions:before{background:linear-gradient(to bottom,transparent,var(--jp-white-off));bottom:calc(100% + 1px);content:"";display:block;height:80px;position:absolute;right:0;width:100%}.jp-connection__disconnect-dialog__btn-dismiss,.jp-connection__disconnect-dialog__btn-dismiss.components-button{background:var(--jp-black)!important;margin-left:10px}.jp-connection__disconnect-dialog__btn-disconnect{background:var(--jp-red)!important}.jp-connection__disconnect-dialog__btn-back-to-wp{background:var(--jp-black)!important}.jp-connection__disconnect-dialog__button-wrap{text-align:right}@media(min-width:960px){.jp-connection__disconnect-dialog__button-wrap{text-align:center}}.jp-connection__disconnect-dialog__error{color:var(--jp-red)}.jp-connection__disconnect-dialog__survey{margin-bottom:1.5rem;max-width:100%}.jp-connection__disconnect-dialog__step-copy{margin:0 auto;max-width:800px}.jp-connection__disconnect-dialog__step-copy--narrow{max-width:600px}@media(max-height:900px){.jp-connection__disconnect-dialog__content .jp-components__decorative-card{display:none}}@media(min-width:600px){.jp-connection__disconnect-dialog,.jp-connection__disconnect-dialog.components-modal__frame{max-width:calc(100% - 32px);width:100%}.jp-connection__disconnect-dialog__actions,.jp-connection__disconnect-dialog__content{padding:2rem}}@media(min-width:960px){.jp-connection__disconnect-dialog,.jp-connection__disconnect-dialog.components-modal__frame{display:flex;flex-direction:column;height:900px;width:1200px}.jp-connection__disconnect-dialog h1{font-size:var(--font-title-large)}.jp-connection__disconnect-dialog__large-text,.jp-connection__disconnect-dialog p.jp-connection__disconnect-dialog__large-text{font-size:1.5rem}.jp-connection__disconnect-dialog__content{padding:80px}.jp-connection__disconnect-dialog__actions{padding:2rem 3rem}.jp-row{margin-right:0}}.jp-connection__disconnect-card{background-color:var(--jp-white);border:none;border-radius:3px;box-shadow:0 0 15px var(--jp-gray-off);margin:0 auto 1rem;max-width:100%;padding:1rem 2rem;text-align:right;width:800px}.jp-connection__disconnect-card__group{margin-bottom:1rem;max-width:100%}.jp-connection__disconnect-card__card-content{display:block;font-size:.875rem}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-content{align-items:center;display:flex;justify-content:space-between}}.jp-connection__disconnect-card .jp-connection__disconnect-card__card-headline,.jp-connection__disconnect-card__card-headline{flex-shrink:0;font-size:1.25rem;font-weight:600;margin-bottom:0;margin-top:0}@media only screen and (min-width:782px){.jp-connection__disconnect-card .jp-connection__disconnect-card__card-headline,.jp-connection__disconnect-card__card-headline{font-size:1.5rem;margin-left:1.5rem}}@media only screen and (max-width:782px){.jp-connection__disconnect-card .jp-connection__disconnect-card__card-headline+.jp-disconnect-card__card-stat-block,.jp-connection__disconnect-card__card-headline+.jp-disconnect-card__card-stat-block{margin-top:.5rem}}.jp-connection__disconnect-card__card-stat-block{align-items:baseline;display:flex;flex-grow:1}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-stat-block{flex-direction:row-reverse}}.jp-connection__disconnect-card__card-description{flex-grow:1}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-description{text-align:left}}.jp-connection__disconnect-card__card-stat{font-size:1rem;font-weight:600;margin-left:.5rem}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-stat{font-size:1.5rem;margin-left:0;margin-right:1rem}}.jp-components__decorative-card{border-radius:8px;box-shadow:0 0 15px var(--jp-gray);display:flex;height:280px;margin:0 auto 3rem;max-width:100%;overflow:hidden;position:relative;width:360px}.jp-components__decorative-card__content,.jp-components__decorative-card__image{width:50%}.jp-components__decorative-card__image{background:var(--jp-gray);background-size:cover;position:relative}.jp-components__decorative-card__image:before{background-image:url('data:image/svg+xml;uf8,<svg width="38" height="8" viewBox="0 0 38 8" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 7C1 7 2.37087 1 6.89831 1C11.4257 1 14.3709 7 18.8983 7C23.4257 7 26.7777 1 31.3051 1C35.912 1 37 7 37 7" stroke="white" stroke-width="1.5" stroke-linejoin="round"/></svg>');content:"";display:block;height:8px;position:absolute;right:24px;top:24px;width:38px}.jp-components__decorative-card__content{background:#fff;padding:2rem}.jp-components__decorative-card__icon-container{background:var(--jp-red);border-radius:50px;height:80px;position:absolute;right:50%;top:50%;transform:translate(50%,-50%);width:80px}.jp-components__decorative-card__icon{background-position:50%,50%;background-repeat:no-repeat;height:40px;position:absolute;right:50%;top:50%;transform:translate(50%,-50%);width:40px}.jp-components__decorative-card__icon--unlink{background-image:url('data:image/svg+xml;uf8,<svg width="34" height="37" viewBox="0 0 34 37" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M22.3335 10.001H25.0002C29.4184 10.001 33.0002 13.5827 33.0002 18.001V19.7788C33.0002 24.197 29.4184 27.7788 25.0002 27.7788H22.3335" stroke="white" stroke-width="1.5" stroke-linecap="square"/> <path d="M11.6675 27.7783L9.00082 27.7783C4.58254 27.7783 1.00081 24.1966 1.00081 19.7783L1.00081 18.0005C1.00081 13.5823 4.58253 10.0005 9.00081 10.0005L11.6675 10.0005" stroke="white" stroke-width="1.5" stroke-linecap="square"/> <path d="M10.9998 19.167L16.9998 19.167" stroke="white" stroke-width="1.5"/> <path d="M8.99951 35.998L24.9995 0.998048" stroke="white"/> </svg>')}.jp-components__decorative-card__lines,.jp-components__decorative-card__lines:after,.jp-components__decorative-card__lines:before{background:#e9eff5;border-radius:6px;display:block;height:12px;position:relative;width:100%}.jp-components__decorative-card__lines:after,.jp-components__decorative-card__lines:before{content:"";top:calc(100% + 16px)}.jp-components__decorative-card__lines:after{top:calc(100% + 32px);width:75%}.jp-components__decorative-card--vertical{flex-direction:column}.jp-components__decorative-card--vertical .jp-components__decorative-card__content,.jp-components__decorative-card--vertical .jp-components__decorative-card__image{height:50%;width:100%}.jp-components__decorative-card--vertical .jp-components__decorative-card__lines{margin-left:auto;margin-right:auto;max-width:135px}.jp-components__decorative-card--vertical .jp-components__decorative-card__lines:after,.jp-components__decorative-card--vertical .jp-components__decorative-card__lines:before{margin-left:auto;margin-right:auto}.jp-connect__disconnect-survey-card{border:2px solid transparent;border-radius:4px;box-shadow:0 0 15px var(--jp-gray-off);margin-left:auto;margin-right:auto;max-width:100%;padding:1rem;position:relative;text-align:right;width:800px}.jp-connect__disconnect-survey-card--selected{background:var(--jp-gray-off);border-color:var(--jp-black)}.jp-connect__disconnect-survey-card:after{border-left:2px solid var(--jp-black);border-top:2px solid var(--jp-black);content:"";display:block;height:5px;left:1.5rem;position:absolute;top:50%;transform:translateY(-50%) rotate(-45deg);width:5px}.jp-connect__disconnect-survey-card:hover{cursor:pointer}.jp-connect__disconnect-survey-card:focus:not(.jp-disconnect-survey-card--selected),.jp-connect__disconnect-survey-card:hover:not(.jp-disconnect-survey-card--selected){border-color:var(--jp-black-80)}.jp-connect__disconnect-survey-card__answer{align-items:center;display:flex;font-weight:700;margin:0}input.jp-connect__disconnect-survey-card__input{-webkit-appearance:none;background-color:transparent;border:none;color:var(--jp-black-80);flex-grow:1;max-width:calc(100% - 40px);padding-left:40px}@media(min-width:1080px){.jp-connection__connect-screen-layout__left{width:70%}.jp-connection__connect-screen-required-plan{background:linear-gradient(270deg,#fff 70%,#f9f9f6 0);position:relative}}.jp-connection__connect-screen-required-plan__loading{display:none}@media(min-width:1080px){.jp-connection__connect-screen-required-plan__pricing-card{position:absolute;right:62%;top:14%}}.jp-connection__connect-screen-required-plan__pricing-card .components-button{align-items:center;background:var(--jp-black)!important;border-radius:var(--jp-border-radius);color:var(--jp-white)!important;font-size:18px;font-weight:500;height:auto;justify-content:center;margin:24px 0 32px;padding:14px 24px;width:100%}.jp-connection__connect-screen-required-plan__with-subscription{margin-top:38px}.jp-connection__connect-screen-required-plan__with-subscription .jp-action-button{display:inline}.jp-connection__connect-screen-required-plan__with-subscription .jp-action-button--button{background:inherit!important;color:var(--jp-black)!important;display:inline;font-size:var(--font-title-small);font:inherit;height:auto;line-height:20px;min-width:0;padding:0;text-decoration:underline;width:auto}.jp-connection__connect-screen-required-plan__with-subscription .jp-action-button--button:hover{background:inherit;text-decoration-thickness:var(--jp-underline-thickness)}.jp-connection__connect-screen-required-plan__with-subscription .jp-action-button--button:focus{background:inherit;box-shadow:none!important}.jp-connection__connect-screen-required-plan__with-subscription .jp-components-spinner__inner,.jp-connection__connect-screen-required-plan__with-subscription .jp-components-spinner__outer{border-left-color:var(--jp-black);border-top-color:var(--jp-black)}.jp-action-button--button{background:#000}.jp-action-button--button,.jp-action-button--button.components-button{border-radius:4px;display:block;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;font-size:14px;font-style:normal;font-weight:600;height:40px;line-height:18px;min-width:264px;text-align:center}.jp-action-button__error{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDIwYTggOCAwIDEgMCAwLTE2IDggOCAwIDAgMCAwIDE2WiIgc3Ryb2tlPSIjRDYzNjM5IiBzdHJva2Utd2lkdGg9IjEuNSIvPjxwYXRoIGQ9Ik0xMyA3aC0ydjZoMlY3Wk0xMyAxNWgtMnYyaDJ2LTJaIiBmaWxsPSIjRDYzNjM5Ii8+PC9zdmc+) no-repeat 100% 0;color:var(--jp-red)!important;line-height:25px!important;padding-right:25px}@keyframes rotate-spinner{to{transform:rotate(-1turn)}}.jp-components-spinner{align-items:center;display:flex}.jp-components-spinner__inner,.jp-components-spinner__outer{animation:3s linear infinite;animation-name:rotate-spinner;border:.1em solid transparent;border-radius:50%;box-sizing:border-box;margin:auto}.jp-components-spinner__outer{border-top-color:#fff}.jp-components-spinner__inner{border-left-color:#fff;border-top-color:#fff;height:100%;opacity:.4;width:100%}.jp-connection__connect-screen-layout{background:var(--jp-white);border-radius:4px;box-shadow:0 0 40px rgba(0,0,0,.08)}.jp-connection__connect-screen-layout__loading{display:none}.jp-connection__connect-screen-layout__left,.jp-connection__connect-screen-layout__right{box-sizing:border-box}.jp-connection__connect-screen-layout__left{padding:25px}@media(min-width:600px){.jp-connection__connect-screen-layout__left{padding:64px 96px}}.jp-connection__connect-screen-layout__left .jetpack-logo{margin-bottom:24px}.jp-connection__connect-screen-layout__left h2{color:var(--jp-black);font-size:36px;font-style:normal;font-weight:700;line-height:40px;margin-bottom:0;margin-top:32px}.jp-connection__connect-screen-layout__left h3{color:var(--jp-black);font-size:24px;font-style:normal;font-weight:500;line-height:32px;margin-bottom:0;margin-top:32px}.jp-connection__connect-screen-layout__left li,.jp-connection__connect-screen-layout__left p{font-size:16px;font-style:normal;font-weight:400;line-height:24px}.jp-connection__connect-screen-layout__left p{color:#101517;margin:16px 0}.jp-connection__connect-screen-layout__left a{color:var(--jp-black);font-size:var(--font-body);font:inherit;height:auto;padding:0;text-decoration:underline}.jp-connection__connect-screen-layout__left a:hover{color:var(--jp-black);text-decoration-thickness:var(--jp-underline-thickness)}.jp-connection__connect-screen-layout__left a:focus{box-shadow:none!important;color:var(--jp-black)}.jp-connection__connect-screen-layout__left ul{list-style-type:none;padding:0}.jp-connection__connect-screen-layout__left ul li{background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAMAAABg3Am1AAAANlBMVEVHcEwFnwUInggGnggGnggHnAcAnwUFnQcAnwcGnwkFnQgGnQgFnwcGnQYFnQcFnAcGnQkDnwdhiL0pAAAAEnRSTlMAMF//f2Aw7yBQ3+9gcIBgcED+HDbkAAAAZklEQVR4Ae3LNwICARDDQC0+cv7/Y8mwV9odSfWIcf/+VegnGkIvDaGXKvTTn/Gz+Uf5xTL0K1XotS7fs5H6GHvvaO8d7c3j7rdgHne/A/PYt/cO+R42oYdN6OEQetiFHo4A//6dAXqtBEkmtWutAAAAAElFTkSuQmCC) no-repeat;background-size:24px;color:var(--jp-black);margin-bottom:9px;padding-right:30px}.jp-connection__connect-screen-layout__right{padding:64px 0}.jp-connection__connect-screen-layout__right img{max-width:100%}.jp-connection__connect-screen-layout__two-columns{display:flex;flex-wrap:wrap}.jp-connection__connect-screen-layout__two-columns .jp-connection__connect-screen-layout__left{flex-basis:100%;flex-grow:1}@media(min-width:1080px){.jp-connection__connect-screen-layout__two-columns .jp-connection__connect-screen-layout__left{flex-basis:52%}}.jp-connection__connect-screen-layout__two-columns .jp-connection__connect-screen-layout__right{background:#f9f9f6;display:none;flex-basis:47%;flex-grow:1}@media(min-width:1080px){.jp-connection__connect-screen-layout__two-columns .jp-connection__connect-screen-layout__right{display:block}}:root{--font-title-large:36px;--font-title-small:24px;--font-body:16px;--font-label:12px;--jp-black:#000;--jp-black-80:#2c3338;--jp-white:#fff;--jp-white-off:#f9f9f6;--jp-gray:#dcdcde;--jp-gray-0:#f6f7f7;--jp-gray-20:#a7aaad;--jp-gray-40:#787c82;--jp-gray-50:#646970;--jp-gray-60:#50575e;--jp-gray-80:#8a2424;--jp-gray-off:#e2e2df;--jp-red-0:#f7ebec;--jp-red-50:#d63638;--jp-red-60:#b32d2e;--jp-red-80:#8a2424;--jp-red:#d63639;--jp-pink:#c9356e;--jp-green-0:#f0f2eb;--jp-green-5:#d0e6b8;--jp-green-10:#9dd977;--jp-green-20:#64ca43;--jp-green-30:#2fb41f;--jp-green-40:#069e08;--jp-green-50:#008710;--jp-green-60:#007117;--jp-green-70:#005b18;--jp-green-80:#004515;--jp-green-90:#003010;--jp-green-100:#001c09;--jp-green:#069e08;--jp-green-primary:var( --jp-green-40 );--jp-green-secondary:var( --jp-green-30 );--jp-border-radius:4px;--jp-menu-border-height:1px;--jp-underline-thickness:2px}*{box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;margin:0;min-height:100%;padding:0}.jp-wrap{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.jp-row{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.jp-row{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.jp-row{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.sm-col-span-1{grid-column-end:span 1}.sm-col-span-2{grid-column-end:span 2}.sm-col-span-3{grid-column-end:span 3}.sm-col-span-4{grid-column-end:span 4}@media(min-width:600px){.md-col-span-1{grid-column-end:span 1}.md-col-span-2{grid-column-end:span 2}.md-col-span-3{grid-column-end:span 3}.md-col-span-4{grid-column-end:span 4}.md-col-span-5{grid-column-end:span 5}.md-col-span-6{grid-column-end:span 6}.md-col-span-7{grid-column-end:span 7}.md-col-span-8{grid-column-end:span 8}}@media(min-width:960px){.lg-col-span-1{grid-column-end:span 1}.lg-col-span-2{grid-column-end:span 2}.lg-col-span-3{grid-column-end:span 3}.lg-col-span-4{grid-column-end:span 4}.lg-col-span-5{grid-column-end:span 5}.lg-col-span-6{grid-column-end:span 6}.lg-col-span-7{grid-column-end:span 7}.lg-col-span-8{grid-column-end:span 8}.lg-col-span-9{grid-column-end:span 9}.lg-col-span-10{grid-column-end:span 10}.lg-col-span-11{grid-column-end:span 11}.lg-col-span-12{grid-column-end:span 12}}@media(max-width:960px){.md-col-span-0{display:none}}@media(max-width:600px){.sm-col-span-0{display:none}}.jp-cut{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);margin:32px 0;padding:16px 24px 16px 64px;position:relative;text-decoration:none}.jp-cut,.jp-cut span{display:block}.jp-cut span:last-of-type{font-weight:600}.jp-cut:focus span:last-of-type,.jp-cut:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.jp-cut:focus:after,.jp-cut:hover:after{transform:translateY(-50%) translateX(-8px)}.jp-cut:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;left:24px;position:absolute;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.jp-components__pricing-card{background:var(--jp-white);border-radius:var(--jp-border-radius);box-shadow:0 10px 40px rgba(0,0,0,.08);max-width:384px;padding:24px 24px 32px;width:-moz-fit-content;width:fit-content}@media screen and (min-width:600px){.jp-components__pricing-card{padding:32px 32px 44px}}.jp-components__pricing-card__icon img{height:32px;width:32px}.jp-components__pricing-card__title{color:#101517;font-size:32px;line-height:38px;margin:16px 0 24px}.jp-components__pricing-card__pricing{display:flex;flex-wrap:wrap}.jp-components__pricing-card__price-after,.jp-components__pricing-card__price-before{display:inline-block;font-size:54px;font-weight:700;line-height:40px;margin-bottom:8px;padding:0 2px}.jp-components__pricing-card__price-before{color:var(--jp-gray-20);margin-left:16px;position:relative}.jp-components__pricing-card__price-strikethrough{background:var(--jp-pink);border-radius:1.5px;height:3px;position:absolute;right:0;top:20px;width:100%}.jp-components__pricing-card__price-after{color:var(--jp-black)}.jp-components__pricing-card__currency{font-size:var(--font-title-small);font-weight:400;line-height:20px;vertical-align:super}.jp-components__pricing-card__price-details{align-self:flex-end;color:var(--jp-gray-50);font-size:14px;font-weight:400;letter-spacing:-.02em;line-height:17px;margin-bottom:8px}.jp-components__pricing-card__price-decimal{font-size:var(--font-label);line-height:14px;vertical-align:top}.jp-components__pricing-card__button{align-items:center;background:var(--jp-black);border-radius:var(--jp-border-radius);color:var(--jp-white)!important;font-size:18px;height:auto;justify-content:center;margin:24px 0 32px;padding:14px 24px;width:100%}.jp-components__pricing-card__info{color:var(--jp-gray-60);font-size:var(--font-label);letter-spacing:-.02em;line-height:20px} \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/src/class-admin.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/src/class-admin.php
new file mode 100644
index 00000000..1a76c464
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/src/class-admin.php
@@ -0,0 +1,107 @@
+<?php
+/**
+ * The Connection UI Admin Area.
+ *
+ * @package automattic/jetpack-connection-ui
+ */
+
+namespace Automattic\Jetpack\ConnectionUI;
+
+use Automattic\Jetpack\Assets;
+
+/**
+ * The Connection UI Admin Area
+ */
+class Admin {
+
+ /**
+ * Construction.
+ */
+ public function __construct() {
+ if ( ! did_action( 'jetpack_on_connection_ui_init' ) ) {
+ add_action( 'admin_menu', array( $this, 'register_submenu_page' ), 1000 );
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
+
+ $this->maybe_init_idc();
+
+ /**
+ * Action called after initializing Connection UI Admin resources.
+ *
+ * @since 1.1.0
+ */
+ do_action( 'jetpack_on_connection_ui_init' );
+ }
+ }
+
+ /**
+ * Initialize the UI.
+ */
+ public static function init() {
+ new static();
+ }
+
+ /**
+ * Register's submenu.
+ */
+ public function register_submenu_page() {
+ add_submenu_page(
+ 'tools.php',
+ __( 'Connection Manager', 'jetpack-connection-ui' ),
+ __( 'Connection Manager', 'jetpack-connection-ui' ),
+ 'read',
+ 'wpcom-connection-manager',
+ array( $this, 'render_ui' ),
+ 4
+ );
+ }
+
+ /**
+ * Enqueue scripts!
+ *
+ * @param string $hook Page hook.
+ */
+ public function enqueue_scripts( $hook ) {
+ if ( strpos( $hook, 'tools_page_wpcom-connection-manager' ) === 0 ) {
+ Assets::register_script(
+ 'jetpack_connection_ui',
+ '../build/index.js',
+ __FILE__,
+ array(
+ 'in_footer' => true,
+ 'textdomain' => 'jetpack-connection-ui',
+ )
+ );
+ Assets::enqueue_script( 'jetpack_connection_ui' );
+ wp_add_inline_script( 'jetpack_connection_ui', $this->get_initial_state(), 'before' );
+ }
+ }
+
+ /**
+ * Render UI.
+ */
+ public function render_ui() {
+ ?>
+ <div id="jetpack-connection-ui-container"></div>
+ <?php
+ }
+
+ /**
+ * Return the rendered initial state JavaScript code.
+ *
+ * @return string
+ */
+ private function get_initial_state() {
+ return ( new Initial_State() )->render();
+ }
+
+ /**
+ * If this is the Connection Manager UI page, activate IDC.
+ */
+ private function maybe_init_idc() {
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( ! empty( $_GET['page'] ) && 'wpcom-connection-manager' === $_GET['page'] ) {
+ add_action( 'plugins_loaded', array( 'Automattic\\Jetpack\\Identity_Crisis', 'init' ) );
+ }
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/src/class-initial-state.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/src/class-initial-state.php
new file mode 100644
index 00000000..803b655f
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection-ui/src/class-initial-state.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * The React initial state.
+ *
+ * @package automattic/jetpack-connection-ui
+ */
+
+namespace Automattic\Jetpack\ConnectionUI;
+
+use Automattic\Jetpack\Identity_Crisis;
+
+/**
+ * The React initial state.
+ */
+class Initial_State {
+
+ const CONNECTION_MANAGER_URI = '/tools.php?page=wpcom-connection-manager';
+
+ /**
+ * Get the initial state data.
+ *
+ * @return array
+ */
+ private function get_data() {
+ return array(
+ 'API' => array(
+ 'WP_API_root' => esc_url_raw( rest_url() ),
+ 'WP_API_nonce' => wp_create_nonce( 'wp_rest' ),
+ 'registrationNonce' => wp_create_nonce( 'jetpack-registration-nonce' ),
+ ),
+ 'assets' => array(
+ 'buildUrl' => plugins_url( 'build/', __DIR__ ),
+ ),
+ 'IDC' => array(
+ 'hasIDC' => Identity_Crisis::has_identity_crisis(),
+ 'isSafeModeConfirmed' => Identity_Crisis::safe_mode_is_confirmed(),
+ 'canManageConnection' => current_user_can( 'jetpack_disconnect' ),
+ ),
+ );
+ }
+
+ /**
+ * Render the initial state into a JavaScript variable.
+ *
+ * @return string
+ */
+ public function render() {
+ add_action( 'jetpack_use_iframe_authorization_flow', '__return_true' );
+
+ return 'var CUI_INITIAL_STATE=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( $this->get_data() ) ) . '"));';
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/CHANGELOG.md
new file mode 100644
index 00000000..ffc65326
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/CHANGELOG.md
@@ -0,0 +1,571 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.34.0] - 2022-01-04
+### Added
+- Jetpack Connection: Added fallback for keeping `jetpack_connection_active_plugins` consistent on WPCOM when Sync is not present.
+
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+- Updated package textdomain from `jetpack` to `jetpack-connection`.
+
+## [1.33.0] - 2021-12-14
+### Changed
+- Jetpack Connection: handle package versions on site registration.
+
+## [1.32.0] - 2021-11-30
+### Added
+- Added a way to set the has_seen_wc_connection_modal option from the API
+- Provides an Initial State that can be used by JS packages
+
+### Changed
+- Updated package dependencies.
+
+## [1.31.0] - 2021-11-22
+### Added
+- Added plugin_slug parameter to the v4/register endpoint
+
+### Changed
+- Updated package dependencies
+
+## [1.30.13] - 2021-11-09
+### Fixed
+- Fix PHP 8.1 deprecation warning.
+
+## [1.30.12] - 2021-11-02
+### Added
+- Client: add IDC query args to remote requests
+
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.30.11] - 2021-10-26
+### Changed
+- Change the error code returned when a remoteRegister XMLRPC call is executed to the more helpful "already_registered" when the blog is already registered
+- Updated package dependencies.
+
+## [1.30.10] - 2021-10-19
+### Deprecated
+- General: remove numerous long-deprecated functions.
+
+### Fixed
+- Fix permission check for authorization_url endpoint.
+
+## [1.30.9] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.30.8] - 2021-10-12
+### Added
+- Add a new action to the Client::remote_request method, jetpack_received_remote_request_response
+
+### Changed
+- Updated package dependencies
+
+## [1.30.7] - 2021-10-04
+### Added
+- Sandbox Server: add the sandbox-server class to the connection package.
+
+## [1.30.6] - 2021-09-30
+### Changed
+- Moved the Package Tracker execution to the shutdown hook for performance improvement.
+
+## [1.30.5] - 2021-09-28
+### Changed
+- Package Version Tracker: send package versions to wpcom on the init hook instead of plugins_loaded
+- Updated package dependencies.
+
+### Fixed
+- Load WordPress's IXR classes on demand.
+
+## [1.30.4] - 2021-09-02
+### Fixed
+- Remove invalid user token before reconnect.
+
+## [1.30.3] - 2021-08-30
+### Added
+- Limit repeated failed attempts to update remote DNA package versions.
+
+### Changed
+- Make sure generated secrets have the required length
+- Remove tracked package versions when disconnecting the site.
+- Run composer update on test-php command instead of phpunit
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- update annotations versions
+
+## [1.30.2] - 2021-08-12
+### Added
+- Add package version tracking.
+
+## [1.30.1] - 2021-07-27
+### Added
+- Add a package version constant.
+
+### Changed
+- Move connection/data endpoint to Connection package.
+- Move site disconnection endpoint to Connection package.
+
+### Fixed
+- Fix `@covers` directives in tests.
+
+## [1.30.0] - 2021-07-13
+### Added
+- Added second parameter to Tokens::get_connected_users to allow any connected user to be returned.
+
+### Changed
+- Moved the get_connected_users logic back to the Manager class
+
+## [1.29.0] - 2021-06-29
+### Changed
+- Implement disconnect_site function.
+- Updated package dependencies.
+
+## [1.28.0] - 2021-06-15
+### Added
+- Added Urls class, migrated from Sync Functions.
+- Adding new REST endpoint /jetpack/v4/user-token that allows us to add/update user tokens remotely.
+- Add new 'connection/authorize_url' endpoint.
+- Adds information received from the server to the register_site REST response.
+- Enable site-level authentication (blog token) for REST API endpoints.
+- Move 'connection/owner' endpoint to Connection package.
+
+## [1.27.0] - 2021-05-25
+### Added
+- Add "isUserConnected" to the connection status data.
+- Connection: add the default value of JETPACK__WPCOM_JSON_API_BASE to the Connection Utils class.
+
+### Changed
+- Connection package independence: Move a Jetpack specfic connection routine out of the package and into the plugin
+- Package Independence: Add a filter to the remote_uri returned by remote_register XMLRPC method
+
+### Removed
+- Removed "user-less" jargon from code
+- Remove do_post_authorization routine and add a hook instead
+- Remove onboarding_token logic in the Remote provision XMLRPC method from the Connection package and add it to the Jetpack plugin
+
+### Fixed
+- Disconnection flow: disconnect users from WordPress.com before to delete data locally.
+
+## [1.26.0] - 2021-04-27
+### Added
+- Adds segmentation "from" parameter to the registration flow
+- Connection: moving the registration REST endpoint to the package.
+
+### Changed
+- Added "userless" parameter to the authorization URL.
+- Updated package dependencies.
+
+## [1.25.2] - 2021-04-13
+### Fixed
+- Connection: nonce cleanup safeguard against accidental option removal.
+
+## [1.25.1] - 2021-04-08
+### Fixed
+- Avoid determine_current_user going through infinite loops
+- Tokens: Fix token validation logic.
+
+## [1.25.0] - 2021-03-30
+### Added
+- Add new test for blog token health to support user-less sites
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- API Nonces: performance optimization and refactoring
+- Replace is_active usage towards gradually deprecating it.
+- Do not use is_active to determine the XMLRPC methods that should be registered
+- Make connected_plugins REST endpoint available for the Jetpack Debugger
+- Move Jetpack specific XMLRPC methods from the Connection package into the plugin
+- Update package dependencies.
+- User-less connection: Reconnect without asking the user to connect their WPCOM account
+
+### Deprecated
+- add deprecation notice and remove user-less check in is_active
+
+### Fixed
+- Only check offline mode when needed in map_meta_cap filters
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.24.0] - 2021-02-23
+
+- Refactor secrets and tokens
+- User-less connection: Restrict first connection to admins only
+- Connection: Prevent pointless calls to the test API
+- CI: Make tests more generic
+- Connection: extracting the Jetpack's authorization webhook
+- codesniffer: Update mediawiki-codesniffer dep to v35.0
+
+## [1.23.2] - 2021-02-08
+
+- Connection: Prevent pointless calls to the test API
+
+## [1.23.1] - 2021-01-28
+
+- Update dependencies to latest stable
+
+## [1.23.0] - 2021-01-26
+
+- Sync Concurrency / Race Conditions
+- Add mirror-repo information to all current composer packages
+- Mirroring: Fix auth, attempt 2
+- Monorepo: Reorganize all projects
+
+## [1.22.0] - 2021-01-05
+
+- Connection: setting valid connection owner.
+- userless testing mode: do not discard user tokens
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+- Connection: New methods and tests to the manager
+- Connection: refreshing connected plugin storage on multisite networks
+- Build: migrate from Travis to GH Actions
+- fix typos and add section to error handling docs
+
+## [1.21.1] - 2020-11-24
+
+- Version packages for release
+
+## [1.21.0] - 2020-11-24
+
+- Handle empty SERVER_PORT information on signature checks
+- Fix remaining phpcs warnings in most of requirelist
+- Add the no_user_testing mode
+- Clarified error message for non-writable options table.
+- Pass HTTP POST when making a wp.com api request
+
+## [1.20.0] - 2020-10-29
+
+- Connection: Plugin Tracking
+- Connection Package: Ensure a text/xml header is set
+- Updated PHPCS: Packages and Debugger
+
+## [1.19.2] - 2020-11-05
+
+- Pass HTTP POST when making a wp.com api request
+
+## [1.19.1] - 2020-10-29
+
+- Connection: Plugin Tracking
+
+## [1.19.0] - 2020-10-27
+
+- Connection Errors: Tracking additional error data
+- Replaced intval() with (int) as part of issue #17432.
+- Replaced strval() with type casting (string) as part of issue #17432.
+- Connection: Add wp.com function
+- Instagram oEmbed: Simplify
+- Client: Extract `validate_args_for_wpcom_json_api_request` helper.
+- Connection: add the constant filter hooks directly
+- Make XMLRPC methods available for blog token
+- API Nonces: Revert of the Runtime Cleanup.
+- PHPCS: Update directory structure, and modernize class usage
+- deprecates JETPACK_MASTER_USER and adds linter
+- API: Remove the constant `JETPACK_CLIENT__HTTPS`.
+
+## [1.18.4] - 2020-10-14
+
+- Connection: Add wp.com function
+
+## [1.18.3] - 2020-10-09
+
+- Connection: add the constant filter hooks directly
+
+## [1.18.2] - 2020-10-06
+
+- API Nonces: Revert of the Runtime Cleanup.
+
+## [1.18.1] - 2020-10-01
+
+- deprecates JETPACK_MASTER_USER and adds linter
+
+## [1.18.0] - 2020-09-29
+
+- API Nonces: Runtime cleanup
+- Packages: update list of files distributed in production packages
+- Connection: Add success message on completed partial reconnection
+- Jetpack_IXR_ClientMulticall: Fix sort_calls() producing undefined relative order between equal items
+- Connection: Initializing default constants for the REST authorization.
+- Trigger action jetpack_user_authorize after we save the token
+- Partial reconnect: Cleanup redundant actions
+- Signature Class: Adds support for nested query strings
+
+## [1.17.2] - 2020-09-16
+
+- Connection: Initializing default constants for the REST authorization.
+
+## [1.17.1] - 2020-09-09
+
+- Update dependencies to latest stable
+
+## [1.17.0] - 2020-08-26
+
+- Connection: move the rest authentication methods to the connection package
+- Connection: use heartbeat to send connected plugins info
+- clear xmlrpc error on site disconnect
+- add new features to debug helper
+- remove no longer needed check
+- disable xmlrpc errors out of JP dashboard
+- Connection REST API: Unit test for the `remote_authorize` request.
+- Simplify error notices for broken connections
+- Unit Tests: Fixing a failing `remote_authorize` test.
+- Reconnect Process: Partial Reconnect
+- Packages: Update filenames after #16810
+- CI: Try collect js coverage
+- Docker: Add package testing shortcut
+- Remove usages of removed HTTP_RAW_POST_DATA
+- adds tracking for deleted but active master users
+- New class to handle async XML-RPC requests
+- Connection Register: Add current user email to connection register request
+
+## [1.15.2] - 2020-08-10
+
+- Connection Register: Add current user email to connection register request
+
+## [1.15.1] - 2020-08-10
+
+- adds tracking for deleted but active master users
+
+## [1.15.0] - 2020-07-28
+
+- Core Compat: Site Environment
+- Reconnect Notice: In-place Reconnect
+- add messages for some common connection errors
+- REST API: Move some endpoints to `jetpack-connection` package.
+- REST API: Add permission callback to all that lack one.
+- Secondary user in-place connection flow
+- Tests: Update WorDBless location
+- Connection Errors: Clear when there's a successful request to /sites
+- Connection: add unit tests for is_registered
+- only display connection errors to allowed users
+
+## [1.14.2] - 2020-07-06
+
+- Connection Errors: Clear when there's a successful request to /sites
+
+## [1.14.1] - 2020-07-01
+
+- only display connection errors to allowed users
+
+## [1.14.0] - 2020-06-30
+
+- Jetpack_XMLRPC_Server: set up jsonAPI and testConnection endpoint when Jetpack is active
+- Connection Error Handling
+- Connection: Update XMLRPC overload
+- Connection: move connection custom caps to the Connection package
+- Connection package: Don't throw warnings if the database is corrupted somehow
+- Check blog token when computing is_registered
+- Connection: add the api_constant filter before setup_xmlrpc_handlers is called
+- Connection Package: Soft Disconnects
+- Remove usage of the Jetpack_Error class in the connection package
+- Connection: Fix issue where ABSPATH not included with register
+
+## [1.13.1] - 2020-06-01
+
+- Connection: Fix issue where ABSPATH not included with register
+
+## [1.13.0] - 2020-05-26
+
+- Store the list of active plugins that uses connection in an option
+- Connection: increase timeout on the token request
+- Connection Package: Handle disconnections gracefully
+
+## [1.12.0] - 2020-04-28
+
+- Correct inline documentation "Array" type
+- Use jp.com redirect in all links
+- Docs: Update the Connection Manager namespace, a minor typo
+- Connection: add a filter for setting Jetpack api constants
+
+## [1.11.0] - 2020-03-31
+
+- Update dependencies to latest stable
+
+## [1.10.0] - 2020-03-31
+
+- Connection: move post authorization work to package
+
+## [1.9.0] - 2020-02-25
+
+## [1.8.3] - 2020-02-14
+
+## [1.8.2] - 2020-02-12
+
+## [1.8.1] - 2020-02-12
+
+- Added a specific filter to enable iframe authorization API URL.
+- Added better defaults for the connection package.
+
+## [1.8.0] - 2020-01-27
+
+- Connection\Utils: Add a new function which provides the Jetpack API version
+- Connection\Manager: Use jetpack_master_user class constant
+
+## [1.7.2] - 2020-01-20
+
+- Move connection manager related logic to after plugins_loaded.
+
+## [1.7.1] - 2020-01-14
+
+- Packages: Various improvements for wp.com or self-contained consumers
+
+## [1.7.0] - 2019-11-26
+
+- Connection package: Add new methods to for disconnecting/delet…
+
+## [1.6.1] - 2019-11-25
+
+- Connection: Loose Comparison for Port Number in Signatures
+
+## [1.6.0] - 2019-11-19
+
+## [1.5.0] - 2019-11-15
+
+- Removed Jetpack references in the IXR client.
+- Connection: Move get_token() to Connection package
+
+## [1.4.0] - 2019-11-15
+
+- Add connection authentication URL.
+- Connection: Move the authorize() method to the connection package
+- Connection: Move update_user_token to the Connection package
+- Connection: Set the value of user_id in Manager::generate_secrets
+
+## [1.3.1] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+- Moved tracking code to the Tracking file.
+
+## [1.3.0] - 2019-11-08
+
+- Move fix_url_for_bad_hosts from Jetpack class to Connection pa…
+
+## [1.2.0] - 2019-11-07
+
+- Connection: call verify_secrets() in verify_action()
+
+## [1.1.0] - 2019-10-31
+
+- Adds filter capability to the api_url method.
+
+## [1.0.7] - 2019-10-28
+
+- Packages: Add gitattributes files to all packages that need th…
+- Replace parse_url with wp_parse_url
+
+## [1.0.6] - 2019-10-07
+
+- Update dependency phpcompatibility/phpcompatibility-wp to v2.1.0
+
+## [1.0.5] - 2019-09-26
+
+- XMLRPC: Fires clean_nonce action in all cases
+
+## [1.0.4] - 2019-09-24
+
+- Connection: Make sure port is an integer
+
+## [1.0.3] - 2019-09-23
+
+- Moves unreachable code to where it would be run.
+- Connection: Deprecate Manager interface
+
+## [1.0.2] - 2019-09-23
+
+- Connection: Fix all PHPCS errors in the connection package
+
+## [1.0.1] - 2019-09-20
+
+- Various: Remove pre-PHP 5.6 shims and fallbacks
+- Store "Assumed site creation date" in transient
+- Sync: Move sync_object XML-RPC method from connection to sync
+- Connection: Fix PHPCS errors in Jetpack_Signature
+- Docs: Unify usage of @package phpdoc tags
+- Janitorial: Remove the leading backslash from namespaces
+
+## 1.0.0 - 2019-09-14
+
+- Separate the connection library into its own package.
+
+[1.34.0]: https://github.com/Automattic/jetpack-connection/compare/v1.33.0...v1.34.0
+[1.33.0]: https://github.com/Automattic/jetpack-connection/compare/v1.32.0...v1.33.0
+[1.32.0]: https://github.com/Automattic/jetpack-connection/compare/v1.31.0...v1.32.0
+[1.31.0]: https://github.com/Automattic/jetpack-connection/compare/v1.30.13...v1.31.0
+[1.30.13]: https://github.com/Automattic/jetpack-connection/compare/v1.30.12...v1.30.13
+[1.30.12]: https://github.com/Automattic/jetpack-connection/compare/v1.30.11...v1.30.12
+[1.30.11]: https://github.com/Automattic/jetpack-connection/compare/v1.30.10...v1.30.11
+[1.30.10]: https://github.com/Automattic/jetpack-connection/compare/v1.30.9...v1.30.10
+[1.30.9]: https://github.com/Automattic/jetpack-connection/compare/v1.30.8...v1.30.9
+[1.30.8]: https://github.com/Automattic/jetpack-connection/compare/v1.30.7...v1.30.8
+[1.30.7]: https://github.com/Automattic/jetpack-connection/compare/v1.30.6...v1.30.7
+[1.30.6]: https://github.com/Automattic/jetpack-connection/compare/v1.30.5...v1.30.6
+[1.30.5]: https://github.com/Automattic/jetpack-connection/compare/v1.30.4...v1.30.5
+[1.30.4]: https://github.com/Automattic/jetpack-connection/compare/v1.30.3...v1.30.4
+[1.30.3]: https://github.com/Automattic/jetpack-connection/compare/v1.30.2...v1.30.3
+[1.30.2]: https://github.com/Automattic/jetpack-connection/compare/v1.30.1...v1.30.2
+[1.30.1]: https://github.com/Automattic/jetpack-connection/compare/v1.30.0...v1.30.1
+[1.30.0]: https://github.com/Automattic/jetpack-connection/compare/v1.29.0...v1.30.0
+[1.29.0]: https://github.com/Automattic/jetpack-connection/compare/v1.28.0...v1.29.0
+[1.28.0]: https://github.com/Automattic/jetpack-connection/compare/v1.27.0...v1.28.0
+[1.27.0]: https://github.com/Automattic/jetpack-connection/compare/v1.26.0...v1.27.0
+[1.26.0]: https://github.com/Automattic/jetpack-connection/compare/v1.25.2...v1.26.0
+[1.25.2]: https://github.com/Automattic/jetpack-connection/compare/v1.25.1...v1.25.2
+[1.25.1]: https://github.com/Automattic/jetpack-connection/compare/v1.25.0...v1.25.1
+[1.25.0]: https://github.com/Automattic/jetpack-connection/compare/v1.24.0...v1.25.0
+[1.24.0]: https://github.com/Automattic/jetpack-connection/compare/v1.23.2...v1.24.0
+[1.23.2]: https://github.com/Automattic/jetpack-connection/compare/v1.23.1...v1.23.2
+[1.23.1]: https://github.com/Automattic/jetpack-connection/compare/v1.23.0...v1.23.1
+[1.23.0]: https://github.com/Automattic/jetpack-connection/compare/v1.22.0...v1.23.0
+[1.22.0]: https://github.com/Automattic/jetpack-connection/compare/v1.21.1...v1.22.0
+[1.21.1]: https://github.com/Automattic/jetpack-connection/compare/v1.21.0...v1.21.1
+[1.21.0]: https://github.com/Automattic/jetpack-connection/compare/v1.20.0...v1.21.0
+[1.20.0]: https://github.com/Automattic/jetpack-connection/compare/v1.19.2...v1.20.0
+[1.19.2]: https://github.com/Automattic/jetpack-connection/compare/v1.19.1...v1.19.2
+[1.19.1]: https://github.com/Automattic/jetpack-connection/compare/v1.19.0...v1.19.1
+[1.19.0]: https://github.com/Automattic/jetpack-connection/compare/v1.18.4...v1.19.0
+[1.18.4]: https://github.com/Automattic/jetpack-connection/compare/v1.18.3...v1.18.4
+[1.18.3]: https://github.com/Automattic/jetpack-connection/compare/v1.18.2...v1.18.3
+[1.18.2]: https://github.com/Automattic/jetpack-connection/compare/v1.18.1...v1.18.2
+[1.18.1]: https://github.com/Automattic/jetpack-connection/compare/v1.18.0...v1.18.1
+[1.18.0]: https://github.com/Automattic/jetpack-connection/compare/v1.17.2...v1.18.0
+[1.17.2]: https://github.com/Automattic/jetpack-connection/compare/v1.17.1...v1.17.2
+[1.17.1]: https://github.com/Automattic/jetpack-connection/compare/v1.17.0...v1.17.1
+[1.17.0]: https://github.com/Automattic/jetpack-connection/compare/v1.15.2...v1.17.0
+[1.15.2]: https://github.com/Automattic/jetpack-connection/compare/v1.15.1...v1.15.2
+[1.15.1]: https://github.com/Automattic/jetpack-connection/compare/v1.15.0...v1.15.1
+[1.15.0]: https://github.com/Automattic/jetpack-connection/compare/v1.14.2...v1.15.0
+[1.14.2]: https://github.com/Automattic/jetpack-connection/compare/v1.14.1...v1.14.2
+[1.14.1]: https://github.com/Automattic/jetpack-connection/compare/v1.14.0...v1.14.1
+[1.14.0]: https://github.com/Automattic/jetpack-connection/compare/v1.13.1...v1.14.0
+[1.13.1]: https://github.com/Automattic/jetpack-connection/compare/v1.13.0...v1.13.1
+[1.13.0]: https://github.com/Automattic/jetpack-connection/compare/v1.12.0...v1.13.0
+[1.12.0]: https://github.com/Automattic/jetpack-connection/compare/v1.11.0...v1.12.0
+[1.11.0]: https://github.com/Automattic/jetpack-connection/compare/1.10.0...v1.11.0
+[1.10.0]: https://github.com/Automattic/jetpack-connection/compare/v1.9.0...1.10.0
+[1.9.0]: https://github.com/Automattic/jetpack-connection/compare/v1.8.3...v1.9.0
+[1.8.3]: https://github.com/Automattic/jetpack-connection/compare/v1.8.2...v1.8.3
+[1.8.2]: https://github.com/Automattic/jetpack-connection/compare/v1.8.1...v1.8.2
+[1.8.1]: https://github.com/Automattic/jetpack-connection/compare/v1.8.0...v1.8.1
+[1.8.0]: https://github.com/Automattic/jetpack-connection/compare/v1.7.2...v1.8.0
+[1.7.2]: https://github.com/Automattic/jetpack-connection/compare/v1.7.1...v1.7.2
+[1.7.1]: https://github.com/Automattic/jetpack-connection/compare/v1.7.0...v1.7.1
+[1.7.0]: https://github.com/Automattic/jetpack-connection/compare/v1.6.1...v1.7.0
+[1.6.1]: https://github.com/Automattic/jetpack-connection/compare/v1.6.0...v1.6.1
+[1.6.0]: https://github.com/Automattic/jetpack-connection/compare/v1.5.0...v1.6.0
+[1.5.0]: https://github.com/Automattic/jetpack-connection/compare/v1.4.0...v1.5.0
+[1.4.0]: https://github.com/Automattic/jetpack-connection/compare/v1.3.1...v1.4.0
+[1.3.1]: https://github.com/Automattic/jetpack-connection/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/Automattic/jetpack-connection/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-connection/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/Automattic/jetpack-connection/compare/v1.0.7...v1.1.0
+[1.0.7]: https://github.com/Automattic/jetpack-connection/compare/v1.0.6...v1.0.7
+[1.0.6]: https://github.com/Automattic/jetpack-connection/compare/v1.0.5...v1.0.6
+[1.0.5]: https://github.com/Automattic/jetpack-connection/compare/v1.0.4...v1.0.5
+[1.0.4]: https://github.com/Automattic/jetpack-connection/compare/v1.0.3...v1.0.4
+[1.0.3]: https://github.com/Automattic/jetpack-connection/compare/v1.0.2...v1.0.3
+[1.0.2]: https://github.com/Automattic/jetpack-connection/compare/v1.0.1...v1.0.2
+[1.0.1]: https://github.com/Automattic/jetpack-connection/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php
new file mode 100644
index 00000000..5349506c
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-ixr-client.php
@@ -0,0 +1,157 @@
+<?php
+/**
+ * IXR_Client
+ *
+ * @package automattic/jetpack-connection
+ *
+ * @since 1.7.0
+ * @since-jetpack 1.5
+ * @since-jetpack 7.7 Moved to the jetpack-connection package.
+ */
+
+use Automattic\Jetpack\Connection\Client;
+use Automattic\Jetpack\Connection\Manager;
+
+if ( ! class_exists( IXR_Client::class ) ) {
+ require_once ABSPATH . WPINC . '/class-IXR.php';
+}
+
+/**
+ * A Jetpack implementation of the WordPress core IXR client.
+ */
+class Jetpack_IXR_Client extends IXR_Client {
+ /**
+ * Jetpack args, used for the remote requests.
+ *
+ * @var array
+ */
+ public $jetpack_args = null;
+
+ /**
+ * Remote Response Headers.
+ *
+ * @var array
+ */
+ public $response_headers = null;
+
+ /**
+ * Constructor.
+ * Initialize a new Jetpack IXR client instance.
+ *
+ * @param array $args Jetpack args, used for the remote requests.
+ * @param string|bool $path Path to perform the reuqest to.
+ * @param int $port Port number.
+ * @param int $timeout The connection timeout, in seconds.
+ */
+ public function __construct( $args = array(), $path = false, $port = 80, $timeout = 15 ) {
+ $connection = new Manager();
+
+ $defaults = array(
+ 'url' => $connection->xmlrpc_api_url(),
+ 'user_id' => 0,
+ 'headers' => array(),
+ );
+
+ $args = wp_parse_args( $args, $defaults );
+ $args['headers'] = array_merge( array( 'Content-Type' => 'text/xml' ), (array) $args['headers'] );
+
+ $this->jetpack_args = $args;
+
+ $this->IXR_Client( $args['url'], $path, $port, $timeout );
+ }
+
+ /**
+ * Perform the IXR request.
+ *
+ * @param string[] ...$args IXR args.
+ *
+ * @return bool True if request succeeded, false otherwise.
+ */
+ public function query( ...$args ) {
+ $method = array_shift( $args );
+ $request = new IXR_Request( $method, $args );
+ $xml = trim( $request->getXml() );
+
+ $response = Client::remote_request( $this->jetpack_args, $xml );
+
+ // Store response headers.
+ $this->response_headers = wp_remote_retrieve_headers( $response );
+
+ if ( is_wp_error( $response ) ) {
+ $this->error = new IXR_Error( -10520, sprintf( 'Jetpack: [%s] %s', $response->get_error_code(), $response->get_error_message() ) );
+ return false;
+ }
+
+ if ( ! $response ) {
+ $this->error = new IXR_Error( -10520, 'Jetpack: Unknown Error' );
+ return false;
+ }
+
+ if ( 200 !== wp_remote_retrieve_response_code( $response ) ) {
+ $this->error = new IXR_Error( -32300, 'transport error - HTTP status code was not 200' );
+ return false;
+ }
+
+ $content = wp_remote_retrieve_body( $response );
+
+ // Now parse what we've got back.
+ $this->message = new IXR_Message( $content );
+ if ( ! $this->message->parse() ) {
+ // XML error.
+ $this->error = new IXR_Error( -32700, 'parse error. not well formed' );
+ return false;
+ }
+
+ // Is the message a fault?
+ if ( 'fault' === $this->message->messageType ) {
+ $this->error = new IXR_Error( $this->message->faultCode, $this->message->faultString );
+ return false;
+ }
+
+ // Message must be OK.
+ return true;
+ }
+
+ /**
+ * Retrieve the Jetpack error from the result of the last request.
+ *
+ * @param int $fault_code Fault code.
+ * @param string $fault_string Fault string.
+ * @return WP_Error Error object.
+ */
+ public function get_jetpack_error( $fault_code = null, $fault_string = null ) {
+ if ( is_null( $fault_code ) ) {
+ $fault_code = $this->error->code;
+ }
+
+ if ( is_null( $fault_string ) ) {
+ $fault_string = $this->error->message;
+ }
+
+ if ( preg_match( '#jetpack:\s+\[(\w+)\]\s*(.*)?$#i', $fault_string, $match ) ) {
+ $code = $match[1];
+ $message = $match[2];
+ $status = $fault_code;
+ return new \WP_Error( $code, $message, $status );
+ }
+
+ return new \WP_Error( "IXR_{$fault_code}", $fault_string );
+ }
+
+ /**
+ * Retrieve a response header if set.
+ *
+ * @param string $name header name.
+ * @return string|bool Header value if set, false if not set.
+ */
+ public function get_response_header( $name ) {
+ if ( isset( $this->response_headers[ $name ] ) ) {
+ return $this->response_headers[ $name ];
+ }
+ // case-insensitive header names: http://www.ietf.org/rfc/rfc2616.txt.
+ if ( isset( $this->response_headers[ strtolower( $name ) ] ) ) {
+ return $this->response_headers[ strtolower( $name ) ];
+ }
+ return false;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php
new file mode 100644
index 00000000..3c52f05d
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-ixr-clientmulticall.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * IXR_ClientMulticall
+ *
+ * @package automattic/jetpack-connection
+ *
+ * @since 1.7.0
+ * @since-jetpack 1.5
+ * @since-jetpack 7.7 Moved to the jetpack-connection package.
+ */
+
+/**
+ * A Jetpack implementation of the WordPress core IXR client, capable of multiple calls in a single request.
+ */
+class Jetpack_IXR_ClientMulticall extends Jetpack_IXR_Client {
+ /**
+ * Storage for the IXR calls.
+ *
+ * @var array
+ */
+ public $calls = array();
+
+ /**
+ * Add a IXR call to the client.
+ * First argument is the method name.
+ * The rest of the arguments are the params specified to the method.
+ *
+ * @param string[] ...$args IXR args.
+ */
+ public function addCall( ...$args ) {
+ $method_name = array_shift( $args );
+ $struct = array(
+ 'methodName' => $method_name,
+ 'params' => $args,
+ );
+ $this->calls[] = $struct;
+ }
+
+ /**
+ * Perform the IXR multicall request.
+ *
+ * @param string[] ...$args IXR args.
+ *
+ * @return bool True if request succeeded, false otherwise.
+ */
+ public function query( ...$args ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $this->calls = $this->sort_calls( $this->calls );
+
+ // Prepare multicall, then call the parent::query() method.
+ return parent::query( 'system.multicall', $this->calls );
+ }
+
+ /**
+ * Sort the IXR calls.
+ * Make sure syncs are always done first preserving relative order.
+ *
+ * @param array $calls Calls to sort.
+ * @return array Sorted calls.
+ */
+ public function sort_calls( $calls ) {
+ $sync_calls = array();
+ $other_calls = array();
+
+ foreach ( $calls as $call ) {
+ if ( 'jetpack.syncContent' === $call['methodName'] ) {
+ $sync_calls[] = $call;
+ } else {
+ $other_calls[] = $call;
+ }
+ }
+
+ return array_merge( $sync_calls, $other_calls );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-signature.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-signature.php
new file mode 100644
index 00000000..125aa20a
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-signature.php
@@ -0,0 +1,404 @@
+<?php
+/**
+ * The Jetpack Connection signature class file.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+use Automattic\Jetpack\Connection\Manager as Connection_Manager;
+
+/**
+ * The Jetpack Connection signature class that is used to sign requests.
+ */
+class Jetpack_Signature {
+ /**
+ * Token part of the access token.
+ *
+ * @access public
+ * @var string
+ */
+ public $token;
+
+ /**
+ * Access token secret.
+ *
+ * @access public
+ * @var string
+ */
+ public $secret;
+
+ /**
+ * The current request URL.
+ *
+ * @access public
+ * @var string
+ */
+ public $current_request_url;
+
+ /**
+ * Constructor.
+ *
+ * @param array $access_token Access token.
+ * @param int $time_diff Timezone difference (in seconds).
+ */
+ public function __construct( $access_token, $time_diff = 0 ) {
+ $secret = explode( '.', $access_token );
+ if ( 2 !== count( $secret ) ) {
+ return;
+ }
+
+ $this->token = $secret[0];
+ $this->secret = $secret[1];
+ $this->time_diff = $time_diff;
+ }
+
+ /**
+ * Sign the current request.
+ *
+ * @todo Implement a proper nonce verification.
+ *
+ * @param array $override Optional arguments to override the ones from the current request.
+ * @return string|WP_Error Request signature, or a WP_Error on failure.
+ */
+ public function sign_current_request( $override = array() ) {
+ if ( isset( $override['scheme'] ) ) {
+ $scheme = $override['scheme'];
+ if ( ! in_array( $scheme, array( 'http', 'https' ), true ) ) {
+ return new WP_Error( 'invalid_scheme', 'Invalid URL scheme' );
+ }
+ } else {
+ if ( is_ssl() ) {
+ $scheme = 'https';
+ } else {
+ $scheme = 'http';
+ }
+ }
+
+ $port = $this->get_current_request_port();
+
+ $this->current_request_url = "{$scheme}://{$_SERVER['HTTP_HOST']}:{$port}" . stripslashes( $_SERVER['REQUEST_URI'] );
+
+ if ( array_key_exists( 'body', $override ) && ! empty( $override['body'] ) ) {
+ $body = $override['body'];
+ } elseif ( 'POST' === strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
+ $body = isset( $GLOBALS['HTTP_RAW_POST_DATA'] ) ? $GLOBALS['HTTP_RAW_POST_DATA'] : null;
+
+ // Convert the $_POST to the body, if the body was empty. This is how arrays are hashed
+ // and encoded on the Jetpack side.
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ if ( empty( $body ) && is_array( $_POST ) && count( $_POST ) > 0 ) {
+ $body = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ }
+ }
+ } elseif ( 'PUT' === strtoupper( $_SERVER['REQUEST_METHOD'] ) ) {
+ // This is a little strange-looking, but there doesn't seem to be another way to get the PUT body.
+ $raw_put_data = file_get_contents( 'php://input' );
+ parse_str( $raw_put_data, $body );
+
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ $put_data = json_decode( $raw_put_data, true );
+ if ( is_array( $put_data ) && count( $put_data ) > 0 ) {
+ $body = $put_data;
+ }
+ }
+ } else {
+ $body = null;
+ }
+
+ if ( empty( $body ) ) {
+ $body = null;
+ }
+
+ $a = array();
+ foreach ( array( 'token', 'timestamp', 'nonce', 'body-hash' ) as $parameter ) {
+ if ( isset( $override[ $parameter ] ) ) {
+ $a[ $parameter ] = $override[ $parameter ];
+ } else {
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ $a[ $parameter ] = isset( $_GET[ $parameter ] ) ? stripslashes( $_GET[ $parameter ] ) : '';
+ }
+ }
+
+ $method = isset( $override['method'] ) ? $override['method'] : $_SERVER['REQUEST_METHOD'];
+ return $this->sign_request( $a['token'], $a['timestamp'], $a['nonce'], $a['body-hash'], $method, $this->current_request_url, $body, true );
+ }
+
+ /**
+ * Sign a specified request.
+ *
+ * @todo Having body_hash v. body-hash is annoying. Refactor to accept an array?
+ * @todo Use wp_json_encode() instead of json_encode()?
+ *
+ * @param string $token Request token.
+ * @param int $timestamp Timestamp of the request.
+ * @param string $nonce Request nonce.
+ * @param string $body_hash Request body hash.
+ * @param string $method Request method.
+ * @param string $url Request URL.
+ * @param mixed $body Request body.
+ * @param bool $verify_body_hash Whether to verify the body hash against the body.
+ * @return string|WP_Error Request signature, or a WP_Error on failure.
+ */
+ public function sign_request( $token = '', $timestamp = 0, $nonce = '', $body_hash = '', $method = '', $url = '', $body = null, $verify_body_hash = true ) {
+ if ( ! $this->secret ) {
+ return new WP_Error( 'invalid_secret', 'Invalid secret' );
+ }
+
+ if ( ! $this->token ) {
+ return new WP_Error( 'invalid_token', 'Invalid token' );
+ }
+
+ list( $token ) = explode( '.', $token );
+
+ $signature_details = compact( 'token', 'timestamp', 'nonce', 'body_hash', 'method', 'url' );
+
+ if ( 0 !== strpos( $token, "$this->token:" ) ) {
+ return new WP_Error( 'token_mismatch', 'Incorrect token', compact( 'signature_details' ) );
+ }
+
+ // If we got an array at this point, let's encode it, so we can see what it looks like as a string.
+ if ( is_array( $body ) ) {
+ if ( count( $body ) > 0 ) {
+ // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode
+ $body = json_encode( $body );
+
+ } else {
+ $body = '';
+ }
+ }
+
+ $required_parameters = array( 'token', 'timestamp', 'nonce', 'method', 'url' );
+ if ( ! is_null( $body ) ) {
+ $required_parameters[] = 'body_hash';
+ if ( ! is_string( $body ) ) {
+ return new WP_Error( 'invalid_body', 'Body is malformed.', compact( 'signature_details' ) );
+ }
+ }
+
+ foreach ( $required_parameters as $required ) {
+ if ( ! is_scalar( $$required ) ) {
+ return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', str_replace( '_', '-', $required ) ), compact( 'signature_details' ) );
+ }
+
+ if ( ! strlen( $$required ) ) {
+ return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is missing.', str_replace( '_', '-', $required ) ), compact( 'signature_details' ) );
+ }
+ }
+
+ if ( empty( $body ) ) {
+ if ( $body_hash ) {
+ return new WP_Error( 'invalid_body_hash', 'Invalid body hash for empty body.', compact( 'signature_details' ) );
+ }
+ } else {
+ $connection = new Connection_Manager();
+ if ( $verify_body_hash && $connection->sha1_base64( $body ) !== $body_hash ) {
+ return new WP_Error( 'invalid_body_hash', 'The body hash does not match.', compact( 'signature_details' ) );
+ }
+ }
+
+ $parsed = wp_parse_url( $url );
+ if ( ! isset( $parsed['host'] ) ) {
+ return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', 'url' ), compact( 'signature_details' ) );
+ }
+
+ if ( ! empty( $parsed['port'] ) ) {
+ $port = $parsed['port'];
+ } else {
+ if ( 'http' === $parsed['scheme'] ) {
+ $port = 80;
+ } elseif ( 'https' === $parsed['scheme'] ) {
+ $port = 443;
+ } else {
+ return new WP_Error( 'unknown_scheme_port', "The scheme's port is unknown", compact( 'signature_details' ) );
+ }
+ }
+
+ if ( ! ctype_digit( "$timestamp" ) || 10 < strlen( $timestamp ) ) { // If Jetpack is around in 275 years, you can blame mdawaffe for the bug.
+ return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', 'timestamp' ), compact( 'signature_details' ) );
+ }
+
+ $local_time = $timestamp - $this->time_diff;
+ if ( $local_time < time() - 600 || $local_time > time() + 300 ) {
+ return new WP_Error( 'invalid_signature', 'The timestamp is too old.', compact( 'signature_details' ) );
+ }
+
+ if ( 12 < strlen( $nonce ) || preg_match( '/[^a-zA-Z0-9]/', $nonce ) ) {
+ return new WP_Error( 'invalid_signature', sprintf( 'The required "%s" parameter is malformed.', 'nonce' ), compact( 'signature_details' ) );
+ }
+
+ $normalized_request_pieces = array(
+ $token,
+ $timestamp,
+ $nonce,
+ $body_hash,
+ strtoupper( $method ),
+ strtolower( $parsed['host'] ),
+ $port,
+ empty( $parsed['path'] ) ? '' : $parsed['path'],
+ // Normalized Query String.
+ );
+
+ $normalized_request_pieces = array_merge( $normalized_request_pieces, $this->normalized_query_parameters( isset( $parsed['query'] ) ? $parsed['query'] : '' ) );
+ $flat_normalized_request_pieces = array();
+ foreach ( $normalized_request_pieces as $piece ) {
+ if ( is_array( $piece ) ) {
+ foreach ( $piece as $subpiece ) {
+ $flat_normalized_request_pieces[] = $subpiece;
+ }
+ } else {
+ $flat_normalized_request_pieces[] = $piece;
+ }
+ }
+ $normalized_request_pieces = $flat_normalized_request_pieces;
+
+ $normalized_request_string = join( "\n", $normalized_request_pieces ) . "\n";
+
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
+ return base64_encode( hash_hmac( 'sha1', $normalized_request_string, $this->secret, true ) );
+ }
+
+ /**
+ * Retrieve and normalize the parameters from a query string.
+ *
+ * @param string $query_string Query string.
+ * @return array Normalized query string parameters.
+ */
+ public function normalized_query_parameters( $query_string ) {
+ parse_str( $query_string, $array );
+
+ unset( $array['signature'] );
+
+ $names = array_keys( $array );
+ $values = array_values( $array );
+
+ $names = array_map( array( $this, 'encode_3986' ), $names );
+ $values = array_map( array( $this, 'encode_3986' ), $values );
+
+ $pairs = array_map( array( $this, 'join_with_equal_sign' ), $names, $values );
+
+ sort( $pairs );
+
+ return $pairs;
+ }
+
+ /**
+ * Encodes a string or array of strings according to RFC 3986.
+ *
+ * @param string|array $string_or_array String or array to encode.
+ * @return string|array URL-encoded string or array.
+ */
+ public function encode_3986( $string_or_array ) {
+ if ( is_array( $string_or_array ) ) {
+ return array_map( array( $this, 'encode_3986' ), $string_or_array );
+ }
+
+ return rawurlencode( $string_or_array );
+ }
+
+ /**
+ * Concatenates a parameter name and a parameter value with an equals sign between them.
+ *
+ * @param string $name Parameter name.
+ * @param string|array $value Parameter value.
+ * @return string|array A string pair (e.g. `name=value`) or an array of string pairs.
+ */
+ public function join_with_equal_sign( $name, $value ) {
+ if ( is_array( $value ) ) {
+ return $this->join_array_with_equal_sign( $name, $value );
+ }
+ return "{$name}={$value}";
+ }
+
+ /**
+ * Helper function for join_with_equal_sign for handling arrayed values.
+ * Explicitly supports nested arrays.
+ *
+ * @param string $name Parameter name.
+ * @param array $value Parameter value.
+ * @return array An array of string pairs (e.g. `[ name[example]=value ]`).
+ */
+ private function join_array_with_equal_sign( $name, $value ) {
+ $result = array();
+ foreach ( $value as $value_key => $value_value ) {
+ $joined_value = $this->join_with_equal_sign( $name . '[' . $value_key . ']', $value_value );
+ if ( is_array( $joined_value ) ) {
+ foreach ( array_values( $joined_value ) as $individual_joined_value ) {
+ $result[] = $individual_joined_value;
+ }
+ } elseif ( is_string( $joined_value ) ) {
+ $result[] = $joined_value;
+ }
+ }
+
+ sort( $result );
+ return $result;
+ }
+
+ /**
+ * Gets the port that should be considered to sign the current request.
+ *
+ * It will analyze the current request, as well as some Jetpack constants, to return the string
+ * to be concatenated in the URL representing the port of the current request.
+ *
+ * @since 1.8.4
+ *
+ * @return string The port to be used in the signature
+ */
+ public function get_current_request_port() {
+ $host_port = isset( $_SERVER['HTTP_X_FORWARDED_PORT'] ) ? $this->sanitize_host_post( $_SERVER['HTTP_X_FORWARDED_PORT'] ) : '';
+ if ( '' === $host_port && isset( $_SERVER['SERVER_PORT'] ) ) {
+ $host_port = $this->sanitize_host_post( $_SERVER['SERVER_PORT'] );
+ }
+
+ /**
+ * Note: This port logic is tested in the Jetpack_Cxn_Tests->test__server_port_value() test.
+ * Please update the test if any changes are made in this logic.
+ */
+ if ( is_ssl() ) {
+ // 443: Standard Port
+ // 80: Assume we're behind a proxy without X-Forwarded-Port. Hardcoding "80" here means most sites
+ // with SSL termination proxies (self-served, Cloudflare, etc.) don't need to fiddle with
+ // the JETPACK_SIGNATURE__HTTPS_PORT constant. The code also implies we can't talk to a
+ // site at https://example.com:80/ (which would be a strange configuration).
+ // JETPACK_SIGNATURE__HTTPS_PORT: Set this constant in wp-config.php to the back end webserver's port
+ // if the site is behind a proxy running on port 443 without
+ // X-Forwarded-Port and the back end's port is *not* 80. It's better,
+ // though, to configure the proxy to send X-Forwarded-Port.
+ $https_port = defined( 'JETPACK_SIGNATURE__HTTPS_PORT' ) ? $this->sanitize_host_post( JETPACK_SIGNATURE__HTTPS_PORT ) : '443';
+ $port = in_array( $host_port, array( '443', '80', $https_port ), true ) ? '' : $host_port;
+ } else {
+ // 80: Standard Port
+ // JETPACK_SIGNATURE__HTTPS_PORT: Set this constant in wp-config.php to the back end webserver's port
+ // if the site is behind a proxy running on port 80 without
+ // X-Forwarded-Port. It's better, though, to configure the proxy to
+ // send X-Forwarded-Port.
+ $http_port = defined( 'JETPACK_SIGNATURE__HTTP_PORT' ) ? $this->sanitize_host_post( JETPACK_SIGNATURE__HTTP_PORT ) : '80';
+ $port = in_array( $host_port, array( '80', $http_port ), true ) ? '' : $host_port;
+ }
+ return (string) $port;
+ }
+
+ /**
+ * Sanitizes a variable checking if it's a valid port number, which can be an integer or a numeric string
+ *
+ * @since 1.8.4
+ *
+ * @param mixed $port_number Variable representing a port number.
+ * @return string Always a string with a valid port number, or an empty string if input is invalid
+ */
+ public function sanitize_host_post( $port_number ) {
+
+ if ( ! is_int( $port_number ) && ! is_string( $port_number ) ) {
+ return '';
+ }
+ if ( is_string( $port_number ) && ! ctype_digit( $port_number ) ) {
+ return '';
+ }
+
+ if ( 0 >= (int) $port_number || 65535 < $port_number ) {
+ return '';
+ }
+ return (string) $port_number;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-xmlrpc-server.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-xmlrpc-server.php
new file mode 100644
index 00000000..37acf8a4
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/legacy/class-jetpack-xmlrpc-server.php
@@ -0,0 +1,866 @@
+<?php
+/**
+ * Jetpack XMLRPC Server.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+use Automattic\Jetpack\Connection\Client;
+use Automattic\Jetpack\Connection\Manager as Connection_Manager;
+use Automattic\Jetpack\Connection\Secrets;
+use Automattic\Jetpack\Connection\Tokens;
+use Automattic\Jetpack\Connection\Urls;
+use Automattic\Jetpack\Roles;
+
+/**
+ * Just a sack of functions. Not actually an IXR_Server
+ */
+class Jetpack_XMLRPC_Server {
+ /**
+ * The current error object
+ *
+ * @var \WP_Error
+ */
+ public $error = null;
+
+ /**
+ * The current user
+ *
+ * @var \WP_User
+ */
+ public $user = null;
+
+ /**
+ * The connection manager object.
+ *
+ * @var Automattic\Jetpack\Connection\Manager
+ */
+ private $connection;
+
+ /**
+ * Creates a new XMLRPC server object.
+ */
+ public function __construct() {
+ $this->connection = new Connection_Manager();
+ }
+
+ /**
+ * Whitelist of the XML-RPC methods available to the Jetpack Server. If the
+ * user is not authenticated (->login()) then the methods are never added,
+ * so they will get a "does not exist" error.
+ *
+ * @param array $core_methods Core XMLRPC methods.
+ */
+ public function xmlrpc_methods( $core_methods ) {
+ $jetpack_methods = array(
+ 'jetpack.verifyAction' => array( $this, 'verify_action' ),
+ 'jetpack.idcUrlValidation' => array( $this, 'validate_urls_for_idc_mitigation' ),
+ 'jetpack.unlinkUser' => array( $this, 'unlink_user' ),
+ 'jetpack.testConnection' => array( $this, 'test_connection' ),
+ );
+
+ $jetpack_methods = array_merge( $jetpack_methods, $this->provision_xmlrpc_methods() );
+
+ $this->user = $this->login();
+
+ if ( $this->user ) {
+ $jetpack_methods = array_merge(
+ $jetpack_methods,
+ array(
+ 'jetpack.testAPIUserCode' => array( $this, 'test_api_user_code' ),
+ )
+ );
+
+ if ( isset( $core_methods['metaWeblog.editPost'] ) ) {
+ $jetpack_methods['metaWeblog.newMediaObject'] = $core_methods['metaWeblog.newMediaObject'];
+ $jetpack_methods['jetpack.updateAttachmentParent'] = array( $this, 'update_attachment_parent' );
+ }
+
+ /**
+ * Filters the XML-RPC methods available to Jetpack for authenticated users.
+ *
+ * @since 1.7.0
+ * @since-jetpack 1.1.0
+ *
+ * @param array $jetpack_methods XML-RPC methods available to the Jetpack Server.
+ * @param array $core_methods Available core XML-RPC methods.
+ * @param \WP_User $user Information about the user authenticated in the request.
+ */
+ $jetpack_methods = apply_filters( 'jetpack_xmlrpc_methods', $jetpack_methods, $core_methods, $this->user );
+ }
+
+ /**
+ * Filters the XML-RPC methods available to Jetpack for requests signed both with a blog token or a user token.
+ *
+ * @since 1.7.0
+ * @since 1.9.5 Introduced the $user parameter.
+ * @since-jetpack 3.0.0
+ *
+ * @param array $jetpack_methods XML-RPC methods available to the Jetpack Server.
+ * @param array $core_methods Available core XML-RPC methods.
+ * @param \WP_User|bool $user Information about the user authenticated in the request. False if authenticated with blog token.
+ */
+ return apply_filters( 'jetpack_xmlrpc_unauthenticated_methods', $jetpack_methods, $core_methods, $this->user );
+ }
+
+ /**
+ * Whitelist of the bootstrap XML-RPC methods
+ */
+ public function bootstrap_xmlrpc_methods() {
+ return array(
+ 'jetpack.remoteAuthorize' => array( $this, 'remote_authorize' ),
+ 'jetpack.remoteRegister' => array( $this, 'remote_register' ),
+ );
+ }
+
+ /**
+ * Additional method needed for authorization calls.
+ */
+ public function authorize_xmlrpc_methods() {
+ return array(
+ 'jetpack.remoteAuthorize' => array( $this, 'remote_authorize' ),
+ 'jetpack.remoteRegister' => array( $this, 'remote_already_registered' ),
+ );
+ }
+
+ /**
+ * Remote provisioning methods.
+ */
+ public function provision_xmlrpc_methods() {
+ return array(
+ 'jetpack.remoteRegister' => array( $this, 'remote_register' ),
+ 'jetpack.remoteProvision' => array( $this, 'remote_provision' ),
+ 'jetpack.remoteConnect' => array( $this, 'remote_connect' ),
+ 'jetpack.getUser' => array( $this, 'get_user' ),
+ );
+ }
+
+ /**
+ * Used to verify whether a local user exists and what role they have.
+ *
+ * @param int|string|array $request One of:
+ * int|string The local User's ID, username, or email address.
+ * array A request array containing:
+ * 0: int|string The local User's ID, username, or email address.
+ *
+ * @return array|\IXR_Error Information about the user, or error if no such user found:
+ * roles: string[] The user's rols.
+ * login: string The user's username.
+ * email_hash string[] The MD5 hash of the user's normalized email address.
+ * caps string[] The user's capabilities.
+ * allcaps string[] The user's granular capabilities, merged from role capabilities.
+ * token_key string The Token Key of the user's Jetpack token. Empty string if none.
+ */
+ public function get_user( $request ) {
+ $user_id = is_array( $request ) ? $request[0] : $request;
+
+ if ( ! $user_id ) {
+ return $this->error(
+ new \WP_Error(
+ 'invalid_user',
+ __( 'Invalid user identifier.', 'jetpack-connection' ),
+ 400
+ ),
+ 'get_user'
+ );
+ }
+
+ $user = $this->get_user_by_anything( $user_id );
+
+ if ( ! $user ) {
+ return $this->error(
+ new \WP_Error(
+ 'user_unknown',
+ __( 'User not found.', 'jetpack-connection' ),
+ 404
+ ),
+ 'get_user'
+ );
+ }
+
+ $user_token = ( new Tokens() )->get_access_token( $user->ID );
+
+ if ( $user_token ) {
+ list( $user_token_key ) = explode( '.', $user_token->secret );
+ if ( $user_token_key === $user_token->secret ) {
+ $user_token_key = '';
+ }
+ } else {
+ $user_token_key = '';
+ }
+
+ return array(
+ 'id' => $user->ID,
+ 'login' => $user->user_login,
+ 'email_hash' => md5( strtolower( trim( $user->user_email ) ) ),
+ 'roles' => $user->roles,
+ 'caps' => $user->caps,
+ 'allcaps' => $user->allcaps,
+ 'token_key' => $user_token_key,
+ );
+ }
+
+ /**
+ * Remote authorization XMLRPC method handler.
+ *
+ * @param array $request the request.
+ */
+ public function remote_authorize( $request ) {
+ $user = get_user_by( 'id', $request['state'] );
+
+ /**
+ * Happens on various request handling events in the Jetpack XMLRPC server.
+ * The action combines several types of events:
+ * - remote_authorize
+ * - remote_provision
+ * - get_user.
+ *
+ * @since 1.7.0
+ * @since-jetpack 8.0.0
+ *
+ * @param String $action the action name, i.e., 'remote_authorize'.
+ * @param String $stage the execution stage, can be 'begin', 'success', 'error', etc.
+ * @param array $parameters extra parameters from the event.
+ * @param WP_User $user the acting user.
+ */
+ do_action( 'jetpack_xmlrpc_server_event', 'remote_authorize', 'begin', array(), $user );
+
+ foreach ( array( 'secret', 'state', 'redirect_uri', 'code' ) as $required ) {
+ if ( ! isset( $request[ $required ] ) || empty( $request[ $required ] ) ) {
+ return $this->error(
+ new \WP_Error( 'missing_parameter', 'One or more parameters is missing from the request.', 400 ),
+ 'remote_authorize'
+ );
+ }
+ }
+
+ if ( ! $user ) {
+ return $this->error( new \WP_Error( 'user_unknown', 'User not found.', 404 ), 'remote_authorize' );
+ }
+
+ if ( $this->connection->has_connected_owner() && $this->connection->is_user_connected( $request['state'] ) ) {
+ return $this->error( new \WP_Error( 'already_connected', 'User already connected.', 400 ), 'remote_authorize' );
+ }
+
+ $verified = $this->verify_action( array( 'authorize', $request['secret'], $request['state'] ) );
+
+ if ( is_a( $verified, 'IXR_Error' ) ) {
+ return $this->error( $verified, 'remote_authorize' );
+ }
+
+ wp_set_current_user( $request['state'] );
+
+ $result = $this->connection->authorize( $request );
+
+ if ( is_wp_error( $result ) ) {
+ return $this->error( $result, 'remote_authorize' );
+ }
+
+ // This action is documented in class.jetpack-xmlrpc-server.php.
+ do_action( 'jetpack_xmlrpc_server_event', 'remote_authorize', 'success' );
+
+ return array(
+ 'result' => $result,
+ );
+ }
+
+ /**
+ * This XML-RPC method is called from the /jpphp/provision endpoint on WPCOM in order to
+ * register this site so that a plan can be provisioned.
+ *
+ * @param array $request An array containing at minimum nonce and local_user keys.
+ *
+ * @return \WP_Error|array
+ */
+ public function remote_register( $request ) {
+ // This action is documented in class.jetpack-xmlrpc-server.php.
+ do_action( 'jetpack_xmlrpc_server_event', 'remote_register', 'begin', array() );
+
+ $user = $this->fetch_and_verify_local_user( $request );
+
+ if ( ! $user ) {
+ return $this->error(
+ new WP_Error( 'input_error', __( 'Valid user is required', 'jetpack-connection' ), 400 ),
+ 'remote_register'
+ );
+ }
+
+ if ( is_wp_error( $user ) || is_a( $user, 'IXR_Error' ) ) {
+ return $this->error( $user, 'remote_register' );
+ }
+
+ if ( empty( $request['nonce'] ) ) {
+ return $this->error(
+ new \WP_Error(
+ 'nonce_missing',
+ __( 'The required "nonce" parameter is missing.', 'jetpack-connection' ),
+ 400
+ ),
+ 'remote_register'
+ );
+ }
+
+ $nonce = sanitize_text_field( $request['nonce'] );
+ unset( $request['nonce'] );
+
+ $api_url = $this->connection->api_url( 'partner_provision_nonce_check' );
+ $response = Client::_wp_remote_request(
+ esc_url_raw( add_query_arg( 'nonce', $nonce, $api_url ) ),
+ array( 'method' => 'GET' ),
+ true
+ );
+
+ if (
+ 200 !== wp_remote_retrieve_response_code( $response ) ||
+ 'OK' !== trim( wp_remote_retrieve_body( $response ) )
+ ) {
+ return $this->error(
+ new \WP_Error(
+ 'invalid_nonce',
+ __( 'There was an issue validating this request.', 'jetpack-connection' ),
+ 400
+ ),
+ 'remote_register'
+ );
+ }
+
+ if ( ! Jetpack_Options::get_option( 'id' ) || ! ( new Tokens() )->get_access_token() || ! empty( $request['force'] ) ) {
+ wp_set_current_user( $user->ID );
+
+ // This code mostly copied from Jetpack::admin_page_load.
+ if ( isset( $request['from'] ) ) {
+ $this->connection->add_register_request_param( 'from', (string) $request['from'] );
+ }
+ $registered = $this->connection->try_registration();
+ if ( is_wp_error( $registered ) ) {
+ return $this->error( $registered, 'remote_register' );
+ } elseif ( ! $registered ) {
+ return $this->error(
+ new \WP_Error(
+ 'registration_error',
+ __( 'There was an unspecified error registering the site', 'jetpack-connection' ),
+ 400
+ ),
+ 'remote_register'
+ );
+ }
+ }
+
+ // This action is documented in class.jetpack-xmlrpc-server.php.
+ do_action( 'jetpack_xmlrpc_server_event', 'remote_register', 'success' );
+
+ return array(
+ 'client_id' => Jetpack_Options::get_option( 'id' ),
+ );
+ }
+
+ /**
+ * This is a substitute for remote_register() when the blog is already registered which returns an error code
+ * signifying that state.
+ * This is an unauthorized call and we should not be responding with any data other than the error code.
+ *
+ * @return \IXR_Error
+ */
+ public function remote_already_registered() {
+ return $this->error(
+ new \WP_Error( 'already_registered', __( 'Blog is already registered', 'jetpack-connection' ), 400 ),
+ 'remote_register'
+ );
+ }
+
+ /**
+ * This XML-RPC method is called from the /jpphp/provision endpoint on WPCOM in order to
+ * register this site so that a plan can be provisioned.
+ *
+ * @param array $request An array containing at minimum a nonce key and a local_username key.
+ *
+ * @return \WP_Error|array
+ */
+ public function remote_provision( $request ) {
+ $user = $this->fetch_and_verify_local_user( $request );
+
+ if ( ! $user ) {
+ return $this->error(
+ new WP_Error( 'input_error', __( 'Valid user is required', 'jetpack-connection' ), 400 ),
+ 'remote_provision'
+ );
+ }
+
+ if ( is_wp_error( $user ) || is_a( $user, 'IXR_Error' ) ) {
+ return $this->error( $user, 'remote_provision' );
+ }
+
+ $site_icon = get_site_icon_url();
+
+ /**
+ * Filters the Redirect URI returned by the remote_register XMLRPC method
+ *
+ * @param string $redirect_uri The Redirect URI
+ *
+ * @since 1.9.7
+ */
+ $redirect_uri = apply_filters( 'jetpack_xmlrpc_remote_register_redirect_uri', admin_url() );
+
+ // Generate secrets.
+ $roles = new Roles();
+ $role = $roles->translate_user_to_role( $user );
+ $secrets = ( new Secrets() )->generate( 'authorize', $user->ID );
+
+ $response = array(
+ 'jp_version' => JETPACK__VERSION,
+ 'redirect_uri' => $redirect_uri,
+ 'user_id' => $user->ID,
+ 'user_email' => $user->user_email,
+ 'user_login' => $user->user_login,
+ 'scope' => $this->connection->sign_role( $role, $user->ID ),
+ 'secret' => $secrets['secret_1'],
+ 'is_active' => $this->connection->has_connected_owner(),
+ );
+
+ if ( $site_icon ) {
+ $response['site_icon'] = $site_icon;
+ }
+
+ /**
+ * Filters the response of the remote_provision XMLRPC method
+ *
+ * @param array $response The response.
+ * @param array $request An array containing at minimum a nonce key and a local_username key.
+ * @param \WP_User $user The local authenticated user.
+ *
+ * @since 1.9.7
+ */
+ $response = apply_filters( 'jetpack_remote_xmlrpc_provision_response', $response, $request, $user );
+
+ return $response;
+ }
+
+ /**
+ * Given an array containing a local user identifier and a nonce, will attempt to fetch and set
+ * an access token for the given user.
+ *
+ * @param array $request An array containing local_user and nonce keys at minimum.
+ * @param \IXR_Client $ixr_client The client object, optional.
+ * @return mixed
+ */
+ public function remote_connect( $request, $ixr_client = false ) {
+ if ( $this->connection->has_connected_owner() ) {
+ return $this->error(
+ new WP_Error(
+ 'already_connected',
+ __( 'Jetpack is already connected.', 'jetpack-connection' ),
+ 400
+ ),
+ 'remote_connect'
+ );
+ }
+
+ $user = $this->fetch_and_verify_local_user( $request );
+
+ if ( ! $user || is_wp_error( $user ) || is_a( $user, 'IXR_Error' ) ) {
+ return $this->error(
+ new WP_Error(
+ 'input_error',
+ __( 'Valid user is required.', 'jetpack-connection' ),
+ 400
+ ),
+ 'remote_connect'
+ );
+ }
+
+ if ( empty( $request['nonce'] ) ) {
+ return $this->error(
+ new WP_Error(
+ 'input_error',
+ __( 'A non-empty nonce must be supplied.', 'jetpack-connection' ),
+ 400
+ ),
+ 'remote_connect'
+ );
+ }
+
+ if ( ! $ixr_client ) {
+ $ixr_client = new Jetpack_IXR_Client();
+ }
+ // TODO: move this query into the Tokens class?
+ $ixr_client->query(
+ 'jetpack.getUserAccessToken',
+ array(
+ 'nonce' => sanitize_text_field( $request['nonce'] ),
+ 'external_user_id' => $user->ID,
+ )
+ );
+
+ $token = $ixr_client->isError() ? false : $ixr_client->getResponse();
+ if ( empty( $token ) ) {
+ return $this->error(
+ new WP_Error(
+ 'token_fetch_failed',
+ __( 'Failed to fetch user token from WordPress.com.', 'jetpack-connection' ),
+ 400
+ ),
+ 'remote_connect'
+ );
+ }
+ $token = sanitize_text_field( $token );
+
+ ( new Tokens() )->update_user_token( $user->ID, sprintf( '%s.%d', $token, $user->ID ), true );
+
+ /**
+ * Hook fired at the end of the jetpack.remoteConnect XML-RPC callback
+ *
+ * @since 1.9.7
+ */
+ do_action( 'jetpack_remote_connect_end' );
+
+ return $this->connection->has_connected_owner();
+ }
+
+ /**
+ * Getter for the local user to act as.
+ *
+ * @param array $request the current request data.
+ */
+ private function fetch_and_verify_local_user( $request ) {
+ if ( empty( $request['local_user'] ) ) {
+ return $this->error(
+ new \WP_Error(
+ 'local_user_missing',
+ __( 'The required "local_user" parameter is missing.', 'jetpack-connection' ),
+ 400
+ ),
+ 'remote_provision'
+ );
+ }
+
+ // Local user is used to look up by login, email or ID.
+ $local_user_info = $request['local_user'];
+
+ return $this->get_user_by_anything( $local_user_info );
+ }
+
+ /**
+ * Gets the user object by its data.
+ *
+ * @param string $user_id can be any identifying user data.
+ */
+ private function get_user_by_anything( $user_id ) {
+ $user = get_user_by( 'login', $user_id );
+
+ if ( ! $user ) {
+ $user = get_user_by( 'email', $user_id );
+ }
+
+ if ( ! $user ) {
+ $user = get_user_by( 'ID', $user_id );
+ }
+
+ return $user;
+ }
+
+ /**
+ * Possible error_codes:
+ *
+ * - verify_secret_1_missing
+ * - verify_secret_1_malformed
+ * - verify_secrets_missing: verification secrets are not found in database
+ * - verify_secrets_incomplete: verification secrets are only partially found in database
+ * - verify_secrets_expired: verification secrets have expired
+ * - verify_secrets_mismatch: stored secret_1 does not match secret_1 sent by Jetpack.WordPress.com
+ * - state_missing: required parameter of state not found
+ * - state_malformed: state is not a digit
+ * - invalid_state: state in request does not match the stored state
+ *
+ * The 'authorize' and 'register' actions have additional error codes
+ *
+ * state_missing: a state ( user id ) was not supplied
+ * state_malformed: state is not the correct data type
+ * invalid_state: supplied state does not match the stored state
+ *
+ * @param array $params action An array of 3 parameters:
+ * [0]: string action. Possible values are `authorize`, `publicize` and `register`.
+ * [1]: string secret_1.
+ * [2]: int state.
+ * @return \IXR_Error|string IXR_Error on failure, secret_2 on success.
+ */
+ public function verify_action( $params ) {
+ $action = isset( $params[0] ) ? $params[0] : '';
+ $verify_secret = isset( $params[1] ) ? $params[1] : '';
+ $state = isset( $params[2] ) ? $params[2] : '';
+
+ $result = ( new Secrets() )->verify( $action, $verify_secret, $state );
+
+ if ( is_wp_error( $result ) ) {
+ return $this->error( $result );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Wrapper for wp_authenticate( $username, $password );
+ *
+ * @return \WP_User|bool
+ */
+ public function login() {
+ $this->connection->require_jetpack_authentication();
+ $user = wp_authenticate( 'username', 'password' );
+ if ( is_wp_error( $user ) ) {
+ if ( 'authentication_failed' === $user->get_error_code() ) { // Generic error could mean most anything.
+ $this->error = new \WP_Error( 'invalid_request', 'Invalid Request', 403 );
+ } else {
+ $this->error = $user;
+ }
+ return false;
+ } elseif ( ! $user ) { // Shouldn't happen.
+ $this->error = new \WP_Error( 'invalid_request', 'Invalid Request', 403 );
+ return false;
+ }
+
+ wp_set_current_user( $user->ID );
+
+ return $user;
+ }
+
+ /**
+ * Returns the current error as an \IXR_Error
+ *
+ * @param \WP_Error|\IXR_Error $error The error object, optional.
+ * @param string $event_name The event name.
+ * @param \WP_User $user The user object.
+ * @return bool|\IXR_Error
+ */
+ public function error( $error = null, $event_name = null, $user = null ) {
+ if ( null !== $event_name ) {
+ // This action is documented in class.jetpack-xmlrpc-server.php.
+ do_action( 'jetpack_xmlrpc_server_event', $event_name, 'fail', $error, $user );
+ }
+
+ if ( ! is_null( $error ) ) {
+ $this->error = $error;
+ }
+
+ if ( is_wp_error( $this->error ) ) {
+ $code = $this->error->get_error_data();
+ if ( ! $code ) {
+ $code = -10520;
+ }
+ $message = sprintf( 'Jetpack: [%s] %s', $this->error->get_error_code(), $this->error->get_error_message() );
+ if ( ! class_exists( \IXR_Error::class ) ) {
+ require_once ABSPATH . WPINC . '/class-IXR.php';
+ }
+ return new \IXR_Error( $code, $message );
+ } elseif ( is_a( $this->error, 'IXR_Error' ) ) {
+ return $this->error;
+ }
+
+ return false;
+ }
+
+ /* API Methods */
+
+ /**
+ * Just authenticates with the given Jetpack credentials.
+ *
+ * @return string A success string. The Jetpack plugin filters it and make it return the Jetpack plugin version.
+ */
+ public function test_connection() {
+ /**
+ * Filters the successful response of the XMLRPC test_connection method
+ *
+ * @param string $response The response string.
+ */
+ return apply_filters( 'jetpack_xmlrpc_test_connection_response', 'success' );
+ }
+
+ /**
+ * Test the API user code.
+ *
+ * @param array $args arguments identifying the test site.
+ */
+ public function test_api_user_code( $args ) {
+ $client_id = (int) $args[0];
+ $user_id = (int) $args[1];
+ $nonce = (string) $args[2];
+ $verify = (string) $args[3];
+
+ if ( ! $client_id || ! $user_id || ! strlen( $nonce ) || 32 !== strlen( $verify ) ) {
+ return false;
+ }
+
+ $user = get_user_by( 'id', $user_id );
+ if ( ! $user || is_wp_error( $user ) ) {
+ return false;
+ }
+
+ /* phpcs:ignore
+ debugging
+ error_log( "CLIENT: $client_id" );
+ error_log( "USER: $user_id" );
+ error_log( "NONCE: $nonce" );
+ error_log( "VERIFY: $verify" );
+ */
+
+ $jetpack_token = ( new Tokens() )->get_access_token( $user_id );
+
+ $api_user_code = get_user_meta( $user_id, "jetpack_json_api_$client_id", true );
+ if ( ! $api_user_code ) {
+ return false;
+ }
+
+ $hmac = hash_hmac(
+ 'md5',
+ json_encode( // phpcs:ignore WordPress.WP.AlternativeFunctions.json_encode_json_encode
+ (object) array(
+ 'client_id' => (int) $client_id,
+ 'user_id' => (int) $user_id,
+ 'nonce' => (string) $nonce,
+ 'code' => (string) $api_user_code,
+ )
+ ),
+ $jetpack_token->secret
+ );
+
+ if ( ! hash_equals( $hmac, $verify ) ) {
+ return false;
+ }
+
+ return $user_id;
+ }
+
+ /**
+ * Unlink a user from WordPress.com
+ *
+ * When the request is done without any parameter, this XMLRPC callback gets an empty array as input.
+ *
+ * If $user_id is not provided, it will try to disconnect the current logged in user. This will fail if called by the Master User.
+ *
+ * If $user_id is is provided, it will try to disconnect the informed user, even if it's the Master User.
+ *
+ * @param mixed $user_id The user ID to disconnect from this site.
+ */
+ public function unlink_user( $user_id = array() ) {
+ $user_id = (int) $user_id;
+ if ( $user_id < 1 ) {
+ $user_id = null;
+ }
+ /**
+ * Fired when we want to log an event to the Jetpack event log.
+ *
+ * @since 1.7.0
+ * @since-jetpack 7.7.0
+ *
+ * @param string $code Unique name for the event.
+ * @param string $data Optional data about the event.
+ */
+ do_action( 'jetpack_event_log', 'unlink' );
+ return $this->connection->disconnect_user(
+ $user_id,
+ (bool) $user_id
+ );
+ }
+
+ /**
+ * Returns the home URL and site URL for the current site which can be used on the WPCOM side for
+ * IDC mitigation to decide whether sync should be allowed if the home and siteurl values differ between WPCOM
+ * and the remote Jetpack site.
+ *
+ * @return array
+ */
+ public function validate_urls_for_idc_mitigation() {
+ return array(
+ 'home' => Urls::home_url(),
+ 'siteurl' => Urls::site_url(),
+ );
+ }
+
+ /**
+ * Updates the attachment parent object.
+ *
+ * @param array $args attachment and parent identifiers.
+ */
+ public function update_attachment_parent( $args ) {
+ $attachment_id = (int) $args[0];
+ $parent_id = (int) $args[1];
+
+ return wp_update_post(
+ array(
+ 'ID' => $attachment_id,
+ 'post_parent' => $parent_id,
+ )
+ );
+ }
+
+ /**
+ * Deprecated: This method is no longer part of the Connection package and now lives on the Jetpack plugin.
+ *
+ * Disconnect this blog from the connected wordpress.com account
+ *
+ * @deprecated since 1.25.0
+ * @see Jetpack_XMLRPC_Methods::disconnect_blog() in the Jetpack plugin
+ *
+ * @return boolean
+ */
+ public function disconnect_blog() {
+ _deprecated_function( __METHOD__, '1.25.0', 'Jetpack_XMLRPC_Methods::disconnect_blog()' );
+ if ( class_exists( 'Jetpack_XMLRPC_Methods' ) ) {
+ return Jetpack_XMLRPC_Methods::disconnect_blog();
+ }
+ return false;
+ }
+
+ /**
+ * Deprecated: This method is no longer part of the Connection package and now lives on the Jetpack plugin.
+ *
+ * Returns what features are available. Uses the slug of the module files.
+ *
+ * @deprecated since 1.25.0
+ * @see Jetpack_XMLRPC_Methods::features_available() in the Jetpack plugin
+ *
+ * @return array
+ */
+ public function features_available() {
+ _deprecated_function( __METHOD__, '1.25.0', 'Jetpack_XMLRPC_Methods::features_available()' );
+ if ( class_exists( 'Jetpack_XMLRPC_Methods' ) ) {
+ return Jetpack_XMLRPC_Methods::features_available();
+ }
+ return array();
+ }
+
+ /**
+ * Deprecated: This method is no longer part of the Connection package and now lives on the Jetpack plugin.
+ *
+ * Returns what features are enabled. Uses the slug of the modules files.
+ *
+ * @deprecated since 1.25.0
+ * @see Jetpack_XMLRPC_Methods::features_enabled() in the Jetpack plugin
+ *
+ * @return array
+ */
+ public function features_enabled() {
+ _deprecated_function( __METHOD__, '1.25.0', 'Jetpack_XMLRPC_Methods::features_enabled()' );
+ if ( class_exists( 'Jetpack_XMLRPC_Methods' ) ) {
+ return Jetpack_XMLRPC_Methods::features_enabled();
+ }
+ return array();
+ }
+
+ /**
+ * Deprecated: This method is no longer part of the Connection package and now lives on the Jetpack plugin.
+ *
+ * Serve a JSON API request.
+ *
+ * @deprecated since 1.25.0
+ * @see Jetpack_XMLRPC_Methods::json_api() in the Jetpack plugin
+ *
+ * @param array $args request arguments.
+ */
+ public function json_api( $args = array() ) {
+ _deprecated_function( __METHOD__, '1.25.0', 'Jetpack_XMLRPC_Methods::json_api()' );
+ if ( class_exists( 'Jetpack_XMLRPC_Methods' ) ) {
+ return Jetpack_XMLRPC_Methods::json_api( $args );
+ }
+ return array();
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-client.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-client.php
new file mode 100644
index 00000000..fea9e9c7
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-client.php
@@ -0,0 +1,488 @@
+<?php
+/**
+ * The Connection Client class file.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+use Automattic\Jetpack\Constants;
+
+/**
+ * The Client class that is used to connect to WordPress.com Jetpack API.
+ */
+class Client {
+ const WPCOM_JSON_API_VERSION = '1.1';
+
+ /**
+ * Makes an authorized remote request using Jetpack_Signature
+ *
+ * @param array $args the arguments for the remote request.
+ * @param array|String $body the request body.
+ * @return array|WP_Error WP HTTP response on success
+ */
+ public static function remote_request( $args, $body = null ) {
+ if ( isset( $args['url'] ) ) {
+ /**
+ * Filters the remote request url.
+ *
+ * @since 1.30.12
+ *
+ * @param string The remote request url.
+ */
+ $args['url'] = apply_filters( 'jetpack_remote_request_url', $args['url'] );
+ }
+
+ $result = self::build_signed_request( $args, $body );
+ if ( ! $result || is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ $response = self::_wp_remote_request( $result['url'], $result['request'] );
+
+ /**
+ * Fired when the remote request response has been received.
+ *
+ * @since 1.30.8
+ *
+ * @param array|WP_Error The HTTP response.
+ */
+ do_action( 'jetpack_received_remote_request_response', $response );
+
+ return $response;
+ }
+
+ /**
+ * Adds authorization signature to a remote request using Jetpack_Signature
+ *
+ * @param array $args the arguments for the remote request.
+ * @param array|String $body the request body.
+ * @return WP_Error|array {
+ * An array containing URL and request items.
+ *
+ * @type String $url The request URL.
+ * @type array $request Request arguments.
+ * }
+ */
+ public static function build_signed_request( $args, $body = null ) {
+ add_filter(
+ 'jetpack_constant_default_value',
+ __NAMESPACE__ . '\Utils::jetpack_api_constant_filter',
+ 10,
+ 2
+ );
+
+ $defaults = array(
+ 'url' => '',
+ 'user_id' => 0,
+ 'blog_id' => 0,
+ 'auth_location' => Constants::get_constant( 'JETPACK_CLIENT__AUTH_LOCATION' ),
+ 'method' => 'POST',
+ 'timeout' => 10,
+ 'redirection' => 0,
+ 'headers' => array(),
+ 'stream' => false,
+ 'filename' => null,
+ 'sslverify' => true,
+ );
+
+ $args = wp_parse_args( $args, $defaults );
+
+ $args['blog_id'] = (int) $args['blog_id'];
+
+ if ( 'header' !== $args['auth_location'] ) {
+ $args['auth_location'] = 'query_string';
+ }
+
+ $token = ( new Tokens() )->get_access_token( $args['user_id'] );
+ if ( ! $token ) {
+ return new \WP_Error( 'missing_token' );
+ }
+
+ $method = strtoupper( $args['method'] );
+
+ $timeout = (int) $args['timeout'];
+
+ $redirection = $args['redirection'];
+ $stream = $args['stream'];
+ $filename = $args['filename'];
+ $sslverify = $args['sslverify'];
+
+ $request = compact( 'method', 'body', 'timeout', 'redirection', 'stream', 'filename', 'sslverify' );
+
+ @list( $token_key, $secret ) = explode( '.', $token->secret ); // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
+ if ( empty( $token ) || empty( $secret ) ) {
+ return new \WP_Error( 'malformed_token' );
+ }
+
+ $token_key = sprintf(
+ '%s:%d:%d',
+ $token_key,
+ Constants::get_constant( 'JETPACK__API_VERSION' ),
+ $token->external_user_id
+ );
+
+ $time_diff = (int) \Jetpack_Options::get_option( 'time_diff' );
+ $jetpack_signature = new \Jetpack_Signature( $token->secret, $time_diff );
+
+ $timestamp = time() + $time_diff;
+
+ if ( function_exists( 'wp_generate_password' ) ) {
+ $nonce = wp_generate_password( 10, false );
+ } else {
+ $nonce = substr( sha1( wp_rand( 0, 1000000 ) ), 0, 10 );
+ }
+
+ // Kind of annoying. Maybe refactor Jetpack_Signature to handle body-hashing.
+ if ( is_null( $body ) ) {
+ $body_hash = '';
+
+ } else {
+ // Allow arrays to be used in passing data.
+ $body_to_hash = $body;
+
+ if ( is_array( $body ) ) {
+ // We cast this to a new variable, because the array form of $body needs to be
+ // maintained so it can be passed into the request later on in the code.
+ if ( count( $body ) > 0 ) {
+ $body_to_hash = wp_json_encode( self::_stringify_data( $body ) );
+ } else {
+ $body_to_hash = '';
+ }
+ }
+
+ if ( ! is_string( $body_to_hash ) ) {
+ return new \WP_Error( 'invalid_body', 'Body is malformed.' );
+ }
+
+ $body_hash = base64_encode( sha1( $body_to_hash, true ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
+ }
+
+ $auth = array(
+ 'token' => $token_key,
+ 'timestamp' => $timestamp,
+ 'nonce' => $nonce,
+ 'body-hash' => $body_hash,
+ );
+
+ if ( false !== strpos( $args['url'], 'xmlrpc.php' ) ) {
+ $url_args = array(
+ 'for' => 'jetpack',
+ 'wpcom_blog_id' => \Jetpack_Options::get_option( 'id' ),
+ );
+ } else {
+ $url_args = array();
+ }
+
+ if ( 'header' !== $args['auth_location'] ) {
+ $url_args += $auth;
+ }
+
+ $url = add_query_arg( urlencode_deep( $url_args ), $args['url'] );
+
+ $signature = $jetpack_signature->sign_request( $token_key, $timestamp, $nonce, $body_hash, $method, $url, $body, false );
+
+ if ( ! $signature || is_wp_error( $signature ) ) {
+ return $signature;
+ }
+
+ // Send an Authorization header so various caches/proxies do the right thing.
+ $auth['signature'] = $signature;
+ $auth['version'] = Constants::get_constant( 'JETPACK__VERSION' );
+ $header_pieces = array();
+ foreach ( $auth as $key => $value ) {
+ $header_pieces[] = sprintf( '%s="%s"', $key, $value );
+ }
+ $request['headers'] = array_merge(
+ $args['headers'],
+ array(
+ 'Authorization' => 'X_JETPACK ' . join( ' ', $header_pieces ),
+ )
+ );
+
+ if ( 'header' !== $args['auth_location'] ) {
+ $url = add_query_arg( 'signature', rawurlencode( $signature ), $url );
+ }
+
+ return compact( 'url', 'request' );
+ }
+
+ /**
+ * Wrapper for wp_remote_request(). Turns off SSL verification for certain SSL errors.
+ * This is lame, but many, many, many hosts have misconfigured SSL.
+ *
+ * When Jetpack is registered, the jetpack_fallback_no_verify_ssl_certs option is set to the current time if:
+ * 1. a certificate error is found AND
+ * 2. not verifying the certificate works around the problem.
+ *
+ * The option is checked on each request.
+ *
+ * @internal
+ *
+ * @param String $url the request URL.
+ * @param array $args request arguments.
+ * @param Boolean $set_fallback whether to allow flagging this request to use a fallback certficate override.
+ * @return array|WP_Error WP HTTP response on success
+ */
+ public static function _wp_remote_request( $url, $args, $set_fallback = false ) { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
+ $fallback = \Jetpack_Options::get_option( 'fallback_no_verify_ssl_certs' );
+ if ( false === $fallback ) {
+ \Jetpack_Options::update_option( 'fallback_no_verify_ssl_certs', 0 );
+ }
+
+ /**
+ * SSL verification (`sslverify`) for the JetpackClient remote request
+ * defaults to off, use this filter to force it on.
+ *
+ * Return `true` to ENABLE SSL verification, return `false`
+ * to DISABLE SSL verification.
+ *
+ * @since 1.7.0
+ * @since-jetpack 3.6.0
+ *
+ * @param bool Whether to force `sslverify` or not.
+ */
+ if ( apply_filters( 'jetpack_client_verify_ssl_certs', false ) ) {
+ return wp_remote_request( $url, $args );
+ }
+
+ if ( (int) $fallback ) {
+ // We're flagged to fallback.
+ $args['sslverify'] = false;
+ }
+
+ $response = wp_remote_request( $url, $args );
+
+ if (
+ ! $set_fallback // We're not allowed to set the flag on this request, so whatever happens happens.
+ ||
+ isset( $args['sslverify'] ) && ! $args['sslverify'] // No verification - no point in doing it again.
+ ||
+ ! is_wp_error( $response ) // Let it ride.
+ ) {
+ self::set_time_diff( $response, $set_fallback );
+ return $response;
+ }
+
+ // At this point, we're not flagged to fallback and we are allowed to set the flag on this request.
+
+ $message = $response->get_error_message();
+
+ // Is it an SSL Certificate verification error?
+ if (
+ false === strpos( $message, '14090086' ) // OpenSSL SSL3 certificate error.
+ &&
+ false === strpos( $message, '1407E086' ) // OpenSSL SSL2 certificate error.
+ &&
+ false === strpos( $message, 'error setting certificate verify locations' ) // cURL CA bundle not found.
+ &&
+ false === strpos( $message, 'Peer certificate cannot be authenticated with' ) // cURL CURLE_SSL_CACERT: CA bundle found, but not helpful
+ // Different versions of curl have different error messages
+ // this string should catch them all.
+ &&
+ false === strpos( $message, 'Problem with the SSL CA cert' ) // cURL CURLE_SSL_CACERT_BADFILE: probably access rights.
+ ) {
+ // No, it is not.
+ return $response;
+ }
+
+ // Redo the request without SSL certificate verification.
+ $args['sslverify'] = false;
+ $response = wp_remote_request( $url, $args );
+
+ if ( ! is_wp_error( $response ) ) {
+ // The request went through this time, flag for future fallbacks.
+ \Jetpack_Options::update_option( 'fallback_no_verify_ssl_certs', time() );
+ self::set_time_diff( $response, $set_fallback );
+ }
+
+ return $response;
+ }
+
+ /**
+ * Sets the time difference for correct signature computation.
+ *
+ * @param HTTP_Response $response the response object.
+ * @param Boolean $force_set whether to force setting the time difference.
+ */
+ public static function set_time_diff( &$response, $force_set = false ) {
+ $code = wp_remote_retrieve_response_code( $response );
+
+ // Only trust the Date header on some responses.
+ if ( 200 != $code && 304 != $code && 400 != $code && 401 != $code ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
+ return;
+ }
+
+ $date = wp_remote_retrieve_header( $response, 'date' );
+ if ( ! $date ) {
+ return;
+ }
+
+ $time = (int) strtotime( $date );
+ if ( 0 >= $time ) {
+ return;
+ }
+
+ $time_diff = $time - time();
+
+ if ( $force_set ) { // During register.
+ \Jetpack_Options::update_option( 'time_diff', $time_diff );
+ } else { // Otherwise.
+ $old_diff = \Jetpack_Options::get_option( 'time_diff' );
+ if ( false === $old_diff || abs( $time_diff - (int) $old_diff ) > 10 ) {
+ \Jetpack_Options::update_option( 'time_diff', $time_diff );
+ }
+ }
+ }
+
+ /**
+ * Validate and build arguments for a WordPress.com REST API request.
+ *
+ * @param string $path REST API path.
+ * @param string $version REST API version. Default is `2`.
+ * @param array $args Arguments to {@see WP_Http}. Default is `array()`.
+ * @param string $base_api_path REST API root. Default is `wpcom`.
+ *
+ * @return array|WP_Error $response Response data, else {@see WP_Error} on failure.
+ */
+ public static function validate_args_for_wpcom_json_api_request(
+ $path,
+ $version = '2',
+ $args = array(),
+ $base_api_path = 'wpcom'
+ ) {
+ $base_api_path = trim( $base_api_path, '/' );
+ $version = ltrim( $version, 'v' );
+ $path = ltrim( $path, '/' );
+
+ $filtered_args = array_intersect_key(
+ $args,
+ array(
+ 'headers' => 'array',
+ 'method' => 'string',
+ 'timeout' => 'int',
+ 'redirection' => 'int',
+ 'stream' => 'boolean',
+ 'filename' => 'string',
+ 'sslverify' => 'boolean',
+ )
+ );
+
+ // Use GET by default whereas `remote_request` uses POST.
+ $request_method = isset( $filtered_args['method'] ) ? strtoupper( $filtered_args['method'] ) : 'GET';
+
+ $url = sprintf(
+ '%s/%s/v%s/%s',
+ Constants::get_constant( 'JETPACK__WPCOM_JSON_API_BASE' ),
+ $base_api_path,
+ $version,
+ $path
+ );
+
+ $validated_args = array_merge(
+ $filtered_args,
+ array(
+ 'url' => $url,
+ 'method' => $request_method,
+ )
+ );
+
+ return $validated_args;
+ }
+
+ /**
+ * Queries the WordPress.com REST API with a user token.
+ *
+ * @param string $path REST API path.
+ * @param string $version REST API version. Default is `2`.
+ * @param array $args Arguments to {@see WP_Http}. Default is `array()`.
+ * @param string $body Body passed to {@see WP_Http}. Default is `null`.
+ * @param string $base_api_path REST API root. Default is `wpcom`.
+ *
+ * @return array|WP_Error $response Response data, else {@see WP_Error} on failure.
+ */
+ public static function wpcom_json_api_request_as_user(
+ $path,
+ $version = '2',
+ $args = array(),
+ $body = null,
+ $base_api_path = 'wpcom'
+ ) {
+ $args = self::validate_args_for_wpcom_json_api_request( $path, $version, $args, $base_api_path );
+ $args['user_id'] = get_current_user_id();
+
+ if ( isset( $body ) && ! isset( $args['headers'] ) && in_array( $args['method'], array( 'POST', 'PUT', 'PATCH' ), true ) ) {
+ $args['headers'] = array( 'Content-Type' => 'application/json' );
+ }
+
+ if ( isset( $body ) && ! is_string( $body ) ) {
+ $body = wp_json_encode( $body );
+ }
+
+ return self::remote_request( $args, $body );
+ }
+
+ /**
+ * Query the WordPress.com REST API using the blog token
+ *
+ * @param String $path The API endpoint relative path.
+ * @param String $version The API version.
+ * @param array $args Request arguments.
+ * @param String $body Request body.
+ * @param String $base_api_path (optional) the API base path override, defaults to 'rest'.
+ * @return array|WP_Error $response Data.
+ */
+ public static function wpcom_json_api_request_as_blog(
+ $path,
+ $version = self::WPCOM_JSON_API_VERSION,
+ $args = array(),
+ $body = null,
+ $base_api_path = 'rest'
+ ) {
+ $validated_args = self::validate_args_for_wpcom_json_api_request( $path, $version, $args, $base_api_path );
+ $validated_args['blog_id'] = (int) \Jetpack_Options::get_option( 'id' );
+
+ // For Simple sites get the response directly without any HTTP requests.
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ add_filter( 'is_jetpack_authorized_for_site', '__return_true' );
+ require_lib( 'wpcom-api-direct' );
+ return \WPCOM_API_Direct::do_request( $validated_args, $body );
+ }
+
+ return self::remote_request( $validated_args, $body );
+ }
+
+ /**
+ * Takes an array or similar structure and recursively turns all values into strings. This is used to
+ * make sure that body hashes are made ith the string version, which is what will be seen after a
+ * server pulls up the data in the $_POST array.
+ *
+ * @param array|Mixed $data the data that needs to be stringified.
+ *
+ * @return array|string
+ */
+ public static function _stringify_data( $data ) { // phpcs:ignore PSR2.Methods.MethodDeclaration.Underscore
+
+ // Booleans are special, lets just makes them and explicit 1/0 instead of the 0 being an empty string.
+ if ( is_bool( $data ) ) {
+ return $data ? '1' : '0';
+ }
+
+ // Cast objects into arrays.
+ if ( is_object( $data ) ) {
+ $data = (array) $data;
+ }
+
+ // Non arrays at this point should be just converted to strings.
+ if ( ! is_array( $data ) ) {
+ return (string) $data;
+ }
+
+ foreach ( $data as &$value ) {
+ $value = self::_stringify_data( $value );
+ }
+
+ return $data;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-error-handler.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-error-handler.php
new file mode 100644
index 00000000..9da55442
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-error-handler.php
@@ -0,0 +1,690 @@
+<?php
+/**
+ * The Jetpack Connection error class file.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+/**
+ * The Jetpack Connection Errors that handles errors
+ *
+ * This class handles the following workflow:
+ *
+ * 1. A XML-RCP request with an invalid signature triggers a error
+ * 2. Applies a gate to only process each error code once an hour to avoid overflow
+ * 3. It stores the error on the database, but we don't know yet if this is a valid error, because
+ * we can't confirm it came from WP.com.
+ * 4. It encrypts the error details and send it to thw wp.com server
+ * 5. wp.com checks it and, if valid, sends a new request back to this site using the verify_xml_rpc_error REST endpoint
+ * 6. This endpoint add this error to the Verified errors in the database
+ * 7. Triggers a workflow depending on the error (display user an error message, do some self healing, etc.)
+ *
+ * Errors are stored in the database as options in the following format:
+ *
+ * [
+ * $error_code => [
+ * $user_id => [
+ * $error_details
+ * ]
+ * ]
+ * ]
+ *
+ * For each error code we store a maximum of 5 errors for 5 different user ids.
+ *
+ * An user ID can be
+ * * 0 for blog tokens
+ * * positive integer for user tokens
+ * * 'invalid' for malformed tokens
+ *
+ * @since 1.14.2
+ */
+class Error_Handler {
+
+ /**
+ * The name of the option that stores the errors
+ *
+ * @since 1.14.2
+ *
+ * @var string
+ */
+ const STORED_ERRORS_OPTION = 'jetpack_connection_xmlrpc_errors';
+
+ /**
+ * The name of the option that stores the errors
+ *
+ * @since 1.14.2
+ *
+ * @var string
+ */
+ const STORED_VERIFIED_ERRORS_OPTION = 'jetpack_connection_xmlrpc_verified_errors';
+
+ /**
+ * The prefix of the transient that controls the gate for each error code
+ *
+ * @since 1.14.2
+ *
+ * @var string
+ */
+ const ERROR_REPORTING_GATE = 'jetpack_connection_error_reporting_gate_';
+
+ /**
+ * Time in seconds a test should live in the database before being discarded
+ *
+ * @since 1.14.2
+ */
+ const ERROR_LIFE_TIME = DAY_IN_SECONDS;
+
+ /**
+ * The error code for event tracking purposes.
+ * If there are many, only the first error code will be tracked.
+ *
+ * @var string
+ */
+ private $error_code;
+
+ /**
+ * List of known errors. Only error codes in this list will be handled
+ *
+ * @since 1.14.2
+ *
+ * @var array
+ */
+ public $known_errors = array(
+ 'malformed_token',
+ 'malformed_user_id',
+ 'unknown_user',
+ 'no_user_tokens',
+ 'empty_master_user_option',
+ 'no_token_for_user',
+ 'token_malformed',
+ 'user_id_mismatch',
+ 'no_possible_tokens',
+ 'no_valid_user_token',
+ 'no_valid_blog_token',
+ 'unknown_token',
+ 'could_not_sign',
+ 'invalid_scheme',
+ 'invalid_secret',
+ 'invalid_token',
+ 'token_mismatch',
+ 'invalid_body',
+ 'invalid_signature',
+ 'invalid_body_hash',
+ 'invalid_nonce',
+ 'signature_mismatch',
+ );
+
+ /**
+ * Holds the instance of this singleton class
+ *
+ * @since 1.14.2
+ *
+ * @var Error_Handler $instance
+ */
+ public static $instance = null;
+
+ /**
+ * Initialize instance, hookds and load verified errors handlers
+ *
+ * @since 1.14.2
+ */
+ private function __construct() {
+ defined( 'JETPACK__ERRORS_PUBLIC_KEY' ) || define( 'JETPACK__ERRORS_PUBLIC_KEY', 'KdZY80axKX+nWzfrOcizf0jqiFHnrWCl9X8yuaClKgM=' );
+
+ add_action( 'rest_api_init', array( $this, 'register_verify_error_endpoint' ) );
+
+ $this->handle_verified_errors();
+
+ // If the site gets reconnected, clear errors.
+ add_action( 'jetpack_site_registered', array( $this, 'delete_all_errors' ) );
+ add_action( 'jetpack_get_site_data_success', array( $this, 'delete_all_errors' ) );
+ add_filter( 'jetpack_connection_disconnect_site_wpcom', array( $this, 'delete_all_errors_and_return_unfiltered_value' ) );
+ add_filter( 'jetpack_connection_delete_all_tokens', array( $this, 'delete_all_errors_and_return_unfiltered_value' ) );
+ add_action( 'jetpack_unlinked_user', array( $this, 'delete_all_errors' ) );
+ add_action( 'jetpack_updated_user_token', array( $this, 'delete_all_errors' ) );
+ }
+
+ /**
+ * Gets the list of verified errors and act upon them
+ *
+ * @since 1.14.2
+ *
+ * @return void
+ */
+ public function handle_verified_errors() {
+ $verified_errors = $this->get_verified_errors();
+ foreach ( array_keys( $verified_errors ) as $error_code ) {
+ switch ( $error_code ) {
+ case 'malformed_token':
+ case 'token_malformed':
+ case 'no_possible_tokens':
+ case 'no_valid_user_token':
+ case 'no_valid_blog_token':
+ case 'unknown_token':
+ case 'could_not_sign':
+ case 'invalid_token':
+ case 'token_mismatch':
+ case 'invalid_signature':
+ case 'signature_mismatch':
+ case 'no_user_tokens':
+ case 'no_token_for_user':
+ add_action( 'admin_notices', array( $this, 'generic_admin_notice_error' ) );
+ add_action( 'react_connection_errors_initial_state', array( $this, 'jetpack_react_dashboard_error' ) );
+ $this->error_code = $error_code;
+
+ // Since we are only generically handling errors, we don't need to trigger error messages for each one of them.
+ break 2;
+ }
+ }
+ }
+
+ /**
+ * Gets the instance of this singleton class
+ *
+ * @since 1.14.2
+ *
+ * @return Error_Handler $instance
+ */
+ public static function get_instance() {
+ if ( is_null( self::$instance ) ) {
+ self::$instance = new self();
+ }
+ return self::$instance;
+ }
+
+ /**
+ * Keep track of a connection error that was encountered
+ *
+ * @since 1.14.2
+ *
+ * @param \WP_Error $error the error object.
+ * @param boolean $force Force the report, even if should_report_error is false.
+ * @return void
+ */
+ public function report_error( \WP_Error $error, $force = false ) {
+ if ( in_array( $error->get_error_code(), $this->known_errors, true ) && $this->should_report_error( $error ) || $force ) {
+ $stored_error = $this->store_error( $error );
+ if ( $stored_error ) {
+ $this->send_error_to_wpcom( $stored_error );
+ }
+ }
+ }
+
+ /**
+ * Checks the status of the gate
+ *
+ * This protects the site (and WPCOM) against over loads.
+ *
+ * @since 1.14.2
+ *
+ * @param \WP_Error $error the error object.
+ * @return boolean $should_report True if gate is open and the error should be reported.
+ */
+ public function should_report_error( \WP_Error $error ) {
+
+ if ( defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG ) {
+ return true;
+ }
+
+ /**
+ * Whether to bypass the gate for XML-RPC error handling
+ *
+ * By default, we only process XML-RPC errors once an hour for each error code.
+ * This is done to avoid overflows. If you need to disable this gate, you can set this variable to true.
+ *
+ * This filter is useful for unit testing
+ *
+ * @since 1.14.2
+ *
+ * @param boolean $bypass_gate whether to bypass the gate. Default is false, do not bypass.
+ */
+ $bypass_gate = apply_filters( 'jetpack_connection_bypass_error_reporting_gate', false );
+ if ( true === $bypass_gate ) {
+ return true;
+ }
+
+ $transient = self::ERROR_REPORTING_GATE . $error->get_error_code();
+
+ if ( get_transient( $transient ) ) {
+ return false;
+ }
+
+ set_transient( $transient, true, HOUR_IN_SECONDS );
+ return true;
+ }
+
+ /**
+ * Stores the error in the database so we know there is an issue and can inform the user
+ *
+ * @since 1.14.2
+ *
+ * @param \WP_Error $error the error object.
+ * @return boolean|array False if stored errors were not updated and the error array if it was successfully stored.
+ */
+ public function store_error( \WP_Error $error ) {
+
+ $stored_errors = $this->get_stored_errors();
+ $error_array = $this->wp_error_to_array( $error );
+ $error_code = $error->get_error_code();
+ $user_id = $error_array['user_id'];
+
+ if ( ! isset( $stored_errors[ $error_code ] ) || ! is_array( $stored_errors[ $error_code ] ) ) {
+ $stored_errors[ $error_code ] = array();
+ }
+
+ $stored_errors[ $error_code ][ $user_id ] = $error_array;
+
+ // Let's store a maximum of 5 different user ids for each error code.
+ if ( count( $stored_errors[ $error_code ] ) > 5 ) {
+ // array_shift will destroy keys here because they are numeric, so manually remove first item.
+ $keys = array_keys( $stored_errors[ $error_code ] );
+ unset( $stored_errors[ $error_code ][ $keys[0] ] );
+ }
+
+ if ( update_option( self::STORED_ERRORS_OPTION, $stored_errors ) ) {
+ return $error_array;
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Converts a WP_Error object in the array representation we store in the database
+ *
+ * @since 1.14.2
+ *
+ * @param \WP_Error $error the error object.
+ * @return boolean|array False if error is invalid or the error array
+ */
+ public function wp_error_to_array( \WP_Error $error ) {
+
+ $data = $error->get_error_data();
+
+ if ( ! isset( $data['signature_details'] ) || ! is_array( $data['signature_details'] ) ) {
+ return false;
+ }
+
+ $data = $data['signature_details'];
+
+ if ( ! isset( $data['token'] ) || empty( $data['token'] ) ) {
+ return false;
+ }
+
+ $user_id = $this->get_user_id_from_token( $data['token'] );
+
+ $error_array = array(
+ 'error_code' => $error->get_error_code(),
+ 'user_id' => $user_id,
+ 'error_message' => $error->get_error_message(),
+ 'error_data' => $data,
+ 'timestamp' => time(),
+ 'nonce' => wp_generate_password( 10, false ),
+ );
+
+ return $error_array;
+
+ }
+
+ /**
+ * Sends the error to WP.com to be verified
+ *
+ * @since 1.14.2
+ *
+ * @param array $error_array The array representation of the error as it is stored in the database.
+ * @return bool
+ */
+ public function send_error_to_wpcom( $error_array ) {
+
+ $blog_id = \Jetpack_Options::get_option( 'id' );
+
+ $encrypted_data = $this->encrypt_data_to_wpcom( $error_array );
+
+ if ( false === $encrypted_data ) {
+ return false;
+ }
+
+ $args = array(
+ 'body' => array(
+ 'error_data' => $encrypted_data,
+ ),
+ );
+
+ // send encrypted data to WP.com Public-API v2.
+ wp_remote_post( "https://public-api.wordpress.com/wpcom/v2/sites/{$blog_id}/jetpack-report-error/", $args );
+ return true;
+ }
+
+ /**
+ * Encrypt data to be sent over to WP.com
+ *
+ * @since 1.14.2
+ *
+ * @param array|string $data the data to be encoded.
+ * @return boolean|string The encoded string on success, false on failure
+ */
+ public function encrypt_data_to_wpcom( $data ) {
+
+ try {
+ // phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
+ // phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
+ $encrypted_data = base64_encode( sodium_crypto_box_seal( wp_json_encode( $data ), base64_decode( JETPACK__ERRORS_PUBLIC_KEY ) ) );
+ // phpcs:enable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
+ // phpcs:enable WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
+ } catch ( \SodiumException $e ) {
+ // error encrypting data.
+ return false;
+ }
+
+ return $encrypted_data;
+
+ }
+
+ /**
+ * Extracts the user ID from a token
+ *
+ * @since 1.14.2
+ *
+ * @param string $token the token used to make the xml-rpc request.
+ * @return string $the user id or `invalid` if user id not present.
+ */
+ public function get_user_id_from_token( $token ) {
+ $parsed_token = explode( ':', wp_unslash( $token ) );
+
+ if ( isset( $parsed_token[2] ) && ctype_digit( $parsed_token[2] ) ) {
+ $user_id = $parsed_token[2];
+ } else {
+ $user_id = 'invalid';
+ }
+
+ return $user_id;
+
+ }
+
+ /**
+ * Gets the reported errors stored in the database
+ *
+ * @since 1.14.2
+ *
+ * @return array $errors
+ */
+ public function get_stored_errors() {
+
+ $stored_errors = get_option( self::STORED_ERRORS_OPTION );
+
+ if ( ! is_array( $stored_errors ) ) {
+ $stored_errors = array();
+ }
+
+ $stored_errors = $this->garbage_collector( $stored_errors );
+
+ return $stored_errors;
+ }
+
+ /**
+ * Gets the verified errors stored in the database
+ *
+ * @since 1.14.2
+ *
+ * @return array $errors
+ */
+ public function get_verified_errors() {
+
+ $verified_errors = get_option( self::STORED_VERIFIED_ERRORS_OPTION );
+
+ if ( ! is_array( $verified_errors ) ) {
+ $verified_errors = array();
+ }
+
+ $verified_errors = $this->garbage_collector( $verified_errors );
+
+ return $verified_errors;
+ }
+
+ /**
+ * Removes expired errors from the array
+ *
+ * This method is called by get_stored_errors and get_verified errors and filters their result
+ * Whenever a new error is stored to the database or verified, this will be triggered and the
+ * expired error will be permantently removed from the database
+ *
+ * @since 1.14.2
+ *
+ * @param array $errors array of errors as stored in the database.
+ * @return array
+ */
+ private function garbage_collector( $errors ) {
+ foreach ( $errors as $error_code => $users ) {
+ foreach ( $users as $user_id => $error ) {
+ if ( self::ERROR_LIFE_TIME < time() - (int) $error['timestamp'] ) {
+ unset( $errors[ $error_code ][ $user_id ] );
+ }
+ }
+ }
+ // Clear empty error codes.
+ $errors = array_filter(
+ $errors,
+ function ( $user_errors ) {
+ return ! empty( $user_errors );
+ }
+ );
+ return $errors;
+ }
+
+ /**
+ * Delete all stored and verified errors from the database
+ *
+ * @since 1.14.2
+ *
+ * @return void
+ */
+ public function delete_all_errors() {
+ $this->delete_stored_errors();
+ $this->delete_verified_errors();
+ }
+
+ /**
+ * Delete all stored and verified errors from the database and returns unfiltered value
+ *
+ * This is used to hook into a couple of filters that expect true to not short circuit the disconnection flow
+ *
+ * @since 8.9.0
+ *
+ * @param mixed $check The input sent by the filter.
+ * @return boolean
+ */
+ public function delete_all_errors_and_return_unfiltered_value( $check ) {
+ $this->delete_all_errors();
+ return $check;
+ }
+
+ /**
+ * Delete the reported errors stored in the database
+ *
+ * @since 1.14.2
+ *
+ * @return boolean True, if option is successfully deleted. False on failure.
+ */
+ public function delete_stored_errors() {
+ return delete_option( self::STORED_ERRORS_OPTION );
+ }
+
+ /**
+ * Delete the verified errors stored in the database
+ *
+ * @since 1.14.2
+ *
+ * @return boolean True, if option is successfully deleted. False on failure.
+ */
+ public function delete_verified_errors() {
+ return delete_option( self::STORED_VERIFIED_ERRORS_OPTION );
+ }
+
+ /**
+ * Gets an error based on the nonce
+ *
+ * Receives a nonce and finds the related error.
+ *
+ * @since 1.14.2
+ *
+ * @param string $nonce The nonce created for the error we want to get.
+ * @return null|array Returns the error array representation or null if error not found.
+ */
+ public function get_error_by_nonce( $nonce ) {
+ $errors = $this->get_stored_errors();
+ foreach ( $errors as $user_group ) {
+ foreach ( $user_group as $error ) {
+ if ( $error['nonce'] === $nonce ) {
+ return $error;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Adds an error to the verified error list
+ *
+ * @since 1.14.2
+ *
+ * @param array $error The error array, as it was saved in the unverified errors list.
+ * @return void
+ */
+ public function verify_error( $error ) {
+
+ $verified_errors = $this->get_verified_errors();
+ $error_code = $error['error_code'];
+ $user_id = $error['user_id'];
+
+ if ( ! isset( $verified_errors[ $error_code ] ) ) {
+ $verified_errors[ $error_code ] = array();
+ }
+
+ $verified_errors[ $error_code ][ $user_id ] = $error;
+
+ update_option( self::STORED_VERIFIED_ERRORS_OPTION, $verified_errors );
+
+ }
+
+ /**
+ * Register REST API end point for error hanlding.
+ *
+ * @since 1.14.2
+ *
+ * @return void
+ */
+ public function register_verify_error_endpoint() {
+ register_rest_route(
+ 'jetpack/v4',
+ '/verify_xmlrpc_error',
+ array(
+ 'methods' => \WP_REST_Server::CREATABLE,
+ 'callback' => array( $this, 'verify_xml_rpc_error' ),
+ 'permission_callback' => '__return_true',
+ 'args' => array(
+ 'nonce' => array(
+ 'required' => true,
+ 'type' => 'string',
+ ),
+ ),
+ )
+ );
+ }
+
+ /**
+ * Handles verification that a xml rpc error is legit and came from WordPres.com
+ *
+ * @since 1.14.2
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return boolean
+ */
+ public function verify_xml_rpc_error( \WP_REST_Request $request ) {
+
+ $error = $this->get_error_by_nonce( $request['nonce'] );
+
+ if ( $error ) {
+ $this->verify_error( $error );
+ return new \WP_REST_Response( true, 200 );
+ }
+
+ return new \WP_REST_Response( false, 200 );
+
+ }
+
+ /**
+ * Prints a generic error notice for all connection errors
+ *
+ * @since 8.9.0
+ *
+ * @return void
+ */
+ public function generic_admin_notice_error() {
+ // do not add admin notice to the jetpack dashboard.
+ global $pagenow;
+ if ( 'admin.php' === $pagenow || isset( $_GET['page'] ) && 'jetpack' === $_GET['page'] ) { // phpcs:ignore
+ return;
+ }
+
+ if ( ! current_user_can( 'jetpack_connect' ) ) {
+ return;
+ }
+
+ /**
+ * Filters the message to be displayed in the admin notices area when there's a xmlrpc error.
+ *
+ * By default we don't display any errors.
+ *
+ * Return an empty value to disable the message.
+ *
+ * @since 8.9.0
+ *
+ * @param string $message The error message.
+ * @param array $errors The array of errors. See Automattic\Jetpack\Connection\Error_Handler for details on the array structure.
+ */
+ $message = apply_filters( 'jetpack_connection_error_notice_message', '', $this->get_verified_errors() );
+
+ /**
+ * Fires inside the admin_notices hook just before displaying the error message for a broken connection.
+ *
+ * If you want to disable the default message from being displayed, return an emtpy value in the jetpack_connection_error_notice_message filter.
+ *
+ * @since 8.9.0
+ *
+ * @param array $errors The array of errors. See Automattic\Jetpack\Connection\Error_Handler for details on the array structure.
+ */
+ do_action( 'jetpack_connection_error_notice', $this->get_verified_errors() );
+
+ if ( empty( $message ) ) {
+ return;
+ }
+
+ ?>
+ <div class="notice notice-error is-dismissible jetpack-message jp-connect" style="display:block !important;">
+ <p><?php echo esc_html( $message ); ?></p>
+ </div>
+ <?php
+ }
+
+ /**
+ * Adds the error message to the Jetpack React Dashboard
+ *
+ * @since 8.9.0
+ *
+ * @param array $errors The array of errors. See Automattic\Jetpack\Connection\Error_Handler for details on the array structure.
+ * @return array
+ */
+ public function jetpack_react_dashboard_error( $errors ) {
+ $errors[] = array(
+ 'code' => 'xmlrpc_error',
+ 'message' => __( 'Your connection with WordPress.com seems to be broken. If you\'re experiencing issues, please try reconnecting.', 'jetpack-connection' ),
+ 'action' => 'reconnect',
+ 'data' => array( 'api_error_code' => $this->error_code ),
+ );
+ return $errors;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-initial-state.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-initial-state.php
new file mode 100644
index 00000000..0ee6d7ab
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-initial-state.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * The React initial state.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+/**
+ * The React initial state.
+ */
+class Initial_State {
+
+ /**
+ * Whether the initial state was already rendered
+ *
+ * @var boolean
+ */
+ private static $rendered = false;
+
+ /**
+ * Get the initial state data.
+ *
+ * @return array
+ */
+ private static function get_data() {
+ return array(
+ 'WP_API_root' => esc_url_raw( rest_url() ),
+ 'WP_API_nonce' => wp_create_nonce( 'wp_rest' ),
+ 'registrationNonce' => wp_create_nonce( 'jetpack-registration-nonce' ),
+ 'connectionStatus' => REST_Connector::connection_status( false ),
+ );
+ }
+
+ /**
+ * Render the initial state into a JavaScript variable.
+ *
+ * @return string
+ */
+ public static function render() {
+ if ( self::$rendered ) {
+ return null;
+ }
+ self::$rendered = true;
+ return 'var JP_CONNECTION_INITIAL_STATE=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( self::get_data() ) ) . '"));';
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-manager.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-manager.php
new file mode 100644
index 00000000..0c95bde8
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-manager.php
@@ -0,0 +1,2484 @@
+<?php
+/**
+ * The Jetpack Connection manager class file.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+use Automattic\Jetpack\A8c_Mc_Stats as A8c_Mc_Stats;
+use Automattic\Jetpack\Constants;
+use Automattic\Jetpack\Heartbeat;
+use Automattic\Jetpack\Roles;
+use Automattic\Jetpack\Status;
+use Automattic\Jetpack\Terms_Of_Service;
+use Automattic\Jetpack\Tracking;
+use Jetpack_IXR_Client;
+use WP_Error;
+use WP_User;
+
+/**
+ * The Jetpack Connection Manager class that is used as a single gateway between WordPress.com
+ * and Jetpack.
+ */
+class Manager {
+ /**
+ * A copy of the raw POST data for signature verification purposes.
+ *
+ * @var String
+ */
+ protected $raw_post_data;
+
+ /**
+ * Verification data needs to be stored to properly verify everything.
+ *
+ * @var Object
+ */
+ private $xmlrpc_verification = null;
+
+ /**
+ * Plugin management object.
+ *
+ * @var Plugin
+ */
+ private $plugin = null;
+
+ /**
+ * Holds extra parameters that will be sent along in the register request body.
+ *
+ * Use Manager::add_register_request_param to add values to this array.
+ *
+ * @since 1.26.0
+ * @var array
+ */
+ private static $extra_register_params = array();
+
+ /**
+ * Initialize the object.
+ * Make sure to call the "Configure" first.
+ *
+ * @param string $plugin_slug Slug of the plugin using the connection (optional, but encouraged).
+ *
+ * @see \Automattic\Jetpack\Config
+ */
+ public function __construct( $plugin_slug = null ) {
+ if ( $plugin_slug && is_string( $plugin_slug ) ) {
+ $this->set_plugin_instance( new Plugin( $plugin_slug ) );
+ }
+ }
+
+ /**
+ * Initializes required listeners. This is done separately from the constructors
+ * because some objects sometimes need to instantiate separate objects of this class.
+ *
+ * @todo Implement a proper nonce verification.
+ */
+ public static function configure() {
+ $manager = new self();
+
+ add_filter(
+ 'jetpack_constant_default_value',
+ __NAMESPACE__ . '\Utils::jetpack_api_constant_filter',
+ 10,
+ 2
+ );
+
+ $manager->setup_xmlrpc_handlers(
+ $_GET, // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ $manager->has_connected_owner(),
+ $manager->verify_xml_rpc_signature()
+ );
+
+ $manager->error_handler = Error_Handler::get_instance();
+
+ if ( $manager->is_connected() ) {
+ add_filter( 'xmlrpc_methods', array( $manager, 'public_xmlrpc_methods' ) );
+ add_filter( 'shutdown', array( new Package_Version_Tracker(), 'maybe_update_package_versions' ) );
+ }
+
+ add_action( 'rest_api_init', array( $manager, 'initialize_rest_api_registration_connector' ) );
+
+ ( new Nonce_Handler() )->init_schedule();
+
+ add_action( 'plugins_loaded', __NAMESPACE__ . '\Plugin_Storage::configure', 100 );
+
+ add_filter( 'map_meta_cap', array( $manager, 'jetpack_connection_custom_caps' ), 1, 4 );
+
+ Heartbeat::init();
+ add_filter( 'jetpack_heartbeat_stats_array', array( $manager, 'add_stats_to_heartbeat' ) );
+
+ Webhooks::init( $manager );
+
+ // Set up package version hook.
+ add_filter( 'jetpack_package_versions', __NAMESPACE__ . '\Package_Version::send_package_version_to_tracker' );
+
+ if ( defined( 'JETPACK__SANDBOX_DOMAIN' ) && JETPACK__SANDBOX_DOMAIN ) {
+ ( new Server_Sandbox() )->init();
+ }
+ }
+
+ /**
+ * Sets up the XMLRPC request handlers.
+ *
+ * @since 1.25.0 Deprecate $is_active param.
+ *
+ * @param array $request_params incoming request parameters.
+ * @param bool $has_connected_owner Whether the site has a connected owner.
+ * @param bool $is_signed whether the signature check has been successful.
+ * @param \Jetpack_XMLRPC_Server $xmlrpc_server (optional) an instance of the server to use instead of instantiating a new one.
+ */
+ public function setup_xmlrpc_handlers(
+ $request_params,
+ $has_connected_owner,
+ $is_signed,
+ \Jetpack_XMLRPC_Server $xmlrpc_server = null
+ ) {
+ add_filter( 'xmlrpc_blog_options', array( $this, 'xmlrpc_options' ), 1000, 2 );
+
+ if (
+ ! isset( $request_params['for'] )
+ || 'jetpack' !== $request_params['for']
+ ) {
+ return false;
+ }
+
+ // Alternate XML-RPC, via ?for=jetpack&jetpack=comms.
+ if (
+ isset( $request_params['jetpack'] )
+ && 'comms' === $request_params['jetpack']
+ ) {
+ if ( ! Constants::is_defined( 'XMLRPC_REQUEST' ) ) {
+ // Use the real constant here for WordPress' sake.
+ define( 'XMLRPC_REQUEST', true );
+ }
+
+ add_action( 'template_redirect', array( $this, 'alternate_xmlrpc' ) );
+
+ add_filter( 'xmlrpc_methods', array( $this, 'remove_non_jetpack_xmlrpc_methods' ), 1000 );
+ }
+
+ if ( ! Constants::get_constant( 'XMLRPC_REQUEST' ) ) {
+ return false;
+ }
+ // Display errors can cause the XML to be not well formed.
+ @ini_set( 'display_errors', false ); // phpcs:ignore
+
+ if ( $xmlrpc_server ) {
+ $this->xmlrpc_server = $xmlrpc_server;
+ } else {
+ $this->xmlrpc_server = new \Jetpack_XMLRPC_Server();
+ }
+
+ $this->require_jetpack_authentication();
+
+ if ( $is_signed ) {
+ // If the site is connected either at a site or user level and the request is signed, expose the methods.
+ // The callback is responsible to determine whether the request is signed with blog or user token and act accordingly.
+ // The actual API methods.
+ $callback = array( $this->xmlrpc_server, 'xmlrpc_methods' );
+
+ // Hack to preserve $HTTP_RAW_POST_DATA.
+ add_filter( 'xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
+
+ } elseif ( $has_connected_owner && ! $is_signed ) {
+ // The jetpack.authorize method should be available for unauthenticated users on a site with an
+ // active Jetpack connection, so that additional users can link their account.
+ $callback = array( $this->xmlrpc_server, 'authorize_xmlrpc_methods' );
+
+ } else {
+ // Any other unsigned request should expose the bootstrap methods.
+ $callback = array( $this->xmlrpc_server, 'bootstrap_xmlrpc_methods' );
+ new XMLRPC_Connector( $this );
+ }
+
+ add_filter( 'xmlrpc_methods', $callback );
+
+ // Now that no one can authenticate, and we're whitelisting all XML-RPC methods, force enable_xmlrpc on.
+ add_filter( 'pre_option_enable_xmlrpc', '__return_true' );
+ return true;
+ }
+
+ /**
+ * Initializes the REST API connector on the init hook.
+ */
+ public function initialize_rest_api_registration_connector() {
+ new REST_Connector( $this );
+ }
+
+ /**
+ * Since a lot of hosts use a hammer approach to "protecting" WordPress sites,
+ * and just blanket block all requests to /xmlrpc.php, or apply other overly-sensitive
+ * security/firewall policies, we provide our own alternate XML RPC API endpoint
+ * which is accessible via a different URI. Most of the below is copied directly
+ * from /xmlrpc.php so that we're replicating it as closely as possible.
+ *
+ * @todo Tighten $wp_xmlrpc_server_class a bit to make sure it doesn't do bad things.
+ */
+ public function alternate_xmlrpc() {
+ // Some browser-embedded clients send cookies. We don't want them.
+ $_COOKIE = array();
+
+ include_once ABSPATH . 'wp-admin/includes/admin.php';
+ include_once ABSPATH . WPINC . '/class-IXR.php';
+ include_once ABSPATH . WPINC . '/class-wp-xmlrpc-server.php';
+
+ /**
+ * Filters the class used for handling XML-RPC requests.
+ *
+ * @since 1.7.0
+ * @since-jetpack 3.1.0
+ *
+ * @param string $class The name of the XML-RPC server class.
+ */
+ $wp_xmlrpc_server_class = apply_filters( 'wp_xmlrpc_server_class', 'wp_xmlrpc_server' );
+ $wp_xmlrpc_server = new $wp_xmlrpc_server_class();
+
+ // Fire off the request.
+ nocache_headers();
+ $wp_xmlrpc_server->serve_request();
+
+ exit;
+ }
+
+ /**
+ * Removes all XML-RPC methods that are not `jetpack.*`.
+ * Only used in our alternate XML-RPC endpoint, where we want to
+ * ensure that Core and other plugins' methods are not exposed.
+ *
+ * @param array $methods a list of registered WordPress XMLRPC methods.
+ * @return array filtered $methods
+ */
+ public function remove_non_jetpack_xmlrpc_methods( $methods ) {
+ $jetpack_methods = array();
+
+ foreach ( $methods as $method => $callback ) {
+ if ( 0 === strpos( $method, 'jetpack.' ) ) {
+ $jetpack_methods[ $method ] = $callback;
+ }
+ }
+
+ return $jetpack_methods;
+ }
+
+ /**
+ * Removes all other authentication methods not to allow other
+ * methods to validate unauthenticated requests.
+ */
+ public function require_jetpack_authentication() {
+ // Don't let anyone authenticate.
+ $_COOKIE = array();
+ remove_all_filters( 'authenticate' );
+ remove_all_actions( 'wp_login_failed' );
+
+ if ( $this->is_connected() ) {
+ // Allow Jetpack authentication.
+ add_filter( 'authenticate', array( $this, 'authenticate_jetpack' ), 10, 3 );
+ }
+ }
+
+ /**
+ * Authenticates XML-RPC and other requests from the Jetpack Server
+ *
+ * @param WP_User|Mixed $user user object if authenticated.
+ * @param String $username username.
+ * @param String $password password string.
+ * @return WP_User|Mixed authenticated user or error.
+ */
+ public function authenticate_jetpack( $user, $username, $password ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ if ( is_a( $user, '\\WP_User' ) ) {
+ return $user;
+ }
+
+ $token_details = $this->verify_xml_rpc_signature();
+
+ if ( ! $token_details ) {
+ return $user;
+ }
+
+ if ( 'user' !== $token_details['type'] ) {
+ return $user;
+ }
+
+ if ( ! $token_details['user_id'] ) {
+ return $user;
+ }
+
+ nocache_headers();
+
+ return new \WP_User( $token_details['user_id'] );
+ }
+
+ /**
+ * Verifies the signature of the current request.
+ *
+ * @return false|array
+ */
+ public function verify_xml_rpc_signature() {
+ if ( is_null( $this->xmlrpc_verification ) ) {
+ $this->xmlrpc_verification = $this->internal_verify_xml_rpc_signature();
+
+ if ( is_wp_error( $this->xmlrpc_verification ) ) {
+ /**
+ * Action for logging XMLRPC signature verification errors. This data is sensitive.
+ *
+ * @since 1.7.0
+ * @since-jetpack 7.5.0
+ *
+ * @param WP_Error $signature_verification_error The verification error
+ */
+ do_action( 'jetpack_verify_signature_error', $this->xmlrpc_verification );
+
+ Error_Handler::get_instance()->report_error( $this->xmlrpc_verification );
+
+ }
+ }
+
+ return is_wp_error( $this->xmlrpc_verification ) ? false : $this->xmlrpc_verification;
+ }
+
+ /**
+ * Verifies the signature of the current request.
+ *
+ * This function has side effects and should not be used. Instead,
+ * use the memoized version `->verify_xml_rpc_signature()`.
+ *
+ * @internal
+ * @todo Refactor to use proper nonce verification.
+ */
+ private function internal_verify_xml_rpc_signature() {
+ // phpcs:disable WordPress.Security.NonceVerification.Recommended
+ // It's not for us.
+ if ( ! isset( $_GET['token'] ) || empty( $_GET['signature'] ) ) {
+ return false;
+ }
+
+ $signature_details = array(
+ 'token' => isset( $_GET['token'] ) ? wp_unslash( $_GET['token'] ) : '',
+ 'timestamp' => isset( $_GET['timestamp'] ) ? wp_unslash( $_GET['timestamp'] ) : '',
+ 'nonce' => isset( $_GET['nonce'] ) ? wp_unslash( $_GET['nonce'] ) : '',
+ 'body_hash' => isset( $_GET['body-hash'] ) ? wp_unslash( $_GET['body-hash'] ) : '',
+ 'method' => wp_unslash( $_SERVER['REQUEST_METHOD'] ),
+ 'url' => wp_unslash( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ), // Temp - will get real signature URL later.
+ 'signature' => isset( $_GET['signature'] ) ? wp_unslash( $_GET['signature'] ) : '',
+ );
+
+ // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
+ @list( $token_key, $version, $user_id ) = explode( ':', wp_unslash( $_GET['token'] ) );
+ // phpcs:enable WordPress.Security.NonceVerification.Recommended
+
+ $jetpack_api_version = Constants::get_constant( 'JETPACK__API_VERSION' );
+
+ if (
+ empty( $token_key )
+ ||
+ empty( $version ) || (string) $jetpack_api_version !== $version ) {
+ return new \WP_Error( 'malformed_token', 'Malformed token in request', compact( 'signature_details' ) );
+ }
+
+ if ( '0' === $user_id ) {
+ $token_type = 'blog';
+ $user_id = 0;
+ } else {
+ $token_type = 'user';
+ if ( empty( $user_id ) || ! ctype_digit( $user_id ) ) {
+ return new \WP_Error(
+ 'malformed_user_id',
+ 'Malformed user_id in request',
+ compact( 'signature_details' )
+ );
+ }
+ $user_id = (int) $user_id;
+
+ $user = new \WP_User( $user_id );
+ if ( ! $user || ! $user->exists() ) {
+ return new \WP_Error(
+ 'unknown_user',
+ sprintf( 'User %d does not exist', $user_id ),
+ compact( 'signature_details' )
+ );
+ }
+ }
+
+ $token = $this->get_tokens()->get_access_token( $user_id, $token_key, false );
+ if ( is_wp_error( $token ) ) {
+ $token->add_data( compact( 'signature_details' ) );
+ return $token;
+ } elseif ( ! $token ) {
+ return new \WP_Error(
+ 'unknown_token',
+ sprintf( 'Token %s:%s:%d does not exist', $token_key, $version, $user_id ),
+ compact( 'signature_details' )
+ );
+ }
+
+ $jetpack_signature = new \Jetpack_Signature( $token->secret, (int) \Jetpack_Options::get_option( 'time_diff' ) );
+ // phpcs:disable WordPress.Security.NonceVerification.Missing
+ if ( isset( $_POST['_jetpack_is_multipart'] ) ) {
+ $post_data = $_POST;
+ $file_hashes = array();
+ foreach ( $post_data as $post_data_key => $post_data_value ) {
+ if ( 0 !== strpos( $post_data_key, '_jetpack_file_hmac_' ) ) {
+ continue;
+ }
+ $post_data_key = substr( $post_data_key, strlen( '_jetpack_file_hmac_' ) );
+ $file_hashes[ $post_data_key ] = $post_data_value;
+ }
+
+ foreach ( $file_hashes as $post_data_key => $post_data_value ) {
+ unset( $post_data[ "_jetpack_file_hmac_{$post_data_key}" ] );
+ $post_data[ $post_data_key ] = $post_data_value;
+ }
+
+ ksort( $post_data );
+
+ $body = http_build_query( stripslashes_deep( $post_data ) );
+ } elseif ( is_null( $this->raw_post_data ) ) {
+ $body = file_get_contents( 'php://input' );
+ } else {
+ $body = null;
+ }
+ // phpcs:enable
+
+ $signature = $jetpack_signature->sign_current_request(
+ array( 'body' => is_null( $body ) ? $this->raw_post_data : $body )
+ );
+
+ $signature_details['url'] = $jetpack_signature->current_request_url;
+
+ if ( ! $signature ) {
+ return new \WP_Error(
+ 'could_not_sign',
+ 'Unknown signature error',
+ compact( 'signature_details' )
+ );
+ } elseif ( is_wp_error( $signature ) ) {
+ return $signature;
+ }
+
+ // phpcs:disable WordPress.Security.NonceVerification.Recommended
+ $timestamp = (int) $_GET['timestamp'];
+ $nonce = stripslashes( (string) $_GET['nonce'] );
+ // phpcs:enable WordPress.Security.NonceVerification.Recommended
+
+ // Use up the nonce regardless of whether the signature matches.
+ if ( ! ( new Nonce_Handler() )->add( $timestamp, $nonce ) ) {
+ return new \WP_Error(
+ 'invalid_nonce',
+ 'Could not add nonce',
+ compact( 'signature_details' )
+ );
+ }
+
+ // Be careful about what you do with this debugging data.
+ // If a malicious requester has access to the expected signature,
+ // bad things might be possible.
+ $signature_details['expected'] = $signature;
+
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( ! hash_equals( $signature, $_GET['signature'] ) ) {
+ return new \WP_Error(
+ 'signature_mismatch',
+ 'Signature mismatch',
+ compact( 'signature_details' )
+ );
+ }
+
+ /**
+ * Action for additional token checking.
+ *
+ * @since 1.7.0
+ * @since-jetpack 7.7.0
+ *
+ * @param array $post_data request data.
+ * @param array $token_data token data.
+ */
+ return apply_filters(
+ 'jetpack_signature_check_token',
+ array(
+ 'type' => $token_type,
+ 'token_key' => $token_key,
+ 'user_id' => $token->external_user_id,
+ ),
+ $token,
+ $this->raw_post_data
+ );
+ }
+
+ /**
+ * Returns true if the current site is connected to WordPress.com and has the minimum requirements to enable Jetpack UI.
+ *
+ * This method is deprecated since version 1.25.0 of this package. Please use has_connected_owner instead.
+ *
+ * Since this method has a wide spread use, we decided not to throw any deprecation warnings for now.
+ *
+ * @deprecated 1.25.0
+ * @see Manager::has_connected_owner
+ * @return Boolean is the site connected?
+ */
+ public function is_active() {
+ return (bool) $this->get_tokens()->get_access_token( true );
+ }
+
+ /**
+ * Obtains an instance of the Tokens class.
+ *
+ * @return Tokens the Tokens object
+ */
+ public function get_tokens() {
+ return new Tokens();
+ }
+
+ /**
+ * Returns true if the site has both a token and a blog id, which indicates a site has been registered.
+ *
+ * @access public
+ * @deprecated 1.12.1 Use is_connected instead
+ * @see Manager::is_connected
+ *
+ * @return bool
+ */
+ public function is_registered() {
+ _deprecated_function( __METHOD__, '1.12.1' );
+ return $this->is_connected();
+ }
+
+ /**
+ * Returns true if the site has both a token and a blog id, which indicates a site has been connected.
+ *
+ * @access public
+ * @since 1.21.1
+ *
+ * @return bool
+ */
+ public function is_connected() {
+ $has_blog_id = (bool) \Jetpack_Options::get_option( 'id' );
+ $has_blog_token = (bool) $this->get_tokens()->get_access_token();
+ return $has_blog_id && $has_blog_token;
+ }
+
+ /**
+ * Returns true if the site has at least one connected administrator.
+ *
+ * @access public
+ * @since 1.21.1
+ *
+ * @return bool
+ */
+ public function has_connected_admin() {
+ return (bool) count( $this->get_connected_users( 'manage_options' ) );
+ }
+
+ /**
+ * Returns true if the site has any connected user.
+ *
+ * @access public
+ * @since 1.21.1
+ *
+ * @return bool
+ */
+ public function has_connected_user() {
+ return (bool) count( $this->get_connected_users( 'any', 1 ) );
+ }
+
+ /**
+ * Returns an array of users that have user tokens for communicating with wpcom.
+ * Able to select by specific capability.
+ *
+ * @since 9.9.1 Added $limit parameter.
+ *
+ * @param string $capability The capability of the user.
+ * @param int|null $limit How many connected users to get before returning.
+ * @return WP_User[] Array of WP_User objects if found.
+ */
+ public function get_connected_users( $capability = 'any', $limit = null ) {
+ $connected_users = array();
+ $user_tokens = $this->get_tokens()->get_user_tokens();
+
+ if ( ! is_array( $user_tokens ) || empty( $user_tokens ) ) {
+ return $connected_users;
+ }
+ $connected_user_ids = array_keys( $user_tokens );
+
+ if ( ! empty( $connected_user_ids ) ) {
+ foreach ( $connected_user_ids as $id ) {
+ // Check for capability.
+ if ( 'any' !== $capability && ! user_can( $id, $capability ) ) {
+ continue;
+ }
+
+ $user_data = get_userdata( $id );
+ if ( $user_data instanceof \WP_User ) {
+ $connected_users[] = $user_data;
+ if ( $limit && count( $connected_users ) >= $limit ) {
+ return $connected_users;
+ }
+ }
+ }
+ }
+
+ return $connected_users;
+ }
+
+ /**
+ * Returns true if the site has a connected Blog owner (master_user).
+ *
+ * @access public
+ * @since 1.21.1
+ *
+ * @return bool
+ */
+ public function has_connected_owner() {
+ return (bool) $this->get_connection_owner_id();
+ }
+
+ /**
+ * Returns true if the site is connected only at a site level.
+ *
+ * Note that we are explicitly checking for the existence of the master_user option in order to account for cases where we don't have any user tokens (user-level connection) but the master_user option is set, which could be the result of a problematic user connection.
+ *
+ * @access public
+ * @since 1.25.0
+ * @deprecated 1.27.0
+ *
+ * @return bool
+ */
+ public function is_userless() {
+ _deprecated_function( __METHOD__, '1.27.0', 'Automattic\\Jetpack\\Connection\\Manager::is_site_connection' );
+ return $this->is_site_connection();
+ }
+
+ /**
+ * Returns true if the site is connected only at a site level.
+ *
+ * Note that we are explicitly checking for the existence of the master_user option in order to account for cases where we don't have any user tokens (user-level connection) but the master_user option is set, which could be the result of a problematic user connection.
+ *
+ * @access public
+ * @since 1.27.0
+ *
+ * @return bool
+ */
+ public function is_site_connection() {
+ return $this->is_connected() && ! $this->has_connected_user() && ! \Jetpack_Options::get_option( 'master_user' );
+ }
+
+ /**
+ * Checks to see if the connection owner of the site is missing.
+ *
+ * @return bool
+ */
+ public function is_missing_connection_owner() {
+ $connection_owner = $this->get_connection_owner_id();
+ if ( ! get_user_by( 'id', $connection_owner ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the user with the specified identifier is connected to
+ * WordPress.com.
+ *
+ * @param int $user_id the user identifier. Default is the current user.
+ * @return bool Boolean is the user connected?
+ */
+ public function is_user_connected( $user_id = false ) {
+ $user_id = false === $user_id ? get_current_user_id() : absint( $user_id );
+ if ( ! $user_id ) {
+ return false;
+ }
+
+ return (bool) $this->get_tokens()->get_access_token( $user_id );
+ }
+
+ /**
+ * Returns the local user ID of the connection owner.
+ *
+ * @return bool|int Returns the ID of the connection owner or False if no connection owner found.
+ */
+ public function get_connection_owner_id() {
+ $owner = $this->get_connection_owner();
+ return $owner instanceof \WP_User ? $owner->ID : false;
+ }
+
+ /**
+ * Get the wpcom user data of the current|specified connected user.
+ *
+ * @todo Refactor to properly load the XMLRPC client independently.
+ *
+ * @param Integer $user_id the user identifier.
+ * @return bool|array An array with the WPCOM user data on success, false otherwise.
+ */
+ public function get_connected_user_data( $user_id = null ) {
+ if ( ! $user_id ) {
+ $user_id = get_current_user_id();
+ }
+
+ // Check if the user is connected and return false otherwise.
+ if ( ! $this->is_user_connected( $user_id ) ) {
+ return false;
+ }
+
+ $transient_key = "jetpack_connected_user_data_$user_id";
+ $cached_user_data = get_transient( $transient_key );
+
+ if ( $cached_user_data ) {
+ return $cached_user_data;
+ }
+
+ $xml = new Jetpack_IXR_Client(
+ array(
+ 'user_id' => $user_id,
+ )
+ );
+ $xml->query( 'wpcom.getUser' );
+
+ if ( ! $xml->isError() ) {
+ $user_data = $xml->getResponse();
+ set_transient( $transient_key, $xml->getResponse(), DAY_IN_SECONDS );
+ return $user_data;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a user object of the connection owner.
+ *
+ * @return WP_User|false False if no connection owner found.
+ */
+ public function get_connection_owner() {
+
+ $user_id = \Jetpack_Options::get_option( 'master_user' );
+
+ if ( ! $user_id ) {
+ return false;
+ }
+
+ // Make sure user is connected.
+ $user_token = $this->get_tokens()->get_access_token( $user_id );
+
+ $connection_owner = false;
+
+ if ( $user_token && is_object( $user_token ) && isset( $user_token->external_user_id ) ) {
+ $connection_owner = get_userdata( $user_token->external_user_id );
+ }
+
+ return $connection_owner;
+ }
+
+ /**
+ * Returns true if the provided user is the Jetpack connection owner.
+ * If user ID is not specified, the current user will be used.
+ *
+ * @param Integer|Boolean $user_id the user identifier. False for current user.
+ * @return Boolean True the user the connection owner, false otherwise.
+ */
+ public function is_connection_owner( $user_id = false ) {
+ if ( ! $user_id ) {
+ $user_id = get_current_user_id();
+ }
+
+ return ( (int) $user_id ) === $this->get_connection_owner_id();
+ }
+
+ /**
+ * Connects the user with a specified ID to a WordPress.com user using the
+ * remote login flow.
+ *
+ * @access public
+ *
+ * @param Integer $user_id (optional) the user identifier, defaults to current user.
+ * @param String $redirect_url the URL to redirect the user to for processing, defaults to
+ * admin_url().
+ * @return WP_Error only in case of a failed user lookup.
+ */
+ public function connect_user( $user_id = null, $redirect_url = null ) {
+ $user = null;
+ if ( null === $user_id ) {
+ $user = wp_get_current_user();
+ } else {
+ $user = get_user_by( 'ID', $user_id );
+ }
+
+ if ( empty( $user ) ) {
+ return new \WP_Error( 'user_not_found', 'Attempting to connect a non-existent user.' );
+ }
+
+ if ( null === $redirect_url ) {
+ $redirect_url = admin_url();
+ }
+
+ // Using wp_redirect intentionally because we're redirecting outside.
+ wp_redirect( $this->get_authorization_url( $user, $redirect_url ) ); // phpcs:ignore WordPress.Security.SafeRedirect
+ exit();
+ }
+
+ /**
+ * Unlinks the current user from the linked WordPress.com user.
+ *
+ * @access public
+ * @static
+ *
+ * @todo Refactor to properly load the XMLRPC client independently.
+ *
+ * @param Integer $user_id the user identifier.
+ * @param bool $can_overwrite_primary_user Allow for the primary user to be disconnected.
+ * @param bool $force_disconnect_locally Disconnect user locally even if we were unable to disconnect them from WP.com.
+ * @return Boolean Whether the disconnection of the user was successful.
+ */
+ public function disconnect_user( $user_id = null, $can_overwrite_primary_user = false, $force_disconnect_locally = false ) {
+ $user_id = empty( $user_id ) ? get_current_user_id() : (int) $user_id;
+
+ // Attempt to disconnect the user from WordPress.com.
+ $is_disconnected_from_wpcom = $this->unlink_user_from_wpcom( $user_id );
+
+ $is_disconnected_locally = false;
+ if ( $is_disconnected_from_wpcom || $force_disconnect_locally ) {
+ // Disconnect the user locally.
+ $is_disconnected_locally = $this->get_tokens()->disconnect_user( $user_id, $can_overwrite_primary_user );
+
+ if ( $is_disconnected_locally ) {
+ // Delete cached connected user data.
+ $transient_key = "jetpack_connected_user_data_$user_id";
+ delete_transient( $transient_key );
+
+ /**
+ * Fires after the current user has been unlinked from WordPress.com.
+ *
+ * @since 1.7.0
+ * @since-jetpack 4.1.0
+ *
+ * @param int $user_id The current user's ID.
+ */
+ do_action( 'jetpack_unlinked_user', $user_id );
+ }
+ }
+
+ return $is_disconnected_from_wpcom && $is_disconnected_locally;
+ }
+
+ /**
+ * Request to wpcom for a user to be unlinked from their WordPress.com account
+ *
+ * @access public
+ *
+ * @param Integer $user_id the user identifier.
+ *
+ * @return Boolean Whether the disconnection of the user was successful.
+ */
+ public function unlink_user_from_wpcom( $user_id ) {
+ // Attempt to disconnect the user from WordPress.com.
+ $xml = new Jetpack_IXR_Client( compact( 'user_id' ) );
+
+ $xml->query( 'jetpack.unlink_user', $user_id );
+ if ( $xml->isError() ) {
+ return false;
+ }
+
+ return (bool) $xml->getResponse();
+ }
+
+ /**
+ * Update the connection owner.
+ *
+ * @since 1.29.0
+ *
+ * @param Integer $new_owner_id The ID of the user to become the connection owner.
+ *
+ * @return true|WP_Error True if owner successfully changed, WP_Error otherwise.
+ */
+ public function update_connection_owner( $new_owner_id ) {
+ if ( ! user_can( $new_owner_id, 'administrator' ) ) {
+ return new WP_Error(
+ 'new_owner_not_admin',
+ __( 'New owner is not admin', 'jetpack-connection' ),
+ array( 'status' => 400 )
+ );
+ }
+
+ $old_owner_id = $this->get_connection_owner_id();
+
+ if ( $old_owner_id === $new_owner_id ) {
+ return new WP_Error(
+ 'new_owner_is_existing_owner',
+ __( 'New owner is same as existing owner', 'jetpack-connection' ),
+ array( 'status' => 400 )
+ );
+ }
+
+ if ( ! $this->is_user_connected( $new_owner_id ) ) {
+ return new WP_Error(
+ 'new_owner_not_connected',
+ __( 'New owner is not connected', 'jetpack-connection' ),
+ array( 'status' => 400 )
+ );
+ }
+
+ // Notify WPCOM about the connection owner change.
+ $owner_updated_wpcom = $this->update_connection_owner_wpcom( $new_owner_id );
+
+ if ( $owner_updated_wpcom ) {
+ // Update the connection owner in Jetpack only if they were successfully updated on WPCOM.
+ // This will ensure consistency with WPCOM.
+ \Jetpack_Options::update_option( 'master_user', $new_owner_id );
+
+ // Track it.
+ ( new Tracking() )->record_user_event( 'set_connection_owner_success' );
+
+ return true;
+ }
+ return new WP_Error(
+ 'error_setting_new_owner',
+ __( 'Could not confirm new owner.', 'jetpack-connection' ),
+ array( 'status' => 500 )
+ );
+ }
+
+ /**
+ * Request to WPCOM to update the connection owner.
+ *
+ * @since 1.29.0
+ *
+ * @param Integer $new_owner_id The ID of the user to become the connection owner.
+ *
+ * @return Boolean Whether the ownership transfer was successful.
+ */
+ public function update_connection_owner_wpcom( $new_owner_id ) {
+ // Notify WPCOM about the connection owner change.
+ $xml = new Jetpack_IXR_Client(
+ array(
+ 'user_id' => get_current_user_id(),
+ )
+ );
+ $xml->query(
+ 'jetpack.switchBlogOwner',
+ array(
+ 'new_blog_owner' => $new_owner_id,
+ )
+ );
+ if ( $xml->isError() ) {
+ return false;
+ }
+
+ return (bool) $xml->getResponse();
+ }
+
+ /**
+ * Returns the requested Jetpack API URL.
+ *
+ * @param String $relative_url the relative API path.
+ * @return String API URL.
+ */
+ public function api_url( $relative_url ) {
+ $api_base = Constants::get_constant( 'JETPACK__API_BASE' );
+ $api_version = '/' . Constants::get_constant( 'JETPACK__API_VERSION' ) . '/';
+
+ /**
+ * Filters whether the connection manager should use the iframe authorization
+ * flow instead of the regular redirect-based flow.
+ *
+ * @since 1.9.0
+ *
+ * @param Boolean $is_iframe_flow_used should the iframe flow be used, defaults to false.
+ */
+ $iframe_flow = apply_filters( 'jetpack_use_iframe_authorization_flow', false );
+
+ // Do not modify anything that is not related to authorize requests.
+ if ( 'authorize' === $relative_url && $iframe_flow ) {
+ $relative_url = 'authorize_iframe';
+ }
+
+ /**
+ * Filters the API URL that Jetpack uses for server communication.
+ *
+ * @since 1.7.0
+ * @since-jetpack 8.0.0
+ *
+ * @param String $url the generated URL.
+ * @param String $relative_url the relative URL that was passed as an argument.
+ * @param String $api_base the API base string that is being used.
+ * @param String $api_version the API version string that is being used.
+ */
+ return apply_filters(
+ 'jetpack_api_url',
+ rtrim( $api_base . $relative_url, '/\\' ) . $api_version,
+ $relative_url,
+ $api_base,
+ $api_version
+ );
+ }
+
+ /**
+ * Returns the Jetpack XMLRPC WordPress.com API endpoint URL.
+ *
+ * @return String XMLRPC API URL.
+ */
+ public function xmlrpc_api_url() {
+ $base = preg_replace(
+ '#(https?://[^?/]+)(/?.*)?$#',
+ '\\1',
+ Constants::get_constant( 'JETPACK__API_BASE' )
+ );
+ return untrailingslashit( $base ) . '/xmlrpc.php';
+ }
+
+ /**
+ * Attempts Jetpack registration which sets up the site for connection. Should
+ * remain public because the call to action comes from the current site, not from
+ * WordPress.com.
+ *
+ * @param String $api_endpoint (optional) an API endpoint to use, defaults to 'register'.
+ * @return true|WP_Error The error object.
+ */
+ public function register( $api_endpoint = 'register' ) {
+ add_action( 'pre_update_jetpack_option_register', array( '\\Jetpack_Options', 'delete_option' ) );
+ $secrets = ( new Secrets() )->generate( 'register', get_current_user_id(), 600 );
+
+ if ( false === $secrets ) {
+ return new WP_Error( 'cannot_save_secrets', __( 'Jetpack experienced an issue trying to save options (cannot_save_secrets). We suggest that you contact your hosting provider, and ask them for help checking that the options table is writable on your site.', 'jetpack-connection' ) );
+ }
+
+ if (
+ empty( $secrets['secret_1'] ) ||
+ empty( $secrets['secret_2'] ) ||
+ empty( $secrets['exp'] )
+ ) {
+ return new \WP_Error( 'missing_secrets' );
+ }
+
+ // Better to try (and fail) to set a higher timeout than this system
+ // supports than to have register fail for more users than it should.
+ $timeout = $this->set_min_time_limit( 60 ) / 2;
+
+ $gmt_offset = get_option( 'gmt_offset' );
+ if ( ! $gmt_offset ) {
+ $gmt_offset = 0;
+ }
+
+ $stats_options = get_option( 'stats_options' );
+ $stats_id = isset( $stats_options['blog_id'] )
+ ? $stats_options['blog_id']
+ : null;
+
+ /* This action is documented in src/class-package-version-tracker.php */
+ $package_versions = apply_filters( 'jetpack_package_versions', array() );
+
+ $active_plugins_using_connection = Plugin_Storage::get_all();
+
+ /**
+ * Filters the request body for additional property addition.
+ *
+ * @since 1.7.0
+ * @since-jetpack 7.7.0
+ *
+ * @param array $post_data request data.
+ * @param Array $token_data token data.
+ */
+ $body = apply_filters(
+ 'jetpack_register_request_body',
+ array_merge(
+ array(
+ 'siteurl' => Urls::site_url(),
+ 'home' => Urls::home_url(),
+ 'gmt_offset' => $gmt_offset,
+ 'timezone_string' => (string) get_option( 'timezone_string' ),
+ 'site_name' => (string) get_option( 'blogname' ),
+ 'secret_1' => $secrets['secret_1'],
+ 'secret_2' => $secrets['secret_2'],
+ 'site_lang' => get_locale(),
+ 'timeout' => $timeout,
+ 'stats_id' => $stats_id,
+ 'state' => get_current_user_id(),
+ 'site_created' => $this->get_assumed_site_creation_date(),
+ 'jetpack_version' => Constants::get_constant( 'JETPACK__VERSION' ),
+ 'ABSPATH' => Constants::get_constant( 'ABSPATH' ),
+ 'current_user_email' => wp_get_current_user()->user_email,
+ 'connect_plugin' => $this->get_plugin() ? $this->get_plugin()->get_slug() : null,
+ 'package_versions' => $package_versions,
+ 'active_connected_plugins' => $active_plugins_using_connection,
+ ),
+ self::$extra_register_params
+ )
+ );
+
+ $args = array(
+ 'method' => 'POST',
+ 'body' => $body,
+ 'headers' => array(
+ 'Accept' => 'application/json',
+ ),
+ 'timeout' => $timeout,
+ );
+
+ $args['body'] = $this->apply_activation_source_to_args( $args['body'] );
+
+ // TODO: fix URLs for bad hosts.
+ $response = Client::_wp_remote_request(
+ $this->api_url( $api_endpoint ),
+ $args,
+ true
+ );
+
+ // Make sure the response is valid and does not contain any Jetpack errors.
+ $registration_details = $this->validate_remote_register_response( $response );
+
+ if ( is_wp_error( $registration_details ) ) {
+ return $registration_details;
+ } elseif ( ! $registration_details ) {
+ return new \WP_Error(
+ 'unknown_error',
+ 'Unknown error registering your Jetpack site.',
+ wp_remote_retrieve_response_code( $response )
+ );
+ }
+
+ if ( empty( $registration_details->jetpack_secret ) || ! is_string( $registration_details->jetpack_secret ) ) {
+ return new \WP_Error(
+ 'jetpack_secret',
+ 'Unable to validate registration of your Jetpack site.',
+ wp_remote_retrieve_response_code( $response )
+ );
+ }
+
+ if ( isset( $registration_details->jetpack_public ) ) {
+ $jetpack_public = (int) $registration_details->jetpack_public;
+ } else {
+ $jetpack_public = false;
+ }
+
+ \Jetpack_Options::update_options(
+ array(
+ 'id' => (int) $registration_details->jetpack_id,
+ 'public' => $jetpack_public,
+ )
+ );
+
+ update_option( Package_Version_Tracker::PACKAGE_VERSION_OPTION, $package_versions );
+
+ $this->get_tokens()->update_blog_token( (string) $registration_details->jetpack_secret );
+
+ $allow_inplace_authorization = isset( $registration_details->allow_inplace_authorization ) ? $registration_details->allow_inplace_authorization : false;
+ $alternate_authorization_url = isset( $registration_details->alternate_authorization_url ) ? $registration_details->alternate_authorization_url : '';
+
+ if ( ! $allow_inplace_authorization ) {
+ // Forces register_site REST endpoint to return the Calypso authorization URL.
+ add_filter( 'jetpack_use_iframe_authorization_flow', '__return_false', 20 );
+ }
+
+ add_filter(
+ 'jetpack_register_site_rest_response',
+ function ( $response ) use ( $allow_inplace_authorization, $alternate_authorization_url ) {
+ $response['allowInplaceAuthorization'] = $allow_inplace_authorization;
+ $response['alternateAuthorizeUrl'] = $alternate_authorization_url;
+ return $response;
+ }
+ );
+
+ /**
+ * Fires when a site is registered on WordPress.com.
+ *
+ * @since 1.7.0
+ * @since-jetpack 3.7.0
+ *
+ * @param int $json->jetpack_id Jetpack Blog ID.
+ * @param string $json->jetpack_secret Jetpack Blog Token.
+ * @param int|bool $jetpack_public Is the site public.
+ */
+ do_action(
+ 'jetpack_site_registered',
+ $registration_details->jetpack_id,
+ $registration_details->jetpack_secret,
+ $jetpack_public
+ );
+
+ if ( isset( $registration_details->token ) ) {
+ /**
+ * Fires when a user token is sent along with the registration data.
+ *
+ * @since 1.7.0
+ * @since-jetpack 7.6.0
+ *
+ * @param object $token the administrator token for the newly registered site.
+ */
+ do_action( 'jetpack_site_registered_user_token', $registration_details->token );
+ }
+
+ return true;
+ }
+
+ /**
+ * Attempts Jetpack registration.
+ *
+ * @param bool $tos_agree Whether the user agreed to TOS.
+ *
+ * @return bool|WP_Error
+ */
+ public function try_registration( $tos_agree = true ) {
+ if ( $tos_agree ) {
+ $terms_of_service = new Terms_Of_Service();
+ $terms_of_service->agree();
+ }
+
+ /**
+ * Action fired when the user attempts the registration.
+ *
+ * @since 1.26.0
+ */
+ $pre_register = apply_filters( 'jetpack_pre_register', null );
+
+ if ( is_wp_error( $pre_register ) ) {
+ return $pre_register;
+ }
+
+ $tracking_data = array();
+
+ if ( null !== $this->get_plugin() ) {
+ $tracking_data['plugin_slug'] = $this->get_plugin()->get_slug();
+ }
+
+ $tracking = new Tracking();
+ $tracking->record_user_event( 'jpc_register_begin', $tracking_data );
+
+ add_filter( 'jetpack_register_request_body', array( Utils::class, 'filter_register_request_body' ) );
+
+ $result = $this->register();
+
+ remove_filter( 'jetpack_register_request_body', array( Utils::class, 'filter_register_request_body' ) );
+
+ // If there was an error with registration and the site was not registered, record this so we can show a message.
+ if ( ! $result || is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ return true;
+ }
+
+ /**
+ * Adds a parameter to the register request body
+ *
+ * @since 1.26.0
+ *
+ * @param string $name The name of the parameter to be added.
+ * @param string $value The value of the parameter to be added.
+ *
+ * @throws \InvalidArgumentException If supplied arguments are not strings.
+ * @return void
+ */
+ public function add_register_request_param( $name, $value ) {
+ if ( ! is_string( $name ) || ! is_string( $value ) ) {
+ throw new \InvalidArgumentException( 'name and value must be strings' );
+ }
+ self::$extra_register_params[ $name ] = $value;
+ }
+
+ /**
+ * Takes the response from the Jetpack register new site endpoint and
+ * verifies it worked properly.
+ *
+ * @since 1.7.0
+ * @since-jetpack 2.6.0
+ *
+ * @param Mixed $response the response object, or the error object.
+ * @return string|WP_Error A JSON object on success or WP_Error on failures
+ **/
+ protected function validate_remote_register_response( $response ) {
+ if ( is_wp_error( $response ) ) {
+ return new \WP_Error(
+ 'register_http_request_failed',
+ $response->get_error_message()
+ );
+ }
+
+ $code = wp_remote_retrieve_response_code( $response );
+ $entity = wp_remote_retrieve_body( $response );
+
+ if ( $entity ) {
+ $registration_response = json_decode( $entity );
+ } else {
+ $registration_response = false;
+ }
+
+ $code_type = (int) ( $code / 100 );
+ if ( 5 === $code_type ) {
+ return new \WP_Error( 'wpcom_5??', $code );
+ } elseif ( 408 === $code ) {
+ return new \WP_Error( 'wpcom_408', $code );
+ } elseif ( ! empty( $registration_response->error ) ) {
+ if (
+ 'xml_rpc-32700' === $registration_response->error
+ && ! function_exists( 'xml_parser_create' )
+ ) {
+ $error_description = __( "PHP's XML extension is not available. Jetpack requires the XML extension to communicate with WordPress.com. Please contact your hosting provider to enable PHP's XML extension.", 'jetpack-connection' );
+ } else {
+ $error_description = isset( $registration_response->error_description )
+ ? (string) $registration_response->error_description
+ : '';
+ }
+
+ return new \WP_Error(
+ (string) $registration_response->error,
+ $error_description,
+ $code
+ );
+ } elseif ( 200 !== $code ) {
+ return new \WP_Error( 'wpcom_bad_response', $code );
+ }
+
+ // Jetpack ID error block.
+ if ( empty( $registration_response->jetpack_id ) ) {
+ return new \WP_Error(
+ 'jetpack_id',
+ /* translators: %s is an error message string */
+ sprintf( __( 'Error Details: Jetpack ID is empty. Do not publicly post this error message! %s', 'jetpack-connection' ), $entity ),
+ $entity
+ );
+ } elseif ( ! is_scalar( $registration_response->jetpack_id ) ) {
+ return new \WP_Error(
+ 'jetpack_id',
+ /* translators: %s is an error message string */
+ sprintf( __( 'Error Details: Jetpack ID is not a scalar. Do not publicly post this error message! %s', 'jetpack-connection' ), $entity ),
+ $entity
+ );
+ } elseif ( preg_match( '/[^0-9]/', $registration_response->jetpack_id ) ) {
+ return new \WP_Error(
+ 'jetpack_id',
+ /* translators: %s is an error message string */
+ sprintf( __( 'Error Details: Jetpack ID begins with a numeral. Do not publicly post this error message! %s', 'jetpack-connection' ), $entity ),
+ $entity
+ );
+ }
+
+ return $registration_response;
+ }
+
+ /**
+ * Adds a used nonce to a list of known nonces.
+ *
+ * @param int $timestamp the current request timestamp.
+ * @param string $nonce the nonce value.
+ * @return bool whether the nonce is unique or not.
+ *
+ * @deprecated since 1.24.0
+ * @see Nonce_Handler::add()
+ */
+ public function add_nonce( $timestamp, $nonce ) {
+ _deprecated_function( __METHOD__, '1.24.0', 'Automattic\\Jetpack\\Connection\\Nonce_Handler::add' );
+ return ( new Nonce_Handler() )->add( $timestamp, $nonce );
+ }
+
+ /**
+ * Cleans nonces that were saved when calling ::add_nonce.
+ *
+ * @todo Properly prepare the query before executing it.
+ *
+ * @param bool $all whether to clean even non-expired nonces.
+ *
+ * @deprecated since 1.24.0
+ * @see Nonce_Handler::clean_all()
+ */
+ public function clean_nonces( $all = false ) {
+ _deprecated_function( __METHOD__, '1.24.0', 'Automattic\\Jetpack\\Connection\\Nonce_Handler::clean_all' );
+ ( new Nonce_Handler() )->clean_all( $all ? PHP_INT_MAX : ( time() - Nonce_Handler::LIFETIME ) );
+ }
+
+ /**
+ * Sets the Connection custom capabilities.
+ *
+ * @param string[] $caps Array of the user's capabilities.
+ * @param string $cap Capability name.
+ * @param int $user_id The user ID.
+ * @param array $args Adds the context to the cap. Typically the object ID.
+ */
+ public function jetpack_connection_custom_caps( $caps, $cap, $user_id, $args ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ switch ( $cap ) {
+ case 'jetpack_connect':
+ case 'jetpack_reconnect':
+ $is_offline_mode = ( new Status() )->is_offline_mode();
+ if ( $is_offline_mode ) {
+ $caps = array( 'do_not_allow' );
+ break;
+ }
+ // Pass through. If it's not offline mode, these should match disconnect.
+ // Let users disconnect if it's offline mode, just in case things glitch.
+ case 'jetpack_disconnect':
+ /**
+ * Filters the jetpack_disconnect capability.
+ *
+ * @since 1.14.2
+ *
+ * @param array An array containing the capability name.
+ */
+ $caps = apply_filters( 'jetpack_disconnect_cap', array( 'manage_options' ) );
+ break;
+ case 'jetpack_connect_user':
+ $is_offline_mode = ( new Status() )->is_offline_mode();
+ if ( $is_offline_mode ) {
+ $caps = array( 'do_not_allow' );
+ break;
+ }
+ // With site connections in mind, non-admin users can connect their account only if a connection owner exists.
+ $caps = $this->has_connected_owner() ? array( 'read' ) : array( 'manage_options' );
+ break;
+ }
+ return $caps;
+ }
+
+ /**
+ * Builds the timeout limit for queries talking with the wpcom servers.
+ *
+ * Based on local php max_execution_time in php.ini
+ *
+ * @since 1.7.0
+ * @since-jetpack 5.4.0
+ * @return int
+ **/
+ public function get_max_execution_time() {
+ $timeout = (int) ini_get( 'max_execution_time' );
+
+ // Ensure exec time set in php.ini.
+ if ( ! $timeout ) {
+ $timeout = 30;
+ }
+ return $timeout;
+ }
+
+ /**
+ * Sets a minimum request timeout, and returns the current timeout
+ *
+ * @since 1.7.0
+ * @since-jetpack 5.4.0
+ * @param Integer $min_timeout the minimum timeout value.
+ **/
+ public function set_min_time_limit( $min_timeout ) {
+ $timeout = $this->get_max_execution_time();
+ if ( $timeout < $min_timeout ) {
+ $timeout = $min_timeout;
+ set_time_limit( $timeout );
+ }
+ return $timeout;
+ }
+
+ /**
+ * Get our assumed site creation date.
+ * Calculated based on the earlier date of either:
+ * - Earliest admin user registration date.
+ * - Earliest date of post of any post type.
+ *
+ * @since 1.7.0
+ * @since-jetpack 7.2.0
+ *
+ * @return string Assumed site creation date and time.
+ */
+ public function get_assumed_site_creation_date() {
+ $cached_date = get_transient( 'jetpack_assumed_site_creation_date' );
+ if ( ! empty( $cached_date ) ) {
+ return $cached_date;
+ }
+
+ $earliest_registered_users = get_users(
+ array(
+ 'role' => 'administrator',
+ 'orderby' => 'user_registered',
+ 'order' => 'ASC',
+ 'fields' => array( 'user_registered' ),
+ 'number' => 1,
+ )
+ );
+ $earliest_registration_date = $earliest_registered_users[0]->user_registered;
+
+ $earliest_posts = get_posts(
+ array(
+ 'posts_per_page' => 1,
+ 'post_type' => 'any',
+ 'post_status' => 'any',
+ 'orderby' => 'date',
+ 'order' => 'ASC',
+ )
+ );
+
+ // If there are no posts at all, we'll count only on user registration date.
+ if ( $earliest_posts ) {
+ $earliest_post_date = $earliest_posts[0]->post_date;
+ } else {
+ $earliest_post_date = PHP_INT_MAX;
+ }
+
+ $assumed_date = min( $earliest_registration_date, $earliest_post_date );
+ set_transient( 'jetpack_assumed_site_creation_date', $assumed_date );
+
+ return $assumed_date;
+ }
+
+ /**
+ * Adds the activation source string as a parameter to passed arguments.
+ *
+ * @todo Refactor to use rawurlencode() instead of urlencode().
+ *
+ * @param array $args arguments that need to have the source added.
+ * @return array $amended arguments.
+ */
+ public static function apply_activation_source_to_args( $args ) {
+ list( $activation_source_name, $activation_source_keyword ) = get_option( 'jetpack_activation_source' );
+
+ if ( $activation_source_name ) {
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.urlencode_urlencode
+ $args['_as'] = urlencode( $activation_source_name );
+ }
+
+ if ( $activation_source_keyword ) {
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.urlencode_urlencode
+ $args['_ak'] = urlencode( $activation_source_keyword );
+ }
+
+ return $args;
+ }
+
+ /**
+ * Generates two secret tokens and the end of life timestamp for them.
+ *
+ * @param String $action The action name.
+ * @param Integer $user_id The user identifier.
+ * @param Integer $exp Expiration time in seconds.
+ */
+ public function generate_secrets( $action, $user_id = false, $exp = 600 ) {
+ return ( new Secrets() )->generate( $action, $user_id, $exp );
+ }
+
+ /**
+ * Returns two secret tokens and the end of life timestamp for them.
+ *
+ * @deprecated 1.24.0 Use Automattic\Jetpack\Connection\Secrets->get() instead.
+ *
+ * @param String $action The action name.
+ * @param Integer $user_id The user identifier.
+ * @return string|array an array of secrets or an error string.
+ */
+ public function get_secrets( $action, $user_id ) {
+ _deprecated_function( __METHOD__, '1.24.0', 'Automattic\\Jetpack\\Connection\\Secrets->get' );
+ return ( new Secrets() )->get( $action, $user_id );
+ }
+
+ /**
+ * Deletes secret tokens in case they, for example, have expired.
+ *
+ * @deprecated 1.24.0 Use Automattic\Jetpack\Connection\Secrets->delete() instead.
+ *
+ * @param String $action The action name.
+ * @param Integer $user_id The user identifier.
+ */
+ public function delete_secrets( $action, $user_id ) {
+ _deprecated_function( __METHOD__, '1.24.0', 'Automattic\\Jetpack\\Connection\\Secrets->delete' );
+ ( new Secrets() )->delete( $action, $user_id );
+ }
+
+ /**
+ * Deletes all connection tokens and transients from the local Jetpack site.
+ * If the plugin object has been provided in the constructor, the function first checks
+ * whether it's the only active connection.
+ * If there are any other connections, the function will do nothing and return `false`
+ * (unless `$ignore_connected_plugins` is set to `true`).
+ *
+ * @param bool $ignore_connected_plugins Delete the tokens even if there are other connected plugins.
+ *
+ * @return bool True if disconnected successfully, false otherwise.
+ */
+ public function delete_all_connection_tokens( $ignore_connected_plugins = false ) {
+ // refuse to delete if we're not the last Jetpack plugin installed.
+ if ( ! $ignore_connected_plugins && null !== $this->plugin && ! $this->plugin->is_only() ) {
+ return false;
+ }
+
+ /**
+ * Fires upon the disconnect attempt.
+ * Return `false` to prevent the disconnect.
+ *
+ * @since 1.14.2
+ */
+ if ( ! apply_filters( 'jetpack_connection_delete_all_tokens', true ) ) {
+ return false;
+ }
+
+ \Jetpack_Options::delete_option(
+ array(
+ 'master_user',
+ 'time_diff',
+ 'fallback_no_verify_ssl_certs',
+ )
+ );
+
+ ( new Secrets() )->delete_all();
+ $this->get_tokens()->delete_all();
+
+ // Delete cached connected user data.
+ $transient_key = 'jetpack_connected_user_data_' . get_current_user_id();
+ delete_transient( $transient_key );
+
+ // Delete all XML-RPC errors.
+ Error_Handler::get_instance()->delete_all_errors();
+
+ return true;
+ }
+
+ /**
+ * Tells WordPress.com to disconnect the site and clear all tokens from cached site.
+ * If the plugin object has been provided in the constructor, the function first check
+ * whether it's the only active connection.
+ * If there are any other connections, the function will do nothing and return `false`
+ * (unless `$ignore_connected_plugins` is set to `true`).
+ *
+ * @param bool $ignore_connected_plugins Delete the tokens even if there are other connected plugins.
+ *
+ * @return bool True if disconnected successfully, false otherwise.
+ */
+ public function disconnect_site_wpcom( $ignore_connected_plugins = false ) {
+ if ( ! $ignore_connected_plugins && null !== $this->plugin && ! $this->plugin->is_only() ) {
+ return false;
+ }
+
+ /**
+ * Fires upon the disconnect attempt.
+ * Return `false` to prevent the disconnect.
+ *
+ * @since 1.14.2
+ */
+ if ( ! apply_filters( 'jetpack_connection_disconnect_site_wpcom', true, $this ) ) {
+ return false;
+ }
+
+ $xml = new Jetpack_IXR_Client();
+ $xml->query( 'jetpack.deregister', get_current_user_id() );
+
+ return true;
+ }
+
+ /**
+ * Disconnect the plugin and remove the tokens.
+ * This function will automatically perform "soft" or "hard" disconnect depending on whether other plugins are using the connection.
+ * This is a proxy method to simplify the Connection package API.
+ *
+ * @see Manager::disable_plugin()
+ * @see Manager::disconnect_site_wpcom()
+ * @see Manager::delete_all_connection_tokens()
+ *
+ * @return bool
+ */
+ public function remove_connection() {
+ $this->disable_plugin();
+ $this->disconnect_site_wpcom();
+ $this->delete_all_connection_tokens();
+
+ return true;
+ }
+
+ /**
+ * Completely clearing up the connection, and initiating reconnect.
+ *
+ * @return true|WP_Error True if reconnected successfully, a `WP_Error` object otherwise.
+ */
+ public function reconnect() {
+ ( new Tracking() )->record_user_event( 'restore_connection_reconnect' );
+
+ $this->disconnect_site_wpcom( true );
+ $this->delete_all_connection_tokens( true );
+
+ return $this->register();
+ }
+
+ /**
+ * Validate the tokens, and refresh the invalid ones.
+ *
+ * @return string|bool|WP_Error True if connection restored or string indicating what's to be done next. A `WP_Error` object or false otherwise.
+ */
+ public function restore() {
+ // If this is a site connection we need to trigger a full reconnection as our only secure means of
+ // communication with WPCOM, aka the blog token, is compromised.
+ if ( $this->is_site_connection() ) {
+ return $this->reconnect();
+ }
+
+ $validate_tokens_response = $this->get_tokens()->validate();
+
+ // If token validation failed, trigger a full reconnection.
+ if ( is_array( $validate_tokens_response ) &&
+ isset( $validate_tokens_response['blog_token']['is_healthy'] ) &&
+ isset( $validate_tokens_response['user_token']['is_healthy'] ) ) {
+ $blog_token_healthy = $validate_tokens_response['blog_token']['is_healthy'];
+ $user_token_healthy = $validate_tokens_response['user_token']['is_healthy'];
+ } else {
+ $blog_token_healthy = false;
+ $user_token_healthy = false;
+ }
+
+ // Tokens are both valid, or both invalid. We can't fix the problem we don't see, so the full reconnection is needed.
+ if ( $blog_token_healthy === $user_token_healthy ) {
+ $result = $this->reconnect();
+ return ( true === $result ) ? 'authorize' : $result;
+ }
+
+ if ( ! $blog_token_healthy ) {
+ return $this->refresh_blog_token();
+ }
+
+ if ( ! $user_token_healthy ) {
+ return ( true === $this->refresh_user_token() ) ? 'authorize' : false;
+ }
+
+ return false;
+ }
+
+ /**
+ * Responds to a WordPress.com call to register the current site.
+ * Should be changed to protected.
+ *
+ * @param array $registration_data Array of [ secret_1, user_id ].
+ */
+ public function handle_registration( array $registration_data ) {
+ list( $registration_secret_1, $registration_user_id ) = $registration_data;
+ if ( empty( $registration_user_id ) ) {
+ return new \WP_Error( 'registration_state_invalid', __( 'Invalid Registration State', 'jetpack-connection' ), 400 );
+ }
+
+ return ( new Secrets() )->verify( 'register', $registration_secret_1, (int) $registration_user_id );
+ }
+
+ /**
+ * Perform the API request to validate the blog and user tokens.
+ *
+ * @deprecated 1.24.0 Use Automattic\Jetpack\Connection\Tokens->validate_tokens() instead.
+ *
+ * @param int|null $user_id ID of the user we need to validate token for. Current user's ID by default.
+ *
+ * @return array|false|WP_Error The API response: `array( 'blog_token_is_healthy' => true|false, 'user_token_is_healthy' => true|false )`.
+ */
+ public function validate_tokens( $user_id = null ) {
+ _deprecated_function( __METHOD__, '1.24.0', 'Automattic\\Jetpack\\Connection\\Tokens->validate' );
+ return $this->get_tokens()->validate( $user_id );
+ }
+
+ /**
+ * Verify a Previously Generated Secret.
+ *
+ * @deprecated 1.24.0 Use Automattic\Jetpack\Connection\Secrets->verify() instead.
+ *
+ * @param string $action The type of secret to verify.
+ * @param string $secret_1 The secret string to compare to what is stored.
+ * @param int $user_id The user ID of the owner of the secret.
+ * @return \WP_Error|string WP_Error on failure, secret_2 on success.
+ */
+ public function verify_secrets( $action, $secret_1, $user_id ) {
+ _deprecated_function( __METHOD__, '1.24.0', 'Automattic\\Jetpack\\Connection\\Secrets->verify' );
+ return ( new Secrets() )->verify( $action, $secret_1, $user_id );
+ }
+
+ /**
+ * Responds to a WordPress.com call to authorize the current user.
+ * Should be changed to protected.
+ */
+ public function handle_authorization() {
+
+ }
+
+ /**
+ * Obtains the auth token.
+ *
+ * @param array $data The request data.
+ * @return object|\WP_Error Returns the auth token on success.
+ * Returns a \WP_Error on failure.
+ */
+ public function get_token( $data ) {
+ return $this->get_tokens()->get( $data, $this->api_url( 'token' ) );
+ }
+
+ /**
+ * Builds a URL to the Jetpack connection auth page.
+ *
+ * @param WP_User $user (optional) defaults to the current logged in user.
+ * @param String $redirect (optional) a redirect URL to use instead of the default.
+ * @return string Connect URL.
+ */
+ public function get_authorization_url( $user = null, $redirect = null ) {
+ if ( empty( $user ) ) {
+ $user = wp_get_current_user();
+ }
+
+ $roles = new Roles();
+ $role = $roles->translate_user_to_role( $user );
+ $signed_role = $this->get_tokens()->sign_role( $role );
+
+ /**
+ * Filter the URL of the first time the user gets redirected back to your site for connection
+ * data processing.
+ *
+ * @since 1.7.0
+ * @since-jetpack 8.0.0
+ *
+ * @param string $redirect_url Defaults to the site admin URL.
+ */
+ $processing_url = apply_filters( 'jetpack_connect_processing_url', admin_url( 'admin.php' ) );
+
+ /**
+ * Filter the URL to redirect the user back to when the authorization process
+ * is complete.
+ *
+ * @since 1.7.0
+ * @since-jetpack 8.0.0
+ *
+ * @param string $redirect_url Defaults to the site URL.
+ */
+ $redirect = apply_filters( 'jetpack_connect_redirect_url', $redirect );
+
+ $secrets = ( new Secrets() )->generate( 'authorize', $user->ID, 2 * HOUR_IN_SECONDS );
+
+ /**
+ * Filter the type of authorization.
+ * 'calypso' completes authorization on wordpress.com/jetpack/connect
+ * while 'jetpack' ( or any other value ) completes the authorization at jetpack.wordpress.com.
+ *
+ * @since 1.7.0
+ * @since-jetpack 4.3.3
+ *
+ * @param string $auth_type Defaults to 'calypso', can also be 'jetpack'.
+ */
+ $auth_type = apply_filters( 'jetpack_auth_type', 'calypso' );
+
+ /**
+ * Filters the user connection request data for additional property addition.
+ *
+ * @since 1.7.0
+ * @since-jetpack 8.0.0
+ *
+ * @param array $request_data request data.
+ */
+ $body = apply_filters(
+ 'jetpack_connect_request_body',
+ array(
+ 'response_type' => 'code',
+ 'client_id' => \Jetpack_Options::get_option( 'id' ),
+ 'redirect_uri' => add_query_arg(
+ array(
+ 'handler' => 'jetpack-connection-webhooks',
+ 'action' => 'authorize',
+ '_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
+ 'redirect' => $redirect ? rawurlencode( $redirect ) : false,
+ ),
+ esc_url( $processing_url )
+ ),
+ 'state' => $user->ID,
+ 'scope' => $signed_role,
+ 'user_email' => $user->user_email,
+ 'user_login' => $user->user_login,
+ 'is_active' => $this->is_active(), // TODO Deprecate this.
+ 'jp_version' => Constants::get_constant( 'JETPACK__VERSION' ),
+ 'auth_type' => $auth_type,
+ 'secret' => $secrets['secret_1'],
+ 'blogname' => get_option( 'blogname' ),
+ 'site_url' => Urls::site_url(),
+ 'home_url' => Urls::home_url(),
+ 'site_icon' => get_site_icon_url(),
+ 'site_lang' => get_locale(),
+ 'site_created' => $this->get_assumed_site_creation_date(),
+ 'allow_site_connection' => ! $this->has_connected_owner(),
+ )
+ );
+
+ $body = $this->apply_activation_source_to_args( urlencode_deep( $body ) );
+
+ $api_url = $this->api_url( 'authorize' );
+
+ return add_query_arg( $body, $api_url );
+ }
+
+ /**
+ * Authorizes the user by obtaining and storing the user token.
+ *
+ * @param array $data The request data.
+ * @return string|\WP_Error Returns a string on success.
+ * Returns a \WP_Error on failure.
+ */
+ public function authorize( $data = array() ) {
+ /**
+ * Action fired when user authorization starts.
+ *
+ * @since 1.7.0
+ * @since-jetpack 8.0.0
+ */
+ do_action( 'jetpack_authorize_starting' );
+
+ $roles = new Roles();
+ $role = $roles->translate_current_user_to_role();
+
+ if ( ! $role ) {
+ return new \WP_Error( 'no_role', 'Invalid request.', 400 );
+ }
+
+ $cap = $roles->translate_role_to_cap( $role );
+ if ( ! $cap ) {
+ return new \WP_Error( 'no_cap', 'Invalid request.', 400 );
+ }
+
+ if ( ! empty( $data['error'] ) ) {
+ return new \WP_Error( $data['error'], 'Error included in the request.', 400 );
+ }
+
+ if ( ! isset( $data['state'] ) ) {
+ return new \WP_Error( 'no_state', 'Request must include state.', 400 );
+ }
+
+ if ( ! ctype_digit( $data['state'] ) ) {
+ return new \WP_Error( $data['error'], 'State must be an integer.', 400 );
+ }
+
+ $current_user_id = get_current_user_id();
+ if ( $current_user_id !== (int) $data['state'] ) {
+ return new \WP_Error( 'wrong_state', 'State does not match current user.', 400 );
+ }
+
+ if ( empty( $data['code'] ) ) {
+ return new \WP_Error( 'no_code', 'Request must include an authorization code.', 400 );
+ }
+
+ $token = $this->get_tokens()->get( $data, $this->api_url( 'token' ) );
+
+ if ( is_wp_error( $token ) ) {
+ $code = $token->get_error_code();
+ if ( empty( $code ) ) {
+ $code = 'invalid_token';
+ }
+ return new \WP_Error( $code, $token->get_error_message(), 400 );
+ }
+
+ if ( ! $token ) {
+ return new \WP_Error( 'no_token', 'Error generating token.', 400 );
+ }
+
+ $is_connection_owner = ! $this->has_connected_owner();
+
+ $this->get_tokens()->update_user_token( $current_user_id, sprintf( '%s.%d', $token, $current_user_id ), $is_connection_owner );
+
+ /**
+ * Fires after user has successfully received an auth token.
+ *
+ * @since 1.7.0
+ * @since-jetpack 3.9.0
+ */
+ do_action( 'jetpack_user_authorized' );
+
+ if ( ! $is_connection_owner ) {
+ /**
+ * Action fired when a secondary user has been authorized.
+ *
+ * @since 1.7.0
+ * @since-jetpack 8.0.0
+ */
+ do_action( 'jetpack_authorize_ending_linked' );
+ return 'linked';
+ }
+
+ /**
+ * Action fired when the master user has been authorized.
+ *
+ * @since 1.7.0
+ * @since-jetpack 8.0.0
+ *
+ * @param array $data The request data.
+ */
+ do_action( 'jetpack_authorize_ending_authorized', $data );
+
+ \Jetpack_Options::delete_raw_option( 'jetpack_last_connect_url_check' );
+
+ ( new Nonce_Handler() )->reschedule();
+
+ return 'authorized';
+ }
+
+ /**
+ * Disconnects from the Jetpack servers.
+ * Forgets all connection details and tells the Jetpack servers to do the same.
+ *
+ * @param boolean $disconnect_wpcom Should disconnect_site_wpcom be called.
+ */
+ public function disconnect_site( $disconnect_wpcom = true ) {
+ wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
+
+ ( new Nonce_Handler() )->clean_all();
+
+ // If the site is in an IDC because sync is not allowed,
+ // let's make sure to not disconnect the production site.
+ if ( $disconnect_wpcom ) {
+ $tracking = new Tracking();
+ $tracking->record_user_event( 'disconnect_site', array() );
+
+ $this->disconnect_site_wpcom( true );
+ }
+
+ $this->delete_all_connection_tokens( true );
+
+ // Remove tracked package versions, since they depend on the Jetpack Connection.
+ delete_option( Package_Version_Tracker::PACKAGE_VERSION_OPTION );
+
+ $jetpack_unique_connection = \Jetpack_Options::get_option( 'unique_connection' );
+ if ( $jetpack_unique_connection ) {
+ // Check then record unique disconnection if site has never been disconnected previously.
+ if ( - 1 === $jetpack_unique_connection['disconnected'] ) {
+ $jetpack_unique_connection['disconnected'] = 1;
+ } else {
+ if ( 0 === $jetpack_unique_connection['disconnected'] ) {
+ $a8c_mc_stats_instance = new A8c_Mc_Stats();
+ $a8c_mc_stats_instance->add( 'connections', 'unique-disconnect' );
+ $a8c_mc_stats_instance->do_server_side_stats();
+ }
+ // increment number of times disconnected.
+ $jetpack_unique_connection['disconnected'] += 1;
+ }
+
+ \Jetpack_Options::update_option( 'unique_connection', $jetpack_unique_connection );
+ }
+
+ /**
+ * Fires when a site is disconnected.
+ *
+ * @since 1.30.1
+ */
+ do_action( 'jetpack_site_disconnected' );
+ }
+
+ /**
+ * The Base64 Encoding of the SHA1 Hash of the Input.
+ *
+ * @param string $text The string to hash.
+ * @return string
+ */
+ public function sha1_base64( $text ) {
+ return base64_encode( sha1( $text, true ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
+ }
+
+ /**
+ * This function mirrors Jetpack_Data::is_usable_domain() in the WPCOM codebase.
+ *
+ * @param string $domain The domain to check.
+ *
+ * @return bool|WP_Error
+ */
+ public function is_usable_domain( $domain ) {
+
+ // If it's empty, just fail out.
+ if ( ! $domain ) {
+ return new \WP_Error(
+ 'fail_domain_empty',
+ /* translators: %1$s is a domain name. */
+ sprintf( __( 'Domain `%1$s` just failed is_usable_domain check as it is empty.', 'jetpack-connection' ), $domain )
+ );
+ }
+
+ /**
+ * Skips the usuable domain check when connecting a site.
+ *
+ * Allows site administrators with domains that fail gethostname-based checks to pass the request to WP.com
+ *
+ * @since 1.7.0
+ * @since-jetpack 4.1.0
+ *
+ * @param bool If the check should be skipped. Default false.
+ */
+ if ( apply_filters( 'jetpack_skip_usuable_domain_check', false ) ) {
+ return true;
+ }
+
+ // None of the explicit localhosts.
+ $forbidden_domains = array(
+ 'wordpress.com',
+ 'localhost',
+ 'localhost.localdomain',
+ '127.0.0.1',
+ 'local.wordpress.test', // VVV pattern.
+ 'local.wordpress-trunk.test', // VVV pattern.
+ 'src.wordpress-develop.test', // VVV pattern.
+ 'build.wordpress-develop.test', // VVV pattern.
+ );
+ if ( in_array( $domain, $forbidden_domains, true ) ) {
+ return new \WP_Error(
+ 'fail_domain_forbidden',
+ sprintf(
+ /* translators: %1$s is a domain name. */
+ __(
+ 'Domain `%1$s` just failed is_usable_domain check as it is in the forbidden array.',
+ 'jetpack-connection'
+ ),
+ $domain
+ )
+ );
+ }
+
+ // No .test or .local domains.
+ if ( preg_match( '#\.(test|local)$#i', $domain ) ) {
+ return new \WP_Error(
+ 'fail_domain_tld',
+ sprintf(
+ /* translators: %1$s is a domain name. */
+ __(
+ 'Domain `%1$s` just failed is_usable_domain check as it uses an invalid top level domain.',
+ 'jetpack-connection'
+ ),
+ $domain
+ )
+ );
+ }
+
+ // No WPCOM subdomains.
+ if ( preg_match( '#\.WordPress\.com$#i', $domain ) ) {
+ return new \WP_Error(
+ 'fail_subdomain_wpcom',
+ sprintf(
+ /* translators: %1$s is a domain name. */
+ __(
+ 'Domain `%1$s` just failed is_usable_domain check as it is a subdomain of WordPress.com.',
+ 'jetpack-connection'
+ ),
+ $domain
+ )
+ );
+ }
+
+ // If PHP was compiled without support for the Filter module (very edge case).
+ if ( ! function_exists( 'filter_var' ) ) {
+ // Just pass back true for now, and let wpcom sort it out.
+ return true;
+ }
+
+ return true;
+ }
+
+ /**
+ * Gets the requested token.
+ *
+ * @deprecated 1.24.0 Use Automattic\Jetpack\Connection\Tokens->get_access_token() instead.
+ *
+ * @param int|false $user_id false: Return the Blog Token. int: Return that user's User Token.
+ * @param string|false $token_key If provided, check that the token matches the provided input.
+ * @param bool|true $suppress_errors If true, return a falsy value when the token isn't found; When false, return a descriptive WP_Error when the token isn't found.
+ *
+ * @return object|false
+ *
+ * @see $this->get_tokens()->get_access_token()
+ */
+ public function get_access_token( $user_id = false, $token_key = false, $suppress_errors = true ) {
+ _deprecated_function( __METHOD__, '1.24.0', 'Automattic\\Jetpack\\Connection\\Tokens->get_access_token' );
+ return $this->get_tokens()->get_access_token( $user_id, $token_key, $suppress_errors );
+ }
+
+ /**
+ * In some setups, $HTTP_RAW_POST_DATA can be emptied during some IXR_Server paths
+ * since it is passed by reference to various methods.
+ * Capture it here so we can verify the signature later.
+ *
+ * @param array $methods an array of available XMLRPC methods.
+ * @return array the same array, since this method doesn't add or remove anything.
+ */
+ public function xmlrpc_methods( $methods ) {
+ $this->raw_post_data = isset( $GLOBALS['HTTP_RAW_POST_DATA'] ) ? $GLOBALS['HTTP_RAW_POST_DATA'] : null;
+ return $methods;
+ }
+
+ /**
+ * Resets the raw post data parameter for testing purposes.
+ */
+ public function reset_raw_post_data() {
+ $this->raw_post_data = null;
+ }
+
+ /**
+ * Registering an additional method.
+ *
+ * @param array $methods an array of available XMLRPC methods.
+ * @return array the amended array in case the method is added.
+ */
+ public function public_xmlrpc_methods( $methods ) {
+ if ( array_key_exists( 'wp.getOptions', $methods ) ) {
+ $methods['wp.getOptions'] = array( $this, 'jetpack_get_options' );
+ }
+ return $methods;
+ }
+
+ /**
+ * Handles a getOptions XMLRPC method call.
+ *
+ * @param array $args method call arguments.
+ * @return an amended XMLRPC server options array.
+ */
+ public function jetpack_get_options( $args ) {
+ global $wp_xmlrpc_server;
+
+ $wp_xmlrpc_server->escape( $args );
+
+ $username = $args[1];
+ $password = $args[2];
+
+ $user = $wp_xmlrpc_server->login( $username, $password );
+ if ( ! $user ) {
+ return $wp_xmlrpc_server->error;
+ }
+
+ $options = array();
+ $user_data = $this->get_connected_user_data();
+ if ( is_array( $user_data ) ) {
+ $options['jetpack_user_id'] = array(
+ 'desc' => __( 'The WP.com user ID of the connected user', 'jetpack-connection' ),
+ 'readonly' => true,
+ 'value' => $user_data['ID'],
+ );
+ $options['jetpack_user_login'] = array(
+ 'desc' => __( 'The WP.com username of the connected user', 'jetpack-connection' ),
+ 'readonly' => true,
+ 'value' => $user_data['login'],
+ );
+ $options['jetpack_user_email'] = array(
+ 'desc' => __( 'The WP.com user email of the connected user', 'jetpack-connection' ),
+ 'readonly' => true,
+ 'value' => $user_data['email'],
+ );
+ $options['jetpack_user_site_count'] = array(
+ 'desc' => __( 'The number of sites of the connected WP.com user', 'jetpack-connection' ),
+ 'readonly' => true,
+ 'value' => $user_data['site_count'],
+ );
+ }
+ $wp_xmlrpc_server->blog_options = array_merge( $wp_xmlrpc_server->blog_options, $options );
+ $args = stripslashes_deep( $args );
+ return $wp_xmlrpc_server->wp_getOptions( $args );
+ }
+
+ /**
+ * Adds Jetpack-specific options to the output of the XMLRPC options method.
+ *
+ * @param array $options standard Core options.
+ * @return array amended options.
+ */
+ public function xmlrpc_options( $options ) {
+ $jetpack_client_id = false;
+ if ( $this->is_connected() ) {
+ $jetpack_client_id = \Jetpack_Options::get_option( 'id' );
+ }
+ $options['jetpack_version'] = array(
+ 'desc' => __( 'Jetpack Plugin Version', 'jetpack-connection' ),
+ 'readonly' => true,
+ 'value' => Constants::get_constant( 'JETPACK__VERSION' ),
+ );
+
+ $options['jetpack_client_id'] = array(
+ 'desc' => __( 'The Client ID/WP.com Blog ID of this site', 'jetpack-connection' ),
+ 'readonly' => true,
+ 'value' => $jetpack_client_id,
+ );
+ return $options;
+ }
+
+ /**
+ * Resets the saved authentication state in between testing requests.
+ */
+ public function reset_saved_auth_state() {
+ $this->xmlrpc_verification = null;
+ }
+
+ /**
+ * Sign a user role with the master access token.
+ * If not specified, will default to the current user.
+ *
+ * @access public
+ *
+ * @param string $role User role.
+ * @param int $user_id ID of the user.
+ * @return string Signed user role.
+ */
+ public function sign_role( $role, $user_id = null ) {
+ return $this->get_tokens()->sign_role( $role, $user_id );
+ }
+
+ /**
+ * Set the plugin instance.
+ *
+ * @param Plugin $plugin_instance The plugin instance.
+ *
+ * @return $this
+ */
+ public function set_plugin_instance( Plugin $plugin_instance ) {
+ $this->plugin = $plugin_instance;
+
+ return $this;
+ }
+
+ /**
+ * Retrieve the plugin management object.
+ *
+ * @return Plugin|null
+ */
+ public function get_plugin() {
+ return $this->plugin;
+ }
+
+ /**
+ * Get all connected plugins information, excluding those disconnected by user.
+ * WARNING: the method cannot be called until Plugin_Storage::configure is called, which happens on plugins_loaded
+ * Even if you don't use Jetpack Config, it may be introduced later by other plugins,
+ * so please make sure not to run the method too early in the code.
+ *
+ * @return array|WP_Error
+ */
+ public function get_connected_plugins() {
+ $maybe_plugins = Plugin_Storage::get_all( true );
+
+ if ( $maybe_plugins instanceof WP_Error ) {
+ return $maybe_plugins;
+ }
+
+ return $maybe_plugins;
+ }
+
+ /**
+ * Force plugin disconnect. After its called, the plugin will not be allowed to use the connection.
+ * Note: this method does not remove any access tokens.
+ *
+ * @return bool
+ */
+ public function disable_plugin() {
+ if ( ! $this->plugin ) {
+ return false;
+ }
+
+ return $this->plugin->disable();
+ }
+
+ /**
+ * Force plugin reconnect after user-initiated disconnect.
+ * After its called, the plugin will be allowed to use the connection again.
+ * Note: this method does not initialize access tokens.
+ *
+ * @return bool
+ */
+ public function enable_plugin() {
+ if ( ! $this->plugin ) {
+ return false;
+ }
+
+ return $this->plugin->enable();
+ }
+
+ /**
+ * Whether the plugin is allowed to use the connection, or it's been disconnected by user.
+ * If no plugin slug was passed into the constructor, always returns true.
+ *
+ * @return bool
+ */
+ public function is_plugin_enabled() {
+ if ( ! $this->plugin ) {
+ return true;
+ }
+
+ return $this->plugin->is_enabled();
+ }
+
+ /**
+ * Perform the API request to refresh the blog token.
+ * Note that we are making this request on behalf of the Jetpack master user,
+ * given they were (most probably) the ones that registered the site at the first place.
+ *
+ * @return WP_Error|bool The result of updating the blog_token option.
+ */
+ public function refresh_blog_token() {
+ ( new Tracking() )->record_user_event( 'restore_connection_refresh_blog_token' );
+
+ $blog_id = \Jetpack_Options::get_option( 'id' );
+ if ( ! $blog_id ) {
+ return new WP_Error( 'site_not_registered', 'Site not registered.' );
+ }
+
+ $url = sprintf(
+ '%s/%s/v%s/%s',
+ Constants::get_constant( 'JETPACK__WPCOM_JSON_API_BASE' ),
+ 'wpcom',
+ '2',
+ 'sites/' . $blog_id . '/jetpack-refresh-blog-token'
+ );
+ $method = 'POST';
+ $user_id = get_current_user_id();
+
+ $response = Client::remote_request( compact( 'url', 'method', 'user_id' ) );
+
+ if ( is_wp_error( $response ) ) {
+ return new WP_Error( 'refresh_blog_token_http_request_failed', $response->get_error_message() );
+ }
+
+ $code = wp_remote_retrieve_response_code( $response );
+ $entity = wp_remote_retrieve_body( $response );
+
+ if ( $entity ) {
+ $json = json_decode( $entity );
+ } else {
+ $json = false;
+ }
+
+ if ( 200 !== $code ) {
+ if ( empty( $json->code ) ) {
+ return new WP_Error( 'unknown', '', $code );
+ }
+
+ /* translators: Error description string. */
+ $error_description = isset( $json->message ) ? sprintf( __( 'Error Details: %s', 'jetpack-connection' ), (string) $json->message ) : '';
+
+ return new WP_Error( (string) $json->code, $error_description, $code );
+ }
+
+ if ( empty( $json->jetpack_secret ) || ! is_scalar( $json->jetpack_secret ) ) {
+ return new WP_Error( 'jetpack_secret', '', $code );
+ }
+
+ return $this->get_tokens()->update_blog_token( (string) $json->jetpack_secret );
+ }
+
+ /**
+ * Disconnect the user from WP.com, and initiate the reconnect process.
+ *
+ * @return bool
+ */
+ public function refresh_user_token() {
+ ( new Tracking() )->record_user_event( 'restore_connection_refresh_user_token' );
+ $this->disconnect_user( null, true, true );
+ return true;
+ }
+
+ /**
+ * Fetches a signed token.
+ *
+ * @deprecated 1.24.0 Use Automattic\Jetpack\Connection\Tokens->get_signed_token() instead.
+ *
+ * @param object $token the token.
+ * @return WP_Error|string a signed token
+ */
+ public function get_signed_token( $token ) {
+ _deprecated_function( __METHOD__, '1.24.0', 'Automattic\\Jetpack\\Connection\\Tokens->get_signed_token' );
+ return $this->get_tokens()->get_signed_token( $token );
+ }
+
+ /**
+ * If the site-level connection is active, add the list of plugins using connection to the heartbeat (except Jetpack itself)
+ *
+ * @param array $stats The Heartbeat stats array.
+ * @return array $stats
+ */
+ public function add_stats_to_heartbeat( $stats ) {
+
+ if ( ! $this->is_connected() ) {
+ return $stats;
+ }
+
+ $active_plugins_using_connection = Plugin_Storage::get_all();
+ foreach ( array_keys( $active_plugins_using_connection ) as $plugin_slug ) {
+ if ( 'jetpack' !== $plugin_slug ) {
+ $stats_group = isset( $active_plugins_using_connection['jetpack'] ) ? 'combined-connection' : 'standalone-connection';
+ $stats[ $stats_group ][] = $plugin_slug;
+ }
+ }
+ return $stats;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-nonce-handler.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-nonce-handler.php
new file mode 100644
index 00000000..d5790b1f
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-nonce-handler.php
@@ -0,0 +1,213 @@
+<?php
+/**
+ * The nonce handler.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+/**
+ * The nonce handler.
+ */
+class Nonce_Handler {
+
+ /**
+ * How long the scheduled cleanup can run (in seconds).
+ * Can be modified using the filter `jetpack_connection_nonce_scheduled_cleanup_limit`.
+ */
+ const SCHEDULED_CLEANUP_TIME_LIMIT = 5;
+
+ /**
+ * How many nonces should be removed per batch during the `clean_all()` run.
+ */
+ const CLEAN_ALL_LIMIT_PER_BATCH = 1000;
+
+ /**
+ * Nonce lifetime in seconds.
+ */
+ const LIFETIME = HOUR_IN_SECONDS;
+
+ /**
+ * The nonces used during the request are stored here to keep them valid.
+ * The property is static to keep the nonces accessible between the `Nonce_Handler` instances.
+ *
+ * @var array
+ */
+ private static $nonces_used_this_request = array();
+
+ /**
+ * The database object.
+ *
+ * @var \wpdb
+ */
+ private $db;
+
+ /**
+ * Initializing the object.
+ */
+ public function __construct() {
+ global $wpdb;
+
+ $this->db = $wpdb;
+ }
+
+ /**
+ * Scheduling the WP-cron cleanup event.
+ */
+ public function init_schedule() {
+ add_action( 'jetpack_clean_nonces', array( __CLASS__, 'clean_scheduled' ) );
+ if ( ! wp_next_scheduled( 'jetpack_clean_nonces' ) ) {
+ wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
+ }
+ }
+
+ /**
+ * Reschedule the WP-cron cleanup event to make it start sooner.
+ */
+ public function reschedule() {
+ wp_clear_scheduled_hook( 'jetpack_clean_nonces' );
+ wp_schedule_event( time(), 'hourly', 'jetpack_clean_nonces' );
+ }
+
+ /**
+ * Adds a used nonce to a list of known nonces.
+ *
+ * @param int $timestamp the current request timestamp.
+ * @param string $nonce the nonce value.
+ *
+ * @return bool whether the nonce is unique or not.
+ */
+ public function add( $timestamp, $nonce ) {
+ if ( isset( static::$nonces_used_this_request[ "$timestamp:$nonce" ] ) ) {
+ return static::$nonces_used_this_request[ "$timestamp:$nonce" ];
+ }
+
+ // This should always have gone through Jetpack_Signature::sign_request() first to check $timestamp and $nonce.
+ $timestamp = (int) $timestamp;
+ $nonce = esc_sql( $nonce );
+
+ // Raw query so we can avoid races: add_option will also update.
+ $show_errors = $this->db->hide_errors();
+
+ // Running `try...finally` to make sure that we re-enable errors in case of an exception.
+ try {
+ $old_nonce = $this->db->get_row(
+ $this->db->prepare( "SELECT 1 FROM `{$this->db->options}` WHERE option_name = %s", "jetpack_nonce_{$timestamp}_{$nonce}" )
+ );
+
+ if ( is_null( $old_nonce ) ) {
+ $return = (bool) $this->db->query(
+ $this->db->prepare(
+ "INSERT INTO `{$this->db->options}` (`option_name`, `option_value`, `autoload`) VALUES (%s, %s, %s)",
+ "jetpack_nonce_{$timestamp}_{$nonce}",
+ time(),
+ 'no'
+ )
+ );
+ } else {
+ $return = false;
+ }
+ } finally {
+ $this->db->show_errors( $show_errors );
+ }
+
+ static::$nonces_used_this_request[ "$timestamp:$nonce" ] = $return;
+
+ return $return;
+ }
+
+ /**
+ * Removing all existing nonces, or at least as many as possible.
+ * Capped at 20 seconds to avoid breaking the site.
+ *
+ * @param int $cutoff_timestamp All nonces added before this timestamp will be removed.
+ * @param int $time_limit How long the cleanup can run (in seconds).
+ *
+ * @return true
+ */
+ public function clean_all( $cutoff_timestamp = PHP_INT_MAX, $time_limit = 20 ) {
+ // phpcs:ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
+ for ( $end_time = time() + $time_limit; time() < $end_time; ) {
+ $result = $this->delete( static::CLEAN_ALL_LIMIT_PER_BATCH, $cutoff_timestamp );
+
+ if ( ! $result ) {
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Scheduled clean up of the expired nonces.
+ */
+ public static function clean_scheduled() {
+ /**
+ * Adjust the time limit for the scheduled cleanup.
+ *
+ * @since 9.5.0
+ *
+ * @param int $time_limit How long the cleanup can run (in seconds).
+ */
+ $time_limit = apply_filters( 'jetpack_connection_nonce_cleanup_runtime_limit', static::SCHEDULED_CLEANUP_TIME_LIMIT );
+
+ ( new static() )->clean_all( time() - static::LIFETIME, $time_limit );
+ }
+
+ /**
+ * Delete the nonces.
+ *
+ * @param int $limit How many nonces to delete.
+ * @param null|int $cutoff_timestamp All nonces added before this timestamp will be removed.
+ *
+ * @return int|false Number of removed nonces, or `false` if nothing to remove (or in case of a database error).
+ */
+ public function delete( $limit = 10, $cutoff_timestamp = null ) {
+ global $wpdb;
+
+ $ids = $wpdb->get_col(
+ $wpdb->prepare(
+ "SELECT option_id FROM `{$wpdb->options}`"
+ . " WHERE `option_name` >= 'jetpack_nonce_' AND `option_name` < %s"
+ . ' LIMIT %d',
+ 'jetpack_nonce_' . $cutoff_timestamp,
+ $limit
+ )
+ );
+
+ if ( ! is_array( $ids ) ) {
+ // There's an error and we can't proceed.
+ return false;
+ }
+
+ // Removing zeroes in case AUTO_INCREMENT of the options table is broken, and all ID's are zeroes.
+ $ids = array_filter( $ids );
+
+ if ( ! count( $ids ) ) {
+ // There's nothing to remove.
+ return false;
+ }
+
+ $ids_fill = implode( ', ', array_fill( 0, count( $ids ), '%d' ) );
+
+ $args = $ids;
+ $args[] = 'jetpack_nonce_%';
+
+ // The Code Sniffer is unable to understand what's going on...
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared,WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
+ return $wpdb->query( $wpdb->prepare( "DELETE FROM `{$wpdb->options}` WHERE `option_id` IN ( {$ids_fill} ) AND option_name LIKE %s", $args ) );
+ }
+
+ /**
+ * Clean the cached nonces valid during the current request, therefore making them invalid.
+ *
+ * @return bool
+ */
+ public static function invalidate_request_nonces() {
+ static::$nonces_used_this_request = array();
+
+ return true;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-package-version-tracker.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-package-version-tracker.php
new file mode 100644
index 00000000..2514b606
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-package-version-tracker.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * The Package_Version_Tracker class.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+/**
+ * The Package_Version_Tracker class.
+ */
+class Package_Version_Tracker {
+
+ const PACKAGE_VERSION_OPTION = 'jetpack_package_versions';
+
+ /**
+ * The cache key for storing a failed request to update remote package versions.
+ * The caching logic is that when a failed request occurs, we cache it temporarily
+ * with a set expiration time.
+ * Only after the key has expired, we'll be able to repeat a remote request.
+ * This also implies that the cached value is redundant, however we chose the datetime
+ * of the failed request to avoid using booleans.
+ */
+ const CACHED_FAILED_REQUEST_KEY = 'jetpack_failed_update_remote_package_versions';
+
+ /**
+ * The min time difference in seconds for attempting to
+ * update remote tracked package versions after a failed remote request.
+ */
+ const CACHED_FAILED_REQUEST_EXPIRATION = 1 * HOUR_IN_SECONDS;
+
+ /**
+ * Uses the jetpack_package_versions filter to obtain the package versions from packages that need
+ * version tracking. If the package versions have changed, updates the option and notifies WPCOM.
+ */
+ public function maybe_update_package_versions() {
+ /**
+ * Obtains the package versions.
+ *
+ * @since $$next_version$$
+ *
+ * @param array An associative array of Jetpack package slugs and their corresponding versions as key/value pairs.
+ */
+ $filter_versions = apply_filters( 'jetpack_package_versions', array() );
+
+ if ( ! is_array( $filter_versions ) ) {
+ return;
+ }
+
+ $option_versions = get_option( self::PACKAGE_VERSION_OPTION, array() );
+
+ foreach ( $filter_versions as $package => $version ) {
+ if ( ! is_string( $package ) || ! is_string( $version ) ) {
+ unset( $filter_versions[ $package ] );
+ }
+ }
+
+ if ( ! is_array( $option_versions )
+ || count( array_diff_assoc( $filter_versions, $option_versions ) )
+ || count( array_diff_assoc( $option_versions, $filter_versions ) )
+ ) {
+ $this->update_package_versions_option( $filter_versions );
+ }
+ }
+
+ /**
+ * Updates the package versions:
+ * - Sends the updated package versions to wpcom.
+ * - Updates the 'jetpack_package_versions' option.
+ *
+ * @param array $package_versions The package versions.
+ */
+ protected function update_package_versions_option( $package_versions ) {
+ $connection = new Manager();
+ if ( ! $connection->is_connected() ) {
+ return;
+ }
+
+ $site_id = \Jetpack_Options::get_option( 'id' );
+
+ $last_failed_attempt_within_hour = get_transient( self::CACHED_FAILED_REQUEST_KEY );
+
+ if ( $last_failed_attempt_within_hour ) {
+ return;
+ }
+
+ $body = wp_json_encode(
+ array(
+ 'package_versions' => $package_versions,
+ )
+ );
+
+ $response = Client::wpcom_json_api_request_as_blog(
+ sprintf( '/sites/%d/jetpack-package-versions', $site_id ),
+ '2',
+ array(
+ 'headers' => array( 'content-type' => 'application/json' ),
+ 'method' => 'POST',
+ ),
+ $body,
+ 'wpcom'
+ );
+
+ if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
+ update_option( self::PACKAGE_VERSION_OPTION, $package_versions );
+ } else {
+ set_transient( self::CACHED_FAILED_REQUEST_KEY, time(), self::CACHED_FAILED_REQUEST_EXPIRATION );
+ }
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-package-version.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-package-version.php
new file mode 100644
index 00000000..4064bf62
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-package-version.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * The Package_Version class.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+/**
+ * The Package_Version class.
+ */
+class Package_Version {
+
+ const PACKAGE_VERSION = '1.34.0';
+
+ const PACKAGE_SLUG = 'connection';
+
+ /**
+ * Adds the package slug and version to the package version tracker's data.
+ *
+ * @param array $package_versions The package version array.
+ *
+ * @return array The packge version array.
+ */
+ public static function send_package_version_to_tracker( $package_versions ) {
+ $package_versions[ self::PACKAGE_SLUG ] = self::PACKAGE_VERSION;
+ return $package_versions;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-plugin-storage.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-plugin-storage.php
new file mode 100644
index 00000000..8bf1884f
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-plugin-storage.php
@@ -0,0 +1,269 @@
+<?php
+/**
+ * Storage for plugin connection information.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+use WP_Error;
+
+/**
+ * The class serves a single purpose - to store the data which plugins use the connection, along with some auxiliary information.
+ */
+class Plugin_Storage {
+
+ const ACTIVE_PLUGINS_OPTION_NAME = 'jetpack_connection_active_plugins';
+
+ const PLUGINS_DISABLED_OPTION_NAME = 'jetpack_connection_disabled_plugins';
+
+ /**
+ * Whether this class was configured for the first time or not.
+ *
+ * @var boolean
+ */
+ private static $configured = false;
+
+ /**
+ * Refresh list of connected plugins upon intialization.
+ *
+ * @var boolean
+ */
+ private static $refresh_connected_plugins = false;
+
+ /**
+ * Connected plugins.
+ *
+ * @var array
+ */
+ private static $plugins = array();
+
+ /**
+ * The blog ID the storage is setup for.
+ * The data will be refreshed if the blog ID changes.
+ * Used for the multisite networks.
+ *
+ * @var int
+ */
+ private static $current_blog_id = null;
+
+ /**
+ * Add or update the plugin information in the storage.
+ *
+ * @param string $slug Plugin slug.
+ * @param array $args Plugin arguments, optional.
+ *
+ * @return bool
+ */
+ public static function upsert( $slug, array $args = array() ) {
+ self::$plugins[ $slug ] = $args;
+
+ // if plugin is not in the list of active plugins, refresh the list.
+ if ( ! array_key_exists( $slug, (array) get_option( self::ACTIVE_PLUGINS_OPTION_NAME, array() ) ) ) {
+ self::$refresh_connected_plugins = true;
+ }
+
+ return true;
+ }
+
+ /**
+ * Retrieve the plugin information by slug.
+ * WARNING: the method cannot be called until Plugin_Storage::configure is called, which happens on plugins_loaded
+ * Even if you don't use Jetpack Config, it may be introduced later by other plugins,
+ * so please make sure not to run the method too early in the code.
+ *
+ * @param string $slug The plugin slug.
+ *
+ * @return array|null|WP_Error
+ */
+ public static function get_one( $slug ) {
+ $plugins = self::get_all();
+
+ if ( $plugins instanceof WP_Error ) {
+ return $plugins;
+ }
+
+ return empty( $plugins[ $slug ] ) ? null : $plugins[ $slug ];
+ }
+
+ /**
+ * Retrieve info for all plugins that use the connection.
+ * WARNING: the method cannot be called until Plugin_Storage::configure is called, which happens on plugins_loaded
+ * Even if you don't use Jetpack Config, it may be introduced later by other plugins,
+ * so please make sure not to run the method too early in the code.
+ *
+ * @param bool $connected_only Exclude plugins that were explicitly disconnected.
+ *
+ * @return array|WP_Error
+ */
+ public static function get_all( $connected_only = false ) {
+ $maybe_error = self::ensure_configured();
+
+ if ( $maybe_error instanceof WP_Error ) {
+ return $maybe_error;
+ }
+
+ return $connected_only ? array_diff_key( self::$plugins, array_flip( self::get_all_disabled_plugins() ) ) : self::$plugins;
+ }
+
+ /**
+ * Remove the plugin connection info from Jetpack.
+ * WARNING: the method cannot be called until Plugin_Storage::configure is called, which happens on plugins_loaded
+ * Even if you don't use Jetpack Config, it may be introduced later by other plugins,
+ * so please make sure not to run the method too early in the code.
+ *
+ * @param string $slug The plugin slug.
+ *
+ * @return bool|WP_Error
+ */
+ public static function delete( $slug ) {
+ $maybe_error = self::ensure_configured();
+
+ if ( $maybe_error instanceof WP_Error ) {
+ return $maybe_error;
+ }
+
+ if ( array_key_exists( $slug, self::$plugins ) ) {
+ unset( self::$plugins[ $slug ] );
+ }
+
+ return true;
+ }
+
+ /**
+ * The method makes sure that `Jetpack\Config` has finished, and it's now safe to retrieve the list of plugins.
+ *
+ * @return bool|WP_Error
+ */
+ private static function ensure_configured() {
+ if ( ! self::$configured ) {
+ return new WP_Error( 'too_early', __( 'You cannot call this method until Jetpack Config is configured', 'jetpack-connection' ) );
+ }
+
+ if ( is_multisite() && get_current_blog_id() !== self::$current_blog_id ) {
+ self::$plugins = (array) get_option( self::ACTIVE_PLUGINS_OPTION_NAME, array() );
+ self::$current_blog_id = get_current_blog_id();
+ }
+
+ return true;
+ }
+
+ /**
+ * Called once to configure this class after plugins_loaded.
+ *
+ * @return void
+ */
+ public static function configure() {
+ if ( self::$configured ) {
+ return;
+ }
+
+ if ( is_multisite() ) {
+ self::$current_blog_id = get_current_blog_id();
+ }
+
+ // If a plugin was activated or deactivated.
+ $number_of_plugins_differ = count( self::$plugins ) !== count( (array) get_option( self::ACTIVE_PLUGINS_OPTION_NAME, array() ) );
+
+ if ( $number_of_plugins_differ || true === self::$refresh_connected_plugins ) {
+ self::update_active_plugins_option();
+ }
+
+ self::$configured = true;
+
+ }
+
+ /**
+ * Updates the active plugins option with current list of active plugins.
+ *
+ * @return void
+ */
+ public static function update_active_plugins_option() {
+ // Note: Since this options is synced to wpcom, if you change its structure, you have to update the sanitizer at wpcom side.
+ update_option( self::ACTIVE_PLUGINS_OPTION_NAME, self::$plugins );
+
+ if ( ! class_exists( 'Automattic\Jetpack\Sync\Settings' ) || ! \Automattic\Jetpack\Sync\Settings::is_sync_enabled() ) {
+ self::update_active_plugins_wpcom_no_sync_fallback();
+ }
+ }
+
+ /**
+ * Add the plugin to the set of disconnected ones.
+ *
+ * @param string $slug Plugin slug.
+ *
+ * @return bool
+ */
+ public static function disable_plugin( $slug ) {
+ $disconnects = self::get_all_disabled_plugins();
+
+ if ( ! in_array( $slug, $disconnects, true ) ) {
+ $disconnects[] = $slug;
+ update_option( self::PLUGINS_DISABLED_OPTION_NAME, $disconnects );
+ }
+
+ return true;
+ }
+
+ /**
+ * Remove the plugin from the set of disconnected ones.
+ *
+ * @param string $slug Plugin slug.
+ *
+ * @return bool
+ */
+ public static function enable_plugin( $slug ) {
+ $disconnects = self::get_all_disabled_plugins();
+
+ $slug_index = array_search( $slug, $disconnects, true );
+ if ( false !== $slug_index ) {
+ unset( $disconnects[ $slug_index ] );
+ update_option( self::PLUGINS_DISABLED_OPTION_NAME, $disconnects );
+ }
+
+ return true;
+ }
+
+ /**
+ * Get all plugins that were disconnected by user.
+ *
+ * @return array
+ */
+ public static function get_all_disabled_plugins() {
+ return (array) get_option( self::PLUGINS_DISABLED_OPTION_NAME, array() );
+ }
+
+ /**
+ * Update active plugins option with current list of active plugins on WPCOM.
+ * This is a fallback to ensure this option is always up to date on WPCOM in case
+ * Sync is not present or disabled.
+ *
+ * @since $$next_version$$
+ */
+ private static function update_active_plugins_wpcom_no_sync_fallback() {
+ $connection = new Manager();
+ if ( ! $connection->is_connected() ) {
+ return;
+ }
+
+ $site_id = \Jetpack_Options::get_option( 'id' );
+
+ $body = wp_json_encode(
+ array(
+ 'active_connected_plugins' => self::$plugins,
+ )
+ );
+
+ Client::wpcom_json_api_request_as_blog(
+ sprintf( '/sites/%d/jetpack-active-connected-plugins', $site_id ),
+ '2',
+ array(
+ 'headers' => array( 'content-type' => 'application/json' ),
+ 'method' => 'POST',
+ ),
+ $body,
+ 'wpcom'
+ );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-plugin.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-plugin.php
new file mode 100644
index 00000000..92914350
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-plugin.php
@@ -0,0 +1,118 @@
+<?php
+/**
+ * Plugin connection management class.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+/**
+ * Plugin connection management class.
+ * The class represents a single plugin that uses Jetpack connection.
+ * Its functionality has been pretty simplistic so far: add to the storage (`Plugin_Storage`), remove it from there,
+ * and determine whether it's the last active connection. As the component grows, there'll be more functionality added.
+ */
+class Plugin {
+
+ /**
+ * List of the keys allowed as arguments
+ *
+ * @var array
+ */
+ private $arguments_whitelist = array(
+ 'url_info',
+ );
+
+ /**
+ * Plugin slug.
+ *
+ * @var string
+ */
+ private $slug;
+
+ /**
+ * Initialize the plugin manager.
+ *
+ * @param string $slug Plugin slug.
+ */
+ public function __construct( $slug ) {
+ $this->slug = $slug;
+ }
+
+ /**
+ * Get the plugin slug.
+ *
+ * @return string
+ */
+ public function get_slug() {
+ return $this->slug;
+ }
+
+ /**
+ * Add the plugin connection info into Jetpack.
+ *
+ * @param string $name Plugin name, required.
+ * @param array $args Plugin arguments, optional.
+ *
+ * @return $this
+ * @see $this->arguments_whitelist
+ */
+ public function add( $name, array $args = array() ) {
+ $args = compact( 'name' ) + array_intersect_key( $args, array_flip( $this->arguments_whitelist ) );
+
+ Plugin_Storage::upsert( $this->slug, $args );
+
+ return $this;
+ }
+
+ /**
+ * Remove the plugin connection info from Jetpack.
+ *
+ * @return $this
+ */
+ public function remove() {
+ Plugin_Storage::delete( $this->slug );
+
+ return $this;
+ }
+
+ /**
+ * Determine if this plugin connection is the only one active at the moment, if any.
+ *
+ * @return bool
+ */
+ public function is_only() {
+ $plugins = Plugin_Storage::get_all( true );
+
+ return ! $plugins || ( array_key_exists( $this->slug, $plugins ) && 1 === count( $plugins ) );
+ }
+
+ /**
+ * Add the plugin to the set of disconnected ones.
+ *
+ * @return bool
+ */
+ public function disable() {
+ return Plugin_Storage::disable_plugin( $this->slug );
+ }
+
+ /**
+ * Remove the plugin from the set of disconnected ones.
+ *
+ * @return bool
+ */
+ public function enable() {
+ return Plugin_Storage::enable_plugin( $this->slug );
+ }
+
+ /**
+ * Whether this plugin is allowed to use the connection.
+ *
+ * @return bool
+ */
+ public function is_enabled() {
+ return ! in_array( $this->slug, Plugin_Storage::get_all_disabled_plugins(), true );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-rest-authentication.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-rest-authentication.php
new file mode 100644
index 00000000..c5a89f53
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-rest-authentication.php
@@ -0,0 +1,220 @@
+<?php
+/**
+ * The Jetpack Connection Rest Authentication file.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+/**
+ * The Jetpack Connection Rest Authentication class.
+ */
+class Rest_Authentication {
+
+ /**
+ * The rest authentication status.
+ *
+ * @since 1.17.0
+ * @var boolean
+ */
+ private $rest_authentication_status = null;
+
+ /**
+ * The rest authentication type.
+ * Can be either 'user' or 'blog' depending on whether the request
+ * is signed with a user or a blog token.
+ *
+ * @since 1.29.0
+ * @var string
+ */
+ private $rest_authentication_type = null;
+
+ /**
+ * The Manager object.
+ *
+ * @since 1.17.0
+ * @var Object
+ */
+ private $connection_manager = null;
+
+ /**
+ * Holds the singleton instance of this class
+ *
+ * @since 1.17.0
+ * @var Object
+ */
+ private static $instance = false;
+
+ /**
+ * Flag used to avoid determine_current_user filter to enter an infinite loop
+ *
+ * @since 1.26.0
+ * @var boolean
+ */
+ private $doing_determine_current_user_filter = false;
+
+ /**
+ * The constructor.
+ */
+ private function __construct() {
+ $this->connection_manager = new Manager();
+ }
+
+ /**
+ * Controls the single instance of this class.
+ *
+ * @static
+ */
+ public static function init() {
+ if ( ! self::$instance ) {
+ self::$instance = new self();
+
+ add_filter( 'determine_current_user', array( self::$instance, 'wp_rest_authenticate' ) );
+ add_filter( 'rest_authentication_errors', array( self::$instance, 'wp_rest_authentication_errors' ) );
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Authenticates requests from Jetpack server to WP REST API endpoints.
+ * Uses the existing XMLRPC request signing implementation.
+ *
+ * @param int|bool $user User ID if one has been determined, false otherwise.
+ *
+ * @return int|null The user id or null if the request was authenticated via blog token, or not authenticated at all.
+ */
+ public function wp_rest_authenticate( $user ) {
+ if ( $this->doing_determine_current_user_filter ) {
+ return $user;
+ }
+
+ $this->doing_determine_current_user_filter = true;
+
+ try {
+ if ( ! empty( $user ) ) {
+ // Another authentication method is in effect.
+ return $user;
+ }
+
+ add_filter(
+ 'jetpack_constant_default_value',
+ __NAMESPACE__ . '\Utils::jetpack_api_constant_filter',
+ 10,
+ 2
+ );
+
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( ! isset( $_GET['_for'] ) || 'jetpack' !== $_GET['_for'] ) {
+ // Nothing to do for this authentication method.
+ return null;
+ }
+
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( ! isset( $_GET['token'] ) && ! isset( $_GET['signature'] ) ) {
+ // Nothing to do for this authentication method.
+ return null;
+ }
+
+ if ( ! isset( $_SERVER['REQUEST_METHOD'] ) ) {
+ $this->rest_authentication_status = new \WP_Error(
+ 'rest_invalid_request',
+ __( 'The request method is missing.', 'jetpack-connection' ),
+ array( 'status' => 400 )
+ );
+ return null;
+ }
+
+ // Only support specific request parameters that have been tested and
+ // are known to work with signature verification. A different method
+ // can be passed to the WP REST API via the '?_method=' parameter if
+ // needed.
+ if ( 'GET' !== $_SERVER['REQUEST_METHOD'] && 'POST' !== $_SERVER['REQUEST_METHOD'] ) {
+ $this->rest_authentication_status = new \WP_Error(
+ 'rest_invalid_request',
+ __( 'This request method is not supported.', 'jetpack-connection' ),
+ array( 'status' => 400 )
+ );
+ return null;
+ }
+ if ( 'POST' !== $_SERVER['REQUEST_METHOD'] && ! empty( file_get_contents( 'php://input' ) ) ) {
+ $this->rest_authentication_status = new \WP_Error(
+ 'rest_invalid_request',
+ __( 'This request method does not support body parameters.', 'jetpack-connection' ),
+ array( 'status' => 400 )
+ );
+ return null;
+ }
+
+ $verified = $this->connection_manager->verify_xml_rpc_signature();
+
+ if (
+ $verified &&
+ isset( $verified['type'] ) &&
+ 'blog' === $verified['type']
+ ) {
+ // Site-level authentication successful.
+ $this->rest_authentication_status = true;
+ $this->rest_authentication_type = 'blog';
+ return null;
+ }
+
+ if (
+ $verified &&
+ isset( $verified['type'] ) &&
+ 'user' === $verified['type'] &&
+ ! empty( $verified['user_id'] )
+ ) {
+ // User-level authentication successful.
+ $this->rest_authentication_status = true;
+ $this->rest_authentication_type = 'user';
+ return $verified['user_id'];
+ }
+
+ // Something else went wrong. Probably a signature error.
+ $this->rest_authentication_status = new \WP_Error(
+ 'rest_invalid_signature',
+ __( 'The request is not signed correctly.', 'jetpack-connection' ),
+ array( 'status' => 400 )
+ );
+ return null;
+ } finally {
+ $this->doing_determine_current_user_filter = false;
+ }
+ }
+
+ /**
+ * Report authentication status to the WP REST API.
+ *
+ * @param WP_Error|mixed $value Error from another authentication handler, null if we should handle it, or another value if not.
+ * @return WP_Error|boolean|null {@see WP_JSON_Server::check_authentication}
+ */
+ public function wp_rest_authentication_errors( $value ) {
+ if ( null !== $value ) {
+ return $value;
+ }
+ return $this->rest_authentication_status;
+ }
+
+ /**
+ * Resets the saved authentication state in between testing requests.
+ */
+ public function reset_saved_auth_state() {
+ $this->rest_authentication_status = null;
+ $this->connection_manager->reset_saved_auth_state();
+ }
+
+ /**
+ * Whether the request was signed with a blog token.
+ *
+ * @since 1.29.0
+ *
+ * @return bool True if the request was signed with a valid blog token, false otherwise.
+ */
+ public static function is_signed_with_blog_token() {
+ $instance = self::init();
+
+ return true === $instance->rest_authentication_status && 'blog' === $instance->rest_authentication_type;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-rest-connector.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-rest-connector.php
new file mode 100644
index 00000000..c327d7df
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-rest-connector.php
@@ -0,0 +1,860 @@
+<?php
+/**
+ * Sets up the Connection REST API endpoints.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+use Automattic\Jetpack\Constants;
+use Automattic\Jetpack\Redirect;
+use Automattic\Jetpack\Status;
+use Jetpack_XMLRPC_Server;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Registers the REST routes for Connections.
+ */
+class REST_Connector {
+ /**
+ * The Connection Manager.
+ *
+ * @var Manager
+ */
+ private $connection;
+
+ /**
+ * This property stores the localized "Insufficient Permissions" error message.
+ *
+ * @var string Generic error message when user is not allowed to perform an action.
+ */
+ private static $user_permissions_error_msg;
+
+ const JETPACK__DEBUGGER_PUBLIC_KEY = "\r\n" . '-----BEGIN PUBLIC KEY-----' . "\r\n"
+ . 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm+uLLVoxGCY71LS6KFc6' . "\r\n"
+ . '1UnF6QGBAsi5XF8ty9kR3/voqfOkpW+gRerM2Kyjy6DPCOmzhZj7BFGtxSV2ZoMX' . "\r\n"
+ . '9ZwWxzXhl/Q/6k8jg8BoY1QL6L2K76icXJu80b+RDIqvOfJruaAeBg1Q9NyeYqLY' . "\r\n"
+ . 'lEVzN2vIwcFYl+MrP/g6Bc2co7Jcbli+tpNIxg4Z+Hnhbs7OJ3STQLmEryLpAxQO' . "\r\n"
+ . 'q8cbhQkMx+FyQhxzSwtXYI/ClCUmTnzcKk7SgGvEjoKGAmngILiVuEJ4bm7Q1yok' . "\r\n"
+ . 'xl9+wcfW6JAituNhml9dlHCWnn9D3+j8pxStHihKy2gVMwiFRjLEeD8K/7JVGkb/' . "\r\n"
+ . 'EwIDAQAB' . "\r\n"
+ . '-----END PUBLIC KEY-----' . "\r\n";
+
+ /**
+ * Constructor.
+ *
+ * @param Manager $connection The Connection Manager.
+ */
+ public function __construct( Manager $connection ) {
+ $this->connection = $connection;
+
+ self::$user_permissions_error_msg = esc_html__(
+ 'You do not have the correct user permissions to perform this action.
+ Please contact your site admin if you think this is a mistake.',
+ 'jetpack-connection'
+ );
+
+ $jp_version = Constants::get_constant( 'JETPACK__VERSION' );
+
+ if ( ! $this->connection->has_connected_owner() ) {
+ // Register a site.
+ register_rest_route(
+ 'jetpack/v4',
+ '/verify_registration',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => array( $this, 'verify_registration' ),
+ 'permission_callback' => '__return_true',
+ )
+ );
+ }
+
+ // Authorize a remote user.
+ register_rest_route(
+ 'jetpack/v4',
+ '/remote_authorize',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::remote_authorize',
+ 'permission_callback' => '__return_true',
+ )
+ );
+
+ // Get current connection status of Jetpack.
+ register_rest_route(
+ 'jetpack/v4',
+ '/connection',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::connection_status',
+ 'permission_callback' => '__return_true',
+ )
+ );
+
+ // Disconnect site.
+ register_rest_route(
+ 'jetpack/v4',
+ '/connection',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::disconnect_site',
+ 'permission_callback' => __CLASS__ . '::disconnect_site_permission_check',
+ 'args' => array(
+ 'isActive' => array(
+ 'description' => __( 'Set to false will trigger the site to disconnect.', 'jetpack-connection' ),
+ 'validate_callback' => function ( $value ) {
+ if ( false !== $value ) {
+ return new WP_Error(
+ 'rest_invalid_param',
+ __( 'The isActive argument should be set to false.', 'jetpack-connection' ),
+ array( 'status' => 400 )
+ );
+ }
+
+ return true;
+ },
+ 'required' => true,
+ ),
+ ),
+ )
+ );
+
+ // We are only registering this route if Jetpack-the-plugin is not active or it's version is ge 10.0-alpha.
+ // The reason for doing so is to avoid conflicts between the Connection package and
+ // older versions of Jetpack, registering the same route twice.
+ if ( empty( $jp_version ) || version_compare( $jp_version, '10.0-alpha', '>=' ) ) {
+ // Get current user connection data.
+ register_rest_route(
+ 'jetpack/v4',
+ '/connection/data',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::get_user_connection_data',
+ 'permission_callback' => __CLASS__ . '::user_connection_data_permission_check',
+ )
+ );
+ }
+
+ // Get list of plugins that use the Jetpack connection.
+ register_rest_route(
+ 'jetpack/v4',
+ '/connection/plugins',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'get_connection_plugins' ),
+ 'permission_callback' => __CLASS__ . '::connection_plugins_permission_check',
+ )
+ );
+
+ // Full or partial reconnect in case of connection issues.
+ register_rest_route(
+ 'jetpack/v4',
+ '/connection/reconnect',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => array( $this, 'connection_reconnect' ),
+ 'permission_callback' => __CLASS__ . '::jetpack_reconnect_permission_check',
+ )
+ );
+
+ // Register the site (get `blog_token`).
+ register_rest_route(
+ 'jetpack/v4',
+ '/connection/register',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => array( $this, 'connection_register' ),
+ 'permission_callback' => __CLASS__ . '::jetpack_register_permission_check',
+ 'args' => array(
+ 'from' => array(
+ 'description' => __( 'Indicates where the registration action was triggered for tracking/segmentation purposes', 'jetpack-connection' ),
+ 'type' => 'string',
+ ),
+ 'registration_nonce' => array(
+ 'description' => __( 'The registration nonce', 'jetpack-connection' ),
+ 'type' => 'string',
+ 'required' => true,
+ ),
+ 'no_iframe' => array(
+ 'description' => __( 'Disable In-Place connection flow and go straight to Calypso', 'jetpack-connection' ),
+ 'type' => 'boolean',
+ ),
+ 'redirect_uri' => array(
+ 'description' => __( 'URI of the admin page where the user should be redirected after connection flow', 'jetpack-connection' ),
+ 'type' => 'string',
+ ),
+ 'plugin_slug' => array(
+ 'description' => __( 'Indicates from what plugin the request is coming from', 'jetpack-connection' ),
+ 'type' => 'string',
+ ),
+ ),
+ )
+ );
+
+ // Get authorization URL.
+ register_rest_route(
+ 'jetpack/v4',
+ '/connection/authorize_url',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'connection_authorize_url' ),
+ 'permission_callback' => __CLASS__ . '::user_connection_data_permission_check',
+ 'args' => array(
+ 'no_iframe' => array(
+ 'description' => __( 'Disable In-Place connection flow and go straight to Calypso', 'jetpack-connection' ),
+ 'type' => 'boolean',
+ ),
+ 'redirect_uri' => array(
+ 'description' => __( 'URI of the admin page where the user should be redirected after connection flow', 'jetpack-connection' ),
+ 'type' => 'string',
+ ),
+ ),
+ )
+ );
+
+ register_rest_route(
+ 'jetpack/v4',
+ '/user-token',
+ array(
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => array( static::class, 'update_user_token' ),
+ 'permission_callback' => array( static::class, 'update_user_token_permission_check' ),
+ 'args' => array(
+ 'user_token' => array(
+ 'description' => __( 'New user token', 'jetpack-connection' ),
+ 'type' => 'string',
+ 'required' => true,
+ ),
+ 'is_connection_owner' => array(
+ 'description' => __( 'Is connection owner', 'jetpack-connection' ),
+ 'type' => 'boolean',
+ ),
+ ),
+ ),
+ )
+ );
+
+ // Set the connection owner.
+ register_rest_route(
+ 'jetpack/v4',
+ '/connection/owner',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => array( static::class, 'set_connection_owner' ),
+ 'permission_callback' => array( static::class, 'set_connection_owner_permission_check' ),
+ 'args' => array(
+ 'owner' => array(
+ 'description' => __( 'New owner', 'jetpack-connection' ),
+ 'type' => 'integer',
+ 'required' => true,
+ ),
+ ),
+ )
+ );
+ }
+
+ /**
+ * Handles verification that a site is registered.
+ *
+ * @since 1.7.0
+ * @since-jetpack 5.4.0
+ *
+ * @param WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return string|WP_Error
+ */
+ public function verify_registration( WP_REST_Request $request ) {
+ $registration_data = array( $request['secret_1'], $request['state'] );
+
+ return $this->connection->handle_registration( $registration_data );
+ }
+
+ /**
+ * Handles verification that a site is registered
+ *
+ * @since 1.7.0
+ * @since-jetpack 5.4.0
+ *
+ * @param WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return array|wp-error
+ */
+ public static function remote_authorize( $request ) {
+ $xmlrpc_server = new Jetpack_XMLRPC_Server();
+ $result = $xmlrpc_server->remote_authorize( $request );
+
+ if ( is_a( $result, 'IXR_Error' ) ) {
+ $result = new WP_Error( $result->code, $result->message );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get connection status for this Jetpack site.
+ *
+ * @since 1.7.0
+ * @since-jetpack 4.3.0
+ *
+ * @param bool $rest_response Should we return a rest response or a simple array. Default to rest response.
+ *
+ * @return WP_REST_Response|array Connection information.
+ */
+ public static function connection_status( $rest_response = true ) {
+ $status = new Status();
+ $connection = new Manager();
+
+ $connection_status = array(
+ 'isActive' => $connection->is_active(), // TODO deprecate this.
+ 'isStaging' => $status->is_staging_site(),
+ 'isRegistered' => $connection->is_connected(),
+ 'isUserConnected' => $connection->is_user_connected(),
+ 'hasConnectedOwner' => $connection->has_connected_owner(),
+ 'offlineMode' => array(
+ 'isActive' => $status->is_offline_mode(),
+ 'constant' => defined( 'JETPACK_DEV_DEBUG' ) && JETPACK_DEV_DEBUG,
+ 'url' => $status->is_local_site(),
+ /** This filter is documented in packages/status/src/class-status.php */
+ 'filter' => ( apply_filters( 'jetpack_development_mode', false ) || apply_filters( 'jetpack_offline_mode', false ) ), // jetpack_development_mode is deprecated.
+ 'wpLocalConstant' => defined( 'WP_LOCAL_DEV' ) && WP_LOCAL_DEV,
+ ),
+ 'isPublic' => '1' == get_option( 'blog_public' ), // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
+ );
+
+ /**
+ * Filters the connection status data.
+ *
+ * @since 1.25.0
+ *
+ * @param array An array containing the connection status data.
+ */
+ $connection_status = apply_filters( 'jetpack_connection_status', $connection_status );
+
+ if ( $rest_response ) {
+ return rest_ensure_response(
+ $connection_status
+ );
+ } else {
+ return $connection_status;
+ }
+ }
+
+ /**
+ * Get plugins connected to the Jetpack.
+ *
+ * @since 1.13.1
+ *
+ * @return WP_REST_Response|WP_Error Response or error object, depending on the request result.
+ */
+ public function get_connection_plugins() {
+ $plugins = $this->connection->get_connected_plugins();
+
+ if ( is_wp_error( $plugins ) ) {
+ return $plugins;
+ }
+
+ array_walk(
+ $plugins,
+ function ( &$data, $slug ) {
+ $data['slug'] = $slug;
+ }
+ );
+
+ return rest_ensure_response( array_values( $plugins ) );
+ }
+
+ /**
+ * Verify that user can view Jetpack admin page and can activate plugins.
+ *
+ * @since 1.15.0
+ *
+ * @return bool|WP_Error Whether user has the capability 'activate_plugins'.
+ */
+ public static function activate_plugins_permission_check() {
+ if ( current_user_can( 'activate_plugins' ) ) {
+ return true;
+ }
+
+ return new WP_Error( 'invalid_user_permission_activate_plugins', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
+ }
+
+ /**
+ * Permission check for the connection_plugins endpoint
+ *
+ * @return bool|WP_Error
+ */
+ public static function connection_plugins_permission_check() {
+ if ( true === static::activate_plugins_permission_check() ) {
+ return true;
+ }
+
+ if ( true === static::is_request_signed_by_jetpack_debugger() ) {
+ return true;
+ }
+
+ return new WP_Error( 'invalid_user_permission_activate_plugins', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
+
+ }
+
+ /**
+ * Permission check for the disconnect site endpoint.
+ *
+ * @since 1.30.1
+ *
+ * @return bool|WP_Error True if user is able to disconnect the site.
+ */
+ public static function disconnect_site_permission_check() {
+ if ( current_user_can( 'jetpack_disconnect' ) ) {
+ return true;
+ }
+
+ return new WP_Error(
+ 'invalid_user_permission_jetpack_disconnect',
+ self::get_user_permissions_error_msg(),
+ array( 'status' => rest_authorization_required_code() )
+ );
+ }
+
+ /**
+ * Get miscellaneous user data related to the connection. Similar data available in old "My Jetpack".
+ * Information about the master/primary user.
+ * Information about the current user.
+ *
+ * @since 1.30.1
+ *
+ * @return \WP_REST_Response
+ */
+ public static function get_user_connection_data() {
+ $connection = new Manager();
+
+ $current_user = wp_get_current_user();
+ $connection_owner = $connection->get_connection_owner();
+
+ $owner_display_name = false === $connection_owner ? null : $connection_owner->display_name;
+
+ $is_user_connected = $connection->is_user_connected();
+ $is_master_user = false === $connection_owner ? false : ( $current_user->ID === $connection_owner->ID );
+ $wpcom_user_data = $connection->get_connected_user_data();
+
+ // Add connected user gravatar to the returned wpcom_user_data.
+ // Probably we shouldn't do this when $wpcom_user_data is false, but we have been since 2016 so
+ // clients probably expect that by now.
+ if ( false === $wpcom_user_data ) {
+ $wpcom_user_data = array();
+ }
+ $wpcom_user_data['avatar'] = ( ! empty( $wpcom_user_data['email'] ) ?
+ get_avatar_url(
+ $wpcom_user_data['email'],
+ array(
+ 'size' => 64,
+ 'default' => 'mysteryman',
+ )
+ )
+ : false );
+
+ $current_user_connection_data = array(
+ 'isConnected' => $is_user_connected,
+ 'isMaster' => $is_master_user,
+ 'username' => $current_user->user_login,
+ 'id' => $current_user->ID,
+ 'wpcomUser' => $wpcom_user_data,
+ 'gravatar' => get_avatar_url( $current_user->ID, 64, 'mm', '', array( 'force_display' => true ) ),
+ 'permissions' => array(
+ 'connect' => current_user_can( 'jetpack_connect' ),
+ 'connect_user' => current_user_can( 'jetpack_connect_user' ),
+ 'disconnect' => current_user_can( 'jetpack_disconnect' ),
+ ),
+ );
+
+ /**
+ * Filters the current user connection data.
+ *
+ * @since 1.30.1
+ *
+ * @param array An array containing the current user connection data.
+ */
+ $current_user_connection_data = apply_filters( 'jetpack_current_user_connection_data', $current_user_connection_data );
+
+ $response = array(
+ 'currentUser' => $current_user_connection_data,
+ 'connectionOwner' => $owner_display_name,
+ );
+ return rest_ensure_response( $response );
+ }
+
+ /**
+ * Permission check for the connection/data endpoint
+ *
+ * @return bool|WP_Error
+ */
+ public static function user_connection_data_permission_check() {
+ if ( current_user_can( 'jetpack_connect_user' ) ) {
+ return true;
+ }
+
+ return new WP_Error(
+ 'invalid_user_permission_user_connection_data',
+ self::get_user_permissions_error_msg(),
+ array( 'status' => rest_authorization_required_code() )
+ );
+
+ }
+
+ /**
+ * Verifies if the request was signed with the Jetpack Debugger key
+ *
+ * @param string|null $pub_key The public key used to verify the signature. Default is the Jetpack Debugger key. This is used for testing purposes.
+ *
+ * @return bool
+ */
+ public static function is_request_signed_by_jetpack_debugger( $pub_key = null ) {
+ // phpcs:disable WordPress.Security.NonceVerification.Recommended
+ if ( ! isset( $_GET['signature'], $_GET['timestamp'], $_GET['url'], $_GET['rest_route'] ) ) {
+ return false;
+ }
+
+ // signature timestamp must be within 5min of current time.
+ if ( abs( time() - (int) $_GET['timestamp'] ) > 300 ) {
+ return false;
+ }
+
+ $signature = base64_decode( $_GET['signature'] ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
+
+ $signature_data = wp_json_encode(
+ array(
+ 'rest_route' => $_GET['rest_route'],
+ 'timestamp' => (int) $_GET['timestamp'],
+ 'url' => wp_unslash( $_GET['url'] ),
+ )
+ );
+
+ if (
+ ! function_exists( 'openssl_verify' )
+ || 1 !== openssl_verify(
+ $signature_data,
+ $signature,
+ $pub_key ? $pub_key : static::JETPACK__DEBUGGER_PUBLIC_KEY
+ )
+ ) {
+ return false;
+ }
+
+ // phpcs:enable WordPress.Security.NonceVerification.Recommended
+
+ return true;
+ }
+
+ /**
+ * Verify that user is allowed to disconnect Jetpack.
+ *
+ * @since 1.15.0
+ *
+ * @return bool|WP_Error Whether user has the capability 'jetpack_disconnect'.
+ */
+ public static function jetpack_reconnect_permission_check() {
+ if ( current_user_can( 'jetpack_reconnect' ) ) {
+ return true;
+ }
+
+ return new WP_Error( 'invalid_user_permission_jetpack_disconnect', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
+ }
+
+ /**
+ * Returns generic error message when user is not allowed to perform an action.
+ *
+ * @return string The error message.
+ */
+ public static function get_user_permissions_error_msg() {
+ return self::$user_permissions_error_msg;
+ }
+
+ /**
+ * The endpoint tried to partially or fully reconnect the website to WP.com.
+ *
+ * @since 1.15.0
+ *
+ * @return \WP_REST_Response|WP_Error
+ */
+ public function connection_reconnect() {
+ $response = array();
+
+ $next = null;
+
+ $result = $this->connection->restore();
+
+ if ( is_wp_error( $result ) ) {
+ $response = $result;
+ } elseif ( is_string( $result ) ) {
+ $next = $result;
+ } else {
+ $next = true === $result ? 'completed' : 'failed';
+ }
+
+ switch ( $next ) {
+ case 'authorize':
+ $response['status'] = 'in_progress';
+ $response['authorizeUrl'] = $this->connection->get_authorization_url();
+ break;
+ case 'completed':
+ $response['status'] = 'completed';
+ /**
+ * Action fired when reconnection has completed successfully.
+ *
+ * @since 1.18.1
+ */
+ do_action( 'jetpack_reconnection_completed' );
+ break;
+ case 'failed':
+ $response = new WP_Error( 'Reconnect failed' );
+ break;
+ }
+
+ return rest_ensure_response( $response );
+ }
+
+ /**
+ * Verify that user is allowed to connect Jetpack.
+ *
+ * @since 1.26.0
+ *
+ * @return bool|WP_Error Whether user has the capability 'jetpack_connect'.
+ */
+ public static function jetpack_register_permission_check() {
+ if ( current_user_can( 'jetpack_connect' ) ) {
+ return true;
+ }
+
+ return new WP_Error( 'invalid_user_permission_jetpack_connect', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
+ }
+
+ /**
+ * The endpoint tried to partially or fully reconnect the website to WP.com.
+ *
+ * @since 1.7.0
+ * @since-jetpack 7.7.0
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response|WP_Error
+ */
+ public function connection_register( $request ) {
+ if ( ! wp_verify_nonce( $request->get_param( 'registration_nonce' ), 'jetpack-registration-nonce' ) ) {
+ return new WP_Error( 'invalid_nonce', __( 'Unable to verify your request.', 'jetpack-connection' ), array( 'status' => 403 ) );
+ }
+
+ if ( isset( $request['from'] ) ) {
+ $this->connection->add_register_request_param( 'from', (string) $request['from'] );
+ }
+
+ if ( ! empty( $request['plugin_slug'] ) ) {
+ // If `plugin_slug` matches a plugin using the connection, let's inform the plugin that is establishing the connection.
+ $connected_plugin = Plugin_Storage::get_one( (string) $request['plugin_slug'] );
+ if ( ! is_wp_error( $connected_plugin ) && ! empty( $connected_plugin ) ) {
+ $this->connection->set_plugin_instance( new Plugin( (string) $request['plugin_slug'] ) );
+ }
+ }
+
+ $result = $this->connection->try_registration();
+
+ if ( is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ $redirect_uri = $request->get_param( 'redirect_uri' ) ? admin_url( $request->get_param( 'redirect_uri' ) ) : null;
+
+ if ( class_exists( 'Jetpack' ) ) {
+ $authorize_url = \Jetpack::build_authorize_url( $redirect_uri, ! $request->get_param( 'no_iframe' ) );
+ } else {
+ if ( ! $request->get_param( 'no_iframe' ) ) {
+ add_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' );
+ }
+
+ $authorize_url = $this->connection->get_authorization_url( null, $redirect_uri );
+
+ if ( ! $request->get_param( 'no_iframe' ) ) {
+ remove_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' );
+ }
+ }
+
+ /**
+ * Filters the response of jetpack/v4/connection/register endpoint
+ *
+ * @param array $response Array response
+ * @since 1.27.0
+ */
+ $response_body = apply_filters(
+ 'jetpack_register_site_rest_response',
+ array()
+ );
+
+ // We manipulate the alternate URLs after the filter is applied, so they can not be overwritten.
+ $response_body['authorizeUrl'] = $authorize_url;
+ if ( ! empty( $response_body['alternateAuthorizeUrl'] ) ) {
+ $response_body['alternateAuthorizeUrl'] = Redirect::get_url( $response_body['alternateAuthorizeUrl'] );
+ }
+
+ return rest_ensure_response( $response_body );
+ }
+
+ /**
+ * Get the authorization URL.
+ *
+ * @since 1.27.0
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response|WP_Error
+ */
+ public function connection_authorize_url( $request ) {
+ $redirect_uri = $request->get_param( 'redirect_uri' ) ? admin_url( $request->get_param( 'redirect_uri' ) ) : null;
+
+ if ( ! $request->get_param( 'no_iframe' ) ) {
+ add_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' );
+ }
+
+ $authorize_url = $this->connection->get_authorization_url( null, $redirect_uri );
+
+ if ( ! $request->get_param( 'no_iframe' ) ) {
+ remove_filter( 'jetpack_use_iframe_authorization_flow', '__return_true' );
+ }
+
+ return rest_ensure_response(
+ array(
+ 'authorizeUrl' => $authorize_url,
+ )
+ );
+ }
+
+ /**
+ * The endpoint tried to partially or fully reconnect the website to WP.com.
+ *
+ * @since 1.29.0
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response|WP_Error
+ */
+ public static function update_user_token( $request ) {
+ $token_parts = explode( '.', $request['user_token'] );
+
+ if ( count( $token_parts ) !== 3 || ! (int) $token_parts[2] || ! ctype_digit( $token_parts[2] ) ) {
+ return new WP_Error( 'invalid_argument_user_token', esc_html__( 'Invalid user token is provided', 'jetpack-connection' ) );
+ }
+
+ $user_id = (int) $token_parts[2];
+
+ if ( false === get_userdata( $user_id ) ) {
+ return new WP_Error( 'invalid_argument_user_id', esc_html__( 'Invalid user id is provided', 'jetpack-connection' ) );
+ }
+
+ $connection = new Manager();
+
+ if ( ! $connection->is_connected() ) {
+ return new WP_Error( 'site_not_connected', esc_html__( 'Site is not connected', 'jetpack-connection' ) );
+ }
+
+ $is_connection_owner = isset( $request['is_connection_owner'] )
+ ? (bool) $request['is_connection_owner']
+ : ( new Manager() )->get_connection_owner_id() === $user_id;
+
+ ( new Tokens() )->update_user_token( $user_id, $request['user_token'], $is_connection_owner );
+
+ /**
+ * Fires when the user token gets successfully replaced.
+ *
+ * @since 1.29.0
+ *
+ * @param int $user_id User ID.
+ * @param string $token New user token.
+ */
+ do_action( 'jetpack_updated_user_token', $user_id, $request['user_token'] );
+
+ return rest_ensure_response(
+ array(
+ 'success' => true,
+ )
+ );
+ }
+
+ /**
+ * Disconnects Jetpack from the WordPress.com Servers
+ *
+ * @since 1.30.1
+ *
+ * @return bool|WP_Error True if Jetpack successfully disconnected.
+ */
+ public static function disconnect_site() {
+ $connection = new Manager();
+
+ if ( $connection->is_connected() ) {
+ $connection->disconnect_site();
+ return rest_ensure_response( array( 'code' => 'success' ) );
+ }
+
+ return new WP_Error(
+ 'disconnect_failed',
+ esc_html__( 'Failed to disconnect the site as it appears already disconnected.', 'jetpack-connection' ),
+ array( 'status' => 400 )
+ );
+ }
+
+ /**
+ * Verify that the API client is allowed to replace user token.
+ *
+ * @since 1.29.0
+ *
+ * @return bool|WP_Error.
+ */
+ public static function update_user_token_permission_check() {
+ return Rest_Authentication::is_signed_with_blog_token()
+ ? true
+ : new WP_Error( 'invalid_permission_update_user_token', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
+ }
+
+ /**
+ * Change the connection owner.
+ *
+ * @since 1.29.0
+ *
+ * @param WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response|WP_Error
+ */
+ public static function set_connection_owner( $request ) {
+ $new_owner_id = $request['owner'];
+
+ $owner_set = ( new Manager() )->update_connection_owner( $new_owner_id );
+
+ if ( is_wp_error( $owner_set ) ) {
+ return $owner_set;
+ }
+
+ return rest_ensure_response(
+ array(
+ 'code' => 'success',
+ )
+ );
+ }
+
+ /**
+ * Check that user has permission to change the master user.
+ *
+ * @since 1.7.0
+ * @since-jetpack 6.2.0
+ * @since-jetpack 7.7.0 Update so that any user with jetpack_disconnect privs can set owner.
+ *
+ * @return bool|WP_Error True if user is able to change master user.
+ */
+ public static function set_connection_owner_permission_check() {
+ if ( current_user_can( 'jetpack_disconnect' ) ) {
+ return true;
+ }
+
+ return new WP_Error( 'invalid_user_permission_set_connection_owner', self::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-secrets.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-secrets.php
new file mode 100644
index 00000000..f91acb43
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-secrets.php
@@ -0,0 +1,281 @@
+<?php
+/**
+ * The Jetpack Connection Secrets class file.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+use Jetpack_Options;
+use WP_Error;
+
+/**
+ * The Jetpack Connection Secrets class that is used to manage secrets.
+ */
+class Secrets {
+
+ const SECRETS_MISSING = 'secrets_missing';
+ const SECRETS_EXPIRED = 'secrets_expired';
+ const LEGACY_SECRETS_OPTION_NAME = 'jetpack_secrets';
+
+ /**
+ * Deletes all connection secrets from the local Jetpack site.
+ */
+ public function delete_all() {
+ Jetpack_Options::delete_raw_option( 'jetpack_secrets' );
+ }
+
+ /**
+ * Runs the wp_generate_password function with the required parameters. This is the
+ * default implementation of the secret callable, can be overridden using the
+ * jetpack_connection_secret_generator filter.
+ *
+ * @return String $secret value.
+ */
+ private function secret_callable_method() {
+ $secret = wp_generate_password( 32, false );
+
+ // Some sites may hook into the random_password filter and make the password shorter, let's make sure our secret has the required length.
+ $attempts = 1;
+ $secret_length = strlen( $secret );
+ while ( $secret_length < 32 && $attempts < 32 ) {
+ $attempts++;
+ $secret .= wp_generate_password( 32, false );
+ $secret_length = strlen( $secret );
+ }
+ return (string) substr( $secret, 0, 32 );
+ }
+
+ /**
+ * Generates two secret tokens and the end of life timestamp for them.
+ *
+ * @param String $action The action name.
+ * @param Integer|bool $user_id The user identifier. Defaults to `false`.
+ * @param Integer $exp Expiration time in seconds.
+ */
+ public function generate( $action, $user_id = false, $exp = 600 ) {
+ if ( false === $user_id ) {
+ $user_id = get_current_user_id();
+ }
+
+ $callable = apply_filters( 'jetpack_connection_secret_generator', array( get_called_class(), 'secret_callable_method' ) );
+
+ $secrets = Jetpack_Options::get_raw_option(
+ self::LEGACY_SECRETS_OPTION_NAME,
+ array()
+ );
+
+ $secret_name = 'jetpack_' . $action . '_' . $user_id;
+
+ if (
+ isset( $secrets[ $secret_name ] ) &&
+ $secrets[ $secret_name ]['exp'] > time()
+ ) {
+ return $secrets[ $secret_name ];
+ }
+
+ $secret_value = array(
+ 'secret_1' => call_user_func( $callable ),
+ 'secret_2' => call_user_func( $callable ),
+ 'exp' => time() + $exp,
+ );
+
+ $secrets[ $secret_name ] = $secret_value;
+
+ $res = Jetpack_Options::update_raw_option( self::LEGACY_SECRETS_OPTION_NAME, $secrets );
+ return $res ? $secrets[ $secret_name ] : false;
+ }
+
+ /**
+ * Returns two secret tokens and the end of life timestamp for them.
+ *
+ * @param String $action The action name.
+ * @param Integer $user_id The user identifier.
+ * @return string|array an array of secrets or an error string.
+ */
+ public function get( $action, $user_id ) {
+ $secret_name = 'jetpack_' . $action . '_' . $user_id;
+ $secrets = Jetpack_Options::get_raw_option(
+ self::LEGACY_SECRETS_OPTION_NAME,
+ array()
+ );
+
+ if ( ! isset( $secrets[ $secret_name ] ) ) {
+ return self::SECRETS_MISSING;
+ }
+
+ if ( $secrets[ $secret_name ]['exp'] < time() ) {
+ $this->delete( $action, $user_id );
+ return self::SECRETS_EXPIRED;
+ }
+
+ return $secrets[ $secret_name ];
+ }
+
+ /**
+ * Deletes secret tokens in case they, for example, have expired.
+ *
+ * @param String $action The action name.
+ * @param Integer $user_id The user identifier.
+ */
+ public function delete( $action, $user_id ) {
+ $secret_name = 'jetpack_' . $action . '_' . $user_id;
+ $secrets = Jetpack_Options::get_raw_option(
+ self::LEGACY_SECRETS_OPTION_NAME,
+ array()
+ );
+ if ( isset( $secrets[ $secret_name ] ) ) {
+ unset( $secrets[ $secret_name ] );
+ Jetpack_Options::update_raw_option( self::LEGACY_SECRETS_OPTION_NAME, $secrets );
+ }
+ }
+
+ /**
+ * Verify a Previously Generated Secret.
+ *
+ * @param string $action The type of secret to verify.
+ * @param string $secret_1 The secret string to compare to what is stored.
+ * @param int $user_id The user ID of the owner of the secret.
+ * @return WP_Error|string WP_Error on failure, secret_2 on success.
+ */
+ public function verify( $action, $secret_1, $user_id ) {
+ $allowed_actions = array( 'register', 'authorize', 'publicize' );
+ if ( ! in_array( $action, $allowed_actions, true ) ) {
+ return new WP_Error( 'unknown_verification_action', 'Unknown Verification Action', 400 );
+ }
+
+ $user = get_user_by( 'id', $user_id );
+
+ /**
+ * We've begun verifying the previously generated secret.
+ *
+ * @since 1.7.0
+ * @since-jetpack 7.5.0
+ *
+ * @param string $action The type of secret to verify.
+ * @param \WP_User $user The user object.
+ */
+ do_action( 'jetpack_verify_secrets_begin', $action, $user );
+
+ $return_error = function ( WP_Error $error ) use ( $action, $user ) {
+ /**
+ * Verifying of the previously generated secret has failed.
+ *
+ * @since 1.7.0
+ * @since-jetpack 7.5.0
+ *
+ * @param string $action The type of secret to verify.
+ * @param \WP_User $user The user object.
+ * @param WP_Error $error The error object.
+ */
+ do_action( 'jetpack_verify_secrets_fail', $action, $user, $error );
+
+ return $error;
+ };
+
+ $stored_secrets = $this->get( $action, $user_id );
+ $this->delete( $action, $user_id );
+
+ $error = null;
+ if ( empty( $secret_1 ) ) {
+ $error = $return_error(
+ new WP_Error(
+ 'verify_secret_1_missing',
+ /* translators: "%s" is the name of a paramter. It can be either "secret_1" or "state". */
+ sprintf( __( 'The required "%s" parameter is missing.', 'jetpack-connection' ), 'secret_1' ),
+ 400
+ )
+ );
+ } elseif ( ! is_string( $secret_1 ) ) {
+ $error = $return_error(
+ new WP_Error(
+ 'verify_secret_1_malformed',
+ /* translators: "%s" is the name of a paramter. It can be either "secret_1" or "state". */
+ sprintf( __( 'The required "%s" parameter is malformed.', 'jetpack-connection' ), 'secret_1' ),
+ 400
+ )
+ );
+ } elseif ( empty( $user_id ) ) {
+ // $user_id is passed around during registration as "state".
+ $error = $return_error(
+ new WP_Error(
+ 'state_missing',
+ /* translators: "%s" is the name of a paramter. It can be either "secret_1" or "state". */
+ sprintf( __( 'The required "%s" parameter is missing.', 'jetpack-connection' ), 'state' ),
+ 400
+ )
+ );
+ } elseif ( ! ctype_digit( (string) $user_id ) ) {
+ $error = $return_error(
+ new WP_Error(
+ 'state_malformed',
+ /* translators: "%s" is the name of a paramter. It can be either "secret_1" or "state". */
+ sprintf( __( 'The required "%s" parameter is malformed.', 'jetpack-connection' ), 'state' ),
+ 400
+ )
+ );
+ } elseif ( self::SECRETS_MISSING === $stored_secrets ) {
+ $error = $return_error(
+ new WP_Error(
+ 'verify_secrets_missing',
+ __( 'Verification secrets not found', 'jetpack-connection' ),
+ 400
+ )
+ );
+ } elseif ( self::SECRETS_EXPIRED === $stored_secrets ) {
+ $error = $return_error(
+ new WP_Error(
+ 'verify_secrets_expired',
+ __( 'Verification took too long', 'jetpack-connection' ),
+ 400
+ )
+ );
+ } elseif ( ! $stored_secrets ) {
+ $error = $return_error(
+ new WP_Error(
+ 'verify_secrets_empty',
+ __( 'Verification secrets are empty', 'jetpack-connection' ),
+ 400
+ )
+ );
+ } elseif ( is_wp_error( $stored_secrets ) ) {
+ $stored_secrets->add_data( 400 );
+ $error = $return_error( $stored_secrets );
+ } elseif ( empty( $stored_secrets['secret_1'] ) || empty( $stored_secrets['secret_2'] ) || empty( $stored_secrets['exp'] ) ) {
+ $error = $return_error(
+ new WP_Error(
+ 'verify_secrets_incomplete',
+ __( 'Verification secrets are incomplete', 'jetpack-connection' ),
+ 400
+ )
+ );
+ } elseif ( ! hash_equals( $secret_1, $stored_secrets['secret_1'] ) ) {
+ $error = $return_error(
+ new WP_Error(
+ 'verify_secrets_mismatch',
+ __( 'Secret mismatch', 'jetpack-connection' ),
+ 400
+ )
+ );
+ }
+
+ // Something went wrong during the checks, returning the error.
+ if ( ! empty( $error ) ) {
+ return $error;
+ }
+
+ /**
+ * We've succeeded at verifying the previously generated secret.
+ *
+ * @since 1.7.0
+ * @since-jetpack 7.5.0
+ *
+ * @param string $action The type of secret to verify.
+ * @param \WP_User $user The user object.
+ */
+ do_action( 'jetpack_verify_secrets_success', $action, $user );
+
+ return $stored_secrets['secret_2'];
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-server-sandbox.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-server-sandbox.php
new file mode 100644
index 00000000..619194ad
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-server-sandbox.php
@@ -0,0 +1,132 @@
+<?php
+/**
+ * The Server_Sandbox class.
+ *
+ * This feature is only useful for Automattic developers.
+ * It configures Jetpack to talk to staging/sandbox servers
+ * on WordPress.com instead of production servers.
+ *
+ * @package automattic/jetpack-sandbox
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+use Automattic\Jetpack\Constants;
+
+/**
+ * The Server_Sandbox class.
+ */
+class Server_Sandbox {
+
+ /**
+ * Sets up the action hooks for the server sandbox.
+ */
+ public function init() {
+ if ( did_action( 'jetpack_server_sandbox_init' ) ) {
+ return;
+ }
+
+ add_action( 'requests-requests.before_request', array( $this, 'server_sandbox' ), 10, 2 );
+ add_action( 'admin_bar_menu', array( $this, 'admin_bar_add_sandbox_item' ), 999 );
+
+ /**
+ * Fires when the server sandbox is initialized. This action is used to ensure that
+ * the server sandbox action hooks are set up only once.
+ *
+ * @since $$next_version$$
+ */
+ do_action( 'jetpack_server_sandbox_init' );
+ }
+
+ /**
+ * Returns the new url and host values.
+ *
+ * @param string $sandbox Sandbox domain.
+ * @param string $url URL of request about to be made.
+ * @param array $headers Headers of request about to be made.
+ *
+ * @return array [ 'url' => new URL, 'host' => new Host ]
+ */
+ public function server_sandbox_request_parameters( $sandbox, $url, $headers ) {
+ $host = '';
+
+ if ( ! is_string( $sandbox ) || ! is_string( $url ) ) {
+ return array(
+ 'url' => $url,
+ 'host' => $host,
+ );
+ }
+
+ $url_host = wp_parse_url( $url, PHP_URL_HOST );
+
+ switch ( $url_host ) {
+ case 'public-api.wordpress.com':
+ case 'jetpack.wordpress.com':
+ case 'jetpack.com':
+ case 'dashboard.wordpress.com':
+ $host = isset( $headers['Host'] ) ? $headers['Host'] : $url_host;
+ $url = preg_replace(
+ '@^(https?://)' . preg_quote( $url_host, '@' ) . '(?=[/?#].*|$)@',
+ '${1}' . $sandbox,
+ $url,
+ 1
+ );
+ }
+
+ return compact( 'url', 'host' );
+ }
+
+ /**
+ * Modifies parameters of request in order to send the request to the
+ * server specified by `JETPACK__SANDBOX_DOMAIN`.
+ *
+ * Attached to the `requests-requests.before_request` filter.
+ *
+ * @param string $url URL of request about to be made.
+ * @param array $headers Headers of request about to be made.
+ * @return void
+ */
+ public function server_sandbox( &$url, &$headers ) {
+ if ( ! Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ) ) {
+ return;
+ }
+
+ $original_url = $url;
+
+ $request_parameters = $this->server_sandbox_request_parameters( Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ), $url, $headers );
+
+ $url = $request_parameters['url'];
+
+ if ( $request_parameters['host'] ) {
+ $headers['Host'] = $request_parameters['host'];
+
+ if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
+ error_log( sprintf( "SANDBOXING via '%s': '%s'", Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ), $original_url ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
+ }
+ }
+ }
+
+ /**
+ * Adds a "Jetpack API Sandboxed" item to the admin bar if the JETPACK__SANDBOX_DOMAIN
+ * constant is set.
+ *
+ * Attached to the `admin_bar_menu` action.
+ *
+ * @param WP_Admin_Bar $wp_admin_bar The WP_Admin_Bar instance.
+ */
+ public function admin_bar_add_sandbox_item( $wp_admin_bar ) {
+ if ( ! Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ) ) {
+ return;
+ }
+
+ $node = array(
+ 'id' => 'jetpack-connection-api-sandbox',
+ 'title' => 'Jetpack API Sandboxed',
+ 'meta' => array(
+ 'title' => 'Sandboxing via ' . Constants::get_constant( 'JETPACK__SANDBOX_DOMAIN' ),
+ ),
+ );
+
+ $wp_admin_bar->add_menu( $node );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-tokens.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-tokens.php
new file mode 100644
index 00000000..38fb58d4
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-tokens.php
@@ -0,0 +1,595 @@
+<?php
+/**
+ * The Jetpack Connection Tokens class file.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+use Automattic\Jetpack\Constants;
+use Automattic\Jetpack\Roles;
+use Jetpack_Options;
+use WP_Error;
+
+/**
+ * The Jetpack Connection Tokens class that manages tokens.
+ */
+class Tokens {
+
+ const MAGIC_NORMAL_TOKEN_KEY = ';normal;';
+
+ /**
+ * Deletes all connection tokens and transients from the local Jetpack site.
+ */
+ public function delete_all() {
+ Jetpack_Options::delete_option(
+ array(
+ 'blog_token',
+ 'user_token',
+ 'user_tokens',
+ )
+ );
+ }
+
+ /**
+ * Perform the API request to validate the blog and user tokens.
+ *
+ * @param int|null $user_id ID of the user we need to validate token for. Current user's ID by default.
+ *
+ * @return array|false|WP_Error The API response: `array( 'blog_token_is_healthy' => true|false, 'user_token_is_healthy' => true|false )`.
+ */
+ public function validate( $user_id = null ) {
+ $blog_id = Jetpack_Options::get_option( 'id' );
+ if ( ! $blog_id ) {
+ return new WP_Error( 'site_not_registered', 'Site not registered.' );
+ }
+ $url = sprintf(
+ '%s/%s/v%s/%s',
+ Constants::get_constant( 'JETPACK__WPCOM_JSON_API_BASE' ),
+ 'wpcom',
+ '2',
+ 'sites/' . $blog_id . '/jetpack-token-health'
+ );
+
+ $user_token = $this->get_access_token( $user_id ? $user_id : get_current_user_id() );
+ $blog_token = $this->get_access_token();
+
+ // Cannot validate non-existent tokens.
+ if ( false === $user_token || false === $blog_token ) {
+ return false;
+ };
+
+ $method = 'POST';
+ $body = array(
+ 'user_token' => $this->get_signed_token( $user_token ),
+ 'blog_token' => $this->get_signed_token( $blog_token ),
+ );
+ $response = Client::_wp_remote_request( $url, compact( 'body', 'method' ) );
+
+ if ( is_wp_error( $response ) || ! wp_remote_retrieve_body( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
+ return false;
+ }
+
+ $body = json_decode( wp_remote_retrieve_body( $response ), true );
+
+ return $body ? $body : false;
+ }
+
+ /**
+ * Perform the API request to validate only the blog.
+ *
+ * @return bool|WP_Error Boolean with the test result. WP_Error if test cannot be performed.
+ */
+ public function validate_blog_token() {
+ $blog_id = Jetpack_Options::get_option( 'id' );
+ if ( ! $blog_id ) {
+ return new WP_Error( 'site_not_registered', 'Site not registered.' );
+ }
+ $url = sprintf(
+ '%s/%s/v%s/%s',
+ Constants::get_constant( 'JETPACK__WPCOM_JSON_API_BASE' ),
+ 'wpcom',
+ '2',
+ 'sites/' . $blog_id . '/jetpack-token-health/blog'
+ );
+
+ $method = 'GET';
+ $response = Client::remote_request( compact( 'url', 'method' ) );
+
+ if ( is_wp_error( $response ) || ! wp_remote_retrieve_body( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
+ return false;
+ }
+
+ $body = json_decode( wp_remote_retrieve_body( $response ), true );
+
+ return is_array( $body ) && isset( $body['is_healthy'] ) && true === $body['is_healthy'];
+ }
+
+ /**
+ * Obtains the auth token.
+ *
+ * @param array $data The request data.
+ * @param string $token_api_url The URL of the Jetpack "token" API.
+ * @return object|WP_Error Returns the auth token on success.
+ * Returns a WP_Error on failure.
+ */
+ public function get( $data, $token_api_url ) {
+ $roles = new Roles();
+ $role = $roles->translate_current_user_to_role();
+
+ if ( ! $role ) {
+ return new WP_Error( 'role', __( 'An administrator for this blog must set up the Jetpack connection.', 'jetpack-connection' ) );
+ }
+
+ $client_secret = $this->get_access_token();
+ if ( ! $client_secret ) {
+ return new WP_Error( 'client_secret', __( 'You need to register your Jetpack before connecting it.', 'jetpack-connection' ) );
+ }
+
+ /**
+ * Filter the URL of the first time the user gets redirected back to your site for connection
+ * data processing.
+ *
+ * @since 1.7.0
+ * @since-jetpack 8.0.0
+ *
+ * @param string $redirect_url Defaults to the site admin URL.
+ */
+ $processing_url = apply_filters( 'jetpack_token_processing_url', admin_url( 'admin.php' ) );
+
+ $redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
+
+ /**
+ * Filter the URL to redirect the user back to when the authentication process
+ * is complete.
+ *
+ * @since 1.7.0
+ * @since-jetpack 8.0.0
+ *
+ * @param string $redirect_url Defaults to the site URL.
+ */
+ $redirect = apply_filters( 'jetpack_token_redirect_url', $redirect );
+
+ $redirect_uri = ( 'calypso' === $data['auth_type'] )
+ ? $data['redirect_uri']
+ : add_query_arg(
+ array(
+ 'handler' => 'jetpack-connection-webhooks',
+ 'action' => 'authorize',
+ '_wpnonce' => wp_create_nonce( "jetpack-authorize_{$role}_{$redirect}" ),
+ 'redirect' => $redirect ? rawurlencode( $redirect ) : false,
+ ),
+ esc_url( $processing_url )
+ );
+
+ /**
+ * Filters the token request data.
+ *
+ * @since 1.7.0
+ * @since-jetpack 8.0.0
+ *
+ * @param array $request_data request data.
+ */
+ $body = apply_filters(
+ 'jetpack_token_request_body',
+ array(
+ 'client_id' => Jetpack_Options::get_option( 'id' ),
+ 'client_secret' => $client_secret->secret,
+ 'grant_type' => 'authorization_code',
+ 'code' => $data['code'],
+ 'redirect_uri' => $redirect_uri,
+ )
+ );
+
+ $args = array(
+ 'method' => 'POST',
+ 'body' => $body,
+ 'headers' => array(
+ 'Accept' => 'application/json',
+ ),
+ );
+ add_filter( 'http_request_timeout', array( $this, 'return_30' ), PHP_INT_MAX - 1 );
+ $response = Client::_wp_remote_request( $token_api_url, $args );
+ remove_filter( 'http_request_timeout', array( $this, 'return_30' ), PHP_INT_MAX - 1 );
+
+ if ( is_wp_error( $response ) ) {
+ return new WP_Error( 'token_http_request_failed', $response->get_error_message() );
+ }
+
+ $code = wp_remote_retrieve_response_code( $response );
+ $entity = wp_remote_retrieve_body( $response );
+
+ if ( $entity ) {
+ $json = json_decode( $entity );
+ } else {
+ $json = false;
+ }
+
+ if ( 200 !== $code || ! empty( $json->error ) ) {
+ if ( empty( $json->error ) ) {
+ return new WP_Error( 'unknown', '', $code );
+ }
+
+ /* translators: Error description string. */
+ $error_description = isset( $json->error_description ) ? sprintf( __( 'Error Details: %s', 'jetpack-connection' ), (string) $json->error_description ) : '';
+
+ return new WP_Error( (string) $json->error, $error_description, $code );
+ }
+
+ if ( empty( $json->access_token ) || ! is_scalar( $json->access_token ) ) {
+ return new WP_Error( 'access_token', '', $code );
+ }
+
+ if ( empty( $json->token_type ) || 'X_JETPACK' !== strtoupper( $json->token_type ) ) {
+ return new WP_Error( 'token_type', '', $code );
+ }
+
+ if ( empty( $json->scope ) ) {
+ return new WP_Error( 'scope', 'No Scope', $code );
+ }
+
+ // TODO: get rid of the error silencer.
+ // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged
+ @list( $role, $hmac ) = explode( ':', $json->scope );
+ if ( empty( $role ) || empty( $hmac ) ) {
+ return new WP_Error( 'scope', 'Malformed Scope', $code );
+ }
+
+ if ( $this->sign_role( $role ) !== $json->scope ) {
+ return new WP_Error( 'scope', 'Invalid Scope', $code );
+ }
+
+ $cap = $roles->translate_role_to_cap( $role );
+ if ( ! $cap ) {
+ return new WP_Error( 'scope', 'No Cap', $code );
+ }
+
+ if ( ! current_user_can( $cap ) ) {
+ return new WP_Error( 'scope', 'current_user_cannot', $code );
+ }
+
+ return (string) $json->access_token;
+ }
+
+ /**
+ * Enters a user token into the user_tokens option
+ *
+ * @param int $user_id The user id.
+ * @param string $token The user token.
+ * @param bool $is_master_user Whether the user is the master user.
+ * @return bool
+ */
+ public function update_user_token( $user_id, $token, $is_master_user ) {
+ // Not designed for concurrent updates.
+ $user_tokens = $this->get_user_tokens();
+ if ( ! is_array( $user_tokens ) ) {
+ $user_tokens = array();
+ }
+ $user_tokens[ $user_id ] = $token;
+ if ( $is_master_user ) {
+ $master_user = $user_id;
+ $options = compact( 'user_tokens', 'master_user' );
+ } else {
+ $options = compact( 'user_tokens' );
+ }
+ return Jetpack_Options::update_options( $options );
+ }
+
+ /**
+ * Sign a user role with the master access token.
+ * If not specified, will default to the current user.
+ *
+ * @access public
+ *
+ * @param string $role User role.
+ * @param int $user_id ID of the user.
+ * @return string Signed user role.
+ */
+ public function sign_role( $role, $user_id = null ) {
+ if ( empty( $user_id ) ) {
+ $user_id = (int) get_current_user_id();
+ }
+
+ if ( ! $user_id ) {
+ return false;
+ }
+
+ $token = $this->get_access_token();
+ if ( ! $token || is_wp_error( $token ) ) {
+ return false;
+ }
+
+ return $role . ':' . hash_hmac( 'md5', "{$role}|{$user_id}", $token->secret );
+ }
+
+ /**
+ * Increases the request timeout value to 30 seconds.
+ *
+ * @return int Returns 30.
+ */
+ public function return_30() {
+ return 30;
+ }
+
+ /**
+ * Gets the requested token.
+ *
+ * Tokens are one of two types:
+ * 1. Blog Tokens: These are the "main" tokens. Each site typically has one Blog Token,
+ * though some sites can have multiple "Special" Blog Tokens (see below). These tokens
+ * are not associated with a user account. They represent the site's connection with
+ * the Jetpack servers.
+ * 2. User Tokens: These are "sub-"tokens. Each connected user account has one User Token.
+ *
+ * All tokens look like "{$token_key}.{$private}". $token_key is a public ID for the
+ * token, and $private is a secret that should never be displayed anywhere or sent
+ * over the network; it's used only for signing things.
+ *
+ * Blog Tokens can be "Normal" or "Special".
+ * * Normal: The result of a normal connection flow. They look like
+ * "{$random_string_1}.{$random_string_2}"
+ * That is, $token_key and $private are both random strings.
+ * Sites only have one Normal Blog Token. Normal Tokens are found in either
+ * Jetpack_Options::get_option( 'blog_token' ) (usual) or the JETPACK_BLOG_TOKEN
+ * constant (rare).
+ * * Special: A connection token for sites that have gone through an alternative
+ * connection flow. They look like:
+ * ";{$special_id}{$special_version};{$wpcom_blog_id};.{$random_string}"
+ * That is, $private is a random string and $token_key has a special structure with
+ * lots of semicolons.
+ * Most sites have zero Special Blog Tokens. Special tokens are only found in the
+ * JETPACK_BLOG_TOKEN constant.
+ *
+ * In particular, note that Normal Blog Tokens never start with ";" and that
+ * Special Blog Tokens always do.
+ *
+ * When searching for a matching Blog Tokens, Blog Tokens are examined in the following
+ * order:
+ * 1. Defined Special Blog Tokens (via the JETPACK_BLOG_TOKEN constant)
+ * 2. Stored Normal Tokens (via Jetpack_Options::get_option( 'blog_token' ))
+ * 3. Defined Normal Tokens (via the JETPACK_BLOG_TOKEN constant)
+ *
+ * @param int|false $user_id false: Return the Blog Token. int: Return that user's User Token.
+ * @param string|false $token_key If provided, check that the token matches the provided input.
+ * @param bool|true $suppress_errors If true, return a falsy value when the token isn't found; When false, return a descriptive WP_Error when the token isn't found.
+ *
+ * @return object|false
+ */
+ public function get_access_token( $user_id = false, $token_key = false, $suppress_errors = true ) {
+ $possible_special_tokens = array();
+ $possible_normal_tokens = array();
+ $user_tokens = $this->get_user_tokens();
+
+ if ( $user_id ) {
+ if ( ! $user_tokens ) {
+ return $suppress_errors ? false : new WP_Error( 'no_user_tokens', __( 'No user tokens found', 'jetpack-connection' ) );
+ }
+ if ( true === $user_id ) { // connection owner.
+ $user_id = Jetpack_Options::get_option( 'master_user' );
+ if ( ! $user_id ) {
+ return $suppress_errors ? false : new WP_Error( 'empty_master_user_option', __( 'No primary user defined', 'jetpack-connection' ) );
+ }
+ }
+ if ( ! isset( $user_tokens[ $user_id ] ) || ! $user_tokens[ $user_id ] ) {
+ // translators: %s is the user ID.
+ return $suppress_errors ? false : new WP_Error( 'no_token_for_user', sprintf( __( 'No token for user %d', 'jetpack-connection' ), $user_id ) );
+ }
+ $user_token_chunks = explode( '.', $user_tokens[ $user_id ] );
+ if ( empty( $user_token_chunks[1] ) || empty( $user_token_chunks[2] ) ) {
+ // translators: %s is the user ID.
+ return $suppress_errors ? false : new WP_Error( 'token_malformed', sprintf( __( 'Token for user %d is malformed', 'jetpack-connection' ), $user_id ) );
+ }
+ if ( $user_token_chunks[2] !== (string) $user_id ) {
+ // translators: %1$d is the ID of the requested user. %2$d is the user ID found in the token.
+ return $suppress_errors ? false : new WP_Error( 'user_id_mismatch', sprintf( __( 'Requesting user_id %1$d does not match token user_id %2$d', 'jetpack-connection' ), $user_id, $user_token_chunks[2] ) );
+ }
+ $possible_normal_tokens[] = "{$user_token_chunks[0]}.{$user_token_chunks[1]}";
+ } else {
+ $stored_blog_token = Jetpack_Options::get_option( 'blog_token' );
+ if ( $stored_blog_token ) {
+ $possible_normal_tokens[] = $stored_blog_token;
+ }
+
+ $defined_tokens_string = Constants::get_constant( 'JETPACK_BLOG_TOKEN' );
+
+ if ( $defined_tokens_string ) {
+ $defined_tokens = explode( ',', $defined_tokens_string );
+ foreach ( $defined_tokens as $defined_token ) {
+ if ( ';' === $defined_token[0] ) {
+ $possible_special_tokens[] = $defined_token;
+ } else {
+ $possible_normal_tokens[] = $defined_token;
+ }
+ }
+ }
+ }
+
+ if ( self::MAGIC_NORMAL_TOKEN_KEY === $token_key ) {
+ $possible_tokens = $possible_normal_tokens;
+ } else {
+ $possible_tokens = array_merge( $possible_special_tokens, $possible_normal_tokens );
+ }
+
+ if ( ! $possible_tokens ) {
+ // If no user tokens were found, it would have failed earlier, so this is about blog token.
+ return $suppress_errors ? false : new WP_Error( 'no_possible_tokens', __( 'No blog token found', 'jetpack-connection' ) );
+ }
+
+ $valid_token = false;
+
+ if ( false === $token_key ) {
+ // Use first token.
+ $valid_token = $possible_tokens[0];
+ } elseif ( self::MAGIC_NORMAL_TOKEN_KEY === $token_key ) {
+ // Use first normal token.
+ $valid_token = $possible_tokens[0]; // $possible_tokens only contains normal tokens because of earlier check.
+ } else {
+ // Use the token matching $token_key or false if none.
+ // Ensure we check the full key.
+ $token_check = rtrim( $token_key, '.' ) . '.';
+
+ foreach ( $possible_tokens as $possible_token ) {
+ if ( hash_equals( substr( $possible_token, 0, strlen( $token_check ) ), $token_check ) ) {
+ $valid_token = $possible_token;
+ break;
+ }
+ }
+ }
+
+ if ( ! $valid_token ) {
+ if ( $user_id ) {
+ // translators: %d is the user ID.
+ return $suppress_errors ? false : new WP_Error( 'no_valid_user_token', sprintf( __( 'Invalid token for user %d', 'jetpack-connection' ), $user_id ) );
+ } else {
+ return $suppress_errors ? false : new WP_Error( 'no_valid_blog_token', __( 'Invalid blog token', 'jetpack-connection' ) );
+ }
+ }
+
+ return (object) array(
+ 'secret' => $valid_token,
+ 'external_user_id' => (int) $user_id,
+ );
+ }
+
+ /**
+ * Updates the blog token to a new value.
+ *
+ * @access public
+ *
+ * @param string $token the new blog token value.
+ * @return Boolean Whether updating the blog token was successful.
+ */
+ public function update_blog_token( $token ) {
+ return Jetpack_Options::update_option( 'blog_token', $token );
+ }
+
+ /**
+ * Unlinks the current user from the linked WordPress.com user.
+ *
+ * @access public
+ * @static
+ *
+ * @todo Refactor to properly load the XMLRPC client independently.
+ *
+ * @param Integer $user_id the user identifier.
+ * @param bool $can_overwrite_primary_user Allow for the primary user to be disconnected.
+ * @return Boolean Whether the disconnection of the user was successful.
+ */
+ public function disconnect_user( $user_id, $can_overwrite_primary_user = false ) {
+ $tokens = $this->get_user_tokens();
+ if ( ! $tokens ) {
+ return false;
+ }
+
+ if ( Jetpack_Options::get_option( 'master_user' ) === $user_id && ! $can_overwrite_primary_user ) {
+ return false;
+ }
+
+ if ( ! isset( $tokens[ $user_id ] ) ) {
+ return false;
+ }
+
+ unset( $tokens[ $user_id ] );
+
+ $this->update_user_tokens( $tokens );
+
+ return true;
+ }
+
+ /**
+ * Returns an array of user_id's that have user tokens for communicating with wpcom.
+ * Able to select by specific capability.
+ *
+ * @deprecated 1.30.0
+ * @see Manager::get_connected_users
+ *
+ * @param string $capability The capability of the user.
+ * @param int|null $limit How many connected users to get before returning.
+ * @return array Array of WP_User objects if found.
+ */
+ public function get_connected_users( $capability = 'any', $limit = null ) {
+ _deprecated_function( __METHOD__, '1.30.0' );
+ return ( new Manager( 'jetpack' ) )->get_connected_users( $capability, $limit );
+ }
+
+ /**
+ * Fetches a signed token.
+ *
+ * @param object $token the token.
+ * @return WP_Error|string a signed token
+ */
+ public function get_signed_token( $token ) {
+ if ( ! isset( $token->secret ) || empty( $token->secret ) ) {
+ return new WP_Error( 'invalid_token' );
+ }
+
+ list( $token_key, $token_secret ) = explode( '.', $token->secret );
+
+ $token_key = sprintf(
+ '%s:%d:%d',
+ $token_key,
+ Constants::get_constant( 'JETPACK__API_VERSION' ),
+ $token->external_user_id
+ );
+
+ $timestamp = time();
+
+ if ( function_exists( 'wp_generate_password' ) ) {
+ $nonce = wp_generate_password( 10, false );
+ } else {
+ $nonce = substr( sha1( wp_rand( 0, 1000000 ) ), 0, 10 );
+ }
+
+ $normalized_request_string = join(
+ "\n",
+ array(
+ $token_key,
+ $timestamp,
+ $nonce,
+ )
+ ) . "\n";
+
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
+ $signature = base64_encode( hash_hmac( 'sha1', $normalized_request_string, $token_secret, true ) );
+
+ $auth = array(
+ 'token' => $token_key,
+ 'timestamp' => $timestamp,
+ 'nonce' => $nonce,
+ 'signature' => $signature,
+ );
+
+ $header_pieces = array();
+ foreach ( $auth as $key => $value ) {
+ $header_pieces[] = sprintf( '%s="%s"', $key, $value );
+ }
+
+ return join( ' ', $header_pieces );
+ }
+
+ /**
+ * Gets the list of user tokens
+ *
+ * @since 1.30.0
+ *
+ * @return bool|array An array of user tokens where keys are user IDs and values are the tokens. False if no user token is found.
+ */
+ public function get_user_tokens() {
+ return Jetpack_Options::get_option( 'user_tokens' );
+ }
+
+ /**
+ * Updates the option that stores the user tokens
+ *
+ * @since 1.30.0
+ *
+ * @param array $tokens An array of user tokens where keys are user IDs and values are the tokens.
+ * @return bool Was the option successfully updated?
+ *
+ * @todo add validate the input.
+ */
+ public function update_user_tokens( $tokens ) {
+ return Jetpack_Options::update_option( 'user_tokens', $tokens );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-urls.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-urls.php
new file mode 100644
index 00000000..f9a29176
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-urls.php
@@ -0,0 +1,187 @@
+<?php
+/**
+ * The Jetpack Connection package Urls class file.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+use Automattic\Jetpack\Constants;
+
+/**
+ * Provides Url methods for the Connection package.
+ */
+class Urls {
+
+ const HTTPS_CHECK_OPTION_PREFIX = 'jetpack_sync_https_history_';
+ const HTTPS_CHECK_HISTORY = 5;
+
+ /**
+ * Return URL from option or PHP constant.
+ *
+ * @param string $option_name (e.g. 'home').
+ *
+ * @return mixed|null URL.
+ */
+ public static function get_raw_url( $option_name ) {
+ $value = null;
+ $constant = ( 'home' === $option_name )
+ ? 'WP_HOME'
+ : 'WP_SITEURL';
+
+ // Since we disregard the constant for multisites in ms-default-filters.php,
+ // let's also use the db value if this is a multisite.
+ if ( ! is_multisite() && Constants::is_defined( $constant ) ) {
+ $value = Constants::get_constant( $constant );
+ } else {
+ // Let's get the option from the database so that we can bypass filters. This will help
+ // ensure that we get more uniform values.
+ $value = \Jetpack_Options::get_raw_option( $option_name );
+ }
+
+ return $value;
+ }
+
+ /**
+ * Normalize domains by removing www unless declared in the site's option.
+ *
+ * @param string $option Option value from the site.
+ * @param callable $url_function Function retrieving the URL to normalize.
+ * @return mixed|string URL.
+ */
+ public static function normalize_www_in_url( $option, $url_function ) {
+ $url = wp_parse_url( call_user_func( $url_function ) );
+ $option_url = wp_parse_url( get_option( $option ) );
+
+ if ( ! $option_url || ! $url ) {
+ return $url;
+ }
+
+ if ( "www.{$option_url[ 'host' ]}" === $url['host'] ) {
+ // remove www if not present in option URL.
+ $url['host'] = $option_url['host'];
+ }
+ if ( "www.{$url[ 'host' ]}" === $option_url['host'] ) {
+ // add www if present in option URL.
+ $url['host'] = $option_url['host'];
+ }
+
+ $normalized_url = "{$url['scheme']}://{$url['host']}";
+ if ( isset( $url['path'] ) ) {
+ $normalized_url .= "{$url['path']}";
+ }
+
+ if ( isset( $url['query'] ) ) {
+ $normalized_url .= "?{$url['query']}";
+ }
+
+ return $normalized_url;
+ }
+
+ /**
+ * Return URL with a normalized protocol.
+ *
+ * @param callable $callable Function to retrieve URL option.
+ * @param string $new_value URL Protocol to set URLs to.
+ * @return string Normalized URL.
+ */
+ public static function get_protocol_normalized_url( $callable, $new_value ) {
+ $option_key = self::HTTPS_CHECK_OPTION_PREFIX . $callable;
+
+ $parsed_url = wp_parse_url( $new_value );
+ if ( ! $parsed_url ) {
+ return $new_value;
+ }
+ if ( array_key_exists( 'scheme', $parsed_url ) ) {
+ $scheme = $parsed_url['scheme'];
+ } else {
+ $scheme = '';
+ }
+ $scheme_history = get_option( $option_key, array() );
+ $scheme_history[] = $scheme;
+
+ // Limit length to self::HTTPS_CHECK_HISTORY.
+ $scheme_history = array_slice( $scheme_history, ( self::HTTPS_CHECK_HISTORY * -1 ) );
+
+ update_option( $option_key, $scheme_history );
+
+ $forced_scheme = in_array( 'https', $scheme_history, true ) ? 'https' : 'http';
+
+ return set_url_scheme( $new_value, $forced_scheme );
+ }
+
+ /**
+ * Helper function that is used when getting home or siteurl values. Decides
+ * whether to get the raw or filtered value.
+ *
+ * @param string $url_type URL to get, home or siteurl.
+ * @return string
+ */
+ public static function get_raw_or_filtered_url( $url_type ) {
+ $url_function = ( 'home' === $url_type )
+ ? 'home_url'
+ : 'site_url';
+
+ if (
+ ! Constants::is_defined( 'JETPACK_SYNC_USE_RAW_URL' ) ||
+ Constants::get_constant( 'JETPACK_SYNC_USE_RAW_URL' )
+ ) {
+ $scheme = is_ssl() ? 'https' : 'http';
+ $url = self::get_raw_url( $url_type );
+ $url = set_url_scheme( $url, $scheme );
+ } else {
+ $url = self::normalize_www_in_url( $url_type, $url_function );
+ }
+
+ return self::get_protocol_normalized_url( $url_function, $url );
+ }
+
+ /**
+ * Return the escaped home_url.
+ *
+ * @return string
+ */
+ public static function home_url() {
+ $url = self::get_raw_or_filtered_url( 'home' );
+
+ /**
+ * Allows overriding of the home_url value that is synced back to WordPress.com.
+ *
+ * @since 1.7.0
+ * @since-jetpack 5.2.0
+ *
+ * @param string $home_url
+ */
+ return esc_url_raw( apply_filters( 'jetpack_sync_home_url', $url ) );
+ }
+
+ /**
+ * Return the escaped siteurl.
+ *
+ * @return string
+ */
+ public static function site_url() {
+ $url = self::get_raw_or_filtered_url( 'siteurl' );
+
+ /**
+ * Allows overriding of the site_url value that is synced back to WordPress.com.
+ *
+ * @since 1.7.0
+ * @since-jetpack 5.2.0
+ *
+ * @param string $site_url
+ */
+ return esc_url_raw( apply_filters( 'jetpack_sync_site_url', $url ) );
+ }
+
+ /**
+ * Return main site URL with a normalized protocol.
+ *
+ * @return string
+ */
+ public static function main_network_site_url() {
+ return self::get_protocol_normalized_url( 'main_network_site_url', network_site_url() );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-utils.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-utils.php
new file mode 100644
index 00000000..f8bd065b
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-utils.php
@@ -0,0 +1,87 @@
+<?php
+/**
+ * The Jetpack Connection package Utils class file.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+use Automattic\Jetpack\Tracking;
+
+/**
+ * Provides utility methods for the Connection package.
+ */
+class Utils {
+
+ const DEFAULT_JETPACK__API_VERSION = 1;
+ const DEFAULT_JETPACK__API_BASE = 'https://jetpack.wordpress.com/jetpack.';
+ const DEFAULT_JETPACK__WPCOM_JSON_API_BASE = 'https://public-api.wordpress.com';
+
+ /**
+ * Enters a user token into the user_tokens option
+ *
+ * @deprecated 1.24.0 Use Automattic\Jetpack\Connection\Tokens->update_user_token() instead.
+ *
+ * @param int $user_id The user id.
+ * @param string $token The user token.
+ * @param bool $is_master_user Whether the user is the master user.
+ * @return bool
+ */
+ public static function update_user_token( $user_id, $token, $is_master_user ) {
+ _deprecated_function( __METHOD__, '1.24.0', 'Automattic\\Jetpack\\Connection\\Tokens->update_user_token' );
+ return ( new Tokens() )->update_user_token( $user_id, $token, $is_master_user );
+ }
+
+ /**
+ * Filters the value of the api constant.
+ *
+ * @param String $constant_value The constant value.
+ * @param String $constant_name The constant name.
+ * @return mixed | null
+ */
+ public static function jetpack_api_constant_filter( $constant_value, $constant_name ) {
+ if ( ! is_null( $constant_value ) ) {
+ // If the constant value was already set elsewhere, use that value.
+ return $constant_value;
+ }
+
+ if ( defined( "self::DEFAULT_$constant_name" ) ) {
+ return constant( "self::DEFAULT_$constant_name" );
+ }
+
+ return null;
+ }
+
+ /**
+ * Add a filter to initialize default values of the constants.
+ */
+ public static function init_default_constants() {
+ add_filter(
+ 'jetpack_constant_default_value',
+ array( __CLASS__, 'jetpack_api_constant_filter' ),
+ 10,
+ 2
+ );
+ }
+
+ /**
+ * Filters the registration request body to include tracking properties.
+ *
+ * @param array $properties Already prepared tracking properties.
+ * @return array amended properties.
+ */
+ public static function filter_register_request_body( $properties ) {
+ $tracking = new Tracking();
+ $tracks_identity = $tracking->tracks_get_identity( get_current_user_id() );
+
+ return array_merge(
+ $properties,
+ array(
+ '_ui' => $tracks_identity['_ui'],
+ '_ut' => $tracks_identity['_ut'],
+ )
+ );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-webhooks.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-webhooks.php
new file mode 100644
index 00000000..72225d19
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-webhooks.php
@@ -0,0 +1,127 @@
+<?php
+/**
+ * Connection Webhooks class.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+use Automattic\Jetpack\Roles;
+use Automattic\Jetpack\Tracking;
+use Jetpack_Options;
+
+/**
+ * Connection Webhooks class.
+ */
+class Webhooks {
+
+ /**
+ * The Connection Manager object.
+ *
+ * @var Manager
+ */
+ private $connection;
+
+ /**
+ * Webhooks constructor.
+ *
+ * @param Manager $connection The Connection Manager object.
+ */
+ public function __construct( $connection ) {
+ $this->connection = $connection;
+ }
+
+ /**
+ * Initialize the webhooks.
+ *
+ * @param Manager $connection The Connection Manager object.
+ */
+ public static function init( $connection ) {
+ $webhooks = new static( $connection );
+
+ add_action( 'init', array( $webhooks, 'controller' ) );
+ }
+
+ /**
+ * The "controller" decides which handler we need to run.
+ */
+ public function controller() {
+ // The nonce is verified in specific handlers.
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( empty( $_GET['handler'] ) || empty( $_GET['action'] ) || 'jetpack-connection-webhooks' !== $_GET['handler'] ) {
+ return;
+ }
+
+ // The nonce is verified in specific handlers.
+ // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ switch ( $_GET['action'] ) {
+ case 'authorize':
+ $this->handle_authorize();
+ break;
+ }
+
+ $this->do_exit();
+ }
+
+ /**
+ * Perform the authorization action.
+ */
+ public function handle_authorize() {
+ if ( $this->connection->is_connected() && $this->connection->is_user_connected() ) {
+ $redirect_url = apply_filters( 'jetpack_client_authorize_already_authorized_url', admin_url() );
+ wp_safe_redirect( $redirect_url );
+
+ return;
+ }
+ do_action( 'jetpack_client_authorize_processing' );
+
+ $data = stripslashes_deep( $_GET );
+ $data['auth_type'] = 'client';
+ $roles = new Roles();
+ $role = $roles->translate_current_user_to_role();
+ $redirect = isset( $data['redirect'] ) ? esc_url_raw( (string) $data['redirect'] ) : '';
+
+ check_admin_referer( "jetpack-authorize_{$role}_{$redirect}" );
+
+ $tracking = new Tracking();
+
+ $result = $this->connection->authorize( $data );
+
+ if ( is_wp_error( $result ) ) {
+ do_action( 'jetpack_client_authorize_error', $result );
+
+ $tracking->record_user_event(
+ 'jpc_client_authorize_fail',
+ array(
+ 'error_code' => $result->get_error_code(),
+ 'error_message' => $result->get_error_message(),
+ )
+ );
+ } else {
+ /**
+ * Fires after the Jetpack client is authorized to communicate with WordPress.com.
+ *
+ * @param int Jetpack Blog ID.
+ *
+ * @since 1.7.0
+ * @since-jetpack 4.2.0
+ */
+ do_action( 'jetpack_client_authorized', Jetpack_Options::get_option( 'id' ) );
+
+ $tracking->record_user_event( 'jpc_client_authorize_success' );
+ }
+
+ $fallback_redirect = apply_filters( 'jetpack_client_authorize_fallback_url', admin_url() );
+ $redirect = wp_validate_redirect( $redirect ) ? $redirect : $fallback_redirect;
+
+ wp_safe_redirect( $redirect );
+ }
+
+ /**
+ * The `exit` is wrapped into a method so we could mock it.
+ */
+ protected function do_exit() {
+ exit;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-xmlrpc-async-call.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-xmlrpc-async-call.php
new file mode 100644
index 00000000..f27090c9
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-xmlrpc-async-call.php
@@ -0,0 +1,105 @@
+<?php
+/**
+ * XMLRPC Async Call class.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+use Jetpack_IXR_ClientMulticall;
+
+/**
+ * Make XMLRPC async calls to WordPress.com
+ *
+ * This class allows you to enqueue XMLRPC calls that will be grouped and sent
+ * at once in a multi-call request at shutdown.
+ *
+ * Usage:
+ *
+ * XMLRPC_Async_Call::add_call( 'methodName', get_current_user_id(), $arg1, $arg2, etc... )
+ *
+ * See XMLRPC_Async_Call::add_call for details
+ */
+class XMLRPC_Async_Call {
+
+ /**
+ * Hold the IXR Clients that will be dispatched at shutdown
+ *
+ * Clients are stored in the following schema:
+ * [
+ * $blog_id => [
+ * $user_id => [
+ * arrat of Jetpack_IXR_ClientMulticall
+ * ]
+ * ]
+ * ]
+ *
+ * @var array
+ */
+ public static $clients = array();
+
+ /**
+ * Adds a new XMLRPC call to the queue to be processed on shutdown
+ *
+ * @param string $method The XML-RPC method.
+ * @param integer $user_id The user ID used to make the request (will use this user's token); Use 0 for the blog token.
+ * @param mixed ...$args This function accepts any number of additional arguments, that will be passed to the call.
+ * @return void
+ */
+ public static function add_call( $method, $user_id = 0, ...$args ) {
+ global $blog_id;
+
+ $client_blog_id = is_multisite() ? $blog_id : 0;
+
+ if ( ! isset( self::$clients[ $client_blog_id ] ) ) {
+ self::$clients[ $client_blog_id ] = array();
+ }
+
+ if ( ! isset( self::$clients[ $client_blog_id ][ $user_id ] ) ) {
+ self::$clients[ $client_blog_id ][ $user_id ] = new Jetpack_IXR_ClientMulticall( array( 'user_id' => $user_id ) );
+ }
+
+ if ( function_exists( 'ignore_user_abort' ) ) {
+ ignore_user_abort( true );
+ }
+
+ array_unshift( $args, $method );
+
+ call_user_func_array( array( self::$clients[ $client_blog_id ][ $user_id ], 'addCall' ), $args );
+
+ if ( false === has_action( 'shutdown', array( 'Automattic\Jetpack\Connection\XMLRPC_Async_Call', 'do_calls' ) ) ) {
+ add_action( 'shutdown', array( 'Automattic\Jetpack\Connection\XMLRPC_Async_Call', 'do_calls' ) );
+ }
+ }
+
+ /**
+ * Trigger the calls at shutdown
+ *
+ * @return void
+ */
+ public static function do_calls() {
+ foreach ( self::$clients as $client_blog_id => $blog_clients ) {
+ if ( $client_blog_id > 0 ) {
+ $switch_success = switch_to_blog( $client_blog_id, true );
+
+ if ( ! $switch_success ) {
+ continue;
+ }
+ }
+
+ foreach ( $blog_clients as $client ) {
+ if ( empty( $client->calls ) ) {
+ continue;
+ }
+
+ flush();
+ $client->query();
+ }
+
+ if ( $client_blog_id > 0 ) {
+ restore_current_blog();
+ }
+ }
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-xmlrpc-connector.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-xmlrpc-connector.php
new file mode 100644
index 00000000..c3d786d8
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/class-xmlrpc-connector.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Sets up the Connection XML-RPC methods.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+/**
+ * Registers the XML-RPC methods for Connections.
+ */
+class XMLRPC_Connector {
+ /**
+ * The Connection Manager.
+ *
+ * @var Manager
+ */
+ private $connection;
+
+ /**
+ * Constructor.
+ *
+ * @param Manager $connection The Connection Manager.
+ */
+ public function __construct( Manager $connection ) {
+ $this->connection = $connection;
+
+ // Adding the filter late to avoid being overwritten by Jetpack's XMLRPC server.
+ add_filter( 'xmlrpc_methods', array( $this, 'xmlrpc_methods' ), 20 );
+ }
+
+ /**
+ * Attached to the `xmlrpc_methods` filter.
+ *
+ * @param array $methods The already registered XML-RPC methods.
+ * @return array
+ */
+ public function xmlrpc_methods( $methods ) {
+ return array_merge(
+ $methods,
+ array(
+ 'jetpack.verifyRegistration' => array( $this, 'verify_registration' ),
+ )
+ );
+ }
+
+ /**
+ * Handles verification that a site is registered.
+ *
+ * @param array $registration_data The data sent by the XML-RPC client:
+ * [ $secret_1, $user_id ].
+ *
+ * @return string|IXR_Error
+ */
+ public function verify_registration( $registration_data ) {
+ return $this->output( $this->connection->handle_registration( $registration_data ) );
+ }
+
+ /**
+ * Normalizes output for XML-RPC.
+ *
+ * @param mixed $data The data to output.
+ */
+ private function output( $data ) {
+ if ( is_wp_error( $data ) ) {
+ $code = $data->get_error_data();
+ if ( ! $code ) {
+ $code = -10520;
+ }
+
+ if ( ! class_exists( \IXR_Error::class ) ) {
+ require_once ABSPATH . WPINC . '/class-IXR.php';
+ }
+ return new \IXR_Error(
+ $code,
+ sprintf( 'Jetpack: [%s] %s', $data->get_error_code(), $data->get_error_message() )
+ );
+ }
+
+ return $data;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/interface-manager.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/interface-manager.php
new file mode 100644
index 00000000..804f3848
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-connection/src/interface-manager.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * The Jetpack Connection Interface file.
+ * No longer used.
+ *
+ * @package automattic/jetpack-connection
+ */
+
+namespace Automattic\Jetpack\Connection;
+
+/**
+ * This interface is no longer used and is now deprecated.
+ *
+ * @deprecated since jetpack 7.8
+ */
+interface Manager_Interface {
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/CHANGELOG.md
new file mode 100644
index 00000000..c4b42f45
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/CHANGELOG.md
@@ -0,0 +1,144 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.6.14] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+
+## [1.6.13] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.6.12] - 2021-11-22
+### Changed
+- Updated package dependencies
+
+## [1.6.11] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.6.10] - 2021-10-26
+### Fixed
+- Updated is_true docblock to be more accurate.
+
+## [1.6.9] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.6.8] - 2021-10-06
+### Changed
+- Updated package dependencies
+
+## [1.6.7] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.6.6] - 2021-08-30
+### Changed
+- Run composer update on test-php command instead of phpunit
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- Updated versions in annotations
+
+## [1.6.5] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.6.4] - 2021-04-08
+### Changed
+- Packaging and build changes, no change to the package itself.
+
+## [1.6.3] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.6.2] - 2021-02-05
+
+- CI: Make tests more generic
+
+## [1.6.1] - 2021-01-19
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.6.0] - 2020-12-14
+
+- Update dependency brain/monkey to v2.6.0
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.5.1] - 2020-10-28
+
+- Updated PHPCS: Packages and Debugger
+
+## [1.5.0] - 2020-08-13
+
+- CI: Try collect js coverage
+- Docker: Add package testing shortcut
+
+## [1.4.0] - 2020-07-01
+
+- Package Unit tests: update test file names to make sure they runs in Travis
+
+## [1.3.0] - 2020-06-22
+
+- PHPCS: Clean up the packages
+
+## [1.2.0] - 2020-04-15
+
+- Use jp.com redirect in all links
+- Connection: add a filter for setting Jetpack api constants
+
+## [1.1.3] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## [1.1.2] - 2019-10-28
+
+- Packages: Add gitattributes files to all packages that need th…
+
+## [1.1.1] - 2019-09-20
+
+- Docs: Unify usage of @package phpdoc tags
+
+## [1.1.0] - 2019-09-14
+
+## 1.0.0 - 2019-07-09
+
+- Packages: Finish the constants package
+
+[1.6.14]: https://github.com/Automattic/jetpack-constants/compare/v1.6.13...v1.6.14
+[1.6.13]: https://github.com/Automattic/jetpack-constants/compare/v1.6.12...v1.6.13
+[1.6.12]: https://github.com/Automattic/jetpack-constants/compare/v1.6.11...v1.6.12
+[1.6.11]: https://github.com/Automattic/jetpack-constants/compare/v1.6.10...v1.6.11
+[1.6.10]: https://github.com/Automattic/jetpack-constants/compare/v1.6.9...v1.6.10
+[1.6.9]: https://github.com/Automattic/jetpack-constants/compare/v1.6.8...v1.6.9
+[1.6.8]: https://github.com/Automattic/jetpack-constants/compare/v1.6.7...v1.6.8
+[1.6.7]: https://github.com/Automattic/jetpack-constants/compare/v1.6.6...v1.6.7
+[1.6.6]: https://github.com/Automattic/jetpack-constants/compare/v1.6.5...v1.6.6
+[1.6.5]: https://github.com/Automattic/jetpack-constants/compare/v1.6.4...v1.6.5
+[1.6.4]: https://github.com/Automattic/jetpack-constants/compare/v1.6.3...v1.6.4
+[1.6.3]: https://github.com/Automattic/jetpack-constants/compare/v1.6.2...v1.6.3
+[1.6.2]: https://github.com/Automattic/jetpack-constants/compare/v1.6.1...v1.6.2
+[1.6.1]: https://github.com/Automattic/jetpack-constants/compare/v1.6.0...v1.6.1
+[1.6.0]: https://github.com/Automattic/jetpack-constants/compare/v1.5.1...v1.6.0
+[1.5.1]: https://github.com/Automattic/jetpack-constants/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/Automattic/jetpack-constants/compare/v1.4.0...v1.5.0
+[1.4.0]: https://github.com/Automattic/jetpack-constants/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/Automattic/jetpack-constants/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-constants/compare/v1.1.3...v1.2.0
+[1.1.3]: https://github.com/Automattic/jetpack-constants/compare/v1.1.2...v1.1.3
+[1.1.2]: https://github.com/Automattic/jetpack-constants/compare/v1.1.1...v1.1.2
+[1.1.1]: https://github.com/Automattic/jetpack-constants/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-constants/compare/v1.0.0...v1.1.0
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/src/class-constants.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/src/class-constants.php
new file mode 100644
index 00000000..eaad50be
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-constants/src/class-constants.php
@@ -0,0 +1,124 @@
+<?php
+/**
+ * A constants manager for Jetpack.
+ *
+ * @package automattic/jetpack-constants
+ */
+
+namespace Automattic\Jetpack;
+
+/**
+ * Class Automattic\Jetpack\Constants
+ *
+ * Testing constants is hard. Once you define a constant, it's defined. Constants Manager is an
+ * abstraction layer so that unit tests can set "constants" for tests.
+ *
+ * To test your code, you'll need to swap out `defined( 'CONSTANT' )` with `Automattic\Jetpack\Constants::is_defined( 'CONSTANT' )`
+ * and replace `CONSTANT` with `Automattic\Jetpack\Constants::get_constant( 'CONSTANT' )`. Then in the unit test, you can set the
+ * constant with `Automattic\Jetpack\Constants::set_constant( 'CONSTANT', $value )` and then clean up after each test with something like
+ * this:
+ *
+ * function tearDown() {
+ * Automattic\Jetpack\Constants::clear_constants();
+ * }
+ */
+class Constants {
+ /**
+ * A container for all defined constants.
+ *
+ * @access public
+ * @static
+ *
+ * @var array.
+ */
+ public static $set_constants = array();
+
+ /**
+ * Checks if a "constant" has been set in constants Manager
+ * and has a truthy value (e.g. not null, not false, not 0, any string).
+ *
+ * @param string $name The name of the constant.
+ *
+ * @return bool
+ */
+ public static function is_true( $name ) {
+ return self::is_defined( $name ) && self::get_constant( $name );
+ }
+
+ /**
+ * Checks if a "constant" has been set in constants Manager, and if not,
+ * checks if the constant was defined with define( 'name', 'value ).
+ *
+ * @param string $name The name of the constant.
+ *
+ * @return bool
+ */
+ public static function is_defined( $name ) {
+ return array_key_exists( $name, self::$set_constants )
+ ? true
+ : defined( $name );
+ }
+
+ /**
+ * Attempts to retrieve the "constant" from constants Manager, and if it hasn't been set,
+ * then attempts to get the constant with the constant() function. If that also hasn't
+ * been set, attempts to get a value from filters.
+ *
+ * @param string $name The name of the constant.
+ *
+ * @return mixed null if the constant does not exist or the value of the constant.
+ */
+ public static function get_constant( $name ) {
+ if ( array_key_exists( $name, self::$set_constants ) ) {
+ return self::$set_constants[ $name ];
+ }
+
+ if ( defined( $name ) ) {
+ return constant( $name );
+ }
+
+ /**
+ * Filters the value of the constant.
+ *
+ * @since 1.2.0
+ *
+ * @param null The constant value to be filtered. The default is null.
+ * @param String $name The constant name.
+ */
+ return apply_filters( 'jetpack_constant_default_value', null, $name );
+ }
+
+ /**
+ * Sets the value of the "constant" within constants Manager.
+ *
+ * @param string $name The name of the constant.
+ * @param string $value The value of the constant.
+ */
+ public static function set_constant( $name, $value ) {
+ self::$set_constants[ $name ] = $value;
+ }
+
+ /**
+ * Will unset a "constant" from constants Manager if the constant exists.
+ *
+ * @param string $name The name of the constant.
+ *
+ * @return bool Whether the constant was removed.
+ */
+ public static function clear_single_constant( $name ) {
+ if ( ! array_key_exists( $name, self::$set_constants ) ) {
+ return false;
+ }
+
+ unset( self::$set_constants[ $name ] );
+
+ return true;
+ }
+
+ /**
+ * Resets all of the constants within constants Manager.
+ */
+ public static function clear_constants() {
+ self::$set_constants = array();
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/CHANGELOG.md
new file mode 100644
index 00000000..7330dced
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/CHANGELOG.md
@@ -0,0 +1,116 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.4.11] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+
+## [1.4.10] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.4.9] - 2021-11-16
+### Fixed
+- Verify $_SERVER['HTTP_USER_AGENT'] exists before use.
+
+## [1.4.8] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.4.7] - 2021-10-19
+### Deprecated
+- General: remove numerous long-deprecated functions.
+
+## [1.4.6] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.4.5] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.4.4] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.4.3] - 2021-08-31
+### Changed
+- Run composer update on test-php command instead of phpunit.
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- Updated versions in annotations.
+
+## [1.4.2] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.4.1] - 2021-04-27
+### Changed
+- Updated package dependencies.
+
+## [1.4.0] - 2021-03-30
+### Added
+- Added Opera Desktop detection
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.3.2] - 2021-02-05
+
+- CI: Make tests more generic
+
+## [1.3.1] - 2021-01-19
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.3.0] - 2020-12-09
+
+- Codesniffer: Update mediawiki/mediawiki-codesniffer dependency
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.2.1] - 2020-11-10
+
+- Improve PHP 8 compatibility
+- Updated PHPCS: Packages and Debugger
+
+## [1.2.0] - 2020-10-19
+
+- Replaced intval() with (int) as part of issue #17432.
+
+## [1.1.0] - 2020-08-13
+
+- CI: Try collect js coverage
+
+## 1.0.0 - 2020-06-25
+
+- Moving jetpack_is_mobile into a package
+
+[1.4.11]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.10...v1.4.11
+[1.4.10]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.9...v1.4.10
+[1.4.9]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.8...v1.4.9
+[1.4.8]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.7...v1.4.8
+[1.4.7]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.6...v1.4.7
+[1.4.6]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.5...v1.4.6
+[1.4.5]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.4...v1.4.5
+[1.4.4]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.3...v1.4.4
+[1.4.3]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.2...v1.4.3
+[1.4.2]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.1...v1.4.2
+[1.4.1]: https://github.com/Automattic/jetpack-device-detection/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/Automattic/jetpack-device-detection/compare/v1.3.2...v1.4.0
+[1.3.2]: https://github.com/Automattic/jetpack-device-detection/compare/v1.3.1...v1.3.2
+[1.3.1]: https://github.com/Automattic/jetpack-device-detection/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/Automattic/jetpack-device-detection/compare/v1.2.1...v1.3.0
+[1.2.1]: https://github.com/Automattic/jetpack-device-detection/compare/v1.2.0...v1.2.1
+[1.2.0]: https://github.com/Automattic/jetpack-device-detection/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/Automattic/jetpack-device-detection/compare/v1.0.0...v1.1.0
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/src/class-device-detection.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/src/class-device-detection.php
new file mode 100644
index 00000000..6760c4ef
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/src/class-device-detection.php
@@ -0,0 +1,211 @@
+<?php
+/**
+ * Device detection for Jetpack.
+ *
+ * @package automattic/jetpack-device-detection
+ */
+
+namespace Automattic\Jetpack;
+
+use Automattic\Jetpack\Device_Detection\User_Agent_Info;
+
+/**
+ * Class Device_Detection
+ *
+ * Determine if the current User Agent matches the passed $kind.
+ */
+class Device_Detection {
+
+ /**
+ * Returns information about the current device accessing the page.
+ *
+ * @param string $ua (Optional) User-Agent string.
+ *
+ * @return array Device information.
+ *
+ * array(
+ * 'is_phone' => (bool) Whether the current device is a mobile phone.
+ * 'is_smartphone' => (bool) Whether the current device is a smartphone.
+ * 'is_tablet' => (bool) Whether the current device is a tablet device.
+ * 'is_handheld' => (bool) Whether the current device is a handheld device.
+ * 'is_desktop' => (bool) Whether the current device is a laptop / desktop device.
+ * 'platform' => (string) Detected platform.
+ * 'is_phone_matched_ua' => (string) Matched UA.
+ * );
+ */
+ public static function get_info( $ua = '' ) {
+ $ua_info = new User_Agent_Info( $ua );
+
+ $info = array(
+ 'is_phone' => self::is_mobile( 'any', false, $ua_info ),
+ 'is_phone_matched_ua' => self::is_mobile( 'any', true, $ua_info ),
+ 'is_smartphone' => self::is_mobile( 'smart', false, $ua_info ),
+ 'is_tablet' => $ua_info->is_tablet(),
+ 'platform' => $ua_info->get_platform(),
+ );
+
+ $info['is_handheld'] = $info['is_phone'] || $info['is_tablet'];
+ $info['is_desktop'] = ! $info['is_handheld'];
+
+ if ( function_exists( 'apply_filters' ) ) {
+ /**
+ * Filter the value of Device_Detection::get_info.
+ *
+ * @since 1.0.0
+ *
+ * @param array $info Array of device information.
+ * @param string $ua User agent string passed to Device_Detection::get_info.
+ * @param User_Agent_Info $ua_info Instance of Automattic\Jetpack\Device_Detection\User_Agent_Info.
+ */
+ $info = apply_filters( 'jetpack_device_detection_get_info', $info, $ua, $ua_info );
+ }
+ return $info;
+ }
+
+ /**
+ * Detects phone devices.
+ *
+ * @param string $ua User-Agent string.
+ *
+ * @return bool
+ */
+ public static function is_phone( $ua = '' ) {
+ $device_info = self::get_info( $ua );
+ return true === $device_info['is_phone'];
+ }
+
+ /**
+ * Detects smartphone devices.
+ *
+ * @param string $ua User-Agent string.
+ *
+ * @return bool
+ */
+ public static function is_smartphone( $ua = '' ) {
+ $device_info = self::get_info( $ua );
+ return true === $device_info['is_smartphone'];
+ }
+
+ /**
+ * Detects tablet devices.
+ *
+ * @param string $ua User-Agent string.
+ *
+ * @return bool
+ */
+ public static function is_tablet( $ua = '' ) {
+ $device_info = self::get_info( $ua );
+ return true === $device_info['is_tablet'];
+ }
+
+ /**
+ * Detects desktop devices.
+ *
+ * @param string $ua User-Agent string.
+ *
+ * @return bool
+ */
+ public static function is_desktop( $ua = '' ) {
+ $device_info = self::get_info( $ua );
+ return true === $device_info['is_desktop'];
+ }
+
+ /**
+ * Detects handheld (i.e. phone + tablet) devices.
+ *
+ * @param string $ua User-Agent string.
+ *
+ * @return bool
+ */
+ public static function is_handheld( $ua = '' ) {
+ $device_info = self::get_info( $ua );
+ return true === $device_info['is_handheld'];
+ }
+
+ /**
+ * Determine if the current User Agent matches the passed $kind.
+ *
+ * @param string $kind Category of mobile device to check for. Either: any, dumb, smart.
+ * @param bool $return_matched_agent Boolean indicating if the UA should be returned.
+ * @param User_Agent_Info $ua_info Boolean indicating if the UA should be returned.
+ *
+ * @return bool|string Boolean indicating if current UA matches $kind. If `$return_matched_agent` is true, returns the UA string.
+ */
+ private static function is_mobile( $kind, $return_matched_agent, $ua_info ) {
+ $kinds = array(
+ 'smart' => false,
+ 'dumb' => false,
+ 'any' => false,
+ );
+ $first_run = true;
+ $matched_agent = '';
+
+ // If an invalid kind is passed in, reset it to default.
+ if ( ! isset( $kinds[ $kind ] ) ) {
+ $kind = 'any';
+ }
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) || strpos( strtolower( $_SERVER['HTTP_USER_AGENT'] ), 'ipad' ) ) {
+ return false;
+ }
+
+ // Remove Samsung Galaxy tablets (SCH-I800) from being mobile devices.
+ if ( strpos( strtolower( $_SERVER['HTTP_USER_AGENT'] ), 'sch-i800' ) ) {
+ return false;
+ }
+
+ if ( $ua_info->is_android_tablet() && false === $ua_info->is_kindle_touch() ) {
+ return false;
+ }
+
+ if ( $ua_info->is_blackberry_tablet() ) {
+ return false;
+ }
+
+ if ( $first_run ) {
+ $first_run = false;
+
+ // checks for iPhoneTier devices & RichCSS devices.
+ if ( $ua_info->isTierIphone() || $ua_info->isTierRichCSS() ) {
+ $kinds['smart'] = true;
+ $matched_agent = $ua_info->matched_agent;
+ }
+
+ if ( ! $kinds['smart'] ) {
+ // if smart, we are not dumb so no need to check.
+ $dumb_agents = $ua_info->dumb_agents;
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ foreach ( $dumb_agents as $dumb_agent ) {
+ if ( false !== strpos( $agent, $dumb_agent ) ) {
+ $kinds['dumb'] = true;
+ $matched_agent = $dumb_agent;
+
+ break;
+ }
+ }
+
+ if ( ! $kinds['dumb'] ) {
+ if ( isset( $_SERVER['HTTP_X_WAP_PROFILE'] ) ) {
+ $kinds['dumb'] = true;
+ $matched_agent = 'http_x_wap_profile';
+ } elseif ( isset( $_SERVER['HTTP_ACCEPT'] ) && ( preg_match( '/wap\.|\.wap/i', $_SERVER['HTTP_ACCEPT'] ) || false !== strpos( strtolower( $_SERVER['HTTP_ACCEPT'] ), 'application/vnd.wap.xhtml+xml' ) ) ) {
+ $kinds['dumb'] = true;
+ $matched_agent = 'vnd.wap.xhtml+xml';
+ }
+ }
+ }
+
+ if ( $kinds['dumb'] || $kinds['smart'] ) {
+ $kinds['any'] = true;
+ }
+ }
+
+ $value = $kinds[ $kind ];
+
+ if ( $return_matched_agent ) {
+ $value = $matched_agent;
+ }
+ return $value;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/src/class-user-agent-info.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/src/class-user-agent-info.php
new file mode 100644
index 00000000..e8d2f20a
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-device-detection/src/class-user-agent-info.php
@@ -0,0 +1,1579 @@
+<?php
+/**
+ * User agent detection for Jetpack.
+ *
+ * @package automattic/jetpack-device-detection
+ *
+ * We don't want to rename public members.
+ * @phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
+ * @phpcs:disable WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
+ * @phpcs:disable WordPress.NamingConventions.ValidVariableName.PropertyNotSnakeCase
+ * @phpcs:disable WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
+ */
+
+namespace Automattic\Jetpack\Device_Detection;
+
+/**
+ * A class providing device properties detection.
+ */
+class User_Agent_Info {
+
+ /**
+ * Provided or fetched User-Agent string.
+ *
+ * @var string
+ */
+ public $useragent;
+
+ /**
+ * A device group matched user agent, e.g. 'iphone'.
+ *
+ * @var string
+ */
+ public $matched_agent;
+
+ /**
+ * Stores whether is the iPhone tier of devices.
+ *
+ * @var bool
+ */
+ public $isTierIphone;
+
+ /**
+ * Stores whether the device can probably support Rich CSS, but JavaScript (jQuery) support is not assumed.
+ *
+ * @var bool
+ */
+ public $isTierRichCss;
+
+ /**
+ * Stores whether it is another mobile device, which cannot be assumed to support CSS or JS (eg, older BlackBerry, RAZR)
+ *
+ * @var bool
+ */
+ public $isTierGenericMobile;
+
+ /**
+ * Stores the device platform name
+ *
+ * @var null|string
+ */
+ private $platform = null;
+ const PLATFORM_WINDOWS = 'windows';
+ const PLATFORM_IPHONE = 'iphone';
+ const PLATFORM_IPOD = 'ipod';
+ const PLATFORM_IPAD = 'ipad';
+ const PLATFORM_BLACKBERRY = 'blackberry';
+ const PLATFORM_BLACKBERRY_10 = 'blackberry_10';
+ const PLATFORM_SYMBIAN = 'symbian_series60';
+ const PLATFORM_SYMBIAN_S40 = 'symbian_series40';
+ const PLATFORM_J2ME_MIDP = 'j2me_midp';
+ const PLATFORM_ANDROID = 'android';
+ const PLATFORM_ANDROID_TABLET = 'android_tablet';
+ const PLATFORM_FIREFOX_OS = 'firefoxOS';
+
+ /**
+ * A list of dumb-phone user agent parts.
+ *
+ * @var array
+ */
+ public $dumb_agents = array(
+ 'nokia',
+ 'blackberry',
+ 'philips',
+ 'samsung',
+ 'sanyo',
+ 'sony',
+ 'panasonic',
+ 'webos',
+ 'ericsson',
+ 'alcatel',
+ 'palm',
+ 'windows ce',
+ 'opera mini',
+ 'series60',
+ 'series40',
+ 'au-mic,',
+ 'audiovox',
+ 'avantgo',
+ 'blazer',
+ 'danger',
+ 'docomo',
+ 'epoc',
+ 'ericy',
+ 'i-mode',
+ 'ipaq',
+ 'midp-',
+ 'mot-',
+ 'netfront',
+ 'nitro',
+ 'palmsource',
+ 'pocketpc',
+ 'portalmmm',
+ 'rover',
+ 'sie-',
+ 'symbian',
+ 'cldc-',
+ 'j2me',
+ 'smartphone',
+ 'up.browser',
+ 'up.link',
+ 'up.link',
+ 'vodafone/',
+ 'wap1.',
+ 'wap2.',
+ 'mobile',
+ 'googlebot-mobile',
+ );
+
+ /**
+ * The constructor.
+ *
+ * @param string $ua (Optional) User agent.
+ */
+ public function __construct( $ua = '' ) {
+ if ( $ua ) {
+ $this->useragent = $ua;
+ } else {
+ if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ $this->useragent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ }
+ }
+ }
+
+ /**
+ * This method detects the mobile User Agent name.
+ *
+ * @return string The matched User Agent name, false otherwise.
+ */
+ public function get_mobile_user_agent_name() {
+ if ( $this->is_chrome_for_iOS() ) { // Keep this check before the safari rule.
+ return 'chrome-for-ios';
+ } elseif ( $this->is_iphone_or_ipod( 'iphone-safari' ) ) {
+ return 'iphone';
+ } elseif ( $this->is_ipad( 'ipad-safari' ) ) {
+ return 'ipad';
+ } elseif ( $this->is_android_tablet() ) { // Keep this check before the android rule.
+ return 'android_tablet';
+ } elseif ( $this->is_android() ) {
+ return 'android';
+ } elseif ( $this->is_blackberry_10() ) {
+ return 'blackberry_10';
+ } elseif ( $this->is_blackbeberry() ) {
+ return 'blackberry';
+ } elseif ( $this->is_WindowsPhone7() ) {
+ return 'win7';
+ } elseif ( $this->is_windows_phone_8() ) {
+ return 'winphone8';
+ } elseif ( $this->is_opera_mini() ) {
+ return 'opera-mini';
+ } elseif ( $this->is_opera_mini_dumb() ) {
+ return 'opera-mini-dumb';
+ } elseif ( $this->is_opera_mobile() ) {
+ return 'opera-mobi';
+ } elseif ( $this->is_blackberry_tablet() ) {
+ return 'blackberry_tablet';
+ } elseif ( $this->is_kindle_fire() ) {
+ return 'kindle-fire';
+ } elseif ( $this->is_PalmWebOS() ) {
+ return 'webos';
+ } elseif ( $this->is_S60_OSSBrowser() ) {
+ return 'series60';
+ } elseif ( $this->is_firefox_os() ) {
+ return 'firefoxOS';
+ } elseif ( $this->is_firefox_mobile() ) {
+ return 'firefox_mobile';
+ } elseif ( $this->is_MaemoTablet() ) {
+ return 'maemo';
+ } elseif ( $this->is_MeeGo() ) {
+ return 'meego';
+ } elseif ( $this->is_TouchPad() ) {
+ return 'hp_tablet';
+ } elseif ( $this->is_facebook_for_iphone() ) {
+ return 'facebook-for-iphone';
+ } elseif ( $this->is_facebook_for_ipad() ) {
+ return 'facebook-for-ipad';
+ } elseif ( $this->is_twitter_for_iphone() ) {
+ return 'twitter-for-iphone';
+ } elseif ( $this->is_twitter_for_ipad() ) {
+ return 'twitter-for-ipad';
+ } elseif ( $this->is_wordpress_for_ios() ) {
+ return 'ios-app';
+ } elseif ( $this->is_iphone_or_ipod( 'iphone-not-safari' ) ) {
+ return 'iphone-unknown';
+ } elseif ( $this->is_ipad( 'ipad-not-safari' ) ) {
+ return 'ipad-unknown';
+ } elseif ( $this->is_Nintendo_3DS() ) {
+ return 'nintendo-3ds';
+ } else {
+ $agent = $this->useragent;
+ $dumb_agents = $this->dumb_agents;
+ foreach ( $dumb_agents as $dumb_agent ) {
+ if ( false !== strpos( $agent, $dumb_agent ) ) {
+ return $dumb_agent;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * This method detects the mobile device's platform. All return strings are from the class constants.
+ * Note that this function returns the platform name, not the UA name/type. You should use a different function
+ * if you need to test the UA capabilites.
+ *
+ * @return string Name of the platform, false otherwise.
+ */
+ public function get_platform() {
+ if ( isset( $this->platform ) ) {
+ return $this->platform;
+ }
+
+ if ( strpos( $this->useragent, 'windows phone' ) !== false ) {
+ $this->platform = self::PLATFORM_WINDOWS;
+ } elseif ( strpos( $this->useragent, 'windows ce' ) !== false ) {
+ $this->platform = self::PLATFORM_WINDOWS;
+ } elseif ( strpos( $this->useragent, 'ipad' ) !== false ) {
+ $this->platform = self::PLATFORM_IPAD;
+ } elseif ( strpos( $this->useragent, 'ipod' ) !== false ) {
+ $this->platform = self::PLATFORM_IPOD;
+ } elseif ( strpos( $this->useragent, 'iphone' ) !== false ) {
+ $this->platform = self::PLATFORM_IPHONE;
+ } elseif ( strpos( $this->useragent, 'android' ) !== false ) {
+ if ( $this->is_android_tablet() ) {
+ $this->platform = self::PLATFORM_ANDROID_TABLET;
+ } else {
+ $this->platform = self::PLATFORM_ANDROID;
+ }
+ } elseif ( $this->is_kindle_fire() ) {
+ $this->platform = self::PLATFORM_ANDROID_TABLET;
+ } elseif ( $this->is_blackberry_10() ) {
+ $this->platform = self::PLATFORM_BLACKBERRY_10;
+ } elseif ( strpos( $this->useragent, 'blackberry' ) !== false ) {
+ $this->platform = self::PLATFORM_BLACKBERRY;
+ } elseif ( $this->is_blackberry_tablet() ) {
+ $this->platform = self::PLATFORM_BLACKBERRY;
+ } elseif ( $this->is_symbian_platform() ) {
+ $this->platform = self::PLATFORM_SYMBIAN;
+ } elseif ( $this->is_symbian_s40_platform() ) {
+ $this->platform = self::PLATFORM_SYMBIAN_S40;
+ } elseif ( $this->is_J2ME_platform() ) {
+ $this->platform = self::PLATFORM_J2ME_MIDP;
+ } elseif ( $this->is_firefox_os() ) {
+ $this->platform = self::PLATFORM_FIREFOX_OS;
+ } else {
+ $this->platform = false;
+ }
+
+ return $this->platform;
+ }
+
+ /**
+ * This method detects for UA which can display iPhone-optimized web content.
+ * Includes iPhone, iPod Touch, Android, WebOS, Fennec (Firefox mobile), etc.
+ */
+ public function isTierIphone() {
+ if ( isset( $this->isTierIphone ) ) {
+ return $this->isTierIphone;
+ }
+ if ( $this->is_iphoneOrIpod() ) {
+ $this->matched_agent = 'iphone';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_android() ) {
+ $this->matched_agent = 'android';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_windows_phone_8() ) {
+ $this->matched_agent = 'winphone8';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_WindowsPhone7() ) {
+ $this->matched_agent = 'win7';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_blackberry_10() ) {
+ $this->matched_agent = 'blackberry-10';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_blackbeberry() && 'blackberry-webkit' === $this->detect_blackberry_browser_version() ) {
+ $this->matched_agent = 'blackberry-webkit';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_blackberry_tablet() ) {
+ $this->matched_agent = 'blackberry_tablet';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_PalmWebOS() ) {
+ $this->matched_agent = 'webos';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_TouchPad() ) {
+ $this->matched_agent = 'hp_tablet';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_firefox_os() ) {
+ $this->matched_agent = 'firefoxOS';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_firefox_mobile() ) {
+ $this->matched_agent = 'fennec';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_opera_mobile() ) {
+ $this->matched_agent = 'opera-mobi';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_MaemoTablet() ) {
+ $this->matched_agent = 'maemo';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_MeeGo() ) {
+ $this->matched_agent = 'meego';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_kindle_touch() ) {
+ $this->matched_agent = 'kindle-touch';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_Nintendo_3DS() ) {
+ $this->matched_agent = 'nintendo-3ds';
+ $this->isTierIphone = true;
+ $this->isTierRichCss = false;
+ $this->isTierGenericMobile = false;
+ } else {
+ $this->isTierIphone = false;
+ }
+ return $this->isTierIphone;
+ }
+
+ /**
+ * This method detects for UA which are likely to be capable
+ * but may not necessarily support JavaScript.
+ * Excludes all iPhone Tier UA.
+ */
+ public function isTierRichCss() {
+ if ( isset( $this->isTierRichCss ) ) {
+ return $this->isTierRichCss;
+ }
+ if ( $this->isTierIphone() ) {
+ return false;
+ }
+
+ // The following devices are explicitly ok.
+ if ( $this->is_S60_OSSBrowser() ) {
+ $this->matched_agent = 'series60';
+ $this->isTierIphone = false;
+ $this->isTierRichCss = true;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_opera_mini() ) {
+ $this->matched_agent = 'opera-mini';
+ $this->isTierIphone = false;
+ $this->isTierRichCss = true;
+ $this->isTierGenericMobile = false;
+ } elseif ( $this->is_blackbeberry() ) {
+ $detectedDevice = $this->detect_blackberry_browser_version();
+ if (
+ 'blackberry-5' === $detectedDevice
+ || 'blackberry-4.7' === $detectedDevice
+ || 'blackberry-4.6' === $detectedDevice
+ ) {
+ $this->matched_agent = $detectedDevice;
+ $this->isTierIphone = false;
+ $this->isTierRichCss = true;
+ $this->isTierGenericMobile = false;
+ }
+ } else {
+ $this->isTierRichCss = false;
+ }
+
+ return $this->isTierRichCss;
+ }
+
+ /**
+ * Detects if the user is using a tablet.
+ * props Corey Gilmore, BGR.com
+ *
+ * @return bool
+ */
+ public function is_tablet() {
+ return ( 0 // Never true, but makes it easier to manage our list of tablet conditions.
+ || self::is_ipad()
+ || self::is_android_tablet()
+ || self::is_blackberry_tablet()
+ || self::is_kindle_fire()
+ || self::is_MaemoTablet()
+ || self::is_TouchPad()
+ );
+ }
+
+ /**
+ * Detects if the current UA is the default iPhone or iPod Touch Browser.
+ *
+ * DEPRECATED: use is_iphone_or_ipod
+ */
+ public function is_iphoneOrIpod() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ if ( ( strpos( $ua, 'iphone' ) !== false ) || ( strpos( $ua, 'ipod' ) !== false ) ) {
+ if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current UA is iPhone Mobile Safari or another iPhone or iPod Touch Browser.
+ *
+ * They type can check for any iPhone, an iPhone using Safari, or an iPhone using something other than Safari.
+ *
+ * Note: If you want to check for Opera mini, Opera mobile or Firefox mobile (or any 3rd party iPhone browser),
+ * you should put the check condition before the check for 'iphone-any' or 'iphone-not-safari'.
+ * Otherwise those browsers will be 'catched' by the iphone string.
+ *
+ * @param string $type Type of iPhone detection.
+ */
+ public static function is_iphone_or_ipod( $type = 'iphone-any' ) {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ $is_iphone = ( strpos( $ua, 'iphone' ) !== false ) || ( strpos( $ua, 'ipod' ) !== false );
+ $is_safari = ( false !== strpos( $ua, 'safari' ) );
+
+ if ( 'iphone-safari' === $type ) {
+ return $is_iphone && $is_safari;
+ } elseif ( 'iphone-not-safari' === $type ) {
+ return $is_iphone && ! $is_safari;
+ } else {
+ return $is_iphone;
+ }
+ }
+
+ /**
+ * Detects if the current UA is Chrome for iOS
+ *
+ * The User-Agent string in Chrome for iOS is the same as the Mobile Safari User-Agent, with CriOS/<ChromeRevision> instead of Version/<VersionNum>.
+ * - Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3
+ */
+ public static function is_chrome_for_iOS() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ if ( self::is_iphone_or_ipod( 'iphone-safari' ) === false ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( strpos( $ua, 'crios/' ) !== false ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current UA is Twitter for iPhone
+ *
+ * Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_5 like Mac OS X; nb-no) AppleWebKit/533.17.9 (KHTML, like Gecko) Mobile/8L1 Twitter for iPhone
+ * Mozilla/5.0 (iPhone; CPU iPhone OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Mobile/9B206 Twitter for iPhone
+ */
+ public static function is_twitter_for_iphone() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( strpos( $ua, 'ipad' ) !== false ) {
+ return false;
+ }
+
+ if ( strpos( $ua, 'twitter for iphone' ) !== false ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current UA is Twitter for iPad
+ *
+ * Old version 4.X - Mozilla/5.0 (iPad; U; CPU OS 4_3_5 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Mobile/8L1 Twitter for iPad
+ * Ver 5.0 or Higher - Mozilla/5.0 (iPad; CPU OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Mobile/9B206 Twitter for iPhone
+ */
+ public static function is_twitter_for_ipad() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( strpos( $ua, 'twitter for ipad' ) !== false ) {
+ return true;
+ } elseif ( strpos( $ua, 'ipad' ) !== false && strpos( $ua, 'twitter for iphone' ) !== false ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current UA is Facebook for iPhone
+ * - Facebook 4020.0 (iPhone; iPhone OS 5.0.1; fr_FR)
+ * - Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_0 like Mac OS X; en_US) AppleWebKit (KHTML, like Gecko) Mobile [FBAN/FBForIPhone;FBAV/4.0.2;FBBV/4020.0;FBDV/iPhone3,1;FBMD/iPhone;FBSN/iPhone OS;FBSV/5.0;FBSS/2; FBCR/O2;FBID/phone;FBLC/en_US;FBSF/2.0]
+ * - Mozilla/5.0 (iPhone; CPU iPhone OS 5_1_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Mobile/9B206 [FBAN/FBIOS;FBAV/5.0;FBBV/47423;FBDV/iPhone3,1;FBMD/iPhone;FBSN/iPhone OS;FBSV/5.1.1;FBSS/2; FBCR/3ITA;FBID/phone;FBLC/en_US]
+ */
+ public static function is_facebook_for_iphone() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( false === strpos( $ua, 'iphone' ) ) {
+ return false;
+ }
+
+ if ( false !== strpos( $ua, 'facebook' ) && false === strpos( $ua, 'ipad' ) ) {
+ return true;
+ } elseif ( false !== strpos( $ua, 'fbforiphone' ) && false === strpos( $ua, 'tablet' ) ) {
+ return true;
+ } elseif ( false !== strpos( $ua, 'fban/fbios;' ) && false === strpos( $ua, 'tablet' ) ) { // FB app v5.0 or higher.
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current UA is Facebook for iPad
+ * - Facebook 4020.0 (iPad; iPhone OS 5.0.1; en_US)
+ * - Mozilla/5.0 (iPad; U; CPU iPhone OS 5_0 like Mac OS X; en_US) AppleWebKit (KHTML, like Gecko) Mobile [FBAN/FBForIPhone;FBAV/4.0.2;FBBV/4020.0;FBDV/iPad2,1;FBMD/iPad;FBSN/iPhone OS;FBSV/5.0;FBSS/1; FBCR/;FBID/tablet;FBLC/en_US;FBSF/1.0]
+ * - Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Mobile/10A403 [FBAN/FBIOS;FBAV/5.0;FBBV/47423;FBDV/iPad2,1;FBMD/iPad;FBSN/iPhone OS;FBSV/6.0;FBSS/1; FBCR/;FBID/tablet;FBLC/en_US]
+ */
+ public static function is_facebook_for_ipad() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( false === strpos( $ua, 'ipad' ) ) {
+ return false;
+ }
+
+ if ( false !== strpos( $ua, 'facebook' ) || false !== strpos( $ua, 'fbforiphone' ) || false !== strpos( $ua, 'fban/fbios;' ) ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current UA is WordPress for iOS
+ */
+ public static function is_wordpress_for_ios() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ if ( false !== strpos( $ua, 'wp-iphone' ) ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current device is an iPad.
+ * They type can check for any iPad, an iPad using Safari, or an iPad using something other than Safari.
+ *
+ * Note: If you want to check for Opera mini, Opera mobile or Firefox mobile (or any 3rd party iPad browser),
+ * you should put the check condition before the check for 'iphone-any' or 'iphone-not-safari'.
+ * Otherwise those browsers will be 'catched' by the ipad string.
+ *
+ * @param string $type iPad type.
+ */
+ public static function is_ipad( $type = 'ipad-any' ) {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ $is_ipad = ( false !== strpos( $ua, 'ipad' ) );
+ $is_safari = ( false !== strpos( $ua, 'safari' ) );
+
+ if ( 'ipad-safari' === $type ) {
+ return $is_ipad && $is_safari;
+ } elseif ( 'ipad-not-safari' === $type ) {
+ return $is_ipad && ! $is_safari;
+ } else {
+ return $is_ipad;
+ }
+ }
+
+ /**
+ * Detects if the current browser is Firefox Mobile (Fennec)
+ *
+ * See http://www.useragentstring.com/pages/Fennec/
+ * Mozilla/5.0 (Windows NT 6.1; WOW64; rv:2.1.1) Gecko/20110415 Firefox/4.0.2pre Fennec/4.0.1
+ * Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.1b2pre) Gecko/20081015 Fennec/1.0a1
+ */
+ public static function is_firefox_mobile() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( strpos( $ua, 'fennec' ) !== false ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current browser is Firefox for desktop
+ *
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/User-Agent/Firefox
+ * Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion
+ * The platform section will include 'Mobile' for phones and 'Tablet' for tablets.
+ */
+ public static function is_firefox_desktop() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( false !== strpos( $ua, 'firefox' ) && false === strpos( $ua, 'mobile' ) && false === strpos( $ua, 'tablet' ) ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current browser is FirefoxOS Native browser
+ *
+ * Mozilla/5.0 (Mobile; rv:14.0) Gecko/14.0 Firefox/14.0
+ */
+ public static function is_firefox_os() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( strpos( $ua, 'mozilla' ) !== false && strpos( $ua, 'mobile' ) !== false && strpos( $ua, 'gecko' ) !== false && strpos( $ua, 'firefox' ) !== false ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detect modern Opera desktop
+ *
+ * Mozilla/5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36 OPR/74.0.3911.203
+ *
+ * Looking for "OPR/" specifically.
+ */
+ public static function is_opera_desktop() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ if ( false === strpos( $_SERVER['HTTP_USER_AGENT'], 'OPR/' ) ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Detects if the current browser is Opera Mobile
+ *
+ * What is the difference between Opera Mobile and Opera Mini?
+ * - Opera Mobile is a full Internet browser for mobile devices.
+ * - Opera Mini always uses a transcoder to convert the page for a small display.
+ * (it uses Opera advanced server compression technology to compress web content before it gets to a device.
+ * The rendering engine is on Opera's server.)
+ *
+ * Opera/9.80 (Windows NT 6.1; Opera Mobi/14316; U; en) Presto/2.7.81 Version/11.00"
+ * Opera/9.50 (Nintendo DSi; Opera/507; U; en-US)
+ */
+ public static function is_opera_mobile() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'mobi' ) !== false ) {
+ return true;
+ } elseif ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'nintendo dsi' ) !== false ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current browser is Opera Mini
+ *
+ * Opera/8.01 (J2ME/MIDP; Opera Mini/3.0.6306/1528; en; U; ssr)
+ * Opera/9.80 (Android;Opera Mini/6.0.24212/24.746 U;en) Presto/2.5.25 Version/10.5454
+ * Opera/9.80 (iPhone; Opera Mini/5.0.019802/18.738; U; en) Presto/2.4.15
+ * Opera/9.80 (J2ME/iPhone;Opera Mini/5.0.019802/886; U; ja) Presto/2.4.15
+ * Opera/9.80 (J2ME/iPhone;Opera Mini/5.0.019802/886; U; ja) Presto/2.4.15
+ * Opera/9.80 (Series 60; Opera Mini/5.1.22783/23.334; U; en) Presto/2.5.25 Version/10.54
+ * Opera/9.80 (BlackBerry; Opera Mini/5.1.22303/22.387; U; en) Presto/2.5.25 Version/10.54
+ */
+ public static function is_opera_mini() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( strpos( $ua, 'opera' ) !== false && strpos( $ua, 'mini' ) !== false ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current browser is Opera Mini, but not on a smart device OS(Android, iOS, etc)
+ * Used to send users on dumb devices to m.wor
+ */
+ public static function is_opera_mini_dumb() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( self::is_opera_mini() ) {
+ if ( strpos( $ua, 'android' ) !== false || strpos( $ua, 'iphone' ) !== false || strpos( $ua, 'ipod' ) !== false
+ || strpos( $ua, 'ipad' ) !== false || strpos( $ua, 'blackberry' ) !== false ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current browser is a Windows Phone 7 device.
+ * ex: Mozilla/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident/3.1; IEMobile/7.0; LG; GW910)
+ */
+ public static function is_WindowsPhone7() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( false === strpos( $ua, 'windows phone os 7' ) ) {
+ return false;
+ } else {
+ if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Detects if the current browser is a Windows Phone 8 device.
+ * ex: Mozilla/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident/6.0; ARM; Touch; IEMobile/10.0; <Manufacturer>; <Device> [;<Operator>])
+ */
+ public static function is_windows_phone_8() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ if ( strpos( $ua, 'windows phone 8' ) === false ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Detects if the current browser is on a Palm device running the new WebOS. This EXCLUDES TouchPad.
+ *
+ * Ex1: Mozilla/5.0 (webOS/1.4.0; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pre/1.1
+ * Ex2: Mozilla/5.0 (webOS/1.4.0; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Pixi/1.1
+ */
+ public static function is_PalmWebOS() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( false === strpos( $ua, 'webos' ) ) {
+ return false;
+ } else {
+ if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Detects if the current browser is the HP TouchPad default browser. This excludes phones wt WebOS.
+ *
+ * TouchPad Emulator: Mozilla/5.0 (hp-desktop; Linux; hpwOS/2.0; U; it-IT) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 Desktop/1.0
+ * TouchPad: Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0
+ */
+ public static function is_TouchPad() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $http_user_agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ if ( false !== strpos( $http_user_agent, 'hp-tablet' ) || false !== strpos( $http_user_agent, 'hpwos' ) || false !== strpos( $http_user_agent, 'touchpad' ) ) {
+ if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current browser is the Series 60 Open Source Browser.
+ *
+ * OSS Browser 3.2 on E75: Mozilla/5.0 (SymbianOS/9.3; U; Series60/3.2 NokiaE75-1/110.48.125 Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413
+ *
+ * 7.0 Browser (Nokia 5800 XpressMusic (v21.0.025)) : Mozilla/5.0 (SymbianOS/9.4; U; Series60/5.0 Nokia5800d-1/21.0.025; Profile/MIDP-2.1 Configuration/CLDC-1.1 ) AppleWebKit/413 (KHTML, like Gecko) Safari/413
+ *
+ * Browser 7.1 (Nokia N97 (v12.0.024)) : Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/12.0.024; Profile/MIDP-2.1 Configuration/CLDC-1.1; en-us) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.12344
+ */
+ public static function is_S60_OSSBrowser() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
+ return false;
+ }
+
+ $pos_webkit = strpos( $agent, 'webkit' );
+ if ( false !== $pos_webkit ) {
+ // First, test for WebKit, then make sure it's either Symbian or S60.
+ if ( strpos( $agent, 'symbian' ) !== false || strpos( $agent, 'series60' ) !== false ) {
+ return true;
+ } else {
+ return false;
+ }
+ } elseif ( strpos( $agent, 'symbianos' ) !== false && strpos( $agent, 'series60' ) !== false ) {
+ return true;
+ } elseif ( strpos( $agent, 'nokia' ) !== false && strpos( $agent, 'series60' ) !== false ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Detects if the device platform is the Symbian Series 60.
+ */
+ public static function is_symbian_platform() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ $pos_webkit = strpos( $agent, 'webkit' );
+ if ( false !== $pos_webkit ) {
+ // First, test for WebKit, then make sure it's either Symbian or S60.
+ if ( strpos( $agent, 'symbian' ) !== false || strpos( $agent, 'series60' ) !== false ) {
+ return true;
+ } else {
+ return false;
+ }
+ } elseif ( strpos( $agent, 'symbianos' ) !== false && strpos( $agent, 'series60' ) !== false ) {
+ return true;
+ } elseif ( strpos( $agent, 'nokia' ) !== false && strpos( $agent, 'series60' ) !== false ) {
+ return true;
+ } elseif ( strpos( $agent, 'opera mini' ) !== false ) {
+ if ( strpos( $agent, 'symbianos' ) !== false || strpos( $agent, 'symbos' ) !== false || strpos( $agent, 'series 60' ) !== false ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Detects if the device platform is the Symbian Series 40.
+ * Nokia Browser for Series 40 is a proxy based browser, previously known as Ovi Browser.
+ * This browser will report 'NokiaBrowser' in the header, however some older version will also report 'OviBrowser'.
+ */
+ public static function is_symbian_s40_platform() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( strpos( $agent, 'series40' ) !== false ) {
+ if ( strpos( $agent, 'nokia' ) !== false || strpos( $agent, 'ovibrowser' ) !== false || strpos( $agent, 'nokiabrowser' ) !== false ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns if the device belongs to J2ME capable family.
+ *
+ * @return bool
+ */
+ public static function is_J2ME_platform() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( strpos( $agent, 'j2me/midp' ) !== false ) {
+ return true;
+ } elseif ( strpos( $agent, 'midp' ) !== false && strpos( $agent, 'cldc' ) ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Detects if the current UA is on one of the Maemo-based Nokia Internet Tablets.
+ */
+ public static function is_MaemoTablet() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ $pos_maemo = strpos( $agent, 'maemo' );
+ if ( false === $pos_maemo ) {
+ return false;
+ }
+
+ // Must be Linux + Tablet, or else it could be something else.
+ if ( strpos( $agent, 'tablet' ) !== false && strpos( $agent, 'linux' ) !== false ) {
+ if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current UA is a MeeGo device (Nokia Smartphone).
+ */
+ public static function is_MeeGo() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( false === strpos( $ua, 'meego' ) ) {
+ return false;
+ } else {
+ if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+
+ /**
+ * The is_webkit() method can be used to check the User Agent for an webkit generic browser.
+ */
+ public static function is_webkit() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ $pos_webkit = strpos( $agent, 'webkit' );
+
+ if ( false !== $pos_webkit ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current browser is the Native Android browser.
+ *
+ * @return boolean true if the browser is Android otherwise false
+ */
+ public static function is_android() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ $pos_android = strpos( $agent, 'android' );
+ if ( false !== $pos_android ) {
+ if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current browser is the Native Android Tablet browser.
+ * Assumes 'Android' should be in the user agent, but not 'mobile'
+ *
+ * @return boolean true if the browser is Android and not 'mobile' otherwise false
+ */
+ public static function is_android_tablet() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ $pos_android = strpos( $agent, 'android' );
+ $pos_mobile = strpos( $agent, 'mobile' );
+ $post_android_app = strpos( $agent, 'wp-android' );
+
+ if ( false !== $pos_android && false === $pos_mobile && false === $post_android_app ) {
+ if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current browser is the Kindle Fire Native browser.
+ *
+ * Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk/1.1.0-84) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 Silk-Accelerated=true
+ * Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk/1.1.0-84) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 Silk-Accelerated=false
+ *
+ * @return boolean true if the browser is Kindle Fire Native browser otherwise false
+ */
+ public static function is_kindle_fire() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ $pos_silk = strpos( $agent, 'silk/' );
+ $pos_silk_acc = strpos( $agent, 'silk-accelerated=' );
+ if ( false !== $pos_silk && false !== $pos_silk_acc ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detects if the current browser is the Kindle Touch Native browser
+ *
+ * Mozilla/5.0 (X11; U; Linux armv7l like Android; en-us) AppleWebKit/531.2+ (KHTML, like Gecko) Version/5.0 Safari/533.2+ Kindle/3.0+
+ *
+ * @return boolean true if the browser is Kindle monochrome Native browser otherwise false
+ */
+ public static function is_kindle_touch() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ $pos_kindle_touch = strpos( $agent, 'kindle/3.0+' );
+ if ( false !== $pos_kindle_touch && false === self::is_kindle_fire() ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detect if user agent is the WordPress.com Windows 8 app (used ONLY on the custom oauth stylesheet)
+ */
+ public static function is_windows8_auth() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ $pos = strpos( $agent, 'msauthhost' );
+ if ( false !== $pos ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detect if user agent is the WordPress.com Windows 8 app.
+ */
+ public static function is_wordpress_for_win8() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ $pos = strpos( $agent, 'wp-windows8' );
+ if ( false !== $pos ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Detect if user agent is the WordPress.com Desktop app.
+ */
+ public static function is_wordpress_desktop_app() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ $pos = strpos( $agent, 'WordPressDesktop' );
+ if ( false !== $pos ) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * The is_blackberry_tablet() method can be used to check the User Agent for a RIM blackberry tablet.
+ * The user agent of the BlackBerry® Tablet OS follows a format similar to the following:
+ * Mozilla/5.0 (PlayBook; U; RIM Tablet OS 1.0.0; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/0.0.1 Safari/534.8+
+ */
+ public static function is_blackberry_tablet() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ $pos_playbook = stripos( $agent, 'PlayBook' );
+ $pos_rim_tablet = stripos( $agent, 'RIM Tablet' );
+
+ if ( ( false === $pos_playbook ) || ( false === $pos_rim_tablet ) ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * The is_blackbeberry() method can be used to check the User Agent for a blackberry device.
+ * Note that opera mini on BB matches this rule.
+ */
+ public static function is_blackbeberry() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ $pos_blackberry = strpos( $agent, 'blackberry' );
+ if ( false !== $pos_blackberry ) {
+ if ( self::is_opera_mini() || self::is_opera_mobile() || self::is_firefox_mobile() ) {
+ return false;
+ } else {
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * The is_blackberry_10() method can be used to check the User Agent for a BlackBerry 10 device.
+ */
+ public static function is_blackberry_10() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ return ( strpos( $agent, 'bb10' ) !== false ) && ( strpos( $agent, 'mobile' ) !== false );
+ }
+
+ /**
+ * Retrieve the blackberry OS version.
+ *
+ * Return strings are from the following list:
+ * - blackberry-10
+ * - blackberry-7
+ * - blackberry-6
+ * - blackberry-torch //only the first edition. The 2nd edition has the OS7 onboard and doesn't need any special rule.
+ * - blackberry-5
+ * - blackberry-4.7
+ * - blackberry-4.6
+ * - blackberry-4.5
+ *
+ * @return string Version of the BB OS.
+ * If version is not found, get_blackbeberry_OS_version will return boolean false.
+ */
+ public static function get_blackbeberry_OS_version() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ if ( self::is_blackberry_10() ) {
+ return 'blackberry-10';
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ $pos_blackberry = stripos( $agent, 'blackberry' );
+ if ( false === $pos_blackberry ) {
+ // Not a blackberry device.
+ return false;
+ }
+
+ // Blackberry devices OS 6.0 or higher.
+ // Mozilla/5.0 (BlackBerry; U; BlackBerry 9670; en) AppleWebKit/534.3+ (KHTML, like Gecko) Version/6.0.0.286 Mobile Safari/534.3+.
+ // Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, Like Gecko) Version/6.0.0.141 Mobile Safari/534.1+.
+ // Mozilla/5.0 (BlackBerry; U; BlackBerry 9900; en-US) AppleWebKit/534.11+ (KHTML, like Gecko) Version/7.0.0 Mobile Safari/534.11+.
+ $pos_webkit = stripos( $agent, 'webkit' );
+ if ( false !== $pos_webkit ) {
+ // Detected blackberry webkit browser.
+ $pos_torch = stripos( $agent, 'BlackBerry 9800' );
+ if ( false !== $pos_torch ) {
+ return 'blackberry-torch'; // Match the torch first edition. the 2nd edition should use the OS7 and doesn't need any special rule.
+ } else {
+ // Detecting the BB OS version for devices running OS 6.0 or higher.
+ if ( preg_match( '#Version\/([\d\.]+)#i', $agent, $matches ) ) {
+ $version = $matches[1];
+ $version_num = explode( '.', $version );
+ if ( false === is_array( $version_num ) || count( $version_num ) <= 1 ) {
+ return 'blackberry-6'; // not a BB device that match our rule.
+ } else {
+ return 'blackberry-' . $version_num[0];
+ }
+ } else {
+ // if doesn't match returns the minimun version with a webkit browser. we should never fall here.
+ return 'blackberry-6'; // not a BB device that match our rule.
+ }
+ }
+ }
+
+ // Blackberry devices <= 5.XX.
+ // BlackBerry9000/5.0.0.93 Profile/MIDP-2.0 Configuration/CLDC-1.1 VendorID/179.
+ if ( preg_match( '#BlackBerry\w+\/([\d\.]+)#i', $agent, $matches ) ) {
+ $version = $matches[1];
+ } else {
+ return false; // not a BB device that match our rule.
+ }
+
+ $version_num = explode( '.', $version );
+
+ if ( is_array( $version_num ) === false || count( $version_num ) <= 1 ) {
+ return false;
+ }
+
+ $version_num_major = (int) $version_num[0];
+ $version_num_minor = (int) $version_num[1];
+
+ if ( 5 === $version_num_major ) {
+ return 'blackberry-5';
+ } elseif ( 4 === $version_num_major && 7 === $version_num_minor ) {
+ return 'blackberry-4.7';
+ } elseif ( 4 === $version_num_major && 6 === $version_num_minor ) {
+ return 'blackberry-4.6';
+ } elseif ( 4 === $version_num_major && 5 === $version_num_minor ) {
+ return 'blackberry-4.5';
+ } else {
+ return false;
+ }
+
+ }
+
+ /**
+ * Retrieve the blackberry browser version.
+ *
+ * Return string are from the following list:
+ * - blackberry-10
+ * - blackberry-webkit
+ * - blackberry-5
+ * - blackberry-4.7
+ * - blackberry-4.6
+ *
+ * @return string Type of the BB browser.
+ * If browser's version is not found, detect_blackbeberry_browser_version will return boolean false.
+ */
+ public static function detect_blackberry_browser_version() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( self::is_blackberry_10() ) {
+ return 'blackberry-10';
+ }
+
+ $pos_blackberry = strpos( $agent, 'blackberry' );
+ if ( false === $pos_blackberry ) {
+ // Not a blackberry device.
+ return false;
+ }
+
+ $pos_webkit = strpos( $agent, 'webkit' );
+
+ if ( ! ( false === $pos_webkit ) ) {
+ return 'blackberry-webkit';
+ } else {
+ if ( ! preg_match( '#BlackBerry\w+\/([\d\.]+)#i', $agent, $matches ) ) {
+ return false; // not a BB device that match our rule.
+ }
+
+ $version_num = explode( '.', $matches[1] );
+
+ if ( false === is_array( $version_num ) || count( $version_num ) <= 1 ) {
+ return false;
+ }
+
+ $version_num_major = (int) $version_num[0];
+ $version_num_minor = (int) $version_num[1];
+
+ if ( 5 === $version_num_major ) {
+ return 'blackberry-5';
+ } elseif ( 4 === $version_num_major && 7 === $version_num_minor ) {
+ return 'blackberry-4.7';
+ } elseif ( 4 === $version_num_major && 6 === $version_num_minor ) {
+ return 'blackberry-4.6';
+ } else {
+ // A very old BB device is found or this is a BB device that doesn't match our rules.
+ return false;
+ }
+ }
+
+ }
+
+ /**
+ * Checks if a visitor is coming from one of the WordPress mobile apps.
+ *
+ * @return bool
+ */
+ public static function is_mobile_app() {
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $agent = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+
+ if ( isset( $_SERVER['X_USER_AGENT'] ) && preg_match( '|wp-webos|', $_SERVER['X_USER_AGENT'] ) ) {
+ return true; // Wp4webos 1.1 or higher.
+ }
+
+ $app_agents = array( 'wp-android', 'wp-blackberry', 'wp-iphone', 'wp-nokia', 'wp-webos', 'wp-windowsphone' );
+ // the mobile reader on iOS has an incorrect UA when loading the reader
+ // currently it is the default one provided by the iOS framework which
+ // causes problems with 2-step-auth
+ // User-Agent WordPress/3.1.4 CFNetwork/609 Darwin/13.0.0.
+ $app_agents[] = 'wordpress/3.1';
+
+ foreach ( $app_agents as $app_agent ) {
+ if ( false !== strpos( $agent, $app_agent ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Detects if the current browser is Nintendo 3DS handheld.
+ *
+ * Example: Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1.7498.US
+ * can differ in language, version and region
+ */
+ public static function is_Nintendo_3DS() {
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ $ua = strtolower( $_SERVER['HTTP_USER_AGENT'] );
+ if ( strpos( $ua, 'nintendo 3ds' ) !== false ) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Was the current request made by a known bot?
+ *
+ * @return boolean
+ */
+ public static function is_bot() {
+ static $is_bot = null;
+
+ if ( empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
+ return false;
+ }
+
+ if ( is_null( $is_bot ) ) {
+ $is_bot = self::is_bot_user_agent( $_SERVER['HTTP_USER_AGENT'] );
+ }
+
+ return $is_bot;
+ }
+
+ /**
+ * Is the given user-agent a known bot?
+ * If you want an is_bot check for the current request's UA, use is_bot() instead of passing a user-agent to this method.
+ *
+ * @param string $ua A user-agent string.
+ *
+ * @return boolean
+ */
+ public static function is_bot_user_agent( $ua = null ) {
+
+ if ( empty( $ua ) ) {
+ return false;
+ }
+
+ $bot_agents = array(
+ 'alexa',
+ 'altavista',
+ 'ask jeeves',
+ 'attentio',
+ 'baiduspider',
+ 'bingbot',
+ 'chtml generic',
+ 'crawler',
+ 'fastmobilecrawl',
+ 'feedfetcher-google',
+ 'firefly',
+ 'froogle',
+ 'gigabot',
+ 'googlebot',
+ 'googlebot-mobile',
+ 'heritrix',
+ 'httrack',
+ 'ia_archiver',
+ 'irlbot',
+ 'iescholar',
+ 'infoseek',
+ 'jumpbot',
+ 'linkcheck',
+ 'lycos',
+ 'mediapartners',
+ 'mediobot',
+ 'motionbot',
+ 'msnbot',
+ 'mshots',
+ 'openbot',
+ 'pss-webkit-request',
+ 'pythumbnail',
+ 'scooter',
+ 'slurp',
+ 'snapbot',
+ 'spider',
+ 'taptubot',
+ 'technoratisnoop',
+ 'teoma',
+ 'twiceler',
+ 'yahooseeker',
+ 'yahooysmcm',
+ 'yammybot',
+ 'ahrefsbot',
+ 'pingdom.com_bot',
+ 'kraken',
+ 'yandexbot',
+ 'twitterbot',
+ 'tweetmemebot',
+ 'openhosebot',
+ 'queryseekerspider',
+ 'linkdexbot',
+ 'grokkit-crawler',
+ 'livelapbot',
+ 'germcrawler',
+ 'domaintunocrawler',
+ 'grapeshotcrawler',
+ 'cloudflare-alwaysonline',
+ );
+
+ foreach ( $bot_agents as $bot_agent ) {
+ if ( false !== stripos( $ua, $bot_agent ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-error/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-error/CHANGELOG.md
new file mode 100644
index 00000000..ea0c6df4
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-error/CHANGELOG.md
@@ -0,0 +1,112 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.3.12] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+
+## [1.3.11] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.3.10] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.3.9] - 2021-10-19
+### Changed
+- Updated package dependencies.
+
+## [1.3.8] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.3.7] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.3.6] - 2021-08-31
+### Changed
+- Run composer update on test-php command instead of phpunit.
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+
+## [1.3.5] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.3.4] - 2021-04-27
+### Changed
+- Updated package dependencies.
+
+## [1.3.3] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.3.2] - 2021-02-05
+
+- CI: Make tests more generic
+
+## [1.3.1] - 2021-01-19
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.3.0] - 2020-12-07
+
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.2.0] - 2020-08-13
+
+- CI: Try collect js coverage
+
+## [1.1.0] - 2020-06-22
+
+- PHPCS: Clean up the packages
+
+## [1.0.4] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## [1.0.2] - 2019-10-28
+
+- Packages: Add gitattributes files to all packages that need th…
+
+## [1.0.1] - 2019-09-20
+
+- Docs: Unify usage of @package phpdoc tags
+
+## 1.0.0 - 2019-09-14
+
+- Packages: Introduce a jetpack-error package
+
+[1.3.12]: https://github.com/Automattic/jetpack-error/compare/v1.3.11...v1.3.12
+[1.3.11]: https://github.com/Automattic/jetpack-error/compare/v1.3.10...v1.3.11
+[1.3.10]: https://github.com/Automattic/jetpack-error/compare/v1.3.9...v1.3.10
+[1.3.9]: https://github.com/Automattic/jetpack-error/compare/v1.3.8...v1.3.9
+[1.3.8]: https://github.com/Automattic/jetpack-error/compare/v1.3.7...v1.3.8
+[1.3.7]: https://github.com/Automattic/jetpack-error/compare/v1.3.6...v1.3.7
+[1.3.6]: https://github.com/Automattic/jetpack-error/compare/v1.3.5...v1.3.6
+[1.3.5]: https://github.com/Automattic/jetpack-error/compare/v1.3.4...v1.3.5
+[1.3.4]: https://github.com/Automattic/jetpack-error/compare/v1.3.3...v1.3.4
+[1.3.3]: https://github.com/Automattic/jetpack-error/compare/v1.3.2...v1.3.3
+[1.3.2]: https://github.com/Automattic/jetpack-error/compare/v1.3.1...v1.3.2
+[1.3.1]: https://github.com/Automattic/jetpack-error/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/Automattic/jetpack-error/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-error/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/Automattic/jetpack-error/compare/v1.0.4...v1.1.0
+[1.0.4]: https://github.com/Automattic/jetpack-error/compare/v1.0.2...v1.0.4
+[1.0.2]: https://github.com/Automattic/jetpack-error/compare/v1.0.1...v1.0.2
+[1.0.1]: https://github.com/Automattic/jetpack-error/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-error/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-error/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-error/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-error/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-error/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-error/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-error/src/class-error.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-error/src/class-error.php
new file mode 100644
index 00000000..579b851f
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-error/src/class-error.php
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Jetpack Error - a wrapper around WP_Error.
+ *
+ * @see https://codex.wordpress.org/Class_Reference/WP_Error
+ *
+ * @package automattic/jetpack-error
+ */
+
+namespace Automattic\Jetpack;
+
+/**
+ * Class Automattic\Jetpack\Error
+ */
+class Error extends \WP_Error {}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/CHANGELOG.md
new file mode 100644
index 00000000..ea1f7642
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/CHANGELOG.md
@@ -0,0 +1,123 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.4.0] - 2022-01-04
+### Changed
+- Updated package textdomain from `jetpack` to `jetpack-heartbeat`.
+
+## [1.3.15] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.3.14] - 2021-11-30
+### Changed
+- Updated package dependencies.
+
+## [1.3.13] - 2021-10-19
+### Changed
+- Updated package dependencies.
+
+## [1.3.12] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.3.11] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.3.10] - 2021-09-03
+
+- Clean up doc blocks.
+
+## [1.3.9] - 2021-08-30
+### Changed
+- Updated versions in annotations
+
+## [1.3.8] - 2021-06-15
+### Changed
+- Updated package dependencies.
+
+## [1.3.7] - 2021-05-25
+### Fixed
+- Fixed new PHPCS errors.
+
+## [1.3.6] - 2021-04-27
+### Changed
+- Updated package dependencies.
+
+## [1.3.5] - 2021-04-08
+### Changed
+- Packaging and build changes, no change to the package itself.
+
+## [1.3.4] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+## [1.3.3] - 2021-02-23
+
+- CI: Make tests more generic
+
+## [1.3.2] - 2021-01-28
+
+- Update dependencies to latest stable
+
+## [1.3.1] - 2021-01-26
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.3.0] - 2021-01-05
+
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.2.2] - 2020-11-24
+
+- Version packages for release
+
+## [1.2.1] - 2020-11-24
+
+- Updated PHPCS: Packages and Debugger
+
+## [1.2.0] - 2020-10-27
+
+- Make XMLRPC methods available for blog token
+
+## [1.1.0] - 2020-09-29
+
+- Update dependencies to latest stable
+
+## 1.0.0 - 2020-08-26
+
+- Connection: use heartbeat to send connected plugins info
+- Use new heartbeat package
+- Creates the Jetpack Heartbeat package
+
+[1.4.0]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.15...v1.4.0
+[1.3.15]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.14...v1.3.15
+[1.3.14]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.13...v1.3.14
+[1.3.13]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.12...v1.3.13
+[1.3.12]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.11...v1.3.12
+[1.3.11]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.10...v1.3.11
+[1.3.10]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.9...v1.3.10
+[1.3.9]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.8...v1.3.9
+[1.3.8]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.7...v1.3.8
+[1.3.7]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.6...v1.3.7
+[1.3.6]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.5...v1.3.6
+[1.3.5]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.4...v1.3.5
+[1.3.4]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.3...v1.3.4
+[1.3.3]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.2...v1.3.3
+[1.3.2]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.1...v1.3.2
+[1.3.1]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.2.2...v1.3.0
+[1.2.2]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.2.1...v1.2.2
+[1.2.1]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.2.0...v1.2.1
+[1.2.0]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/Automattic/jetpack-heartbeat/compare/v1.0.0...v1.1.0
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/src/class-heartbeat.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/src/class-heartbeat.php
new file mode 100644
index 00000000..0cca49bb
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-heartbeat/src/class-heartbeat.php
@@ -0,0 +1,254 @@
+<?php
+/**
+ * Jetpack Heartbeat package.
+ *
+ * @package automattic/jetpack-heartbeat
+ */
+
+namespace Automattic\Jetpack;
+
+use Jetpack_Options;
+use WP_CLI;
+
+/**
+ * Heartbeat sends a batch of stats to wp.com once a day
+ */
+class Heartbeat {
+
+ /**
+ * Holds the singleton instance of this class
+ *
+ * @since 1.0.0
+ * @since-jetpack 2.3.3
+ * @var Heartbeat
+ */
+ private static $instance = false;
+
+ /**
+ * Cronjob identifier
+ *
+ * @var string
+ */
+ private $cron_name = 'jetpack_v2_heartbeat';
+
+ /**
+ * Singleton
+ *
+ * @since 1.0.0
+ * @since-jetpack 2.3.3
+ * @static
+ * @return Heartbeat
+ */
+ public static function init() {
+ if ( ! self::$instance ) {
+ self::$instance = new Heartbeat();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Constructor for singleton
+ *
+ * @since 1.0.0
+ * @since-jetpack 2.3.3
+ */
+ private function __construct() {
+
+ // Schedule the task.
+ add_action( $this->cron_name, array( $this, 'cron_exec' ) );
+
+ if ( ! wp_next_scheduled( $this->cron_name ) ) {
+ // Deal with the old pre-3.0 weekly one.
+ $timestamp = wp_next_scheduled( 'jetpack_heartbeat' );
+ if ( $timestamp ) {
+ wp_unschedule_event( $timestamp, 'jetpack_heartbeat' );
+ }
+
+ wp_schedule_event( time(), 'daily', $this->cron_name );
+ }
+
+ add_filter( 'jetpack_xmlrpc_unauthenticated_methods', array( __CLASS__, 'jetpack_xmlrpc_methods' ) );
+
+ if ( defined( 'WP_CLI' ) && WP_CLI ) {
+ WP_CLI::add_command( 'jetpack-heartbeat', array( $this, 'cli_callback' ) );
+ }
+ }
+
+ /**
+ * Method that gets executed on the wp-cron call
+ *
+ * @since 1.0.0
+ * @since-jetpack 2.3.3
+ * @global string $wp_version
+ */
+ public function cron_exec() {
+
+ $a8c_mc_stats = new A8c_Mc_Stats();
+
+ /*
+ * This should run daily. Figuring in for variances in
+ * WP_CRON, don't let it run more than every 23 hours at most.
+ *
+ * i.e. if it ran less than 23 hours ago, fail out.
+ */
+ $last = (int) Jetpack_Options::get_option( 'last_heartbeat' );
+ if ( $last && ( $last + DAY_IN_SECONDS - HOUR_IN_SECONDS > time() ) ) {
+ return;
+ }
+
+ /*
+ * Check for an identity crisis
+ *
+ * If one exists:
+ * - Bump stat for ID crisis
+ * - Email site admin about potential ID crisis
+ */
+
+ // Coming Soon!
+
+ foreach ( self::generate_stats_array( 'v2-' ) as $key => $value ) {
+ if ( is_array( $value ) ) {
+ foreach ( $value as $v ) {
+ $a8c_mc_stats->add( $key, (string) $v );
+ }
+ } else {
+ $a8c_mc_stats->add( $key, (string) $value );
+ }
+ }
+
+ Jetpack_Options::update_option( 'last_heartbeat', time() );
+
+ $a8c_mc_stats->do_server_side_stats();
+
+ /**
+ * Fires when we synchronize all registered options on heartbeat.
+ *
+ * @since 3.3.0
+ */
+ do_action( 'jetpack_heartbeat' );
+ }
+
+ /**
+ * Generates heartbeat stats data.
+ *
+ * @param string $prefix Prefix to add before stats identifier.
+ *
+ * @return array The stats array.
+ */
+ public static function generate_stats_array( $prefix = '' ) {
+
+ /**
+ * This filter is used to build the array of stats that are bumped once a day by Jetpack Heartbeat.
+ *
+ * Filter the array and add key => value pairs where
+ * * key is the stat group name
+ * * value is the stat name.
+ *
+ * Example:
+ * add_filter( 'jetpack_heartbeat_stats_array', function( $stats ) {
+ * $stats['is-https'] = is_ssl() ? 'https' : 'http';
+ * });
+ *
+ * This will bump the stats for the 'is-https/https' or 'is-https/http' stat.
+ *
+ * @param array $stats The stats to be filtered.
+ * @param string $prefix The prefix that will automatically be added at the begining at each stat group name.
+ */
+ $stats = apply_filters( 'jetpack_heartbeat_stats_array', array(), $prefix );
+ $return = array();
+
+ // Apply prefix to stats.
+ foreach ( $stats as $stat => $value ) {
+ $return[ "$prefix$stat" ] = $value;
+ }
+
+ return $return;
+
+ }
+
+ /**
+ * Registers jetpack.getHeartbeatData xmlrpc method
+ *
+ * @param array $methods The list of methods to be filtered.
+ * @return array $methods
+ */
+ public static function jetpack_xmlrpc_methods( $methods ) {
+ $methods['jetpack.getHeartbeatData'] = array( __CLASS__, 'xmlrpc_data_response' );
+ return $methods;
+ }
+
+ /**
+ * Handles the response for the jetpack.getHeartbeatData xmlrpc method
+ *
+ * @param array $params The parameters received in the request.
+ * @return array $params all the stats that heartbeat handles.
+ */
+ public static function xmlrpc_data_response( $params = array() ) {
+ // The WordPress XML-RPC server sets a default param of array()
+ // if no argument is passed on the request and the method handlers get this array in $params.
+ // generate_stats_array() needs a string as first argument.
+ $params = empty( $params ) ? '' : $params;
+ return self::generate_stats_array( $params );
+ }
+
+ /**
+ * Clear scheduled events
+ *
+ * @return void
+ */
+ public function deactivate() {
+ // Deal with the old pre-3.0 weekly one.
+ $timestamp = wp_next_scheduled( 'jetpack_heartbeat' );
+ if ( $timestamp ) {
+ wp_unschedule_event( $timestamp, 'jetpack_heartbeat' );
+ }
+
+ $timestamp = wp_next_scheduled( $this->cron_name );
+ wp_unschedule_event( $timestamp, $this->cron_name );
+ }
+
+ /**
+ * Interact with the Heartbeat
+ *
+ * ## OPTIONS
+ *
+ * inspect (default): Gets the list of data that is going to be sent in the heartbeat and the date/time of the last heartbeat
+ *
+ * @param array $args Arguments passed via CLI.
+ *
+ * @return void
+ */
+ public function cli_callback( $args ) {
+
+ $allowed_args = array(
+ 'inspect',
+ );
+
+ if ( isset( $args[0] ) && ! in_array( $args[0], $allowed_args, true ) ) {
+ /* translators: %s is a command like "prompt" */
+ WP_CLI::error( sprintf( __( '%s is not a valid command.', 'jetpack-heartbeat' ), $args[0] ) );
+ }
+
+ $stats = self::generate_stats_array();
+ $formatted_stats = array();
+
+ foreach ( $stats as $stat_name => $bin ) {
+ $formatted_stats[] = array(
+ 'Stat name' => $stat_name,
+ 'Bin' => $bin,
+ );
+ }
+
+ WP_CLI\Utils\format_items( 'table', $formatted_stats, array( 'Stat name', 'Bin' ) );
+
+ $last_heartbeat = Jetpack_Options::get_option( 'last_heartbeat' );
+
+ if ( $last_heartbeat ) {
+ $last_date = gmdate( 'Y-m-d H:i:s', $last_heartbeat );
+ /* translators: %s is the full datetime of the last heart beat e.g. 2020-01-01 12:21:23 */
+ WP_CLI::line( sprintf( __( 'Last heartbeat sent at: %s', 'jetpack-heartbeat' ), $last_date ) );
+ }
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/CHANGELOG.md
new file mode 100644
index 00000000..3039161a
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/CHANGELOG.md
@@ -0,0 +1,144 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [0.6.1] - 2022-01-11
+### Fixed
+- Do not add IDC query args to authenticated request when in offline or staging mode.
+
+## [0.6.0] - 2022-01-04
+### Added
+- Build and display the new RNA IDC banner.
+
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies.
+- Updated package textdomain from `jetpack` to `jetpack-idc`.
+- Updated references to '.jp-recommendations__app-badge' to its new name '.apps-badge'
+
+## [0.5.0] - 2021-12-14
+### Added
+- Add a method to determine the safe mode status.
+
+### Changed
+- Updated package dependencies.
+
+## [0.4.4] - 2021-11-30
+### Changed
+- Colors: update Jetpack Primary color to match latest brand book.
+- Remove now-redundant `output.filename` from Webpack config.
+
+## [0.4.3] - 2021-11-23
+### Changed
+- Updated package dependencies.
+
+## [0.4.2] - 2021-11-17
+
+## [0.4.1] - 2021-11-16
+### Added
+- Use monorepo `validate-es` script to validate Webpack builds.
+
+### Changed
+- Updated package dependencies
+
+## [0.4.0] - 2021-11-09
+### Added
+- Add a method to unambiguously determine whether the site is experiencing identity crisis.
+
+### Changed
+- Updated package dependencies.
+- Update webpack build config. Removes IE 11 support in the JavaScript.
+
+## [0.3.1] - 2021-11-02
+### Changed
+- Add the idc url query args to remote requests
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [0.3.0] - 2021-10-26
+### Added
+- Add redirect_uri parameter for IDC Start Fresh endpoint.
+- Delete the migrate_for_idc option when a remote request returns migrated_for_idc
+
+### Changed
+- Updated package dependencies
+
+## [0.2.8] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [0.2.7] - 2021-10-12
+### Added
+- Add the new check_response_for_idc method to the Identity_Crisis class
+
+### Changed
+- Updated package dependencies
+
+## [0.2.6] - 2021-09-28
+### Changed
+- Allow Node ^14.17.6 to be used in this project. This shouldn't change the behavior of the code itself.
+- IDC: Rename the Identity_Crisis::sync_idc_optin method to Identity_Crisis:should_handle_idc. Add a new filter and constant that use the new name.
+- Updated package dependencies.
+
+## [0.2.5] - 2021-08-31
+### Changed
+- Updated package dependencies.
+
+## [0.2.4] - 2021-08-30
+### Changed
+- Bump changelogger version
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- update annotations versions
+- Update to latest webpack, webpack-cli and calypso-build
+- Use Node 16.7.0 in tooling. This shouldn't change the behavior of the code itself.
+
+## [0.2.3] - 2021-08-12
+### Changed
+- Updated package dependencies
+
+## [0.2.2] - 2021-07-27
+### Added
+- Add jetpack_connection_disconnect_site_wpcom filter.
+
+## [0.2.1] - 2021-07-13
+### Changed
+- Updated package dependencies.
+
+## [0.2.0] - 2021-06-29
+### Added
+- Add jetpack_idc_disconnect hook to properly disconnect based on IDC settings and clear IDC options.
+
+### Changed
+- Migrate jetpack/v4/identity-crisis endpoints into package.
+- Update node version requirement to 14.16.1
+
+## 0.1.0 - 2021-06-15
+### Added
+- Sync: Adding the Identity_Crisis package.
+
+### Changed
+- Updated package dependencies.
+- Use Connection/Urls for home_url and site_url functions migrated from Sync.
+
+[0.6.1]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.6.0...v0.6.1
+[0.6.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.5.0...v0.6.0
+[0.5.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.4.4...v0.5.0
+[0.4.4]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.4.3...v0.4.4
+[0.4.3]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.4.2...v0.4.3
+[0.4.2]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.4.1...v0.4.2
+[0.4.1]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.4.0...v0.4.1
+[0.4.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.3.1...v0.4.0
+[0.3.1]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.3.0...v0.3.1
+[0.3.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.2.8...v0.3.0
+[0.2.8]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.2.7...v0.2.8
+[0.2.7]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.2.6...v0.2.7
+[0.2.6]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.2.5...v0.2.6
+[0.2.5]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.2.4...v0.2.5
+[0.2.4]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.2.3...v0.2.4
+[0.2.3]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.2.2...v0.2.3
+[0.2.2]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.2.1...v0.2.2
+[0.2.1]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.2.0...v0.2.1
+[0.2.0]: https://github.com/Automattic/jetpack-identity-crisis/compare/v0.1.0...v0.2.0
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/babel.config.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/babel.config.js
new file mode 100644
index 00000000..d730e3e8
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/babel.config.js
@@ -0,0 +1,10 @@
+const config = {
+ presets: [
+ [
+ '@automattic/jetpack-webpack-config/babel/preset',
+ { pluginReplaceTextdomain: { textdomain: 'jetpack-idc' } },
+ ],
+ ],
+};
+
+module.exports = config;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.asset.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.asset.php
new file mode 100644
index 00000000..4ed4784a
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-components', 'wp-compose', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-url'), 'version' => '472c28cf4d566875562ae0cb3c2e44b1'); \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.css b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.css
new file mode 100644
index 00000000..ea21ba25
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.css
@@ -0,0 +1 @@
+#wp-admin-bar-jetpack-idc.hide{display:none}#wp-admin-bar-jetpack-idc .jp-idc-admin-bar{background:#fff;border-radius:2px;color:#1d2327;font-size:12px;padding:4px 8px}#wpadminbar #wp-admin-bar-jetpack-idc .dashicons{color:#1d2327;font-family:dashicons}#wpadminbar #wp-admin-bar-jetpack-idc .dashicons:before{font-size:16px}#wpadminbar #wp-admin-bar-jetpack-idc:hover .ab-item{background:inherit}#wpadminbar #wp-admin-bar-jetpack-idc:hover .jp-idc-admin-bar{background:#f0f0f1}#jp-identity-crisis-container .jp-idc__idc-screen{margin-bottom:40px;margin-top:40px}.jp-idc__idc-screen{background:#fff;border-left:4px solid #e68b28;border-radius:4px;box-shadow:0 0 40px rgba(0,0,0,.04);box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;margin:0 auto;max-width:1128px;padding:10px}.jp-idc__idc-screen.jp-idc__idc-screen__success{border-color:#069e08}@media(min-width:600px){.jp-idc__idc-screen{padding:48px}}.jp-idc__idc-screen .jp-idc__idc-screen__header{align-items:center;display:flex}.jp-idc__idc-screen .jp-idc__idc-screen__header .jp-idc__idc-screen__logo-image{max-height:100px;max-width:100px}.jp-idc__idc-screen .jp-idc__idc-screen__header .jp-idc__idc-screen__logo-label{font-size:14px;line-height:22px;margin:-7px 0 0 8px}.jp-idc__idc-screen h2{font-size:24px;font-weight:600;line-height:28px;margin:32px 0 0}.jp-idc__idc-screen h3{font-size:20px;font-weight:600;line-height:28px;margin:24px 0 0}.jp-idc__idc-screen p{color:#2c3338;font-size:16px;line-height:24px;margin:16px 0 0;max-width:710px}.jp-idc__idc-screen a{color:#2c3338}.jp-idc__idc-screen .jp-idc__idc-screen__cards{align-items:center;display:flex;flex-direction:column;flex-wrap:wrap}@media only screen and (min-width:1403px){.jp-idc__idc-screen .jp-idc__idc-screen__cards{align-items:normal;flex-direction:row}}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__cards-separator{align-self:center;color:#23282d;font-size:20px;font-weight:600;line-height:28px;margin:0 24px}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base{border:1px solid #c3c4c7;border-radius:4px;box-sizing:border-box;display:flex;flex-direction:column;justify-content:space-between;margin:24px 0;max-width:100%;padding:10px;width:480px}@media(min-width:600px){.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base{padding:24px}}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base h4{font-size:20px;font-weight:400;line-height:28px;margin:0 0 8px}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base p{margin:0 0 24px}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base .jp-idc__idc-screen__card-action-sitename{background:#f9f9f6;border-radius:33px;box-sizing:border-box;color:#2c3338;font-size:16px;font-weight:700;line-height:24px;overflow-wrap:anywhere;padding:16px;text-align:center;width:100%}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base .jp-idc__idc-screen__card-action-separator{display:block;margin:12px auto}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base .jp-idc__idc-screen__card-action-button{padding:8px 24px;width:100%}.jp-idc__idc-screen .jp-idc__idc-screen__card-action-button{background:#000;border-radius:4px;color:#fff;font-size:16px;font-weight:600;height:auto;justify-content:center;line-height:24px;margin-top:24px;min-height:40px;padding:8px}.jp-idc__idc-screen .jp-idc__idc-screen__card-action-button-migrated{margin-top:64px;width:141px}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated{align-items:center;display:flex;flex-direction:column;flex-wrap:wrap;margin-top:24px;width:100%}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-hostname{border:1px solid #c3c4c7;border-radius:4px;color:#2c3338;flex-grow:1;font-size:16px;font-weight:700;line-height:24px;padding:24px;width:100%}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-separator-wide{display:none}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-separator{display:block}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-separator,.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-separator-wide{margin:28px}@media only screen and (min-width:1400px){.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated{flex-direction:row;width:auto}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-separator{display:none}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-separator-wide{display:block}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-hostname{width:auto}}@keyframes rotate-spinner{to{transform:rotate(1turn)}}.jp-components-spinner{align-items:center;display:flex}.jp-components-spinner__inner,.jp-components-spinner__outer{animation:3s linear infinite;animation-name:rotate-spinner;border:.1em solid transparent;border-radius:50%;box-sizing:border-box;margin:auto}.jp-components-spinner__outer{border-top-color:#fff}.jp-components-spinner__inner{border-right-color:#fff;border-top-color:#fff;height:100%;opacity:.4;width:100%}:root{--font-title-large:36px;--font-title-small:24px;--font-body:16px;--font-label:12px;--jp-black:#000;--jp-black-80:#2c3338;--jp-white:#fff;--jp-white-off:#f9f9f6;--jp-gray:#dcdcde;--jp-gray-0:#f6f7f7;--jp-gray-20:#a7aaad;--jp-gray-40:#787c82;--jp-gray-50:#646970;--jp-gray-60:#50575e;--jp-gray-80:#8a2424;--jp-gray-off:#e2e2df;--jp-red-0:#f7ebec;--jp-red-50:#d63638;--jp-red-60:#b32d2e;--jp-red-80:#8a2424;--jp-red:#d63639;--jp-pink:#c9356e;--jp-green-0:#f0f2eb;--jp-green-5:#d0e6b8;--jp-green-10:#9dd977;--jp-green-20:#64ca43;--jp-green-30:#2fb41f;--jp-green-40:#069e08;--jp-green-50:#008710;--jp-green-60:#007117;--jp-green-70:#005b18;--jp-green-80:#004515;--jp-green-90:#003010;--jp-green-100:#001c09;--jp-green:#069e08;--jp-green-primary:var( --jp-green-40 );--jp-green-secondary:var( --jp-green-30 );--jp-border-radius:4px;--jp-menu-border-height:1px;--jp-underline-thickness:2px}*{box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;margin:0;min-height:100%;padding:0}.jp-wrap{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.jp-row{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.jp-row{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.jp-row{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.sm-col-span-1{grid-column-end:span 1}.sm-col-span-2{grid-column-end:span 2}.sm-col-span-3{grid-column-end:span 3}.sm-col-span-4{grid-column-end:span 4}@media(min-width:600px){.md-col-span-1{grid-column-end:span 1}.md-col-span-2{grid-column-end:span 2}.md-col-span-3{grid-column-end:span 3}.md-col-span-4{grid-column-end:span 4}.md-col-span-5{grid-column-end:span 5}.md-col-span-6{grid-column-end:span 6}.md-col-span-7{grid-column-end:span 7}.md-col-span-8{grid-column-end:span 8}}@media(min-width:960px){.lg-col-span-1{grid-column-end:span 1}.lg-col-span-2{grid-column-end:span 2}.lg-col-span-3{grid-column-end:span 3}.lg-col-span-4{grid-column-end:span 4}.lg-col-span-5{grid-column-end:span 5}.lg-col-span-6{grid-column-end:span 6}.lg-col-span-7{grid-column-end:span 7}.lg-col-span-8{grid-column-end:span 8}.lg-col-span-9{grid-column-end:span 9}.lg-col-span-10{grid-column-end:span 10}.lg-col-span-11{grid-column-end:span 11}.lg-col-span-12{grid-column-end:span 12}}@media(max-width:960px){.md-col-span-0{display:none}}@media(max-width:600px){.sm-col-span-0{display:none}}.jp-cut{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);margin:32px 0;padding:16px 64px 16px 24px;position:relative;text-decoration:none}.jp-cut,.jp-cut span{display:block}.jp-cut span:last-of-type{font-weight:600}.jp-cut:focus span:last-of-type,.jp-cut:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.jp-cut:focus:after,.jp-cut:hover:after{transform:translateY(-50%) translateX(8px)}.jp-cut:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;position:absolute;right:24px;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.jp-idc__idc-screen .jp-idc__error-message{align-items:center;color:var(--jp-red);display:flex;flex-direction:row;justify-content:center;margin:15px 0}.jp-idc__idc-screen .jp-idc__error-message .error-gridicon{fill:var(--jp-red);margin-right:8px}.jp-idc__idc-screen .jp-idc__error-message a,.jp-idc__idc-screen .jp-idc__error-message span{color:var(--jp-red);font-size:var(--font-body)}.jp-idc__idc-screen .jp-idc__safe-mode .jp-idc__error-message{margin-top:5px}.jp-idc__idc-screen .jp-idc__idc-screen__cards.jp-idc__idc-screen__cards-error .jp-idc__idc-screen__card-action-base{padding-bottom:75px}.jp-idc__idc-screen .jp-idc__idc-screen__cards.jp-idc__idc-screen__cards-error .jp-idc__idc-screen__card-action-base.jp-idc__idc-screen__card-action-error{padding-bottom:5px}.jp-idc__idc-screen .jp-idc__idc-screen__cards.jp-idc__idc-screen__cards-error .jp-idc__idc-screen__card-action-base .jp-idc__error-message{height:40px}.jp-idc__idc-screen .jp-idc__safe-mode{text-align:center}.jp-idc__idc-screen .jp-idc__safe-mode .jp-idc__safe-mode__staying-safe{display:flex;justify-content:center;padding:6px}.jp-idc__idc-screen .jp-idc__safe-mode .jp-idc__safe-mode__staying-safe .jp-components-spinner{margin:0 10px}.jp-idc__idc-screen .jp-idc__safe-mode,.jp-idc__idc-screen .jp-idc__safe-mode button{color:#2c3338;font-size:16px;line-height:24px}.jp-idc__idc-screen .jp-idc__safe-mode button{padding:0;text-decoration:underline} \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.js
new file mode 100644
index 00000000..7e9ca216
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.js
@@ -0,0 +1,8 @@
+/*! For license information please see index.js.LICENSE.txt */
+!function(){var e={7538:function(e){e.exports=function(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e},e.exports.default=e.exports,e.exports.__esModule=!0},9183:function(e){function t(){return e.exports=t=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},e.exports.default=e.exports,e.exports.__esModule=!0,t.apply(this,arguments)}e.exports=t,e.exports.default=e.exports,e.exports.__esModule=!0},9105:function(e,t){var n;!function(){"use strict";var r={}.hasOwnProperty;function s(){for(var e=[],t=0;t<arguments.length;t++){var n=arguments[t];if(n){var i=typeof n;if("string"===i||"number"===i)e.push(n);else if(Array.isArray(n)){if(n.length){var o=s.apply(null,n);o&&e.push(o)}}else if("object"===i)if(n.toString===Object.prototype.toString)for(var a in n)r.call(n,a)&&n[a]&&e.push(a);else e.push(n.toString())}}return e.join(" ")}e.exports?(s.default=s,e.exports=s):void 0===(n=function(){return s}.apply(t,[]))||(e.exports=n)}()},5771:function(e,t,n){t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const n="color: "+this.color;t.splice(1,0,n,"color: inherit");let r=0,s=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{"%%"!==e&&(r++,"%c"===e&&(s=r))})),t.splice(s,0,n)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}!e&&"undefined"!=typeof process&&"env"in process&&(e=process.env.DEBUG);return e},t.useColors=function(){if("undefined"!=typeof window&&window.process&&("renderer"===window.process.type||window.process.__nwjs))return!0;if("undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;return"undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)},t.storage=function(){try{return localStorage}catch(e){}}(),t.destroy=(()=>{let e=!1;return()=>{e||(e=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=n(1244)(t);const{formatters:r}=e.exports;r.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},1244:function(e,t,n){e.exports=function(e){function t(e){let n,s,i,o=null;function a(...e){if(!a.enabled)return;const r=a,s=Number(new Date),i=s-(n||s);r.diff=i,r.prev=n,r.curr=s,n=s,e[0]=t.coerce(e[0]),"string"!=typeof e[0]&&e.unshift("%O");let o=0;e[0]=e[0].replace(/%([a-zA-Z%])/g,((n,s)=>{if("%%"===n)return"%";o++;const i=t.formatters[s];if("function"==typeof i){const t=e[o];n=i.call(r,t),e.splice(o,1),o--}return n})),t.formatArgs.call(r,e);(r.log||t.log).apply(r,e)}return a.namespace=e,a.useColors=t.useColors(),a.color=t.selectColor(e),a.extend=r,a.destroy=t.destroy,Object.defineProperty(a,"enabled",{enumerable:!0,configurable:!1,get:()=>null!==o?o:(s!==t.namespaces&&(s=t.namespaces,i=t.enabled(e)),i),set:e=>{o=e}}),"function"==typeof t.init&&t.init(a),a}function r(e,n){const r=t(this.namespace+(void 0===n?":":n)+e);return r.log=this.log,r}function s(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return t.debug=t,t.default=t,t.coerce=function(e){if(e instanceof Error)return e.stack||e.message;return e},t.disable=function(){const e=[...t.names.map(s),...t.skips.map(s).map((e=>"-"+e))].join(",");return t.enable(""),e},t.enable=function(e){let n;t.save(e),t.namespaces=e,t.names=[],t.skips=[];const r=("string"==typeof e?e:"").split(/[\s,]+/),s=r.length;for(n=0;n<s;n++)r[n]&&("-"===(e=r[n].replace(/\*/g,".*?"))[0]?t.skips.push(new RegExp("^"+e.substr(1)+"$")):t.names.push(new RegExp("^"+e+"$")))},t.enabled=function(e){if("*"===e[e.length-1])return!0;let n,r;for(n=0,r=t.skips.length;n<r;n++)if(t.skips[n].test(e))return!1;for(n=0,r=t.names.length;n<r;n++)if(t.names[n].test(e))return!0;return!1},t.humanize=n(2002),t.destroy=function(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")},Object.keys(e).forEach((n=>{t[n]=e[n]})),t.names=[],t.skips=[],t.formatters={},t.selectColor=function(e){let n=0;for(let t=0;t<e.length;t++)n=(n<<5)-n+e.charCodeAt(t),n|=0;return t.colors[Math.abs(n)%t.colors.length]},t.enable(t.load()),t}},6212:function(){},4943:function(){},8911:function(){},2780:function(){},2002:function(e){var t=1e3,n=60*t,r=60*n,s=24*r,i=7*s,o=365.25*s;function a(e,t,n,r){var s=t>=1.5*n;return Math.round(e/n)+" "+r+(s?"s":"")}e.exports=function(e,c){c=c||{};var l=typeof e;if("string"===l&&e.length>0)return function(e){if((e=String(e)).length>100)return;var a=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!a)return;var c=parseFloat(a[1]);switch((a[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return c*o;case"weeks":case"week":case"w":return c*i;case"days":case"day":case"d":return c*s;case"hours":case"hour":case"hrs":case"hr":case"h":return c*r;case"minutes":case"minute":case"mins":case"min":case"m":return c*n;case"seconds":case"second":case"secs":case"sec":case"s":return c*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return c;default:return}}(e);if("number"===l&&isFinite(e))return c.long?function(e){var i=Math.abs(e);if(i>=s)return a(e,i,s,"day");if(i>=r)return a(e,i,r,"hour");if(i>=n)return a(e,i,n,"minute");if(i>=t)return a(e,i,t,"second");return e+" ms"}(e):function(e){var i=Math.abs(e);if(i>=s)return Math.round(e/s)+"d";if(i>=r)return Math.round(e/r)+"h";if(i>=n)return Math.round(e/n)+"m";if(i>=t)return Math.round(e/t)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},9587:function(e,t,n){"use strict";var r=n(5843);function s(){}function i(){}i.resetWarningCache=s,e.exports=function(){function e(e,t,n,s,i,o){if(o!==r){var a=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw a.name="Invariant Violation",a}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:i,resetWarningCache:s};return n.PropTypes=n,n}},1268:function(e,t,n){e.exports=n(9587)()},5843:function(e){"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},816:function(e,t,n){"use strict";var r=n(5771),s=n.n(r),i=n(2819);const o=s()("dops:analytics");let a,c;window._tkq=window._tkq||[],window.ga=window.ga||function(){(window.ga.q=window.ga.q||[]).push(arguments)},window.ga.l=+new Date;const l={initialize:function(e,t,n){l.setUser(e,t),l.setSuperProps(n),l.identifyUser()},setGoogleAnalyticsEnabled:function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;this.googleAnalyticsEnabled=e,this.googleAnalyticsKey=t},setMcAnalyticsEnabled:function(e){this.mcAnalyticsEnabled=e},setUser:function(e,t){c={ID:e,username:t}},setSuperProps:function(e){a=e},mc:{bumpStat:function(e,t){const n=function(e,t){let n="";if("object"==typeof e){for(const t in e)n+="&x_"+encodeURIComponent(t)+"="+encodeURIComponent(e[t]);o("Bumping stats %o",e)}else n="&x_"+encodeURIComponent(e)+"="+encodeURIComponent(t),o('Bumping stat "%s" in group "%s"',t,e);return n}(e,t);this.mcAnalyticsEnabled&&((new Image).src=document.location.protocol+"//pixel.wp.com/g.gif?v=wpcom-no-pv"+n+"&t="+Math.random())},bumpStatWithPageView:function(e,t){const n=function(e,t){let n="";if("object"==typeof e){for(const t in e)n+="&"+encodeURIComponent(t)+"="+encodeURIComponent(e[t]);o("Built stats %o",e)}else n="&"+encodeURIComponent(e)+"="+encodeURIComponent(t),o('Built stat "%s" in group "%s"',t,e);return n}(e,t);this.mcAnalyticsEnabled&&((new Image).src=document.location.protocol+"//pixel.wp.com/g.gif?v=wpcom"+n+"&t="+Math.random())}},pageView:{record:function(e,t){l.tracks.recordPageView(e),l.ga.recordPageView(e,t)}},purchase:{record:function(e,t,n,r,s,i,o){l.ga.recordPurchase(e,t,n,r,s,i,o)}},tracks:{recordEvent:function(e,t){t=t||{},0===e.indexOf("akismet_")||0===e.indexOf("jetpack_")?(a&&(o("- Super Props: %o",a),t=(0,i.assign)(t,a)),o('Record event "%s" called with props %s',e,JSON.stringify(t)),window._tkq.push(["recordEvent",e,t])):o('- Event name must be prefixed by "akismet_" or "jetpack_"')},recordJetpackClick:function(e){const t="object"==typeof e?e:{target:e};l.tracks.recordEvent("jetpack_wpa_click",t)},recordPageView:function(e){l.tracks.recordEvent("akismet_page_view",{path:e})},setOptOut:function(e){o("Pushing setOptOut: %o",e),window._tkq.push(["setOptOut",e])}},ga:{initialized:!1,initialize:function(){let e={};l.ga.initialized||(c&&(e={userId:"u-"+c.ID}),window.ga("create",this.googleAnalyticsKey,"auto",e),l.ga.initialized=!0)},recordPageView:function(e,t){l.ga.initialize(),o("Recording Page View ~ [URL: "+e+"] [Title: "+t+"]"),this.googleAnalyticsEnabled&&(window.ga("set","page",e),window.ga("send",{hitType:"pageview",page:e,title:t}))},recordEvent:function(e,t,n,r){l.ga.initialize();let s="Recording Event ~ [Category: "+e+"] [Action: "+t+"]";void 0!==n&&(s+=" [Option Label: "+n+"]"),void 0!==r&&(s+=" [Option Value: "+r+"]"),o(s),this.googleAnalyticsEnabled&&window.ga("send","event",e,t,n,r)},recordPurchase:function(e,t,n,r,s,i,o){window.ga("require","ecommerce"),window.ga("ecommerce:addTransaction",{id:e,revenue:r,currency:o}),window.ga("ecommerce:addItem",{id:e,name:t,sku:n,price:s,quantity:i}),window.ga("ecommerce:send")}},identifyUser:function(){c&&window._tkq.push(["identifyUser",c.ID,c.username])},setProperties:function(e){window._tkq.push(["setProperties",e])},clearedIdentity:function(){window._tkq.push(["clearIdentity"])}};t.Z=l},9570:function(e,t,n){"use strict";var r=n(2819),s=n(6483),i=n(6251);function o(e){class t extends Error{constructor(){super(...arguments),this.name=e}}return t}const a=o("JsonParseError"),c=o("JsonParseAfterRedirectError"),l=o("Api404Error"),u=o("Api404AfterRedirectError"),d=o("FetchNetworkError");const p=new function(e,t){let n=e,o={"X-WP-Nonce":t},a={credentials:"same-origin",headers:o},c={method:"post",credentials:"same-origin",headers:(0,r.assign)({},o,{"Content-type":"application/json"})},l=function(e){const t=e.split("?"),n=t.length>1?t[1]:"",r=n.length?n.split("&"):[];return r.push("_cacheBuster="+(new Date).getTime()),t[0]+"?"+r.join("&")};const u={setApiRoot(e){n=e},setApiNonce(e){o={"X-WP-Nonce":e},a={credentials:"same-origin",headers:o},c={method:"post",credentials:"same-origin",headers:(0,r.assign)({},o,{"Content-type":"application/json"})}},setCacheBusterCallback:e=>{l=e},registerSite:(e,t)=>{const r={registration_nonce:e,no_iframe:!0};return(0,i.jetpackConfigHas)("consumer_slug")&&(r.plugin_slug=(0,i.jetpackConfigGet)("consumer_slug")),null!==t&&(r.redirect_uri=t),p(`${n}jetpack/v4/connection/register`,c,{body:JSON.stringify(r)}).then(h).then(m)},fetchAuthorizationUrl:e=>d((0,s.addQueryArgs)(`${n}jetpack/v4/connection/authorize_url`,{no_iframe:"1",redirect_uri:e}),a).then(h).then(m),fetchSiteConnectionData:()=>d(`${n}jetpack/v4/connection/data`,a).then(m),fetchSiteConnectionStatus:()=>d(`${n}jetpack/v4/connection`,a).then(m),fetchSiteConnectionTest:()=>d(`${n}jetpack/v4/connection/test`,a).then(h).then(m),fetchUserConnectionData:()=>d(`${n}jetpack/v4/connection/data`,a).then(m),fetchUserTrackingSettings:()=>d(`${n}jetpack/v4/tracking/settings`,a).then(h).then(m),updateUserTrackingSettings:e=>p(`${n}jetpack/v4/tracking/settings`,c,{body:JSON.stringify(e)}).then(h).then(m),disconnectSite:()=>p(`${n}jetpack/v4/connection`,c,{body:JSON.stringify({isActive:!1})}).then(h).then(m),fetchConnectUrl:()=>d(`${n}jetpack/v4/connection/url`,a).then(h).then(m),unlinkUser:()=>p(`${n}jetpack/v4/connection/user`,c,{body:JSON.stringify({linked:!1})}).then(h).then(m),reconnect:()=>p(`${n}jetpack/v4/connection/reconnect`,c).then(h).then(m),fetchConnectedPlugins:()=>d(`${n}jetpack/v4/connection/plugins`,a).then(h).then(m),setHasSeenWCConnectionModal:()=>p(`${n}jetpack/v4/seen-wc-connection-modal`,c).then(h).then(m),fetchModules:()=>d(`${n}jetpack/v4/module/all`,a).then(h).then(m),fetchModule:e=>d(`${n}jetpack/v4/module/${e}`,a).then(h).then(m),activateModule:e=>p(`${n}jetpack/v4/module/${e}/active`,c,{body:JSON.stringify({active:!0})}).then(h).then(m),deactivateModule:e=>p(`${n}jetpack/v4/module/${e}/active`,c,{body:JSON.stringify({active:!1})}),updateModuleOptions:(e,t)=>p(`${n}jetpack/v4/module/${e}`,c,{body:JSON.stringify(t)}).then(h).then(m),updateSettings:e=>p(`${n}jetpack/v4/settings`,c,{body:JSON.stringify(e)}).then(h).then(m),getProtectCount:()=>d(`${n}jetpack/v4/module/protect/data`,a).then(h).then(m),resetOptions:e=>p(`${n}jetpack/v4/options/${e}`,c,{body:JSON.stringify({reset:!0})}).then(h).then(m),activateVaultPress:()=>p(`${n}jetpack/v4/plugins`,c,{body:JSON.stringify({slug:"vaultpress",status:"active"})}).then(h).then(m),getVaultPressData:()=>d(`${n}jetpack/v4/module/vaultpress/data`,a).then(h).then(m),installPlugin:(e,t)=>{const r={slug:e,status:"active"};return t&&(r.source=t),p(`${n}jetpack/v4/plugins`,c,{body:JSON.stringify(r)}).then(h).then(m)},activateAkismet:()=>p(`${n}jetpack/v4/plugins`,c,{body:JSON.stringify({slug:"akismet",status:"active"})}).then(h).then(m),getAkismetData:()=>d(`${n}jetpack/v4/module/akismet/data`,a).then(h).then(m),checkAkismetKey:()=>d(`${n}jetpack/v4/module/akismet/key/check`,a).then(h).then(m),checkAkismetKeyTyped:e=>p(`${n}jetpack/v4/module/akismet/key/check`,c,{body:JSON.stringify({api_key:e})}).then(h).then(m),fetchStatsData:e=>d(function(e){let t=`${n}jetpack/v4/module/stats/data`;-1!==t.indexOf("?")?t+=`&range=${encodeURIComponent(e)}`:t+=`?range=${encodeURIComponent(e)}`;return t}(e),a).then(h).then(m).then(g),getPluginUpdates:()=>d(`${n}jetpack/v4/updates/plugins`,a).then(h).then(m),getPlans:()=>d(`${n}jetpack/v4/plans`,a).then(h).then(m),fetchSettings:()=>d(`${n}jetpack/v4/settings`,a).then(h).then(m),updateSetting:e=>p(`${n}jetpack/v4/settings`,c,{body:JSON.stringify(e)}).then(h).then(m),fetchSiteData:()=>d(`${n}jetpack/v4/site`,a).then(h).then(m).then((e=>JSON.parse(e.data))),fetchSiteFeatures:()=>d(`${n}jetpack/v4/site/features`,a).then(h).then(m).then((e=>JSON.parse(e.data))),fetchSiteProducts:()=>d(`${n}jetpack/v4/site/products`,a).then(h).then(m),fetchSitePurchases:()=>d(`${n}jetpack/v4/site/purchases`,a).then(h).then(m).then((e=>JSON.parse(e.data))),fetchSiteBenefits:()=>d(`${n}jetpack/v4/site/benefits`,a).then(h).then(m).then((e=>JSON.parse(e.data))),fetchSetupQuestionnaire:()=>d(`${n}jetpack/v4/setup/questionnaire`,a).then(h).then(m),fetchRecommendationsData:()=>d(`${n}jetpack/v4/recommendations/data`,a).then(h).then(m),fetchRecommendationsProductSuggestions:()=>d(`${n}jetpack/v4/recommendations/product-suggestions`,a).then(h).then(m),fetchRecommendationsUpsell:()=>d(`${n}jetpack/v4/recommendations/upsell`,a).then(h).then(m),saveRecommendationsData:e=>p(`${n}jetpack/v4/recommendations/data`,c,{body:JSON.stringify({data:e})}).then(h),fetchProducts:()=>d(`${n}jetpack/v4/products`,a).then(h).then(m),fetchRewindStatus:()=>d(`${n}jetpack/v4/rewind`,a).then(h).then(m).then((e=>JSON.parse(e.data))),fetchScanStatus:()=>d(`${n}jetpack/v4/scan`,a).then(h).then(m).then((e=>JSON.parse(e.data))),dismissJetpackNotice:e=>p(`${n}jetpack/v4/notice/${e}`,c,{body:JSON.stringify({dismissed:!0})}).then(h).then(m),fetchPluginsData:()=>d(`${n}jetpack/v4/plugins`,a).then(h).then(m),fetchVerifySiteGoogleStatus:e=>d(null!==e?`${n}jetpack/v4/verify-site/google/${e}`:`${n}jetpack/v4/verify-site/google`,a).then(h).then(m),verifySiteGoogle:e=>p(`${n}jetpack/v4/verify-site/google`,c,{body:JSON.stringify({keyring_id:e})}).then(h).then(m),sendMobileLoginEmail:()=>p(`${n}jetpack/v4/mobile/send-login-email`,c).then(h).then(m),submitSurvey:e=>p(`${n}jetpack/v4/marketing/survey`,c,{body:JSON.stringify(e)}).then(h).then(m),saveSetupQuestionnaire:e=>p(`${n}jetpack/v4/setup/questionnaire`,c,{body:JSON.stringify(e)}).then(h).then(m),updateLicensingError:e=>p(`${n}jetpack/v4/licensing/error`,c,{body:JSON.stringify(e)}).then(h).then(m),updateLicenseKey:e=>p(`${n}jetpack/v4/licensing/set-license`,c,{body:JSON.stringify({license:e})}).then(h).then(m),getUserLicensesCounts:()=>d(`${n}jetpack/v4/licensing/user/counts`,a).then(h).then(m),updateLicensingActivationNoticeDismiss:e=>p(`${n}jetpack/v4/licensing/user/activation-notice-dismiss`,c,{body:JSON.stringify({last_detached_count:e})}).then(h).then(m),updateRecommendationsStep:e=>p(`${n}jetpack/v4/recommendations/step`,c,{body:JSON.stringify({step:e})}).then(h),confirmIDCSafeMode:()=>p(`${n}jetpack/v4/identity-crisis/confirm-safe-mode`,c).then(h),startIDCFresh:e=>p(`${n}jetpack/v4/identity-crisis/start-fresh`,c,{body:JSON.stringify({redirect_uri:e})}).then(h).then(m),migrateIDC:()=>p(`${n}jetpack/v4/identity-crisis/migrate`,c).then(h),attachLicenses:e=>p(`${n}jetpack/v4/licensing/attach-licenses`,c,{body:JSON.stringify({licenses:e})}).then(h).then(m),fetchSearchPlanInfo:()=>d(`${n}jetpack/v4/search/plan`,a).then(h).then(m),fetchSearchSettings:()=>d(`${n}jetpack/v4/search/settings`,a).then(h).then(m),updateSearchSettings:e=>p(`${n}jetpack/v4/search/settings`,c,{body:JSON.stringify(e)}).then(h).then(m)};function d(e,t){return fetch(l(e),t)}function p(e,t,n){return fetch(e,(0,r.assign)({},t,n)).catch(f)}function g(e){return e.general&&void 0===e.general.response||e.week&&void 0===e.week.response||e.month&&void 0===e.month.response?e:{}}(0,r.assign)(this,u)};function h(e){return e.status>=200&&e.status<300?e:404===e.status?new Promise((()=>{throw e.redirected?new u(e.redirected):new l})):e.json().catch((e=>g(e))).then((t=>{const n=new Error(`${t.message} (Status ${e.status})`);throw n.response=t,n.name="ApiError",n}))}function m(e){return e.json().catch((t=>g(t,e.redirected,e.url)))}function g(e,t,n){throw t?new c(n):new a}function f(){throw new d}t.ZP=p},1546:function(e,t,n){"use strict";var r=n(9183),s=n.n(r),i=n(7538),o=n.n(i),a=n(1268),c=n.n(a),l=n(9196),u=n.n(l),d=n(9105),p=n.n(d),h=n(5736);const __=h.__;class m extends u().Component{render(){const{logoColor:e,showText:t,className:n,...r}=this.props,i=t?"0 0 118 32":"0 0 32 32";return u().createElement("svg",s()({xmlns:"http://www.w3.org/2000/svg",x:"0px",y:"0px",viewBox:i,className:p()("jetpack-logo",n),"aria-labelledby":"jetpack-logo-title"},r),u().createElement("title",{id:"jetpack-logo-title"},__("Jetpack Logo","jetpack-idc")),u().createElement("path",{fill:e,d:"M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M15,19H7l8-16V19z M17,29V13h8L17,29z"}),t&&u().createElement(l.Fragment,null,u().createElement("path",{d:"M41.3,26.6c-0.5-0.7-0.9-1.4-1.3-2.1c2.3-1.4,3-2.5,3-4.6V8h-3V6h6v13.4C46,22.8,45,24.8,41.3,26.6z"}),u().createElement("path",{d:"M65,18.4c0,1.1,0.8,1.3,1.4,1.3c0.5,0,2-0.2,2.6-0.4v2.1c-0.9,0.3-2.5,0.5-3.7,0.5c-1.5,0-3.2-0.5-3.2-3.1V12H60v-2h2.1V7.1 H65V10h4v2h-4V18.4z"}),u().createElement("path",{d:"M71,10h3v1.3c1.1-0.8,1.9-1.3,3.3-1.3c2.5,0,4.5,1.8,4.5,5.6s-2.2,6.3-5.8,6.3c-0.9,0-1.3-0.1-2-0.3V28h-3V10z M76.5,12.3 c-0.8,0-1.6,0.4-2.5,1.2v5.9c0.6,0.1,0.9,0.2,1.8,0.2c2,0,3.2-1.3,3.2-3.9C79,13.4,78.1,12.3,76.5,12.3z"}),u().createElement("path",{d:"M93,22h-3v-1.5c-0.9,0.7-1.9,1.5-3.5,1.5c-1.5,0-3.1-1.1-3.1-3.2c0-2.9,2.5-3.4,4.2-3.7l2.4-0.3v-0.3c0-1.5-0.5-2.3-2-2.3 c-0.7,0-2.3,0.5-3.7,1.1L84,11c1.2-0.4,3-1,4.4-1c2.7,0,4.6,1.4,4.6,4.7L93,22z M90,16.4l-2.2,0.4c-0.7,0.1-1.4,0.5-1.4,1.6 c0,0.9,0.5,1.4,1.3,1.4s1.5-0.5,2.3-1V16.4z"}),u().createElement("path",{d:"M104.5,21.3c-1.1,0.4-2.2,0.6-3.5,0.6c-4.2,0-5.9-2.4-5.9-5.9c0-3.7,2.3-6,6.1-6c1.4,0,2.3,0.2,3.2,0.5V13 c-0.8-0.3-2-0.6-3.2-0.6c-1.7,0-3.2,0.9-3.2,3.6c0,2.9,1.5,3.8,3.3,3.8c0.9,0,1.9-0.2,3.2-0.7V21.3z"}),u().createElement("path",{d:"M110,15.2c0.2-0.3,0.2-0.8,3.8-5.2h3.7l-4.6,5.7l5,6.3h-3.7l-4.2-5.8V22h-3V6h3V15.2z"}),u().createElement("path",{d:"M58.5,21.3c-1.5,0.5-2.7,0.6-4.2,0.6c-3.6,0-5.8-1.8-5.8-6c0-3.1,1.9-5.9,5.5-5.9s4.9,2.5,4.9,4.9c0,0.8,0,1.5-0.1,2h-7.3 c0.1,2.5,1.5,2.8,3.6,2.8c1.1,0,2.2-0.3,3.4-0.7C58.5,19,58.5,21.3,58.5,21.3z M56,15c0-1.4-0.5-2.9-2-2.9c-1.4,0-2.3,1.3-2.4,2.9 C51.6,15,56,15,56,15z"})))}}o()(m,"propTypes",{className:c().string,width:c().number,height:c().number,showText:c().bool,logoColor:c().string}),o()(m,"defaultProps",{className:"",height:32,showText:!0,logoColor:"#069e08"}),t.Z=m},7262:function(e,t,n){"use strict";var r=n(9196),s=n.n(r),i=n(1268),o=n.n(i);n(6212);const a=e=>{const t=e.className+" jp-components-spinner",n={width:e.size,height:e.size,fontSize:e.size,borderTopColor:e.color},r={borderTopColor:e.color,borderRightColor:e.color};return s().createElement("div",{className:t},s().createElement("div",{className:"jp-components-spinner__outer",style:n},s().createElement("div",{className:"jp-components-spinner__inner",style:r})))};a.propTypes={color:o().string,className:o().string,size:o().number},a.defaultProps={color:"#FFFFFF",className:"",size:20},t.Z=a},1415:function(e,t,n){"use strict";function r(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const n={};let r;if("undefined"!=typeof window&&(r=window.Initial_State?.calypsoEnv),0===e.search("https://")){const t=new URL(e);e=`https://${t.host}${t.pathname}`,n.url=encodeURIComponent(e)}else n.source=encodeURIComponent(e);Object.keys(t).map((e=>{n[e]=encodeURIComponent(t[e])})),!Object.keys(n).includes("site")&&"undefined"!=typeof jetpack_redirects&&jetpack_redirects.hasOwnProperty("currentSiteRawUrl")&&(n.site=jetpack_redirects.currentSiteRawUrl),r&&(n.calypso_env=r);const s=Object.keys(n).map((e=>e+"="+n[e])).join("&");return"https://jetpack.com/redirect/?"+s}n.d(t,{Z:function(){return r}})},6251:function(e,t,n){let r={};try{r=n(8510)}catch{console.error("jetpackConfig is missing in your webpack config file. See @automattic/jetpack-config"),r={missingConfig:!0}}const s=e=>r.hasOwnProperty(e);e.exports={jetpackConfigHas:s,jetpackConfigGet:e=>{if(!s(e))throw'This app requires the "'+e+'" Jetpack Config to be defined in your webpack configuration file. See details in @automattic/jetpack-config package docs.';return r[e]}}},7226:function(e,t,n){"use strict";var r=n(9196),s=n.n(r),i=n(1268),o=n.n(i),a=n(5609),c=n(9818),l=n(9307),u=n(5736),d=n(1415),p=n(7262),h=n(7619),m=n(2690),g=n(2665),f=n(8086);const __=u.__,C=e=>{const{isStartingFresh:t,startFreshCallback:n,customContent:r,hasError:i}=e,o=(0,m.Z)(e.wpcomHomeUrl),g=(0,m.Z)(e.currentUrl),C=(0,c.useSelect)((e=>e(h.t).getIsActionInProgress()),[]),y=__("Create a fresh connection","jetpack-idc");return s().createElement("div",{className:"jp-idc__idc-screen__card-action-base"+(i?" jp-idc__idc-screen__card-action-error":"")},s().createElement("div",{className:"jp-idc__idc-screen__card-action-top"},s().createElement("h4",null,r.startFreshCardTitle||__("Treat each site as independent sites","jetpack-idc")),s().createElement("p",null,r.startFreshCardBodyText||(0,l.createInterpolateElement)((0,u.sprintf)(
+/* translators: %1$s: The current site domain name. %2$s: The original site domain name. */
+__("<hostname>%1$s</hostname> settings, stats, and subscribers will start fresh. <hostname>%2$s</hostname> will keep its data as is.","jetpack-idc"),g,o),{hostname:s().createElement("strong",null)}))),s().createElement("div",{className:"jp-idc__idc-screen__card-action-bottom"},s().createElement("div",{className:"jp-idc__idc-screen__card-action-sitename"},o),s().createElement(a.Dashicon,{icon:"minus",className:"jp-idc__idc-screen__card-action-separator"}),s().createElement("div",{className:"jp-idc__idc-screen__card-action-sitename"},g),s().createElement(a.Button,{className:"jp-idc__idc-screen__card-action-button",label:y,onClick:n,disabled:C},t?s().createElement(p.Z,null):y),i&&s().createElement(f.Z,null,(0,l.createInterpolateElement)(__("Could not create the connection. Retry or find out more <a>here</a>.","jetpack-idc"),{a:s().createElement("a",{href:(0,d.Z)("jetpack-support-safe-mode"),rel:"noopener noreferrer",target:"_blank"})}))))};C.propTypes={wpcomHomeUrl:o().string.isRequired,currentUrl:o().string.isRequired,isStartingFresh:o().bool.isRequired,startFreshCallback:o().func.isRequired,customContent:o().shape(g.Z),hasError:o().bool.isRequired},C.defaultProps={isStartingFresh:!1,startFreshCallback:()=>{},customContent:{},hasError:!1},t.Z=C},3023:function(e,t,n){"use strict";var r=n(9196),s=n.n(r),i=n(1268),o=n.n(i),a=n(5609),c=n(9818),l=n(9307),u=n(5736),d=n(1415),p=n(7262),h=n(7619),m=n(2690),g=n(2665),f=n(8086);const __=u.__,C=e=>{const t=(0,m.Z)(e.wpcomHomeUrl),n=(0,m.Z)(e.currentUrl),r=(0,c.useSelect)((e=>e(h.t).getIsActionInProgress()),[]),{isMigrating:i,migrateCallback:o,customContent:g,hasError:C}=e,y=__("Move your settings","jetpack-idc");return s().createElement("div",{className:"jp-idc__idc-screen__card-action-base"+(C?" jp-idc__idc-screen__card-action-error":"")},s().createElement("div",{className:"jp-idc__idc-screen__card-action-top"},s().createElement("h4",null,g.migrateCardTitle||__("Move Jetpack data","jetpack-idc")),s().createElement("p",null,g.migrateCardBodyText||(0,l.createInterpolateElement)((0,u.sprintf)(
+/* translators: %1$s: The current site domain name. %2$s: The original site domain name. */
+__("Move all your settings, stats and subscribers to your other URL, <hostname>%1$s</hostname>. <hostname>%2$s</hostname> will be disconnected from Jetpack.","jetpack-idc"),n,t),{hostname:s().createElement("strong",null)}))),s().createElement("div",{className:"jp-idc__idc-screen__card-action-bottom"},s().createElement("div",{className:"jp-idc__idc-screen__card-action-sitename"},t),s().createElement(a.Dashicon,{icon:"arrow-down-alt",className:"jp-idc__idc-screen__card-action-separator"}),s().createElement("div",{className:"jp-idc__idc-screen__card-action-sitename"},n),s().createElement(a.Button,{className:"jp-idc__idc-screen__card-action-button",label:y,onClick:o,disabled:r},i?s().createElement(p.Z,null):y),C&&s().createElement(f.Z,null,(0,l.createInterpolateElement)(__("Could not move your settings. Retry or find out more <a>here</a>.","jetpack-idc"),{a:s().createElement("a",{href:(0,d.Z)("jetpack-support-safe-mode"),rel:"noopener noreferrer",target:"_blank"})}))))};C.propTypes={wpcomHomeUrl:o().string.isRequired,currentUrl:o().string.isRequired,isMigrating:o().bool.isRequired,migrateCallback:o().func.isRequired,customContent:o().shape(g.Z),hasError:o().bool.isRequired},C.defaultProps={isMigrating:!1,migrateCallback:()=>{},customContent:{},hasError:!1},t.Z=C},7090:function(e,t,n){"use strict";var r=n(9196),s=n.n(r);t.Z=()=>s().createElement("svg",{className:"error-gridicon",xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",height:24},s().createElement("rect",{x:"0",fill:"none",width:"24",height:"24"}),s().createElement("g",null,s().createElement("path",{d:"M12 4c4.411 0 8 3.589 8 8s-3.589 8-8 8-8-3.589-8-8 3.589-8 8-8m0-2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm1 13h-2v2h2v-2zm-2-2h2l.5-6h-3l.5 6z"})))},8086:function(e,t,n){"use strict";var r=n(9196),s=n.n(r),i=n(7090);n(4943);t.Z=e=>{const{children:t}=e;return s().createElement("div",{className:"jp-idc__error-message"},s().createElement(i.Z,null),s().createElement("span",null,t))}},5643:function(e,t,n){"use strict";var r=n(9196),s=n.n(r),i=n(1268),o=n.n(i),a=n(816),c=n(9570),l=n(9818),u=n(7436),d=n(5853),p=n(463),h=n(8970),m=n(1517),g=n(2665),f=n(7619);const C=e=>{const{logo:t,customContent:n,wpcomHomeUrl:i,currentUrl:o,apiNonce:g,apiRoot:C,redirectUri:y,tracksUserData:k,tracksEventData:_,isAdmin:v}=e,[w,b]=(0,r.useState)(!1),E=(0,l.useSelect)((e=>e(f.t).getErrorType()),[]),{isMigrating:j,migrateCallback:S}=(0,p.Z)((0,r.useCallback)((()=>{b(!0)}),[b])),{isStartingFresh:F,startFreshCallback:I}=(0,m.Z)(y),{isFinishingMigration:R,finishMigrationCallback:P}=(0,h.Z)();return(0,r.useEffect)((()=>{c.ZP.setApiRoot(C),c.ZP.setApiNonce(g),k&&k.hasOwnProperty("userid")&&k.hasOwnProperty("username")&&a.Z.initialize(k.userid,k.username),_&&(_.hasOwnProperty("isAdmin")&&_.isAdmin?(0,d.Z)("notice_view"):(0,d.Z)("non_admin_notice_view",{page:!!_.hasOwnProperty("currentScreen")&&_.currentScreen}))}),[C,g,k,_]),s().createElement(u.Z,{logo:t,customContent:n,wpcomHomeUrl:i,currentUrl:o,redirectUri:y,isMigrating:j,migrateCallback:S,isMigrated:w,finishMigrationCallback:P,isFinishingMigration:R,isStartingFresh:F,startFreshCallback:I,isAdmin:v,hasStaySafeError:"safe-mode"===E,hasFreshError:"start-fresh"===E,hasMigrateError:"migrate"===E})};C.propTypes={logo:o().object,customContent:o().shape(g.Z),wpcomHomeUrl:o().string.isRequired,currentUrl:o().string.isRequired,redirectUri:o().string.isRequired,apiRoot:o().string.isRequired,apiNonce:o().string.isRequired,tracksUserData:o().object,tracksEventData:o().object,isAdmin:o().bool.isRequired},C.defaultProps={customContent:{}},t.Z=C},2027:function(e,t,n){"use strict";var r=n(9196),s=n.n(r),i=n(1268),o=n.n(i),a=n(9307),c=n(5736),l=n(1415),u=n(3023),d=n(7226),p=n(1693),h=n(2665);const __=c.__,m=e=>{const{wpcomHomeUrl:t,currentUrl:n,isMigrating:r,migrateCallback:i,isStartingFresh:o,startFreshCallback:c,customContent:h,hasMigrateError:m,hasFreshError:g,hasStaySafeError:f}=e;return s().createElement(s().Fragment,null,s().createElement("h2",null,h.mainTitle||__("Safe Mode has been activated","jetpack-idc")),s().createElement("p",null,h.mainBodyText||(0,a.createInterpolateElement)(__("Your site is in Safe Mode because you have 2 Jetpack-powered sites that appear to be duplicates. 2 sites that are telling Jetpack they’re the same site. <safeModeLink>Learn more about safe mode.</safeModeLink>","jetpack-idc"),{safeModeLink:s().createElement("a",{href:(0,l.Z)("jetpack-support-safe-mode"),rel:"noopener noreferrer",target:"_blank"})})),s().createElement("h3",null,__("Please select an option","jetpack-idc")),s().createElement("div",{className:"jp-idc__idc-screen__cards"+(m||g?" jp-idc__idc-screen__cards-error":"")},s().createElement(u.Z,{wpcomHomeUrl:t,currentUrl:n,isMigrating:r,migrateCallback:i,customContent:h,hasError:m}),s().createElement("div",{className:"jp-idc__idc-screen__cards-separator"},"or"),s().createElement(d.Z,{wpcomHomeUrl:t,currentUrl:n,isStartingFresh:o,startFreshCallback:c,customContent:h,hasError:g})),s().createElement(p.Z,{hasError:f}))};m.propTypes={wpcomHomeUrl:o().string.isRequired,currentUrl:o().string.isRequired,isMigrating:o().bool.isRequired,migrateCallback:o().func,isStartingFresh:o().bool.isRequired,startFreshCallback:o().func,customContent:o().shape(h.Z),hasMigrateError:o().bool.isRequired,hasFreshError:o().bool.isRequired,hasStaySafeError:o().bool.isRequired},m.defaultProps={isMigrating:!1,isStartingFresh:!1,customContent:{},hasMigrateError:!1,hasFreshError:!1,hasStaySafeError:!1},t.Z=m},4642:function(e,t,n){"use strict";var r=n(9196),s=n.n(r),i=n(1268),o=n.n(i),a=n(5609),c=n(9307),l=n(5736),u=n(7262),d=n(2690),p=n(2665);const __=l.__,h=e=>{const{finishCallback:t,isFinishing:n,customContent:r}=e,i=(0,d.Z)(e.wpcomHomeUrl),o=(0,d.Z)(e.currentUrl),p=__("Got it, thanks","jetpack-idc");return s().createElement(s().Fragment,null,s().createElement("h2",null,r.migratedTitle||__("Your Jetpack settings have migrated successfully","jetpack-idc")),s().createElement("p",null,r.migratedBodyText||(0,c.createInterpolateElement)((0,l.sprintf)(
+/* translators: %1$s: The current site domain name. */
+__("Safe Mode has been switched off for <hostname>%1$s</hostname> website and Jetpack is fully functional.","jetpack-idc"),o),{hostname:s().createElement("strong",null)})),s().createElement("div",{className:"jp-idc__idc-screen__card-migrated"},s().createElement("div",{className:"jp-idc__idc-screen__card-migrated-hostname"},i),s().createElement(a.Dashicon,{icon:"arrow-down-alt",className:"jp-idc__idc-screen__card-migrated-separator"}),s().createElement(a.Dashicon,{icon:"arrow-right-alt",className:"jp-idc__idc-screen__card-migrated-separator-wide"}),s().createElement("div",{className:"jp-idc__idc-screen__card-migrated-hostname"},o)),s().createElement(a.Button,{className:"jp-idc__idc-screen__card-action-button jp-idc__idc-screen__card-action-button-migrated",onClick:t,label:p},n?s().createElement(u.Z,null):p))};h.propTypes={wpcomHomeUrl:o().string.isRequired,currentUrl:o().string.isRequired,finishCallback:o().func,isFinishing:o().bool.isRequired,customContent:o().shape(p.Z)},h.defaultProps={finishCallback:()=>{},isFinishing:!1,customContent:{}},t.Z=h},4523:function(e,t,n){"use strict";var r=n(9196),s=n.n(r),i=n(1268),o=n.n(i),a=n(9307),c=n(5736),l=n(1415),u=n(2665);const __=c.__,d=e=>{const{customContent:t}=e;return s().createElement(s().Fragment,null,s().createElement("h2",null,t.nonAdminTitle||__("Safe Mode has been activated","jetpack-idc")),s().createElement("p",null,t.nonAdminBodyText||(0,a.createInterpolateElement)(__("This site is in Safe Mode because there are 2 Jetpack-powered sites that appear to be duplicates. 2 sites that are telling Jetpack they’re the same site. <safeModeLink>Learn more about safe mode.</safeModeLink>","jetpack-idc"),{safeModeLink:s().createElement("a",{href:(0,l.Z)("jetpack-support-safe-mode"),rel:"noopener noreferrer",target:"_blank"})})),t.nonAdminBodyText?"":s().createElement("p",null,__("An administrator of this site can take Jetpack out of Safe Mode.","jetpack-idc")))};d.propTypes={customContent:o().shape(u.Z)},d.defaultProps={customContent:{}},t.Z=d},7436:function(e,t,n){"use strict";var r=n(9196),s=n.n(r),i=n(1268),o=n.n(i),a=n(5736),c=n(1546),l=n(2027),u=n(4523),d=n(4642),p=n(2665);n(8911);const __=a.__,h=e=>{const{logo:t,customContent:n,wpcomHomeUrl:r,currentUrl:i,redirectUri:o,isMigrating:a,migrateCallback:c,isMigrated:p,finishMigrationCallback:h,isFinishingMigration:m,isStartingFresh:g,startFreshCallback:f,isAdmin:C,hasMigrateError:y,hasFreshError:k,hasStaySafeError:_}=e,v=C?"":s().createElement(u.Z,{customContent:n});let w="";return C&&(w=p?s().createElement(d.Z,{wpcomHomeUrl:r,currentUrl:i,finishCallback:h,isFinishing:m,customContent:n}):s().createElement(l.Z,{wpcomHomeUrl:r,currentUrl:i,redirectUri:o,customContent:n,isMigrating:a,migrateCallback:c,isStartingFresh:g,startFreshCallback:f,hasMigrateError:y,hasFreshError:k,hasStaySafeError:_})),s().createElement("div",{className:"jp-idc__idc-screen"+(p?" jp-idc__idc-screen__success":"")},s().createElement("div",{className:"jp-idc__idc-screen__header"},s().createElement("div",{className:"jp-idc__idc-screen__logo"},((e,t)=>"string"==typeof e||e instanceof String?s().createElement("img",{src:e,alt:t,className:"jp-idc__idc-screen__logo-image"}):e)(t,n.logoAlt||"")),s().createElement("div",{className:"jp-idc__idc-screen__logo-label"},n.headerText||__("Safe Mode","jetpack-idc"))),v,w)};h.propTypes={logo:o().object.isRequired,customContent:o().shape(p.Z),wpcomHomeUrl:o().string.isRequired,currentUrl:o().string.isRequired,redirectUri:o().string.isRequired,isMigrating:o().bool.isRequired,migrateCallback:o().func,isMigrated:o().bool.isRequired,finishMigrationCallback:o().func,isFinishingMigration:o().bool.isRequired,isStartingFresh:o().bool.isRequired,startFreshCallback:o().func,isAdmin:o().bool.isRequired,hasMigrateError:o().bool.isRequired,hasFreshError:o().bool.isRequired,hasStaySafeError:o().bool.isRequired},h.defaultProps={logo:s().createElement(c.Z,{height:24}),isMigrated:!1,isFinishingMigration:!1,isMigrating:!1,isStartingFresh:!1,customContent:{},hasMigrateError:!1,hasFreshError:!1,hasStaySafeError:!1},t.Z=h},1693:function(e,t,n){"use strict";var r=n(9196),s=n.n(r),i=n(1268),o=n.n(i),a=n(4333),c=n(9818),l=n(9307),u=n(5609),d=n(5736),p=n(6483),h=n(9570),m=n(7262),g=n(1415),f=n(7619),C=n(5853),y=n(8086);n(2780);const __=d.__,k=e=>{const{isActionInProgress:t,setIsActionInProgress:n,setErrorType:i,clearErrorType:o,hasError:a}=e,[c,d]=(0,r.useState)(!1),f=(0,r.useCallback)((()=>{t||(d(!0),n(!0),o(),(0,C.Z)("confirm_safe_mode"),h.ZP.confirmIDCSafeMode().then((()=>{window.location.href=(0,p.removeQueryArgs)(window.location.href,"jetpack_idc_clear_confirmation","_wpnonce")})).catch((e=>{throw n(!1),d(!1),i("safe-mode"),e})))}),[t,n,i,o]);return s().createElement("div",{className:"jp-idc__safe-mode"},c?s().createElement("div",{className:"jp-idc__safe-mode__staying-safe"},s().createElement(m.Z,{color:"black"}),s().createElement("span",null,__("Finishing setting up Safe mode…","jetpack-idc"))):(k=f,_=t,(0,l.createInterpolateElement)(__("Or decide later and stay in <button>Safe mode</button>","jetpack-idc"),{button:s().createElement(u.Button,{label:__("Safe mode","jetpack-idc"),variant:"link",onClick:k,disabled:_})})),a&&s().createElement(y.Z,null,(0,l.createInterpolateElement)(__("Could not stay in safe mode. Retry or find out more <a>here</a>.","jetpack-idc"),{a:s().createElement("a",{href:(0,g.Z)("jetpack-support-safe-mode"),rel:"noopener noreferrer",target:"_blank"})})));var k,_};k.propTypes={isActionInProgress:o().bool,setIsActionInProgress:o().func.isRequired,setErrorType:o().func.isRequired,clearErrorType:o().func.isRequired,hasError:o().bool.isRequired},k.defaultProps={hasError:!1},t.Z=(0,a.compose)([(0,c.withSelect)((e=>({isActionInProgress:e(f.t).getIsActionInProgress()}))),(0,c.withDispatch)((e=>({setIsActionInProgress:e(f.t).setIsActionInProgress,setErrorType:e(f.t).setErrorType,clearErrorType:e(f.t).clearErrorType})))])(k)},8970:function(e,t,n){"use strict";var r=n(9196);t.Z=()=>{const[e,t]=(0,r.useState)(!1),n=(0,r.useCallback)((()=>{e||(t(!0),window.location.reload())}),[e,t]);return{isFinishingMigration:e,finishMigrationCallback:n}}},463:function(e,t,n){"use strict";var r=n(9196),s=n(9818),i=n(9570),o=n(5853),a=n(7619);t.Z=e=>{const[t,n]=(0,r.useState)(!1),c=(0,s.useSelect)((e=>e(a.t).getIsActionInProgress()),[]),{setIsActionInProgress:l,setErrorType:u,clearErrorType:d}=(0,s.useDispatch)(a.t);return{isMigrating:t,migrateCallback:(0,r.useCallback)((()=>{c||((0,o.Z)("migrate"),l(!0),n(!0),d(),i.ZP.migrateIDC().then((()=>{n(!1),e&&"[object Function]"==={}.toString.call(e)&&e()})).catch((e=>{throw l(!1),n(!1),u("migrate"),e})))}),[n,e,c,l,u,d])}}},1517:function(e,t,n){"use strict";var r=n(9196),s=n(9818),i=n(9570),o=n(5853),a=n(7619);t.Z=e=>{const[t,n]=(0,r.useState)(!1),c=(0,s.useSelect)((e=>e(a.t).getIsActionInProgress()),[]),{setIsActionInProgress:l,setErrorType:u,clearErrorType:d}=(0,s.useDispatch)(a.t);return{isStartingFresh:t,startFreshCallback:(0,r.useCallback)((()=>{c||((0,o.Z)("start_fresh"),l(!0),n(!0),d(),i.ZP.startIDCFresh(e).then((e=>{window.location.href=e+"&from=idc-notice"})).catch((e=>{throw l(!1),n(!1),u("start-fresh"),e})))}),[n,c,l,e,u,d])}}},8401:function(e,t,n){"use strict";n.d(t,{hG:function(){return r},vC:function(){return s},jk:function(){return i},ZP:function(){return o}});const r="SET_IS_ACTION_IN_PROGRESS",s="SET_ERROR_TYPE",i="CLEAR_ERROR_TYPE",o={setIsActionInProgress:e=>({type:r,isInProgress:e}),setErrorType:e=>({type:s,errorType:e}),clearErrorType:()=>({type:i})}},2944:function(e,t,n){"use strict";var r=n(9818),s=n(8401);const i=(0,r.combineReducers)({isActionInProgress:function(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=arguments.length>1?arguments[1]:void 0;return t.type===s.hG?t.isInProgress:e},errorType:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case s.vC:return t.errorType;case s.jk:return null}return e}});t.Z=i},3642:function(e,t){"use strict";t.Z={getIsActionInProgress:e=>e.isActionInProgress||!1,getErrorType:e=>e.errorType||null}},9520:function(e,t,n){"use strict";var r=n(7538),s=n.n(r),i=n(9818);class o{static mayBeInit(e,t){null===o.store&&(o.store=(0,i.createReduxStore)(e,t),(0,i.register)(o.store))}}s()(o,"store",null),t.Z=o},7619:function(e,t,n){"use strict";n.d(t,{t:function(){return a}});var r=n(2944),s=n(8401),i=n(3642),o=n(9520);const a="jetpack-idc";o.Z.mayBeInit(a,{reducer:r.Z,actions:s.ZP,selectors:i.Z})},2665:function(e,t,n){"use strict";var r=n(1268),s=n.n(r);t.Z={headerText:s().string,logoAlt:s().string,mainTitle:s().string,mainBodyText:s().string,migratedTitle:s().string,migratedBodyText:s().string,migrateCardTitle:s().string,migrateCardBodyText:s().string,startFreshCardTitle:s().string,startFreshCardBodyText:s().string,nonAdminTitle:s().string,nonAdminBodyText:s().string}},2690:function(e,t){"use strict";t.Z=e=>/^https?:\/\//.test(e)?new URL(e).hostname:e.replace(/\/$/,"")},5853:function(e,t,n){"use strict";n.d(t,{Z:function(){return s}});var r=n(816);function s(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};void 0!==t&&"object"==typeof t||(t={}),e&&e.length&&void 0!==r.Z&&r.Z.tracks&&r.Z.mc&&(e=0!==(e=e.replace(/-/g,"_")).indexOf("jetpack_idc_")?"jetpack_idc_"+e:e,r.Z.tracks.recordEvent(e,t),e=(e=e.replace("jetpack_idc_","")).replace(/_/g,"-"),r.Z.mc.bumpStat("jetpack-idc",e))}},8510:function(e){"use strict";if(void 0==={consumer_slug:"identity_crisis"}){var t=new Error('Cannot find module \'{"consumer_slug":"identity_crisis"}\'');throw t.code="MODULE_NOT_FOUND",t}e.exports={consumer_slug:"identity_crisis"}},9196:function(e){"use strict";e.exports=window.React},1850:function(e){"use strict";e.exports=window.ReactDOM},2819:function(e){"use strict";e.exports=window.lodash},5609:function(e){"use strict";e.exports=window.wp.components},4333:function(e){"use strict";e.exports=window.wp.compose},9818:function(e){"use strict";e.exports=window.wp.data},9307:function(e){"use strict";e.exports=window.wp.element},5736:function(e){"use strict";e.exports=window.wp.i18n},6483:function(e){"use strict";e.exports=window.wp.url}},t={};function n(r){var s=t[r];if(void 0!==s)return s.exports;var i=t[r]={exports:{}};return e[r](i,i.exports,n),i.exports}n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,{a:t}),t},n.d=function(e,t){for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},function(){"use strict";var e=n(1850),t=n.n(e),r=n(9196),s=n.n(r),i=n(5643);!function(){const e=document.getElementById("jp-identity-crisis-container");if(null===e||!window.hasOwnProperty("JP_IDENTITY_CRISIS__INITIAL_STATE"))return;const{WP_API_root:n,WP_API_nonce:r,wpcomHomeUrl:o,currentUrl:a,redirectUri:c,tracksUserData:l,tracksEventData:u,isSafeModeConfirmed:d,consumerData:p,isAdmin:h}=window.JP_IDENTITY_CRISIS__INITIAL_STATE;d||t().render(s().createElement(i.Z,{wpcomHomeUrl:o,currentUrl:a,apiRoot:n,apiNonce:r,redirectUri:c,tracksUserData:l,tracksEventData:u,customContent:p.hasOwnProperty("customContent")?p.customContent:{},isAdmin:h,logo:p.hasOwnProperty("logo")?p.logo:void 0}),e)}()}()}(); \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.js.LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.js.LICENSE.txt
new file mode 100644
index 00000000..0c20a875
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.js.LICENSE.txt
@@ -0,0 +1,5 @@
+/*!
+ Copyright (c) 2018 Jed Watson.
+ Licensed under the MIT License (MIT), see
+ http://jedwatson.github.io/classnames
+*/
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.rtl.css b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.rtl.css
new file mode 100644
index 00000000..5c2ffd77
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/build/index.rtl.css
@@ -0,0 +1 @@
+#wp-admin-bar-jetpack-idc.hide{display:none}#wp-admin-bar-jetpack-idc .jp-idc-admin-bar{background:#fff;border-radius:2px;color:#1d2327;font-size:12px;padding:4px 8px}#wpadminbar #wp-admin-bar-jetpack-idc .dashicons{color:#1d2327;font-family:dashicons}#wpadminbar #wp-admin-bar-jetpack-idc .dashicons:before{font-size:16px}#wpadminbar #wp-admin-bar-jetpack-idc:hover .ab-item{background:inherit}#wpadminbar #wp-admin-bar-jetpack-idc:hover .jp-idc-admin-bar{background:#f0f0f1}#jp-identity-crisis-container .jp-idc__idc-screen{margin-bottom:40px;margin-top:40px}.jp-idc__idc-screen{background:#fff;border-radius:4px;border-right:4px solid #e68b28;box-shadow:0 0 40px rgba(0,0,0,.04);box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;margin:0 auto;max-width:1128px;padding:10px}.jp-idc__idc-screen.jp-idc__idc-screen__success{border-color:#069e08}@media(min-width:600px){.jp-idc__idc-screen{padding:48px}}.jp-idc__idc-screen .jp-idc__idc-screen__header{align-items:center;display:flex}.jp-idc__idc-screen .jp-idc__idc-screen__header .jp-idc__idc-screen__logo-image{max-height:100px;max-width:100px}.jp-idc__idc-screen .jp-idc__idc-screen__header .jp-idc__idc-screen__logo-label{font-size:14px;line-height:22px;margin:-7px 8px 0 0}.jp-idc__idc-screen h2{font-size:24px;font-weight:600;line-height:28px;margin:32px 0 0}.jp-idc__idc-screen h3{font-size:20px;font-weight:600;line-height:28px;margin:24px 0 0}.jp-idc__idc-screen p{color:#2c3338;font-size:16px;line-height:24px;margin:16px 0 0;max-width:710px}.jp-idc__idc-screen a{color:#2c3338}.jp-idc__idc-screen .jp-idc__idc-screen__cards{align-items:center;display:flex;flex-direction:column;flex-wrap:wrap}@media only screen and (min-width:1403px){.jp-idc__idc-screen .jp-idc__idc-screen__cards{align-items:normal;flex-direction:row}}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__cards-separator{align-self:center;color:#23282d;font-size:20px;font-weight:600;line-height:28px;margin:0 24px}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base{border:1px solid #c3c4c7;border-radius:4px;box-sizing:border-box;display:flex;flex-direction:column;justify-content:space-between;margin:24px 0;max-width:100%;padding:10px;width:480px}@media(min-width:600px){.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base{padding:24px}}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base h4{font-size:20px;font-weight:400;line-height:28px;margin:0 0 8px}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base p{margin:0 0 24px}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base .jp-idc__idc-screen__card-action-sitename{background:#f9f9f6;border-radius:33px;box-sizing:border-box;color:#2c3338;font-size:16px;font-weight:700;line-height:24px;overflow-wrap:anywhere;padding:16px;text-align:center;width:100%}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base .jp-idc__idc-screen__card-action-separator{display:block;margin:12px auto}.jp-idc__idc-screen .jp-idc__idc-screen__cards .jp-idc__idc-screen__card-action-base .jp-idc__idc-screen__card-action-button{padding:8px 24px;width:100%}.jp-idc__idc-screen .jp-idc__idc-screen__card-action-button{background:#000;border-radius:4px;color:#fff;font-size:16px;font-weight:600;height:auto;justify-content:center;line-height:24px;margin-top:24px;min-height:40px;padding:8px}.jp-idc__idc-screen .jp-idc__idc-screen__card-action-button-migrated{margin-top:64px;width:141px}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated{align-items:center;display:flex;flex-direction:column;flex-wrap:wrap;margin-top:24px;width:100%}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-hostname{border:1px solid #c3c4c7;border-radius:4px;color:#2c3338;flex-grow:1;font-size:16px;font-weight:700;line-height:24px;padding:24px;width:100%}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-separator-wide{display:none}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-separator{display:block}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-separator,.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-separator-wide{margin:28px}@media only screen and (min-width:1400px){.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated{flex-direction:row;width:auto}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-separator{display:none}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-separator-wide{display:block}.jp-idc__idc-screen .jp-idc__idc-screen__card-migrated .jp-idc__idc-screen__card-migrated-hostname{width:auto}}@keyframes rotate-spinner{to{transform:rotate(-1turn)}}.jp-components-spinner{align-items:center;display:flex}.jp-components-spinner__inner,.jp-components-spinner__outer{animation:3s linear infinite;animation-name:rotate-spinner;border:.1em solid transparent;border-radius:50%;box-sizing:border-box;margin:auto}.jp-components-spinner__outer{border-top-color:#fff}.jp-components-spinner__inner{border-left-color:#fff;border-top-color:#fff;height:100%;opacity:.4;width:100%}:root{--font-title-large:36px;--font-title-small:24px;--font-body:16px;--font-label:12px;--jp-black:#000;--jp-black-80:#2c3338;--jp-white:#fff;--jp-white-off:#f9f9f6;--jp-gray:#dcdcde;--jp-gray-0:#f6f7f7;--jp-gray-20:#a7aaad;--jp-gray-40:#787c82;--jp-gray-50:#646970;--jp-gray-60:#50575e;--jp-gray-80:#8a2424;--jp-gray-off:#e2e2df;--jp-red-0:#f7ebec;--jp-red-50:#d63638;--jp-red-60:#b32d2e;--jp-red-80:#8a2424;--jp-red:#d63639;--jp-pink:#c9356e;--jp-green-0:#f0f2eb;--jp-green-5:#d0e6b8;--jp-green-10:#9dd977;--jp-green-20:#64ca43;--jp-green-30:#2fb41f;--jp-green-40:#069e08;--jp-green-50:#008710;--jp-green-60:#007117;--jp-green-70:#005b18;--jp-green-80:#004515;--jp-green-90:#003010;--jp-green-100:#001c09;--jp-green:#069e08;--jp-green-primary:var( --jp-green-40 );--jp-green-secondary:var( --jp-green-30 );--jp-border-radius:4px;--jp-menu-border-height:1px;--jp-underline-thickness:2px}*{box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;margin:0;min-height:100%;padding:0}.jp-wrap{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.jp-row{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.jp-row{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.jp-row{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.sm-col-span-1{grid-column-end:span 1}.sm-col-span-2{grid-column-end:span 2}.sm-col-span-3{grid-column-end:span 3}.sm-col-span-4{grid-column-end:span 4}@media(min-width:600px){.md-col-span-1{grid-column-end:span 1}.md-col-span-2{grid-column-end:span 2}.md-col-span-3{grid-column-end:span 3}.md-col-span-4{grid-column-end:span 4}.md-col-span-5{grid-column-end:span 5}.md-col-span-6{grid-column-end:span 6}.md-col-span-7{grid-column-end:span 7}.md-col-span-8{grid-column-end:span 8}}@media(min-width:960px){.lg-col-span-1{grid-column-end:span 1}.lg-col-span-2{grid-column-end:span 2}.lg-col-span-3{grid-column-end:span 3}.lg-col-span-4{grid-column-end:span 4}.lg-col-span-5{grid-column-end:span 5}.lg-col-span-6{grid-column-end:span 6}.lg-col-span-7{grid-column-end:span 7}.lg-col-span-8{grid-column-end:span 8}.lg-col-span-9{grid-column-end:span 9}.lg-col-span-10{grid-column-end:span 10}.lg-col-span-11{grid-column-end:span 11}.lg-col-span-12{grid-column-end:span 12}}@media(max-width:960px){.md-col-span-0{display:none}}@media(max-width:600px){.sm-col-span-0{display:none}}.jp-cut{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);margin:32px 0;padding:16px 24px 16px 64px;position:relative;text-decoration:none}.jp-cut,.jp-cut span{display:block}.jp-cut span:last-of-type{font-weight:600}.jp-cut:focus span:last-of-type,.jp-cut:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.jp-cut:focus:after,.jp-cut:hover:after{transform:translateY(-50%) translateX(-8px)}.jp-cut:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;left:24px;position:absolute;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.jp-idc__idc-screen .jp-idc__error-message{align-items:center;color:var(--jp-red);display:flex;flex-direction:row;justify-content:center;margin:15px 0}.jp-idc__idc-screen .jp-idc__error-message .error-gridicon{fill:var(--jp-red);margin-left:8px}.jp-idc__idc-screen .jp-idc__error-message a,.jp-idc__idc-screen .jp-idc__error-message span{color:var(--jp-red);font-size:var(--font-body)}.jp-idc__idc-screen .jp-idc__safe-mode .jp-idc__error-message{margin-top:5px}.jp-idc__idc-screen .jp-idc__idc-screen__cards.jp-idc__idc-screen__cards-error .jp-idc__idc-screen__card-action-base{padding-bottom:75px}.jp-idc__idc-screen .jp-idc__idc-screen__cards.jp-idc__idc-screen__cards-error .jp-idc__idc-screen__card-action-base.jp-idc__idc-screen__card-action-error{padding-bottom:5px}.jp-idc__idc-screen .jp-idc__idc-screen__cards.jp-idc__idc-screen__cards-error .jp-idc__idc-screen__card-action-base .jp-idc__error-message{height:40px}.jp-idc__idc-screen .jp-idc__safe-mode{text-align:center}.jp-idc__idc-screen .jp-idc__safe-mode .jp-idc__safe-mode__staying-safe{display:flex;justify-content:center;padding:6px}.jp-idc__idc-screen .jp-idc__safe-mode .jp-idc__safe-mode__staying-safe .jp-components-spinner{margin:0 10px}.jp-idc__idc-screen .jp-idc__safe-mode,.jp-idc__idc-screen .jp-idc__safe-mode button{color:#2c3338;font-size:16px;line-height:24px}.jp-idc__idc-screen .jp-idc__safe-mode button{padding:0;text-decoration:underline} \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/_inc/admin-bar.scss b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/_inc/admin-bar.scss
new file mode 100644
index 00000000..cd2008cf
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/_inc/admin-bar.scss
@@ -0,0 +1,28 @@
+#wp-admin-bar-jetpack-idc.hide {
+ display: none;
+}
+
+#wp-admin-bar-jetpack-idc .jp-idc-admin-bar {
+ background: #fff;
+ border-radius: 2px;
+ color: #1d2327;
+ padding: 4px 8px;
+ font-size: 12px;
+}
+
+#wpadminbar #wp-admin-bar-jetpack-idc .dashicons {
+ color: #1d2327;
+ font-family: 'dashicons';
+}
+
+#wpadminbar #wp-admin-bar-jetpack-idc .dashicons:before {
+ font-size: 16px;
+}
+
+#wpadminbar #wp-admin-bar-jetpack-idc:hover .ab-item {
+ background: inherit;
+}
+
+#wpadminbar #wp-admin-bar-jetpack-idc:hover .jp-idc-admin-bar {
+ background: #f0f0f1;
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/_inc/admin.jsx b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/_inc/admin.jsx
new file mode 100644
index 00000000..296b1898
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/_inc/admin.jsx
@@ -0,0 +1,58 @@
+/**
+ * External dependencies
+ */
+import ReactDOM from 'react-dom';
+import React from 'react';
+import { IDCScreen } from '@automattic/jetpack-idc';
+
+/**
+ * Internal dependencies
+ */
+import './admin-bar.scss';
+import './style.scss';
+
+/**
+ * The initial renderer function.
+ */
+function render() {
+ const container = document.getElementById( 'jp-identity-crisis-container' );
+
+ if ( null === container || ! window.hasOwnProperty( 'JP_IDENTITY_CRISIS__INITIAL_STATE' ) ) {
+ return;
+ }
+
+ const {
+ WP_API_root,
+ WP_API_nonce,
+ wpcomHomeUrl,
+ currentUrl,
+ redirectUri,
+ tracksUserData,
+ tracksEventData,
+ isSafeModeConfirmed,
+ consumerData,
+ isAdmin,
+ } = window.JP_IDENTITY_CRISIS__INITIAL_STATE;
+
+ if ( ! isSafeModeConfirmed ) {
+ ReactDOM.render(
+ <IDCScreen
+ wpcomHomeUrl={ wpcomHomeUrl }
+ currentUrl={ currentUrl }
+ apiRoot={ WP_API_root }
+ apiNonce={ WP_API_nonce }
+ redirectUri={ redirectUri }
+ tracksUserData={ tracksUserData }
+ tracksEventData={ tracksEventData }
+ customContent={
+ consumerData.hasOwnProperty( 'customContent' ) ? consumerData.customContent : {}
+ }
+ isAdmin={ isAdmin }
+ logo={ consumerData.hasOwnProperty( 'logo' ) ? consumerData.logo : undefined }
+ />,
+ container
+ );
+ }
+}
+
+render();
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/_inc/style.scss b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/_inc/style.scss
new file mode 100644
index 00000000..0b9ccb86
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/_inc/style.scss
@@ -0,0 +1,4 @@
+#jp-identity-crisis-container .jp-idc__idc-screen {
+ margin-top: 40px;
+ margin-bottom: 40px;
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/class-identity-crisis.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/class-identity-crisis.php
new file mode 100644
index 00000000..cae3ed16
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/class-identity-crisis.php
@@ -0,0 +1,1219 @@
+<?php
+/**
+ * Identity_Crisis package.
+ *
+ * @package automattic/jetpack-identity-crisis
+ */
+
+namespace Automattic\Jetpack;
+
+use Automattic\Jetpack\Assets\Logo as Jetpack_Logo;
+use Automattic\Jetpack\Connection\Manager as Connection_Manager;
+use Automattic\Jetpack\Connection\Urls;
+use Automattic\Jetpack\Constants as Constants;
+use Automattic\Jetpack\IdentityCrisis\UI;
+use Automattic\Jetpack\Status as Status;
+use Automattic\Jetpack\Tracking as Tracking;
+use Jetpack_Options;
+use WP_Error;
+
+/**
+ * This class will handle everything involved with fixing an Identity Crisis.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+class Identity_Crisis {
+
+ /**
+ * Package Version
+ */
+ const PACKAGE_VERSION = '0.6.1';
+
+ /**
+ * Instance of the object.
+ *
+ * @var Identity_Crisis
+ **/
+ private static $instance = null;
+
+ /**
+ * The wpcom value of the home URL.
+ *
+ * @var string
+ */
+ public static $wpcom_home_url;
+
+ /**
+ * Has safe mode been confirmed?
+ * Beware, it never contains `true` for non-admins, so doesn't always reflect the actual value.
+ *
+ * @var bool
+ */
+ public static $is_safe_mode_confirmed;
+
+ /**
+ * The current screen, which is set if the current user is a non-admin and this is an admin page.
+ *
+ * @var WP_Screen
+ */
+ public static $current_screen;
+
+ /**
+ * Initializer.
+ *
+ * @return object
+ */
+ public static function init() {
+ if ( is_null( self::$instance ) ) {
+ self::$instance = new Identity_Crisis();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Class constructor.
+ *
+ * @return void
+ */
+ private function __construct() {
+ add_action( 'jetpack_sync_processed_actions', array( $this, 'maybe_clear_migrate_option' ) );
+ add_action( 'rest_api_init', array( 'Automattic\\Jetpack\\IdentityCrisis\\REST_Endpoints', 'initialize_rest_api' ) );
+ add_action( 'jetpack_idc_disconnect', array( __CLASS__, 'do_jetpack_idc_disconnect' ) );
+ add_action( 'jetpack_received_remote_request_response', array( $this, 'check_http_response_for_idc_detected' ) );
+
+ add_filter( 'jetpack_connection_disconnect_site_wpcom', array( __CLASS__, 'jetpack_connection_disconnect_site_wpcom_filter' ) );
+
+ add_filter( 'jetpack_remote_request_url', array( $this, 'add_idc_query_args_to_url' ) );
+
+ $urls_in_crisis = self::check_identity_crisis();
+ if ( false === $urls_in_crisis ) {
+ return;
+ }
+
+ self::$wpcom_home_url = $urls_in_crisis['wpcom_home'];
+ add_action( 'init', array( $this, 'wordpress_init' ) );
+ }
+
+ /**
+ * Disconnect current connection and clear IDC options.
+ */
+ public static function do_jetpack_idc_disconnect() {
+ $connection = new Connection_Manager();
+
+ // If the site is in an IDC because sync is not allowed,
+ // let's make sure to not disconnect the production site.
+ if ( ! self::validate_sync_error_idc_option() ) {
+ $connection->disconnect_site( true );
+ } else {
+ $connection->disconnect_site( false );
+ }
+
+ // Clear IDC options.
+ self::clear_all_idc_options();
+ }
+
+ /**
+ * Filter to prevent site from disconnecting from WPCOM if it's in an IDC.
+ *
+ * @see jetpack_connection_disconnect_site_wpcom filter.
+ *
+ * @return bool False if the site is in IDC, true otherwise.
+ */
+ public static function jetpack_connection_disconnect_site_wpcom_filter() {
+ return ! self::validate_sync_error_idc_option();
+ }
+
+ /**
+ * Gets the link to the support document used to explain Safe Mode to users.
+ *
+ * @return string
+ */
+ public static function get_safe_mod_doc_url() {
+ return Redirect::get_url( 'jetpack-support-safe-mode' );
+ }
+
+ /**
+ * This method loops through the array of processed items from sync and checks if one of the items was the
+ * home_url or site_url callable. If so, then we delete the jetpack_migrate_for_idc option.
+ *
+ * @param array $processed_items Array of processed items that were synced to WordPress.com.
+ */
+ public function maybe_clear_migrate_option( $processed_items ) {
+ foreach ( (array) $processed_items as $item ) {
+
+ // First, is this item a jetpack_sync_callable action? If so, then proceed.
+ $callable_args = ( is_array( $item ) && isset( $item[0], $item[1] ) && 'jetpack_sync_callable' === $item[0] )
+ ? $item[1]
+ : null;
+
+ // Second, if $callable_args is set, check if the callable was home_url or site_url. If so,
+ // clear the migrate option.
+ if (
+ isset( $callable_args, $callable_args[0] )
+ && ( 'home_url' === $callable_args[0] || 'site_url' === $callable_args[1] )
+ ) {
+ Jetpack_Options::delete_option( 'migrate_for_idc' );
+ break;
+ }
+ }
+ }
+
+ /**
+ * WordPress init.
+ *
+ * @return void
+ */
+ public function wordpress_init() {
+ if ( current_user_can( 'jetpack_disconnect' ) ) {
+ if (
+ isset( $_GET['jetpack_idc_clear_confirmation'], $_GET['_wpnonce'] ) &&
+ wp_verify_nonce( $_GET['_wpnonce'], 'jetpack_idc_clear_confirmation' )
+ ) {
+ Jetpack_Options::delete_option( 'safe_mode_confirmed' );
+ self::$is_safe_mode_confirmed = false;
+ } else {
+ self::$is_safe_mode_confirmed = (bool) Jetpack_Options::get_option( 'safe_mode_confirmed' );
+ }
+ }
+
+ // 121 Priority so that it's the most inner Jetpack item in the admin bar.
+ add_action( 'admin_bar_menu', array( $this, 'display_admin_bar_button' ), 121 );
+
+ UI::init();
+ }
+
+ /**
+ * Add the idc query arguments to the url.
+ *
+ * @param string $url The remote request url.
+ */
+ public function add_idc_query_args_to_url( $url ) {
+ $status = new Status();
+ if ( ! is_string( $url )
+ || $status->is_offline_mode()
+ || $status->is_staging_site()
+ || self::validate_sync_error_idc_option() ) {
+ return $url;
+ }
+
+ $query_args = array(
+ 'home' => Urls::home_url(),
+ 'siteurl' => Urls::site_url(),
+ );
+
+ if ( self::should_handle_idc() ) {
+ $query_args['idc'] = true;
+ }
+
+ if ( \Jetpack_Options::get_option( 'migrate_for_idc', false ) ) {
+ $query_args['migrate_for_idc'] = true;
+ }
+
+ return add_query_arg( $query_args, $url );
+ }
+
+ /**
+ * Non-admins current screen check.
+ *
+ * @param object $current_screen Current screen.
+ *
+ * @return null
+ * @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
+ */
+ public function non_admins_current_screen_check( $current_screen ) {
+ _deprecated_function( __METHOD__, '0.5.0' );
+
+ self::$current_screen = $current_screen;
+ if ( isset( $current_screen->id ) && 'toplevel_page_jetpack' === $current_screen->id ) {
+ return null;
+ }
+
+ return null;
+ }
+
+ /**
+ * Renders the admin bar button.
+ *
+ * @return void
+ */
+ public function display_admin_bar_button() {
+ global $wp_admin_bar;
+
+ $href = is_admin()
+ ? add_query_arg( 'jetpack_idc_clear_confirmation', '1' )
+ : add_query_arg( 'jetpack_idc_clear_confirmation', '1', admin_url() );
+
+ $href = wp_nonce_url( $href, 'jetpack_idc_clear_confirmation' );
+
+ $title = sprintf(
+ '<span class="jp-idc-admin-bar">%s %s</span>',
+ '<span class="dashicons dashicons-warning"></span>',
+ esc_html__( 'Jetpack Safe Mode', 'jetpack-idc' )
+ );
+
+ $menu = array(
+ 'id' => 'jetpack-idc',
+ 'title' => $title,
+ 'href' => esc_url( $href ),
+ 'parent' => 'top-secondary',
+ );
+
+ if ( ! self::$is_safe_mode_confirmed ) {
+ $menu['meta'] = array(
+ 'class' => 'hide',
+ );
+ }
+
+ $wp_admin_bar->add_node( $menu );
+ }
+
+ /**
+ * Checks if the site is currently in an identity crisis.
+ *
+ * @return array|bool Array of options that are in a crisis, or false if everything is OK.
+ */
+ public static function check_identity_crisis() {
+ $connection = new Connection_Manager( 'jetpack' );
+
+ if ( ! $connection->is_connected() || ( new Status() )->is_offline_mode() || ! self::validate_sync_error_idc_option() ) {
+ return false;
+ }
+
+ return Jetpack_Options::get_option( 'sync_error_idc' );
+ }
+
+ /**
+ * Checks the HTTP response body for the 'idc_detected' key. If the key exists,
+ * checks the idc_detected value for a valid idc error.
+ *
+ * @param array|WP_Error $http_response The HTTP response.
+ *
+ * @return bool Whether the site is in an identity crisis.
+ */
+ public function check_http_response_for_idc_detected( $http_response ) {
+ if ( ! is_array( $http_response ) ) {
+ return false;
+ }
+ $response_body = json_decode( wp_remote_retrieve_body( $http_response ), true );
+
+ if ( isset( $response_body['idc_detected'] ) ) {
+ return $this->check_response_for_idc( $response_body['idc_detected'] );
+ }
+
+ if ( isset( $response_body['migrated_for_idc'] ) ) {
+ Jetpack_Options::delete_option( 'migrate_for_idc' );
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks the WPCOM response to determine if the site is in an identity crisis. Updates the
+ * sync_error_idc option if it is.
+ *
+ * @param array $response The response data.
+ *
+ * @return bool Whether the site is in an identity crisis.
+ */
+ public function check_response_for_idc( $response ) {
+ if ( ! is_array( $response ) ) {
+ return false;
+ }
+
+ if ( is_array( $response ) && isset( $response['error_code'] ) ) {
+ $error_code = $response['error_code'];
+ $allowed_idc_error_codes = array(
+ 'jetpack_url_mismatch',
+ 'jetpack_home_url_mismatch',
+ 'jetpack_site_url_mismatch',
+ );
+
+ if ( in_array( $error_code, $allowed_idc_error_codes, true ) ) {
+ \Jetpack_Options::update_option(
+ 'sync_error_idc',
+ self::get_sync_error_idc_option( $response )
+ );
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Prepare URL for display.
+ *
+ * @param string $url URL to display.
+ *
+ * @return string
+ */
+ public static function prepare_url_for_display( $url ) {
+ return untrailingslashit( self::normalize_url_protocol_agnostic( $url ) );
+ }
+
+ /**
+ * Clears all IDC specific options. This method is used on disconnect and reconnect.
+ *
+ * @return void
+ */
+ public static function clear_all_idc_options() {
+ // If the site is currently in IDC, let's also clear the VaultPress connection options.
+ // We have to check if the site is in IDC, otherwise we'd be clearing the VaultPress
+ // connection any time the Jetpack connection is cycled.
+ if ( self::validate_sync_error_idc_option() ) {
+ delete_option( 'vaultpress' );
+ delete_option( 'vaultpress_auto_register' );
+ }
+
+ Jetpack_Options::delete_option(
+ array(
+ 'sync_error_idc',
+ 'safe_mode_confirmed',
+ 'migrate_for_idc',
+ )
+ );
+ }
+
+ /**
+ * Checks whether the sync_error_idc option is valid or not, and if not, will do cleanup.
+ *
+ * @return bool
+ * @since-jetpack 5.4.0 Do not call get_sync_error_idc_option() unless site is in IDC
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ public static function validate_sync_error_idc_option() {
+ $is_valid = false;
+
+ // Is the site opted in and does the stored sync_error_idc option match what we now generate?
+ $sync_error = Jetpack_Options::get_option( 'sync_error_idc' );
+ if ( $sync_error && self::should_handle_idc() ) {
+ $local_options = self::get_sync_error_idc_option();
+
+ // Ensure all values are set.
+ if ( isset( $sync_error['home'] ) && isset( $local_options['home'] ) && isset( $sync_error['siteurl'] ) && isset( $local_options['siteurl'] ) ) {
+ // If the WP.com expected home and siteurl match local home and siteurl it is not valid IDC.
+ if (
+ isset( $sync_error['wpcom_home'] ) &&
+ isset( $sync_error['wpcom_siteurl'] ) &&
+ $sync_error['wpcom_home'] === $local_options['home'] &&
+ $sync_error['wpcom_siteurl'] === $local_options['siteurl']
+ ) {
+ $is_valid = false;
+ // Enable migrate_for_idc so that sync actions are accepted.
+ Jetpack_Options::update_option( 'migrate_for_idc', true );
+ } elseif ( $sync_error['home'] === $local_options['home'] && $sync_error['siteurl'] === $local_options['siteurl'] ) {
+ $is_valid = true;
+ }
+ }
+ }
+
+ /**
+ * Filters whether the sync_error_idc option is valid.
+ *
+ * @param bool $is_valid If the sync_error_idc is valid or not.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ $is_valid = (bool) apply_filters( 'jetpack_sync_error_idc_validation', $is_valid );
+
+ if ( ! $is_valid && $sync_error ) {
+ // Since the option exists, and did not validate, delete it.
+ Jetpack_Options::delete_option( 'sync_error_idc' );
+ }
+
+ return $is_valid;
+ }
+
+ /**
+ * Normalizes a url by doing three things:
+ * - Strips protocol
+ * - Strips www
+ * - Adds a trailing slash
+ *
+ * @param string $url URL to parse.
+ *
+ * @return WP_Error|string
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ public static function normalize_url_protocol_agnostic( $url ) {
+ $parsed_url = wp_parse_url( trailingslashit( esc_url_raw( $url ) ) );
+ if ( ! $parsed_url || empty( $parsed_url['host'] ) || empty( $parsed_url['path'] ) ) {
+ return new WP_Error(
+ 'cannot_parse_url',
+ sprintf(
+ /* translators: %s: URL to parse. */
+ esc_html__( 'Cannot parse URL %s', 'jetpack-idc' ),
+ $url
+ )
+ );
+ }
+
+ // Strip www and protocols.
+ $url = preg_replace( '/^www\./i', '', $parsed_url['host'] . $parsed_url['path'] );
+
+ return $url;
+ }
+
+ /**
+ * Gets the value that is to be saved in the jetpack_sync_error_idc option.
+ *
+ * @param array $response HTTP response.
+ *
+ * @return array Array of the local urls, wpcom urls, and error code.
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ * @since-jetpack 5.4.0 Add transient since home/siteurl retrieved directly from DB.
+ */
+ public static function get_sync_error_idc_option( $response = array() ) {
+ // Since the local options will hit the database directly, store the values
+ // in a transient to allow for autoloading and caching on subsequent views.
+ $local_options = get_transient( 'jetpack_idc_local' );
+ if ( false === $local_options ) {
+ $local_options = array(
+ 'home' => Urls::home_url(),
+ 'siteurl' => Urls::site_url(),
+ );
+ set_transient( 'jetpack_idc_local', $local_options, MINUTE_IN_SECONDS );
+ }
+
+ $options = array_merge( $local_options, $response );
+
+ $returned_values = array();
+ foreach ( $options as $key => $option ) {
+ if ( 'error_code' === $key ) {
+ $returned_values[ $key ] = $option;
+ continue;
+ }
+
+ $normalized_url = self::normalize_url_protocol_agnostic( $option );
+ if ( is_wp_error( $normalized_url ) ) {
+ continue;
+ }
+
+ $returned_values[ $key ] = $normalized_url;
+ }
+
+ return $returned_values;
+ }
+
+ /**
+ * Returns the value of the jetpack_sync_idc_optin filter, or constant.
+ * If set to true, the site will be put into staging mode.
+ *
+ * @return bool
+ * @since 0.2.0
+ * @since-jetpack 4.3.2
+ * @deprecated 0.2.6 Use should_handle_idc()
+ * @see Automattic\Jetpack\Identity_Crisis::should_handle_idc
+ */
+ public static function sync_idc_optin() {
+ _deprecated_function( __METHOD__, '0.2.6', 'Automattic\\Jetpack\\Identity_Crisis::should_handle_idc' );
+ return self::should_handle_idc();
+ }
+
+ /**
+ * Returns the value of the jetpack_should_handle_idc filter or constant.
+ * If set to true, the site will be put into staging mode.
+ *
+ * This method uses both the current jetpack_should_handle_idc filter and constant and the
+ * legacy jetpack_sync_idc_optin filter and constant to determine whether an IDC should be
+ * handled.
+ *
+ * @return bool
+ * @since 0.2.6
+ */
+ public static function should_handle_idc() {
+ if ( Constants::is_defined( 'JETPACK_SHOULD_HANDLE_IDC' ) ) {
+ $default = Constants::get_constant( 'JETPACK_SHOULD_HANDLE_IDC' );
+ } elseif ( Constants::is_defined( 'JETPACK_SYNC_IDC_OPTIN' ) ) {
+ // Check the legacy constant. This constant should be considered deprecated as of version 0.2.6.
+ $default = Constants::get_constant( 'JETPACK_SYNC_IDC_OPTIN' );
+ } else {
+ $default = ! Constants::is_defined( 'SUNRISE' ) && ! is_multisite();
+ }
+
+ // Add a callback which uses the legacy filter 'jetpack_sync_idc_optin'.
+ add_filter( 'jetpack_should_handle_idc', array( __CLASS__, 'legacy_jetpack_sync_idc_optin_filter' ) );
+
+ /**
+ * Allows sites to opt in for IDC mitigation which blocks the site from syncing to WordPress.com when the home
+ * URL or site URL do not match what WordPress.com expects. The default value is either true, or the value of
+ * JETPACK_SHOULD_HANDLE_IDC constant if set.
+ *
+ * @param bool $default Whether the site is opted in to IDC mitigation.
+ *
+ * @since 0.2.6
+ */
+ return (bool) apply_filters( 'jetpack_should_handle_idc', $default );
+ }
+
+ /**
+ * Returns the value for the deprecated filter, 'jetpack_sync_idc_optin'. That filter has been replaced with the
+ * 'jetpack_should_handle_idc' filter.
+ *
+ * @since 0.2.6
+ *
+ * @param bool $default Whether the site is opted in to IDC mitigation.
+ */
+ public static function legacy_jetpack_sync_idc_optin_filter( $default ) {
+ /**
+ * Allows sites to opt in for IDC mitigation which blocks the site from syncing to WordPress.com when the home
+ * URL or site URL do not match what WordPress.com expects. The default value is either true, or the value of
+ * JETPACK_SYNC_IDC_OPTIN constant if set.
+ *
+ * @param bool $default Whether the site is opted in to IDC mitigation.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.3.2
+ * @deprecated 0.2.6 Use jetpack_should_handle_idc
+ */
+ return (bool) apply_filters_deprecated( 'jetpack_sync_idc_optin', array( $default ), '0.2.6', 'jetpack_should_handle_idc' );
+ }
+
+ /**
+ * Does the current admin page have help tabs?
+ *
+ * @return bool
+ * @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
+ */
+ public function admin_page_has_help_tabs() {
+ _deprecated_function( __METHOD__, '0.5.0' );
+
+ if ( ! function_exists( 'get_current_screen' ) ) {
+ return false;
+ }
+
+ $current_screen = get_current_screen();
+ $tabs = $current_screen->get_help_tabs();
+
+ return ! empty( $tabs );
+ }
+
+ /**
+ * Renders the non-admin IDC notice.
+ *
+ * @return void
+ * @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
+ */
+ public function display_non_admin_idc_notice() {
+ _deprecated_function( __METHOD__, '0.5.0' );
+
+ $classes = 'jp-idc-notice inline is-non-admin notice notice-warning';
+ if ( isset( self::$current_screen ) && 'toplevel_page_jetpack' !== self::$current_screen->id ) {
+ $classes .= ' is-dismissible';
+ }
+
+ if ( $this->admin_page_has_help_tabs() ) {
+ $classes .= ' has-help-tabs';
+ }
+ ?>
+
+ <div class="<?php echo esc_attr( $classes ); ?>">
+ <?php $this->render_notice_header(); ?>
+ <div class="jp-idc-notice__content-header">
+ <h3 class="jp-idc-notice__content-header__lead">
+ <?php echo $this->get_non_admin_notice_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </h3>
+
+ <p class="jp-idc-notice__content-header__explanation">
+ <?php echo $this->get_non_admin_contact_admin_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </p>
+ </div>
+ </div>
+ <?php
+ }
+
+ /**
+ * First "step" of the IDC mitigation. Will provide some messaging and two options/buttons.
+ * "Confirm Staging" - Dismiss the notice and continue on with our lives in staging mode.
+ * "Fix Jetpack Connection" - Will disconnect the site and start the mitigation...
+ *
+ * @return void
+ * @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
+ */
+ public function display_idc_notice() {
+ _deprecated_function( __METHOD__, '0.5.0' );
+
+ $classes = 'jp-idc-notice inline notice notice-warning';
+ if ( $this->admin_page_has_help_tabs() ) {
+ $classes .= ' has-help-tabs';
+ }
+ ?>
+ <div class="<?php echo esc_attr( $classes ); ?>">
+ <?php $this->render_notice_header(); ?>
+ <?php $this->render_notice_first_step(); ?>
+ <?php $this->render_notice_second_step(); ?>
+ </div>
+ <?php
+ }
+
+ /**
+ * Enqueue CSS for the admin bar.
+ *
+ * @return void
+ * @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
+ */
+ public function enqueue_admin_bar_css() {
+ _deprecated_function( __METHOD__, '0.5.0' );
+ }
+
+ /**
+ * Enqueue scripts for the notice.
+ *
+ * @return void
+ * @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
+ */
+ public function enqueue_idc_notice_files() {
+ _deprecated_function( __METHOD__, '0.5.0' );
+
+ // Register and Enqueue jp-tracks-functions.
+ Tracking::register_tracks_functions_scripts( true );
+ }
+
+ /**
+ * Renders the notice header.
+ *
+ * @return void
+ * @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
+ */
+ public function render_notice_header() {
+ _deprecated_function( __METHOD__, '0.5.0' );
+
+ ?>
+ <div class="jp-idc-notice__header">
+ <div class="jp-idc-notice__header__emblem">
+ <?php
+ $jetpack_logo = new Jetpack_Logo();
+ echo $jetpack_logo->get_jp_emblem(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ ?>
+ </div>
+ <p class="jp-idc-notice__header__text">
+ <?php esc_html_e( 'Jetpack Safe Mode', 'jetpack-idc' ); ?>
+ </p>
+ </div>
+
+ <div class="jp-idc-notice__separator"></div>
+ <?php
+ }
+
+ /**
+ * Is a container for the error notices.
+ * Will be shown/controlled by jQuery in idc-notice.js.
+ *
+ * @return void
+ */
+ public function render_error_notice() {
+ ?>
+ <div class="jp-idc-error__notice dops-notice is-error">
+ <svg class="gridicon gridicons-notice dops-notice__icon" height="24" width="24" viewBox="0 0 24 24">
+ <g>
+ <path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2zm1 15h-2v-2h2v2zm0-4h-2l-.5-6h3l-.5 6z"></path>
+ </g>
+ </svg>
+ <div class="dops-notice__content">
+ <span class="dops-notice__text">
+ <?php esc_html_e( 'Something went wrong:', 'jetpack-idc' ); ?>
+ <span class="jp-idc-error__desc"></span>
+ </span>
+ <a class="dops-notice__action" href="javascript:void(0);">
+ <span id="jp-idc-error__action">
+ <?php esc_html_e( 'Try Again', 'jetpack-idc' ); ?>
+ </span>
+ </a>
+ </div>
+ </div>
+ <?php
+ }
+
+ /**
+ * Renders the first step notice.
+ *
+ * @return void
+ */
+ public function render_notice_first_step() {
+ ?>
+ <div class="jp-idc-notice__first-step">
+ <div class="jp-idc-notice__content-header">
+ <h3 class="jp-idc-notice__content-header__lead">
+ <?php echo $this->get_first_step_header_lead(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </h3>
+
+ <p class="jp-idc-notice__content-header__explanation">
+ <?php echo $this->get_first_step_header_explanation(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </p>
+ </div>
+
+ <?php $this->render_error_notice(); ?>
+
+ <div class="jp-idc-notice__actions">
+ <div class="jp-idc-notice__action">
+ <p class="jp-idc-notice__action__explanation">
+ <?php echo $this->get_confirm_safe_mode_action_explanation(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </p>
+ <button id="jp-idc-confirm-safe-mode-action" class="dops-button">
+ <?php echo $this->get_confirm_safe_mode_button_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </button>
+ </div>
+
+ <div class="jp-idc-notice__action">
+ <p class="jp-idc-notice__action__explanation">
+ <?php echo $this->get_first_step_fix_connection_action_explanation(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </p>
+ <button id="jp-idc-fix-connection-action" class="dops-button">
+ <?php echo $this->get_first_step_fix_connection_button_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </button>
+ </div>
+ </div>
+ </div>
+ <?php
+ }
+
+ /**
+ * Renders the second step notice.
+ *
+ * @return void
+ */
+ public function render_notice_second_step() {
+ ?>
+ <div class="jp-idc-notice__second-step">
+ <div class="jp-idc-notice__content-header">
+ <h3 class="jp-idc-notice__content-header__lead">
+ <?php echo $this->get_second_step_header_lead(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </h3>
+ </div>
+
+ <?php $this->render_error_notice(); ?>
+
+ <div class="jp-idc-notice__actions">
+ <div class="jp-idc-notice__action">
+ <p class="jp-idc-notice__action__explanation">
+ <?php echo $this->get_migrate_site_action_explanation(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </p>
+ <button id="jp-idc-migrate-action" class="dops-button">
+ <?php echo $this->get_migrate_site_button_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </button>
+ </div>
+
+ <div class="jp-idc-notice__action">
+ <p class="jp-idc-notice__action__explanation">
+ <?php echo $this->get_start_fresh_action_explanation(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </p>
+ <button id="jp-idc-reconnect-site-action" class="dops-button">
+ <?php echo $this->get_start_fresh_button_text(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </button>
+ </div>
+
+ </div>
+
+ <p class="jp-idc-notice__unsure-prompt">
+ <?php echo $this->get_unsure_prompt(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
+ </p>
+ </div>
+ <?php
+ }
+
+ /**
+ * Returns the first step header lead.
+ *
+ * @return string
+ */
+ public function get_first_step_header_lead() {
+ $html = wp_kses(
+ sprintf(
+ /* translators: %s: Safe mode docs URL and site URL. */
+ __( 'Jetpack has been placed into <a href="%1$s">Safe mode</a> because we noticed this is an exact copy of <a href="%2$s">%3$s</a>.', 'jetpack-idc' ),
+ esc_url( self::get_safe_mod_doc_url() ),
+ esc_url( self::$wpcom_home_url ),
+ self::prepare_url_for_display( esc_url_raw( self::$wpcom_home_url ) )
+ ),
+ array( 'a' => array( 'href' => array() ) )
+ );
+
+ /**
+ * Allows overriding of the default header text in the first step of the Safe Mode notice.
+ *
+ * @param string $html The HTML to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_first_step_header_lead', $html );
+ }
+
+ /**
+ * Returns the first step header explanation.
+ *
+ * @return string
+ */
+ public function get_first_step_header_explanation() {
+ $html = wp_kses(
+ sprintf(
+ /* translators: %s: Safe mode docs URL. */
+ __( 'Please confirm Safe Mode or fix the Jetpack connection. Select one of the options below or <a href="%1$s">learn more about Safe Mode</a>.', 'jetpack-idc' ),
+ esc_url( self::get_safe_mod_doc_url() )
+ ),
+ array( 'a' => array( 'href' => array() ) )
+ );
+
+ /**
+ * Allows overriding of the default header explanation text in the first step of the Safe Mode notice.
+ *
+ * @param string $html The HTML to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_first_step_header_explanation', $html );
+ }
+
+ /**
+ * Returns the confirm safe mode explanation.
+ *
+ * @return string
+ */
+ public function get_confirm_safe_mode_action_explanation() {
+ $html = wp_kses(
+ sprintf(
+ /* translators: %s: Site URL. */
+ __( 'Is this website a temporary duplicate of <a href="%1$s">%2$s</a> for the purposes of testing, staging or development? If so, we recommend keeping it in Safe Mode.', 'jetpack-idc' ),
+ esc_url( untrailingslashit( self::$wpcom_home_url ) ),
+ self::prepare_url_for_display( esc_url( self::$wpcom_home_url ) )
+ ),
+ array( 'a' => array( 'href' => array() ) )
+ );
+
+ /**
+ * Allows overriding of the default text used to explain the confirm safe mode action.
+ *
+ * @param string $html The HTML to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_confirm_safe_mode_explanation', $html );
+ }
+
+ /**
+ * Returns the confirm safe mode button text.
+ *
+ * @return string
+ */
+ public function get_confirm_safe_mode_button_text() {
+ $string = esc_html__( 'Confirm Safe Mode', 'jetpack-idc' );
+
+ /**
+ * Allows overriding of the default text used for the confirm safe mode action button.
+ *
+ * @param string $string The string to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_confirm_safe_mode_button_text', $string );
+ }
+
+ /**
+ * Returns the first step fix connection action explanation.
+ *
+ * @return string
+ */
+ public function get_first_step_fix_connection_action_explanation() {
+ $html = wp_kses(
+ sprintf(
+ /* translators: %s: Site URL. */
+ __( 'If this is a separate and new website, or the new home of <a href="%1$s">%2$s</a>, we recommend turning Safe Mode off, and re-establishing your connection to WordPress.com.', 'jetpack-idc' ),
+ esc_url( untrailingslashit( self::$wpcom_home_url ) ),
+ self::prepare_url_for_display( esc_url( self::$wpcom_home_url ) )
+ ),
+ array( 'a' => array( 'href' => array() ) )
+ );
+
+ /**
+ * Allows overriding of the default text used to explain the fix Jetpack connection action.
+ *
+ * @param string $html The HTML to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_first_fix_connection_explanation', $html );
+ }
+
+ /**
+ * Returns the first step fix connection button text.
+ *
+ * @return string
+ */
+ public function get_first_step_fix_connection_button_text() {
+ $string = esc_html__( "Fix Jetpack's Connection", 'jetpack-idc' );
+
+ /**
+ * Allows overriding of the default text used for the fix Jetpack connection action button.
+ *
+ * @param string $string The string to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_first_step_fix_connection_button_text', $string );
+ }
+
+ /**
+ * Returns the second step header lead.
+ *
+ * @return string
+ */
+ public function get_second_step_header_lead() {
+ $string = sprintf(
+ /* translators: %s: Site URL. */
+ esc_html__( 'Is %1$s the new home of %2$s?', 'jetpack-idc' ),
+ untrailingslashit( self::normalize_url_protocol_agnostic( get_home_url() ) ),
+ untrailingslashit( self::normalize_url_protocol_agnostic( esc_url_raw( self::$wpcom_home_url ) ) )
+ );
+
+ /**
+ * Allows overriding of the default header text in the second step of the Safe Mode notice.
+ *
+ * @param string $html The HTML to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_second_step_header_lead', $string );
+ }
+
+ /**
+ * Returns the site action explanation.
+ *
+ * @return string
+ */
+ public function get_migrate_site_action_explanation() {
+ $html = wp_kses(
+ sprintf(
+ /* translators: %s: Site URL. */
+ __( 'Yes. <a href="%1$s">%2$s</a> is replacing <a href="%3$s">%4$s</a>. I would like to migrate my stats and subscribers from <a href="%3$s">%4$s</a> to <a href="%1$s">%2$s</a>.', 'jetpack-idc' ),
+ esc_url( get_home_url() ),
+ self::prepare_url_for_display( get_home_url() ),
+ esc_url( self::$wpcom_home_url ),
+ untrailingslashit( self::normalize_url_protocol_agnostic( esc_url_raw( self::$wpcom_home_url ) ) )
+ ),
+ array( 'a' => array( 'href' => array() ) )
+ );
+
+ /**
+ * Allows overriding of the default text for explaining the migrate site action.
+ *
+ * @param string $html The HTML to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_migrate_site_explanation', $html );
+ }
+
+ /**
+ * Returns the migrate site button text.
+ *
+ * @return string
+ */
+ public function get_migrate_site_button_text() {
+ $string = esc_html__( 'Migrate Stats &amp; Subscribers', 'jetpack-idc' );
+
+ /**
+ * Allows overriding of the default text used for the migrate site action button.
+ *
+ * @param string $string The string to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_migrate_site_button_text', $string );
+ }
+
+ /**
+ * Returns the start fresh explanation.
+ *
+ * @return string
+ */
+ public function get_start_fresh_action_explanation() {
+ $html = wp_kses(
+ sprintf(
+ /* translators: %s: Site URL. */
+ __( 'No. <a href="%1$s">%2$s</a> is a new and different website that\'s separate from <a href="%3$s">%4$s</a>. It requires a new connection to WordPress.com for new stats and subscribers.', 'jetpack-idc' ),
+ esc_url( get_home_url() ),
+ self::prepare_url_for_display( get_home_url() ),
+ esc_url( self::$wpcom_home_url ),
+ untrailingslashit( self::normalize_url_protocol_agnostic( esc_url_raw( self::$wpcom_home_url ) ) )
+ ),
+ array( 'a' => array( 'href' => array() ) )
+ );
+
+ /**
+ * Allows overriding of the default text for explaining the start fresh action.
+ *
+ * @param string $html The HTML to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_start_fresh_explanation', $html );
+ }
+
+ /**
+ * Returns the start fresh button text.
+ *
+ * @return string
+ */
+ public function get_start_fresh_button_text() {
+ $string = esc_html__( 'Start Fresh &amp; Create New Connection', 'jetpack-idc' );
+
+ /**
+ * Allows overriding of the default text used for the start fresh action button.
+ *
+ * @param string $string The string to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_start_fresh_button_text', $string );
+ }
+
+ /**
+ * Returns the unsure prompt text.
+ *
+ * @return string
+ */
+ public function get_unsure_prompt() {
+ $html = wp_kses(
+ sprintf(
+ /* translators: %s: Safe mode docs URL. */
+ __( 'Unsure what to do? <a href="%1$s">Read more about Jetpack Safe Mode</a>', 'jetpack-idc' ),
+ esc_url( self::get_safe_mod_doc_url() )
+ ),
+ array( 'a' => array( 'href' => array() ) )
+ );
+
+ /**
+ * Allows overriding of the default text using in the "Unsure what to do?" prompt.
+ *
+ * @param string $html The HTML to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_unsure_prompt', $html );
+ }
+
+ /**
+ * Returns the non-admin notice text.
+ *
+ * @return string
+ * @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
+ */
+ public function get_non_admin_notice_text() {
+ _deprecated_function( __METHOD__, '0.5.0' );
+
+ $html = wp_kses(
+ sprintf(
+ /* translators: %s: Safe mode docs URL. */
+ __( 'Jetpack has been placed into Safe Mode. Learn more about <a href="%1$s">Safe Mode</a>.', 'jetpack-idc' ),
+ esc_url( self::get_safe_mod_doc_url() )
+ ),
+ array( 'a' => array( 'href' => array() ) )
+ );
+
+ /**
+ * Allows overriding of the default text that is displayed to non-admin on the Jetpack admin page.
+ *
+ * @param string $html The HTML to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_non_admin_notice_text', $html );
+ }
+
+ /**
+ * Returns the non-admin contact admin text.
+ *
+ * @return string
+ * @deprecated 0.5.0 Use `@automattic/jetpack-idc` instead.
+ */
+ public function get_non_admin_contact_admin_text() {
+ _deprecated_function( __METHOD__, '0.5.0' );
+
+ $string = esc_html__( 'An administrator of this site can take Jetpack out of Safe Mode.', 'jetpack-idc' );
+
+ /**
+ * Allows overriding of the default text that is displayed to non-admins prompting them to contact an admin.
+ *
+ * @param string $string The string to be displayed.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ */
+ return apply_filters( 'jetpack_idc_non_admin_contact_admin_text', $string );
+ }
+
+ /**
+ * Whether the site is undergoing identity crisis.
+ *
+ * @return bool
+ */
+ public static function has_identity_crisis() {
+ return false !== static::check_identity_crisis() && ! static::$is_safe_mode_confirmed;
+ }
+
+ /**
+ * Whether an admin has confirmed safe mode.
+ * Unlike `static::$is_safe_mode_confirmed` this function always returns the actual flag value.
+ *
+ * @return bool
+ */
+ public static function safe_mode_is_confirmed() {
+ return Jetpack_Options::get_option( 'safe_mode_confirmed' );
+ }
+
+ /**
+ * Returns the mismatched URLs.
+ *
+ * @return array|bool The mismatched urls, or false if the site is not connected, offline, in safe mode, or the IDC error is not valid.
+ */
+ public static function get_mismatched_urls() {
+ if ( ! static::has_identity_crisis() ) {
+ return false;
+ }
+
+ $data = static::check_identity_crisis();
+
+ if ( ! $data ||
+ ! isset( $data['error_code'] ) ||
+ ! isset( $data['wpcom_home'] ) ||
+ ! isset( $data['home'] ) ||
+ ! isset( $data['wpcom_siteurl'] ) ||
+ ! isset( $data['siteurl'] )
+ ) {
+ // The jetpack_sync_error_idc option is missing a key.
+ return false;
+ }
+
+ if ( 'jetpack_site_url_mismatch' === $data['error_code'] ) {
+ return array(
+ 'wpcom_url' => $data['wpcom_siteurl'],
+ 'current_url' => $data['siteurl'],
+ );
+ }
+
+ return array(
+ 'wpcom_url' => $data['wpcom_home'],
+ 'current_url' => $data['home'],
+ );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/class-rest-endpoints.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/class-rest-endpoints.php
new file mode 100644
index 00000000..496f53fa
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/class-rest-endpoints.php
@@ -0,0 +1,188 @@
+<?php
+/**
+ * Identity_Crisis package.
+ *
+ * @package automattic/jetpack-identity-crisis
+ */
+
+namespace Automattic\Jetpack\IdentityCrisis;
+
+use Automattic\Jetpack\Connection\Manager as Connection_Manager;
+use Jetpack_Options;
+use WP_Error;
+use WP_REST_Server;
+
+/**
+ * This class will handle Identity Crisis Endpoints
+ *
+ * @since 0.2.0
+ */
+class REST_Endpoints {
+
+ /**
+ * Initialize REST routes.
+ */
+ public static function initialize_rest_api() {
+
+ // Confirm that a site in identity crisis should be in staging mode.
+ register_rest_route(
+ 'jetpack/v4',
+ '/identity-crisis/confirm-safe-mode',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::confirm_safe_mode',
+ 'permission_callback' => __CLASS__ . '::identity_crisis_mitigation_permission_check',
+ )
+ );
+
+ // Handles the request to migrate stats and subscribers during an identity crisis.
+ register_rest_route(
+ 'jetpack/v4',
+ 'identity-crisis/migrate',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::migrate_stats_and_subscribers',
+ 'permission_callback' => __CLASS__ . '::identity_crisis_mitigation_permission_check',
+ )
+ );
+
+ // IDC resolve: create an entirely new shadow site for this URL.
+ register_rest_route(
+ 'jetpack/v4',
+ '/identity-crisis/start-fresh',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::start_fresh_connection',
+ 'permission_callback' => __CLASS__ . '::identity_crisis_mitigation_permission_check',
+ 'args' => array(
+ 'redirect_uri' => array(
+ 'description' => __( 'URI of the admin page where the user should be redirected after connection flow', 'jetpack-idc' ),
+ 'type' => 'string',
+ ),
+ ),
+ )
+ );
+
+ }
+
+ /**
+ * Handles identity crisis mitigation, confirming safe mode for this site.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ *
+ * @return bool | WP_Error True if option is properly set.
+ */
+ public static function confirm_safe_mode() {
+ $updated = Jetpack_Options::update_option( 'safe_mode_confirmed', true );
+ if ( $updated ) {
+ return rest_ensure_response(
+ array(
+ 'code' => 'success',
+ )
+ );
+ }
+
+ return new WP_Error(
+ 'error_setting_jetpack_safe_mode',
+ esc_html__( 'Could not confirm safe mode.', 'jetpack-idc' ),
+ array( 'status' => 500 )
+ );
+ }
+
+ /**
+ * Handles identity crisis mitigation, migrating stats and subscribers from old url to this, new url.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ *
+ * @return bool | WP_Error True if option is properly set.
+ */
+ public static function migrate_stats_and_subscribers() {
+ if ( Jetpack_Options::get_option( 'sync_error_idc' ) && ! Jetpack_Options::delete_option( 'sync_error_idc' ) ) {
+ return new WP_Error(
+ 'error_deleting_sync_error_idc',
+ esc_html__( 'Could not delete sync error option.', 'jetpack-idc' ),
+ array( 'status' => 500 )
+ );
+ }
+
+ if ( Jetpack_Options::get_option( 'migrate_for_idc' ) || Jetpack_Options::update_option( 'migrate_for_idc', true ) ) {
+ return rest_ensure_response(
+ array(
+ 'code' => 'success',
+ )
+ );
+ }
+ return new WP_Error(
+ 'error_setting_jetpack_migrate',
+ esc_html__( 'Could not confirm migration.', 'jetpack-idc' ),
+ array( 'status' => 500 )
+ );
+ }
+
+ /**
+ * This IDC resolution will disconnect the site and re-connect to a completely new
+ * and separate shadow site than the original.
+ *
+ * It will first will disconnect the site without phoning home as to not disturb the production site.
+ * It then builds a fresh connection URL and sends it back along with the response.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response|WP_Error
+ */
+ public static function start_fresh_connection( $request ) {
+ /**
+ * Fires when Users have requested through Identity Crisis for the connection to be reset.
+ * Should be used to disconnect any connections and reset options.
+ *
+ * @since 0.2.0
+ */
+ do_action( 'jetpack_idc_disconnect' );
+
+ $connection = new Connection_Manager();
+ $result = $connection->try_registration( true );
+
+ // early return if site registration fails.
+ if ( ! $result || is_wp_error( $result ) ) {
+ return rest_ensure_response( $result );
+ }
+
+ $redirect_uri = $request->get_param( 'redirect_uri' ) ? admin_url( $request->get_param( 'redirect_uri' ) ) : null;
+
+ /**
+ * Filters the connection url that users should be redirected to for re-establishing their connection.
+ *
+ * @since 0.2.0
+ *
+ * @param \WP_REST_Response|WP_Error $connection_url Connection URL user should be redirected to.
+ */
+ return apply_filters( 'jetpack_idc_authorization_url', rest_ensure_response( $connection->get_authorization_url( null, $redirect_uri ) ) );
+ }
+
+ /**
+ * Verify that user can mitigate an identity crisis.
+ *
+ * @since 0.2.0
+ * @since-jetpack 4.4.0
+ *
+ * @return true|WP_Error True if the user has capability 'jetpack_disconnect', an error object otherwise.
+ */
+ public static function identity_crisis_mitigation_permission_check() {
+ if ( current_user_can( 'jetpack_disconnect' ) ) {
+ return true;
+ }
+ $error_msg = esc_html__(
+ 'You do not have the correct user permissions to perform this action.
+ Please contact your site admin if you think this is a mistake.',
+ 'jetpack-idc'
+ );
+
+ return new WP_Error( 'invalid_user_permission_identity_crisis', $error_msg, array( 'status' => rest_authorization_required_code() ) );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/class-ui.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/class-ui.php
new file mode 100644
index 00000000..a3c2694a
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-identity-crisis/src/class-ui.php
@@ -0,0 +1,151 @@
+<?php
+/**
+ * Identity_Crisis package.
+ *
+ * @package automattic/jetpack-identity-crisis
+ */
+
+namespace Automattic\Jetpack\IdentityCrisis;
+
+use Automattic\Jetpack\Assets;
+use Automattic\Jetpack\Identity_Crisis;
+use Automattic\Jetpack\Tracking as Tracking;
+use Jetpack_Tracks_Client;
+
+/**
+ * The Identity Crisis UI handling.
+ */
+class UI {
+
+ /**
+ * Initialization.
+ */
+ public static function init() {
+ if ( did_action( 'jetpack_identity_crisis_ui_init' ) ) {
+ return;
+ }
+
+ /**
+ * Action called after initializing Identity Crisis UI.
+ *
+ * @since 0.6.0
+ */
+ do_action( 'jetpack_identity_crisis_ui_init' );
+
+ $idc_data = Identity_Crisis::check_identity_crisis();
+
+ if ( false === $idc_data ) {
+ return;
+ }
+
+ add_action( 'admin_enqueue_scripts', array( static::class, 'enqueue_scripts' ) );
+
+ Tracking::register_tracks_functions_scripts( true );
+ }
+
+ /**
+ * Enqueue scripts!
+ */
+ public static function enqueue_scripts() {
+ if ( is_admin() ) {
+ Assets::register_script(
+ 'jp_identity_crisis_banner',
+ '../build/index.js',
+ __FILE__,
+ array(
+ 'in_footer' => true,
+ 'textdomain' => 'jetpack-idc',
+ )
+ );
+ Assets::enqueue_script( 'jp_identity_crisis_banner' );
+ wp_add_inline_script( 'jp_identity_crisis_banner', static::get_initial_state(), 'before' );
+
+ add_action( 'admin_notices', array( static::class, 'render_container' ) );
+ }
+ }
+
+ /**
+ * Create the container element for the IDC banner.
+ */
+ public static function render_container() {
+ ?>
+ <div id="jp-identity-crisis-container"></div>
+ <?php
+ }
+
+ /**
+ * Return the rendered initial state JavaScript code.
+ *
+ * @return string
+ */
+ private static function get_initial_state() {
+ return 'var JP_IDENTITY_CRISIS__INITIAL_STATE=JSON.parse(decodeURIComponent("' . rawurlencode( wp_json_encode( static::get_initial_state_data() ) ) . '"));';
+ }
+
+ /**
+ * Get the initial state data.
+ *
+ * @return array
+ */
+ private static function get_initial_state_data() {
+ $idc_urls = Identity_Crisis::get_mismatched_urls();
+ $current_screen = get_current_screen();
+ $is_admin = current_user_can( 'jetpack_disconnect' );
+
+ return array(
+ 'WP_API_root' => esc_url_raw( rest_url() ),
+ 'WP_API_nonce' => wp_create_nonce( 'wp_rest' ),
+ 'wpcomHomeUrl' => ( is_array( $idc_urls ) && array_key_exists( 'wpcom_url', $idc_urls ) ) ? $idc_urls['wpcom_url'] : null,
+ 'currentUrl' => ( is_array( $idc_urls ) && array_key_exists( 'current_url', $idc_urls ) ) ? $idc_urls['current_url'] : null,
+ 'redirectUri' => str_replace( '/wp-admin/', '/', $_SERVER['REQUEST_URI'] ),
+ 'tracksUserData' => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
+ 'tracksEventData' => array(
+ 'isAdmin' => $is_admin,
+ 'currentScreen' => $current_screen ? $current_screen->id : false,
+ ),
+ 'isSafeModeConfirmed' => Identity_Crisis::$is_safe_mode_confirmed,
+ 'consumerData' => static::get_consumer_data(),
+ 'isAdmin' => $is_admin,
+ );
+ }
+
+ /**
+ * Get the package consumer data.
+ *
+ * @return array
+ */
+ private static function get_consumer_data() {
+ $consumers = apply_filters( 'jetpack_idc_consumers', array() );
+
+ if ( ! $consumers ) {
+ return array();
+ }
+
+ usort(
+ $consumers,
+ function ( $c1, $c2 ) {
+ $priority1 = ( array_key_exists( 'priority', $c1 ) && (int) $c1['priority'] ) ? (int) $c1['priority'] : 10;
+ $priority2 = ( array_key_exists( 'priority', $c2 ) && (int) $c2['priority'] ) ? (int) $c2['priority'] : 10;
+
+ return $priority1 > $priority2 ? 1 : -1;
+ }
+ );
+
+ $consumer_chosen = null;
+ $consumer_url_length = 0;
+
+ foreach ( $consumers as $consumer ) {
+ if ( empty( $consumer['admin_page'] ) || ! is_string( $consumer['admin_page'] ) ) {
+ continue;
+ }
+
+ if ( 0 === strpos( $_SERVER['REQUEST_URI'], $consumer['admin_page'] ) && strlen( $consumer['admin_page'] ) > $consumer_url_length ) {
+ $consumer_chosen = $consumer;
+ $consumer_url_length = strlen( $consumer['admin_page'] );
+ }
+ }
+
+ return $consumer_chosen ? $consumer_chosen : array_shift( $consumers );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/CHANGELOG.md
new file mode 100644
index 00000000..f734a12f
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/CHANGELOG.md
@@ -0,0 +1,431 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [2.2.0] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies.
+- Updated package textdomain from `jetpack` to `jetpack-jitm`.
+
+## [2.1.1] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [2.1.0] - 2021-11-30
+### Added
+- Add proper JS and CSS builder.
+- Adds filters to allow sideloading of the Jetpack Backup plugin through JITMs.
+- JITM: Added ability to sideload Jetpack Boost plugin.
+
+### Changed
+- Add `output.filename` in Webpack config to override changed default.
+- Colors: update Jetpack Primary color to match latest brand book.
+
+### Fixed
+- JITM: wrap CTA below text on small viewports
+
+## [2.0.8] - 2021-11-23
+### Changed
+- Updated package dependencies.
+
+## [2.0.7] - 2021-11-16
+### Added
+- Use monorepo `validate-es` script to validate Webpack builds.
+
+### Changed
+- Updated package dependencies.
+
+## [2.0.6] - 2021-11-09
+### Changed
+- Update webpack build config.
+
+## [2.0.5] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [2.0.4] - 2021-10-26
+### Changed
+- Updated package dependencies.
+
+## [2.0.3] - 2021-10-19
+### Changed
+- Updated package dependencies.
+
+## [2.0.2] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [2.0.1] - 2021-09-28
+### Changed
+- Allow Node ^14.17.6 to be used in this project. This shouldn't change the behavior of the code itself.
+- Updated package dependencies.
+
+## [2.0.0] - 2021-08-31
+### Changed
+- Run composer update on test-php command instead of phpunit.
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- Update annotations versions.
+- Updated package dependencies.
+- Update to latest webpack, webpack-cli and calypso-build.
+- Upgrade to Webpack 5.
+- Use Node 16.7.0 in tooling.
+
+### Removed
+- Removed IE11 support.
+
+## [1.16.2] - 2021-07-27
+### Changed
+- Updated package dependencies.
+
+## [1.16.1] - 2021-06-29
+### Changed
+- Update package dependencies.
+- Update node version requirement to 14.16.1
+- Update the usage of Redirect lib and passes the unlinked param as a query argument.
+
+## [1.16.0] - 2021-05-25
+### Added
+- JITM: allow the plugin to set the icon for pre-connection JITMs.
+- JITM: move sync updated option hook to the JITM package.
+
+### Changed
+- JITM: prevent JITMs from being registered more than once.
+- JITM: remove jetpack-constants dependency from composer.json
+- JITM: set the default values of the jetpack_just_in_time_msgs and jetpack_just_in_time_msg_cache filters to true.
+- JITM: Use an action instead of a property to prevent JITMs from being registered multiple times
+- JITM: Use the Device_Detection package to determine if the device is mobile.
+- Updated package dependencies
+- update jetpack-redirect dependency
+
+## [1.15.1] - 2021-05-03
+### Changed
+- JITM: Use manager::get_authorization_url to obtain the authorization url in the user deletion notice.
+
+## [1.15.0] - 2021-04-27
+### Added
+- Move JITM's REST API endpoints into the package
+
+### Changed
+- Always display pre-connection JITMs, without the need to set a filter.
+- Avoid wrapping text in the main CTA button.
+- Bump JITM package version requirement.
+- JITM: Update CTA redirect url with unlinked query arg to indicate current user is not connected.
+- Update package dependencies.
+- Use the a8c-mc-stats package to generate stats.
+
+## [1.14.1] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Use is_connected instead of is_active to instantiate Pre/Post_Connection_JITM
+- Add a jetpack_pre_connection_jitms filter.
+- Update colors to match upcoming WP 5.7 color changes
+- Update Node to match latest LTS 12
+- Update package dependencies.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.14.0] - 2021-02-23
+
+- Recommendations: Hide JITMs when banner is displaying
+- Setup Wizard: Remove setup wizard
+- JITM: move jetpack-jitm.js to the JITM package
+- CI: Make tests more generic
+
+## [1.13.5] - 2021-02-08
+
+- Update dependencies to latest stable
+
+## [1.13.4] - 2021-01-28
+
+- Update dependencies to latest stable
+
+## [1.13.3] - 2021-01-26
+
+- Update dependencies to latest stable
+
+## [1.13.2] - 2021-01-26
+
+- Update dependencies to latest stable
+
+## [1.13.1] - 2021-01-26
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.13.0] - 2021-01-05
+
+- Update dependency brain/monkey to v2.6.0
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.12.2] - 2020-12-09
+
+- Update dependencies to latest stable
+
+## [1.12.1] - 2020-11-24
+
+- Version packages for release
+
+## [1.12.0] - 2020-11-24
+
+- Status: Introduce get_site_suffix method
+- General: update minimum required version to WordPress 5.5
+- Updated PHPCS: Packages and Debugger
+
+## [1.11.2] - 2020-11-05
+
+- Update dependencies to latest stable
+
+## [1.11.1] - 2020-10-29
+
+- Update dependencies to latest stable
+
+## [1.11.0] - 2020-10-27
+
+- JITM: add a Pre_Connection_JITM::generate_admin_url method
+- JITM: use is_active from the connection package
+- JITM: regenerate assets
+
+## [1.10.4] - 2020-10-14
+
+- Update dependencies to latest stable
+
+## [1.10.3] - 2020-10-09
+
+- Update dependencies to latest stable
+
+## [1.10.2] - 2020-10-06
+
+- Update dependencies to latest stable
+
+## [1.10.1] - 2020-10-01
+
+- Update dependencies to latest stable
+
+## [1.10.0] - 2020-09-29
+
+- Update dependencies to latest stable
+
+## [1.9.1] - 2020-09-09
+
+- Update dependencies to latest stable
+
+## [1.9.0] - 2020-08-26
+
+- Compat: add new Creative Mail compat file
+- Packages: Update filenames after #16810
+- CI: Try collect js coverage
+- Docker: Add package testing shortcut
+
+## [1.8.2] - 2020-08-10
+
+- Update dependencies to latest stable
+
+## [1.8.1] - 2020-08-10
+
+- Update dependencies to latest stable
+
+## [1.8.0] - 2020-07-28
+
+- Core Compat: Site Environment
+- Core REST API: Add permission callback to delete_jitm_message endpoint
+
+## [1.7.2] - 2020-07-06
+
+- Update dependencies to latest stable
+
+## [1.7.1] - 2020-07-01
+
+- Update dependencies to latest stable
+
+## [1.7.0] - 2020-06-30
+
+- PHPCS: Clean up the packages
+- Hide pre-connection JITM on the posts page when few posts are published
+- Jetpack Setup Wizard: Do not show pre-connection JITMs to non admins
+- JITM: change 'setup' to 'set up' in pre-connection JITMs
+- Pre-connection JITMS: Link to connect-in-place flow
+- JITM: add Redirect use statement
+
+## [1.6.5] - 2020-06-01
+
+- Hide pre-connection JITM on the posts page when few posts are published
+
+## [1.6.4] - 2020-06-01
+
+- Update dependencies to latest stable
+
+## [1.6.3] - 2020-05-29
+
+- Jetpack Setup Wizard: Do not show pre-connection JITMs to non admins
+
+## [1.6.2] - 2020-05-29
+
+- JITM: change 'setup' to 'set up' in pre-connection JITMs
+- Pre-connection JITMS: Link to connect-in-place flow
+
+## [1.6.1] - 2020-05-28
+
+- JITM: add Redirect use statement
+
+## [1.6.0] - 2020-05-26
+
+- JITM: expand docs and tests to account for pre-connection messages
+- Improve responsiveness of JITMs
+- JITM: fix the use statements
+- Implement pre-connection JITMs
+- JITM: Allow JITM on stats pages
+
+## [1.5.1] - 2020-04-30
+
+- JITM: Allow JITM on stats pages
+
+## [1.5.0] - 2020-04-28
+
+- Use jp.com redirect in all links
+
+## [1.4.0] - 2020-03-31
+
+- Update dependencies to latest stable
+
+## [1.3.0] - 2020-03-31
+
+- Use dynamic Jetpack logos on JITMs
+
+## [1.2.0] - 2020-02-25
+
+- JITM: Show ToS update notice
+
+## [1.1.2] - 2020-02-14
+
+- SSO: do not display JITM when not in wp-admin
+
+## [1.1.1] - 2020-01-23
+
+- Moved JITM initialization to plugins_loaded.
+
+## [1.1.0] - 2020-01-07
+
+- Add partner subsidiary id to upgrade URLs.
+
+## [1.0.10] - 2019-11-25
+
+- Connection Owner Deletion Notice: Fix display bug and sanitize…
+
+## [1.0.9] - 2019-11-19
+
+- Don't show JITMs on Gutenberg editor pages (for now)
+
+## [1.0.8] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## [1.0.7] - 2019-11-08
+
+- Remove unused get_emblem method
+
+## [1.0.6] - 2019-10-31
+
+- Inherit 400 weight for button fonts
+
+## [1.0.5] - 2019-10-28
+
+- PHPCS: JITM and Assets packages
+
+## [1.0.4] - 2019-10-24
+
+- Update Jetpack button and card styles to match WordPress 5.3
+
+## [1.0.3] - 2019-10-23
+
+- Use spread operator instead of func_get_args
+
+## [1.0.2] - 2019-10-17
+
+- Change the class in the add_filter() calls to $this. Also fix some
+
+## [1.0.1] - 2019-09-27
+
+- Initial trial of prefer-dist
+- JITM: Send the user's role in the request for JITM messages
+
+## 1.0.0 - 2019-09-14
+
+- Update Jetpack to use new JITM package
+
+[2.2.0]: https://github.com/Automattic/jetpack-jitm/compare/v2.1.1...v2.2.0
+[2.1.1]: https://github.com/Automattic/jetpack-jitm/compare/v2.1.0...v2.1.1
+[2.1.0]: https://github.com/Automattic/jetpack-jitm/compare/v2.0.8...v2.1.0
+[2.0.8]: https://github.com/Automattic/jetpack-jitm/compare/v2.0.7...v2.0.8
+[2.0.7]: https://github.com/Automattic/jetpack-jitm/compare/v2.0.6...v2.0.7
+[2.0.6]: https://github.com/Automattic/jetpack-jitm/compare/v2.0.5...v2.0.6
+[2.0.5]: https://github.com/Automattic/jetpack-jitm/compare/v2.0.4...v2.0.5
+[2.0.4]: https://github.com/Automattic/jetpack-jitm/compare/v2.0.3...v2.0.4
+[2.0.3]: https://github.com/Automattic/jetpack-jitm/compare/v2.0.2...v2.0.3
+[2.0.2]: https://github.com/Automattic/jetpack-jitm/compare/v2.0.1...v2.0.2
+[2.0.1]: https://github.com/Automattic/jetpack-jitm/compare/v2.0.0...v2.0.1
+[2.0.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.16.2...v2.0.0
+[1.16.2]: https://github.com/Automattic/jetpack-jitm/compare/v1.16.1...v1.16.2
+[1.16.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.16.0...v1.16.1
+[1.16.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.15.1...v1.16.0
+[1.15.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.15.0...v1.15.1
+[1.15.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.14.1...v1.15.0
+[1.14.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.14.0...v1.14.1
+[1.14.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.13.5...v1.14.0
+[1.13.5]: https://github.com/Automattic/jetpack-jitm/compare/v1.13.4...v1.13.5
+[1.13.4]: https://github.com/Automattic/jetpack-jitm/compare/v1.13.3...v1.13.4
+[1.13.3]: https://github.com/Automattic/jetpack-jitm/compare/v1.13.2...v1.13.3
+[1.13.2]: https://github.com/Automattic/jetpack-jitm/compare/v1.13.1...v1.13.2
+[1.13.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.13.0...v1.13.1
+[1.13.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.12.2...v1.13.0
+[1.12.2]: https://github.com/Automattic/jetpack-jitm/compare/v1.12.1...v1.12.2
+[1.12.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.12.0...v1.12.1
+[1.12.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.11.2...v1.12.0
+[1.11.2]: https://github.com/Automattic/jetpack-jitm/compare/v1.11.1...v1.11.2
+[1.11.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.11.0...v1.11.1
+[1.11.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.10.4...v1.11.0
+[1.10.4]: https://github.com/Automattic/jetpack-jitm/compare/v1.10.3...v1.10.4
+[1.10.3]: https://github.com/Automattic/jetpack-jitm/compare/v1.10.2...v1.10.3
+[1.10.2]: https://github.com/Automattic/jetpack-jitm/compare/v1.10.1...v1.10.2
+[1.10.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.10.0...v1.10.1
+[1.10.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.9.1...v1.10.0
+[1.9.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.9.0...v1.9.1
+[1.9.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.8.2...v1.9.0
+[1.8.2]: https://github.com/Automattic/jetpack-jitm/compare/v1.8.1...v1.8.2
+[1.8.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.8.0...v1.8.1
+[1.8.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.7.2...v1.8.0
+[1.7.2]: https://github.com/Automattic/jetpack-jitm/compare/v1.7.1...v1.7.2
+[1.7.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.7.0...v1.7.1
+[1.7.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.6.5...v1.7.0
+[1.6.5]: https://github.com/Automattic/jetpack-jitm/compare/v1.6.4...v1.6.5
+[1.6.4]: https://github.com/Automattic/jetpack-jitm/compare/v1.6.3...v1.6.4
+[1.6.3]: https://github.com/Automattic/jetpack-jitm/compare/v1.6.2...v1.6.3
+[1.6.2]: https://github.com/Automattic/jetpack-jitm/compare/v1.6.1...v1.6.2
+[1.6.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.6.0...v1.6.1
+[1.6.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.5.1...v1.6.0
+[1.5.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.4.0...v1.5.0
+[1.4.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.1.2...v1.2.0
+[1.1.2]: https://github.com/Automattic/jetpack-jitm/compare/v1.1.1...v1.1.2
+[1.1.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-jitm/compare/v1.0.10...v1.1.0
+[1.0.10]: https://github.com/Automattic/jetpack-jitm/compare/v1.0.9...v1.0.10
+[1.0.9]: https://github.com/Automattic/jetpack-jitm/compare/v1.0.8...v1.0.9
+[1.0.8]: https://github.com/Automattic/jetpack-jitm/compare/v1.0.7...v1.0.8
+[1.0.7]: https://github.com/Automattic/jetpack-jitm/compare/v1.0.6...v1.0.7
+[1.0.6]: https://github.com/Automattic/jetpack-jitm/compare/v1.0.5...v1.0.6
+[1.0.5]: https://github.com/Automattic/jetpack-jitm/compare/v1.0.4...v1.0.5
+[1.0.4]: https://github.com/Automattic/jetpack-jitm/compare/v1.0.3...v1.0.4
+[1.0.3]: https://github.com/Automattic/jetpack-jitm/compare/v1.0.2...v1.0.3
+[1.0.2]: https://github.com/Automattic/jetpack-jitm/compare/v1.0.1...v1.0.2
+[1.0.1]: https://github.com/Automattic/jetpack-jitm/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.asset.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.asset.php
new file mode 100644
index 00000000..72b05048
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('wp-polyfill'), 'version' => 'dd5a50b1bccd783c463742176b0af4b9'); \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.css b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.css
new file mode 100644
index 00000000..bace1249
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.css
@@ -0,0 +1 @@
+.jitm-button{-webkit-appearance:none;appearance:none;background:#f6f7f7;border:1px solid #2271b1;border-radius:4px;box-sizing:border-box;color:#2271b1;cursor:pointer;display:inline-block;font-size:14px;margin:0;min-width:90px;outline:0;overflow:hidden;padding:7px 14px 9px;text-align:center;text-decoration:none;text-overflow:ellipsis;vertical-align:top}.jitm-button:hover{background:#f0f0f1;border-color:#0a4b78;color:#0a4b78}.jitm-button:disabled,.jitm-button[disabled]{background:#fff;border-color:#eee;color:#eee;cursor:default}.jitm-button:focus{background:#fff;border-color:#2271b1;box-shadow:0 0 0 1px #2271b1}.jitm-button.is-compact{font-size:11px;line-height:1;padding:7px;text-transform:uppercase;white-space:nowrap}.jitm-button.is-compact:disabled{color:#eee}.jitm-button.is-compact .gridicon{margin-top:-8px;top:4px}.jitm-button.is-compact .gridicons-plus-small{margin-left:-4px}.jitm-button.is-compact .gridicons-plus-small:last-of-type{margin-left:0}.jitm-button.is-compact .gridicons-plus-small+.gridicon{margin-left:-4px}.jitm-button.hidden{display:none}.jitm-button.is-primary{background:#3582c4;border-color:#3582c4;color:#fff}.jitm-button.is-primary:focus,.jitm-button.is-primary:hover{background:#2271b1;border-color:#2271b1;color:#fff}.jitm-button.is-primary:focus{box-shadow:0 0 0 1px #fff,0 0 0 3px #2271b1}.jitm-button.is-primary:disabled,.jitm-button.is-primary[disabled]{background:#bceefd;border-color:#8cc9e2;color:#fff}.jitm-button.is-primary.is-compact{color:#fff;white-space:nowrap}.jitm-card{background:#fff;box-shadow:0 0 0 1px #c3c4c7,0 1px 1px 1px rgba(0,0,0,.04);box-sizing:border-box;clear:both;display:block;margin:3rem 1.25rem 0 auto;padding:1rem;position:relative}.jitm-card:after{clear:both;content:".";display:block;height:0;visibility:hidden}@media(min-width:481px){.jitm-card{margin-bottom:1rem;padding:1.5rem}}.jitm-card.is-compact{margin-bottom:.0625rem}@media(min-width:481px){.jitm-card.is-compact{margin-bottom:1px;padding:1rem 1.5rem}}.jitm-card.is-card-link{padding-right:3rem}#screen-meta-links+.jitm-card{margin:2.5rem 1.5385em 0 auto}.post-php .jitm-card{margin-right:0}.jp-lower .jitm-card{margin:0 0 1.5rem}.jitm-banner.jitm-card{border-left:4px solid #4ab866;display:flex;padding:.75rem .375rem .75rem .75rem;position:relative;z-index:2}@media(max-width:480px){.jitm-banner.jitm-card{display:flex;flex-direction:column;padding:.75rem}}.jitm-banner.jitm-card.is-card-link{padding:.75rem 3rem .75rem 1rem}.jitm-banner.jitm-card.is-dismissible{padding-right:3rem}.jitm-banner.jitm-card .jitm-banner__icon{color:#4ab866}.jitm-banner.jitm-card .jitm-banner__icon-circle{background-color:#4ab866}.jitm-banner.jitm-card.is-upgrade-personal{border-left-color:#f0b849}.jitm-banner.jitm-card.is-upgrade-personal .jitm-banner__icon{color:#f0b849}.jitm-banner.jitm-card.is-upgrade-personal .jitm-banner__icon-circle{background-color:#f0b849}.jitm-banner.jitm-card.is-upgrade-premium{border-left-color:#4ab866}.jitm-banner.jitm-card.is-upgrade-premium .jitm-banner__icon{color:#4ab866}.jitm-banner.jitm-card.is-upgrade-premium .jitm-banner__icon-circle{background-color:#4ab866}.jitm-banner.jitm-card.is-upgrade-business,.jitm-banner.jitm-card.woo-jitm{border-left-color:#855da6}.jitm-banner.jitm-card.is-upgrade-business .jitm-banner__icon,.jitm-banner.jitm-card.woo-jitm .jitm-banner__icon{color:#855da6}.jitm-banner.jitm-card.is-upgrade-business .jitm-banner__icon-circle,.jitm-banner.jitm-card.woo-jitm .jitm-banner__icon-circle{background-color:#855da6}.jitm-banner.jitm-card .jitm-card__link-indicator{align-items:center;color:#0087be;display:flex}.jitm-banner.jitm-card:hover{transition:all .1s ease-in-out}.jitm-banner.jitm-card:hover.is-card-link{box-shadow:0 0 0 1px #a2a2a2,0 2px 4px #d5d5d5}.jitm-banner.jitm-card:hover .jitm-card__link-indicator{color:#005082}@media(min-width:481px){.jitm-banner.jitm-card{padding:.75rem 1rem}.jitm-banner.jitm-card.is-dismissible{padding-right:1rem}}.jitm-banner__buttons_container{display:grid;height:50%;margin-bottom:auto;margin-top:auto}@media(min-width:481px){.jitm-banner__buttons_container{display:flex}}.jitm-banner__icons{display:flex}.jitm-banner__icons .jitm-banner__icon,.jitm-banner__icons .jitm-banner__icon-circle{border-radius:50%;flex-shrink:0;height:1.5rem;margin-right:1rem;margin-top:-.125rem;text-align:center;top:.25rem;width:1.5rem}.jitm-banner__icons .jitm-banner__icon{align-self:center;color:#fff;display:block}.jitm-banner__icons .jitm-banner__icon-circle{color:#fff;display:none;padding:.1875rem .25rem .25rem .1875rem}@media(min-width:481px){.jitm-banner__icons{align-items:center}.jitm-banner__icons .jitm-banner__icon{display:none}.jitm-banner__icons .jitm-banner__icon-circle{display:block}}.jitm-banner__icon-plan{display:flex;margin-right:1rem}.jitm-banner__icon-plan .dops-plan-icon{height:2rem;width:2rem}.jitm-banner__icon-plan .jp-emblem{position:relative;top:.125rem}@media(max-width:480px){.jitm-banner__icon-plan .jp-emblem{margin-bottom:.75rem}}.jitm-banner__icon-plan .jp-emblem svg{fill:#069e08;height:2rem;width:2rem}.jitm-banner__icon-plan .jp-emblem .jitm-jp-logo{fill:inherit;height:inherit;width:6rem}@media(min-width:481px){.jitm-banner__icon-plan{align-items:center}}@media(max-width:960px){.jitm-banner__icon-plan{margin-bottom:10px}}.jitm-banner__content{align-items:center;display:flex;flex-grow:1;flex-wrap:wrap}@media(max-width:480px){.jitm-banner__content{margin-right:0}}@media(min-width:481px){.jitm-banner__content{flex-wrap:nowrap}}@media(max-width:960px){.jitm-banner__content{display:grid;margin-right:5px}}.jitm-banner__info{flex-grow:1;line-height:1.4}@media(min-width:481px){.jitm-banner__info{flex-basis:50%}}@media(min-width:961px){.jitm-banner__info{flex-basis:70%}}.jitm-banner__info .jitm-banner__description,.jitm-banner__info .jitm-banner__title{color:#414141}.jitm-banner__info .jitm-banner__title{font-size:14px;font-weight:500}.jitm-banner__info .jitm-banner__description{font-size:.75rem;line-height:1.5;margin-top:.375rem}.jitm-banner__info .banner__list{font-size:12px;list-style:none;margin:10px 0}.jitm-banner__info .banner__list li{margin:6px 0}.jitm-banner__info .banner__list li .gridicon{fill:#a2a2a2;display:inline;margin-right:12px;vertical-align:bottom}.jitm-banner__action{align-self:center;font-size:.75rem;margin:.5rem 0 0;text-align:left;width:100%}.jitm-banner__action .jitm-banner__prices{display:flex;justify-content:flex-start}.jitm-banner__action .jitm-banner__prices .dops-plan-price{margin-bottom:0}.jitm-banner__action .jitm-banner__prices .dops-plan-price.is-discounted,.jitm-banner__action .jitm-banner__prices .dops-plan-price.is-discounted .dops-plan-price__currency-symbol{color:#414141}.has-call-to-action .jitm-banner__action .jitm-banner__prices .dops-plan-price{margin-bottom:.5rem}@media(max-width:480px){.jitm-banner__action{margin-top:1rem}}@media(min-width:481px){.jitm-banner__action{margin:0 .25rem 0 .5rem;text-align:center;width:auto}.jitm-banner__action .is-dismissible{margin-top:2.5rem}.jitm-banner__action .jitm-banner__prices{justify-content:flex-end;text-align:right}}.jitm-banner__dismiss{display:block;line-height:.5;margin-bottom:auto;margin-top:auto;text-decoration:none}.jitm-banner__dismiss:before{color:#6f6f6f;content:"";font:400 16px/1 dashicons}@media(min-width:661px){.jitm-banner__dismiss{margin-right:-.5rem}}@media(max-width:480px){.jitm-banner__dismiss{align-items:center;display:flex;height:48px;justify-content:center;margin:0;position:absolute;right:0;top:0;width:48px}}.jitm-banner__action+.jitm-banner__dismiss{margin-left:.625rem}#dolly+.jitm-card{margin:3rem 1rem 0 auto} \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.js
new file mode 100644
index 00000000..718bf770
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.js
@@ -0,0 +1 @@
+!function(){"use strict";jQuery(document).ready((function(t){var a={default:function(a){var e='<div class="jitm-card jitm-banner '+(a.CTA.message?"has-call-to-action":"")+" is-upgrade-premium "+a.content.classes+'" data-stats_url="'+a.jitm_stats_url+'">';if(e+='<div class="jitm-banner__content">',e+='<div class="jitm-banner__icon-plan">'+a.content.icon+"</div>",e+='<div class="jitm-banner__info">',e+='<div class="jitm-banner__title">'+a.content.message+"</div>",a.content.description&&""!==a.content.description){if(e+='<div class="jitm-banner__description">'+a.content.description,a.content.list.length>0){e+='<ul class="banner__list">';for(var i=0;i<a.content.list.length;i++){var n=a.content.list[i].item;a.content.list[i].url&&(n='<a href="'+a.content.list[i].url+'" target="_blank" rel="noopener noreferrer" data-module="'+a.feature_class+'" data-jptracks-name="nudge_item_click" data-jptracks-prop="jitm-'+a.id+'">'+n+"</a>"),e+='<li><svg class="gridicon gridicons-checkmark" height="16" width="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g><path d="M9 19.414l-6.707-6.707 1.414-1.414L9 16.586 20.293 5.293l1.414 1.414" /></g></svg>'+n+"</li>"}}e+="</div>"}if(e+="</div>",e+="</div>",e+='<div class="jitm-banner__buttons_container">',a.activate_module&&(e+='<div class="jitm-banner__action" id="jitm-banner__activate">',e+='<a href="#" data-module="'+a.activate_module+'" type="button" class="jitm-button is-compact is-primary jptracks" data-jptracks-name="nudge_click" data-jptracks-prop="jitm-'+a.id+'-activate_module">'+window.jitm_config.activate_module_text+"</a>",e+="</div>"),a.CTA.message){var c="jitm-button is-compact jptracks";a.CTA.primary&&null===a.activate_module&&(c+=" is-primary");var o=a.CTA.ajax_action;e+='<div class="jitm-banner__action">',e+='<a href="'+(a.CTA.hasOwnProperty("link")&&a.CTA.link.length?a.CTA.link:a.url)+'" target="'+(!1===a.CTA.newWindow||o?"_self":"_blank")+'" rel="noopener noreferrer" title="'+a.CTA.message+'" data-module="'+a.feature_class+'" type="button" class="'+c+'" data-jptracks-name="nudge_click" data-jptracks-prop="jitm-'+a.id+'" '+(o?'data-ajax-action="'+o+'"':"")+">"+a.CTA.message+"</a>",e+="</div>"}return e+="</div>",a.is_dismissible&&(e+='<a href="#" data-module="'+a.feature_class+'" class="jitm-banner__dismiss"></a>'),t(e+="</div>")}},e=function(){t(".jetpack-jitm-message").each((function(){var e=t(this),i=e.data("message-path"),n=e.data("query"),c=e.data("redirect"),o=location.hash;"_dashboard"!==(o=o.replace(/#\//,"_"))&&(i=i.replace("toplevel_page_jetpack","toplevel_page_jetpack"+o));var r=!!t(".jetpack-logo__masthead").length;t.get(window.jitm_config.api_root+"jetpack/v4/jitm",{message_path:i,query:n,full_jp_logo_exists:r,_wpnonce:e.data("nonce")}).then((function(i){"object"==typeof i&&i[1]&&(i=[i[1]]),0!==i.length&&i[0].content&&function(e,i,n){var c;(c=i.template)&&a[c]||(c="default"),i.url=i.url+"&redirect="+n;var o,r=a[c](i);r.find(".jitm-banner__dismiss").click((o=r,function(a){a.preventDefault(),o.hide(),t.ajax({url:window.jitm_config.api_root+"jetpack/v4/jitm",method:"POST",beforeSend:function(t){t.setRequestHeader("X-WP-Nonce",window.jitm_config.nonce)},data:{id:i.id,feature_class:i.feature_class}})})),t("#jp-admin-notices").length>0?(e.innerHTML=r,t("#jp-admin-notices").find(".jitm-card")&&t(".jitm-card").replaceWith(r),r.prependTo(t("#jp-admin-notices"))):e.replaceWith(r),r.find("#jitm-banner__activate a").click((function(){var a=t(this);if(a.attr("disabled"))return!1;t.ajax({url:window.jitm_config.api_root+"jetpack/v4/module/"+a.data("module")+"/active",method:"POST",beforeSend:function(a){a.setRequestHeader("X-WP-Nonce",e.data("nonce")),t("#jitm-banner__activate a").text(window.jitm_config.activating_module_text),t("#jitm-banner__activate a").attr("disabled",!0)}}).done((function(){t("#jitm-banner__activate a").text(window.jitm_config.activated_module_text),t("#jitm-banner__activate a").attr("disabled",!0),setTimeout((function(){r.fadeOut("slow")}),2e3)}))})),r.find(".jitm-button[data-ajax-action]").click((function(a){a.preventDefault();var i=t(this);return i.attr("disabled",!0),t.post(window.ajaxurl,{action:i.data("ajax-action"),_nonce:e.data("ajax-nonce")}).done((function(){r.fadeOut("slow")})).fail((function(){i.attr("disabled",!1)})),!1}))}(e,i[0],c)}))}))};e(),t(window).bind("hashchange",(function(t){if(t.originalEvent.newURL.indexOf("jetpack#/")>=0){var a=document.querySelector(".jitm-card");a&&a.remove(),e()}}))}))}(); \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.rtl.css b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.rtl.css
new file mode 100644
index 00000000..ca5911ef
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/build/index.rtl.css
@@ -0,0 +1 @@
+.jitm-button{-webkit-appearance:none;appearance:none;background:#f6f7f7;border:1px solid #2271b1;border-radius:4px;box-sizing:border-box;color:#2271b1;cursor:pointer;display:inline-block;font-size:14px;margin:0;min-width:90px;outline:0;overflow:hidden;padding:7px 14px 9px;text-align:center;text-decoration:none;text-overflow:ellipsis;vertical-align:top}.jitm-button:hover{background:#f0f0f1;border-color:#0a4b78;color:#0a4b78}.jitm-button:disabled,.jitm-button[disabled]{background:#fff;border-color:#eee;color:#eee;cursor:default}.jitm-button:focus{background:#fff;border-color:#2271b1;box-shadow:0 0 0 1px #2271b1}.jitm-button.is-compact{font-size:11px;line-height:1;padding:7px;text-transform:uppercase;white-space:nowrap}.jitm-button.is-compact:disabled{color:#eee}.jitm-button.is-compact .gridicon{margin-top:-8px;top:4px}.jitm-button.is-compact .gridicons-plus-small{margin-right:-4px}.jitm-button.is-compact .gridicons-plus-small:last-of-type{margin-right:0}.jitm-button.is-compact .gridicons-plus-small+.gridicon{margin-right:-4px}.jitm-button.hidden{display:none}.jitm-button.is-primary{background:#3582c4;border-color:#3582c4;color:#fff}.jitm-button.is-primary:focus,.jitm-button.is-primary:hover{background:#2271b1;border-color:#2271b1;color:#fff}.jitm-button.is-primary:focus{box-shadow:0 0 0 1px #fff,0 0 0 3px #2271b1}.jitm-button.is-primary:disabled,.jitm-button.is-primary[disabled]{background:#bceefd;border-color:#8cc9e2;color:#fff}.jitm-button.is-primary.is-compact{color:#fff;white-space:nowrap}.jitm-card{background:#fff;box-shadow:0 0 0 1px #c3c4c7,0 1px 1px 1px rgba(0,0,0,.04);box-sizing:border-box;clear:both;display:block;margin:3rem auto 0 1.25rem;padding:1rem;position:relative}.jitm-card:after{clear:both;content:".";display:block;height:0;visibility:hidden}@media(min-width:481px){.jitm-card{margin-bottom:1rem;padding:1.5rem}}.jitm-card.is-compact{margin-bottom:.0625rem}@media(min-width:481px){.jitm-card.is-compact{margin-bottom:1px;padding:1rem 1.5rem}}.jitm-card.is-card-link{padding-left:3rem}#screen-meta-links+.jitm-card{margin:2.5rem auto 0 1.5385em}.post-php .jitm-card{margin-left:0}.jp-lower .jitm-card{margin:0 0 1.5rem}.jitm-banner.jitm-card{border-right:4px solid #4ab866;display:flex;padding:.75rem .75rem .75rem .375rem;position:relative;z-index:2}@media(max-width:480px){.jitm-banner.jitm-card{display:flex;flex-direction:column;padding:.75rem}}.jitm-banner.jitm-card.is-card-link{padding:.75rem 1rem .75rem 3rem}.jitm-banner.jitm-card.is-dismissible{padding-left:3rem}.jitm-banner.jitm-card .jitm-banner__icon{color:#4ab866}.jitm-banner.jitm-card .jitm-banner__icon-circle{background-color:#4ab866}.jitm-banner.jitm-card.is-upgrade-personal{border-right-color:#f0b849}.jitm-banner.jitm-card.is-upgrade-personal .jitm-banner__icon{color:#f0b849}.jitm-banner.jitm-card.is-upgrade-personal .jitm-banner__icon-circle{background-color:#f0b849}.jitm-banner.jitm-card.is-upgrade-premium{border-right-color:#4ab866}.jitm-banner.jitm-card.is-upgrade-premium .jitm-banner__icon{color:#4ab866}.jitm-banner.jitm-card.is-upgrade-premium .jitm-banner__icon-circle{background-color:#4ab866}.jitm-banner.jitm-card.is-upgrade-business,.jitm-banner.jitm-card.woo-jitm{border-right-color:#855da6}.jitm-banner.jitm-card.is-upgrade-business .jitm-banner__icon,.jitm-banner.jitm-card.woo-jitm .jitm-banner__icon{color:#855da6}.jitm-banner.jitm-card.is-upgrade-business .jitm-banner__icon-circle,.jitm-banner.jitm-card.woo-jitm .jitm-banner__icon-circle{background-color:#855da6}.jitm-banner.jitm-card .jitm-card__link-indicator{align-items:center;color:#0087be;display:flex}.jitm-banner.jitm-card:hover{transition:all .1s ease-in-out}.jitm-banner.jitm-card:hover.is-card-link{box-shadow:0 0 0 1px #a2a2a2,0 2px 4px #d5d5d5}.jitm-banner.jitm-card:hover .jitm-card__link-indicator{color:#005082}@media(min-width:481px){.jitm-banner.jitm-card{padding:.75rem 1rem}.jitm-banner.jitm-card.is-dismissible{padding-left:1rem}}.jitm-banner__buttons_container{display:grid;height:50%;margin-bottom:auto;margin-top:auto}@media(min-width:481px){.jitm-banner__buttons_container{display:flex}}.jitm-banner__icons{display:flex}.jitm-banner__icons .jitm-banner__icon,.jitm-banner__icons .jitm-banner__icon-circle{border-radius:50%;flex-shrink:0;height:1.5rem;margin-left:1rem;margin-top:-.125rem;text-align:center;top:.25rem;width:1.5rem}.jitm-banner__icons .jitm-banner__icon{align-self:center;color:#fff;display:block}.jitm-banner__icons .jitm-banner__icon-circle{color:#fff;display:none;padding:.1875rem .1875rem .25rem .25rem}@media(min-width:481px){.jitm-banner__icons{align-items:center}.jitm-banner__icons .jitm-banner__icon{display:none}.jitm-banner__icons .jitm-banner__icon-circle{display:block}}.jitm-banner__icon-plan{display:flex;margin-left:1rem}.jitm-banner__icon-plan .dops-plan-icon{height:2rem;width:2rem}.jitm-banner__icon-plan .jp-emblem{position:relative;top:.125rem}@media(max-width:480px){.jitm-banner__icon-plan .jp-emblem{margin-bottom:.75rem}}.jitm-banner__icon-plan .jp-emblem svg{fill:#069e08;height:2rem;width:2rem}.jitm-banner__icon-plan .jp-emblem .jitm-jp-logo{fill:inherit;height:inherit;width:6rem}@media(min-width:481px){.jitm-banner__icon-plan{align-items:center}}@media(max-width:960px){.jitm-banner__icon-plan{margin-bottom:10px}}.jitm-banner__content{align-items:center;display:flex;flex-grow:1;flex-wrap:wrap}@media(max-width:480px){.jitm-banner__content{margin-left:0}}@media(min-width:481px){.jitm-banner__content{flex-wrap:nowrap}}@media(max-width:960px){.jitm-banner__content{display:grid;margin-left:5px}}.jitm-banner__info{flex-grow:1;line-height:1.4}@media(min-width:481px){.jitm-banner__info{flex-basis:50%}}@media(min-width:961px){.jitm-banner__info{flex-basis:70%}}.jitm-banner__info .jitm-banner__description,.jitm-banner__info .jitm-banner__title{color:#414141}.jitm-banner__info .jitm-banner__title{font-size:14px;font-weight:500}.jitm-banner__info .jitm-banner__description{font-size:.75rem;line-height:1.5;margin-top:.375rem}.jitm-banner__info .banner__list{font-size:12px;list-style:none;margin:10px 0}.jitm-banner__info .banner__list li{margin:6px 0}.jitm-banner__info .banner__list li .gridicon{fill:#a2a2a2;display:inline;margin-left:12px;vertical-align:bottom}.jitm-banner__action{align-self:center;font-size:.75rem;margin:.5rem 0 0;text-align:right;width:100%}.jitm-banner__action .jitm-banner__prices{display:flex;justify-content:flex-start}.jitm-banner__action .jitm-banner__prices .dops-plan-price{margin-bottom:0}.jitm-banner__action .jitm-banner__prices .dops-plan-price.is-discounted,.jitm-banner__action .jitm-banner__prices .dops-plan-price.is-discounted .dops-plan-price__currency-symbol{color:#414141}.has-call-to-action .jitm-banner__action .jitm-banner__prices .dops-plan-price{margin-bottom:.5rem}@media(max-width:480px){.jitm-banner__action{margin-top:1rem}}@media(min-width:481px){.jitm-banner__action{margin:0 .5rem 0 .25rem;text-align:center;width:auto}.jitm-banner__action .is-dismissible{margin-top:2.5rem}.jitm-banner__action .jitm-banner__prices{justify-content:flex-end;text-align:left}}.jitm-banner__dismiss{display:block;line-height:.5;margin-bottom:auto;margin-top:auto;text-decoration:none}.jitm-banner__dismiss:before{color:#6f6f6f;content:"";font:400 16px/1 dashicons}@media(min-width:661px){.jitm-banner__dismiss{margin-left:-.5rem}}@media(max-width:480px){.jitm-banner__dismiss{align-items:center;display:flex;height:48px;justify-content:center;left:0;margin:0;position:absolute;top:0;width:48px}}.jitm-banner__action+.jitm-banner__dismiss{margin-right:.625rem}#dolly+.jitm-card{margin:3rem auto 0 1rem} \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-jitm.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-jitm.php
new file mode 100644
index 00000000..12577b09
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-jitm.php
@@ -0,0 +1,312 @@
+<?php
+/**
+ * Jetpack's JITM class.
+ *
+ * @package automattic/jetpack-jitm
+ */
+
+namespace Automattic\Jetpack\JITMS;
+
+use Automattic\Jetpack\Assets;
+use Automattic\Jetpack\Assets\Logo as Jetpack_Logo;
+use Automattic\Jetpack\Connection\Manager as Connection_Manager;
+use Automattic\Jetpack\Status;
+
+/**
+ * Jetpack just in time messaging through out the admin
+ *
+ * @since 1.1.0
+ * @since-jetpack 5.6.0
+ */
+class JITM {
+
+ const PACKAGE_VERSION = '2.2.0';
+
+ /**
+ * The configuration method that is called from the jetpack-config package.
+ */
+ public static function configure() {
+ $jitm = self::get_instance();
+ $jitm->register();
+ }
+
+ /**
+ * Pre/Post Connection JITM factory metod
+ *
+ * @return Post_Connection_JITM|Pre_Connection_JITM JITM instance.
+ */
+ public static function get_instance() {
+ if ( ( new Connection_Manager() )->is_connected() ) {
+ $jitm = new Post_Connection_JITM();
+ } else {
+ $jitm = new Pre_Connection_JITM();
+ }
+ return $jitm;
+ }
+
+ /**
+ * Sets up JITM action callbacks if needed.
+ */
+ public function register() {
+ if ( did_action( 'jetpack_registered_jitms' ) ) {
+ // JITMs have already been registered.
+ return;
+ }
+
+ if ( ! $this->jitms_enabled() ) {
+ // Do nothing.
+ return;
+ }
+
+ add_action( 'rest_api_init', array( __NAMESPACE__ . '\\Rest_Api_Endpoints', 'register_endpoints' ) );
+
+ add_action( 'current_screen', array( $this, 'prepare_jitms' ) );
+
+ /**
+ * These are sync actions that we need to keep track of for jitms.
+ */
+ add_filter( 'jetpack_sync_before_send_updated_option', array( $this, 'jetpack_track_last_sync_callback' ), 99 );
+
+ /**
+ * Fires when the JITMs are registered. This action is used to ensure that
+ * JITMs are registered only once.
+ *
+ * @since 1.16.0
+ */
+ do_action( 'jetpack_registered_jitms' );
+ }
+
+ /**
+ * Checks the jetpack_just_in_time_msgs filters and whether the site
+ * is offline to determine whether JITMs are enabled.
+ *
+ * @return bool True if JITMs are enabled, else false.
+ */
+ public function jitms_enabled() {
+ /**
+ * Filter to turn off all just in time messages
+ *
+ * @since 1.1.0
+ * @since-jetpack 3.7.0
+ * @since-jetpack 5.4.0 Correct docblock to reflect default arg value
+ *
+ * @param bool true Whether to show just in time messages.
+ */
+ if ( ! apply_filters( 'jetpack_just_in_time_msgs', true ) ) {
+ return false;
+ }
+
+ // Folks cannot connect to WordPress.com and won't really be able to act on the pre-connection messages. So bail.
+ if ( ( new Status() )->is_offline_mode() ) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Prepare actions according to screen and post type.
+ *
+ * @since 1.1.0
+ * @since-jetpack 3.8.2
+ *
+ * @uses Jetpack_Autoupdate::get_possible_failures()
+ *
+ * @param \WP_Screen $screen WP Core's screen object.
+ */
+ public function prepare_jitms( $screen ) {
+ /**
+ * Filter to hide JITMs on certain screens.
+ *
+ * @since 1.14.0
+ *
+ * @param bool true Whether to show just in time messages.
+ * @param string $string->id The ID of the current screen.
+ */
+ if ( apply_filters( 'jetpack_display_jitms_on_screen', true, $screen->id ) ) {
+ add_action( 'admin_enqueue_scripts', array( $this, 'jitm_enqueue_files' ) );
+ add_action( 'admin_notices', array( $this, 'ajax_message' ) );
+ add_action( 'edit_form_top', array( $this, 'ajax_message' ) );
+ }
+ }
+
+ /**
+ * Function to enqueue jitm css and js
+ */
+ public function jitm_enqueue_files() {
+ if ( $this->is_gutenberg_page() ) {
+ return;
+ }
+
+ Assets::register_script(
+ 'jetpack-jitm',
+ '../build/index.js',
+ __FILE__,
+ array(
+ 'in_footer' => true,
+ 'dependencies' => array( 'jquery' ),
+ )
+ );
+ Assets::enqueue_script( 'jetpack-jitm' );
+ wp_localize_script(
+ 'jetpack-jitm',
+ 'jitm_config',
+ array(
+ 'api_root' => esc_url_raw( rest_url() ),
+ 'activate_module_text' => esc_html__( 'Activate', 'jetpack-jitm' ),
+ 'activated_module_text' => esc_html__( 'Activated', 'jetpack-jitm' ),
+ 'activating_module_text' => esc_html__( 'Activating', 'jetpack-jitm' ),
+ 'nonce' => wp_create_nonce( 'wp_rest' ),
+ )
+ );
+ }
+
+ /**
+ * Is the current page a block editor page?
+ *
+ * @since 1.1.0
+ * @since-jetpack 8.0.0
+ */
+ public function is_gutenberg_page() {
+ $current_screen = get_current_screen();
+ return ( method_exists( $current_screen, 'is_block_editor' ) && $current_screen->is_block_editor() );
+ }
+
+ /**
+ * Get's the current message path for display of a JITM
+ *
+ * @return string The message path
+ */
+ public function get_message_path() {
+ $screen = get_current_screen();
+
+ return 'wp:' . $screen->id . ':' . current_filter();
+ }
+
+ /**
+ * Injects the dom to show a JITM inside of wp-admin.
+ */
+ public function ajax_message() {
+ if ( ! is_admin() ) {
+ return;
+ }
+
+ // do not display on Gutenberg pages.
+ if ( $this->is_gutenberg_page() ) {
+ return;
+ }
+
+ $message_path = $this->get_message_path();
+ $query_string = _http_build_query( $_GET, '', ',' ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ $current_screen = wp_unslash( $_SERVER['REQUEST_URI'] );
+ ?>
+ <div class="jetpack-jitm-message"
+ data-nonce="<?php echo esc_attr( wp_create_nonce( 'wp_rest' ) ); ?>"
+ data-ajax-nonce="<?php echo esc_attr( wp_create_nonce( 'wp_ajax_action' ) ); ?>"
+ data-message-path="<?php echo esc_attr( $message_path ); ?>"
+ data-query="<?php echo urlencode_deep( $query_string ); ?>"
+ data-redirect="<?php echo urlencode_deep( $current_screen ); ?>"
+ ></div>
+ <?php
+ }
+
+ /**
+ * Generate the icon to display on the JITM.
+ *
+ * All icons supported in this method should be included in the array returned by
+ * JITM::get_supported_icons.
+ *
+ * @param string $content_icon Icon type name.
+ * @param bool $full_jp_logo_exists Is there a big JP logo already displayed on this screen.
+ */
+ public function generate_icon( $content_icon, $full_jp_logo_exists ) {
+ switch ( $content_icon ) {
+ case 'jetpack':
+ $jetpack_logo = new Jetpack_Logo();
+ $content_icon = '<div class="jp-emblem">' . ( ( $full_jp_logo_exists ) ? $jetpack_logo->get_jp_emblem() : $jetpack_logo->get_jp_emblem_larger() ) . '</div>';
+ break;
+ case 'woocommerce':
+ $content_icon = '<div class="jp-emblem"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 168 100" xml:space="preserve" enable-background="new 0 0 168 100" width="50" height="30"><style type="text/css">
+ .st0{clip-path:url(#SVGID_2_);enable-background:new ;}
+ .st1{clip-path:url(#SVGID_4_);}
+ .st2{clip-path:url(#SVGID_6_);}
+ .st3{clip-path:url(#SVGID_8_);fill:#8F567F;}
+ .st4{clip-path:url(#SVGID_10_);fill:#FFFFFE;}
+ .st5{clip-path:url(#SVGID_12_);fill:#FFFFFE;}
+ .st6{clip-path:url(#SVGID_14_);fill:#FFFFFE;}
+ </style><g><defs><polygon id="SVGID_1_" points="83.8 100 0 100 0 0.3 83.8 0.3 167.6 0.3 167.6 100 "/></defs><clipPath id="SVGID_2_"><use xlink:href="#SVGID_1_" overflow="visible"/></clipPath><g class="st0"><g><defs><rect id="SVGID_3_" width="168" height="100"/></defs><clipPath id="SVGID_4_"><use xlink:href="#SVGID_3_" overflow="visible"/></clipPath><g class="st1"><defs><path id="SVGID_5_" d="M15.6 0.3H152c8.6 0 15.6 7 15.6 15.6v52c0 8.6-7 15.6-15.6 15.6h-48.9l6.7 16.4L80.2 83.6H15.6C7 83.6 0 76.6 0 67.9v-52C0 7.3 7 0.3 15.6 0.3"/></defs><clipPath id="SVGID_6_"><use xlink:href="#SVGID_5_" overflow="visible"/></clipPath><g class="st2"><defs><rect id="SVGID_7_" width="168" height="100"/></defs><clipPath id="SVGID_8_"><use xlink:href="#SVGID_7_" overflow="visible"/></clipPath><rect x="-10" y="-9.7" class="st3" width="187.6" height="119.7"/></g></g></g></g></g><g><defs><path id="SVGID_9_" d="M8.4 14.5c1-1.3 2.4-2 4.3-2.1 3.5-0.2 5.5 1.4 6 4.9 2.1 14.3 4.4 26.4 6.9 36.4l15-28.6c1.4-2.6 3.1-3.9 5.2-4.1 3-0.2 4.9 1.7 5.6 5.7 1.7 9.1 3.9 16.9 6.5 23.4 1.8-17.4 4.8-30 9-37.7 1-1.9 2.5-2.9 4.5-3 1.6-0.1 3 0.3 4.3 1.4 1.3 1 2 2.3 2.1 3.9 0.1 1.2-0.1 2.3-0.7 3.3 -2.7 5-4.9 13.2-6.6 24.7 -1.7 11.1-2.3 19.8-1.9 26.1 0.1 1.7-0.1 3.2-0.8 4.5 -0.8 1.5-2 2.4-3.7 2.5 -1.8 0.1-3.6-0.7-5.4-2.5C52.4 66.7 47.4 57 43.7 44.1c-4.4 8.8-7.7 15.3-9.9 19.7 -4 7.7-7.5 11.7-10.3 11.9 -1.9 0.1-3.5-1.4-4.8-4.7 -3.5-9-7.3-26.3-11.3-52C7.1 17.3 7.5 15.8 8.4 14.5"/></defs><clipPath id="SVGID_10_"><use xlink:href="#SVGID_9_" overflow="visible"/></clipPath><rect x="-2.7" y="-0.6" class="st4" width="90.6" height="86.4"/></g><g><defs><path id="SVGID_11_" d="M155.6 25.2c-2.5-4.3-6.1-6.9-11-7.9 -1.3-0.3-2.5-0.4-3.7-0.4 -6.6 0-11.9 3.4-16.1 10.2 -3.6 5.8-5.3 12.3-5.3 19.3 0 5.3 1.1 9.8 3.3 13.6 2.5 4.3 6.1 6.9 11 7.9 1.3 0.3 2.5 0.4 3.7 0.4 6.6 0 12-3.4 16.1-10.2 3.6-5.9 5.3-12.4 5.3-19.4C159 33.4 157.9 28.9 155.6 25.2zM147 44.2c-0.9 4.5-2.7 7.9-5.2 10.1 -2 1.8-3.9 2.5-5.5 2.2 -1.7-0.3-3-1.8-4-4.4 -0.8-2.1-1.2-4.2-1.2-6.2 0-1.7 0.2-3.4 0.5-5 0.6-2.8 1.8-5.5 3.6-8.1 2.3-3.3 4.7-4.8 7.1-4.2 1.7 0.3 3 1.8 4 4.4 0.8 2.1 1.2 4.2 1.2 6.2C147.5 40.9 147.3 42.6 147 44.2z"/></defs><clipPath id="SVGID_12_"><use xlink:href="#SVGID_11_" overflow="visible"/></clipPath><rect x="109.6" y="6.9" class="st5" width="59.4" height="71.4"/></g><g><defs><path id="SVGID_13_" d="M112.7 25.2c-2.5-4.3-6.1-6.9-11-7.9 -1.3-0.3-2.5-0.4-3.7-0.4 -6.6 0-11.9 3.4-16.1 10.2 -3.5 5.8-5.3 12.3-5.3 19.3 0 5.3 1.1 9.8 3.3 13.6 2.5 4.3 6.1 6.9 11 7.9 1.3 0.3 2.5 0.4 3.7 0.4 6.6 0 12-3.4 16.1-10.2 3.5-5.9 5.3-12.4 5.3-19.4C116 33.4 114.9 28.9 112.7 25.2zM104.1 44.2c-0.9 4.5-2.7 7.9-5.2 10.1 -2 1.8-3.9 2.5-5.5 2.2 -1.7-0.3-3-1.8-4-4.4 -0.8-2.1-1.2-4.2-1.2-6.2 0-1.7 0.2-3.4 0.5-5 0.6-2.8 1.8-5.5 3.6-8.1 2.3-3.3 4.7-4.8 7.1-4.2 1.7 0.3 3 1.8 4 4.4 0.8 2.1 1.2 4.2 1.2 6.2C104.6 40.9 104.4 42.6 104.1 44.2z"/></defs><clipPath id="SVGID_14_"><use xlink:href="#SVGID_13_" overflow="visible"/></clipPath><rect x="66.7" y="6.9" class="st6" width="59.4" height="71.4"/></g></svg></div>';
+ break;
+ default:
+ $content_icon = '';
+ break;
+ }
+ return $content_icon;
+ }
+
+ /**
+ * Returns an array containing the supported icons for JITMs.
+ *
+ * The list includes an empty string, which is used when no icon should be displayed.
+ *
+ * @return array The list of supported icons.
+ */
+ public function get_supported_icons() {
+ return array(
+ 'jetpack',
+ 'woocommerce',
+ '',
+ );
+ }
+
+ /**
+ * Stores dismiss data into an option
+ *
+ * @param string $key Dismiss key.
+ */
+ public function save_dismiss( $key ) {
+ $hide_jitm = \Jetpack_Options::get_option( 'hide_jitm' );
+ if ( ! is_array( $hide_jitm ) ) {
+ $hide_jitm = array();
+ }
+
+ if ( ! isset( $hide_jitm[ $key ] ) || ! is_array( $hide_jitm[ $key ] ) ) {
+ $hide_jitm[ $key ] = array(
+ 'last_dismissal' => 0,
+ 'number' => 0,
+ );
+ }
+
+ $hide_jitm[ $key ] = array(
+ 'last_dismissal' => time(),
+ 'number' => $hide_jitm[ $key ]['number'] + 1,
+ );
+
+ \Jetpack_Options::update_option( 'hide_jitm', $hide_jitm );
+ }
+
+ /**
+ * Sets the 'jetpack_last_plugin_sync' transient when the active_plugins option is synced.
+ *
+ * @param array $params The action parameters.
+ *
+ * @return array Returns the action parameters unchanged.
+ */
+ public function jetpack_track_last_sync_callback( $params ) {
+ /**
+ * This filter is documented in the Automattic\Jetpack\JITMS\Post_Connection_JITM class.
+ */
+ if ( ! apply_filters( 'jetpack_just_in_time_msg_cache', true ) ) {
+ return $params;
+ }
+
+ if ( is_array( $params ) && isset( $params[0] ) ) {
+ $option = $params[0];
+ if ( 'active_plugins' === $option ) {
+ // Use the cache if we can, but not terribly important if it gets evicted.
+ set_transient( 'jetpack_last_plugin_sync', time(), HOUR_IN_SECONDS );
+ }
+ }
+
+ return $params;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-post-connection-jitm.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-post-connection-jitm.php
new file mode 100644
index 00000000..79aaf3cf
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-post-connection-jitm.php
@@ -0,0 +1,607 @@
+<?php
+/**
+ * Jetpack's Post-Connection JITM class.
+ *
+ * @package automattic/jetpack-jitm
+ */
+
+namespace Automattic\Jetpack\JITMS;
+
+use Automattic\Jetpack\A8c_Mc_Stats;
+use Automattic\Jetpack\Connection\Client;
+use Automattic\Jetpack\Connection\Manager;
+use Automattic\Jetpack\Device_Detection;
+use Automattic\Jetpack\Partner;
+use Automattic\Jetpack\Redirect;
+use Automattic\Jetpack\Tracking;
+
+/**
+ * Jetpack just in time messaging through out the admin
+ *
+ * @since 1.1.0
+ *
+ * @since-jetpack 5.6.0
+ */
+class Post_Connection_JITM extends JITM {
+
+ /**
+ * Tracking object.
+ *
+ * @var Automattic\Jetpack\Tracking
+ *
+ * @access private
+ */
+ public $tracking;
+
+ /**
+ * JITM constructor.
+ */
+ public function __construct() {
+ $this->tracking = new Tracking();
+ }
+
+ /**
+ * Prepare actions according to screen and post type.
+ *
+ * @since 1.1.0
+ * @since-jetpack 3.8.2
+ *
+ * @uses Jetpack_Autoupdate::get_possible_failures()
+ *
+ * @param \WP_Screen $screen WP Core's screen object.
+ */
+ public function prepare_jitms( $screen ) {
+ parent::prepare_jitms( $screen );
+ if ( ! in_array(
+ $screen->id,
+ array(
+ 'jetpack_page_akismet-key-config',
+ 'admin_page_jetpack_modules',
+ ),
+ true
+ ) ) {
+ // Not really a JITM. Don't know where else to put this :) .
+ add_action( 'admin_notices', array( $this, 'delete_user_update_connection_owner_notice' ) );
+ }
+ }
+
+ /**
+ * A special filter for WooCommerce, to set a message based on local state.
+ *
+ * @param string $content The current message.
+ *
+ * @return array The new message.
+ */
+ public static function jitm_woocommerce_services_msg( $content ) {
+ if ( ! function_exists( 'wc_get_base_location' ) ) {
+ return $content;
+ }
+
+ $base_location = wc_get_base_location();
+
+ switch ( $base_location['country'] ) {
+ case 'US':
+ $content->message = esc_html__( 'New free service: Show USPS shipping rates on your store! Added bonus: print shipping labels without leaving WooCommerce.', 'jetpack-jitm' );
+ break;
+ case 'CA':
+ $content->message = esc_html__( 'New free service: Show Canada Post shipping rates on your store!', 'jetpack-jitm' );
+ break;
+ default:
+ $content->message = '';
+ }
+
+ return $content;
+ }
+
+ /**
+ * A special filter for WooCommerce Call To Action button
+ *
+ * @return string The new CTA
+ */
+ public static function jitm_jetpack_woo_services_install() {
+ return wp_nonce_url(
+ add_query_arg(
+ array(
+ 'wc-services-action' => 'install',
+ ),
+ admin_url( 'admin.php?page=wc-settings' )
+ ),
+ 'wc-services-install'
+ );
+ }
+
+ /**
+ * A special filter for WooCommerce Call To Action button.
+ *
+ * @return string The new CTA
+ */
+ public static function jitm_jetpack_woo_services_activate() {
+ return wp_nonce_url(
+ add_query_arg(
+ array(
+ 'wc-services-action' => 'activate',
+ ),
+ admin_url( 'admin.php?page=wc-settings' )
+ ),
+ 'wc-services-install'
+ );
+ }
+
+ /**
+ * A special filter used in the CTA of a JITM offering to install the Creative Mail plugin.
+ *
+ * @return string The new CTA
+ */
+ public static function jitm_jetpack_creative_mail_install() {
+ return wp_nonce_url(
+ add_query_arg(
+ array(
+ 'creative-mail-action' => 'install',
+ ),
+ admin_url( 'edit.php?post_type=feedback' )
+ ),
+ 'creative-mail-install'
+ );
+ }
+
+ /**
+ * A special filter used in the CTA of a JITM offering to activate the Creative Mail plugin.
+ *
+ * @return string The new CTA
+ */
+ public static function jitm_jetpack_creative_mail_activate() {
+ return wp_nonce_url(
+ add_query_arg(
+ array(
+ 'creative-mail-action' => 'activate',
+ ),
+ admin_url( 'edit.php?post_type=feedback' )
+ ),
+ 'creative-mail-install'
+ );
+ }
+
+ /**
+ * A special filter used in the CTA of a JITM offering to install the Jetpack Backup plugin.
+ *
+ * @return string The new CTA
+ */
+ public static function jitm_jetpack_backup_install() {
+ return wp_nonce_url(
+ add_query_arg(
+ array(
+ 'jetpack-backup-action' => 'install',
+ ),
+ admin_url( 'admin.php?page=jetpack' )
+ ),
+ 'jetpack-backup-install'
+ );
+ }
+
+ /**
+ * A special filter used in the CTA of a JITM offering to activate the Jetpack Backup plugin.
+ *
+ * @return string The new CTA
+ */
+ public static function jitm_jetpack_backup_activate() {
+ return wp_nonce_url(
+ add_query_arg(
+ array(
+ 'jetpack-backup-action' => 'activate',
+ ),
+ admin_url( 'admin.php?page=jetpack' )
+ ),
+ 'jetpack-backup-install'
+ );
+ }
+
+ /**
+ * A special filter used in the CTA of a JITM offering to install the Jetpack Boost plugin.
+ *
+ * @return string The new CTA
+ */
+ public static function jitm_jetpack_boost_install() {
+ return wp_nonce_url(
+ add_query_arg(
+ array(
+ 'jetpack-boost-action' => 'install',
+ ),
+ admin_url( 'admin.php?page=jetpack' )
+ ),
+ 'jetpack-boost-install'
+ );
+ }
+
+ /**
+ * A special filter used in the CTA of a JITM offering to activate the Jetpack Boost plugin.
+ *
+ * @return string The new CTA
+ */
+ public static function jitm_jetpack_boost_activate() {
+ return wp_nonce_url(
+ add_query_arg(
+ array(
+ 'jetpack-boost-action' => 'activate',
+ ),
+ admin_url( 'admin.php?page=jetpack' )
+ ),
+ 'jetpack-boost-install'
+ );
+ }
+
+ /**
+ * This is an entire admin notice dedicated to messaging and handling of the case where a user is trying to delete
+ * the connection owner.
+ */
+ public function delete_user_update_connection_owner_notice() {
+ global $current_screen;
+
+ /*
+ * phpcs:disable WordPress.Security.NonceVerification.Recommended
+ *
+ * This function is firing within wp-admin and checks (below) if it is in the midst of a deletion on the users
+ * page. Nonce will be already checked by WordPress, so we do not need to check ourselves.
+ */
+
+ if ( ! isset( $current_screen->base ) || 'users' !== $current_screen->base ) {
+ return;
+ }
+
+ if ( ! isset( $_REQUEST['action'] ) || 'delete' !== $_REQUEST['action'] ) {
+ return;
+ }
+
+ // Get connection owner or bail.
+ $connection_manager = new Manager();
+ $connection_owner_id = $connection_manager->get_connection_owner_id();
+ if ( ! $connection_owner_id ) {
+ return;
+ }
+ $connection_owner_userdata = get_userdata( $connection_owner_id );
+
+ // Bail if we're not trying to delete connection owner.
+ $user_ids_to_delete = array();
+ if ( isset( $_REQUEST['users'] ) ) {
+ $user_ids_to_delete = array_map( 'sanitize_text_field', wp_unslash( $_REQUEST['users'] ) );
+ } elseif ( isset( $_REQUEST['user'] ) ) {
+ $user_ids_to_delete[] = sanitize_text_field( wp_unslash( $_REQUEST['user'] ) );
+ }
+
+ // phpcs:enable
+ $user_ids_to_delete = array_map( 'absint', $user_ids_to_delete );
+ $deleting_connection_owner = in_array( $connection_owner_id, (array) $user_ids_to_delete, true );
+ if ( ! $deleting_connection_owner ) {
+ return;
+ }
+
+ // Bail if they're trying to delete themselves to avoid confusion.
+ if ( get_current_user_id() === $connection_owner_id ) {
+ return;
+ }
+
+ // Track it!
+ if ( method_exists( $this->tracking, 'record_user_event' ) ) {
+ $this->tracking->record_user_event( 'delete_connection_owner_notice_view' );
+ }
+
+ $connected_admins = $connection_manager->get_connected_users( 'jetpack_disconnect' );
+ $user = is_a( $connection_owner_userdata, 'WP_User' ) ? esc_html( $connection_owner_userdata->data->user_login ) : '';
+
+ echo "<div class='notice notice-warning' id='jetpack-notice-switch-connection-owner'>";
+ echo '<h2>' . esc_html__( 'Important notice about your Jetpack connection:', 'jetpack-jitm' ) . '</h2>';
+ echo '<p>' . sprintf(
+ /* translators: WordPress User, if available. */
+ esc_html__( 'Warning! You are about to delete the Jetpack connection owner (%s) for this site, which may cause some of your Jetpack features to stop working.', 'jetpack-jitm' ),
+ esc_html( $user )
+ ) . '</p>';
+
+ if ( ! empty( $connected_admins ) && count( $connected_admins ) > 1 ) {
+ echo '<form id="jp-switch-connection-owner" action="" method="post">';
+ echo "<label for='owner'>" . esc_html__( 'You can choose to transfer connection ownership to one of these already-connected admins:', 'jetpack-jitm' ) . ' </label>';
+
+ $connected_admin_ids = array_map(
+ function ( $connected_admin ) {
+ return $connected_admin->ID;
+ },
+ $connected_admins
+ );
+
+ wp_dropdown_users(
+ array(
+ 'name' => 'owner',
+ 'include' => array_diff( $connected_admin_ids, array( $connection_owner_id ) ),
+ 'show' => 'display_name_with_login',
+ )
+ );
+
+ echo '<p>';
+ submit_button( esc_html__( 'Set new connection owner', 'jetpack-jitm' ), 'primary', 'jp-switch-connection-owner-submit', false );
+ echo '</p>';
+
+ echo "<div id='jp-switch-user-results'></div>";
+ echo '</form>';
+ ?>
+ <script type="text/javascript">
+ jQuery( document ).ready( function( $ ) {
+ $( '#jp-switch-connection-owner' ).on( 'submit', function( e ) {
+ var formData = $( this ).serialize();
+ var submitBtn = document.getElementById( 'jp-switch-connection-owner-submit' );
+ var results = document.getElementById( 'jp-switch-user-results' );
+
+ submitBtn.disabled = true;
+
+ $.ajax( {
+ type : "POST",
+ url : "<?php echo esc_url( get_rest_url() . 'jetpack/v4/connection/owner' ); ?>",
+ data : formData,
+ headers : {
+ 'X-WP-Nonce': "<?php echo esc_js( wp_create_nonce( 'wp_rest' ) ); ?>",
+ },
+ success: function() {
+ results.innerHTML = "<?php esc_html_e( 'Success!', 'jetpack-jitm' ); ?>";
+ setTimeout( function() {
+ $( '#jetpack-notice-switch-connection-owner' ).hide( 'slow' );
+ }, 1000 );
+ }
+ } ).done( function() {
+ submitBtn.disabled = false;
+ } );
+
+ e.preventDefault();
+ return false;
+ } );
+ } );
+ </script>
+ <?php
+ } else {
+ echo '<p>' . esc_html__( 'Every Jetpack site needs at least one connected admin for the features to work properly. Please connect to your WordPress.com account via the button below. Once you connect, you may refresh this page to see an option to change the connection owner.', 'jetpack-jitm' ) . '</p>';
+ $connect_url = $connection_manager->get_authorization_url();
+ $connect_url = add_query_arg( 'from', 'delete_connection_owner_notice', $connect_url );
+ echo "<a href='" . esc_url( $connect_url ) . "' target='_blank' rel='noopener noreferrer' class='button-primary'>" . esc_html__( 'Connect to WordPress.com', 'jetpack-jitm' ) . '</a>';
+ }
+
+ echo '<p>';
+ printf(
+ wp_kses(
+ /* translators: URL to Jetpack support doc regarding the primary user. */
+ __( "<a href='%s' target='_blank' rel='noopener noreferrer'>Learn more</a> about the connection owner and what will break if you do not have one.", 'jetpack-jitm' ),
+ array(
+ 'a' => array(
+ 'href' => true,
+ 'target' => true,
+ 'rel' => true,
+ ),
+ )
+ ),
+ esc_url( Redirect::get_url( 'jetpack-support-primary-user' ) )
+ );
+ echo '</p>';
+ echo '<p>';
+ printf(
+ wp_kses(
+ /* translators: URL to contact Jetpack support. */
+ __( 'As always, feel free to <a href="%s" target="_blank" rel="noopener noreferrer">contact our support team</a> if you have any questions.', 'jetpack-jitm' ),
+ array(
+ 'a' => array(
+ 'href' => true,
+ 'target' => true,
+ 'rel' => true,
+ ),
+ )
+ ),
+ esc_url( Redirect::get_url( 'jetpack-contact-support' ) )
+ );
+ echo '</p>';
+ echo '</div>';
+ }
+
+ /**
+ * Dismisses a JITM feature class so that it will no longer be shown.
+ *
+ * @param string $id The id of the JITM that was dismissed.
+ * @param string $feature_class The feature class of the JITM that was dismissed.
+ *
+ * @return bool Always true.
+ */
+ public function dismiss( $id, $feature_class ) {
+ $this->tracking->record_user_event(
+ 'jitm_dismiss_client',
+ array(
+ 'jitm_id' => $id,
+ 'feature_class' => $feature_class,
+ )
+ );
+ $this->save_dismiss( $feature_class );
+ return true;
+ }
+
+ /**
+ * Asks the wpcom API for the current message to display keyed on query string and message path
+ *
+ * @param string $message_path The message path to ask for.
+ * @param string $query The query string originally from the front end.
+ * @param bool $full_jp_logo_exists If there is a full Jetpack logo already on the page.
+ *
+ * @return array The JITM's to show, or an empty array if there is nothing to show
+ */
+ public function get_messages( $message_path, $query, $full_jp_logo_exists ) {
+ // WooCommerce Services.
+ add_filter( 'jitm_woocommerce_services_msg', array( $this, 'jitm_woocommerce_services_msg' ) );
+ add_filter( 'jitm_jetpack_woo_services_install', array( $this, 'jitm_jetpack_woo_services_install' ) );
+ add_filter( 'jitm_jetpack_woo_services_activate', array( $this, 'jitm_jetpack_woo_services_activate' ) );
+
+ // Creative Mail.
+ add_filter( 'jitm_jetpack_creative_mail_install', array( $this, 'jitm_jetpack_creative_mail_install' ) );
+ add_filter( 'jitm_jetpack_creative_mail_activate', array( $this, 'jitm_jetpack_creative_mail_activate' ) );
+
+ // Jetpack Backup.
+ add_filter( 'jitm_jetpack_backup_install', array( $this, 'jitm_jetpack_backup_install' ) );
+ add_filter( 'jitm_jetpack_backup_activate', array( $this, 'jitm_jetpack_backup_activate' ) );
+
+ // Jetpack Boost.
+ add_filter( 'jitm_jetpack_boost_install', array( $this, 'jitm_jetpack_boost_install' ) );
+ add_filter( 'jitm_jetpack_boost_activate', array( $this, 'jitm_jetpack_boost_activate' ) );
+
+ $user = wp_get_current_user();
+
+ // Unauthenticated or invalid requests just bail.
+ if ( ! $user ) {
+ return array();
+ }
+
+ $user_roles = implode( ',', $user->roles );
+ $site_id = \Jetpack_Options::get_option( 'id' );
+
+ // Build our jitm request.
+ $path = add_query_arg(
+ array(
+ 'external_user_id' => urlencode_deep( $user->ID ),
+ 'user_roles' => urlencode_deep( $user_roles ),
+ 'query_string' => urlencode_deep( $query ),
+ 'mobile_browser' => Device_Detection::is_smartphone() ? 1 : 0,
+ '_locale' => get_user_locale(),
+ ),
+ sprintf( '/sites/%d/jitm/%s', $site_id, $message_path )
+ );
+
+ // Attempt to get from cache.
+ $envelopes = get_transient( 'jetpack_jitm_' . substr( md5( $path ), 0, 31 ) );
+
+ // If something is in the cache and it was put in the cache after the last sync we care about, use it.
+ $use_cache = false;
+
+ /**
+ * Filter to turn off jitm caching
+ *
+ * @since 1.1.0
+ * @since-jetpack 5.4.0
+ *
+ * @param bool true Whether to cache just in time messages
+ */
+ if ( apply_filters( 'jetpack_just_in_time_msg_cache', true ) ) {
+ $use_cache = true;
+ }
+
+ if ( $use_cache ) {
+ $last_sync = (int) get_transient( 'jetpack_last_plugin_sync' );
+ $from_cache = $envelopes && $last_sync > 0 && $last_sync < $envelopes['last_response_time'];
+ } else {
+ $from_cache = false;
+ }
+
+ // Otherwise, ask again.
+ if ( ! $from_cache ) {
+ $wpcom_response = Client::wpcom_json_api_request_as_blog(
+ $path,
+ '2',
+ array(
+ 'user_id' => $user->ID,
+ 'user_roles' => implode( ',', $user->roles ),
+ ),
+ null,
+ 'wpcom'
+ );
+
+ // silently fail...might be helpful to track it?
+ if ( is_wp_error( $wpcom_response ) ) {
+ return array();
+ }
+
+ $envelopes = json_decode( $wpcom_response['body'] );
+
+ if ( ! is_array( $envelopes ) ) {
+ return array();
+ }
+
+ $expiration = isset( $envelopes[0] ) ? $envelopes[0]->ttl : 300;
+
+ // Do not cache if expiration is 0 or we're not using the cache.
+ if ( 0 !== $expiration && $use_cache ) {
+ $envelopes['last_response_time'] = time();
+
+ set_transient( 'jetpack_jitm_' . substr( md5( $path ), 0, 31 ), $envelopes, $expiration );
+ }
+ }
+
+ $hidden_jitms = \Jetpack_Options::get_option( 'hide_jitm' );
+ unset( $envelopes['last_response_time'] );
+
+ /**
+ * Allow adding your own custom JITMs after a set of JITMs has been received.
+ *
+ * @since 1.1.0
+ * @since-jetpack 6.9.0
+ * @since-jetpack 8.3.0 - Added Message path.
+ *
+ * @param array $envelopes array of existing JITMs.
+ * @param string $message_path The message path to ask for.
+ */
+ $envelopes = apply_filters( 'jetpack_jitm_received_envelopes', $envelopes, $message_path );
+
+ foreach ( $envelopes as $idx => &$envelope ) {
+
+ $dismissed_feature = isset( $hidden_jitms[ $envelope->feature_class ] ) && is_array( $hidden_jitms[ $envelope->feature_class ] ) ? $hidden_jitms[ $envelope->feature_class ] : null;
+
+ // If the this feature class has been dismissed and the request has not passed the ttl, skip it as it's been dismissed.
+ if ( is_array( $dismissed_feature ) && ( time() - $dismissed_feature['last_dismissal'] < $envelope->expires || $dismissed_feature['number'] >= $envelope->max_dismissal ) ) {
+ unset( $envelopes[ $idx ] );
+ continue;
+ }
+
+ $this->tracking->record_user_event(
+ 'jitm_view_client',
+ array(
+ 'jitm_id' => $envelope->id,
+ )
+ );
+
+ $url_params = array(
+ 'u' => $user->ID,
+ );
+
+ // Get affiliate code and add it to the array of URL parameters.
+ $aff = Partner::init()->get_partner_code( Partner::AFFILIATE_CODE );
+ if ( '' !== $aff ) {
+ $url_params['aff'] = $aff;
+ }
+
+ // Check if the current user has connected their WP.com account
+ // and if not add this information to the the array of URL parameters.
+ if ( ! ( new Manager() )->is_user_connected( $user->ID ) ) {
+ $url_params['query'] = 'unlinked=1';
+ }
+ $envelope->url = esc_url( Redirect::get_url( "jitm-$envelope->id", $url_params ) );
+
+ $stats = new A8c_Mc_Stats();
+
+ $envelope->jitm_stats_url = $stats->build_stats_url( array( 'x_jetpack-jitm' => $envelope->id ) );
+
+ // phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
+ // $CTA is not valid per PHPCS, but it is part of the return from WordPress.com, so allowing.
+ if ( $envelope->CTA->hook ) {
+ $envelope->url = apply_filters( 'jitm_' . $envelope->CTA->hook, $envelope->url );
+ unset( $envelope->CTA->hook );
+ }
+ // phpcs:enable
+
+ if ( isset( $envelope->content->hook ) ) {
+ $envelope->content = apply_filters( 'jitm_' . $envelope->content->hook, $envelope->content );
+ unset( $envelope->content->hook );
+ }
+
+ // No point in showing an empty message.
+ if ( empty( $envelope->content->message ) ) {
+ unset( $envelopes[ $idx ] );
+ continue;
+ }
+
+ $envelope->content->icon = $this->generate_icon( $envelope->content->icon, $full_jp_logo_exists );
+
+ $stats->add( 'jitm', $envelope->id . '-viewed' );
+ $stats->do_server_side_stats();
+ }
+
+ return $envelopes;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-pre-connection-jitm.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-pre-connection-jitm.php
new file mode 100644
index 00000000..6a779873
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-pre-connection-jitm.php
@@ -0,0 +1,171 @@
+<?php
+/**
+ * Jetpack's Pre-Connection JITM class.
+ *
+ * @package automattic/jetpack-jitm
+ */
+
+namespace Automattic\Jetpack\JITMS;
+
+/**
+ * Jetpack pre-connection just in time messaging through out the admin.
+ */
+class Pre_Connection_JITM extends JITM {
+
+ /**
+ * Filters and formats the messages for the client-side JS renderer
+ *
+ * @param string $message_path Current message path.
+ *
+ * @return array Formatted messages.
+ */
+ private function filter_messages( $message_path ) {
+ /**
+ * Allows filtering of the pre-connection JITMs.
+ *
+ * This filter allows plugins to add pre-connection JITMs that will be
+ * displayed by the JITM package.
+ *
+ * @since 1.14.1
+ *
+ * @param array An array of pre-connection messages.
+ */
+ $messages = apply_filters( 'jetpack_pre_connection_jitms', array() );
+
+ $messages = $this->validate_messages( $messages );
+
+ $formatted_messages = array();
+
+ foreach ( $messages as $message ) {
+ if ( ! preg_match( $message['message_path'], $message_path ) ) {
+ continue;
+ }
+
+ $obj = new \stdClass();
+ $obj->CTA = array( // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
+ 'message' => $message['button_caption'],
+ 'newWindow' => false,
+ );
+ $obj->url = $message['button_link'];
+ $obj->id = $message['id'];
+ $obj->is_dismissible = true;
+ $obj->content = array(
+ 'message' => $message['message'],
+ 'description' => $message['description'],
+ 'list' => array(),
+ 'icon' => $this->get_message_icon( $message ),
+ );
+
+ $formatted_messages[] = $obj;
+ }
+
+ return $formatted_messages;
+ }
+
+ /**
+ * Validates that each of the messages contains all of the required keys:
+ * - id
+ * - message_path
+ * - message
+ * - description
+ * - button_link
+ * - button_caption
+ *
+ * @param array $messages An array of JITM messages.
+ *
+ * @return array An array of JITM messages that contain all of the required keys.
+ */
+ private function validate_messages( $messages ) {
+ if ( ! is_array( $messages ) ) {
+ return array();
+ }
+
+ $expected_keys = array_flip( array( 'id', 'message_path', 'message', 'description', 'button_link', 'button_caption' ) );
+
+ foreach ( $messages as $index => $message ) {
+ if ( count( array_intersect_key( $expected_keys, $message ) ) !== count( $expected_keys ) ) {
+ // Remove any messages that are missing expected keys.
+ unset( $messages[ $index ] );
+ }
+ }
+
+ return $messages;
+ }
+
+ /**
+ * Get the icon for the message.
+ *
+ * The message may contain an 'icon' key. If the value of the 'icon' key matches a supported icon (or empty string), the value is used.
+ * If the message does not contain an icon key or if the value does not match a supported icon, the Jetpack icon is used by default.
+ *
+ * @param array $message A pre-connection JITM.
+ *
+ * @return string The icon to use in the JITM.
+ */
+ private function get_message_icon( $message ) {
+ // Default to the Jetpack icon.
+ $icon = 'jetpack';
+
+ if ( ! isset( $message['icon'] ) ) {
+ return $icon;
+ }
+
+ $supported_icons = $this->get_supported_icons();
+
+ if ( in_array( $message['icon'], $supported_icons, true ) ) {
+ // Only use the message icon if it's a supported icon or an empty string (for no icon).
+ $icon = $message['icon'];
+ }
+
+ return $icon;
+ }
+
+ /**
+ * Retrieve the current message to display keyed on query string and message path
+ *
+ * @param string $message_path The message path to ask for.
+ * @param string $query The query string originally from the front end. Unused in this subclass.
+ * @param bool $full_jp_logo_exists If there is a full Jetpack logo already on the page.
+ *
+ * @return array The JITMs to show, or an empty array if there is nothing to show
+ */
+ public function get_messages( $message_path, $query, $full_jp_logo_exists ) {
+ if ( ! current_user_can( 'install_plugins' ) ) {
+ return array();
+ }
+
+ $messages = $this->filter_messages( $message_path );
+
+ if ( empty( $messages ) ) {
+ return array();
+ }
+
+ $hidden_jitms = \Jetpack_Options::get_option( 'hide_jitm' );
+
+ foreach ( $messages as $idx => &$envelope ) {
+ $dismissed_feature = isset( $hidden_jitms[ 'pre-connection-' . $envelope->id ] ) &&
+ is_array( $hidden_jitms[ 'pre-connection-' . $envelope->id ] ) ? $hidden_jitms[ 'pre-connection-' . $envelope->id ] : null;
+
+ if ( is_array( $dismissed_feature ) ) {
+ unset( $messages[ $idx ] );
+ continue;
+ }
+
+ $envelope->content['icon'] = $this->generate_icon( $envelope->content['icon'], $full_jp_logo_exists );
+ }
+
+ return $messages;
+ }
+
+ /**
+ * Dismisses a JITM ID so that it will no longer be shown.
+ *
+ * @param string $id The id of the JITM that was dismissed.
+ *
+ * @return bool Always true
+ */
+ public function dismiss( $id ) {
+ $this->save_dismiss( 'pre-connection-' . $id );
+ return true;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-rest-api-endpoints.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-rest-api-endpoints.php
new file mode 100644
index 00000000..2fa3d2bf
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/class-rest-api-endpoints.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * JITM's REST API Endpoints
+ *
+ * @package automattic/jetpack-jitm
+ */
+
+namespace Automattic\Jetpack\JITMS;
+
+use Automattic\Jetpack\Connection\REST_Connector;
+use WP_REST_Server;
+
+/**
+ * Register the JITM's REST API Endpoints and their callbacks.
+ */
+class Rest_Api_Endpoints {
+
+ /**
+ * Declare the JITM's REST API endpoints.
+ */
+ public static function register_endpoints() {
+
+ register_rest_route(
+ 'jetpack/v4',
+ '/jitm',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::get_jitm_message',
+ 'permission_callback' => '__return_true',
+ )
+ );
+
+ register_rest_route(
+ 'jetpack/v4',
+ '/jitm',
+ array(
+ 'methods' => WP_REST_Server::CREATABLE,
+ 'callback' => __CLASS__ . '::delete_jitm_message',
+ 'permission_callback' => __CLASS__ . '::delete_jitm_message_permission_callback',
+ )
+ );
+
+ }
+
+ /**
+ * Asks for a jitm, unless they've been disabled, in which case it returns an empty array
+ *
+ * @param WP_REST_Request $request The request object.
+ *
+ * @return array An array of jitms
+ */
+ public static function get_jitm_message( $request ) {
+ $jitm = JITM::get_instance();
+
+ if ( ! $jitm->jitms_enabled() ) {
+ return array();
+ }
+
+ return $jitm->get_messages( $request['message_path'], urldecode_deep( $request['query'] ), 'true' === $request['full_jp_logo_exists'] ? true : false );
+ }
+
+ /**
+ * Dismisses a jitm.
+ *
+ * @param WP_REST_Request $request The request object.
+ *
+ * @return bool Always True
+ */
+ public static function delete_jitm_message( $request ) {
+ $jitm = JITM::get_instance();
+
+ if ( ! $jitm->jitms_enabled() ) {
+ return true;
+ }
+
+ return $jitm->dismiss( $request['id'], $request['feature_class'] );
+ }
+
+ /**
+ * Verify that the user can dismiss JITM messages.
+ *
+ * @return bool|WP_Error True if user is able to dismiss JITM messages.
+ */
+ public static function delete_jitm_message_permission_callback() {
+ if ( current_user_can( 'read' ) ) {
+ return true;
+ }
+
+ return new \WP_Error( 'invalid_user_permission_jetpack_delete_jitm_message', REST_Connector::get_user_permissions_error_msg(), array( 'status' => rest_authorization_required_code() ) );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/js/jetpack-jitm.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/js/jetpack-jitm.js
new file mode 100644
index 00000000..785a38a7
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-jitm/src/js/jetpack-jitm.js
@@ -0,0 +1,267 @@
+import '../css/jetpack-admin-jitm.scss';
+
+jQuery( document ).ready( function ( $ ) {
+ var templates = {
+ default: function ( envelope ) {
+ var html =
+ '<div class="jitm-card jitm-banner ' +
+ ( envelope.CTA.message ? 'has-call-to-action' : '' ) +
+ ' is-upgrade-premium ' +
+ envelope.content.classes +
+ '" data-stats_url="' +
+ envelope.jitm_stats_url +
+ '">';
+ html += '<div class="jitm-banner__content">';
+ html += '<div class="jitm-banner__icon-plan">' + envelope.content.icon + '</div>';
+ html += '<div class="jitm-banner__info">';
+ html += '<div class="jitm-banner__title">' + envelope.content.message + '</div>';
+ if ( envelope.content.description && envelope.content.description !== '' ) {
+ html += '<div class="jitm-banner__description">' + envelope.content.description;
+ if ( envelope.content.list.length > 0 ) {
+ html += '<ul class="banner__list">';
+ for ( var i = 0; i < envelope.content.list.length; i++ ) {
+ var text = envelope.content.list[ i ].item;
+
+ if ( envelope.content.list[ i ].url ) {
+ text =
+ '<a href="' +
+ envelope.content.list[ i ].url +
+ '" target="_blank" rel="noopener noreferrer" data-module="' +
+ envelope.feature_class +
+ '" data-jptracks-name="nudge_item_click" data-jptracks-prop="jitm-' +
+ envelope.id +
+ '">' +
+ text +
+ '</a>';
+ }
+
+ html +=
+ '<li>' +
+ '<svg class="gridicon gridicons-checkmark" height="16" width="16" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><g>' +
+ '<path d="M9 19.414l-6.707-6.707 1.414-1.414L9 16.586 20.293 5.293l1.414 1.414" /></g></svg>' +
+ text +
+ '</li>';
+ }
+ }
+ html += '</div>';
+ }
+ html += '</div>';
+ html += '</div>';
+
+ html += '<div class="jitm-banner__buttons_container">';
+
+ if ( envelope.activate_module ) {
+ html += '<div class="jitm-banner__action" id="jitm-banner__activate">';
+ html +=
+ '<a href="#" data-module="' +
+ envelope.activate_module +
+ '" type="button" class="jitm-button is-compact is-primary jptracks" data-jptracks-name="nudge_click" data-jptracks-prop="jitm-' +
+ envelope.id +
+ '-activate_module">' +
+ window.jitm_config.activate_module_text +
+ '</a>';
+ html += '</div>';
+ }
+ if ( envelope.CTA.message ) {
+ var ctaClasses = 'jitm-button is-compact jptracks';
+ if ( envelope.CTA.primary && null === envelope.activate_module ) {
+ ctaClasses += ' is-primary';
+ }
+
+ var ajaxAction = envelope.CTA.ajax_action;
+
+ html += '<div class="jitm-banner__action">';
+ html +=
+ '<a href="' +
+ ( envelope.CTA.hasOwnProperty( 'link' ) && envelope.CTA.link.length ? envelope.CTA.link : envelope.url ) +
+ '" target="' +
+ ( envelope.CTA.newWindow === false || ajaxAction ? '_self' : '_blank' ) +
+ '" rel="noopener noreferrer" title="' +
+ envelope.CTA.message +
+ '" data-module="' +
+ envelope.feature_class +
+ '" type="button" class="' +
+ ctaClasses +
+ '" data-jptracks-name="nudge_click" data-jptracks-prop="jitm-' +
+ envelope.id +
+ '" ' +
+ ( ajaxAction ? 'data-ajax-action="' + ajaxAction + '"' : '' ) +
+ '>' +
+ envelope.CTA.message +
+ '</a>';
+ html += '</div>';
+ }
+
+ html += '</div>';
+
+ if ( envelope.is_dismissible ) {
+ html +=
+ '<a href="#" data-module="' +
+ envelope.feature_class +
+ '" class="jitm-banner__dismiss"></a>';
+ }
+ html += '</div>';
+
+ return $( html );
+ },
+ };
+
+ var setJITMContent = function ( $el, response, redirect ) {
+ var template;
+
+ var render = function ( $my_template ) {
+ return function ( e ) {
+ e.preventDefault();
+
+ $my_template.hide();
+
+ $.ajax( {
+ url: window.jitm_config.api_root + 'jetpack/v4/jitm',
+ method: 'POST', // using DELETE without permalinks is broken in default nginx configuration
+ beforeSend: function ( xhr ) {
+ xhr.setRequestHeader( 'X-WP-Nonce', window.jitm_config.nonce );
+ },
+ data: {
+ id: response.id,
+ feature_class: response.feature_class,
+ },
+ } );
+ };
+ };
+
+ template = response.template;
+
+ // if we don't have a template for this version, just use the default template
+ if ( ! template || ! templates[ template ] ) {
+ template = 'default';
+ }
+
+ response.url = response.url + '&redirect=' + redirect;
+
+ var $template = templates[ template ]( response );
+ $template.find( '.jitm-banner__dismiss' ).click( render( $template ) );
+
+ if ( $( '#jp-admin-notices' ).length > 0 ) {
+ // Add to Jetpack notices within the Jetpack settings app.
+ $el.innerHTML = $template;
+
+ // If we already have a message, replace it.
+ if ( $( '#jp-admin-notices' ).find( '.jitm-card' ) ) {
+ $( '.jitm-card' ).replaceWith( $template );
+ }
+
+ // No existing JITM? Add ours to the top of the Jetpack admin notices.
+ $template.prependTo( $( '#jp-admin-notices' ) );
+ } else {
+ // Replace placeholder div on other pages.
+ $el.replaceWith( $template );
+ }
+
+ // Handle Module activation button if it exists.
+ $template.find( '#jitm-banner__activate a' ).click( function () {
+ var $activate_button = $( this );
+
+ // Do not allow any requests if the button is disabled.
+ if ( $activate_button.attr( 'disabled' ) ) {
+ return false;
+ }
+
+ // Make request to activate module.
+ $.ajax( {
+ url:
+ window.jitm_config.api_root +
+ 'jetpack/v4/module/' +
+ $activate_button.data( 'module' ) +
+ '/active',
+ method: 'POST',
+ beforeSend: function ( xhr ) {
+ xhr.setRequestHeader( 'X-WP-Nonce', $el.data( 'nonce' ) );
+
+ // Change the button status to disabled as the change is in progress.
+ $( '#jitm-banner__activate a' ).text( window.jitm_config.activating_module_text );
+ $( '#jitm-banner__activate a' ).attr( 'disabled', true );
+ },
+ } ).done( function () {
+ $( '#jitm-banner__activate a' ).text( window.jitm_config.activated_module_text );
+ $( '#jitm-banner__activate a' ).attr( 'disabled', true );
+
+ // Hide the JITM after 2 seconds.
+ setTimeout( function () {
+ $template.fadeOut( 'slow' );
+ }, 2000 );
+ } );
+ } );
+
+ // Handle CTA ajax actions.
+ $template.find( '.jitm-button[data-ajax-action]' ).click( function ( e ) {
+ e.preventDefault();
+ var button = $( this );
+ button.attr( 'disabled', true );
+ $.post( window.ajaxurl, {
+ action: button.data( 'ajax-action' ),
+ _nonce: $el.data( 'ajax-nonce' ),
+ } )
+ .done( function () {
+ $template.fadeOut( 'slow' );
+ } )
+ .fail( function () {
+ button.attr( 'disabled', false );
+ } );
+ return false;
+ } );
+ };
+
+ var reFetch = function () {
+ $( '.jetpack-jitm-message' ).each( function () {
+ var $el = $( this );
+
+ var message_path = $el.data( 'message-path' );
+ var query = $el.data( 'query' );
+ var redirect = $el.data( 'redirect' );
+ var hash = location.hash;
+
+ hash = hash.replace( /#\//, '_' );
+ if ( '_dashboard' !== hash ) {
+ message_path = message_path.replace(
+ 'toplevel_page_jetpack',
+ 'toplevel_page_jetpack' + hash
+ );
+ }
+
+ var full_jp_logo_exists = $( '.jetpack-logo__masthead' ).length ? true : false;
+
+ $.get( window.jitm_config.api_root + 'jetpack/v4/jitm', {
+ message_path: message_path,
+ query: query,
+ full_jp_logo_exists: full_jp_logo_exists,
+ _wpnonce: $el.data( 'nonce' ),
+ } ).then( function ( response ) {
+ if ( 'object' === typeof response && response[ '1' ] ) {
+ response = [ response[ '1' ] ];
+ }
+
+ // properly handle the case of an empty array or no content set
+ if ( 0 === response.length || ! response[ 0 ].content ) {
+ return;
+ }
+
+ // for now, always take the first response
+ setJITMContent( $el, response[ 0 ], redirect );
+ } );
+ } );
+ };
+
+ reFetch();
+
+ $( window ).bind( 'hashchange', function ( e ) {
+ var newURL = e.originalEvent.newURL;
+
+ if ( newURL.indexOf( 'jetpack#/' ) >= 0 ) {
+ var jitm_card = document.querySelector( '.jitm-card' );
+ if ( jitm_card ) {
+ jitm_card.remove();
+ }
+ reFetch();
+ }
+ } );
+} );
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/CHANGELOG.md
new file mode 100644
index 00000000..fbeb8c83
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/CHANGELOG.md
@@ -0,0 +1,189 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [2.1.0] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies.
+- Updated package textdomain from `jetpack` to `jetpack-lazy-images`.
+
+## [2.0.10] - 2021-12-14
+
+## [2.0.9] - 2021-11-30
+### Changed
+- Remove `.min` from built JS.
+- Updated package dependencies.
+
+## [2.0.8] - 2021-11-22
+### Changed
+- Updated package dependencies
+
+## [2.0.7] - 2021-11-17
+### Changed
+- Updated package dependencies.
+
+## [2.0.6] - 2021-11-16
+### Added
+- Use monorepo `validate-es` script to validate Webpack builds.
+
+### Changed
+- Updated package dependencies.
+
+## [2.0.5] - 2021-11-09
+### Changed
+- Update webpack build config.
+
+## [2.0.4] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [2.0.3] - 2021-10-19
+### Changed
+- Updated package dependencies.
+
+### Deprecated
+- General: remove numerous long-deprecated functions.
+
+## [2.0.2] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [2.0.1] - 2021-09-28
+### Changed
+- Allow Node ^14.17.6 to be used in this project. This shouldn't change the behavior of the code itself.
+- Updated package dependencies.
+
+## [2.0.0] - 2021-08-31
+### Changed
+- Run composer update on test-php command instead of phpunit.
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- Update annotations versions.
+- Update to latest webpack, webpack-cli and calypso-build.
+- Use Node 16.7.0 in tooling. This shouldn't change the behavior of the code itself.
+
+### Removed
+- Removed IE11 support.
+
+## [1.5.1] - 2021-08-10
+### Changed
+- Updated package dependencies
+
+## [1.5.0] - 2021-06-29
+### Changed
+- Build using calypso-build, and use the intersection-observer npm module instead of bundling a copy.
+- Update docs to replace yarn with pnpm.
+- Update node version requirement to 14.16.1
+
+## [1.4.4] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.4.3] - 2021-04-27
+### Changed
+- Updated package dependencies
+
+## [1.4.2] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Update icon file used for tests, WP 5.7 no longer silences exif errors.
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.4.1] - 2021-02-23
+
+- Update dependency webpack to v4.46.0
+- Update dependency webpack-cli to v4.5.0
+- CI: Make tests more generic
+- CLI: Add install command
+
+## [1.4.0] - 2021-01-26
+
+- Mirroring: Move build command into composer
+- Mirroring: Fix vendor copy of lazy-images in jetpack-production
+- Add mirror-repo information to all current composer packages
+- Lazy Images: do not include js linting config in production
+- Monorepo: Reorganize all projects
+- Various PHPCS and Cleanup
+
+## [1.3.0] - 2021-01-05
+
+- Lazy-Images: Downgrade to ES5
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+- Reorganize composer scripts
+- General: update minimum required version to WordPress 5.5
+- Codecoverage: fix reports
+- Updated PHPCS: Packages and Debugger
+
+## [1.2.2] - 2020-12-09
+
+- Update dependencies to latest stable
+- Updated dependencies to latest stable
+
+## [1.2.1] - 2020-11-24
+
+- General: update minimum required version to WordPress 5.5
+- Codecoverage: fix reports
+- Updated PHPCS: Packages and Debugger
+
+## [1.2.0] - 2020-10-27
+
+- Lazy Images: Use a better name for wp_localize_script's l10n object
+- Lazy Images: Start linting lazy-images.js
+
+## [1.1.3] - 2020-12-09
+
+- Update dependencies to latest stable
+
+## [1.1.2] - 2020-11-24
+
+- Version packages for release
+
+## [1.1.1] - 2020-11-10
+
+- Update dependencies to latest stable
+
+## [1.1.0] - 2020-09-29
+
+- Consolidate the Lazy Images package to rely on the Assets package
+
+## 1.0.0 - 2020-08-25
+
+- Lazy Images: Move into a package
+
+[2.1.0]: https://github.com/Automattic/jetpack-lazy-images/compare/v2.0.10...v2.1.0
+[2.0.10]: https://github.com/Automattic/jetpack-lazy-images/compare/v2.0.9...v2.0.10
+[2.0.9]: https://github.com/Automattic/jetpack-lazy-images/compare/v2.0.8...v2.0.9
+[2.0.8]: https://github.com/Automattic/jetpack-lazy-images/compare/v2.0.7...v2.0.8
+[2.0.7]: https://github.com/Automattic/jetpack-lazy-images/compare/v2.0.6...v2.0.7
+[2.0.6]: https://github.com/Automattic/jetpack-lazy-images/compare/v2.0.5...v2.0.6
+[2.0.5]: https://github.com/Automattic/jetpack-lazy-images/compare/v2.0.4...v2.0.5
+[2.0.4]: https://github.com/Automattic/jetpack-lazy-images/compare/v2.0.3...v2.0.4
+[2.0.3]: https://github.com/Automattic/jetpack-lazy-images/compare/v2.0.2...v2.0.3
+[2.0.2]: https://github.com/Automattic/jetpack-lazy-images/compare/v2.0.1...v2.0.2
+[2.0.1]: https://github.com/Automattic/jetpack-lazy-images/compare/v2.0.0...v2.0.1
+[2.0.0]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.5.1...v2.0.0
+[1.5.1]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.4.4...v1.5.0
+[1.4.4]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.4.3...v1.4.4
+[1.4.3]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.4.2...v1.4.3
+[1.4.2]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.4.1...v1.4.2
+[1.4.1]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.2.2...v1.3.0
+[1.2.2]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.2.1...v1.2.2
+[1.2.1]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.2.0...v1.2.1
+[1.2.0]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.1.3...v1.2.0
+[1.1.3]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.1.2...v1.1.3
+[1.1.2]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.1.1...v1.1.2
+[1.1.1]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-lazy-images/compare/v1.0.0...v1.1.0
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/intersection-observer.asset.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/intersection-observer.asset.php
new file mode 100644
index 00000000..d56f1d1f
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/intersection-observer.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array(), 'version' => '6ae6d91d4b64fe31e32c2797e7c3dd42'); \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/intersection-observer.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/intersection-observer.js
new file mode 100644
index 00000000..5fa4e6e7
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/intersection-observer.js
@@ -0,0 +1 @@
+!function(){"use strict";if("object"==typeof window)if("IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype)"isIntersecting"in window.IntersectionObserverEntry.prototype||Object.defineProperty(window.IntersectionObserverEntry.prototype,"isIntersecting",{get:function(){return this.intersectionRatio>0}});else{var t=function(t){for(var e=window.document,o=i(e);o;)o=i(e=o.ownerDocument);return e}(),e=[],o=null,n=null;s.prototype.THROTTLE_TIMEOUT=100,s.prototype.POLL_INTERVAL=null,s.prototype.USE_MUTATION_OBSERVER=!0,s._setupCrossOriginUpdater=function(){return o||(o=function(t,o){n=t&&o?l(t,o):{top:0,bottom:0,left:0,right:0,width:0,height:0},e.forEach((function(t){t._checkForIntersections()}))}),o},s._resetCrossOriginUpdater=function(){o=null,n=null},s.prototype.observe=function(t){if(!this._observationTargets.some((function(e){return e.element==t}))){if(!t||1!=t.nodeType)throw new Error("target must be an Element");this._registerInstance(),this._observationTargets.push({element:t,entry:null}),this._monitorIntersections(t.ownerDocument),this._checkForIntersections()}},s.prototype.unobserve=function(t){this._observationTargets=this._observationTargets.filter((function(e){return e.element!=t})),this._unmonitorIntersections(t.ownerDocument),0==this._observationTargets.length&&this._unregisterInstance()},s.prototype.disconnect=function(){this._observationTargets=[],this._unmonitorAllIntersections(),this._unregisterInstance()},s.prototype.takeRecords=function(){var t=this._queuedEntries.slice();return this._queuedEntries=[],t},s.prototype._initThresholds=function(t){var e=t||[0];return Array.isArray(e)||(e=[e]),e.sort().filter((function(t,e,o){if("number"!=typeof t||isNaN(t)||t<0||t>1)throw new Error("threshold must be a number between 0 and 1 inclusively");return t!==o[e-1]}))},s.prototype._parseRootMargin=function(t){var e=(t||"0px").split(/\s+/).map((function(t){var e=/^(-?\d*\.?\d+)(px|%)$/.exec(t);if(!e)throw new Error("rootMargin must be specified in pixels or percent");return{value:parseFloat(e[1]),unit:e[2]}}));return e[1]=e[1]||e[0],e[2]=e[2]||e[0],e[3]=e[3]||e[1],e},s.prototype._monitorIntersections=function(e){var o=e.defaultView;if(o&&-1==this._monitoringDocuments.indexOf(e)){var n=this._checkForIntersections,r=null,s=null;this.POLL_INTERVAL?r=o.setInterval(n,this.POLL_INTERVAL):(h(o,"resize",n,!0),h(e,"scroll",n,!0),this.USE_MUTATION_OBSERVER&&"MutationObserver"in o&&(s=new o.MutationObserver(n)).observe(e,{attributes:!0,childList:!0,characterData:!0,subtree:!0})),this._monitoringDocuments.push(e),this._monitoringUnsubscribes.push((function(){var t=e.defaultView;t&&(r&&t.clearInterval(r),c(t,"resize",n,!0)),c(e,"scroll",n,!0),s&&s.disconnect()}));var u=this.root&&(this.root.ownerDocument||this.root)||t;if(e!=u){var a=i(e);a&&this._monitorIntersections(a.ownerDocument)}}},s.prototype._unmonitorIntersections=function(e){var o=this._monitoringDocuments.indexOf(e);if(-1!=o){var n=this.root&&(this.root.ownerDocument||this.root)||t,r=this._observationTargets.some((function(t){var o=t.element.ownerDocument;if(o==e)return!0;for(;o&&o!=n;){var r=i(o);if((o=r&&r.ownerDocument)==e)return!0}return!1}));if(!r){var s=this._monitoringUnsubscribes[o];if(this._monitoringDocuments.splice(o,1),this._monitoringUnsubscribes.splice(o,1),s(),e!=n){var h=i(e);h&&this._unmonitorIntersections(h.ownerDocument)}}}},s.prototype._unmonitorAllIntersections=function(){var t=this._monitoringUnsubscribes.slice(0);this._monitoringDocuments.length=0,this._monitoringUnsubscribes.length=0;for(var e=0;e<t.length;e++)t[e]()},s.prototype._checkForIntersections=function(){if(this.root||!o||n){var t=this._rootIsInDom(),e=t?this._getRootRect():{top:0,bottom:0,left:0,right:0,width:0,height:0};this._observationTargets.forEach((function(n){var i=n.element,s=u(i),h=this._rootContainsTarget(i),c=n.entry,a=t&&h&&this._computeTargetAndRootIntersection(i,s,e),l=null;this._rootContainsTarget(i)?o&&!this.root||(l=e):l={top:0,bottom:0,left:0,right:0,width:0,height:0};var f=n.entry=new r({time:window.performance&&performance.now&&performance.now(),target:i,boundingClientRect:s,rootBounds:l,intersectionRect:a});c?t&&h?this._hasCrossedThreshold(c,f)&&this._queuedEntries.push(f):c&&c.isIntersecting&&this._queuedEntries.push(f):this._queuedEntries.push(f)}),this),this._queuedEntries.length&&this._callback(this.takeRecords(),this)}},s.prototype._computeTargetAndRootIntersection=function(e,i,r){if("none"!=window.getComputedStyle(e).display){for(var s,h,c,a,f,d,g,m,v=i,_=p(e),b=!1;!b&&_;){var w=null,y=1==_.nodeType?window.getComputedStyle(_):{};if("none"==y.display)return null;if(_==this.root||9==_.nodeType)if(b=!0,_==this.root||_==t)o&&!this.root?!n||0==n.width&&0==n.height?(_=null,w=null,v=null):w=n:w=r;else{var I=p(_),E=I&&u(I),T=I&&this._computeTargetAndRootIntersection(I,E,r);E&&T?(_=I,w=l(E,T)):(_=null,v=null)}else{var R=_.ownerDocument;_!=R.body&&_!=R.documentElement&&"visible"!=y.overflow&&(w=u(_))}if(w&&(s=w,h=v,c=void 0,a=void 0,f=void 0,d=void 0,g=void 0,m=void 0,c=Math.max(s.top,h.top),a=Math.min(s.bottom,h.bottom),f=Math.max(s.left,h.left),d=Math.min(s.right,h.right),m=a-c,v=(g=d-f)>=0&&m>=0&&{top:c,bottom:a,left:f,right:d,width:g,height:m}||null),!v)break;_=_&&p(_)}return v}},s.prototype._getRootRect=function(){var e;if(this.root&&!d(this.root))e=u(this.root);else{var o=d(this.root)?this.root:t,n=o.documentElement,i=o.body;e={top:0,left:0,right:n.clientWidth||i.clientWidth,width:n.clientWidth||i.clientWidth,bottom:n.clientHeight||i.clientHeight,height:n.clientHeight||i.clientHeight}}return this._expandRectByRootMargin(e)},s.prototype._expandRectByRootMargin=function(t){var e=this._rootMarginValues.map((function(e,o){return"px"==e.unit?e.value:e.value*(o%2?t.width:t.height)/100})),o={top:t.top-e[0],right:t.right+e[1],bottom:t.bottom+e[2],left:t.left-e[3]};return o.width=o.right-o.left,o.height=o.bottom-o.top,o},s.prototype._hasCrossedThreshold=function(t,e){var o=t&&t.isIntersecting?t.intersectionRatio||0:-1,n=e.isIntersecting?e.intersectionRatio||0:-1;if(o!==n)for(var i=0;i<this.thresholds.length;i++){var r=this.thresholds[i];if(r==o||r==n||r<o!=r<n)return!0}},s.prototype._rootIsInDom=function(){return!this.root||f(t,this.root)},s.prototype._rootContainsTarget=function(e){var o=this.root&&(this.root.ownerDocument||this.root)||t;return f(o,e)&&(!this.root||o==e.ownerDocument)},s.prototype._registerInstance=function(){e.indexOf(this)<0&&e.push(this)},s.prototype._unregisterInstance=function(){var t=e.indexOf(this);-1!=t&&e.splice(t,1)},window.IntersectionObserver=s,window.IntersectionObserverEntry=r}function i(t){try{return t.defaultView&&t.defaultView.frameElement||null}catch(t){return null}}function r(t){this.time=t.time,this.target=t.target,this.rootBounds=a(t.rootBounds),this.boundingClientRect=a(t.boundingClientRect),this.intersectionRect=a(t.intersectionRect||{top:0,bottom:0,left:0,right:0,width:0,height:0}),this.isIntersecting=!!t.intersectionRect;var e=this.boundingClientRect,o=e.width*e.height,n=this.intersectionRect,i=n.width*n.height;this.intersectionRatio=o?Number((i/o).toFixed(4)):this.isIntersecting?1:0}function s(t,e){var o,n,i,r=e||{};if("function"!=typeof t)throw new Error("callback must be a function");if(r.root&&1!=r.root.nodeType&&9!=r.root.nodeType)throw new Error("root must be a Document or Element");this._checkForIntersections=(o=this._checkForIntersections.bind(this),n=this.THROTTLE_TIMEOUT,i=null,function(){i||(i=setTimeout((function(){o(),i=null}),n))}),this._callback=t,this._observationTargets=[],this._queuedEntries=[],this._rootMarginValues=this._parseRootMargin(r.rootMargin),this.thresholds=this._initThresholds(r.threshold),this.root=r.root||null,this.rootMargin=this._rootMarginValues.map((function(t){return t.value+t.unit})).join(" "),this._monitoringDocuments=[],this._monitoringUnsubscribes=[]}function h(t,e,o,n){"function"==typeof t.addEventListener?t.addEventListener(e,o,n||!1):"function"==typeof t.attachEvent&&t.attachEvent("on"+e,o)}function c(t,e,o,n){"function"==typeof t.removeEventListener?t.removeEventListener(e,o,n||!1):"function"==typeof t.detatchEvent&&t.detatchEvent("on"+e,o)}function u(t){var e;try{e=t.getBoundingClientRect()}catch(t){}return e?(e.width&&e.height||(e={top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:e.right-e.left,height:e.bottom-e.top}),e):{top:0,bottom:0,left:0,right:0,width:0,height:0}}function a(t){return!t||"x"in t?t:{top:t.top,y:t.top,bottom:t.bottom,left:t.left,x:t.left,right:t.right,width:t.width,height:t.height}}function l(t,e){var o=e.top-t.top,n=e.left-t.left;return{top:o,left:n,height:e.height,width:e.width,bottom:o+e.height,right:n+e.width}}function f(t,e){for(var o=e;o;){if(o==t)return!0;o=p(o)}return!1}function p(e){var o=e.parentNode;return 9==e.nodeType&&e!=t?i(e):(o&&o.assignedSlot&&(o=o.assignedSlot.parentNode),o&&11==o.nodeType&&o.host?o.host:o)}function d(t){return t&&9===t.nodeType}}(); \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/intersection-observer.src.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/intersection-observer.src.js
new file mode 100644
index 00000000..5fa4e6e7
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/intersection-observer.src.js
@@ -0,0 +1 @@
+!function(){"use strict";if("object"==typeof window)if("IntersectionObserver"in window&&"IntersectionObserverEntry"in window&&"intersectionRatio"in window.IntersectionObserverEntry.prototype)"isIntersecting"in window.IntersectionObserverEntry.prototype||Object.defineProperty(window.IntersectionObserverEntry.prototype,"isIntersecting",{get:function(){return this.intersectionRatio>0}});else{var t=function(t){for(var e=window.document,o=i(e);o;)o=i(e=o.ownerDocument);return e}(),e=[],o=null,n=null;s.prototype.THROTTLE_TIMEOUT=100,s.prototype.POLL_INTERVAL=null,s.prototype.USE_MUTATION_OBSERVER=!0,s._setupCrossOriginUpdater=function(){return o||(o=function(t,o){n=t&&o?l(t,o):{top:0,bottom:0,left:0,right:0,width:0,height:0},e.forEach((function(t){t._checkForIntersections()}))}),o},s._resetCrossOriginUpdater=function(){o=null,n=null},s.prototype.observe=function(t){if(!this._observationTargets.some((function(e){return e.element==t}))){if(!t||1!=t.nodeType)throw new Error("target must be an Element");this._registerInstance(),this._observationTargets.push({element:t,entry:null}),this._monitorIntersections(t.ownerDocument),this._checkForIntersections()}},s.prototype.unobserve=function(t){this._observationTargets=this._observationTargets.filter((function(e){return e.element!=t})),this._unmonitorIntersections(t.ownerDocument),0==this._observationTargets.length&&this._unregisterInstance()},s.prototype.disconnect=function(){this._observationTargets=[],this._unmonitorAllIntersections(),this._unregisterInstance()},s.prototype.takeRecords=function(){var t=this._queuedEntries.slice();return this._queuedEntries=[],t},s.prototype._initThresholds=function(t){var e=t||[0];return Array.isArray(e)||(e=[e]),e.sort().filter((function(t,e,o){if("number"!=typeof t||isNaN(t)||t<0||t>1)throw new Error("threshold must be a number between 0 and 1 inclusively");return t!==o[e-1]}))},s.prototype._parseRootMargin=function(t){var e=(t||"0px").split(/\s+/).map((function(t){var e=/^(-?\d*\.?\d+)(px|%)$/.exec(t);if(!e)throw new Error("rootMargin must be specified in pixels or percent");return{value:parseFloat(e[1]),unit:e[2]}}));return e[1]=e[1]||e[0],e[2]=e[2]||e[0],e[3]=e[3]||e[1],e},s.prototype._monitorIntersections=function(e){var o=e.defaultView;if(o&&-1==this._monitoringDocuments.indexOf(e)){var n=this._checkForIntersections,r=null,s=null;this.POLL_INTERVAL?r=o.setInterval(n,this.POLL_INTERVAL):(h(o,"resize",n,!0),h(e,"scroll",n,!0),this.USE_MUTATION_OBSERVER&&"MutationObserver"in o&&(s=new o.MutationObserver(n)).observe(e,{attributes:!0,childList:!0,characterData:!0,subtree:!0})),this._monitoringDocuments.push(e),this._monitoringUnsubscribes.push((function(){var t=e.defaultView;t&&(r&&t.clearInterval(r),c(t,"resize",n,!0)),c(e,"scroll",n,!0),s&&s.disconnect()}));var u=this.root&&(this.root.ownerDocument||this.root)||t;if(e!=u){var a=i(e);a&&this._monitorIntersections(a.ownerDocument)}}},s.prototype._unmonitorIntersections=function(e){var o=this._monitoringDocuments.indexOf(e);if(-1!=o){var n=this.root&&(this.root.ownerDocument||this.root)||t,r=this._observationTargets.some((function(t){var o=t.element.ownerDocument;if(o==e)return!0;for(;o&&o!=n;){var r=i(o);if((o=r&&r.ownerDocument)==e)return!0}return!1}));if(!r){var s=this._monitoringUnsubscribes[o];if(this._monitoringDocuments.splice(o,1),this._monitoringUnsubscribes.splice(o,1),s(),e!=n){var h=i(e);h&&this._unmonitorIntersections(h.ownerDocument)}}}},s.prototype._unmonitorAllIntersections=function(){var t=this._monitoringUnsubscribes.slice(0);this._monitoringDocuments.length=0,this._monitoringUnsubscribes.length=0;for(var e=0;e<t.length;e++)t[e]()},s.prototype._checkForIntersections=function(){if(this.root||!o||n){var t=this._rootIsInDom(),e=t?this._getRootRect():{top:0,bottom:0,left:0,right:0,width:0,height:0};this._observationTargets.forEach((function(n){var i=n.element,s=u(i),h=this._rootContainsTarget(i),c=n.entry,a=t&&h&&this._computeTargetAndRootIntersection(i,s,e),l=null;this._rootContainsTarget(i)?o&&!this.root||(l=e):l={top:0,bottom:0,left:0,right:0,width:0,height:0};var f=n.entry=new r({time:window.performance&&performance.now&&performance.now(),target:i,boundingClientRect:s,rootBounds:l,intersectionRect:a});c?t&&h?this._hasCrossedThreshold(c,f)&&this._queuedEntries.push(f):c&&c.isIntersecting&&this._queuedEntries.push(f):this._queuedEntries.push(f)}),this),this._queuedEntries.length&&this._callback(this.takeRecords(),this)}},s.prototype._computeTargetAndRootIntersection=function(e,i,r){if("none"!=window.getComputedStyle(e).display){for(var s,h,c,a,f,d,g,m,v=i,_=p(e),b=!1;!b&&_;){var w=null,y=1==_.nodeType?window.getComputedStyle(_):{};if("none"==y.display)return null;if(_==this.root||9==_.nodeType)if(b=!0,_==this.root||_==t)o&&!this.root?!n||0==n.width&&0==n.height?(_=null,w=null,v=null):w=n:w=r;else{var I=p(_),E=I&&u(I),T=I&&this._computeTargetAndRootIntersection(I,E,r);E&&T?(_=I,w=l(E,T)):(_=null,v=null)}else{var R=_.ownerDocument;_!=R.body&&_!=R.documentElement&&"visible"!=y.overflow&&(w=u(_))}if(w&&(s=w,h=v,c=void 0,a=void 0,f=void 0,d=void 0,g=void 0,m=void 0,c=Math.max(s.top,h.top),a=Math.min(s.bottom,h.bottom),f=Math.max(s.left,h.left),d=Math.min(s.right,h.right),m=a-c,v=(g=d-f)>=0&&m>=0&&{top:c,bottom:a,left:f,right:d,width:g,height:m}||null),!v)break;_=_&&p(_)}return v}},s.prototype._getRootRect=function(){var e;if(this.root&&!d(this.root))e=u(this.root);else{var o=d(this.root)?this.root:t,n=o.documentElement,i=o.body;e={top:0,left:0,right:n.clientWidth||i.clientWidth,width:n.clientWidth||i.clientWidth,bottom:n.clientHeight||i.clientHeight,height:n.clientHeight||i.clientHeight}}return this._expandRectByRootMargin(e)},s.prototype._expandRectByRootMargin=function(t){var e=this._rootMarginValues.map((function(e,o){return"px"==e.unit?e.value:e.value*(o%2?t.width:t.height)/100})),o={top:t.top-e[0],right:t.right+e[1],bottom:t.bottom+e[2],left:t.left-e[3]};return o.width=o.right-o.left,o.height=o.bottom-o.top,o},s.prototype._hasCrossedThreshold=function(t,e){var o=t&&t.isIntersecting?t.intersectionRatio||0:-1,n=e.isIntersecting?e.intersectionRatio||0:-1;if(o!==n)for(var i=0;i<this.thresholds.length;i++){var r=this.thresholds[i];if(r==o||r==n||r<o!=r<n)return!0}},s.prototype._rootIsInDom=function(){return!this.root||f(t,this.root)},s.prototype._rootContainsTarget=function(e){var o=this.root&&(this.root.ownerDocument||this.root)||t;return f(o,e)&&(!this.root||o==e.ownerDocument)},s.prototype._registerInstance=function(){e.indexOf(this)<0&&e.push(this)},s.prototype._unregisterInstance=function(){var t=e.indexOf(this);-1!=t&&e.splice(t,1)},window.IntersectionObserver=s,window.IntersectionObserverEntry=r}function i(t){try{return t.defaultView&&t.defaultView.frameElement||null}catch(t){return null}}function r(t){this.time=t.time,this.target=t.target,this.rootBounds=a(t.rootBounds),this.boundingClientRect=a(t.boundingClientRect),this.intersectionRect=a(t.intersectionRect||{top:0,bottom:0,left:0,right:0,width:0,height:0}),this.isIntersecting=!!t.intersectionRect;var e=this.boundingClientRect,o=e.width*e.height,n=this.intersectionRect,i=n.width*n.height;this.intersectionRatio=o?Number((i/o).toFixed(4)):this.isIntersecting?1:0}function s(t,e){var o,n,i,r=e||{};if("function"!=typeof t)throw new Error("callback must be a function");if(r.root&&1!=r.root.nodeType&&9!=r.root.nodeType)throw new Error("root must be a Document or Element");this._checkForIntersections=(o=this._checkForIntersections.bind(this),n=this.THROTTLE_TIMEOUT,i=null,function(){i||(i=setTimeout((function(){o(),i=null}),n))}),this._callback=t,this._observationTargets=[],this._queuedEntries=[],this._rootMarginValues=this._parseRootMargin(r.rootMargin),this.thresholds=this._initThresholds(r.threshold),this.root=r.root||null,this.rootMargin=this._rootMarginValues.map((function(t){return t.value+t.unit})).join(" "),this._monitoringDocuments=[],this._monitoringUnsubscribes=[]}function h(t,e,o,n){"function"==typeof t.addEventListener?t.addEventListener(e,o,n||!1):"function"==typeof t.attachEvent&&t.attachEvent("on"+e,o)}function c(t,e,o,n){"function"==typeof t.removeEventListener?t.removeEventListener(e,o,n||!1):"function"==typeof t.detatchEvent&&t.detatchEvent("on"+e,o)}function u(t){var e;try{e=t.getBoundingClientRect()}catch(t){}return e?(e.width&&e.height||(e={top:e.top,right:e.right,bottom:e.bottom,left:e.left,width:e.right-e.left,height:e.bottom-e.top}),e):{top:0,bottom:0,left:0,right:0,width:0,height:0}}function a(t){return!t||"x"in t?t:{top:t.top,y:t.top,bottom:t.bottom,left:t.left,x:t.left,right:t.right,width:t.width,height:t.height}}function l(t,e){var o=e.top-t.top,n=e.left-t.left;return{top:o,left:n,height:e.height,width:e.width,bottom:o+e.height,right:n+e.width}}function f(t,e){for(var o=e;o;){if(o==t)return!0;o=p(o)}return!1}function p(e){var o=e.parentNode;return 9==e.nodeType&&e!=t?i(e):(o&&o.assignedSlot&&(o=o.assignedSlot.parentNode),o&&11==o.nodeType&&o.host?o.host:o)}function d(t){return t&&9===t.nodeType}}(); \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/lazy-images.asset.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/lazy-images.asset.php
new file mode 100644
index 00000000..13eb125f
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/lazy-images.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array(), 'version' => '22d8bb42bcd6edc9c8ef53eb9b087d17'); \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/lazy-images.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/lazy-images.js
new file mode 100644
index 00000000..d606a494
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/dist/lazy-images.js
@@ -0,0 +1 @@
+!function(){var e=function(){var e,t,n,a={rootMargin:"200px 0px",threshold:.01},i=[];d();var r=document.querySelector("body");function d(){e=[].slice.call(document.querySelectorAll("img.jetpack-lazy-image:not(.jetpack-lazy-image--handled)")),n&&n.disconnect(),"IntersectionObserver"in window?(n=new IntersectionObserver(l,a),e.forEach((function(e){e.getAttribute("data-lazy-loaded")||n.observe(e)})),window.addEventListener("beforeprint",c),window.matchMedia&&window.matchMedia("print").addListener((function(e){e.matches&&c()}))):o()}function o(){for(n&&n.disconnect();e.length>0;)s(e[0])}function l(t){for(var a=0;a<t.length;a++){var i=t[a];i.intersectionRatio>0&&(n.unobserve(i.target),s(i.target))}0===e.length&&n.disconnect()}function c(){if(!t&&(e.length>0||i.length>0)){(t=document.createElement("div")).id="loadingWarning",t.style.fontWeight="bold",t.innerText=jetpackLazyImagesL10n.loading_warning;var n=document.createElement("style");n.innerHTML="#loadingWarning { display: none; }\n@media print {\n#loadingWarning { display: block; }\nbody > #loadingWarning ~ * { display: none !important; }\n}",t.appendChild(n),r.insertBefore(t,r.firstChild)}e.length>0&&o(),t&&alert(jetpackLazyImagesL10n.loading_warning)}function s(t){var n;if(t instanceof HTMLImageElement){var a=t.getAttribute("data-lazy-srcset"),r=t.getAttribute("data-lazy-sizes");t.removeAttribute("data-lazy-srcset"),t.removeAttribute("data-lazy-sizes"),t.removeAttribute("data-lazy-src"),t.classList.add("jetpack-lazy-image--handled"),t.setAttribute("data-lazy-loaded",1),r&&t.setAttribute("sizes",r),a?t.setAttribute("srcset",a):t.removeAttribute("srcset"),t.setAttribute("loading","eager"),i.push(t);var d=e.indexOf(t);d>=0&&e.splice(d,1),t.complete?g.call(t,null):(t.addEventListener("load",g,{once:!0}),t.addEventListener("error",g,{once:!0}));try{n=new Event("jetpack-lazy-loaded-image",{bubbles:!0,cancelable:!0})}catch(e){(n=document.createEvent("Event")).initEvent("jetpack-lazy-loaded-image",!0,!0)}t.dispatchEvent(n)}}function g(){var n=i.indexOf(this);n>=0&&i.splice(n,1),t&&0===e.length&&0===i.length&&(t.parentNode.removeChild(t),t=null)}r&&(r.addEventListener("is.post-load",d),r.addEventListener("jetpack-lazy-images-load",d))};"interactive"===document.readyState||"complete"===document.readyState?e():document.addEventListener("DOMContentLoaded",e)}(); \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/src/images/1x1.trans.gif b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/src/images/1x1.trans.gif
new file mode 100644
index 00000000..f191b280
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/src/images/1x1.trans.gif
Binary files differ
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/src/js/lazy-images.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/src/js/lazy-images.js
new file mode 100644
index 00000000..92b4f217
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/src/js/lazy-images.js
@@ -0,0 +1,219 @@
+/* global jetpackLazyImagesL10n */
+
+var jetpackLazyImagesModule = function () {
+ var config = {
+ // If the image gets within 200px in the Y axis, start the download.
+ rootMargin: '200px 0px',
+ threshold: 0.01,
+ };
+ var loadingImages = [];
+ var lazyImages, loadingWarning, observer;
+
+ lazy_load_init();
+
+ var bodyEl = document.querySelector( 'body' );
+ if ( bodyEl ) {
+ // Lazy load images that are brought in from Infinite Scroll
+ bodyEl.addEventListener( 'is.post-load', lazy_load_init );
+
+ // Add event to provide optional compatibility for other code.
+ bodyEl.addEventListener( 'jetpack-lazy-images-load', lazy_load_init );
+ }
+
+ /**
+ * Initialize the module.
+ */
+ function lazy_load_init() {
+ // @todo: Use Array.from once es6 is allowed.
+ lazyImages = [].slice.call(
+ document.querySelectorAll( 'img.jetpack-lazy-image:not(.jetpack-lazy-image--handled)' )
+ );
+
+ // If initialized, then disconnect the observer
+ if ( observer ) {
+ observer.disconnect();
+ }
+
+ // If we don't have support for intersection observer, loads the images immediately
+ if ( ! ( 'IntersectionObserver' in window ) ) {
+ loadAllImages();
+ } else {
+ // It is supported, load the images
+ observer = new IntersectionObserver( onIntersection, config );
+
+ lazyImages.forEach( function ( image ) {
+ if ( ! image.getAttribute( 'data-lazy-loaded' ) ) {
+ observer.observe( image );
+ }
+ } );
+
+ // Watch for attempts to print, and load all images. Most browsers
+ // support beforeprint, Safari needs a media listener. Doesn't hurt
+ // to double-fire if a browser supports both.
+ window.addEventListener( 'beforeprint', onPrint );
+ if ( window.matchMedia ) {
+ window.matchMedia( 'print' ).addListener( function ( mql ) {
+ if ( mql.matches ) {
+ onPrint();
+ }
+ } );
+ }
+ }
+ }
+
+ /**
+ * Load all of the images immediately
+ */
+ function loadAllImages() {
+ if ( observer ) {
+ observer.disconnect();
+ }
+
+ while ( lazyImages.length > 0 ) {
+ applyImage( lazyImages[ 0 ] );
+ }
+ }
+
+ /**
+ * On intersection
+ *
+ * @param {Array} entries - List of elements being observed.
+ */
+ function onIntersection( entries ) {
+ // Loop through the entries
+ for ( var i = 0; i < entries.length; i++ ) {
+ var entry = entries[ i ];
+
+ // Are we in viewport?
+ if ( entry.intersectionRatio > 0 ) {
+ // Stop watching and load the image
+ observer.unobserve( entry.target );
+ applyImage( entry.target );
+ }
+ }
+
+ // Disconnect if we've already loaded all of the images
+ if ( lazyImages.length === 0 ) {
+ observer.disconnect();
+ }
+ }
+
+ /**
+ * On print
+ */
+ function onPrint() {
+ if ( ! loadingWarning && ( lazyImages.length > 0 || loadingImages.length > 0 ) ) {
+ // Replace the printed page with a notice that images are still loading.
+ // Hopefully the user won't actually print this, but if they do at least it'll not
+ // waste too much ink.
+ loadingWarning = document.createElement( 'div' );
+ loadingWarning.id = 'loadingWarning';
+ loadingWarning.style.fontWeight = 'bold';
+ loadingWarning.innerText = jetpackLazyImagesL10n.loading_warning;
+
+ var s = document.createElement( 'style' );
+ s.innerHTML =
+ '#loadingWarning { display: none; }\n@media print {\n#loadingWarning { display: block; }\nbody > #loadingWarning ~ * { display: none !important; }\n}';
+ loadingWarning.appendChild( s );
+
+ bodyEl.insertBefore( loadingWarning, bodyEl.firstChild );
+ }
+
+ if ( lazyImages.length > 0 ) {
+ loadAllImages();
+ }
+
+ // May as well try an alert() too. The browser may block it, but if not
+ // it could save them some trouble.
+ if ( loadingWarning ) {
+ alert( jetpackLazyImagesL10n.loading_warning );
+ }
+ }
+
+ /**
+ * Apply the image
+ *
+ * @param {Element} image - The image object.
+ */
+ function applyImage( image ) {
+ var lazyLoadedImageEvent;
+
+ if ( ! ( image instanceof HTMLImageElement ) ) {
+ return;
+ }
+
+ var srcset = image.getAttribute( 'data-lazy-srcset' );
+ var sizes = image.getAttribute( 'data-lazy-sizes' );
+
+ // Remove lazy attributes.
+ image.removeAttribute( 'data-lazy-srcset' );
+ image.removeAttribute( 'data-lazy-sizes' );
+ image.removeAttribute( 'data-lazy-src' );
+
+ // Add the attributes we want.
+ image.classList.add( 'jetpack-lazy-image--handled' );
+ image.setAttribute( 'data-lazy-loaded', 1 );
+
+ if ( sizes ) {
+ image.setAttribute( 'sizes', sizes );
+ }
+
+ if ( ! srcset ) {
+ image.removeAttribute( 'srcset' );
+ } else {
+ image.setAttribute( 'srcset', srcset );
+ }
+
+ // Force eager loading, otherwise the browser-native loading=lazy support will still
+ // prevent the loading.
+ image.setAttribute( 'loading', 'eager' );
+
+ loadingImages.push( image );
+ var idx = lazyImages.indexOf( image );
+ if ( idx >= 0 ) {
+ lazyImages.splice( idx, 1 );
+ }
+
+ if ( image.complete ) {
+ loadedImage.call( image, null );
+ } else {
+ image.addEventListener( 'load', loadedImage, { once: true } );
+ image.addEventListener( 'error', loadedImage, { once: true } );
+ }
+
+ // Fire an event so that third-party code can perform actions after an image is loaded.
+ try {
+ lazyLoadedImageEvent = new Event( 'jetpack-lazy-loaded-image', {
+ bubbles: true,
+ cancelable: true,
+ } );
+ } catch ( e ) {
+ lazyLoadedImageEvent = document.createEvent( 'Event' );
+ lazyLoadedImageEvent.initEvent( 'jetpack-lazy-loaded-image', true, true );
+ }
+
+ image.dispatchEvent( lazyLoadedImageEvent );
+ }
+
+ /**
+ * An image from applyImage() finished loading.
+ */
+ function loadedImage() {
+ var idx = loadingImages.indexOf( this );
+ if ( idx >= 0 ) {
+ loadingImages.splice( idx, 1 );
+ }
+
+ if ( loadingWarning && lazyImages.length === 0 && loadingImages.length === 0 ) {
+ loadingWarning.parentNode.removeChild( loadingWarning );
+ loadingWarning = null;
+ }
+ }
+};
+
+// Let's kick things off now
+if ( document.readyState === 'interactive' || document.readyState === 'complete' ) {
+ jetpackLazyImagesModule();
+} else {
+ document.addEventListener( 'DOMContentLoaded', jetpackLazyImagesModule );
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/src/lazy-images.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/src/lazy-images.php
new file mode 100644
index 00000000..8be08ed5
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/src/lazy-images.php
@@ -0,0 +1,516 @@
+<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
+
+/**
+ * The Lazy Images package.
+ *
+ * @since 1.0.0
+ * @since-jetpack 8.8.0
+ *
+ * This package has been lifted from the Jetpack modules folder and adapted to be
+ * an standalone package reusable outside Jetpack.
+ *
+ * @package automattic/jetpack-lazy-images
+ */
+
+/**
+ * This package relies heavily upon the Lazy Load plugin which was worked on by
+ * Mohammad Jangda (batmoo), the WordPress.com VIP team, the TechCrunch 2011
+ * redesign team, and Jake Goldman of 10up LLC.
+ *
+ * The JavaScript has been updated to rely on InterSection observer instead of
+ * jQuery Sonar. Many thanks to Dean Hume (deanhume) and his example:
+ * https://github.com/deanhume/lazy-observer-load
+ */
+
+namespace Automattic\Jetpack;
+
+/**
+ * Class Automattic\Jetpack\Jetpack_Lazy_Images
+ *
+ * @since 1.0.0
+ * @since-jetpack 8.8.0
+ */
+class Jetpack_Lazy_Images {
+
+ /**
+ * Class instance.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @var null
+ */
+ private static $instance = null;
+
+ /**
+ * Singleton implementation.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @return object The class instance.
+ */
+ public static function instance() {
+ if ( is_null( self::$instance ) ) {
+ self::$instance = new Jetpack_Lazy_Images();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Check if the request is a AMP request.
+ *
+ * @since 1.0.0
+ * @since-jetpack 8.8.0
+ *
+ * @return bool
+ */
+ public static function is_amp_request() {
+ $is_amp_request = ( function_exists( 'is_amp_endpoint' ) && is_amp_endpoint() );
+
+ /**
+ * Returns true if the current request should return valid AMP content.
+ *
+ * @since 1.0.0
+ * @since-jetpack 8.8.0
+ *
+ * @param boolean $is_amp_request Is this request supposed to return valid AMP content?
+ */
+ return apply_filters( 'jetpack_lazy_images_is_amp_request', $is_amp_request );
+ }
+
+ /**
+ * Registers actions.
+ *
+ * @since 1.0.0
+ * @since-jetpack 8.8.0
+ *
+ * @return void
+ */
+ private function __construct() {
+ if ( is_admin() ) {
+ return;
+ }
+
+ /**
+ * Whether the lazy-images module should load.
+ *
+ * This filter is not prefixed with jetpack_ to provide a smoother migration
+ * process from the WordPress Lazy Load plugin.
+ *
+ * @package automattic/jetpack-lazy-images
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @param bool true Whether lazy image loading should occur.
+ */
+ if ( ! apply_filters( 'lazyload_is_enabled', true ) ) {
+ return;
+ }
+
+ if ( self::is_amp_request() ) {
+ return;
+ }
+
+ add_action( 'wp_head', array( $this, 'setup_filters' ), 9999 ); // We don't really want to modify anything in <head> since it's mostly all metadata.
+ add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
+
+ // Do not lazy load avatar in admin bar.
+ add_action( 'admin_bar_menu', array( $this, 'remove_filters' ), 0 );
+
+ add_filter( 'wp_kses_allowed_html', array( $this, 'allow_lazy_attributes' ) );
+ add_action( 'wp_head', array( $this, 'add_nojs_fallback' ) );
+ }
+
+ /**
+ * Setup filters.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @return void
+ */
+ public function setup_filters() {
+ add_filter( 'the_content', array( $this, 'add_image_placeholders' ), PHP_INT_MAX ); // Run this later, so other content filters have run, including image_add_wh on WP.com.
+ add_filter( 'post_thumbnail_html', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ add_filter( 'get_avatar', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ add_filter( 'widget_text', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ add_filter( 'get_image_tag', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ add_filter( 'wp_get_attachment_image_attributes', array( __CLASS__, 'process_image_attributes' ), PHP_INT_MAX );
+ }
+
+ /**
+ * Remove filters.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @return void
+ */
+ public function remove_filters() {
+ remove_filter( 'the_content', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ remove_filter( 'post_thumbnail_html', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ remove_filter( 'get_avatar', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ remove_filter( 'widget_text', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ remove_filter( 'get_image_tag', array( $this, 'add_image_placeholders' ), PHP_INT_MAX );
+ remove_filter( 'wp_get_attachment_image_attributes', array( __CLASS__, 'process_image_attributes' ), PHP_INT_MAX );
+ }
+
+ /**
+ * Ensure that our lazy image attributes are not filtered out of image tags.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @param array $allowed_tags The allowed tags and their attributes.
+ * @return array
+ */
+ public function allow_lazy_attributes( $allowed_tags ) {
+ if ( ! isset( $allowed_tags['img'] ) ) {
+ return $allowed_tags;
+ }
+
+ // But, if images are allowed, ensure that our attributes are allowed!
+ $img_attributes = array_merge(
+ $allowed_tags['img'],
+ array(
+ 'data-lazy-src' => 1,
+ 'data-lazy-srcset' => 1,
+ 'data-lazy-sizes' => 1,
+ )
+ );
+
+ $allowed_tags['img'] = $img_attributes;
+
+ return $allowed_tags;
+ }
+
+ /**
+ * Add image placeholders.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @param string $content Content.
+ * @return string
+ */
+ public function add_image_placeholders( $content ) {
+ // Don't lazy load for feeds, previews.
+ if ( is_feed() || is_preview() ) {
+ return $content;
+ }
+
+ // Don't lazy-load if the content has already been run through previously.
+ if ( false !== strpos( $content, 'data-lazy-src' ) ) {
+ return $content;
+ }
+
+ // This is a pretty simple regex, but it works.
+ $content = preg_replace_callback( '#<(img)([^>]+?)(>(.*?)</\\1>|[\/]?>)#si', array( __CLASS__, 'process_image' ), $content );
+
+ return $content;
+ }
+
+ /**
+ * Returns true when a given string of classes contains a class signifying lazy images.
+ * should not process the image.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.9.0
+ *
+ * @param string $classes A string of space-separated classes.
+ * @return bool
+ */
+ public static function should_skip_image_with_blocked_class( $classes ) {
+ $blocked_classes = array(
+ 'skip-lazy',
+ 'gazette-featured-content-thumbnail',
+ );
+
+ /**
+ * Allow plugins and themes to tell lazy images to skip an image with a given class.
+ *
+ * @package automattic/jetpack-lazy-images
+ *
+ * @since 1.0.0
+ * @since-jetpack 8.7.0
+ *
+ * @param array An array of strings where each string is a class.
+ */
+ $blocked_classes = apply_filters( 'jetpack_lazy_images_blocked_classes', $blocked_classes );
+
+ if ( ! is_array( $blocked_classes ) || empty( $blocked_classes ) ) {
+ return false;
+ }
+
+ foreach ( $blocked_classes as $class ) {
+ if ( false !== strpos( $classes, $class ) ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Processes images in content by acting as the preg_replace_callback.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @param array $matches Matches.
+ *
+ * @return string The image with updated lazy attributes.
+ */
+ public static function process_image( $matches ) {
+ $old_attributes_str = $matches[2];
+ $old_attributes_kses_hair = wp_kses_hair( $old_attributes_str, wp_allowed_protocols() );
+
+ if ( empty( $old_attributes_kses_hair['src'] ) ) {
+ return $matches[0];
+ }
+
+ $old_attributes = self::flatten_kses_hair_data( $old_attributes_kses_hair );
+
+ // If we didn't add lazy attributes, just return the original image source.
+ if ( ! empty( $old_attributes['class'] ) && false !== strpos( $old_attributes['class'], 'jetpack-lazy-image' ) ) {
+ return $matches[0];
+ }
+
+ $new_attributes = self::process_image_attributes( $old_attributes );
+ $new_attributes_str = self::build_attributes_string( $new_attributes );
+
+ return sprintf( '<img %1$s><noscript>%2$s</noscript>', $new_attributes_str, $matches[0] );
+ }
+
+ /**
+ * Given an array of image attributes, updates the `src`, `srcset`, and `sizes` attributes so
+ * that they load lazily.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.7.0
+ *
+ * @param array $attributes Attributes.
+ *
+ * @return array The updated image attributes array with lazy load attributes.
+ */
+ public static function process_image_attributes( $attributes ) {
+ if ( empty( $attributes['src'] ) ) {
+ return $attributes;
+ }
+
+ if ( ! empty( $attributes['class'] ) && self::should_skip_image_with_blocked_class( $attributes['class'] ) ) {
+ return $attributes;
+ }
+
+ if ( isset( $attributes['data-skip-lazy'] ) ) {
+ return $attributes;
+ }
+
+ /**
+ * Allow plugins and themes to conditionally skip processing an image via its attributes.
+ *
+ * @package automattic/jetpack-lazy-images
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.9.0
+ * @deprecated-jetpack 6.5.0 Use jetpack_lazy_images_skip_image_with_attributes instead.
+ *
+ * @param bool Default to not skip processing the current image.
+ * @param array An array of attributes via wp_kses_hair() for the current image.
+ */
+ if ( apply_filters( 'jetpack_lazy_images_skip_image_with_atttributes', false, $attributes ) ) {
+ return $attributes;
+ }
+
+ /**
+ * Allow plugins and themes to conditionally skip processing an image via its attributes.
+ *
+ * @package automattic/jetpack-lazy-images
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.9.0
+ * @since-jetpack 6.5.0 Filter name was updated from jetpack_lazy_images_skip_image_with_atttributes to correct typo.
+ *
+ * @param bool Default to not skip processing the current image.
+ * @param array An array of attributes via wp_kses_hair() for the current image.
+ */
+ if ( apply_filters( 'jetpack_lazy_images_skip_image_with_attributes', false, $attributes ) ) {
+ return $attributes;
+ }
+
+ $old_attributes = $attributes;
+
+ // Stash srcset and sizes in data attributes.
+ foreach ( array( 'srcset', 'sizes' ) as $attribute ) {
+ if ( isset( $old_attributes[ $attribute ] ) ) {
+ $attributes[ "data-lazy-$attribute" ] = $old_attributes[ $attribute ];
+ unset( $attributes[ $attribute ] );
+ }
+ }
+
+ // We set this, adding the query arg so that it doesn't exactly equal the src attribute, so that photon JavaScript
+ // will hold off on processing this image.
+ $attributes['data-lazy-src'] = esc_url_raw( add_query_arg( 'is-pending-load', true, $attributes['src'] ) );
+
+ $attributes['srcset'] = self::get_placeholder_image();
+ $attributes['class'] = sprintf(
+ '%s jetpack-lazy-image',
+ empty( $old_attributes['class'] )
+ ? ''
+ : $old_attributes['class']
+ );
+
+ /**
+ * Allow plugins and themes to override the attributes on the image before the content is updated.
+ *
+ * One potential use of this filter is for themes that set `height:auto` on the `img` tag.
+ * With this filter, the theme could get the width and height attributes from the
+ * $attributes array and then add a style tag that sets those values as well, which could
+ * minimize reflow as images load.
+ *
+ * @package automattic/jetpack-lazy-images
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @param array An array containing the attributes for the image, where the key is the attribute name
+ * and the value is the attribute value.
+ */
+ return apply_filters( 'jetpack_lazy_images_new_attributes', $attributes );
+ }
+
+ /**
+ * Adds JavaScript to check if the current browser supports JavaScript as well as some styles to hide lazy
+ * images when the browser does not support JavaScript.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @return void
+ */
+ public function add_nojs_fallback() {
+ ?>
+ <style type="text/css">
+ /* If html does not have either class, do not show lazy loaded images. */
+ html:not( .jetpack-lazy-images-js-enabled ):not( .js ) .jetpack-lazy-image {
+ display: none;
+ }
+ </style>
+ <script>
+ document.documentElement.classList.add(
+ 'jetpack-lazy-images-js-enabled'
+ );
+ </script>
+ <?php
+ }
+
+ /**
+ * Retrieves the placeholder image after running it through the lazyload_images_placeholder_image filter.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @return string The placeholder image source.
+ */
+ private static function get_placeholder_image() {
+ /**
+ * Allows plugins and themes to modify the placeholder image.
+ *
+ * This filter is not prefixed with jetpack_ to provide a smoother migration
+ * process from the WordPress Lazy Load plugin.
+ *
+ * @module lazy-images
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ * @since-jetpack 6.5.0 Default image is now a base64 encoded transparent gif.
+ *
+ * @param string The URL to the placeholder image.
+ */
+ return apply_filters(
+ 'lazyload_images_placeholder_image',
+ 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
+ );
+ }
+
+ /**
+ * Flatter KSES hair data.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @param array $attributes Attributes.
+ *
+ * @return array
+ */
+ private static function flatten_kses_hair_data( $attributes ) {
+ $flattened_attributes = array();
+ foreach ( $attributes as $name => $attribute ) {
+ $flattened_attributes[ $name ] = $attribute['value'];
+ }
+ return $flattened_attributes;
+ }
+
+ /**
+ * Build attributes string.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @param array $attributes Attributes.
+ *
+ * @return string
+ */
+ private static function build_attributes_string( $attributes ) {
+ $string = array();
+ foreach ( $attributes as $name => $value ) {
+ if ( '' === $value ) {
+ $string[] = sprintf( '%s', $name );
+ } else {
+ $string[] = sprintf( '%s="%s"', $name, esc_attr( $value ) );
+ }
+ }
+ return implode( ' ', $string );
+ }
+
+ /**
+ * Enqueue assets.
+ *
+ * @since 1.0.0
+ * @since-jetpack 5.6.0
+ *
+ * @return void
+ */
+ public function enqueue_assets() {
+ Assets::register_script(
+ 'jetpack-lazy-images-polyfill-intersectionobserver',
+ '../dist/intersection-observer.js',
+ __FILE__,
+ array(
+ 'nonmin_path' => '../dist/intersection-observer.src.js',
+ 'in_footer' => true,
+ )
+ );
+ Assets::register_script(
+ 'jetpack-lazy-images',
+ '../dist/lazy-images.js',
+ __FILE__,
+ array(
+ 'nonmin_path' => 'js/lazy-images.js',
+ 'dependencies' => array( 'jetpack-lazy-images-polyfill-intersectionobserver' ),
+ 'in_footer' => true,
+ )
+ );
+ Assets::enqueue_script( 'jetpack-lazy-images' );
+ wp_localize_script(
+ 'jetpack-lazy-images',
+ 'jetpackLazyImagesL10n',
+ array(
+ 'loading_warning' => __( 'Images are still loading. Please cancel your print and try again.', 'jetpack-lazy-images' ),
+ )
+ );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/webpack.config.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/webpack.config.js
new file mode 100644
index 00000000..4d5d2bdb
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-lazy-images/webpack.config.js
@@ -0,0 +1,43 @@
+const jetpackWebpackConfig = require( '@automattic/jetpack-webpack-config/webpack' );
+const path = require( 'path' );
+const CopyWebpackPlugin = require( 'copy-webpack-plugin' );
+
+module.exports = [
+ {
+ entry: {
+ 'lazy-images': './src/js/lazy-images.js',
+ 'intersection-observer': require.resolve( 'intersection-observer/intersection-observer.js' ),
+ },
+ mode: jetpackWebpackConfig.mode,
+ devtool: jetpackWebpackConfig.devtool,
+ output: {
+ ...jetpackWebpackConfig.output,
+ path: path.resolve( './dist' ),
+ },
+ optimization: {
+ ...jetpackWebpackConfig.optimization,
+ },
+ resolve: {
+ ...jetpackWebpackConfig.resolve,
+ },
+ node: false,
+ plugins: [
+ ...jetpackWebpackConfig.StandardPlugins(),
+ new CopyWebpackPlugin( {
+ patterns: [
+ {
+ from: require.resolve( 'intersection-observer/intersection-observer.js' ),
+ to: 'intersection-observer.src.js',
+ },
+ ],
+ } ),
+ ],
+ module: {
+ strictExportPresence: true,
+ rules: [
+ // Transpile JavaScript, including node_modules.
+ jetpackWebpackConfig.TranspileRule(),
+ ],
+ },
+ },
+];
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/CHANGELOG.md
new file mode 100644
index 00000000..259c8b2c
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/CHANGELOG.md
@@ -0,0 +1,184 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.6.0] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+- Updated package textdomain from `jetpack` to `jetpack-licensing`.
+
+## [1.5.4] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.5.3] - 2021-12-03
+### Changed
+- Increases the timeout of the license activation request from 10 to 30 seconds.
+
+## [1.5.2] - 2021-11-30
+### Changed
+- Updated package dependencies.
+
+## [1.5.1] - 2021-11-23
+### Changed
+- Updated package dependencies.
+
+## [1.5.0] - 2021-11-16
+### Added
+- Add a test for update to WPCOM return change.
+- Added get_license_activation_notice_dismiss() function.
+
+## [1.4.9] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.4.8] - 2021-10-19
+### Changed
+- Updated package dependencies.
+
+## [1.4.7] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.4.6] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.4.5] - 2021-08-31
+### Changed
+- Run composer update on test-php command instead of phpunit.
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- Updated versions in annotations.
+
+## [1.4.4] - 2021-07-27
+### Changed
+- Updated package dependencies.
+
+## [1.4.3] - 2021-06-29
+### Changed
+- Updated package dependencies.
+
+## [1.4.2] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.4.1] - 2021-04-27
+### Changed
+- Updated package dependencies.
+
+## [1.4.0] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+- Dashboard: add new option to input license key.
+
+### Changed
+- Replace usage of deprecated is_active method
+- Update package dependencies.
+
+### Fixed
+- Fix stored licenses not being attached on option creation
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.3.4] - 2021-02-23
+
+- CI: Make tests more generic
+- codesniffer: Hack around mediawiki-codesniffer bug
+
+## [1.3.3] - 2021-02-08
+
+- Update dependencies to latest stable
+
+## [1.3.2] - 2021-01-28
+
+- Update dependencies to latest stable
+
+## [1.3.1] - 2021-01-26
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.3.0] - 2021-01-05
+
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.2.4] - 2020-11-24
+
+- Version packages for release
+
+## [1.2.3] - 2020-11-24
+
+- Updated PHPCS: Packages and Debugger
+
+## [1.2.2] - 2020-11-05
+
+- Update dependencies to latest stable
+
+## [1.2.1] - 2020-10-29
+
+- Update dependencies to latest stable
+
+## [1.2.0] - 2020-10-27
+
+- Licensing: use Oxford comma in error message
+
+## [1.1.4] - 2020-10-14
+
+- Update dependencies to latest stable
+
+## [1.1.3] - 2020-10-09
+
+- Update dependencies to latest stable
+
+## [1.1.2] - 2020-10-06
+
+- Update dependencies to latest stable
+
+## [1.1.1] - 2020-10-01
+
+- Update dependencies to latest stable
+
+## [1.1.0] - 2020-09-29
+
+- Update dependencies to latest stable
+
+## 1.0.0 - 2020-09-24
+
+- Licensing: Add support for Jetpack licenses
+
+[1.6.0]: https://github.com/Automattic/jetpack-licensing/compare/v1.5.4...v1.6.0
+[1.5.4]: https://github.com/Automattic/jetpack-licensing/compare/v1.5.3...v1.5.4
+[1.5.3]: https://github.com/Automattic/jetpack-licensing/compare/v1.5.2...v1.5.3
+[1.5.2]: https://github.com/Automattic/jetpack-licensing/compare/v1.5.1...v1.5.2
+[1.5.1]: https://github.com/Automattic/jetpack-licensing/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/Automattic/jetpack-licensing/compare/v1.4.9...v1.5.0
+[1.4.9]: https://github.com/Automattic/jetpack-licensing/compare/v1.4.8...v1.4.9
+[1.4.8]: https://github.com/Automattic/jetpack-licensing/compare/v1.4.7...v1.4.8
+[1.4.7]: https://github.com/Automattic/jetpack-licensing/compare/v1.4.6...v1.4.7
+[1.4.6]: https://github.com/Automattic/jetpack-licensing/compare/v1.4.5...v1.4.6
+[1.4.5]: https://github.com/Automattic/jetpack-licensing/compare/v1.4.4...v1.4.5
+[1.4.4]: https://github.com/Automattic/jetpack-licensing/compare/v1.4.3...v1.4.4
+[1.4.3]: https://github.com/Automattic/jetpack-licensing/compare/v1.4.2...v1.4.3
+[1.4.2]: https://github.com/Automattic/jetpack-licensing/compare/v1.4.1...v1.4.2
+[1.4.1]: https://github.com/Automattic/jetpack-licensing/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/Automattic/jetpack-licensing/compare/v1.3.4...v1.4.0
+[1.3.4]: https://github.com/Automattic/jetpack-licensing/compare/v1.3.3...v1.3.4
+[1.3.3]: https://github.com/Automattic/jetpack-licensing/compare/v1.3.2...v1.3.3
+[1.3.2]: https://github.com/Automattic/jetpack-licensing/compare/v1.3.1...v1.3.2
+[1.3.1]: https://github.com/Automattic/jetpack-licensing/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/Automattic/jetpack-licensing/compare/v1.2.4...v1.3.0
+[1.2.4]: https://github.com/Automattic/jetpack-licensing/compare/v1.2.3...v1.2.4
+[1.2.3]: https://github.com/Automattic/jetpack-licensing/compare/v1.2.2...v1.2.3
+[1.2.2]: https://github.com/Automattic/jetpack-licensing/compare/v1.2.1...v1.2.2
+[1.2.1]: https://github.com/Automattic/jetpack-licensing/compare/v1.2.0...v1.2.1
+[1.2.0]: https://github.com/Automattic/jetpack-licensing/compare/v1.1.4...v1.2.0
+[1.1.4]: https://github.com/Automattic/jetpack-licensing/compare/v1.1.3...v1.1.4
+[1.1.3]: https://github.com/Automattic/jetpack-licensing/compare/v1.1.2...v1.1.3
+[1.1.2]: https://github.com/Automattic/jetpack-licensing/compare/v1.1.1...v1.1.2
+[1.1.1]: https://github.com/Automattic/jetpack-licensing/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-licensing/compare/v1.0.0...v1.1.0
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/src/class-licensing.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/src/class-licensing.php
new file mode 100644
index 00000000..056004f5
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-licensing/src/class-licensing.php
@@ -0,0 +1,272 @@
+<?php
+/**
+ * A Terms of Service class for Jetpack.
+ *
+ * @package automattic/jetpack-licensing
+ */
+
+namespace Automattic\Jetpack;
+
+use Automattic\Jetpack\Connection\Manager as Connection_Manager;
+use Jetpack_IXR_ClientMulticall;
+use Jetpack_Options;
+use WP_Error;
+
+/**
+ * Class Licensing.
+ * Helper class that is responsible for attaching licenses to the current site.
+ *
+ * @since 1.1.1
+ */
+class Licensing {
+ /**
+ * Name of the WordPress option that holds all known Jetpack licenses.
+ *
+ * @const string
+ */
+ const LICENSES_OPTION_NAME = 'jetpack_licenses';
+
+ /**
+ * Name of the WordPress transient that holds the last license attaching error, if any.
+ *
+ * @const string
+ */
+ const ERROR_TRANSIENT_NAME = 'jetpack_licenses_error';
+
+ /**
+ * Holds the singleton instance of this class.
+ *
+ * @var self
+ */
+ protected static $instance = false;
+
+ /**
+ * Singleton.
+ *
+ * @static
+ */
+ public static function instance() {
+ if ( ! self::$instance ) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Initialize.
+ *
+ * @return void
+ */
+ public function initialize() {
+ add_action( 'add_option_' . self::LICENSES_OPTION_NAME, array( $this, 'attach_stored_licenses' ) );
+ add_action( 'update_option_' . self::LICENSES_OPTION_NAME, array( $this, 'attach_stored_licenses' ) );
+ add_action( 'jetpack_authorize_ending_authorized', array( $this, 'attach_stored_licenses_on_connection' ) );
+ }
+
+ /**
+ * Get Jetpack connection manager instance.
+ *
+ * @return Connection_Manager
+ */
+ protected function connection() {
+ static $connection;
+
+ if ( null === $connection ) {
+ $connection = new Connection_Manager();
+ }
+
+ return $connection;
+ }
+
+ /**
+ * Get the last license attach request error that has occurred, if any.
+ *
+ * @return string Human-readable error message or an empty string.
+ */
+ public function last_error() {
+ return Jetpack_Options::get_option( 'licensing_error', '' );
+ }
+
+ /**
+ * Log an error to be surfaced to the user at a later time.
+ *
+ * @param string $error Human-readable error message.
+ * @return void
+ */
+ public function log_error( $error ) {
+ $substr = function_exists( 'mb_substr' ) ? 'mb_substr' : 'substr';
+ Jetpack_Options::update_option( 'licensing_error', $substr( $error, 0, 1024 ) );
+ }
+
+ /**
+ * Get all stored licenses.
+ *
+ * @return string[] License keys.
+ */
+ public function stored_licenses() {
+ $licenses = (array) get_option( self::LICENSES_OPTION_NAME, array() );
+ $licenses = array_filter( $licenses, 'is_scalar' );
+ $licenses = array_map( 'strval', $licenses );
+ $licenses = array_filter( $licenses );
+
+ return $licenses;
+ }
+
+ /**
+ * Append a license
+ *
+ * @param string $license A jetpack license key.
+ * @return bool True if the option was updated with the new license, false otherwise.
+ */
+ public function append_license( $license ) {
+ $licenses = $this->stored_licenses();
+
+ array_push( $licenses, $license );
+
+ return update_option( self::LICENSES_OPTION_NAME, $licenses );
+ }
+
+ /**
+ * Make an authenticated WP.com XMLRPC multicall request to attach the provided license keys.
+ *
+ * @param string[] $licenses License keys to attach.
+ * @return Jetpack_IXR_ClientMulticall
+ */
+ protected function attach_licenses_request( array $licenses ) {
+ $xml = new Jetpack_IXR_ClientMulticall( array( 'timeout' => 30 ) );
+
+ foreach ( $licenses as $license ) {
+ $xml->addCall( 'jetpack.attachLicense', $license );
+ }
+
+ $xml->query();
+
+ return $xml;
+ }
+
+ /**
+ * Attach the given licenses.
+ *
+ * @param string[] $licenses Licenses to attach.
+ * @return array|WP_Error Results for each license (which may include WP_Error instances) or a WP_Error instance.
+ */
+ public function attach_licenses( array $licenses ) {
+ if ( ! $this->connection()->has_connected_owner() ) {
+ return new WP_Error( 'not_connected', __( 'Jetpack doesn\'t have a connected owner.', 'jetpack-licensing' ) );
+ }
+
+ if ( empty( $licenses ) ) {
+ return array();
+ }
+
+ $xml = $this->attach_licenses_request( $licenses );
+
+ if ( $xml->isError() ) {
+ $error = new WP_Error( 'request_failed', __( 'License attach request failed.', 'jetpack-licensing' ) );
+ $error->add( $xml->getErrorCode(), $xml->getErrorMessage() );
+ return $error;
+ }
+
+ $results = array_map(
+ function ( $response ) {
+ if ( isset( $response['faultCode'] ) || isset( $response['faultString'] ) ) {
+ return new WP_Error( $response['faultCode'], $response['faultString'] );
+ }
+
+ return $response;
+ },
+ (array) $xml->getResponse()
+ );
+
+ return $results;
+ }
+
+ /**
+ * Attach all stored licenses.
+ *
+ * @return array|WP_Error Results for each license (which may include WP_Error instances) or a WP_Error instance.
+ */
+ public function attach_stored_licenses() {
+ $licenses = $this->stored_licenses();
+ $results = $this->attach_licenses( $licenses );
+
+ if ( is_wp_error( $results ) ) {
+ if ( 'request_failed' === $results->get_error_code() ) {
+ $this->log_error(
+ __( 'Failed to attach your Jetpack license(s). Please try reconnecting Jetpack.', 'jetpack-licensing' )
+ );
+ }
+
+ return $results;
+ }
+
+ $failed = array();
+
+ foreach ( $results as $index => $result ) {
+ if ( isset( $licenses[ $index ] ) && is_wp_error( $result ) ) {
+ $failed[] = $licenses[ $index ];
+ }
+ }
+
+ if ( ! empty( $failed ) ) {
+ $this->log_error(
+ sprintf(
+ /* translators: %s is a comma-separated list of license keys. */
+ __( 'The following Jetpack licenses are invalid, already in use, or revoked: %s', 'jetpack-licensing' ),
+ implode( ', ', $failed )
+ )
+ );
+ }
+
+ return $results;
+ }
+
+ /**
+ * Attach all stored licenses during connection flow for the connection owner.
+ *
+ * @return void
+ */
+ public function attach_stored_licenses_on_connection() {
+ if ( $this->connection()->is_connection_owner() ) {
+ $this->attach_stored_licenses();
+ }
+ }
+
+ /**
+ * Is the current user allowed to use the Licensing Input UI?
+ *
+ * @since 1.4.0
+ * @return bool
+ */
+ public static function is_licensing_input_enabled() {
+ /**
+ * Filter that checks if the user is allowed to see the Licensing UI. `true` enables it.
+ *
+ * @since 1.4.0
+ *
+ * @param bool False by default.
+ */
+ return apply_filters( 'jetpack_licensing_ui_enabled', false ) && current_user_can( 'jetpack_connect_user' );
+ }
+
+ /**
+ * Gets the user-licensing activation notice dismissal info.
+ *
+ * @since 10.4.0
+ * @return array
+ */
+ public function get_license_activation_notice_dismiss() {
+
+ $default = array(
+ 'last_detached_count' => null,
+ 'last_dismissed_time' => null,
+ );
+
+ if ( $this->connection()->is_user_connected() && $this->connection()->is_connection_owner() ) {
+ return Jetpack_Options::get_option( 'licensing_activation_notice_dismiss', $default );
+ }
+
+ return $default;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/CHANGELOG.md
new file mode 100644
index 00000000..c922a297
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/CHANGELOG.md
@@ -0,0 +1,133 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.5.13] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+
+## [1.5.12] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.5.11] - 2021-11-30
+### Changed
+- Colors: update Jetpack Primary color to match latest brand book.
+
+## [1.5.10] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.5.9] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.5.8] - 2021-10-07
+### Changed
+- Updated package dependencies
+
+## [1.5.7] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.5.6] - 2021-08-30
+### Changed
+- Run composer update on test-php command instead of phpunit
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- update annotations versions
+
+## [1.5.5] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.5.4] - 2021-04-27
+### Changed
+- Updated package dependencies.
+
+## [1.5.3] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.5.2] - 2021-02-05
+
+- CI: Make tests more generic
+
+## [1.5.1] - 2021-01-19
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.5.0] - 2020-12-07
+
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.4.0] - 2020-08-13
+
+- CI: Try collect js coverage
+
+## [1.3.0] - 2020-06-22
+
+- PHPCS: Clean up the packages
+- PHPCS Updates after WPCS 2.3
+
+## [1.2.0] - 2020-03-27
+
+- Use dynamic Jetpack logos on JITMs
+
+## [1.1.4] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## [1.1.2] - 2019-10-28
+
+- Packages: Add gitattributes files to all packages that need th…
+
+## [1.1.1] - 2019-09-20
+
+- Docs: Unify usage of @package phpdoc tags
+
+## [1.1.0] - 2019-06-11
+
+- Feature/jetpack packages pt 1. (May 31 - June 6)
+- Update/package logo add gray
+- Packages: Move JITM tests to package and fix deps
+- Update Jetpack to use new JITM package
+- Packages: Make logo package tests independent
+
+## 1.0.0 - 2019-05-29
+
+- Packages: Add a basic Jetpack Logo package
+
+[1.5.13]: https://github.com/Automattic/jetpack-logo/compare/v1.5.12...v1.5.13
+[1.5.12]: https://github.com/Automattic/jetpack-logo/compare/v1.5.11...v1.5.12
+[1.5.11]: https://github.com/Automattic/jetpack-logo/compare/v1.5.10...v1.5.11
+[1.5.10]: https://github.com/Automattic/jetpack-logo/compare/v1.5.9...v1.5.10
+[1.5.9]: https://github.com/Automattic/jetpack-logo/compare/v1.5.8...v1.5.9
+[1.5.8]: https://github.com/Automattic/jetpack-logo/compare/v1.5.7...v1.5.8
+[1.5.7]: https://github.com/Automattic/jetpack-logo/compare/v1.5.6...v1.5.7
+[1.5.6]: https://github.com/Automattic/jetpack-logo/compare/v1.5.5...v1.5.6
+[1.5.5]: https://github.com/Automattic/jetpack-logo/compare/v1.5.4...v1.5.5
+[1.5.4]: https://github.com/Automattic/jetpack-logo/compare/v1.5.3...v1.5.4
+[1.5.3]: https://github.com/Automattic/jetpack-logo/compare/v1.5.2...v1.5.3
+[1.5.2]: https://github.com/Automattic/jetpack-logo/compare/v1.5.1...v1.5.2
+[1.5.1]: https://github.com/Automattic/jetpack-logo/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/Automattic/jetpack-logo/compare/v1.4.0...v1.5.0
+[1.4.0]: https://github.com/Automattic/jetpack-logo/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/Automattic/jetpack-logo/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-logo/compare/v1.1.4...v1.2.0
+[1.1.4]: https://github.com/Automattic/jetpack-logo/compare/v1.1.2...v1.1.4
+[1.1.2]: https://github.com/Automattic/jetpack-logo/compare/v1.1.1...v1.1.2
+[1.1.1]: https://github.com/Automattic/jetpack-logo/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-logo/compare/v1.0.0...v1.1.0
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/src/class-logo.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/src/class-logo.php
new file mode 100644
index 00000000..79fda33c
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-logo/src/class-logo.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * A logo for Jetpack.
+ *
+ * @package automattic/jetpack-logo
+ */
+
+namespace Automattic\Jetpack\Assets;
+
+/**
+ * Jetpack logo as SVG shapes.
+ *
+ * Initializes the logo property with a string describing the Jetpack logo.
+ * The Jetpack logo SVG string includes CSS classes to stylize it:
+ * - jetpack-logo: the wrapper <svg> tag.
+ * - jetpack-logo__icon-circle: the circle of the Jetpack mark.
+ * - jetpack-logo__icon-triangle: two shapes that correspond to each triangle in the Jetpack mark.
+ * - jetpack-logo__icon-text: the Jetpack lettering.
+ *
+ * @var string
+ */
+const JETPACK_LOGO_SVG = <<<'EOSVG'
+<svg xmlns="http://www.w3.org/2000/svg" height="32" class="jetpack-logo" viewBox="0 0 118 32">
+ <path class="jetpack-logo__icon-circle" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z" fill="#069e08" />
+ <polygon class="jetpack-logo__icon-triangle" points="15,19 7,19 15,3" fill="#fff" />
+ <polygon class="jetpack-logo__icon-triangle" points="17,29 17,13 25,13" fill="#fff" />
+ <path class="jetpack-logo__text" d="M41.3 26.6c-.5-.7-.9-1.4-1.3-2.1 2.3-1.4 3-2.5 3-4.6V8h-3V6h6v13.4C46 22.8 45 24.8 41.3 26.6zM58.5 21.3c-1.5.5-2.7.6-4.2.6-3.6 0-5.8-1.8-5.8-6 0-3.1 1.9-5.9 5.5-5.9s4.9 2.5 4.9 4.9c0 .8 0 1.5-.1 2h-7.3c.1 2.5 1.5 2.8 3.6 2.8 1.1 0 2.2-.3 3.4-.7C58.5 19 58.5 21.3 58.5 21.3zM56 15c0-1.4-.5-2.9-2-2.9-1.4 0-2.3 1.3-2.4 2.9C51.6 15 56 15 56 15zM65 18.4c0 1.1.8 1.3 1.4 1.3.5 0 2-.2 2.6-.4v2.1c-.9.3-2.5.5-3.7.5-1.5 0-3.2-.5-3.2-3.1V12H60v-2h2.1V7.1H65V10h4v2h-4V18.4zM71 10h3v1.3c1.1-.8 1.9-1.3 3.3-1.3 2.5 0 4.5 1.8 4.5 5.6s-2.2 6.3-5.8 6.3c-.9 0-1.3-.1-2-.3V28h-3V10zM76.5 12.3c-.8 0-1.6.4-2.5 1.2v5.9c.6.1.9.2 1.8.2 2 0 3.2-1.3 3.2-3.9C79 13.4 78.1 12.3 76.5 12.3zM93 22h-3v-1.5c-.9.7-1.9 1.5-3.5 1.5-1.5 0-3.1-1.1-3.1-3.2 0-2.9 2.5-3.4 4.2-3.7l2.4-.3v-.3c0-1.5-.5-2.3-2-2.3-.7 0-2.3.5-3.7 1.1L84 11c1.2-.4 3-1 4.4-1 2.7 0 4.6 1.4 4.6 4.7L93 22zM90 16.4l-2.2.4c-.7.1-1.4.5-1.4 1.6 0 .9.5 1.4 1.3 1.4s1.5-.5 2.3-1V16.4zM104.5 21.3c-1.1.4-2.2.6-3.5.6-4.2 0-5.9-2.4-5.9-5.9 0-3.7 2.3-6 6.1-6 1.4 0 2.3.2 3.2.5V13c-.8-.3-2-.6-3.2-.6-1.7 0-3.2.9-3.2 3.6 0 2.9 1.5 3.8 3.3 3.8.9 0 1.9-.2 3.2-.7V21.3zM110 15.2c.2-.3.2-.8 3.8-5.2h3.7l-4.6 5.7 5 6.3h-3.7l-4.2-5.8V22h-3V6h3V15.2z" />
+</svg>
+EOSVG;
+
+/**
+ * Create and render a Jetpack logo.
+ */
+class Logo {
+
+ /**
+ * Return the Jetpack logo.
+ *
+ * @return string The Jetpack logo.
+ */
+ public function render() {
+ return JETPACK_LOGO_SVG;
+ }
+
+ /**
+ * Return string containing the Jetpack logo.
+ *
+ * @since 1.1.4
+ * @since-jetpack 7.5.0
+ *
+ * @param bool $logotype Should we use the full logotype (logo + text). Default to false.
+ *
+ * @return string
+ */
+ public function get_jp_emblem( $logotype = false ) {
+ $logo_text = $this->get_jp_logo_parts();
+ return sprintf(
+ '<svg id="jetpack-logo__icon" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 %1$s 32">%2$s</svg>',
+ ( true === $logotype ? '118' : '32' ),
+ ( true === $logotype ? $logo_text['logo'] . $logo_text['text'] : $logo_text['logo'] )
+ );
+ }
+
+ /**
+ * Return string containing the Jetpack logo in a slightly larger format than get_jp_emblem().
+ *
+ * @return string
+ */
+ public function get_jp_emblem_larger() {
+ $logo_text = $this->get_jp_logo_parts();
+ return '<svg class="jitm-jp-logo" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" height="32" viewBox="0 0 118 32">' . $logo_text['logo'] . $logo_text['text'] . '</svg>';
+ }
+
+ /**
+ * Return array containing the Jetpack logo and text
+ *
+ * @return array
+ */
+ private function get_jp_logo_parts() {
+ return array(
+ 'logo' => '<path fill="#069e08" d="M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16c8.8,0,16-7.2,16-16S24.8,0,16,0z M15.2,18.7h-8l8-15.5V18.7z M16.8,28.8 V13.3h8L16.8,28.8z"/>',
+ 'text' => '<path d="M41.3,26.6c-0.5-0.7-0.9-1.4-1.3-2.1c2.3-1.4,3-2.5,3-4.6V8h-3V6h6v13.4C46,22.8,45,24.8,41.3,26.6z" />
+ <path d="M65,18.4c0,1.1,0.8,1.3,1.4,1.3c0.5,0,2-0.2,2.6-0.4v2.1c-0.9,0.3-2.5,0.5-3.7,0.5c-1.5,0-3.2-0.5-3.2-3.1V12H60v-2h2.1V7.1 H65V10h4v2h-4V18.4z" />
+ <path d="M71,10h3v1.3c1.1-0.8,1.9-1.3,3.3-1.3c2.5,0,4.5,1.8,4.5,5.6s-2.2,6.3-5.8,6.3c-0.9,0-1.3-0.1-2-0.3V28h-3V10z M76.5,12.3 c-0.8,0-1.6,0.4-2.5,1.2v5.9c0.6,0.1,0.9,0.2,1.8,0.2c2,0,3.2-1.3,3.2-3.9C79,13.4,78.1,12.3,76.5,12.3z" />
+ <path d="M93,22h-3v-1.5c-0.9,0.7-1.9,1.5-3.5,1.5c-1.5,0-3.1-1.1-3.1-3.2c0-2.9,2.5-3.4,4.2-3.7l2.4-0.3v-0.3c0-1.5-0.5-2.3-2-2.3 c-0.7,0-2.3,0.5-3.7,1.1L84,11c1.2-0.4,3-1,4.4-1c2.7,0,4.6,1.4,4.6,4.7L93,22z M90,16.4l-2.2,0.4c-0.7,0.1-1.4,0.5-1.4,1.6 c0,0.9,0.5,1.4,1.3,1.4s1.5-0.5,2.3-1V16.4z" />
+ <path d="M104.5,21.3c-1.1,0.4-2.2,0.6-3.5,0.6c-4.2,0-5.9-2.4-5.9-5.9c0-3.7,2.3-6,6.1-6c1.4,0,2.3,0.2,3.2,0.5V13 c-0.8-0.3-2-0.6-3.2-0.6c-1.7,0-3.2,0.9-3.2,3.6c0,2.9,1.5,3.8,3.3,3.8c0.9,0,1.9-0.2,3.2-0.7V21.3z" />
+ <path d="M110,15.2c0.2-0.3,0.2-0.8,3.8-5.2h3.7l-4.6,5.7l5,6.3h-3.7l-4.2-5.8V22h-3V6h3V15.2z" />
+ <path d="M58.5,21.3c-1.5,0.5-2.7,0.6-4.2,0.6c-3.6,0-5.8-1.8-5.8-6c0-3.1,1.9-5.9,5.5-5.9s4.9,2.5,4.9,4.9c0,0.8,0,1.5-0.1,2h-7.3 c0.1,2.5,1.5,2.8,3.6,2.8c1.1,0,2.2-0.3,3.4-0.7C58.5,19,58.5,21.3,58.5,21.3z M56,15c0-1.4-0.5-2.9-2-2.9c-1.4,0-2.3,1.3-2.4,2.9 C51.6,15,56,15,56,15z" />',
+ );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/.babelrc b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/.babelrc
new file mode 100644
index 00000000..cf088480
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/.babelrc
@@ -0,0 +1,9 @@
+{
+ "presets": [
+ ["@babel/preset-env", {
+ "targets": {
+ "node": "current"
+ }
+ }]
+ ]
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/CHANGELOG.md
new file mode 100644
index 00000000..117d759f
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/CHANGELOG.md
@@ -0,0 +1,50 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [0.3.0] - 2022-01-04
+### Changed
+- Drop isRegistered and isUserConnected params from ConnectionStatusCard component
+- Switch to pcov for code coverage.
+- Updated package dependencies.
+- Updated package textdomain from `jetpack` to `jetpack-my-jetpack`.
+
+## [0.2.0] - 2021-12-14
+### Added
+- Added Connection Status Card to the page.
+- Janitorial: add watch command to the plugin.
+
+### Changed
+- Adapt to new layout components.
+- Build: do not ship scss and jsx files in production build.
+
+### Fixed
+- Build minimized JS for the production build.
+- Fix JavaScript i18n strings.
+
+## [0.1.3] - 2021-12-07
+### Changed
+- Updated package dependencies.
+
+## [0.1.2] - 2021-11-30
+### Added
+- Janitorial: create mirror repo for the package.
+
+### Changed
+- Remove now-redundant `output.filename` from Webpack config.
+
+## 0.1.1 - 2021-11-23
+### Changed
+- Updated package dependencies.
+
+## 0.1.0 - 2021-11-17
+### Added
+- Created package
+
+[0.3.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/0.2.0...0.3.0
+[0.2.0]: https://github.com/Automattic/jetpack-my-jetpack/compare/0.1.3...0.2.0
+[0.1.3]: https://github.com/Automattic/jetpack-my-jetpack/compare/0.1.2...0.1.3
+[0.1.2]: https://github.com/Automattic/jetpack-my-jetpack/compare/0.1.1...0.1.2
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/_inc/admin.jsx b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/_inc/admin.jsx
new file mode 100644
index 00000000..feb7a69c
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/_inc/admin.jsx
@@ -0,0 +1,25 @@
+/**
+ * External dependencies
+ */
+import ReactDOM from 'react-dom';
+import React from 'react';
+
+/**
+ * Internal dependencies
+ */
+import MyJetpackScreen from './components/my-jetpack-screen';
+
+/**
+ * The initial renderer function.
+ */
+function render() {
+ const container = document.getElementById( 'my-jetpack-container' );
+
+ if ( null === container ) {
+ return;
+ }
+
+ ReactDOM.render( <MyJetpackScreen />, container );
+}
+
+render();
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/_inc/components/my-jetpack-screen/index.jsx b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/_inc/components/my-jetpack-screen/index.jsx
new file mode 100644
index 00000000..8bfe39f5
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/_inc/components/my-jetpack-screen/index.jsx
@@ -0,0 +1,63 @@
+/* global myJetpackInitialState */
+
+/**
+ * External dependencies
+ */
+import React, { useCallback } from 'react';
+import { __ } from '@wordpress/i18n';
+import {
+ AdminSection,
+ AdminSectionHero,
+ AdminPage,
+ Row,
+ Col,
+} from '@automattic/jetpack-components';
+import { ConnectionStatusCard } from '@automattic/jetpack-connection';
+
+import './style.scss';
+
+/**
+ * The My Jetpack App Main Screen.
+ *
+ * @returns {object} The MyJetpackScreen component.
+ */
+export default function MyJetpackScreen() {
+ const redirectAfterDisconnect = useCallback( () => {
+ window.location = myJetpackInitialState.topJetpackMenuItemUrl;
+ }, [] );
+
+ return (
+ <div className="jp-my-jetpack-screen">
+ <AdminPage>
+ <AdminSectionHero>
+ <Row>
+ <Col lg={ 12 } md={ 8 } sm={ 4 }>
+ <h1>
+ { __(
+ 'Manage your Jetpack plan and products all in one place',
+ 'jetpack-my-jetpack'
+ ) }
+ </h1>
+ </Col>
+ </Row>
+ </AdminSectionHero>
+
+ <AdminSection>
+ <Row>
+ <Col lg={ 6 } sm={ 4 }>
+ <h1>{ __( 'My Plan', 'jetpack-my-jetpack' ) }</h1>
+ </Col>
+ <Col lg={ 6 } sm={ 4 }>
+ <ConnectionStatusCard
+ apiRoot={ myJetpackInitialState.apiRoot }
+ apiNonce={ myJetpackInitialState.apiNonce }
+ redirectUri={ myJetpackInitialState.redirectUri }
+ onDisconnected={ redirectAfterDisconnect }
+ />
+ </Col>
+ </Row>
+ </AdminSection>
+ </AdminPage>
+ </div>
+ );
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/_inc/components/my-jetpack-screen/style.scss b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/_inc/components/my-jetpack-screen/style.scss
new file mode 100644
index 00000000..358c0418
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/_inc/components/my-jetpack-screen/style.scss
@@ -0,0 +1 @@
+@import '~@automattic/jetpack-base-styles/style';
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/babel.config.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/babel.config.js
new file mode 100644
index 00000000..4f3daf51
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/babel.config.js
@@ -0,0 +1,10 @@
+const config = {
+ presets: [
+ [
+ '@automattic/jetpack-webpack-config/babel/preset',
+ { pluginReplaceTextdomain: { textdomain: 'jetpack-my-jetpack' } },
+ ],
+ ],
+};
+
+module.exports = config;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/images/disconnect-confirm-dc9fe8f5c68cfd1320e0.jpg b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/images/disconnect-confirm-dc9fe8f5c68cfd1320e0.jpg
new file mode 100644
index 00000000..13f5651c
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/images/disconnect-confirm-dc9fe8f5c68cfd1320e0.jpg
Binary files differ
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/images/disconnect-thanks-5873bfac56a9bd7322cd.jpg b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/images/disconnect-thanks-5873bfac56a9bd7322cd.jpg
new file mode 100644
index 00000000..a191ec9e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/images/disconnect-thanks-5873bfac56a9bd7322cd.jpg
Binary files differ
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.asset.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.asset.php
new file mode 100644
index 00000000..aefe6276
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.asset.php
@@ -0,0 +1 @@
+<?php return array('dependencies' => array('lodash', 'react', 'react-dom', 'wp-components', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill', 'wp-url'), 'version' => '29d086a288ded38b2a57d1829d39cc12'); \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.css b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.css
new file mode 100644
index 00000000..319c91ee
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.css
@@ -0,0 +1 @@
+.O5NYbFTsxmrm4P2SIdOC{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.rkV4U_hzC04NwXFQcTHl{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.rkV4U_hzC04NwXFQcTHl{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.rkV4U_hzC04NwXFQcTHl{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.G81E6mesnld_OhhOvz_F{grid-column-end:span 1}.SfCZpjwiu2F0KDjGIXEA{grid-column-end:span 2}.qPxwbSu_GTDTz_8jFHJD{grid-column-end:span 3}.xYAWQ0wk6FPfZOZQgFiw{grid-column-end:span 4}@media(min-width:600px){.zwZjA7ofpMucaB_UzG48{grid-column-end:span 1}.YfwKGGBEqiINFYy6ZpKM{grid-column-end:span 2}.pcHQbVFRxA_OvNRQ2OwP{grid-column-end:span 3}.U_LJMBlRZ8ItXZuvCXGg{grid-column-end:span 4}.st_7w_Ja1Gp2AgGaTysO{grid-column-end:span 5}.gQsaHmJo6Gp7Yq9IB9Sg{grid-column-end:span 6}.P0a4FWsiJJmR6bySI8QC{grid-column-end:span 7}.UxSHj7jyDp6sGKHILNRc{grid-column-end:span 8}}@media(min-width:960px){.TExt5ebNqUrEn2NzeDDh{grid-column-end:span 1}.cdwvRRac4c2djSpHfOpF{grid-column-end:span 2}.hvRpxlyFY9BQIDdEGTjg{grid-column-end:span 3}.Ev9JGJi3yKkBq6cW3Xge{grid-column-end:span 4}.mhL__tIHFH_tViX5718D{grid-column-end:span 5}.X1lGIxHHxsFl_39u4Px6{grid-column-end:span 6}.pc5UnY_DzsSDkyih78Ti{grid-column-end:span 7}.QCjBtfnG3u20FwoDd59Z{grid-column-end:span 8}.MX1aL3BeJpoSE0aXghp0{grid-column-end:span 9}.h_JAcO8a8ClV2LmTWsMz{grid-column-end:span 10}.gKlDMi0N7LOd9q8uJHi5{grid-column-end:span 11}.KZWhYB7r6TG75uJu2LsQ{grid-column-end:span 12}}@media(max-width:960px){.G6NxfG2gGwvEYb0aGdPJ{display:none}}@media(max-width:600px){.aceZPGfrg7IoR0Vu7ZJg{display:none}}.MZSHJVi991kNJhn_Xnip{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);display:block;margin:32px 0;padding:16px 64px 16px 24px;position:relative;text-decoration:none}.MZSHJVi991kNJhn_Xnip span{display:block}.MZSHJVi991kNJhn_Xnip span:last-of-type{font-weight:600}.MZSHJVi991kNJhn_Xnip:focus span:last-of-type,.MZSHJVi991kNJhn_Xnip:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.MZSHJVi991kNJhn_Xnip:focus:after,.MZSHJVi991kNJhn_Xnip:hover:after{transform:translateY(-50%) translateX(8px)}.MZSHJVi991kNJhn_Xnip:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;position:absolute;right:24px;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.EZ590eIMC5y_t1_gWRua{margin-left:-20px}.BYzOly4G7Gry9wdRH0AY{background-color:#fff;padding:40px 0}.iwMEh2CwJ_r23Y9sVcJ1,.O8YnwnZegI89S2Q3k5Qn{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.EC1Yirv2kOgRU77F2XRd{grid-column-end:span 1}.FHZqFvZXEDJ9mpOm7CeY{grid-column-end:span 2}.VOmONLKUkcSrIRYmX6YZ{grid-column-end:span 3}.eECO55N5YGkBB3tKa0If{grid-column-end:span 4}@media(min-width:600px){.u3ZB3zRkuglILO6FSIrx{grid-column-end:span 1}.iZnPl6piWO_GqHn16XKi{grid-column-end:span 2}.WWctfqi1wHZDWrue8GIt{grid-column-end:span 3}.p678NN_M0K78Kpprfndu{grid-column-end:span 4}._6D2TXtGKmepmwrxSf5DW{grid-column-end:span 5}.cWORs5VDjuStXsiVPtiO{grid-column-end:span 6}.yJsc8qt82L9miwDoe2Y2{grid-column-end:span 7}.hD6bXWfNn5elhKCaBFTD{grid-column-end:span 8}}@media(min-width:960px){.k9j6XIJdXbQ5YNyU8sNH{grid-column-end:span 1}.TnVGX0JHm_muRRcmYYwV{grid-column-end:span 2}.VILIswQXi8XzNJ3BqePc{grid-column-end:span 3}.h7mXuoRFWMGCiWYH4Ij1{grid-column-end:span 4}.uHYkk5STnhikqYMIxtE1{grid-column-end:span 5}.E8zp8nDOvAQ7VHsXKJJw{grid-column-end:span 6}.jwOhfvvsXGTCtHfB2854{grid-column-end:span 7}.DmSEZVFn_2XNNKJGgkXw{grid-column-end:span 8}.RdgOIY1qMZ8eqAeNSGNw{grid-column-end:span 9}.XBQd6xiLipeHg0O6cf7N{grid-column-end:span 10}.JyQ6Tbn3FCHfrCEyWrAr{grid-column-end:span 11}.fzJ_qaXYWs3EWQEupbLD{grid-column-end:span 12}}@media(max-width:960px){.LE2ZLIt_Y0slsr6TSmqe{display:none}}@media(max-width:600px){.vYxwZbK_covZoLjDe3BM{display:none}}.fmzqGXmbTSiUblRujgqB{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);display:block;margin:32px 0;padding:16px 64px 16px 24px;position:relative;text-decoration:none}.fmzqGXmbTSiUblRujgqB span{display:block}.fmzqGXmbTSiUblRujgqB span:last-of-type{font-weight:600}.fmzqGXmbTSiUblRujgqB:focus span:last-of-type,.fmzqGXmbTSiUblRujgqB:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.fmzqGXmbTSiUblRujgqB:focus:after,.fmzqGXmbTSiUblRujgqB:hover:after{transform:translateY(-50%) translateX(8px)}.fmzqGXmbTSiUblRujgqB:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;position:absolute;right:24px;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.HwO79godDUI97Xub9ey4{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.HwO79godDUI97Xub9ey4{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.HwO79godDUI97Xub9ey4{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.WcIPVMUrQMdwzisuVSh7{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.Nxx7T20LwPUQPXCSDMfA{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.Nxx7T20LwPUQPXCSDMfA{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.Nxx7T20LwPUQPXCSDMfA{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.MnTtKIHRyzXYoesgRCgm{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);display:block;margin:32px 0;padding:16px 64px 16px 24px;position:relative;text-decoration:none}.MnTtKIHRyzXYoesgRCgm span{display:block}.MnTtKIHRyzXYoesgRCgm span:last-of-type{font-weight:600}.MnTtKIHRyzXYoesgRCgm:focus span:last-of-type,.MnTtKIHRyzXYoesgRCgm:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.MnTtKIHRyzXYoesgRCgm:focus:after,.MnTtKIHRyzXYoesgRCgm:hover:after{transform:translateY(-50%) translateX(8px)}.MnTtKIHRyzXYoesgRCgm:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;position:absolute;right:24px;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.mmVmo1nZYrRywmnSKQFx{grid-column-end:span 1}.UrIp26XpdUSMJft8YxdC{grid-column-end:span 2}.pMXlFWglEDyK6MbwJ8sX{grid-column-end:span 3}.P5H1ohz9m5_rEqZFAkFm{grid-column-end:span 4}@media(min-width:600px){.ofLUn8LwR3LLeWl8tPdU{grid-column-end:span 1}.fq1Z8JYbcrrXavvSfzKk{grid-column-end:span 2}.jq9gui11HqTDfI7tXH1d{grid-column-end:span 3}.mVYkSRC7j44WiWLZHOnM{grid-column-end:span 4}.S3wOGmx7YLTrqz_bcLth{grid-column-end:span 5}.Y3GW3wokLgm9jnX78Uwk{grid-column-end:span 6}.ShMEdZjpjdYj7mCQzrSO{grid-column-end:span 7}.nfBAID75QGC1VZ8t0RfR{grid-column-end:span 8}}@media(min-width:960px){.Vr2EQcrmKOPJtFU72Vv2{grid-column-end:span 1}.CTS6MNweODFo4ZxcT0iV{grid-column-end:span 2}.XTISRluUo3o5xxnPNu09{grid-column-end:span 3}.c_EtRaSOJafAl5r9WkBm{grid-column-end:span 4}.HcpW_q5aO8Bf_ngIjyjv{grid-column-end:span 5}.XF3r0hMrFrrmxH5TJee0{grid-column-end:span 6}.Jl9ognyJ9XOZ6g0BTzLf{grid-column-end:span 7}._8w8oD2R9CVt9AU4PvUy{grid-column-end:span 8}.ltOXxurwUtxy7XIR_loo{grid-column-end:span 9}.bKUzzGEJ3wCoAOZZvVCK{grid-column-end:span 10}.GltQVCPa1x4tZ7sWFg1v{grid-column-end:span 11}.UzWicuFiKrGgMEjmRAFA{grid-column-end:span 12}}@media(max-width:960px){.CDwHAcVQxDeV2mFXS1Dz{display:none}}@media(max-width:600px){.sr1184KrdJ0UtgNsMQnR{display:none}}.jp-dashboard-footer{align-items:center;color:#000;display:flex;flex-flow:row wrap;justify-content:space-between;max-width:1128px;width:100%}.jp-dashboard-footer__jetpack-symbol,.jp-dashboard-footer__module-name{display:inline-block;vertical-align:middle}.jp-dashboard-footer__module-name{font-size:12px;font-weight:600;margin-left:5px}.K6gniZequRm1zBi4xa2G{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.vickZxEIl6kGZp7KkFYF{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.vickZxEIl6kGZp7KkFYF{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.vickZxEIl6kGZp7KkFYF{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.U2ZD1antXfCcQlBdFwyw{grid-column-end:span 1}.caLSPVQ3JP3b2Ia68Seq{grid-column-end:span 2}.h6FKJdwoY_zIXh6pEj1U{grid-column-end:span 3}.GkjDOzh_5n5VxKrqEW_I{grid-column-end:span 4}@media(min-width:600px){.V2HSxZHesNOXMBWOmdFG{grid-column-end:span 1}.i3fz9zvErDyXYherb6Ew{grid-column-end:span 2}._3bKcKfanUgurPMcHEiV{grid-column-end:span 3}.U44hqBGsmVA1lI9R2ZnH{grid-column-end:span 4}.AzaRYYQQYJjKUF9D87mw{grid-column-end:span 5}.sag3QKXTxIQXPPjmbrAl{grid-column-end:span 6}.ifY5fUWFNROS_F7Jys9v{grid-column-end:span 7}.nZFgLvw9LsUJnEViXnJZ{grid-column-end:span 8}}@media(min-width:960px){.Tqir6CbiyHOgYmsAgY57{grid-column-end:span 1}.VeVLz3DEagxiXMMPEOCe{grid-column-end:span 2}.G3R07cw2eP3D3juW3A3l{grid-column-end:span 3}.eQ3UseyneBNu_ZLuqSzg{grid-column-end:span 4}.VlgV8xjgkGoOLJShoyNM{grid-column-end:span 5}.DJCwqamaIDFQoj0jEgBc{grid-column-end:span 6}.CxtXPQUYJfNUQVn3y2y_{grid-column-end:span 7}.BroOgbyuFxoSv48H70B5{grid-column-end:span 8}.KnUcRFDSsxeb_z46ajNY{grid-column-end:span 9}.bXiAAxiM2mhcI43ET0pp{grid-column-end:span 10}.ipYVW2jwG2cFjIHpnQk2{grid-column-end:span 11}.KcREgfiJ4ix9nAvNAXqQ{grid-column-end:span 12}}@media(max-width:960px){.qSfEJeFAh4WmcPtmo8qe{display:none}}@media(max-width:600px){._RxxiRFsUS3nYTZhkprA{display:none}}.RSw9xhnEr8xpkX3hPPaM{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);display:block;margin:32px 0;padding:16px 64px 16px 24px;position:relative;text-decoration:none}.RSw9xhnEr8xpkX3hPPaM span{display:block}.RSw9xhnEr8xpkX3hPPaM span:last-of-type{font-weight:600}.RSw9xhnEr8xpkX3hPPaM:focus span:last-of-type,.RSw9xhnEr8xpkX3hPPaM:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.RSw9xhnEr8xpkX3hPPaM:focus:after,.RSw9xhnEr8xpkX3hPPaM:hover:after{transform:translateY(-50%) translateX(8px)}.RSw9xhnEr8xpkX3hPPaM:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;position:absolute;right:24px;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.kEBaF5NOHtQsQn5jfOEA{background:var(--jp-white-off);padding:48px 0 64px}.kEBaF5NOHtQsQn5jfOEA h1,.kEBaF5NOHtQsQn5jfOEA h2,.kEBaF5NOHtQsQn5jfOEA h3,.kEBaF5NOHtQsQn5jfOEA h4,.kEBaF5NOHtQsQn5jfOEA h5,.kEBaF5NOHtQsQn5jfOEA h6{line-height:1.2;margin-top:0}._UqttTwRJeajPRpRGZPJ{background-color:#fff;padding:64px 0}._UqttTwRJeajPRpRGZPJ h1,._UqttTwRJeajPRpRGZPJ h2,._UqttTwRJeajPRpRGZPJ h3,._UqttTwRJeajPRpRGZPJ h4,._UqttTwRJeajPRpRGZPJ h5,._UqttTwRJeajPRpRGZPJ h6{line-height:1.2;margin-top:0}.jp-connection-status-card h3{color:var(--jp-black);font-size:36px;font-weight:400;line-height:40px;margin:0}.jp-connection-status-card a,.jp-connection-status-card a:active,.jp-connection-status-card a:hover{color:var(--jp-black)}.jp-connection-status-card p{color:var(--jp-black);margin:16px 0}.jp-connection-status-card a,.jp-connection-status-card li,.jp-connection-status-card p{font-size:16px;line-height:24px}.jp-connection-status-card--status{align-items:center;display:flex;margin:24px 0 24px -6px}.jp-connection-status-card--cloud{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDIiIGhlaWdodD0iNDIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PG1hc2sgaWQ9ImEiIG1hc2stdHlwZT0iYWxwaGEiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjciIHk9IjkiIHdpZHRoPSIyOCIgaGVpZ2h0PSIyMSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zMC4zMzMgMTcuNjk3di0uMDhjMC00LjI1My0zLjc2LTcuNy04LjQtNy43LTMuOTE2IDAtNy4yMDYgMi40NTctOC4xMzYgNS43OGE2LjM4NiA2LjM4NiAwIDAgMC0uMjY0LS4wMDVDOS45MjUgMTUuNjkyIDcgMTguNzA4IDcgMjIuNDI5czIuOTI1IDYuNzM4IDYuNTMzIDYuNzM4SDI5LjRjMy4wOTMgMCA1LjYtMi41ODYgNS42LTUuNzc1IDAtMi44NjEtMi4wMTgtNS4yMzctNC42NjYtNS42OTVaIiBmaWxsPSIjZmZmIi8+PC9tYXNrPjxnIG1hc2s9InVybCgjYSkiPjxwYXRoIGQ9Im0zMC4zMzMgMTcuNjk3LTIuNjI1LS4wMy0uMDI1IDIuMjM1IDIuMjAyLjM4MS40NDgtMi41ODZabS0xNi41MzctMi0uMTA3IDIuNjIzIDIuMDc1LjA4NS41Ni0yLTIuNTI4LS43MDhabS0uMjYzIDEzLjQ3djIuNjI1aC4wMDhsLS4wMDgtMi42MjVabS4wNCAwdi0yLjYyNWgtLjAwOWwuMDA4IDIuNjI1Wm0xNS43OTUgMCAuMDE1LTIuNjI1aC0uMDE1djIuNjI1Wm0zLjU5LTExLjQ0MXYtLjExaC01LjI1di4wNTJsNS4yNS4wNThabTAtLjExYzAtNS45MTItNS4xNTUtMTAuMzI0LTExLjAyNS0xMC4zMjR2NS4yNWMzLjQxIDAgNS43NzUgMi40ODIgNS43NzUgNS4wNzVoNS4yNVpNMjEuOTMzIDcuMjkzYy00Ljk4NCAwLTkuMzg4IDMuMTM5LTEwLjY2NCA3LjY5OGw1LjA1NSAxLjQxNWMuNTg1LTIuMDg5IDIuNzYyLTMuODYzIDUuNjEtMy44NjN2LTUuMjVabS04LjAzIDUuNzgyYTkuMTExIDkuMTExIDAgMCAwLS4zNy0uMDA3djUuMjVjLjA1MyAwIC4xMDUuMDAxLjE1Ni4wMDNsLjIxNS01LjI0NlptLS4zNy0uMDA3Yy01LjEzMyAwLTkuMTU4IDQuMjY4LTkuMTU4IDkuMzYyaDUuMjVjMC0yLjM0OCAxLjgyNS00LjExMiAzLjkwOC00LjExMnYtNS4yNVptLTkuMTU4IDkuMzYyYzAgNS4wOTUgNC4wMjUgOS4zNjMgOS4xNTggOS4zNjN2LTUuMjVjLTIuMDgzIDAtMy45MDgtMS43NjUtMy45MDgtNC4xMTNoLTUuMjVabTkuMTY2IDkuMzYzaC4wNGwtLjAxNy01LjI1aC0uMDM5bC4wMTYgNS4yNVptLjAzMSAwaDE1Ljc5NnYtNS4yNUgxMy41NzJ2NS4yNVptMTUuODI3LTUuMjVoLS4wMTZsLS4wMyA1LjI1aC4wNDZ2LTUuMjVabTIuOTc1LTMuMTVjMCAxLjgxNi0xLjQwNyAzLjE1LTIuOTc1IDMuMTV2NS4yNWM0LjYxOCAwIDguMjI1LTMuODM4IDguMjI1LTguNGgtNS4yNVptLTIuNDg5LTMuMTA5YzEuMzU1LjIzNSAyLjQ5IDEuNDg4IDIuNDkgMy4xMDloNS4yNWMwLTQuMTAyLTIuOTAyLTcuNi02Ljg0NC04LjI4MmwtLjg5NiA1LjE3M1oiIGZpbGw9IiMxRTFFMUUiLz48L2c+PC9zdmc+);height:42px;margin-right:4px;width:42px}.jp-connection-status-card--jetpack-logo{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE2IDMyYzguODM3IDAgMTYtNy4xNjMgMTYtMTZTMjQuODM3IDAgMTYgMCAwIDcuMTYzIDAgMTZzNy4xNjMgMTYgMTYgMTZaIiBmaWxsPSIjMDAwIi8+PHBhdGggZD0iTTE2Ljc5NCAxMy4zMTN2MTUuNTExbDgtMTUuNTEyaC04Wk0xNS4xNzUgMTguNjU3VjMuMTc2bC03Ljk2OSAxNS40OGg3Ljk3WiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==);height:32px;margin-left:11px;width:32px}.jp-connection-status-card--btn-connect-user{background:var(--jp-black)!important;border-radius:4px;font-size:var(--font-body-small);height:40px}.jp-connection-status-card--avatar{background-color:var(--jp-white);background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='32' height='32' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='16' cy='16' r='16' fill='%23fff'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M4.498 27.123C6.038 24.165 10.916 21.5 16 21.5c5.084 0 9.963 2.665 11.502 5.623a15.952 15.952 0 0 1-11.257 4.875L16 32l-.245-.002a15.952 15.952 0 0 1-11.257-4.875zM16 8a6 6 0 1 1 0 12 6 6 0 0 1 0-12z' fill='%23A2AAB2'/%3E%3C/svg%3E");background-repeat:no-repeat;background-size:contain;border:0;border-radius:20px;height:32px;margin-left:-10px;width:32px}.jp-connection-status-card--line{border-top:2px solid var(--jp-black);height:0;width:67px}.jp-connection-status-card--line.jp-connection-status-card--site-only{border-top-style:dashed}.jp-connection-status-card--list{list-style-type:none;margin:16px 0}.jp-connection-status-card--list li{color:var(--jp-black);margin:0 0 8px -3px;padding-left:25px}.jp-connection-status-card--list-item-success{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTE4LjkzNSA2LTguOSAxMS45Ny01LjE2Mi0zLjgzNyIgc3Ryb2tlPSIjMUUxRTFFIiBzdHJva2Utd2lkdGg9IjEuNSIvPjwvc3ZnPg==) no-repeat 0 0}.jp-connection-status-card--list-item-error{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDIwYTggOCAwIDEgMCAwLTE2IDggOCAwIDAgMCAwIDE2WiIgc3Ryb2tlPSIjRDYzNjM5IiBzdHJva2Utd2lkdGg9IjEuNSIvPjxwYXRoIGQ9Ik0xMyA3aC0ydjZoMlY3Wk0xMyAxNWgtMnYyaDJ2LTJaIiBmaWxsPSIjRDYzNjM5Ii8+PC9zdmc+) no-repeat 0 0;color:var(--jp-red)!important}.jp-connection__disconnect-dialog h1{font-size:var(--font-title-small);font-weight:600;line-height:1.2;margin-top:0}.jp-connection__disconnect-dialog h2{font-size:var(--font-title-small);font-weight:400;line-height:1.2;margin:0}.jp-connection__disconnect-dialog p{font-size:var(--font-body);margin-top:0}.jp-connection__disconnect-dialog__large-text,.jp-connection__disconnect-dialog p.jp-connection__disconnect-dialog__large-text{font-size:1.25rem}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link,.jp-connection__disconnect-dialog__link{color:var(--jp-black);font-size:var(--font-body);font:inherit;height:auto;padding:0;text-decoration:underline}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link:hover,.jp-connection__disconnect-dialog__link:hover{color:var(--jp-black);text-decoration-thickness:var(--jp-underline-thickness)}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link:focus,.jp-connection__disconnect-dialog__link:focus{box-shadow:none!important;color:var(--jp-black)}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link--bold,.jp-connection__disconnect-dialog__link--bold{font-weight:700}.jp-connection__disconnect-dialog .components-button{border-radius:4px;font-size:var(--font-body-small);height:40px}.jp-connection__disconnect-dialog .components-modal__content{display:flex;flex-direction:column;flex-grow:1;margin:0;padding:0}.jp-connection__disconnect-dialog .components-modal__content:before,.jp-connection__disconnect-dialog .components-modal__header{display:none}.jp-connection__disconnect-dialog .jp-row{align-items:center;width:calc(100% - 48px)}.jp-connection__disconnect-dialog__content{align-items:center;background:var(--jp-white-off);border-radius:4px;display:flex;flex-direction:column;flex-grow:1;justify-content:center;margin:0;padding:2rem 1rem;text-align:center}.jp-connection__disconnect-dialog__actions{background:var(--jp-white);border-top:1px solid var(--jp-gray);bottom:0;padding:2rem 0;position:sticky}.jp-connection__disconnect-dialog__actions p{margin-bottom:0}.jp-connection__disconnect-dialog__actions:before{background:linear-gradient(to bottom,transparent,var(--jp-white-off));bottom:calc(100% + 1px);content:"";display:block;height:80px;left:0;position:absolute;width:100%}.jp-connection__disconnect-dialog__btn-dismiss,.jp-connection__disconnect-dialog__btn-dismiss.components-button{background:var(--jp-black)!important;margin-right:10px}.jp-connection__disconnect-dialog__btn-disconnect{background:var(--jp-red)!important}.jp-connection__disconnect-dialog__btn-back-to-wp{background:var(--jp-black)!important}.jp-connection__disconnect-dialog__button-wrap{text-align:left}@media(min-width:960px){.jp-connection__disconnect-dialog__button-wrap{text-align:center}}.jp-connection__disconnect-dialog__error{color:var(--jp-red)}.jp-connection__disconnect-dialog__survey{margin-bottom:1.5rem;max-width:100%}.jp-connection__disconnect-dialog__step-copy{margin:0 auto;max-width:800px}.jp-connection__disconnect-dialog__step-copy--narrow{max-width:600px}@media(max-height:900px){.jp-connection__disconnect-dialog__content .jp-components__decorative-card{display:none}}@media(min-width:600px){.jp-connection__disconnect-dialog,.jp-connection__disconnect-dialog.components-modal__frame{max-width:calc(100% - 32px);width:100%}.jp-connection__disconnect-dialog__actions,.jp-connection__disconnect-dialog__content{padding:2rem}}@media(min-width:960px){.jp-connection__disconnect-dialog,.jp-connection__disconnect-dialog.components-modal__frame{display:flex;flex-direction:column;height:900px;width:1200px}.jp-connection__disconnect-dialog h1{font-size:var(--font-title-large)}.jp-connection__disconnect-dialog__large-text,.jp-connection__disconnect-dialog p.jp-connection__disconnect-dialog__large-text{font-size:1.5rem}.jp-connection__disconnect-dialog__content{padding:80px}.jp-connection__disconnect-dialog__actions{padding:2rem 3rem}.jp-row{margin-left:0}}.jp-connection__disconnect-card{background-color:var(--jp-white);border:none;border-radius:3px;box-shadow:0 0 15px var(--jp-gray-off);margin:0 auto 1rem;max-width:100%;padding:1rem 2rem;text-align:left;width:800px}.jp-connection__disconnect-card__group{margin-bottom:1rem;max-width:100%}.jp-connection__disconnect-card__card-content{display:block;font-size:.875rem}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-content{align-items:center;display:flex;justify-content:space-between}}.jp-connection__disconnect-card .jp-connection__disconnect-card__card-headline,.jp-connection__disconnect-card__card-headline{flex-shrink:0;font-size:1.25rem;font-weight:600;margin-bottom:0;margin-top:0}@media only screen and (min-width:782px){.jp-connection__disconnect-card .jp-connection__disconnect-card__card-headline,.jp-connection__disconnect-card__card-headline{font-size:1.5rem;margin-right:1.5rem}}@media only screen and (max-width:782px){.jp-connection__disconnect-card .jp-connection__disconnect-card__card-headline+.jp-disconnect-card__card-stat-block,.jp-connection__disconnect-card__card-headline+.jp-disconnect-card__card-stat-block{margin-top:.5rem}}.jp-connection__disconnect-card__card-stat-block{align-items:baseline;display:flex;flex-grow:1}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-stat-block{flex-direction:row-reverse}}.jp-connection__disconnect-card__card-description{flex-grow:1}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-description{text-align:right}}.jp-connection__disconnect-card__card-stat{font-size:1rem;font-weight:600;margin-right:.5rem}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-stat{font-size:1.5rem;margin-left:1rem;margin-right:0}}.jp-components__decorative-card{border-radius:8px;box-shadow:0 0 15px var(--jp-gray);display:flex;height:280px;margin:0 auto 3rem;max-width:100%;overflow:hidden;position:relative;width:360px}.jp-components__decorative-card__content,.jp-components__decorative-card__image{width:50%}.jp-components__decorative-card__image{background:var(--jp-gray);background-size:cover;position:relative}.jp-components__decorative-card__image:before{background-image:url('data:image/svg+xml;uf8,<svg width="38" height="8" viewBox="0 0 38 8" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 7C1 7 2.37087 1 6.89831 1C11.4257 1 14.3709 7 18.8983 7C23.4257 7 26.7777 1 31.3051 1C35.912 1 37 7 37 7" stroke="white" stroke-width="1.5" stroke-linejoin="round"/></svg>');content:"";display:block;height:8px;left:24px;position:absolute;top:24px;width:38px}.jp-components__decorative-card__content{background:#fff;padding:2rem}.jp-components__decorative-card__icon-container{background:var(--jp-red);border-radius:50px;height:80px;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);width:80px}.jp-components__decorative-card__icon{background-position:50%,50%;background-repeat:no-repeat;height:40px;left:50%;position:absolute;top:50%;transform:translate(-50%,-50%);width:40px}.jp-components__decorative-card__icon--unlink{background-image:url('data:image/svg+xml;uf8,<svg width="34" height="37" viewBox="0 0 34 37" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M22.3335 10.001H25.0002C29.4184 10.001 33.0002 13.5827 33.0002 18.001V19.7788C33.0002 24.197 29.4184 27.7788 25.0002 27.7788H22.3335" stroke="white" stroke-width="1.5" stroke-linecap="square"/> <path d="M11.6675 27.7783L9.00082 27.7783C4.58254 27.7783 1.00081 24.1966 1.00081 19.7783L1.00081 18.0005C1.00081 13.5823 4.58253 10.0005 9.00081 10.0005L11.6675 10.0005" stroke="white" stroke-width="1.5" stroke-linecap="square"/> <path d="M10.9998 19.167L16.9998 19.167" stroke="white" stroke-width="1.5"/> <path d="M8.99951 35.998L24.9995 0.998048" stroke="white"/> </svg>')}.jp-components__decorative-card__lines,.jp-components__decorative-card__lines:after,.jp-components__decorative-card__lines:before{background:#e9eff5;border-radius:6px;display:block;height:12px;position:relative;width:100%}.jp-components__decorative-card__lines:after,.jp-components__decorative-card__lines:before{content:"";top:calc(100% + 16px)}.jp-components__decorative-card__lines:after{top:calc(100% + 32px);width:75%}.jp-components__decorative-card--vertical{flex-direction:column}.jp-components__decorative-card--vertical .jp-components__decorative-card__content,.jp-components__decorative-card--vertical .jp-components__decorative-card__image{height:50%;width:100%}.jp-components__decorative-card--vertical .jp-components__decorative-card__lines{margin-left:auto;margin-right:auto;max-width:135px}.jp-components__decorative-card--vertical .jp-components__decorative-card__lines:after,.jp-components__decorative-card--vertical .jp-components__decorative-card__lines:before{margin-left:auto;margin-right:auto}:root{--font-title-large:36px;--font-title-small:24px;--font-body:16px;--font-label:12px;--jp-black:#000;--jp-black-80:#2c3338;--jp-white:#fff;--jp-white-off:#f9f9f6;--jp-gray:#dcdcde;--jp-gray-0:#f6f7f7;--jp-gray-20:#a7aaad;--jp-gray-40:#787c82;--jp-gray-50:#646970;--jp-gray-60:#50575e;--jp-gray-80:#8a2424;--jp-gray-off:#e2e2df;--jp-red-0:#f7ebec;--jp-red-50:#d63638;--jp-red-60:#b32d2e;--jp-red-80:#8a2424;--jp-red:#d63639;--jp-pink:#c9356e;--jp-green-0:#f0f2eb;--jp-green-5:#d0e6b8;--jp-green-10:#9dd977;--jp-green-20:#64ca43;--jp-green-30:#2fb41f;--jp-green-40:#069e08;--jp-green-50:#008710;--jp-green-60:#007117;--jp-green-70:#005b18;--jp-green-80:#004515;--jp-green-90:#003010;--jp-green-100:#001c09;--jp-green:#069e08;--jp-green-primary:var( --jp-green-40 );--jp-green-secondary:var( --jp-green-30 );--jp-border-radius:4px;--jp-menu-border-height:1px;--jp-underline-thickness:2px}*{box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;margin:0;min-height:100%;padding:0}.jp-wrap{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.jp-row{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.jp-row{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.jp-row{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.sm-col-span-1{grid-column-end:span 1}.sm-col-span-2{grid-column-end:span 2}.sm-col-span-3{grid-column-end:span 3}.sm-col-span-4{grid-column-end:span 4}@media(min-width:600px){.md-col-span-1{grid-column-end:span 1}.md-col-span-2{grid-column-end:span 2}.md-col-span-3{grid-column-end:span 3}.md-col-span-4{grid-column-end:span 4}.md-col-span-5{grid-column-end:span 5}.md-col-span-6{grid-column-end:span 6}.md-col-span-7{grid-column-end:span 7}.md-col-span-8{grid-column-end:span 8}}@media(min-width:960px){.lg-col-span-1{grid-column-end:span 1}.lg-col-span-2{grid-column-end:span 2}.lg-col-span-3{grid-column-end:span 3}.lg-col-span-4{grid-column-end:span 4}.lg-col-span-5{grid-column-end:span 5}.lg-col-span-6{grid-column-end:span 6}.lg-col-span-7{grid-column-end:span 7}.lg-col-span-8{grid-column-end:span 8}.lg-col-span-9{grid-column-end:span 9}.lg-col-span-10{grid-column-end:span 10}.lg-col-span-11{grid-column-end:span 11}.lg-col-span-12{grid-column-end:span 12}}@media(max-width:960px){.md-col-span-0{display:none}}@media(max-width:600px){.sm-col-span-0{display:none}}.jp-cut{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);margin:32px 0;padding:16px 64px 16px 24px;position:relative;text-decoration:none}.jp-cut,.jp-cut span{display:block}.jp-cut span:last-of-type{font-weight:600}.jp-cut:focus span:last-of-type,.jp-cut:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.jp-cut:focus:after,.jp-cut:hover:after{transform:translateY(-50%) translateX(8px)}.jp-cut:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;position:absolute;right:24px;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.jp-connect__disconnect-survey-card{border:2px solid transparent;border-radius:4px;box-shadow:0 0 15px var(--jp-gray-off);margin-left:auto;margin-right:auto;max-width:100%;padding:1rem;position:relative;text-align:left;width:800px}.jp-connect__disconnect-survey-card--selected{background:var(--jp-gray-off);border-color:var(--jp-black)}.jp-connect__disconnect-survey-card:after{border-right:2px solid var(--jp-black);border-top:2px solid var(--jp-black);content:"";display:block;height:5px;position:absolute;right:1.5rem;top:50%;transform:translateY(-50%) rotate(45deg);width:5px}.jp-connect__disconnect-survey-card:hover{cursor:pointer}.jp-connect__disconnect-survey-card:focus:not(.jp-disconnect-survey-card--selected),.jp-connect__disconnect-survey-card:hover:not(.jp-disconnect-survey-card--selected){border-color:var(--jp-black-80)}.jp-connect__disconnect-survey-card__answer{align-items:center;display:flex;font-weight:700;margin:0}input.jp-connect__disconnect-survey-card__input{-webkit-appearance:none;background-color:transparent;border:none;color:var(--jp-black-80);flex-grow:1;max-width:calc(100% - 40px);padding-right:40px} \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.js
new file mode 100644
index 00000000..b5192a4a
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.js
@@ -0,0 +1,2 @@
+/*! For license information please see index.js.LICENSE.txt */
+(()=>{var e={7538:e=>{e.exports=function(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e},e.exports.default=e.exports,e.exports.__esModule=!0},9183:e=>{function t(){return e.exports=t=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var c in n)Object.prototype.hasOwnProperty.call(n,c)&&(e[c]=n[c])}return e},e.exports.default=e.exports,e.exports.__esModule=!0,t.apply(this,arguments)}e.exports=t,e.exports.default=e.exports,e.exports.__esModule=!0},9105:(e,t)=>{var n;!function(){"use strict";var c={}.hasOwnProperty;function o(){for(var e=[],t=0;t<arguments.length;t++){var n=arguments[t];if(n){var s=typeof n;if("string"===s||"number"===s)e.push(n);else if(Array.isArray(n)){if(n.length){var a=o.apply(null,n);a&&e.push(a)}}else if("object"===s)if(n.toString===Object.prototype.toString)for(var r in n)c.call(n,r)&&n[r]&&e.push(r);else e.push(n.toString())}}return e.join(" ")}e.exports?(o.default=o,e.exports=o):void 0===(n=function(){return o}.apply(t,[]))||(e.exports=n)}()},5771:(e,t,n)=>{t.formatArgs=function(t){if(t[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+t[0]+(this.useColors?"%c ":" ")+"+"+e.exports.humanize(this.diff),!this.useColors)return;const n="color: "+this.color;t.splice(1,0,n,"color: inherit");let c=0,o=0;t[0].replace(/%[a-zA-Z%]/g,(e=>{"%%"!==e&&(c++,"%c"===e&&(o=c))})),t.splice(o,0,n)},t.save=function(e){try{e?t.storage.setItem("debug",e):t.storage.removeItem("debug")}catch(e){}},t.load=function(){let e;try{e=t.storage.getItem("debug")}catch(e){}!e&&"undefined"!=typeof process&&"env"in process&&(e=process.env.DEBUG);return e},t.useColors=function(){if("undefined"!=typeof window&&window.process&&("renderer"===window.process.type||window.process.__nwjs))return!0;if("undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/))return!1;return"undefined"!=typeof document&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||"undefined"!=typeof window&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||"undefined"!=typeof navigator&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)},t.storage=function(){try{return localStorage}catch(e){}}(),t.destroy=(()=>{let e=!1;return()=>{e||(e=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})(),t.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"],t.log=console.debug||console.log||(()=>{}),e.exports=n(1244)(t);const{formatters:c}=e.exports;c.j=function(e){try{return JSON.stringify(e)}catch(e){return"[UnexpectedJSONParseError]: "+e.message}}},1244:(e,t,n)=>{e.exports=function(e){function t(e){let n,o,s,a=null;function r(...e){if(!r.enabled)return;const c=r,o=Number(new Date),s=o-(n||o);c.diff=s,c.prev=n,c.curr=o,n=o,e[0]=t.coerce(e[0]),"string"!=typeof e[0]&&e.unshift("%O");let a=0;e[0]=e[0].replace(/%([a-zA-Z%])/g,((n,o)=>{if("%%"===n)return"%";a++;const s=t.formatters[o];if("function"==typeof s){const t=e[a];n=s.call(c,t),e.splice(a,1),a--}return n})),t.formatArgs.call(c,e);(c.log||t.log).apply(c,e)}return r.namespace=e,r.useColors=t.useColors(),r.color=t.selectColor(e),r.extend=c,r.destroy=t.destroy,Object.defineProperty(r,"enabled",{enumerable:!0,configurable:!1,get:()=>null!==a?a:(o!==t.namespaces&&(o=t.namespaces,s=t.enabled(e)),s),set:e=>{a=e}}),"function"==typeof t.init&&t.init(r),r}function c(e,n){const c=t(this.namespace+(void 0===n?":":n)+e);return c.log=this.log,c}function o(e){return e.toString().substring(2,e.toString().length-2).replace(/\.\*\?$/,"*")}return t.debug=t,t.default=t,t.coerce=function(e){if(e instanceof Error)return e.stack||e.message;return e},t.disable=function(){const e=[...t.names.map(o),...t.skips.map(o).map((e=>"-"+e))].join(",");return t.enable(""),e},t.enable=function(e){let n;t.save(e),t.namespaces=e,t.names=[],t.skips=[];const c=("string"==typeof e?e:"").split(/[\s,]+/),o=c.length;for(n=0;n<o;n++)c[n]&&("-"===(e=c[n].replace(/\*/g,".*?"))[0]?t.skips.push(new RegExp("^"+e.substr(1)+"$")):t.names.push(new RegExp("^"+e+"$")))},t.enabled=function(e){if("*"===e[e.length-1])return!0;let n,c;for(n=0,c=t.skips.length;n<c;n++)if(t.skips[n].test(e))return!1;for(n=0,c=t.names.length;n<c;n++)if(t.names[n].test(e))return!0;return!1},t.humanize=n(2002),t.destroy=function(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")},Object.keys(e).forEach((n=>{t[n]=e[n]})),t.names=[],t.skips=[],t.formatters={},t.selectColor=function(e){let n=0;for(let t=0;t<e.length;t++)n=(n<<5)-n+e.charCodeAt(t),n|=0;return t.colors[Math.abs(n)%t.colors.length]},t.enable(t.load()),t}},6619:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});const c={"jp-wrap":"O5NYbFTsxmrm4P2SIdOC","jp-row":"rkV4U_hzC04NwXFQcTHl","sm-col-span-1":"G81E6mesnld_OhhOvz_F","sm-col-span-2":"SfCZpjwiu2F0KDjGIXEA","sm-col-span-3":"qPxwbSu_GTDTz_8jFHJD","sm-col-span-4":"xYAWQ0wk6FPfZOZQgFiw","md-col-span-1":"zwZjA7ofpMucaB_UzG48","md-col-span-2":"YfwKGGBEqiINFYy6ZpKM","md-col-span-3":"pcHQbVFRxA_OvNRQ2OwP","md-col-span-4":"U_LJMBlRZ8ItXZuvCXGg","md-col-span-5":"st_7w_Ja1Gp2AgGaTysO","md-col-span-6":"gQsaHmJo6Gp7Yq9IB9Sg","md-col-span-7":"P0a4FWsiJJmR6bySI8QC","md-col-span-8":"UxSHj7jyDp6sGKHILNRc","lg-col-span-1":"TExt5ebNqUrEn2NzeDDh","lg-col-span-2":"cdwvRRac4c2djSpHfOpF","lg-col-span-3":"hvRpxlyFY9BQIDdEGTjg","lg-col-span-4":"Ev9JGJi3yKkBq6cW3Xge","lg-col-span-5":"mhL__tIHFH_tViX5718D","lg-col-span-6":"X1lGIxHHxsFl_39u4Px6","lg-col-span-7":"pc5UnY_DzsSDkyih78Ti","lg-col-span-8":"QCjBtfnG3u20FwoDd59Z","lg-col-span-9":"MX1aL3BeJpoSE0aXghp0","lg-col-span-10":"h_JAcO8a8ClV2LmTWsMz","lg-col-span-11":"gKlDMi0N7LOd9q8uJHi5","lg-col-span-12":"KZWhYB7r6TG75uJu2LsQ","md-col-span-0":"G6NxfG2gGwvEYb0aGdPJ","sm-col-span-0":"aceZPGfrg7IoR0Vu7ZJg","jp-cut":"MZSHJVi991kNJhn_Xnip","jp-admin-page":"EZ590eIMC5y_t1_gWRua","jp-admin-page-section":"BYzOly4G7Gry9wdRH0AY"}},9855:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});const c={"jp-admin-section":"_UqttTwRJeajPRpRGZPJ"}},8459:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});const c={"jp-wrap":"K6gniZequRm1zBi4xa2G","jp-row":"vickZxEIl6kGZp7KkFYF","sm-col-span-1":"U2ZD1antXfCcQlBdFwyw","sm-col-span-2":"caLSPVQ3JP3b2Ia68Seq","sm-col-span-3":"h6FKJdwoY_zIXh6pEj1U","sm-col-span-4":"GkjDOzh_5n5VxKrqEW_I","md-col-span-1":"V2HSxZHesNOXMBWOmdFG","md-col-span-2":"i3fz9zvErDyXYherb6Ew","md-col-span-3":"_3bKcKfanUgurPMcHEiV","md-col-span-4":"U44hqBGsmVA1lI9R2ZnH","md-col-span-5":"AzaRYYQQYJjKUF9D87mw","md-col-span-6":"sag3QKXTxIQXPPjmbrAl","md-col-span-7":"ifY5fUWFNROS_F7Jys9v","md-col-span-8":"nZFgLvw9LsUJnEViXnJZ","lg-col-span-1":"Tqir6CbiyHOgYmsAgY57","lg-col-span-2":"VeVLz3DEagxiXMMPEOCe","lg-col-span-3":"G3R07cw2eP3D3juW3A3l","lg-col-span-4":"eQ3UseyneBNu_ZLuqSzg","lg-col-span-5":"VlgV8xjgkGoOLJShoyNM","lg-col-span-6":"DJCwqamaIDFQoj0jEgBc","lg-col-span-7":"CxtXPQUYJfNUQVn3y2y_","lg-col-span-8":"BroOgbyuFxoSv48H70B5","lg-col-span-9":"KnUcRFDSsxeb_z46ajNY","lg-col-span-10":"bXiAAxiM2mhcI43ET0pp","lg-col-span-11":"ipYVW2jwG2cFjIHpnQk2","lg-col-span-12":"KcREgfiJ4ix9nAvNAXqQ","md-col-span-0":"qSfEJeFAh4WmcPtmo8qe","sm-col-span-0":"_RxxiRFsUS3nYTZhkprA","jp-cut":"RSw9xhnEr8xpkX3hPPaM","jp-admin-section-hero":"kEBaF5NOHtQsQn5jfOEA"}},8776:()=>{},3220:()=>{},4555:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});const c={"jp-wrap":"WcIPVMUrQMdwzisuVSh7","jp-row":"Nxx7T20LwPUQPXCSDMfA","sm-col-span-1":"mmVmo1nZYrRywmnSKQFx","sm-col-span-2":"UrIp26XpdUSMJft8YxdC","sm-col-span-3":"pMXlFWglEDyK6MbwJ8sX","sm-col-span-4":"P5H1ohz9m5_rEqZFAkFm","md-col-span-1":"ofLUn8LwR3LLeWl8tPdU","md-col-span-2":"fq1Z8JYbcrrXavvSfzKk","md-col-span-3":"jq9gui11HqTDfI7tXH1d","md-col-span-4":"mVYkSRC7j44WiWLZHOnM","md-col-span-5":"S3wOGmx7YLTrqz_bcLth","md-col-span-6":"Y3GW3wokLgm9jnX78Uwk","md-col-span-7":"ShMEdZjpjdYj7mCQzrSO","md-col-span-8":"nfBAID75QGC1VZ8t0RfR","lg-col-span-1":"Vr2EQcrmKOPJtFU72Vv2","lg-col-span-2":"CTS6MNweODFo4ZxcT0iV","lg-col-span-3":"XTISRluUo3o5xxnPNu09","lg-col-span-4":"c_EtRaSOJafAl5r9WkBm","lg-col-span-5":"HcpW_q5aO8Bf_ngIjyjv","lg-col-span-6":"XF3r0hMrFrrmxH5TJee0","lg-col-span-7":"Jl9ognyJ9XOZ6g0BTzLf","lg-col-span-8":"_8w8oD2R9CVt9AU4PvUy","lg-col-span-9":"ltOXxurwUtxy7XIR_loo","lg-col-span-10":"bKUzzGEJ3wCoAOZZvVCK","lg-col-span-11":"GltQVCPa1x4tZ7sWFg1v","lg-col-span-12":"UzWicuFiKrGgMEjmRAFA","md-col-span-0":"CDwHAcVQxDeV2mFXS1Dz","sm-col-span-0":"sr1184KrdJ0UtgNsMQnR","jp-cut":"MnTtKIHRyzXYoesgRCgm"}},8422:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});const c={"jp-container":"O8YnwnZegI89S2Q3k5Qn"}},7941:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});const c={"jp-wrap":"iwMEh2CwJ_r23Y9sVcJ1","jp-row":"HwO79godDUI97Xub9ey4","sm-col-span-1":"EC1Yirv2kOgRU77F2XRd","sm-col-span-2":"FHZqFvZXEDJ9mpOm7CeY","sm-col-span-3":"VOmONLKUkcSrIRYmX6YZ","sm-col-span-4":"eECO55N5YGkBB3tKa0If","md-col-span-1":"u3ZB3zRkuglILO6FSIrx","md-col-span-2":"iZnPl6piWO_GqHn16XKi","md-col-span-3":"WWctfqi1wHZDWrue8GIt","md-col-span-4":"p678NN_M0K78Kpprfndu","md-col-span-5":"_6D2TXtGKmepmwrxSf5DW","md-col-span-6":"cWORs5VDjuStXsiVPtiO","md-col-span-7":"yJsc8qt82L9miwDoe2Y2","md-col-span-8":"hD6bXWfNn5elhKCaBFTD","lg-col-span-1":"k9j6XIJdXbQ5YNyU8sNH","lg-col-span-2":"TnVGX0JHm_muRRcmYYwV","lg-col-span-3":"VILIswQXi8XzNJ3BqePc","lg-col-span-4":"h7mXuoRFWMGCiWYH4Ij1","lg-col-span-5":"uHYkk5STnhikqYMIxtE1","lg-col-span-6":"E8zp8nDOvAQ7VHsXKJJw","lg-col-span-7":"jwOhfvvsXGTCtHfB2854","lg-col-span-8":"DmSEZVFn_2XNNKJGgkXw","lg-col-span-9":"RdgOIY1qMZ8eqAeNSGNw","lg-col-span-10":"XBQd6xiLipeHg0O6cf7N","lg-col-span-11":"JyQ6Tbn3FCHfrCEyWrAr","lg-col-span-12":"fzJ_qaXYWs3EWQEupbLD","md-col-span-0":"LE2ZLIt_Y0slsr6TSmqe","sm-col-span-0":"vYxwZbK_covZoLjDe3BM","jp-cut":"fmzqGXmbTSiUblRujgqB"}},404:()=>{},1294:()=>{},1545:()=>{},843:()=>{},3498:()=>{},2002:e=>{var t=1e3,n=60*t,c=60*n,o=24*c,s=7*o,a=365.25*o;function r(e,t,n,c){var o=t>=1.5*n;return Math.round(e/n)+" "+c+(o?"s":"")}e.exports=function(e,i){i=i||{};var l=typeof e;if("string"===l&&e.length>0)return function(e){if((e=String(e)).length>100)return;var r=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!r)return;var i=parseFloat(r[1]);switch((r[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return i*a;case"weeks":case"week":case"w":return i*s;case"days":case"day":case"d":return i*o;case"hours":case"hour":case"hrs":case"hr":case"h":return i*c;case"minutes":case"minute":case"mins":case"min":case"m":return i*n;case"seconds":case"second":case"secs":case"sec":case"s":return i*t;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return i;default:return}}(e);if("number"===l&&isFinite(e))return i.long?function(e){var s=Math.abs(e);if(s>=o)return r(e,s,o,"day");if(s>=c)return r(e,s,c,"hour");if(s>=n)return r(e,s,n,"minute");if(s>=t)return r(e,s,t,"second");return e+" ms"}(e):function(e){var s=Math.abs(e);if(s>=o)return Math.round(e/o)+"d";if(s>=c)return Math.round(e/c)+"h";if(s>=n)return Math.round(e/n)+"m";if(s>=t)return Math.round(e/t)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))}},9587:(e,t,n)=>{"use strict";var c=n(5843);function o(){}function s(){}s.resetWarningCache=o,e.exports=function(){function e(e,t,n,o,s,a){if(a!==c){var r=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw r.name="Invariant Violation",r}}function t(){return e}e.isRequired=e;var n={array:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:s,resetWarningCache:o};return n.PropTypes=n,n}},1268:(e,t,n)=>{e.exports=n(9587)()},5843:e=>{"use strict";e.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},816:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var c=n(5771),o=n.n(c),s=n(2819);const a=o()("dops:analytics");let r,i;window._tkq=window._tkq||[],window.ga=window.ga||function(){(window.ga.q=window.ga.q||[]).push(arguments)},window.ga.l=+new Date;const l={initialize:function(e,t,n){l.setUser(e,t),l.setSuperProps(n),l.identifyUser()},setGoogleAnalyticsEnabled:function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;this.googleAnalyticsEnabled=e,this.googleAnalyticsKey=t},setMcAnalyticsEnabled:function(e){this.mcAnalyticsEnabled=e},setUser:function(e,t){i={ID:e,username:t}},setSuperProps:function(e){r=e},mc:{bumpStat:function(e,t){const n=function(e,t){let n="";if("object"==typeof e){for(const t in e)n+="&x_"+encodeURIComponent(t)+"="+encodeURIComponent(e[t]);a("Bumping stats %o",e)}else n="&x_"+encodeURIComponent(e)+"="+encodeURIComponent(t),a('Bumping stat "%s" in group "%s"',t,e);return n}(e,t);this.mcAnalyticsEnabled&&((new Image).src=document.location.protocol+"//pixel.wp.com/g.gif?v=wpcom-no-pv"+n+"&t="+Math.random())},bumpStatWithPageView:function(e,t){const n=function(e,t){let n="";if("object"==typeof e){for(const t in e)n+="&"+encodeURIComponent(t)+"="+encodeURIComponent(e[t]);a("Built stats %o",e)}else n="&"+encodeURIComponent(e)+"="+encodeURIComponent(t),a('Built stat "%s" in group "%s"',t,e);return n}(e,t);this.mcAnalyticsEnabled&&((new Image).src=document.location.protocol+"//pixel.wp.com/g.gif?v=wpcom"+n+"&t="+Math.random())}},pageView:{record:function(e,t){l.tracks.recordPageView(e),l.ga.recordPageView(e,t)}},purchase:{record:function(e,t,n,c,o,s,a){l.ga.recordPurchase(e,t,n,c,o,s,a)}},tracks:{recordEvent:function(e,t){t=t||{},0===e.indexOf("akismet_")||0===e.indexOf("jetpack_")?(r&&(a("- Super Props: %o",r),t=(0,s.assign)(t,r)),a('Record event "%s" called with props %s',e,JSON.stringify(t)),window._tkq.push(["recordEvent",e,t])):a('- Event name must be prefixed by "akismet_" or "jetpack_"')},recordJetpackClick:function(e){const t="object"==typeof e?e:{target:e};l.tracks.recordEvent("jetpack_wpa_click",t)},recordPageView:function(e){l.tracks.recordEvent("akismet_page_view",{path:e})},setOptOut:function(e){a("Pushing setOptOut: %o",e),window._tkq.push(["setOptOut",e])}},ga:{initialized:!1,initialize:function(){let e={};l.ga.initialized||(i&&(e={userId:"u-"+i.ID}),window.ga("create",this.googleAnalyticsKey,"auto",e),l.ga.initialized=!0)},recordPageView:function(e,t){l.ga.initialize(),a("Recording Page View ~ [URL: "+e+"] [Title: "+t+"]"),this.googleAnalyticsEnabled&&(window.ga("set","page",e),window.ga("send",{hitType:"pageview",page:e,title:t}))},recordEvent:function(e,t,n,c){l.ga.initialize();let o="Recording Event ~ [Category: "+e+"] [Action: "+t+"]";void 0!==n&&(o+=" [Option Label: "+n+"]"),void 0!==c&&(o+=" [Option Value: "+c+"]"),a(o),this.googleAnalyticsEnabled&&window.ga("send","event",e,t,n,c)},recordPurchase:function(e,t,n,c,o,s,a){window.ga("require","ecommerce"),window.ga("ecommerce:addTransaction",{id:e,revenue:c,currency:a}),window.ga("ecommerce:addItem",{id:e,name:t,sku:n,price:o,quantity:s}),window.ga("ecommerce:send")}},identifyUser:function(){i&&window._tkq.push(["identifyUser",i.ID,i.username])},setProperties:function(e){window._tkq.push(["setProperties",e])},clearedIdentity:function(){window._tkq.push(["clearIdentity"])}},p=l},9570:(e,t,n)=>{"use strict";n.d(t,{ZP:()=>u});var c=n(2819),o=n(6483),s=n(6251);function a(e){class t extends Error{constructor(){super(...arguments),this.name=e}}return t}const r=a("JsonParseError"),i=a("JsonParseAfterRedirectError"),l=a("Api404Error"),p=a("Api404AfterRedirectError"),d=a("FetchNetworkError");const u=new function(e,t){let n=e,a={"X-WP-Nonce":t},r={credentials:"same-origin",headers:a},i={method:"post",credentials:"same-origin",headers:(0,c.assign)({},a,{"Content-type":"application/json"})},l=function(e){const t=e.split("?"),n=t.length>1?t[1]:"",c=n.length?n.split("&"):[];return c.push("_cacheBuster="+(new Date).getTime()),t[0]+"?"+c.join("&")};const p={setApiRoot(e){n=e},setApiNonce(e){a={"X-WP-Nonce":e},r={credentials:"same-origin",headers:a},i={method:"post",credentials:"same-origin",headers:(0,c.assign)({},a,{"Content-type":"application/json"})}},setCacheBusterCallback:e=>{l=e},registerSite:(e,t)=>{const c={registration_nonce:e,no_iframe:!0};return(0,s.jetpackConfigHas)("consumer_slug")&&(c.plugin_slug=(0,s.jetpackConfigGet)("consumer_slug")),null!==t&&(c.redirect_uri=t),u(`${n}jetpack/v4/connection/register`,i,{body:JSON.stringify(c)}).then(m).then(g)},fetchAuthorizationUrl:e=>d((0,o.addQueryArgs)(`${n}jetpack/v4/connection/authorize_url`,{no_iframe:"1",redirect_uri:e}),r).then(m).then(g),fetchSiteConnectionData:()=>d(`${n}jetpack/v4/connection/data`,r).then(g),fetchSiteConnectionStatus:()=>d(`${n}jetpack/v4/connection`,r).then(g),fetchSiteConnectionTest:()=>d(`${n}jetpack/v4/connection/test`,r).then(m).then(g),fetchUserConnectionData:()=>d(`${n}jetpack/v4/connection/data`,r).then(g),fetchUserTrackingSettings:()=>d(`${n}jetpack/v4/tracking/settings`,r).then(m).then(g),updateUserTrackingSettings:e=>u(`${n}jetpack/v4/tracking/settings`,i,{body:JSON.stringify(e)}).then(m).then(g),disconnectSite:()=>u(`${n}jetpack/v4/connection`,i,{body:JSON.stringify({isActive:!1})}).then(m).then(g),fetchConnectUrl:()=>d(`${n}jetpack/v4/connection/url`,r).then(m).then(g),unlinkUser:()=>u(`${n}jetpack/v4/connection/user`,i,{body:JSON.stringify({linked:!1})}).then(m).then(g),reconnect:()=>u(`${n}jetpack/v4/connection/reconnect`,i).then(m).then(g),fetchConnectedPlugins:()=>d(`${n}jetpack/v4/connection/plugins`,r).then(m).then(g),setHasSeenWCConnectionModal:()=>u(`${n}jetpack/v4/seen-wc-connection-modal`,i).then(m).then(g),fetchModules:()=>d(`${n}jetpack/v4/module/all`,r).then(m).then(g),fetchModule:e=>d(`${n}jetpack/v4/module/${e}`,r).then(m).then(g),activateModule:e=>u(`${n}jetpack/v4/module/${e}/active`,i,{body:JSON.stringify({active:!0})}).then(m).then(g),deactivateModule:e=>u(`${n}jetpack/v4/module/${e}/active`,i,{body:JSON.stringify({active:!1})}),updateModuleOptions:(e,t)=>u(`${n}jetpack/v4/module/${e}`,i,{body:JSON.stringify(t)}).then(m).then(g),updateSettings:e=>u(`${n}jetpack/v4/settings`,i,{body:JSON.stringify(e)}).then(m).then(g),getProtectCount:()=>d(`${n}jetpack/v4/module/protect/data`,r).then(m).then(g),resetOptions:e=>u(`${n}jetpack/v4/options/${e}`,i,{body:JSON.stringify({reset:!0})}).then(m).then(g),activateVaultPress:()=>u(`${n}jetpack/v4/plugins`,i,{body:JSON.stringify({slug:"vaultpress",status:"active"})}).then(m).then(g),getVaultPressData:()=>d(`${n}jetpack/v4/module/vaultpress/data`,r).then(m).then(g),installPlugin:(e,t)=>{const c={slug:e,status:"active"};return t&&(c.source=t),u(`${n}jetpack/v4/plugins`,i,{body:JSON.stringify(c)}).then(m).then(g)},activateAkismet:()=>u(`${n}jetpack/v4/plugins`,i,{body:JSON.stringify({slug:"akismet",status:"active"})}).then(m).then(g),getAkismetData:()=>d(`${n}jetpack/v4/module/akismet/data`,r).then(m).then(g),checkAkismetKey:()=>d(`${n}jetpack/v4/module/akismet/key/check`,r).then(m).then(g),checkAkismetKeyTyped:e=>u(`${n}jetpack/v4/module/akismet/key/check`,i,{body:JSON.stringify({api_key:e})}).then(m).then(g),fetchStatsData:e=>d(function(e){let t=`${n}jetpack/v4/module/stats/data`;-1!==t.indexOf("?")?t+=`&range=${encodeURIComponent(e)}`:t+=`?range=${encodeURIComponent(e)}`;return t}(e),r).then(m).then(g).then(h),getPluginUpdates:()=>d(`${n}jetpack/v4/updates/plugins`,r).then(m).then(g),getPlans:()=>d(`${n}jetpack/v4/plans`,r).then(m).then(g),fetchSettings:()=>d(`${n}jetpack/v4/settings`,r).then(m).then(g),updateSetting:e=>u(`${n}jetpack/v4/settings`,i,{body:JSON.stringify(e)}).then(m).then(g),fetchSiteData:()=>d(`${n}jetpack/v4/site`,r).then(m).then(g).then((e=>JSON.parse(e.data))),fetchSiteFeatures:()=>d(`${n}jetpack/v4/site/features`,r).then(m).then(g).then((e=>JSON.parse(e.data))),fetchSiteProducts:()=>d(`${n}jetpack/v4/site/products`,r).then(m).then(g),fetchSitePurchases:()=>d(`${n}jetpack/v4/site/purchases`,r).then(m).then(g).then((e=>JSON.parse(e.data))),fetchSiteBenefits:()=>d(`${n}jetpack/v4/site/benefits`,r).then(m).then(g).then((e=>JSON.parse(e.data))),fetchSetupQuestionnaire:()=>d(`${n}jetpack/v4/setup/questionnaire`,r).then(m).then(g),fetchRecommendationsData:()=>d(`${n}jetpack/v4/recommendations/data`,r).then(m).then(g),fetchRecommendationsProductSuggestions:()=>d(`${n}jetpack/v4/recommendations/product-suggestions`,r).then(m).then(g),fetchRecommendationsUpsell:()=>d(`${n}jetpack/v4/recommendations/upsell`,r).then(m).then(g),saveRecommendationsData:e=>u(`${n}jetpack/v4/recommendations/data`,i,{body:JSON.stringify({data:e})}).then(m),fetchProducts:()=>d(`${n}jetpack/v4/products`,r).then(m).then(g),fetchRewindStatus:()=>d(`${n}jetpack/v4/rewind`,r).then(m).then(g).then((e=>JSON.parse(e.data))),fetchScanStatus:()=>d(`${n}jetpack/v4/scan`,r).then(m).then(g).then((e=>JSON.parse(e.data))),dismissJetpackNotice:e=>u(`${n}jetpack/v4/notice/${e}`,i,{body:JSON.stringify({dismissed:!0})}).then(m).then(g),fetchPluginsData:()=>d(`${n}jetpack/v4/plugins`,r).then(m).then(g),fetchVerifySiteGoogleStatus:e=>d(null!==e?`${n}jetpack/v4/verify-site/google/${e}`:`${n}jetpack/v4/verify-site/google`,r).then(m).then(g),verifySiteGoogle:e=>u(`${n}jetpack/v4/verify-site/google`,i,{body:JSON.stringify({keyring_id:e})}).then(m).then(g),sendMobileLoginEmail:()=>u(`${n}jetpack/v4/mobile/send-login-email`,i).then(m).then(g),submitSurvey:e=>u(`${n}jetpack/v4/marketing/survey`,i,{body:JSON.stringify(e)}).then(m).then(g),saveSetupQuestionnaire:e=>u(`${n}jetpack/v4/setup/questionnaire`,i,{body:JSON.stringify(e)}).then(m).then(g),updateLicensingError:e=>u(`${n}jetpack/v4/licensing/error`,i,{body:JSON.stringify(e)}).then(m).then(g),updateLicenseKey:e=>u(`${n}jetpack/v4/licensing/set-license`,i,{body:JSON.stringify({license:e})}).then(m).then(g),getUserLicensesCounts:()=>d(`${n}jetpack/v4/licensing/user/counts`,r).then(m).then(g),updateLicensingActivationNoticeDismiss:e=>u(`${n}jetpack/v4/licensing/user/activation-notice-dismiss`,i,{body:JSON.stringify({last_detached_count:e})}).then(m).then(g),updateRecommendationsStep:e=>u(`${n}jetpack/v4/recommendations/step`,i,{body:JSON.stringify({step:e})}).then(m),confirmIDCSafeMode:()=>u(`${n}jetpack/v4/identity-crisis/confirm-safe-mode`,i).then(m),startIDCFresh:e=>u(`${n}jetpack/v4/identity-crisis/start-fresh`,i,{body:JSON.stringify({redirect_uri:e})}).then(m).then(g),migrateIDC:()=>u(`${n}jetpack/v4/identity-crisis/migrate`,i).then(m),attachLicenses:e=>u(`${n}jetpack/v4/licensing/attach-licenses`,i,{body:JSON.stringify({licenses:e})}).then(m).then(g),fetchSearchPlanInfo:()=>d(`${n}jetpack/v4/search/plan`,r).then(m).then(g),fetchSearchSettings:()=>d(`${n}jetpack/v4/search/settings`,r).then(m).then(g),updateSearchSettings:e=>u(`${n}jetpack/v4/search/settings`,i,{body:JSON.stringify(e)}).then(m).then(g)};function d(e,t){return fetch(l(e),t)}function u(e,t,n){return fetch(e,(0,c.assign)({},t,n)).catch(f)}function h(e){return e.general&&void 0===e.general.response||e.week&&void 0===e.week.response||e.month&&void 0===e.month.response?e:{}}(0,c.assign)(this,p)};function m(e){return e.status>=200&&e.status<300?e:404===e.status?new Promise((()=>{throw e.redirected?new p(e.redirected):new l})):e.json().catch((e=>h(e))).then((t=>{const n=new Error(`${t.message} (Status ${e.status})`);throw n.response=t,n.name="ApiError",n}))}function g(e){return e.json().catch((t=>h(t,e.redirected,e.url)))}function h(e,t,n){throw t?new i(n):new r}function f(){throw new d}},2234:(e,t,n)=>{"use strict";n.d(t,{Z:()=>g});var c=n(9196),o=n.n(c),s=n(5736),a=n(1268),r=n.n(a),i=n(6619),l=n(7967),p=n(69),d=n(4191),u=n(134);const __=s.__,m=e=>{const{moduleName:t,a8cLogoHref:n}=e;return o().createElement("div",{className:i.Z["jp-admin-page-section"]},o().createElement(d.Z,null,o().createElement(p.Z,null,o().createElement(u.Z,{lg:12,md:8,sm:4},o().createElement(l.Z,{moduleName:t,a8cLogoHref:n})))))};m.defaultProps={a8cLogoHref:"https://jetpack.com",moduleName:__("Jetpack","jetpack-my-jetpack")},m.propTypes={a8cLogoHref:r().string,moduleName:r().string};const g=m},4123:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var c=n(9196),o=n.n(c),s=n(6619),a=n(1546),r=n(69),i=n(4191),l=n(134);const p=()=>o().createElement("div",{className:s.Z["jp-admin-page-section"]},o().createElement(i.Z,null,o().createElement(r.Z,null,o().createElement(l.Z,{lg:12,md:8,sm:4},o().createElement(a.Z,null)))))},8670:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var c=n(9196),o=n.n(c),s=n(5736),a=n(1268),r=n.n(a),i=n(6619),l=n(4123),p=n(2234);const __=s.__,d=e=>{const{children:t,moduleName:n,a8cLogoHref:c,showHeader:s,showFooter:a}=e;return o().createElement("div",{className:i.Z["jp-admin-page"]},s&&o().createElement(l.Z,null),t,a&&o().createElement(p.Z,{moduleName:n,a8cLogoHref:c}))};d.defaultProps={a8cLogoHref:"https://jetpack.com",moduleName:__("Jetpack","jetpack-my-jetpack"),showHeader:!0,showFooter:!0},d.propTypes={a8cLogoHref:r().string,moduleName:r().string,showHeader:r().bool,showFooter:r().bool};const u=d},6160:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});var c=n(9196),o=n.n(c),s=n(9855),a=n(4191);const r=e=>{const{children:t}=e;return o().createElement("div",{className:s.Z["jp-admin-section"]},o().createElement(a.Z,null,t))}},4502:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});var c=n(9196),o=n.n(c),s=n(8459),a=n(4191);const r=e=>{const{children:t}=e;return o().createElement("div",{className:s.Z["jp-admin-section-hero"]},o().createElement(a.Z,null,t))}},1824:(e,t,n)=>{"use strict";n.d(t,{Z:()=>m});var c=n(9183),o=n.n(c),s=n(9196),a=n.n(s),r=n(9105),i=n.n(r),l=n(5736),p=n(1268),d=n.n(p);const __=l.__,u=e=>{const{title:t,height:n,className:c,...s}=e;return a().createElement("svg",o()({role:"img",x:"0",y:"0",viewBox:"0 0 935 38.2",enableBackground:"new 0 0 935 38.2","aria-labelledby":"jp-automattic-byline-logo-title",height:n,className:i()("jp-automattic-byline-logo",c)},s),a().createElement("title",{id:"jp-automattic-byline-logo-title"},t),a().createElement("path",{d:"M317.1 38.2c-12.6 0-20.7-9.1-20.7-18.5v-1.2c0-9.6 8.2-18.5 20.7-18.5 12.6 0 20.8 8.9 20.8 18.5v1.2C337.9 29.1 329.7 38.2 317.1 38.2zM331.2 18.6c0-6.9-5-13-14.1-13s-14 6.1-14 13v0.9c0 6.9 5 13.1 14 13.1s14.1-6.2 14.1-13.1V18.6zM175 36.8l-4.7-8.8h-20.9l-4.5 8.8h-7L157 1.3h5.5L182 36.8H175zM159.7 8.2L152 23.1h15.7L159.7 8.2zM212.4 38.2c-12.7 0-18.7-6.9-18.7-16.2V1.3h6.6v20.9c0 6.6 4.3 10.5 12.5 10.5 8.4 0 11.9-3.9 11.9-10.5V1.3h6.7V22C231.4 30.8 225.8 38.2 212.4 38.2zM268.6 6.8v30h-6.7v-30h-15.5V1.3h37.7v5.5H268.6zM397.3 36.8V8.7l-1.8 3.1 -14.9 25h-3.3l-14.7-25 -1.8-3.1v28.1h-6.5V1.3h9.2l14 24.4 1.7 3 1.7-3 13.9-24.4h9.1v35.5H397.3zM454.4 36.8l-4.7-8.8h-20.9l-4.5 8.8h-7l19.2-35.5h5.5l19.5 35.5H454.4zM439.1 8.2l-7.7 14.9h15.7L439.1 8.2zM488.4 6.8v30h-6.7v-30h-15.5V1.3h37.7v5.5H488.4zM537.3 6.8v30h-6.7v-30h-15.5V1.3h37.7v5.5H537.3zM569.3 36.8V4.6c2.7 0 3.7-1.4 3.7-3.4h2.8v35.5L569.3 36.8 569.3 36.8zM628 11.3c-3.2-2.9-7.9-5.7-14.2-5.7 -9.5 0-14.8 6.5-14.8 13.3v0.7c0 6.7 5.4 13 15.3 13 5.9 0 10.8-2.8 13.9-5.7l4 4.2c-3.9 3.8-10.5 7.1-18.3 7.1 -13.4 0-21.6-8.7-21.6-18.3v-1.2c0-9.6 8.9-18.7 21.9-18.7 7.5 0 14.3 3.1 18 7.1L628 11.3zM321.5 12.4c1.2 0.8 1.5 2.4 0.8 3.6l-6.1 9.4c-0.8 1.2-2.4 1.6-3.6 0.8l0 0c-1.2-0.8-1.5-2.4-0.8-3.6l6.1-9.4C318.7 11.9 320.3 11.6 321.5 12.4L321.5 12.4z"}),a().createElement("path",{d:"M37.5 36.7l-4.7-8.9H11.7l-4.6 8.9H0L19.4 0.8H25l19.7 35.9H37.5zM22 7.8l-7.8 15.1h15.9L22 7.8zM82.8 36.7l-23.3-24 -2.3-2.5v26.6h-6.7v-36H57l22.6 24 2.3 2.6V0.8h6.7v35.9H82.8z"}),a().createElement("path",{d:"M719.9 37l-4.8-8.9H694l-4.6 8.9h-7.1l19.5-36h5.6l19.8 36H719.9zM704.4 8l-7.8 15.1h15.9L704.4 8zM733 37V1h6.8v36H733zM781 37c-1.8 0-2.6-2.5-2.9-5.8l-0.2-3.7c-0.2-3.6-1.7-5.1-8.4-5.1h-12.8V37H750V1h19.6c10.8 0 15.7 4.3 15.7 9.9 0 3.9-2 7.7-9 9 7 0.5 8.5 3.7 8.6 7.9l0.1 3c0.1 2.5 0.5 4.3 2.2 6.1V37H781zM778.5 11.8c0-2.6-2.1-5.1-7.9-5.1h-13.8v10.8h14.4c5 0 7.3-2.4 7.3-5.2V11.8zM794.8 37V1h6.8v30.4h28.2V37H794.8zM836.7 37V1h6.8v36H836.7zM886.2 37l-23.4-24.1 -2.3-2.5V37h-6.8V1h6.5l22.7 24.1 2.3 2.6V1h6.8v36H886.2zM902.3 37V1H935v5.6h-26v9.2h20v5.5h-20v10.1h26V37H902.3z"}))};u.defaultProps={title:__("An Automattic Airline","jetpack-my-jetpack"),height:7,className:""},u.propTypes={title:d().string,height:d().number,className:d().string};const m=u},9697:(e,t,n)=>{"use strict";n.d(t,{Z:()=>i});var c=n(9196),o=n.n(c),s=n(1268),a=n.n(s);n(8776);const r=e=>{const{format:t,icon:n,imageUrl:c}=e;return o().createElement("div",{className:"jp-components__decorative-card "+(t?"jp-components__decorative-card--"+t:"")},o().createElement("div",{className:"jp-components__decorative-card__image",style:{backgroundImage:c?`url( ${c} )`:""}}),o().createElement("div",{className:"jp-components__decorative-card__content"},o().createElement("div",{className:"jp-components__decorative-card__lines"})),(()=>{if(n)return o().createElement("div",{className:"jp-components__decorative-card__icon-container"},o().createElement("span",{className:"jp-components__decorative-card__icon jp-components__decorative-card__icon--"+n}))})())};r.propTypes={format:a().oneOf(["horizontal","vertical"]),icon:a().oneOf(["unlink"]),imageUrl:a().string},r.defaultProps={format:"horizontal"};const i=r},7967:(e,t,n)=>{"use strict";n.d(t,{Z:()=>h});var c=n(9183),o=n.n(c),s=n(9196),a=n.n(s),r=n(5736),i=n(9105),l=n.n(i),p=n(1268),d=n.n(p),u=n(1824),m=(n(3220),n(1546));const __=r.__,g=e=>{const{a8cLogoHref:t,moduleName:n,className:c,...s}=e;return a().createElement("div",o()({className:l()("jp-dashboard-footer",c)},s),a().createElement("div",{className:"jp-dashboard-footer__footer-left"},a().createElement(m.Z,{logoColor:"#000",showText:!1,height:16,className:"jp-dashboard-footer__jetpack-symbol","aria-label":__("Jetpack logo","jetpack-my-jetpack")}),a().createElement("span",{className:"jp-dashboard-footer__module-name"},n)),a().createElement("div",{className:"jp-dashboard-footer__footer-right"},a().createElement("a",{href:t,"aria-label":__("An Automattic Airline","jetpack-my-jetpack")},a().createElement(u.Z,null))))};g.defaultProps={a8cLogoHref:"https://jetpack.com",moduleName:__("Jetpack","jetpack-my-jetpack"),className:""},g.propTypes={a8cLogoHref:d().string,moduleName:d().string,className:d().string};const h=g},1546:(e,t,n)=>{"use strict";n.d(t,{Z:()=>h});var c=n(9183),o=n.n(c),s=n(7538),a=n.n(s),r=n(1268),i=n.n(r),l=n(9196),p=n.n(l),d=n(9105),u=n.n(d),m=n(5736);const __=m.__;class g extends p().Component{render(){const{logoColor:e,showText:t,className:n,...c}=this.props,s=t?"0 0 118 32":"0 0 32 32";return p().createElement("svg",o()({xmlns:"http://www.w3.org/2000/svg",x:"0px",y:"0px",viewBox:s,className:u()("jetpack-logo",n),"aria-labelledby":"jetpack-logo-title"},c),p().createElement("title",{id:"jetpack-logo-title"},__("Jetpack Logo","jetpack-my-jetpack")),p().createElement("path",{fill:e,d:"M16,0C7.2,0,0,7.2,0,16s7.2,16,16,16s16-7.2,16-16S24.8,0,16,0z M15,19H7l8-16V19z M17,29V13h8L17,29z"}),t&&p().createElement(l.Fragment,null,p().createElement("path",{d:"M41.3,26.6c-0.5-0.7-0.9-1.4-1.3-2.1c2.3-1.4,3-2.5,3-4.6V8h-3V6h6v13.4C46,22.8,45,24.8,41.3,26.6z"}),p().createElement("path",{d:"M65,18.4c0,1.1,0.8,1.3,1.4,1.3c0.5,0,2-0.2,2.6-0.4v2.1c-0.9,0.3-2.5,0.5-3.7,0.5c-1.5,0-3.2-0.5-3.2-3.1V12H60v-2h2.1V7.1 H65V10h4v2h-4V18.4z"}),p().createElement("path",{d:"M71,10h3v1.3c1.1-0.8,1.9-1.3,3.3-1.3c2.5,0,4.5,1.8,4.5,5.6s-2.2,6.3-5.8,6.3c-0.9,0-1.3-0.1-2-0.3V28h-3V10z M76.5,12.3 c-0.8,0-1.6,0.4-2.5,1.2v5.9c0.6,0.1,0.9,0.2,1.8,0.2c2,0,3.2-1.3,3.2-3.9C79,13.4,78.1,12.3,76.5,12.3z"}),p().createElement("path",{d:"M93,22h-3v-1.5c-0.9,0.7-1.9,1.5-3.5,1.5c-1.5,0-3.1-1.1-3.1-3.2c0-2.9,2.5-3.4,4.2-3.7l2.4-0.3v-0.3c0-1.5-0.5-2.3-2-2.3 c-0.7,0-2.3,0.5-3.7,1.1L84,11c1.2-0.4,3-1,4.4-1c2.7,0,4.6,1.4,4.6,4.7L93,22z M90,16.4l-2.2,0.4c-0.7,0.1-1.4,0.5-1.4,1.6 c0,0.9,0.5,1.4,1.3,1.4s1.5-0.5,2.3-1V16.4z"}),p().createElement("path",{d:"M104.5,21.3c-1.1,0.4-2.2,0.6-3.5,0.6c-4.2,0-5.9-2.4-5.9-5.9c0-3.7,2.3-6,6.1-6c1.4,0,2.3,0.2,3.2,0.5V13 c-0.8-0.3-2-0.6-3.2-0.6c-1.7,0-3.2,0.9-3.2,3.6c0,2.9,1.5,3.8,3.3,3.8c0.9,0,1.9-0.2,3.2-0.7V21.3z"}),p().createElement("path",{d:"M110,15.2c0.2-0.3,0.2-0.8,3.8-5.2h3.7l-4.6,5.7l5,6.3h-3.7l-4.2-5.8V22h-3V6h3V15.2z"}),p().createElement("path",{d:"M58.5,21.3c-1.5,0.5-2.7,0.6-4.2,0.6c-3.6,0-5.8-1.8-5.8-6c0-3.1,1.9-5.9,5.5-5.9s4.9,2.5,4.9,4.9c0,0.8,0,1.5-0.1,2h-7.3 c0.1,2.5,1.5,2.8,3.6,2.8c1.1,0,2.2-0.3,3.4-0.7C58.5,19,58.5,21.3,58.5,21.3z M56,15c0-1.4-0.5-2.9-2-2.9c-1.4,0-2.3,1.3-2.4,2.9 C51.6,15,56,15,56,15z"})))}}a()(g,"propTypes",{className:i().string,width:i().number,height:i().number,showText:i().bool,logoColor:i().string}),a()(g,"defaultProps",{className:"",height:32,showText:!0,logoColor:"#069e08"});const h=g},134:(e,t,n)=>{"use strict";n.d(t,{Z:()=>d});var c=n(9196),o=n.n(c),s=n(9105),a=n.n(s),r=n(1268),i=n.n(r),l=n(4555);const p=e=>{const{children:t,sm:n,md:c,lg:s}=e,r=Number.isInteger(n)?n:0,i=Number.isInteger(c)?c:0,p=Number.isInteger(s)?s:0,d=[r,i,p].reduce(((e,t)=>t>0&&t<e?t:e)),u=a()(r>0?l.Z["sm-col-span-"+r]:l.Z["sm-col-span-"+d],i>0?l.Z["md-col-span-"+i]:l.Z["md-col-span-"+d],p>0?l.Z["lg-col-span-"+p]:l.Z["lg-col-span-"+d]);return o().createElement("div",{className:u},t)};p.proptypes={sm:i().number,md:i().number,lg:i().number};const d=p},4191:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});var c=n(9196),o=n.n(c),s=n(8422);const a=e=>{const{children:t}=e;return o().createElement("div",{className:s.Z["jp-container"]},t)}},69:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});var c=n(9196),o=n.n(c),s=n(7941);const a=e=>{const{children:t}=e;return o().createElement("div",{className:s.Z["jp-row"]},t)}},1415:(e,t,n)=>{"use strict";function c(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const n={};let c;if("undefined"!=typeof window&&(c=window.Initial_State?.calypsoEnv),0===e.search("https://")){const t=new URL(e);e=`https://${t.host}${t.pathname}`,n.url=encodeURIComponent(e)}else n.source=encodeURIComponent(e);Object.keys(t).map((e=>{n[e]=encodeURIComponent(t[e])})),!Object.keys(n).includes("site")&&"undefined"!=typeof jetpack_redirects&&jetpack_redirects.hasOwnProperty("currentSiteRawUrl")&&(n.site=jetpack_redirects.currentSiteRawUrl),c&&(n.calypso_env=c);const o=Object.keys(n).map((e=>e+"="+n[e])).join("&");return"https://jetpack.com/redirect/?"+o}n.d(t,{Z:()=>c})},6251:(e,t,n)=>{let c={};try{c=n(4462)}catch{console.error("jetpackConfig is missing in your webpack config file. See @automattic/jetpack-config"),c={missingConfig:!0}}const o=e=>c.hasOwnProperty(e);e.exports={jetpackConfigHas:o,jetpackConfigGet:e=>{if(!o(e))throw'This app requires the "'+e+'" Jetpack Config to be defined in your webpack configuration file. See details in @automattic/jetpack-config package docs.';return c[e]}}},9565:(e,t,n)=>{"use strict";n.d(t,{Z:()=>i});var c=n(9196),o=n(1268),s=n.n(o),a=n(9570);const r=e=>{const{redirectFunc:t,connectUrl:n,redirectUri:o,from:s}=e,[r,i]=(0,c.useState)(null);return n&&n!==r&&i(n),(0,c.useEffect)((()=>{r||a.ZP.fetchAuthorizationUrl(o).then((e=>i(e.authorizeUrl))).catch((e=>{throw e}))}),[]),r?(t(r+(s?(r.includes("?")?"&":"?")+"from="+encodeURIComponent(s):"")),null):null};r.propTypes={connectUrl:s().string,redirectUri:s().string.isRequired,from:s().string,redirectFunc:s().func},r.defaultProps={redirectFunc:e=>window.location.assign(e),redirectUri:null};const i=r},3593:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var c=n(9196),o=n.n(c),s=n(1268),a=n.n(s),r=n(5736),i=n(2332);const __=r.__,l=e=>{const{connectedPlugins:t,disconnectingPlugin:n}=e,s=(0,c.useMemo)((()=>{if(t){return Object.keys(t).map((e=>Object.assign({slug:e},t[e]))).filter((e=>n!==e.slug))}return[]}),[t,n]);return t&&s.length>0?o().createElement(o().Fragment,null,o().createElement("div",{className:"jp-connection__disconnect-dialog__step-copy"},o().createElement("p",{className:"jp-connection__disconnect-dialog__large-text"},__("Jetpack is powering other plugins on your site. If you disconnect, these plugins will no longer work.","jetpack-my-jetpack"))),o().createElement("div",{className:"jp-connection__disconnect-card__group"},s.map((e=>o().createElement(i.Z,{title:e.name}))))):null};l.PropTypes={connectedPlugins:a().object,disconnectingPlugin:a().string};const p=l},7132:(e,t,n)=>{"use strict";n.d(t,{Z:()=>f});var c=n(9196),o=n.n(c),s=n(5736),a=n(5609),r=n(1268),i=n.n(r),l=n(9570),p=n(9818),d=n(9565),u=n(5628),m=n(2199),g=n(1631);n(404);const __=s.__,h=e=>{const{apiRoot:t,apiNonce:n,redirectUri:s,title:r,connectionInfoText:i,onDisconnected:h,connectedPlugins:f,connectedSiteId:k,context:_}=e,{isRegistered:y,isUserConnected:v}=(0,g.Z)({apiRoot:t,apiNonce:n}),[j,C]=(0,c.useState)(!1),[E,w]=(0,c.useState)({}),[b,S]=(0,c.useState)(!1),N=(0,p.useSelect)((e=>e(m.t).getUserIsConnecting()),[]),{setConnectionStatus:F,setUserIsConnecting:I}=(0,p.useDispatch)(m.t),Z=(0,c.useRef)();(0,c.useEffect)((()=>{l.ZP.setApiRoot(t),l.ZP.setApiNonce(n)}),[t,n]),(0,c.useEffect)((()=>{C(!0),l.ZP.fetchSiteConnectionData().then((e=>{C(!1),w(e.currentUser?.wpcomUser);const t=e.currentUser?.wpcomUser?.avatar;t&&(Z.current.style.backgroundImage=`url('${t}')`)})).catch((e=>{throw C(!1),e}))}),[C,w]);const O=(0,c.useCallback)((e=>{e&&e.preventDefault(),S(!0)}),[S]),R=(0,c.useCallback)((e=>{e&&e.preventDefault(),S(!1)}),[S]),P=(0,c.useCallback)((e=>{e&&e.preventDefault(),F({isActive:!1,isRegistered:!1,isUserConnected:!1}),h&&"[object Function]"==={}.toString.call(h)&&h()}),[h,F]);return y?o().createElement("div",{className:"jp-connection-status-card"},o().createElement("h3",null,r),o().createElement("p",null,i),o().createElement("div",{className:"jp-connection-status-card--status"},o().createElement("div",{className:"jp-connection-status-card--cloud"}),o().createElement("div",{className:"jp-connection-status-card--line"+(v?"":" jp-connection-status-card--site-only")}),o().createElement("div",{className:"jp-connection-status-card--jetpack-logo"}),o().createElement("div",{className:"jp-connection-status-card--avatar",ref:Z})),o().createElement("ul",{className:"jp-connection-status-card--list"},o().createElement("li",{className:"jp-connection-status-card--list-item-success"},__("Site connected.","jetpack-my-jetpack")," ",o().createElement(a.Button,{variant:"link",onClick:O,className:"jp-connection__disconnect-dialog__link"},__("Disconnect","jetpack-my-jetpack")),o().createElement(u.Z,{apiRoot:t,apiNonce:n,onDisconnected:P,connectedPlugins:f,connectedSiteId:k,connectedUser:E,isOpen:b,onClose:R,context:_})),v&&!j&&o().createElement("li",{className:"jp-connection-status-card--list-item-success"},__("Logged in as","jetpack-my-jetpack")," ",E?.display_name),!v&&!j&&o().createElement("li",{className:"jp-connection-status-card--list-item-error"},__("Your WordPress.com account is not connected.","jetpack-my-jetpack"))),!v&&!j&&o().createElement(a.Button,{isPrimary:!0,disabled:N,onClick:I,className:"jp-connection-status-card--btn-connect-user"},__("Connect your WordPress.com account","jetpack-my-jetpack")),N&&o().createElement(d.Z,{redirectUri:s})):null};h.propTypes={apiRoot:i().string.isRequired,apiNonce:i().string.isRequired,redirectUri:i().string.isRequired,connectedPlugins:i().object,connectedSiteId:i().number,title:i().string,connectionInfoText:i().string,onDisconnected:i().func,context:i().string},h.defaultProps={title:__("Connection","jetpack-my-jetpack"),connectionInfoText:__("Leverages the Jetpack Cloud for more features on your side.","jetpack-my-jetpack")};const f=h},2332:(e,t,n)=>{"use strict";n.d(t,{Z:()=>i});var c=n(9196),o=n.n(c),s=n(1268),a=n.n(s);n(1294);const r=e=>{const{title:t,value:n,description:c}=e;return o().createElement("div",{className:"jp-connection__disconnect-card card"},o().createElement("div",{className:"jp-connection__disconnect-card__card-content"},o().createElement("p",{className:"jp-connection__disconnect-card__card-headline"},t),(n||c)&&o().createElement("div",{className:"jp-connection__disconnect-card__card-stat-block"},o().createElement("span",{className:"jp-connection__disconnect-card__card-stat"},n),o().createElement("div",{className:"jp-connection__disconnect-card__card-description"},c))))};r.propTypes={title:a().string,value:a().string|a().number,description:a().number};const i=r},5628:(e,t,n)=>{"use strict";n.d(t,{Z:()=>k});var c=n(9196),o=n.n(c),s=n(1268),a=n.n(s),r=n(5736),i=n(5609),l=n(9570),p=n(816),d=n(6251),u=(n(1545),n(163)),m=n(1496),g=n(4130),h=n(5700);const __=r.__,f=e=>{const[t,n]=(0,c.useState)(!1),[s,a]=(0,c.useState)(!1),[r,f]=(0,c.useState)(!1),[k,_]=(0,c.useState)(!1),[y,v]=(0,c.useState)(!1),[j,C]=(0,c.useState)(!1),{apiRoot:E,apiNonce:w,connectedPlugins:b,title:S,pluginScreenDisconnectCallback:N,onDisconnected:F,onError:I,disconnectStepComponent:Z,context:O,connectedUser:R,connectedSiteId:P,isOpen:x,onClose:U}=e;let T="";(0,d.jetpackConfigHas)("consumer_slug")&&(T=(0,d.jetpackConfigGet)("consumer_slug"));const A=(0,c.useMemo)((()=>({context:O,plugin:T})),[O,T]);(0,c.useEffect)((()=>{l.ZP.setApiRoot(E),l.ZP.setApiNonce(w)}),[E,w]),(0,c.useEffect)((()=>{R&&R.ID&&R.login&&p.Z.initialize(R.ID,R.login)}),[R,R.ID,R.login]),(0,c.useEffect)((()=>{x&&p.Z.tracks.recordEvent("jetpack_disconnect_dialog_open",A)}),[x,A]),(0,c.useEffect)((()=>{x&&(s?!s||k||y?k&&!y?p.Z.tracks.recordEvent("jetpack_disconnect_dialog_step",Object.assign({},{step:"survey"},A)):y&&p.Z.tracks.recordEvent("jetpack_disconnect_dialog_step",Object.assign({},{step:"thank_you"},A)):p.Z.tracks.recordEvent("jetpack_disconnect_dialog_step",Object.assign({},{step:"disconnect_confirm"},A)):p.Z.tracks.recordEvent("jetpack_disconnect_dialog_step",Object.assign({},{step:"disconnect"},A)))}),[x,s,k,y,A]);const J=(0,c.useCallback)((()=>{l.ZP.disconnectSite().then((()=>{n(!1),a(!0)})).catch((e=>{n(!1),f(e),I&&I(e)}))}),[n,a,f,I]),z=(0,c.useCallback)(((e,t)=>{C(!0),fetch("https://public-api.wordpress.com/wpcom/v2/marketing/feedback-survey",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(e)}).then((e=>e.json())).then((e=>{if(!0!==e.success)throw new Error("Survey endpoint returned error code "+e.code);p.Z.tracks.recordEvent("jetpack_disconnect_survey_submit",t),v(!0),C(!1)})).catch((e=>{p.Z.tracks.recordEvent("jetpack_disconnect_survey_error",Object.assign({},{error:e.message},t)),v(!0),C(!1)}))}),[C,v]),D=(0,c.useCallback)((e=>{e&&e.preventDefault(),f(!1),n(!0),"plugins"!==O?J():N&&N(e)}),[f,n,N,O,J]),M=(0,c.useCallback)((()=>R.ID&&P),[R,P]),H=(0,c.useCallback)(((e,t,n)=>{if(n&&n.preventDefault(),!M())return void v(!0);const c={site_id:P,user_id:R.ID,survey_id:"jetpack-plugin-disconnect",survey_responses:{"why-cancel":{response:e,text:t||null}}},o=Object.assign({},A,{disconnect_reason:e});z(c,o)}),[z,v,M,P,R,A]),L=(0,c.useCallback)((e=>{e&&e.preventDefault(),F&&F(),U()}),[F,U]),V=(0,c.useCallback)((e=>{e&&e.preventDefault(),_(!0)}),[_]);return o().createElement(o().Fragment,null,x&&o().createElement(i.Modal,{title:"",contentLabel:S,aria:{labelledby:"jp-connection__disconnect-dialog__heading"},onRequestClose:U,shouldCloseOnClickOutside:!1,shouldCloseOnEsc:!1,isDismissible:!1,className:"jp-connection__disconnect-dialog"+(s?" jp-connection__disconnect-dialog__success":"")},s?!s||k||y?k&&!y?o().createElement(g.Z,{isSubmittingFeedback:j,onFeedBackProvided:H,onExit:L}):y?o().createElement(h.Z,{onExit:L}):void 0:o().createElement(m.Z,{canProvideFeedback:M(),onProvideFeedback:V,onExit:L}):o().createElement(u.Z,{title:S,connectedPlugins:b,disconnectStepComponent:Z,isDisconnecting:t,closeModal:U,onDisconnect:D,disconnectError:r,context:O,disconnectingPlugin:T})))};f.propTypes={apiRoot:a().string.isRequired,apiNonce:a().string.isRequired,title:a().string,onDisconnected:a().func,onError:a().func,context:a().string,connectedPlugins:a().object,pluginScreenDisconnectCallback:a().func,disconnectStepComponent:a().element,connectedUser:a().object,connectedSiteId:a().number,isOpen:a().bool,onClose:a().func},f.defaultProps={title:__("Are you sure you want to disconnect?","jetpack-my-jetpack"),context:"jetpack-dashboard",connectedUser:{}};const k=f},1496:(e,t,n)=>{"use strict";n.d(t,{Z:()=>m});var c=n(9196),o=n.n(c),s=n(1268),a=n.n(s),r=n(9307),i=n(5736),l=n(5609),p=n(9697),d=n(2042);const __=i.__,u=e=>{const{onExit:t,canProvideFeedback:n,onProvideFeedback:c}=e;return o().createElement("div",{className:"jp-connection__disconnect-dialog__content"},o().createElement(p.Z,{icon:"unlink",imageUrl:d}),o().createElement("div",{className:"jp-connection__disconnect-dialog__step-copy jp-connection__disconnect-dialog__step-copy--narrow"},o().createElement("h1",null,(0,r.createInterpolateElement)(__("Jetpack has been <br/>successfully disconnected.","jetpack-my-jetpack"),{br:o().createElement("br",null)})),n&&o().createElement(o().Fragment,null,o().createElement("p",null,__("We’re sorry to see you go. Here at Jetpack, we’re always striving to provide the best experience for our customers. Please take our short survey (2 minutes, promise).","jetpack-my-jetpack")),o().createElement("p",null,o().createElement(l.Button,{isPrimary:!0,onClick:c,className:"jp-connection__disconnect-dialog__btn-back-to-wp"},__("Help us improve","jetpack-my-jetpack"))),o().createElement("a",{className:"jp-connection__disconnect-dialog__link jp-connection__disconnect-dialog__link--bold",href:"#",onClick:t},__("No thank you","jetpack-my-jetpack"))),!n&&o().createElement(o().Fragment,null,o().createElement("p",null,o().createElement(l.Button,{isPrimary:!0,onClick:t,className:"jp-connection__disconnect-dialog__btn-back-to-wp"},__("Back to my website","jetpack-my-jetpack"))))))};u.PropTypes={onExit:a().func,onProvideFeedback:a().func,canProvideFeedback:a().bool};const m=u},163:(e,t,n)=>{"use strict";n.d(t,{Z:()=>m});var c=n(9196),o=n.n(c),s=n(9307),a=n(5736),r=n(1415),i=n(5609),l=n(1268),p=n.n(l),d=n(3593);const __=a.__,u=e=>{const{title:t,isDisconnecting:n,onDisconnect:c,disconnectError:a,disconnectStepComponent:l,connectedPlugins:p,disconnectingPlugin:u,closeModal:m,context:g}=e;return o().createElement(o().Fragment,null,o().createElement("div",{className:"jp-connection__disconnect-dialog__content"},o().createElement("h1",{id:"jp-connection__disconnect-dialog__heading"},t),o().createElement(d.Z,{connectedPlugins:p,disconnectingPlugin:u}),l,(()=>{if(!p&&!l)return o().createElement("div",{className:"jp-connection__disconnect-dialog__step-copy"},o().createElement("p",{className:"jp-connection__disconnect-dialog__large-text"},__("Jetpack is currently powering multiple products on your site.","jetpack-my-jetpack"),o().createElement("br",null),__("Once you disconnect Jetpack, these will no longer work.","jetpack-my-jetpack")))})()),o().createElement("div",{className:"jp-connection__disconnect-dialog__actions"},o().createElement("div",{className:"jp-row"},o().createElement("div",{className:"lg-col-span-7 md-col-span-8 sm-col-span-4"},o().createElement("p",null,(0,s.createInterpolateElement)(__("<strong>Need help?</strong> Learn more about the <jpConnectionInfoLink>Jetpack connection</jpConnectionInfoLink> or <jpSupportLink>contact Jetpack support</jpSupportLink>.","jetpack-my-jetpack"),{strong:o().createElement("strong",null),jpConnectionInfoLink:o().createElement("a",{href:(0,r.Z)("why-the-wordpress-com-connection-is-important-for-jetpack"),rel:"noopener noreferrer",target:"_blank",className:"jp-connection__disconnect-dialog__link"}),jpSupportLink:o().createElement("a",{href:(0,r.Z)("jetpack-support"),rel:"noopener noreferrer",target:"_blank",className:"jp-connection__disconnect-dialog__link"})}))),o().createElement("div",{className:"jp-connection__disconnect-dialog__button-wrap lg-col-span-5 md-col-span-8 sm-col-span-4"},o().createElement(i.Button,{isPrimary:!0,disabled:n,onClick:m,className:"jp-connection__disconnect-dialog__btn-dismiss"},__("Stay connected","jetpack-my-jetpack")),(()=>{let e=__("Disconnect","jetpack-my-jetpack");return n?e=__("Disconnecting…","jetpack-my-jetpack"):"plugins"===g&&(e=__("Disconnect and Deactivate","jetpack-my-jetpack")),o().createElement(i.Button,{isPrimary:!0,disabled:n,onClick:c,className:"jp-connection__disconnect-dialog__btn-disconnect"},e)})())),a&&o().createElement("p",{className:"jp-connection__disconnect-dialog__error"},a)))};u.propTypes={title:p().string,isDisconnecting:p().bool,onDisconnect:p().func,disconnectError:p().bool,disconnectStepComponent:p().elementType,connectedPlugins:p().array,disconnectingPlugin:p().string,closeModal:p().func,context:p().string};const m=u},4130:(e,t,n)=>{"use strict";n.d(t,{Z:()=>p});var c=n(9196),o=n.n(c),s=n(1268),a=n.n(s),r=n(5736),i=(n(843),n(1194));const __=r.__,l=e=>{const{onExit:t,onFeedBackProvided:n,isSubmittingFeedback:c}=e;return o().createElement("div",{className:"jp-connection__disconnect-dialog__content"},o().createElement("h1",null,__("Before you go, help us improve Jetpack","jetpack-my-jetpack")),o().createElement("p",{className:"jp-connection__disconnect-dialog__large-text"},__("Let us know what didn‘t work for you","jetpack-my-jetpack")),o().createElement(i.Z,{onSubmit:n,isSubmittingFeedback:c}),o().createElement("a",{className:"jp-connection__disconnect-dialog__link jp-connection__disconnect-dialog__link--bold",href:"#",onClick:t},__("Skip for now","jetpack-my-jetpack")))};l.PropTypes={onExit:a().func,onFeedBackProvided:a().func,isSubmittingFeedback:a().bool};const p=l},5700:(e,t,n)=>{"use strict";n.d(t,{Z:()=>m});var c=n(9196),o=n.n(c),s=n(1268),a=n.n(s),r=n(9697),i=n(5736),l=n(5609),p=n(9307),d=n(724);const __=i.__,u=e=>{const{onExit:t}=e;return o().createElement("div",{className:"jp-connection__disconnect-dialog__content"},o().createElement(r.Z,{format:"vertical",imageUrl:d}),o().createElement("div",{className:"jp-connection__disconnect-dialog__copy"},o().createElement("h1",null,__("Thank you!","jetpack-my-jetpack")),o().createElement("p",{className:"jp-connection__disconnect-dialog__large-text"},(0,p.createInterpolateElement)(__("Your answer has been submitted. <br/>Thanks for your input on how we can improve Jetpack.","jetpack-my-jetpack"),{br:o().createElement("br",null)})),o().createElement(l.Button,{isPrimary:!0,onClick:t,className:"jp-connection__disconnect-dialog__btn-back-to-wp"},__("Back to my website","jetpack-my-jetpack"))))};u.PropTypes={onExit:a().func,assetBaseUrl:a().string};const m=u},1194:(e,t,n)=>{"use strict";n.d(t,{Z:()=>d});var c=n(9196),o=n.n(c),s=n(1268),a=n.n(s),r=n(5609),i=n(5736),l=n(7215);const __=i.__,p=e=>{const{onSubmit:t,isSubmittingFeedback:n}=e,[s,a]=(0,c.useState)(),[i,p]=(0,c.useState)(),d=[{id:"troubleshooting",answerText:__("Troubleshooting - I'll be reconnecting afterwards.","jetpack-my-jetpack")},{id:"not-working",answerText:__("I can't get it to work.","jetpack-my-jetpack")},{id:"slowed-down-site",answerText:__("It slowed down my site.","jetpack-my-jetpack")},{id:"buggy",answerText:__("It's buggy.","jetpack-my-jetpack")},{id:"what-does-it-do",answerText:__("I don't know what it does.","jetpack-my-jetpack")}],u="another-reason",m=(0,c.useCallback)((()=>{t(s,s===u?i:"")}),[t,u,i,s]),g=(0,c.useCallback)((e=>{const t=e.target.value;e.stopPropagation(),p(t)}),[p]),h=e=>e===s?"jp-connect__disconnect-survey-card--selected":"",f=(0,c.useCallback)(((e,t)=>{switch(t.key){case"Enter":case"Space":case"Spacebar":case" ":a(e)}}),[a]);return o().createElement(o().Fragment,null,o().createElement("div",{className:"jp-connection__disconnect-dialog__survey"},d.map((e=>o().createElement(l.Z,{id:e.id,onClick:a,onKeyDown:f,className:"card jp-connect__disconnect-survey-card "+h(e.id)},o().createElement("p",{className:"jp-connect__disconnect-survey-card__answer"},e.answerText)))),o().createElement(l.Z,{id:u,onClick:a,onKeyDown:f,className:"card jp-connect__disconnect-survey-card "+h(u)},o().createElement("p",{className:"jp-connect__disconnect-survey-card__answer"},__("Other:","jetpack-my-jetpack")," ",o().createElement("input",{placeholder:__("share your experience","jetpack-my-jetpack"),className:"jp-connect__disconnect-survey-card__input",type:"text",value:i,onChange:g,maxLength:1e3})))),o().createElement("p",null,o().createElement(r.Button,{disabled:!s||n,isPrimary:!0,onClick:m,className:"jp-connection__disconnect-dialog__btn-back-to-wp"},n?__("Submitting…","jetpack-my-jetpack"):__("Submit Feedback","jetpack-my-jetpack",0))))};p.PropTypes={onSubmit:a().func,isSubmittingFeedback:a().bool};const d=p},7215:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s});var c=n(9196),o=n.n(c);n(843);const s=e=>{const{id:t,onClick:n,onKeyDown:s,children:a,className:r}=e,i=(0,c.useCallback)((()=>{n(t)}),[t,n]),l=(0,c.useCallback)((e=>{s(t,e)}),[t,s]);return o().createElement("div",{tabIndex:"0",role:"button",onClick:i,onKeyDown:l,className:"card jp-connect__disconnect-survey-card "+r},a)}},1631:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});var c=n(9196),o=n(9818),s=n(9570),a=n(2199);const r=e=>{let{registrationNonce:t,redirectUri:n,apiRoot:r,apiNonce:i,autoTrigger:l,from:p}=e;const{registerSite:d,connectUser:u}=(0,o.useDispatch)(a.t),m=(0,o.useSelect)((e=>e(a.t).getRegistrationError())),{siteIsRegistering:g,userIsConnecting:h,isRegistered:f,isUserConnected:k}=(0,o.useSelect)((e=>({siteIsRegistering:e(a.t).getSiteIsRegistering(),userIsConnecting:e(a.t).getUserIsConnecting(),...e(a.t).getConnectionStatus()}))),_=()=>u({from:p}),y=e=>{e&&e.preventDefault(),f?_():d({registrationNonce:t,redirectUri:n}).then((()=>{_()}))};return(0,c.useEffect)((()=>{s.ZP.setApiRoot(r),s.ZP.setApiNonce(i)}),[r,i]),(0,c.useEffect)((()=>{!l||g||h||y()}),[]),{handleRegisterSite:y,handleConnectUser:_,isRegistered:f,isUserConnected:k,siteIsRegistering:g,userIsConnecting:h,registrationError:m}}},6973:(e,t,n)=>{"use strict";n.d(t,{i6:()=>c,LI:()=>o,r7:()=>s,N4:()=>a,qV:()=>r,T1:()=>i,TS:()=>l,ZP:()=>k});const c="SET_CONNECTION_STATUS",o="SET_CONNECTION_STATUS_IS_FETCHING",s="SET_SITE_IS_REGISTERING",a="SET_USER_IS_CONNECTING",r="SET_REGISTRATION_ERROR",i="CLEAR_REGISTRATION_ERROR",l="SET_AUTHORIZATION_URL",p="CONNECT_USER",d=e=>({type:c,connectionStatus:e}),u=e=>({type:s,isRegistering:e}),m=e=>({type:a,isConnecting:e}),g=e=>({type:r,registrationError:e}),h=()=>({type:i}),f=e=>({type:l,authorizationUrl:e});const k={setConnectionStatus:d,setConnectionStatusIsFetching:e=>({type:o,isFetching:e}),fetchConnectionStatus:()=>({type:"FETCH_CONNECTION_STATUS"}),fetchAuthorizationUrl:e=>({type:"FETCH_AUTHORIZATION_URL",redirectUri:e}),setSiteIsRegistering:u,setUserIsConnecting:m,setRegistrationError:g,clearRegistrationError:h,setAuthorizationUrl:f,registerSite:function*(e){let{registrationNonce:t,redirectUri:n}=e;yield h(),yield u(!0);try{const e=yield{type:"REGISTER_SITE",registrationNonce:t,redirectUri:n};return yield d({isRegistered:!0}),yield f(e.authorizeUrl),yield u(!1),Promise.resolve(e)}catch(e){return yield g(e),yield u(!1),Promise.reject(e)}},connectUser:function*(){let{from:e,redirectFunc:t}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};yield m(!0),yield{type:p,from:e,redirectFunc:t}}}},4213:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});var c=n(9570),o=n(9818),s=n(2199);const a={FETCH_CONNECTION_STATUS:()=>new Promise(((e,t)=>{c.ZP.fetchSiteConnectionStatus().then((t=>e(t))).catch((e=>t(e)))})),FETCH_AUTHORIZATION_URL:e=>{let{redirectUri:t}=e;return c.ZP.fetchAuthorizationUrl(t)},REGISTER_SITE:e=>{let{registrationNonce:t,redirectUri:n}=e;return c.ZP.registerSite(t,n)},CONNECT_USER:(0,o.createRegistryControl)((e=>{let{resolveSelect:t}=e;return function(){let{from:e,redirectFunc:n}=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return new Promise(((c,o)=>{t(s.t).getAuthorizationUrl().then((t=>{const o=n||(e=>window.location.assign(e)),s=new URL(t);e&&s.searchParams.set("from",encodeURIComponent(e));const a=s.toString();o(a),c(a)})).catch((e=>{o(e)}))}))}}))}},1147:(e,t,n)=>{"use strict";n.d(t,{Z:()=>s});var c=n(9818),o=n(6973);const s=(0,c.combineReducers)({connectionStatus:function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments.length>1?arguments[1]:void 0;return t.type===o.i6?{...e,...t.connectionStatus}:e},connectionStatusIsFetching:function(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=arguments.length>1?arguments[1]:void 0;return t.type===o.LI?t.isFetching:e},siteIsRegistering:function(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=arguments.length>1?arguments[1]:void 0;return t.type===o.r7?t.isRegistering:e},userIsConnecting:function(){let e=arguments.length>0&&void 0!==arguments[0]&&arguments[0],t=arguments.length>1?arguments[1]:void 0;return t.type===o.N4?t.isConnecting:e},registrationError:(e,t)=>{switch(t.type){case o.T1:return!1;case o.qV:return t.registrationError;default:return e}},authorizationUrl:(e,t)=>t.type===o.TS?t.authorizationUrl:e})},8310:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});var c=n(9818),o=n(6973),s=n(2199);const a={...{*getConnectionStatus(){yield o.ZP.setConnectionStatusIsFetching(!0);const e=yield o.ZP.fetchConnectionStatus();return yield o.ZP.setConnectionStatusIsFetching(!1),o.ZP.setConnectionStatus(e)},getAuthorizationUrl:{isFulfilled:function(e){const t=Boolean(e.authorizationUrl);for(var n=arguments.length,o=new Array(n>1?n-1:0),a=1;a<n;a++)o[a-1]=arguments[a];const r=(0,c.select)(s.t).hasFinishedResolution("getAuthorizationUrl",o);return t&&!r&&(0,c.dispatch)(s.t).finishResolution("getAuthorizationUrl",o),t},*fulfill(e){const t=yield o.ZP.fetchAuthorizationUrl(e);yield o.ZP.setAuthorizationUrl(t.authorizeUrl)}}}}},387:(e,t,n)=>{"use strict";n.d(t,{Z:()=>c});const c={getConnectionStatus:e=>e.connectionStatus||{},getConnectionStatusIsFetching:e=>e.connectionStatusIsFetching||!1,getSiteIsRegistering:e=>e.siteIsRegistering||!1,getUserIsConnecting:e=>e.userIsConnecting||!1,getRegistrationError:e=>e.registrationError||!1,getAuthorizationUrl:e=>e.authorizationUrl||!1}},5333:(e,t,n)=>{"use strict";n.d(t,{Z:()=>r});var c=n(7538),o=n.n(c),s=n(9818);class a{static mayBeInit(e,t){null===a.store&&(a.store=(0,s.createReduxStore)(e,t),(0,s.register)(a.store),a.resolveResolvers(e,t.initialState))}static resolveResolvers(e,t){t.connectionStatus&&t.connectionStatus.hasOwnProperty("isRegistered")&&(0,s.dispatch)(e).finishResolution("getConnectionStatus",[])}}o()(a,"store",null);const r=a},2199:(e,t,n)=>{"use strict";n.d(t,{t:()=>l});var c=n(1147),o=n(6973),s=n(387),a=n(5333),r=n(8310),i=n(4213);const l="jetpack-connection";a.Z.mayBeInit(l,{reducer:c.Z,actions:o.ZP,selectors:s.Z,resolvers:r.Z,controls:i.Z,initialState:window.JP_CONNECTION_INITIAL_STATE||{}})},1046:(e,t,n)=>{"use strict";n.d(t,{Z:()=>u});var c=n(9196),o=n.n(c),s=n(5736),a=n(8670),r=n(4502),i=n(69),l=n(134),p=n(6160),d=n(7132);n(3498);const __=s.__;function u(){const e=(0,c.useCallback)((()=>{window.location=myJetpackInitialState.topJetpackMenuItemUrl}),[]);return o().createElement("div",{className:"jp-my-jetpack-screen"},o().createElement(a.Z,null,o().createElement(r.Z,null,o().createElement(i.Z,null,o().createElement(l.Z,{lg:12,md:8,sm:4},o().createElement("h1",null,__("Manage your Jetpack plan and products all in one place","jetpack-my-jetpack"))))),o().createElement(p.Z,null,o().createElement(i.Z,null,o().createElement(l.Z,{lg:6,sm:4},o().createElement("h1",null,__("My Plan","jetpack-my-jetpack"))),o().createElement(l.Z,{lg:6,sm:4},o().createElement(d.Z,{apiRoot:myJetpackInitialState.apiRoot,apiNonce:myJetpackInitialState.apiNonce,redirectUri:myJetpackInitialState.redirectUri,onDisconnected:e}))))))}},2042:(e,t,n)=>{"use strict";e.exports=n.p+"images/disconnect-confirm-dc9fe8f5c68cfd1320e0.jpg"},724:(e,t,n)=>{"use strict";e.exports=n.p+"images/disconnect-thanks-5873bfac56a9bd7322cd.jpg"},4462:e=>{"use strict";if(void 0==={consumer_slug:"my_jetpack"}){var t=new Error('Cannot find module \'{"consumer_slug":"my_jetpack"}\'');throw t.code="MODULE_NOT_FOUND",t}e.exports={consumer_slug:"my_jetpack"}},9196:e=>{"use strict";e.exports=window.React},1850:e=>{"use strict";e.exports=window.ReactDOM},2819:e=>{"use strict";e.exports=window.lodash},5609:e=>{"use strict";e.exports=window.wp.components},9818:e=>{"use strict";e.exports=window.wp.data},9307:e=>{"use strict";e.exports=window.wp.element},5736:e=>{"use strict";e.exports=window.wp.i18n},6483:e=>{"use strict";e.exports=window.wp.url}},t={};function n(c){var o=t[c];if(void 0!==o)return o.exports;var s=t[c]={exports:{}};return e[c](s,s.exports,n),s.exports}n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},n.d=(e,t)=>{for(var c in t)n.o(t,c)&&!n.o(e,c)&&Object.defineProperty(e,c,{enumerable:!0,get:t[c]})},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e;n.g.importScripts&&(e=n.g.location+"");var t=n.g.document;if(!e&&t&&(t.currentScript&&(e=t.currentScript.src),!e)){var c=t.getElementsByTagName("script");c.length&&(e=c[c.length-1].src)}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),n.p=e})(),(()=>{"use strict";var e=n(1850),t=n.n(e),c=n(9196),o=n.n(c),s=n(1046);!function(){const e=document.getElementById("my-jetpack-container");null!==e&&t().render(o().createElement(s.Z,null),e)}()})()})(); \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.js.LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.js.LICENSE.txt
new file mode 100644
index 00000000..0c20a875
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.js.LICENSE.txt
@@ -0,0 +1,5 @@
+/*!
+ Copyright (c) 2018 Jed Watson.
+ Licensed under the MIT License (MIT), see
+ http://jedwatson.github.io/classnames
+*/
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.rtl.css b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.rtl.css
new file mode 100644
index 00000000..bd36f5b7
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/build/index.rtl.css
@@ -0,0 +1 @@
+.O5NYbFTsxmrm4P2SIdOC{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.rkV4U_hzC04NwXFQcTHl{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.rkV4U_hzC04NwXFQcTHl{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.rkV4U_hzC04NwXFQcTHl{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.G81E6mesnld_OhhOvz_F{grid-column-end:span 1}.SfCZpjwiu2F0KDjGIXEA{grid-column-end:span 2}.qPxwbSu_GTDTz_8jFHJD{grid-column-end:span 3}.xYAWQ0wk6FPfZOZQgFiw{grid-column-end:span 4}@media(min-width:600px){.zwZjA7ofpMucaB_UzG48{grid-column-end:span 1}.YfwKGGBEqiINFYy6ZpKM{grid-column-end:span 2}.pcHQbVFRxA_OvNRQ2OwP{grid-column-end:span 3}.U_LJMBlRZ8ItXZuvCXGg{grid-column-end:span 4}.st_7w_Ja1Gp2AgGaTysO{grid-column-end:span 5}.gQsaHmJo6Gp7Yq9IB9Sg{grid-column-end:span 6}.P0a4FWsiJJmR6bySI8QC{grid-column-end:span 7}.UxSHj7jyDp6sGKHILNRc{grid-column-end:span 8}}@media(min-width:960px){.TExt5ebNqUrEn2NzeDDh{grid-column-end:span 1}.cdwvRRac4c2djSpHfOpF{grid-column-end:span 2}.hvRpxlyFY9BQIDdEGTjg{grid-column-end:span 3}.Ev9JGJi3yKkBq6cW3Xge{grid-column-end:span 4}.mhL__tIHFH_tViX5718D{grid-column-end:span 5}.X1lGIxHHxsFl_39u4Px6{grid-column-end:span 6}.pc5UnY_DzsSDkyih78Ti{grid-column-end:span 7}.QCjBtfnG3u20FwoDd59Z{grid-column-end:span 8}.MX1aL3BeJpoSE0aXghp0{grid-column-end:span 9}.h_JAcO8a8ClV2LmTWsMz{grid-column-end:span 10}.gKlDMi0N7LOd9q8uJHi5{grid-column-end:span 11}.KZWhYB7r6TG75uJu2LsQ{grid-column-end:span 12}}@media(max-width:960px){.G6NxfG2gGwvEYb0aGdPJ{display:none}}@media(max-width:600px){.aceZPGfrg7IoR0Vu7ZJg{display:none}}.MZSHJVi991kNJhn_Xnip{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);display:block;margin:32px 0;padding:16px 24px 16px 64px;position:relative;text-decoration:none}.MZSHJVi991kNJhn_Xnip span{display:block}.MZSHJVi991kNJhn_Xnip span:last-of-type{font-weight:600}.MZSHJVi991kNJhn_Xnip:focus span:last-of-type,.MZSHJVi991kNJhn_Xnip:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.MZSHJVi991kNJhn_Xnip:focus:after,.MZSHJVi991kNJhn_Xnip:hover:after{transform:translateY(-50%) translateX(-8px)}.MZSHJVi991kNJhn_Xnip:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;left:24px;position:absolute;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.EZ590eIMC5y_t1_gWRua{margin-right:-20px}.BYzOly4G7Gry9wdRH0AY{background-color:#fff;padding:40px 0}.iwMEh2CwJ_r23Y9sVcJ1,.O8YnwnZegI89S2Q3k5Qn{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.EC1Yirv2kOgRU77F2XRd{grid-column-end:span 1}.FHZqFvZXEDJ9mpOm7CeY{grid-column-end:span 2}.VOmONLKUkcSrIRYmX6YZ{grid-column-end:span 3}.eECO55N5YGkBB3tKa0If{grid-column-end:span 4}@media(min-width:600px){.u3ZB3zRkuglILO6FSIrx{grid-column-end:span 1}.iZnPl6piWO_GqHn16XKi{grid-column-end:span 2}.WWctfqi1wHZDWrue8GIt{grid-column-end:span 3}.p678NN_M0K78Kpprfndu{grid-column-end:span 4}._6D2TXtGKmepmwrxSf5DW{grid-column-end:span 5}.cWORs5VDjuStXsiVPtiO{grid-column-end:span 6}.yJsc8qt82L9miwDoe2Y2{grid-column-end:span 7}.hD6bXWfNn5elhKCaBFTD{grid-column-end:span 8}}@media(min-width:960px){.k9j6XIJdXbQ5YNyU8sNH{grid-column-end:span 1}.TnVGX0JHm_muRRcmYYwV{grid-column-end:span 2}.VILIswQXi8XzNJ3BqePc{grid-column-end:span 3}.h7mXuoRFWMGCiWYH4Ij1{grid-column-end:span 4}.uHYkk5STnhikqYMIxtE1{grid-column-end:span 5}.E8zp8nDOvAQ7VHsXKJJw{grid-column-end:span 6}.jwOhfvvsXGTCtHfB2854{grid-column-end:span 7}.DmSEZVFn_2XNNKJGgkXw{grid-column-end:span 8}.RdgOIY1qMZ8eqAeNSGNw{grid-column-end:span 9}.XBQd6xiLipeHg0O6cf7N{grid-column-end:span 10}.JyQ6Tbn3FCHfrCEyWrAr{grid-column-end:span 11}.fzJ_qaXYWs3EWQEupbLD{grid-column-end:span 12}}@media(max-width:960px){.LE2ZLIt_Y0slsr6TSmqe{display:none}}@media(max-width:600px){.vYxwZbK_covZoLjDe3BM{display:none}}.fmzqGXmbTSiUblRujgqB{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);display:block;margin:32px 0;padding:16px 24px 16px 64px;position:relative;text-decoration:none}.fmzqGXmbTSiUblRujgqB span{display:block}.fmzqGXmbTSiUblRujgqB span:last-of-type{font-weight:600}.fmzqGXmbTSiUblRujgqB:focus span:last-of-type,.fmzqGXmbTSiUblRujgqB:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.fmzqGXmbTSiUblRujgqB:focus:after,.fmzqGXmbTSiUblRujgqB:hover:after{transform:translateY(-50%) translateX(-8px)}.fmzqGXmbTSiUblRujgqB:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;left:24px;position:absolute;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.HwO79godDUI97Xub9ey4{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.HwO79godDUI97Xub9ey4{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.HwO79godDUI97Xub9ey4{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.WcIPVMUrQMdwzisuVSh7{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.Nxx7T20LwPUQPXCSDMfA{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.Nxx7T20LwPUQPXCSDMfA{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.Nxx7T20LwPUQPXCSDMfA{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.MnTtKIHRyzXYoesgRCgm{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);display:block;margin:32px 0;padding:16px 24px 16px 64px;position:relative;text-decoration:none}.MnTtKIHRyzXYoesgRCgm span{display:block}.MnTtKIHRyzXYoesgRCgm span:last-of-type{font-weight:600}.MnTtKIHRyzXYoesgRCgm:focus span:last-of-type,.MnTtKIHRyzXYoesgRCgm:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.MnTtKIHRyzXYoesgRCgm:focus:after,.MnTtKIHRyzXYoesgRCgm:hover:after{transform:translateY(-50%) translateX(-8px)}.MnTtKIHRyzXYoesgRCgm:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;left:24px;position:absolute;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.mmVmo1nZYrRywmnSKQFx{grid-column-end:span 1}.UrIp26XpdUSMJft8YxdC{grid-column-end:span 2}.pMXlFWglEDyK6MbwJ8sX{grid-column-end:span 3}.P5H1ohz9m5_rEqZFAkFm{grid-column-end:span 4}@media(min-width:600px){.ofLUn8LwR3LLeWl8tPdU{grid-column-end:span 1}.fq1Z8JYbcrrXavvSfzKk{grid-column-end:span 2}.jq9gui11HqTDfI7tXH1d{grid-column-end:span 3}.mVYkSRC7j44WiWLZHOnM{grid-column-end:span 4}.S3wOGmx7YLTrqz_bcLth{grid-column-end:span 5}.Y3GW3wokLgm9jnX78Uwk{grid-column-end:span 6}.ShMEdZjpjdYj7mCQzrSO{grid-column-end:span 7}.nfBAID75QGC1VZ8t0RfR{grid-column-end:span 8}}@media(min-width:960px){.Vr2EQcrmKOPJtFU72Vv2{grid-column-end:span 1}.CTS6MNweODFo4ZxcT0iV{grid-column-end:span 2}.XTISRluUo3o5xxnPNu09{grid-column-end:span 3}.c_EtRaSOJafAl5r9WkBm{grid-column-end:span 4}.HcpW_q5aO8Bf_ngIjyjv{grid-column-end:span 5}.XF3r0hMrFrrmxH5TJee0{grid-column-end:span 6}.Jl9ognyJ9XOZ6g0BTzLf{grid-column-end:span 7}._8w8oD2R9CVt9AU4PvUy{grid-column-end:span 8}.ltOXxurwUtxy7XIR_loo{grid-column-end:span 9}.bKUzzGEJ3wCoAOZZvVCK{grid-column-end:span 10}.GltQVCPa1x4tZ7sWFg1v{grid-column-end:span 11}.UzWicuFiKrGgMEjmRAFA{grid-column-end:span 12}}@media(max-width:960px){.CDwHAcVQxDeV2mFXS1Dz{display:none}}@media(max-width:600px){.sr1184KrdJ0UtgNsMQnR{display:none}}.jp-dashboard-footer{align-items:center;color:#000;display:flex;flex-flow:row wrap;justify-content:space-between;max-width:1128px;width:100%}.jp-dashboard-footer__jetpack-symbol,.jp-dashboard-footer__module-name{display:inline-block;vertical-align:middle}.jp-dashboard-footer__module-name{font-size:12px;font-weight:600;margin-right:5px}.K6gniZequRm1zBi4xa2G{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.vickZxEIl6kGZp7KkFYF{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.vickZxEIl6kGZp7KkFYF{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.vickZxEIl6kGZp7KkFYF{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.U2ZD1antXfCcQlBdFwyw{grid-column-end:span 1}.caLSPVQ3JP3b2Ia68Seq{grid-column-end:span 2}.h6FKJdwoY_zIXh6pEj1U{grid-column-end:span 3}.GkjDOzh_5n5VxKrqEW_I{grid-column-end:span 4}@media(min-width:600px){.V2HSxZHesNOXMBWOmdFG{grid-column-end:span 1}.i3fz9zvErDyXYherb6Ew{grid-column-end:span 2}._3bKcKfanUgurPMcHEiV{grid-column-end:span 3}.U44hqBGsmVA1lI9R2ZnH{grid-column-end:span 4}.AzaRYYQQYJjKUF9D87mw{grid-column-end:span 5}.sag3QKXTxIQXPPjmbrAl{grid-column-end:span 6}.ifY5fUWFNROS_F7Jys9v{grid-column-end:span 7}.nZFgLvw9LsUJnEViXnJZ{grid-column-end:span 8}}@media(min-width:960px){.Tqir6CbiyHOgYmsAgY57{grid-column-end:span 1}.VeVLz3DEagxiXMMPEOCe{grid-column-end:span 2}.G3R07cw2eP3D3juW3A3l{grid-column-end:span 3}.eQ3UseyneBNu_ZLuqSzg{grid-column-end:span 4}.VlgV8xjgkGoOLJShoyNM{grid-column-end:span 5}.DJCwqamaIDFQoj0jEgBc{grid-column-end:span 6}.CxtXPQUYJfNUQVn3y2y_{grid-column-end:span 7}.BroOgbyuFxoSv48H70B5{grid-column-end:span 8}.KnUcRFDSsxeb_z46ajNY{grid-column-end:span 9}.bXiAAxiM2mhcI43ET0pp{grid-column-end:span 10}.ipYVW2jwG2cFjIHpnQk2{grid-column-end:span 11}.KcREgfiJ4ix9nAvNAXqQ{grid-column-end:span 12}}@media(max-width:960px){.qSfEJeFAh4WmcPtmo8qe{display:none}}@media(max-width:600px){._RxxiRFsUS3nYTZhkprA{display:none}}.RSw9xhnEr8xpkX3hPPaM{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);display:block;margin:32px 0;padding:16px 24px 16px 64px;position:relative;text-decoration:none}.RSw9xhnEr8xpkX3hPPaM span{display:block}.RSw9xhnEr8xpkX3hPPaM span:last-of-type{font-weight:600}.RSw9xhnEr8xpkX3hPPaM:focus span:last-of-type,.RSw9xhnEr8xpkX3hPPaM:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.RSw9xhnEr8xpkX3hPPaM:focus:after,.RSw9xhnEr8xpkX3hPPaM:hover:after{transform:translateY(-50%) translateX(-8px)}.RSw9xhnEr8xpkX3hPPaM:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;left:24px;position:absolute;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.kEBaF5NOHtQsQn5jfOEA{background:var(--jp-white-off);padding:48px 0 64px}.kEBaF5NOHtQsQn5jfOEA h1,.kEBaF5NOHtQsQn5jfOEA h2,.kEBaF5NOHtQsQn5jfOEA h3,.kEBaF5NOHtQsQn5jfOEA h4,.kEBaF5NOHtQsQn5jfOEA h5,.kEBaF5NOHtQsQn5jfOEA h6{line-height:1.2;margin-top:0}._UqttTwRJeajPRpRGZPJ{background-color:#fff;padding:64px 0}._UqttTwRJeajPRpRGZPJ h1,._UqttTwRJeajPRpRGZPJ h2,._UqttTwRJeajPRpRGZPJ h3,._UqttTwRJeajPRpRGZPJ h4,._UqttTwRJeajPRpRGZPJ h5,._UqttTwRJeajPRpRGZPJ h6{line-height:1.2;margin-top:0}.jp-connection-status-card h3{color:var(--jp-black);font-size:36px;font-weight:400;line-height:40px;margin:0}.jp-connection-status-card a,.jp-connection-status-card a:active,.jp-connection-status-card a:hover{color:var(--jp-black)}.jp-connection-status-card p{color:var(--jp-black);margin:16px 0}.jp-connection-status-card a,.jp-connection-status-card li,.jp-connection-status-card p{font-size:16px;line-height:24px}.jp-connection-status-card--status{align-items:center;display:flex;margin:24px -6px 24px 0}.jp-connection-status-card--cloud{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDIiIGhlaWdodD0iNDIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PG1hc2sgaWQ9ImEiIG1hc2stdHlwZT0iYWxwaGEiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjciIHk9IjkiIHdpZHRoPSIyOCIgaGVpZ2h0PSIyMSI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik0zMC4zMzMgMTcuNjk3di0uMDhjMC00LjI1My0zLjc2LTcuNy04LjQtNy43LTMuOTE2IDAtNy4yMDYgMi40NTctOC4xMzYgNS43OGE2LjM4NiA2LjM4NiAwIDAgMC0uMjY0LS4wMDVDOS45MjUgMTUuNjkyIDcgMTguNzA4IDcgMjIuNDI5czIuOTI1IDYuNzM4IDYuNTMzIDYuNzM4SDI5LjRjMy4wOTMgMCA1LjYtMi41ODYgNS42LTUuNzc1IDAtMi44NjEtMi4wMTgtNS4yMzctNC42NjYtNS42OTVaIiBmaWxsPSIjZmZmIi8+PC9tYXNrPjxnIG1hc2s9InVybCgjYSkiPjxwYXRoIGQ9Im0zMC4zMzMgMTcuNjk3LTIuNjI1LS4wMy0uMDI1IDIuMjM1IDIuMjAyLjM4MS40NDgtMi41ODZabS0xNi41MzctMi0uMTA3IDIuNjIzIDIuMDc1LjA4NS41Ni0yLTIuNTI4LS43MDhabS0uMjYzIDEzLjQ3djIuNjI1aC4wMDhsLS4wMDgtMi42MjVabS4wNCAwdi0yLjYyNWgtLjAwOWwuMDA4IDIuNjI1Wm0xNS43OTUgMCAuMDE1LTIuNjI1aC0uMDE1djIuNjI1Wm0zLjU5LTExLjQ0MXYtLjExaC01LjI1di4wNTJsNS4yNS4wNThabTAtLjExYzAtNS45MTItNS4xNTUtMTAuMzI0LTExLjAyNS0xMC4zMjR2NS4yNWMzLjQxIDAgNS43NzUgMi40ODIgNS43NzUgNS4wNzVoNS4yNVpNMjEuOTMzIDcuMjkzYy00Ljk4NCAwLTkuMzg4IDMuMTM5LTEwLjY2NCA3LjY5OGw1LjA1NSAxLjQxNWMuNTg1LTIuMDg5IDIuNzYyLTMuODYzIDUuNjEtMy44NjN2LTUuMjVabS04LjAzIDUuNzgyYTkuMTExIDkuMTExIDAgMCAwLS4zNy0uMDA3djUuMjVjLjA1MyAwIC4xMDUuMDAxLjE1Ni4wMDNsLjIxNS01LjI0NlptLS4zNy0uMDA3Yy01LjEzMyAwLTkuMTU4IDQuMjY4LTkuMTU4IDkuMzYyaDUuMjVjMC0yLjM0OCAxLjgyNS00LjExMiAzLjkwOC00LjExMnYtNS4yNVptLTkuMTU4IDkuMzYyYzAgNS4wOTUgNC4wMjUgOS4zNjMgOS4xNTggOS4zNjN2LTUuMjVjLTIuMDgzIDAtMy45MDgtMS43NjUtMy45MDgtNC4xMTNoLTUuMjVabTkuMTY2IDkuMzYzaC4wNGwtLjAxNy01LjI1aC0uMDM5bC4wMTYgNS4yNVptLjAzMSAwaDE1Ljc5NnYtNS4yNUgxMy41NzJ2NS4yNVptMTUuODI3LTUuMjVoLS4wMTZsLS4wMyA1LjI1aC4wNDZ2LTUuMjVabTIuOTc1LTMuMTVjMCAxLjgxNi0xLjQwNyAzLjE1LTIuOTc1IDMuMTV2NS4yNWM0LjYxOCAwIDguMjI1LTMuODM4IDguMjI1LTguNGgtNS4yNVptLTIuNDg5LTMuMTA5YzEuMzU1LjIzNSAyLjQ5IDEuNDg4IDIuNDkgMy4xMDloNS4yNWMwLTQuMTAyLTIuOTAyLTcuNi02Ljg0NC04LjI4MmwtLjg5NiA1LjE3M1oiIGZpbGw9IiMxRTFFMUUiLz48L2c+PC9zdmc+);height:42px;margin-left:4px;width:42px}.jp-connection-status-card--jetpack-logo{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE2IDMyYzguODM3IDAgMTYtNy4xNjMgMTYtMTZTMjQuODM3IDAgMTYgMCAwIDcuMTYzIDAgMTZzNy4xNjMgMTYgMTYgMTZaIiBmaWxsPSIjMDAwIi8+PHBhdGggZD0iTTE2Ljc5NCAxMy4zMTN2MTUuNTExbDgtMTUuNTEyaC04Wk0xNS4xNzUgMTguNjU3VjMuMTc2bC03Ljk2OSAxNS40OGg3Ljk3WiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==);height:32px;margin-right:11px;width:32px}.jp-connection-status-card--btn-connect-user{background:var(--jp-black)!important;border-radius:4px;font-size:var(--font-body-small);height:40px}.jp-connection-status-card--avatar{background-color:var(--jp-white);background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='32' height='32' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='16' cy='16' r='16' fill='%23fff'/%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='M4.498 27.123C6.038 24.165 10.916 21.5 16 21.5c5.084 0 9.963 2.665 11.502 5.623a15.952 15.952 0 0 1-11.257 4.875L16 32l-.245-.002a15.952 15.952 0 0 1-11.257-4.875zM16 8a6 6 0 1 1 0 12 6 6 0 0 1 0-12z' fill='%23A2AAB2'/%3E%3C/svg%3E");background-repeat:no-repeat;background-size:contain;border:0;border-radius:20px;height:32px;margin-right:-10px;width:32px}.jp-connection-status-card--line{border-top:2px solid var(--jp-black);height:0;width:67px}.jp-connection-status-card--line.jp-connection-status-card--site-only{border-top-style:dashed}.jp-connection-status-card--list{list-style-type:none;margin:16px 0}.jp-connection-status-card--list li{color:var(--jp-black);margin:0 -3px 8px 0;padding-right:25px}.jp-connection-status-card--list-item-success{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0ibTE4LjkzNSA2LTguOSAxMS45Ny01LjE2Mi0zLjgzNyIgc3Ryb2tlPSIjMUUxRTFFIiBzdHJva2Utd2lkdGg9IjEuNSIvPjwvc3ZnPg==) no-repeat 100% 0}.jp-connection-status-card--list-item-error{background:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTEyIDIwYTggOCAwIDEgMCAwLTE2IDggOCAwIDAgMCAwIDE2WiIgc3Ryb2tlPSIjRDYzNjM5IiBzdHJva2Utd2lkdGg9IjEuNSIvPjxwYXRoIGQ9Ik0xMyA3aC0ydjZoMlY3Wk0xMyAxNWgtMnYyaDJ2LTJaIiBmaWxsPSIjRDYzNjM5Ii8+PC9zdmc+) no-repeat 100% 0;color:var(--jp-red)!important}.jp-connection__disconnect-dialog h1{font-size:var(--font-title-small);font-weight:600;line-height:1.2;margin-top:0}.jp-connection__disconnect-dialog h2{font-size:var(--font-title-small);font-weight:400;line-height:1.2;margin:0}.jp-connection__disconnect-dialog p{font-size:var(--font-body);margin-top:0}.jp-connection__disconnect-dialog__large-text,.jp-connection__disconnect-dialog p.jp-connection__disconnect-dialog__large-text{font-size:1.25rem}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link,.jp-connection__disconnect-dialog__link{color:var(--jp-black);font-size:var(--font-body);font:inherit;height:auto;padding:0;text-decoration:underline}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link:hover,.jp-connection__disconnect-dialog__link:hover{color:var(--jp-black);text-decoration-thickness:var(--jp-underline-thickness)}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link:focus,.jp-connection__disconnect-dialog__link:focus{box-shadow:none!important;color:var(--jp-black)}.jp-connection__disconnect-dialog .jp-connection__disconnect-dialog__link--bold,.jp-connection__disconnect-dialog__link--bold{font-weight:700}.jp-connection__disconnect-dialog .components-button{border-radius:4px;font-size:var(--font-body-small);height:40px}.jp-connection__disconnect-dialog .components-modal__content{display:flex;flex-direction:column;flex-grow:1;margin:0;padding:0}.jp-connection__disconnect-dialog .components-modal__content:before,.jp-connection__disconnect-dialog .components-modal__header{display:none}.jp-connection__disconnect-dialog .jp-row{align-items:center;width:calc(100% - 48px)}.jp-connection__disconnect-dialog__content{align-items:center;background:var(--jp-white-off);border-radius:4px;display:flex;flex-direction:column;flex-grow:1;justify-content:center;margin:0;padding:2rem 1rem;text-align:center}.jp-connection__disconnect-dialog__actions{background:var(--jp-white);border-top:1px solid var(--jp-gray);bottom:0;padding:2rem 0;position:sticky}.jp-connection__disconnect-dialog__actions p{margin-bottom:0}.jp-connection__disconnect-dialog__actions:before{background:linear-gradient(to bottom,transparent,var(--jp-white-off));bottom:calc(100% + 1px);content:"";display:block;height:80px;position:absolute;right:0;width:100%}.jp-connection__disconnect-dialog__btn-dismiss,.jp-connection__disconnect-dialog__btn-dismiss.components-button{background:var(--jp-black)!important;margin-left:10px}.jp-connection__disconnect-dialog__btn-disconnect{background:var(--jp-red)!important}.jp-connection__disconnect-dialog__btn-back-to-wp{background:var(--jp-black)!important}.jp-connection__disconnect-dialog__button-wrap{text-align:right}@media(min-width:960px){.jp-connection__disconnect-dialog__button-wrap{text-align:center}}.jp-connection__disconnect-dialog__error{color:var(--jp-red)}.jp-connection__disconnect-dialog__survey{margin-bottom:1.5rem;max-width:100%}.jp-connection__disconnect-dialog__step-copy{margin:0 auto;max-width:800px}.jp-connection__disconnect-dialog__step-copy--narrow{max-width:600px}@media(max-height:900px){.jp-connection__disconnect-dialog__content .jp-components__decorative-card{display:none}}@media(min-width:600px){.jp-connection__disconnect-dialog,.jp-connection__disconnect-dialog.components-modal__frame{max-width:calc(100% - 32px);width:100%}.jp-connection__disconnect-dialog__actions,.jp-connection__disconnect-dialog__content{padding:2rem}}@media(min-width:960px){.jp-connection__disconnect-dialog,.jp-connection__disconnect-dialog.components-modal__frame{display:flex;flex-direction:column;height:900px;width:1200px}.jp-connection__disconnect-dialog h1{font-size:var(--font-title-large)}.jp-connection__disconnect-dialog__large-text,.jp-connection__disconnect-dialog p.jp-connection__disconnect-dialog__large-text{font-size:1.5rem}.jp-connection__disconnect-dialog__content{padding:80px}.jp-connection__disconnect-dialog__actions{padding:2rem 3rem}.jp-row{margin-right:0}}.jp-connection__disconnect-card{background-color:var(--jp-white);border:none;border-radius:3px;box-shadow:0 0 15px var(--jp-gray-off);margin:0 auto 1rem;max-width:100%;padding:1rem 2rem;text-align:right;width:800px}.jp-connection__disconnect-card__group{margin-bottom:1rem;max-width:100%}.jp-connection__disconnect-card__card-content{display:block;font-size:.875rem}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-content{align-items:center;display:flex;justify-content:space-between}}.jp-connection__disconnect-card .jp-connection__disconnect-card__card-headline,.jp-connection__disconnect-card__card-headline{flex-shrink:0;font-size:1.25rem;font-weight:600;margin-bottom:0;margin-top:0}@media only screen and (min-width:782px){.jp-connection__disconnect-card .jp-connection__disconnect-card__card-headline,.jp-connection__disconnect-card__card-headline{font-size:1.5rem;margin-left:1.5rem}}@media only screen and (max-width:782px){.jp-connection__disconnect-card .jp-connection__disconnect-card__card-headline+.jp-disconnect-card__card-stat-block,.jp-connection__disconnect-card__card-headline+.jp-disconnect-card__card-stat-block{margin-top:.5rem}}.jp-connection__disconnect-card__card-stat-block{align-items:baseline;display:flex;flex-grow:1}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-stat-block{flex-direction:row-reverse}}.jp-connection__disconnect-card__card-description{flex-grow:1}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-description{text-align:left}}.jp-connection__disconnect-card__card-stat{font-size:1rem;font-weight:600;margin-left:.5rem}@media only screen and (min-width:782px){.jp-connection__disconnect-card__card-stat{font-size:1.5rem;margin-left:0;margin-right:1rem}}.jp-components__decorative-card{border-radius:8px;box-shadow:0 0 15px var(--jp-gray);display:flex;height:280px;margin:0 auto 3rem;max-width:100%;overflow:hidden;position:relative;width:360px}.jp-components__decorative-card__content,.jp-components__decorative-card__image{width:50%}.jp-components__decorative-card__image{background:var(--jp-gray);background-size:cover;position:relative}.jp-components__decorative-card__image:before{background-image:url('data:image/svg+xml;uf8,<svg width="38" height="8" viewBox="0 0 38 8" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M1 7C1 7 2.37087 1 6.89831 1C11.4257 1 14.3709 7 18.8983 7C23.4257 7 26.7777 1 31.3051 1C35.912 1 37 7 37 7" stroke="white" stroke-width="1.5" stroke-linejoin="round"/></svg>');content:"";display:block;height:8px;position:absolute;right:24px;top:24px;width:38px}.jp-components__decorative-card__content{background:#fff;padding:2rem}.jp-components__decorative-card__icon-container{background:var(--jp-red);border-radius:50px;height:80px;position:absolute;right:50%;top:50%;transform:translate(50%,-50%);width:80px}.jp-components__decorative-card__icon{background-position:50%,50%;background-repeat:no-repeat;height:40px;position:absolute;right:50%;top:50%;transform:translate(50%,-50%);width:40px}.jp-components__decorative-card__icon--unlink{background-image:url('data:image/svg+xml;uf8,<svg width="34" height="37" viewBox="0 0 34 37" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M22.3335 10.001H25.0002C29.4184 10.001 33.0002 13.5827 33.0002 18.001V19.7788C33.0002 24.197 29.4184 27.7788 25.0002 27.7788H22.3335" stroke="white" stroke-width="1.5" stroke-linecap="square"/> <path d="M11.6675 27.7783L9.00082 27.7783C4.58254 27.7783 1.00081 24.1966 1.00081 19.7783L1.00081 18.0005C1.00081 13.5823 4.58253 10.0005 9.00081 10.0005L11.6675 10.0005" stroke="white" stroke-width="1.5" stroke-linecap="square"/> <path d="M10.9998 19.167L16.9998 19.167" stroke="white" stroke-width="1.5"/> <path d="M8.99951 35.998L24.9995 0.998048" stroke="white"/> </svg>')}.jp-components__decorative-card__lines,.jp-components__decorative-card__lines:after,.jp-components__decorative-card__lines:before{background:#e9eff5;border-radius:6px;display:block;height:12px;position:relative;width:100%}.jp-components__decorative-card__lines:after,.jp-components__decorative-card__lines:before{content:"";top:calc(100% + 16px)}.jp-components__decorative-card__lines:after{top:calc(100% + 32px);width:75%}.jp-components__decorative-card--vertical{flex-direction:column}.jp-components__decorative-card--vertical .jp-components__decorative-card__content,.jp-components__decorative-card--vertical .jp-components__decorative-card__image{height:50%;width:100%}.jp-components__decorative-card--vertical .jp-components__decorative-card__lines{margin-left:auto;margin-right:auto;max-width:135px}.jp-components__decorative-card--vertical .jp-components__decorative-card__lines:after,.jp-components__decorative-card--vertical .jp-components__decorative-card__lines:before{margin-left:auto;margin-right:auto}:root{--font-title-large:36px;--font-title-small:24px;--font-body:16px;--font-label:12px;--jp-black:#000;--jp-black-80:#2c3338;--jp-white:#fff;--jp-white-off:#f9f9f6;--jp-gray:#dcdcde;--jp-gray-0:#f6f7f7;--jp-gray-20:#a7aaad;--jp-gray-40:#787c82;--jp-gray-50:#646970;--jp-gray-60:#50575e;--jp-gray-80:#8a2424;--jp-gray-off:#e2e2df;--jp-red-0:#f7ebec;--jp-red-50:#d63638;--jp-red-60:#b32d2e;--jp-red-80:#8a2424;--jp-red:#d63639;--jp-pink:#c9356e;--jp-green-0:#f0f2eb;--jp-green-5:#d0e6b8;--jp-green-10:#9dd977;--jp-green-20:#64ca43;--jp-green-30:#2fb41f;--jp-green-40:#069e08;--jp-green-50:#008710;--jp-green-60:#007117;--jp-green-70:#005b18;--jp-green-80:#004515;--jp-green-90:#003010;--jp-green-100:#001c09;--jp-green:#069e08;--jp-green-primary:var( --jp-green-40 );--jp-green-secondary:var( --jp-green-30 );--jp-border-radius:4px;--jp-menu-border-height:1px;--jp-underline-thickness:2px}*{box-sizing:border-box}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen-Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;margin:0;min-height:100%;padding:0}.jp-wrap{align-items:center;display:flex;flex-wrap:wrap;margin:0 auto;max-width:1128px}.jp-row{grid-gap:24px;display:grid;grid-template-columns:repeat(4,1fr);margin:0 16px;width:100%}@media(min-width:600px){.jp-row{grid-template-columns:repeat(8,1fr);margin:0 18px}}@media(min-width:960px){.jp-row{grid-template-columns:repeat(12,1fr);margin:0 24px;max-width:1128px}}.sm-col-span-1{grid-column-end:span 1}.sm-col-span-2{grid-column-end:span 2}.sm-col-span-3{grid-column-end:span 3}.sm-col-span-4{grid-column-end:span 4}@media(min-width:600px){.md-col-span-1{grid-column-end:span 1}.md-col-span-2{grid-column-end:span 2}.md-col-span-3{grid-column-end:span 3}.md-col-span-4{grid-column-end:span 4}.md-col-span-5{grid-column-end:span 5}.md-col-span-6{grid-column-end:span 6}.md-col-span-7{grid-column-end:span 7}.md-col-span-8{grid-column-end:span 8}}@media(min-width:960px){.lg-col-span-1{grid-column-end:span 1}.lg-col-span-2{grid-column-end:span 2}.lg-col-span-3{grid-column-end:span 3}.lg-col-span-4{grid-column-end:span 4}.lg-col-span-5{grid-column-end:span 5}.lg-col-span-6{grid-column-end:span 6}.lg-col-span-7{grid-column-end:span 7}.lg-col-span-8{grid-column-end:span 8}.lg-col-span-9{grid-column-end:span 9}.lg-col-span-10{grid-column-end:span 10}.lg-col-span-11{grid-column-end:span 11}.lg-col-span-12{grid-column-end:span 12}}@media(max-width:960px){.md-col-span-0{display:none}}@media(max-width:600px){.sm-col-span-0{display:none}}.jp-cut{border:2px solid var(--jp-green-primary);border-radius:var(--jp-border-radius);margin:32px 0;padding:16px 24px 16px 64px;position:relative;text-decoration:none}.jp-cut,.jp-cut span{display:block}.jp-cut span:last-of-type{font-weight:600}.jp-cut:focus span:last-of-type,.jp-cut:hover span:last-of-type{text-decoration:underline;text-decoration-thickness:var(--jp-underline-thickness)}.jp-cut:focus:after,.jp-cut:hover:after{transform:translateY(-50%) translateX(-8px)}.jp-cut:after{color:var(--jp-green-primary);content:"→";font-size:24px;font-weight:600;left:24px;position:absolute;top:50%;transform:translateY(-50%);transition:transform .15s ease-out}.jp-connect__disconnect-survey-card{border:2px solid transparent;border-radius:4px;box-shadow:0 0 15px var(--jp-gray-off);margin-left:auto;margin-right:auto;max-width:100%;padding:1rem;position:relative;text-align:right;width:800px}.jp-connect__disconnect-survey-card--selected{background:var(--jp-gray-off);border-color:var(--jp-black)}.jp-connect__disconnect-survey-card:after{border-left:2px solid var(--jp-black);border-top:2px solid var(--jp-black);content:"";display:block;height:5px;left:1.5rem;position:absolute;top:50%;transform:translateY(-50%) rotate(-45deg);width:5px}.jp-connect__disconnect-survey-card:hover{cursor:pointer}.jp-connect__disconnect-survey-card:focus:not(.jp-disconnect-survey-card--selected),.jp-connect__disconnect-survey-card:hover:not(.jp-disconnect-survey-card--selected){border-color:var(--jp-black-80)}.jp-connect__disconnect-survey-card__answer{align-items:center;display:flex;font-weight:700;margin:0}input.jp-connect__disconnect-survey-card__input{-webkit-appearance:none;background-color:transparent;border:none;color:var(--jp-black-80);flex-grow:1;max-width:calc(100% - 40px);padding-left:40px} \ No newline at end of file
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-initializer.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-initializer.php
new file mode 100644
index 00000000..94f18ca4
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/src/class-initializer.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * WP Admin page with information and configuration shared among all Jetpack stand-alone plugins
+ *
+ * @package automattic/my-jetpack
+ */
+
+namespace Automattic\Jetpack\My_Jetpack;
+
+use Automattic\Jetpack\Admin_UI\Admin_Menu;
+use Automattic\Jetpack\Assets;
+use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
+
+/**
+ * The main Initializer class that registers the admin menu and eneuque the assets.
+ */
+class Initializer {
+
+ /**
+ * Initialize My Jetapack
+ *
+ * @return void
+ */
+ public static function init() {
+ if ( did_action( 'my_jetpack_init' ) ) {
+ return;
+ }
+
+ // Feature flag while we are developing it.
+ if ( ! defined( 'JETPACK_ENABLE_MY_JETPACK' ) || ! JETPACK_ENABLE_MY_JETPACK ) {
+ return;
+ }
+
+ $page_suffix = Admin_Menu::add_menu(
+ __( 'My Jetpack', 'jetpack-my-jetpack' ),
+ __( 'My Jetpack', 'jetpack-my-jetpack' ),
+ 'manage_options',
+ 'my-jetpack',
+ array( __CLASS__, 'admin_page' ),
+ 999
+ );
+
+ add_action( 'load-' . $page_suffix, array( __CLASS__, 'admin_init' ) );
+
+ /**
+ * Fires after the My Jetpack package is initialized
+ *
+ * @since $$next_version$$
+ */
+ do_action( 'my_jetpack_init' );
+ }
+
+ /**
+ * Callback for the load my jetpack page hook.
+ *
+ * @return void
+ */
+ public static function admin_init() {
+ add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_scritps' ) );
+ }
+
+ /**
+ * Enqueue admin page assets.
+ *
+ * @return void
+ */
+ public static function enqueue_scritps() {
+ Assets::register_script(
+ 'my_jetpack_main_app',
+ '../build/index.js',
+ __FILE__,
+ array(
+ 'enqueue' => true,
+ 'in_footer' => true,
+ 'textdomain' => 'jetpack-my-jetpack',
+ )
+ );
+ wp_localize_script(
+ 'my_jetpack_main_app',
+ 'myJetpackInitialState',
+ array(
+ 'apiRoot' => esc_url_raw( rest_url() ),
+ 'apiNonce' => wp_create_nonce( 'wp_rest' ),
+ 'redirectUrl' => admin_url( '?page=my-jetpack' ),
+ 'topJetpackMenuItemUrl' => Admin_Menu::get_top_level_menu_item_url(),
+ )
+ );
+
+ // Connection Initial State.
+ wp_add_inline_script( 'my_jetpack_main_app', Connection_Initial_State::render(), 'before' );
+ }
+
+ /**
+ * Echos the admin page content.
+ *
+ * @return void
+ */
+ public static function admin_page() {
+ echo '<div id="my-jetpack-container"></div>';
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/webpack.config.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/webpack.config.js
new file mode 100644
index 00000000..ad461e3d
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-my-jetpack/webpack.config.js
@@ -0,0 +1,60 @@
+/**
+ * External dependencies
+ */
+const jetpackWebpackConfig = require( '@automattic/jetpack-webpack-config/webpack' );
+const path = require( 'path' );
+
+module.exports = [
+ {
+ entry: {
+ index: './_inc/admin.jsx',
+ },
+ mode: jetpackWebpackConfig.mode,
+ devtool: jetpackWebpackConfig.isDevelopment ? 'source-map' : false,
+ output: {
+ ...jetpackWebpackConfig.output,
+ path: path.resolve( './build' ),
+ },
+ optimization: {
+ ...jetpackWebpackConfig.optimization,
+ },
+ resolve: {
+ ...jetpackWebpackConfig.resolve,
+ },
+ node: false,
+ plugins: [
+ ...jetpackWebpackConfig.StandardPlugins( {
+ DependencyExtractionPlugin: { injectPolyfill: true },
+ } ),
+ ],
+ module: {
+ strictExportPresence: true,
+ rules: [
+ // Transpile JavaScript
+ jetpackWebpackConfig.TranspileRule( {
+ exclude: /node_modules\//,
+ } ),
+
+ // Transpile @automattic/jetpack-* in node_modules too.
+ jetpackWebpackConfig.TranspileRule( {
+ includeNodeModules: [ '@automattic/jetpack-' ],
+ } ),
+
+ // Handle CSS.
+ jetpackWebpackConfig.CssRule( {
+ extensions: [ 'css', 'sass', 'scss' ],
+ extraLoaders: [ 'sass-loader' ],
+ } ),
+
+ // Handle images.
+ jetpackWebpackConfig.FileRule(),
+ ],
+ },
+ externals: {
+ ...jetpackWebpackConfig.externals,
+ jetpackConfig: JSON.stringify( {
+ consumer_slug: 'my_jetpack',
+ } ),
+ },
+ },
+];
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-options/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-options/CHANGELOG.md
new file mode 100644
index 00000000..9f7ade0b
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-options/CHANGELOG.md
@@ -0,0 +1,178 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.14.2] - 2022-01-04
+### Changed
+- Updated package dependencies
+
+## [1.14.1] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.14.0] - 2021-11-30
+### Added
+- Added has_seen_wc_connection_modal option to the list of available options
+- Add new Jetpack plugin partner coupon options
+
+## [1.13.5] - 2021-11-16
+### Added
+- Added Jetpack Option 'licensing_activation_notice_dismiss'.
+
+## [1.13.4] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.13.3] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.13.2] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.13.1] - 2021-08-30
+### Changed
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- update annotations versions
+
+## [1.13.0] - 2021-06-15
+### Changed
+- Added 'purchaseToken' option for logged out user purchases on WordPress.com.
+
+## [1.12.1] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.12.0] - 2021-04-27
+### Added
+- Added unique_registrations option
+
+## [1.11.4] - 2021-04-08
+### Changed
+- Packaging and build changes, no change to the package itself.
+
+## [1.11.3] - 2021-03-30
+### Added
+- added active_modules_initialized option
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+## [1.11.2] - 2021-02-23
+
+- Setup Wizard: Remove setup wizard
+- Jetpack Recommendations: Add dashboard banner
+
+## [1.11.1] - 2021-01-28
+
+- Jetpack Recommendations: Show Recommendations tab for all users (#18576)\n\nCommitted via a GitHub action: https://github.com/automattic/jetpack/runs/518520328
+
+## [1.11.0] - 2021-01-26
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.10.0] - 2021-01-05
+
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.9.1] - 2020-11-24
+
+- Version packages for release
+
+## [1.9.0] - 2020-09-29
+
+- Licensing: Add support for Jetpack licenses
+
+## [1.8.0] - 2020-08-25
+
+- Jetpack Anti-Spam: update Anti-Spam label in sidebar menu item
+
+## [1.7.0] - 2020-07-28
+
+- Add a filter to jetpack options
+
+## [1.6.0] - 2020-06-30
+
+- Various: Update use of whitelist/blacklist
+
+## [1.5.0] - 2020-05-26
+
+- Jetpack Setup Wizard: Add Setup Wizard status and routing memory
+- Implement Jetpack Wizard banner
+- Implemented /setup/questionnaire API endpoint for the new setup wizard
+
+## [1.4.0] - 2020-04-28
+
+- Update dependencies to latest stable
+
+## [1.3.0] - 2020-03-31
+
+- Update dependencies to latest stable
+
+## [1.2.0] - 2020-02-25
+
+- Mobile Theme: remove feature
+
+## [1.1.3] - 2020-02-14
+
+- Initial Sync Health Status Class and Data Loss Handler
+
+## [1.1.2] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## [1.1.1] - 2019-10-29
+
+- PHPCS: Rest of the packages
+
+## [1.1.0] - 2019-09-14
+
+- Add Stats to XML-RPC Errors
+- Stats: Use Transient API to Improve Cache Performance
+
+## [1.0.1] - 2019-07-10
+
+- Adding a readme to the Jetpack Options package.
+
+## 1.0.0 - 2019-07-09
+
+- Initial release
+
+[1.14.2]: https://github.com/Automattic/jetpack-options/compare/v1.14.1...v1.14.2
+[1.14.1]: https://github.com/Automattic/jetpack-options/compare/v1.14.0...v1.14.1
+[1.14.0]: https://github.com/Automattic/jetpack-options/compare/v1.13.5...v1.14.0
+[1.13.5]: https://github.com/Automattic/jetpack-options/compare/v1.13.4...v1.13.5
+[1.13.4]: https://github.com/Automattic/jetpack-options/compare/v1.13.3...v1.13.4
+[1.13.3]: https://github.com/Automattic/jetpack-options/compare/v1.13.2...v1.13.3
+[1.13.2]: https://github.com/Automattic/jetpack-options/compare/v1.13.1...v1.13.2
+[1.13.1]: https://github.com/Automattic/jetpack-options/compare/v1.13.0...v1.13.1
+[1.13.0]: https://github.com/Automattic/jetpack-options/compare/v1.12.1...v1.13.0
+[1.12.1]: https://github.com/Automattic/jetpack-options/compare/v1.12.0...v1.12.1
+[1.12.0]: https://github.com/Automattic/jetpack-options/compare/v1.11.4...v1.12.0
+[1.11.4]: https://github.com/Automattic/jetpack-options/compare/v1.11.3...v1.11.4
+[1.11.3]: https://github.com/Automattic/jetpack-options/compare/v1.11.2...v1.11.3
+[1.11.2]: https://github.com/Automattic/jetpack-options/compare/v1.11.1...v1.11.2
+[1.11.1]: https://github.com/Automattic/jetpack-options/compare/v1.11.0...v1.11.1
+[1.11.0]: https://github.com/Automattic/jetpack-options/compare/v1.10.0...v1.11.0
+[1.10.0]: https://github.com/Automattic/jetpack-options/compare/v1.9.1...v1.10.0
+[1.9.1]: https://github.com/Automattic/jetpack-options/compare/v1.9.0...v1.9.1
+[1.9.0]: https://github.com/Automattic/jetpack-options/compare/v1.8.0...v1.9.0
+[1.8.0]: https://github.com/Automattic/jetpack-options/compare/v1.7.0...v1.8.0
+[1.7.0]: https://github.com/Automattic/jetpack-options/compare/v1.6.0...v1.7.0
+[1.6.0]: https://github.com/Automattic/jetpack-options/compare/v1.5.0...v1.6.0
+[1.5.0]: https://github.com/Automattic/jetpack-options/compare/v1.4.0...v1.5.0
+[1.4.0]: https://github.com/Automattic/jetpack-options/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/Automattic/jetpack-options/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-options/compare/v1.1.3...v1.2.0
+[1.1.3]: https://github.com/Automattic/jetpack-options/compare/v1.1.2...v1.1.3
+[1.1.2]: https://github.com/Automattic/jetpack-options/compare/v1.1.1...v1.1.2
+[1.1.1]: https://github.com/Automattic/jetpack-options/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-options/compare/v1.0.1...v1.1.0
+[1.0.1]: https://github.com/Automattic/jetpack-options/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-options/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-options/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-options/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-options/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-options/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-options/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-options/legacy/class-jetpack-options.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-options/legacy/class-jetpack-options.php
new file mode 100644
index 00000000..164ff111
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-options/legacy/class-jetpack-options.php
@@ -0,0 +1,689 @@
+<?php
+/**
+ * Legacy Jetpack_Options class.
+ *
+ * @package automattic/jetpack-options
+ */
+
+use Automattic\Jetpack\Constants;
+
+/**
+ * Class Jetpack_Options
+ */
+class Jetpack_Options {
+
+ /**
+ * An array that maps a grouped option type to an option name.
+ *
+ * @var array
+ */
+ private static $grouped_options = array(
+ 'compact' => 'jetpack_options',
+ 'private' => 'jetpack_private_options',
+ );
+
+ /**
+ * Returns an array of option names for a given type.
+ *
+ * @param string $type The type of option to return. Defaults to 'compact'.
+ *
+ * @return array
+ */
+ public static function get_option_names( $type = 'compact' ) {
+ switch ( $type ) {
+ case 'non-compact':
+ case 'non_compact':
+ return array(
+ 'activated',
+ 'active_modules',
+ 'active_modules_initialized', // (bool) used to determine that all the default modules were activated, so we know how to act on a reconnection.
+ 'allowed_xsite_search_ids', // (array) Array of WP.com blog ids that are allowed to search the content of this site
+ 'available_modules',
+ 'do_activate',
+ 'edit_links_calypso_redirect', // (bool) Whether post/page edit links on front end should point to Calypso.
+ 'log',
+ 'slideshow_background_color',
+ 'widget_twitter',
+ 'wpcc_options',
+ 'relatedposts',
+ 'file_data',
+ 'autoupdate_plugins', // (array) An array of plugin ids ( eg. jetpack/jetpack ) that should be autoupdated
+ 'autoupdate_plugins_translations', // (array) An array of plugin ids ( eg. jetpack/jetpack ) that should be autoupdated translation files.
+ 'autoupdate_themes', // (array) An array of theme ids ( eg. twentyfourteen ) that should be autoupdated
+ 'autoupdate_themes_translations', // (array) An array of theme ids ( eg. twentyfourteen ) that should autoupdated translation files.
+ 'autoupdate_core', // (bool) Whether or not to autoupdate core
+ 'autoupdate_translations', // (bool) Whether or not to autoupdate all translations
+ 'json_api_full_management', // (bool) Allow full management (eg. Activate, Upgrade plugins) of the site via the JSON API.
+ 'sync_non_public_post_stati', // (bool) Allow synchronisation of posts and pages with non-public status.
+ 'site_icon_url', // (string) url to the full site icon
+ 'site_icon_id', // (int) Attachment id of the site icon file
+ 'dismissed_manage_banner', // (bool) Dismiss Jetpack manage banner allows the user to dismiss the banner permanently
+ 'unique_connection', // (array) A flag to determine a unique connection to wordpress.com two values "connected" and "disconnected" with values for how many times each has occured
+ 'unique_registrations', // (integer) A counter of how many times the site was registered
+ 'protect_whitelist', // (array) IP Address for the Protect module to ignore
+ 'sync_error_idc', // (bool|array) false or array containing the site's home and siteurl at time of IDC error
+ 'sync_health_status', // (bool|array) An array of data relating to Jetpack's sync health.
+ 'safe_mode_confirmed', // (bool) True if someone confirms that this site was correctly put into safe mode automatically after an identity crisis is discovered.
+ 'migrate_for_idc', // (bool) True if someone confirms that this site should migrate stats and subscribers from its previous URL
+ 'dismissed_connection_banner', // (bool) True if the connection banner has been dismissed
+ 'ab_connect_banner_green_bar', // (int) Version displayed of the A/B test for the green bar at the top of the connect banner.
+ 'onboarding', // (string) Auth token to be used in the onboarding connection flow
+ 'tos_agreed', // (bool) Whether or not the TOS for connection has been agreed upon.
+ 'static_asset_cdn_files', // (array) An nested array of files that we can swap out for cdn versions.
+ 'mapbox_api_key', // (string) Mapbox API Key, for use with Map block.
+ 'mailchimp', // (string) Mailchimp keyring data, for mailchimp block.
+ 'xmlrpc_errors', // (array) Keys are XML-RPC signature error codes. Values are truthy.
+ 'dismissed_wizard_banner', // (int) (DEPRECATED) True if the Wizard banner has been dismissed.
+ );
+
+ case 'private':
+ return array(
+ 'blog_token', // (string) The Client Secret/Blog Token of this site.
+ 'user_token', // (string) The User Token of this site. (deprecated)
+ 'user_tokens', // (array) User Tokens for each user of this site who has connected to jetpack.wordpress.com.
+ 'purchase_token', // (string) Token for logged out user purchases.
+ );
+
+ case 'network':
+ return array(
+ 'onboarding', // (string) Auth token to be used in the onboarding connection flow
+ 'file_data', // (array) List of absolute paths to all Jetpack modules
+ );
+ }
+
+ return array(
+ 'id', // (int) The Client ID/WP.com Blog ID of this site.
+ 'publicize_connections', // (array) An array of Publicize connections from WordPress.com.
+ 'master_user', // (int) The local User ID of the user who connected this site to jetpack.wordpress.com.
+ 'version', // (string) Used during upgrade procedure to auto-activate new modules. version:time.
+ 'old_version', // (string) Used to determine which modules are the most recently added. previous_version:time.
+ 'fallback_no_verify_ssl_certs', // (int) Flag for determining if this host must skip SSL Certificate verification due to misconfigured SSL.
+ 'time_diff', // (int) Offset between Jetpack server's clocks and this server's clocks. Jetpack Server Time = time() + (int) Jetpack_Options::get_option( 'time_diff' )
+ 'public', // (int|bool) If we think this site is public or not (1, 0), false if we haven't yet tried to figure it out.
+ 'videopress', // (array) VideoPress options array.
+ 'is_network_site', // (int|bool) If we think this site is a network or a single blog (1, 0), false if we haven't yet tried to figue it out.
+ 'social_links', // (array) The specified links for each social networking site.
+ 'identity_crisis_whitelist', // (array) An array of options, each having an array of the values whitelisted for it.
+ 'gplus_authors', // (array) The Google+ authorship information for connected users.
+ 'last_heartbeat', // (int) The timestamp of the last heartbeat that fired.
+ 'hide_jitm', // (array) A list of just in time messages that we should not show because they have been dismissed by the user.
+ 'custom_css_4.7_migration', // (bool) Whether Custom CSS has scanned for and migrated any legacy CSS CPT entries to the new Core format.
+ 'image_widget_migration', // (bool) Whether any legacy Image Widgets have been converted to the new Core widget.
+ 'gallery_widget_migration', // (bool) Whether any legacy Gallery Widgets have been converted to the new Core widget.
+ 'sso_first_login', // (bool) Is this the first time the user logins via SSO.
+ 'dismissed_hints', // (array) Part of Plugin Search Hints. List of cards that have been dismissed.
+ 'first_admin_view', // (bool) Set to true the first time the user views the admin. Usually after the initial connection.
+ 'setup_wizard_questionnaire', // (array) (DEPRECATED) List of user choices from the setup wizard.
+ 'setup_wizard_status', // (string) (DEPRECATED) Status of the setup wizard.
+ 'licensing_error', // (string) Last error message occurred while attaching licenses that is yet to be surfaced to the user.
+ 'recommendations_banner_dismissed', // (bool) Determines if the recommendations dashboard banner is dismissed or not.
+ 'recommendations_banner_enabled', // (bool) Whether the recommendations are enabled or not.
+ 'recommendations_data', // (array) The user choice and other data for the recommendations.
+ 'recommendations_step', // (string) The current step of the recommendations.
+ 'licensing_activation_notice_dismiss', // (array) The `last_detached_count` and the `last_dismissed_time` for the user-license activation notice.
+ 'has_seen_wc_connection_modal', // (bool) Whether the site has displayed the WooCommerce Connection modal
+ 'partner_coupon', // (string) A Jetpack partner issued coupon to promote a sale together with Jetpack.
+ 'partner_coupon_added', // (string) A date for when `partner_coupon` was added, so we can auto-purge after a certain time interval.
+ );
+ }
+
+ /**
+ * Is the option name valid?
+ *
+ * @param string $name The name of the option.
+ * @param string|null $group The name of the group that the option is in. Default to null, which will search non_compact.
+ *
+ * @return bool Is the option name valid?
+ */
+ public static function is_valid( $name, $group = null ) {
+ if ( is_array( $name ) ) {
+ $compact_names = array();
+ foreach ( array_keys( self::$grouped_options ) as $_group ) {
+ $compact_names = array_merge( $compact_names, self::get_option_names( $_group ) );
+ }
+
+ $result = array_diff( $name, self::get_option_names( 'non_compact' ), $compact_names );
+
+ return empty( $result );
+ }
+
+ if ( is_null( $group ) || 'non_compact' === $group ) {
+ if ( in_array( $name, self::get_option_names( $group ), true ) ) {
+ return true;
+ }
+ }
+
+ foreach ( array_keys( self::$grouped_options ) as $_group ) {
+ if ( is_null( $group ) || $group === $_group ) {
+ if ( in_array( $name, self::get_option_names( $_group ), true ) ) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if an option must be saved for the whole network in WP Multisite
+ *
+ * @param string $option_name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
+ *
+ * @return bool
+ */
+ public static function is_network_option( $option_name ) {
+ if ( ! is_multisite() ) {
+ return false;
+ }
+ return in_array( $option_name, self::get_option_names( 'network' ), true );
+ }
+
+ /**
+ * Filters the requested option.
+ * This is a wrapper around `get_option_from_database` so that we can filter the option.
+ *
+ * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
+ * @param mixed $default (optional).
+ *
+ * @return mixed
+ */
+ public static function get_option( $name, $default = false ) {
+ /**
+ * Filter Jetpack Options.
+ * Can be useful in environments when Jetpack is running with a different setup
+ *
+ * @since 1.7.0
+ *
+ * @param string $value The value from the database.
+ * @param string $name Option name, _without_ `jetpack_%` prefix.
+ * @return string $value, unless the filters modify it.
+ */
+ return apply_filters( 'jetpack_options', self::get_option_from_database( $name, $default ), $name );
+ }
+
+ /**
+ * Returns the requested option. Looks in jetpack_options or jetpack_$name as appropriate.
+ *
+ * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
+ * @param mixed $default (optional).
+ *
+ * @return mixed
+ */
+ private static function get_option_from_database( $name, $default = false ) {
+ if ( self::is_valid( $name, 'non_compact' ) ) {
+ if ( self::is_network_option( $name ) ) {
+ return get_site_option( "jetpack_$name", $default );
+ }
+
+ return get_option( "jetpack_$name", $default );
+ }
+
+ foreach ( array_keys( self::$grouped_options ) as $group ) {
+ if ( self::is_valid( $name, $group ) ) {
+ return self::get_grouped_option( $group, $name, $default );
+ }
+ }
+
+ trigger_error( sprintf( 'Invalid Jetpack option name: %s', esc_html( $name ) ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Don't wish to change legacy behavior.
+
+ return $default;
+ }
+
+ /**
+ * Returns the requested option, and ensures it's autoloaded in the future.
+ * This does _not_ adjust the prefix in any way (does not prefix jetpack_%)
+ *
+ * @param string $name Option name.
+ * @param mixed $default (optional).
+ *
+ * @return mixed
+ */
+ public static function get_option_and_ensure_autoload( $name, $default ) {
+ // In this function the name is not adjusted by prefixing jetpack_
+ // so if it has already prefixed, we'll replace it and then
+ // check if the option name is a network option or not.
+ $jetpack_name = preg_replace( '/^jetpack_/', '', $name, 1 );
+ $is_network_option = self::is_network_option( $jetpack_name );
+ $value = $is_network_option ? get_site_option( $name ) : get_option( $name );
+
+ if ( false === $value && false !== $default ) {
+ if ( $is_network_option ) {
+ add_site_option( $name, $default );
+ } else {
+ add_option( $name, $default );
+ }
+ $value = $default;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Update grouped option
+ *
+ * @param string $group Options group.
+ * @param string $name Options name.
+ * @param mixed $value Options value.
+ *
+ * @return bool Success or failure.
+ */
+ private static function update_grouped_option( $group, $name, $value ) {
+ $options = get_option( self::$grouped_options[ $group ] );
+ if ( ! is_array( $options ) ) {
+ $options = array();
+ }
+ $options[ $name ] = $value;
+
+ return update_option( self::$grouped_options[ $group ], $options );
+ }
+
+ /**
+ * Updates the single given option. Updates jetpack_options or jetpack_$name as appropriate.
+ *
+ * @param string $name Option name. It must come _without_ `jetpack_%` prefix. The method will prefix the option name.
+ * @param mixed $value Option value.
+ * @param string $autoload If not compact option, allows specifying whether to autoload or not.
+ *
+ * @return bool Was the option successfully updated?
+ */
+ public static function update_option( $name, $value, $autoload = null ) {
+ /**
+ * Fires before Jetpack updates a specific option.
+ *
+ * @since 1.1.2
+ * @since-jetpack 3.0.0
+ *
+ * @param str $name The name of the option being updated.
+ * @param mixed $value The new value of the option.
+ */
+ do_action( 'pre_update_jetpack_option_' . $name, $name, $value );
+ if ( self::is_valid( $name, 'non_compact' ) ) {
+ if ( self::is_network_option( $name ) ) {
+ return update_site_option( "jetpack_$name", $value );
+ }
+
+ return update_option( "jetpack_$name", $value, $autoload );
+
+ }
+
+ foreach ( array_keys( self::$grouped_options ) as $group ) {
+ if ( self::is_valid( $name, $group ) ) {
+ return self::update_grouped_option( $group, $name, $value );
+ }
+ }
+
+ trigger_error( sprintf( 'Invalid Jetpack option name: %s', esc_html( $name ) ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Don't want to change legacy behavior.
+
+ return false;
+ }
+
+ /**
+ * Updates the multiple given options. Updates jetpack_options and/or jetpack_$name as appropriate.
+ *
+ * @param array $array array( option name => option value, ... ).
+ */
+ public static function update_options( $array ) {
+ $names = array_keys( $array );
+
+ foreach ( array_diff( $names, self::get_option_names(), self::get_option_names( 'non_compact' ), self::get_option_names( 'private' ) ) as $unknown_name ) {
+ trigger_error( sprintf( 'Invalid Jetpack option name: %s', esc_html( $unknown_name ) ), E_USER_WARNING ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Don't change legacy behavior.
+ unset( $array[ $unknown_name ] );
+ }
+
+ foreach ( $names as $name ) {
+ self::update_option( $name, $array[ $name ] );
+ }
+ }
+
+ /**
+ * Deletes the given option. May be passed multiple option names as an array.
+ * Updates jetpack_options and/or deletes jetpack_$name as appropriate.
+ *
+ * @param string|array $names Option names. They must come _without_ `jetpack_%` prefix. The method will prefix the option names.
+ *
+ * @return bool Was the option successfully deleted?
+ */
+ public static function delete_option( $names ) {
+ $result = true;
+ $names = (array) $names;
+
+ if ( ! self::is_valid( $names ) ) {
+ // phpcs:disable -- This line triggers a handful of errors; ignoring to avoid changing legacy behavior.
+ trigger_error( sprintf( 'Invalid Jetpack option names: %s', print_r( $names, 1 ) ), E_USER_WARNING );
+ // phpcs:enable
+ return false;
+ }
+
+ foreach ( array_intersect( $names, self::get_option_names( 'non_compact' ) ) as $name ) {
+ if ( self::is_network_option( $name ) ) {
+ $result = delete_site_option( "jetpack_$name" );
+ } else {
+ $result = delete_option( "jetpack_$name" );
+ }
+ }
+
+ foreach ( array_keys( self::$grouped_options ) as $group ) {
+ if ( ! self::delete_grouped_option( $group, $names ) ) {
+ $result = false;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Get group option.
+ *
+ * @param string $group Option group name.
+ * @param string $name Option name.
+ * @param mixed $default Default option value.
+ *
+ * @return mixed Option.
+ */
+ private static function get_grouped_option( $group, $name, $default ) {
+ $options = get_option( self::$grouped_options[ $group ] );
+ if ( is_array( $options ) && isset( $options[ $name ] ) ) {
+ return $options[ $name ];
+ }
+
+ return $default;
+ }
+
+ /**
+ * Delete grouped option.
+ *
+ * @param string $group Option group name.
+ * @param array $names Option names.
+ *
+ * @return bool Success or failure.
+ */
+ private static function delete_grouped_option( $group, $names ) {
+ $options = get_option( self::$grouped_options[ $group ], array() );
+
+ $to_delete = array_intersect( $names, self::get_option_names( $group ), array_keys( $options ) );
+ if ( $to_delete ) {
+ foreach ( $to_delete as $name ) {
+ unset( $options[ $name ] );
+ }
+
+ return update_option( self::$grouped_options[ $group ], $options );
+ }
+
+ return true;
+ }
+
+ /*
+ * Raw option methods allow Jetpack to get / update / delete options via direct DB queries, including options
+ * that are not created by the Jetpack plugin. This is helpful only in rare cases when we need to bypass
+ * cache and filters.
+ */
+
+ /**
+ * Deletes an option via $wpdb query.
+ *
+ * @param string $name Option name.
+ *
+ * @return bool Is the option deleted?
+ */
+ public static function delete_raw_option( $name ) {
+ if ( self::bypass_raw_option( $name ) ) {
+ return delete_option( $name );
+ }
+ global $wpdb;
+ $result = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->options WHERE option_name = %s", $name ) );
+ return $result;
+ }
+
+ /**
+ * Updates an option via $wpdb query.
+ *
+ * @param string $name Option name.
+ * @param mixed $value Option value.
+ * @param bool $autoload Specifying whether to autoload or not.
+ *
+ * @return bool Is the option updated?
+ */
+ public static function update_raw_option( $name, $value, $autoload = false ) {
+ if ( self::bypass_raw_option( $name ) ) {
+ return update_option( $name, $value, $autoload );
+ }
+ global $wpdb;
+ $autoload_value = $autoload ? 'yes' : 'no';
+
+ $old_value = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1",
+ $name
+ )
+ );
+ if ( $old_value === $value ) {
+ return false;
+ }
+
+ $serialized_value = maybe_serialize( $value );
+ // below we used "insert ignore" to at least suppress the resulting error.
+ $updated_num = $wpdb->query(
+ $wpdb->prepare(
+ "UPDATE $wpdb->options SET option_value = %s WHERE option_name = %s",
+ $serialized_value,
+ $name
+ )
+ );
+
+ // Try inserting the option if the value doesn't exits.
+ if ( ! $updated_num ) {
+ $updated_num = $wpdb->query(
+ $wpdb->prepare(
+ "INSERT IGNORE INTO $wpdb->options ( option_name, option_value, autoload ) VALUES ( %s, %s, %s )",
+ $name,
+ $serialized_value,
+ $autoload_value
+ )
+ );
+ }
+ return (bool) $updated_num;
+ }
+
+ /**
+ * Gets an option via $wpdb query.
+ *
+ * @since 1.1.2
+ * @since-jetpack 5.4.0
+ *
+ * @param string $name Option name.
+ * @param mixed $default Default option value if option is not found.
+ *
+ * @return mixed Option value, or null if option is not found and default is not specified.
+ */
+ public static function get_raw_option( $name, $default = null ) {
+ if ( self::bypass_raw_option( $name ) ) {
+ return get_option( $name, $default );
+ }
+
+ global $wpdb;
+ $value = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1",
+ $name
+ )
+ );
+ $value = maybe_unserialize( $value );
+
+ if ( null === $value && null !== $default ) {
+ return $default;
+ }
+
+ return $value;
+ }
+
+ /**
+ * This function checks for a constant that, if present, will disable direct DB queries Jetpack uses to manage certain options and force Jetpack to always use Options API instead.
+ * Options can be selectively managed via a blocklist by filtering option names via the jetpack_disabled_raw_option filter.
+ *
+ * @param string $name Option name.
+ *
+ * @return bool
+ */
+ public static function bypass_raw_option( $name ) {
+
+ if ( Constants::get_constant( 'JETPACK_DISABLE_RAW_OPTIONS' ) ) {
+ return true;
+ }
+ /**
+ * Allows to disable particular raw options.
+ *
+ * @since 1.1.2
+ * @since-jetpack 5.5.0
+ *
+ * @param array $disabled_raw_options An array of option names that you can selectively blocklist from being managed via direct database queries.
+ */
+ $disabled_raw_options = apply_filters( 'jetpack_disabled_raw_options', array() );
+ return isset( $disabled_raw_options[ $name ] );
+ }
+
+ /**
+ * Gets all known options that are used by Jetpack and managed by Jetpack_Options.
+ *
+ * @since 1.1.2
+ * @since-jetpack 5.4.0
+ *
+ * @param boolean $strip_unsafe_options If true, and by default, will strip out options necessary for the connection to WordPress.com.
+ * @return array An array of all options managed via the Jetpack_Options class.
+ */
+ public static function get_all_jetpack_options( $strip_unsafe_options = true ) {
+ $jetpack_options = self::get_option_names();
+ $jetpack_options_non_compat = self::get_option_names( 'non_compact' );
+ $jetpack_options_private = self::get_option_names( 'private' );
+
+ $all_jp_options = array_merge( $jetpack_options, $jetpack_options_non_compat, $jetpack_options_private );
+
+ if ( $strip_unsafe_options ) {
+ // Flag some Jetpack options as unsafe.
+ $unsafe_options = array(
+ 'id', // (int) The Client ID/WP.com Blog ID of this site.
+ 'master_user', // (int) The local User ID of the user who connected this site to jetpack.wordpress.com.
+ 'version', // (string) Used during upgrade procedure to auto-activate new modules. version:time
+
+ // non_compact.
+ 'activated',
+
+ // private.
+ 'register',
+ 'blog_token', // (string) The Client Secret/Blog Token of this site.
+ 'user_token', // (string) The User Token of this site. (deprecated)
+ 'user_tokens',
+ );
+
+ // Remove the unsafe Jetpack options.
+ foreach ( $unsafe_options as $unsafe_option ) {
+ $key = array_search( $unsafe_option, $all_jp_options, true );
+ if ( false !== $key ) {
+ unset( $all_jp_options[ $key ] );
+ }
+ }
+ }
+
+ return $all_jp_options;
+ }
+
+ /**
+ * Get all options that are not managed by the Jetpack_Options class that are used by Jetpack.
+ *
+ * @since 1.1.2
+ * @since-jetpack 5.4.0
+ *
+ * @return array
+ */
+ public static function get_all_wp_options() {
+ // A manual build of the wp options.
+ return array(
+ 'sharing-options',
+ 'disabled_likes',
+ 'disabled_reblogs',
+ 'jetpack_comments_likes_enabled',
+ 'stats_options',
+ 'stats_dashboard_widget',
+ 'safecss_preview_rev',
+ 'safecss_rev',
+ 'safecss_revision_migrated',
+ 'nova_menu_order',
+ 'jetpack_portfolio',
+ 'jetpack_portfolio_posts_per_page',
+ 'jetpack_testimonial',
+ 'jetpack_testimonial_posts_per_page',
+ 'sharedaddy_disable_resources',
+ 'sharing-options',
+ 'sharing-services',
+ 'site_icon_temp_data',
+ 'featured-content',
+ 'site_logo',
+ 'jetpack_dismissed_notices',
+ 'jetpack-twitter-cards-site-tag',
+ 'jetpack-sitemap-state',
+ 'jetpack_sitemap_post_types',
+ 'jetpack_sitemap_location',
+ 'jetpack_protect_key',
+ 'jetpack_protect_blocked_attempts',
+ 'jetpack_protect_activating',
+ 'jetpack_connection_banner_ab',
+ 'jetpack_active_plan',
+ 'jetpack_activation_source',
+ 'jetpack_site_products',
+ 'jetpack_sso_match_by_email',
+ 'jetpack_sso_require_two_step',
+ 'jetpack_sso_remove_login_form',
+ 'jetpack_last_connect_url_check',
+ 'jpo_business_address',
+ 'jpo_site_type',
+ 'jpo_homepage_format',
+ 'jpo_contact_page',
+ 'jetpack_excluded_extensions',
+ );
+ }
+
+ /**
+ * Gets all options that can be safely reset by CLI.
+ *
+ * @since 1.1.2
+ * @since-jetpack 5.4.0
+ *
+ * @return array array Associative array containing jp_options which are managed by the Jetpack_Options class and wp_options which are not.
+ */
+ public static function get_options_for_reset() {
+ $all_jp_options = self::get_all_jetpack_options();
+
+ $wp_options = self::get_all_wp_options();
+
+ $options = array(
+ 'jp_options' => $all_jp_options,
+ 'wp_options' => $wp_options,
+ );
+
+ return $options;
+ }
+
+ /**
+ * Delete all known options
+ *
+ * @since 1.1.2
+ * @since-jetpack 5.4.0
+ *
+ * @return void
+ */
+ public static function delete_all_known_options() {
+ // Delete all compact options.
+ foreach ( (array) self::$grouped_options as $option_name ) {
+ delete_option( $option_name );
+ }
+
+ // Delete all non-compact Jetpack options.
+ foreach ( (array) self::get_option_names( 'non-compact' ) as $option_name ) {
+ self::delete_option( $option_name );
+ }
+
+ // Delete all options that can be reset via CLI, that aren't Jetpack options.
+ foreach ( (array) self::get_all_wp_options() as $option_name ) {
+ delete_option( $option_name );
+ }
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/CHANGELOG.md
new file mode 100644
index 00000000..e9aef962
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/CHANGELOG.md
@@ -0,0 +1,130 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.6.2] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+
+## [1.6.1] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.6.0] - 2021-11-30
+### Added
+- Addde partner coupon logic
+
+## [1.5.7] - 2021-11-23
+### Changed
+- Updated package dependencies
+
+## [1.5.6] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.5.5] - 2021-10-19
+### Changed
+- Updated package dependencies.
+
+## [1.5.4] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.5.3] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.5.2] - 2021-08-31
+### Changed
+- Run composer update on test-php command instead of phpunit.
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- update annotations versions.
+
+## [1.5.1] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.5.0] - 2021-04-27
+### Added
+- Adds segmentation "from" parameter to the registration flow
+
+## [1.4.3] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.4.2] - 2021-02-05
+
+- CI: Make tests more generic
+
+## [1.4.1] - 2021-01-20
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.4.0] - 2020-12-14
+
+- Update dependency brain/monkey to v2.6.0
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.3.1] - 2020-10-29
+
+- Update dependency brain/monkey to v2.5.0
+- Updated PHPCS: Packages and Debugger
+
+## [1.3.0] - 2020-08-25
+
+- Update Authorize URL iframe to include affiliate code
+- Packages: Update filenames after #16810
+- CI: Try collect js coverage
+- Docker: Add package testing shortcut
+
+## [1.2.0] - 2020-07-01
+
+- Package Unit tests: update test file names to make sure they runs in Travis
+
+## [1.1.0] - 2020-06-22
+
+- PHPCS: Clean up the packages
+- PHPCS Updates after WPCS 2.3
+- Update README.md on partner package
+
+## [1.0.1] - 2020-01-27
+
+- Pin dependency brain/monkey to 2.4.0
+
+## 1.0.0 - 2019-12-16
+
+- Add partner subsidiary id to upgrade URLs.
+
+[1.6.2]: https://github.com/Automattic/jetpack-partner/compare/v1.6.1...v1.6.2
+[1.6.1]: https://github.com/Automattic/jetpack-partner/compare/v1.6.0...v1.6.1
+[1.6.0]: https://github.com/Automattic/jetpack-partner/compare/v1.5.7...v1.6.0
+[1.5.7]: https://github.com/Automattic/jetpack-partner/compare/v1.5.6...v1.5.7
+[1.5.6]: https://github.com/Automattic/jetpack-partner/compare/v1.5.5...v1.5.6
+[1.5.5]: https://github.com/Automattic/jetpack-partner/compare/v1.5.4...v1.5.5
+[1.5.4]: https://github.com/Automattic/jetpack-partner/compare/v1.5.3...v1.5.4
+[1.5.3]: https://github.com/Automattic/jetpack-partner/compare/v1.5.2...v1.5.3
+[1.5.2]: https://github.com/Automattic/jetpack-partner/compare/v1.5.1...v1.5.2
+[1.5.1]: https://github.com/Automattic/jetpack-partner/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/Automattic/jetpack-partner/compare/v1.4.3...v1.5.0
+[1.4.3]: https://github.com/Automattic/jetpack-partner/compare/v1.4.2...v1.4.3
+[1.4.2]: https://github.com/Automattic/jetpack-partner/compare/v1.4.1...v1.4.2
+[1.4.1]: https://github.com/Automattic/jetpack-partner/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/Automattic/jetpack-partner/compare/v1.3.1...v1.4.0
+[1.3.1]: https://github.com/Automattic/jetpack-partner/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/Automattic/jetpack-partner/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-partner/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/Automattic/jetpack-partner/compare/v1.0.1...v1.1.0
+[1.0.1]: https://github.com/Automattic/jetpack-partner/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/src/class-partner-coupon.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/src/class-partner-coupon.php
new file mode 100644
index 00000000..a1619a2b
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/src/class-partner-coupon.php
@@ -0,0 +1,324 @@
+<?php
+/**
+ * Class for the Jetpack partner coupon logic.
+ *
+ * @package automattic/jetpack-partner
+ */
+
+namespace Automattic\Jetpack;
+
+use Automattic\Jetpack\Connection\Manager as Connection_Manager;
+use Jetpack_Options;
+
+/**
+ * Disable direct access.
+ */
+if ( ! defined( 'ABSPATH' ) ) {
+ exit;
+}
+
+/**
+ * Class Jetpack_Partner_Coupon
+ *
+ * @since $$next_version$$
+ */
+class Partner_Coupon {
+
+ /**
+ * Name of the Jetpack_Option coupon option.
+ *
+ * @var string
+ */
+ public static $coupon_option = 'partner_coupon';
+
+ /**
+ * Name of the Jetpack_Option added option.
+ *
+ * @var string
+ */
+ public static $added_option = 'partner_coupon_added';
+
+ /**
+ * Jetpack_Partner_Coupon
+ *
+ * @var Partner_Coupon|null
+ **/
+ private static $instance = null;
+
+ /**
+ * A list of supported partners.
+ *
+ * @var array
+ */
+ private static $supported_partners = array(
+ 'IONOS' => 'IONOS',
+ );
+
+ /**
+ * A list of supported presets.
+ *
+ * @var array
+ */
+ private static $supported_presets = array(
+ 'IONA' => 'jetpack_backup_daily',
+ );
+
+ /**
+ * Get singleton instance of class.
+ */
+ public static function get_instance() {
+ if ( is_null( self::$instance ) ) {
+ self::$instance = new Partner_Coupon();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Register hooks to catch and purge coupon.
+ *
+ * @param string $plugin_slug The plugin slug to differentiate between Jetpack connections.
+ * @param string $redirect_location The location we should redirect to after catching the coupon.
+ */
+ public static function register_coupon_admin_hooks( $plugin_slug, $redirect_location ) {
+ $instance = self::get_instance();
+
+ add_action( 'admin_init', array( $instance, 'purge_coupon' ) );
+
+ // We have to use an anonymous function, so we can pass along relevant information
+ // and not have to hardcode values for a single plugin.
+ // This open up the opportunity for e.g. the "all-in-one" and backup plugins
+ // to both implement partner coupon logic.
+ add_action(
+ 'admin_init',
+ function () use ( $plugin_slug, $redirect_location, $instance ) {
+ $instance->catch_coupon( $plugin_slug, $redirect_location );
+ }
+ );
+ }
+
+ /**
+ * Catch partner coupon and redirect to claim component.
+ *
+ * @param string $plugin_slug The plugin slug to differentiate between Jetpack connections.
+ * @param string $redirect_location The location we should redirect to after catching the coupon.
+ */
+ public function catch_coupon( $plugin_slug, $redirect_location ) {
+ // Accept and store a partner coupon if present, and redirect to Jetpack connection screen.
+ $partner_coupon = isset( $_GET['jetpack-partner-coupon'] ) ? sanitize_text_field( $_GET['jetpack-partner-coupon'] ) : false; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( $partner_coupon ) {
+ Jetpack_Options::update_options(
+ array(
+ self::$coupon_option => $partner_coupon,
+ self::$added_option => time(),
+ )
+ );
+
+ $connection = new Connection_Manager( $plugin_slug );
+ if ( $connection->is_connected() ) {
+ $redirect_location = add_query_arg( array( 'showCouponRedemption' => 1 ), $redirect_location );
+ wp_safe_redirect( $redirect_location );
+ } else {
+ wp_safe_redirect( $redirect_location );
+ }
+ }
+ }
+
+ /**
+ * Purge partner coupon.
+ *
+ * We automatically purge partner coupons after a certain amount of time to prevent
+ * us from unnecessarily promoting a product for months or years in the future.
+ */
+ public function purge_coupon() {
+ $date = Jetpack_Options::get_option( self::$added_option, '' );
+
+ if ( empty( $date ) ) {
+ return;
+ }
+
+ $expire_date = strtotime( '+30 days', $date );
+ $today = time();
+
+ if ( $today >= $expire_date ) {
+ Jetpack_Options::delete_option(
+ array(
+ self::$coupon_option,
+ self::$added_option,
+ )
+ );
+ }
+ }
+
+ /**
+ * Get partner coupon data.
+ *
+ * @return array|bool
+ */
+ public static function get_coupon() {
+ $coupon_code = Jetpack_Options::get_option( self::$coupon_option, '' );
+
+ if ( ! is_string( $coupon_code ) || empty( $coupon_code ) ) {
+ return false;
+ }
+
+ $instance = self::get_instance();
+ $partner = $instance->get_coupon_partner( $coupon_code );
+
+ if ( ! $partner ) {
+ return false;
+ }
+
+ $preset = $instance->get_coupon_preset( $coupon_code );
+
+ if ( ! $preset ) {
+ return false;
+ }
+
+ $product = $instance->get_coupon_product( $preset );
+
+ if ( ! $product ) {
+ return false;
+ }
+
+ return array(
+ 'coupon_code' => $coupon_code,
+ 'partner' => $partner,
+ 'preset' => $preset,
+ 'product' => $product,
+ );
+ }
+
+ /**
+ * Get coupon partner.
+ *
+ * @param string $coupon_code Coupon code to go through.
+ * @return array|bool
+ */
+ private function get_coupon_partner( $coupon_code ) {
+ if ( ! is_string( $coupon_code ) || false === strpos( $coupon_code, '_' ) ) {
+ return false;
+ }
+
+ $prefix = strtok( $coupon_code, '_' );
+ $supported_partners = $this->get_supported_partners();
+
+ if ( ! isset( $supported_partners[ $prefix ] ) ) {
+ return false;
+ }
+
+ return array(
+ 'name' => $supported_partners[ $prefix ],
+ 'prefix' => $prefix,
+ );
+ }
+
+ /**
+ * Get coupon product.
+ *
+ * @param string $coupon_preset The preset we wish to find a product for.
+ * @return array|bool
+ */
+ private function get_coupon_product( $coupon_preset ) {
+ if ( ! is_string( $coupon_preset ) ) {
+ return false;
+ }
+
+ /**
+ * Allow for plugins to register supported products.
+ *
+ * @since $$next_version$$
+ *
+ * @param array A list of product details.
+ * @return array
+ */
+ $product_details = apply_filters( 'jetpack_partner_coupon_products', array() );
+ $product_slug = $this->get_supported_presets()[ $coupon_preset ];
+
+ foreach ( $product_details as $product ) {
+ if ( ! $this->array_keys_exist( array( 'title', 'slug', 'description', 'features' ), $product ) ) {
+ continue;
+ }
+
+ if ( $product_slug === $product['slug'] ) {
+ return $product;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if multiple keys are present in an array.
+ *
+ * @param array $needles The keys we wish to check for.
+ * @param array $haystack The array we want to compare keys against.
+ *
+ * @return bool
+ */
+ private function array_keys_exist( $needles, $haystack ) {
+ foreach ( $needles as $needle ) {
+ if ( ! isset( $haystack[ $needle ] ) ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Get coupon preset.
+ *
+ * @param string $coupon_code Coupon code to go through.
+ * @return string|bool
+ */
+ private function get_coupon_preset( $coupon_code ) {
+ if ( ! is_string( $coupon_code ) ) {
+ return false;
+ }
+
+ $regex = '/^.*?_(?P<slug>.*?)_.+$/';
+ $matches = array();
+
+ if ( ! preg_match( $regex, $coupon_code, $matches ) ) {
+ return false;
+ }
+
+ return isset( $this->get_supported_presets()[ $matches['slug'] ] ) ? $matches['slug'] : false;
+ }
+
+ /**
+ * Get supported partners.
+ *
+ * @return array
+ */
+ private function get_supported_partners() {
+ /**
+ * Allow external code to add additional supported partners.
+ *
+ * @since $$next_version$$
+ *
+ * @param array $supported_partners A list of supported partners.
+ * @return array
+ */
+ return apply_filters( 'jetpack_partner_coupon_supported_partners', self::$supported_partners );
+ }
+
+ /**
+ * Get supported presets.
+ *
+ * @return array
+ */
+ private function get_supported_presets() {
+ /**
+ * Allow external code to add additional supported presets.
+ *
+ * @since $$next_version$$
+ *
+ * @param array $supported_presets A list of supported presets.
+ * @return array
+ */
+ return apply_filters( 'jetpack_partner_coupon_supported_presets', self::$supported_presets );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/src/class-partner.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/src/class-partner.php
new file mode 100644
index 00000000..bec501bc
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-partner/src/class-partner.php
@@ -0,0 +1,199 @@
+<?php
+/**
+ * Jetpack Partner package.
+ *
+ * @package automattic/jetpack-partner
+ */
+
+namespace Automattic\Jetpack;
+
+/**
+ * This class introduces functionality used by Jetpack hosting partners.
+ *
+ * @since 1.0.0
+ */
+class Partner {
+
+ /**
+ * Affiliate code.
+ */
+ const AFFILIATE_CODE = 'affiliate';
+
+ /**
+ * Subsidiary id code.
+ */
+ const SUBSIDIARY_CODE = 'subsidiary';
+
+ /**
+ * Singleton instance.
+ *
+ * @since 1.0.0
+ *
+ * @var Partner This class instance.
+ */
+ private static $instance = null;
+
+ /**
+ * Partner constructor.
+ */
+ private function __construct() {
+ }
+
+ /**
+ * Initializes the class or returns the singleton.
+ *
+ * @return Partner | false
+ * @since 1.0.0
+ */
+ public static function init() {
+ if ( is_null( self::$instance ) ) {
+ self::$instance = new Partner();
+ add_filter( 'jetpack_build_authorize_url', array( self::$instance, 'add_subsidiary_id_as_query_arg' ) );
+ add_filter( 'jetpack_build_authorize_url', array( self::$instance, 'add_affiliate_code_as_query_arg' ) );
+ add_filter( 'jetpack_build_connection_url', array( self::$instance, 'add_subsidiary_id_as_query_arg' ) );
+ add_filter( 'jetpack_build_connection_url', array( self::$instance, 'add_affiliate_code_as_query_arg' ) );
+
+ add_filter( 'jetpack_register_request_body', array( self::$instance, 'add_subsidiary_id_to_params_array' ) );
+ add_filter( 'jetpack_register_request_body', array( self::$instance, 'add_affiliate_code_to_params_array' ) );
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Adds the partner subsidiary code to the passed URL.
+ *
+ * @param string $url The URL.
+ *
+ * @return string
+ */
+ public function add_subsidiary_id_as_query_arg( $url ) {
+ return $this->add_code_as_query_arg( self::SUBSIDIARY_CODE, $url );
+ }
+
+ /**
+ * Adds the affiliate code to the passed URL.
+ *
+ * @param string $url The URL.
+ *
+ * @return string
+ */
+ public function add_affiliate_code_as_query_arg( $url ) {
+ return $this->add_code_as_query_arg( self::AFFILIATE_CODE, $url );
+ }
+
+ /**
+ * Adds the partner subsidiary code to the passed array.
+ *
+ * @param array $params The parameters array.
+ *
+ * @return array
+ * @since 1.5.0
+ */
+ public function add_subsidiary_id_to_params_array( $params ) {
+ if ( ! is_array( $params ) ) {
+ return $params;
+ }
+ return array_merge( $params, $this->get_code_as_array( self::SUBSIDIARY_CODE ) );
+ }
+
+ /**
+ * Adds the affiliate code to the passed array.
+ *
+ * @param array $params The parameters array.
+ *
+ * @return array
+ * @since 1.5.0
+ */
+ public function add_affiliate_code_to_params_array( $params ) {
+ if ( ! is_array( $params ) ) {
+ return $params;
+ }
+ return array_merge( $params, $this->get_code_as_array( self::AFFILIATE_CODE ) );
+ }
+
+ /**
+ * Returns the passed URL with the partner code added as a URL query arg.
+ *
+ * @param string $type The partner code.
+ * @param string $url The URL where the partner subsidiary id will be added.
+ *
+ * @return string The passed URL with the partner code added.
+ * @since 1.0.0
+ */
+ public function add_code_as_query_arg( $type, $url ) {
+ return add_query_arg( $this->get_code_as_array( $type ), $url );
+ }
+
+ /**
+ * Gets the partner code in an associative array format
+ *
+ * @param string $type The partner code.
+ * @return array
+ * @since 1.5.0
+ */
+ private function get_code_as_array( $type ) {
+ switch ( $type ) {
+ case self::AFFILIATE_CODE:
+ $query_arg_name = 'aff';
+ break;
+ case self::SUBSIDIARY_CODE:
+ $query_arg_name = 'subsidiaryId';
+ break;
+ default:
+ return array();
+ }
+
+ $code = $this->get_partner_code( $type );
+
+ if ( '' === $code ) {
+ return array();
+ }
+
+ return array( $query_arg_name => $code );
+ }
+
+ /**
+ * Returns a partner code.
+ *
+ * @param string $type This can be either 'affiliate' or 'subsidiary'. Returns empty string when code is unknown.
+ *
+ * @return string The partner code.
+ * @since 1.0.0
+ */
+ public function get_partner_code( $type ) {
+ switch ( $type ) {
+ case self::AFFILIATE_CODE:
+ /**
+ * Allow to filter the affiliate code.
+ *
+ * @param string $affiliate_code The affiliate code, blank by default.
+ *
+ * @since 1.0.0
+ * @since-jetpack 6.9.0
+ */
+ return apply_filters( 'jetpack_affiliate_code', get_option( 'jetpack_affiliate_code', '' ) );
+ case self::SUBSIDIARY_CODE:
+ /**
+ * Allow to filter the partner subsidiary id.
+ *
+ * @param string $subsidiary_id The partner subsidiary id, blank by default.
+ *
+ * @since 1.0.0
+ */
+ return apply_filters(
+ 'jetpack_partner_subsidiary_id',
+ get_option( 'jetpack_partner_subsidiary_id', '' )
+ );
+ default:
+ return '';
+ }
+ }
+
+ /**
+ * Resets the singleton for testing purposes.
+ */
+ public static function reset() {
+ self::$instance = null;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/CHANGELOG.md
new file mode 100644
index 00000000..faa074b4
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/CHANGELOG.md
@@ -0,0 +1,63 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [0.2.0] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+- Updated package textdomain from `jetpack` to `jetpack-password-checker`.
+
+## [0.1.8] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [0.1.7] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [0.1.6] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [0.1.5] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [0.1.4] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [0.1.3] - 2021-08-30
+### Changed
+- Run composer update on test-php command instead of phpunit
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+
+## [0.1.2] - 2021-05-25
+### Fixed
+- Avoid checking in vendor directory.
+
+## [0.1.1] - 2021-04-27
+### Changed
+- Updated package dependencies.
+
+## 0.1.0 - 2021-03-30
+### Added
+- Initial release.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+[0.2.0]: https://github.com/Automattic/jetpack-password-checker/compare/v0.1.8...v0.2.0
+[0.1.8]: https://github.com/Automattic/jetpack-password-checker/compare/v0.1.7...v0.1.8
+[0.1.7]: https://github.com/Automattic/jetpack-password-checker/compare/v0.1.6...v0.1.7
+[0.1.6]: https://github.com/Automattic/jetpack-password-checker/compare/v0.1.5...v0.1.6
+[0.1.5]: https://github.com/Automattic/jetpack-password-checker/compare/v0.1.4...v0.1.5
+[0.1.4]: https://github.com/Automattic/jetpack-password-checker/compare/v0.1.3...v0.1.4
+[0.1.3]: https://github.com/Automattic/jetpack-password-checker/compare/v0.1.2...v0.1.3
+[0.1.2]: https://github.com/Automattic/jetpack-password-checker/compare/v0.1.1...v0.1.2
+[0.1.1]: https://github.com/Automattic/jetpack-password-checker/compare/v0.1.0...v0.1.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/src/class-password-checker.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/src/class-password-checker.php
new file mode 100644
index 00000000..7c9cec39
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-password-checker/src/class-password-checker.php
@@ -0,0 +1,1313 @@
+<?php
+/**
+ * Password Checker package.
+ *
+ * @package automattic/jetpack-password-checker
+ */
+
+namespace Automattic\Jetpack;
+
+/**
+ * Checks passwords strength.
+ */
+class Password_Checker {
+ /**
+ * Minimum entropy bits a password should contain. 36 bits of entropy is considered
+ * to be a reasonable password, 28 stands for a weak one.
+ *
+ * @var int
+ */
+ public $minimum_entropy_bits = 28;
+
+ /**
+ * Test results array.
+ *
+ * @var array
+ */
+ public $test_results = array();
+
+ /**
+ * Current password score.
+ *
+ * @var int
+ */
+ public $score = 0;
+
+ /**
+ * Current multiplier affecting the score.
+ *
+ * @var int
+ */
+ public $multiplier = 4;
+
+ /**
+ * A common password disallow list, which on match will immediately disqualify the password.
+ *
+ * @var array
+ */
+ public $common_passwords = array();
+
+ /**
+ * Minimum password length setting.
+ *
+ * @var int
+ */
+ public $minimum_password_length = 6;
+
+ /**
+ * User defined strings that passwords need to be tested for a match against.
+ *
+ * @var array
+ */
+ private $user_strings_to_test = array();
+
+ /**
+ * The user object for whom the password is being tested.
+ *
+ * @var mixed
+ */
+ protected $user = null;
+
+ /**
+ * The user identifier for whom the password is being tested, used if there's no user object.
+ *
+ * @var mixed
+ */
+ protected $user_id = null;
+
+ /**
+ * Creates an instance of the password checker class for the specified user, or
+ * defaults to the currently logged in user.
+ *
+ * @param mixed $user can be an integer ID, or a WP_User object.
+ */
+ public function __construct( $user = null ) {
+ if ( is_null( $user ) ) {
+ $this->user_id = get_current_user_id();
+ } elseif ( is_object( $user ) && isset( $user->ID ) ) {
+ // Existing user, using their ID.
+ $this->user_id = $user->ID;
+ } elseif ( is_object( $user ) ) {
+ // Newly created user, using existing data.
+ $this->user = $user;
+ $this->user_id = 'new_user';
+ } else {
+ $this->user_id = $user;
+ }
+
+ /**
+ * Filters the password strength enforcement settings.
+ *
+ * You can supply your own passwords that should not be used for authenticating in addition to weak and easy
+ * to guess strings for each user. For example, you can add passwords from known password databases to avoid
+ * compromised password usage.
+ *
+ * @param array $common_passwords strings that are forbidden for use as passwords.
+ */
+ $this->common_passwords = apply_filters( 'password_checker_common_passwords', $this->common_passwords );
+
+ /**
+ * Filters the password strength enforcement settings.
+ *
+ * You can modify the minimum password length using this filter.
+ *
+ * @param int $minimum_password_length minimum password length.
+ */
+ $this->minimum_password_length = apply_filters( 'password_checker_minimum_password_length', $this->minimum_password_length );
+
+ /**
+ * Filters the password strength enforcement settings.
+ *
+ * You can modify the minimum entropy bits requirement using this filter.
+ *
+ * @param int $minimum_entropy_bits minimum entropy bits requirement.
+ */
+ $this->minimum_entropy_bits = apply_filters( 'password_checker_minimum_entropy_bits', $this->minimum_entropy_bits );
+ }
+
+ /**
+ * Run tests against a password.
+ *
+ * @param string $password the password.
+ * @param bool $required_only only test against required conditions, defaults to false.
+ *
+ * @return array an array containing failed and passed test results.
+ */
+ public function test( $password, $required_only = false ) {
+ // Run the tests.
+ $results = $this->run_tests( $password, $this->get_tests(), $required_only );
+
+ // If we've failed on the required tests, return now.
+ if ( ! empty( $results['failed'] ) ) {
+ return array(
+ 'passed' => false,
+ 'test_results' => $results,
+ );
+ }
+
+ $entropy_bits = $this->calculate_entropy_bits( $password );
+
+ // If we have failed the entropy bits test, run the regex tests so we can suggest improvements.
+ if ( $entropy_bits < $this->minimum_entropy_bits ) {
+ $results['failed']['entropy_bits'] = $entropy_bits;
+ // Run the tests.
+ $results = array_merge( $results, $this->run_tests( $password, $this->get_tests( 'preg_match' ) ) );
+ }
+
+ return ( array(
+ 'passed' => empty( $results['failed'] ),
+ 'test_results' => $results,
+ ) );
+ }
+
+ /**
+ * Run the tests using the currently set up object values.
+ *
+ * @param string $password the password.
+ * @param array $tests tests to run.
+ * @param bool $required_only whether to run only required tests.
+ *
+ * @return array test results.
+ */
+ public function run_tests( $password, $tests, $required_only = false ) {
+ $results = array(
+ 'passed' => array(),
+ 'failed' => array(),
+ );
+
+ foreach ( $tests as $test_type => $section_tests ) {
+ foreach ( $section_tests as $test_name => $test_data ) {
+ // Skip non-required tests if required_only param is set.
+ if ( $required_only && ! $test_data['required'] ) {
+ continue;
+ }
+
+ $result = call_user_func_array( array( $this, 'test_' . $test_type ), array( $password, $test_data ) );
+ if ( $result ) {
+ $results['passed'][] = array( 'test_name' => $test_name );
+ } else {
+ $results['failed'][] = array(
+ 'test_name' => $test_name,
+ 'explanation' => $test_data['error'],
+ );
+
+ if ( isset( $test_data['fail_immediately'] ) ) {
+ return $results;
+ }
+ }
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Returns an array of tests that need to be run on password strings.
+ *
+ * @param array $sections only return specific sections with the passed keys, defaults to all.
+ *
+ * @return array test descriptions.
+ */
+ public function get_tests( $sections = false ) {
+ // Note: these should be in order of priority.
+ $tests = array(
+ 'preg_match' => array(
+ 'no_backslashes' => array(
+ 'pattern' => '/^[^\\\\]*$/u',
+ 'error' => __( 'Passwords may not contain the character "\".', 'jetpack-password-checker' ),
+ 'required' => true,
+ 'fail_immediately' => true,
+ ),
+ 'minimum_length' => array(
+ 'pattern' => '/^.{' . $this->minimum_password_length . ',}/u',
+ /* translators: %d is a number of characters in the password. */
+ 'error' => sprintf( __( 'Password must be at least %d characters.', 'jetpack-password-checker' ), $this->minimum_password_length ),
+ 'required' => true,
+ 'fail_immediately' => true,
+ ),
+ 'has_mixed_case' => array(
+ 'pattern' => '/([a-z].*?[A-Z]|[A-Z].*?[a-z])/u',
+ 'error' => __( 'This password is too easy to guess: you can improve it by adding additional uppercase letters, lowercase letters, or numbers.', 'jetpack-password-checker' ),
+ 'required' => false,
+ ),
+ 'has_digit' => array(
+ 'pattern' => '/\d/u',
+ 'error' => __( 'This password is too easy to guess: you can improve it by mixing both letters and numbers.', 'jetpack-password-checker' ),
+ 'required' => false,
+ ),
+ 'has_special_char' => array(
+ 'pattern' => '/[^a-zA-Z\d]/u',
+ 'error' => __( 'This password is too easy to guess: you can improve it by including special characters such as !#=?*&.', 'jetpack-password-checker' ),
+ 'required' => false,
+ ),
+ ),
+ 'compare_to_list' => array(
+ 'not_a_common_password' => array(
+ 'list_callback' => 'get_common_passwords',
+ 'compare_callback' => 'negative_in_array',
+ 'error' => __( 'This is a very common password. Choose something that will be harder for others to guess.', 'jetpack-password-checker' ),
+ 'required' => true,
+ ),
+ 'not_same_as_other_user_data' => array(
+ 'list_callback' => 'get_other_user_data',
+ 'compare_callback' => 'test_not_same_as_other_user_data',
+ 'error' => __( 'Your password is too weak: Looks like you are including easy to guess information about yourself. Try something a little more unique.', 'jetpack-password-checker' ),
+ 'required' => true,
+ ),
+ ),
+ );
+
+ /**
+ * Filters the password strength enforcement settings.
+ *
+ * You can determine the tests run and their order based on whatever criteria you wish to specify.
+ *
+ * @param array $tests tests to run.
+ */
+ $tests = apply_filters( 'password_checker_tests', $tests );
+
+ if ( ! $sections ) {
+ return $tests;
+ }
+
+ $sections = (array) $sections;
+
+ return array_intersect_key( $tests, array_flip( $sections ) );
+ }
+
+ /**
+ * Provides the regular expression tester functionality.
+ *
+ * @param string $password the password.
+ * @param array $test_data the current test data.
+ *
+ * @return bool does the test pass?
+ */
+ protected function test_preg_match( $password, $test_data ) {
+ return preg_match( $test_data['pattern'], $password );
+ }
+
+ /**
+ * Provides the comparison tester functionality.
+ *
+ * @param string $password the password.
+ * @param array $test_data the current test data.
+ *
+ * @return bool does the test pass?
+ */
+ protected function test_compare_to_list( $password, $test_data ) {
+ if (
+ ! is_callable( array( $this, $test_data['list_callback'] ) )
+ || ! is_callable( array( $this, $test_data['compare_callback'] ) )
+ ) {
+ return false;
+ }
+
+ return call_user_func(
+ array(
+ $this,
+ $test_data['compare_callback'],
+ ),
+ $password,
+ call_user_func( array( $this, $test_data['list_callback'] ) )
+ );
+ }
+
+ /**
+ * Getter for the common password list.
+ *
+ * @return array common passwords.
+ */
+ protected function get_common_passwords() {
+ return $this->common_passwords;
+ }
+
+ /**
+ * Returns the widely known user data that can not be used in the password to avoid
+ * predictable strings.
+ *
+ * @return array user data.
+ */
+ protected function get_other_user_data() {
+ if ( empty( $this->user_id ) ) {
+ return array();
+ }
+
+ $user_data = get_userdata( $this->user_id );
+ if ( ! $user_data ) {
+ return array();
+ }
+
+ if ( isset( $user_data->ID ) ) {
+ $this->add_user_strings_to_test( get_user_meta( $user_data->ID, 'first_name', true ) );
+ $this->add_user_strings_to_test( get_user_meta( $user_data->ID, 'last_name', true ) );
+ $this->add_user_strings_to_test( get_user_meta( $user_data->ID, 'nickname', true ) );
+ }
+
+ if ( isset( $user_data->user_nicename ) ) {
+ $this->add_user_strings_to_test( $user_data->user_nicename );
+ }
+
+ if ( isset( $user_data->display_name ) ) {
+ $this->add_user_strings_to_test( $user_data->display_name );
+ }
+
+ if ( isset( $user_data->first_name ) ) {
+ $this->add_user_strings_to_test( $user_data->first_name );
+ }
+
+ if ( isset( $user_data->last_name ) ) {
+ $this->add_user_strings_to_test( $user_data->last_name );
+ }
+
+ if ( isset( $user_data->user_email ) ) {
+ $email_username = substr( $user_data->user_email, 0, strpos( $user_data->user_email, '@' ) );
+ $this->add_user_strings_to_test( $email_username, '.' );
+ $this->add_user_strings_to_test( $user_data->user_email );
+ }
+
+ return $this->get_user_strings_to_test();
+ }
+
+ /**
+ * Compare the password for matches with known user data.
+ *
+ * @param string $password the password.
+ * @param array $strings_to_test known user data.
+ *
+ * @return bool does the test pass?
+ */
+ protected function test_not_same_as_other_user_data( $password, $strings_to_test ) {
+ if ( empty( $strings_to_test ) ) {
+ return false;
+ }
+
+ $password_lowercase = strtolower( $password );
+
+ foreach ( $strings_to_test as $string ) {
+ $string = strtolower( $string );
+ $string_reversed = strrev( $string );
+
+ if ( $password_lowercase === $string || $password_lowercase === $string_reversed ) {
+ return false;
+ }
+
+ // Also check for the string or reversed string with any numbers just stuck to the end to catch things like bob123 as passwords.
+ if (
+ preg_match( '/^' . preg_quote( $string, '/' ) . '\d+$/', $password_lowercase )
+ || preg_match( '/^' . preg_quote( $string_reversed, '/' ) . '\d+$/', $password_lowercase )
+ ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * A shorthand for the not in array construct.
+ *
+ * @param mixed $needle the needle.
+ * @param array $haystack the haystack.
+ *
+ * @return bool is the needle not in the haystack?
+ */
+ protected function negative_in_array( $needle, $haystack ) {
+ return ! in_array( $needle, $haystack, true );
+ }
+
+ /**
+ * A helper function used to break a single string into its constituents so
+ * that both the full string and its constituents and any variants thereof
+ * can be tested against the password.
+ *
+ * @param string $string the string to be broken down.
+ * @param string $explode_delimiter delimiter.
+ *
+ * @return bool
+ */
+ protected function add_user_strings_to_test( $string, $explode_delimiter = ' ' ) {
+ // Don't check against empty strings.
+ if ( empty( $string ) ) {
+ return false;
+ }
+
+ $strings = explode( $explode_delimiter, $string );
+
+ // Remove any non alpha numeric characters from the strings to check against.
+ foreach ( $strings as $key => $_string ) {
+ $_string = trim( preg_replace( '/[^a-zA-Z0-9]/', '', $_string ) );
+ if ( empty( $_string ) ) {
+ continue;
+ }
+
+ $strings[ $key ] = $_string;
+ }
+
+ // Check the original too.
+ $strings[] = trim( $string );
+
+ // Check the original minus non alpha numeric characters.
+ $strings[] = trim( preg_replace( '/[^a-zA-Z0-9]/', '', $string ) );
+
+ // Remove any empty strings.
+ // Note: This will also filter out '0'.
+ $strings = array_filter( $strings );
+ if ( empty( $strings ) ) {
+ return false;
+ }
+
+ $this->user_strings_to_test = array_unique( array_merge( $this->user_strings_to_test, $strings ) );
+
+ return true;
+ }
+
+ /**
+ * Getter for the user strings array.
+ *
+ * @return array user strings.
+ */
+ protected function get_user_strings_to_test() {
+ return $this->user_strings_to_test;
+ }
+
+ /**
+ * Return a character set size that is used in the string.
+ *
+ * @param string $password the password.
+ *
+ * @return int number of different character sets in use.
+ */
+ protected function get_charset_size( $password ) {
+ $size = 0;
+
+ // Lowercase a-z.
+ if ( preg_match( '/[a-z]/', $password ) ) {
+ $size += 26;
+ }
+
+ // Uppercase A-Z.
+ if ( preg_match( '/[A-Z]/', substr( $password, 1, - 1 ) ) ) {
+ $size += 26;
+ }
+
+ // Digits.
+ if ( preg_match( '/\d/', substr( $password, 1, - 1 ) ) ) {
+ $size += 10;
+ }
+
+ // Over digits symbols.
+ if ( preg_match( '/[!|@|#|$|%|^|&|*|(|)]/', $password ) ) {
+ $size += 10;
+ }
+
+ // Other symbols.
+ if ( preg_match( '#[`|~|-|_|=|+|\[|{|\]|}|\\|\|;:\'",<\.>/\?]#', $password ) ) {
+ $size += 20;
+ }
+
+ // Spaces.
+ if ( strpos( $password, ' ' ) ) {
+ $size ++;
+ }
+
+ return $size;
+ }
+
+ /**
+ * Shorthand for getting a character index.
+ *
+ * @param string $char character.
+ *
+ * @return int the character code.
+ */
+ protected function get_char_index( $char ) {
+ $char = strtolower( $char[0] );
+ if ( $char < 'a' || $char > 'z' ) {
+ return 0;
+ } else {
+ return ord( $char[0] ) - ord( 'a' ) + 1;
+ }
+ }
+
+ /**
+ * This is the password strength calculation algorithm, based on the formula H = L(logN/log2).
+ *
+ * H = Entropy
+ * L = String length (the for iterator)
+ * N = Our charset size, via get_charset_size()
+ *
+ * @see https://en.wikipedia.org/wiki/Password_strength#Random_passwords
+ *
+ * On top of the base formula, we're also multiplying the bits of entropy for every char
+ * by 1 - (the probabily of it following the previous char)
+ * i.e.: the probablity of U following Q is ~0.84. If our password contains this pair of characters,
+ * the u char will only add ( 0.16^2 * charset_score ) to our total of entropy bits.
+ *
+ * @param string $password the password.
+ *
+ * @return float|int
+ */
+ protected function calculate_entropy_bits( $password ) {
+ $bits = 0;
+ // Calculate the score.
+ $charset_score = log( $this->get_charset_size( $password ) ) / log( 2 );
+
+ $aidx = $this->get_char_index( $password[0] );
+ $length = strlen( $password );
+
+ for ( $b = 1; $b < $length; $b ++ ) {
+ $bidx = $this->get_char_index( $password[ $b ] );
+
+ // 27 = number of chars in the index (a-z,' ').
+ $c = 1.0 - $this->frequency_table[ $aidx * 27 + $bidx ];
+
+ // Increment the bits.
+ $bits += $charset_score * $c * $c;
+
+ // Move on to next pair.
+ $aidx = $bidx;
+ }
+
+ return $bits;
+ }
+
+ /**
+ * A frequency table of character pairs, starting with ' ' then ' a', ' b' [...] , 'a ', 'aa' etc.
+ *
+ * @see http://rumkin.com/tools/password/passchk.php
+ *
+ * @var array
+ */
+ public $frequency_table = array(
+ 0.23653710453418866,
+ 0.04577693541332556,
+ 0.03449832337075375,
+ 0.042918209651552706,
+ 0.037390873305146524,
+ 0.028509112115468728,
+ 0.02350896632162123,
+ 0.022188657238664526,
+ 0.028429800262428927,
+ 0.04357019973757107,
+ 0.00913602565971716,
+ 0.03223093745443942,
+ 0.02235311269864412,
+ 0.04438081352966905,
+ 0.04512377897652719,
+ 0.020055401662049863,
+ 0.055903192885260244,
+ 0.0024388394809739026,
+ 0.035207464644991984,
+ 0.07355941099285611,
+ 0.036905671380667734,
+ 0.026134421927394666,
+ 0.023787724158040528,
+ 0.011352092141711621,
+ 0.0032354570637119114,
+ 0.005986878553725033,
+ 0.008861933226417843,
+ 0.11511532293337222,
+ 0.027556203528211108,
+ 0.024331243621519172,
+ 0.039266365359381834,
+ 0.031599941682461,
+ 0.014403265782183991,
+ 0.015480973902901297,
+ 0.027770812071730572,
+ 0.00942761335471643,
+ 0.039872867764980315,
+ 0.0078122175244204695,
+ 0.02808456043154979,
+ 0.08429100451960927,
+ 0.04688963405744277,
+ 0.13831170724595424,
+ 0.002540311998833649,
+ 0.025211838460416972,
+ 0.001543082081936142,
+ 0.09519638431258201,
+ 0.061845750109345385,
+ 0.08907071001603732,
+ 0.02137571074500656,
+ 0.027093162268552268,
+ 0.005521504592506197,
+ 0.003023181221752442,
+ 0.007086747339262283,
+ 0.010262720513194342,
+ 0.08785070710016038,
+ 0.14617757690625455,
+ 0.03417291150313457,
+ 0.0059635515381250915,
+ 0.006146668610584633,
+ 0.195202799241872,
+ 0.002774748505613063,
+ 0.004715556203528212,
+ 0.0044776206444088066,
+ 0.11205481848665985,
+ 0.005654468581425864,
+ 0.0028820527773727946,
+ 0.07383000437381543,
+ 0.005516839189386207,
+ 0.006496573844583759,
+ 0.09843067502551392,
+ 0.0027140982650532145,
+ 0.0006893133109782768,
+ 0.08425368129464937,
+ 0.021325557661466685,
+ 0.006493074792243767,
+ 0.07023414491908442,
+ 0.002077270739174807,
+ 0.0024633328473538415,
+ 0.0007744569179180639,
+ 0.015413325557661468,
+ 0.0011990086018370024,
+ 0.13162851727657093,
+ 0.10115993585070711,
+ 0.0026989357049132527,
+ 0.03319317684793702,
+ 0.002946202070272634,
+ 0.0783216212275842,
+ 0.0018358361277154103,
+ 0.00258813238081353,
+ 0.2141688292754046,
+ 0.09853681294649366,
+ 0.0032482869222918796,
+ 0.04359352675317102,
+ 0.01993526753171016,
+ 0.0036880011663507797,
+ 0.008011663507799971,
+ 0.12014696019827964,
+ 0.0029846916460125384,
+ 0.0017553579238956116,
+ 0.029470185158186325,
+ 0.010413179763813967,
+ 0.030699518880303252,
+ 0.03508499781309229,
+ 0.002021285901734947,
+ 0.0010613792097973467,
+ 0.0005295232541186761,
+ 0.009677212421635807,
+ 0.010585799679253535,
+ 0.17101734946785244,
+ 0.07968625164018078,
+ 0.007839043592360402,
+ 0.005438693687126403,
+ 0.0183606939787141,
+ 0.2732701559994168,
+ 0.004953491762647616,
+ 0.007259367254701851,
+ 0.008104971570199739,
+ 0.13274588132380813,
+ 0.004210526315789474,
+ 0.004997813092287506,
+ 0.017006560723137484,
+ 0.007442484327161393,
+ 0.016789619478058026,
+ 0.08477737279486806,
+ 0.005106283714827234,
+ 0.0005026971861787433,
+ 0.04040355736987899,
+ 0.037535500801866156,
+ 0.00885960052485785,
+ 0.0336410555474559,
+ 0.007066919376002332,
+ 0.005344219273946639,
+ 0.0006333284735384167,
+ 0.010684939495553289,
+ 0.0063064586674442345,
+ 0.15386849394955532,
+ 0.015049424114302375,
+ 0.012162705933809595,
+ 0.020425134859308938,
+ 0.037366379938766583,
+ 0.02157165767604607,
+ 0.009373961218836564,
+ 0.0173214754337367,
+ 0.009616562181075958,
+ 0.029522670943286193,
+ 0.010154249890654615,
+ 0.018600962239393497,
+ 0.06362210234728094,
+ 0.03157078291296107,
+ 0.151603440734801,
+ 0.0062329785683044175,
+ 0.014775331681003062,
+ 0.0020854351946347867,
+ 0.1826342032366234,
+ 0.0878017203674005,
+ 0.054190989940224525,
+ 0.010329202507654177,
+ 0.012763376585508092,
+ 0.0064872430383437815,
+ 0.006381105117364048,
+ 0.005388540603586529,
+ 0.0090800408222773,
+ 0.09611196967487973,
+ 0.09940691062837148,
+ 0.01033969966467415,
+ 0.004034407348009914,
+ 0.008826942703017933,
+ 0.11474675608689314,
+ 0.07132584924916169,
+ 0.012388977985129028,
+ 0.005435194634786413,
+ 0.1417174515235457,
+ 0.0037066627788307337,
+ 0.0045802595130485495,
+ 0.060800699810468,
+ 0.005341886572386646,
+ 0.005683627350925791,
+ 0.12434932205860913,
+ 0.004596588423968508,
+ 0.0007534626038781163,
+ 0.07107041842834232,
+ 0.022361277154104096,
+ 0.04784720804782038,
+ 0.06277533168100306,
+ 0.003441901151771395,
+ 0.005828254847645429,
+ 0.0009669047966175828,
+ 0.009470768333576322,
+ 0.002077270739174807,
+ 0.12797667298440007,
+ 0.08797783933518005,
+ 0.005388540603586529,
+ 0.0024913252660737715,
+ 0.007550954949701123,
+ 0.2786866890217233,
+ 0.002509986878553725,
+ 0.029002478495407494,
+ 0.0303204548768042,
+ 0.07576614666861058,
+ 0.00246799825047383,
+ 0.00592389561160519,
+ 0.039574281965301064,
+ 0.00706808572678233,
+ 0.03304505029887739,
+ 0.05474150750838315,
+ 0.0028633911648928414,
+ 0.0005073625892987316,
+ 0.07293541332555767,
+ 0.053528502697186175,
+ 0.022566554891383584,
+ 0.038151334013704616,
+ 0.002716430966613209,
+ 0.005049132526607377,
+ 0.0009902318122175246,
+ 0.008997229916897508,
+ 0.0011861787432570347,
+ 0.1666377022889634,
+ 0.14414462749671964,
+ 0.003374252806531564,
+ 0.005169266656947077,
+ 0.008468873013558828,
+ 0.16337541915731155,
+ 0.002873888321912815,
+ 0.004305000728969237,
+ 0.0031141565825922144,
+ 0.1241172182533897,
+ 0.0052800699810468,
+ 0.008969237498177577,
+ 0.024094474413179766,
+ 0.017029887738737422,
+ 0.01722700102055693,
+ 0.10618457501093455,
+ 0.006147834961364631,
+ 0.0008269427030179326,
+ 0.03303571949263741,
+ 0.024188948826359528,
+ 0.05213937891820965,
+ 0.04505846333284735,
+ 0.0035270447587111824,
+ 0.006799825047383001,
+ 0.0008199445983379502,
+ 0.02206735675754483,
+ 0.001010059775477475,
+ 0.11971191135734072,
+ 0.04656538854060359,
+ 0.011243621519171892,
+ 0.06513019390581717,
+ 0.032375564951159064,
+ 0.06347047674588133,
+ 0.013678961947805804,
+ 0.03309870243475726,
+ 0.006982942119842543,
+ 0.009726199154395685,
+ 0.010121592068814697,
+ 0.032514360693978714,
+ 0.04986032949409535,
+ 0.039734072022160664,
+ 0.15690683773144773,
+ 0.03949963551538125,
+ 0.014790494241143023,
+ 0.002722262720513194,
+ 0.02614375273363464,
+ 0.10753637556495116,
+ 0.06764834523983088,
+ 0.006221315060504448,
+ 0.021317393206006705,
+ 0.0030826651115322934,
+ 0.002399183554454002,
+ 0.0019069835252952323,
+ 0.015595276279341012,
+ 0.0925126111678087,
+ 0.18437906400349907,
+ 0.006538562472663654,
+ 0.008719638431258201,
+ 0.02116693395538708,
+ 0.18241376293920394,
+ 0.007290858725761773,
+ 0.005976381396705059,
+ 0.005629975215045925,
+ 0.09721300481119698,
+ 0.004810030616707975,
+ 0.024303251202799244,
+ 0.012954658113427612,
+ 0.011057005394372358,
+ 0.02733459688001166,
+ 0.10135121737862662,
+ 0.012016912086309959,
+ 0.001055547455897361,
+ 0.009027555037177431,
+ 0.07162326869806095,
+ 0.01007143898527482,
+ 0.07297623560285756,
+ 0.006741507508383147,
+ 0.0036891675171307776,
+ 0.0008409389123778977,
+ 0.011272780288671819,
+ 0.007020265344802449,
+ 0.1030389269572824,
+ 0.15350809155853623,
+ 0.004232686980609419,
+ 0.004353987461729115,
+ 0.0023385333138941536,
+ 0.14450386353695874,
+ 0.002546143752733635,
+ 0.0024470039364338824,
+ 0.01200758128006998,
+ 0.0981227584195947,
+ 0.003161976964572095,
+ 0.040695145064878264,
+ 0.03460446129173349,
+ 0.003908441463770229,
+ 0.01598483743986004,
+ 0.13107216795451232,
+ 0.003129319142732177,
+ 0.00032307916605919226,
+ 0.04050386353695874,
+ 0.05452689896486368,
+ 0.03589677795597026,
+ 0.07087097244496282,
+ 0.006143169558244642,
+ 0.008684647907858289,
+ 0.0004607085580988482,
+ 0.022010205569324977,
+ 0.0009097536083977258,
+ 0.07328765126111678,
+ 0.14751421490013122,
+ 0.008015162560139961,
+ 0.006601545414783497,
+ 0.025279486805656802,
+ 0.1682449336637994,
+ 0.008313748359819215,
+ 0.007010934538562473,
+ 0.005886572386645284,
+ 0.16889575739903775,
+ 0.004123050007289692,
+ 0.011925936725470185,
+ 0.10007289692374982,
+ 0.013380376148126549,
+ 0.009021723283277445,
+ 0.08650823735238372,
+ 0.007756232686980609,
+ 0.0007243038343781893,
+ 0.0026791077416533026,
+ 0.02797492345823006,
+ 0.032384895757399036,
+ 0.04187432570345531,
+ 0.00882461000145794,
+ 0.0032401224668318998,
+ 0.00033357632307916605,
+ 0.027878116343490307,
+ 0.0022277299897944304,
+ 0.14333518005540166,
+ 0.1725534334451086,
+ 0.02781629975215046,
+ 0.006909462020702727,
+ 0.005264907420906838,
+ 0.16661437527336345,
+ 0.004325995043009185,
+ 0.003334596880011664,
+ 0.005312727802886718,
+ 0.14024668318996938,
+ 0.0013261408368566844,
+ 0.003504884093891238,
+ 0.006375273363464061,
+ 0.04964922000291588,
+ 0.008290421344219274,
+ 0.09536783787724158,
+ 0.05394372357486515,
+ 0.005505175681586237,
+ 0.005339553870826651,
+ 0.01782067356757545,
+ 0.006710016037323225,
+ 0.05105933809593235,
+ 0.002983525295232541,
+ 0.002940370316372649,
+ 0.0004548768041988629,
+ 0.01208456043154979,
+ 0.000915585362297711,
+ 0.20146260387811635,
+ 0.067196967487972,
+ 0.006158332118384605,
+ 0.025438110511736407,
+ 0.07753783350342616,
+ 0.1273876658405015,
+ 0.009337804344656656,
+ 0.07683452398308792,
+ 0.0070412596588423975,
+ 0.08747164309666132,
+ 0.0038827817466102928,
+ 0.018116926665694706,
+ 0.005017641055547455,
+ 0.004567429654468581,
+ 0.028277008310249308,
+ 0.05271555620352821,
+ 0.004394809739029013,
+ 0.0013343052923166642,
+ 0.00411605190260971,
+ 0.059621519171890944,
+ 0.09073859163143316,
+ 0.01446858142586383,
+ 0.006770666277883074,
+ 0.003425572240851436,
+ 0.0004455459979588861,
+ 0.010401516256013998,
+ 0.005825922146085436,
+ 0.10833882490158916,
+ 0.007584779122321038,
+ 0.016903921854497742,
+ 0.02719580113719201,
+ 0.0304814112844438,
+ 0.02206385770520484,
+ 0.013064295086747339,
+ 0.02696369733197259,
+ 0.009581571657676046,
+ 0.026761918647033093,
+ 0.006510570053943724,
+ 0.021941390873305145,
+ 0.07042659279778393,
+ 0.05437410701268406,
+ 0.1425175681586237,
+ 0.027802303542790494,
+ 0.037690625455605774,
+ 0.0019606356611750987,
+ 0.1095623268698061,
+ 0.06157748942994606,
+ 0.044618749088788455,
+ 0.04955124653739612,
+ 0.03608689313310978,
+ 0.018381688292754043,
+ 0.003404577926811489,
+ 0.015036594255722409,
+ 0.009600233270156,
+ 0.10794693103951014,
+ 0.12447528794284882,
+ 0.0031981338387520046,
+ 0.0074716430966613205,
+ 0.003202799241871993,
+ 0.13437643971424407,
+ 0.006655197550663361,
+ 0.0036693395538708266,
+ 0.049338970695436656,
+ 0.09486863974340283,
+ 0.0015990669193760023,
+ 0.0026604461291733486,
+ 0.051775477474850555,
+ 0.0041347135150896636,
+ 0.005450357194926374,
+ 0.12030325120279925,
+ 0.04581309228750547,
+ 0.0004537104534188657,
+ 0.12425601399620935,
+ 0.025981629975215047,
+ 0.023926519900860182,
+ 0.04423385333138941,
+ 0.0017950138504155123,
+ 0.002661612479953346,
+ 0.0006333284735384167,
+ 0.008449045050298877,
+ 0.000653156436798367,
+ 0.04816678816153958,
+ 0.008625164018078437,
+ 0.0039037760606502403,
+ 0.005228750546726928,
+ 0.004531272780288672,
+ 0.0056672984400058316,
+ 0.00359585945473101,
+ 0.0032179618020119548,
+ 0.0038093016474704767,
+ 0.011452398308791368,
+ 0.002519317684793702,
+ 0.00280390727511299,
+ 0.005572824026826068,
+ 0.004554599795888614,
+ 0.004531272780288672,
+ 0.0035841959469310393,
+ 0.004400641492928998,
+ 0.0036670068523108326,
+ 0.004839189386207902,
+ 0.006258638285464354,
+ 0.004897506925207757,
+ 0.840776789619478,
+ 0.004968654322787578,
+ 0.002886718180492783,
+ 0.0019757982213150604,
+ 0.0018568304417553576,
+ 0.001691208630995772,
+ 0.09009243329931477,
+ 0.14030150167662925,
+ 0.013242746756086894,
+ 0.013746610293045632,
+ 0.027342761335471644,
+ 0.16938912377897652,
+ 0.006607377168683481,
+ 0.01661933226417845,
+ 0.008173786266219566,
+ 0.13297448607668758,
+ 0.0034675608689313307,
+ 0.016641492928998396,
+ 0.011722991689750693,
+ 0.021493512173786266,
+ 0.03430820819361423,
+ 0.10099548039072752,
+ 0.00873596734217816,
+ 0.0018323370753754193,
+ 0.020103222044029742,
+ 0.047197550663362,
+ 0.040833940807697915,
+ 0.03361189677795597,
+ 0.010844729552412887,
+ 0.005544831608106138,
+ 0.0007522962530981193,
+ 0.01525120279924187,
+ 0.00815512465373961,
+ 0.2109648636827526,
+ 0.058258055110074355,
+ 0.007181221752442048,
+ 0.043560868931331105,
+ 0.004058900714389853,
+ 0.10618107595859454,
+ 0.0062399766729844,
+ 0.004835690333867911,
+ 0.02679224376731302,
+ 0.08414637702288964,
+ 0.0030698352529523252,
+ 0.03637498177576906,
+ 0.01592885260242018,
+ 0.017413617145356466,
+ 0.008430383437818923,
+ 0.037231083248286924,
+ 0.03290275550371775,
+ 0.007538125091121154,
+ 0.004500947660008748,
+ 0.05932409972299169,
+ 0.16006764834523984,
+ 0.03309636973319726,
+ 0.007766729844000583,
+ 0.005225251494386936,
+ 0.0006321621227584196,
+ 0.012989648636827526,
+ 0.005274238227146815,
+ 0.1254503571949264,
+ 0.12852719055255868,
+ 0.0035433736696311416,
+ 0.005203090829566993,
+ 0.0019314768916751715,
+ 0.20520775623268697,
+ 0.002509986878553725,
+ 0.00343606939787141,
+ 0.027138649948972155,
+ 0.13926578218399185,
+ 0.004565096952908587,
+ 0.005614812654905963,
+ 0.00874413179763814,
+ 0.004109053797929727,
+ 0.008300918501239247,
+ 0.08270943286193323,
+ 0.002912377897652719,
+ 0.0037066627788307337,
+ 0.06909578655780726,
+ 0.03242805073625893,
+ 0.05237614812654906,
+ 0.04723487388832191,
+ 0.0038991106575302524,
+ 0.006299460562764251,
+ 0.00043388249015891526,
+ 0.020029741944889927,
+ 0.005311561452106721,
+ 0.09334072022160665,
+ 0.022940953491762648,
+ 0.024658988190698353,
+ 0.02901297565242747,
+ 0.03531593526753171,
+ 0.0758023035427905,
+ 0.013711619769645722,
+ 0.021597317393206007,
+ 0.009670214316955824,
+ 0.044728386062108175,
+ 0.010596296836273509,
+ 0.03264382563055839,
+ 0.0604822860475288,
+ 0.05489546581134276,
+ 0.11501851581863246,
+ 0.01837585653885406,
+ 0.026237060796034405,
+ 0.0011255285026971862,
+ 0.08704125965884241,
+ 0.10156349322058608,
+ 0.06660562764251349,
+ 0.023434319871701415,
+ 0.010777081207173057,
+ 0.005409534917626476,
+ 0.003123487388832191,
+ 0.0028762210234728096,
+ 0.0089995626184575,
+ 0.07518297127861205,
+ 0.2314868056568013,
+ 0.002226563639014434,
+ 0.003285610147251786,
+ 0.0027455897361131363,
+ 0.2724537104534189,
+ 0.0016655489138358362,
+ 0.0019209797346551977,
+ 0.0022137337804344656,
+ 0.17690392185449774,
+ 0.0014532730718763668,
+ 0.0024994897215337513,
+ 0.015302522233561744,
+ 0.003441901151771395,
+ 0.015303688584341741,
+ 0.09314593964134713,
+ 0.0017833503426155418,
+ 0.0005108616416387229,
+ 0.017828838023035427,
+ 0.010385187345094037,
+ 0.003168975069252078,
+ 0.01902901297565243,
+ 0.005525003644846187,
+ 0.0010088934246974776,
+ 0.0009272488700976819,
+ 0.036282840064149294,
+ 0.0022977110365942554,
+ 0.0766805656801283,
+ 0.22270418428342326,
+ 0.005283569033386791,
+ 0.007155562035282111,
+ 0.01173582154833066,
+ 0.1715620352821111,
+ 0.003925936725470185,
+ 0.004425134859308937,
+ 0.020040239101909902,
+ 0.14243242455168392,
+ 0.0016737133692958156,
+ 0.0066808572678232975,
+ 0.011980755212130047,
+ 0.012638577052048404,
+ 0.07206065024055984,
+ 0.08115701997375711,
+ 0.00710424260096224,
+ 0.0007278028867181805,
+ 0.02347630849978131,
+ 0.04595538708266512,
+ 0.01481965301064295,
+ 0.013925061962385188,
+ 0.0018125091121154687,
+ 0.00529173348884677,
+ 0.0016340574427759146,
+ 0.03072401224668319,
+ 0.0023746901880740633,
+ 0.25174165330223064,
+ 0.06673392622831317,
+ 0.00878378772415804,
+ 0.03956261845750109,
+ 0.010077270739174807,
+ 0.0844787869951888,
+ 0.00985216503863537,
+ 0.004973319725907567,
+ 0.01893220586091267,
+ 0.11200583175389998,
+ 0.0028715556203528212,
+ 0.004095057588569762,
+ 0.01202391019098994,
+ 0.01756757544831608,
+ 0.014825484764542934,
+ 0.05312961073042717,
+ 0.06746872721971132,
+ 0.003845458521650386,
+ 0.0210806239976673,
+ 0.019443067502551394,
+ 0.08017028721387957,
+ 0.01825572240851436,
+ 0.005365213587986587,
+ 0.01959702580551101,
+ 0.026184575010934536,
+ 0.02474879720075813,
+ 0.002171745152354571,
+ 0.25827321767021433,
+ 0.048050153083539875,
+ 0.01043184137629392,
+ 0.03930485493512174,
+ 0.027640180784370902,
+ 0.03294007872867765,
+ 0.006474413179763814,
+ 0.018314039947514214,
+ 0.015119405161102202,
+ 0.014706516984983233,
+ 0.005494678524566263,
+ 0.03309870243475726,
+ 0.043864120134130345,
+ 0.058996355153812505,
+ 0.06265986295378335,
+ 0.04633328473538417,
+ 0.03790756670068523,
+ 0.0004642076104388394,
+ 0.037849249161685375,
+ 0.08369966467415076,
+ 0.04999679253535501,
+ 0.02392768625164018,
+ 0.010998687855372504,
+ 0.009881323808135296,
+ 0.003867619186470331,
+ 0.012434465665548913,
+ 0.007253535500801866,
+ 0.11106225397288234,
+ 0.17624726636535937,
+ 0.008209943140399476,
+ 0.008390727511299025,
+ 0.012682898381688294,
+ 0.1825653885406036,
+ 0.001538416678816154,
+ 0.004590756670068524,
+ 0.008710307625018223,
+ 0.1299513048549351,
+ 0.002677941390873305,
+ 0.012309666132089225,
+ 0.014087184720804781,
+ 0.01199941682461,
+ 0.031246537396121883,
+ 0.07206648199445984,
+ 0.008254264470039366,
+ 0.0007033095203382417,
+ 0.007034261554162415,
+ 0.006599212713223502,
+ 0.013906400349905234,
+ 0.050098265053214755,
+ 0.007133401370462167,
+ 0.017750692520775622,
+ 0.0008257763522379356,
+ 0.03918821985712203,
+ 0.06015454147834961,
+ );
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/CHANGELOG.md
new file mode 100644
index 00000000..5af72f2d
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/CHANGELOG.md
@@ -0,0 +1,143 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.7.9] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+
+## [1.7.8] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.7.7] - 2021-11-22
+### Changed
+- Updated package dependencies
+
+## [1.7.6] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.7.5] - 2021-10-26
+### Changed
+- Updated package dependencies.
+
+## [1.7.4] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.7.3] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.7.2] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.7.1] - 2021-08-30
+### Changed
+- Run composer update on test-php command instead of phpunit
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+
+## [1.7.0] - 2021-06-29
+### Changed
+- Allow any argument to be passed.
+- Improve documentation.
+
+## [1.6.1] - 2021-06-15
+### Changed
+- Updated package dependencies.
+
+## [1.6.0] - 2021-05-25
+### Removed
+- Removed filter from the final Redirect URL
+
+## [1.5.5] - 2021-04-27
+### Changed
+- Updated package dependencies.
+
+## [1.5.4] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+- Userless Connection: Redirect "userless" users to the "Plans" page
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.5.3] - 2021-02-23
+
+- CI: Make tests more generic
+
+## [1.5.2] - 2021-01-26
+
+- Update dependencies to latest stable
+
+## [1.5.1] - 2021-01-26
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.5.0] - 2021-01-05
+
+- Update dependency brain/monkey to v2.6.0
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+- Pin dependency brain/monkey to 2.5.0
+
+## [1.4.1] - 2020-11-24
+
+- Status: Introduce get_site_suffix method
+
+## [1.4.0] - 2020-10-27
+
+- Masterbar: Add Admin Menu endpoint
+
+## [1.3.0] - 2020-08-14
+
+- Packages: Update filenames after #16810
+- CI: Try collect js coverage
+- Docker: Add package testing shortcut
+
+## [1.2.0] - 2020-06-16
+
+- Add a trailing / to jetpack.com/redirect URLs.
+
+## [1.1.0] - 2020-05-22
+
+- add filter to Redirect::get_url
+
+## 1.0.0 - 2020-04-24
+
+- Create Jetpack Redirect package
+
+[1.7.9]: https://github.com/Automattic/jetpack-redirect/compare/v1.7.8...v1.7.9
+[1.7.8]: https://github.com/Automattic/jetpack-redirect/compare/v1.7.7...v1.7.8
+[1.7.7]: https://github.com/Automattic/jetpack-redirect/compare/v1.7.6...v1.7.7
+[1.7.6]: https://github.com/Automattic/jetpack-redirect/compare/v1.7.5...v1.7.6
+[1.7.5]: https://github.com/Automattic/jetpack-redirect/compare/v1.7.4...v1.7.5
+[1.7.4]: https://github.com/Automattic/jetpack-redirect/compare/v1.7.3...v1.7.4
+[1.7.3]: https://github.com/Automattic/jetpack-redirect/compare/v1.7.2...v1.7.3
+[1.7.2]: https://github.com/Automattic/jetpack-redirect/compare/v1.7.1...v1.7.2
+[1.7.1]: https://github.com/Automattic/jetpack-redirect/compare/v1.7.0...v1.7.1
+[1.7.0]: https://github.com/Automattic/jetpack-redirect/compare/v1.6.1...v1.7.0
+[1.6.1]: https://github.com/Automattic/jetpack-redirect/compare/v1.6.0...v1.6.1
+[1.6.0]: https://github.com/Automattic/jetpack-redirect/compare/v1.5.5...v1.6.0
+[1.5.5]: https://github.com/Automattic/jetpack-redirect/compare/v1.5.4...v1.5.5
+[1.5.4]: https://github.com/Automattic/jetpack-redirect/compare/v1.5.3...v1.5.4
+[1.5.3]: https://github.com/Automattic/jetpack-redirect/compare/v1.5.2...v1.5.3
+[1.5.2]: https://github.com/Automattic/jetpack-redirect/compare/v1.5.1...v1.5.2
+[1.5.1]: https://github.com/Automattic/jetpack-redirect/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/Automattic/jetpack-redirect/compare/v1.4.1...v1.5.0
+[1.4.1]: https://github.com/Automattic/jetpack-redirect/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/Automattic/jetpack-redirect/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/Automattic/jetpack-redirect/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-redirect/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/Automattic/jetpack-redirect/compare/v1.0.0...v1.1.0
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/src/class-redirect.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/src/class-redirect.php
new file mode 100644
index 00000000..da5979b3
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-redirect/src/class-redirect.php
@@ -0,0 +1,79 @@
+<?php
+/**
+ * Jetpack Redirect package.
+ *
+ * @package automattic/jetpack-redirect
+ */
+
+namespace Automattic\Jetpack;
+
+/**
+ * Class Redirect
+ */
+class Redirect {
+ /**
+ * Constructor.
+ *
+ * Static-only class, so nothing here.
+ */
+ private function __construct() {}
+
+ /**
+ * Builds and returns an URL using the jetpack.com/redirect/ service
+ *
+ * If $source is a simple slug, it will be sent using the source query parameter. e.g. jetpack.com/redirect/?source=slug
+ *
+ * If $source is a full URL, starting with https://, it will be sent using the url query parameter. e.g. jetpack.com/redirect/?url=https://wordpress.com
+ *
+ * Note: if using full URL, query parameters and anchor must be passed in $args. Any querystring of url fragment in the URL will be discarded.
+ *
+ * @param string $source The URL handler registered in the server or the full destination URL (starting with https://).
+ * @param array|string $args {
+ * Optional. Additional arguments to build the url. This is not a complete list as any argument passed here will be sent to as a query parameter to the Redirect server. These parameters will not necessarily be passed over to the final destination URL. If you want to add a parameter to the final destination URL, use the `query` argument.
+ *
+ * @type string $site URL of the site; Default is current site.
+ * @type string $path Additional path to be appended to the URL.
+ * @type string $query Query parameters to be added to the final destination URL. should be in query string format (e.g. 'key=value&foo=bar').
+ * @type string $anchor Anchor to be added to the URL.
+ * @type integer $u The user ID.
+ * }
+ *
+ * @return string The built URL
+ */
+ public static function get_url( $source, $args = array() ) {
+
+ $url = 'https://jetpack.com/redirect/';
+ $site_suffix = ( new Status() )->get_site_suffix();
+ $args = wp_parse_args( $args, array( 'site' => $site_suffix ) );
+
+ $source_key = 'source';
+
+ if ( 0 === strpos( $source, 'https://' ) ) {
+ $source_key = 'url';
+ $source_url = \wp_parse_url( $source );
+
+ // discard any query and fragments.
+ $source = 'https://' . $source_url['host'] . ( isset( $source_url['path'] ) ? $source_url['path'] : '' );
+ }
+
+ $to_be_added = array(
+ $source_key => rawurlencode( $source ),
+ );
+
+ foreach ( $args as $arg_name => $arg_value ) {
+
+ if ( empty( $arg_value ) ) {
+ continue;
+ }
+
+ $to_be_added[ $arg_name ] = rawurlencode( $arg_value );
+
+ }
+
+ if ( ! empty( $to_be_added ) ) {
+ $url = add_query_arg( $to_be_added, $url );
+ }
+
+ return $url;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/CHANGELOG.md
new file mode 100644
index 00000000..86d4e90e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/CHANGELOG.md
@@ -0,0 +1,130 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.4.13] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+
+## [1.4.12] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.4.11] - 2021-11-22
+### Changed
+- Updated package dependencies
+
+## [1.4.10] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.4.9] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.4.8] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.4.7] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.4.6] - 2021-08-30
+### Changed
+- Run composer update on test-php command instead of phpunit
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+
+## [1.4.5] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.4.4] - 2021-04-08
+### Changed
+- Packaging and build changes, no change to the package itself.
+
+## [1.4.3] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.4.2] - 2021-02-05
+
+- CI: Make tests more generic
+
+## [1.4.1] - 2021-01-20
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.4.0] - 2020-12-14
+
+- Update dependency brain/monkey to v2.6.0
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.3.1] - 2020-10-28
+
+- Updated PHPCS: Packages and Debugger
+
+## [1.3.0] - 2020-08-13
+
+- CI: Try collect js coverage
+- Docker: Add package testing shortcut
+
+## [1.2.0] - 2020-07-01
+
+- Package Unit tests: update test file names to make sure they runs in Travis
+
+## [1.1.0] - 2020-06-22
+
+- PHPCS: Clean up the packages
+- PHPCS Updates after WPCS 2.3
+
+## [1.0.4] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## [1.0.2] - 2019-10-28
+
+- Packages: Add gitattributes files to all packages that need th…
+
+## [1.0.1] - 2019-09-20
+
+- Docs: Unify usage of @package phpdoc tags
+
+## 1.0.0 - 2019-09-14
+
+- Jetpack DNA: Introduce a Roles package
+
+[1.4.13]: https://github.com/Automattic/jetpack-roles/compare/v1.4.12...v1.4.13
+[1.4.12]: https://github.com/Automattic/jetpack-roles/compare/v1.4.11...v1.4.12
+[1.4.11]: https://github.com/Automattic/jetpack-roles/compare/v1.4.10...v1.4.11
+[1.4.10]: https://github.com/Automattic/jetpack-roles/compare/v1.4.9...v1.4.10
+[1.4.9]: https://github.com/Automattic/jetpack-roles/compare/v1.4.8...v1.4.9
+[1.4.8]: https://github.com/Automattic/jetpack-roles/compare/v1.4.7...v1.4.8
+[1.4.7]: https://github.com/Automattic/jetpack-roles/compare/v1.4.6...v1.4.7
+[1.4.6]: https://github.com/Automattic/jetpack-roles/compare/v1.4.5...v1.4.6
+[1.4.5]: https://github.com/Automattic/jetpack-roles/compare/v1.4.4...v1.4.5
+[1.4.4]: https://github.com/Automattic/jetpack-roles/compare/v1.4.3...v1.4.4
+[1.4.3]: https://github.com/Automattic/jetpack-roles/compare/v1.4.2...v1.4.3
+[1.4.2]: https://github.com/Automattic/jetpack-roles/compare/v1.4.1...v1.4.2
+[1.4.1]: https://github.com/Automattic/jetpack-roles/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/Automattic/jetpack-roles/compare/v1.3.1...v1.4.0
+[1.3.1]: https://github.com/Automattic/jetpack-roles/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/Automattic/jetpack-roles/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-roles/compare/v1.1.0...v1.2.0
+[1.1.0]: https://github.com/Automattic/jetpack-roles/compare/v1.0.4...v1.1.0
+[1.0.4]: https://github.com/Automattic/jetpack-roles/compare/v1.0.2...v1.0.4
+[1.0.2]: https://github.com/Automattic/jetpack-roles/compare/v1.0.1...v1.0.2
+[1.0.1]: https://github.com/Automattic/jetpack-roles/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/src/class-roles.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/src/class-roles.php
new file mode 100644
index 00000000..7bce3462
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-roles/src/class-roles.php
@@ -0,0 +1,81 @@
+<?php
+/**
+ * A user roles class for Jetpack.
+ *
+ * @package automattic/jetpack-roles
+ */
+
+namespace Automattic\Jetpack;
+
+/**
+ * Class Automattic\Jetpack\Roles
+ *
+ * Contains utilities for translating user roles to capabilities and vice versa.
+ */
+class Roles {
+ /**
+ * Map of roles we care about, and their corresponding minimum capabilities.
+ *
+ * @access protected
+ *
+ * @var array
+ */
+ protected $capability_translations = array(
+ 'administrator' => 'manage_options',
+ 'editor' => 'edit_others_posts',
+ 'author' => 'publish_posts',
+ 'contributor' => 'edit_posts',
+ 'subscriber' => 'read',
+ );
+
+ /**
+ * Get the role of the current user.
+ *
+ * @access public
+ *
+ * @return string|boolean Current user's role, false if not enough capabilities for any of the roles.
+ */
+ public function translate_current_user_to_role() {
+ foreach ( $this->capability_translations as $role => $cap ) {
+ if ( current_user_can( $role ) || current_user_can( $cap ) ) {
+ return $role;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the role of a particular user.
+ *
+ * @access public
+ *
+ * @param \WP_User $user User object.
+ * @return string|boolean User's role, false if not enough capabilities for any of the roles.
+ */
+ public function translate_user_to_role( $user ) {
+ foreach ( $this->capability_translations as $role => $cap ) {
+ if ( user_can( $user, $role ) || user_can( $user, $cap ) ) {
+ return $role;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the minimum capability for a role.
+ *
+ * @access public
+ *
+ * @param string $role Role name.
+ * @return string|boolean Capability, false if role isn't mapped to any capabilities.
+ */
+ public function translate_role_to_cap( $role ) {
+ if ( ! isset( $this->capability_translations[ $role ] ) ) {
+ return false;
+ }
+
+ return $this->capability_translations[ $role ];
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/CHANGELOG.md
new file mode 100644
index 00000000..90de0b6a
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/CHANGELOG.md
@@ -0,0 +1,51 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [0.4.0] - 2022-01-04
+### Changed
+- Do not escape widget title value
+- Switch to pcov for code coverage.
+- Updated package dependencies.
+- Updated package textdomain from `jetpack` to `jetpack-search-pkg`.
+
+### Fixed
+- Add missing textdomains in JS code.
+
+## [0.3.0] - 2021-12-14
+### Changed
+- Search package: add new methods and update timing for `Plan` class.
+- Search package: refactored Module_Control class.
+
+## [0.2.1] - 2021-12-07
+### Changed
+- Updated package dependencies.
+
+## [0.2.0] - 2021-11-30
+### Added
+- Added essential scaffolding for package.
+- Migrate additional helper classes to package
+- Search: added new state store for search dashboard
+- Search package: duplicated search dashboard dependencies to the package
+
+### Changed
+- Search: migrate/create necessary APIs for the frontend
+- Search: removed other dependencies from copied code
+
+## 0.1.0 - 2021-11-09
+### Added
+- Add a new Search package with Helper and Options classes.
+- Search: Migrate helper classes from Jetpack plugin
+
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Updated package dependencies.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+[0.4.0]: https://github.com/Automattic/jetpack-search/compare/v0.3.0...v0.4.0
+[0.3.0]: https://github.com/Automattic/jetpack-search/compare/v0.2.1...v0.3.0
+[0.2.1]: https://github.com/Automattic/jetpack-search/compare/v0.2.0...v0.2.1
+[0.2.0]: https://github.com/Automattic/jetpack-search/compare/v0.1.0...v0.2.0
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-helper.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-helper.php
new file mode 100644
index 00000000..f7f85591
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-helper.php
@@ -0,0 +1,908 @@
+<?php
+/**
+ * Helper class providing various static utility functions for use in Search.
+ *
+ * @package automattic/jetpack-search
+ */
+
+namespace Automattic\Jetpack\Search;
+
+use GP_Locales; // TODO: Migrate this to the package, or find an alternative.
+use Jetpack; // TODO: Remove this once migrated.
+
+/**
+ * Various helper functions for reuse throughout the Jetpack Search code.
+ */
+class Helper {
+
+ /**
+ * The search widget's base ID.
+ *
+ * @since 5.8.0
+ * @var string
+ */
+ const FILTER_WIDGET_BASE = 'jetpack-search-filters';
+
+ /**
+ * Create a URL for the current search that doesn't include the "paged" parameter.
+ *
+ * @since 5.8.0
+ *
+ * @return string The search URL.
+ */
+ public static function get_search_url() {
+ // WordPress search doesn't use nonces.
+ $query_args = stripslashes_deep( $_GET ); //phpcs:ignore WordPress.Security.NonceVerification.Recommended
+
+ // Handle the case where a permastruct is being used, such as /search/{$query}.
+ if ( ! isset( $query_args['s'] ) ) {
+ $query_args['s'] = get_search_query();
+ }
+
+ if ( isset( $query_args['paged'] ) ) {
+ unset( $query_args['paged'] );
+ }
+
+ $query = http_build_query( $query_args );
+
+ return home_url( "?{$query}" );
+ }
+
+ /**
+ * Wraps add_query_arg() with the URL defaulting to the current search URL.
+ *
+ * @see add_query_arg()
+ *
+ * @since 5.8.0
+ *
+ * @param string|array $key Either a query variable key, or an associative array of query variables.
+ * @param string $value Optional. A query variable value.
+ * @param bool|string $url Optional. A URL to act upon. Defaults to the current search URL.
+ *
+ * @return string New URL query string (unescaped).
+ */
+ public static function add_query_arg( $key, $value = false, $url = false ) {
+ $url = empty( $url ) ? self::get_search_url() : $url;
+ if ( is_array( $key ) ) {
+ return add_query_arg( $key, $url );
+ }
+
+ return add_query_arg( $key, $value, $url );
+ }
+
+ /**
+ * Wraps remove_query_arg() with the URL defaulting to the current search URL.
+ *
+ * @see remove_query_arg()
+ *
+ * @since 5.8.0
+ *
+ * @param string|array $key Query key or keys to remove.
+ * @param bool|string $url Optional. A URL to act upon. Defaults to the current search URL.
+ *
+ * @return string New URL query string (unescaped).
+ */
+ public static function remove_query_arg( $key, $url = false ) {
+ $url = empty( $url ) ? self::get_search_url() : $url;
+
+ return remove_query_arg( $key, $url );
+ }
+
+ /**
+ * Returns the name of the search widget's option.
+ *
+ * @since 5.8.0
+ *
+ * @return string The search widget option name.
+ */
+ public static function get_widget_option_name() {
+ return sprintf( 'widget_%s', self::FILTER_WIDGET_BASE );
+ }
+
+ /**
+ * Returns the search widget instances from the widget's option.
+ *
+ * @since 5.8.0
+ *
+ * @return array The widget options.
+ */
+ public static function get_widgets_from_option() {
+ $widget_options = get_option( self::get_widget_option_name(), array() );
+
+ // We don't need this.
+ if ( ! empty( $widget_options ) && isset( $widget_options['_multiwidget'] ) ) {
+ unset( $widget_options['_multiwidget'] );
+ }
+
+ return $widget_options;
+ }
+
+ /**
+ * Returns the widget ID (widget base plus the numeric ID).
+ *
+ * @param int $number The widget's numeric ID.
+ *
+ * @return string The widget's numeric ID prefixed with the search widget base.
+ */
+ public static function build_widget_id( $number ) {
+ return sprintf( '%s-%d', self::FILTER_WIDGET_BASE, $number );
+ }
+
+ /**
+ * Wrapper for is_active_widget() with the other parameters automatically supplied.
+ *
+ * @see is_active_widget()
+ *
+ * @since 5.8.0
+ *
+ * @param int $widget_id Widget ID.
+ *
+ * @return bool Whether the widget is active or not.
+ */
+ public static function is_active_widget( $widget_id ) {
+ return (bool) is_active_widget( false, $widget_id, self::FILTER_WIDGET_BASE, true );
+ }
+
+ /**
+ * Returns an array of the filters from all active search widgets.
+ *
+ * @since 5.8.0
+ *
+ * @param array|null $allowed_widget_ids array of allowed widget IDs.
+ *
+ * @return array Active filters.
+ */
+ public static function get_filters_from_widgets( $allowed_widget_ids = null ) {
+ $filters = array();
+
+ $widget_options = self::get_widgets_from_option();
+ if ( empty( $widget_options ) ) {
+ return $filters;
+ }
+
+ foreach ( (array) $widget_options as $number => $settings ) {
+ $widget_id = self::build_widget_id( $number );
+ if ( ! self::is_active_widget( $widget_id ) || empty( $settings['filters'] ) ) {
+ continue;
+ }
+ if ( isset( $allowed_widget_ids ) && ! in_array( $widget_id, $allowed_widget_ids, true ) ) {
+ continue;
+ }
+
+ foreach ( (array) $settings['filters'] as $widget_filter ) {
+ $widget_filter['widget_id'] = $widget_id;
+
+ if ( empty( $widget_filter['name'] ) ) {
+ $widget_filter['name'] = self::generate_widget_filter_name( $widget_filter );
+ }
+
+ $type = ( isset( $widget_filter['type'] ) ) ? $widget_filter['type'] : '';
+ $key = sprintf( '%s_%d', $type, count( $filters ) );
+
+ $filters[ $key ] = $widget_filter;
+ }
+ }
+
+ return $filters;
+ }
+
+ /**
+ * Get the localized default label for a date filter.
+ *
+ * @since 5.8.0
+ *
+ * @param string $type Date type, either year or month.
+ * @param bool $is_updated Whether the filter was updated or not (adds "Updated" to the end).
+ *
+ * @return string The filter label.
+ */
+ public static function get_date_filter_type_name( $type, $is_updated = false ) {
+ switch ( $type ) {
+ case 'year':
+ $string = ( $is_updated )
+ ? esc_html_x( 'Year Updated', 'label for filtering posts', 'jetpack-search-pkg' )
+ : esc_html_x( 'Year', 'label for filtering posts', 'jetpack-search-pkg' );
+ break;
+ case 'month':
+ default:
+ $string = ( $is_updated )
+ ? esc_html_x( 'Month Updated', 'label for filtering posts', 'jetpack-search-pkg' )
+ : esc_html_x( 'Month', 'label for filtering posts', 'jetpack-search-pkg' );
+ break;
+ }
+
+ return $string;
+ }
+
+ /**
+ * Creates a default name for a filter. Used when the filter label is blank.
+ *
+ * @since 5.8.0
+ *
+ * @param array $widget_filter The filter to generate the title for.
+ *
+ * @return string The suggested filter name.
+ */
+ public static function generate_widget_filter_name( $widget_filter ) {
+ $name = '';
+
+ if ( ! isset( $widget_filter['type'] ) ) {
+ return $name;
+ }
+
+ switch ( $widget_filter['type'] ) {
+ case 'post_type':
+ $name = _x( 'Post Types', 'label for filtering posts', 'jetpack-search-pkg' );
+ break;
+
+ case 'date_histogram':
+ $modified_fields = array(
+ 'post_modified',
+ 'post_modified_gmt',
+ );
+ switch ( $widget_filter['interval'] ) {
+ case 'year':
+ $name = self::get_date_filter_type_name(
+ 'year',
+ in_array( $widget_filter['field'], $modified_fields, true )
+ );
+ break;
+ case 'month':
+ default:
+ $name = self::get_date_filter_type_name(
+ 'month',
+ in_array( $widget_filter['field'], $modified_fields, true )
+ );
+ break;
+ }
+ break;
+
+ case 'taxonomy':
+ $tax = get_taxonomy( $widget_filter['taxonomy'] );
+ if ( ! $tax ) {
+ break;
+ }
+
+ if ( isset( $tax->label ) ) {
+ $name = $tax->label;
+ } elseif ( isset( $tax->labels ) && isset( $tax->labels->name ) ) {
+ $name = $tax->labels->name;
+ }
+ break;
+ }
+
+ return $name;
+ }
+
+ /**
+ * Whether we should rerun a search in the customizer preview or not.
+ *
+ * @since 5.8.0
+ *
+ * @return bool
+ */
+ public static function should_rerun_search_in_customizer_preview() {
+ // Only update when in a customizer preview and data is being posted.
+ // Check for $_POST removes an extra update when the customizer loads.
+ //
+ // Note: We use $GLOBALS['wp_customize'] here instead of is_customize_preview() to support unit tests.
+ return isset( $GLOBALS['wp_customize'] ) && $GLOBALS['wp_customize']->is_preview() && ! empty( $_POST ); // phpcs:ignore
+ }
+
+ /**
+ * Since PHP's built-in array_diff() works by comparing the values that are in array 1 to the other arrays,
+ * if there are less values in array 1, it's possible to get an empty diff where one might be expected.
+ *
+ * @since 5.8.0
+ *
+ * @param array $array_1 The first array.
+ * @param array $array_2 The second array.
+ *
+ * @return array
+ */
+ public static function array_diff( $array_1, $array_2 ) {
+ // If the array counts are the same, then the order doesn't matter. If the count of
+ // $array_1 is higher than $array_2, that's also fine. If the count of $array_2 is higher,
+ // we need to swap the array order though.
+ if ( count( $array_1 ) !== count( $array_2 ) && count( $array_2 ) > count( $array_1 ) ) {
+ $temp = $array_1;
+ $array_1 = $array_2;
+ $array_2 = $temp;
+ }
+
+ // Disregard keys.
+ return array_values( array_diff( $array_1, $array_2 ) );
+ }
+
+ /**
+ * Given the widget instance, will return true when selected post types differ from searchable post types.
+ *
+ * @since 5.8.0
+ *
+ * @param array $post_types An array of post types.
+ *
+ * @return bool
+ */
+ public static function post_types_differ_searchable( $post_types ) {
+ if ( empty( $post_types ) ) {
+ return false;
+ }
+
+ $searchable_post_types = get_post_types( array( 'exclude_from_search' => false ) );
+ $diff_of_searchable = self::array_diff( $searchable_post_types, (array) $post_types );
+
+ return ! empty( $diff_of_searchable );
+ }
+
+ /**
+ * Given the array of post types, will return true when these differ from the current search query.
+ *
+ * @since 5.8.0
+ *
+ * @param array $post_types An array of post types.
+ *
+ * @return bool
+ */
+ public static function post_types_differ_query( $post_types ) {
+ if ( empty( $post_types ) ) {
+ return false;
+ }
+
+ // WordPress search doesn't use nonces.
+ // phpcs:disable WordPress.Security.NonceVerification.Recommended
+ if ( empty( $_GET['post_type'] ) ) {
+ $post_types_from_query = array();
+ } elseif ( is_array( $_GET['post_type'] ) ) {
+ $post_types_from_query = $_GET['post_type'];
+ } else {
+ $post_types_from_query = (array) explode( ',', $_GET['post_type'] );
+ }
+ // phpcs:enable WordPress.Security.NonceVerification.Recommended
+
+ $post_types_from_query = array_map( 'trim', $post_types_from_query );
+
+ $diff_query = self::array_diff( (array) $post_types, $post_types_from_query );
+
+ return ! empty( $diff_query );
+ }
+
+ /**
+ * Determine what Tracks value should be used when updating a widget.
+ *
+ * @since 5.8.0
+ *
+ * @param mixed $old_value The old option value.
+ * @param mixed $new_value The new option value.
+ *
+ * @return array|false False if the widget wasn't updated, otherwise an array of the Tracks action and widget properties.
+ */
+ public static function get_widget_tracks_value( $old_value, $new_value ) {
+ $old_value = (array) $old_value;
+ if ( isset( $old_value['_multiwidget'] ) ) {
+ unset( $old_value['_multiwidget'] );
+ }
+
+ $new_value = (array) $new_value;
+ if ( isset( $new_value['_multiwidget'] ) ) {
+ unset( $new_value['_multiwidget'] );
+ }
+
+ $old_keys = array_keys( $old_value );
+ $new_keys = array_keys( $new_value );
+
+ if ( count( $new_keys ) > count( $old_keys ) ) { // This is the case for a widget being added.
+ $diff = self::array_diff( $new_keys, $old_keys );
+ $action = 'widget_added';
+ $widget = empty( $diff ) || ! isset( $new_value[ $diff[0] ] )
+ ? false
+ : $new_value[ $diff[0] ];
+ } elseif ( count( $old_keys ) > count( $new_keys ) ) { // This is the case for a widget being deleted.
+ $diff = self::array_diff( $old_keys, $new_keys );
+ $action = 'widget_deleted';
+ $widget = empty( $diff ) || ! isset( $old_value[ $diff[0] ] )
+ ? false
+ : $old_value[ $diff[0] ];
+ } else {
+ $action = 'widget_updated';
+ $widget = false;
+
+ // This is a bit crazy. Since there can be multiple widgets stored in a single option,
+ // we need to diff the old and new values to figure out which widget was updated.
+ foreach ( $new_value as $key => $new_instance ) {
+ if ( ! isset( $old_value[ $key ] ) ) {
+ continue;
+ }
+ $old_instance = $old_value[ $key ];
+
+ // First, let's test the keys of each instance.
+ $diff = self::array_diff( array_keys( $new_instance ), array_keys( $old_instance ) );
+ if ( ! empty( $diff ) ) {
+ $widget = $new_instance;
+ break;
+ }
+
+ // Next, lets's loop over each value and compare it.
+ foreach ( $new_instance as $k => $v ) {
+ if ( is_scalar( $v ) && (string) $v !== (string) $old_instance[ $k ] ) {
+ $widget = $new_instance;
+ break;
+ }
+
+ if ( 'filters' === $k ) {
+ if ( count( $new_instance['filters'] ) !== count( $old_instance['filters'] ) ) {
+ $widget = $new_instance;
+ break;
+ }
+
+ foreach ( $v as $filter_key => $new_filter_value ) {
+ $diff = self::array_diff( $new_filter_value, $old_instance['filters'][ $filter_key ] );
+ if ( ! empty( $diff ) ) {
+ $widget = $new_instance;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( empty( $action ) || empty( $widget ) ) {
+ return false;
+ }
+
+ return array(
+ 'action' => $action,
+ 'widget' => self::get_widget_properties_for_tracks( $widget ),
+ );
+ }
+
+ /**
+ * Creates the widget properties for sending to Tracks.
+ *
+ * @since 5.8.0
+ *
+ * @param array $widget The widget instance.
+ *
+ * @return array The widget properties.
+ */
+ public static function get_widget_properties_for_tracks( $widget ) {
+ $sanitized = array();
+
+ foreach ( (array) $widget as $key => $value ) {
+ if ( '_multiwidget' === $key ) {
+ continue;
+ }
+
+ if ( is_scalar( $value ) ) {
+ $key = str_replace( '-', '_', sanitize_key( $key ) );
+ $key = "widget_{$key}";
+ $sanitized[ $key ] = $value;
+ }
+ }
+
+ $filters_properties = ! empty( $widget['filters'] )
+ ? self::get_filter_properties_for_tracks( $widget['filters'] )
+ : array();
+
+ return array_merge( $sanitized, $filters_properties );
+ }
+
+ /**
+ * Creates the filter properties for sending to Tracks.
+ *
+ * @since 5.8.0
+ *
+ * @param array $filters An array of filters.
+ *
+ * @return array The filter properties.
+ */
+ public static function get_filter_properties_for_tracks( $filters ) {
+ if ( empty( $filters ) ) {
+ return $filters;
+ }
+
+ $filters_properties = array(
+ 'widget_filter_count' => count( $filters ),
+ );
+
+ foreach ( $filters as $filter ) {
+ if ( empty( $filter['type'] ) ) {
+ continue;
+ }
+
+ $key = sprintf( 'widget_filter_type_%s', $filter['type'] );
+ if ( isset( $filters_properties[ $key ] ) ) {
+ $filters_properties[ $key ] ++;
+ } else {
+ $filters_properties[ $key ] = 1;
+ }
+ }
+
+ return $filters_properties;
+ }
+
+ /**
+ * Gets the active post types given a set of filters.
+ *
+ * @since 5.8.0
+ *
+ * @param array $filters The active filters for the current query.
+ *
+ * @return array The active post types.
+ */
+ public static function get_active_post_types( $filters ) {
+ $active_post_types = array();
+
+ foreach ( $filters as $item ) {
+ if ( ( 'post_type' === $item['type'] ) && isset( $item['query_vars']['post_type'] ) ) {
+ $active_post_types[] = $item['query_vars']['post_type'];
+ }
+ }
+
+ return $active_post_types;
+ }
+
+ /**
+ * Sets active to false on all post type buckets.
+ *
+ * @since 5.8.0
+ *
+ * @param array $filters The available filters for the current query.
+ *
+ * @return array The filters for the current query with modified active field.
+ */
+ public static function remove_active_from_post_type_buckets( $filters ) {
+ $modified = $filters;
+ foreach ( $filters as $key => $filter ) {
+ if ( 'post_type' === $filter['type'] && ! empty( $filter['buckets'] ) ) {
+ foreach ( $filter['buckets'] as $k => $bucket ) {
+ $bucket['active'] = false;
+ $modified[ $key ]['buckets'][ $k ] = $bucket;
+ }
+ }
+ }
+
+ return $modified;
+ }
+
+ /**
+ * Given a url and an array of post types, will ensure that the post types are properly applied to the URL as args.
+ *
+ * @since 5.8.0
+ *
+ * @param string $url The URL to add post types to.
+ * @param array $post_types An array of post types that should be added to the URL.
+ *
+ * @return string The URL with added post types.
+ */
+ public static function add_post_types_to_url( $url, $post_types ) {
+ $url = self::remove_query_arg( 'post_type', $url );
+ if ( empty( $post_types ) ) {
+ return $url;
+ }
+
+ $url = self::add_query_arg(
+ 'post_type',
+ implode( ',', $post_types ),
+ $url
+ );
+
+ return $url;
+ }
+
+ /**
+ * Since we provide support for the widget restricting post types by adding the selected post types as
+ * active filters, if removing a post type filter would result in there no longer be post_type args in the URL,
+ * we need to be sure to add them back.
+ *
+ * @since 5.8.0
+ *
+ * @param array $filters An array of possible filters for the current query.
+ * @param array $post_types The post types to ensure are on the link.
+ *
+ * @return array The updated array of filters with post typed added to the remove URLs.
+ */
+ public static function ensure_post_types_on_remove_url( $filters, $post_types ) {
+ $modified = $filters;
+
+ foreach ( (array) $filters as $filter_key => $filter ) {
+ if ( 'post_type' !== $filter['type'] || empty( $filter['buckets'] ) ) {
+ $modified[ $filter_key ] = $filter;
+ continue;
+ }
+
+ foreach ( (array) $filter['buckets'] as $bucket_key => $bucket ) {
+ if ( empty( $bucket['remove_url'] ) ) {
+ continue;
+ }
+
+ $parsed = wp_parse_url( $bucket['remove_url'] );
+ if ( ! $parsed ) {
+ continue;
+ }
+
+ $query = array();
+ if ( ! empty( $parsed['query'] ) ) {
+ wp_parse_str( $parsed['query'], $query );
+ }
+
+ if ( empty( $query['post_type'] ) ) {
+ $modified[ $filter_key ]['buckets'][ $bucket_key ]['remove_url'] = self::add_post_types_to_url(
+ $bucket['remove_url'],
+ $post_types
+ );
+ }
+ }
+ }
+
+ return $modified;
+ }
+
+ /**
+ * Wraps a WordPress filter called "jetpack_search_disable_widget_filters" that allows
+ * developers to disable filters supplied by the search widget. Useful if filters are
+ * being defined at the code level.
+ *
+ * @since 5.8.0
+ *
+ * @return bool
+ */
+ public static function are_filters_by_widget_disabled() {
+ /**
+ * Allows developers to disable filters being set by widget, in favor of manually
+ * setting filters via `Jetpack_Search::set_filters()`.
+ *
+ * @module search
+ *
+ * @since 5.7.0
+ *
+ * @param bool false
+ */
+ return apply_filters( 'jetpack_search_disable_widget_filters', false );
+ }
+
+ /**
+ * Returns the maximum posts per page for a search query.
+ *
+ * @since 5.8.0
+ *
+ * @return int
+ */
+ public static function get_max_posts_per_page() {
+ return Options::site_has_vip_index() ? 1000 : 100;
+ }
+
+ /**
+ * Returns the maximum offset for a search query.
+ *
+ * @since 5.8.0
+ *
+ * @return int
+ */
+ public static function get_max_offset() {
+ return Options::site_has_vip_index() ? 9000 : 1000;
+ }
+
+ /**
+ * Returns the maximum offset for a search query.
+ *
+ * @since 8.4.0
+ * @param string $locale A potentially valid locale string.
+ *
+ * @return bool
+ */
+ public static function is_valid_locale( $locale ) {
+ // TODO: Replace JETPACK__GLOTPRESS_LOCALES_PATH.
+ if ( ! class_exists( 'GP_Locales' ) ) {
+ if ( defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) && file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
+ require JETPACK__GLOTPRESS_LOCALES_PATH;
+ } else {
+ // Assume locale to be valid if we can't check with GlotPress.
+ return true;
+ }
+ }
+ return false !== GP_Locales::by_field( 'wp_locale', $locale );
+ }
+
+ /**
+ * Get the version number to use when loading the file. Allows us to bypass cache when developing.
+ *
+ * @since 8.6.0
+ * @param string $file Path of the file we are looking for.
+ * @return string $script_version Version number.
+ */
+ public static function get_asset_version( $file ) {
+ // TODO: Replace Jetpack:: invocation.
+ // TODO: Replace JETPACK__PLUGIN_DIR and JETPACK__VERSION.
+ return Jetpack::is_development_version() && file_exists( JETPACK__PLUGIN_DIR . $file )
+ ? filemtime( JETPACK__PLUGIN_DIR . $file )
+ : JETPACK__VERSION;
+ }
+
+ /**
+ * Generates a customizer settings ID for a given post type.
+ *
+ * @since 8.8.0
+ * @param object $post_type Post type object returned from get_post_types.
+ * @return string $customizer_id Customizer setting ID.
+ */
+ public static function generate_post_type_customizer_id( $post_type ) {
+ return Options::OPTION_PREFIX . 'disable_post_type_' . $post_type->name;
+ }
+
+ /**
+ * Generates an array of post types associated with their customizer IDs.
+ *
+ * @since 8.8.0
+ * @return array $ids Post type => post type customizer ID object.
+ */
+ public static function generate_post_type_customizer_ids() {
+ return array_map(
+ array( 'self', 'generate_post_type_customizer_id' ),
+ get_post_types( array( 'exclude_from_search' => false ), 'objects' )
+ );
+ }
+
+ /**
+ * Sanitizes a checkbox value for writing to the database.
+ *
+ * @since 8.9.0
+ *
+ * @param any $value from the customizer form.
+ * @return string either '0' or '1'.
+ */
+ public static function sanitize_checkbox_value( $value ) {
+ return true === $value ? '1' : '0';
+ }
+
+ /**
+ * Sanitizes a checkbox value for rendering the Customizer.
+ *
+ * @since 8.9.0
+ *
+ * @param any $value from the database.
+ * @return boolean
+ */
+ public static function sanitize_checkbox_value_for_js( $value ) {
+ return '1' === $value;
+ }
+
+ /**
+ * Passes all options to the JS app.
+ */
+ public static function generate_initial_javascript_state() {
+ $widget_options = self::get_widgets_from_option();
+ if ( is_array( $widget_options ) ) {
+ $widget_options = end( $widget_options );
+ }
+
+ $overlay_widget_ids = is_active_sidebar( 'jetpack-instant-search-sidebar' ) ?
+ wp_get_sidebars_widgets()['jetpack-instant-search-sidebar'] : array();
+ $filters = self::get_filters_from_widgets();
+ $widgets = array();
+ $widgets_outside_overlay = array();
+ foreach ( $filters as $key => &$filter ) {
+ $filter['filter_id'] = $key;
+
+ if ( in_array( $filter['widget_id'], $overlay_widget_ids, true ) ) {
+ if ( ! isset( $widgets[ $filter['widget_id'] ] ) ) {
+ $widgets[ $filter['widget_id'] ]['filters'] = array();
+ $widgets[ $filter['widget_id'] ]['widget_id'] = $filter['widget_id'];
+ }
+ $widgets[ $filter['widget_id'] ]['filters'][] = $filter;
+ } else {
+ if ( ! isset( $widgets_outside_overlay[ $filter['widget_id'] ] ) ) {
+ $widgets_outside_overlay[ $filter['widget_id'] ]['filters'] = array();
+ $widgets_outside_overlay[ $filter['widget_id'] ]['widget_id'] = $filter['widget_id'];
+ }
+ $widgets_outside_overlay[ $filter['widget_id'] ]['filters'][] = $filter;
+ }
+ }
+ unset( $filter );
+
+ $has_non_search_widgets = false;
+ foreach ( $overlay_widget_ids as $overlay_widget_id ) {
+ if ( strpos( $overlay_widget_id, self::FILTER_WIDGET_BASE ) === false ) {
+ $has_non_search_widgets = true;
+ break;
+ }
+ }
+
+ $post_type_objs = get_post_types( array( 'exclude_from_search' => false ), 'objects' );
+ $post_type_labels = array();
+ foreach ( $post_type_objs as $key => $obj ) {
+ $post_type_labels[ $key ] = array(
+ 'singular_name' => $obj->labels->singular_name,
+ 'name' => $obj->labels->name,
+ );
+ }
+
+ $prefix = Options::OPTION_PREFIX;
+ $posts_per_page = (int) get_option( 'posts_per_page' );
+ if ( ( $posts_per_page > 20 ) || ( $posts_per_page <= 0 ) ) {
+ $posts_per_page = 20;
+ }
+
+ $excluded_post_types = get_option( $prefix . 'excluded_post_types' ) ? explode( ',', get_option( $prefix . 'excluded_post_types', '' ) ) : array();
+ $post_types = array_values(
+ get_post_types(
+ array(
+ 'exclude_from_search' => false,
+ 'public' => true,
+ )
+ )
+ );
+ $unexcluded_post_types = array_diff( $post_types, $excluded_post_types );
+ // NOTE: If all post types are being excluded, ignore the option value.
+ if ( count( $unexcluded_post_types ) === 0 ) {
+ $excluded_post_types = array();
+ }
+
+ $is_wpcom = defined( 'IS_WPCOM' ) && constant( 'IS_WPCOM' );
+ $is_private_site = '-1' === get_option( 'blog_public' );
+ $is_jetpack_photon_enabled = method_exists( 'Jetpack', 'is_module_active' ) && Jetpack::is_module_active( 'photon' );
+
+ $options = array(
+ 'overlayOptions' => array(
+ 'colorTheme' => get_option( $prefix . 'color_theme', 'light' ),
+ 'enableInfScroll' => get_option( $prefix . 'inf_scroll', '1' ) === '1',
+ 'enableSort' => get_option( $prefix . 'enable_sort', '1' ) === '1',
+ 'highlightColor' => get_option( $prefix . 'highlight_color', '#FFC' ),
+ 'overlayTrigger' => get_option( $prefix . 'overlay_trigger', 'immediate' ),
+ 'resultFormat' => get_option( $prefix . 'result_format', Options::RESULT_FORMAT_MINIMAL ),
+ 'showPoweredBy' => get_option( $prefix . 'show_powered_by', '1' ) === '1',
+
+ // These options require kicking off a new search.
+ 'defaultSort' => get_option( $prefix . 'default_sort', 'relevance' ),
+ 'excludedPostTypes' => $excluded_post_types,
+ ),
+
+ // core config.
+ 'homeUrl' => home_url(),
+ 'locale' => str_replace( '_', '-', self::is_valid_locale( get_locale() ) ? get_locale() : 'en_US' ),
+ 'postsPerPage' => $posts_per_page,
+ 'siteId' => class_exists( 'Jetpack' ) && method_exists( 'Jetpack', 'get_option' ) ? Jetpack::get_option( 'id' ) : get_current_blog_id(),
+ 'postTypes' => $post_type_labels,
+ // TODO: Enable this once instant search build pipeline has been moved to the Search package.
+ // 'webpackPublicPath' => plugins_url( '/build/instant-search/', __DIR__ ).
+ 'webpackPublicPath' => plugins_url( '_inc/build/instant-search/', JETPACK__PLUGIN_FILE ),
+ 'isPhotonEnabled' => ( $is_wpcom || $is_jetpack_photon_enabled ) && ! $is_private_site,
+
+ // config values related to private site support.
+ 'apiRoot' => esc_url_raw( rest_url() ),
+ 'apiNonce' => wp_create_nonce( 'wp_rest' ),
+ 'isPrivateSite' => $is_private_site,
+ 'isWpcom' => $is_wpcom,
+
+ // widget info.
+ 'hasOverlayWidgets' => count( $overlay_widget_ids ) > 0,
+ 'widgets' => array_values( $widgets ),
+ 'widgetsOutsideOverlay' => array_values( $widgets_outside_overlay ),
+ 'hasNonSearchWidgets' => $has_non_search_widgets,
+ );
+
+ /**
+ * Customize Instant Search Options.
+ *
+ * @module search
+ *
+ * @since 7.7.0
+ *
+ * @param array $options Array of parameters used in Instant Search queries.
+ */
+ return apply_filters( 'jetpack_instant_search_options', $options );
+ }
+
+ /**
+ * Prints the Instant Search sidebar.
+ */
+ public static function print_instant_search_sidebar() {
+ ?>
+ <div class="jetpack-instant-search__widget-area" style="display: none">
+ <?php if ( is_active_sidebar( 'jetpack-instant-search-sidebar' ) ) { ?>
+ <?php dynamic_sidebar( 'jetpack-instant-search-sidebar' ); ?>
+ <?php } ?>
+ </div>
+ <?php
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-module-control.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-module-control.php
new file mode 100644
index 00000000..ea97b03c
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-module-control.php
@@ -0,0 +1,217 @@
+<?php
+/**
+ * Jetpack Search: Module_Control class
+ *
+ * @package automattic/jetpack-search
+ */
+
+namespace Automattic\Jetpack\Search;
+
+use Automattic\Jetpack\Status;
+use Jetpack_Options;
+use WP_Error;
+
+/**
+ * To get and set Searh module settings
+ */
+class Module_Control {
+ /**
+ * Plan object
+ *
+ * @var Plan
+ */
+ protected $plan;
+
+ /**
+ * We use the same options as Jetpack the plugin to flag whether Search is active.
+ */
+ const JETPACK_ACTIVE_MODULES_OPTION_KEY = 'active_modules';
+ const JETPACK_SEARCH_MODULE_SLUG = 'search';
+ const SEARCH_MODULE_INSTANT_SEARCH_OPTION_KEY = 'instant_search_enabled';
+
+ /**
+ * Contructor
+ *
+ * @param Plan|null $plan - Plan object.
+ */
+ public function __construct( $plan = null ) {
+ $this->plan = is_null( $plan ) ? new Plan() : $plan;
+ }
+
+ /**
+ * Returns a boolean for whether of the module is enabled.
+ *
+ * @return bool
+ */
+ public function is_active() {
+ return in_array( self::JETPACK_SEARCH_MODULE_SLUG, $this->get_active_modules(), true );
+ }
+
+ /**
+ * Returns a boolean for whether instant search is enabled.
+ *
+ * @return bool
+ */
+ public function is_instant_search_enabled() {
+ return (bool) get_option( self::SEARCH_MODULE_INSTANT_SEARCH_OPTION_KEY );
+ }
+
+ /**
+ * Activiate Search module
+ */
+ public function activate() {
+ /**
+ * Fires before a module is activated.
+ *
+ * @since 2.6.0
+ *
+ * @param string $module Module slug.
+ * @param bool $exit Should we exit after the module has been activated. Default to true.
+ * @param bool $redirect Should the user be redirected after module activation? Default to true.
+ */
+ do_action( 'jetpack_pre_activate_module', self::JETPACK_SEARCH_MODULE_SLUG );
+
+ // If it's already active, then don't do it again.
+ if ( $this->is_active() ) {
+ return true;
+ }
+ // Not available for offline mode.
+ $is_offline_mode = ( new Status() )->is_offline_mode();
+ if ( $is_offline_mode ) {
+ return new WP_Error( 'offline_mode', __( 'Search module can not be activated in offline mode.', 'jetpack-search-pkg' ) );
+ }
+ // Return false if no plan supports search.
+ if ( ! $this->plan->supports_search() ) {
+ return new WP_Error( 'not_supported', __( 'Your plan does not support Jetpack Search.', 'jetpack-search-pkg' ) );
+ }
+
+ $active_modules = $this->get_active_modules();
+ $active_modules[] = self::JETPACK_SEARCH_MODULE_SLUG;
+
+ $success = Jetpack_Options::update_option( self::JETPACK_ACTIVE_MODULES_OPTION_KEY, $active_modules );
+
+ /**
+ * Fired after a module has been deactivated.
+ *
+ * @since 4.2.0
+ *
+ * @param string $module Module slug.
+ * @param boolean $success whether the module was deactivated.
+ */
+ do_action( 'jetpack_activate_module', self::JETPACK_SEARCH_MODULE_SLUG, $success );
+ /**
+ * Fires when a module is deactivated.
+ * The dynamic part of the filter, $module, is the module slug.
+ *
+ * @since 1.9.0
+ *
+ * @param string $module Module slug.
+ */
+ do_action( 'jetpack_activate_module_' . self::JETPACK_SEARCH_MODULE_SLUG );
+
+ return $success;
+ }
+
+ /**
+ * Deactiviate Search module
+ */
+ public function deactivate() {
+ /**
+ * Fires when a module is deactivated.
+ *
+ * @since 1.9.0
+ *
+ * @param string $module Module slug.
+ */
+ do_action( 'jetpack_pre_deactivate_module', self::JETPACK_SEARCH_MODULE_SLUG );
+
+ $active_modules = $this->get_active_modules();
+ $active_modules = array_values( array_diff( $active_modules, array( self::JETPACK_SEARCH_MODULE_SLUG ) ) );
+
+ $success = Jetpack_Options::update_option( self::JETPACK_ACTIVE_MODULES_OPTION_KEY, $active_modules );
+
+ /**
+ * Fired after a module has been deactivated.
+ *
+ * @since 4.2.0
+ *
+ * @param string $module Module slug.
+ * @param boolean $success whether the module was deactivated.
+ */
+ do_action( 'jetpack_deactivate_module', self::JETPACK_SEARCH_MODULE_SLUG, $success );
+ /**
+ * Fires when a module is deactivated.
+ * The dynamic part of the filter, $module, is the module slug.
+ *
+ * @since 1.9.0
+ *
+ * @param string $module Module slug.
+ */
+ do_action( 'jetpack_deactivate_module_' . self::JETPACK_SEARCH_MODULE_SLUG );
+
+ $this->disable_instant_search();
+
+ return $success;
+ }
+
+ /**
+ * Update module status
+ *
+ * @param boolean $active - true to activate, false to deactivate.
+ */
+ public function update_status( $active ) {
+ return $active ? $this->activate() : $this->deactivate();
+ }
+
+ /**
+ * Disable Instant Search Experience
+ */
+ public function disable_instant_search() {
+ return update_option( self::SEARCH_MODULE_INSTANT_SEARCH_OPTION_KEY, false );
+ }
+
+ /**
+ * Enable Instant Search Experience
+ */
+ public function enable_instant_search() {
+ if ( ! $this->is_active() ) {
+ return new WP_Error( 'search_module_inactive', __( 'Search module needs to be activated before enabling instant search.', 'jetpack-search-pkg' ) );
+ }
+ return update_option( self::SEARCH_MODULE_INSTANT_SEARCH_OPTION_KEY, true );
+ }
+
+ /**
+ * Update instant search status
+ *
+ * @param boolean $enabled - true to enable, false to disable.
+ */
+ public function update_instant_search_status( $enabled ) {
+ return $enabled ? $this->enable_instant_search() : $this->disable_instant_search();
+ }
+
+ /**
+ * Get a list of activated modules as an array of module slugs.
+ */
+ public function get_active_modules() {
+ $active_modules = Jetpack_Options::get_option( self::JETPACK_ACTIVE_MODULES_OPTION_KEY );
+
+ if ( ! is_array( $active_modules ) ) {
+ $active_modules = array();
+ }
+
+ /**
+ * Allow filtering of the active modules.
+ *
+ * Gives theme and plugin developers the power to alter the modules that
+ * are activated on the fly.
+ *
+ * @since 5.8.0
+ *
+ * @param array $active Array of active module slugs.
+ */
+ $active_modules = apply_filters( 'jetpack_active_modules', $active_modules );
+
+ return array_unique( $active_modules );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-options.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-options.php
new file mode 100644
index 00000000..0eca3283
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-options.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * Another helper class for parsing Jetpack Search options.
+ *
+ * @package automattic/jetpack-search
+ */
+
+namespace Automattic\Jetpack\Search;
+
+use Automattic\Jetpack\Constants;
+
+/**
+ * Helpers for parsing the various Search options
+ */
+class Options {
+ /**
+ * The search widget's base ID.
+ *
+ * @since 5.8.0
+ * @var string
+ */
+ const FILTER_WIDGET_BASE = 'jetpack-search-filters';
+
+ /**
+ * Prefix for options in DB.
+ *
+ * @since 8.3.0
+ * @var string
+ */
+ const OPTION_PREFIX = 'jetpack_search_';
+
+ /**
+ * Available result formats.
+ *
+ * @since 9.6.0
+ * @var string
+ */
+ const RESULT_FORMAT_MINIMAL = 'minimal';
+ const RESULT_FORMAT_EXPANDED = 'expanded';
+ const RESULT_FORMAT_PRODUCT = 'product';
+
+ /**
+ * Available overlay triggers.
+ *
+ * @since 9.9.0
+ * @var string
+ */
+ const OVERLAY_TRIGGER_IMMEDIATE = 'immediate';
+ const OVERLAY_TRIGGER_RESULTS = 'results';
+ const OVERLAY_TRIGGER_SUBMIT = 'submit';
+
+ /**
+ * Returns a boolean for whether instant search is enabled.
+ *
+ * @since 8.3.0
+ *
+ * @return bool
+ */
+ public static function is_instant_enabled() {
+ return true === (bool) get_option( 'instant_search_enabled' );
+ }
+
+ /**
+ * Returns a boolean for whether the current site has a VIP index.
+ *
+ * @since 5.8.0
+ *
+ * @return bool
+ */
+ public static function site_has_vip_index() {
+ $has_vip_index = (
+ Constants::is_defined( 'JETPACK_SEARCH_VIP_INDEX' ) &&
+ Constants::get_constant( 'JETPACK_SEARCH_VIP_INDEX' )
+ );
+
+ /**
+ * Allows developers to filter whether the current site has a VIP index.
+ *
+ * @module search
+ *
+ * @since 5.8.0
+ *
+ * @param bool $has_vip_index Whether the current site has a VIP index.
+ */
+ return apply_filters( 'jetpack_search_has_vip_index', $has_vip_index );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-plan.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-plan.php
new file mode 100644
index 00000000..41134260
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-plan.php
@@ -0,0 +1,140 @@
+<?php
+/**
+ * The Search Plan class.
+ * Registers the REST routes for Search.
+ *
+ * @package automattic/jetpack-search
+ */
+
+namespace Automattic\Jetpack\Search;
+
+use Automattic\Jetpack\Connection\Client;
+use Jetpack_Options;
+use WP_Error;
+
+/**
+ * Registers the REST routes for Search.
+ */
+class Plan {
+ const JETPACK_SEARCH_PLAN_INFO_OPTION_KEY = 'jetpack_search_plan_info';
+ const JETPACK_SEARCH_EVER_SUPPORTED_SEARCH = 'jetpack_search_ever_supported_search';
+
+ /**
+ * Whether we have hooked the actions.
+ *
+ * @var boolean
+ */
+ protected static $update_plan_hook_initialized = false;
+
+ /**
+ * Init hooks for updating plan info
+ */
+ public function init_hooks() {
+ // Update plan info from WPCOM on Jetpack heartbeat.
+ // TODO: implement heartbeart for search.
+ if ( ! static::$update_plan_hook_initialized ) {
+ add_action( 'jetpack_heartbeat', array( $this, 'get_plan_info_from_wpcom' ) );
+ static::$update_plan_hook_initialized = true;
+ }
+ }
+
+ /**
+ * Refresh plan info stored in options
+ */
+ public function get_plan_info_from_wpcom() {
+ $blog_id = Jetpack_Options::get_option( 'id' );
+ $response = Client::wpcom_json_api_request_as_user(
+ '/sites/' . $blog_id . '/jetpack-search/plan',
+ '2'
+ );
+
+ // store plan in options.
+ $this->update_search_plan_info( $response );
+
+ return $response;
+ }
+
+ /**
+ * Get plan info.
+ *
+ * @param {bool} $force_refresh - Default to false. Set true to load from WPCOM.
+ */
+ public function get_plan_info( $force_refresh = false ) {
+ if ( $force_refresh ) {
+ $this->get_plan_info_from_wpcom();
+ }
+ $plan_info = get_option( self::JETPACK_SEARCH_PLAN_INFO_OPTION_KEY );
+ if ( false === $plan_info && ! $force_refresh ) {
+ $plan_info = $this->get_plan_info( true );
+ }
+ return $plan_info;
+ }
+
+ /**
+ * Please use `supports_instant_search` instead.
+ *
+ * @deprecated
+ */
+ public function has_jetpack_search_product() {
+ return (bool) get_option( 'has_jetpack_search_product' );
+ }
+
+ /**
+ * Returns true if plan supports Instant Search.
+ */
+ public function supports_instant_search() {
+ $plan_info = $this->get_plan_info();
+ return isset( $plan_info['supports_instant_search'] ) && $plan_info['supports_instant_search'];
+ }
+
+ /**
+ * Returns true if the plan support either Instant Search or Classic Search.
+ */
+ public function supports_search() {
+ $plan_info = $this->get_plan_info();
+ return isset( $plan_info['supports_search'] ) && $plan_info['supports_search'];
+ }
+
+ /**
+ * Returns true if the plan only supports Classic Search.
+ */
+ public function supports_only_classic_search() {
+ $plan_info = $this->get_plan_info();
+ return isset( $plan_info['supports_only_classic_search'] ) && $plan_info['supports_only_classic_search'];
+ }
+
+ /**
+ * Whether the plan(s) ever supported search.
+ */
+ public function ever_supported_search() {
+ return (bool) get_option( self::JETPACK_SEARCH_EVER_SUPPORTED_SEARCH ) || $this->supports_search();
+ }
+
+ /**
+ * Update `has_jetpack_search_product` regarding the plan information
+ *
+ * @param array|WP_Error $response - Resopnse from WPCOM.
+ */
+ public function update_search_plan_info( $response ) {
+ if ( is_wp_error( $response ) ) {
+ return null;
+ }
+ $body = json_decode( wp_remote_retrieve_body( $response ), true );
+ $status_code = wp_remote_retrieve_response_code( $response );
+
+ if ( 200 !== $status_code || ! isset( $body['supports_instant_search'] ) ) {
+ return null;
+ }
+ // set option whether has Jetpack Search plan for capability reason.
+ if ( get_option( 'has_jetpack_search_product' ) !== (bool) $body['supports_instant_search'] ) {
+ update_option( 'has_jetpack_search_product', (bool) $body['supports_instant_search'] );
+ }
+ // We use this option to determine the visibility of search submenu.
+ // If the site ever had search subscription, then we record it and show the menu after.
+ if ( $body['supports_instant_search'] ) {
+ update_option( self::JETPACK_SEARCH_EVER_SUPPORTED_SEARCH, true, false );
+ }
+ update_option( self::JETPACK_SEARCH_PLAN_INFO_OPTION_KEY, $body );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-rest-controller.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-rest-controller.php
new file mode 100644
index 00000000..209e989e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-rest-controller.php
@@ -0,0 +1,250 @@
+<?php
+/**
+ * The Search Rest Controller class.
+ * Registers the REST routes for Search.
+ *
+ * @package automattic/jetpack-search
+ */
+
+namespace Automattic\Jetpack\Search;
+
+use Automattic\Jetpack\Connection\Client;
+use Jetpack_Options;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Server;
+
+/**
+ * Registers the REST routes for Search.
+ */
+class REST_Controller {
+ /**
+ * Whether it's run on WPCOM.
+ *
+ * @var bool
+ */
+ protected $is_wpcom;
+
+ /**
+ * Module Control object.
+ *
+ * @var Module_Control
+ */
+ protected $search_module;
+
+ /**
+ * Constructor
+ *
+ * @param bool $is_wpcom - Whether it's run on WPCOM.
+ * @param Module_Control|null $module_control - Module_Control object if any.
+ */
+ public function __construct( $is_wpcom = false, $module_control = null ) {
+ $this->is_wpcom = $is_wpcom;
+ $this->search_module = is_null( $module_control ) ? new Module_Control() : $module_control;
+ }
+
+ /**
+ * Registers the REST routes for Search.
+ *
+ * @access public
+ * @static
+ */
+ public function register_rest_routes() {
+ register_rest_route(
+ 'jetpack/v4',
+ '/search/plan',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'get_search_plan' ),
+ 'permission_callback' => array( $this, 'search_permissions_callback' ),
+ )
+ );
+ register_rest_route(
+ 'jetpack/v4',
+ '/search/settings',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => array( $this, 'update_settings' ),
+ 'permission_callback' => array( $this, 'search_permissions_callback' ),
+ )
+ );
+ register_rest_route(
+ 'jetpack/v4',
+ '/search/settings',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'get_settings' ),
+ 'permission_callback' => array( $this, 'search_permissions_callback' ),
+ )
+ );
+ register_rest_route(
+ 'jetpack/v4',
+ '/search',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => array( $this, 'get_search_results' ),
+ 'permission_callback' => 'is_user_logged_in',
+ )
+ );
+ }
+
+ /**
+ * Only administrators can access the API.
+ *
+ * @return bool|WP_Error True if a blog token was used to sign the request, WP_Error otherwise.
+ */
+ public function search_permissions_callback() {
+ if ( current_user_can( 'manage_options' ) ) {
+ return true;
+ }
+
+ $error_msg = esc_html__(
+ 'You are not allowed to perform this action.',
+ 'jetpack-search-pkg'
+ );
+
+ return new WP_Error( 'rest_forbidden', $error_msg, array( 'status' => rest_authorization_required_code() ) );
+ }
+
+ /**
+ * Proxy the request to WPCOM and return the response.
+ *
+ * GET `jetpack/v4/search/plan`
+ */
+ public function get_search_plan() {
+ $response = ( new Plan() )->get_plan_info_from_wpcom();
+ return $this->make_proper_response( $response );
+ }
+
+ /**
+ * POST `jetpack/v4/search/settings`
+ *
+ * @param WP_REST_Request $request - REST request.
+ */
+ public function update_settings( $request ) {
+ $request_body = $request->get_json_params();
+
+ $module_active = isset( $request_body['module_active'] ) ? (bool) $request_body['module_active'] : null;
+ $instant_search_enabled = isset( $request_body['instant_search_enabled'] ) ? (bool) $request_body['instant_search_enabled'] : null;
+
+ $error = $this->validate_search_settings( $module_active, $instant_search_enabled );
+
+ if ( is_wp_error( $error ) ) {
+ return $error;
+ }
+
+ // Enabling instant search should enable the module too.
+ if ( true === $instant_search_enabled && true !== $module_active ) {
+ $module_active = true;
+ }
+
+ $errors = array();
+ if ( ! is_null( $module_active ) ) {
+ $module_active_updated = $this->search_module->update_status( $module_active );
+ if ( is_wp_error( $module_active_updated ) ) {
+ $errors['module_active'] = $module_active_updated;
+ }
+ }
+
+ if ( ! is_null( $instant_search_enabled ) ) {
+ $instant_search_enabled_updated = $this->search_module->update_instant_search_status( $instant_search_enabled );
+ if ( is_wp_error( $instant_search_enabled_updated ) ) {
+ $errors['instant_search_enabled'] = $instant_search_enabled_updated;
+ }
+ }
+
+ if ( ! empty( $errors ) ) {
+ return new WP_Error(
+ 'some_updated',
+ sprintf(
+ /* translators: %s are the setting name that not updated. */
+ __( 'Some settings ( %s ) not updated.', 'jetpack-search-pkg' ),
+ implode(
+ ',',
+ array_keys( $errors )
+ )
+ ),
+ array( 'status' => 400 )
+ );
+ }
+
+ return $this->get_settings();
+ }
+
+ /**
+ * Validate $module_active and $instant_search_enabled. Returns an WP_Error instance if invalid.
+ *
+ * @param boolean $module_active - Module status.
+ * @param boolean $instant_search_enabled - Instant Search status.
+ */
+ protected function validate_search_settings( $module_active, $instant_search_enabled ) {
+ if ( ( true === $instant_search_enabled && false === $module_active ) || ( is_null( $module_active ) && is_null( $instant_search_enabled ) ) ) {
+ return new WP_Error(
+ 'rest_invalid_arguments',
+ esc_html__( 'The arguments passed in are invalid.', 'jetpack-search-pkg' ),
+ array( 'status' => 400 )
+ );
+ }
+ return true;
+ }
+
+ /**
+ * GET `jetpack/v4/search/settings`
+ */
+ public function get_settings() {
+ return array(
+ 'module_active' => $this->search_module->is_active(),
+ 'instant_search_enabled' => $this->search_module->is_instant_search_enabled(),
+ );
+ }
+
+ /**
+ * Search Endpoint for private sites.
+ *
+ * GET `jetpack/v4/search`
+ *
+ * @param WP_REST_Request $request - REST request.
+ */
+ public function get_search_results( $request ) {
+ $blog_id = $this->get_blog_id();
+ $path = sprintf( '/sites/%d/search', absint( $blog_id ) );
+ $path = add_query_arg(
+ $request->get_query_params(),
+ sprintf( '/sites/%d/search', absint( $blog_id ) )
+ );
+ $response = Client::wpcom_json_api_request_as_user( $path, '1.3', array(), null, 'rest' );
+ return $this->make_proper_response( $response );
+ }
+
+ /**
+ * Forward remote response to client with error handling.
+ *
+ * @param array|WP_Error $response - Resopnse from WPCOM.
+ */
+ protected function make_proper_response( $response ) {
+ if ( is_wp_error( $response ) ) {
+ return $response;
+ }
+
+ $body = json_decode( wp_remote_retrieve_body( $response ), true );
+ $status_code = wp_remote_retrieve_response_code( $response );
+
+ if ( 200 === $status_code ) {
+ return $body;
+ }
+
+ return new WP_Error(
+ isset( $body['error'] ) ? 'remote-error-' . $body['error'] : 'remote-error',
+ isset( $body['message'] ) ? $body['message'] : 'unknown remote error',
+ array( 'status' => $status_code )
+ );
+ }
+
+ /**
+ * Get blog id
+ */
+ protected function get_blog_id() {
+ return $this->is_wpcom ? get_current_blog_id() : Jetpack_Options::get_option( 'id' );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-settings.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-settings.php
new file mode 100644
index 00000000..ec4a9ed8
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-settings.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Jetpack Search Overlay Settings
+ *
+ * @package automattic/jetpack-search
+ */
+
+namespace Automattic\Jetpack\Search;
+
+// Exit if file is accessed directly.
+if ( ! defined( 'ABSPATH' ) ) {
+ exit;
+}
+
+/**
+ * Class to initialize search settings on the site.
+ */
+class Settings {
+
+ /**
+ * Class initialization.
+ */
+ public function __construct() {
+ add_action( 'admin_init', array( $this, 'settings_register' ) );
+ add_action( 'rest_api_init', array( $this, 'settings_register' ) );
+ }
+
+ /**
+ * Register requisite settings.
+ *
+ * @since 9.x.x
+ */
+ public function settings_register() {
+ // NOTE: This contains significant code overlap with class-jetpack-search-customize.
+ $setting_prefix = Options::OPTION_PREFIX;
+ $settings = array(
+ array( $setting_prefix . 'color_theme', 'string', 'light' ),
+ array( $setting_prefix . 'result_format', 'string', 'minimal' ),
+ array( $setting_prefix . 'default_sort', 'string', 'relevance' ),
+ array( $setting_prefix . 'overlay_trigger', 'string', 'results' ),
+ array( $setting_prefix . 'excluded_post_types', 'string', '' ),
+ array( $setting_prefix . 'highlight_color', 'string', '#FFC' ),
+ array( $setting_prefix . 'enable_sort', 'boolean', true ),
+ array( $setting_prefix . 'inf_scroll', 'boolean', true ),
+ array( $setting_prefix . 'show_powered_by', 'boolean', true ),
+ );
+ foreach ( $settings as $value ) {
+ register_setting(
+ 'options',
+ $value[0],
+ array(
+ 'default' => $value[2],
+ 'show_in_rest' => true,
+ 'type' => $value[1],
+ )
+ );
+ }
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-template-tags.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-template-tags.php
new file mode 100644
index 00000000..a212af00
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/class-template-tags.php
@@ -0,0 +1,335 @@
+<?php
+/**
+ * Template tags class used primarily for rendering widget-related HTML.
+ *
+ * Currently, this package can only run in the Jetpack plugin due to its usage of Jetpack_Search.
+ * Once Jetpack_Search has been migrated to the package as Classic_Search,
+ * this library will be independent from the Jetpack plugin.
+ *
+ * @package automattic/jetpack-search
+ */
+
+namespace Automattic\Jetpack\Search;
+
+/**
+ * Class that has various methods for outputting functionality into a theme that doesn't support widgets.
+ * Additionally the widget itself makes use of these class.
+ *
+ * @since 5.8.0
+ */
+class Template_Tags {
+
+ /**
+ * Renders all available filters that can be used to filter down search results on the frontend.
+ *
+ * @since 5.8.0
+ *
+ * @param array $filters The available filters for the current query.
+ * @param array $post_types An array of post types to make filterable.
+ */
+ public static function render_available_filters( $filters = null, $post_types = null ) {
+ if ( is_null( $filters ) ) {
+ // TODO: Must be migrated to use Classic_Search once the migration is underway.
+ $filters = \Jetpack_Search::instance()->get_filters();
+ }
+
+ if ( is_null( $post_types ) ) {
+ $post_types = get_post_types( array( 'exclude_from_search' => false ) );
+ }
+
+ /**
+ * If the post types specified by the widget differ from the default set of searchable post types,
+ * then we need to track their state.
+ */
+ $active_post_types = array();
+ if ( Helper::post_types_differ_searchable( $post_types ) ) {
+ // get the active filter buckets from the query.
+ // TODO: Must be migrated to use Classic_Search once the migration is underway.
+ $active_buckets = \Jetpack_Search::instance()->get_active_filter_buckets();
+ $post_types_differ_query = Helper::post_types_differ_query( $post_types );
+
+ // remove any post_type filters from display if the current query
+ // already specifies to match all post types.
+ if ( ! $post_types_differ_query ) {
+ $active_buckets = array_filter( $active_buckets, array( __CLASS__, 'is_not_post_type_filter' ) );
+ }
+
+ $active_post_types = Helper::get_active_post_types( $active_buckets );
+ if ( empty( $active_post_types ) ) {
+ $active_post_types = $post_types;
+ }
+
+ if ( $post_types_differ_query ) {
+ $filters = Helper::ensure_post_types_on_remove_url( $filters, $post_types );
+ } else {
+ $filters = Helper::remove_active_from_post_type_buckets( $filters );
+ }
+ } else {
+ $post_types = array();
+ }
+
+ foreach ( (array) $filters as $filter ) {
+ if ( 'post_type' === $filter['type'] ) {
+ self::render_filter( $filter, $post_types );
+ } else {
+ self::render_filter( $filter, $active_post_types );
+ }
+ }
+ }
+
+ /**
+ * Renders filters for instant search.
+ *
+ * @param array $filters The available filters for the current query.
+ */
+ public static function render_instant_filters( $filters = null ) {
+ if ( is_null( $filters ) ) {
+ // TODO: Must be migrated to use Classic_Search once the migration is underway.
+ $filters = \Jetpack_Search::instance()->get_filters();
+ }
+
+ foreach ( (array) $filters as $filter ) {
+ self::render_instant_filter( $filter );
+ }
+ }
+
+ /**
+ * Renders a single filter that can be applied to the current search.
+ *
+ * @since 5.8.0
+ *
+ * @param array $filter The filter to render.
+ * @param array $default_post_types The default post types for this filter.
+ */
+ public static function render_filter( $filter, $default_post_types ) {
+ if ( empty( $filter ) || empty( $filter['buckets'] ) ) {
+ return;
+ }
+
+ $query_vars = null;
+ foreach ( $filter['buckets'] as $item ) {
+ if ( $item['active'] ) {
+ $query_vars = array_keys( $item['query_vars'] );
+ break;
+ }
+ }
+ $clear_url = null;
+ if ( ! empty( $query_vars ) ) {
+ $clear_url = Helper::remove_query_arg( $query_vars );
+ if ( ! empty( $default_post_types ) ) {
+ $clear_url = Helper::add_post_types_to_url( $clear_url, $default_post_types );
+ }
+ }
+
+ ?>
+ <h4 class="jetpack-search-filters-widget__sub-heading">
+ <?php echo esc_html( $filter['name'] ); ?>
+ </h4>
+ <?php if ( $clear_url ) : ?>
+ <div class="jetpack-search-filters-widget__clear">
+ <a href="<?php echo esc_url( $clear_url ); ?>">
+ <?php esc_html_e( '< Clear Filters', 'jetpack-search-pkg' ); ?>
+ </a>
+ </div>
+ <?php endif; ?>
+ <ul class="jetpack-search-filters-widget__filter-list">
+ <?php
+ foreach ( $filter['buckets'] as $item ) :
+ $url = ( empty( $item['active'] ) ) ? $item['url'] : $item['remove_url'];
+ ?>
+ <li>
+ <label>
+ <input type="checkbox"<?php checked( ! empty( $item['active'] ) ); ?> disabled="disabled" />&nbsp;
+ <a href="<?php echo esc_url( $url ); ?>">
+ <?php
+ echo esc_html( $item['name'] );
+ echo '&nbsp;';
+ echo esc_html(
+ sprintf(
+ '(%s)',
+ number_format_i18n( absint( $item['count'] ) )
+ )
+ );
+ ?>
+ </a>
+ </label>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+ <?php
+ }
+
+ /**
+ * Renders a single filter for instant search.
+ *
+ * @since 8.3.0
+ *
+ * @param array $filter The filter to render.
+ */
+ public static function render_instant_filter( $filter ) {
+ if ( empty( $filter ) || empty( $filter['buckets'] ) ) {
+ return;
+ }
+
+ $data_base = '';
+ $qv = $filter['buckets'][0]['query_vars'];
+ $tax_key = '';
+ switch ( $filter['buckets'][0]['type'] ) {
+ case 'taxonomy':
+ $data_base = 'data-filter-type="' . esc_attr( $filter['buckets'][0]['type'] ) . '" ';
+ $tax_key = key( $qv );
+ if ( 'category_name' === $tax_key ) {
+ $data_base .= 'data-taxonomy="category"';
+ } elseif ( 'tag' === $tax_key ) {
+ $data_base .= 'data-taxonomy="post_tag"';
+ } else {
+ $data_base .= 'data-taxonomy="' . esc_attr( $tax_key ) . '"';
+ }
+ break;
+ case 'post_type':
+ $data_base = 'data-filter-type="post_types" ';
+ break;
+ case 'date_histogram':
+ if ( $filter['buckets'][0]['query_vars']['monthnum'] ) {
+ $data_base = 'data-filter-type="month_post_date" ';
+ } else {
+ $data_base = 'data-filter-type="year_post_date" ';
+ }
+ break;
+ }
+
+ ?>
+ <h4 class="jetpack-search-filters-widget__sub-heading">
+ <?php echo esc_html( $filter['name'] ); ?>
+ </h4>
+ <ul class="jetpack-search-filters-widget__filter-list">
+ <?php
+ foreach ( $filter['buckets'] as $item ) :
+ $data_str = $data_base . ' ';
+ switch ( $filter['buckets'][0]['type'] ) {
+ case 'taxonomy':
+ $data_str .= 'data-val="' . esc_attr( $item['query_vars'][ $tax_key ] ) . '"';
+ break;
+ case 'post_type':
+ $data_str .= 'data-val="' . esc_attr( $item['query_vars']['post_type'] ) . '"';
+ break;
+ case 'date_histogram':
+ if ( $item['query_vars']['monthnum'] ) {
+ $d = sprintf( '%d-%02d-01 00:00:00', $item['query_vars']['year'], $item['query_vars']['monthnum'] );
+ } else {
+ $d = sprintf( '%d-01-01 00:00:00', $item['query_vars']['year'] );
+ }
+ $data_str .= 'data-val="' . esc_attr( $d ) . '" ';
+ break;
+ }
+ ?>
+ <li>
+ <?php // TODO: Figure out how to properly escape $data_str below. ?>
+ <a href="#" class="jetpack-search-filter__link" <?php echo $data_str; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>>
+ <?php
+ echo esc_html( $item['name'] );
+ echo '&nbsp;';
+ echo esc_html(
+ sprintf(
+ '(%s)',
+ number_format_i18n( absint( $item['count'] ) )
+ )
+ );
+ ?>
+ </a>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+ <?php
+ }
+
+ /**
+ * Outputs the search widget's title.
+ *
+ * @since 5.8.0
+ *
+ * @param string $title The widget's title.
+ * @param string $before_title The HTML tag to display before the title.
+ * @param string $after_title The HTML tag to display after the title.
+ */
+ public static function render_widget_title( $title, $before_title, $after_title ) {
+ // This doesn't need to be escaped because it's provided by WP and goes through filters, where it is not escaped.
+ echo $before_title . $title . $after_title; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ }
+
+ /**
+ * Responsible for rendering the search box within our widget on the frontend.
+ *
+ * @since 5.8.0
+ *
+ * @param array $post_types Array of post types to limit search results to.
+ * @param string $orderby How to order the search results.
+ * @param string $order In what direction to order the search results.
+ */
+ public static function render_widget_search_form( $post_types, $orderby, $order ) {
+ $form = get_search_form( false );
+
+ $fields_to_inject = array(
+ 'orderby' => $orderby,
+ 'order' => $order,
+ );
+
+ // If the widget has specified post types to search within and IF the post types differ
+ // from the default post types that would have been searched, set the selected post
+ // types via hidden inputs.
+ if ( Helper::post_types_differ_searchable( $post_types ) ) {
+ $fields_to_inject['post_type'] = implode( ',', $post_types );
+ }
+
+ $form = self::inject_hidden_form_fields( $form, $fields_to_inject );
+
+ echo '<div class="jetpack-search-form">';
+ // TODO: Figure out how to properly escape this.
+ echo $form; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ echo '</div>';
+ }
+
+ /**
+ * Modifies an HTML form to add some additional hidden fields.
+ *
+ * @since 5.8.0
+ *
+ * @param string $form The form HTML to modify.
+ * @param array $fields Array of hidden fields to add. Key is field name and value is the field value.
+ *
+ * @return string The modified form HTML.
+ */
+ private static function inject_hidden_form_fields( $form, $fields ) {
+ $form_injection = '';
+
+ foreach ( $fields as $field_name => $field_value ) {
+ $form_injection .= sprintf(
+ '<input type="hidden" name="%s" value="%s" />',
+ esc_attr( $field_name ),
+ esc_attr( $field_value )
+ );
+ }
+
+ // This shouldn't need to be escaped since we've escaped above as we built $form_injection.
+ $form = str_replace(
+ '</form>',
+ $form_injection . '</form>',
+ $form
+ );
+
+ return $form;
+ }
+
+ /**
+ * Internal method for filtering out non-post_type filters.
+ *
+ * @since 5.8.0
+ *
+ * @param array $filter Filter object.
+ *
+ * @return bool
+ */
+ private static function is_not_post_type_filter( $filter ) {
+ return 'post_type' !== $filter['type'];
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/button/index.jsx b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/button/index.jsx
new file mode 100644
index 00000000..d945554f
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/button/index.jsx
@@ -0,0 +1,52 @@
+/**
+ * External dependencies
+ */
+import PropTypes from 'prop-types';
+import React from 'react';
+import classNames from 'classnames';
+import { noop } from 'lodash';
+
+/**
+ * Internal dependencies
+ */
+import './style.scss';
+
+export default class Button extends React.Component {
+ static displayName = 'Button';
+
+ static propTypes = {
+ disabled: PropTypes.bool,
+ compact: PropTypes.bool,
+ primary: PropTypes.bool,
+ scary: PropTypes.bool,
+ type: PropTypes.string,
+ href: PropTypes.string,
+ onClick: PropTypes.func,
+ borderless: PropTypes.bool,
+ className: PropTypes.string,
+ };
+
+ static defaultProps = {
+ disabled: false,
+ type: 'button',
+ onClick: noop,
+ borderless: false,
+ };
+
+ render() {
+ const element = this.props.href ? 'a' : 'button';
+ const { primary, compact, scary, borderless, className, ...props } = this.props;
+
+ const buttonClasses = classNames( {
+ 'dops-button': true,
+ 'is-compact': compact,
+ 'is-primary': primary,
+ 'is-scary': scary,
+ 'is-borderless': borderless,
+ } );
+
+ props.className = classNames( className, buttonClasses );
+
+ return React.createElement( element, props, this.props.children );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/card/compact.jsx b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/card/compact.jsx
new file mode 100644
index 00000000..7bfacab4
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/card/compact.jsx
@@ -0,0 +1,23 @@
+/**
+ * External dependencies
+ */
+import React from 'react';
+import { assign } from 'lodash';
+import classnames from 'classnames';
+
+/**
+ * Internal dependencies
+ */
+import Card from 'components/card';
+
+export default class CompactCard extends React.Component {
+ static displayName = 'CompactCard';
+
+ render() {
+ const props = assign( {}, this.props, {
+ className: classnames( this.props.className, 'is-compact' ),
+ } );
+
+ return <Card { ...props }>{ this.props.children }</Card>;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/card/index.jsx b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/card/index.jsx
new file mode 100644
index 00000000..e8dbf9e1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/card/index.jsx
@@ -0,0 +1,141 @@
+/**
+ * External dependencies
+ */
+import PropTypes from 'prop-types';
+import React from 'react';
+import classnames from 'classnames';
+import { assign, omit } from 'lodash';
+
+/**
+ * Internal dependencies
+ */
+// TODO change to our own gridicon component, when instant search is migrated.
+import Gridicon from 'gridicons';
+
+import './style.scss';
+
+class CardSection extends React.Component {
+ static propTypes = {
+ title: PropTypes.any,
+ vertical: PropTypes.any,
+ style: PropTypes.object,
+ className: PropTypes.string,
+ device: PropTypes.oneOf( [ 'desktop', 'tablet', 'phone' ] ),
+ };
+
+ static defaultProps = { vertical: null };
+
+ render() {
+ return (
+ <div
+ className={ classnames( 'dops-card-section', this.props.className ) }
+ style={ this.props.style }
+ >
+ { this.props.title ? this._renderWithTitle() : this.props.children }
+ </div>
+ );
+ }
+
+ _renderWithTitle = () => {
+ const orientation = this.props.vertical ? 'vertical' : 'horizontal';
+ const wrapperClassName = 'dops-card-section-orient-' + orientation;
+
+ return (
+ <div className={ wrapperClassName }>
+ <h4 ref="label" className="dops-card-section-label">
+ { this.props.title }
+ </h4>
+ <div ref="content" className="dops-card-section-content">
+ { this.props.children }
+ </div>
+ </div>
+ );
+ };
+}
+
+class CardFooter extends React.Component {
+ render() {
+ return <div className="dops-card-footer">{ this.props.children }</div>;
+ }
+}
+
+class Card extends React.Component {
+ static propTypes = {
+ meta: PropTypes.any,
+ icon: PropTypes.string,
+ iconLabel: PropTypes.any,
+ iconColor: PropTypes.string,
+ style: PropTypes.object,
+ className: PropTypes.string,
+ href: PropTypes.string,
+ onClick: PropTypes.func,
+ title: PropTypes.string,
+ tagName: PropTypes.string,
+ target: PropTypes.string,
+ compact: PropTypes.bool,
+ children: PropTypes.node,
+ };
+
+ static defaultProps = {
+ iconColor: '#787878',
+ className: '',
+ tagName: 'div',
+ onClick: () => {},
+ };
+
+ render() {
+ const className = classnames( 'dops-card', this.props.className, {
+ 'is-card-link': !! this.props.href,
+ 'is-compact': this.props.compact,
+ } );
+
+ const omitProps = [ 'compact', 'tagName', 'meta', 'iconColor' ];
+
+ let linkIndicator;
+ if ( this.props.href ) {
+ linkIndicator = (
+ <Gridicon
+ className="dops-card__link-indicator"
+ icon={ this.props.target ? 'external' : 'chevron-right' }
+ />
+ );
+ } else {
+ omitProps.push( 'href', 'target' );
+ }
+
+ let fancyTitle;
+ if ( this.props.title ) {
+ fancyTitle = (
+ <h2 className="dops-card-title">
+ { this.props.title }
+ { this.props.meta && <span className="dops-card-meta">{ this.props.meta }</span> }
+ { ( this.props.icon || this.props.iconLabel ) && this._renderIcon() }
+ </h2>
+ );
+ }
+
+ return React.createElement(
+ this.props.href ? 'a' : this.props.tagName,
+ assign( omit( this.props, omitProps ), { className } ),
+ linkIndicator,
+ fancyTitle,
+ this.props.children
+ );
+ }
+
+ _renderIcon = () => {
+ return (
+ <span className="dops-card-icon" style={ { color: this.props.iconColor } }>
+ { this.props.icon && (
+ <Gridicon icon={ this.props.icon } style={ { backgroundColor: this.props.iconColor } } />
+ ) }
+ { this.props.iconLabel }
+ </span>
+ );
+ };
+}
+
+Card.Section = CardSection;
+Card.Footer = CardFooter;
+
+export default Card;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/form-toggle/compact.jsx b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/form-toggle/compact.jsx
new file mode 100644
index 00000000..72341c8e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/form-toggle/compact.jsx
@@ -0,0 +1,26 @@
+/**
+ * External dependencies
+ */
+import React from 'react';
+import classNames from 'classnames';
+import { omit } from 'lodash';
+
+/**
+ * Internal dependencies
+ */
+import Toggle from 'components/form-toggle';
+
+export default class CompactFormToggle extends React.Component {
+ static displayName = 'CompactFormToggle';
+
+ render() {
+ return (
+ <Toggle
+ { ...omit( this.props, 'className' ) }
+ className={ classNames( this.props.className, 'is-compact' ) }
+ >
+ { this.props.children }
+ </Toggle>
+ );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/form-toggle/index.jsx b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/form-toggle/index.jsx
new file mode 100644
index 00000000..31739fc8
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/form-toggle/index.jsx
@@ -0,0 +1,126 @@
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+
+/**
+ * External dependencies
+ */
+import PropTypes from 'prop-types';
+import React, { Component, Fragment } from 'react';
+import classNames from 'classnames';
+
+import './style.scss';
+
+export default class FormToggle extends Component {
+ static propTypes = {
+ onChange: PropTypes.func,
+ onKeyDown: PropTypes.func,
+ checked: PropTypes.bool,
+ disabled: PropTypes.bool,
+ id: PropTypes.string,
+ className: PropTypes.string,
+ toggling: PropTypes.bool,
+ 'aria-label': PropTypes.string,
+ children: PropTypes.node,
+ disabledReason: PropTypes.node,
+ switchClassNames: PropTypes.string,
+ labelClassNames: PropTypes.string,
+ };
+
+ static defaultProps = {
+ checked: false,
+ disabled: false,
+ onKeyDown: () => {},
+ onChange: () => {},
+ disabledReason: '',
+ };
+
+ state = {};
+
+ static idNum = 0;
+
+ constructor() {
+ super( ...arguments );
+
+ this.onKeyDown = this.onKeyDown.bind( this );
+ this.onClick = this.onClick.bind( this );
+ this.onLabelClick = this.onLabelClick.bind( this );
+ }
+
+ UNSAFE_componentWillMount() {
+ this.id = this.constructor.idNum++;
+ }
+
+ onKeyDown( event ) {
+ if ( this.props.disabled ) {
+ return;
+ }
+
+ if ( event.key === 'Enter' || event.key === ' ' ) {
+ event.preventDefault();
+ this.props.onChange();
+ }
+
+ this.props.onKeyDown( event );
+ }
+
+ onClick() {
+ if ( ! this.props.disabled ) {
+ this.props.onChange();
+ }
+ }
+
+ onLabelClick( event ) {
+ if ( this.props.disabled ) {
+ return;
+ }
+
+ const nodeName = event.target.nodeName.toLowerCase();
+ if ( nodeName !== 'a' && nodeName !== 'input' && nodeName !== 'select' ) {
+ event.preventDefault();
+ this.props.onChange();
+ }
+ }
+
+ render() {
+ const id = this.props.id || 'toggle-' + this.id;
+ const toggleClasses = classNames( 'form-toggle', this.props.className, {
+ 'is-toggling': this.props.toggling,
+ } );
+
+ return (
+ <Fragment>
+ <input
+ className={ toggleClasses }
+ type="checkbox"
+ checked={ this.props.checked }
+ readOnly={ true }
+ disabled={ this.props.disabled }
+ />
+
+ <span
+ className={ classNames( 'form-toggle__switch', this.props.switchClassNames ) }
+ disabled={ this.props.disabled }
+ id={ id }
+ onClick={ this.onClick }
+ onKeyDown={ this.onKeyDown }
+ role="checkbox"
+ aria-checked={ this.props.checked }
+ aria-label={ this.props[ 'aria-label' ] }
+ tabIndex={ this.props.disabled ? -1 : 0 }
+ ref="toggleSwitch"
+ />
+ <label
+ className={ classNames( 'form-toggle__label', this.props.labelClassNames ) }
+ htmlFor={ id }
+ >
+ <span
+ className={ classNames( 'form-toggle__label-content', this.props.labelClassNames ) }
+ onClick={ this.onLabelClick }
+ >
+ { this.props.children }
+ </span>
+ </label>
+ </Fragment>
+ );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/index.jsx b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/index.jsx
new file mode 100644
index 00000000..c0df631a
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/index.jsx
@@ -0,0 +1,55 @@
+/**
+ * External Dependencies
+ */
+import React from 'react';
+
+/**
+ * Internal Dependencies
+ */
+import SimpleNotice from 'components/notice/index.jsx';
+import NoticeAction from 'components/notice/notice-action';
+
+import './style.scss';
+
+/**
+ * NoticesList component
+ *
+ * @param {*} props - Props
+ * @returns {React.Component} - NoticesList component
+ */
+export default function NoticesList(
+ props = { handleLocalNoticeDismissClick: null, notices: Object.freeze( [] ) }
+) {
+ const noticesList = props.notices.map( function ( notice ) {
+ const onDismissClick = theNotice => () => {
+ theNotice && props.handleLocalNoticeDismissClick( theNotice.id );
+ };
+ return (
+ <SimpleNotice
+ key={ 'notice-' + notice.id }
+ status={ notice.status }
+ duration={ notice.duration || null }
+ text={ notice.text }
+ isCompact={ notice.isCompact }
+ onDismissClick={ onDismissClick( notice ) }
+ showDismiss={ notice.showDismiss }
+ >
+ { notice.button && (
+ <NoticeAction href={ notice.href } onClick={ onDismissClick( notice ) }>
+ { notice.button }
+ </NoticeAction>
+ ) }
+ </SimpleNotice>
+ );
+ } );
+
+ if ( ! noticesList.length ) {
+ return null;
+ }
+
+ return (
+ <div id={ props.id } className="global-notices">
+ { noticesList }
+ </div>
+ );
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/store/actions.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/store/actions.js
new file mode 100644
index 00000000..09c739d0
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/store/actions.js
@@ -0,0 +1,61 @@
+/**
+ * External dependencies
+ */
+import { uniqueId } from 'lodash';
+import { __ } from '@wordpress/i18n';
+
+export const CREATE_NOTICE = 'CREATE_NOTICE';
+export const REMOVE_NOTICE = 'REMOVE_NOTICE';
+
+/**
+ * Create global notice
+ *
+ * @param {*} status - success, error, info or warning.
+ * @param {*} text - the text to show.
+ * @param {*} options - Options.
+ * @returns {object} - action object.
+ */
+export function createNotice( status, text, options = {} ) {
+ const notice = {
+ id: options.id || uniqueId(),
+ duration: options.duration ?? 2000,
+ showDismiss: typeof options.showDismiss === 'boolean' ? options.showDismiss : true,
+ isPersistent: options.isPersistent || false,
+ displayOnNextPage: options.displayOnNextPage || false,
+ status: status,
+ text: text,
+ };
+
+ return {
+ type: CREATE_NOTICE,
+ notice: notice,
+ };
+}
+
+/**
+ * Remove notice by ID
+ *
+ * @param {*} noticeId - noticeID.
+ * @returns {object} - action object.
+ */
+export function removeNotice( noticeId ) {
+ return { type: REMOVE_NOTICE, notice: { id: noticeId } };
+}
+
+export const successNotice = createNotice.bind( null, 'is-success' );
+export const errorNotice = createNotice.bind( null, 'is-error' );
+export const infoNotice = createNotice.bind( null, 'is-info' );
+export const warningNotice = createNotice.bind( null, 'is-warning' );
+export const updatingNotice = ( text = __( 'Updating settings…', 'jetpack-search-pkg' ) ) =>
+ createNotice( 'is-info', text, { duration: 30000, id: 'search-updating-settings' } );
+export const removeUpdatingNotice = () => removeNotice( 'search-updating-settings' );
+
+export default {
+ createNotice,
+ removeNotice,
+ successNotice,
+ errorNotice,
+ warningNotice,
+ updatingNotice,
+ removeUpdatingNotice,
+};
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/store/reducer.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/store/reducer.js
new file mode 100644
index 00000000..e0749197
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/store/reducer.js
@@ -0,0 +1,22 @@
+/**
+ * Internal dependencies
+ */
+import { CREATE_NOTICE, REMOVE_NOTICE } from './actions';
+
+const notices = ( state = { notices: [] }, action ) => {
+ switch ( action.type ) {
+ case CREATE_NOTICE:
+ return {
+ ...state,
+ notices: [ ...state.notices, action.notice ],
+ };
+ case REMOVE_NOTICE:
+ return {
+ ...state,
+ notices: state.notices.filter( notice => notice.id !== action.notice.id ),
+ };
+ }
+ return state;
+};
+
+export default notices;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/store/selectors.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/store/selectors.js
new file mode 100644
index 00000000..da2d9829
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/global-notices/store/selectors.js
@@ -0,0 +1,5 @@
+const noticeSelectors = {
+ getNotices: state => state.notices.notices ?? [],
+};
+
+export default noticeSelectors;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/notice/index.jsx b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/notice/index.jsx
new file mode 100644
index 00000000..9c1b2e88
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/notice/index.jsx
@@ -0,0 +1,136 @@
+/**
+ * External dependencies
+ */
+import PropTypes from 'prop-types';
+import React from 'react';
+import classnames from 'classnames';
+import { noop } from 'lodash';
+
+/**
+ * Internal dependencies
+ */
+// TODO change to our own gridicon component, when instant search is migrated.
+import Gridicon from 'gridicons';
+import './style.scss';
+
+export default class SimpleNotice extends React.Component {
+ static displayName = 'SimpleNotice';
+
+ static defaultProps = {
+ duration: 0,
+ status: null,
+ showDismiss: true,
+ className: '',
+ onDismissClick: noop,
+ };
+
+ static propTypes = {
+ // we should validate the allowed statuses
+ status: PropTypes.string,
+ showDismiss: PropTypes.bool,
+ isCompact: PropTypes.bool,
+ duration: PropTypes.number,
+ text: PropTypes.oneOfType( [
+ PropTypes.oneOfType( [ PropTypes.string, PropTypes.node ] ),
+ PropTypes.arrayOf( PropTypes.oneOfType( [ PropTypes.string, PropTypes.node ] ) ),
+ ] ),
+ icon: PropTypes.string,
+ onDismissClick: PropTypes.func,
+ className: PropTypes.string,
+ };
+
+ dismissTimeout = null;
+
+ componentDidMount() {
+ if ( this.props.duration > 0 ) {
+ this.dismissTimeout = setTimeout( this.props.onDismissClick, this.props.duration );
+ }
+ }
+
+ componentWillUnmount() {
+ if ( this.dismissTimeout ) {
+ clearTimeout( this.dismissTimeout );
+ }
+ }
+
+ getIcon = () => {
+ let icon;
+
+ switch ( this.props.status ) {
+ case 'is-info':
+ icon = 'info';
+ break;
+ case 'is-success':
+ icon = 'checkmark';
+ break;
+ case 'is-error':
+ icon = 'notice';
+ break;
+ case 'is-warning':
+ icon = 'notice';
+ break;
+ default:
+ icon = 'info';
+ break;
+ }
+
+ return icon;
+ };
+
+ clearText = text => {
+ if ( 'string' === typeof text ) {
+ return text.replace( /(<([^>]+)>)/gi, '' );
+ }
+ return text;
+ };
+
+ onKeyDownCallback = callback => event => {
+ if ( event.which === 13 || event.which === 32 ) {
+ callback && callback( event );
+ }
+ };
+
+ render() {
+ const {
+ children,
+ className,
+ icon,
+ isCompact,
+ onDismissClick,
+ showDismiss = ! isCompact, // by default, show on normal notices, don't show on compact ones
+ status,
+ text,
+ dismissText,
+ } = this.props;
+ const classes = classnames( 'dops-notice', status, className, {
+ 'is-compact': isCompact,
+ 'is-dismissable': showDismiss,
+ } );
+
+ return (
+ <div className={ classes }>
+ <span className="dops-notice__icon-wrapper">
+ <Gridicon className="dops-notice__icon" icon={ icon || this.getIcon() } size={ 24 } />
+ </span>
+ <span className="dops-notice__content">
+ <span className="dops-notice__text">{ text ? this.clearText( text ) : children }</span>
+ </span>
+ { text ? children : null }
+ { showDismiss && (
+ <span
+ role="button"
+ onKeyDown={ this.onKeyDownCallback( onDismissClick ) }
+ tabIndex="0"
+ className="dops-notice__dismiss"
+ onClick={ onDismissClick }
+ >
+ <Gridicon icon="cross" size={ 24 } />
+ <span className="dops-notice__screen-reader-text screen-reader-text">
+ { dismissText }
+ </span>
+ </span>
+ ) }
+ </div>
+ );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/notice/notice-action.jsx b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/notice/notice-action.jsx
new file mode 100644
index 00000000..5a3e8ebb
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/components/notice/notice-action.jsx
@@ -0,0 +1,48 @@
+/**
+ * External dependencies
+ */
+import PropTypes from 'prop-types';
+import React from 'react';
+
+/**
+ * Internal dependencies
+ */
+// TODO change to our own gridicon component, when instant search is migrated.
+import Gridicon from 'gridicons';
+
+import './style.scss';
+
+export default class NoticeAction extends React.Component {
+ static displayName = 'NoticeAction';
+
+ static propTypes = {
+ href: PropTypes.string,
+ onClick: PropTypes.func,
+ external: PropTypes.bool,
+ icon: PropTypes.string,
+ };
+
+ static defaultProps = {
+ external: false,
+ };
+
+ render() {
+ const attributes = {
+ className: 'dops-notice__action',
+ href: this.props.href,
+ onClick: this.props.onClick,
+ };
+
+ if ( this.props.external ) {
+ attributes.target = '_blank';
+ }
+
+ return (
+ <a { ...attributes }>
+ <span>{ this.props.children }</span>
+ { this.props.icon && <Gridicon icon={ this.props.icon } size={ 24 } /> }
+ { this.props.external && <Gridicon icon="external" size={ 24 } /> }
+ </a>
+ );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/actions/index.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/actions/index.js
new file mode 100644
index 00000000..3b831225
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/actions/index.js
@@ -0,0 +1,14 @@
+/**
+ * Internal dependencies
+ */
+import siteSettingActions from './jetpack-settings';
+import sitePlanActions from './site-plan';
+import noticeActions from 'components/global-notices/store/actions';
+
+const actions = {
+ ...siteSettingActions,
+ ...sitePlanActions,
+ ...noticeActions,
+};
+
+export default actions;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/actions/jetpack-settings.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/actions/jetpack-settings.js
new file mode 100644
index 00000000..acf807a9
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/actions/jetpack-settings.js
@@ -0,0 +1,73 @@
+/**
+ * Internal dependencies
+ */
+import {
+ fetchJetpackSettings,
+ updateJetpackSettings as updateJetpackSettingsControl,
+} from '../controls';
+import {
+ removeUpdatingNotice,
+ updatingNotice,
+ errorNotice,
+ successNotice,
+} from 'components/global-notices/store/actions';
+import { __ } from '@wordpress/i18n';
+
+export const SET_JETPACK_SETTINGS = 'SET_JETPACK_SETTINGS';
+export const TOGGLE_SEARCH_MODULE = 'TOGGLE_SEARCH_MODULE';
+
+/**
+ * Yield actions to update Search Settings
+ *
+ * @param {object} settings - settings to apply.
+ * @param {object} oldSettings - Old settings.
+ * @yields {object} - an action object.
+ * @returns {object} - an action object.
+ */
+export function* updateJetpackSettings( settings, oldSettings ) {
+ try {
+ yield updatingNotice();
+ yield setJetpackSettings( settings );
+ yield setUpdatingJetpackSettings();
+ yield updateJetpackSettingsControl( settings );
+ const updatedSettings = yield fetchJetpackSettings();
+ yield setJetpackSettings( updatedSettings );
+ return successNotice( __( 'Updated settings.', 'jetpack-search-pkg' ) );
+ } catch ( e ) {
+ yield setJetpackSettings( oldSettings );
+ return errorNotice( __( 'Error Update settings…', 'jetpack-search-pkg' ) );
+ } finally {
+ yield removeUpdatingNotice();
+ yield setUpdatingJetpackSettingsDone();
+ }
+}
+
+/**
+ * Set state updating action
+ *
+ * @returns {object} - an action object.
+ */
+export function setUpdatingJetpackSettings() {
+ return setJetpackSettings( { is_updating: true } );
+}
+
+/**
+ * Set state updating finished
+ *
+ * @returns {object} - an action object.
+ */
+export function setUpdatingJetpackSettingsDone() {
+ return setJetpackSettings( { is_updating: false } );
+}
+
+/**
+ * Set Jetpack settings action
+ *
+ * @param {object} options - Jetpack settings.
+ * @returns {object} - an action object.
+ */
+export function setJetpackSettings( options ) {
+ return { type: SET_JETPACK_SETTINGS, options };
+}
+
+export default { updateJetpackSettings, setJetpackSettings };
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/actions/site-plan.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/actions/site-plan.js
new file mode 100644
index 00000000..b59c630e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/actions/site-plan.js
@@ -0,0 +1,16 @@
+export const SET_SEARCH_PLAN_INFO = 'SET_SEARCH_PLAN_INFO';
+
+/**
+ * Action to set plan info
+ *
+ * @param {*} options - plan info.
+ * @returns {object} - an action object.
+ */
+export function setSearchPlanInfo( options ) {
+ return {
+ type: 'SET_SEARCH_PLAN_INFO',
+ options,
+ };
+}
+
+export default { setSearchPlanInfo };
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/controls.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/controls.js
new file mode 100644
index 00000000..055f975c
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/controls.js
@@ -0,0 +1,55 @@
+/**
+ * Internal dependencies
+ */
+import restApi from '@automattic/jetpack-api';
+
+export const FETCH_JETPACK_SETTINGS = 'FETCH_JETPACK_SETTINGS';
+export const UPDATE_JETPACK_SETTINGS = 'UPDATE_JETPACK_SETTINGS';
+export const FETCH_SEARCH_PLAN_INFO = 'FETCH_SEARCH_PLAN_INFO';
+
+/**
+ * fetchJetpackSettings action
+ *
+ * @returns {object} - an action object.
+ */
+export const fetchJetpackSettings = () => {
+ return {
+ type: FETCH_JETPACK_SETTINGS,
+ };
+};
+
+/**
+ * updateJetpackSettings action
+ *
+ * @param {*} settings - Jetpack settings object.
+ * @returns {object} - an action object.
+ */
+export const updateJetpackSettings = settings => {
+ return {
+ type: UPDATE_JETPACK_SETTINGS,
+ settings,
+ };
+};
+
+/**
+ * fetchSearchPlanInfo action
+ *
+ * @returns {object} - an action object.
+ */
+export const fetchSearchPlanInfo = () => {
+ return {
+ type: FETCH_SEARCH_PLAN_INFO,
+ };
+};
+
+export default {
+ [ FETCH_JETPACK_SETTINGS ]: function () {
+ return restApi.fetchSearchSettings();
+ },
+ [ UPDATE_JETPACK_SETTINGS ]: function ( action ) {
+ return restApi.updateSearchSettings( action.settings );
+ },
+ [ FETCH_SEARCH_PLAN_INFO ]: function () {
+ return restApi.fetchSearchPlanInfo();
+ },
+};
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/index.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/index.js
new file mode 100644
index 00000000..56b96527
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/index.js
@@ -0,0 +1,18 @@
+/**
+ * Internal dependencies
+ */
+import reducer from './reducer';
+import actions from './actions';
+import selectors from './selectors';
+import resolvers from './resolvers';
+import controls from './controls';
+
+export const STORE_ID = 'jetpack-search-plugin';
+export const storeConfig = {
+ reducer,
+ actions,
+ selectors,
+ resolvers,
+ controls,
+ initialState: window.JETPACK_SEARCH_DASHBOARD_INITIAL_STATE || {},
+};
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/index.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/index.js
new file mode 100644
index 00000000..cf734005
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/index.js
@@ -0,0 +1,23 @@
+/**
+ * WordPress dependencies
+ */
+import { combineReducers } from '@wordpress/data';
+
+/**
+ * Internal dependencies
+ */
+import siteData from './site-data';
+import userData from './user-data';
+import jetpackSettings from './jetpack-settings';
+import sitePlan from './site-plan';
+import notices from 'components/global-notices/store/reducer';
+
+const reducer = combineReducers( {
+ siteData,
+ jetpackSettings,
+ sitePlan,
+ userData,
+ notices,
+} );
+
+export default reducer;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/jetpack-settings.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/jetpack-settings.js
new file mode 100644
index 00000000..1a1fc138
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/jetpack-settings.js
@@ -0,0 +1,22 @@
+/**
+ * Internal dependencies
+ */
+import { SET_JETPACK_SETTINGS } from '../actions/jetpack-settings';
+
+const jetpackSettings = ( state = {}, action ) => {
+ switch ( action.type ) {
+ case SET_JETPACK_SETTINGS:
+ return {
+ ...state,
+ ...action.options,
+ is_toggling_module:
+ state.module_active !== action.options.module_active && !! action.options.is_updating,
+ is_toggling_instant_search:
+ state.instant_search_enabled !== action.options.instant_search_enabled &&
+ !! action.options.is_updating,
+ };
+ }
+ return state;
+};
+
+export default jetpackSettings;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/site-data.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/site-data.js
new file mode 100644
index 00000000..8e1a578d
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/site-data.js
@@ -0,0 +1,5 @@
+const siteData = ( state = {} ) => {
+ return state;
+};
+
+export default siteData;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/site-plan.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/site-plan.js
new file mode 100644
index 00000000..9fff3d43
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/site-plan.js
@@ -0,0 +1,18 @@
+/**
+ * Internal dependencies
+ */
+import { SET_SEARCH_PLAN_INFO } from '../actions/site-plan';
+
+const sitePlan = ( state = {}, action ) => {
+ switch ( action.type ) {
+ case SET_SEARCH_PLAN_INFO:
+ return {
+ ...state,
+ ...action.options,
+ };
+ }
+
+ return state;
+};
+
+export default sitePlan;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/user-data.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/user-data.js
new file mode 100644
index 00000000..17af1898
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/reducer/user-data.js
@@ -0,0 +1,5 @@
+const userData = ( state = {} ) => {
+ return state;
+};
+
+export default userData;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/resolvers.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/resolvers.js
new file mode 100644
index 00000000..3c5272bc
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/resolvers.js
@@ -0,0 +1,48 @@
+/**
+ * External dependencies
+ */
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import { fetchJetpackSettings, fetchSearchPlanInfo } from './controls';
+import { setJetpackSettings } from './actions/jetpack-settings';
+import { setSearchPlanInfo } from './actions/site-plan';
+import { errorNotice } from '../components/global-notices/store/actions';
+
+/**
+ * Yield actions to get Search Module Status
+ *
+ * @yields {object} - an action object.
+ * @returns {object} - an action object.
+ */
+export function* getSearchModuleStatus() {
+ try {
+ const settings = yield fetchJetpackSettings();
+ if ( settings ) {
+ return setJetpackSettings( settings );
+ }
+ } catch ( e ) {
+ return errorNotice( __( 'Error fetching settings…', 'jetpack-search-pkg' ) );
+ }
+}
+
+/**
+ * Yield actions to get search plan info
+ *
+ * @yields {object} - an action object.
+ * @returns {object} - an action object.
+ */
+export function* getSearchPlanInfo() {
+ try {
+ const planInfo = yield fetchSearchPlanInfo();
+ if ( planInfo ) {
+ return setSearchPlanInfo( planInfo );
+ }
+ } catch ( e ) {
+ return errorNotice( __( 'Error fetching search plan…', 'jetpack-search-pkg' ) );
+ }
+}
+
+export default { getSearchModuleStatus, getSearchPlanInfo };
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/index.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/index.js
new file mode 100644
index 00000000..8020ea19
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/index.js
@@ -0,0 +1,18 @@
+/**
+ * Internal dependencies
+ */
+import siteDataSelectors from './site-data';
+import jetpackSettingSelectors from './jetpack-settings';
+import sitePlanSelectors from './site-plan';
+import userDataSelectors from './user-data';
+import noticeSelectors from 'components/global-notices/store/selectors';
+
+const selectors = {
+ ...siteDataSelectors,
+ ...jetpackSettingSelectors,
+ ...sitePlanSelectors,
+ ...userDataSelectors,
+ ...noticeSelectors,
+};
+
+export default selectors;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/jetpack-settings.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/jetpack-settings.js
new file mode 100644
index 00000000..6cd876c4
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/jetpack-settings.js
@@ -0,0 +1,10 @@
+const jetpackSettingSelectors = {
+ getSearchModuleStatus: state => state.jetpackSettings,
+ isModuleEnabled: state => state.jetpackSettings.module_active,
+ isInstantSearchEnabled: state => state.jetpackSettings.instant_search_enabled,
+ isUpdatingJetpackSettings: state => state.jetpackSettings.is_updating,
+ isTogglingModule: state => state.jetpackSettings.is_toggling_module,
+ isTogglingInstantSearch: state => state.jetpackSettings.is_toggling_instant_search,
+};
+
+export default jetpackSettingSelectors;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/site-data.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/site-data.js
new file mode 100644
index 00000000..5669e52d
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/site-data.js
@@ -0,0 +1,12 @@
+const siteDataSelectors = {
+ getAPIRootUrl: state => state.siteData?.WP_API_root ?? null,
+ getAPINonce: state => state.siteData?.WP_API_nonce ?? null,
+ getRegistrationNonce: state => state.siteData?.registrationNonce ?? null,
+ getSiteAdminUrl: state => state.siteData?.adminUrl ?? null,
+ isInstantSearchPromotionActive: state => state.siteData?.showPromotions ?? true,
+ getBlogId: state => state.siteData?.blogId ?? 0,
+ getVersion: state => state.siteData?.version ?? 'development',
+ getCalypsoSlug: state => state.siteData?.calypsoSlug,
+};
+
+export default siteDataSelectors;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/site-plan.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/site-plan.js
new file mode 100644
index 00000000..32d24989
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/site-plan.js
@@ -0,0 +1,12 @@
+const sitePlanSelectors = {
+ getSearchPlanInfo: state => state.sitePlan,
+ hasBusinessPlan: state => state.sitePlan.supports_only_classic_search,
+ hasActiveSearchPurchase: state => state.sitePlan.supports_instant_search,
+ supportsInstantSearch: state => state.sitePlan.supports_instant_search,
+ supportsOnlyClassicSearch: state => state.sitePlan.supports_only_classic_search,
+ getUpgradeBillPeriod: state => state.sitePlan?.default_upgrade_bill_period,
+ supportsSearch: state =>
+ state.sitePlan.supports_instant_search || state.sitePlan.supports_only_classic_search,
+};
+
+export default sitePlanSelectors;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/user-data.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/user-data.js
new file mode 100644
index 00000000..6e5ac9d3
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/dashboard/store/selectors/user-data.js
@@ -0,0 +1,5 @@
+const userDataSelectors = {
+ getWpcomUser: state => state.userData?.currentUser?.wpcomUser,
+};
+
+export default userDataSelectors;
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/wpes/class-query-builder.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/wpes/class-query-builder.php
new file mode 100644
index 00000000..faa405ad
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/wpes/class-query-builder.php
@@ -0,0 +1,430 @@
+<?php
+/**
+ * Provides an interface for easily building a complex search query that
+ * combines multiple ranking signals.
+ *
+ * $bldr = new Query_Builder();
+ * $bldr->add_filter( ... );
+ * $bldr->add_filter( ... );
+ * $bldr->add_query( ... );
+ * $es_query = $bldr->build_query();
+ *
+ *
+ * All ES queries take a standard form with main query (with some filters),
+ * wrapped in a function_score
+ *
+ * Most functions are chainable, e.g. $bldr->add_filter( ... )->add_query( ... )->build_query();
+ *
+ * Bucketed queries use an aggregation to diversify results. eg a bunch
+ * of separate filters where to get different sets of results.
+ *
+ * @package automattic/jetpack-search
+ */
+
+// Disables comment checks.
+// phpcs:disable Squiz.Commenting
+
+namespace Automattic\Jetpack\Search\WPES;
+
+/**
+ * Query builder class.
+ */
+class Query_Builder {
+ /**
+ * ElasticSerach filters.
+ *
+ * @var array
+ */
+ protected $es_filters = array();
+
+ //
+ // Variables for handling custom boosting with function_score.
+ //
+ protected $functions = array();
+ protected $weighting_functions = array();
+ protected $decays = array();
+ protected $scripts = array();
+ protected $functions_max_boost = 2.0;
+ protected $functions_score_mode = 'multiply';
+ protected $functions_boost_mode = 'multiply';
+ protected $query_bool_boost = null;
+
+ //
+ // General aggregations for buckets and metrics.
+ //
+ protected $aggs_query = false;
+ protected $aggs = array();
+
+ //
+ // The set of top level text queries to combine.
+ //
+ protected $must_queries = array();
+ protected $should_queries = array();
+ protected $dis_max_queries = array();
+
+ protected $diverse_buckets_query = false;
+ protected $bucket_filters = array();
+ protected $bucket_sub_aggs = array();
+
+ public function get_langs() {
+ if ( isset( $this->langs ) ) {
+ return $this->langs;
+ }
+ return false;
+ }
+
+ //
+ // Methods for building a query.
+ //
+ public function add_filter( $filter ) {
+ $this->es_filters[] = $filter;
+
+ return $this;
+ }
+
+ public function add_query( $query, $type = 'must' ) {
+ switch ( $type ) {
+ case 'dis_max':
+ $this->dis_max_queries[] = $query;
+ break;
+
+ case 'should':
+ $this->should_queries[] = $query;
+ break;
+
+ case 'must':
+ default:
+ $this->must_queries[] = $query;
+ break;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add any weighting function to the query
+ *
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html
+ *
+ * @param $function array A function structure to apply to the query
+ *
+ * @return void
+ */
+ public function add_weighting_function( $function ) {
+ // check for danger.
+ if ( isset( $function['random_score'] ) ) {
+ return $this;
+ }
+ if ( isset( $function['script_score'] ) ) {
+ return $this;
+ }
+
+ $this->weighting_functions[] = $function;
+
+ return $this;
+ }
+
+ /**
+ * Add a scoring function to the query
+ *
+ * NOTE: For decays (linear, exp, or gauss), use Query_Builder::add_decay() instead
+ *
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html
+ *
+ * @param $function string name of the function
+ * @param $params array functions parameters
+ *
+ * @return void
+ */
+ public function add_function( $function, $params ) {
+ $this->functions[ $function ][] = $params;
+
+ return $this;
+ }
+
+ /**
+ * Add a decay function to score results
+ *
+ * This method should be used instead of Query_Builder::add_function() for decays, as the internal ES structure
+ * is slightly different for them.
+ *
+ * @see https://www.elastic.co/guide/en/elasticsearch/guide/current/decay-functions.html
+ *
+ * @param $function string name of the decay function - linear, exp, or gauss
+ * @param $params array The decay functions parameters, passed to ES directly
+ *
+ * @return void
+ */
+ public function add_decay( $function, $params ) {
+ $this->decays[ $function ][] = $params;
+
+ return $this;
+ }
+
+ /**
+ * Add a scoring mode to the query
+ *
+ * @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html
+ *
+ * @param $mode string name of how to score
+ *
+ * @return void
+ */
+ public function add_score_mode_to_functions( $mode = 'multiply' ) {
+ $this->functions_score_mode = $mode;
+
+ return $this;
+ }
+
+ public function add_boost_mode_to_functions( $mode = 'multiply' ) {
+ $this->functions_boost_mode = $mode;
+
+ return $this;
+ }
+
+ public function add_max_boost_to_functions( $boost ) {
+ $this->functions_max_boost = $boost;
+
+ return $this;
+ }
+
+ public function add_boost_to_query_bool( $boost ) {
+ $this->query_bool_boost = $boost;
+
+ return $this;
+ }
+
+ public function add_aggs( $aggs_name, $aggs ) {
+ $this->aggs_query = true;
+ $this->aggs[ $aggs_name ] = $aggs;
+
+ return $this;
+ }
+
+ public function set_all_aggs( $aggs ) {
+ $this->aggs_query = true;
+ $this->aggs = $aggs;
+
+ return $this;
+ }
+
+ public function add_aggs_sub_aggs( $aggs_name, $sub_aggs ) {
+ if ( ! array_key_exists( 'aggs', $this->aggs[ $aggs_name ] ) ) {
+ $this->aggs[ $aggs_name ]['aggs'] = array();
+ }
+ $this->aggs[ $aggs_name ]['aggs'] = $sub_aggs;
+
+ return $this;
+ }
+
+ public function add_bucketed_query( $name, $query ) {
+ $this->add_bucket_filter( $name, $query );
+
+ $this->add_query( $query, 'dis_max' );
+
+ return $this;
+ }
+
+ public function add_bucketed_terms( $name, $field, $terms, $boost = 1 ) {
+ if ( ! is_array( $terms ) ) {
+ $terms = array( $terms );
+ }
+
+ $this->add_bucket_filter(
+ $name,
+ array(
+ 'terms' => array(
+ $field => $terms,
+ ),
+ )
+ );
+
+ $this->add_query(
+ array(
+ 'constant_score' => array(
+ 'filter' => array(
+ 'terms' => array(
+ $field => $terms,
+ ),
+ ),
+ 'boost' => $boost,
+ ),
+ ),
+ 'dis_max'
+ );
+
+ return $this;
+ }
+
+ public function add_bucket_sub_aggs( $agg ) {
+ $this->bucket_sub_aggs = array_merge( $this->bucket_sub_aggs, $agg );
+
+ return $this;
+ }
+
+ protected function add_bucket_filter( $name, $filter ) {
+ $this->diverse_buckets_query = true;
+ $this->bucket_filters[ $name ] = $filter;
+ }
+
+ ////////////////////////////////////
+ // Building Final Query
+
+ /**
+ * Combine all the queries, functions, decays, scripts, and max_boost into an ES query
+ *
+ * @return array Array representation of the built ES query
+ */
+ public function build_query() {
+ $query = array();
+
+ //dis_max queries just become a single must query
+ if ( ! empty( $this->dis_max_queries ) ) {
+ $this->must_queries[] = array(
+ 'dis_max' => array(
+ 'queries' => $this->dis_max_queries,
+ ),
+ );
+ }
+
+ if ( empty( $this->must_queries ) ) {
+ $this->must_queries = array(
+ array(
+ 'match_all' => array(),
+ ),
+ );
+ }
+
+ if ( empty( $this->should_queries ) ) {
+ $query = array(
+ 'bool' => array(
+ 'must' => $this->must_queries,
+ ),
+ );
+ } else {
+ $query = array(
+ 'bool' => array(
+ 'must' => $this->must_queries,
+ 'should' => $this->should_queries,
+ ),
+ );
+ }
+
+ if ( ! is_null( $this->query_bool_boost ) && isset( $query['bool'] ) ) {
+ $query['bool']['boost'] = $this->query_bool_boost;
+ }
+
+ // If there are any function score adjustments, then combine those
+ if ( $this->functions || $this->decays || $this->scripts || $this->weighting_functions ) {
+ $weighting_functions = $this->weighting_functions;
+
+ if ( $this->functions ) {
+ foreach ( $this->functions as $function_type => $configs ) {
+ foreach ( $configs as $config ) {
+ foreach ( $config as $field => $params ) {
+ $func_arr = $params;
+
+ $func_arr['field'] = $field;
+
+ $weighting_functions[] = array(
+ $function_type => $func_arr,
+ );
+ }
+ }
+ }
+ }
+
+ if ( $this->decays ) {
+ foreach ( $this->decays as $decay_type => $configs ) {
+ foreach ( $configs as $config ) {
+ foreach ( $config as $field => $params ) {
+ $weighting_functions[] = array(
+ $decay_type => array(
+ $field => $params,
+ ),
+ );
+ }
+ }
+ }
+ }
+
+ if ( $this->scripts ) {
+ foreach ( $this->scripts as $script ) {
+ $weighting_functions[] = array(
+ 'script_score' => array(
+ 'script' => $script,
+ ),
+ );
+ }
+ }
+
+ $query = array(
+ 'function_score' => array(
+ 'query' => $query,
+ 'functions' => $weighting_functions,
+ 'max_boost' => $this->functions_max_boost,
+ 'score_mode' => $this->functions_score_mode,
+ 'boost_mode' => $this->functions_boost_mode,
+ ),
+ );
+ }
+
+ return $query;
+ }
+
+ /**
+ * Assemble the 'filter' portion of an ES query, from all registered filters
+ *
+ * @return array|null Combined ES filters, or null if none have been defined
+ */
+ public function build_filter() {
+ if ( empty( $this->es_filters ) ) {
+ $filter = null;
+ } elseif ( 1 === count( $this->es_filters ) ) {
+ $filter = $this->es_filters[0];
+ } else {
+ $filter = array(
+ 'and' => $this->es_filters,
+ );
+ }
+
+ return $filter;
+ }
+
+ /**
+ * Assemble the 'aggregation' portion of an ES query, from all general aggregations.
+ *
+ * @return array An aggregation query as an array of topics, filters, and bucket names
+ */
+ public function build_aggregation() {
+ if ( empty( $this->bucket_sub_aggs ) && empty( $this->aggs_query ) ) {
+ return array();
+ }
+
+ if ( ! $this->diverse_buckets_query && empty( $this->aggs_query ) ) {
+ return $this->bucket_sub_aggs;
+ }
+
+ $aggregations = array(
+ 'topics' => array(
+ 'filters' => array(
+ 'filters' => array(),
+ ),
+ ),
+ );
+
+ if ( ! empty( $this->bucket_sub_aggs ) ) {
+ $aggregations['topics']['aggs'] = $this->bucket_sub_aggs;
+ }
+
+ foreach ( $this->bucket_filters as $bucket_name => $filter ) {
+ $aggregations['topics']['filters']['filters'][ $bucket_name ] = $filter;
+ }
+
+ if ( ! empty( $this->aggs_query ) ) {
+ $aggregations = $this->aggs;
+ }
+
+ return $aggregations;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/wpes/class-query-parser.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/wpes/class-query-parser.php
new file mode 100644
index 00000000..8bd3970d
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-search/src/wpes/class-query-parser.php
@@ -0,0 +1,703 @@
+<?php
+/**
+ * Parse a pure text query into WordPress Elasticsearch query. This builds on
+ * the Query_Builder() to provide search query parsing.
+ *
+ * The key part of this parser is taking a user's query string typed into a box
+ * and converting it into an ES search query.
+ *
+ * This varies by application, but roughly it means extracting some parts of the query
+ * (authors, tags, and phrases) that are treated as a filter. Then taking the
+ * remaining words and building the correct query (possibly with prefix searching
+ * if we are doing search as you type)
+ *
+ * This class only supports ES 2.x+
+ *
+ * Disables comment chehcks.
+ * phpcs:disable Squiz.Commenting
+ *
+ * This parser builds queries of the form:
+ * bool:
+ * must:
+ * AND match of a single field (ideally an edgengram field)
+ * filter:
+ * filter clauses from context (eg @gibrown, #news, etc)
+ * should:
+ * boosting of results by various fields
+ *
+ * Features supported:
+ * - search as you type
+ * - phrases
+ * - supports querying across multiple languages at once
+ *
+ * @package automattic/jetpack-search
+ */
+
+namespace Automattic\Jetpack\Search\WPES;
+
+/**
+ * Query parser class.
+ */
+class Query_Parser extends Query_Builder {
+ protected $orig_query = '';
+ protected $current_query = '';
+ protected $langs;
+ protected $avail_langs = array( 'ar', 'bg', 'ca', 'cs', 'da', 'de', 'el', 'en', 'es', 'eu', 'fa', 'fi', 'fr', 'he', 'hi', 'hu', 'hy', 'id', 'it', 'ja', 'ko', 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh' );
+
+ public function __construct( $user_query, $langs ) {
+ $this->orig_query = $user_query;
+ $this->current_query = $this->orig_query;
+ $this->langs = $this->norm_langs( $langs );
+ }
+
+ protected $extracted_phrases = array();
+
+ public function get_current_query() {
+ return $this->current_query;
+ }
+
+ public function set_current_query( $q ) {
+ $this->current_query = $q;
+ }
+
+ ///////////////////////////////////////////////////////
+ // Methods for Building arrays of multilingual fields
+
+ /*
+ * Normalize language codes
+ */
+ public function norm_langs( $langs ) {
+ $lst = array();
+ foreach ( $langs as $l ) {
+ $l = strtok( $l, '-_' );
+ if ( in_array( $l, $this->avail_langs, true ) ) {
+ $lst[ $l ] = true;
+ } else {
+ $lst['default'] = true;
+ }
+ }
+ return array_keys( $lst );
+ }
+
+ public function get_lang_field_suffix() {
+ if ( ! is_array( $this->langs ) || empty( $this->langs ) ) {
+ return;
+ }
+
+ // Returns the first language only
+ return $this->langs[0];
+ }
+
+ /*
+ * Take a list of field prefixes and expand them for multi-lingual
+ * with the provided boostings.
+ */
+ public function merge_ml_fields( $fields2boosts, $additional_fields ) {
+ $flds = array();
+ foreach ( $fields2boosts as $f => $b ) {
+ foreach ( $this->langs as $l ) {
+ $flds[] = $f . '.' . $l . '^' . $b;
+ }
+ }
+ foreach ( $additional_fields as $f ) {
+ $flds[] = $f;
+ }
+ return $flds;
+ }
+
+ ////////////////////////////////////
+ // Extract Fields for Filtering on
+
+ /*
+ * Extract any @mentions from the user query
+ * use them as a filter if we can find a wp.com id
+ * otherwise use them as a
+ *
+ * args:
+ * wpcom_id_field: wp.com id field
+ * must_query_fields: array of fields to search for matching results (optional)
+ * boost_query_fields: array of fields to search in for boosting results (optional)
+ * prefixes: array of prefixes that the user can use to indicate an author
+ *
+ * returns true/false of whether any were found
+ *
+ * See also: https://github.com/twitter/twitter-text/blob/master/java/src/com/twitter/Regex.java
+ */
+ public function author_field_filter( $args ) {
+ $defaults = array(
+ 'wpcom_id_field' => 'author_id',
+ 'must_query_fields' => null,
+ 'boost_query_fields' => null,
+ 'prefixes' => array( '@' ),
+ );
+ $args = wp_parse_args( $args, $defaults );
+
+ $names = array();
+ foreach ( $args['prefixes'] as $p ) {
+ $found = $this->get_fields( $p );
+ if ( $found ) {
+ foreach ( $found as $f ) {
+ $names[] = $f;
+ }
+ }
+ }
+
+ if ( empty( $names ) ) {
+ return false;
+ }
+
+ foreach ( $args['prefixes'] as $p ) {
+ $this->remove_fields( $p );
+ }
+
+ $user_ids = array();
+
+ //loop through the matches and separate into filters and queries
+ foreach ( $names as $n ) {
+ //check for exact match on login
+ $userdata = get_user_by( 'login', strtolower( $n ) );
+ $filtering = false;
+ if ( $userdata ) {
+ $user_ids[ $userdata->ID ] = true;
+ $filtering = true;
+ }
+
+ $is_phrase = false;
+ if ( preg_match( '/"/', $n ) ) {
+ $is_phrase = true;
+ $n = preg_replace( '/"/', '', $n );
+ }
+
+ if ( ! empty( $args['must_query_fields'] ) && ! $filtering ) {
+ if ( $is_phrase ) {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['must_query_fields'],
+ 'query' => $n,
+ 'type' => 'phrase',
+ ),
+ )
+ );
+ } else {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['must_query_fields'],
+ 'query' => $n,
+ ),
+ )
+ );
+ }
+ }
+
+ if ( ! empty( $args['boost_query_fields'] ) ) {
+ if ( $is_phrase ) {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['boost_query_fields'],
+ 'query' => $n,
+ 'type' => 'phrase',
+ ),
+ ),
+ 'should'
+ );
+ } else {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['boost_query_fields'],
+ 'query' => $n,
+ ),
+ ),
+ 'should'
+ );
+ }
+ }
+ }
+
+ if ( ! empty( $user_ids ) ) {
+ $user_ids = array_keys( $user_ids );
+ $this->add_filter( array( 'terms' => array( $args['wpcom_id_field'] => $user_ids ) ) );
+ }
+
+ return true;
+ }
+
+ /*
+ * Extract any prefix followed by text use them as a must clause,
+ * and optionally as a boost to the should query
+ * This can be used for hashtags. eg #News, or #"current events",
+ * but also works for any arbitrary field. eg from:Greg
+ *
+ * args:
+ * must_query_fields: array of fields that must match the tag (optional)
+ * boost_query_fields: array of fields to boost search on (optional)
+ * prefixes: array of prefixes that the user can use to indicate a tag
+ *
+ * returns true/false of whether any were found
+ *
+ */
+ public function text_field_filter( $args ) {
+ $defaults = array(
+ 'must_query_fields' => array( 'tag.name' ),
+ 'boost_query_fields' => array( 'tag.name' ),
+ 'prefixes' => array( '#' ),
+ );
+ $args = wp_parse_args( $args, $defaults );
+
+ $tags = array();
+ foreach ( $args['prefixes'] as $p ) {
+ $found = $this->get_fields( $p );
+ if ( $found ) {
+ foreach ( $found as $f ) {
+ $tags[] = $f;
+ }
+ }
+ }
+
+ if ( empty( $tags ) ) {
+ return false;
+ }
+
+ foreach ( $args['prefixes'] as $p ) {
+ $this->remove_fields( $p );
+ }
+
+ foreach ( $tags as $t ) {
+ $is_phrase = false;
+ if ( preg_match( '/"/', $t ) ) {
+ $is_phrase = true;
+ $t = preg_replace( '/"/', '', $t );
+ }
+
+ if ( ! empty( $args['must_query_fields'] ) ) {
+ if ( $is_phrase ) {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['must_query_fields'],
+ 'query' => $t,
+ 'type' => 'phrase',
+ ),
+ )
+ );
+ } else {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['must_query_fields'],
+ 'query' => $t,
+ ),
+ )
+ );
+ }
+ }
+
+ if ( ! empty( $args['boost_query_fields'] ) ) {
+ if ( $is_phrase ) {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['boost_query_fields'],
+ 'query' => $t,
+ 'type' => 'phrase',
+ ),
+ ),
+ 'should'
+ );
+ } else {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['boost_query_fields'],
+ 'query' => $t,
+ ),
+ ),
+ 'should'
+ );
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /*
+ * Extract anything surrounded by quotes or if there is an opening quote
+ * that is not complete, and add them to the query as a phrase query.
+ * Quotes can be either '' or ""
+ *
+ * args:
+ * must_query_fields: array of fields that must match the phrases
+ * boost_query_fields: array of fields to boost the phrases on (optional)
+ *
+ * returns true/false of whether any were found
+ *
+ */
+ public function phrase_filter( $args ) {
+ $defaults = array(
+ 'must_query_fields' => array( 'all_content' ),
+ 'boost_query_fields' => array( 'title' ),
+ );
+ $args = wp_parse_args( $args, $defaults );
+
+ $phrases = array();
+ if ( preg_match_all( '/"([^"]+)"/', $this->current_query, $matches ) ) {
+ foreach ( $matches[1] as $match ) {
+ $phrases[] = $match;
+ }
+ $this->current_query = preg_replace( '/"([^"]+)"/', '', $this->current_query );
+ }
+
+ if ( preg_match_all( "/'([^']+)'/", $this->current_query, $matches ) ) {
+ foreach ( $matches[1] as $match ) {
+ $phrases[] = $match;
+ }
+ $this->current_query = preg_replace( "/'([^']+)'/", '', $this->current_query );
+ }
+
+ //look for a final, uncompleted phrase
+ $phrase_prefix = false;
+ if ( preg_match_all( '/"([^"]+)$/', $this->current_query, $matches ) ) {
+ $phrase_prefix = $matches[1][0];
+ $this->current_query = preg_replace( '/"([^"]+)$/', '', $this->current_query );
+ }
+ if ( preg_match_all( "/(?:'\B|\B')([^']+)$/", $this->current_query, $matches ) ) {
+ $phrase_prefix = $matches[1][0];
+ $this->current_query = preg_replace( "/(?:'\B|\B')([^']+)$/", '', $this->current_query );
+ }
+
+ if ( $phrase_prefix ) {
+ $phrases[] = $phrase_prefix;
+ }
+ if ( empty( $phrases ) ) {
+ return false;
+ }
+
+ foreach ( $phrases as $p ) {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['must_query_fields'],
+ 'query' => $p,
+ 'type' => 'phrase',
+ ),
+ )
+ );
+
+ if ( ! empty( $args['boost_query_fields'] ) ) {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['boost_query_fields'],
+ 'query' => $p,
+ 'operator' => 'and',
+ ),
+ ),
+ 'should'
+ );
+ }
+ }
+
+ return true;
+ }
+
+ /*
+ * Query fields based on the remaining parts of the query
+ * This could be the final AND part of the query terms to match, or it
+ * could be boosting certain elements of the query
+ *
+ * args:
+ * must_query_fields: array of fields that must match the remaining terms (optional)
+ * boost_query_fields: array of fields to boost the remaining terms on (optional)
+ *
+ */
+ public function remaining_query( $args ) {
+ $defaults = array(
+ 'must_query_fields' => null,
+ 'boost_query_fields' => null,
+ 'boost_operator' => 'and',
+ 'boost_query_type' => 'best_fields',
+ );
+ $args = wp_parse_args( $args, $defaults );
+
+ if ( empty( $this->current_query ) || ctype_space( $this->current_query ) ) {
+ return;
+ }
+
+ if ( ! empty( $args['must_query_fields'] ) ) {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['must_query_fields'],
+ 'query' => $this->current_query,
+ 'operator' => 'and',
+ ),
+ )
+ );
+ }
+
+ if ( ! empty( $args['boost_query_fields'] ) ) {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['boost_query_fields'],
+ 'query' => $this->current_query,
+ 'operator' => $args['boost_operator'],
+ 'type' => $args['boost_query_type'],
+ ),
+ ),
+ 'should'
+ );
+ }
+
+ }
+
+ /*
+ * Query fields using a prefix query (alphabetical expansions on the index).
+ * This is not recommended. Slower performance and worse relevancy.
+ *
+ * (UNTESTED! Copied from old prefix expansion code)
+ *
+ * args:
+ * must_query_fields: array of fields that must match the remaining terms (optional)
+ * boost_query_fields: array of fields to boost the remaining terms on (optional)
+ *
+ */
+ public function remaining_prefix_query( $args ) {
+ $defaults = array(
+ 'must_query_fields' => array( 'all_content' ),
+ 'boost_query_fields' => array( 'title' ),
+ 'boost_operator' => 'and',
+ 'boost_query_type' => 'best_fields',
+ );
+ $args = wp_parse_args( $args, $defaults );
+
+ if ( empty( $this->current_query ) || ctype_space( $this->current_query ) ) {
+ return;
+ }
+
+ //////////////////////////////////
+ // Example cases to think about:
+ // "elasticse"
+ // "elasticsearch"
+ // "elasticsearch "
+ // "elasticsearch lucen"
+ // "elasticsearch lucene"
+ // "the future" - note the stopword which will match nothing!
+ // "F1" - an exact match that also has tons of expansions
+ // "こんにちは" ja "hello"
+ // "こんにちは友人" ja "hello friend" - we just rely on the prefix phrase and ES to split words
+ // - this could still be better I bet. Maybe we need to analyze with ES first?
+ //
+
+ /////////////////////////////
+ //extract pieces of query
+ // eg: "PREFIXREMAINDER PREFIXWORD"
+ // "elasticsearch lucen"
+
+ $prefix_word = false;
+ $prefix_remainder = false;
+ if ( preg_match_all( '/([^ ]+)$/', $this->current_query, $matches ) ) {
+ $prefix_word = $matches[1][0];
+ }
+
+ $prefix_remainder = preg_replace( '/([^ ]+)$/', '', $this->current_query );
+ if ( ctype_space( $prefix_remainder ) ) {
+ $prefix_remainder = false;
+ }
+
+ if ( ! $prefix_word ) {
+ //Space at the end of the query, so skip using a prefix query
+ if ( ! empty( $args['must_query_fields'] ) ) {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['must_query_fields'],
+ 'query' => $this->current_query,
+ 'operator' => 'and',
+ ),
+ )
+ );
+ }
+
+ if ( ! empty( $args['boost_query_fields'] ) ) {
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['boost_query_fields'],
+ 'query' => $this->current_query,
+ 'operator' => $args['boost_operator'],
+ 'type' => $args['boost_query_type'],
+ ),
+ ),
+ 'should'
+ );
+ }
+ } else {
+
+ //must match the prefix word and the prefix remainder
+ if ( ! empty( $args['must_query_fields'] ) ) {
+ //need to do an OR across a few fields to handle all cases
+ $must_q = array(
+ 'bool' => array(
+ 'should' => array(),
+ 'minimum_should_match' => 1,
+ ),
+ );
+
+ //treat all words as an exact search (boosts complete word like "news"
+ //from prefixes of "newspaper")
+ $must_q['bool']['should'][] = array(
+ 'multi_match' => array(
+ 'fields' => $this->all_fields,
+ // NOTE: This line has been disabled since $full_text is not available.
+ // 'query' => $full_text,
+ 'operator' => 'and',
+ 'type' => 'cross_fields',
+ ),
+ );
+
+ //always optimistically try and match the full text as a phrase
+ //prefix "the futu" should try to match "the future"
+ //otherwise the first stopword kinda breaks
+ //This also works as the prefix match for a single word "elasticsea"
+ $must_q['bool']['should'][] = array(
+ 'multi_match' => array(
+ 'fields' => $this->phrase_fields,
+ // NOTE: This line has been disabled since $full_text is not available.
+ // 'query' => $full_text,
+ 'operator' => 'and',
+ 'type' => 'phrase_prefix',
+ 'max_expansions' => 100,
+ ),
+ );
+
+ if ( $prefix_remainder ) {
+ //Multiple words found, so treat each word on its own and not just as
+ //a part of a phrase
+ //"elasticsearch lucen" => "elasticsearch" exact AND "lucen" prefix
+ $must_q['bool']['should'][] = array(
+ 'bool' => array(
+ 'must' => array(
+ array(
+ 'multi_match' => array(
+ 'fields' => $this->phrase_fields,
+ 'query' => $prefix_word,
+ 'operator' => 'and',
+ 'type' => 'phrase_prefix',
+ 'max_expansions' => 100,
+ ),
+ ),
+ array(
+ 'multi_match' => array(
+ 'fields' => $this->all_fields,
+ 'query' => $prefix_remainder,
+ 'operator' => 'and',
+ 'type' => 'cross_fields',
+ ),
+ ),
+ ),
+ ),
+ );
+ }
+
+ $this->add_query( $must_q );
+ }
+
+ //Now add any boosting of the query
+ if ( ! empty( $args['boost_query_fields'] ) ) {
+ //treat all words as an exact search (boosts complete word like "news"
+ //from prefixes of "newspaper")
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['boost_query_fields'],
+ 'query' => $this->current_query,
+ 'operator' => $args['boost_query_operator'],
+ 'type' => $args['boost_query_type'],
+ ),
+ ),
+ 'should'
+ );
+
+ //optimistically boost the full phrase prefix match
+ $this->add_query(
+ array(
+ 'multi_match' => array(
+ 'fields' => $args['boost_query_fields'],
+ 'query' => $this->current_query,
+ 'operator' => 'and',
+ 'type' => 'phrase_prefix',
+ 'max_expansions' => 100,
+ ),
+ )
+ );
+ }
+ }
+ }
+
+ /*
+ * Boost results based on the lang probability overlaps
+ *
+ * args:
+ * langs2prob: list of languages to search in with associated boosts
+ */
+ public function boost_lang_probs( $langs2prob ) {
+ foreach ( $langs2prob as $p ) {
+ $this->add_function(
+ 'field_value_factor',
+ array(
+ 'modifier' => 'none',
+ 'factor' => $p,
+ 'missing' => 0.01, //1% chance doc did not have right lang detected
+ )
+ );
+ }
+ }
+
+ ////////////////////////////////////
+ // Helper Methods
+
+ //Get the text after some prefix. eg @gibrown, or @"Greg Brown"
+ protected function get_fields( $field_prefix ) {
+ $regex = '/' . $field_prefix . '(("[^"]+")|([^\\p{Z}]+))/';
+ if ( preg_match_all( $regex, $this->current_query, $match ) ) {
+ return $match[1];
+ }
+ return false;
+ }
+
+ //Remove the prefix and text from the query
+ protected function remove_fields( $field_name ) {
+ $regex = '/' . $field_name . '(("[^"]+")|([^\\p{Z}]+))/';
+ $this->current_query = preg_replace( $regex, '', $this->current_query );
+ }
+
+ //Best effort string truncation that splits on word breaks
+ protected function truncate_string( $string, $limit, $break = ' ' ) {
+ if ( mb_strwidth( $string ) <= $limit ) {
+ return $string;
+ }
+
+ // walk backwards from $limit to find first break
+ $breakpoint = $limit;
+ $broken = false;
+ while ( $breakpoint > 0 ) {
+ if ( mb_strimwidth( $string, $breakpoint, 1 ) === $break ) {
+ $string = mb_strimwidth( $string, 0, $breakpoint );
+ $broken = true;
+ break;
+ }
+ $breakpoint--;
+ }
+ // if we weren't able to find a break, need to chop mid-word
+ if ( ! $broken ) {
+ $string = mb_strimwidth( $string, 0, $limit );
+ }
+ return $string;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/CHANGELOG.md
new file mode 100644
index 00000000..b05aac40
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/CHANGELOG.md
@@ -0,0 +1,177 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.9.5] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+
+## [1.9.4] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.9.3] - 2021-11-22
+### Changed
+- Updated package dependencies
+
+## [1.9.2] - 2021-11-16
+### Changed
+- Add a function_exists check before calling wp_get_environment_type
+
+## [1.9.1] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.9.0] - 2021-10-26
+### Added
+- Added Host class for reporting known hosting environment information.
+
+## [1.8.4] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.8.3] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.8.2] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.8.1] - 2021-08-30
+### Changed
+- Run composer update on test-php command instead of phpunit
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- update annotations versions
+
+## [1.8.0] - 2021-06-15
+### Changed
+- Update callback to Jetpack to new Identity_Crisis class.
+
+## [1.7.6] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.7.5] - 2021-04-27
+### Deprecated
+- Deprecates is_no_user_testing_mode
+
+## [1.7.4] - 2021-04-08
+### Changed
+- Packaging and build changes, no change to the package itself.
+
+## [1.7.3] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.7.2] - 2021-02-05
+
+- CI: Make tests more generic
+
+## [1.7.1] - 2021-01-20
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.7.0] - 2020-12-14
+
+- Update dependency brain/monkey to v2.6.0
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.6.0] - 2020-11-23
+
+- Status: Introduce get_site_suffix method
+- Status: Fix test failure
+- Status: Improve the staging site detection
+- General: update minimum required version to WordPress 5.5
+- Add the no_user_testing mode
+- Status: Add a couple of test cases for staging site detection
+- Update dependency brain/monkey to v2.5.0
+- Updated PHPCS: Packages and Debugger
+
+## [1.5.0] - 2020-10-13
+
+- Also use Core `wp_get_environment_type` for local
+
+## [1.4.0] - 2020-08-13
+
+- CI: Try collect js coverage
+
+## [1.3.0] - 2020-07-28
+
+- Core Compat: Site Environment
+
+## [1.2.0] - 2020-06-22
+
+- PHPCS: Clean up the packages
+- Staging Sites: add newspack staging to the list of known providers
+
+## [1.1.1] - 2020-01-27
+
+- Pin dependency brain/monkey to 2.4.0
+
+## [1.1.0] - 2020-01-14
+
+- Packages: Various improvements for wp.com or self-contained consumers
+
+## [1.0.4] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## [1.0.3] - 2019-10-28
+
+- Packages: Add gitattributes files to all packages that need th…
+
+## [1.0.2] - 2019-10-23
+
+- Use spread operator instead of func_get_args
+
+## [1.0.1] - 2019-09-20
+
+- Docs: Unify usage of @package phpdoc tags
+
+## 1.0.0 - 2019-09-14
+
+- Packages: Introduce a status package
+
+[1.9.5]: https://github.com/Automattic/jetpack-status/compare/v1.9.4...v1.9.5
+[1.9.4]: https://github.com/Automattic/jetpack-status/compare/v1.9.3...v1.9.4
+[1.9.3]: https://github.com/Automattic/jetpack-status/compare/v1.9.2...v1.9.3
+[1.9.2]: https://github.com/Automattic/jetpack-status/compare/v1.9.1...v1.9.2
+[1.9.1]: https://github.com/Automattic/jetpack-status/compare/v1.9.0...v1.9.1
+[1.9.0]: https://github.com/Automattic/jetpack-status/compare/v1.8.4...v1.9.0
+[1.8.4]: https://github.com/Automattic/jetpack-status/compare/v1.8.3...v1.8.4
+[1.8.3]: https://github.com/Automattic/jetpack-status/compare/v1.8.2...v1.8.3
+[1.8.2]: https://github.com/Automattic/jetpack-status/compare/v1.8.1...v1.8.2
+[1.8.1]: https://github.com/Automattic/jetpack-status/compare/v1.8.0...v1.8.1
+[1.8.0]: https://github.com/Automattic/jetpack-status/compare/v1.7.6...v1.8.0
+[1.7.6]: https://github.com/Automattic/jetpack-status/compare/v1.7.5...v1.7.6
+[1.7.5]: https://github.com/Automattic/jetpack-status/compare/v1.7.4...v1.7.5
+[1.7.4]: https://github.com/Automattic/jetpack-status/compare/v1.7.3...v1.7.4
+[1.7.3]: https://github.com/Automattic/jetpack-status/compare/v1.7.2...v1.7.3
+[1.7.2]: https://github.com/Automattic/jetpack-status/compare/v1.7.1...v1.7.2
+[1.7.1]: https://github.com/Automattic/jetpack-status/compare/v1.7.0...v1.7.1
+[1.7.0]: https://github.com/Automattic/jetpack-status/compare/v1.6.0...v1.7.0
+[1.6.0]: https://github.com/Automattic/jetpack-status/compare/v1.5.0...v1.6.0
+[1.5.0]: https://github.com/Automattic/jetpack-status/compare/v1.4.0...v1.5.0
+[1.4.0]: https://github.com/Automattic/jetpack-status/compare/v1.3.0...v1.4.0
+[1.3.0]: https://github.com/Automattic/jetpack-status/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-status/compare/v1.1.1...v1.2.0
+[1.1.1]: https://github.com/Automattic/jetpack-status/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-status/compare/v1.0.4...v1.1.0
+[1.0.4]: https://github.com/Automattic/jetpack-status/compare/v1.0.3...v1.0.4
+[1.0.3]: https://github.com/Automattic/jetpack-status/compare/v1.0.2...v1.0.3
+[1.0.2]: https://github.com/Automattic/jetpack-status/compare/v1.0.1...v1.0.2
+[1.0.1]: https://github.com/Automattic/jetpack-status/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/src/class-host.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/src/class-host.php
new file mode 100644
index 00000000..a0765f6d
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/src/class-host.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * A hosting provide class for Jetpack.
+ *
+ * @package automattic/jetpack-status
+ */
+
+namespace Automattic\Jetpack\Status;
+
+use Automattic\Jetpack\Constants;
+
+/**
+ * Hosting provider class.
+ */
+class Host {
+ /**
+ * Determine if this site is an WordPress.com on Atomic site or not looking first at the 'at_options' option.
+ * As a fallback, check for presence of wpcomsh plugin to determine if a current site has undergone AT.
+ *
+ * @since $$next_version$$
+ *
+ * @return bool
+ */
+ public function is_woa_site() {
+ $at_options = get_option( 'at_options', array() );
+ return $this->is_atomic_platform() && ( ! empty( $at_options ) || Constants::is_true( 'WPCOMSH__PLUGIN_FILE' ) );
+ }
+
+ /**
+ * Determine if site is hosted on the Atomic hosting platform.
+ *
+ * @since $$next_version$$
+ *
+ * @return bool;
+ */
+ public function is_atomic_platform() {
+ return Constants::is_true( 'ATOMIC_SITE_ID' ) && Constants::is_true( 'ATOMIC_CLIENT_ID' );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/src/class-status.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/src/class-status.php
new file mode 100644
index 00000000..ea161122
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-status/src/class-status.php
@@ -0,0 +1,288 @@
+<?php
+/**
+ * A status class for Jetpack.
+ *
+ * @package automattic/jetpack-status
+ */
+
+namespace Automattic\Jetpack;
+
+use WPCOM_Masterbar;
+
+/**
+ * Class Automattic\Jetpack\Status
+ *
+ * Used to retrieve information about the current status of Jetpack and the site overall.
+ */
+class Status {
+ /**
+ * Is Jetpack in development (offline) mode?
+ *
+ * @deprecated 1.3.0 Use Status->is_offline_mode().
+ *
+ * @return bool Whether Jetpack's offline mode is active.
+ */
+ public function is_development_mode() {
+ _deprecated_function( __FUNCTION__, '1.3.0', 'Automattic\Jetpack\Status->is_offline_mode' );
+ return $this->is_offline_mode();
+ }
+
+ /**
+ * Is Jetpack in offline mode?
+ *
+ * This was formerly called "Development Mode", but sites "in development" aren't always offline/localhost.
+ *
+ * @since 1.3.0
+ *
+ * @return bool Whether Jetpack's offline mode is active.
+ */
+ public function is_offline_mode() {
+ $offline_mode = false;
+
+ if ( defined( '\\JETPACK_DEV_DEBUG' ) ) {
+ $offline_mode = constant( '\\JETPACK_DEV_DEBUG' );
+ } elseif ( defined( '\\WP_LOCAL_DEV' ) ) {
+ $offline_mode = constant( '\\WP_LOCAL_DEV' );
+ } elseif ( $this->is_local_site() ) {
+ $offline_mode = true;
+ }
+
+ /**
+ * Filters Jetpack's offline mode.
+ *
+ * @see https://jetpack.com/support/development-mode/
+ * @todo Update documentation ^^.
+ *
+ * @since 1.1.1
+ * @since-jetpack 2.2.1
+ * @deprecated 1.3.0
+ *
+ * @param bool $offline_mode Is Jetpack's offline mode active.
+ */
+ $offline_mode = (bool) apply_filters_deprecated( 'jetpack_development_mode', array( $offline_mode ), '1.3.0', 'jetpack_offline_mode' );
+
+ /**
+ * Filters Jetpack's offline mode.
+ *
+ * @see https://jetpack.com/support/development-mode/
+ * @todo Update documentation ^^.
+ *
+ * @since 1.3.0
+ *
+ * @param bool $offline_mode Is Jetpack's offline mode active.
+ */
+ $offline_mode = (bool) apply_filters( 'jetpack_offline_mode', $offline_mode );
+
+ return $offline_mode;
+ }
+
+ /**
+ * Is Jetpack in "No User test mode"?
+ *
+ * This will make Jetpack act as if there were no connected users, but only a site connection (aka blog token)
+ *
+ * @since 1.6.0
+ * @deprecated 1.7.5 Since this version, Jetpack connection is considered active after registration, making no_user_testing_mode obsolete.
+ *
+ * @return bool Whether Jetpack's No User Testing Mode is active.
+ */
+ public function is_no_user_testing_mode() {
+ _deprecated_function( __METHOD__, '1.7.5' );
+ return true;
+ }
+
+ /**
+ * Whether this is a system with a multiple networks.
+ * Implemented since there is no core is_multi_network function.
+ * Right now there is no way to tell which network is the dominant network on the system.
+ *
+ * @return boolean
+ */
+ public function is_multi_network() {
+ global $wpdb;
+
+ // If we don't have a multi site setup no need to do any more.
+ if ( ! is_multisite() ) {
+ return false;
+ }
+
+ $num_sites = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->site}" );
+ if ( $num_sites > 1 ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Whether the current site is single user site.
+ *
+ * @return bool
+ */
+ public function is_single_user_site() {
+ global $wpdb;
+
+ $some_users = get_transient( 'jetpack_is_single_user' );
+ if ( false === $some_users ) {
+ $some_users = $wpdb->get_var( "SELECT COUNT(*) FROM (SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}capabilities' LIMIT 2) AS someusers" );
+ set_transient( 'jetpack_is_single_user', (int) $some_users, 12 * HOUR_IN_SECONDS );
+ }
+ return 1 === (int) $some_users;
+ }
+
+ /**
+ * If the site is a local site.
+ *
+ * @since 1.3.0
+ *
+ * @return bool
+ */
+ public function is_local_site() {
+ // Check for localhost and sites using an IP only first.
+ $is_local = site_url() && false === strpos( site_url(), '.' );
+
+ // @todo Remove function_exists when the package has a documented minimum WP version.
+ // Use Core's environment check, if available. Added in 5.5.0 / 5.5.1 (for `local` return value).
+ if ( function_exists( 'wp_get_environment_type' ) && 'local' === wp_get_environment_type() ) {
+ $is_local = true;
+ }
+
+ // Then check for usual usual domains used by local dev tools.
+ $known_local = array(
+ '#\.local$#i',
+ '#\.localhost$#i',
+ '#\.test$#i',
+ '#\.docksal$#i', // Docksal.
+ '#\.docksal\.site$#i', // Docksal.
+ '#\.dev\.cc$#i', // ServerPress.
+ '#\.lndo\.site$#i', // Lando.
+ );
+
+ if ( ! $is_local ) {
+ foreach ( $known_local as $url ) {
+ if ( preg_match( $url, site_url() ) ) {
+ $is_local = true;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Filters is_local_site check.
+ *
+ * @since 1.3.0
+ *
+ * @param bool $is_local If the current site is a local site.
+ */
+ return apply_filters( 'jetpack_is_local_site', $is_local );
+ }
+
+ /**
+ * If is a staging site.
+ *
+ * @todo Add IDC detection to a package.
+ *
+ * @return bool
+ */
+ public function is_staging_site() {
+ // @todo Remove function_exists when the package has a documented minimum WP version.
+ // Core's wp_get_environment_type allows for a few specific options. We should default to bowing out gracefully for anything other than production or local.
+ $is_staging = function_exists( 'wp_get_environment_type' ) && ! in_array( wp_get_environment_type(), array( 'production', 'local' ), true );
+
+ $known_staging = array(
+ 'urls' => array(
+ '#\.staging\.wpengine\.com$#i', // WP Engine.
+ '#\.staging\.kinsta\.com$#i', // Kinsta.com.
+ '#\.kinsta\.cloud$#i', // Kinsta.com.
+ '#\.stage\.site$#i', // DreamPress.
+ '#\.newspackstaging\.com$#i', // Newspack.
+ '#\.pantheonsite\.io$#i', // Pantheon.
+ '#\.flywheelsites\.com$#i', // Flywheel.
+ '#\.flywheelstaging\.com$#i', // Flywheel.
+ '#\.cloudwaysapps\.com$#i', // Cloudways.
+ '#\.azurewebsites\.net$#i', // Azure.
+ '#\.wpserveur\.net$#i', // WPServeur.
+ '#\-liquidwebsites\.com$#i', // Liquidweb.
+ ),
+ 'constants' => array(
+ 'IS_WPE_SNAPSHOT', // WP Engine.
+ 'KINSTA_DEV_ENV', // Kinsta.com.
+ 'WPSTAGECOACH_STAGING', // WP Stagecoach.
+ 'JETPACK_STAGING_MODE', // Generic.
+ 'WP_LOCAL_DEV', // Generic.
+ ),
+ );
+ /**
+ * Filters the flags of known staging sites.
+ *
+ * @since 1.1.1
+ * @since-jetpack 3.9.0
+ *
+ * @param array $known_staging {
+ * An array of arrays that each are used to check if the current site is staging.
+ * @type array $urls URLs of staging sites in regex to check against site_url.
+ * @type array $constants PHP constants of known staging/developement environments.
+ * }
+ */
+ $known_staging = apply_filters( 'jetpack_known_staging', $known_staging );
+
+ if ( isset( $known_staging['urls'] ) ) {
+ foreach ( $known_staging['urls'] as $url ) {
+ if ( preg_match( $url, wp_parse_url( site_url(), PHP_URL_HOST ) ) ) {
+ $is_staging = true;
+ break;
+ }
+ }
+ }
+
+ if ( isset( $known_staging['constants'] ) ) {
+ foreach ( $known_staging['constants'] as $constant ) {
+ if ( defined( $constant ) && constant( $constant ) ) {
+ $is_staging = true;
+ }
+ }
+ }
+
+ // Last, let's check if sync is erroring due to an IDC. If so, set the site to staging mode.
+ if ( ! $is_staging && method_exists( 'Automattic\\Jetpack\\Identity_Crisis', 'validate_sync_error_idc_option' ) && \Automattic\Jetpack\Identity_Crisis::validate_sync_error_idc_option() ) {
+ $is_staging = true;
+ }
+
+ /**
+ * Filters is_staging_site check.
+ *
+ * @since 1.1.1
+ * @since-jetpack 3.9.0
+ *
+ * @param bool $is_staging If the current site is a staging site.
+ */
+ return apply_filters( 'jetpack_is_staging_site', $is_staging );
+ }
+
+ /**
+ * Returns the site slug suffix to be used as part of Calypso URLs.
+ *
+ * Strips http:// or https:// from a url, replaces forward slash with ::.
+ *
+ * @since 1.6.0
+ *
+ * @param string $url Optional. URL to build the site suffix from. Default: Home URL.
+ *
+ * @return string
+ */
+ public function get_site_suffix( $url = '' ) {
+ // On WordPress.com, site suffixes are a bit different.
+ if ( method_exists( 'WPCOM_Masterbar', 'get_calypso_site_slug' ) ) {
+ return WPCOM_Masterbar::get_calypso_site_slug( get_current_blog_id() );
+ }
+
+ if ( empty( $url ) ) {
+ $url = \home_url();
+ }
+
+ $url = preg_replace( '#^.*?://#', '', $url );
+ $url = str_replace( '/', '::', $url );
+
+ return $url;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/CHANGELOG.md
new file mode 100644
index 00000000..aca0a702
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/CHANGELOG.md
@@ -0,0 +1,641 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.28.0] - 2022-01-04
+### Changed
+- Listener: Do not enqueue actions when the site is disconnected
+- Switch to pcov for code coverage.
+- Theme deletions: rely on Core WP hook now that the package requires WP 5.8.
+- Updated package dependencies
+- Updated package textdomain from `jetpack` to `jetpack-sync`.
+
+## [1.27.6] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.27.5] - 2021-11-30
+### Changed
+- Updated package dependencies.
+
+## [1.27.4] - 2021-11-23
+### Changed
+- Updated package dependencies.
+
+## [1.27.3] - 2021-11-16
+### Changed
+- Actions: add the do_only_first_initial_sync method which starts an initial sync only when one hasn't already been done
+
+## [1.27.2] - 2021-11-09
+### Added
+- Constants: Now syncing Atomic platform constant
+
+### Changed
+- Full Sync : limit included users to contributors and above (based on wp_user_limit)
+- Updated package dependencies.
+- User Checksums - limit scope to users with wp_user_level > 0
+
+### Fixed
+- Fix PHP 8.1 deprecation warnings.
+
+## [1.27.1] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.27.0] - 2021-10-26
+### Added
+- Added the _wpas_feature_enabled meta key to the sync list
+- Sync Error Log to capture failed sync requests.
+
+### Fixed
+- Check the return value of get_comment() before to use it.
+- Increase send timeout to 20 seconds allowing capture of WP.com 408 responses.
+
+## [1.26.4] - 2021-10-13
+### Changed
+- Sync Checksums: Convert text fields to latin1 before generating checksum.
+- Updated package dependencies.
+
+### Fixed
+- Sync Checksums - Update distinct clause to use $wpdb-> table names to accouunt for differences in prefixes.
+
+## [1.26.3] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+### Removed
+- Remove initialization of the identity-crisis package. That will be handled by the Config package.
+
+### Fixed
+- Reduce transient expiration for how often we check the state of the queue.
+- Sync Checksums - exclude locale from checksum if same as site setting
+- Sync Checksums - use distinct query when calculating count of Term Relationships
+
+## [1.26.2] - 2021-09-28
+### Added
+- Add support for checksumming user-related tabled: wp_users and wp_usermeta
+
+### Changed
+- Update annotations versions.
+- Updated package dependencies.
+
+### Fixed
+- Resolve indirect modification notice.
+- Sync Checksums: utilize distinct clause in term counts.
+- Sync Queue: better handling of serialization issues and empty actions.
+
+## [1.26.1] - 2021-09-03
+### Fixed
+- Add better checks if the WooCommerce tables should be enabled for checksum/fix.
+- Prevent PHP notices on queue_pull if all args are not set.
+
+## [1.26.0] - 2021-08-30
+### Added
+- Add support for WooCommerce table to the checksum/fix process.
+- Enable support for utf8 conversion during checksum calculation.
+
+### Changed
+- Don't run composer install on regular phpunit script
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+
+### Fixed
+- Sync Checksums - ensure last object is included in histogram
+
+## [1.25.0] - 2021-08-12
+### Added
+- Add package version tracking.
+- Add `wpcom_is_fse_activated` to sync list
+- Made /sync/object endpoint accessible over POST, not only GET, to allow fetching more items in a single request.
+
+## [1.24.2] - 2021-08-02
+
+- Reverted: Sync option for the Carousel to display colorized slide background.
+
+## [1.24.1] - 2021-07-29
+### Changed
+- Utilize an import for WP_Error in all instances.
+
+### Fixed
+- Fixed unqualified WP_Error use in the Rest_Sender class.
+
+## [1.24.0] - 2021-07-27
+### Added
+- Add a package version constant.
+- Add Full Site Editing support to callback options.
+- Sync option for the Carousel to display colorized slide background.
+
+### Fixed
+- Update Sender so it adheres to max upload bytes when not encoding items.
+
+## [1.23.3] - 2021-07-16
+### Fixed
+- Update Options module to return jetpack_sync_settings_* values from the Settings class vs direct option lookup.
+
+## [1.23.2] - 2021-07-13
+### Changed
+- Updated package dependencies.
+
+### Fixed
+- Performance of Sync checksums degraded with the update to correlated subquery. This restricts its usage to term_taxonomy joins only."
+
+## [1.23.1] - 2021-07-01
+### Changed
+- Checksum parent_table joins need distinct selection to account for possibility of multiple rows.
+
+### Fixed
+- Update term_taxonomy checksum query to an allowed list vs disallowed
+
+## [1.23.0] - 2021-06-29
+### Added
+- Add jetpack_idc_disonnect action to clear Sync options on disconnect.
+- Add support to callables to sync/object endpoint.
+- Enable sync/object endpoint support for theme-info.
+- Enhance updates module to support get_objects_by_id.
+- Expand sync/object to support constants.
+- Extend sync/object to support callables.
+- Implement v4 REST endpoints.
+- Initialize the IDC package in the Sync package.
+
+### Removed
+- Remove product_cat from blocked taxonomies
+
+## [1.22.0] - 2021-06-15
+### Changed
+- Sync: Adding the Identity_Crisis package.
+- Updated package dependencies.
+
+### Deprecated
+- Deprecated URL methods in `Automattic\Jetpack\Sync\Functions` in favor of `Automattic\Jetpack\Connection\Urls`.
+
+## [1.21.3] - 2021-05-25
+### Changed
+- Performance: If no Full Sync is in process early return before we update options.
+
+### Fixed
+- Janitorial: avoid PHP notices in some edge-cases
+- Update Meta Module so get_object_by_id returns all meta values.
+
+## [1.21.2] - 2021-04-27
+### Added
+- Added the password-checker package the the Sync package composer.json file.
+
+### Changed
+- Updated package dependencies.
+
+### Fixed
+- Sync: removed references to the JETPACK__PLUGIN_DIR constant.
+- Sync Checksums : updated postmeta range query performance #19337.
+
+## [1.21.1] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+- Implement a 60 second back-off for non-200 respones, if no retry-after header is present in the response.
+- Impose a max limit of 2MB on post meta values that are synced.
+- Impose a max limit of 5MB on post_content that can be synced.
+
+### Changed
+- Sync: Use the new Password_Checker package instead of Jetpack_Password_Checker.
+- Update package dependencies.
+- Use the Heartbeat package to generate the stats array
+
+### Fixed
+- Migrate locks to update_option to avaoid memcache inconsistencies that can be introduced by delete_option usage.
+- Update Sync Queue so that serialize is wrapped to catch errors
+
+## [1.21.0] - 2021-02-23
+
+- General: update WordPress version requirements to WP 5.6
+- Update Checksums to support blacklisted taxonomies.
+- Refactor Jetpack callables into the plugin using existing filter jetpack_sync_callable_whitelist
+- Wrap call_user_func in is_callable so that we don't trigger warnings for callables that don't exist.
+- Sync: Trigger initial sync on jetpack_site_registered
+- Update Comments checksum field to comment_date_gmt. We cannot use comment_content directly due to charset/filters.
+- Deprecate jetpack_json_wrap
+- Remove Sync's usage of wp_startswith
+
+## [1.20.2] - 2021-02-08
+
+- Update dependencies to latest stable
+
+## [1.20.1] - 2021-01-28
+
+- Update dependencies to latest stable
+
+## [1.20.0] - 2021-01-26
+
+- Sync Concurrency / Race Conditions
+- Sync: Prevent an PHP warning
+- Jetpack Sync: Checksums: Use a better way to fetch and validate fields against table
+- Add mirror-repo information to all current composer packages
+- Full Sync :: Reduce Concurrency.
+- Monorepo: Reorganize all projects
+- Various PHPCS and Cleanup
+
+## [1.19.4] - 2021-01-18
+
+- Update dependencies to latest stable
+
+## [1.19.3] - 2021-01-18
+
+- Full Sync :: Reduce Concurrency.
+
+## [1.19.2] - 2020-12-21
+
+- Update the do_full_sync function to early return if we are in SYNC READ ONLY mode.
+- Return an empty array if the specified range is empty. (It was returning the checksum for the WHOLE dataset).
+
+## [1.19.1] - 2020-12-17
+
+## [1.19.0] - 2020-12-17
+
+- sync: Improve sync checksum algorithm and endpoints
+- wp_get_environment_type as callable.
+- Disallow amp_validated_url as it is not site content but instead validation errors for amp mark-up.
+- Whitelist (allow) jetpack_sync_settings_* options to be synced
+- Re-order Sync default option whitelist (allowlist)
+
+## [1.18.1] - 2020-11-24
+
+- Version packages for release
+
+## [1.18.0] - 2020-11-24
+
+- Migrate jetpack_published_post to wp_after_insert_post hook
+- Check value to determine if we should enable sync after an action enqueuement.
+- General: update minimum required version to WordPress 5.5
+- Fix remaining phpcs warnings in most of requirelist
+- Update access of comment_status_to_approval_value to allow extension.
+- Update get_term Replicastore function to handle term_taxonomy_id option
+- Update get_terms to utilize ensure_taxonomy so that the Taxonomy is registered.
+- Addtion of note on explict return of null instead of false if option not found.
+- Alignment of comment_status_to_approval_value function. Addition of post-trashed status and cleanup of cases.
+- Alignment with implemenations. Call ensure_taxonomy to ensure Taxonomies have been initialized.
+- Call ensure_taxonomy within get_object_terms so that the taxonomy is registered before action is performed.
+- Updated PHPCS: Packages and Debugger
+
+## [1.17.2] - 2020-11-05
+
+- Update dependencies to latest stable
+
+## [1.17.1] - 2020-10-29
+
+- Update dependencies to latest stable
+
+## [1.17.0] - 2020-10-27
+
+- WPCOM Block Editor: Update meta key name
+- Resolve PHP Warning with array_filter usage in sync of action_links.
+- Sync: Seperate theme data ( name, version, slug and uri) from theme support data
+- Replaced intval() with (int) as part of issue #17432.
+- Replaced strval() with type casting (string) as part of issue #17432.
+- Replaced floatval() with type cast (float) as part of issue #17432.
+- Make XMLRPC methods available for blog token
+
+## [1.16.4] - 2020-10-14
+
+- Update dependencies to latest stable
+
+## [1.16.3] - 2020-10-09
+
+- Update dependencies to latest stable
+
+## [1.16.2] - 2020-10-06
+
+- Update dependencies to latest stable
+
+## [1.16.1] - 2020-10-01
+
+- Update dependencies to latest stable
+
+## [1.16.0] - 2020-09-29
+
+- Publicize: Allow publishing a post as a Twitter thread.
+- props @jmdodd - filter out set_object_terms actions that don't perform any update. Includes unit tests.
+- Sort Arrays by keys before generating callable checksums
+- Packages: avoid PHPCS warnings
+- Adding 'review' to whitelisted comment types
+- Disable Sync sending on Backup API Requests
+- Sync: stop trying to check for edit_comment capability
+- Added options to sync wc whitelist
+- Sync: Improve theme support syncing
+
+## [1.15.1] - 2020-09-09
+
+- Update dependencies to latest stable
+
+## [1.15.0] - 2020-08-26
+
+- Sync: add Creative Mail configuration option to synced options
+- Extend sync_status endpoint with optional debug_details field
+- REST API endpoints: expand management endpoints
+- Sync: Fix nonce action string in theme edit sync
+- WP 5.5 Compat: Align Jetpack and Core's plugin autoupdates
+- use current user token to updateRole request
+- Resolve Sync Errors from empty edge case and WP.com returning concurrent_request_error
+- Rework Sender to properly return state during do_full_sync
+
+## [1.14.4] - 2020-08-10
+
+- WP 5.5 Compat: Align Jetpack and Core's plugin autoupdates
+
+## [1.14.3] - 2020-08-10
+
+- Update dependencies to latest stable
+
+## [1.14.2] - 2020-08-10
+
+- Update dependencies to latest stable
+
+## [1.14.1] - 2020-08-10
+
+- Resolve Sync Errors from empty edge case and WP.com returning concurrent_request_error
+
+## [1.14.0] - 2020-07-28
+
+- Core Compat: Site Environment
+- Unit Tests: fix tests according to changes in Core
+- Utilize the blog token vs master user token to send sync actions.
+
+## [1.13.2] - 2020-07-06
+
+- Update dependencies to latest stable
+
+## [1.13.1] - 2020-07-01
+
+- Update dependencies to latest stable
+
+## [1.13.0] - 2020-06-30
+
+- Block Flamingo Plugin post types in Jetpack Sync
+- Explicit single execution of do_full_sync from cron
+- Update to reference the property defined in the Jetpack Connection Manager class
+- PHPCS: Clean up the packages
+- WordAds: Add consent support for California Consumer Privacy Act (CCPA)
+- Sync: Add additional support for theme_support_whitelist
+
+## [1.12.4] - 2020-06-02
+
+- Revert "Fix `jetpack sync start` CLI command (#16010)"
+
+## [1.12.3] - 2020-06-01
+
+- Update dependencies to latest stable
+
+## [1.12.2] - 2020-06-01
+
+- Fix `jetpack sync start` CLI command
+
+## [1.12.1] - 2020-05-29
+
+- Sync: Add additional support for theme_support_whitelist
+
+## [1.12.0] - 2020-05-26
+
+- Update ReplicaStore to call clean_comment_cache when comments are upserted or a reset is perofrmed.
+- Store the list of active plugins that uses connection in an option
+- Jetpack Sync :: Alternate non-blocking flow
+- Settings - Writing: add a toggle to Carousel so users can hide comment area
+- Sender needs to load consistently utilizing logic
+- Always delete items from the queue even if the buffer is no longer checked out.
+- Update the hook of Sync's Comment module to not send meta actions when the comment_type is not whitelisted.
+- Sync Comments apply whitelist to all actions
+
+## [1.11.0] - 2020-04-28
+
+- Correct inline documentation "Array" type
+- Filter out blacklisted post_types for deleted_post actions.
+- Publicize: Add jetpack_publicize_options
+- Blacklisting Post Types from Sync
+- Comments: update default comment type
+- Jetpack Sync: Split `jetpack_post_meta_batch_delete` in action to be called in chunks of 100 items, compared to all at once.
+- Update Sync limits based on analysis of data loss events.
+
+## [1.10.0] - 2020-03-31
+
+- Update dependencies to latest stable
+
+## [1.9.0] - 2020-03-31
+
+- Debugger: Add sync health progress bar
+- Add main network WPCOM blog ID to sync functions
+- Masterbar: send wpcom user ID to wpcom when attempting to log…
+- Sync: a better readme
+
+## [1.8.0] - 2020-02-25
+
+- Minileven: add options back as they still exist on sites
+- Sync: add queue size to actions
+- Mobile Theme: remove feature
+
+## [1.7.6] - 2020-02-14
+
+- get_sync_status does not properly account for unexpected states.
+
+## [1.7.5] - 2020-02-14
+
+- Empty Helper function for checkin handler
+- Sync Health: fix excessive data loss reports
+- Initial Sync Health Status Class and Data Loss Handler
+- Stop REST API Log entries from being synced
+
+## [1.7.4+vip] - 2020-02-14
+
+- Empty Helper function for checkin handler
+
+## [1.7.4] - 2020-01-23
+
+- Sync Chunk Keys need to be unique
+
+## [1.7.3] - 2020-01-20
+
+- Sync: ensure we run the initial sync on new connections
+
+## [1.7.2] - 2020-01-17
+
+- Sync Package: use Full_Sync_Immediately by default
+- Adding new managed WordPress hosts to be identified in class-functions.php.
+
+## [1.7.1] - 2020-01-14
+
+- Packages: Various improvements for wp.com or self-contained consumers
+
+## [1.7.0] - 2020-01-14
+
+- Trying to add deterministic initialization.
+
+## [1.6.3] - 2020-01-07
+
+- Fix git history.
+
+## [1.6.2] - 2019-12-31
+
+- Sync: Remove DEFAULT_SYNC_MODULES legacy map
+- Connection: Loose Comparison for Port Number in Signatures
+
+## [1.6.1] - 2019-12-13
+
+- tweak default sync settings
+
+## [1.6.0] - 2019-12-02
+
+- Sync: Full Sync: Send immediately.
+
+## [1.5.1] - 2019-11-26
+
+- Marked the xmlrpc_api_url method as deprecated.
+
+## [1.5.0] - 2019-11-25
+
+- Remove sync settings cache
+
+## [1.4.0] - 2019-11-19
+
+- Full Sync: Don't allow more than one request to enqueue
+- Sync: Update Max Int
+
+## [1.3.4] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## [1.3.3] - 2019-11-08
+
+- Deprecate Jetpack::is_development_mode() in favor of the packaged Status()-&gt;is_development_mode()
+
+## [1.3.2] - 2019-11-01
+
+- Full Sync updates to allow full enqueuing of chunks.
+
+## [1.3.1] - 2019-10-29
+
+- PHPCS: Rest of the packages
+
+## [1.3.0] - 2019-10-29
+
+- Sync: Checkout Endpoint: Add `pop` argument 😱
+
+## [1.2.1] - 2019-10-28
+
+- Sync: Add Settings to enable/disable the sender for a particular queue
+
+## [1.2.0] - 2019-10-24
+
+- Sync: Fix how we enqueue term_relationships on full sync 🏝
+- WP 5.3: Use modern wp_timezone
+- Check for last_error when enqueuing IDs
+
+## [1.1.1] - 2019-10-23
+
+- Use spread operator instead of func_get_args
+
+## [1.1.0] - 2019-10-07
+
+- Sync: Ensure a post object is returned
+- PHPCS: Sync Functions
+- Sync: Bail initial sync if there is an ongoing full sync
+
+## [1.0.2] - 2019-09-25
+
+- Sync: Only allow white listed comment types to be inserted.
+- Sync: Move sync_object XML-RPC method from connection to sync
+- Sync: do not sync comments made via Action Scheduler
+- Docs: Unify usage of @package phpdoc tags
+
+## [1.0.1] - 2019-09-14
+
+## 1.0.0 - 2019-09-14
+
+- Packages: Move sync to a classmapped package
+
+[1.28.0]: https://github.com/Automattic/jetpack-sync/compare/v1.27.6...v1.28.0
+[1.27.6]: https://github.com/Automattic/jetpack-sync/compare/v1.27.5...v1.27.6
+[1.27.5]: https://github.com/Automattic/jetpack-sync/compare/v1.27.4...v1.27.5
+[1.27.4]: https://github.com/Automattic/jetpack-sync/compare/v1.27.3...v1.27.4
+[1.27.3]: https://github.com/Automattic/jetpack-sync/compare/v1.27.2...v1.27.3
+[1.27.2]: https://github.com/Automattic/jetpack-sync/compare/v1.27.1...v1.27.2
+[1.27.1]: https://github.com/Automattic/jetpack-sync/compare/v1.27.0...v1.27.1
+[1.27.0]: https://github.com/Automattic/jetpack-sync/compare/v1.26.4...v1.27.0
+[1.26.4]: https://github.com/Automattic/jetpack-sync/compare/v1.26.3...v1.26.4
+[1.26.3]: https://github.com/Automattic/jetpack-sync/compare/v1.26.2...v1.26.3
+[1.26.2]: https://github.com/Automattic/jetpack-sync/compare/v1.26.1...v1.26.2
+[1.26.1]: https://github.com/Automattic/jetpack-sync/compare/v1.26.0...v1.26.1
+[1.26.0]: https://github.com/Automattic/jetpack-sync/compare/v1.25.0...v1.26.0
+[1.25.0]: https://github.com/Automattic/jetpack-sync/compare/v1.24.2...v1.25.0
+[1.24.2]: https://github.com/Automattic/jetpack-sync/compare/v1.24.1...v1.24.2
+[1.24.1]: https://github.com/Automattic/jetpack-sync/compare/v1.24.0...v1.24.1
+[1.24.0]: https://github.com/Automattic/jetpack-sync/compare/v1.23.3...v1.24.0
+[1.23.3]: https://github.com/Automattic/jetpack-sync/compare/v1.23.2...v1.23.3
+[1.23.2]: https://github.com/Automattic/jetpack-sync/compare/v1.23.1...v1.23.2
+[1.23.1]: https://github.com/Automattic/jetpack-sync/compare/v1.23.0...v1.23.1
+[1.23.0]: https://github.com/Automattic/jetpack-sync/compare/v1.22.0...v1.23.0
+[1.22.0]: https://github.com/Automattic/jetpack-sync/compare/v1.21.3...v1.22.0
+[1.21.3]: https://github.com/Automattic/jetpack-sync/compare/v1.21.2...v1.21.3
+[1.21.2]: https://github.com/Automattic/jetpack-sync/compare/v1.21.1...v1.21.2
+[1.21.1]: https://github.com/Automattic/jetpack-sync/compare/v1.21.0...v1.21.1
+[1.21.0]: https://github.com/Automattic/jetpack-sync/compare/v1.20.2...v1.21.0
+[1.20.2]: https://github.com/Automattic/jetpack-sync/compare/v1.20.1...v1.20.2
+[1.20.1]: https://github.com/Automattic/jetpack-sync/compare/v1.20.0...v1.20.1
+[1.20.0]: https://github.com/Automattic/jetpack-sync/compare/v1.19.4...v1.20.0
+[1.19.4]: https://github.com/Automattic/jetpack-sync/compare/v1.19.3...v1.19.4
+[1.19.3]: https://github.com/Automattic/jetpack-sync/compare/v1.19.2...v1.19.3
+[1.19.2]: https://github.com/Automattic/jetpack-sync/compare/v1.19.1...v1.19.2
+[1.19.1]: https://github.com/Automattic/jetpack-sync/compare/v1.19.0...v1.19.1
+[1.19.0]: https://github.com/Automattic/jetpack-sync/compare/v1.18.1...v1.19.0
+[1.18.1]: https://github.com/Automattic/jetpack-sync/compare/v1.18.0...v1.18.1
+[1.18.0]: https://github.com/Automattic/jetpack-sync/compare/v1.17.2...v1.18.0
+[1.17.2]: https://github.com/Automattic/jetpack-sync/compare/v1.17.1...v1.17.2
+[1.17.1]: https://github.com/Automattic/jetpack-sync/compare/v1.17.0...v1.17.1
+[1.17.0]: https://github.com/Automattic/jetpack-sync/compare/v1.16.4...v1.17.0
+[1.16.4]: https://github.com/Automattic/jetpack-sync/compare/v1.16.3...v1.16.4
+[1.16.3]: https://github.com/Automattic/jetpack-sync/compare/v1.16.2...v1.16.3
+[1.16.2]: https://github.com/Automattic/jetpack-sync/compare/v1.16.1...v1.16.2
+[1.16.1]: https://github.com/Automattic/jetpack-sync/compare/v1.16.0...v1.16.1
+[1.16.0]: https://github.com/Automattic/jetpack-sync/compare/v1.15.1...v1.16.0
+[1.15.1]: https://github.com/Automattic/jetpack-sync/compare/v1.15.0...v1.15.1
+[1.15.0]: https://github.com/Automattic/jetpack-sync/compare/v1.14.4...v1.15.0
+[1.14.4]: https://github.com/Automattic/jetpack-sync/compare/v1.14.3...v1.14.4
+[1.14.3]: https://github.com/Automattic/jetpack-sync/compare/v1.14.2...v1.14.3
+[1.14.2]: https://github.com/Automattic/jetpack-sync/compare/v1.14.1...v1.14.2
+[1.14.1]: https://github.com/Automattic/jetpack-sync/compare/v1.14.0...v1.14.1
+[1.14.0]: https://github.com/Automattic/jetpack-sync/compare/v1.13.2...v1.14.0
+[1.13.2]: https://github.com/Automattic/jetpack-sync/compare/v1.13.1...v1.13.2
+[1.13.1]: https://github.com/Automattic/jetpack-sync/compare/v1.13.0...v1.13.1
+[1.13.0]: https://github.com/Automattic/jetpack-sync/compare/v1.12.4...v1.13.0
+[1.12.4]: https://github.com/Automattic/jetpack-sync/compare/v1.12.3...v1.12.4
+[1.12.3]: https://github.com/Automattic/jetpack-sync/compare/v1.12.2...v1.12.3
+[1.12.2]: https://github.com/Automattic/jetpack-sync/compare/v1.12.1...v1.12.2
+[1.12.1]: https://github.com/Automattic/jetpack-sync/compare/v1.12.0...v1.12.1
+[1.12.0]: https://github.com/Automattic/jetpack-sync/compare/v1.11.0...v1.12.0
+[1.11.0]: https://github.com/Automattic/jetpack-sync/compare/v1.10.0...v1.11.0
+[1.10.0]: https://github.com/Automattic/jetpack-sync/compare/v1.9.0...v1.10.0
+[1.9.0]: https://github.com/Automattic/jetpack-sync/compare/v1.8.0...v1.9.0
+[1.8.0]: https://github.com/Automattic/jetpack-sync/compare/v1.7.6...v1.8.0
+[1.7.6]: https://github.com/Automattic/jetpack-sync/compare/v1.7.5...v1.7.6
+[1.7.5]: https://github.com/Automattic/jetpack-sync/compare/v1.7.4+vip...v1.7.5
+[1.7.4+vip]: https://github.com/Automattic/jetpack-sync/compare/v1.7.4...v1.7.4+vip
+[1.7.4]: https://github.com/Automattic/jetpack-sync/compare/v1.7.3...v1.7.4
+[1.7.3]: https://github.com/Automattic/jetpack-sync/compare/v1.7.2...v1.7.3
+[1.7.2]: https://github.com/Automattic/jetpack-sync/compare/v1.7.1...v1.7.2
+[1.7.1]: https://github.com/Automattic/jetpack-sync/compare/v1.7.0...v1.7.1
+[1.7.0]: https://github.com/Automattic/jetpack-sync/compare/v1.6.3...v1.7.0
+[1.6.3]: https://github.com/Automattic/jetpack-sync/compare/v1.6.2...v1.6.3
+[1.6.2]: https://github.com/Automattic/jetpack-sync/compare/v1.6.1...v1.6.2
+[1.6.1]: https://github.com/Automattic/jetpack-sync/compare/v1.6.0...v1.6.1
+[1.6.0]: https://github.com/Automattic/jetpack-sync/compare/v1.5.1...v1.6.0
+[1.5.1]: https://github.com/Automattic/jetpack-sync/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/Automattic/jetpack-sync/compare/v1.4.0...v1.5.0
+[1.4.0]: https://github.com/Automattic/jetpack-sync/compare/v1.3.4...v1.4.0
+[1.3.4]: https://github.com/Automattic/jetpack-sync/compare/v1.3.3...v1.3.4
+[1.3.3]: https://github.com/Automattic/jetpack-sync/compare/v1.3.2...v1.3.3
+[1.3.2]: https://github.com/Automattic/jetpack-sync/compare/v1.3.1...v1.3.2
+[1.3.1]: https://github.com/Automattic/jetpack-sync/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/Automattic/jetpack-sync/compare/v1.2.1...v1.3.0
+[1.2.1]: https://github.com/Automattic/jetpack-sync/compare/v1.2.0...v1.2.1
+[1.2.0]: https://github.com/Automattic/jetpack-sync/compare/v1.1.1...v1.2.0
+[1.1.1]: https://github.com/Automattic/jetpack-sync/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-sync/compare/v1.0.2...v1.1.0
+[1.0.2]: https://github.com/Automattic/jetpack-sync/compare/v1.0.1...v1.0.2
+[1.0.1]: https://github.com/Automattic/jetpack-sync/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-actions.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-actions.php
new file mode 100644
index 00000000..e2f05c98
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-actions.php
@@ -0,0 +1,942 @@
+<?php
+/**
+ * A class that defines syncable actions for Jetpack.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+use Automattic\Jetpack\Connection\Manager as Jetpack_Connection;
+use Automattic\Jetpack\Constants;
+use Automattic\Jetpack\Identity_Crisis;
+use Automattic\Jetpack\Status;
+use WP_Error;
+
+/**
+ * The role of this class is to hook the Sync subsystem into WordPress - when to listen for actions,
+ * when to send, when to perform a full sync, etc.
+ *
+ * It also binds the action to send data to WPCOM to Jetpack's XMLRPC client object.
+ */
+class Actions {
+
+ /**
+ * Name of the retry-after option prefix.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const RETRY_AFTER_PREFIX = 'jp_sync_retry_after_';
+
+ /**
+ * Name of the error log option prefix.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const ERROR_LOG_PREFIX = 'jp_sync_error_log_';
+
+ /**
+ * Name of the last successful sync option prefix.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const LAST_SUCCESS_PREFIX = 'jp_sync_last_success_';
+
+ /**
+ * A variable to hold a sync sender object.
+ *
+ * @access public
+ * @static
+ *
+ * @var Automattic\Jetpack\Sync\Sender
+ */
+ public static $sender = null;
+
+ /**
+ * A variable to hold a sync listener object.
+ *
+ * @access public
+ * @static
+ *
+ * @var Automattic\Jetpack\Sync\Listener
+ */
+ public static $listener = null;
+
+ /**
+ * Name of the sync cron schedule.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const DEFAULT_SYNC_CRON_INTERVAL_NAME = 'jetpack_sync_interval';
+
+ /**
+ * Interval between the last and the next sync cron action.
+ *
+ * @access public
+ *
+ * @var int
+ */
+ const DEFAULT_SYNC_CRON_INTERVAL_VALUE = 300; // 5 * MINUTE_IN_SECONDS;
+
+ /**
+ * Initialize Sync for cron jobs, set up listeners for WordPress Actions,
+ * and set up a shut-down action for sending actions to WordPress.com
+ *
+ * @access public
+ * @static
+ */
+ public static function init() {
+ // Everything below this point should only happen if we're a valid sync site.
+ if ( ! self::sync_allowed() ) {
+ return;
+ }
+
+ if ( self::sync_via_cron_allowed() ) {
+ self::init_sync_cron_jobs();
+ } elseif ( wp_next_scheduled( 'jetpack_sync_cron' ) ) {
+ self::clear_sync_cron_jobs();
+ }
+ // When importing via cron, do not sync.
+ add_action( 'wp_cron_importer_hook', array( __CLASS__, 'set_is_importing_true' ), 1 );
+
+ // Sync connected user role changes to WordPress.com.
+ Users::init();
+
+ // Publicize filter to prevent publicizing blacklisted post types.
+ add_filter( 'publicize_should_publicize_published_post', array( __CLASS__, 'prevent_publicize_blacklisted_posts' ), 10, 2 );
+
+ /**
+ * Fires on every request before default loading sync listener code.
+ * Return false to not load sync listener code that monitors common
+ * WP actions to be serialized.
+ *
+ * By default this returns true for cron jobs, non-GET-requests, or requests where the
+ * user is logged-in.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param bool should we load sync listener code for this request
+ */
+ if ( apply_filters( 'jetpack_sync_listener_should_load', true ) ) {
+ self::initialize_listener();
+ }
+
+ add_action( 'init', array( __CLASS__, 'add_sender_shutdown' ), 90 );
+ }
+
+ /**
+ * Prepares sync to send actions on shutdown for the current request.
+ *
+ * @access public
+ * @static
+ */
+ public static function add_sender_shutdown() {
+ /**
+ * Fires on every request before default loading sync sender code.
+ * Return false to not load sync sender code that serializes pending
+ * data and sends it to WPCOM for processing.
+ *
+ * By default this returns true for cron jobs, POST requests, admin requests, or requests
+ * by users who can manage_options.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param bool should we load sync sender code for this request
+ */
+ if ( apply_filters(
+ 'jetpack_sync_sender_should_load',
+ self::should_initialize_sender()
+ ) ) {
+ self::initialize_sender();
+ add_action( 'shutdown', array( self::$sender, 'do_sync' ) );
+ add_action( 'shutdown', array( self::$sender, 'do_full_sync' ), 9999 );
+ }
+ }
+
+ /**
+ * Define JETPACK_SYNC_READ_ONLY constant if not defined.
+ * This notifies sync to not run in shutdown if it was initialized during init.
+ *
+ * @access public
+ * @static
+ */
+ public static function mark_sync_read_only() {
+ Constants::set_constant( 'JETPACK_SYNC_READ_ONLY', true );
+ }
+
+ /**
+ * Decides if the sender should run on shutdown for this request.
+ *
+ * @access public
+ * @static
+ *
+ * @return bool
+ */
+ public static function should_initialize_sender() {
+
+ // Allow for explicit disable of Sync from request param jetpack_sync_read_only.
+ if ( isset( $_REQUEST['jetpack_sync_read_only'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification
+ self::mark_sync_read_only();
+ return false;
+ }
+
+ if ( Constants::is_true( 'DOING_CRON' ) ) {
+ return self::sync_via_cron_allowed();
+ }
+
+ if ( isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === $_SERVER['REQUEST_METHOD'] ) {
+ return true;
+ }
+
+ if ( current_user_can( 'manage_options' ) ) {
+ return true;
+ }
+
+ if ( is_admin() ) {
+ return true;
+ }
+
+ if ( defined( 'PHPUNIT_JETPACK_TESTSUITE' ) ) {
+ return true;
+ }
+
+ if ( Constants::get_constant( 'WP_CLI' ) ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Decides if the sender should run on shutdown when actions are queued.
+ *
+ * @access public
+ * @static
+ *
+ * @param bool $enable Should we initilize sender.
+ * @return bool
+ */
+ public static function should_initialize_sender_enqueue( $enable ) {
+
+ // If $enabled is false don't modify it, only check cron if enabled.
+ if ( false === $enable ) {
+ return $enable;
+ }
+
+ if ( Constants::is_true( 'DOING_CRON' ) ) {
+ return self::sync_via_cron_allowed();
+ }
+
+ return true;
+ }
+
+ /**
+ * Decides if sync should run at all during this request.
+ *
+ * @access public
+ * @static
+ *
+ * @return bool
+ */
+ public static function sync_allowed() {
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ return false;
+ }
+
+ if ( defined( 'PHPUNIT_JETPACK_TESTSUITE' ) ) {
+ return true;
+ }
+
+ if ( ! Settings::is_sync_enabled() ) {
+ return false;
+ }
+
+ if ( ( new Status() )->is_offline_mode() ) {
+ return false;
+ }
+
+ if ( ( new Status() )->is_staging_site() ) {
+ return false;
+ }
+
+ $connection = new Jetpack_Connection();
+ if ( ! $connection->is_connected() ) {
+ if ( ! doing_action( 'jetpack_site_registered' ) ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Helper function to get details as to why sync is not allowed, if it is not allowed.
+ *
+ * @return array
+ */
+ public static function get_debug_details() {
+ $debug = array();
+ $debug['debug_details']['sync_allowed'] = self::sync_allowed();
+ $debug['debug_details']['sync_health'] = Health::get_status();
+ if ( false === $debug['debug_details']['sync_allowed'] ) {
+ if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
+ $debug['debug_details']['is_wpcom'] = true;
+ }
+ if ( defined( 'PHPUNIT_JETPACK_TESTSUITE' ) ) {
+ $debug['debug_details']['PHPUNIT_JETPACK_TESTSUITE'] = true;
+ }
+ if ( ! Settings::is_sync_enabled() ) {
+ $debug['debug_details']['is_sync_enabled'] = false;
+ $debug['debug_details']['jetpack_sync_disable'] = Settings::get_setting( 'disable' );
+ $debug['debug_details']['jetpack_sync_network_disable'] = Settings::get_setting( 'network_disable' );
+ }
+ if ( ( new Status() )->is_offline_mode() ) {
+ $debug['debug_details']['is_offline_mode'] = true;
+ }
+ if ( ( new Status() )->is_staging_site() ) {
+ $debug['debug_details']['is_staging_site'] = true;
+ }
+ $connection = new Jetpack_Connection();
+ if ( ! $connection->is_connected() ) {
+ $debug['debug_details']['active_connection'] = false;
+ }
+ }
+
+ // Sync Logs.
+ $debug['debug_details']['last_succesful_sync'] = get_option( self::LAST_SUCCESS_PREFIX . 'sync', '' );
+ $debug['debug_details']['sync_error_log'] = get_option( self::ERROR_LOG_PREFIX . 'sync', '' );
+
+ return $debug;
+
+ }
+
+ /**
+ * Determines if syncing during a cron job is allowed.
+ *
+ * @access public
+ * @static
+ *
+ * @return bool|int
+ */
+ public static function sync_via_cron_allowed() {
+ return ( Settings::get_setting( 'sync_via_cron' ) );
+ }
+
+ /**
+ * Decides if the given post should be Publicized based on its type.
+ *
+ * @access public
+ * @static
+ *
+ * @param bool $should_publicize Publicize status prior to this filter running.
+ * @param \WP_Post $post The post to test for Publicizability.
+ * @return bool
+ */
+ public static function prevent_publicize_blacklisted_posts( $should_publicize, $post ) {
+ if ( in_array( $post->post_type, Settings::get_setting( 'post_types_blacklist' ), true ) ) {
+ return false;
+ }
+
+ return $should_publicize;
+ }
+
+ /**
+ * Set an importing flag to `true` in sync settings.
+ *
+ * @access public
+ * @static
+ */
+ public static function set_is_importing_true() {
+ Settings::set_importing( true );
+ }
+
+ /**
+ * Sends data to WordPress.com via an XMLRPC request.
+ *
+ * @access public
+ * @static
+ *
+ * @param object $data Data relating to a sync action.
+ * @param string $codec_name The name of the codec that encodes the data.
+ * @param float $sent_timestamp Current server time so we can compensate for clock differences.
+ * @param string $queue_id The queue the action belongs to, sync or full_sync.
+ * @param float $checkout_duration Time spent retrieving queue items from the DB.
+ * @param float $preprocess_duration Time spent converting queue items into data to send.
+ * @param int $queue_size The size of the sync queue at the time of processing.
+ * @param string $buffer_id The ID of the Queue buffer checked out for processing.
+ * @return mixed|WP_Error The result of the sending request.
+ */
+ public static function send_data( $data, $codec_name, $sent_timestamp, $queue_id, $checkout_duration, $preprocess_duration, $queue_size = null, $buffer_id = null ) {
+
+ $query_args = array(
+ 'sync' => '1', // Add an extra parameter to the URL so we can tell it's a sync action.
+ 'codec' => $codec_name,
+ 'timestamp' => $sent_timestamp,
+ 'queue' => $queue_id,
+ 'cd' => sprintf( '%.4f', $checkout_duration ),
+ 'pd' => sprintf( '%.4f', $preprocess_duration ),
+ 'queue_size' => $queue_size,
+ 'buffer_id' => $buffer_id,
+ );
+
+ $query_args['timeout'] = Settings::is_doing_cron() ? 30 : 20;
+
+ if ( 'immediate-send' === $queue_id ) {
+ $query_args['timeout'] = 30;
+ }
+
+ /**
+ * Filters query parameters appended to the Sync request URL sent to WordPress.com.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.7.0
+ *
+ * @param array $query_args associative array of query parameters.
+ */
+ $query_args = apply_filters( 'jetpack_sync_send_data_query_args', $query_args );
+
+ $connection = new Jetpack_Connection();
+ $url = add_query_arg( $query_args, $connection->xmlrpc_api_url() );
+
+ // If we're currently updating to Jetpack 7.7, the IXR client may be missing briefly
+ // because since 7.7 it's being autoloaded with Composer.
+ if ( ! class_exists( '\\Jetpack_IXR_Client' ) ) {
+ return new WP_Error(
+ 'ixr_client_missing',
+ esc_html__( 'Sync has been aborted because the IXR client is missing.', 'jetpack-sync' )
+ );
+ }
+
+ $rpc = new \Jetpack_IXR_Client(
+ array(
+ 'url' => $url,
+ 'timeout' => $query_args['timeout'],
+ )
+ );
+
+ $result = $rpc->query( 'jetpack.syncActions', $data );
+
+ // Adhere to Retry-After headers.
+ $retry_after = $rpc->get_response_header( 'Retry-After' );
+ if ( false !== $retry_after ) {
+ if ( (int) $retry_after > 0 ) {
+ update_option( self::RETRY_AFTER_PREFIX . $queue_id, microtime( true ) + (int) $retry_after, false );
+ } else {
+ // if unexpected value default to 3 minutes.
+ update_option( self::RETRY_AFTER_PREFIX . $queue_id, microtime( true ) + 180, false );
+ }
+ }
+
+ if ( ! $result ) {
+ if ( false === $retry_after ) {
+ // We received a non standard response from WP.com, lets backoff from sending requests for 1 minute.
+ update_option( self::RETRY_AFTER_PREFIX . $queue_id, microtime( true ) + 60, false );
+ }
+ // Record Sync Errors.
+ $error_log = get_option( self::ERROR_LOG_PREFIX . $queue_id, array() );
+ if ( ! is_array( $error_log ) ) {
+ $error_log = array();
+ }
+ // Trim existing array to last 4 entries.
+ if ( 5 <= count( $error_log ) ) {
+ $error_log = array_slice( $error_log, -4, null, true );
+ }
+ // Add new error indexed to time.
+ $error_log[ (string) microtime( true ) ] = $rpc->get_jetpack_error();
+ // Update the error log.
+ update_option( self::ERROR_LOG_PREFIX . $queue_id, $error_log );
+
+ // return request error.
+ return $rpc->get_jetpack_error();
+ }
+
+ $response = $rpc->getResponse();
+
+ // Check if WordPress.com IDC mitigation blocked the sync request.
+ if ( Identity_Crisis::init()->check_response_for_idc( $response ) ) {
+ return new WP_Error(
+ 'sync_error_idc',
+ esc_html__( 'Sync has been blocked from WordPress.com because it would cause an identity crisis', 'jetpack-sync' )
+ );
+ }
+
+ // Record last successful sync.
+ update_option( self::LAST_SUCCESS_PREFIX . $queue_id, microtime( true ), false );
+
+ return $response;
+ }
+
+ /**
+ * Kicks off the initial sync.
+ *
+ * @access public
+ * @static
+ *
+ * @return bool|null False if sync is not allowed.
+ */
+ public static function do_initial_sync() {
+ // Let's not sync if we are not supposed to.
+ if ( ! self::sync_allowed() ) {
+ return false;
+ }
+
+ // Don't start new sync if a full sync is in process.
+ $full_sync_module = Modules::get_module( 'full-sync' );
+ if ( $full_sync_module && $full_sync_module->is_started() && ! $full_sync_module->is_finished() ) {
+ return false;
+ }
+
+ $initial_sync_config = array(
+ 'options' => true,
+ 'functions' => true,
+ 'constants' => true,
+ 'users' => array( get_current_user_id() ),
+ 'network_options' => true,
+ );
+
+ self::do_full_sync( $initial_sync_config );
+ }
+
+ /**
+ * Do an initial full sync only if one has not already been started.
+ *
+ * @return bool|null False if the initial full sync was already started, otherwise null.
+ */
+ public static function do_only_first_initial_sync() {
+ $full_sync_module = Modules::get_module( 'full-sync' );
+ if ( $full_sync_module && $full_sync_module->is_started() ) {
+ return false;
+ }
+
+ static::do_initial_sync();
+ }
+
+ /**
+ * Kicks off a full sync.
+ *
+ * @access public
+ * @static
+ *
+ * @param array $modules The sync modules should be included in this full sync. All will be included if null.
+ * @return bool True if full sync was successfully started.
+ */
+ public static function do_full_sync( $modules = null ) {
+ if ( ! self::sync_allowed() ) {
+ return false;
+ }
+
+ $full_sync_module = Modules::get_module( 'full-sync' );
+
+ if ( ! $full_sync_module ) {
+ return false;
+ }
+
+ self::initialize_listener();
+
+ $full_sync_module->start( $modules );
+
+ return true;
+ }
+
+ /**
+ * Adds a cron schedule for regular syncing via cron, unless the schedule already exists.
+ *
+ * @access public
+ * @static
+ *
+ * @param array $schedules The list of WordPress cron schedules prior to this filter.
+ * @return array A list of WordPress cron schedules with the Jetpack sync interval added.
+ */
+ public static function jetpack_cron_schedule( $schedules ) {
+ if ( ! isset( $schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] ) ) {
+ $minutes = (int) ( self::DEFAULT_SYNC_CRON_INTERVAL_VALUE / 60 );
+ $display = ( 1 === $minutes ) ?
+ __( 'Every minute', 'jetpack-sync' ) :
+ /* translators: %d is an integer indicating the number of minutes. */
+ sprintf( __( 'Every %d minutes', 'jetpack-sync' ), $minutes );
+ $schedules[ self::DEFAULT_SYNC_CRON_INTERVAL_NAME ] = array(
+ 'interval' => self::DEFAULT_SYNC_CRON_INTERVAL_VALUE,
+ 'display' => $display,
+ );
+ }
+ return $schedules;
+ }
+
+ /**
+ * Starts an incremental sync via cron.
+ *
+ * @access public
+ * @static
+ */
+ public static function do_cron_sync() {
+ self::do_cron_sync_by_type( 'sync' );
+ }
+
+ /**
+ * Starts a full sync via cron.
+ *
+ * @access public
+ * @static
+ */
+ public static function do_cron_full_sync() {
+ self::do_cron_sync_by_type( 'full_sync' );
+ }
+
+ /**
+ * Try to send actions until we run out of things to send,
+ * or have to wait more than 15s before sending again,
+ * or we hit a lock or some other sending issue
+ *
+ * @access public
+ * @static
+ *
+ * @param string $type Sync type. Can be `sync` or `full_sync`.
+ */
+ public static function do_cron_sync_by_type( $type ) {
+ if ( ! self::sync_allowed() || ( 'sync' !== $type && 'full_sync' !== $type ) ) {
+ return;
+ }
+
+ self::initialize_sender();
+
+ $time_limit = Settings::get_setting( 'cron_sync_time_limit' );
+ $start_time = time();
+ $executions = 0;
+
+ do {
+ $next_sync_time = self::$sender->get_next_sync_time( $type );
+
+ if ( $next_sync_time ) {
+ $delay = $next_sync_time - time() + 1;
+ if ( $delay > 15 ) {
+ break;
+ } elseif ( $delay > 0 ) {
+ sleep( $delay );
+ }
+ }
+
+ // Explicitly only allow 1 do_full_sync call until issue with Immediate Full Sync is resolved.
+ // For more context see p1HpG7-9pe-p2.
+ if ( 'full_sync' === $type && $executions >= 1 ) {
+ break;
+ }
+
+ $result = 'full_sync' === $type ? self::$sender->do_full_sync() : self::$sender->do_sync();
+
+ // # of send actions performed.
+ $executions ++;
+
+ } while ( $result && ! is_wp_error( $result ) && ( $start_time + $time_limit ) > time() );
+
+ return $executions;
+ }
+
+ /**
+ * Initialize the sync listener.
+ *
+ * @access public
+ * @static
+ */
+ public static function initialize_listener() {
+ self::$listener = Listener::get_instance();
+ }
+
+ /**
+ * Initializes the sync sender.
+ *
+ * @access public
+ * @static
+ */
+ public static function initialize_sender() {
+ self::$sender = Sender::get_instance();
+ add_filter( 'jetpack_sync_send_data', array( __CLASS__, 'send_data' ), 10, 8 );
+ }
+
+ /**
+ * Initializes sync for WooCommerce.
+ *
+ * @access public
+ * @static
+ */
+ public static function initialize_woocommerce() {
+ if ( false === class_exists( 'WooCommerce' ) ) {
+ return;
+ }
+ add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_woocommerce_sync_module' ) );
+ }
+
+ /**
+ * Adds Woo's sync modules to existing modules for sending.
+ *
+ * @access public
+ * @static
+ *
+ * @param array $sync_modules The list of sync modules declared prior to this filter.
+ * @return array A list of sync modules that now includes Woo's modules.
+ */
+ public static function add_woocommerce_sync_module( $sync_modules ) {
+ $sync_modules[] = 'Automattic\\Jetpack\\Sync\\Modules\\WooCommerce';
+ return $sync_modules;
+ }
+
+ /**
+ * Initializes sync for WP Super Cache.
+ *
+ * @access public
+ * @static
+ */
+ public static function initialize_wp_super_cache() {
+ if ( false === function_exists( 'wp_cache_is_enabled' ) ) {
+ return;
+ }
+ add_filter( 'jetpack_sync_modules', array( __CLASS__, 'add_wp_super_cache_sync_module' ) );
+ }
+
+ /**
+ * Adds WP Super Cache's sync modules to existing modules for sending.
+ *
+ * @access public
+ * @static
+ *
+ * @param array $sync_modules The list of sync modules declared prior to this filer.
+ * @return array A list of sync modules that now includes WP Super Cache's modules.
+ */
+ public static function add_wp_super_cache_sync_module( $sync_modules ) {
+ $sync_modules[] = 'Automattic\\Jetpack\\Sync\\Modules\\WP_Super_Cache';
+ return $sync_modules;
+ }
+
+ /**
+ * Sanitizes the name of sync's cron schedule.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $schedule The name of a WordPress cron schedule.
+ * @return string The sanitized name of sync's cron schedule.
+ */
+ public static function sanitize_filtered_sync_cron_schedule( $schedule ) {
+ $schedule = sanitize_key( $schedule );
+ $schedules = wp_get_schedules();
+
+ // Make sure that the schedule has actually been registered using the `cron_intervals` filter.
+ if ( isset( $schedules[ $schedule ] ) ) {
+ return $schedule;
+ }
+
+ return self::DEFAULT_SYNC_CRON_INTERVAL_NAME;
+ }
+
+ /**
+ * Allows offsetting of start times for sync cron jobs.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $schedule The name of a cron schedule.
+ * @param string $hook The hook that this method is responding to.
+ * @return int The offset for the sync cron schedule.
+ */
+ public static function get_start_time_offset( $schedule = '', $hook = '' ) {
+ $start_time_offset = is_multisite()
+ ? wp_rand( 0, ( 2 * self::DEFAULT_SYNC_CRON_INTERVAL_VALUE ) )
+ : 0;
+
+ /**
+ * Allows overriding the offset that the sync cron jobs will first run. This can be useful when scheduling
+ * cron jobs across multiple sites in a network.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.5.0
+ *
+ * @param int $start_time_offset
+ * @param string $hook
+ * @param string $schedule
+ */
+ return (int) apply_filters(
+ 'jetpack_sync_cron_start_time_offset',
+ $start_time_offset,
+ $hook,
+ $schedule
+ );
+ }
+
+ /**
+ * Decides if a sync cron should be scheduled.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $schedule The name of a cron schedule.
+ * @param string $hook The hook that this method is responding to.
+ */
+ public static function maybe_schedule_sync_cron( $schedule, $hook ) {
+ if ( ! $hook ) {
+ return;
+ }
+ $schedule = self::sanitize_filtered_sync_cron_schedule( $schedule );
+
+ $start_time = time() + self::get_start_time_offset( $schedule, $hook );
+ if ( ! wp_next_scheduled( $hook ) ) {
+ // Schedule a job to send pending queue items once a minute.
+ wp_schedule_event( $start_time, $schedule, $hook );
+ } elseif ( wp_get_schedule( $hook ) !== $schedule ) {
+ // If the schedule has changed, update the schedule.
+ wp_clear_scheduled_hook( $hook );
+ wp_schedule_event( $start_time, $schedule, $hook );
+ }
+ }
+
+ /**
+ * Clears Jetpack sync cron jobs.
+ *
+ * @access public
+ * @static
+ */
+ public static function clear_sync_cron_jobs() {
+ wp_clear_scheduled_hook( 'jetpack_sync_cron' );
+ wp_clear_scheduled_hook( 'jetpack_sync_full_cron' );
+ }
+
+ /**
+ * Initializes Jetpack sync cron jobs.
+ *
+ * @access public
+ * @static
+ */
+ public static function init_sync_cron_jobs() {
+ add_filter( 'cron_schedules', array( __CLASS__, 'jetpack_cron_schedule' ) ); // phpcs:ignore WordPress.WP.CronInterval.ChangeDetected
+
+ add_action( 'jetpack_sync_cron', array( __CLASS__, 'do_cron_sync' ) );
+ add_action( 'jetpack_sync_full_cron', array( __CLASS__, 'do_cron_full_sync' ) );
+
+ /**
+ * Allows overriding of the default incremental sync cron schedule which defaults to once every 5 minutes.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.3.2
+ *
+ * @param string self::DEFAULT_SYNC_CRON_INTERVAL_NAME
+ */
+ $incremental_sync_cron_schedule = apply_filters( 'jetpack_sync_incremental_sync_interval', self::DEFAULT_SYNC_CRON_INTERVAL_NAME );
+ self::maybe_schedule_sync_cron( $incremental_sync_cron_schedule, 'jetpack_sync_cron' );
+
+ /**
+ * Allows overriding of the full sync cron schedule which defaults to once every 5 minutes.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.3.2
+ *
+ * @param string self::DEFAULT_SYNC_CRON_INTERVAL_NAME
+ */
+ $full_sync_cron_schedule = apply_filters( 'jetpack_sync_full_sync_interval', self::DEFAULT_SYNC_CRON_INTERVAL_NAME );
+ self::maybe_schedule_sync_cron( $full_sync_cron_schedule, 'jetpack_sync_full_cron' );
+ }
+
+ /**
+ * Perform maintenance when a plugin upgrade occurs.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $new_version New version of the plugin.
+ * @param string $old_version Old version of the plugin.
+ */
+ public static function cleanup_on_upgrade( $new_version = '', $old_version = '' ) {
+ if ( wp_next_scheduled( 'jetpack_sync_send_db_checksum' ) ) {
+ wp_clear_scheduled_hook( 'jetpack_sync_send_db_checksum' );
+ }
+
+ $is_new_sync_upgrade = version_compare( $old_version, '4.2', '>=' );
+ if ( ! empty( $old_version ) && $is_new_sync_upgrade && version_compare( $old_version, '4.5', '<' ) ) {
+ self::clear_sync_cron_jobs();
+ Settings::update_settings(
+ array(
+ 'render_filtered_content' => Defaults::$default_render_filtered_content,
+ )
+ );
+ }
+
+ Health::on_jetpack_upgraded();
+ }
+
+ /**
+ * Get syncing status for the given fields.
+ *
+ * @access public
+ * @static
+ *
+ * @param string|null $fields A comma-separated string of the fields to include in the array from the JSON response.
+ * @return array An associative array with the status report.
+ */
+ public static function get_sync_status( $fields = null ) {
+ self::initialize_sender();
+
+ $sync_module = Modules::get_module( 'full-sync' );
+ $queue = self::$sender->get_sync_queue();
+
+ // _get_cron_array can be false
+ $cron_timestamps = ( _get_cron_array() ) ? array_keys( _get_cron_array() ) : array();
+ $next_cron = ( ! empty( $cron_timestamps ) ) ? $cron_timestamps[0] - time() : '';
+
+ $checksums = array();
+ $debug = array();
+
+ if ( ! empty( $fields ) ) {
+ $store = new Replicastore();
+ $fields_params = array_map( 'trim', explode( ',', $fields ) );
+
+ if ( in_array( 'posts_checksum', $fields_params, true ) ) {
+ $checksums['posts_checksum'] = $store->posts_checksum();
+ }
+ if ( in_array( 'comments_checksum', $fields_params, true ) ) {
+ $checksums['comments_checksum'] = $store->comments_checksum();
+ }
+ if ( in_array( 'post_meta_checksum', $fields_params, true ) ) {
+ $checksums['post_meta_checksum'] = $store->post_meta_checksum();
+ }
+ if ( in_array( 'comment_meta_checksum', $fields_params, true ) ) {
+ $checksums['comment_meta_checksum'] = $store->comment_meta_checksum();
+ }
+
+ if ( in_array( 'debug_details', $fields_params, true ) ) {
+ $debug = self::get_debug_details();
+ }
+ }
+
+ $full_sync_status = ( $sync_module ) ? $sync_module->get_status() : array();
+
+ $full_queue = self::$sender->get_full_sync_queue();
+
+ $result = array_merge(
+ $full_sync_status,
+ $checksums,
+ $debug,
+ array(
+ 'cron_size' => count( $cron_timestamps ),
+ 'next_cron' => $next_cron,
+ 'queue_size' => $queue->size(),
+ 'queue_lag' => $queue->lag(),
+ 'queue_next_sync' => ( self::$sender->get_next_sync_time( 'sync' ) - microtime( true ) ),
+ 'full_queue_next_sync' => ( self::$sender->get_next_sync_time( 'full_sync' ) - microtime( true ) ),
+ )
+ );
+
+ // Verify $sync_module is not false.
+ if ( ( $sync_module ) && false === strpos( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
+ $result['full_queue_size'] = $full_queue->size();
+ $result['full_queue_lag'] = $full_queue->lag();
+ }
+ return $result;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-defaults.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-defaults.php
new file mode 100644
index 00000000..c8c43501
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-defaults.php
@@ -0,0 +1,1272 @@
+<?php
+/**
+ * Jetpack Sync Defaults
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+use Automattic\Jetpack\Status;
+
+/**
+ * Just some defaults that we share with the server.
+ */
+class Defaults {
+
+ /**
+ * Default Options.
+ *
+ * @var array
+ */
+ public static $default_options_whitelist = array(
+ 'active_plugins',
+ 'admin_email',
+ 'advanced_seo_front_page_description', // Jetpack_SEO_Utils::FRONT_PAGE_META_OPTION.
+ 'advanced_seo_title_formats', // Jetpack_SEO_Titles::TITLE_FORMATS_OPTION.
+ 'avatar_default',
+ 'avatar_rating',
+ 'blog_charset',
+ 'blog_public',
+ 'blogdescription',
+ 'blogname',
+ 'carousel_background_color',
+ 'carousel_display_comments',
+ 'carousel_display_exif',
+ 'category_base',
+ 'ce4wp_referred_by', // Creative Mail. See pbtFPC-H5-p2.
+ 'close_comments_days_old',
+ 'close_comments_for_old_posts',
+ 'comment_max_links',
+ 'comment_moderation',
+ 'comment_order',
+ 'comment_previously_approved',
+ 'comment_registration',
+ 'comments_notify',
+ 'comments_per_page',
+ 'date_format',
+ 'default_category',
+ 'default_comment_status',
+ 'default_comments_page',
+ 'default_email_category',
+ 'default_ping_status',
+ 'default_pingback_flag',
+ 'default_post_format',
+ 'default_role',
+ 'disabled_likes',
+ 'disabled_reblogs',
+ 'disallowed_keys',
+ 'enable_header_ad',
+ 'gmt_offset',
+ 'gravatar_disable_hovercards',
+ 'highlander_comment_form_prompt',
+ 'image_default_link_type',
+ 'infinite_scroll',
+ 'infinite_scroll_google_analytics',
+ 'jetpack-memberships-connected-account-id',
+ 'jetpack-twitter-cards-site-tag',
+ 'jetpack_activated',
+ 'jetpack_allowed_xsite_search_ids',
+ 'jetpack_api_cache_enabled',
+ 'jetpack_autoupdate_core',
+ 'jetpack_autoupdate_plugins',
+ 'jetpack_autoupdate_plugins_translations',
+ 'jetpack_autoupdate_themes',
+ 'jetpack_autoupdate_themes_translations',
+ 'jetpack_autoupdate_translations',
+ 'jetpack_available_modules',
+ 'jetpack_comment_form_color_scheme',
+ 'jetpack_comment_likes_enabled',
+ 'jetpack_connection_active_plugins',
+ 'jetpack_excluded_extensions',
+ 'jetpack_mailchimp',
+ 'jetpack_options',
+ 'jetpack_portfolio',
+ 'jetpack_portfolio_posts_per_page',
+ 'jetpack_protect_global_whitelist',
+ 'jetpack_protect_key',
+ 'jetpack_publicize_options',
+ 'jetpack_relatedposts',
+ 'jetpack_sso_match_by_email',
+ 'jetpack_sso_require_two_step',
+ 'jetpack_sync_non_blocking', // is non-blocking Jetpack Sync flow enabled.
+ 'jetpack_sync_non_public_post_stati',
+ 'jetpack_sync_settings_comment_meta_whitelist',
+ 'jetpack_sync_settings_post_meta_whitelist',
+ 'jetpack_sync_settings_post_types_blacklist',
+ 'jetpack_sync_settings_taxonomies_blacklist',
+ 'jetpack_testimonial',
+ 'jetpack_testimonial_posts_per_page',
+ 'jetpack_wga',
+ 'large_size_h',
+ 'large_size_w',
+ 'mailserver_login', // Not syncing contents, only the option name.
+ 'mailserver_pass', // Not syncing contents, only the option name.
+ 'mailserver_port',
+ 'mailserver_url',
+ 'medium_size_h',
+ 'medium_size_w',
+ 'moderation_keys',
+ 'moderation_notify',
+ 'monitor_receive_notifications',
+ 'new_admin_email',
+ 'page_comments',
+ 'page_for_posts',
+ 'page_on_front',
+ 'permalink_structure',
+ 'ping_sites',
+ 'post_by_email_address',
+ 'post_count',
+ 'posts_per_page',
+ 'posts_per_rss',
+ 'require_name_email',
+ 'rss_use_excerpt',
+ 'sharing-options',
+ 'sharing-services',
+ 'show_avatars',
+ 'show_on_front',
+ 'sidebars_widgets',
+ 'site_icon', // (int) - ID of core's Site Icon attachment ID
+ 'site_logo',
+ 'site_segment',
+ 'site_user_type',
+ 'site_vertical',
+ 'social_notifications_like',
+ 'social_notifications_reblog',
+ 'social_notifications_subscribe',
+ 'start_of_week',
+ 'stats_options',
+ 'stb_enabled',
+ 'stc_enabled',
+ 'sticky_posts',
+ 'stylesheet',
+ 'subscription_options',
+ 'tag_base',
+ 'thread_comments',
+ 'thread_comments_depth',
+ 'thumbnail_crop',
+ 'thumbnail_size_h',
+ 'thumbnail_size_w',
+ 'tiled_galleries',
+ 'time_format',
+ 'timezone_string',
+ 'twitter_via',
+ 'uninstall_plugins',
+ 'uploads_use_yearmonth_folders',
+ 'users_can_register',
+ 'verification_services_codes',
+ 'wordads_ccpa_enabled',
+ 'wordads_ccpa_privacy_policy_url',
+ 'wordads_custom_adstxt',
+ 'wordads_custom_adstxt_enabled',
+ 'wordads_display_archive',
+ 'wordads_display_front_page',
+ 'wordads_display_page',
+ 'wordads_display_post',
+ 'wordads_second_belowpost',
+ 'wp_mobile_app_promos',
+ 'wp_mobile_excerpt',
+ 'wp_mobile_featured_images',
+ 'wp_page_for_privacy_policy',
+ 'wpcom_is_fse_activated',
+ 'wpcom_publish_comments_with_markdown',
+ 'wpcom_publish_posts_with_markdown',
+ );
+
+ /**
+ * Return options whitelist filtered.
+ *
+ * @return array Options whitelist.
+ */
+ public static function get_options_whitelist() {
+ /** This filter is already documented in json-endpoints/jetpack/class.wpcom-json-api-get-option-endpoint.php */
+ $options_whitelist = apply_filters( 'jetpack_options_whitelist', self::$default_options_whitelist );
+ /**
+ * Filter the list of WordPress options that are manageable via the JSON API.
+ *
+ * @module sync
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.8.0
+ *
+ * @param array The default list of options.
+ */
+ return apply_filters( 'jetpack_sync_options_whitelist', $options_whitelist );
+ }
+
+ /**
+ * "Contentless" Options.
+ *
+ * Do not sync contents for these events, only the option name. Good for sensitive information that Sync does not need.
+ *
+ * @var array Options to sync name only.
+ */
+ public static $default_options_contentless = array(
+ 'mailserver_login',
+ 'mailserver_pass',
+ );
+
+ /**
+ * Return contentless options.
+ *
+ * These are options that Sync only uses the option names, not the content of the option.
+ *
+ * @return array
+ */
+ public static function get_options_contentless() {
+ /**
+ * Filter the list of WordPress options that should be synced without content
+ *
+ * @module sync
+ *
+ * @since 1.6.3
+ * @since-jetpack 6.1.0
+ *
+ * @param array The list of options synced without content.
+ */
+ return apply_filters( 'jetpack_sync_options_contentless', self::$default_options_contentless );
+ }
+
+ /**
+ * Array of defaulted constants whitelisted.
+ *
+ * @var array Default constants whitelist
+ */
+ public static $default_constants_whitelist = array(
+ 'ABSPATH',
+ 'ALTERNATE_WP_CRON',
+ 'ATOMIC_CLIENT_ID',
+ 'AUTOMATIC_UPDATER_DISABLED',
+ 'DISABLE_WP_CRON',
+ 'DISALLOW_FILE_EDIT',
+ 'DISALLOW_FILE_MODS',
+ 'EMPTY_TRASH_DAYS',
+ 'FS_METHOD',
+ 'IS_PRESSABLE',
+ 'JETPACK__VERSION',
+ 'PHP_VERSION',
+ 'WP_ACCESSIBLE_HOSTS',
+ 'WP_AUTO_UPDATE_CORE',
+ 'WP_CONTENT_DIR',
+ 'WP_CRON_LOCK_TIMEOUT',
+ 'WP_DEBUG',
+ 'WP_HTTP_BLOCK_EXTERNAL',
+ 'WP_MAX_MEMORY_LIMIT',
+ 'WP_MEMORY_LIMIT',
+ 'WP_POST_REVISIONS',
+ );
+
+ /**
+ * Get constants whitelisted by Sync.
+ *
+ * @return array Constants accessible via sync.
+ */
+ public static function get_constants_whitelist() {
+ /**
+ * Filter the list of PHP constants that are manageable via the JSON API.
+ *
+ * @module sync
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.8.0
+ *
+ * @param array The default list of constants options.
+ */
+ return apply_filters( 'jetpack_sync_constants_whitelist', self::$default_constants_whitelist );
+ }
+
+ /**
+ * Callables able to be managed via JSON API.
+ *
+ * @var array Default whitelist of callables.
+ */
+ public static $default_callable_whitelist = array(
+ 'get_plugins' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_plugins' ),
+ 'get_plugins_action_links' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_plugins_action_links' ),
+ 'has_file_system_write_access' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'file_system_write_access' ),
+ 'home_url' => array( 'Automattic\\Jetpack\\Connection\\Urls', 'home_url' ),
+ 'hosting_provider' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_hosting_provider' ),
+ 'is_fse_theme' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_is_fse_theme' ),
+ 'is_main_network' => array( __CLASS__, 'is_multi_network' ),
+ 'is_multi_site' => 'is_multisite',
+ 'is_version_controlled' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'is_version_controlled' ),
+ 'locale' => 'get_locale',
+ 'main_network_site' => array( 'Automattic\\Jetpack\\Connection\\Urls', 'main_network_site_url' ),
+ 'main_network_site_wpcom_id' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'main_network_site_wpcom_id' ),
+ 'paused_plugins' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_paused_plugins' ),
+ 'paused_themes' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_paused_themes' ),
+ 'post_type_features' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_post_type_features' ),
+ 'post_types' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_post_types' ),
+ 'rest_api_allowed_post_types' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'rest_api_allowed_post_types' ),
+ 'rest_api_allowed_public_metadata' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'rest_api_allowed_public_metadata' ),
+ 'roles' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'roles' ),
+ 'shortcodes' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_shortcodes' ),
+ 'site_icon_url' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'site_icon_url' ),
+ 'site_url' => array( 'Automattic\\Jetpack\\Connection\\Urls', 'site_url' ),
+ 'taxonomies' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_taxonomies' ),
+ 'theme_support' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_theme_support' ),
+ 'timezone' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'get_timezone' ),
+ 'wp_get_environment_type' => 'wp_get_environment_type',
+ 'wp_max_upload_size' => 'wp_max_upload_size',
+ 'wp_version' => array( 'Automattic\\Jetpack\\Sync\\Functions', 'wp_version' ),
+ );
+
+ /**
+ * Array of post type attributes synced.
+ *
+ * @var array Default post type attributes.
+ */
+ public static $default_post_type_attributes = array(
+ '_builtin' => false,
+ '_edit_link' => 'post.php?post=%d',
+ 'can_export' => true,
+ 'cap' => array(),
+ 'capabilities' => array(),
+ 'capability_type' => 'post',
+ 'delete_with_user' => null,
+ 'description' => '',
+ 'exclude_from_search' => true,
+ 'has_archive' => false,
+ 'hierarchical' => false,
+ 'label' => '',
+ 'labels' => array(),
+ 'map_meta_cap' => true,
+ 'menu_icon' => null,
+ 'menu_position' => null,
+ 'name' => '',
+ 'public' => false,
+ 'publicly_queryable' => null,
+ 'query_var' => true,
+ 'rest_base' => false,
+ 'rewrite' => true,
+ 'show_in_admin_bar' => false,
+ 'show_in_menu' => null,
+ 'show_in_nav_menus' => null,
+ 'show_in_rest' => false,
+ 'show_ui' => false,
+ 'supports' => array(),
+ 'taxonomies' => array(),
+ );
+
+ /**
+ * Get the whitelist of callables allowed to be managed via the JSON API.
+ *
+ * @return array Whitelist of callables allowed to be managed via the JSON API.
+ */
+ public static function get_callable_whitelist() {
+ /**
+ * Filter the list of callables that are manageable via the JSON API.
+ *
+ * @module sync
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.8.0
+ *
+ * @param array The default list of callables.
+ */
+ return apply_filters( 'jetpack_sync_callable_whitelist', self::$default_callable_whitelist );
+ }
+
+ /**
+ * Post types that will not be synced.
+ *
+ * These are usually automated post types (sitemaps, logs, etc).
+ *
+ * @var array Blacklisted post types.
+ */
+ public static $blacklisted_post_types = array(
+ 'ai1ec_event',
+ 'ai_log', // Logger - https://github.com/alleyinteractive/logger.
+ 'amp_validated_url', // AMP Validation Errors.
+ 'bwg_album',
+ 'bwg_gallery',
+ 'customize_changeset', // WP built-in post type for Customizer changesets.
+ 'dn_wp_yt_log',
+ 'flamingo_contact', // https://wordpress.org/plugins/flamingo/.
+ 'flamingo_inbound',
+ 'flamingo_outbound',
+ 'http',
+ 'idx_page',
+ 'jetpack_migration',
+ 'jp_img_sitemap',
+ 'jp_img_sitemap_index',
+ 'jp_sitemap',
+ 'jp_sitemap_index',
+ 'jp_sitemap_master',
+ 'jp_vid_sitemap',
+ 'jp_vid_sitemap_index',
+ 'msm_sitemap', // Metro Sitemap Plugin.
+ 'postman_sent_mail',
+ 'rssap-feed',
+ 'rssmi_feed_item',
+ 'scheduled-action', // Action Scheduler - Job Queue for WordPress https://github.com/woocommerce/woocommerce/tree/e7762627c37ec1f7590e6cac4218ba0c6a20024d/includes/libraries/action-scheduler .
+ 'secupress_log_action',
+ 'sg_optimizer_jobs',
+ 'snitch',
+ 'vip-legacy-redirect',
+ 'wp-rest-api-log', // https://wordpress.org/plugins/wp-rest-api-log/.
+ 'wp_automatic',
+ 'wp_log', // WP Logging Plugin.
+ 'wpephpcompat_jobs',
+ 'wprss_feed_item',
+ );
+
+ /**
+ * Taxonomies that we're not syncing by default.
+ *
+ * The list is compiled by auditing the dynamic filters and actions that contain taxonomy slugs
+ * and could conflict with other existing filters/actions in WP core, Jetpack and WooCommerce.
+ *
+ * @var array
+ */
+ public static $blacklisted_taxonomies = array(
+ 'ancestors',
+ 'archives_link',
+ 'attached_file',
+ 'attached_media',
+ 'attached_media_args',
+ 'attachment',
+ 'available_languages',
+ 'avatar',
+ 'avatar_comment_types',
+ 'avatar_data',
+ 'avatar_url',
+ 'bloginfo_rss',
+ 'blogs_of_user',
+ 'bookmark_link',
+ 'bookmarks',
+ 'calendar',
+ 'canonical_url',
+ 'categories_per_page',
+ 'categories_taxonomy',
+ 'category_form',
+ 'category_form_fields',
+ 'category_form_pre',
+ 'comment',
+ 'comment_ID',
+ 'comment_author',
+ 'comment_author_IP',
+ 'comment_author_email',
+ 'comment_author_link',
+ 'comment_author_url',
+ 'comment_author_url_link',
+ 'comment_date',
+ 'comment_excerpt',
+ 'comment_link',
+ 'comment_misc_actions',
+ 'comment_text',
+ 'comment_time',
+ 'comment_type',
+ 'comments_link',
+ 'comments_number',
+ 'comments_pagenum_link',
+ 'custom_logo',
+ 'date_sql',
+ 'default_comment_status',
+ 'delete_post_link',
+ 'edit_bookmark_link',
+ 'edit_comment_link',
+ 'edit_post_link',
+ 'edit_tag_link',
+ 'edit_term_link',
+ 'edit_user_link',
+ 'enclosed',
+ 'feed_build_date',
+ 'form_advanced',
+ 'form_after_editor',
+ 'form_after_title',
+ 'form_before_permalink',
+ 'form_top',
+ 'handle_product_cat',
+ 'header_image_tag',
+ 'header_video_url',
+ 'image_tag',
+ 'image_tag_class',
+ 'lastpostdate',
+ 'lastpostmodified',
+ 'link',
+ 'link_category_form',
+ 'link_category_form_fields',
+ 'link_category_form_pre',
+ 'main_network_id',
+ 'media',
+ 'media_item_args',
+ 'ms_user',
+ 'network',
+ 'object_terms',
+ 'option',
+ 'page',
+ 'page_form',
+ 'page_of_comment',
+ 'page_uri',
+ 'pagenum_link',
+ 'pages',
+ 'plugin',
+ 'post',
+ 'post_galleries',
+ 'post_gallery',
+ 'post_link',
+ 'post_modified_time',
+ 'post_status',
+ 'post_time',
+ 'postmeta',
+ 'posts_per_page',
+ 'product_search_form',
+ 'profile_url',
+ 'pung',
+ 'role_list',
+ 'sample_permalink',
+ 'sample_permalink_html',
+ 'schedule',
+ 'search_form',
+ 'search_query',
+ 'shortlink',
+ 'site',
+ 'site_email_content',
+ 'site_icon_url',
+ 'site_option',
+ 'space_allowed',
+ 'tag',
+ 'tag_form',
+ 'tag_form_fields',
+ 'tag_form_pre',
+ 'tag_link',
+ 'tags',
+ 'tags_per_page',
+ 'term',
+ 'term_link',
+ 'term_relationships',
+ 'term_taxonomies',
+ 'term_taxonomy',
+ 'terms',
+ 'terms_args',
+ 'terms_defaults',
+ 'terms_fields',
+ 'terms_orderby',
+ 'the_archive_description',
+ 'the_archive_title',
+ 'the_categories',
+ 'the_date',
+ 'the_excerpt',
+ 'the_guid',
+ 'the_modified_date',
+ 'the_modified_time',
+ 'the_post_type_description',
+ 'the_tags',
+ 'the_terms',
+ 'the_time',
+ 'theme_starter_content',
+ 'to_ping',
+ 'user',
+ 'user_created_user',
+ 'user_form',
+ 'user_profile',
+ 'user_profile_update',
+ 'usermeta',
+ 'usernumposts',
+ 'users_drafts',
+ 'webhook',
+ 'widget',
+ 'woocommerce_archive',
+ 'wp_title_rss',
+ );
+
+ /**
+ * Default array of post table columns.
+ *
+ * @var array Post table columns.
+ */
+ public static $default_post_checksum_columns = array(
+ 'ID',
+ 'post_modified',
+ );
+
+ /**
+ * Default array of post meta table columns.
+ *
+ * @var array Post meta table columns.
+ */
+ public static $default_post_meta_checksum_columns = array(
+ 'meta_id',
+ 'meta_value',
+ );
+
+ /**
+ * Default array of comment table columns.
+ *
+ * @var array Default comment table columns.
+ */
+ public static $default_comment_checksum_columns = array(
+ 'comment_ID',
+ 'comment_content',
+ );
+
+ /**
+ * Default array of comment meta columns.
+ *
+ * @var array Comment meta table columns.
+ */
+ public static $default_comment_meta_checksum_columns = array(
+ 'meta_id',
+ 'meta_value',
+ );
+
+ /**
+ * Default array of option table columns.
+ *
+ * @var array Default array of option columns.
+ */
+ public static $default_option_checksum_columns = array(
+ 'option_name',
+ 'option_value',
+ );
+
+ /**
+ * Default array of term columns.
+ *
+ * @var array array of term columns.
+ */
+ public static $default_term_checksum_columns = array(
+ 'name',
+ 'slug',
+ 'term_id',
+ );
+
+ /**
+ * Default array of term taxonomy columns.
+ *
+ * @var array Array of term taxonomy columns.
+ */
+ public static $default_term_taxonomy_checksum_columns = array(
+ 'count',
+ 'parent',
+ 'taxonomy',
+ 'term_id',
+ 'term_taxonomy_id',
+ );
+
+ /**
+ * Default term relationship columns.
+ *
+ * @var array Array of term relationship columns.
+ */
+ public static $default_term_relationships_checksum_columns = array(
+ 'object_id',
+ 'term_order',
+ 'term_taxonomy_id',
+ );
+
+ /**
+ * Default multisite callables able to be managed via JSON API.
+ *
+ * @var array multsite callables whitelisted
+ */
+ public static $default_multisite_callable_whitelist = array();
+
+ /**
+ * Get array of multisite callables whitelisted.
+ *
+ * @return array Multisite callables managable via JSON API.
+ */
+ public static function get_multisite_callable_whitelist() {
+ /**
+ * Filter the list of multisite callables that are manageable via the JSON API.
+ *
+ * @module sync
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.8.0
+ *
+ * @param array The default list of multisite callables.
+ */
+ return apply_filters( 'jetpack_sync_multisite_callable_whitelist', self::$default_multisite_callable_whitelist );
+ }
+
+ /**
+ * Array of post meta keys whitelisted.
+ *
+ * @var array Post meta whitelist.
+ */
+ public static $post_meta_whitelist = array(
+ '_feedback_akismet_values',
+ '_feedback_email',
+ '_feedback_extra_fields',
+ '_g_feedback_shortcode',
+ '_jetpack_post_thumbnail',
+ '_last_editor_used_jetpack',
+ '_menu_item_classes',
+ '_menu_item_menu_item_parent',
+ '_menu_item_object',
+ '_menu_item_object_id',
+ '_menu_item_orphaned',
+ '_menu_item_type',
+ '_menu_item_xfn',
+ '_publicize_facebook_user',
+ '_publicize_twitter_user',
+ '_thumbnail_id',
+ '_wp_attached_file',
+ '_wp_attachment_backup_sizes',
+ '_wp_attachment_context',
+ '_wp_attachment_image_alt',
+ '_wp_attachment_is_custom_background',
+ '_wp_attachment_is_custom_header',
+ '_wp_attachment_metadata',
+ '_wp_page_template',
+ '_wp_trash_meta_comments_status',
+ '_wpas_feature_enabled',
+ '_wpas_is_tweetstorm',
+ '_wpas_mess',
+ 'advanced_seo_description', // Jetpack_SEO_Posts::DESCRIPTION_META_KEY.
+ 'content_width',
+ 'custom_css_add',
+ 'custom_css_preprocessor',
+ 'enclosure',
+ 'imagedata',
+ 'nova_price',
+ 'publicize_results',
+ 'sharing_disabled',
+ 'switch_like_status',
+ 'videopress_guid',
+ 'vimeo_poster_image',
+ );
+
+ /**
+ * Get the post meta key whitelist.
+ *
+ * @return array Post meta whitelist.
+ */
+ public static function get_post_meta_whitelist() {
+ /**
+ * Filter the list of post meta data that are manageable via the JSON API.
+ *
+ * @module sync
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.8.0
+ *
+ * @param array The default list of meta data keys.
+ */
+ return apply_filters( 'jetpack_sync_post_meta_whitelist', self::$post_meta_whitelist );
+ }
+
+ /**
+ * Comment meta whitelist.
+ *
+ * @var array Comment meta whitelist.
+ */
+ public static $comment_meta_whitelist = array(
+ 'hc_avatar',
+ 'hc_foreign_user_id',
+ 'hc_post_as',
+ 'hc_wpcom_id_sig',
+ );
+
+ /**
+ * Get the comment meta whitelist.
+ *
+ * @return array
+ */
+ public static function get_comment_meta_whitelist() {
+ /**
+ * Filter the list of comment meta data that are manageable via the JSON API.
+ *
+ * @module sync
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.7.0
+ *
+ * @param array The default list of comment meta data keys.
+ */
+ return apply_filters( 'jetpack_sync_comment_meta_whitelist', self::$comment_meta_whitelist );
+ }
+
+ /**
+ * Default theme support whitelist.
+ *
+ * @todo move this to server? - these are theme support values
+ * that should be synced as jetpack_current_theme_supports_foo option values
+ *
+ * @var array Default theme support whitelist.
+ */
+ public static $default_theme_support_whitelist = array(
+ 'align-wide',
+ 'automatic-feed-links',
+ 'custom-background',
+ 'custom-header',
+ 'custom-logo',
+ 'customize-selective-refresh-widgets',
+ 'dark-editor-style',
+ 'disable-custom-colors',
+ 'disable-custom-font-sizes',
+ 'disable-custom-gradients',
+ 'editor-color-palette',
+ 'editor-font-sizes',
+ 'editor-gradient-presets',
+ 'editor-style', // deprecated.
+ 'editor-styles',
+ 'html5',
+ 'infinite-scroll',
+ 'jetpack-responsive-videos',
+ 'jetpack-social-menu',
+ 'menus',
+ 'post-formats',
+ 'post-thumbnails',
+ 'responsive-embeds',
+ 'site-logo',
+ 'title-tag',
+ 'widgets',
+ 'wp-block-styles',
+ );
+
+ /**
+ * Is an option whitelisted?
+ *
+ * @param string $option Option name.
+ * @return bool If option is on the whitelist.
+ */
+ public static function is_whitelisted_option( $option ) {
+ $whitelisted_options = self::get_options_whitelist();
+ foreach ( $whitelisted_options as $whitelisted_option ) {
+ if ( '/' === $whitelisted_option[0] && preg_match( $whitelisted_option, $option ) ) {
+ return true;
+ } elseif ( $whitelisted_option === $option ) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Default whitelist of capabilities to sync.
+ *
+ * @var array Array of WordPress capabilities.
+ */
+ public static $default_capabilities_whitelist = array(
+ 'activate_plugins',
+ 'add_users',
+ 'create_users',
+ 'customize',
+ 'delete_others_pages',
+ 'delete_others_posts',
+ 'delete_pages',
+ 'delete_plugins',
+ 'delete_posts',
+ 'delete_private_pages',
+ 'delete_private_posts',
+ 'delete_published_pages',
+ 'delete_published_posts',
+ 'delete_site',
+ 'delete_themes',
+ 'delete_users',
+ 'edit_dashboard',
+ 'edit_files',
+ 'edit_others_pages',
+ 'edit_others_posts',
+ 'edit_pages',
+ 'edit_plugins',
+ 'edit_posts',
+ 'edit_private_pages',
+ 'edit_private_posts',
+ 'edit_published_pages',
+ 'edit_published_posts',
+ 'edit_theme_options',
+ 'edit_themes',
+ 'edit_users',
+ 'export',
+ 'import',
+ 'install_plugins',
+ 'install_themes',
+ 'list_users',
+ 'manage_categories',
+ 'manage_links',
+ 'manage_options',
+ 'moderate_comments',
+ 'promote_users',
+ 'publish_pages',
+ 'publish_posts',
+ 'read',
+ 'read_private_pages',
+ 'read_private_posts',
+ 'remove_users',
+ 'switch_themes',
+ 'unfiltered_html',
+ 'unfiltered_upload',
+ 'update_core',
+ 'update_plugins',
+ 'update_themes',
+ 'upload_files',
+ 'upload_plugins',
+ 'upload_themes',
+ );
+
+ /**
+ * Get default capabilities whitelist.
+ *
+ * @return array
+ */
+ public static function get_capabilities_whitelist() {
+ /**
+ * Filter the list of capabilities that we care about
+ *
+ * @module sync
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.5.0
+ *
+ * @param array The default list of capabilities.
+ */
+ return apply_filters( 'jetpack_sync_capabilities_whitelist', self::$default_capabilities_whitelist );
+ }
+
+ /**
+ * Get max execution sync time.
+ *
+ * @return float Number of seconds.
+ */
+ public static function get_max_sync_execution_time() {
+ $max_exec_time = (int) ini_get( 'max_execution_time' );
+ if ( 0 === $max_exec_time ) {
+ // 0 actually means "unlimited", but let's not treat it that way.
+ $max_exec_time = 60;
+ }
+ return floor( $max_exec_time / 3 );
+ }
+
+ /**
+ * Get default for a given setting.
+ *
+ * @param string $setting Setting to get.
+ * @return mixed Value will be a string, int, array, based on the particular setting requested.
+ */
+ public static function get_default_setting( $setting ) {
+ $default_name = "default_$setting"; // e.g. default_dequeue_max_bytes.
+ return self::$$default_name;
+ }
+
+ /**
+ * Default list of network options.
+ *
+ * @var array network options
+ */
+ public static $default_network_options_whitelist = array(
+ 'active_sitewide_plugins',
+ 'auto_update_plugins', // WordPress 5.5+ auto-updates.
+ 'jetpack_protect_global_whitelist',
+ 'jetpack_protect_key',
+ 'site_name',
+ );
+
+ /**
+ * A mapping of known importers to friendly names.
+ *
+ * Keys are the class name of the known importer.
+ * Values are the friendly name.
+ *
+ * @since 1.6.3
+ * @since-jetpack 7.3.0
+ *
+ * @var array
+ */
+ public static $default_known_importers = array(
+ 'Blogger_Importer' => 'blogger',
+ 'LJ_API_Import' => 'livejournal',
+ 'MT_Import' => 'mt',
+ 'RSS_Import' => 'rss',
+ 'WC_Tax_Rate_Importer' => 'woo-tax-rate',
+ 'WP_Import' => 'wordpress',
+ );
+
+ /**
+ * Returns a list of known importers.
+ *
+ * @since 1.6.3
+ * @since-jetpack 7.3.0
+ *
+ * @return array Known importers with importer class names as keys and friendly names as values.
+ */
+ public static function get_known_importers() {
+ /**
+ * Filter the list of known importers.
+ *
+ * @module sync
+ *
+ * @since 1.6.3
+ * @since-jetpack 7.3.0
+ *
+ * @param array The default list of known importers.
+ */
+ return apply_filters( 'jetpack_sync_known_importers', self::$default_known_importers );
+ }
+
+ /**
+ * Whether this is a system with a multiple networks.
+ * We currently need this static wrapper because we statically define our default list of callables.
+ *
+ * @since 1.6.3
+ * @since-jetpack 7.6.0
+ *
+ * @uses Automattic\Jetpack\Status::is_multi_network
+ *
+ * @return boolean
+ */
+ public static function is_multi_network() {
+ $status = new Status();
+ return $status->is_multi_network();
+ }
+
+ /**
+ * Default bytes to dequeue.
+ *
+ * @var int Bytes.
+ */
+ public static $default_dequeue_max_bytes = 500000; // very conservative value, 1/2 MB.
+
+ /**
+ * Default upload bytes.
+ *
+ * This value is a little bigger than the upload limit to account for serialization.
+ *
+ * @var int Bytes.
+ */
+ public static $default_upload_max_bytes = 600000;
+
+ /**
+ * Default number of rows uploaded.
+ *
+ * @var int Number of rows.
+ */
+ public static $default_upload_max_rows = 500;
+
+ /**
+ * Default sync wait time.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_sync_wait_time = 10; // seconds, between syncs.
+
+ /**
+ * Only wait before next send if the current send took more than this number of seconds.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_sync_wait_threshold = 10;
+
+ /**
+ * Default wait between attempting to continue a full sync via requests.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_enqueue_wait_time = 1;
+
+ /**
+ * Maximum queue size.
+ *
+ * Each item is represented with a new row in the wp_options table.
+ *
+ * @var int Number of queue items.
+ */
+ public static $default_max_queue_size = 5000;
+
+ /**
+ * Default maximum lag allowed in the queue.
+ *
+ * @var int Number of seconds
+ */
+ public static $default_max_queue_lag = 7200; // 2 hours.
+
+ /**
+ * Default for default writes per sec.
+ *
+ * @var int Rows per second.
+ */
+ public static $default_queue_max_writes_sec = 100; // 100 rows a second.
+
+ /**
+ * Default for post types blacklist.
+ *
+ * @var array Empty array.
+ */
+ public static $default_post_types_blacklist = array();
+
+ /**
+ * Default for taxonomies blacklist.
+ *
+ * @var array Empty array.
+ */
+ public static $default_taxonomies_blacklist = array();
+
+ /**
+ * Default for taxonomies whitelist.
+ *
+ * @var array Empty array.
+ */
+ public static $default_taxonomy_whitelist = array();
+
+ /**
+ * Default for post meta whitelist.
+ *
+ * @var array Empty array.
+ */
+ public static $default_post_meta_whitelist = array();
+
+ /**
+ * Default for comment meta whitelist.
+ *
+ * @var array Empty array.
+ */
+ public static $default_comment_meta_whitelist = array();
+
+ /**
+ * Default for disabling sync across the site.
+ *
+ * @var int Bool-ish. Default to 0.
+ */
+ public static $default_disable = 0; // completely disable sending data to wpcom.
+
+ /**
+ * Default for disabling sync across the entire network on multisite.
+ *
+ * @var int Bool-ish. Default 0.
+ */
+ public static $default_network_disable = 0;
+
+ /**
+ * Default for disabling checksums.
+ *
+ * @var int Bool-ish. Default 0.
+ */
+ public static $default_checksum_disable = 0;
+
+ /**
+ * Should Sync use cron?
+ *
+ * @var int Bool-ish value. Default 1.
+ */
+ public static $default_sync_via_cron = 1;
+
+ /**
+ * Default if Sync should render content.
+ *
+ * @var int Bool-ish value. Default is 0.
+ */
+ public static $default_render_filtered_content = 0;
+
+ /**
+ * Default number of items to enqueue at a time when running full sync.
+ *
+ * @var int Number of items.
+ */
+ public static $default_max_enqueue_full_sync = 100;
+
+ /**
+ * Default for maximum queue size during a full sync.
+ *
+ * Each item will represent a value in the wp_options table.
+ *
+ * @var int Number of items.
+ */
+ public static $default_max_queue_size_full_sync = 1000; // max number of total items in the full sync queue.
+
+ /**
+ * Default max time for sending in immediate mode.
+ *
+ * @var float Number of Seconds
+ */
+ public static $default_full_sync_send_duration = 9;
+
+ /**
+ * Defaul for time between syncing callables.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_sync_callables_wait_time = MINUTE_IN_SECONDS; // seconds before sending callables again.
+
+ /**
+ * Default for time between syncing constants.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_sync_constants_wait_time = HOUR_IN_SECONDS; // seconds before sending constants again.
+ /**
+ * Default for sync queue lock timeout time.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_sync_queue_lock_timeout = 120; // 2 minutes.
+
+ /**
+ * Default for cron sync time limit.
+ *
+ * @var int Number of seconds.
+ */
+ public static $default_cron_sync_time_limit = 4 * MINUTE_IN_SECONDS;
+
+ /**
+ * Default for number of term relationship items sent in an full sync item.
+ *
+ * @var int Number of items.
+ */
+ public static $default_term_relationships_full_sync_item_size = 100;
+
+ /**
+ * Default for enabling incremental sync.
+ *
+ * @var int 1 for true.
+ */
+ public static $default_sync_sender_enabled = 1; // Should send incremental sync items.
+
+ /**
+ * Default for enabling Full Sync.
+ *
+ * @var int 1 for true.
+ */
+ public static $default_full_sync_sender_enabled = 1; // Should send full sync items.
+
+ /**
+ * Default Full Sync config
+ *
+ * @var array list of module names.
+ */
+ public static $default_full_sync_config = array(
+ 'comments' => 1,
+ 'constants' => 1,
+ 'functions' => 1,
+ 'options' => 1,
+ 'posts' => 1,
+ 'term_relationships' => 1,
+ 'terms' => 1,
+ 'themes' => 1,
+ 'updates' => 1,
+ 'users' => 1,
+ );
+
+ /**
+ * Default Full Sync max objects to send on a single request.
+ *
+ * @var array list of module => max.
+ */
+ public static $default_full_sync_limits = array(
+ 'comments' => array(
+ 'chunk_size' => 100,
+ 'max_chunks' => 10,
+ ),
+ 'posts' => array(
+ 'chunk_size' => 100,
+ 'max_chunks' => 1,
+ ),
+ 'term_relationships' => array(
+ 'chunk_size' => 1000,
+ 'max_chunks' => 10,
+ ),
+ 'terms' => array(
+ 'chunk_size' => 1000,
+ 'max_chunks' => 10,
+ ),
+ 'users' => array(
+ 'chunk_size' => 100,
+ 'max_chunks' => 10,
+ ),
+ );
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-functions.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-functions.php
new file mode 100644
index 00000000..02de16cd
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-functions.php
@@ -0,0 +1,631 @@
+<?php
+/**
+ * Utility functions to generate data synced to wpcom
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+use Automattic\Jetpack\Connection\Urls;
+use Automattic\Jetpack\Constants;
+
+/**
+ * Utility functions to generate data synced to wpcom
+ */
+class Functions {
+ const HTTPS_CHECK_OPTION_PREFIX = 'jetpack_sync_https_history_';
+ const HTTPS_CHECK_HISTORY = 5;
+
+ /**
+ * Return array of Jetpack modules.
+ *
+ * @return array
+ */
+ public static function get_modules() {
+ if ( defined( 'JETPACK__PLUGIN_DIR' ) ) {
+ require_once JETPACK__PLUGIN_DIR . 'class.jetpack-admin.php';
+
+ return \Jetpack_Admin::init()->get_modules();
+ }
+
+ return array();
+ }
+
+ /**
+ * Return array of taxonomies registered on the site.
+ *
+ * @return array
+ */
+ public static function get_taxonomies() {
+ global $wp_taxonomies;
+ $wp_taxonomies_without_callbacks = array();
+ foreach ( $wp_taxonomies as $taxonomy_name => $taxonomy ) {
+ $sanitized_taxonomy = self::sanitize_taxonomy( $taxonomy );
+ if ( ! empty( $sanitized_taxonomy ) ) {
+ $wp_taxonomies_without_callbacks[ $taxonomy_name ] = $sanitized_taxonomy;
+ }
+ }
+ return $wp_taxonomies_without_callbacks;
+ }
+
+ /**
+ * Return array of registered shortcodes.
+ *
+ * @return array
+ */
+ public static function get_shortcodes() {
+ global $shortcode_tags;
+ return array_keys( $shortcode_tags );
+ }
+
+ /**
+ * Removes any callback data since we will not be able to process it on our side anyways.
+ *
+ * @param \WP_Taxonomy $taxonomy \WP_Taxonomy item.
+ *
+ * @return mixed|null
+ */
+ public static function sanitize_taxonomy( $taxonomy ) {
+
+ // Lets clone the taxonomy object instead of modifing the global one.
+ $cloned_taxonomy = json_decode( wp_json_encode( $taxonomy ) );
+
+ // recursive taxonomies are no fun.
+ if ( is_null( $cloned_taxonomy ) ) {
+ return null;
+ }
+ // Remove any meta_box_cb if they are not the default wp ones.
+ if ( isset( $cloned_taxonomy->meta_box_cb ) &&
+ ! in_array( $cloned_taxonomy->meta_box_cb, array( 'post_tags_meta_box', 'post_categories_meta_box' ), true ) ) {
+ $cloned_taxonomy->meta_box_cb = null;
+ }
+ // Remove update call back.
+ if ( isset( $cloned_taxonomy->update_count_callback ) &&
+ ! is_null( $cloned_taxonomy->update_count_callback ) ) {
+ $cloned_taxonomy->update_count_callback = null;
+ }
+ // Remove rest_controller_class if it something other then the default.
+ if ( isset( $cloned_taxonomy->rest_controller_class ) &&
+ 'WP_REST_Terms_Controller' !== $cloned_taxonomy->rest_controller_class ) {
+ $cloned_taxonomy->rest_controller_class = null;
+ }
+ return $cloned_taxonomy;
+ }
+
+ /**
+ * Return array of registered post types.
+ *
+ * @return array
+ */
+ public static function get_post_types() {
+ global $wp_post_types;
+
+ $post_types_without_callbacks = array();
+ foreach ( $wp_post_types as $post_type_name => $post_type ) {
+ $sanitized_post_type = self::sanitize_post_type( $post_type );
+ if ( ! empty( $sanitized_post_type ) ) {
+ $post_types_without_callbacks[ $post_type_name ] = $sanitized_post_type;
+ }
+ }
+ return $post_types_without_callbacks;
+ }
+
+ /**
+ * Sanitizes by cloning post type object.
+ *
+ * @param object $post_type \WP_Post_Type.
+ *
+ * @return object
+ */
+ public static function sanitize_post_type( $post_type ) {
+ // Lets clone the post type object instead of modifing the global one.
+ $sanitized_post_type = array();
+ foreach ( Defaults::$default_post_type_attributes as $attribute_key => $default_value ) {
+ if ( isset( $post_type->{ $attribute_key } ) ) {
+ $sanitized_post_type[ $attribute_key ] = $post_type->{ $attribute_key };
+ }
+ }
+ return (object) $sanitized_post_type;
+ }
+
+ /**
+ * Return information about a synced post type.
+ *
+ * @param array $sanitized_post_type Array of args used in constructing \WP_Post_Type.
+ * @param string $post_type Post type name.
+ *
+ * @return object \WP_Post_Type
+ */
+ public static function expand_synced_post_type( $sanitized_post_type, $post_type ) {
+ $post_type = sanitize_key( $post_type );
+ $post_type_object = new \WP_Post_Type( $post_type, $sanitized_post_type );
+ $post_type_object->add_supports();
+ $post_type_object->add_rewrite_rules();
+ $post_type_object->add_hooks();
+ $post_type_object->register_taxonomies();
+ return (object) $post_type_object;
+ }
+
+ /**
+ * Returns site's post_type_features.
+ *
+ * @return array
+ */
+ public static function get_post_type_features() {
+ global $_wp_post_type_features;
+
+ return $_wp_post_type_features;
+ }
+
+ /**
+ * Return hosting provider.
+ *
+ * Uses a set of known constants, classes, or functions to help determine the hosting platform.
+ *
+ * @return string Hosting provider.
+ */
+ public static function get_hosting_provider() {
+ $hosting_provider_detection_methods = array(
+ 'get_hosting_provider_by_known_constant',
+ 'get_hosting_provider_by_known_class',
+ 'get_hosting_provider_by_known_function',
+ );
+
+ $functions = new Functions();
+ foreach ( $hosting_provider_detection_methods as $method ) {
+ $hosting_provider = call_user_func( array( $functions, $method ) );
+ if ( false !== $hosting_provider ) {
+ return $hosting_provider;
+ }
+ }
+
+ return 'unknown';
+ }
+
+ /**
+ * Return a hosting provider using a set of known constants.
+ *
+ * @return mixed A host identifier string or false.
+ */
+ public function get_hosting_provider_by_known_constant() {
+ $hosting_provider_constants = array(
+ 'GD_SYSTEM_PLUGIN_DIR' => 'gd-managed-wp',
+ 'MM_BASE_DIR' => 'bh',
+ 'PAGELYBIN' => 'pagely',
+ 'KINSTAMU_VERSION' => 'kinsta',
+ 'FLYWHEEL_CONFIG_DIR' => 'flywheel',
+ 'IS_PRESSABLE' => 'pressable',
+ 'VIP_GO_ENV' => 'vip-go',
+ );
+
+ foreach ( $hosting_provider_constants as $constant => $constant_value ) {
+ if ( Constants::is_defined( $constant ) ) {
+ if ( 'VIP_GO_ENV' === $constant && false === Constants::get_constant( 'VIP_GO_ENV' ) ) {
+ continue;
+ }
+ return $constant_value;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Return a hosting provider using a set of known classes.
+ *
+ * @return mixed A host identifier string or false.
+ */
+ public function get_hosting_provider_by_known_class() {
+ $hosting_provider = false;
+
+ switch ( true ) {
+ case ( class_exists( '\\WPaaS\\Plugin' ) ):
+ $hosting_provider = 'gd-managed-wp';
+ break;
+ }
+
+ return $hosting_provider;
+ }
+
+ /**
+ * Return a hosting provider using a set of known functions.
+ *
+ * @return mixed A host identifier string or false.
+ */
+ public function get_hosting_provider_by_known_function() {
+ $hosting_provider = false;
+
+ switch ( true ) {
+ case ( function_exists( 'is_wpe' ) || function_exists( 'is_wpe_snapshot' ) ):
+ $hosting_provider = 'wpe';
+ break;
+ }
+
+ return $hosting_provider;
+ }
+
+ /**
+ * Return array of allowed REST API post types.
+ *
+ * @return array Array of allowed post types.
+ */
+ public static function rest_api_allowed_post_types() {
+ /** This filter is already documented in class.json-api-endpoints.php */
+ return apply_filters( 'rest_api_allowed_post_types', array( 'post', 'page', 'revision' ) );
+ }
+
+ /**
+ * Return array of allowed REST API public metadata.
+ *
+ * @return array Array of allowed metadata.
+ */
+ public static function rest_api_allowed_public_metadata() {
+ /**
+ * Filters the meta keys accessible by the REST API.
+ *
+ * @see https://developer.wordpress.com/2013/04/26/custom-post-type-and-metadata-support-in-the-rest-api/
+ *
+ * @module json-api
+ *
+ * @since 1.6.3
+ * @since-jetpack 2.2.3
+ *
+ * @param array $whitelisted_meta Array of metadata that is accessible by the REST API.
+ */
+ return apply_filters( 'rest_api_allowed_public_metadata', array() );
+ }
+
+ /**
+ * Finds out if a site is using a version control system.
+ *
+ * @return bool
+ **/
+ public static function is_version_controlled() {
+
+ if ( ! class_exists( 'WP_Automatic_Updater' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
+ }
+ $updater = new \WP_Automatic_Updater();
+
+ return (bool) (string) $updater->is_vcs_checkout( ABSPATH );
+ }
+
+ /**
+ * Returns true if the site has file write access false otherwise.
+ *
+ * @return bool
+ **/
+ public static function file_system_write_access() {
+ if ( ! function_exists( 'get_filesystem_method' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/file.php';
+ }
+
+ require_once ABSPATH . 'wp-admin/includes/template.php';
+
+ $filesystem_method = get_filesystem_method();
+ if ( 'direct' === $filesystem_method ) {
+ return true;
+ }
+
+ ob_start();
+
+ if ( ! function_exists( 'request_filesystem_credentials' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/file.php';
+ }
+
+ $filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
+ ob_end_clean();
+ if ( $filesystem_credentials_are_stored ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Helper function that is used when getting home or siteurl values. Decides
+ * whether to get the raw or filtered value.
+ *
+ * @deprecated 1.23.1
+ *
+ * @param string $url_type URL to get, home or siteurl.
+ * @return string
+ */
+ public static function get_raw_or_filtered_url( $url_type ) {
+ _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::get_raw_or_filtered_url' );
+ return Urls::get_raw_or_filtered_url( $url_type );
+ }
+
+ /**
+ * Return the escaped home_url.
+ *
+ * @deprecated 1.23.1
+ *
+ * @return string
+ */
+ public static function home_url() {
+ _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::home_url' );
+ return Urls::home_url();
+ }
+
+ /**
+ * Return the escaped siteurl.
+ *
+ * @deprecated 1.23.1
+ *
+ * @return string
+ */
+ public static function site_url() {
+ _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::site_url' );
+ return Urls::site_url();
+ }
+
+ /**
+ * Return main site URL with a normalized protocol.
+ *
+ * @deprecated 1.23.1
+ *
+ * @return string
+ */
+ public static function main_network_site_url() {
+ _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::main_network_site_url' );
+ return Urls::main_network_site_url();
+ }
+
+ /**
+ * Return main site WordPress.com site ID.
+ *
+ * @return string
+ */
+ public static function main_network_site_wpcom_id() {
+ /**
+ * Return the current site WPCOM ID for single site installs
+ */
+ if ( ! is_multisite() ) {
+ return \Jetpack_Options::get_option( 'id' );
+ }
+
+ /**
+ * Return the main network site WPCOM ID for multi-site installs
+ */
+ $current_network = get_network();
+ switch_to_blog( $current_network->blog_id );
+ $wpcom_blog_id = \Jetpack_Options::get_option( 'id' );
+ restore_current_blog();
+ return $wpcom_blog_id;
+ }
+
+ /**
+ * Return URL with a normalized protocol.
+ *
+ * @deprecated 1.23.1
+ *
+ * @param callable $callable Function to retrieve URL option.
+ * @param string $new_value URL Protocol to set URLs to.
+ * @return string Normalized URL.
+ */
+ public static function get_protocol_normalized_url( $callable, $new_value ) {
+ _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::get_protocol_normalized_url' );
+ return Urls::get_protocol_normalized_url( $callable, $new_value );
+ }
+
+ /**
+ * Return URL from option or PHP constant.
+ *
+ * @deprecated 1.23.1
+ *
+ * @param string $option_name (e.g. 'home').
+ *
+ * @return mixed|null URL.
+ */
+ public static function get_raw_url( $option_name ) {
+ _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::get_raw_url' );
+ return Urls::get_raw_url( $option_name );
+ }
+
+ /**
+ * Normalize domains by removing www unless declared in the site's option.
+ *
+ * @deprecated 1.23.1
+ *
+ * @param string $option Option value from the site.
+ * @param callable $url_function Function retrieving the URL to normalize.
+ * @return mixed|string URL.
+ */
+ public static function normalize_www_in_url( $option, $url_function ) {
+ _deprecated_function( __METHOD__, '1.23.1', '\\Automattic\\Jetpack\\Connection\\Urls::normalize_www_in_url' );
+ return Urls::normalize_www_in_url( $option, $url_function );
+ }
+
+ /**
+ * Return filtered value of get_plugins.
+ *
+ * @return mixed|void
+ */
+ public static function get_plugins() {
+ if ( ! function_exists( 'get_plugins' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
+ }
+
+ /** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
+ return apply_filters( 'all_plugins', get_plugins() );
+ }
+
+ /**
+ * Get custom action link tags that the plugin is using
+ * Ref: https://codex.wordpress.org/Plugin_API/Filter_Reference/plugin_action_links_(plugin_file_name)
+ *
+ * @param string $plugin_file_singular Particular plugin.
+ * @return array of plugin action links (key: link name value: url)
+ */
+ public static function get_plugins_action_links( $plugin_file_singular = null ) {
+ // Some sites may have DOM disabled in PHP fail early.
+ if ( ! class_exists( 'DOMDocument' ) ) {
+ return array();
+ }
+ $plugins_action_links = get_option( 'jetpack_plugin_api_action_links', array() );
+ if ( ! empty( $plugins_action_links ) ) {
+ if ( is_null( $plugin_file_singular ) ) {
+ return $plugins_action_links;
+ }
+ return ( isset( $plugins_action_links[ $plugin_file_singular ] ) ? $plugins_action_links[ $plugin_file_singular ] : null );
+ }
+ return array();
+ }
+
+ /**
+ * Return the WP version as defined in the $wp_version global.
+ *
+ * @return string
+ */
+ public static function wp_version() {
+ global $wp_version;
+ return $wp_version;
+ }
+
+ /**
+ * Return site icon url used on the site.
+ *
+ * @param int $size Size of requested icon in pixels.
+ * @return mixed|string|void
+ */
+ public static function site_icon_url( $size = 512 ) {
+ $site_icon = get_site_icon_url( $size );
+ return $site_icon ? $site_icon : get_option( 'jetpack_site_icon_url' );
+ }
+
+ /**
+ * Return roles registered on the site.
+ *
+ * @return array
+ */
+ public static function roles() {
+ $wp_roles = wp_roles();
+ return $wp_roles->roles;
+ }
+
+ /**
+ * Determine time zone from WordPress' options "timezone_string"
+ * and "gmt_offset".
+ *
+ * 1. Check if `timezone_string` is set and return it.
+ * 2. Check if `gmt_offset` is set, formats UTC-offset from it and return it.
+ * 3. Default to "UTC+0" if nothing is set.
+ *
+ * Note: This function is specifically not using wp_timezone() to keep consistency with
+ * the existing formatting of the timezone string.
+ *
+ * @return string
+ */
+ public static function get_timezone() {
+ $timezone_string = get_option( 'timezone_string' );
+
+ if ( ! empty( $timezone_string ) ) {
+ return str_replace( '_', ' ', $timezone_string );
+ }
+
+ $gmt_offset = get_option( 'gmt_offset', 0 );
+
+ $formatted_gmt_offset = sprintf( '%+g', (float) $gmt_offset );
+
+ $formatted_gmt_offset = str_replace(
+ array( '.25', '.5', '.75' ),
+ array( ':15', ':30', ':45' ),
+ (string) $formatted_gmt_offset
+ );
+
+ /* translators: %s is UTC offset, e.g. "+1" */
+ return sprintf( __( 'UTC%s', 'jetpack-sync' ), $formatted_gmt_offset );
+ }
+
+ /**
+ * Return list of paused themes.
+ *
+ * @return array|bool Array of paused themes or false if unsupported.
+ */
+ public static function get_paused_themes() {
+ $paused_themes = wp_paused_themes();
+ return $paused_themes->get_all();
+ }
+
+ /**
+ * Return list of paused plugins.
+ *
+ * @return array|bool Array of paused plugins or false if unsupported.
+ */
+ public static function get_paused_plugins() {
+ $paused_plugins = wp_paused_plugins();
+ return $paused_plugins->get_all();
+ }
+
+ /**
+ * Return the theme's supported features.
+ * Used for syncing the supported feature that we care about.
+ *
+ * @return array List of features that the theme supports.
+ */
+ public static function get_theme_support() {
+ global $_wp_theme_features;
+
+ $theme_support = array();
+ foreach ( Defaults::$default_theme_support_whitelist as $theme_feature ) {
+ $has_support = current_theme_supports( $theme_feature );
+ if ( $has_support ) {
+ $theme_support[ $theme_feature ] = $_wp_theme_features[ $theme_feature ];
+ }
+ }
+
+ return $theme_support;
+ }
+
+ /**
+ * Returns if the current theme is a Full Site Editing theme.
+ *
+ * @return bool Theme is a Full Site Editing theme.
+ */
+ public static function get_is_fse_theme() {
+ return function_exists( 'gutenberg_is_fse_theme' ) && gutenberg_is_fse_theme();
+ }
+
+ /**
+ * Wraps data in a way so that we can distinguish between objects and array and also prevent object recursion.
+ *
+ * @since 1.21.0
+ *
+ * @param array|obj $any Source data to be cleaned up.
+ * @param array $seen_nodes Built array of nodes.
+ *
+ * @return array
+ */
+ public static function json_wrap( &$any, $seen_nodes = array() ) {
+ if ( is_object( $any ) ) {
+ $input = get_object_vars( $any );
+ $input['__o'] = 1;
+ } else {
+ $input = &$any;
+ }
+
+ if ( is_array( $input ) ) {
+ $seen_nodes[] = &$any;
+
+ $return = array();
+
+ foreach ( $input as $k => &$v ) {
+ if ( ( is_array( $v ) || is_object( $v ) ) ) {
+ if ( in_array( $v, $seen_nodes, true ) ) {
+ continue;
+ }
+ $return[ $k ] = self::json_wrap( $v, $seen_nodes );
+ } else {
+ $return[ $k ] = $v;
+ }
+ }
+
+ return $return;
+ }
+
+ return $any;
+
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-health.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-health.php
new file mode 100644
index 00000000..ea3d7bd4
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-health.php
@@ -0,0 +1,190 @@
+<?php
+/**
+ * Health class.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+/**
+ * Health class.
+ */
+class Health {
+
+ /**
+ * Prefix of the blog lock transient.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const STATUS_OPTION = 'sync_health_status';
+
+ /**
+ * Status key in option array.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const OPTION_STATUS_KEY = 'status';
+
+ /**
+ * Timestamp key in option array.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const OPTION_TIMESTAMP_KEY = 'timestamp';
+
+ /**
+ * Unknown status code.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const STATUS_UNKNOWN = 'unknown';
+
+ /**
+ * Disabled status code.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const STATUS_DISABLED = 'disabled';
+
+ /**
+ * Out of sync status code.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const STATUS_OUT_OF_SYNC = 'out_of_sync';
+
+ /**
+ * In sync status code.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const STATUS_IN_SYNC = 'in_sync';
+
+ /**
+ * If sync is active, Health-related hooks will be initialized after plugins are loaded.
+ */
+ public static function init() {
+ add_action( 'jetpack_full_sync_end', array( __ClASS__, 'full_sync_end_update_status' ), 10, 2 );
+ }
+
+ /**
+ * Gets health status code.
+ *
+ * @return string Sync Health Status
+ */
+ public static function get_status() {
+ $status = \Jetpack_Options::get_option( self::STATUS_OPTION );
+
+ if ( false === $status || ! is_array( $status ) || empty( $status[ self::OPTION_STATUS_KEY ] ) ) {
+ return self::STATUS_UNKNOWN;
+ }
+
+ switch ( $status[ self::OPTION_STATUS_KEY ] ) {
+ case self::STATUS_DISABLED:
+ case self::STATUS_OUT_OF_SYNC:
+ case self::STATUS_IN_SYNC:
+ return $status[ self::OPTION_STATUS_KEY ];
+ default:
+ return self::STATUS_UNKNOWN;
+ }
+
+ }
+
+ /**
+ * When the Jetpack plugin is upgraded, set status to disabled if sync is not enabled,
+ * or to unknown, if the status has never been set before.
+ */
+ public static function on_jetpack_upgraded() {
+ if ( ! Settings::is_sync_enabled() ) {
+ self::update_status( self::STATUS_DISABLED );
+ return;
+ }
+ if ( false === self::is_status_defined() ) {
+ self::update_status( self::STATUS_UNKNOWN );
+ }
+ }
+
+ /**
+ * When the Jetpack plugin is activated, set status to disabled if sync is not enabled,
+ * or to unknown.
+ */
+ public static function on_jetpack_activated() {
+ if ( ! Settings::is_sync_enabled() ) {
+ self::update_status( self::STATUS_DISABLED );
+ return;
+ }
+ self::update_status( self::STATUS_UNKNOWN );
+ }
+
+ /**
+ * Updates sync health status with either a valid status, or an unknown status.
+ *
+ * @param string $status Sync Status.
+ *
+ * @return bool True if an update occoured, or false if the status didn't change.
+ */
+ public static function update_status( $status ) {
+ if ( self::get_status() === $status ) {
+ return false;
+ }
+ // Default Status Option.
+ $new_status = array(
+ self::OPTION_STATUS_KEY => self::STATUS_UNKNOWN,
+ self::OPTION_TIMESTAMP_KEY => microtime( true ),
+ );
+
+ switch ( $status ) {
+ case self::STATUS_DISABLED:
+ case self::STATUS_OUT_OF_SYNC:
+ case self::STATUS_IN_SYNC:
+ $new_status[ self::OPTION_STATUS_KEY ] = $status;
+ break;
+ }
+
+ \Jetpack_Options::update_option( self::STATUS_OPTION, $new_status );
+ return true;
+ }
+
+ /**
+ * Check if Status has been previously set.
+ *
+ * @return bool is a Status defined
+ */
+ public static function is_status_defined() {
+ $status = \Jetpack_Options::get_option( self::STATUS_OPTION );
+
+ if ( false === $status || ! is_array( $status ) || empty( $status[ self::OPTION_STATUS_KEY ] ) ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Update Sync Status if Full Sync ended of Posts
+ *
+ * @param string $checksum The checksum that's currently being processed.
+ * @param array $range The ranges of object types being processed.
+ */
+ public static function full_sync_end_update_status( $checksum, $range ) {
+ if ( isset( $range['posts'] ) ) {
+ self::update_status( self::STATUS_IN_SYNC );
+ }
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-json-deflate-array-codec.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-json-deflate-array-codec.php
new file mode 100644
index 00000000..ecc33a94
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-json-deflate-array-codec.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * An implementation of Automattic\Jetpack\Sync\Codec_Interface that uses gzip's DEFLATE
+ * algorithm to compress objects serialized using json_encode.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+/**
+ * An implementation of Automattic\Jetpack\Sync\Codec_Interface that uses gzip's DEFLATE
+ * algorithm to compress objects serialized using json_encode
+ */
+class JSON_Deflate_Array_Codec implements Codec_Interface {
+ const CODEC_NAME = 'deflate-json-array';
+
+ /**
+ * Return the name of the codec.
+ *
+ * @return string
+ */
+ public function name() {
+ return self::CODEC_NAME;
+ }
+
+ /**
+ * Encodes an object.
+ *
+ * @param object $object Item to encode.
+ * @return string
+ */
+ public function encode( $object ) {
+ return base64_encode( gzdeflate( $this->json_serialize( $object ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
+ }
+
+ /**
+ * Decode compressed serialized value.
+ *
+ * @param string $input Item to decode.
+ * @return array|mixed|object
+ */
+ public function decode( $input ) {
+ return $this->json_unserialize( gzinflate( base64_decode( $input ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
+ }
+
+ /**
+ * Serialize JSON
+ *
+ * @see https://gist.github.com/muhqu/820694
+ *
+ * @param string $any Value to serialize and wrap.
+ *
+ * @return false|string
+ */
+ protected function json_serialize( $any ) {
+ return wp_json_encode( Functions::json_wrap( $any ) );
+ }
+
+ /**
+ * Unserialize JSON
+ *
+ * @param string $str JSON string.
+ * @return array|object Unwrapped JSON.
+ */
+ protected function json_unserialize( $str ) {
+ return $this->json_unwrap( json_decode( $str, true ) );
+ }
+
+ /**
+ * Unwraps a json_decode return.
+ *
+ * @param array|object $any json_decode object.
+ * @return array|object
+ */
+ private function json_unwrap( $any ) {
+ if ( is_array( $any ) ) {
+ foreach ( $any as $k => $v ) {
+ if ( '__o' === $k ) {
+ continue;
+ }
+ $any[ $k ] = $this->json_unwrap( $v );
+ }
+
+ if ( isset( $any['__o'] ) ) {
+ unset( $any['__o'] );
+ $any = (object) $any;
+ }
+ }
+
+ return $any;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-listener.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-listener.php
new file mode 100644
index 00000000..ce2862a4
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-listener.php
@@ -0,0 +1,487 @@
+<?php
+/**
+ * Jetpack's Sync Listener
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+use Automattic\Jetpack\Connection\Manager as Connection_Manager;
+use Automattic\Jetpack\Roles;
+
+/**
+ * This class monitors actions and logs them to the queue to be sent.
+ */
+class Listener {
+ const QUEUE_STATE_CHECK_TRANSIENT = 'jetpack_sync_last_checked_queue_state';
+ const QUEUE_STATE_CHECK_TIMEOUT = 30; // 30 seconds.
+
+ /**
+ * Sync queue.
+ *
+ * @var object
+ */
+ private $sync_queue;
+
+ /**
+ * Full sync queue.
+ *
+ * @var object
+ */
+ private $full_sync_queue;
+
+ /**
+ * Sync queue size limit.
+ *
+ * @var int size limit.
+ */
+ private $sync_queue_size_limit;
+
+ /**
+ * Sync queue lag limit.
+ *
+ * @var int Lag limit.
+ */
+ private $sync_queue_lag_limit;
+
+ /**
+ * Singleton implementation.
+ *
+ * @var Listener
+ */
+ private static $instance;
+
+ /**
+ * Get the Listener instance.
+ *
+ * @return Listener
+ */
+ public static function get_instance() {
+ if ( null === self::$instance ) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Listener constructor.
+ *
+ * This is necessary because you can't use "new" when you declare instance properties >:(
+ */
+ protected function __construct() {
+ $this->set_defaults();
+ $this->init();
+ }
+
+ /**
+ * Sync Listener init.
+ */
+ private function init() {
+ $handler = array( $this, 'action_handler' );
+ $full_sync_handler = array( $this, 'full_sync_action_handler' );
+
+ foreach ( Modules::get_modules() as $module ) {
+ $module->init_listeners( $handler );
+ $module->init_full_sync_listeners( $full_sync_handler );
+ }
+
+ // Module Activation.
+ add_action( 'jetpack_activate_module', $handler );
+ add_action( 'jetpack_deactivate_module', $handler );
+
+ // Jetpack Upgrade.
+ add_action( 'updating_jetpack_version', $handler, 10, 2 );
+
+ // Send periodic checksum.
+ add_action( 'jetpack_sync_checksum', $handler );
+ }
+
+ /**
+ * Get incremental sync queue.
+ */
+ public function get_sync_queue() {
+ return $this->sync_queue;
+ }
+
+ /**
+ * Gets the full sync queue.
+ */
+ public function get_full_sync_queue() {
+ return $this->full_sync_queue;
+ }
+
+ /**
+ * Sets queue size limit.
+ *
+ * @param int $limit Queue size limit.
+ */
+ public function set_queue_size_limit( $limit ) {
+ $this->sync_queue_size_limit = $limit;
+ }
+
+ /**
+ * Get queue size limit.
+ */
+ public function get_queue_size_limit() {
+ return $this->sync_queue_size_limit;
+ }
+
+ /**
+ * Sets the queue lag limit.
+ *
+ * @param int $age Queue lag limit.
+ */
+ public function set_queue_lag_limit( $age ) {
+ $this->sync_queue_lag_limit = $age;
+ }
+
+ /**
+ * Return value of queue lag limit.
+ */
+ public function get_queue_lag_limit() {
+ return $this->sync_queue_lag_limit;
+ }
+
+ /**
+ * Force a recheck of the queue limit.
+ */
+ public function force_recheck_queue_limit() {
+ delete_transient( self::QUEUE_STATE_CHECK_TRANSIENT . '_' . $this->sync_queue->id );
+ delete_transient( self::QUEUE_STATE_CHECK_TRANSIENT . '_' . $this->full_sync_queue->id );
+ }
+
+ /**
+ * Determine if an item can be added to the queue.
+ *
+ * Prevent adding items to the queue if it hasn't sent an item for 15 mins
+ * AND the queue is over 1000 items long (by default).
+ *
+ * @param object $queue Sync queue.
+ * @return bool
+ */
+ public function can_add_to_queue( $queue ) {
+ if ( ! Settings::is_sync_enabled() ) {
+ return false;
+ }
+
+ $state_transient_name = self::QUEUE_STATE_CHECK_TRANSIENT . '_' . $queue->id;
+
+ $queue_state = get_transient( $state_transient_name );
+
+ if ( false === $queue_state ) {
+ $queue_state = array( $queue->size(), $queue->lag() );
+ set_transient( $state_transient_name, $queue_state, self::QUEUE_STATE_CHECK_TIMEOUT );
+ }
+
+ list( $queue_size, $queue_age ) = $queue_state;
+
+ return ( $queue_age < $this->sync_queue_lag_limit )
+ ||
+ ( ( $queue_size + 1 ) < $this->sync_queue_size_limit );
+ }
+
+ /**
+ * Full sync action handler.
+ *
+ * @param mixed ...$args Args passed to the action.
+ */
+ public function full_sync_action_handler( ...$args ) {
+ $this->enqueue_action( current_filter(), $args, $this->full_sync_queue );
+ }
+
+ /**
+ * Action handler.
+ *
+ * @param mixed ...$args Args passed to the action.
+ */
+ public function action_handler( ...$args ) {
+ $this->enqueue_action( current_filter(), $args, $this->sync_queue );
+ }
+
+ // add many actions to the queue directly, without invoking them.
+
+ /**
+ * Bulk add action to the queue.
+ *
+ * @param string $action_name The name the full sync action.
+ * @param array $args_array Array of chunked arguments.
+ */
+ public function bulk_enqueue_full_sync_actions( $action_name, $args_array ) {
+ $queue = $this->get_full_sync_queue();
+
+ /*
+ * If we add any items to the queue, we should try to ensure that our script
+ * can't be killed before they are sent.
+ */
+ if ( function_exists( 'ignore_user_abort' ) ) {
+ ignore_user_abort( true );
+ }
+
+ $data_to_enqueue = array();
+ $user_id = get_current_user_id();
+ $currtime = microtime( true );
+ $is_importing = Settings::is_importing();
+
+ foreach ( $args_array as $args ) {
+ $previous_end = isset( $args['previous_end'] ) ? $args['previous_end'] : null;
+ $args = isset( $args['ids'] ) ? $args['ids'] : $args;
+
+ /**
+ * Modify or reject the data within an action before it is enqueued locally.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @module sync
+ *
+ * @param array The action parameters
+ */
+ $args = apply_filters( "jetpack_sync_before_enqueue_$action_name", $args );
+ $action_data = array( $args );
+ if ( ! is_null( $previous_end ) ) {
+ $action_data[] = $previous_end;
+ }
+ // allow listeners to abort.
+ if ( false === $args ) {
+ continue;
+ }
+
+ $data_to_enqueue[] = array(
+ $action_name,
+ $action_data,
+ $user_id,
+ $currtime,
+ $is_importing,
+ );
+ }
+
+ $queue->add_all( $data_to_enqueue );
+ }
+
+ /**
+ * Enqueue the action.
+ *
+ * @param string $current_filter Current WordPress filter.
+ * @param object $args Sync args.
+ * @param string $queue Sync queue.
+ */
+ public function enqueue_action( $current_filter, $args, $queue ) {
+ // don't enqueue an action during the outbound http request - this prevents recursion.
+ if ( Settings::is_sending() ) {
+ return;
+ }
+
+ if ( ! ( new Connection_Manager() )->is_connected() ) {
+ // Don't enqueue an action if the site is disconnected.
+ return;
+ }
+
+ /**
+ * Add an action hook to execute when anything on the whitelist gets sent to the queue to sync.
+ *
+ * @module sync
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.9.0
+ */
+ do_action( 'jetpack_sync_action_before_enqueue' );
+
+ /**
+ * Modify or reject the data within an action before it is enqueued locally.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param array The action parameters
+ */
+ $args = apply_filters( "jetpack_sync_before_enqueue_$current_filter", $args );
+
+ // allow listeners to abort.
+ if ( false === $args ) {
+ return;
+ }
+
+ /*
+ * Periodically check the size of the queue, and disable adding to it if
+ * it exceeds some limit AND the oldest item exceeds the age limit (i.e. sending has stopped).
+ */
+ if ( ! $this->can_add_to_queue( $queue ) ) {
+ if ( 'sync' === $queue->id ) {
+ $this->sync_data_loss( $queue );
+ }
+ return;
+ }
+
+ /*
+ * If we add any items to the queue, we should try to ensure that our script
+ * can't be killed before they are sent.
+ */
+ if ( function_exists( 'ignore_user_abort' ) ) {
+ ignore_user_abort( true );
+ }
+
+ if (
+ 'sync' === $queue->id ||
+ in_array(
+ $current_filter,
+ array(
+ 'jetpack_full_sync_start',
+ 'jetpack_full_sync_end',
+ 'jetpack_full_sync_cancel',
+ ),
+ true
+ )
+ ) {
+ $queue->add(
+ array(
+ $current_filter,
+ $args,
+ get_current_user_id(),
+ microtime( true ),
+ Settings::is_importing(),
+ $this->get_actor( $current_filter, $args ),
+ )
+ );
+ } else {
+ $queue->add(
+ array(
+ $current_filter,
+ $args,
+ get_current_user_id(),
+ microtime( true ),
+ Settings::is_importing(),
+ )
+ );
+ }
+
+ // since we've added some items, let's try to load the sender so we can send them as quickly as possible.
+ if ( ! Actions::$sender ) {
+ add_filter( 'jetpack_sync_sender_should_load', __NAMESPACE__ . '\Actions::should_initialize_sender_enqueue', 10, 1 );
+ if ( did_action( 'init' ) ) {
+ Actions::add_sender_shutdown();
+ }
+ }
+ }
+
+ /**
+ * Sync Data Loss Handler
+ *
+ * @param Queue $queue Sync queue.
+ * @return boolean was send successful
+ */
+ public function sync_data_loss( $queue ) {
+ if ( ! Settings::is_sync_enabled() ) {
+ return;
+ }
+ $updated = Health::update_status( Health::STATUS_OUT_OF_SYNC );
+
+ if ( ! $updated ) {
+ return;
+ }
+
+ $data = array(
+ 'timestamp' => microtime( true ),
+ 'queue_size' => $queue->size(),
+ 'queue_lag' => $queue->lag(),
+ );
+
+ $sender = Sender::get_instance();
+ return $sender->send_action( 'jetpack_sync_data_loss', $data );
+ }
+
+ /**
+ * Get the event's actor.
+ *
+ * @param string $current_filter Current wp-admin page.
+ * @param object $args Sync event.
+ * @return array Actor information.
+ */
+ public function get_actor( $current_filter, $args ) {
+ if ( 'wp_login' === $current_filter ) {
+ $user = get_user_by( 'ID', $args[1]->data->ID );
+ } else {
+ $user = wp_get_current_user();
+ }
+
+ $roles = new Roles();
+ $translated_role = $roles->translate_user_to_role( $user );
+
+ $actor = array(
+ 'wpcom_user_id' => null,
+ 'external_user_id' => isset( $user->ID ) ? $user->ID : null,
+ 'display_name' => isset( $user->display_name ) ? $user->display_name : null,
+ 'user_email' => isset( $user->user_email ) ? $user->user_email : null,
+ 'user_roles' => isset( $user->roles ) ? $user->roles : null,
+ 'translated_role' => $translated_role ? $translated_role : null,
+ 'is_cron' => defined( 'DOING_CRON' ) ? DOING_CRON : false,
+ 'is_rest' => defined( 'REST_API_REQUEST' ) ? REST_API_REQUEST : false,
+ 'is_xmlrpc' => defined( 'XMLRPC_REQUEST' ) ? XMLRPC_REQUEST : false,
+ 'is_wp_rest' => defined( 'REST_REQUEST' ) ? REST_REQUEST : false,
+ 'is_ajax' => defined( 'DOING_AJAX' ) ? DOING_AJAX : false,
+ 'is_wp_admin' => is_admin(),
+ 'is_cli' => defined( 'WP_CLI' ) ? WP_CLI : false,
+ 'from_url' => $this->get_request_url(),
+ );
+
+ if ( $this->should_send_user_data_with_actor( $current_filter ) ) {
+ $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
+ if ( defined( 'JETPACK__PLUGIN_DIR' ) ) {
+ if ( ! function_exists( 'jetpack_protect_get_ip' ) ) {
+ require_once JETPACK__PLUGIN_DIR . 'modules/protect/shared-functions.php';
+ }
+ $ip = jetpack_protect_get_ip();
+ }
+
+ $actor['ip'] = $ip;
+ $actor['user_agent'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : 'unknown';
+ }
+
+ return $actor;
+ }
+
+ /**
+ * Should user data be sent as the actor?
+ *
+ * @param string $current_filter The current WordPress filter being executed.
+ * @return bool
+ */
+ public function should_send_user_data_with_actor( $current_filter ) {
+ $should_send = in_array( $current_filter, array( 'jetpack_wp_login', 'wp_logout', 'jetpack_valid_failed_login_attempt' ), true );
+ /**
+ * Allow or deny sending actor's user data ( IP and UA ) during a sync event
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.8.0
+ *
+ * @module sync
+ *
+ * @param bool True if we should send user data
+ * @param string The current filter that is performing the sync action
+ */
+ return apply_filters( 'jetpack_sync_actor_user_data', $should_send, $current_filter );
+ }
+
+ /**
+ * Sets Listener defaults.
+ */
+ public function set_defaults() {
+ $this->sync_queue = new Queue( 'sync' );
+ $this->full_sync_queue = new Queue( 'full_sync' );
+ $this->set_queue_size_limit( Settings::get_setting( 'max_queue_size' ) );
+ $this->set_queue_lag_limit( Settings::get_setting( 'max_queue_lag' ) );
+ }
+
+ /**
+ * Get the request URL.
+ *
+ * @return string Request URL, if known. Otherwise, wp-admin or home_url.
+ */
+ public function get_request_url() {
+ if ( isset( $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'] ) ) {
+ return 'http' . ( isset( $_SERVER['HTTPS'] ) ? 's' : '' ) . '://' . "{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
+ }
+ return is_admin() ? get_admin_url( get_current_blog_id() ) : home_url();
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-lock.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-lock.php
new file mode 100644
index 00000000..61b89a1c
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-lock.php
@@ -0,0 +1,77 @@
+<?php
+/**
+ * Lock class.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+/**
+ * Lock class
+ */
+class Lock {
+ /**
+ * Prefix of the blog lock transient.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const LOCK_PREFIX = 'jp_sync_lock_';
+
+ /**
+ * Default Lifetime of the lock.
+ * This is the expiration value as such we are setting it high to handle cases where there are
+ * long running requests. Short expiration value leads to concurrent requests and performance issues.
+ *
+ * @access public
+ *
+ * @var int
+ */
+ const LOCK_TRANSIENT_EXPIRY = 180; // Seconds.
+
+ /**
+ * Attempt to lock.
+ *
+ * @access public
+ *
+ * @param string $name lock name.
+ * @param int $expiry lock duration in seconds.
+ *
+ * @return boolean True if succeeded, false otherwise.
+ */
+ public function attempt( $name, $expiry = self::LOCK_TRANSIENT_EXPIRY ) {
+ $lock_name = self::LOCK_PREFIX . $name;
+ $locked_time = get_option( $lock_name );
+
+ if ( $locked_time ) {
+ // If expired update to false but don't send. Send will occurr in new request to avoid race conditions.
+ if ( microtime( true ) > $locked_time ) {
+ update_option( $lock_name, false, false );
+ }
+ return false;
+ }
+
+ $locked_time = microtime( true ) + $expiry;
+ update_option( $lock_name, $locked_time, false );
+ return $locked_time;
+ }
+
+ /**
+ * Remove the lock.
+ *
+ * @access public
+ *
+ * @param string $name lock name.
+ * @param bool|float $lock_expiration lock expiration.
+ */
+ public function remove( $name, $lock_expiration = false ) {
+ $lock_name = self::LOCK_PREFIX . $name;
+
+ // Only remove lock if current value matches our lock.
+ if ( true === $lock_expiration || (string) get_option( $lock_name ) === (string) $lock_expiration ) {
+ update_option( $lock_name, false, false );
+ }
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-main.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-main.php
new file mode 100644
index 00000000..b7e590a9
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-main.php
@@ -0,0 +1,103 @@
+<?php
+/**
+ * This class hooks the main sync actions.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+use Automattic\Jetpack\Sync\Actions as Sync_Actions;
+
+/**
+ * Jetpack Sync main class.
+ */
+class Main {
+
+ /**
+ * Sets up event handlers for the Sync package. Is used from the Config package.
+ *
+ * @action plugins_loaded
+ */
+ public static function configure() {
+ if ( Actions::sync_allowed() ) {
+ add_action( 'plugins_loaded', array( __CLASS__, 'on_plugins_loaded_early' ), 5 );
+ add_action( 'plugins_loaded', array( __CLASS__, 'on_plugins_loaded_late' ), 90 );
+ }
+
+ // Add REST endpoints.
+ add_action( 'rest_api_init', array( 'Automattic\\Jetpack\\Sync\\REST_Endpoints', 'initialize_rest_api' ) );
+
+ // Add IDC disconnect action.
+ add_action( 'jetpack_idc_disconnect', array( __CLASS__, 'on_jetpack_idc_disconnect' ), 100 );
+
+ // Any hooks below are special cases that need to be declared even if Sync is not allowed.
+ add_action( 'jetpack_site_registered', array( 'Automattic\\Jetpack\\Sync\\Actions', 'do_initial_sync' ), 10, 0 );
+
+ // Set up package version hook.
+ add_filter( 'jetpack_package_versions', __NAMESPACE__ . '\Package_Version::send_package_version_to_tracker' );
+ }
+
+ /**
+ * Delete all sync related data on Identity Crisis disconnect.
+ */
+ public static function on_jetpack_idc_disconnect() {
+ Sender::get_instance()->uninstall();
+ }
+
+ /**
+ * Initialize the main sync actions.
+ *
+ * @action plugins_loaded
+ */
+ public static function on_plugins_loaded_early() {
+ /**
+ * Additional Sync modules can be carried out into their own packages and they
+ * will get their own config settings.
+ *
+ * For now additional modules are enabled based on whether the third party plugin
+ * class exists or not.
+ */
+ Sync_Actions::initialize_woocommerce();
+ Sync_Actions::initialize_wp_super_cache();
+
+ // We need to define this here so that it's hooked before `updating_jetpack_version` is called.
+ add_action( 'updating_jetpack_version', array( 'Automattic\\Jetpack\\Sync\\Actions', 'cleanup_on_upgrade' ), 10, 2 );
+ }
+
+ /**
+ * Runs after most of plugins_loaded hook functions have been run.
+ *
+ * @action plugins_loaded
+ */
+ public static function on_plugins_loaded_late() {
+ /*
+ * Init after plugins loaded and before the `init` action. This helps with issues where plugins init
+ * with a high priority or sites that use alternate cron.
+ */
+ Sync_Actions::init();
+
+ // Enable non-blocking Jetpack Sync flow.
+ $non_block_enabled = (bool) get_option( 'jetpack_sync_non_blocking', false );
+
+ /**
+ * Filters the option to enable non-blocking sync.
+ *
+ * Default value is false, filter to true to enable non-blocking mode which will have
+ * WP.com return early and use the sync/close endpoint to check-in processed items.
+ *
+ * @since 1.12.3
+ *
+ * @param bool $enabled Should non-blocking flow be enabled.
+ */
+ $filtered = (bool) apply_filters( 'jetpack_sync_non_blocking', $non_block_enabled );
+
+ if ( $non_block_enabled !== $filtered ) {
+ update_option( 'jetpack_sync_non_blocking', $filtered, false );
+ }
+
+ // Initialize health-related hooks after plugins have loaded.
+ Health::init();
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-modules.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-modules.php
new file mode 100644
index 00000000..993ebef5
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-modules.php
@@ -0,0 +1,160 @@
+<?php
+/**
+ * Simple wrapper that allows enumerating cached static instances
+ * of sync modules.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+use Automattic\Jetpack\Sync\Modules\Module;
+
+/**
+ * A class to handle loading of sync modules.
+ */
+class Modules {
+
+ /**
+ * Lists classnames of sync modules we load by default.
+ *
+ * @access public
+ *
+ * @var array
+ */
+ const DEFAULT_SYNC_MODULES = array(
+ 'Automattic\\Jetpack\\Sync\\Modules\\Constants',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Callables',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Network_Options',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Options',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Terms',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Menus',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Themes',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Users',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Import',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Posts',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Protect',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Comments',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Updates',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Attachments',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Meta',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Plugins',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Stats',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Full_Sync_Immediately',
+ 'Automattic\\Jetpack\\Sync\\Modules\\Term_Relationships',
+ );
+
+ /**
+ * Keeps track of initialized sync modules.
+ *
+ * @access private
+ * @static
+ *
+ * @var null|array
+ */
+ private static $initialized_modules = null;
+
+ /**
+ * Gets a list of initialized modules.
+ *
+ * @access public
+ * @static
+ *
+ * @return Module[]
+ */
+ public static function get_modules() {
+ if ( null === self::$initialized_modules ) {
+ self::$initialized_modules = self::initialize_modules();
+ }
+
+ return self::$initialized_modules;
+ }
+
+ /**
+ * Sets defaults for all initialized modules.
+ *
+ * @access public
+ * @static
+ */
+ public static function set_defaults() {
+ foreach ( self::get_modules() as $module ) {
+ $module->set_defaults();
+ }
+ }
+
+ /**
+ * Gets the name of an initialized module. Returns false if given module has not been initialized.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $module_name A module name.
+ *
+ * @return bool|Automattic\Jetpack\Sync\Modules\Module
+ */
+ public static function get_module( $module_name ) {
+ foreach ( self::get_modules() as $module ) {
+ if ( $module->name() === $module_name ) {
+ return $module;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Loads and sets defaults for all declared modules.
+ *
+ * @access public
+ * @static
+ *
+ * @return array
+ */
+ public static function initialize_modules() {
+ /**
+ * Filters the list of class names of sync modules.
+ * If you add to this list, make sure any classes implement the
+ * Jetpack_Sync_Module interface.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ */
+ $modules = apply_filters( 'jetpack_sync_modules', self::DEFAULT_SYNC_MODULES );
+
+ $modules = array_map( array( __CLASS__, 'load_module' ), $modules );
+
+ return array_map( array( __CLASS__, 'set_module_defaults' ), $modules );
+ }
+
+ /**
+ * Returns an instance of the given module class.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $module_class The classname of a Jetpack sync module.
+ *
+ * @return Automattic\Jetpack\Sync\Modules\Module
+ */
+ public static function load_module( $module_class ) {
+ return new $module_class();
+ }
+
+ /**
+ * Sets defaults for the given instance of a Jetpack sync module.
+ *
+ * @access public
+ * @static
+ *
+ * @param Automattic\Jetpack\Sync\Modules\Module $module Instance of a Jetpack sync module.
+ *
+ * @return Automattic\Jetpack\Sync\Modules\Module
+ */
+ public static function set_module_defaults( $module ) {
+ $module->set_defaults();
+ if ( method_exists( $module, 'set_late_default' ) ) {
+ add_action( 'init', array( $module, 'set_late_default' ), 90 );
+ }
+ return $module;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-package-version.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-package-version.php
new file mode 100644
index 00000000..69a9faf3
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-package-version.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * The Package_Version class.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+/**
+ * The Package_Version class.
+ */
+class Package_Version {
+
+ const PACKAGE_VERSION = '1.28.0';
+
+ const PACKAGE_SLUG = 'sync';
+
+ /**
+ * Adds the package slug and version to the package version tracker's data.
+ *
+ * @param array $package_versions The package version array.
+ *
+ * @return array The packge version array.
+ */
+ public static function send_package_version_to_tracker( $package_versions ) {
+ $package_versions[ self::PACKAGE_SLUG ] = self::PACKAGE_VERSION;
+ return $package_versions;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-queue-buffer.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-queue-buffer.php
new file mode 100644
index 00000000..94735e3a
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-queue-buffer.php
@@ -0,0 +1,78 @@
+<?php
+/**
+ * Sync queue buffer.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+/**
+ * A buffer of items from the queue that can be checked out.
+ */
+class Queue_Buffer {
+ /**
+ * Sync queue buffer ID.
+ *
+ * @access public
+ *
+ * @var int
+ */
+ public $id;
+
+ /**
+ * Sync items.
+ *
+ * @access public
+ *
+ * @var array
+ */
+ public $items_with_ids;
+
+ /**
+ * Constructor.
+ * Initializes the queue buffer.
+ *
+ * @access public
+ *
+ * @param int $id Sync queue buffer ID.
+ * @param array $items_with_ids Items for the buffer to work with.
+ */
+ public function __construct( $id, $items_with_ids ) {
+ $this->id = $id;
+ $this->items_with_ids = $items_with_ids;
+ }
+
+ /**
+ * Retrieve the sync items in the buffer, in an ID => value form.
+ *
+ * @access public
+ *
+ * @return bool|array Sync items in the buffer.
+ */
+ public function get_items() {
+ return array_combine( $this->get_item_ids(), $this->get_item_values() );
+ }
+
+ /**
+ * Retrieve the values of the sync items in the buffer.
+ *
+ * @access public
+ *
+ * @return array Sync items values.
+ */
+ public function get_item_values() {
+ return Utils::get_item_values( $this->items_with_ids );
+ }
+
+ /**
+ * Retrieve the IDs of the sync items in the buffer.
+ *
+ * @access public
+ *
+ * @return array Sync items IDs.
+ */
+ public function get_item_ids() {
+ return Utils::get_item_ids( $this->items_with_ids );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-queue.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-queue.php
new file mode 100644
index 00000000..fe80cf90
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-queue.php
@@ -0,0 +1,744 @@
+<?php
+/**
+ * The class that describes the Queue for the sync package.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+use WP_Error;
+
+/**
+ * A persistent queue that can be flushed in increments of N items,
+ * and which blocks reads until checked-out buffers are checked in or
+ * closed. This uses raw SQL for two reasons: speed, and not triggering
+ * tons of added_option callbacks.
+ */
+class Queue {
+ /**
+ * The queue id.
+ *
+ * @var string
+ */
+ public $id;
+ /**
+ * Keeps track of the rows.
+ *
+ * @var int
+ */
+ private $row_iterator;
+
+ /**
+ * Queue constructor.
+ *
+ * @param string $id Name of the queue.
+ */
+ public function __construct( $id ) {
+ $this->id = str_replace( '-', '_', $id ); // Necessary to ensure we don't have ID collisions in the SQL.
+ $this->row_iterator = 0;
+ $this->random_int = wp_rand( 1, 1000000 );
+ }
+
+ /**
+ * Add a single item to the queue.
+ *
+ * @param object $item Event object to add to queue.
+ */
+ public function add( $item ) {
+ global $wpdb;
+ $added = false;
+
+ // If empty, don't add.
+ if ( empty( $item ) ) {
+ return;
+ }
+
+ // Attempt to serialize data, if an exception (closures) return early.
+ try {
+ $item = serialize( $item ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
+ } catch ( \Exception $ex ) {
+ return;
+ }
+
+ // This basically tries to add the option until enough time has elapsed that
+ // it has a unique (microtime-based) option key.
+ while ( ! $added ) {
+ $rows_added = $wpdb->query(
+ $wpdb->prepare(
+ "INSERT INTO $wpdb->options (option_name, option_value, autoload) VALUES (%s, %s,%s)",
+ $this->get_next_data_row_option_name(),
+ $item,
+ 'no'
+ )
+ );
+ $added = ( 0 !== $rows_added );
+ }
+ }
+
+ /**
+ * Insert all the items in a single SQL query. May be subject to query size limits!
+ *
+ * @param array $items Array of events to add to the queue.
+ *
+ * @return bool|\WP_Error
+ */
+ public function add_all( $items ) {
+ global $wpdb;
+ $base_option_name = $this->get_next_data_row_option_name();
+
+ $query = "INSERT INTO $wpdb->options (option_name, option_value, autoload) VALUES ";
+
+ $rows = array();
+ $count_items = count( $items );
+ for ( $i = 0; $i < $count_items; ++$i ) {
+ // skip empty items.
+ if ( empty( $items[ $i ] ) ) {
+ continue;
+ }
+ try {
+ $option_name = esc_sql( $base_option_name . '-' . $i );
+ $option_value = esc_sql( serialize( $items[ $i ] ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
+ $rows[] = "('$option_name', '$option_value', 'no')";
+ } catch ( \Exception $e ) {
+ // Item cannot be serialized so skip.
+ continue;
+ }
+ }
+
+ $rows_added = $wpdb->query( $query . join( ',', $rows ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+
+ if ( count( $items ) !== $rows_added ) {
+ return new WP_Error( 'row_count_mismatch', "The number of rows inserted didn't match the size of the input array" );
+ }
+ return true;
+ }
+
+ /**
+ * Get the front-most item on the queue without checking it out.
+ *
+ * @param int $count Number of items to return when looking at the items.
+ *
+ * @return array
+ */
+ public function peek( $count = 1 ) {
+ $items = $this->fetch_items( $count );
+ if ( $items ) {
+ return Utils::get_item_values( $items );
+ }
+
+ return array();
+ }
+
+ /**
+ * Gets items with particular IDs.
+ *
+ * @param array $item_ids Array of item IDs to retrieve.
+ *
+ * @return array
+ */
+ public function peek_by_id( $item_ids ) {
+ $items = $this->fetch_items_by_id( $item_ids );
+ if ( $items ) {
+ return Utils::get_item_values( $items );
+ }
+
+ return array();
+ }
+
+ /**
+ * Gets the queue lag.
+ * Lag is the difference in time between the age of the oldest item
+ * (aka first or frontmost item) and the current time.
+ *
+ * @param microtime $now The current time in microtime.
+ *
+ * @return float|int|mixed|null
+ */
+ public function lag( $now = null ) {
+ global $wpdb;
+
+ $first_item_name = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s ORDER BY option_name ASC LIMIT 1",
+ "jpsq_{$this->id}-%"
+ )
+ );
+
+ if ( ! $first_item_name ) {
+ return 0;
+ }
+
+ if ( null === $now ) {
+ $now = microtime( true );
+ }
+
+ // Break apart the item name to get the timestamp.
+ $matches = null;
+ if ( preg_match( '/^jpsq_' . $this->id . '-(\d+\.\d+)-/', $first_item_name, $matches ) ) {
+ return $now - (float) $matches[1];
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Resets the queue.
+ */
+ public function reset() {
+ global $wpdb;
+ $this->delete_checkout_id();
+ $wpdb->query(
+ $wpdb->prepare(
+ "DELETE FROM $wpdb->options WHERE option_name LIKE %s",
+ "jpsq_{$this->id}-%"
+ )
+ );
+ }
+
+ /**
+ * Return the size of the queue.
+ *
+ * @return int
+ */
+ public function size() {
+ global $wpdb;
+
+ return (int) $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT count(*) FROM $wpdb->options WHERE option_name LIKE %s",
+ "jpsq_{$this->id}-%"
+ )
+ );
+ }
+
+ /**
+ * Lets you know if there is any items in the queue.
+ *
+ * We use this peculiar implementation because it's much faster than count(*).
+ *
+ * @return bool
+ */
+ public function has_any_items() {
+ global $wpdb;
+ $value = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT exists( SELECT option_name FROM $wpdb->options WHERE option_name LIKE %s )",
+ "jpsq_{$this->id}-%"
+ )
+ );
+
+ return ( '1' === $value );
+ }
+
+ /**
+ * Used to checkout the queue.
+ *
+ * @param int $buffer_size Size of the buffer to checkout.
+ *
+ * @return Automattic\Jetpack\Sync\Queue_Buffer|bool|int|\WP_Error
+ */
+ public function checkout( $buffer_size ) {
+ if ( $this->get_checkout_id() ) {
+ return new WP_Error( 'unclosed_buffer', 'There is an unclosed buffer' );
+ }
+
+ $buffer_id = uniqid();
+
+ $result = $this->set_checkout_id( $buffer_id );
+
+ if ( ! $result || is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ $items = $this->fetch_items( $buffer_size );
+
+ if ( count( $items ) === 0 ) {
+ return false;
+ }
+
+ $buffer = new Queue_Buffer( $buffer_id, array_slice( $items, 0, $buffer_size ) );
+
+ return $buffer;
+ }
+
+ /**
+ * Given a list of items return the items ids.
+ *
+ * @param array $items List of item objects.
+ *
+ * @return array Ids of the items.
+ */
+ public function get_ids( $items ) {
+ return array_map(
+ function ( $item ) {
+ return $item->id;
+ },
+ $items
+ );
+ }
+
+ /**
+ * Pop elements from the queue.
+ *
+ * @param int $limit Number of items to pop from the queue.
+ *
+ * @return array|object|null
+ */
+ public function pop( $limit ) {
+ $items = $this->fetch_items( $limit );
+
+ $ids = $this->get_ids( $items );
+
+ $this->delete( $ids );
+
+ return $items;
+ }
+
+ /**
+ * Get the items from the queue with a memory limit.
+ *
+ * This checks out rows until it either empties the queue or hits a certain memory limit
+ * it loads the sizes from the DB first so that it doesn't accidentally
+ * load more data into memory than it needs to.
+ * The only way it will load more items than $max_size is if a single queue item
+ * exceeds the memory limit, but in that case it will send that item by itself.
+ *
+ * @param int $max_memory (bytes) Maximum memory threshold.
+ * @param int $max_buffer_size Maximum buffer size (number of items).
+ *
+ * @return Automattic\Jetpack\Sync\Queue_Buffer|bool|int|\WP_Error
+ */
+ public function checkout_with_memory_limit( $max_memory, $max_buffer_size = 500 ) {
+ if ( $this->get_checkout_id() ) {
+ return new WP_Error( 'unclosed_buffer', 'There is an unclosed buffer' );
+ }
+
+ $buffer_id = uniqid();
+
+ $result = $this->set_checkout_id( $buffer_id );
+
+ if ( ! $result || is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ // Get the map of buffer_id -> memory_size.
+ global $wpdb;
+
+ $items_with_size = $wpdb->get_results(
+ $wpdb->prepare(
+ "SELECT option_name AS id, LENGTH(option_value) AS value_size FROM $wpdb->options WHERE option_name LIKE %s ORDER BY option_name ASC LIMIT %d",
+ "jpsq_{$this->id}-%",
+ $max_buffer_size
+ ),
+ OBJECT
+ );
+
+ if ( count( $items_with_size ) === 0 ) {
+ return false;
+ }
+
+ $total_memory = 0;
+ $max_item_id = $items_with_size[0]->id;
+ $min_item_id = $max_item_id;
+
+ foreach ( $items_with_size as $id => $item_with_size ) {
+ $total_memory += $item_with_size->value_size;
+
+ // If this is the first item and it exceeds memory, allow loop to continue
+ // we will exit on the next iteration instead.
+ if ( $total_memory > $max_memory && $id > 0 ) {
+ break;
+ }
+
+ $max_item_id = $item_with_size->id;
+ }
+
+ $query = $wpdb->prepare(
+ "SELECT option_name AS id, option_value AS value FROM $wpdb->options WHERE option_name >= %s and option_name <= %s ORDER BY option_name ASC",
+ $min_item_id,
+ $max_item_id
+ );
+
+ $items = $wpdb->get_results( $query, OBJECT ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ foreach ( $items as $item ) {
+ // @codingStandardsIgnoreStart
+ $item->value = @unserialize( $item->value );
+ // @codingStandardsIgnoreEnd
+ }
+
+ if ( count( $items ) === 0 ) {
+ $this->delete_checkout_id();
+
+ return false;
+ }
+
+ $buffer = new Queue_Buffer( $buffer_id, $items );
+
+ return $buffer;
+ }
+
+ /**
+ * Check in the queue.
+ *
+ * @param Automattic\Jetpack\Sync\Queue_Buffer $buffer Queue_Buffer object.
+ *
+ * @return bool|\WP_Error
+ */
+ public function checkin( $buffer ) {
+ $is_valid = $this->validate_checkout( $buffer );
+
+ if ( is_wp_error( $is_valid ) ) {
+ return $is_valid;
+ }
+
+ $this->delete_checkout_id();
+
+ return true;
+ }
+
+ /**
+ * Close the buffer.
+ *
+ * @param Automattic\Jetpack\Sync\Queue_Buffer $buffer Queue_Buffer object.
+ * @param null|array $ids_to_remove Ids to remove from the queue.
+ *
+ * @return bool|\WP_Error
+ */
+ public function close( $buffer, $ids_to_remove = null ) {
+ $is_valid = $this->validate_checkout( $buffer );
+
+ if ( is_wp_error( $is_valid ) ) {
+ // Always delete ids_to_remove even when buffer is no longer checked-out.
+ // They were processed by WP.com so safe to remove from queue.
+ if ( ! is_null( $ids_to_remove ) ) {
+ $this->delete( $ids_to_remove );
+ }
+ return $is_valid;
+ }
+
+ $this->delete_checkout_id();
+
+ // By default clear all items in the buffer.
+ if ( is_null( $ids_to_remove ) ) {
+ $ids_to_remove = $buffer->get_item_ids();
+ }
+
+ $this->delete( $ids_to_remove );
+
+ return true;
+ }
+
+ /**
+ * Delete elements from the queue.
+ *
+ * @param array $ids Ids to delete.
+ *
+ * @return bool|int
+ */
+ private function delete( $ids ) {
+ if ( 0 === count( $ids ) ) {
+ return 0;
+ }
+ global $wpdb;
+ $sql = "DELETE FROM $wpdb->options WHERE option_name IN (" . implode( ', ', array_fill( 0, count( $ids ), '%s' ) ) . ')';
+ $query = call_user_func_array( array( $wpdb, 'prepare' ), array_merge( array( $sql ), $ids ) );
+
+ return $wpdb->query( $query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ }
+
+ /**
+ * Flushes all items from the queue.
+ *
+ * @return array
+ */
+ public function flush_all() {
+ $items = Utils::get_item_values( $this->fetch_items() );
+ $this->reset();
+
+ return $items;
+ }
+
+ /**
+ * Get all the items from the queue.
+ *
+ * @return array|object|null
+ */
+ public function get_all() {
+ return $this->fetch_items();
+ }
+
+ /**
+ * Forces Checkin of the queue.
+ * Use with caution, this could allow multiple processes to delete
+ * and send from the queue at the same time
+ */
+ public function force_checkin() {
+ $this->delete_checkout_id();
+ }
+
+ /**
+ * Locks checkouts from the queue
+ * tries to wait up to $timeout seconds for the queue to be empty.
+ *
+ * @param int $timeout The wait time in seconds for the queue to be empty.
+ *
+ * @return bool|int|\WP_Error
+ */
+ public function lock( $timeout = 30 ) {
+ $tries = 0;
+
+ while ( $this->has_any_items() && $tries < $timeout ) {
+ sleep( 1 );
+ ++$tries;
+ }
+
+ if ( 30 === $tries ) {
+ return new WP_Error( 'lock_timeout', 'Timeout waiting for sync queue to empty' );
+ }
+
+ if ( $this->get_checkout_id() ) {
+ return new WP_Error( 'unclosed_buffer', 'There is an unclosed buffer' );
+ }
+
+ // Hopefully this means we can acquire a checkout?
+ $result = $this->set_checkout_id( 'lock' );
+
+ if ( ! $result || is_wp_error( $result ) ) {
+ return $result;
+ }
+
+ return true;
+ }
+
+ /**
+ * Unlocks the queue.
+ *
+ * @return bool|int
+ */
+ public function unlock() {
+ return $this->delete_checkout_id();
+ }
+
+ /**
+ * This option is specifically chosen to, as much as possible, preserve time order
+ * and minimise the possibility of collisions between multiple processes working
+ * at the same time.
+ *
+ * @return string
+ */
+ protected function generate_option_name_timestamp() {
+ return sprintf( '%.6f', microtime( true ) );
+ }
+
+ /**
+ * Gets the checkout ID.
+ *
+ * @return bool|string
+ */
+ private function get_checkout_id() {
+ global $wpdb;
+ $checkout_value = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT option_value FROM $wpdb->options WHERE option_name = %s",
+ $this->get_lock_option_name()
+ )
+ );
+
+ if ( $checkout_value ) {
+ list( $checkout_id, $timestamp ) = explode( ':', $checkout_value );
+ if ( (int) $timestamp > time() ) {
+ return $checkout_id;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Sets the checkout id.
+ *
+ * @param string $checkout_id The ID of the checkout.
+ *
+ * @return bool|int
+ */
+ private function set_checkout_id( $checkout_id ) {
+ global $wpdb;
+
+ $expires = time() + Defaults::$default_sync_queue_lock_timeout;
+ $updated_num = $wpdb->query(
+ $wpdb->prepare(
+ "UPDATE $wpdb->options SET option_value = %s WHERE option_name = %s",
+ "$checkout_id:$expires",
+ $this->get_lock_option_name()
+ )
+ );
+
+ if ( ! $updated_num ) {
+ $updated_num = $wpdb->query(
+ $wpdb->prepare(
+ "INSERT INTO $wpdb->options ( option_name, option_value, autoload ) VALUES ( %s, %s, 'no' )",
+ $this->get_lock_option_name(),
+ "$checkout_id:$expires"
+ )
+ );
+ }
+
+ return $updated_num;
+ }
+
+ /**
+ * Deletes the checkout ID.
+ *
+ * @return bool|int
+ */
+ private function delete_checkout_id() {
+ global $wpdb;
+ // Rather than delete, which causes fragmentation, we update in place.
+ return $wpdb->query(
+ $wpdb->prepare(
+ "UPDATE $wpdb->options SET option_value = %s WHERE option_name = %s",
+ '0:0',
+ $this->get_lock_option_name()
+ )
+ );
+
+ }
+
+ /**
+ * Return the lock option name.
+ *
+ * @return string
+ */
+ private function get_lock_option_name() {
+ return "jpsq_{$this->id}_checkout";
+ }
+
+ /**
+ * Return the next data row option name.
+ *
+ * @return string
+ */
+ private function get_next_data_row_option_name() {
+ $timestamp = $this->generate_option_name_timestamp();
+
+ // Row iterator is used to avoid collisions where we're writing data waaay fast in a single process.
+ if ( PHP_INT_MAX === $this->row_iterator ) {
+ $this->row_iterator = 0;
+ } else {
+ $this->row_iterator += 1;
+ }
+
+ return 'jpsq_' . $this->id . '-' . $timestamp . '-' . $this->random_int . '-' . $this->row_iterator;
+ }
+
+ /**
+ * Return the items in the queue.
+ *
+ * @param null|int $limit Limit to the number of items we fetch at once.
+ *
+ * @return array|object|null
+ */
+ private function fetch_items( $limit = null ) {
+ global $wpdb;
+
+ if ( $limit ) {
+ $items = $wpdb->get_results(
+ $wpdb->prepare(
+ "SELECT option_name AS id, option_value AS value FROM $wpdb->options WHERE option_name LIKE %s ORDER BY option_name ASC LIMIT %d",
+ "jpsq_{$this->id}-%",
+ $limit
+ ),
+ OBJECT
+ );
+ } else {
+ $items = $wpdb->get_results(
+ $wpdb->prepare(
+ "SELECT option_name AS id, option_value AS value FROM $wpdb->options WHERE option_name LIKE %s ORDER BY option_name ASC",
+ "jpsq_{$this->id}-%"
+ ),
+ OBJECT
+ );
+ }
+
+ return $this->unserialize_values( $items );
+
+ }
+
+ /**
+ * Return items with specific ids.
+ *
+ * @param array $items_ids Array of event ids.
+ *
+ * @return array|object|null
+ */
+ private function fetch_items_by_id( $items_ids ) {
+ global $wpdb;
+
+ // return early if $items_ids is empty or not an array.
+ if ( empty( $items_ids ) || ! is_array( $items_ids ) ) {
+ return null;
+ }
+
+ $ids_placeholders = implode( ', ', array_fill( 0, count( $items_ids ), '%s' ) );
+ $query_with_placeholders = "SELECT option_name AS id, option_value AS value
+ FROM $wpdb->options
+ WHERE option_name IN ( $ids_placeholders )";
+ $items = $wpdb->get_results(
+ $wpdb->prepare(
+ $query_with_placeholders, // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $items_ids
+ ),
+ OBJECT
+ );
+
+ return $this->unserialize_values( $items );
+ }
+
+ /**
+ * Unserialize item values.
+ *
+ * @param array $items Events from the Queue to be unserialized.
+ *
+ * @return mixed
+ */
+ private function unserialize_values( $items ) {
+ array_walk(
+ $items,
+ function ( $item ) {
+ // @codingStandardsIgnoreStart
+ $item->value = @unserialize( $item->value );
+ // @codingStandardsIgnoreEnd
+ }
+ );
+
+ return $items;
+
+ }
+
+ /**
+ * Return true if the buffer is still valid or an Error other wise.
+ *
+ * @param Automattic\Jetpack\Sync\Queue_Buffer $buffer The Queue_Buffer.
+ *
+ * @return bool|WP_Error
+ */
+ private function validate_checkout( $buffer ) {
+ if ( ! $buffer instanceof Queue_Buffer ) {
+ return new WP_Error( 'not_a_buffer', 'You must checkin an instance of Automattic\\Jetpack\\Sync\\Queue_Buffer' );
+ }
+
+ $checkout_id = $this->get_checkout_id();
+
+ if ( ! $checkout_id ) {
+ return new WP_Error( 'buffer_not_checked_out', 'There are no checked out buffers' );
+ }
+
+ // TODO: change to strict comparison.
+ if ( $checkout_id != $buffer->id ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
+ return new WP_Error( 'buffer_mismatch', 'The buffer you checked in was not checked out' );
+ }
+
+ return true;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-replicastore.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-replicastore.php
new file mode 100644
index 00000000..6687fec5
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-replicastore.php
@@ -0,0 +1,1457 @@
+<?php
+/**
+ * Sync replicastore.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+use Automattic\Jetpack\Sync\Replicastore\Table_Checksum;
+use Automattic\Jetpack\Sync\Replicastore\Table_Checksum_Usermeta;
+use Automattic\Jetpack\Sync\Replicastore\Table_Checksum_Users;
+use Exception;
+use WP_Error;
+
+/**
+ * An implementation of Replicastore Interface which returns data stored in a WordPress.org DB.
+ * This is useful to compare values in the local WP DB to values in the synced replica store
+ */
+class Replicastore implements Replicastore_Interface {
+ /**
+ * Empty and reset the replicastore.
+ *
+ * @access public
+ */
+ public function reset() {
+ global $wpdb;
+
+ $wpdb->query( "DELETE FROM $wpdb->posts" );
+
+ // Delete comments from cache.
+ $comment_ids = $wpdb->get_col( "SELECT comment_ID FROM $wpdb->comments" );
+ if ( ! empty( $comment_ids ) ) {
+ clean_comment_cache( $comment_ids );
+ }
+ $wpdb->query( "DELETE FROM $wpdb->comments" );
+
+ // Also need to delete terms from cache.
+ $term_ids = $wpdb->get_col( "SELECT term_id FROM $wpdb->terms" );
+ foreach ( $term_ids as $term_id ) {
+ wp_cache_delete( $term_id, 'terms' );
+ }
+
+ $wpdb->query( "DELETE FROM $wpdb->terms" );
+
+ $wpdb->query( "DELETE FROM $wpdb->term_taxonomy" );
+ $wpdb->query( "DELETE FROM $wpdb->term_relationships" );
+
+ // Callables and constants.
+ $wpdb->query( "DELETE FROM $wpdb->options WHERE option_name LIKE 'jetpack_%'" );
+ $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key NOT LIKE '\_%'" );
+ }
+
+ /**
+ * Ran when full sync has just started.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ */
+ public function full_sync_start( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $this->reset();
+ }
+
+ /**
+ * Ran when full sync has just finished.
+ *
+ * @access public
+ *
+ * @param string $checksum Deprecated since 7.3.0.
+ */
+ public function full_sync_end( $checksum ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // Noop right now.
+ }
+
+ /**
+ * Retrieve the number of terms.
+ *
+ * @access public
+ *
+ * @return int Number of terms.
+ */
+ public function term_count() {
+ global $wpdb;
+ return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->terms" );
+ }
+
+ /**
+ * Retrieve the number of rows in the `term_taxonomy` table.
+ *
+ * @access public
+ *
+ * @return int Number of terms.
+ */
+ public function term_taxonomy_count() {
+ global $wpdb;
+ return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_taxonomy" );
+ }
+
+ /**
+ * Retrieve the number of term relationships.
+ *
+ * @access public
+ *
+ * @return int Number of rows in the term relationships table.
+ */
+ public function term_relationship_count() {
+ global $wpdb;
+ return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->term_relationships" );
+ }
+
+ /**
+ * Retrieve the number of posts with a particular post status within a certain range.
+ *
+ * @access public
+ *
+ * @todo Prepare the SQL query before executing it.
+ *
+ * @param string $status Post status.
+ * @param int $min_id Minimum post ID.
+ * @param int $max_id Maximum post ID.
+ * @return int Number of posts.
+ */
+ public function post_count( $status = null, $min_id = null, $max_id = null ) {
+ global $wpdb;
+
+ $where = '';
+
+ if ( $status ) {
+ $where = "post_status = '" . esc_sql( $status ) . "'";
+ } else {
+ $where = '1=1';
+ }
+
+ if ( ! empty( $min_id ) ) {
+ $where .= ' AND ID >= ' . (int) $min_id;
+ }
+
+ if ( ! empty( $max_id ) ) {
+ $where .= ' AND ID <= ' . (int) $max_id;
+ }
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->posts WHERE $where" );
+ }
+
+ /**
+ * Retrieve the posts with a particular post status.
+ *
+ * @access public
+ *
+ * @todo Implement range and actually use max_id/min_id arguments.
+ *
+ * @param string $status Post status.
+ * @param int $min_id Minimum post ID.
+ * @param int $max_id Maximum post ID.
+ * @return array Array of posts.
+ */
+ public function get_posts( $status = null, $min_id = null, $max_id = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $args = array(
+ 'orderby' => 'ID',
+ 'posts_per_page' => -1,
+ );
+
+ if ( $status ) {
+ $args['post_status'] = $status;
+ } else {
+ $args['post_status'] = 'any';
+ }
+
+ return get_posts( $args );
+ }
+
+ /**
+ * Retrieve a post object by the post ID.
+ *
+ * @access public
+ *
+ * @param int $id Post ID.
+ * @return \WP_Post Post object.
+ */
+ public function get_post( $id ) {
+ return get_post( $id );
+ }
+
+ /**
+ * Update or insert a post.
+ *
+ * @access public
+ *
+ * @param \WP_Post $post Post object.
+ * @param bool $silent Whether to perform a silent action. Not used in this implementation.
+ */
+ public function upsert_post( $post, $silent = false ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ global $wpdb;
+
+ // Reject the post if it's not a \WP_Post.
+ if ( ! $post instanceof \WP_Post ) {
+ return;
+ }
+
+ $post = $post->to_array();
+
+ // Reject posts without an ID.
+ if ( ! isset( $post['ID'] ) ) {
+ return;
+ }
+
+ $now = current_time( 'mysql' );
+ $now_gmt = get_gmt_from_date( $now );
+
+ $defaults = array(
+ 'ID' => 0,
+ 'post_author' => '0',
+ 'post_content' => '',
+ 'post_content_filtered' => '',
+ 'post_title' => '',
+ 'post_name' => '',
+ 'post_excerpt' => '',
+ 'post_status' => 'draft',
+ 'post_type' => 'post',
+ 'comment_status' => 'closed',
+ 'comment_count' => '0',
+ 'ping_status' => '',
+ 'post_password' => '',
+ 'to_ping' => '',
+ 'pinged' => '',
+ 'post_parent' => 0,
+ 'menu_order' => 0,
+ 'guid' => '',
+ 'post_date' => $now,
+ 'post_date_gmt' => $now_gmt,
+ 'post_modified' => $now,
+ 'post_modified_gmt' => $now_gmt,
+ );
+
+ $post = array_intersect_key( $post, $defaults );
+
+ $post = sanitize_post( $post, 'db' );
+
+ unset( $post['filter'] );
+
+ $exists = $wpdb->get_var( $wpdb->prepare( "SELECT EXISTS( SELECT 1 FROM $wpdb->posts WHERE ID = %d )", $post['ID'] ) );
+
+ if ( $exists ) {
+ $wpdb->update( $wpdb->posts, $post, array( 'ID' => $post['ID'] ) );
+ } else {
+ $wpdb->insert( $wpdb->posts, $post );
+ }
+
+ clean_post_cache( $post['ID'] );
+ }
+
+ /**
+ * Delete a post by the post ID.
+ *
+ * @access public
+ *
+ * @param int $post_id Post ID.
+ */
+ public function delete_post( $post_id ) {
+ wp_delete_post( $post_id, true );
+ }
+
+ /**
+ * Retrieve the checksum for posts within a range.
+ *
+ * @access public
+ *
+ * @param int $min_id Minimum post ID.
+ * @param int $max_id Maximum post ID.
+ * @return int The checksum.
+ */
+ public function posts_checksum( $min_id = null, $max_id = null ) {
+ return $this->summarize_checksum_histogram( $this->checksum_histogram( 'posts', null, $min_id, $max_id ) );
+ }
+
+ /**
+ * Retrieve the checksum for post meta within a range.
+ *
+ * @access public
+ *
+ * @param int $min_id Minimum post meta ID.
+ * @param int $max_id Maximum post meta ID.
+ * @return int The checksum.
+ */
+ public function post_meta_checksum( $min_id = null, $max_id = null ) {
+ return $this->summarize_checksum_histogram( $this->checksum_histogram( 'postmeta', null, $min_id, $max_id ) );
+ }
+
+ /**
+ * Retrieve the number of comments with a particular comment status within a certain range.
+ *
+ * @access public
+ *
+ * @todo Prepare the SQL query before executing it.
+ *
+ * @param string $status Comment status.
+ * @param int $min_id Minimum comment ID.
+ * @param int $max_id Maximum comment ID.
+ * @return int Number of comments.
+ */
+ public function comment_count( $status = null, $min_id = null, $max_id = null ) {
+ global $wpdb;
+
+ $comment_approved = $this->comment_status_to_approval_value( $status );
+
+ if ( false !== $comment_approved ) {
+ $where = "comment_approved = '" . esc_sql( $comment_approved ) . "'";
+ } else {
+ $where = '1=1';
+ }
+
+ if ( ! empty( $min_id ) ) {
+ $where .= ' AND comment_ID >= ' . (int) $min_id;
+ }
+
+ if ( ! empty( $max_id ) ) {
+ $where .= ' AND comment_ID <= ' . (int) $max_id;
+ }
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ return $wpdb->get_var( "SELECT COUNT(*) FROM $wpdb->comments WHERE $where" );
+ }
+
+ /**
+ * Translate a comment status to a value of the comment_approved field.
+ *
+ * @access protected
+ *
+ * @param string $status Comment status.
+ * @return string|bool New comment_approved value, false if the status doesn't affect it.
+ */
+ protected function comment_status_to_approval_value( $status ) {
+ switch ( (string) $status ) {
+ case 'approve':
+ case '1':
+ return '1';
+ case 'hold':
+ case '0':
+ return '0';
+ case 'spam':
+ return 'spam';
+ case 'trash':
+ return 'trash';
+ case 'post-trashed':
+ return 'post-trashed';
+ case 'any':
+ case 'all':
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Retrieve the comments with a particular comment status.
+ *
+ * @access public
+ *
+ * @todo Implement range and actually use max_id/min_id arguments.
+ *
+ * @param string $status Comment status.
+ * @param int $min_id Minimum comment ID.
+ * @param int $max_id Maximum comment ID.
+ * @return array Array of comments.
+ */
+ public function get_comments( $status = null, $min_id = null, $max_id = null ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $args = array(
+ 'orderby' => 'ID',
+ 'status' => 'all',
+ );
+
+ if ( $status ) {
+ $args['status'] = $status;
+ }
+
+ return get_comments( $args );
+ }
+
+ /**
+ * Retrieve a comment object by the comment ID.
+ *
+ * @access public
+ *
+ * @param int $id Comment ID.
+ * @return \WP_Comment Comment object.
+ */
+ public function get_comment( $id ) {
+ return \WP_Comment::get_instance( $id );
+ }
+
+ /**
+ * Update or insert a comment.
+ *
+ * @access public
+ *
+ * @param \WP_Comment $comment Comment object.
+ */
+ public function upsert_comment( $comment ) {
+ global $wpdb;
+
+ $comment = $comment->to_array();
+
+ // Filter by fields on comment table.
+ $comment_fields_whitelist = 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',
+ );
+
+ foreach ( $comment as $key => $value ) {
+ if ( ! in_array( $key, $comment_fields_whitelist, true ) ) {
+ unset( $comment[ $key ] );
+ }
+ }
+
+ $exists = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT EXISTS( SELECT 1 FROM $wpdb->comments WHERE comment_ID = %d )",
+ $comment['comment_ID']
+ )
+ );
+
+ if ( $exists ) {
+ $wpdb->update( $wpdb->comments, $comment, array( 'comment_ID' => $comment['comment_ID'] ) );
+ } else {
+ $wpdb->insert( $wpdb->comments, $comment );
+ }
+ // Remove comment from cache.
+ clean_comment_cache( $comment['comment_ID'] );
+
+ wp_update_comment_count( $comment['comment_post_ID'] );
+ }
+
+ /**
+ * Trash a comment by the comment ID.
+ *
+ * @access public
+ *
+ * @param int $comment_id Comment ID.
+ */
+ public function trash_comment( $comment_id ) {
+ wp_delete_comment( $comment_id );
+ }
+
+ /**
+ * Delete a comment by the comment ID.
+ *
+ * @access public
+ *
+ * @param int $comment_id Comment ID.
+ */
+ public function delete_comment( $comment_id ) {
+ wp_delete_comment( $comment_id, true );
+ }
+
+ /**
+ * Mark a comment by the comment ID as spam.
+ *
+ * @access public
+ *
+ * @param int $comment_id Comment ID.
+ */
+ public function spam_comment( $comment_id ) {
+ wp_spam_comment( $comment_id );
+ }
+
+ /**
+ * Trash the comments of a post.
+ *
+ * @access public
+ *
+ * @param int $post_id Post ID.
+ * @param array $statuses Post statuses. Not used in this implementation.
+ */
+ public function trashed_post_comments( $post_id, $statuses ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ wp_trash_post_comments( $post_id );
+ }
+
+ /**
+ * Untrash the comments of a post.
+ *
+ * @access public
+ *
+ * @param int $post_id Post ID.
+ */
+ public function untrashed_post_comments( $post_id ) {
+ wp_untrash_post_comments( $post_id );
+ }
+
+ /**
+ * Retrieve the checksum for comments within a range.
+ *
+ * @access public
+ *
+ * @param int $min_id Minimum comment ID.
+ * @param int $max_id Maximum comment ID.
+ * @return int The checksum.
+ */
+ public function comments_checksum( $min_id = null, $max_id = null ) {
+ return $this->summarize_checksum_histogram( $this->checksum_histogram( 'comments', null, $min_id, $max_id ) );
+ }
+
+ /**
+ * Retrieve the checksum for comment meta within a range.
+ *
+ * @access public
+ *
+ * @param int $min_id Minimum comment meta ID.
+ * @param int $max_id Maximum comment meta ID.
+ * @return int The checksum.
+ */
+ public function comment_meta_checksum( $min_id = null, $max_id = null ) {
+ return $this->summarize_checksum_histogram( $this->checksum_histogram( 'commentmeta', null, $min_id, $max_id ) );
+ }
+
+ /**
+ * Update the value of an option.
+ *
+ * @access public
+ *
+ * @param string $option Option name.
+ * @param mixed $value Option value.
+ * @return bool False if value was not updated and true if value was updated.
+ */
+ public function update_option( $option, $value ) {
+ return update_option( $option, $value );
+ }
+
+ /**
+ * Retrieve an option value based on an option name.
+ *
+ * @access public
+ *
+ * @param string $option Name of option to retrieve.
+ * @param mixed $default Optional. Default value to return if the option does not exist.
+ * @return mixed Value set for the option.
+ */
+ public function get_option( $option, $default = false ) {
+ return get_option( $option, $default );
+ }
+
+ /**
+ * Remove an option by name.
+ *
+ * @access public
+ *
+ * @param string $option Name of option to remove.
+ * @return bool True, if option is successfully deleted. False on failure.
+ */
+ public function delete_option( $option ) {
+ return delete_option( $option );
+ }
+
+ /**
+ * Change the info of the current theme.
+ *
+ * @access public
+ *
+ * @param array $theme_info Theme info array.
+ */
+ public function set_theme_info( $theme_info ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // Noop.
+ }
+
+ /**
+ * Whether the current theme supports a certain feature.
+ *
+ * @access public
+ *
+ * @param string $feature Name of the feature.
+ */
+ public function current_theme_supports( $feature ) {
+ return current_theme_supports( $feature );
+ }
+
+ /**
+ * Retrieve metadata for the specified object.
+ *
+ * @access public
+ *
+ * @param string $type Meta type.
+ * @param int $object_id ID of the object.
+ * @param string $meta_key Meta key.
+ * @param bool $single If true, return only the first value of the specified meta_key.
+ *
+ * @return mixed Single metadata value, or array of values.
+ */
+ public function get_metadata( $type, $object_id, $meta_key = '', $single = false ) {
+ return get_metadata( $type, $object_id, $meta_key, $single );
+ }
+
+ /**
+ * Stores remote meta key/values alongside an ID mapping key.
+ *
+ * @access public
+ *
+ * @todo Refactor to not use interpolated values when preparing the SQL query.
+ *
+ * @param string $type Meta type.
+ * @param int $object_id ID of the object.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value.
+ * @param int $meta_id ID of the meta.
+ *
+ * @return bool False if meta table does not exist, true otherwise.
+ */
+ public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id ) {
+ $table = _get_meta_table( $type );
+ if ( ! $table ) {
+ return false;
+ }
+
+ global $wpdb;
+
+ $exists = $wpdb->get_var(
+ $wpdb->prepare(
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ "SELECT EXISTS( SELECT 1 FROM $table WHERE meta_id = %d )",
+ $meta_id
+ )
+ );
+
+ if ( $exists ) {
+ $wpdb->update(
+ $table,
+ array(
+ 'meta_key' => $meta_key,
+ 'meta_value' => maybe_serialize( $meta_value ),
+ ),
+ array( 'meta_id' => $meta_id )
+ );
+ } else {
+ $object_id_field = $type . '_id';
+ $wpdb->insert(
+ $table,
+ array(
+ 'meta_id' => $meta_id,
+ $object_id_field => $object_id,
+ 'meta_key' => $meta_key,
+ 'meta_value' => maybe_serialize( $meta_value ),
+ )
+ );
+ }
+
+ wp_cache_delete( $object_id, $type . '_meta' );
+
+ return true;
+ }
+
+ /**
+ * Delete metadata for the specified object.
+ *
+ * @access public
+ *
+ * @todo Refactor to not use interpolated values when preparing the SQL query.
+ *
+ * @param string $type Meta type.
+ * @param int $object_id ID of the object.
+ * @param array $meta_ids IDs of the meta objects to delete.
+ */
+ public function delete_metadata( $type, $object_id, $meta_ids ) {
+ global $wpdb;
+
+ $table = _get_meta_table( $type );
+ if ( ! $table ) {
+ return false;
+ }
+
+ foreach ( $meta_ids as $meta_id ) {
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE meta_id = %d", $meta_id ) );
+ }
+
+ // If we don't have an object ID what do we do - invalidate ALL meta?
+ if ( $object_id ) {
+ wp_cache_delete( $object_id, $type . '_meta' );
+ }
+ }
+
+ /**
+ * Delete metadata with a certain key for the specified objects.
+ *
+ * @access public
+ *
+ * @todo Test this out to make sure it works as expected.
+ * @todo Refactor to not use interpolated values when preparing the SQL query.
+ *
+ * @param string $type Meta type.
+ * @param array $object_ids IDs of the objects.
+ * @param string $meta_key Meta key.
+ */
+ public function delete_batch_metadata( $type, $object_ids, $meta_key ) {
+ global $wpdb;
+
+ $table = _get_meta_table( $type );
+ if ( ! $table ) {
+ return false;
+ }
+ $column = sanitize_key( $type . '_id' );
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE $column IN (%s) && meta_key = %s", implode( ',', $object_ids ), $meta_key ) );
+
+ // If we don't have an object ID what do we do - invalidate ALL meta?
+ foreach ( $object_ids as $object_id ) {
+ wp_cache_delete( $object_id, $type . '_meta' );
+ }
+ }
+
+ /**
+ * Retrieve value of a constant based on the constant name.
+ *
+ * We explicitly return null instead of false if the constant doesn't exist.
+ *
+ * @access public
+ *
+ * @param string $constant Name of constant to retrieve.
+ * @return mixed Value set for the constant.
+ */
+ public function get_constant( $constant ) {
+ $value = get_option( 'jetpack_constant_' . $constant );
+
+ if ( $value ) {
+ return $value;
+ }
+
+ return null;
+ }
+
+ /**
+ * Set the value of a constant.
+ *
+ * @access public
+ *
+ * @param string $constant Name of constant to retrieve.
+ * @param mixed $value Value set for the constant.
+ */
+ public function set_constant( $constant, $value ) {
+ update_option( 'jetpack_constant_' . $constant, $value );
+ }
+
+ /**
+ * Retrieve the number of the available updates of a certain type.
+ * Type is one of: `plugins`, `themes`, `wordpress`, `translations`, `total`, `wp_update_version`.
+ *
+ * @access public
+ *
+ * @param string $type Type of updates to retrieve.
+ * @return int|null Number of updates available, `null` if type is invalid or missing.
+ */
+ public function get_updates( $type ) {
+ $all_updates = get_option( 'jetpack_updates', array() );
+
+ if ( isset( $all_updates[ $type ] ) ) {
+ return $all_updates[ $type ];
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Set the available updates of a certain type.
+ * Type is one of: `plugins`, `themes`, `wordpress`, `translations`, `total`, `wp_update_version`.
+ *
+ * @access public
+ *
+ * @param string $type Type of updates to set.
+ * @param int $updates Total number of updates.
+ */
+ public function set_updates( $type, $updates ) {
+ $all_updates = get_option( 'jetpack_updates', array() );
+ $all_updates[ $type ] = $updates;
+ update_option( 'jetpack_updates', $all_updates );
+ }
+
+ /**
+ * Retrieve a callable value based on its name.
+ *
+ * @access public
+ *
+ * @param string $name Name of the callable to retrieve.
+ * @return mixed Value of the callable.
+ */
+ public function get_callable( $name ) {
+ $value = get_option( 'jetpack_' . $name );
+
+ if ( $value ) {
+ return $value;
+ }
+
+ return null;
+ }
+
+ /**
+ * Update the value of a callable.
+ *
+ * @access public
+ *
+ * @param string $name Callable name.
+ * @param mixed $value Callable value.
+ */
+ public function set_callable( $name, $value ) {
+ update_option( 'jetpack_' . $name, $value );
+ }
+
+ /**
+ * Retrieve a network option value based on a network option name.
+ *
+ * @access public
+ *
+ * @param string $option Name of network option to retrieve.
+ * @return mixed Value set for the network option.
+ */
+ public function get_site_option( $option ) {
+ return get_option( 'jetpack_network_' . $option );
+ }
+
+ /**
+ * Update the value of a network option.
+ *
+ * @access public
+ *
+ * @param string $option Network option name.
+ * @param mixed $value Network option value.
+ * @return bool False if value was not updated and true if value was updated.
+ */
+ public function update_site_option( $option, $value ) {
+ return update_option( 'jetpack_network_' . $option, $value );
+ }
+
+ /**
+ * Remove a network option by name.
+ *
+ * @access public
+ *
+ * @param string $option Name of option to remove.
+ * @return bool True, if option is successfully deleted. False on failure.
+ */
+ public function delete_site_option( $option ) {
+ return delete_option( 'jetpack_network_' . $option );
+ }
+
+ /**
+ * Retrieve the terms from a particular taxonomy.
+ *
+ * @access public
+ *
+ * @param string $taxonomy Taxonomy slug.
+ *
+ * @return array|WP_Error Array of terms or WP_Error object on failure.
+ */
+ public function get_terms( $taxonomy ) {
+ $t = $this->ensure_taxonomy( $taxonomy );
+ if ( ! $t || is_wp_error( $t ) ) {
+ return $t;
+ }
+ return get_terms( $taxonomy );
+ }
+
+ /**
+ * Retrieve a particular term.
+ *
+ * @access public
+ *
+ * @param string $taxonomy Taxonomy slug.
+ * @param int $term_id ID of the term.
+ * @param string $term_key ID Field `term_id` or `term_taxonomy_id`.
+ *
+ * @return \WP_Term|WP_Error Term object on success, \WP_Error object on failure.
+ */
+ public function get_term( $taxonomy, $term_id, $term_key = 'term_id' ) {
+
+ // Full Sync will pass false for the $taxonomy so a check for term_taxonomy_id is needed before ensure_taxonomy.
+ if ( 'term_taxonomy_id' === $term_key ) {
+ return get_term_by( 'term_taxonomy_id', $term_id );
+ }
+
+ $t = $this->ensure_taxonomy( $taxonomy );
+ if ( ! $t || is_wp_error( $t ) ) {
+ return $t;
+ }
+
+ return get_term( $term_id, $taxonomy );
+ }
+
+ /**
+ * Verify a taxonomy is legitimate and register it if necessary.
+ *
+ * @access private
+ *
+ * @param string $taxonomy Taxonomy slug.
+ *
+ * @return bool|void|WP_Error True if already exists; void if it was registered; \WP_Error on error.
+ */
+ private function ensure_taxonomy( $taxonomy ) {
+ if ( ! taxonomy_exists( $taxonomy ) ) {
+ // Try re-registering synced taxonomies.
+ $taxonomies = $this->get_callable( 'taxonomies' );
+ if ( ! isset( $taxonomies[ $taxonomy ] ) ) {
+ // Doesn't exist, or somehow hasn't been synced.
+ return new WP_Error( 'invalid_taxonomy', "The taxonomy '$taxonomy' doesn't exist" );
+ }
+ $t = $taxonomies[ $taxonomy ];
+
+ return register_taxonomy(
+ $taxonomy,
+ $t->object_type,
+ (array) $t
+ );
+ }
+
+ return true;
+ }
+
+ /**
+ * Retrieve all terms from a taxonomy that are related to an object with a particular ID.
+ *
+ * @access public
+ *
+ * @param int $object_id Object ID.
+ * @param string $taxonomy Taxonomy slug.
+ *
+ * @return array|bool|WP_Error Array of terms on success, `false` if no terms or post doesn't exist, \WP_Error on failure.
+ */
+ public function get_the_terms( $object_id, $taxonomy ) {
+ return get_the_terms( $object_id, $taxonomy );
+ }
+
+ /**
+ * Insert or update a term.
+ *
+ * @access public
+ *
+ * @param \WP_Term $term_object Term object.
+ *
+ * @return array|bool|WP_Error Array of term_id and term_taxonomy_id if updated, true if inserted, \WP_Error on failure.
+ */
+ public function update_term( $term_object ) {
+ $taxonomy = $term_object->taxonomy;
+ global $wpdb;
+ $exists = $wpdb->get_var(
+ $wpdb->prepare(
+ "SELECT EXISTS( SELECT 1 FROM $wpdb->terms WHERE term_id = %d )",
+ $term_object->term_id
+ )
+ );
+ if ( ! $exists ) {
+ $term_object = sanitize_term( clone $term_object, $taxonomy, 'db' );
+ $term = array(
+ 'term_id' => $term_object->term_id,
+ 'name' => $term_object->name,
+ 'slug' => $term_object->slug,
+ 'term_group' => $term_object->term_group,
+ );
+ $term_taxonomy = array(
+ 'term_taxonomy_id' => $term_object->term_taxonomy_id,
+ 'term_id' => $term_object->term_id,
+ 'taxonomy' => $term_object->taxonomy,
+ 'description' => $term_object->description,
+ 'parent' => (int) $term_object->parent,
+ 'count' => (int) $term_object->count,
+ );
+ $wpdb->insert( $wpdb->terms, $term );
+ $wpdb->insert( $wpdb->term_taxonomy, $term_taxonomy );
+
+ return true;
+ }
+
+ return wp_update_term( $term_object->term_id, $taxonomy, (array) $term_object );
+ }
+
+ /**
+ * Delete a term by the term ID and its corresponding taxonomy.
+ *
+ * @access public
+ *
+ * @param int $term_id Term ID.
+ * @param string $taxonomy Taxonomy slug.
+ *
+ * @return bool|int|WP_Error True on success, false if term doesn't exist. Zero if trying with default category. \WP_Error on invalid taxonomy.
+ */
+ public function delete_term( $term_id, $taxonomy ) {
+ $this->ensure_taxonomy( $taxonomy );
+ return wp_delete_term( $term_id, $taxonomy );
+ }
+
+ /**
+ * Add/update terms of a particular taxonomy of an object with the specified ID.
+ *
+ * @access public
+ *
+ * @param int $object_id The object to relate to.
+ * @param string $taxonomy The context in which to relate the term to the object.
+ * @param string|int|array $terms A single term slug, single term id, or array of either term slugs or ids.
+ * @param bool $append Optional. If false will delete difference of terms. Default false.
+ */
+ public function update_object_terms( $object_id, $taxonomy, $terms, $append ) {
+ $this->ensure_taxonomy( $taxonomy );
+ wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
+ }
+
+ /**
+ * Remove certain term relationships from the specified object.
+ *
+ * @access public
+ *
+ * @todo Refactor to not use interpolated values when preparing the SQL query.
+ *
+ * @param int $object_id ID of the object.
+ * @param array $tt_ids Term taxonomy IDs.
+ * @return bool True on success, false on failure.
+ */
+ public function delete_object_terms( $object_id, $tt_ids ) {
+ global $wpdb;
+
+ if ( is_array( $tt_ids ) && ! empty( $tt_ids ) ) {
+ // Escape.
+ $tt_ids_sanitized = array_map( 'intval', $tt_ids );
+
+ $taxonomies = array();
+ foreach ( $tt_ids_sanitized as $tt_id ) {
+ $term = get_term_by( 'term_taxonomy_id', $tt_id );
+ $taxonomies[ $term->taxonomy ][] = $tt_id;
+ }
+ $in_tt_ids = implode( ', ', $tt_ids_sanitized );
+
+ /**
+ * Fires immediately before an object-term relationship is deleted.
+ *
+ * @since 1.6.3
+ * @since-jetpack 2.9.0
+ *
+ * @param int $object_id Object ID.
+ * @param array $tt_ids An array of term taxonomy IDs.
+ */
+ do_action( 'delete_term_relationships', $object_id, $tt_ids_sanitized );
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id IN ($in_tt_ids)", $object_id ) );
+ foreach ( $taxonomies as $taxonomy => $taxonomy_tt_ids ) {
+ $this->ensure_taxonomy( $taxonomy );
+ wp_cache_delete( $object_id, $taxonomy . '_relationships' );
+ /**
+ * Fires immediately after an object-term relationship is deleted.
+ *
+ * @since 1.6.3
+ * @since-jetpack 2.9.0
+ *
+ * @param int $object_id Object ID.
+ * @param array $tt_ids An array of term taxonomy IDs.
+ */
+ do_action( 'deleted_term_relationships', $object_id, $taxonomy_tt_ids );
+ wp_update_term_count( $taxonomy_tt_ids, $taxonomy );
+ }
+
+ return (bool) $deleted;
+ }
+
+ return false;
+ }
+
+ /**
+ * Retrieve the number of users.
+ * Not supported in this replicastore.
+ *
+ * @access public
+ */
+ public function user_count() {
+ // Noop.
+ }
+
+ /**
+ * Retrieve a user object by the user ID.
+ *
+ * @access public
+ *
+ * @param int $user_id User ID.
+ * @return \WP_User User object.
+ */
+ public function get_user( $user_id ) {
+ return \WP_User::get_instance( $user_id );
+ }
+
+ /**
+ * Insert or update a user.
+ * Not supported in this replicastore.
+ *
+ * @access public
+ * @throws Exception If this method is invoked.
+ *
+ * @param \WP_User $user User object.
+ */
+ public function upsert_user( $user ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $this->invalid_call();
+ }
+
+ /**
+ * Delete a user.
+ * Not supported in this replicastore.
+ *
+ * @access public
+ * @throws Exception If this method is invoked.
+ *
+ * @param int $user_id User ID.
+ */
+ public function delete_user( $user_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $this->invalid_call();
+ }
+
+ /**
+ * Update/insert user locale.
+ * Not supported in this replicastore.
+ *
+ * @access public
+ * @throws Exception If this method is invoked.
+ *
+ * @param int $user_id User ID.
+ * @param string $local The user locale.
+ */
+ public function upsert_user_locale( $user_id, $local ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $this->invalid_call();
+ }
+
+ /**
+ * Delete user locale.
+ * Not supported in this replicastore.
+ *
+ * @access public
+ * @throws Exception If this method is invoked.
+ *
+ * @param int $user_id User ID.
+ */
+ public function delete_user_locale( $user_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $this->invalid_call();
+ }
+
+ /**
+ * Retrieve the user locale.
+ *
+ * @access public
+ *
+ * @param int $user_id User ID.
+ * @return string The user locale.
+ */
+ public function get_user_locale( $user_id ) {
+ return get_user_locale( $user_id );
+ }
+
+ /**
+ * Retrieve the allowed mime types for the user.
+ * Not supported in this replicastore.
+ *
+ * @access public
+ *
+ * @param int $user_id User ID.
+ */
+ public function get_allowed_mime_types( $user_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // Noop.
+ }
+
+ /**
+ * Retrieve all the checksums we are interested in.
+ * Currently that is posts, comments, post meta and comment meta.
+ *
+ * @access public
+ *
+ * @param boolean $perform_text_conversion If text fields should be latin1 converted.
+ *
+ * @return array Checksums.
+ */
+ public function checksum_all( $perform_text_conversion = false ) {
+ $post_checksum = $this->checksum_histogram( 'posts', null, null, null, null, true, '', false, false, $perform_text_conversion );
+ $comments_checksum = $this->checksum_histogram( 'comments', null, null, null, null, true, '', false, false, $perform_text_conversion );
+ $post_meta_checksum = $this->checksum_histogram( 'postmeta', null, null, null, null, true, '', false, false, $perform_text_conversion );
+ $comment_meta_checksum = $this->checksum_histogram( 'commentmeta', null, null, null, null, true, '', false, false, $perform_text_conversion );
+ $terms_checksum = $this->checksum_histogram( 'terms', null, null, null, null, true, '', false, false, $perform_text_conversion );
+ $term_relationships_checksum = $this->checksum_histogram( 'term_relationships', null, null, null, null, true, '', false, false, $perform_text_conversion );
+ $term_taxonomy_checksum = $this->checksum_histogram( 'term_taxonomy', null, null, null, null, true, '', false, false, $perform_text_conversion );
+
+ $result = array(
+ 'posts' => $this->summarize_checksum_histogram( $post_checksum ),
+ 'comments' => $this->summarize_checksum_histogram( $comments_checksum ),
+ 'post_meta' => $this->summarize_checksum_histogram( $post_meta_checksum ),
+ 'comment_meta' => $this->summarize_checksum_histogram( $comment_meta_checksum ),
+ 'terms' => $this->summarize_checksum_histogram( $terms_checksum ),
+ 'term_relationships' => $this->summarize_checksum_histogram( $term_relationships_checksum ),
+ 'term_taxonomy' => $this->summarize_checksum_histogram( $term_taxonomy_checksum ),
+ );
+
+ /**
+ * WooCommerce tables
+ */
+
+ /**
+ * On WordPress.com, we can't directly check if the site has support for WooCommerce.
+ * Having the option to override the functionality here helps with syncing WooCommerce tables.
+ *
+ * @since 10.1
+ *
+ * @param bool If we should we force-enable WooCommerce tables support.
+ */
+ $force_woocommerce_support = apply_filters( 'jetpack_table_checksum_force_enable_woocommerce', false );
+
+ if ( $force_woocommerce_support || class_exists( 'WooCommerce' ) ) {
+ /**
+ * Guard in Try/Catch as it's possible for the WooCommerce class to exist, but
+ * the tables to not. If we don't do this, the response will be just the exception, without
+ * returning any valid data. This will prevent us from ever performing a checksum/fix
+ * for sites like this.
+ * It's better to just skip the tables in the response, instead of completely failing.
+ */
+
+ try {
+ $woocommerce_order_items_checksum = $this->checksum_histogram( 'woocommerce_order_items' );
+ $result['woocommerce_order_items'] = $this->summarize_checksum_histogram( $woocommerce_order_items_checksum );
+ } catch ( Exception $ex ) {
+ $result['woocommerce_order_items'] = null;
+ }
+
+ try {
+ $woocommerce_order_itemmeta_checksum = $this->checksum_histogram( 'woocommerce_order_itemmeta' );
+ $result['woocommerce_order_itemmeta'] = $this->summarize_checksum_histogram( $woocommerce_order_itemmeta_checksum );
+ } catch ( Exception $ex ) {
+ $result['woocommerce_order_itemmeta'] = null;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Return the summarized checksum from buckets or the WP_Error.
+ *
+ * @param array $histogram checksum_histogram result.
+ *
+ * @return int|WP_Error checksum or Error.
+ */
+ protected function summarize_checksum_histogram( $histogram ) {
+ if ( is_wp_error( $histogram ) ) {
+ return $histogram;
+ } else {
+ return array_sum( $histogram );
+ }
+ }
+
+ /**
+ * Grabs the minimum and maximum object ids for the given parameters.
+ *
+ * @access public
+ *
+ * @param string $id_field The id column in the table to query.
+ * @param string $object_table The table to query.
+ * @param string $where A sql where clause without 'WHERE'.
+ * @param int $bucket_size The maximum amount of objects to include in the query.
+ * For `term_relationships` table, the bucket size will refer to the amount
+ * of distinct object ids. This will likely include more database rows than
+ * the bucket size implies.
+ *
+ * @return object An object with min_id and max_id properties.
+ */
+ public function get_min_max_object_id( $id_field, $object_table, $where, $bucket_size ) {
+ global $wpdb;
+
+ // The term relationship table's unique key is a combination of 2 columns. `DISTINCT` helps us get a more acurate query.
+ $distinct_sql = ( $wpdb->term_relationships === $object_table ) ? 'DISTINCT' : '';
+ $where_sql = $where ? "WHERE $where" : '';
+
+ // Since MIN() and MAX() do not work with LIMIT, we'll need to adjust the dataset we query if a limit is present.
+ // With a limit present, we'll look at a dataset consisting of object_ids that meet the constructs of the $where clause.
+ // Without a limit, we can use the actual table as a dataset.
+ $from = $bucket_size ?
+ "( SELECT $distinct_sql $id_field FROM $object_table $where_sql ORDER BY $id_field ASC LIMIT $bucket_size ) as ids" :
+ "$object_table $where_sql ORDER BY $id_field ASC";
+
+ return $wpdb->get_row(
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ "SELECT MIN($id_field) as min, MAX($id_field) as max FROM $from"
+ );
+ }
+
+ /**
+ * Retrieve the checksum histogram for a specific object type.
+ *
+ * @access public
+ *
+ * @param string $table Object type.
+ * @param null $buckets Number of buckets to split the objects to.
+ * @param null $start_id Minimum object ID.
+ * @param null $end_id Maximum object ID.
+ * @param null $columns Table columns to calculate the checksum from.
+ * @param bool $strip_non_ascii Whether to strip non-ASCII characters.
+ * @param string $salt Salt, used for $wpdb->prepare()'s args.
+ * @param bool $only_range_edges Only return the range edges and not the actual checksums.
+ * @param bool $detailed_drilldown If the call should return a detailed drilldown for the checksum or only the checksum.
+ * @param bool $perform_text_conversion If text fields should be converted to latin1 during the checksum calculation.
+ *
+ * @return array|WP_Error The checksum histogram.
+ * @throws Exception Throws an exception if data validation fails inside `Table_Checksum` calls.
+ */
+ public function checksum_histogram( $table, $buckets = null, $start_id = null, $end_id = null, $columns = null, $strip_non_ascii = true, $salt = '', $only_range_edges = false, $detailed_drilldown = false, $perform_text_conversion = false ) {
+ global $wpdb;
+
+ $wpdb->queries = array();
+ try {
+ $checksum_table = $this->get_table_checksum_instance( $table, $salt, $perform_text_conversion );
+ } catch ( Exception $ex ) {
+ return new WP_Error( 'checksum_disabled', $ex->getMessage() );
+ }
+
+ // Validate / Determine Buckets.
+ if ( is_null( $buckets ) || $buckets < 1 ) {
+ $buckets = $this->calculate_buckets( $table, $start_id, $end_id );
+ }
+ if ( is_wp_error( $buckets ) ) {
+ return $buckets;
+ }
+
+ $range_edges = $checksum_table->get_range_edges( $start_id, $end_id );
+
+ if ( $only_range_edges ) {
+ return $range_edges;
+ }
+
+ $object_count = (int) $range_edges['item_count'];
+
+ if ( 0 === $object_count ) {
+ return array();
+ }
+
+ $bucket_size = (int) ceil( $object_count / $buckets );
+ $previous_max_id = max( 0, $range_edges['min_range'] );
+ $histogram = array();
+
+ do {
+ $ids_range = $checksum_table->get_range_edges( $previous_max_id, null, $bucket_size );
+
+ if ( empty( $ids_range['min_range'] ) || empty( $ids_range['max_range'] ) ) {
+ // Nothing to checksum here...
+ break;
+ }
+
+ // Get the checksum value.
+ $batch_checksum = $checksum_table->calculate_checksum( $ids_range['min_range'], $ids_range['max_range'], null, $detailed_drilldown );
+
+ if ( is_wp_error( $batch_checksum ) ) {
+ return $batch_checksum;
+ }
+
+ if ( $ids_range['min_range'] === $ids_range['max_range'] ) {
+ $histogram[ $ids_range['min_range'] ] = $batch_checksum;
+ } else {
+ $histogram[ "{$ids_range[ 'min_range' ]}-{$ids_range[ 'max_range' ]}" ] = $batch_checksum;
+ }
+
+ $previous_max_id = $ids_range['max_range'] + 1;
+ // If we've reached the max_range lets bail out.
+ if ( $previous_max_id > $range_edges['max_range'] ) {
+ break;
+ }
+ } while ( true );
+
+ return $histogram;
+ }
+
+ /**
+ * Retrieve the type of the checksum.
+ *
+ * @access public
+ *
+ * @return string Type of the checksum.
+ */
+ public function get_checksum_type() {
+ return 'sum';
+ }
+
+ /**
+ * Used in methods that are not implemented and shouldn't be invoked.
+ *
+ * @access private
+ * @throws Exception If this method is invoked.
+ */
+ private function invalid_call() {
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
+ $backtrace = debug_backtrace();
+ $caller = $backtrace[1]['function'];
+ throw new Exception( "This function $caller is not supported on the WP Replicastore" );
+ }
+
+ /**
+ * Determine number of buckets to use in full table checksum.
+ *
+ * @param string $table Object Type.
+ * @param int $start_id Min Object ID.
+ * @param int $end_id Max Object ID.
+ * @return int|WP_Error Number of Buckets to use.
+ */
+ private function calculate_buckets( $table, $start_id = null, $end_id = null ) {
+ // Get # of objects.
+ try {
+ $checksum_table = $this->get_table_checksum_instance( $table );
+ } catch ( Exception $ex ) {
+ return new WP_Error( 'checksum_disabled', $ex->getMessage() );
+ }
+ $range_edges = $checksum_table->get_range_edges( $start_id, $end_id );
+ $object_count = $range_edges['item_count'];
+
+ // Ensure no division by 0.
+ if ( 0 === (int) $object_count ) {
+ return 1;
+ }
+
+ // Default Bucket sizes.
+ $bucket_size = 10000; // Default bucket size is 10,000 items.
+ switch ( $table ) {
+ case 'postmeta':
+ case 'commentmeta':
+ case 'order_itemmeta':
+ $bucket_size = 1000; // Meta bucket size is restricted to 1000 items.
+ }
+
+ return (int) ceil( $object_count / $bucket_size );
+ }
+
+ /**
+ * Return an instance for `Table_Checksum`, depending on the table.
+ *
+ * Some tables require custom instances, due to different checksum logic.
+ *
+ * @param string $table The table that we want to get the instance for.
+ * @param null $salt Salt to be used when generating the checksums.
+ * @param false $perform_text_conversion Should we perform text encoding conversion when calculating the checksum.
+ *
+ * @return Table_Checksum|Table_Checksum_Usermeta
+ * @throws Exception Might throw an exception if any of the input parameters were invalid.
+ */
+ public function get_table_checksum_instance( $table, $salt = null, $perform_text_conversion = false ) {
+ if ( 'users' === $table ) {
+ return new Table_Checksum_Users( $table, $salt, $perform_text_conversion );
+ }
+ if ( 'usermeta' === $table ) {
+ return new Table_Checksum_Usermeta( $table, $salt, $perform_text_conversion );
+ }
+
+ return new Table_Checksum( $table, $salt, $perform_text_conversion );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-rest-endpoints.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-rest-endpoints.php
new file mode 100644
index 00000000..ae12ff32
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-rest-endpoints.php
@@ -0,0 +1,804 @@
+<?php
+/**
+ * Sync package.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+use Automattic\Jetpack\Connection\Rest_Authentication;
+use WP_Error;
+use WP_REST_Server;
+
+/**
+ * This class will handle Sync v4 REST Endpoints.
+ *
+ * @since 1.23.1
+ */
+class REST_Endpoints {
+
+ /**
+ * Items pending send.
+ *
+ * @var array
+ */
+ public $items = array();
+
+ /**
+ * Initialize REST routes.
+ */
+ public static function initialize_rest_api() {
+
+ // Request a Full Sync.
+ register_rest_route(
+ 'jetpack/v4',
+ '/sync/full-sync',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::full_sync_start',
+ 'permission_callback' => __CLASS__ . '::verify_default_permissions',
+ 'args' => array(
+ 'modules' => array(
+ 'description' => __( 'Data Modules that should be included in Full Sync', 'jetpack-sync' ),
+ 'type' => 'array',
+ 'required' => false,
+ ),
+ 'users' => array(
+ 'description' => __( 'User IDs to include in Full Sync or "initial"', 'jetpack-sync' ),
+ 'required' => false,
+ ),
+ 'posts' => array(
+ 'description' => __( 'Post IDs to include in Full Sync', 'jetpack-sync' ),
+ 'type' => 'array',
+ 'required' => false,
+ ),
+ 'comments' => array(
+ 'description' => __( 'Comment IDs to include in Full Sync', 'jetpack-sync' ),
+ 'type' => 'array',
+ 'required' => false,
+ ),
+ ),
+ )
+ );
+
+ // Obtain Sync status.
+ register_rest_route(
+ 'jetpack/v4',
+ '/sync/status',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::sync_status',
+ 'permission_callback' => __CLASS__ . '::verify_default_permissions',
+ 'args' => array(
+ 'fields' => array(
+ 'description' => __( 'Comma seperated list of additional fields that should be included in status.', 'jetpack-sync' ),
+ 'type' => 'string',
+ 'required' => false,
+ ),
+ ),
+ )
+ );
+
+ // Update Sync health status.
+ register_rest_route(
+ 'jetpack/v4',
+ '/sync/health',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::sync_health',
+ 'permission_callback' => __CLASS__ . '::verify_default_permissions',
+ 'args' => array(
+ 'status' => array(
+ 'description' => __( 'New Sync health status', 'jetpack-sync' ),
+ 'type' => 'string',
+ 'required' => true,
+ ),
+ ),
+ )
+ );
+
+ // Obtain Sync settings.
+ register_rest_route(
+ 'jetpack/v4',
+ '/sync/settings',
+ array(
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::get_sync_settings',
+ 'permission_callback' => __CLASS__ . '::verify_default_permissions',
+ ),
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::update_sync_settings',
+ 'permission_callback' => __CLASS__ . '::verify_default_permissions',
+ ),
+ )
+ );
+
+ // Retrieve Sync Object(s).
+ register_rest_route(
+ 'jetpack/v4',
+ '/sync/object',
+ array(
+ 'methods' => WP_REST_Server::ALLMETHODS,
+ 'callback' => __CLASS__ . '::get_sync_objects',
+ 'permission_callback' => __CLASS__ . '::verify_default_permissions',
+ 'args' => array(
+ 'module_name' => array(
+ 'description' => __( 'Name of Sync module', 'jetpack-sync' ),
+ 'type' => 'string',
+ 'required' => false,
+ ),
+ 'object_type' => array(
+ 'description' => __( 'Object Type', 'jetpack-sync' ),
+ 'type' => 'string',
+ 'required' => false,
+ ),
+ 'object_ids' => array(
+ 'description' => __( 'Objects Identifiers', 'jetpack-sync' ),
+ 'type' => 'array',
+ 'required' => false,
+ ),
+ ),
+ )
+ );
+
+ // Retrieve Sync Object(s).
+ register_rest_route(
+ 'jetpack/v4',
+ '/sync/now',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::do_sync',
+ 'permission_callback' => __CLASS__ . '::verify_default_permissions',
+ 'args' => array(
+ 'queue' => array(
+ 'description' => __( 'Name of Sync queue.', 'jetpack-sync' ),
+ 'type' => 'string',
+ 'required' => true,
+ ),
+ ),
+ )
+ );
+
+ // Checkout Sync Objects.
+ register_rest_route(
+ 'jetpack/v4',
+ '/sync/checkout',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::checkout',
+ 'permission_callback' => __CLASS__ . '::verify_default_permissions',
+ )
+ );
+
+ // Checkin Sync Objects.
+ register_rest_route(
+ 'jetpack/v4',
+ '/sync/close',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::close',
+ 'permission_callback' => __CLASS__ . '::verify_default_permissions',
+ )
+ );
+
+ // Unlock Sync Queue.
+ register_rest_route(
+ 'jetpack/v4',
+ '/sync/unlock',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::unlock_queue',
+ 'permission_callback' => __CLASS__ . '::verify_default_permissions',
+ 'args' => array(
+ 'queue' => array(
+ 'description' => __( 'Name of Sync queue.', 'jetpack-sync' ),
+ 'type' => 'string',
+ 'required' => true,
+ ),
+ ),
+ )
+ );
+
+ // Retrieve range of Object Ids.
+ register_rest_route(
+ 'jetpack/v4',
+ '/sync/object-id-range',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::get_object_id_range',
+ 'permission_callback' => __CLASS__ . '::verify_default_permissions',
+ 'args' => array(
+ 'sync_module' => array(
+ 'description' => __( 'Name of Sync module.', 'jetpack-sync' ),
+ 'type' => 'string',
+ 'required' => true,
+ ),
+ 'batch_size' => array(
+ 'description' => __( 'Size of batches', 'jetpack-sync' ),
+ 'type' => 'int',
+ 'required' => true,
+ ),
+ ),
+ )
+ );
+
+ // Obtain table checksums.
+ register_rest_route(
+ 'jetpack/v4',
+ '/sync/data-check',
+ array(
+ 'methods' => WP_REST_Server::READABLE,
+ 'callback' => __CLASS__ . '::data_check',
+ 'permission_callback' => __CLASS__ . '::verify_default_permissions',
+ 'args' => array(
+ 'perform_text_conversion' => array(
+ 'description' => __( 'If text fields should be converted to latin1 in checksum calculation.', 'jetpack-sync' ),
+ 'type' => 'boolean',
+ 'required' => false,
+ ),
+ ),
+ )
+ );
+
+ // Obtain histogram.
+ register_rest_route(
+ 'jetpack/v4',
+ '/sync/data-histogram',
+ array(
+ 'methods' => WP_REST_Server::EDITABLE,
+ 'callback' => __CLASS__ . '::data_histogram',
+ 'permission_callback' => __CLASS__ . '::verify_default_permissions',
+ 'args' => array(
+ 'columns' => array(
+ 'description' => __( 'Column mappings', 'jetpack-sync' ),
+ 'type' => 'array',
+ 'required' => false,
+ ),
+ 'object_type' => array(
+ 'description' => __( 'Object Type', 'jetpack-sync' ),
+ 'type' => 'string',
+ 'required' => false,
+ ),
+ 'buckets' => array(
+ 'description' => __( 'Number of histogram buckets.', 'jetpack-sync' ),
+ 'type' => 'int',
+ 'required' => false,
+ ),
+ 'start_id' => array(
+ 'description' => __( 'Start ID for the histogram', 'jetpack-sync' ),
+ 'type' => 'int',
+ 'required' => false,
+ ),
+ 'end_id' => array(
+ 'description' => __( 'End ID for the histogram', 'jetpack-sync' ),
+ 'type' => 'int',
+ 'required' => false,
+ ),
+ 'strip_non_ascii' => array(
+ 'description' => __( 'Strip non-ascii characters?', 'jetpack-sync' ),
+ 'type' => 'boolean',
+ 'required' => false,
+ ),
+ 'shared_salt' => array(
+ 'description' => __( 'Shared Salt to use when generating checksum', 'jetpack-sync' ),
+ 'type' => 'string',
+ 'required' => false,
+ ),
+ 'only_range_edges' => array(
+ 'description' => __( 'Should only range endges be returned', 'jetpack-sync' ),
+ 'type' => 'boolean',
+ 'required' => false,
+ ),
+ 'detailed_drilldown' => array(
+ 'description' => __( 'Do we want the checksum or object ids.', 'jetpack-sync' ),
+ 'type' => 'boolean',
+ 'required' => false,
+ ),
+ 'perform_text_conversion' => array(
+ 'description' => __( 'If text fields should be converted to latin1 in checksum calculation.', 'jetpack-sync' ),
+ 'type' => 'boolean',
+ 'required' => false,
+ ),
+ ),
+ )
+ );
+
+ }
+
+ /**
+ * Trigger a Full Sync of specified modules.
+ *
+ * @since 1.23.1
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response|WP_Error
+ */
+ public static function full_sync_start( $request ) {
+
+ $modules = $request->get_param( 'modules' );
+
+ // convert list of modules into array format of "$modulename => true".
+ if ( ! empty( $modules ) ) {
+ $modules = array_map( '__return_true', array_flip( $modules ) );
+ }
+
+ // Process additional options.
+ foreach ( array( 'posts', 'comments', 'users' ) as $module_name ) {
+ if ( 'users' === $module_name && 'initial' === $request->get_param( 'users' ) ) {
+ $modules['users'] = 'initial';
+ } elseif ( is_array( $request->get_param( $module_name ) ) ) {
+ $ids = $request->get_param( $module_name );
+ if ( count( $ids ) > 0 ) {
+ $modules[ $module_name ] = $ids;
+ }
+ }
+ }
+
+ if ( empty( $modules ) ) {
+ $modules = null;
+ }
+
+ return rest_ensure_response(
+ array(
+ 'scheduled' => Actions::do_full_sync( $modules ),
+ )
+ );
+ }
+
+ /**
+ * Return Sync's status.
+ *
+ * @since 1.23.1
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response
+ */
+ public static function sync_status( $request ) {
+ $fields = $request->get_param( 'fields' );
+ return rest_ensure_response( Actions::get_sync_status( $fields ) );
+ }
+
+ /**
+ * Return table checksums.
+ *
+ * @since 1.23.1
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response
+ */
+ public static function data_check( $request ) {
+ // Disable Sync during this call, so we can resolve faster.
+ Actions::mark_sync_read_only();
+ $store = new Replicastore();
+
+ $perform_text_conversion = false;
+ if ( true === $request->get_param( 'perform_text_conversion' ) ) {
+ $perform_text_conversion = true;
+ }
+
+ return rest_ensure_response( $store->checksum_all( $perform_text_conversion ) );
+ }
+
+ /**
+ * Return Histogram.
+ *
+ * @since 1.23.1
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response
+ */
+ public static function data_histogram( $request ) {
+
+ // Disable Sync during this call, so we can resolve faster.
+ Actions::mark_sync_read_only();
+
+ $args = $request->get_params();
+
+ if ( empty( $args['columns'] ) ) {
+ $args['columns'] = null; // go with defaults.
+ }
+
+ if ( false !== $args['strip_non_ascii'] ) {
+ $args['strip_non_ascii'] = true;
+ }
+
+ if ( true !== $args['perform_text_conversion'] ) {
+ $args['perform_text_conversion'] = false;
+ }
+
+ /**
+ * Hack: nullify the values of `start_id` and `end_id` if we're only requesting ranges.
+ *
+ * The endpoint doesn't support nullable values :(
+ */
+ if ( true === $args['only_range_edges'] ) {
+ if ( 0 === $args['start_id'] ) {
+ $args['start_id'] = null;
+ }
+
+ if ( 0 === $args['end_id'] ) {
+ $args['end_id'] = null;
+ }
+ }
+
+ $store = new Replicastore();
+ $histogram = $store->checksum_histogram( $args['object_type'], $args['buckets'], $args['start_id'], $args['end_id'], $args['columns'], $args['strip_non_ascii'], $args['shared_salt'], $args['only_range_edges'], $args['detailed_drilldown'], $args['perform_text_conversion'] );
+
+ return rest_ensure_response(
+ array(
+ 'histogram' => $histogram,
+ 'type' => $store->get_checksum_type(),
+ )
+ );
+ }
+
+ /**
+ * Update Sync health.
+ *
+ * @since 1.23.1
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response
+ */
+ public static function sync_health( $request ) {
+
+ switch ( $request->get_param( 'status' ) ) {
+ case Health::STATUS_IN_SYNC:
+ case Health::STATUS_OUT_OF_SYNC:
+ Health::update_status( $request->get_param( 'status' ) );
+ break;
+ default:
+ return new WP_Error( 'invalid_status', 'Invalid Sync Status Provided.' );
+ }
+
+ // re-fetch so we see what's really being stored.
+ return rest_ensure_response(
+ array(
+ 'success' => Health::get_status(),
+ )
+ );
+ }
+
+ /**
+ * Obtain Sync settings.
+ *
+ * @since 1.23.1
+ *
+ * @return \WP_REST_Response
+ */
+ public static function get_sync_settings() {
+ return rest_ensure_response( Settings::get_settings() );
+ }
+
+ /**
+ * Update Sync settings.
+ *
+ * @since 1.23.1
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response
+ */
+ public static function update_sync_settings( $request ) {
+ $args = $request->get_params();
+ $sync_settings = Settings::get_settings();
+
+ foreach ( $args as $key => $value ) {
+ if ( false !== $value ) {
+ if ( is_numeric( $value ) ) {
+ $value = (int) $value;
+ }
+
+ // special case for sending empty arrays - a string with value 'empty'.
+ if ( 'empty' === $value ) {
+ $value = array();
+ }
+
+ $sync_settings[ $key ] = $value;
+ }
+ }
+
+ Settings::update_settings( $sync_settings );
+
+ // re-fetch so we see what's really being stored.
+ return rest_ensure_response( Settings::get_settings() );
+ }
+
+ /**
+ * Retrieve Sync Objects.
+ *
+ * @since 1.23.1
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response
+ */
+ public static function get_sync_objects( $request ) {
+ $args = $request->get_params();
+
+ $module_name = $args['module_name'];
+ // Verify valid Sync Module.
+ $sync_module = Modules::get_module( $module_name );
+ if ( ! $sync_module ) {
+ return new WP_Error( 'invalid_module', 'You specified an invalid sync module' );
+ }
+
+ Actions::mark_sync_read_only();
+
+ $codec = Sender::get_instance()->get_codec();
+ Settings::set_is_syncing( true );
+ $objects = $codec->encode( $sync_module->get_objects_by_id( $args['object_type'], $args['object_ids'] ) );
+ Settings::set_is_syncing( false );
+
+ return rest_ensure_response(
+ array(
+ 'objects' => $objects,
+ 'codec' => $codec->name(),
+ )
+ );
+ }
+
+ /**
+ * Request Sync processing.
+ *
+ * @since 1.23.1
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response
+ */
+ public static function do_sync( $request ) {
+
+ $queue_name = self::validate_queue( $request->get_param( 'queue' ) );
+ if ( is_wp_error( $queue_name ) ) {
+ return $queue_name;
+ }
+
+ $sender = Sender::get_instance();
+ $response = $sender->do_sync_for_queue( new Queue( $queue_name ) );
+
+ return rest_ensure_response(
+ array(
+ 'response' => $response,
+ )
+ );
+ }
+
+ /**
+ * Request sync data from specified queue.
+ *
+ * @since 1.23.1
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response
+ */
+ public static function checkout( $request ) {
+ $args = $request->get_params();
+ $queue_name = self::validate_queue( $args['queue'] );
+
+ if ( is_wp_error( $queue_name ) ) {
+ return $queue_name;
+ }
+
+ $number_of_items = $args['number_of_items'];
+ if ( $number_of_items < 1 || $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 );
+ }
+
+ // REST Sender.
+ $sender = new REST_Sender();
+
+ if ( 'immediate' === $queue_name ) {
+ return rest_ensure_response( $sender->immediate_full_sync_pull( $number_of_items ) );
+ }
+
+ return rest_ensure_response( $sender->queue_pull( $queue_name, $number_of_items, $args ) );
+ }
+
+ /**
+ * Unlock a Sync queue.
+ *
+ * @since 1.23.1
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response
+ */
+ public static function unlock_queue( $request ) {
+
+ $queue_name = $request->get_param( 'queue' );
+
+ if ( ! in_array( $queue_name, array( 'sync', 'full_sync' ), true ) ) {
+ return new WP_Error( 'invalid_queue', 'Queue name should be sync or full_sync', 400 );
+ }
+ $queue = new Queue( $queue_name );
+
+ // False means that there was no lock to delete.
+ $response = $queue->unlock();
+ return rest_ensure_response(
+ array(
+ 'success' => $response,
+ )
+ );
+ }
+
+ /**
+ * Checkin Sync actions.
+ *
+ * @since 1.23.1
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response
+ */
+ public static function close( $request ) {
+
+ $request_body = $request->get_params();
+ $queue_name = self::validate_queue( $request_body['queue'] );
+
+ if ( is_wp_error( $queue_name ) ) {
+ return $queue_name;
+ }
+
+ if ( empty( $request_body['buffer_id'] ) ) {
+ return new WP_Error( 'missing_buffer_id', 'Please provide a buffer id', 400 );
+ }
+
+ if ( ! 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( 'Automattic\Jetpack\Sync\REST_Endpoints', 'sanitize_item_ids' ), $request_body['item_ids'] ) );
+
+ $queue = new Queue( $queue_name );
+
+ $items = $queue->peek_by_id( $request_body['item_ids'] );
+
+ // Update Full Sync Status if queue is "full_sync".
+ if ( 'full_sync' === $queue_name ) {
+ $full_sync_module = Modules::get_module( 'full-sync' );
+ $full_sync_module->update_sent_progress_action( $items );
+ }
+
+ $buffer = new Queue_Buffer( $request_body['buffer_id'], $request_body['item_ids'] );
+ $response = $queue->close( $buffer, $request_body['item_ids'] );
+
+ // Perform another checkout?
+ if ( isset( $request_body['continue'] ) && $request_body['continue'] ) {
+ if ( in_array( $queue_name, array( 'full_sync', 'immediate' ), true ) ) {
+ // Send Full Sync Actions.
+ Sender::get_instance()->do_full_sync();
+ } else {
+ // Send Incremental Sync Actions.
+ if ( $queue->has_any_items() ) {
+ Sender::get_instance()->do_sync();
+ }
+ }
+ }
+
+ if ( is_wp_error( $response ) ) {
+ return $response;
+ }
+
+ return rest_ensure_response(
+ array(
+ 'success' => $response,
+ 'status' => Actions::get_sync_status(),
+ )
+ );
+ }
+
+ /**
+ * Retrieve range of Object Ids for a specified Sync module.
+ *
+ * @since 1.23.1
+ *
+ * @param \WP_REST_Request $request The request sent to the WP REST API.
+ *
+ * @return \WP_REST_Response
+ */
+ public static function get_object_id_range( $request ) {
+
+ $module_name = $request->get_param( 'sync_module' );
+ $batch_size = $request->get_param( 'batch_size' );
+
+ if ( ! self::is_valid_sync_module( $module_name ) ) {
+ return new WP_Error( 'invalid_module', 'This sync module cannot be used to calculate a range.', 400 );
+ }
+ $module = Modules::get_module( $module_name );
+
+ return rest_ensure_response(
+ array(
+ 'ranges' => $module->get_min_max_object_ids_for_batches( $batch_size ),
+ )
+ );
+ }
+
+ /**
+ * Verify that request has default permissions to perform sync actions.
+ *
+ * @since 1.23.1
+ *
+ * @return bool Whether user has capability 'manage_options' or a blog token is used.
+ */
+ public static function verify_default_permissions() {
+ if ( current_user_can( 'manage_options' ) || Rest_Authentication::is_signed_with_blog_token() ) {
+ return true;
+ }
+
+ $error_msg = esc_html__(
+ 'You do not have the correct user permissions to perform this action.
+ Please contact your site admin if you think this is a mistake.',
+ 'jetpack-sync'
+ );
+
+ return new WP_Error( 'invalid_user_permission_sync', $error_msg, array( 'status' => rest_authorization_required_code() ) );
+ }
+
+ /**
+ * Validate Queue name.
+ *
+ * @param string $value Queue Name.
+ *
+ * @return WP_Error
+ */
+ protected static function validate_queue( $value ) {
+ if ( ! isset( $value ) ) {
+ return new WP_Error( 'invalid_queue', 'Queue name is required', 400 );
+ }
+
+ if ( ! in_array( $value, array( 'sync', 'full_sync', 'immediate' ), true ) ) {
+ return new WP_Error( 'invalid_queue', 'Queue name should be sync, full_sync or immediate', 400 );
+ }
+ return $value;
+ }
+
+ /**
+ * Validate name is a valid Sync module.
+ *
+ * @param string $module_name Name of Sync Module.
+ *
+ * @return bool
+ */
+ protected static function is_valid_sync_module( $module_name ) {
+ return in_array(
+ $module_name,
+ array(
+ 'comments',
+ 'posts',
+ 'terms',
+ 'term_relationships',
+ 'users',
+ ),
+ true
+ );
+ }
+
+ /**
+ * Sanitize Item Ids.
+ *
+ * @param string $item Sync item identifier.
+ *
+ * @return string|string[]|null
+ */
+ protected static function sanitize_item_ids( $item ) {
+ // lets not delete any options that don't start with jpsq_sync- .
+ if ( ! is_string( $item ) || substr( $item, 0, 5 ) !== 'jpsq_' ) {
+ return null;
+ }
+ // Limit to A-Z,a-z,0-9,_,-,. .
+ return preg_replace( '/[^A-Za-z0-9-_.]/', '', $item );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-rest-sender.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-rest-sender.php
new file mode 100644
index 00000000..1c5a2a33
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-rest-sender.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * Sync package.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+use WP_Error;
+
+/**
+ * This class will handle checkout of Sync queues for REST Endpoints.
+ *
+ * @since 1.23.1
+ */
+class REST_Sender {
+
+ /**
+ * Items pending send.
+ *
+ * @var array
+ */
+ public $items = array();
+
+ /**
+ * Checkout objects from the queue
+ *
+ * @param string $queue_name Name of Queue.
+ * @param int $number_of_items Number of Items.
+ * @param array $args arguments.
+ *
+ * @return array|WP_Error
+ */
+ public function queue_pull( $queue_name, $number_of_items, $args ) {
+ $queue = new Queue( $queue_name );
+
+ if ( 0 === $queue->size() ) {
+ return new WP_Error( 'queue_size', 'The queue is empty and there is nothing to send', 400 );
+ }
+
+ $sender = Sender::get_instance();
+
+ // try to give ourselves as much time as possible.
+ set_time_limit( 0 );
+
+ if ( ! empty( $args['pop'] ) ) {
+ $buffer = new Queue_Buffer( 'pop', $queue->pop( $number_of_items ) );
+ } else {
+ // let's delete the checkin state.
+ if ( $args['force'] ) {
+ $queue->unlock();
+ }
+ $buffer = $this->get_buffer( $queue, $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 );
+ }
+
+ $encode = isset( $args['encode'] ) ? $args['encode'] : true;
+
+ Settings::set_is_syncing( true );
+ list( $items_to_send, $skipped_items_ids ) = $sender->get_items_to_send( $buffer, $encode );
+ Settings::set_is_syncing( false );
+
+ return array(
+ 'buffer_id' => $buffer->id,
+ 'items' => $items_to_send,
+ 'skipped_items' => $skipped_items_ids,
+ 'codec' => $encode ? $sender->get_codec()->name() : null,
+ 'sent_timestamp' => time(),
+ );
+ }
+
+ /**
+ * Adds Sync items to local property.
+ */
+ public function jetpack_sync_send_data_listener() {
+ foreach ( func_get_args()[0] as $key => $item ) {
+ $this->items[ $key ] = $item;
+ }
+ }
+
+ /**
+ * Check out a buffer of full sync actions.
+ *
+ * @return array Sync Actions to be returned to requestor
+ */
+ public function immediate_full_sync_pull() {
+ // try to give ourselves as much time as possible.
+ set_time_limit( 0 );
+
+ $original_send_data_cb = array( 'Automattic\Jetpack\Sync\Actions', 'send_data' );
+ $temp_send_data_cb = array( $this, 'jetpack_sync_send_data_listener' );
+
+ Sender::get_instance()->set_enqueue_wait_time( 0 );
+ remove_filter( 'jetpack_sync_send_data', $original_send_data_cb );
+ add_filter( 'jetpack_sync_send_data', $temp_send_data_cb, 10, 6 );
+ Sender::get_instance()->do_full_sync();
+ remove_filter( 'jetpack_sync_send_data', $temp_send_data_cb );
+ add_filter( 'jetpack_sync_send_data', $original_send_data_cb, 10, 6 );
+
+ return array(
+ 'items' => $this->items,
+ 'codec' => Sender::get_instance()->get_codec()->name(),
+ 'sent_timestamp' => time(),
+ 'status' => Actions::get_sync_status(),
+ );
+ }
+
+ /**
+ * Checkout items out of the sync queue.
+ *
+ * @param Queue $queue Sync Queue.
+ * @param int $number_of_items Number of items to checkout.
+ *
+ * @return WP_Error
+ */
+ 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 ( false === $buffer ) {
+ return new WP_Error( 'queue_size', 'The queue is empty and there is nothing to send', 400 );
+ }
+
+ return $buffer;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-sender.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-sender.php
new file mode 100644
index 00000000..6699dd61
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-sender.php
@@ -0,0 +1,916 @@
+<?php
+/**
+ * Sync sender.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+use Automattic\Jetpack\Connection\Manager;
+use Automattic\Jetpack\Constants;
+use WP_Error;
+
+/**
+ * This class grabs pending actions from the queue and sends them
+ */
+class Sender {
+ /**
+ * Name of the option that stores the time of the next sync.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const NEXT_SYNC_TIME_OPTION_NAME = 'jetpack_next_sync_time';
+
+ /**
+ * Sync timeout after a WPCOM error.
+ *
+ * @access public
+ *
+ * @var int
+ */
+ const WPCOM_ERROR_SYNC_DELAY = 60;
+
+ /**
+ * Sync timeout after a queue has been locked.
+ *
+ * @access public
+ *
+ * @var int
+ */
+ const QUEUE_LOCKED_SYNC_DELAY = 10;
+
+ /**
+ * Maximum bytes to checkout without exceeding the memory limit.
+ *
+ * @access private
+ *
+ * @var int
+ */
+ private $dequeue_max_bytes;
+
+ /**
+ * Maximum bytes in a single encoded item.
+ *
+ * @access private
+ *
+ * @var int
+ */
+ private $upload_max_bytes;
+
+ /**
+ * Maximum number of sync items in a single action.
+ *
+ * @access private
+ *
+ * @var int
+ */
+ private $upload_max_rows;
+
+ /**
+ * Maximum time for perfirming a checkout of items from the queue (in seconds).
+ *
+ * @access private
+ *
+ * @var int
+ */
+ private $max_dequeue_time;
+
+ /**
+ * How many seconds to wait after sending sync items after exceeding the sync wait threshold (in seconds).
+ *
+ * @access private
+ *
+ * @var int
+ */
+ private $sync_wait_time;
+
+ /**
+ * How much maximum time to wait for the checkout to finish (in seconds).
+ *
+ * @access private
+ *
+ * @var int
+ */
+ private $sync_wait_threshold;
+
+ /**
+ * How much maximum time to wait for the sync items to be queued for sending (in seconds).
+ *
+ * @access private
+ *
+ * @var int
+ */
+ private $enqueue_wait_time;
+
+ /**
+ * Incremental sync queue object.
+ *
+ * @access private
+ *
+ * @var Automattic\Jetpack\Sync\Queue
+ */
+ private $sync_queue;
+
+ /**
+ * Full sync queue object.
+ *
+ * @access private
+ *
+ * @var Automattic\Jetpack\Sync\Queue
+ */
+ private $full_sync_queue;
+
+ /**
+ * Codec object for encoding and decoding sync items.
+ *
+ * @access private
+ *
+ * @var Automattic\Jetpack\Sync\Codec_Interface
+ */
+ private $codec;
+
+ /**
+ * The current user before we change or clear it.
+ *
+ * @access private
+ *
+ * @var \WP_User
+ */
+ private $old_user;
+
+ /**
+ * Container for the singleton instance of this class.
+ *
+ * @access private
+ * @static
+ *
+ * @var Automattic\Jetpack\Sync\Sender
+ */
+ private static $instance;
+
+ /**
+ * Retrieve the singleton instance of this class.
+ *
+ * @access public
+ * @static
+ *
+ * @return Sender
+ */
+ public static function get_instance() {
+ if ( null === self::$instance ) {
+ self::$instance = new self();
+ }
+
+ return self::$instance;
+ }
+
+ /**
+ * Constructor.
+ * This is necessary because you can't use "new" when you declare instance properties >:(
+ *
+ * @access protected
+ * @static
+ */
+ protected function __construct() {
+ $this->set_defaults();
+ $this->init();
+ }
+
+ /**
+ * Initialize the sender.
+ * Prepares the current user and initializes all sync modules.
+ *
+ * @access private
+ */
+ private function init() {
+ add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_set_user_from_token' ), 1 );
+ add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_clear_user_from_token' ), 20 );
+ add_filter( 'jetpack_xmlrpc_unauthenticated_methods', array( $this, 'register_jetpack_xmlrpc_methods' ) );
+ foreach ( Modules::get_modules() as $module ) {
+ $module->init_before_send();
+ }
+ }
+
+ /**
+ * Detect if this is a XMLRPC request with a valid signature.
+ * If so, changes the user to the new one.
+ *
+ * @access public
+ */
+ public function maybe_set_user_from_token() {
+ $connection = new Manager();
+ $verified_user = $connection->verify_xml_rpc_signature();
+ if ( Constants::is_true( 'XMLRPC_REQUEST' ) &&
+ ! is_wp_error( $verified_user )
+ && $verified_user
+ ) {
+ $old_user = wp_get_current_user();
+ $this->old_user = isset( $old_user->ID ) ? $old_user->ID : 0;
+ wp_set_current_user( $verified_user['user_id'] );
+ }
+ }
+
+ /**
+ * If we used to have a previous current user, revert back to it.
+ *
+ * @access public
+ */
+ public function maybe_clear_user_from_token() {
+ if ( isset( $this->old_user ) ) {
+ wp_set_current_user( $this->old_user );
+ }
+ }
+
+ /**
+ * Retrieve the next sync time.
+ *
+ * @access public
+ *
+ * @param string $queue_name Name of the queue.
+ * @return float Timestamp of the next sync.
+ */
+ public function get_next_sync_time( $queue_name ) {
+ return (float) get_option( self::NEXT_SYNC_TIME_OPTION_NAME . '_' . $queue_name, 0 );
+ }
+
+ /**
+ * Set the next sync time.
+ *
+ * @access public
+ *
+ * @param int $time Timestamp of the next sync.
+ * @param string $queue_name Name of the queue.
+ * @return boolean True if update was successful, false otherwise.
+ */
+ public function set_next_sync_time( $time, $queue_name ) {
+ return update_option( self::NEXT_SYNC_TIME_OPTION_NAME . '_' . $queue_name, $time, true );
+ }
+
+ /**
+ * Trigger a full sync.
+ *
+ * @access public
+ *
+ * @return boolean|WP_Error True if this sync sending was successful, error object otherwise.
+ */
+ public function do_full_sync() {
+ $sync_module = Modules::get_module( 'full-sync' );
+ if ( ! $sync_module ) {
+ return;
+ }
+ // Full Sync Disabled.
+ if ( ! Settings::get_setting( 'full_sync_sender_enabled' ) ) {
+ return;
+ }
+
+ // Don't sync if request is marked as read only.
+ if ( Constants::is_true( 'JETPACK_SYNC_READ_ONLY' ) ) {
+ return new WP_Error( 'jetpack_sync_read_only' );
+ }
+
+ // Sync not started or Sync finished.
+ $status = $sync_module->get_status();
+ if ( false === $status['started'] || ( ! empty( $status['started'] ) && ! empty( $status['finished'] ) ) ) {
+ return false;
+ }
+
+ $this->continue_full_sync_enqueue();
+ // immediate full sync sends data in continue_full_sync_enqueue.
+ if ( false === strpos( get_class( $sync_module ), 'Full_Sync_Immediately' ) ) {
+ return $this->do_sync_and_set_delays( $this->full_sync_queue );
+ } else {
+ $status = $sync_module->get_status();
+ // Sync not started or Sync finished.
+ if ( false === $status['started'] || ( ! empty( $status['started'] ) && ! empty( $status['finished'] ) ) ) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Enqueue the next sync items for sending.
+ * Will not be done if the current request is a WP import one.
+ * Will be delayed until the next sync time comes.
+ *
+ * @access private
+ */
+ private function continue_full_sync_enqueue() {
+ if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
+ return false;
+ }
+
+ if ( $this->get_next_sync_time( 'full-sync-enqueue' ) > microtime( true ) ) {
+ return false;
+ }
+
+ Modules::get_module( 'full-sync' )->continue_enqueuing();
+
+ $this->set_next_sync_time( time() + $this->get_enqueue_wait_time(), 'full-sync-enqueue' );
+ }
+
+ /**
+ * Trigger incremental sync.
+ *
+ * @access public
+ *
+ * @return boolean|WP_Error True if this sync sending was successful, error object otherwise.
+ */
+ public function do_sync() {
+ return $this->do_sync_and_set_delays( $this->sync_queue );
+ }
+
+ /**
+ * Trigger sync for a certain sync queue.
+ * Responsible for setting next sync time.
+ * Will not be delayed if the current request is a WP import one.
+ * Will be delayed until the next sync time comes.
+ *
+ * @access public
+ *
+ * @param Automattic\Jetpack\Sync\Queue $queue Queue object.
+ *
+ * @return boolean|WP_Error True if this sync sending was successful, error object otherwise.
+ */
+ public function do_sync_and_set_delays( $queue ) {
+ // Don't sync if importing.
+ if ( defined( 'WP_IMPORTING' ) && WP_IMPORTING ) {
+ return new WP_Error( 'is_importing' );
+ }
+
+ // Don't sync if request is marked as read only.
+ if ( Constants::is_true( 'JETPACK_SYNC_READ_ONLY' ) ) {
+ return new WP_Error( 'jetpack_sync_read_only' );
+ }
+
+ if ( ! Settings::is_sender_enabled( $queue->id ) ) {
+ return new WP_Error( 'sender_disabled_for_queue_' . $queue->id );
+ }
+
+ // Return early if we've gotten a retry-after header response.
+ $retry_time = get_option( Actions::RETRY_AFTER_PREFIX . $queue->id );
+ if ( $retry_time ) {
+ // If expired update to false but don't send. Send will occurr in new request to avoid race conditions.
+ if ( microtime( true ) > $retry_time ) {
+ update_option( Actions::RETRY_AFTER_PREFIX . $queue->id, false, false );
+ }
+ return new WP_Error( 'retry_after' );
+ }
+
+ // Don't sync if we are throttled.
+ if ( $this->get_next_sync_time( $queue->id ) > microtime( true ) ) {
+ return new WP_Error( 'sync_throttled' );
+ }
+
+ $start_time = microtime( true );
+
+ Settings::set_is_syncing( true );
+
+ $sync_result = $this->do_sync_for_queue( $queue );
+
+ Settings::set_is_syncing( false );
+
+ $exceeded_sync_wait_threshold = ( microtime( true ) - $start_time ) > (float) $this->get_sync_wait_threshold();
+
+ if ( is_wp_error( $sync_result ) ) {
+ if ( 'unclosed_buffer' === $sync_result->get_error_code() ) {
+ $this->set_next_sync_time( time() + self::QUEUE_LOCKED_SYNC_DELAY, $queue->id );
+ }
+ if ( 'wpcom_error' === $sync_result->get_error_code() ) {
+ $this->set_next_sync_time( time() + self::WPCOM_ERROR_SYNC_DELAY, $queue->id );
+ }
+ } elseif ( $exceeded_sync_wait_threshold ) {
+ // If we actually sent data and it took a while, wait before sending again.
+ $this->set_next_sync_time( time() + $this->get_sync_wait_time(), $queue->id );
+ }
+
+ return $sync_result;
+ }
+
+ /**
+ * Retrieve the next sync items to send.
+ *
+ * @access public
+ *
+ * @param (array|Automattic\Jetpack\Sync\Queue_Buffer) $buffer_or_items Queue buffer or array of objects.
+ * @param boolean $encode Whether to encode the items.
+ * @return array Sync items to send.
+ */
+ public function get_items_to_send( $buffer_or_items, $encode = true ) {
+ // Track how long we've been processing so we can avoid request timeouts.
+ $start_time = microtime( true );
+ $upload_size = 0;
+ $items_to_send = array();
+ $items = is_array( $buffer_or_items ) ? $buffer_or_items : $buffer_or_items->get_items();
+ if ( ! is_array( $items ) ) {
+ $items = array();
+ }
+
+ // Set up current screen to avoid errors rendering content.
+ require_once ABSPATH . 'wp-admin/includes/class-wp-screen.php';
+ require_once ABSPATH . 'wp-admin/includes/screen.php';
+ set_current_screen( 'sync' );
+ $skipped_items_ids = array();
+ /**
+ * We estimate the total encoded size as we go by encoding each item individually.
+ * This is expensive, but the only way to really know :/
+ */
+ foreach ( $items as $key => $item ) {
+ // Suspending cache addition help prevent overloading in memory cache of large sites.
+ wp_suspend_cache_addition( true );
+ /**
+ * Modify the data within an action before it is serialized and sent to the server
+ * For example, during full sync this expands Post ID's into full Post objects,
+ * so that we don't have to serialize the whole object into the queue.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param array The action parameters
+ * @param int The ID of the user who triggered the action
+ */
+ $item[1] = apply_filters( 'jetpack_sync_before_send_' . $item[0], $item[1], $item[2] );
+ wp_suspend_cache_addition( false );
+ // Serialization usage can lead to empty, null or false action_name. Lets skip as there is no information to send.
+ if ( empty( $item[0] ) || false === $item[1] ) {
+ $skipped_items_ids[] = $key;
+ continue;
+ }
+ $encoded_item = $this->codec->encode( $item );
+ $upload_size += strlen( $encoded_item );
+ if ( $upload_size > $this->upload_max_bytes && count( $items_to_send ) > 0 ) {
+ break;
+ }
+ $items_to_send[ $key ] = $encode ? $encoded_item : $item;
+ if ( microtime( true ) - $start_time > $this->max_dequeue_time ) {
+ break;
+ }
+ }
+
+ return array( $items_to_send, $skipped_items_ids, $items, microtime( true ) - $start_time );
+ }
+
+ /**
+ * If supported, flush all response data to the client and finish the request.
+ * This allows for time consuming tasks to be performed without leaving the connection open.
+ *
+ * @access private
+ */
+ private function fastcgi_finish_request() {
+ if ( function_exists( 'fastcgi_finish_request' ) && version_compare( phpversion(), '7.0.16', '>=' ) ) {
+ fastcgi_finish_request();
+ }
+ }
+
+ /**
+ * Perform sync for a certain sync queue.
+ *
+ * @access public
+ *
+ * @param Automattic\Jetpack\Sync\Queue $queue Queue object.
+ *
+ * @return boolean|WP_Error True if this sync sending was successful, error object otherwise.
+ */
+ public function do_sync_for_queue( $queue ) {
+ do_action( 'jetpack_sync_before_send_queue_' . $queue->id );
+ if ( $queue->size() === 0 ) {
+ return new WP_Error( 'empty_queue_' . $queue->id );
+ }
+
+ /**
+ * Now that we're sure we are about to sync, try to ignore user abort
+ * so we can avoid getting into a bad state.
+ */
+ if ( function_exists( 'ignore_user_abort' ) ) {
+ ignore_user_abort( true );
+ }
+
+ /* Don't make the request block till we finish, if possible. */
+ if ( Constants::is_true( 'REST_REQUEST' ) || Constants::is_true( 'XMLRPC_REQUEST' ) ) {
+ $this->fastcgi_finish_request();
+ }
+
+ $checkout_start_time = microtime( true );
+
+ $buffer = $queue->checkout_with_memory_limit( $this->dequeue_max_bytes, $this->upload_max_rows );
+
+ if ( ! $buffer ) {
+ // Buffer has no items.
+ return new WP_Error( 'empty_buffer' );
+ }
+
+ if ( is_wp_error( $buffer ) ) {
+ return $buffer;
+ }
+
+ $checkout_duration = microtime( true ) - $checkout_start_time;
+
+ list( $items_to_send, $skipped_items_ids, $items, $preprocess_duration ) = $this->get_items_to_send( $buffer, true );
+ if ( ! empty( $items_to_send ) ) {
+ /**
+ * Fires when data is ready to send to the server.
+ * Return false or WP_Error to abort the sync (e.g. if there's an error)
+ * The items will be automatically re-sent later
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param array $data The action buffer
+ * @param string $codec The codec name used to encode the data
+ * @param double $time The current time
+ * @param string $queue The queue used to send ('sync' or 'full_sync')
+ * @param float $checkout_duration The duration of the checkout operation.
+ * @param float $preprocess_duration The duration of the pre-process operation.
+ * @param int $queue_size The size of the sync queue at the time of processing.
+ */
+ Settings::set_is_sending( true );
+ $processed_item_ids = apply_filters( 'jetpack_sync_send_data', $items_to_send, $this->codec->name(), microtime( true ), $queue->id, $checkout_duration, $preprocess_duration, $queue->size(), $buffer->id );
+ Settings::set_is_sending( false );
+ } else {
+ $processed_item_ids = $skipped_items_ids;
+ $skipped_items_ids = array();
+ }
+
+ if ( 'non-blocking' !== $processed_item_ids ) {
+ if ( ! $processed_item_ids || is_wp_error( $processed_item_ids ) ) {
+ $checked_in_item_ids = $queue->checkin( $buffer );
+ if ( is_wp_error( $checked_in_item_ids ) ) {
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
+ error_log( 'Error checking in buffer: ' . $checked_in_item_ids->get_error_message() );
+ $queue->force_checkin();
+ }
+ if ( is_wp_error( $processed_item_ids ) ) {
+ return new WP_Error( 'wpcom_error', $processed_item_ids->get_error_code() );
+ }
+
+ // Returning a wpcom_error is a sign to the caller that we should wait a while before syncing again.
+ return new WP_Error( 'wpcom_error', 'jetpack_sync_send_data_false' );
+ } else {
+ // Detect if the last item ID was an error.
+ $had_wp_error = is_wp_error( end( $processed_item_ids ) );
+ if ( $had_wp_error ) {
+ $wp_error = array_pop( $processed_item_ids );
+ }
+ // Also checkin any items that were skipped.
+ if ( count( $skipped_items_ids ) > 0 ) {
+ $processed_item_ids = array_merge( $processed_item_ids, $skipped_items_ids );
+ }
+ $processed_items = array_intersect_key( $items, array_flip( $processed_item_ids ) );
+ /**
+ * Allows us to keep track of all the actions that have been sent.
+ * Allows us to calculate the progress of specific actions.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param array $processed_actions The actions that we send successfully.
+ */
+ do_action( 'jetpack_sync_processed_actions', $processed_items );
+ $queue->close( $buffer, $processed_item_ids );
+ // Returning a WP_Error is a sign to the caller that we should wait a while before syncing again.
+ if ( $had_wp_error ) {
+ return new WP_Error( 'wpcom_error', $wp_error->get_error_code() );
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Immediately sends a single item without firing or enqueuing it
+ *
+ * @param string $action_name The action.
+ * @param array $data The data associated with the action.
+ *
+ * @return Items processed. TODO: this doesn't make much sense anymore, it should probably be just a bool.
+ */
+ public function send_action( $action_name, $data = null ) {
+ if ( ! Settings::is_sender_enabled( 'full_sync' ) ) {
+ return array();
+ }
+
+ // Compose the data to be sent.
+ $action_to_send = $this->create_action_to_send( $action_name, $data );
+
+ list( $items_to_send, $skipped_items_ids, $items, $preprocess_duration ) = $this->get_items_to_send( $action_to_send, true ); // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ Settings::set_is_sending( true );
+ $processed_item_ids = apply_filters( 'jetpack_sync_send_data', $items_to_send, $this->get_codec()->name(), microtime( true ), 'immediate-send', 0, $preprocess_duration );
+ Settings::set_is_sending( false );
+
+ /**
+ * Allows us to keep track of all the actions that have been sent.
+ * Allows us to calculate the progress of specific actions.
+ *
+ * @param array $processed_actions The actions that we send successfully.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ */
+ do_action( 'jetpack_sync_processed_actions', $action_to_send );
+
+ return $processed_item_ids;
+ }
+
+ /**
+ * Create an synthetic action for direct sending to WPCOM during full sync (for example)
+ *
+ * @access private
+ *
+ * @param string $action_name The action.
+ * @param array $data The data associated with the action.
+ * @return array An array of synthetic sync actions keyed by current microtime(true)
+ */
+ private function create_action_to_send( $action_name, $data ) {
+ return array(
+ (string) microtime( true ) => array(
+ $action_name,
+ $data,
+ get_current_user_id(),
+ microtime( true ),
+ Settings::is_importing(),
+ ),
+ );
+ }
+
+ /**
+ * Returns any object that is able to be synced.
+ *
+ * @access public
+ *
+ * @param array $args the synchronized object parameters.
+ * @return string Encoded sync object.
+ */
+ public function sync_object( $args ) {
+ // For example: posts, post, 5.
+ list( $module_name, $object_type, $id ) = $args;
+
+ $sync_module = Modules::get_module( $module_name );
+ $codec = $this->get_codec();
+
+ return $codec->encode( $sync_module->get_object_by_id( $object_type, $id ) );
+ }
+
+ /**
+ * Register additional sync XML-RPC methods available to Jetpack for authenticated users.
+ *
+ * @access public
+ * @since 1.6.3
+ * @since-jetpack 7.8.0
+ *
+ * @param array $jetpack_methods XML-RPC methods available to the Jetpack Server.
+ * @return array Filtered XML-RPC methods.
+ */
+ public function register_jetpack_xmlrpc_methods( $jetpack_methods ) {
+ $jetpack_methods['jetpack.syncObject'] = array( $this, 'sync_object' );
+ return $jetpack_methods;
+ }
+
+ /**
+ * Get the incremental sync queue object.
+ *
+ * @access public
+ *
+ * @return Automattic\Jetpack\Sync\Queue Queue object.
+ */
+ public function get_sync_queue() {
+ return $this->sync_queue;
+ }
+
+ /**
+ * Get the full sync queue object.
+ *
+ * @access public
+ *
+ * @return Automattic\Jetpack\Sync\Queue Queue object.
+ */
+ public function get_full_sync_queue() {
+ return $this->full_sync_queue;
+ }
+
+ /**
+ * Get the codec object.
+ *
+ * @access public
+ *
+ * @return Automattic\Jetpack\Sync\Codec_Interface Codec object.
+ */
+ public function get_codec() {
+ return $this->codec;
+ }
+
+ /**
+ * Determine the codec object.
+ * Use gzip deflate if supported.
+ *
+ * @access public
+ */
+ public function set_codec() {
+ if ( function_exists( 'gzinflate' ) ) {
+ $this->codec = new JSON_Deflate_Array_Codec();
+ } else {
+ $this->codec = new Simple_Codec();
+ }
+ }
+
+ /**
+ * Compute and send all the checksums.
+ *
+ * @access public
+ */
+ public function send_checksum() {
+ $store = new Replicastore();
+ do_action( 'jetpack_sync_checksum', $store->checksum_all() );
+ }
+
+ /**
+ * Reset the incremental sync queue.
+ *
+ * @access public
+ */
+ public function reset_sync_queue() {
+ $this->sync_queue->reset();
+ }
+
+ /**
+ * Reset the full sync queue.
+ *
+ * @access public
+ */
+ public function reset_full_sync_queue() {
+ $this->full_sync_queue->reset();
+ }
+
+ /**
+ * Set the maximum bytes to checkout without exceeding the memory limit.
+ *
+ * @access public
+ *
+ * @param int $size Maximum bytes to checkout.
+ */
+ public function set_dequeue_max_bytes( $size ) {
+ $this->dequeue_max_bytes = $size;
+ }
+
+ /**
+ * Set the maximum bytes in a single encoded item.
+ *
+ * @access public
+ *
+ * @param int $max_bytes Maximum bytes in a single encoded item.
+ */
+ public function set_upload_max_bytes( $max_bytes ) {
+ $this->upload_max_bytes = $max_bytes;
+ }
+
+ /**
+ * Set the maximum number of sync items in a single action.
+ *
+ * @access public
+ *
+ * @param int $max_rows Maximum number of sync items.
+ */
+ public function set_upload_max_rows( $max_rows ) {
+ $this->upload_max_rows = $max_rows;
+ }
+
+ /**
+ * Set the sync wait time (in seconds).
+ *
+ * @access public
+ *
+ * @param int $seconds Sync wait time.
+ */
+ public function set_sync_wait_time( $seconds ) {
+ $this->sync_wait_time = $seconds;
+ }
+
+ /**
+ * Get current sync wait time (in seconds).
+ *
+ * @access public
+ *
+ * @return int Sync wait time.
+ */
+ public function get_sync_wait_time() {
+ return $this->sync_wait_time;
+ }
+
+ /**
+ * Set the enqueue wait time (in seconds).
+ *
+ * @access public
+ *
+ * @param int $seconds Enqueue wait time.
+ */
+ public function set_enqueue_wait_time( $seconds ) {
+ $this->enqueue_wait_time = $seconds;
+ }
+
+ /**
+ * Get current enqueue wait time (in seconds).
+ *
+ * @access public
+ *
+ * @return int Enqueue wait time.
+ */
+ public function get_enqueue_wait_time() {
+ return $this->enqueue_wait_time;
+ }
+
+ /**
+ * Set the sync wait threshold (in seconds).
+ *
+ * @access public
+ *
+ * @param int $seconds Sync wait threshold.
+ */
+ public function set_sync_wait_threshold( $seconds ) {
+ $this->sync_wait_threshold = $seconds;
+ }
+
+ /**
+ * Get current sync wait threshold (in seconds).
+ *
+ * @access public
+ *
+ * @return int Sync wait threshold.
+ */
+ public function get_sync_wait_threshold() {
+ return $this->sync_wait_threshold;
+ }
+
+ /**
+ * Set the maximum time for perfirming a checkout of items from the queue (in seconds).
+ *
+ * @access public
+ *
+ * @param int $seconds Maximum dequeue time.
+ */
+ public function set_max_dequeue_time( $seconds ) {
+ $this->max_dequeue_time = $seconds;
+ }
+
+ /**
+ * Initialize the sync queues, codec and set the default settings.
+ *
+ * @access public
+ */
+ public function set_defaults() {
+ $this->sync_queue = new Queue( 'sync' );
+ $this->full_sync_queue = new Queue( 'full_sync' );
+ $this->set_codec();
+
+ // Saved settings.
+ Settings::set_importing( null );
+ $settings = Settings::get_settings();
+ $this->set_dequeue_max_bytes( $settings['dequeue_max_bytes'] );
+ $this->set_upload_max_bytes( $settings['upload_max_bytes'] );
+ $this->set_upload_max_rows( $settings['upload_max_rows'] );
+ $this->set_sync_wait_time( $settings['sync_wait_time'] );
+ $this->set_enqueue_wait_time( $settings['enqueue_wait_time'] );
+ $this->set_sync_wait_threshold( $settings['sync_wait_threshold'] );
+ $this->set_max_dequeue_time( Defaults::get_max_sync_execution_time() );
+ }
+
+ /**
+ * Reset sync queues, modules and settings.
+ *
+ * @access public
+ */
+ public function reset_data() {
+ $this->reset_sync_queue();
+ $this->reset_full_sync_queue();
+
+ foreach ( Modules::get_modules() as $module ) {
+ $module->reset_data();
+ }
+
+ foreach ( array( 'sync', 'full_sync', 'full-sync-enqueue' ) as $queue_name ) {
+ delete_option( self::NEXT_SYNC_TIME_OPTION_NAME . '_' . $queue_name );
+ }
+
+ Settings::reset_data();
+ }
+
+ /**
+ * Perform cleanup at the event of plugin uninstallation.
+ *
+ * @access public
+ */
+ public function uninstall() {
+ // Lets delete all the other fun stuff like transient and option and the sync queue.
+ $this->reset_data();
+
+ // Delete the full sync status.
+ delete_option( 'jetpack_full_sync_status' );
+
+ // Clear the sync cron.
+ wp_clear_scheduled_hook( 'jetpack_sync_cron' );
+ wp_clear_scheduled_hook( 'jetpack_sync_full_cron' );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-server.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-server.php
new file mode 100644
index 00000000..7b6d0545
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-server.php
@@ -0,0 +1,195 @@
+<?php
+/**
+ * Sync server.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+use WP_Error;
+
+/**
+ * Simple version of a Jetpack Sync Server - just receives arrays of events and
+ * issues them locally with the 'jetpack_sync_remote_action' action.
+ */
+class Server {
+ /**
+ * Codec used to decode sync events.
+ *
+ * @access private
+ *
+ * @var Automattic\Jetpack\Sync\Codec_Interface
+ */
+ private $codec;
+
+ /**
+ * Maximum time for processing sync actions.
+ *
+ * @access public
+ *
+ * @var int
+ */
+ const MAX_TIME_PER_REQUEST_IN_SECONDS = 15;
+
+ /**
+ * Prefix of the blog lock transient.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const BLOG_LOCK_TRANSIENT_PREFIX = 'jp_sync_req_lock_';
+
+ /**
+ * Lifetime of the blog lock transient.
+ *
+ * @access public
+ *
+ * @var int
+ */
+ const BLOG_LOCK_TRANSIENT_EXPIRY = 60; // Seconds.
+
+ /**
+ * Constructor.
+ *
+ * This is necessary because you can't use "new" when you declare instance properties >:(
+ *
+ * @access public
+ */
+ public function __construct() {
+ $this->codec = new JSON_Deflate_Array_Codec();
+ }
+
+ /**
+ * Set the codec instance.
+ *
+ * @access public
+ *
+ * @param Automattic\Jetpack\Sync\Codec_Interface $codec Codec instance.
+ */
+ public function set_codec( Codec_Interface $codec ) {
+ $this->codec = $codec;
+ }
+
+ /**
+ * Attempt to lock the request when the server receives concurrent requests from the same blog.
+ *
+ * @access public
+ *
+ * @param int $blog_id ID of the blog.
+ * @param int $expiry Blog lock transient lifetime.
+ * @return boolean True if succeeded, false otherwise.
+ */
+ public function attempt_request_lock( $blog_id, $expiry = self::BLOG_LOCK_TRANSIENT_EXPIRY ) {
+ $transient_name = $this->get_concurrent_request_transient_name( $blog_id );
+ $locked_time = get_site_transient( $transient_name );
+ if ( $locked_time ) {
+ return false;
+ }
+ set_site_transient( $transient_name, microtime( true ), $expiry );
+
+ return true;
+ }
+
+ /**
+ * Retrieve the blog lock transient name for a particular blog.
+ *
+ * @access public
+ *
+ * @param int $blog_id ID of the blog.
+ * @return string Name of the blog lock transient.
+ */
+ private function get_concurrent_request_transient_name( $blog_id ) {
+ return self::BLOG_LOCK_TRANSIENT_PREFIX . $blog_id;
+ }
+
+ /**
+ * Remove the request lock from a particular blog ID.
+ *
+ * @access public
+ *
+ * @param int $blog_id ID of the blog.
+ */
+ public function remove_request_lock( $blog_id ) {
+ delete_site_transient( $this->get_concurrent_request_transient_name( $blog_id ) );
+ }
+
+ /**
+ * Receive and process sync events.
+ *
+ * @access public
+ *
+ * @param array $data Sync events.
+ * @param object $token The auth token used to invoke the API.
+ * @param int $sent_timestamp Timestamp (in seconds) when the actions were transmitted.
+ * @param string $queue_id ID of the queue from which the event was sent (`sync` or `full_sync`).
+ * @return array Processed sync events.
+ */
+ public function receive( $data, $token = null, $sent_timestamp = null, $queue_id = null ) {
+ $start_time = microtime( true );
+ if ( ! is_array( $data ) ) {
+ return new WP_Error( 'action_decoder_error', 'Events must be an array' );
+ }
+
+ if ( $token && ! $this->attempt_request_lock( $token->blog_id ) ) {
+ /**
+ * Fires when the server receives two concurrent requests from the same blog
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param token The token object of the misbehaving site
+ */
+ do_action( 'jetpack_sync_multi_request_fail', $token );
+
+ return new WP_Error( 'concurrent_request_error', 'There is another request running for the same blog ID' );
+ }
+
+ $events = wp_unslash( array_map( array( $this->codec, 'decode' ), $data ) );
+ $events_processed = array();
+
+ /**
+ * Fires when an array of actions are received from a remote Jetpack site
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param array Array of actions received from the remote site
+ */
+ do_action( 'jetpack_sync_remote_actions', $events, $token );
+
+ foreach ( $events as $key => $event ) {
+ list( $action_name, $args, $user_id, $timestamp, $silent ) = $event;
+
+ /**
+ * Fires when an action is received from a remote Jetpack site
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param string $action_name The name of the action executed on the remote site
+ * @param array $args The arguments passed to the action
+ * @param int $user_id The external_user_id who did the action
+ * @param bool $silent Whether the item was created via import
+ * @param double $timestamp Timestamp (in seconds) when the action occurred
+ * @param double $sent_timestamp Timestamp (in seconds) when the action was transmitted
+ * @param string $queue_id ID of the queue from which the event was sent (sync or full_sync)
+ * @param array $token The auth token used to invoke the API
+ */
+ do_action( 'jetpack_sync_remote_action', $action_name, $args, $user_id, $silent, $timestamp, $sent_timestamp, $queue_id, $token );
+
+ $events_processed[] = $key;
+
+ if ( microtime( true ) - $start_time > self::MAX_TIME_PER_REQUEST_IN_SECONDS ) {
+ break;
+ }
+ }
+
+ if ( $token ) {
+ $this->remove_request_lock( $token->blog_id );
+ }
+
+ return $events_processed;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-settings.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-settings.php
new file mode 100644
index 00000000..a923fbf3
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-settings.php
@@ -0,0 +1,568 @@
+<?php
+/**
+ * Sync settings.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+/**
+ * Class to manage the sync settings.
+ */
+class Settings {
+ /**
+ * Prefix, used for the sync settings option names.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const SETTINGS_OPTION_PREFIX = 'jetpack_sync_settings_';
+
+ /**
+ * A whitelist of valid settings.
+ *
+ * @access public
+ * @static
+ *
+ * @var array
+ */
+ public static $valid_settings = array(
+ 'dequeue_max_bytes' => true,
+ 'upload_max_bytes' => true,
+ 'upload_max_rows' => true,
+ 'sync_wait_time' => true,
+ 'sync_wait_threshold' => true,
+ 'enqueue_wait_time' => true,
+ 'max_queue_size' => true,
+ 'max_queue_lag' => true,
+ 'queue_max_writes_sec' => true,
+ 'post_types_blacklist' => true,
+ 'taxonomies_blacklist' => true,
+ 'disable' => true,
+ 'network_disable' => true,
+ 'render_filtered_content' => true,
+ 'post_meta_whitelist' => true,
+ 'comment_meta_whitelist' => true,
+ 'max_enqueue_full_sync' => true,
+ 'max_queue_size_full_sync' => true,
+ 'sync_via_cron' => true,
+ 'cron_sync_time_limit' => true,
+ 'known_importers' => true,
+ 'term_relationships_full_sync_item_size' => true,
+ 'sync_sender_enabled' => true,
+ 'full_sync_sender_enabled' => true,
+ 'full_sync_send_duration' => true,
+ 'full_sync_limits' => true,
+ 'checksum_disable' => true,
+ );
+
+ /**
+ * Whether WordPress is currently running an import.
+ *
+ * @access public
+ * @static
+ *
+ * @var null|boolean
+ */
+ public static $is_importing;
+
+ /**
+ * Whether WordPress is currently running a WP cron request.
+ *
+ * @access public
+ * @static
+ *
+ * @var null|boolean
+ */
+ public static $is_doing_cron;
+
+ /**
+ * Whether we're currently syncing.
+ *
+ * @access public
+ * @static
+ *
+ * @var null|boolean
+ */
+ public static $is_syncing;
+
+ /**
+ * Whether we're currently sending sync items.
+ *
+ * @access public
+ * @static
+ *
+ * @var null|boolean
+ */
+ public static $is_sending;
+
+ /**
+ * Retrieve all settings with their current values.
+ *
+ * @access public
+ * @static
+ *
+ * @return array All current settings.
+ */
+ public static function get_settings() {
+ $settings = array();
+ foreach ( array_keys( self::$valid_settings ) as $setting ) {
+ $settings[ $setting ] = self::get_setting( $setting );
+ }
+
+ return $settings;
+ }
+
+ /**
+ * Fetches the setting. It saves it if the setting doesn't exist, so that it gets
+ * autoloaded on page load rather than re-queried every time.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $setting The setting name.
+ * @return mixed The setting value.
+ */
+ public static function get_setting( $setting ) {
+ if ( ! isset( self::$valid_settings[ $setting ] ) ) {
+ return false;
+ }
+
+ if ( self::is_network_setting( $setting ) ) {
+ if ( is_multisite() ) {
+ $value = get_site_option( self::SETTINGS_OPTION_PREFIX . $setting );
+ } else {
+ // On single sites just return the default setting.
+ return Defaults::get_default_setting( $setting );
+ }
+ } else {
+ $value = get_option( self::SETTINGS_OPTION_PREFIX . $setting );
+ }
+
+ if ( false === $value ) { // No default value is set.
+ $value = Defaults::get_default_setting( $setting );
+ if ( self::is_network_setting( $setting ) ) {
+ update_site_option( self::SETTINGS_OPTION_PREFIX . $setting, $value );
+ } else {
+ // We set one so that it gets autoloaded.
+ update_option( self::SETTINGS_OPTION_PREFIX . $setting, $value, true );
+ }
+ }
+
+ if ( is_numeric( $value ) ) {
+ $value = (int) $value;
+ }
+ $default_array_value = null;
+ switch ( $setting ) {
+ case 'post_types_blacklist':
+ $default_array_value = Defaults::$blacklisted_post_types;
+ break;
+ case 'taxonomies_blacklist':
+ $default_array_value = Defaults::$blacklisted_taxonomies;
+ break;
+ case 'post_meta_whitelist':
+ $default_array_value = Defaults::get_post_meta_whitelist();
+ break;
+ case 'comment_meta_whitelist':
+ $default_array_value = Defaults::get_comment_meta_whitelist();
+ break;
+ case 'known_importers':
+ $default_array_value = Defaults::get_known_importers();
+ break;
+ }
+
+ if ( $default_array_value ) {
+ if ( is_array( $value ) ) {
+ $value = array_unique( array_merge( $value, $default_array_value ) );
+ } else {
+ $value = $default_array_value;
+ }
+ }
+
+ return $value;
+ }
+
+ /**
+ * Change multiple settings in the same time.
+ *
+ * @access public
+ * @static
+ *
+ * @param array $new_settings The new settings.
+ */
+ public static function update_settings( $new_settings ) {
+ $validated_settings = array_intersect_key( $new_settings, self::$valid_settings );
+ foreach ( $validated_settings as $setting => $value ) {
+
+ if ( self::is_network_setting( $setting ) ) {
+ if ( is_multisite() && is_main_site() ) {
+ update_site_option( self::SETTINGS_OPTION_PREFIX . $setting, $value );
+ }
+ } else {
+ update_option( self::SETTINGS_OPTION_PREFIX . $setting, $value, true );
+ }
+
+ // If we set the disabled option to true, clear the queues.
+ if ( ( 'disable' === $setting || 'network_disable' === $setting ) && (bool) $value ) {
+ $listener = Listener::get_instance();
+ $listener->get_sync_queue()->reset();
+ $listener->get_full_sync_queue()->reset();
+ }
+ }
+ }
+
+ /**
+ * Whether the specified setting is a network setting.
+ *
+ * @access public
+ * @static
+ *
+ * @param string $setting Setting name.
+ * @return boolean Whether the setting is a network setting.
+ */
+ public static function is_network_setting( $setting ) {
+ return strpos( $setting, 'network_' ) === 0;
+ }
+
+ /**
+ * Returns escaped SQL for blacklisted post types.
+ * Can be injected directly into a WHERE clause.
+ *
+ * @access public
+ * @static
+ *
+ * @return string SQL WHERE clause.
+ */
+ public static function get_blacklisted_post_types_sql() {
+ return 'post_type NOT IN (\'' . join( '\', \'', array_map( 'esc_sql', self::get_setting( 'post_types_blacklist' ) ) ) . '\')';
+ }
+
+ /**
+ * Returns escaped values for disallowed post types.
+ *
+ * @access public
+ * @static
+ *
+ * @return array Post type filter values
+ */
+ public static function get_disallowed_post_types_structured() {
+ return array(
+ 'post_type' => array(
+ 'operator' => 'NOT IN',
+ 'values' => array_map( 'esc_sql', self::get_setting( 'post_types_blacklist' ) ),
+ ),
+ );
+ }
+
+ /**
+ * Returns escaped SQL for blacklisted taxonomies.
+ * Can be injected directly into a WHERE clause.
+ *
+ * @access public
+ * @static
+ *
+ * @return string SQL WHERE clause.
+ */
+ public static function get_blacklisted_taxonomies_sql() {
+ return "taxonomy NOT IN ('" . join( "', '", array_map( 'esc_sql', self::get_setting( 'taxonomies_blacklist' ) ) ) . "')";
+ }
+
+ /**
+ * Returns escaped SQL for blacklisted post meta.
+ * Can be injected directly into a WHERE clause.
+ *
+ * @access public
+ * @static
+ *
+ * @return string SQL WHERE clause.
+ */
+ public static function get_whitelisted_post_meta_sql() {
+ return 'meta_key IN (\'' . join( '\', \'', array_map( 'esc_sql', self::get_setting( 'post_meta_whitelist' ) ) ) . '\')';
+ }
+
+ /**
+ * Returns escaped SQL for allowed post meta keys.
+ *
+ * @access public
+ * @static
+ *
+ * @return array Meta keys filter values
+ */
+ public static function get_allowed_post_meta_structured() {
+ return array(
+ 'meta_key' => array(
+ 'operator' => 'IN',
+ 'values' => array_map( 'esc_sql', self::get_setting( 'post_meta_whitelist' ) ),
+ ),
+ );
+ }
+
+ /**
+ * Returns structured SQL clause for blacklisted taxonomies.
+ *
+ * @access public
+ * @static
+ *
+ * @return array taxonomies filter values
+ */
+ public static function get_blacklisted_taxonomies_structured() {
+ return array(
+ 'taxonomy' => array(
+ 'operator' => 'NOT IN',
+ 'values' => array_map( 'esc_sql', self::get_setting( 'taxonomies_blacklist' ) ),
+ ),
+ );
+ }
+
+ /**
+ * Returns structured SQL clause for allowed taxonomies.
+ *
+ * @access public
+ * @static
+ *
+ * @return array taxonomies filter values
+ */
+ public static function get_allowed_taxonomies_structured() {
+ global $wp_taxonomies;
+
+ $allowed_taxonomies = array_keys( $wp_taxonomies );
+ $allowed_taxonomies = array_diff( $allowed_taxonomies, self::get_setting( 'taxonomies_blacklist' ) );
+ return array(
+ 'taxonomy' => array(
+ 'operator' => 'IN',
+ 'values' => array_map( 'esc_sql', $allowed_taxonomies ),
+ ),
+ );
+ }
+
+ /**
+ * Returns escaped SQL for blacklisted comment meta.
+ * Can be injected directly into a WHERE clause.
+ *
+ * @access public
+ * @static
+ *
+ * @return string SQL WHERE clause.
+ */
+ public static function get_whitelisted_comment_meta_sql() {
+ return 'meta_key IN (\'' . join( '\', \'', array_map( 'esc_sql', self::get_setting( 'comment_meta_whitelist' ) ) ) . '\')';
+ }
+
+ /**
+ * Returns SQL-escaped values for allowed post meta keys.
+ *
+ * @access public
+ * @static
+ *
+ * @return array Meta keys filter values
+ */
+ public static function get_allowed_comment_meta_structured() {
+ return array(
+ 'meta_key' => array(
+ 'operator' => 'IN',
+ 'values' => array_map( 'esc_sql', self::get_setting( 'comment_meta_whitelist' ) ),
+ ),
+ );
+ }
+
+ /**
+ * Returns SQL-escaped values for allowed order_item meta keys.
+ *
+ * @access public
+ * @static
+ *
+ * @return array Meta keys filter values
+ */
+ public static function get_allowed_order_itemmeta_structured() {
+ // Make sure that we only try to add the properties when the class exists.
+ if ( ! class_exists( '\Automattic\Jetpack\Sync\Modules\WooCommerce' ) ) {
+ return array();
+ }
+
+ $values = \Automattic\Jetpack\Sync\Modules\WooCommerce::$order_item_meta_whitelist;
+
+ return array(
+ 'meta_key' => array(
+ 'operator' => 'IN',
+ 'values' => array_map( 'esc_sql', $values ),
+ ),
+ );
+ }
+
+ /**
+ * Returns escaped SQL for comments, excluding any spam comments.
+ * Can be injected directly into a WHERE clause.
+ *
+ * @access public
+ * @static
+ *
+ * @return string SQL WHERE clause.
+ */
+ public static function get_comments_filter_sql() {
+ return "comment_approved <> 'spam'";
+ }
+
+ /**
+ * Delete any settings options and clean up the current settings state.
+ *
+ * @access public
+ * @static
+ */
+ public static function reset_data() {
+ $valid_settings = self::$valid_settings;
+ foreach ( $valid_settings as $option => $value ) {
+ delete_option( self::SETTINGS_OPTION_PREFIX . $option );
+ }
+ self::set_importing( null );
+ self::set_doing_cron( null );
+ self::set_is_syncing( null );
+ self::set_is_sending( null );
+ }
+
+ /**
+ * Set the importing state.
+ *
+ * @access public
+ * @static
+ *
+ * @param boolean $is_importing Whether WordPress is currently importing.
+ */
+ public static function set_importing( $is_importing ) {
+ // Set to NULL to revert to WP_IMPORTING, the standard behavior.
+ self::$is_importing = $is_importing;
+ }
+
+ /**
+ * Whether WordPress is currently importing.
+ *
+ * @access public
+ * @static
+ *
+ * @return boolean Whether WordPress is currently importing.
+ */
+ public static function is_importing() {
+ if ( ! is_null( self::$is_importing ) ) {
+ return self::$is_importing;
+ }
+
+ return defined( 'WP_IMPORTING' ) && WP_IMPORTING;
+ }
+
+ /**
+ * Whether sync is enabled.
+ *
+ * @access public
+ * @static
+ *
+ * @return boolean Whether sync is enabled.
+ */
+ public static function is_sync_enabled() {
+ return ! ( self::get_setting( 'disable' ) || self::get_setting( 'network_disable' ) );
+ }
+
+ /**
+ * Set the WP cron state.
+ *
+ * @access public
+ * @static
+ *
+ * @param boolean $is_doing_cron Whether WordPress is currently doing WP cron.
+ */
+ public static function set_doing_cron( $is_doing_cron ) {
+ // Set to NULL to revert to WP_IMPORTING, the standard behavior.
+ self::$is_doing_cron = $is_doing_cron;
+ }
+
+ /**
+ * Whether WordPress is currently doing WP cron.
+ *
+ * @access public
+ * @static
+ *
+ * @return boolean Whether WordPress is currently doing WP cron.
+ */
+ public static function is_doing_cron() {
+ if ( ! is_null( self::$is_doing_cron ) ) {
+ return self::$is_doing_cron;
+ }
+
+ return defined( 'DOING_CRON' ) && DOING_CRON;
+ }
+
+ /**
+ * Whether we are currently syncing.
+ *
+ * @access public
+ * @static
+ *
+ * @return boolean Whether we are currently syncing.
+ */
+ public static function is_syncing() {
+ return (bool) self::$is_syncing || ( defined( 'REST_API_REQUEST' ) && REST_API_REQUEST );
+ }
+
+ /**
+ * Set the syncing state.
+ *
+ * @access public
+ * @static
+ *
+ * @param boolean $is_syncing Whether we are currently syncing.
+ */
+ public static function set_is_syncing( $is_syncing ) {
+ self::$is_syncing = $is_syncing;
+ }
+
+ /**
+ * Whether we are currently sending sync items.
+ *
+ * @access public
+ * @static
+ *
+ * @return boolean Whether we are currently sending sync items.
+ */
+ public static function is_sending() {
+ return (bool) self::$is_sending;
+ }
+
+ /**
+ * Set the sending state.
+ *
+ * @access public
+ * @static
+ *
+ * @param boolean $is_sending Whether we are currently sending sync items.
+ */
+ public static function set_is_sending( $is_sending ) {
+ self::$is_sending = $is_sending;
+ }
+
+ /**
+ * Whether should send from the queue
+ *
+ * @access public
+ * @static
+ *
+ * @param string $queue_id The queue identifier.
+ *
+ * @return boolean Whether sync is enabled.
+ */
+ public static function is_sender_enabled( $queue_id ) {
+ return (bool) self::get_setting( $queue_id . '_sender_enabled' );
+ }
+
+ /**
+ * Whether checksums are enabled.
+ *
+ * @access public
+ * @static
+ *
+ * @return boolean Whether sync is enabled.
+ */
+ public static function is_checksum_enabled() {
+ return ! (bool) self::get_setting( 'checksum_disable' );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-simple-codec.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-simple-codec.php
new file mode 100644
index 00000000..613323fd
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-simple-codec.php
@@ -0,0 +1,63 @@
+<?php
+/**
+ * Simple codec for encoding and decoding sync objects.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+/**
+ * An implementation of Automattic\Jetpack\Sync\Codec_Interface that uses base64
+ * algorithm to compress objects serialized using json_encode.
+ */
+class Simple_Codec extends JSON_Deflate_Array_Codec {
+ /**
+ * Name of the codec.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const CODEC_NAME = 'simple';
+
+ /**
+ * Retrieve the name of the codec.
+ *
+ * @access public
+ *
+ * @return string Name of the codec.
+ */
+ public function name() {
+ return self::CODEC_NAME;
+ }
+
+ /**
+ * Encode a sync object.
+ *
+ * @access public
+ *
+ * @param mixed $object Sync object to encode.
+ * @return string Encoded sync object.
+ */
+ public function encode( $object ) {
+ // This is intentionally using base64_encode().
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
+ return base64_encode( $this->json_serialize( $object ) );
+ }
+
+ /**
+ * Encode a sync object.
+ *
+ * @access public
+ *
+ * @param string $input Encoded sync object to decode.
+ * @return mixed Decoded sync object.
+ */
+ public function decode( $input ) {
+ // This is intentionally using base64_decode().
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
+ return $this->json_unserialize( base64_decode( $input ) );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-users.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-users.php
new file mode 100644
index 00000000..8a8c83f8
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-users.php
@@ -0,0 +1,152 @@
+<?php
+/**
+ * Sync for users.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+use Automattic\Jetpack\Connection\Manager as Jetpack_Connection;
+use Automattic\Jetpack\Connection\XMLRPC_Async_Call;
+use Automattic\Jetpack\Roles;
+
+/**
+ * Class Users.
+ *
+ * Responsible for syncing user data changes.
+ */
+class Users {
+ /**
+ * Roles of all users, indexed by user ID.
+ *
+ * @access public
+ * @static
+ *
+ * @var array
+ */
+ public static $user_roles = array();
+
+ /**
+ * Initialize sync for user data changes.
+ *
+ * @access public
+ * @static
+ * @todo Eventually, connection needs to be instantiated at the top level in the sync package.
+ */
+ public static function init() {
+ add_action( 'jetpack_user_authorized', array( 'Automattic\\Jetpack\\Sync\\Actions', 'do_initial_sync' ), 10, 0 );
+ $connection = new Jetpack_Connection();
+ if ( $connection->has_connected_user() ) {
+ // Kick off synchronization of user role when it changes.
+ add_action( 'set_user_role', array( __CLASS__, 'user_role_change' ) );
+ }
+ }
+
+ /**
+ * Synchronize connected user role changes.
+ *
+ * @access public
+ * @static
+ *
+ * @param int $user_id ID of the user.
+ */
+ public static function user_role_change( $user_id ) {
+ $connection = new Jetpack_Connection();
+ if ( $connection->is_user_connected( $user_id ) ) {
+ self::update_role_on_com( $user_id );
+ // Try to choose a new master if we're demoting the current one.
+ self::maybe_demote_master_user( $user_id );
+ }
+ }
+
+ /**
+ * Retrieve the role of a user by their ID.
+ *
+ * @access public
+ * @static
+ *
+ * @param int $user_id ID of the user.
+ * @return string Role of the user.
+ */
+ public static function get_role( $user_id ) {
+ if ( isset( self::$user_roles[ $user_id ] ) ) {
+ return self::$user_roles[ $user_id ];
+ }
+
+ $current_user_id = get_current_user_id();
+ wp_set_current_user( $user_id );
+ $roles = new Roles();
+ $role = $roles->translate_current_user_to_role();
+ wp_set_current_user( $current_user_id );
+ self::$user_roles[ $user_id ] = $role;
+
+ return $role;
+ }
+
+ /**
+ * Retrieve the signed role of a user by their ID.
+ *
+ * @access public
+ * @static
+ *
+ * @param int $user_id ID of the user.
+ * @return string Signed role of the user.
+ */
+ public static function get_signed_role( $user_id ) {
+ $connection = new Jetpack_Connection();
+ return $connection->sign_role( self::get_role( $user_id ), $user_id );
+ }
+
+ /**
+ * Retrieve the signed role and update it in WP.com for that user.
+ *
+ * @access public
+ * @static
+ *
+ * @param int $user_id ID of the user.
+ */
+ public static function update_role_on_com( $user_id ) {
+ $signed_role = self::get_signed_role( $user_id );
+ XMLRPC_Async_Call::add_call( 'jetpack.updateRole', get_current_user_id(), $user_id, $signed_role );
+ }
+
+ /**
+ * Choose a new master user if we're demoting the current one.
+ *
+ * @access public
+ * @static
+ * @todo Disconnect if there is no user with enough capabilities to be the master user.
+ * @uses \WP_User_Query
+ *
+ * @param int $user_id ID of the user.
+ */
+ public static function maybe_demote_master_user( $user_id ) {
+ $master_user_id = (int) \Jetpack_Options::get_option( 'master_user' );
+ $role = self::get_role( $user_id );
+ if ( $user_id === $master_user_id && 'administrator' !== $role ) {
+ $query = new \WP_User_Query(
+ array(
+ 'fields' => array( 'id' ),
+ 'role' => 'administrator',
+ 'orderby' => 'id',
+ 'exclude' => array( $master_user_id ),
+ )
+ );
+ $new_master = false;
+ $connection = new Jetpack_Connection();
+ foreach ( $query->results as $result ) {
+ $found_user_id = absint( $result->id );
+ if ( $found_user_id && $connection->is_user_connected( $found_user_id ) ) {
+ $new_master = $found_user_id;
+ break;
+ }
+ }
+
+ if ( $new_master ) {
+ \Jetpack_Options::update_option( 'master_user', $new_master );
+ }
+ // TODO: else disconnect..?
+ }
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-utils.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-utils.php
new file mode 100644
index 00000000..23f24e95
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/class-utils.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Sync utils.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+/**
+ * Class for sync utilities.
+ */
+class Utils {
+ /**
+ * Retrieve the values of sync items.
+ *
+ * @access public
+ * @static
+ *
+ * @param array $items Array of sync items.
+ * @return array Array of sync item values.
+ */
+ public static function get_item_values( $items ) {
+ return array_map( array( __CLASS__, 'get_item_value' ), $items );
+ }
+
+ /**
+ * Retrieve the IDs of sync items.
+ *
+ * @access public
+ * @static
+ *
+ * @param array $items Array of sync items.
+ * @return array Array of sync item IDs.
+ */
+ public static function get_item_ids( $items ) {
+ return array_map( array( __CLASS__, 'get_item_id' ), $items );
+ }
+
+ /**
+ * Get the value of a sync item.
+ *
+ * @access private
+ * @static
+ *
+ * @param array $item Sync item.
+ * @return mixed Sync item value.
+ */
+ private static function get_item_value( $item ) {
+ return $item->value;
+ }
+
+ /**
+ * Get the ID of a sync item.
+ *
+ * @access private
+ * @static
+ *
+ * @param array $item Sync item.
+ * @return int Sync item ID.
+ */
+ private static function get_item_id( $item ) {
+ return $item->id;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/interface-codec.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/interface-codec.php
new file mode 100644
index 00000000..7653f26d
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/interface-codec.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Interface for encoding and decoding sync objects.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+/**
+ * Very simple interface for encoding and decoding input.
+ * This is used to provide compression and serialization to messages.
+ **/
+interface Codec_Interface {
+ /**
+ * Retrieve the name of the codec.
+ * We send this with the payload so we can select the appropriate decoder at the other end.
+ *
+ * @access public
+ *
+ * @return string Name of the codec.
+ */
+ public function name();
+
+ /**
+ * Encode a sync object.
+ *
+ * @access public
+ *
+ * @param mixed $object Sync object to encode.
+ * @return string Encoded sync object.
+ */
+ public function encode( $object );
+
+ /**
+ * Encode a sync object.
+ *
+ * @access public
+ *
+ * @param string $input Encoded sync object to decode.
+ * @return mixed Decoded sync object.
+ */
+ public function decode( $input );
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/interface-replicastore.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/interface-replicastore.php
new file mode 100644
index 00000000..5c57f49e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/interface-replicastore.php
@@ -0,0 +1,566 @@
+<?php
+/**
+ * Sync architecture prototype.
+ *
+ * To run tests: phpunit --testsuite sync --filter New_Sync
+ *
+ * @author Dan Walmsley
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync;
+
+/**
+ * A high-level interface for objects that store synced WordPress data.
+ * Useful for ensuring that different storage mechanisms implement the
+ * required semantics for storing all the data that we sync.
+ */
+interface Replicastore_Interface {
+ /**
+ * Empty and reset the replicastore.
+ *
+ * @access public
+ */
+ public function reset();
+
+ /**
+ * Ran when full sync has just started.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ */
+ public function full_sync_start( $config );
+
+ /**
+ * Ran when full sync has just finished.
+ *
+ * @access public
+ *
+ * @param string $checksum Deprecated since 7.3.0.
+ */
+ public function full_sync_end( $checksum );
+
+ /**
+ * Retrieve the number of posts with a particular post status within a certain range.
+ *
+ * @access public
+ *
+ * @todo Prepare the SQL query before executing it.
+ *
+ * @param string $status Post status.
+ * @param int $min_id Minimum post ID.
+ * @param int $max_id Maximum post ID.
+ */
+ public function post_count( $status = null, $min_id = null, $max_id = null );
+
+ /**
+ * Retrieve the posts with a particular post status.
+ *
+ * @access public
+ *
+ * @param string $status Post status.
+ * @param int $min_id Minimum post ID.
+ * @param int $max_id Maximum post ID.
+ */
+ public function get_posts( $status = null, $min_id = null, $max_id = null );
+
+ /**
+ * Retrieve a post object by the post ID.
+ *
+ * @access public
+ *
+ * @param int $id Post ID.
+ */
+ public function get_post( $id );
+
+ /**
+ * Update or insert a post.
+ *
+ * @access public
+ *
+ * @param \WP_Post $post Post object.
+ * @param bool $silent Whether to perform a silent action.
+ */
+ public function upsert_post( $post, $silent = false );
+
+ /**
+ * Delete a post by the post ID.
+ *
+ * @access public
+ *
+ * @param int $post_id Post ID.
+ */
+ public function delete_post( $post_id );
+
+ /**
+ * Retrieve the checksum for posts within a range.
+ *
+ * @access public
+ *
+ * @param int $min_id Minimum post ID.
+ * @param int $max_id Maximum post ID.
+ */
+ public function posts_checksum( $min_id = null, $max_id = null );
+
+ /**
+ * Retrieve the checksum for post meta within a range.
+ *
+ * @access public
+ *
+ * @param int $min_id Minimum post meta ID.
+ * @param int $max_id Maximum post meta ID.
+ */
+ public function post_meta_checksum( $min_id = null, $max_id = null );
+
+ /**
+ * Retrieve the number of comments with a particular comment status within a certain range.
+ *
+ * @access public
+ *
+ * @param string $status Comment status.
+ * @param int $min_id Minimum comment ID.
+ * @param int $max_id Maximum comment ID.
+ */
+ public function comment_count( $status = null, $min_id = null, $max_id = null );
+
+ /**
+ * Retrieve the comments with a particular comment status.
+ *
+ * @access public
+ *
+ * @param string $status Comment status.
+ * @param int $min_id Minimum comment ID.
+ * @param int $max_id Maximum comment ID.
+ */
+ public function get_comments( $status = null, $min_id = null, $max_id = null );
+
+ /**
+ * Retrieve a comment object by the comment ID.
+ *
+ * @access public
+ *
+ * @param int $id Comment ID.
+ */
+ public function get_comment( $id );
+
+ /**
+ * Update or insert a comment.
+ *
+ * @access public
+ *
+ * @param \WP_Comment $comment Comment object.
+ */
+ public function upsert_comment( $comment );
+
+ /**
+ * Trash a comment by the comment ID.
+ *
+ * @access public
+ *
+ * @param int $comment_id Comment ID.
+ */
+ public function trash_comment( $comment_id );
+
+ /**
+ * Mark a comment by the comment ID as spam.
+ *
+ * @access public
+ *
+ * @param int $comment_id Comment ID.
+ */
+ public function spam_comment( $comment_id );
+
+ /**
+ * Delete a comment by the comment ID.
+ *
+ * @access public
+ *
+ * @param int $comment_id Comment ID.
+ */
+ public function delete_comment( $comment_id );
+
+ /**
+ * Trash the comments of a post.
+ *
+ * @access public
+ *
+ * @param int $post_id Post ID.
+ * @param array $statuses Post statuses.
+ */
+ public function trashed_post_comments( $post_id, $statuses );
+
+ /**
+ * Untrash the comments of a post.
+ *
+ * @access public
+ *
+ * @param int $post_id Post ID.
+ */
+ public function untrashed_post_comments( $post_id );
+
+ /**
+ * Retrieve the checksum for comments within a range.
+ *
+ * @access public
+ *
+ * @param int $min_id Minimum comment ID.
+ * @param int $max_id Maximum comment ID.
+ */
+ public function comments_checksum( $min_id = null, $max_id = null );
+
+ /**
+ * Retrieve the checksum for comment meta within a range.
+ *
+ * @access public
+ *
+ * @param int $min_id Minimum comment meta ID.
+ * @param int $max_id Maximum comment meta ID.
+ */
+ public function comment_meta_checksum( $min_id = null, $max_id = null );
+
+ /**
+ * Update the value of an option.
+ *
+ * @access public
+ *
+ * @param string $option Option name.
+ * @param mixed $value Option value.
+ */
+ public function update_option( $option, $value );
+
+ /**
+ * Retrieve an option value based on an option name.
+ *
+ * @access public
+ *
+ * @param string $option Name of option to retrieve.
+ * @param mixed $default Optional. Default value to return if the option does not exist.
+ */
+ public function get_option( $option, $default = false );
+
+ /**
+ * Remove an option by name.
+ *
+ * @access public
+ *
+ * @param string $option Name of option to remove.
+ */
+ public function delete_option( $option );
+
+ /**
+ * Change the info of the current theme.
+ *
+ * @access public
+ *
+ * @param array $theme_info Theme info array.
+ */
+ public function set_theme_info( $theme_info );
+
+ /**
+ * Whether the current theme supports a certain feature.
+ *
+ * @access public
+ *
+ * @param string $feature Name of the feature.
+ */
+ public function current_theme_supports( $feature );
+
+ /**
+ * Retrieve metadata for the specified object.
+ *
+ * @access public
+ *
+ * @param string $type Meta type.
+ * @param int $object_id ID of the object.
+ * @param string $meta_key Meta key.
+ * @param bool $single If true, return only the first value of the specified meta_key.
+ */
+ public function get_metadata( $type, $object_id, $meta_key = '', $single = false );
+
+ /**
+ * Stores remote meta key/values alongside an ID mapping key.
+ *
+ * @access public
+ *
+ * @param string $type Meta type.
+ * @param int $object_id ID of the object.
+ * @param string $meta_key Meta key.
+ * @param mixed $meta_value Meta value.
+ * @param int $meta_id ID of the meta.
+ */
+ public function upsert_metadata( $type, $object_id, $meta_key, $meta_value, $meta_id );
+
+ /**
+ * Delete metadata for the specified object.
+ *
+ * @access public
+ *
+ * @param string $type Meta type.
+ * @param int $object_id ID of the object.
+ * @param array $meta_ids IDs of the meta objects to delete.
+ */
+ public function delete_metadata( $type, $object_id, $meta_ids );
+
+ /**
+ * Delete metadata with a certain key for the specified objects.
+ *
+ * @access public
+ *
+ * @param string $type Meta type.
+ * @param array $object_ids IDs of the objects.
+ * @param string $meta_key Meta key.
+ */
+ public function delete_batch_metadata( $type, $object_ids, $meta_key );
+
+ /**
+ * Retrieve value of a constant based on the constant name.
+ *
+ * @access public
+ *
+ * @param string $constant Name of constant to retrieve.
+ */
+ public function get_constant( $constant );
+
+ /**
+ * Set the value of a constant.
+ *
+ * @access public
+ *
+ * @param string $constant Name of constant to retrieve.
+ * @param mixed $value Value set for the constant.
+ */
+ public function set_constant( $constant, $value );
+
+ /**
+ * Retrieve the number of the available updates of a certain type.
+ * Type is one of: `plugins`, `themes`, `wordpress`, `translations`, `total`, `wp_update_version`.
+ *
+ * @access public
+ *
+ * @param string $type Type of updates to retrieve.
+ */
+ public function get_updates( $type );
+
+ /**
+ * Set the available updates of a certain type.
+ * Type is one of: `plugins`, `themes`, `wordpress`, `translations`, `total`, `wp_update_version`.
+ *
+ * @access public
+ *
+ * @param string $type Type of updates to set.
+ * @param int $updates Total number of updates.
+ */
+ public function set_updates( $type, $updates );
+
+ /**
+ * Retrieve a callable value based on its name.
+ *
+ * @access public
+ *
+ * @param string $callable Name of the callable to retrieve.
+ */
+ public function get_callable( $callable );
+
+ /**
+ * Update the value of a callable.
+ *
+ * @access public
+ *
+ * @param string $callable Callable name.
+ * @param mixed $value Callable value.
+ */
+ public function set_callable( $callable, $value );
+
+ /**
+ * Retrieve a network option value based on a network option name.
+ *
+ * @access public
+ *
+ * @param string $option Name of network option to retrieve.
+ */
+ public function get_site_option( $option );
+
+ /**
+ * Update the value of a network option.
+ *
+ * @access public
+ *
+ * @param string $option Network option name.
+ * @param mixed $value Network option value.
+ */
+ public function update_site_option( $option, $value );
+
+ /**
+ * Remove a network option by name.
+ *
+ * @access public
+ *
+ * @param string $option Name of option to remove.
+ */
+ public function delete_site_option( $option );
+
+ /**
+ * Retrieve the terms from a particular taxonomy.
+ *
+ * @access public
+ *
+ * @param string $taxonomy Taxonomy slug.
+ */
+ public function get_terms( $taxonomy );
+
+ /**
+ * Retrieve a particular term.
+ *
+ * @access public
+ *
+ * @param string $taxonomy Taxonomy slug.
+ * @param int $term_id ID of the term.
+ * @param string $term_key ID Field `term_id` or `term_taxonomy_id`.
+ */
+ public function get_term( $taxonomy, $term_id, $term_key = 'term_id' );
+
+ /**
+ * Insert or update a term.
+ *
+ * @access public
+ *
+ * @param \WP_Term $term_object Term object.
+ */
+ public function update_term( $term_object );
+
+ /**
+ * Delete a term by the term ID and its corresponding taxonomy.
+ *
+ * @access public
+ *
+ * @param int $term_id Term ID.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ public function delete_term( $term_id, $taxonomy );
+
+ /**
+ * Retrieve all terms from a taxonomy that are related to an object with a particular ID.
+ *
+ * @access public
+ *
+ * @param int $object_id Object ID.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ public function get_the_terms( $object_id, $taxonomy );
+
+ /**
+ * Add/update terms of a particular taxonomy of an object with the specified ID.
+ *
+ * @access public
+ *
+ * @param int $object_id The object to relate to.
+ * @param string $taxonomy The context in which to relate the term to the object.
+ * @param string|int|array $terms A single term slug, single term id, or array of either term slugs or ids.
+ * @param bool $append Optional. If false will delete difference of terms. Default false.
+ */
+ public function update_object_terms( $object_id, $taxonomy, $terms, $append );
+
+ /**
+ * Remove certain term relationships from the specified object.
+ *
+ * @access public
+ *
+ * @todo Refactor to not use interpolated values when preparing the SQL query.
+ *
+ * @param int $object_id ID of the object.
+ * @param array $tt_ids Term taxonomy IDs.
+ */
+ public function delete_object_terms( $object_id, $tt_ids );
+
+ /**
+ * Retrieve the number of users.
+ *
+ * @access public
+ */
+ public function user_count();
+
+ /**
+ * Retrieve a user object by the user ID.
+ *
+ * @access public
+ *
+ * @param int $user_id User ID.
+ */
+ public function get_user( $user_id );
+
+ /**
+ * Insert or update a user.
+ *
+ * @access public
+ *
+ * @param \WP_User $user User object.
+ */
+ public function upsert_user( $user );
+
+ /**
+ * Delete a user.
+ *
+ * @access public
+ *
+ * @param int $user_id User ID.
+ */
+ public function delete_user( $user_id );
+
+ /**
+ * Update/insert user locale.
+ *
+ * @access public
+ *
+ * @param int $user_id User ID.
+ * @param string $locale The user locale.
+ */
+ public function upsert_user_locale( $user_id, $locale );
+
+ /**
+ * Delete user locale.
+ *
+ * @access public
+ *
+ * @param int $user_id User ID.
+ */
+ public function delete_user_locale( $user_id );
+
+ /**
+ * Retrieve the user locale.
+ *
+ * @access public
+ *
+ * @param int $user_id User ID.
+ */
+ public function get_user_locale( $user_id );
+
+ /**
+ * Retrieve the allowed mime types for the user.
+ *
+ * @access public
+ *
+ * @param int $user_id User ID.
+ */
+ public function get_allowed_mime_types( $user_id );
+
+ /**
+ * Retrieve all the checksums we are interested in.
+ * Currently that is posts, comments, post meta and comment meta.
+ *
+ * @access public
+ */
+ public function checksum_all();
+
+ /**
+ * Retrieve the checksum histogram for a specific object type.
+ *
+ * @access public
+ *
+ * @param string $object_type Object type.
+ * @param int $buckets Number of buckets to split the objects to.
+ * @param int $start_id Minimum object ID.
+ * @param int $end_id Maximum object ID.
+ */
+ public function checksum_histogram( $object_type, $buckets, $start_id = null, $end_id = null );
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-attachments.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-attachments.php
new file mode 100644
index 00000000..a111d105
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-attachments.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * Attachments sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+/**
+ * Class to handle sync for attachments.
+ */
+class Attachments extends Module {
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'attachments';
+ }
+
+ /**
+ * Initialize attachment action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ add_action( 'add_attachment', array( $this, 'process_add' ) );
+ add_action( 'attachment_updated', array( $this, 'process_update' ), 10, 3 );
+ add_action( 'jetpack_sync_save_update_attachment', $callable, 10, 2 );
+ add_action( 'jetpack_sync_save_add_attachment', $callable, 10, 2 );
+ add_action( 'jetpack_sync_save_attach_attachment', $callable, 10, 2 );
+ }
+
+ /**
+ * Handle the creation of a new attachment.
+ *
+ * @access public
+ *
+ * @param int $attachment_id ID of the attachment.
+ */
+ public function process_add( $attachment_id ) {
+ $attachment = get_post( $attachment_id );
+ /**
+ * Fires when the client needs to sync an new attachment
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param int Attachment ID.
+ * @param \WP_Post Attachment post object.
+ */
+ do_action( 'jetpack_sync_save_add_attachment', $attachment_id, $attachment );
+ }
+
+ /**
+ * Handle updating an existing attachment.
+ *
+ * @access public
+ *
+ * @param int $attachment_id Attachment ID.
+ * @param \WP_Post $attachment_after Attachment post object before the update.
+ * @param \WP_Post $attachment_before Attachment post object after the update.
+ */
+ public function process_update( $attachment_id, $attachment_after, $attachment_before ) {
+ // Check whether attachment was added to a post for the first time.
+ if ( 0 === $attachment_before->post_parent && 0 !== $attachment_after->post_parent ) {
+ /**
+ * Fires when an existing attachment is added to a post for the first time
+ *
+ * @since 1.6.3
+ * @since-jetpack 6.6.0
+ *
+ * @param int $attachment_id Attachment ID.
+ * @param \WP_Post $attachment_after Attachment post object after the update.
+ */
+ do_action( 'jetpack_sync_save_attach_attachment', $attachment_id, $attachment_after );
+ } else {
+ /**
+ * Fires when the client needs to sync an updated attachment
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.9.0
+ *
+ * @param int $attachment_id Attachment ID.
+ * @param \WP_Post $attachment_after Attachment post object after the update.
+ *
+ * Previously this action was synced using jetpack_sync_save_add_attachment action.
+ */
+ do_action( 'jetpack_sync_save_update_attachment', $attachment_id, $attachment_after );
+ }
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-callables.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-callables.php
new file mode 100644
index 00000000..436554c9
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-callables.php
@@ -0,0 +1,635 @@
+<?php
+/**
+ * Callables sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Constants as Jetpack_Constants;
+use Automattic\Jetpack\Sync\Defaults;
+use Automattic\Jetpack\Sync\Functions;
+use Automattic\Jetpack\Sync\Settings;
+
+/**
+ * Class to handle sync for callables.
+ */
+class Callables extends Module {
+ /**
+ * Name of the callables checksum option.
+ *
+ * @var string
+ */
+ const CALLABLES_CHECKSUM_OPTION_NAME = 'jetpack_callables_sync_checksum';
+
+ /**
+ * Name of the transient for locking callables.
+ *
+ * @var string
+ */
+ const CALLABLES_AWAIT_TRANSIENT_NAME = 'jetpack_sync_callables_await';
+
+ /**
+ * Whitelist for callables we want to sync.
+ *
+ * @access private
+ *
+ * @var array
+ */
+ private $callable_whitelist;
+
+ /**
+ * For some options, we should always send the change right away!
+ *
+ * @access public
+ *
+ * @var array
+ */
+ const ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS = array(
+ 'jetpack_active_modules',
+ 'home', // option is home, callable is home_url.
+ 'siteurl',
+ 'jetpack_sync_error_idc',
+ 'paused_plugins',
+ 'paused_themes',
+
+ );
+
+ const ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS_NEXT_TICK = array(
+ 'stylesheet',
+ );
+ /**
+ * Setting this value to true will make it so that the callables will not be unlocked
+ * but the lock will be removed after content is send so that callables will be
+ * sent in the next request.
+ *
+ * @var bool
+ */
+ private $force_send_callables_on_next_tick = false;
+
+ /**
+ * For some options, the callable key differs from the option name/key
+ *
+ * @access public
+ *
+ * @var array
+ */
+ const OPTION_NAMES_TO_CALLABLE_NAMES = array(
+ // @TODO: Audit the other option names for differences between the option names and callable names.
+ 'home' => 'home_url',
+ 'siteurl' => 'site_url',
+ );
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'functions';
+ }
+
+ /**
+ * Set module defaults.
+ * Define the callable whitelist based on whether this is a single site or a multisite installation.
+ *
+ * @access public
+ */
+ public function set_defaults() {
+ if ( is_multisite() ) {
+ $this->callable_whitelist = array_merge( Defaults::get_callable_whitelist(), Defaults::get_multisite_callable_whitelist() );
+ } else {
+ $this->callable_whitelist = Defaults::get_callable_whitelist();
+ }
+ $this->force_send_callables_on_next_tick = false; // Resets here as well mostly for tests.
+ }
+
+ /**
+ * Initialize callables action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ add_action( 'jetpack_sync_callable', $callable, 10, 2 );
+ add_action( 'current_screen', array( $this, 'set_plugin_action_links' ), 9999 ); // Should happen very late.
+
+ foreach ( self::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS as $option ) {
+ add_action( "update_option_{$option}", array( $this, 'unlock_sync_callable' ) );
+ add_action( "delete_option_{$option}", array( $this, 'unlock_sync_callable' ) );
+ }
+
+ foreach ( self::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS_NEXT_TICK as $option ) {
+ add_action( "update_option_{$option}", array( $this, 'unlock_sync_callable_next_tick' ) );
+ add_action( "delete_option_{$option}", array( $this, 'unlock_sync_callable_next_tick' ) );
+ }
+
+ // Provide a hook so that hosts can send changes to certain callables right away.
+ // Especially useful when a host uses constants to change home and siteurl.
+ add_action( 'jetpack_sync_unlock_sync_callable', array( $this, 'unlock_sync_callable' ) );
+
+ // get_plugins and wp_version
+ // gets fired when new code gets installed, updates etc.
+ add_action( 'upgrader_process_complete', array( $this, 'unlock_plugin_action_link_and_callables' ) );
+ add_action( 'update_option_active_plugins', array( $this, 'unlock_plugin_action_link_and_callables' ) );
+ }
+
+ /**
+ * Initialize callables action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_callables', $callable );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_sync_callables' ) );
+
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_callables', array( $this, 'expand_callables' ) );
+ }
+
+ /**
+ * Perform module cleanup.
+ * Deletes any transients and options that this module uses.
+ * Usually triggered when uninstalling the plugin.
+ *
+ * @access public
+ */
+ public function reset_data() {
+ delete_option( self::CALLABLES_CHECKSUM_OPTION_NAME );
+ delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
+
+ $url_callables = array( 'home_url', 'site_url', 'main_network_site_url' );
+ foreach ( $url_callables as $callable ) {
+ delete_option( Functions::HTTPS_CHECK_OPTION_PREFIX . $callable );
+ }
+ }
+
+ /**
+ * Set the callable whitelist.
+ *
+ * @access public
+ *
+ * @param array $callables The new callables whitelist.
+ */
+ public function set_callable_whitelist( $callables ) {
+ $this->callable_whitelist = $callables;
+ }
+
+ /**
+ * Get the callable whitelist.
+ *
+ * @access public
+ *
+ * @return array The callables whitelist.
+ */
+ public function get_callable_whitelist() {
+ return $this->callable_whitelist;
+ }
+
+ /**
+ * Retrieve all callables as per the current callables whitelist.
+ *
+ * @access public
+ *
+ * @return array All callables.
+ */
+ public function get_all_callables() {
+ // get_all_callables should run as the master user always.
+ $current_user_id = get_current_user_id();
+ wp_set_current_user( \Jetpack_Options::get_option( 'master_user' ) );
+ $callables = array_combine(
+ array_keys( $this->get_callable_whitelist() ),
+ array_map( array( $this, 'get_callable' ), array_values( $this->get_callable_whitelist() ) )
+ );
+ wp_set_current_user( $current_user_id );
+ return $callables;
+ }
+
+ /**
+ * Invoke a particular callable.
+ * Used as a wrapper to standartize invocation.
+ *
+ * @access private
+ *
+ * @param callable $callable Callable to invoke.
+ * @return mixed Return value of the callable, null if not callable.
+ */
+ private function get_callable( $callable ) {
+ if ( is_callable( $callable ) ) {
+ return call_user_func( $callable );
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Enqueue the callable actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ /**
+ * Tells the client to sync all callables to the server
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param boolean Whether to expand callables (should always be true)
+ */
+ do_action( 'jetpack_full_sync_callables', true );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 1, true );
+ }
+
+ /**
+ * Send the callable actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $send_until The timestamp until the current request can send.
+ * @param array $status This Module Full Sync Status.
+ *
+ * @return array This Module Full Sync Status.
+ */
+ public function send_full_sync_actions( $config, $send_until, $status ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // we call this instead of do_action when sending immediately.
+ $this->send_action( 'jetpack_full_sync_callables', array( true ) );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 'finished' => true );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_callables' );
+ }
+
+ /**
+ * Unlock callables so they would be available for syncing again.
+ *
+ * @access public
+ */
+ public function unlock_sync_callable() {
+ delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
+ }
+
+ /**
+ * Unlock callables on the next tick.
+ * Sometime the true callable values are only present on the next tick.
+ * When switching themes for example.
+ *
+ * @access public
+ */
+ public function unlock_sync_callable_next_tick() {
+ $this->force_send_callables_on_next_tick = true;
+ }
+
+ /**
+ * Unlock callables and plugin action links.
+ *
+ * @access public
+ */
+ public function unlock_plugin_action_link_and_callables() {
+ delete_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME );
+ delete_transient( 'jetpack_plugin_api_action_links_refresh' );
+ add_filter( 'jetpack_check_and_send_callables', '__return_true' );
+ }
+
+ /**
+ * Parse and store the plugin action links if on the plugins page.
+ *
+ * @uses \DOMDocument
+ * @uses libxml_use_internal_errors
+ * @uses mb_convert_encoding
+ *
+ * @access public
+ */
+ public function set_plugin_action_links() {
+ if (
+ ! class_exists( '\DOMDocument' ) ||
+ ! function_exists( 'libxml_use_internal_errors' ) ||
+ ! function_exists( 'mb_convert_encoding' )
+ ) {
+ return;
+ }
+
+ $current_screeen = get_current_screen();
+
+ $plugins_action_links = array();
+ // Is the transient lock in place?
+ $plugins_lock = get_transient( 'jetpack_plugin_api_action_links_refresh', false );
+ if ( ! empty( $plugins_lock ) && ( isset( $current_screeen->id ) && 'plugins' !== $current_screeen->id ) ) {
+ return;
+ }
+ $plugins = array_keys( Functions::get_plugins() );
+ foreach ( $plugins as $plugin_file ) {
+ /**
+ * Plugins often like to unset things but things break if they are not able to.
+ */
+ $action_links = array(
+ 'deactivate' => '',
+ 'activate' => '',
+ 'details' => '',
+ 'delete' => '',
+ 'edit' => '',
+ );
+ /** This filter is documented in src/wp-admin/includes/class-wp-plugins-list-table.php */
+ $action_links = apply_filters( 'plugin_action_links', $action_links, $plugin_file, null, 'all' );
+ /** This filter is documented in src/wp-admin/includes/class-wp-plugins-list-table.php */
+ $action_links = apply_filters( "plugin_action_links_{$plugin_file}", $action_links, $plugin_file, null, 'all' );
+ // Verify $action_links is still an array to resolve warnings from filters not returning an array.
+ if ( is_array( $action_links ) ) {
+ $action_links = array_filter( $action_links );
+ } else {
+ $action_links = array();
+ }
+ $formatted_action_links = null;
+ if ( ! empty( $action_links ) && count( $action_links ) > 0 ) {
+ $dom_doc = new \DOMDocument();
+ foreach ( $action_links as $action_link ) {
+ // The @ is not enough to suppress errors when dealing with libxml,
+ // we have to tell it directly how we want to handle errors.
+ libxml_use_internal_errors( true );
+ $dom_doc->loadHTML( mb_convert_encoding( $action_link, 'HTML-ENTITIES', 'UTF-8' ) );
+ libxml_use_internal_errors( false );
+
+ $link_elements = $dom_doc->getElementsByTagName( 'a' );
+ if ( 0 === $link_elements->length ) {
+ continue;
+ }
+
+ $link_element = $link_elements->item( 0 );
+ // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
+ if ( $link_element->hasAttribute( 'href' ) && $link_element->nodeValue ) {
+ $link_url = trim( $link_element->getAttribute( 'href' ) );
+
+ // Add the full admin path to the url if the plugin did not provide it.
+ $link_url_scheme = wp_parse_url( $link_url, PHP_URL_SCHEME );
+ if ( empty( $link_url_scheme ) ) {
+ $link_url = admin_url( $link_url );
+ }
+
+ // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
+ $formatted_action_links[ $link_element->nodeValue ] = $link_url;
+ }
+ }
+ }
+ if ( $formatted_action_links ) {
+ $plugins_action_links[ $plugin_file ] = $formatted_action_links;
+ }
+ }
+ // Cache things for a long time.
+ set_transient( 'jetpack_plugin_api_action_links_refresh', time(), DAY_IN_SECONDS );
+ update_option( 'jetpack_plugin_api_action_links', $plugins_action_links );
+ }
+
+ /**
+ * Whether a certain callable should be sent.
+ *
+ * @access public
+ *
+ * @param array $callable_checksums Callable checksums.
+ * @param string $name Name of the callable.
+ * @param string $checksum A checksum of the callable.
+ * @return boolean Whether to send the callable.
+ */
+ public function should_send_callable( $callable_checksums, $name, $checksum ) {
+ $idc_override_callables = array(
+ 'main_network_site',
+ 'home_url',
+ 'site_url',
+ );
+ if ( in_array( $name, $idc_override_callables, true ) && \Jetpack_Options::get_option( 'migrate_for_idc' ) ) {
+ return true;
+ }
+
+ return ! $this->still_valid_checksum( $callable_checksums, $name, $checksum );
+ }
+
+ /**
+ * Sync the callables if we're supposed to.
+ *
+ * @access public
+ */
+ public function maybe_sync_callables() {
+ $callables = $this->get_all_callables();
+ if ( ! apply_filters( 'jetpack_check_and_send_callables', false ) ) {
+ if ( ! is_admin() ) {
+ // If we're not an admin and we're not doing cron and this isn't WP_CLI, don't sync anything.
+ if ( ! Settings::is_doing_cron() && ! Jetpack_Constants::get_constant( 'WP_CLI' ) ) {
+ return;
+ }
+ // If we're not an admin and we are doing cron, sync the Callables that are always supposed to sync ( See https://github.com/Automattic/jetpack/issues/12924 ).
+ $callables = $this->get_always_sent_callables();
+ }
+ if ( get_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME ) ) {
+ if ( $this->force_send_callables_on_next_tick ) {
+ $this->unlock_sync_callable();
+ }
+ return;
+ }
+ }
+
+ if ( empty( $callables ) ) {
+ return;
+ }
+ // No need to set the transiant we are trying to remove it anyways.
+ if ( ! $this->force_send_callables_on_next_tick ) {
+ set_transient( self::CALLABLES_AWAIT_TRANSIENT_NAME, microtime( true ), Defaults::$default_sync_callables_wait_time );
+ }
+
+ $callable_checksums = (array) \Jetpack_Options::get_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, array() );
+ $has_changed = false;
+ // Only send the callables that have changed.
+ foreach ( $callables as $name => $value ) {
+ $checksum = $this->get_check_sum( $value );
+
+ // Explicitly not using Identical comparison as get_option returns a string.
+ if ( ! is_null( $value ) && $this->should_send_callable( $callable_checksums, $name, $checksum ) ) {
+
+ // Only send callable if the non sorted checksum also does not match.
+ if ( $this->should_send_callable( $callable_checksums, $name, $this->get_check_sum( $value, false ) ) ) {
+
+ /**
+ * Tells the client to sync a callable (aka function) to the server
+ *
+ * @param string The name of the callable
+ * @param mixed The value of the callable
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ */
+ do_action( 'jetpack_sync_callable', $name, $value );
+ }
+
+ $callable_checksums[ $name ] = $checksum;
+ $has_changed = true;
+ } else {
+ $callable_checksums[ $name ] = $checksum;
+ }
+ }
+ if ( $has_changed ) {
+ \Jetpack_Options::update_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, $callable_checksums );
+ }
+
+ if ( $this->force_send_callables_on_next_tick ) {
+ $this->unlock_sync_callable();
+ }
+ }
+
+ /**
+ * Get the callables that should always be sent, e.g. on cron.
+ *
+ * @return array Callables that should always be sent
+ */
+ protected function get_always_sent_callables() {
+ $callables = $this->get_all_callables();
+ $cron_callables = array();
+ foreach ( self::ALWAYS_SEND_UPDATES_TO_THESE_OPTIONS as $option_name ) {
+ if ( array_key_exists( $option_name, $callables ) ) {
+ $cron_callables[ $option_name ] = $callables[ $option_name ];
+ continue;
+ }
+
+ // Check for the Callable name/key for the option, if different from option name.
+ if ( array_key_exists( $option_name, self::OPTION_NAMES_TO_CALLABLE_NAMES ) ) {
+ $callable_name = self::OPTION_NAMES_TO_CALLABLE_NAMES[ $option_name ];
+ if ( array_key_exists( $callable_name, $callables ) ) {
+ $cron_callables[ $callable_name ] = $callables[ $callable_name ];
+ }
+ }
+ }
+ return $cron_callables;
+ }
+
+ /**
+ * Expand the callables within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The hook parameters.
+ */
+ public function expand_callables( $args ) {
+ if ( $args[0] ) {
+ $callables = $this->get_all_callables();
+ $callables_checksums = array();
+ foreach ( $callables as $name => $value ) {
+ $callables_checksums[ $name ] = $this->get_check_sum( $value );
+ }
+ \Jetpack_Options::update_raw_option( self::CALLABLES_CHECKSUM_OPTION_NAME, $callables_checksums );
+ return $callables;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return count( $this->get_callable_whitelist() );
+ }
+
+ /**
+ * Retrieve a set of callables by their IDs.
+ *
+ * @access public
+ *
+ * @param string $object_type Object type.
+ * @param array $ids Object IDs.
+ * @return array Array of objects.
+ */
+ public function get_objects_by_id( $object_type, $ids ) {
+ if ( empty( $ids ) || empty( $object_type ) || 'callable' !== $object_type ) {
+ return array();
+ }
+
+ $objects = array();
+ foreach ( (array) $ids as $id ) {
+ $object = $this->get_object_by_id( $object_type, $id );
+
+ if ( 'CALLABLE-DOES-NOT-EXIST' !== $object ) {
+ if ( 'all' === $id ) {
+ // If all was requested it contains all options and can simply be returned.
+ return $object;
+ }
+ $objects[ $id ] = $object;
+ }
+ }
+
+ return $objects;
+ }
+
+ /**
+ * Retrieve a callable by its name.
+ *
+ * @access public
+ *
+ * @param string $object_type Type of the sync object.
+ * @param string $id ID of the sync object.
+ * @return mixed Value of Callable.
+ */
+ public function get_object_by_id( $object_type, $id ) {
+ if ( 'callable' === $object_type ) {
+
+ // Only whitelisted options can be returned.
+ if ( array_key_exists( $id, $this->get_callable_whitelist() ) ) {
+ // requires master user to be in context.
+ $current_user_id = get_current_user_id();
+ wp_set_current_user( \Jetpack_Options::get_option( 'master_user' ) );
+ $callable = $this->get_callable( $this->callable_whitelist[ $id ] );
+ wp_set_current_user( $current_user_id );
+ return $callable;
+ } elseif ( 'all' === $id ) {
+ return $this->get_all_callables();
+ }
+ }
+
+ return 'CALLABLE-DOES-NOT-EXIST';
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-comments.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-comments.php
new file mode 100644
index 00000000..30268305
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-comments.php
@@ -0,0 +1,495 @@
+<?php
+/**
+ * Comments sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Sync\Modules;
+use Automattic\Jetpack\Sync\Settings;
+
+/**
+ * Class to handle sync for comments.
+ */
+class Comments extends Module {
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'comments';
+ }
+
+ /**
+ * The id field in the database.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function id_field() {
+ return 'comment_ID';
+ }
+
+ /**
+ * The table in the database.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function table_name() {
+ return 'comments';
+ }
+
+ /**
+ * Retrieve a comment by its ID.
+ *
+ * @access public
+ *
+ * @param string $object_type Type of the sync object.
+ * @param int $id ID of the sync object.
+ * @return \WP_Comment|bool Filtered \WP_Comment object, or false if the object is not a comment.
+ */
+ public function get_object_by_id( $object_type, $id ) {
+ $comment_id = (int) $id;
+ if ( 'comment' === $object_type ) {
+ $comment = get_comment( $comment_id );
+ if ( $comment ) {
+ return $this->filter_comment( $comment );
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Initialize comments action listeners.
+ * Also responsible for initializing comment meta listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ add_action( 'wp_insert_comment', $callable, 10, 2 );
+ add_action( 'deleted_comment', $callable );
+ add_action( 'trashed_comment', $callable );
+ add_action( 'spammed_comment', $callable );
+ add_action( 'trashed_post_comments', $callable, 10, 2 );
+ add_action( 'untrash_post_comments', $callable );
+ add_action( 'comment_approved_to_unapproved', $callable );
+ add_action( 'comment_unapproved_to_approved', $callable );
+ add_action( 'jetpack_modified_comment_contents', $callable, 10, 2 );
+ add_action( 'untrashed_comment', $callable, 10, 2 );
+ add_action( 'unspammed_comment', $callable, 10, 2 );
+ add_filter( 'wp_update_comment_data', array( $this, 'handle_comment_contents_modification' ), 10, 3 );
+
+ // comment actions.
+ add_filter( 'jetpack_sync_before_enqueue_wp_insert_comment', array( $this, 'only_allow_white_listed_comment_types' ) );
+ add_filter( 'jetpack_sync_before_enqueue_deleted_comment', array( $this, 'only_allow_white_listed_comment_types' ) );
+ add_filter( 'jetpack_sync_before_enqueue_trashed_comment', array( $this, 'only_allow_white_listed_comment_types' ) );
+ add_filter( 'jetpack_sync_before_enqueue_untrashed_comment', array( $this, 'only_allow_white_listed_comment_types' ) );
+ add_filter( 'jetpack_sync_before_enqueue_spammed_comment', array( $this, 'only_allow_white_listed_comment_types' ) );
+ add_filter( 'jetpack_sync_before_enqueue_unspammed_comment', array( $this, 'only_allow_white_listed_comment_types' ) );
+
+ // comment status transitions.
+ add_filter( 'jetpack_sync_before_enqueue_comment_approved_to_unapproved', array( $this, 'only_allow_white_listed_comment_type_transitions' ) );
+ add_filter( 'jetpack_sync_before_enqueue_comment_unapproved_to_approved', array( $this, 'only_allow_white_listed_comment_type_transitions' ) );
+
+ // Post Actions.
+ add_filter( 'jetpack_sync_before_enqueue_trashed_post_comments', array( $this, 'filter_blacklisted_post_types' ) );
+ add_filter( 'jetpack_sync_before_enqueue_untrash_post_comments', array( $this, 'filter_blacklisted_post_types' ) );
+
+ /**
+ * Even though it's messy, we implement these hooks because
+ * the edit_comment hook doesn't include the data
+ * so this saves us a DB read for every comment event.
+ */
+ foreach ( $this->get_whitelisted_comment_types() as $comment_type ) {
+ foreach ( array( 'unapproved', 'approved' ) as $comment_status ) {
+ $comment_action_name = "comment_{$comment_status}_{$comment_type}";
+ add_action( $comment_action_name, $callable, 10, 2 );
+ }
+ }
+
+ // Listen for meta changes.
+ $this->init_listeners_for_meta_type( 'comment', $callable );
+ $this->init_meta_whitelist_handler( 'comment', array( $this, 'filter_meta' ) );
+ }
+
+ /**
+ * Handler for any comment content updates.
+ *
+ * @access public
+ *
+ * @param array $new_comment The new, processed comment data.
+ * @param array $old_comment The old, unslashed comment data.
+ * @param array $new_comment_with_slashes The new, raw comment data.
+ * @return array The new, processed comment data.
+ */
+ public function handle_comment_contents_modification( $new_comment, $old_comment, $new_comment_with_slashes ) {
+ $changes = array();
+ $content_fields = array(
+ 'comment_author',
+ 'comment_author_email',
+ 'comment_author_url',
+ 'comment_content',
+ );
+ foreach ( $content_fields as $field ) {
+ if ( $new_comment_with_slashes[ $field ] !== $old_comment[ $field ] ) {
+ $changes[ $field ] = array( $new_comment[ $field ], $old_comment[ $field ] );
+ }
+ }
+
+ if ( ! empty( $changes ) ) {
+ /**
+ * Signals to the sync listener that this comment's contents were modified and a sync action
+ * reflecting the change(s) to the content should be sent
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.9.0
+ *
+ * @param int $new_comment['comment_ID'] ID of comment whose content was modified
+ * @param mixed $changes Array of changed comment fields with before and after values
+ */
+ do_action( 'jetpack_modified_comment_contents', $new_comment['comment_ID'], $changes );
+ }
+ return $new_comment;
+ }
+
+ /**
+ * Initialize comments action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_comments', $callable ); // Also send comments meta.
+ }
+
+ /**
+ * Gets a filtered list of comment types that sync can hook into.
+ *
+ * @access public
+ *
+ * @return array Defaults to [ '', 'trackback', 'pingback' ].
+ */
+ public function get_whitelisted_comment_types() {
+ /**
+ * Comment types present in this list will sync their status changes to WordPress.com.
+ *
+ * @since 1.6.3
+ * @since-jetpack 7.6.0
+ *
+ * @param array A list of comment types.
+ */
+ return apply_filters(
+ 'jetpack_sync_whitelisted_comment_types',
+ array( '', 'comment', 'trackback', 'pingback', 'review' )
+ );
+ }
+
+ /**
+ * Prevents any comment types that are not in the whitelist from being enqueued and sent to WordPress.com.
+ *
+ * @param array $args Arguments passed to wp_insert_comment, deleted_comment, spammed_comment, etc.
+ *
+ * @return bool or array $args Arguments passed to wp_insert_comment, deleted_comment, spammed_comment, etc.
+ */
+ public function only_allow_white_listed_comment_types( $args ) {
+ $comment = false;
+
+ if ( isset( $args[1] ) ) {
+ // comment object is available.
+ $comment = $args[1];
+ } elseif ( is_numeric( $args[0] ) ) {
+ // comment_id is available.
+ $comment = get_comment( $args[0] );
+ }
+
+ if (
+ isset( $comment->comment_type )
+ && ! in_array( $comment->comment_type, $this->get_whitelisted_comment_types(), true )
+ ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Filter all blacklisted post types.
+ *
+ * @param array $args Hook arguments.
+ * @return array|false Hook arguments, or false if the post type is a blacklisted one.
+ */
+ public function filter_blacklisted_post_types( $args ) {
+ $post_id = $args[0];
+ $posts_module = Modules::get_module( 'posts' );
+
+ if ( false !== $posts_module && ! $posts_module->is_post_type_allowed( $post_id ) ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Prevents any comment types that are not in the whitelist from being enqueued and sent to WordPress.com.
+ *
+ * @param array $args Arguments passed to wp_{old_status}_to_{new_status}.
+ *
+ * @return bool or array $args Arguments passed to wp_{old_status}_to_{new_status}
+ */
+ public function only_allow_white_listed_comment_type_transitions( $args ) {
+ $comment = $args[0];
+
+ if ( ! in_array( $comment->comment_type, $this->get_whitelisted_comment_types(), true ) ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Whether a comment type is allowed.
+ * A comment type is allowed if it's present in the comment type whitelist.
+ *
+ * @param int $comment_id ID of the comment.
+ * @return boolean Whether the comment type is allowed.
+ */
+ public function is_comment_type_allowed( $comment_id ) {
+ $comment = get_comment( $comment_id );
+
+ if ( isset( $comment->comment_type ) ) {
+ return in_array( $comment->comment_type, $this->get_whitelisted_comment_types(), true );
+ }
+ return false;
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_filter( 'jetpack_sync_before_send_wp_insert_comment', array( $this, 'expand_wp_insert_comment' ) );
+
+ foreach ( $this->get_whitelisted_comment_types() as $comment_type ) {
+ foreach ( array( 'unapproved', 'approved' ) as $comment_status ) {
+ $comment_action_name = "comment_{$comment_status}_{$comment_type}";
+ add_filter(
+ 'jetpack_sync_before_send_' . $comment_action_name,
+ array(
+ $this,
+ 'expand_wp_insert_comment',
+ )
+ );
+ }
+ }
+
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_comments', array( $this, 'expand_comment_ids' ) );
+ }
+
+ /**
+ * Enqueue the comments actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
+ global $wpdb;
+ return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_comments', $wpdb->comments, 'comment_ID', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return int Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) {
+ global $wpdb;
+
+ $query = "SELECT count(*) FROM $wpdb->comments";
+
+ $where_sql = $this->get_where_sql( $config );
+ if ( $where_sql ) {
+ $query .= ' WHERE ' . $where_sql;
+ }
+
+ // TODO: Call $wpdb->prepare on the following query.
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $count = $wpdb->get_var( $query );
+
+ return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
+ }
+
+ /**
+ * Retrieve the WHERE SQL clause based on the module config.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return string WHERE SQL clause, or `null` if no comments are specified in the module config.
+ */
+ public function get_where_sql( $config ) {
+ if ( is_array( $config ) ) {
+ return 'comment_ID IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
+ }
+
+ return '1=1';
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_comments' );
+ }
+
+ /**
+ * Count all the actions that are going to be sent.
+ *
+ * @access public
+ *
+ * @param array $action_names Names of all the actions that will be sent.
+ * @return int Number of actions.
+ */
+ public function count_full_sync_actions( $action_names ) {
+ return $this->count_actions( $action_names, array( 'jetpack_full_sync_comments' ) );
+ }
+
+ /**
+ * Expand the comment status change before the data is serialized and sent to the server.
+ *
+ * @access public
+ * @todo This is not used currently - let's implement it.
+ *
+ * @param array $args The hook parameters.
+ * @return array The expanded hook parameters.
+ */
+ public function expand_wp_comment_status_change( $args ) {
+ return array( $args[0], $this->filter_comment( $args[1] ) );
+ }
+
+ /**
+ * Expand the comment creation before the data is serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array The expanded hook parameters.
+ */
+ public function expand_wp_insert_comment( $args ) {
+ return array( $args[0], $this->filter_comment( $args[1] ) );
+ }
+
+ /**
+ * Filter a comment object to the fields we need.
+ *
+ * @access public
+ *
+ * @param \WP_Comment $comment The unfiltered comment object.
+ * @return \WP_Comment Filtered comment object.
+ */
+ public function filter_comment( $comment ) {
+ /**
+ * Filters whether to prevent sending comment data to .com
+ *
+ * Passing true to the filter will prevent the comment data from being sent
+ * to the WordPress.com.
+ * Instead we pass data that will still enable us to do a checksum against the
+ * Jetpacks data but will prevent us from displaying the data on in the API as well as
+ * other services.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param boolean false prevent post data from bing synced to WordPress.com
+ * @param mixed $comment WP_COMMENT object
+ */
+ if ( apply_filters( 'jetpack_sync_prevent_sending_comment_data', false, $comment ) ) {
+ $blocked_comment = new \stdClass();
+ $blocked_comment->comment_ID = $comment->comment_ID;
+ $blocked_comment->comment_date = $comment->comment_date;
+ $blocked_comment->comment_date_gmt = $comment->comment_date_gmt;
+ $blocked_comment->comment_approved = 'jetpack_sync_blocked';
+ return $blocked_comment;
+ }
+
+ return $comment;
+ }
+
+ /**
+ * Whether a certain comment meta key is whitelisted for sync.
+ *
+ * @access public
+ *
+ * @param string $meta_key Comment meta key.
+ * @return boolean Whether the meta key is whitelisted.
+ */
+ public function is_whitelisted_comment_meta( $meta_key ) {
+ return in_array( $meta_key, Settings::get_setting( 'comment_meta_whitelist' ), true );
+ }
+
+ /**
+ * Handler for filtering out non-whitelisted comment meta.
+ *
+ * @access public
+ *
+ * @param array $args Hook args.
+ * @return array|boolean False if not whitelisted, the original hook args otherwise.
+ */
+ public function filter_meta( $args ) {
+ if ( $this->is_comment_type_allowed( $args[1] ) && $this->is_whitelisted_comment_meta( $args[2] ) ) {
+ return $args;
+ }
+
+ return false;
+ }
+
+ /**
+ * Expand the comment IDs to comment objects and meta before being serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array The expanded hook parameters.
+ */
+ public function expand_comment_ids( $args ) {
+ list( $comment_ids, $previous_interval_end ) = $args;
+ $comments = get_comments(
+ array(
+ 'include_unapproved' => true,
+ 'comment__in' => $comment_ids,
+ 'orderby' => 'comment_ID',
+ 'order' => 'DESC',
+ )
+ );
+
+ return array(
+ $comments,
+ $this->get_metadata( $comment_ids, 'comment', Settings::get_setting( 'comment_meta_whitelist' ) ),
+ $previous_interval_end,
+ );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-constants.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-constants.php
new file mode 100644
index 00000000..d71a0fe1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-constants.php
@@ -0,0 +1,339 @@
+<?php
+/**
+ * Constants sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Sync\Defaults;
+
+/**
+ * Class to handle sync for constants.
+ */
+class Constants extends Module {
+ /**
+ * Name of the constants checksum option.
+ *
+ * @var string
+ */
+ const CONSTANTS_CHECKSUM_OPTION_NAME = 'jetpack_constants_sync_checksum';
+
+ /**
+ * Name of the transient for locking constants.
+ *
+ * @var string
+ */
+ const CONSTANTS_AWAIT_TRANSIENT_NAME = 'jetpack_sync_constants_await';
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'constants';
+ }
+
+ /**
+ * Initialize constants action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ add_action( 'jetpack_sync_constant', $callable, 10, 2 );
+ }
+
+ /**
+ * Initialize constants action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_constants', $callable );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_action( 'jetpack_sync_before_send_queue_sync', array( $this, 'maybe_sync_constants' ) );
+
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_constants', array( $this, 'expand_constants' ) );
+ }
+
+ /**
+ * Perform module cleanup.
+ * Deletes any transients and options that this module uses.
+ * Usually triggered when uninstalling the plugin.
+ *
+ * @access public
+ */
+ public function reset_data() {
+ delete_option( self::CONSTANTS_CHECKSUM_OPTION_NAME );
+ delete_transient( self::CONSTANTS_AWAIT_TRANSIENT_NAME );
+ }
+
+ /**
+ * Set the constants whitelist.
+ *
+ * @access public
+ * @todo We don't seem to use this one. Should we remove it?
+ *
+ * @param array $constants The new constants whitelist.
+ */
+ public function set_constants_whitelist( $constants ) {
+ $this->constants_whitelist = $constants;
+ }
+
+ /**
+ * Get the constants whitelist.
+ *
+ * @access public
+ *
+ * @return array The constants whitelist.
+ */
+ public function get_constants_whitelist() {
+ return Defaults::get_constants_whitelist();
+ }
+
+ /**
+ * Enqueue the constants actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ *
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ /**
+ * Tells the client to sync all constants to the server
+ *
+ * @param boolean Whether to expand constants (should always be true)
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ */
+ do_action( 'jetpack_full_sync_constants', true );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 1, true );
+ }
+
+ /**
+ * Send the constants actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $send_until The timestamp until the current request can send.
+ * @param array $state This module Full Sync status.
+ *
+ * @return array This module Full Sync status.
+ */
+ public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // we call this instead of do_action when sending immediately.
+ $this->send_action( 'jetpack_full_sync_constants', array( true ) );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 'finished' => true );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ *
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_constants' );
+ }
+
+ /**
+ * Sync the constants if we're supposed to.
+ *
+ * @access public
+ */
+ public function maybe_sync_constants() {
+ if ( get_transient( self::CONSTANTS_AWAIT_TRANSIENT_NAME ) ) {
+ return;
+ }
+
+ set_transient( self::CONSTANTS_AWAIT_TRANSIENT_NAME, microtime( true ), Defaults::$default_sync_constants_wait_time );
+
+ $constants = $this->get_all_constants();
+ if ( empty( $constants ) ) {
+ return;
+ }
+
+ $constants_checksums = (array) get_option( self::CONSTANTS_CHECKSUM_OPTION_NAME, array() );
+
+ foreach ( $constants as $name => $value ) {
+ $checksum = $this->get_check_sum( $value );
+ // Explicitly not using Identical comparison as get_option returns a string.
+ if ( ! $this->still_valid_checksum( $constants_checksums, $name, $checksum ) && ! is_null( $value ) ) {
+ /**
+ * Tells the client to sync a constant to the server
+ *
+ * @param string The name of the constant
+ * @param mixed The value of the constant
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ */
+ do_action( 'jetpack_sync_constant', $name, $value );
+ $constants_checksums[ $name ] = $checksum;
+ } else {
+ $constants_checksums[ $name ] = $checksum;
+ }
+ }
+ update_option( self::CONSTANTS_CHECKSUM_OPTION_NAME, $constants_checksums );
+ }
+
+ /**
+ * Retrieve all constants as per the current constants whitelist.
+ * Public so that we don't have to store an option for each constant.
+ *
+ * @access public
+ *
+ * @return array All constants.
+ */
+ public function get_all_constants() {
+ $constants_whitelist = $this->get_constants_whitelist();
+
+ return array_combine(
+ $constants_whitelist,
+ array_map( array( $this, 'get_constant' ), $constants_whitelist )
+ );
+ }
+
+ /**
+ * Retrieve the value of a constant.
+ * Used as a wrapper to standartize access to constants.
+ *
+ * @access private
+ *
+ * @param string $constant Constant name.
+ *
+ * @return mixed Return value of the constant.
+ */
+ private function get_constant( $constant ) {
+ return ( defined( $constant ) ) ?
+ constant( $constant )
+ : null;
+ }
+
+ /**
+ * Expand the constants within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ *
+ * @return array $args The hook parameters.
+ */
+ public function expand_constants( $args ) {
+ if ( $args[0] ) {
+ $constants = $this->get_all_constants();
+ $constants_checksums = array();
+ foreach ( $constants as $name => $value ) {
+ $constants_checksums[ $name ] = $this->get_check_sum( $value );
+ }
+ update_option( self::CONSTANTS_CHECKSUM_OPTION_NAME, $constants_checksums );
+
+ return $constants;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return count( $this->get_constants_whitelist() );
+ }
+
+ /**
+ * Retrieve a set of constants by their IDs.
+ *
+ * @access public
+ *
+ * @param string $object_type Object type.
+ * @param array $ids Object IDs.
+ * @return array Array of objects.
+ */
+ public function get_objects_by_id( $object_type, $ids ) {
+ if ( empty( $ids ) || empty( $object_type ) || 'constant' !== $object_type ) {
+ return array();
+ }
+
+ $objects = array();
+ foreach ( (array) $ids as $id ) {
+ $object = $this->get_object_by_id( $object_type, $id );
+
+ if ( 'all' === $id ) {
+ // If all was requested it contains all options and can simply be returned.
+ return $object;
+ }
+ $objects[ $id ] = $object;
+ }
+
+ return $objects;
+ }
+
+ /**
+ * Retrieve a constant by its name.
+ *
+ * @access public
+ *
+ * @param string $object_type Type of the sync object.
+ * @param string $id ID of the sync object.
+ * @return mixed Value of Constant.
+ */
+ public function get_object_by_id( $object_type, $id ) {
+ if ( 'constant' === $object_type ) {
+
+ // Only whitelisted constants can be returned.
+ if ( in_array( $id, $this->get_constants_whitelist(), true ) ) {
+ return $this->get_constant( $id );
+ } elseif ( 'all' === $id ) {
+ return $this->get_all_constants();
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php
new file mode 100644
index 00000000..4017df16
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync-immediately.php
@@ -0,0 +1,467 @@
+<?php
+/**
+ * Full sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Sync\Actions;
+use Automattic\Jetpack\Sync\Defaults;
+use Automattic\Jetpack\Sync\Lock;
+use Automattic\Jetpack\Sync\Modules;
+use Automattic\Jetpack\Sync\Settings;
+
+/**
+ * This class does a full resync of the database by
+ * sending an outbound action for every single object
+ * that we care about.
+ */
+class Full_Sync_Immediately extends Module {
+ /**
+ * Prefix of the full sync status option name.
+ *
+ * @var string
+ */
+ const STATUS_OPTION = 'jetpack_sync_full_status';
+
+ /**
+ * Sync Lock name.
+ *
+ * @var string
+ */
+ const LOCK_NAME = 'full_sync';
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'full-sync';
+ }
+
+ /**
+ * Initialize action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ }
+
+ /**
+ * Start a full sync.
+ *
+ * @access public
+ *
+ * @param array $full_sync_config Full sync configuration.
+ *
+ * @return bool Always returns true at success.
+ */
+ public function start( $full_sync_config = null ) {
+ // There was a full sync in progress.
+ if ( $this->is_started() && ! $this->is_finished() ) {
+ /**
+ * Fires when a full sync is cancelled.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ */
+ do_action( 'jetpack_full_sync_cancelled' );
+ $this->send_action( 'jetpack_full_sync_cancelled' );
+ }
+
+ // Remove all evidence of previous full sync items and status.
+ $this->reset_data();
+
+ if ( ! is_array( $full_sync_config ) ) {
+ $full_sync_config = Defaults::$default_full_sync_config;
+ if ( is_multisite() ) {
+ $full_sync_config['network_options'] = 1;
+ }
+ }
+
+ if ( isset( $full_sync_config['users'] ) && 'initial' === $full_sync_config['users'] ) {
+ $full_sync_config['users'] = Modules::get_module( 'users' )->get_initial_sync_user_config();
+ }
+
+ $this->update_status(
+ array(
+ 'started' => time(),
+ 'config' => $full_sync_config,
+ 'progress' => $this->get_initial_progress( $full_sync_config ),
+ )
+ );
+
+ $range = $this->get_content_range( $full_sync_config );
+ /**
+ * Fires when a full sync begins. This action is serialized
+ * and sent to the server so that it knows a full sync is coming.
+ *
+ * @param array $full_sync_config Sync configuration for all sync modules.
+ * @param array $range Range of the sync items, containing min and max IDs for some item types.
+ * @param array $empty The modules with no items to sync during a full sync.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ * @since-jetpack 7.3.0 Added $range arg.
+ * @since-jetpack 7.4.0 Added $empty arg.
+ */
+ do_action( 'jetpack_full_sync_start', $full_sync_config, $range );
+ $this->send_action( 'jetpack_full_sync_start', array( $full_sync_config, $range ) );
+
+ return true;
+ }
+
+ /**
+ * Whether full sync has started.
+ *
+ * @access public
+ *
+ * @return boolean
+ */
+ public function is_started() {
+ return (bool) $this->get_status()['started'];
+ }
+
+ /**
+ * Retrieve the status of the current full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync status.
+ */
+ public function get_status() {
+ $default = array(
+ 'started' => false,
+ 'finished' => false,
+ 'progress' => array(),
+ 'config' => array(),
+ );
+
+ return wp_parse_args( \Jetpack_Options::get_raw_option( self::STATUS_OPTION ), $default );
+ }
+
+ /**
+ * Returns the progress percentage of a full sync.
+ *
+ * @access public
+ *
+ * @return int|null
+ */
+ public function get_sync_progress_percentage() {
+ if ( ! $this->is_started() || $this->is_finished() ) {
+ return null;
+ }
+ $status = $this->get_status();
+ if ( empty( $status['progress'] ) ) {
+ return null;
+ }
+ $total_items = array_reduce(
+ array_values( $status['progress'] ),
+ function ( $sum, $sync_item ) {
+ return isset( $sync_item['total'] ) ? ( $sum + (int) $sync_item['total'] ) : $sum;
+ },
+ 0
+ );
+ $total_sent = array_reduce(
+ array_values( $status['progress'] ),
+ function ( $sum, $sync_item ) {
+ return isset( $sync_item['sent'] ) ? ( $sum + (int) $sync_item['sent'] ) : $sum;
+ },
+ 0
+ );
+ return floor( ( $total_sent / $total_items ) * 100 );
+ }
+
+ /**
+ * Whether full sync has finished.
+ *
+ * @access public
+ *
+ * @return boolean
+ */
+ public function is_finished() {
+ return (bool) $this->get_status()['finished'];
+ }
+
+ /**
+ * Clear all the full sync data.
+ *
+ * @access public
+ */
+ public function reset_data() {
+ $this->clear_status();
+ ( new Lock() )->remove( self::LOCK_NAME, true );
+ }
+
+ /**
+ * Clear all the full sync status options.
+ *
+ * @access public
+ */
+ public function clear_status() {
+ \Jetpack_Options::delete_raw_option( self::STATUS_OPTION );
+ }
+
+ /**
+ * Updates the status of the current full sync.
+ *
+ * @access public
+ *
+ * @param array $values New values to set.
+ *
+ * @return bool True if success.
+ */
+ public function update_status( $values ) {
+ return $this->set_status( wp_parse_args( $values, $this->get_status() ) );
+ }
+
+ /**
+ * Retrieve the status of the current full sync.
+ *
+ * @param array $values New values to set.
+ *
+ * @access public
+ *
+ * @return boolean Full sync status.
+ */
+ public function set_status( $values ) {
+ return \Jetpack_Options::update_raw_option( self::STATUS_OPTION, $values );
+ }
+
+ /**
+ * Given an initial Full Sync configuration get the initial status.
+ *
+ * @param array $full_sync_config Full sync configuration.
+ *
+ * @return array Initial Sent status.
+ */
+ public function get_initial_progress( $full_sync_config ) {
+ // Set default configuration, calculate totals, and save configuration if totals > 0.
+ $status = array();
+ foreach ( $full_sync_config as $name => $config ) {
+ $module = Modules::get_module( $name );
+ $status[ $name ] = array(
+ 'total' => $module->total( $config ),
+ 'sent' => 0,
+ 'finished' => false,
+ );
+ }
+
+ return $status;
+ }
+
+ /**
+ * Get the range for content (posts and comments) to sync.
+ *
+ * @access private
+ *
+ * @return array Array of range (min ID, max ID, total items) for all content types.
+ */
+ private function get_content_range() {
+ $range = array();
+ $config = $this->get_status()['config'];
+ // Add range only when syncing all objects.
+ if ( true === isset( $config['posts'] ) && $config['posts'] ) {
+ $range['posts'] = $this->get_range( 'posts' );
+ }
+
+ if ( true === isset( $config['comments'] ) && $config['comments'] ) {
+ $range['comments'] = $this->get_range( 'comments' );
+ }
+
+ return $range;
+ }
+
+ /**
+ * Get the range (min ID, max ID and total items) of items to sync.
+ *
+ * @access public
+ *
+ * @param string $type Type of sync item to get the range for.
+ *
+ * @return array Array of min ID, max ID and total items in the range.
+ */
+ public function get_range( $type ) {
+ global $wpdb;
+ if ( ! in_array( $type, array( 'comments', 'posts' ), true ) ) {
+ return array();
+ }
+
+ switch ( $type ) {
+ case 'posts':
+ $table = $wpdb->posts;
+ $id = 'ID';
+ $where_sql = Settings::get_blacklisted_post_types_sql();
+
+ break;
+ case 'comments':
+ $table = $wpdb->comments;
+ $id = 'comment_ID';
+ $where_sql = Settings::get_comments_filter_sql();
+ break;
+ }
+
+ // TODO: Call $wpdb->prepare on the following query.
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $results = $wpdb->get_results( "SELECT MAX({$id}) as max, MIN({$id}) as min, COUNT({$id}) as count FROM {$table} WHERE {$where_sql}" );
+ if ( isset( $results[0] ) ) {
+ return $results[0];
+ }
+
+ return array();
+ }
+
+ /**
+ * Continue sending instead of enqueueing.
+ *
+ * @access public
+ */
+ public function continue_enqueuing() {
+ $this->continue_sending();
+ }
+
+ /**
+ * Continue sending.
+ *
+ * @access public
+ */
+ public function continue_sending() {
+ // Return early if Full Sync is not running.
+ if ( ! $this->is_started() || $this->get_status()['finished'] ) {
+ return;
+ }
+
+ // Return early if we've gotten a retry-after header response.
+ $retry_time = get_option( Actions::RETRY_AFTER_PREFIX . 'immediate-send' );
+ if ( $retry_time ) {
+ // If expired delete but don't send. Send will occurr in new request to avoid race conditions.
+ if ( microtime( true ) > $retry_time ) {
+ update_option( Actions::RETRY_AFTER_PREFIX . 'immediate-send', false, false );
+ }
+ return false;
+ }
+
+ // Obtain send Lock.
+ $lock = new Lock();
+ $lock_expiration = $lock->attempt( self::LOCK_NAME );
+
+ // Return if unable to obtain lock.
+ if ( false === $lock_expiration ) {
+ return;
+ }
+
+ // Send Full Sync actions.
+ $success = $this->send();
+
+ // Remove lock.
+ if ( $success ) {
+ $lock->remove( self::LOCK_NAME, $lock_expiration );
+ }
+ }
+
+ /**
+ * Immediately send the next items to full sync.
+ *
+ * @access public
+ */
+ public function send() {
+ $config = $this->get_status()['config'];
+
+ $max_duration = Settings::get_setting( 'full_sync_send_duration' );
+ $send_until = microtime( true ) + $max_duration;
+
+ $progress = $this->get_status()['progress'];
+
+ foreach ( $this->get_remaining_modules_to_send() as $module ) {
+ $progress[ $module->name() ] = $module->send_full_sync_actions( $config[ $module->name() ], $progress[ $module->name() ], $send_until );
+ if ( isset( $progress[ $module->name() ]['error'] ) ) {
+ unset( $progress[ $module->name() ]['error'] );
+ $this->update_status( array( 'progress' => $progress ) );
+ return false;
+ } elseif ( ! $progress[ $module->name() ]['finished'] ) {
+ $this->update_status( array( 'progress' => $progress ) );
+ return true;
+ }
+ }
+
+ $this->send_full_sync_end();
+ $this->update_status( array( 'progress' => $progress ) );
+ return true;
+ }
+
+ /**
+ * Get Modules that are configured to Full Sync and haven't finished sending
+ *
+ * @return array
+ */
+ public function get_remaining_modules_to_send() {
+ $status = $this->get_status();
+
+ return array_filter(
+ Modules::get_modules(),
+ /**
+ * Select configured and not finished modules.
+ *
+ * @return bool
+ * @var $module Module
+ */
+ function ( $module ) use ( $status ) {
+ // Skip module if not configured for this sync or module is done.
+ if ( ! isset( $status['config'][ $module->name() ] ) ) {
+ return false;
+ }
+ if ( ! $status['config'][ $module->name() ] ) {
+ return false;
+ }
+ if ( isset( $status['progress'][ $module->name() ]['finished'] ) ) {
+ if ( true === $status['progress'][ $module->name() ]['finished'] ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ );
+ }
+
+ /**
+ * Send 'jetpack_full_sync_end' and update 'finished' status.
+ *
+ * @access public
+ */
+ public function send_full_sync_end() {
+ $range = $this->get_content_range();
+
+ /**
+ * Fires when a full sync ends. This action is serialized
+ * and sent to the server.
+ *
+ * @param string $checksum Deprecated since 7.3.0 - @see https://github.com/Automattic/jetpack/pull/11945/
+ * @param array $range Range of the sync items, containing min and max IDs for some item types.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ * @since-jetpack 7.3.0 Added $range arg.
+ */
+ do_action( 'jetpack_full_sync_end', '', $range );
+ $this->send_action( 'jetpack_full_sync_end', array( '', $range ) );
+
+ // Setting autoload to true means that it's faster to check whether we should continue enqueuing.
+ $this->update_status( array( 'finished' => time() ) );
+ }
+
+ /**
+ * Empty Function as we don't close buffers on Immediate Full Sync.
+ *
+ * @param array $actions an array of actions, ignored for queueless sync.
+ */
+ public function update_sent_progress_action( $actions ) { } // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync.php
new file mode 100644
index 00000000..0fe9245c
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-full-sync.php
@@ -0,0 +1,730 @@
+<?php
+/**
+ * Full sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Sync\Listener;
+use Automattic\Jetpack\Sync\Lock;
+use Automattic\Jetpack\Sync\Modules;
+use Automattic\Jetpack\Sync\Queue;
+use Automattic\Jetpack\Sync\Settings;
+
+/**
+ * This class does a full resync of the database by
+ * enqueuing an outbound action for every single object
+ * that we care about.
+ *
+ * This class, and its related class Jetpack_Sync_Module, contain a few non-obvious optimisations that should be explained:
+ * - we fire an action called jetpack_full_sync_start so that WPCOM can erase the contents of the cached database
+ * - for each object type, we page through the object IDs and enqueue them by firing some monitored actions
+ * - we load the full objects for those IDs in chunks of Jetpack_Sync_Module::ARRAY_CHUNK_SIZE (to reduce the number of MySQL calls)
+ * - we fire a trigger for the entire array which the Automattic\Jetpack\Sync\Listener then serializes and queues.
+ */
+class Full_Sync extends Module {
+ /**
+ * Prefix of the full sync status option name.
+ *
+ * @var string
+ */
+ const STATUS_OPTION_PREFIX = 'jetpack_sync_full_';
+
+ /**
+ * Enqueue Lock name.
+ *
+ * @var string
+ */
+ const ENQUEUE_LOCK_NAME = 'full_sync_enqueue';
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'full-sync';
+ }
+
+ /**
+ * Initialize action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ // Synthetic actions for full sync.
+ add_action( 'jetpack_full_sync_start', $callable, 10, 3 );
+ add_action( 'jetpack_full_sync_end', $callable, 10, 2 );
+ add_action( 'jetpack_full_sync_cancelled', $callable );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ // This is triggered after actions have been processed on the server.
+ add_action( 'jetpack_sync_processed_actions', array( $this, 'update_sent_progress_action' ) );
+ }
+
+ /**
+ * Start a full sync.
+ *
+ * @access public
+ *
+ * @param array $module_configs Full sync configuration for all sync modules.
+ * @return bool Always returns true at success.
+ */
+ public function start( $module_configs = null ) {
+ $was_already_running = $this->is_started() && ! $this->is_finished();
+
+ // Remove all evidence of previous full sync items and status.
+ $this->reset_data();
+
+ if ( $was_already_running ) {
+ /**
+ * Fires when a full sync is cancelled.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ */
+ do_action( 'jetpack_full_sync_cancelled' );
+ }
+
+ $this->update_status_option( 'started', time() );
+ $this->update_status_option( 'params', $module_configs );
+
+ $enqueue_status = array();
+ $full_sync_config = array();
+ $include_empty = false;
+ $empty = array();
+
+ // Default value is full sync.
+ if ( ! is_array( $module_configs ) ) {
+ $module_configs = array();
+ $include_empty = true;
+ foreach ( Modules::get_modules() as $module ) {
+ $module_configs[ $module->name() ] = true;
+ }
+ }
+
+ // Set default configuration, calculate totals, and save configuration if totals > 0.
+ foreach ( Modules::get_modules() as $module ) {
+ $module_name = $module->name();
+ $module_config = isset( $module_configs[ $module_name ] ) ? $module_configs[ $module_name ] : false;
+
+ if ( ! $module_config ) {
+ continue;
+ }
+
+ if ( 'users' === $module_name && 'initial' === $module_config ) {
+ $module_config = $module->get_initial_sync_user_config();
+ }
+
+ $enqueue_status[ $module_name ] = false;
+
+ $total_items = $module->estimate_full_sync_actions( $module_config );
+
+ // If there's information to process, configure this module.
+ if ( ! is_null( $total_items ) && $total_items > 0 ) {
+ $full_sync_config[ $module_name ] = $module_config;
+ $enqueue_status[ $module_name ] = array(
+ $total_items, // Total.
+ 0, // Queued.
+ false, // Current state.
+ );
+ } elseif ( $include_empty && 0 === $total_items ) {
+ $empty[ $module_name ] = true;
+ }
+ }
+
+ $this->set_config( $full_sync_config );
+ $this->set_enqueue_status( $enqueue_status );
+
+ $range = $this->get_content_range( $full_sync_config );
+ /**
+ * Fires when a full sync begins. This action is serialized
+ * and sent to the server so that it knows a full sync is coming.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ * @since-jetpack 7.3.0 Added $range arg.
+ * @since-jetpack 7.4.0 Added $empty arg.
+ *
+ * @param array $full_sync_config Sync configuration for all sync modules.
+ * @param array $range Range of the sync items, containing min and max IDs for some item types.
+ * @param array $empty The modules with no items to sync during a full sync.
+ */
+ do_action( 'jetpack_full_sync_start', $full_sync_config, $range, $empty );
+
+ $this->continue_enqueuing( $full_sync_config );
+
+ return true;
+ }
+
+ /**
+ * Enqueue the next items to sync.
+ *
+ * @access public
+ *
+ * @param array $configs Full sync configuration for all sync modules.
+ */
+ public function continue_enqueuing( $configs = null ) {
+ // Return early if not in progress.
+ if ( ! $this->get_status_option( 'started' ) || $this->get_status_option( 'queue_finished' ) ) {
+ return;
+ }
+
+ // Attempt to obtain lock.
+ $lock = new Lock();
+ $lock_expiration = $lock->attempt( self::ENQUEUE_LOCK_NAME );
+
+ // Return if unable to obtain lock.
+ if ( false === $lock_expiration ) {
+ return;
+ }
+
+ // enqueue full sync actions.
+ $this->enqueue( $configs );
+
+ // Remove lock.
+ $lock->remove( self::ENQUEUE_LOCK_NAME, $lock_expiration );
+ }
+
+ /**
+ * Get Modules that are configured to Full Sync and haven't finished enqueuing
+ *
+ * @param array $configs Full sync configuration for all sync modules.
+ *
+ * @return array
+ */
+ public function get_remaining_modules_to_enqueue( $configs ) {
+ $enqueue_status = $this->get_enqueue_status();
+ return array_filter(
+ Modules::get_modules(),
+ /**
+ * Select configured and not finished modules.
+ *
+ * @var $module Module
+ * @return bool
+ */
+ function ( $module ) use ( $configs, $enqueue_status ) {
+ // Skip module if not configured for this sync or module is done.
+ if ( ! isset( $configs[ $module->name() ] ) ) {
+ return false;
+ }
+ if ( ! $configs[ $module->name() ] ) {
+ return false;
+ }
+ if ( isset( $enqueue_status[ $module->name() ][2] ) ) {
+ if ( true === $enqueue_status[ $module->name() ][2] ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ );
+ }
+
+ /**
+ * Enqueue the next items to sync.
+ *
+ * @access public
+ *
+ * @param array $configs Full sync configuration for all sync modules.
+ */
+ public function enqueue( $configs = null ) {
+ if ( ! $configs ) {
+ $configs = $this->get_config();
+ }
+
+ $enqueue_status = $this->get_enqueue_status();
+ $full_sync_queue = new Queue( 'full_sync' );
+ $available_queue_slots = Settings::get_setting( 'max_queue_size_full_sync' ) - $full_sync_queue->size();
+
+ if ( $available_queue_slots <= 0 ) {
+ return;
+ }
+
+ $remaining_items_to_enqueue = min( Settings::get_setting( 'max_enqueue_full_sync' ), $available_queue_slots );
+
+ /**
+ * If a module exits early (e.g. because it ran out of full sync queue slots, or we ran out of request time)
+ * then it should exit early
+ */
+ foreach ( $this->get_remaining_modules_to_enqueue( $configs ) as $module ) {
+ list( $items_enqueued, $next_enqueue_state ) = $module->enqueue_full_sync_actions( $configs[ $module->name() ], $remaining_items_to_enqueue, $enqueue_status[ $module->name() ][2] );
+
+ $enqueue_status[ $module->name() ][2] = $next_enqueue_state;
+
+ // If items were processed, subtract them from the limit.
+ if ( ! is_null( $items_enqueued ) && $items_enqueued > 0 ) {
+ $enqueue_status[ $module->name() ][1] += $items_enqueued;
+ $remaining_items_to_enqueue -= $items_enqueued;
+ }
+
+ if ( 0 >= $remaining_items_to_enqueue || true !== $next_enqueue_state ) {
+ $this->set_enqueue_status( $enqueue_status );
+ return;
+ }
+ }
+
+ $this->queue_full_sync_end( $configs );
+ $this->set_enqueue_status( $enqueue_status );
+ }
+
+ /**
+ * Enqueue 'jetpack_full_sync_end' and update 'queue_finished' status.
+ *
+ * @access public
+ *
+ * @param array $configs Full sync configuration for all sync modules.
+ */
+ public function queue_full_sync_end( $configs ) {
+ $range = $this->get_content_range( $configs );
+
+ /**
+ * Fires when a full sync ends. This action is serialized
+ * and sent to the server.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ * @since-jetpack 7.3.0 Added $range arg.
+ *
+ * @param string $checksum Deprecated since 7.3.0 - @see https://github.com/Automattic/jetpack/pull/11945/
+ * @param array $range Range of the sync items, containing min and max IDs for some item types.
+ */
+ do_action( 'jetpack_full_sync_end', '', $range );
+
+ // Setting autoload to true means that it's faster to check whether we should continue enqueuing.
+ $this->update_status_option( 'queue_finished', time(), true );
+ }
+
+ /**
+ * Get the range (min ID, max ID and total items) of items to sync.
+ *
+ * @access public
+ *
+ * @param string $type Type of sync item to get the range for.
+ * @return array Array of min ID, max ID and total items in the range.
+ */
+ public function get_range( $type ) {
+ global $wpdb;
+ if ( ! in_array( $type, array( 'comments', 'posts' ), true ) ) {
+ return array();
+ }
+
+ switch ( $type ) {
+ case 'posts':
+ $table = $wpdb->posts;
+ $id = 'ID';
+ $where_sql = Settings::get_blacklisted_post_types_sql();
+
+ break;
+ case 'comments':
+ $table = $wpdb->comments;
+ $id = 'comment_ID';
+ $where_sql = Settings::get_comments_filter_sql();
+ break;
+ }
+
+ // TODO: Call $wpdb->prepare on the following query.
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $results = $wpdb->get_results( "SELECT MAX({$id}) as max, MIN({$id}) as min, COUNT({$id}) as count FROM {$table} WHERE {$where_sql}" );
+ if ( isset( $results[0] ) ) {
+ return $results[0];
+ }
+
+ return array();
+ }
+
+ /**
+ * Get the range for content (posts and comments) to sync.
+ *
+ * @access private
+ *
+ * @param array $config Full sync configuration for this all sync modules.
+ * @return array Array of range (min ID, max ID, total items) for all content types.
+ */
+ private function get_content_range( $config ) {
+ $range = array();
+ // Only when we are sending the whole range do we want to send also the range.
+ if ( true === isset( $config['posts'] ) && $config['posts'] ) {
+ $range['posts'] = $this->get_range( 'posts' );
+ }
+
+ if ( true === isset( $config['comments'] ) && $config['comments'] ) {
+ $range['comments'] = $this->get_range( 'comments' );
+ }
+ return $range;
+ }
+
+ /**
+ * Update the progress after sync modules actions have been processed on the server.
+ *
+ * @access public
+ *
+ * @param array $actions Actions that have been processed on the server.
+ */
+ public function update_sent_progress_action( $actions ) {
+ // Quick way to map to first items with an array of arrays.
+ $actions_with_counts = array_count_values( array_filter( array_map( array( $this, 'get_action_name' ), $actions ) ) );
+
+ // Total item counts for each action.
+ $actions_with_total_counts = $this->get_actions_totals( $actions );
+
+ if ( ! $this->is_started() || $this->is_finished() ) {
+ return;
+ }
+
+ if ( isset( $actions_with_counts['jetpack_full_sync_start'] ) ) {
+ $this->update_status_option( 'send_started', time() );
+ }
+
+ foreach ( Modules::get_modules() as $module ) {
+ $module_actions = $module->get_full_sync_actions();
+ $status_option_name = "{$module->name()}_sent";
+ $total_option_name = "{$status_option_name}_total";
+ $items_sent = $this->get_status_option( $status_option_name, 0 );
+ $items_sent_total = $this->get_status_option( $total_option_name, 0 );
+
+ foreach ( $module_actions as $module_action ) {
+ if ( isset( $actions_with_counts[ $module_action ] ) ) {
+ $items_sent += $actions_with_counts[ $module_action ];
+ }
+
+ if ( ! empty( $actions_with_total_counts[ $module_action ] ) ) {
+ $items_sent_total += $actions_with_total_counts[ $module_action ];
+ }
+ }
+
+ if ( $items_sent > 0 ) {
+ $this->update_status_option( $status_option_name, $items_sent );
+ }
+
+ if ( 0 !== $items_sent_total ) {
+ $this->update_status_option( $total_option_name, $items_sent_total );
+ }
+ }
+
+ if ( isset( $actions_with_counts['jetpack_full_sync_end'] ) ) {
+ $this->update_status_option( 'finished', time() );
+ }
+ }
+
+ /**
+ * Returns the progress percentage of a full sync.
+ *
+ * @access public
+ *
+ * @return int|null
+ */
+ public function get_sync_progress_percentage() {
+ if ( ! $this->is_started() || $this->is_finished() ) {
+ return null;
+ }
+ $status = $this->get_status();
+ if ( ! $status['queue'] || ! $status['sent'] || ! $status['total'] ) {
+ return null;
+ }
+ $queued_multiplier = 0.1;
+ $sent_multiplier = 0.9;
+ $count_queued = array_reduce(
+ $status['queue'],
+ function ( $sum, $value ) {
+ return $sum + $value;
+ },
+ 0
+ );
+ $count_sent = array_reduce(
+ $status['sent'],
+ function ( $sum, $value ) {
+ return $sum + $value;
+ },
+ 0
+ );
+ $count_total = array_reduce(
+ $status['total'],
+ function ( $sum, $value ) {
+ return $sum + $value;
+ },
+ 0
+ );
+ $percent_queued = ( $count_queued / $count_total ) * $queued_multiplier * 100;
+ $percent_sent = ( $count_sent / $count_total ) * $sent_multiplier * 100;
+ return ceil( $percent_queued + $percent_sent );
+ }
+
+ /**
+ * Get the name of the action for an item in the sync queue.
+ *
+ * @access public
+ *
+ * @param array $queue_item Item of the sync queue.
+ * @return string|boolean Name of the action, false if queue item is invalid.
+ */
+ public function get_action_name( $queue_item ) {
+ if ( is_array( $queue_item ) && isset( $queue_item[0] ) ) {
+ return $queue_item[0];
+ }
+ return false;
+ }
+
+ /**
+ * Retrieve the total number of items we're syncing in a particular queue item (action).
+ * `$queue_item[1]` is expected to contain chunks of items, and `$queue_item[1][0]`
+ * represents the first (and only) chunk of items to sync in that action.
+ *
+ * @access public
+ *
+ * @param array $queue_item Item of the sync queue that corresponds to a particular action.
+ * @return int Total number of items in the action.
+ */
+ public function get_action_totals( $queue_item ) {
+ if ( is_array( $queue_item ) && isset( $queue_item[1][0] ) ) {
+ if ( is_array( $queue_item[1][0] ) ) {
+ // Let's count the items we sync in this action.
+ return count( $queue_item[1][0] );
+ }
+ // -1 indicates that this action syncs all items by design.
+ return -1;
+ }
+ return 0;
+ }
+
+ /**
+ * Retrieve the total number of items for a set of actions, grouped by action name.
+ *
+ * @access public
+ *
+ * @param array $actions An array of actions.
+ * @return array An array, representing the total number of items, grouped per action.
+ */
+ public function get_actions_totals( $actions ) {
+ $totals = array();
+
+ foreach ( $actions as $action ) {
+ $name = $this->get_action_name( $action );
+ $action_totals = $this->get_action_totals( $action );
+ if ( ! isset( $totals[ $name ] ) ) {
+ $totals[ $name ] = 0;
+ }
+ $totals[ $name ] += $action_totals;
+ }
+
+ return $totals;
+ }
+
+ /**
+ * Whether full sync has started.
+ *
+ * @access public
+ *
+ * @return boolean
+ */
+ public function is_started() {
+ return (bool) $this->get_status_option( 'started' );
+ }
+
+ /**
+ * Whether full sync has finished.
+ *
+ * @access public
+ *
+ * @return boolean
+ */
+ public function is_finished() {
+ return (bool) $this->get_status_option( 'finished' );
+ }
+
+ /**
+ * Retrieve the status of the current full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync status.
+ */
+ public function get_status() {
+ $status = array(
+ 'started' => $this->get_status_option( 'started' ),
+ 'queue_finished' => $this->get_status_option( 'queue_finished' ),
+ 'send_started' => $this->get_status_option( 'send_started' ),
+ 'finished' => $this->get_status_option( 'finished' ),
+ 'sent' => array(),
+ 'sent_total' => array(),
+ 'queue' => array(),
+ 'config' => $this->get_status_option( 'params' ),
+ 'total' => array(),
+ );
+
+ $enqueue_status = $this->get_enqueue_status();
+
+ foreach ( Modules::get_modules() as $module ) {
+ $name = $module->name();
+
+ if ( ! isset( $enqueue_status[ $name ] ) ) {
+ continue;
+ }
+
+ list( $total, $queued ) = $enqueue_status[ $name ];
+
+ if ( $total ) {
+ $status['total'][ $name ] = $total;
+ }
+
+ if ( $queued ) {
+ $status['queue'][ $name ] = $queued;
+ }
+
+ $sent = $this->get_status_option( "{$name}_sent" );
+ if ( $sent ) {
+ $status['sent'][ $name ] = $sent;
+ }
+
+ $sent_total = $this->get_status_option( "{$name}_sent_total" );
+ if ( $sent_total ) {
+ $status['sent_total'][ $name ] = $sent_total;
+ }
+ }
+
+ return $status;
+ }
+
+ /**
+ * Clear all the full sync status options.
+ *
+ * @access public
+ */
+ public function clear_status() {
+ $prefix = self::STATUS_OPTION_PREFIX;
+ \Jetpack_Options::delete_raw_option( "{$prefix}_started" );
+ \Jetpack_Options::delete_raw_option( "{$prefix}_params" );
+ \Jetpack_Options::delete_raw_option( "{$prefix}_queue_finished" );
+ \Jetpack_Options::delete_raw_option( "{$prefix}_send_started" );
+ \Jetpack_Options::delete_raw_option( "{$prefix}_finished" );
+
+ $this->delete_enqueue_status();
+
+ foreach ( Modules::get_modules() as $module ) {
+ \Jetpack_Options::delete_raw_option( "{$prefix}_{$module->name()}_sent" );
+ \Jetpack_Options::delete_raw_option( "{$prefix}_{$module->name()}_sent_total" );
+ }
+ }
+
+ /**
+ * Clear all the full sync data.
+ *
+ * @access public
+ */
+ public function reset_data() {
+ $this->clear_status();
+ $this->delete_config();
+ ( new Lock() )->remove( self::ENQUEUE_LOCK_NAME, true );
+
+ $listener = Listener::get_instance();
+ $listener->get_full_sync_queue()->reset();
+ }
+
+ /**
+ * Get the value of a full sync status option.
+ *
+ * @access private
+ *
+ * @param string $name Name of the option.
+ * @param mixed $default Default value of the option.
+ * @return mixed Option value.
+ */
+ private function get_status_option( $name, $default = null ) {
+ $value = \Jetpack_Options::get_raw_option( self::STATUS_OPTION_PREFIX . "_$name", $default );
+
+ return is_numeric( $value ) ? (int) $value : $value;
+ }
+
+ /**
+ * Update the value of a full sync status option.
+ *
+ * @access private
+ *
+ * @param string $name Name of the option.
+ * @param mixed $value Value of the option.
+ * @param boolean $autoload Whether the option should be autoloaded at the beginning of the request.
+ */
+ private function update_status_option( $name, $value, $autoload = false ) {
+ \Jetpack_Options::update_raw_option( self::STATUS_OPTION_PREFIX . "_$name", $value, $autoload );
+ }
+
+ /**
+ * Set the full sync enqueue status.
+ *
+ * @access private
+ *
+ * @param array $new_status The new full sync enqueue status.
+ */
+ private function set_enqueue_status( $new_status ) {
+ \Jetpack_Options::update_raw_option( 'jetpack_sync_full_enqueue_status', $new_status );
+ }
+
+ /**
+ * Delete full sync enqueue status.
+ *
+ * @access private
+ *
+ * @return boolean Whether the status was deleted.
+ */
+ private function delete_enqueue_status() {
+ return \Jetpack_Options::delete_raw_option( 'jetpack_sync_full_enqueue_status' );
+ }
+
+ /**
+ * Retrieve the current full sync enqueue status.
+ *
+ * @access private
+ *
+ * @return array Full sync enqueue status.
+ */
+ public function get_enqueue_status() {
+ return \Jetpack_Options::get_raw_option( 'jetpack_sync_full_enqueue_status' );
+ }
+
+ /**
+ * Set the full sync enqueue configuration.
+ *
+ * @access private
+ *
+ * @param array $config The new full sync enqueue configuration.
+ */
+ private function set_config( $config ) {
+ \Jetpack_Options::update_raw_option( 'jetpack_sync_full_config', $config );
+ }
+
+ /**
+ * Delete full sync configuration.
+ *
+ * @access private
+ *
+ * @return boolean Whether the configuration was deleted.
+ */
+ private function delete_config() {
+ return \Jetpack_Options::delete_raw_option( 'jetpack_sync_full_config' );
+ }
+
+ /**
+ * Retrieve the current full sync enqueue config.
+ *
+ * @access private
+ *
+ * @return array Full sync enqueue config.
+ */
+ private function get_config() {
+ return \Jetpack_Options::get_raw_option( 'jetpack_sync_full_config' );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-import.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-import.php
new file mode 100644
index 00000000..839434dd
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-import.php
@@ -0,0 +1,220 @@
+<?php
+/**
+ * Import sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Sync\Settings;
+
+/**
+ * Class to handle sync for imports.
+ */
+class Import extends Module {
+
+ /**
+ * Tracks which actions have already been synced for the import
+ * to prevent the same event from being triggered a second time.
+ *
+ * @var array
+ */
+ private $synced_actions = array();
+
+ /**
+ * A mapping of action types to sync action name.
+ * Keys are the name of the import action.
+ * Values are the resulting sync action.
+ *
+ * Note: import_done and import_end both intentionally map to
+ * jetpack_sync_import_end, as they both track the same type of action,
+ * the successful completion of an import. Different import plugins use
+ * differently named actions, and this is an attempt to consolidate.
+ *
+ * @var array
+ */
+ private static $import_sync_action_map = array(
+ 'import_start' => 'jetpack_sync_import_start',
+ 'import_done' => 'jetpack_sync_import_end',
+ 'import_end' => 'jetpack_sync_import_end',
+ );
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'import';
+ }
+
+ /**
+ * Initialize imports action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ add_action( 'export_wp', $callable );
+ add_action( 'jetpack_sync_import_start', $callable, 10, 2 );
+ add_action( 'jetpack_sync_import_end', $callable, 10, 2 );
+
+ // WordPress.
+ add_action( 'import_start', array( $this, 'sync_import_action' ) );
+
+ // Movable type, RSS, Livejournal.
+ add_action( 'import_done', array( $this, 'sync_import_action' ) );
+
+ // WordPress, Blogger, Livejournal, woo tax rate.
+ add_action( 'import_end', array( $this, 'sync_import_action' ) );
+ }
+
+ /**
+ * Set module defaults.
+ * Define an empty list of synced actions for us to fill later.
+ *
+ * @access public
+ */
+ public function set_defaults() {
+ $this->synced_actions = array();
+ }
+
+ /**
+ * Generic handler for import actions.
+ *
+ * @access public
+ *
+ * @param string $importer Either a string reported by the importer, the class name of the importer, or 'unknown'.
+ */
+ public function sync_import_action( $importer ) {
+ $import_action = current_filter();
+ // Map action to event name.
+ $sync_action = self::$import_sync_action_map[ $import_action ];
+
+ // Only sync each action once per import.
+ if ( array_key_exists( $sync_action, $this->synced_actions ) && $this->synced_actions[ $sync_action ] ) {
+ return;
+ }
+
+ // Mark this action as synced.
+ $this->synced_actions[ $sync_action ] = true;
+
+ // Prefer self-reported $importer value.
+ if ( ! $importer ) {
+ // Fall back to inferring by calling class name.
+ $importer = self::get_calling_importer_class();
+ }
+
+ // Get $importer from known_importers.
+ $known_importers = Settings::get_setting( 'known_importers' );
+ if ( is_string( $importer ) && isset( $known_importers[ $importer ] ) ) {
+ $importer = $known_importers[ $importer ];
+ }
+
+ $importer_name = $this->get_importer_name( $importer );
+
+ switch ( $sync_action ) {
+ case 'jetpack_sync_import_start':
+ /**
+ * Used for syncing the start of an import
+ *
+ * @since 1.6.3
+ * @since-jetpack 7.3.0
+ *
+ * @module sync
+ *
+ * @param string $importer Either a string reported by the importer, the class name of the importer, or 'unknown'.
+ * @param string $importer_name The name reported by the importer, or 'Unknown Importer'.
+ */
+ do_action( 'jetpack_sync_import_start', $importer, $importer_name );
+ break;
+
+ case 'jetpack_sync_import_end':
+ /**
+ * Used for syncing the end of an import
+ *
+ * @since 1.6.3
+ * @since-jetpack 7.3.0
+ *
+ * @module sync
+ *
+ * @param string $importer Either a string reported by the importer, the class name of the importer, or 'unknown'.
+ * @param string $importer_name The name reported by the importer, or 'Unknown Importer'.
+ */
+ do_action( 'jetpack_sync_import_end', $importer, $importer_name );
+ break;
+ }
+ }
+
+ /**
+ * Retrieve the name of the importer.
+ *
+ * @access private
+ *
+ * @param string $importer Either a string reported by the importer, the class name of the importer, or 'unknown'.
+ * @return string Name of the importer, or "Unknown Importer" if importer is unknown.
+ */
+ private function get_importer_name( $importer ) {
+ $importers = get_importers();
+ return isset( $importers[ $importer ] ) ? $importers[ $importer ][0] : 'Unknown Importer';
+ }
+
+ /**
+ * Determine the class that extends `WP_Importer` which is responsible for
+ * the current action. Designed to be used within an action handler.
+ *
+ * @access private
+ * @static
+ *
+ * @return string The name of the calling class, or 'unknown'.
+ */
+ private static function get_calling_importer_class() {
+ // If WP_Importer doesn't exist, neither will any importer that extends it.
+ if ( ! class_exists( 'WP_Importer', false ) ) {
+ return 'unknown';
+ }
+
+ $action = current_filter();
+ $backtrace = debug_backtrace( false ); //phpcs:ignore PHPCompatibility.FunctionUse.NewFunctionParameters.debug_backtrace_optionsFound,WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
+
+ $do_action_pos = -1;
+ $backtrace_len = count( $backtrace );
+ for ( $i = 0; $i < $backtrace_len; $i++ ) {
+ // Find the location in the stack of the calling action.
+ if ( 'do_action' === $backtrace[ $i ]['function'] && $action === $backtrace[ $i ]['args'][0] ) {
+ $do_action_pos = $i;
+ break;
+ }
+ }
+
+ // If the action wasn't called, the calling class is unknown.
+ if ( -1 === $do_action_pos ) {
+ return 'unknown';
+ }
+
+ // Continue iterating the stack looking for a caller that extends WP_Importer.
+ for ( $i = $do_action_pos + 1; $i < $backtrace_len; $i++ ) {
+ // If there is no class on the trace, continue.
+ if ( ! isset( $backtrace[ $i ]['class'] ) ) {
+ continue;
+ }
+
+ $class_name = $backtrace[ $i ]['class'];
+
+ // Check if the class extends WP_Importer.
+ if ( class_exists( $class_name, false ) ) {
+ $parents = class_parents( $class_name, false );
+ if ( $parents && in_array( 'WP_Importer', $parents, true ) ) {
+ return $class_name;
+ }
+ }
+ }
+
+ // If we've exhausted the stack without a match, the calling class is unknown.
+ return 'unknown';
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-menus.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-menus.php
new file mode 100644
index 00000000..bf6c5620
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-menus.php
@@ -0,0 +1,146 @@
+<?php
+/**
+ * Menus sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+/**
+ * Class to handle sync for menus.
+ */
+class Menus extends Module {
+ /**
+ * Navigation menu items that were added but not synced yet.
+ *
+ * @access private
+ *
+ * @var array
+ */
+ private $nav_items_just_added = array();
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'menus';
+ }
+
+ /**
+ * Initialize menus action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ add_action( 'wp_create_nav_menu', $callable, 10, 2 );
+ add_action( 'wp_update_nav_menu', array( $this, 'update_nav_menu' ), 10, 2 );
+ add_action( 'wp_add_nav_menu_item', array( $this, 'update_nav_menu_add_item' ), 10, 3 );
+ add_action( 'wp_update_nav_menu_item', array( $this, 'update_nav_menu_update_item' ), 10, 3 );
+ add_action( 'post_updated', array( $this, 'remove_just_added_menu_item' ), 10, 2 );
+
+ add_action( 'jetpack_sync_updated_nav_menu', $callable, 10, 2 );
+ add_action( 'jetpack_sync_updated_nav_menu_add_item', $callable, 10, 4 );
+ add_action( 'jetpack_sync_updated_nav_menu_update_item', $callable, 10, 4 );
+ add_action( 'delete_nav_menu', $callable, 10, 3 );
+ }
+
+ /**
+ * Nav menu update handler.
+ *
+ * @access public
+ *
+ * @param int $menu_id ID of the menu.
+ * @param array $menu_data An array of menu data.
+ */
+ public function update_nav_menu( $menu_id, $menu_data = array() ) {
+ if ( empty( $menu_data ) ) {
+ return;
+ }
+ /**
+ * Helps sync log that a nav menu was updated.
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.0.0
+ *
+ * @param int $menu_id ID of the menu.
+ * @param array $menu_data An array of menu data.
+ */
+ do_action( 'jetpack_sync_updated_nav_menu', $menu_id, $menu_data );
+ }
+
+ /**
+ * Nav menu item addition handler.
+ *
+ * @access public
+ *
+ * @param int $menu_id ID of the menu.
+ * @param int $nav_item_id ID of the new menu item.
+ * @param array $nav_item_args Arguments used to add the menu item.
+ */
+ public function update_nav_menu_add_item( $menu_id, $nav_item_id, $nav_item_args ) {
+ $menu_data = wp_get_nav_menu_object( $menu_id );
+ $this->nav_items_just_added[] = $nav_item_id;
+ /**
+ * Helps sync log that a new menu item was added.
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.0.0
+ *
+ * @param int $menu_id ID of the menu.
+ * @param array $menu_data An array of menu data.
+ * @param int $nav_item_id ID of the new menu item.
+ * @param array $nav_item_args Arguments used to add the menu item.
+ */
+ do_action( 'jetpack_sync_updated_nav_menu_add_item', $menu_id, $menu_data, $nav_item_id, $nav_item_args );
+ }
+
+ /**
+ * Nav menu item update handler.
+ *
+ * @access public
+ *
+ * @param int $menu_id ID of the menu.
+ * @param int $nav_item_id ID of the new menu item.
+ * @param array $nav_item_args Arguments used to update the menu item.
+ */
+ public function update_nav_menu_update_item( $menu_id, $nav_item_id, $nav_item_args ) {
+ if ( in_array( $nav_item_id, $this->nav_items_just_added, true ) ) {
+ return;
+ }
+ $menu_data = wp_get_nav_menu_object( $menu_id );
+ /**
+ * Helps sync log that an update to the menu item happened.
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.0.0
+ *
+ * @param int $menu_id ID of the menu.
+ * @param array $menu_data An array of menu data.
+ * @param int $nav_item_id ID of the new menu item.
+ * @param array $nav_item_args Arguments used to update the menu item.
+ */
+ do_action( 'jetpack_sync_updated_nav_menu_update_item', $menu_id, $menu_data, $nav_item_id, $nav_item_args );
+ }
+
+ /**
+ * Remove menu items that have already been saved from the "just added" list.
+ *
+ * @access public
+ *
+ * @param int $nav_item_id ID of the new menu item.
+ * @param \WP_Post $post_after Nav menu item post object after the update.
+ */
+ public function remove_just_added_menu_item( $nav_item_id, $post_after ) {
+ if ( 'nav_menu_item' !== $post_after->post_type ) {
+ return;
+ }
+ $this->nav_items_just_added = array_diff( $this->nav_items_just_added, array( $nav_item_id ) );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-meta.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-meta.php
new file mode 100644
index 00000000..de293a9b
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-meta.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * Meta sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+/**
+ * Class to handle sync for meta.
+ */
+class Meta extends Module {
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'meta';
+ }
+
+ /**
+ * This implementation of get_objects_by_id() is a bit hacky since we're not passing in an array of meta IDs,
+ * but instead an array of post or comment IDs for which to retrieve meta for. On top of that,
+ * we also pass in an associative array where we expect there to be 'meta_key' and 'ids' keys present.
+ *
+ * This seemed to be required since if we have missing meta on WP.com and need to fetch it, we don't know what
+ * the meta key is, but we do know that we have missing meta for a given post or comment.
+ *
+ * @todo Refactor the $wpdb->prepare call to use placeholders.
+ *
+ * @param string $object_type The type of object for which we retrieve meta. Either 'post' or 'comment'.
+ * @param array $config Must include 'meta_key' and 'ids' keys.
+ *
+ * @return array
+ */
+ public function get_objects_by_id( $object_type, $config ) {
+ $table = _get_meta_table( $object_type );
+
+ if ( ! $table ) {
+ return array();
+ }
+
+ if ( ! is_array( $config ) ) {
+ return array();
+ }
+
+ $meta_objects = array();
+ foreach ( $config as $item ) {
+ $meta = null;
+ if ( isset( $item['id'] ) && isset( $item['meta_key'] ) ) {
+ $meta = $this->get_object_by_id( $object_type, (int) $item['id'], (string) $item['meta_key'] );
+ }
+ $meta_objects[ $item['id'] . '-' . $item['meta_key'] ] = $meta;
+ }
+
+ return $meta_objects;
+ }
+
+ /**
+ * Get a single Meta Result.
+ *
+ * @param string $object_type post, comment, term, user.
+ * @param null $id Object ID.
+ * @param null $meta_key Meta Key.
+ *
+ * @return mixed|null
+ */
+ public function get_object_by_id( $object_type, $id = null, $meta_key = null ) {
+ global $wpdb;
+
+ if ( ! is_int( $id ) || ! is_string( $meta_key ) ) {
+ return null;
+ }
+
+ $table = _get_meta_table( $object_type );
+ $object_id_column = $object_type . '_id';
+
+ // Sanitize so that the array only has integer values.
+ $meta = $wpdb->get_results(
+ $wpdb->prepare(
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ "SELECT * FROM {$table} WHERE {$object_id_column} = %d AND meta_key = %s",
+ $id,
+ $meta_key
+ ),
+ ARRAY_A
+ );
+
+ $meta_objects = null;
+
+ if ( ! is_wp_error( $meta ) && ! empty( $meta ) ) {
+ foreach ( $meta as $meta_entry ) {
+ if ( 'post' === $object_type && strlen( $meta_entry['meta_value'] ) >= Posts::MAX_POST_META_LENGTH ) {
+ $meta_entry['meta_value'] = '';
+ }
+ $meta_objects[] = array(
+ 'meta_type' => $object_type,
+ 'meta_id' => $meta_entry['meta_id'],
+ 'meta_key' => $meta_key,
+ 'meta_value' => $meta_entry['meta_value'],
+ 'object_id' => $meta_entry[ $object_id_column ],
+ );
+ }
+ }
+
+ return $meta_objects;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-module.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-module.php
new file mode 100644
index 00000000..b69de80e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-module.php
@@ -0,0 +1,604 @@
+<?php
+/**
+ * A base abstraction of a sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Sync\Functions;
+use Automattic\Jetpack\Sync\Listener;
+use Automattic\Jetpack\Sync\Replicastore;
+use Automattic\Jetpack\Sync\Sender;
+use Automattic\Jetpack\Sync\Settings;
+
+/**
+ * Basic methods implemented by Jetpack Sync extensions.
+ *
+ * @abstract
+ */
+abstract class Module {
+ /**
+ * Number of items per chunk when grouping objects for performance reasons.
+ *
+ * @access public
+ *
+ * @var int
+ */
+ const ARRAY_CHUNK_SIZE = 10;
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ abstract public function name();
+
+ /**
+ * The id field in the database.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function id_field() {
+ return 'ID';
+ }
+
+ /**
+ * The table in the database.
+ *
+ * @access public
+ *
+ * @return string|bool
+ */
+ public function table_name() {
+ return false;
+ }
+
+ // phpcs:disable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+
+ /**
+ * Retrieve a sync object by its ID.
+ *
+ * @access public
+ *
+ * @param string $object_type Type of the sync object.
+ * @param int $id ID of the sync object.
+ * @return mixed Object, or false if the object is invalid.
+ */
+ public function get_object_by_id( $object_type, $id ) {
+ return false;
+ }
+
+ /**
+ * Initialize callables action listeners.
+ * Override these to set up listeners and set/reset data/defaults.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ }
+
+ /**
+ * Initialize module action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ }
+
+ /**
+ * Set module defaults.
+ *
+ * @access public
+ */
+ public function set_defaults() {
+ }
+
+ /**
+ * Perform module cleanup.
+ * Usually triggered when uninstalling the plugin.
+ *
+ * @access public
+ */
+ public function reset_data() {
+ }
+
+ /**
+ * Enqueue the module actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
+ // In subclasses, return the number of actions enqueued, and next module state (true == done).
+ return array( null, true );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) {
+ // In subclasses, return the number of items yet to be enqueued.
+ return null;
+ }
+
+ // phpcs:enable VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array();
+ }
+
+ /**
+ * Get the number of actions that we care about.
+ *
+ * @access protected
+ *
+ * @param array $action_names Action names we're interested in.
+ * @param array $actions_to_count Unfiltered list of actions we want to count.
+ * @return array Number of actions that we're interested in.
+ */
+ protected function count_actions( $action_names, $actions_to_count ) {
+ return count( array_intersect( $action_names, $actions_to_count ) );
+ }
+
+ /**
+ * Calculate the checksum of one or more values.
+ *
+ * @access protected
+ *
+ * @param mixed $values Values to calculate checksum for.
+ * @param bool $sort If $values should have ksort called on it.
+ * @return int The checksum.
+ */
+ protected function get_check_sum( $values, $sort = true ) {
+ // Associative array order changes the generated checksum value.
+ if ( $sort && is_array( $values ) ) {
+ $this->recursive_ksort( $values );
+ }
+ return crc32( wp_json_encode( Functions::json_wrap( $values ) ) );
+ }
+
+ /**
+ * Recursively call ksort on an Array
+ *
+ * @param array $values Array.
+ */
+ private function recursive_ksort( &$values ) {
+ ksort( $values );
+ foreach ( $values as &$value ) {
+ if ( is_array( $value ) ) {
+ $this->recursive_ksort( $value );
+ }
+ }
+ }
+
+ /**
+ * Whether a particular checksum in a set of checksums is valid.
+ *
+ * @access protected
+ *
+ * @param array $sums_to_check Array of checksums.
+ * @param string $name Name of the checksum.
+ * @param int $new_sum Checksum to compare against.
+ * @return boolean Whether the checksum is valid.
+ */
+ protected function still_valid_checksum( $sums_to_check, $name, $new_sum ) {
+ if ( isset( $sums_to_check[ $name ] ) && $sums_to_check[ $name ] === $new_sum ) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Enqueue all items of a sync type as an action.
+ *
+ * @access protected
+ *
+ * @param string $action_name Name of the action.
+ * @param string $table_name Name of the database table.
+ * @param string $id_field Name of the ID field in the database.
+ * @param string $where_sql The SQL WHERE clause to filter to the desired items.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue in the same time.
+ * @param boolean $state Whether enqueueing has finished.
+ * @return array Array, containing the number of chunks and TRUE, indicating enqueueing has finished.
+ */
+ protected function enqueue_all_ids_as_action( $action_name, $table_name, $id_field, $where_sql, $max_items_to_enqueue, $state ) {
+ global $wpdb;
+
+ if ( ! $where_sql ) {
+ $where_sql = '1 = 1';
+ }
+
+ $items_per_page = 1000;
+ $page = 1;
+ $chunk_count = 0;
+ $previous_interval_end = $state ? $state : '~0';
+ $listener = Listener::get_instance();
+
+ // Count down from max_id to min_id so we get newest posts/comments/etc first.
+ // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ while ( $ids = $wpdb->get_col( "SELECT {$id_field} FROM {$table_name} WHERE {$where_sql} AND {$id_field} < {$previous_interval_end} ORDER BY {$id_field} DESC LIMIT {$items_per_page}" ) ) {
+ // Request posts in groups of N for efficiency.
+ $chunked_ids = array_chunk( $ids, self::ARRAY_CHUNK_SIZE );
+
+ // If we hit our row limit, process and return.
+ if ( $chunk_count + count( $chunked_ids ) >= $max_items_to_enqueue ) {
+ $remaining_items_count = $max_items_to_enqueue - $chunk_count;
+ $remaining_items = array_slice( $chunked_ids, 0, $remaining_items_count );
+ $remaining_items_with_previous_interval_end = $this->get_chunks_with_preceding_end( $remaining_items, $previous_interval_end );
+ $listener->bulk_enqueue_full_sync_actions( $action_name, $remaining_items_with_previous_interval_end );
+
+ $last_chunk = end( $remaining_items );
+ return array( $remaining_items_count + $chunk_count, end( $last_chunk ) );
+ }
+ $chunked_ids_with_previous_end = $this->get_chunks_with_preceding_end( $chunked_ids, $previous_interval_end );
+
+ $listener->bulk_enqueue_full_sync_actions( $action_name, $chunked_ids_with_previous_end );
+
+ $chunk_count += count( $chunked_ids );
+ $page++;
+ // The $ids are ordered in descending order.
+ $previous_interval_end = end( $ids );
+ }
+
+ if ( $wpdb->last_error ) {
+ // return the values that were passed in so all these chunks get retried.
+ return array( $max_items_to_enqueue, $state );
+ }
+
+ return array( $chunk_count, true );
+ }
+
+ /**
+ * Given the Module Full Sync Configuration and Status return the next chunk of items to send.
+ *
+ * @param array $config This module Full Sync configuration.
+ * @param array $status This module Full Sync status.
+ * @param int $chunk_size Chunk size.
+ *
+ * @return array|object|null
+ */
+ public function get_next_chunk( $config, $status, $chunk_size ) {
+ // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ global $wpdb;
+ return $wpdb->get_col(
+ <<<SQL
+SELECT {$this->id_field()}
+FROM {$wpdb->{$this->table_name()}}
+WHERE {$this->get_where_sql( $config )}
+AND {$this->id_field()} < {$status['last_sent']}
+ORDER BY {$this->id_field()}
+DESC LIMIT {$chunk_size}
+SQL
+ );
+ // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ }
+
+ /**
+ * Return the initial last sent object.
+ *
+ * @return string|array initial status.
+ */
+ public function get_initial_last_sent() {
+ return '~0';
+ }
+
+ /**
+ * Immediately send all items of a sync type as an action.
+ *
+ * @access protected
+ *
+ * @param string $config Full sync configuration for this module.
+ * @param array $status the current module full sync status.
+ * @param float $send_until timestamp until we want this request to send full sync events.
+ *
+ * @return array Status, the module full sync status updated.
+ */
+ public function send_full_sync_actions( $config, $status, $send_until ) {
+ global $wpdb;
+
+ if ( empty( $status['last_sent'] ) ) {
+ $status['last_sent'] = $this->get_initial_last_sent();
+ }
+
+ $limits = Settings::get_setting( 'full_sync_limits' )[ $this->name() ];
+
+ $chunks_sent = 0;
+ // phpcs:ignore WordPress.CodeAnalysis.AssignmentInCondition.FoundInWhileCondition
+ while ( $objects = $this->get_next_chunk( $config, $status, $limits['chunk_size'] ) ) {
+ if ( $chunks_sent++ === $limits['max_chunks'] || microtime( true ) >= $send_until ) {
+ return $status;
+ }
+
+ $result = $this->send_action( 'jetpack_full_sync_' . $this->name(), array( $objects, $status['last_sent'] ) );
+
+ if ( is_wp_error( $result ) || $wpdb->last_error ) {
+ $status['error'] = true;
+ return $status;
+ }
+ // The $ids are ordered in descending order.
+ $status['last_sent'] = end( $objects );
+ $status['sent'] += count( $objects );
+ }
+
+ if ( ! $wpdb->last_error ) {
+ $status['finished'] = true;
+ }
+
+ return $status;
+ }
+
+ /**
+ * Immediately sends a single item without firing or enqueuing it
+ *
+ * @param string $action_name The action.
+ * @param array $data The data associated with the action.
+ */
+ public function send_action( $action_name, $data = null ) {
+ $sender = Sender::get_instance();
+ return $sender->send_action( $action_name, $data );
+ }
+
+ /**
+ * Retrieve chunk IDs with previous interval end.
+ *
+ * @access protected
+ *
+ * @param array $chunks All remaining items.
+ * @param int $previous_interval_end The last item from the previous interval.
+ * @return array Chunk IDs with the previous interval end.
+ */
+ protected function get_chunks_with_preceding_end( $chunks, $previous_interval_end ) {
+ $chunks_with_ends = array();
+ foreach ( $chunks as $chunk ) {
+ $chunks_with_ends[] = array(
+ 'ids' => $chunk,
+ 'previous_end' => $previous_interval_end,
+ );
+ // Chunks are ordered in descending order.
+ $previous_interval_end = end( $chunk );
+ }
+ return $chunks_with_ends;
+ }
+
+ /**
+ * Get metadata of a particular object type within the designated meta key whitelist.
+ *
+ * @access protected
+ *
+ * @todo Refactor to use $wpdb->prepare() on the SQL query.
+ *
+ * @param array $ids Object IDs.
+ * @param string $meta_type Meta type.
+ * @param array $meta_key_whitelist Meta key whitelist.
+ * @return array Unserialized meta values.
+ */
+ protected function get_metadata( $ids, $meta_type, $meta_key_whitelist ) {
+ global $wpdb;
+ $table = _get_meta_table( $meta_type );
+ $id = $meta_type . '_id';
+ if ( ! $table ) {
+ return array();
+ }
+
+ $private_meta_whitelist_sql = "'" . implode( "','", array_map( 'esc_sql', $meta_key_whitelist ) ) . "'";
+
+ return array_map(
+ array( $this, 'unserialize_meta' ),
+ $wpdb->get_results(
+ // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
+ "SELECT $id, meta_key, meta_value, meta_id FROM $table WHERE $id IN ( " . implode( ',', wp_parse_id_list( $ids ) ) . ' )' .
+ " AND meta_key IN ( $private_meta_whitelist_sql ) ",
+ // phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared
+ OBJECT
+ )
+ );
+ }
+
+ /**
+ * Initialize listeners for the particular meta type.
+ *
+ * @access public
+ *
+ * @param string $meta_type Meta type.
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners_for_meta_type( $meta_type, $callable ) {
+ add_action( "added_{$meta_type}_meta", $callable, 10, 4 );
+ add_action( "updated_{$meta_type}_meta", $callable, 10, 4 );
+ add_action( "deleted_{$meta_type}_meta", $callable, 10, 4 );
+ }
+
+ /**
+ * Initialize meta whitelist handler for the particular meta type.
+ *
+ * @access public
+ *
+ * @param string $meta_type Meta type.
+ * @param callable $whitelist_handler Action handler callable.
+ */
+ public function init_meta_whitelist_handler( $meta_type, $whitelist_handler ) {
+ add_filter( "jetpack_sync_before_enqueue_added_{$meta_type}_meta", $whitelist_handler );
+ add_filter( "jetpack_sync_before_enqueue_updated_{$meta_type}_meta", $whitelist_handler );
+ add_filter( "jetpack_sync_before_enqueue_deleted_{$meta_type}_meta", $whitelist_handler );
+ }
+
+ /**
+ * Retrieve the term relationships for the specified object IDs.
+ *
+ * @access protected
+ *
+ * @todo This feels too specific to be in the abstract sync Module class. Move it?
+ *
+ * @param array $ids Object IDs.
+ * @return array Term relationships - object ID and term taxonomy ID pairs.
+ */
+ protected function get_term_relationships( $ids ) {
+ global $wpdb;
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ return $wpdb->get_results( "SELECT object_id, term_taxonomy_id FROM $wpdb->term_relationships WHERE object_id IN ( " . implode( ',', wp_parse_id_list( $ids ) ) . ' )', OBJECT );
+ }
+
+ /**
+ * Unserialize the value of a meta object, if necessary.
+ *
+ * @access public
+ *
+ * @param object $meta Meta object.
+ * @return object Meta object with possibly unserialized value.
+ */
+ public function unserialize_meta( $meta ) {
+ $meta->meta_value = maybe_unserialize( $meta->meta_value );
+ return $meta;
+ }
+
+ /**
+ * Retrieve a set of objects by their IDs.
+ *
+ * @access public
+ *
+ * @param string $object_type Object type.
+ * @param array $ids Object IDs.
+ * @return array Array of objects.
+ */
+ public function get_objects_by_id( $object_type, $ids ) {
+ if ( empty( $ids ) || empty( $object_type ) ) {
+ return array();
+ }
+
+ $objects = array();
+ foreach ( (array) $ids as $id ) {
+ $object = $this->get_object_by_id( $object_type, $id );
+
+ // Only add object if we have the object.
+ if ( $object ) {
+ $objects[ $id ] = $object;
+ }
+ }
+
+ return $objects;
+ }
+
+ /**
+ * Gets a list of minimum and maximum object ids for each batch based on the given batch size.
+ *
+ * @access public
+ *
+ * @param int $batch_size The batch size for objects.
+ * @param string|bool $where_sql The sql where clause minus 'WHERE', or false if no where clause is needed.
+ *
+ * @return array|bool An array of min and max ids for each batch. FALSE if no table can be found.
+ */
+ public function get_min_max_object_ids_for_batches( $batch_size, $where_sql = false ) {
+ global $wpdb;
+
+ if ( ! $this->table_name() ) {
+ return false;
+ }
+
+ $results = array();
+ $table = $wpdb->{$this->table_name()};
+ $current_max = 0;
+ $current_min = 1;
+ $id_field = $this->id_field();
+ $replicastore = new Replicastore();
+
+ $total = $replicastore->get_min_max_object_id(
+ $id_field,
+ $table,
+ $where_sql,
+ false
+ );
+
+ while ( $total->max > $current_max ) {
+ $where = $where_sql ?
+ $where_sql . " AND $id_field > $current_max" :
+ "$id_field > $current_max";
+ $result = $replicastore->get_min_max_object_id(
+ $id_field,
+ $table,
+ $where,
+ $batch_size
+ );
+ if ( empty( $result->min ) && empty( $result->max ) ) {
+ // Our query produced no min and max. We can assume the min from the previous query,
+ // and the total max we found in the initial query.
+ $current_max = (int) $total->max;
+ $result = (object) array(
+ 'min' => $current_min,
+ 'max' => $current_max,
+ );
+ } else {
+ $current_min = (int) $result->min;
+ $current_max = (int) $result->max;
+ }
+ $results[] = $result;
+ }
+
+ return $results;
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) {
+ global $wpdb;
+ $table = $wpdb->{$this->table_name()};
+ $where = $this->get_where_sql( $config );
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ return $wpdb->get_var( "SELECT COUNT(*) FROM $table WHERE $where" );
+ }
+
+ /**
+ * Retrieve the WHERE SQL clause based on the module config.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return string WHERE SQL clause, or `null` if no comments are specified in the module config.
+ */
+ public function get_where_sql( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return '1=1';
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-network-options.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-network-options.php
new file mode 100644
index 00000000..defa700e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-network-options.php
@@ -0,0 +1,252 @@
+<?php
+/**
+ * Network Options sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Sync\Defaults;
+
+/**
+ * Class to handle sync for network options.
+ */
+class Network_Options extends Module {
+ /**
+ * Whitelist for network options we want to sync.
+ *
+ * @access private
+ *
+ * @var array
+ */
+ private $network_options_whitelist;
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'network_options';
+ }
+
+ /**
+ * Initialize network options action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ // Multi site network options.
+ add_action( 'add_site_option', $callable, 10, 2 );
+ add_action( 'update_site_option', $callable, 10, 3 );
+ add_action( 'delete_site_option', $callable, 10, 1 );
+
+ $whitelist_network_option_handler = array( $this, 'whitelist_network_options' );
+ add_filter( 'jetpack_sync_before_enqueue_delete_site_option', $whitelist_network_option_handler );
+ add_filter( 'jetpack_sync_before_enqueue_add_site_option', $whitelist_network_option_handler );
+ add_filter( 'jetpack_sync_before_enqueue_update_site_option', $whitelist_network_option_handler );
+ }
+
+ /**
+ * Initialize network options action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_network_options', $callable );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ // Full sync.
+ add_filter(
+ 'jetpack_sync_before_send_jetpack_full_sync_network_options',
+ array(
+ $this,
+ 'expand_network_options',
+ )
+ );
+ }
+
+ /**
+ * Set module defaults.
+ * Define the network options whitelist based on the default one.
+ *
+ * @access public
+ */
+ public function set_defaults() {
+ $this->network_options_whitelist = Defaults::$default_network_options_whitelist;
+ }
+
+ /**
+ * Enqueue the network options actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ /**
+ * Tells the client to sync all options to the server
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param boolean Whether to expand options (should always be true)
+ */
+ do_action( 'jetpack_full_sync_network_options', true );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 1, true );
+ }
+
+ /**
+ * Send the network options actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $send_until The timestamp until the current request can send.
+ * @param array $state This module Full Sync status.
+ *
+ * @return array This module Full Sync status.
+ */
+ public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // we call this instead of do_action when sending immediately.
+ $this->send_action( 'jetpack_full_sync_network_options', array( true ) );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 'finished' => true );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_network_options' );
+ }
+
+ /**
+ * Retrieve all network options as per the current network options whitelist.
+ *
+ * @access public
+ *
+ * @return array All network options.
+ */
+ public function get_all_network_options() {
+ $options = array();
+ foreach ( $this->network_options_whitelist as $option ) {
+ $options[ $option ] = get_site_option( $option );
+ }
+
+ return $options;
+ }
+
+ /**
+ * Set the network options whitelist.
+ *
+ * @access public
+ *
+ * @param array $options The new network options whitelist.
+ */
+ public function set_network_options_whitelist( $options ) {
+ $this->network_options_whitelist = $options;
+ }
+
+ /**
+ * Get the network options whitelist.
+ *
+ * @access public
+ *
+ * @return array The network options whitelist.
+ */
+ public function get_network_options_whitelist() {
+ return $this->network_options_whitelist;
+ }
+
+ /**
+ * Reject non-whitelisted network options.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array|false $args The hook parameters, false if not a whitelisted network option.
+ */
+ public function whitelist_network_options( $args ) {
+ if ( ! $this->is_whitelisted_network_option( $args[0] ) ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Whether the option is a whitelisted network option.
+ *
+ * @access public
+ *
+ * @param string $option Option name.
+ * @return boolean True if this is a whitelisted network option.
+ */
+ public function is_whitelisted_network_option( $option ) {
+ return in_array( $option, $this->network_options_whitelist, true );
+ }
+
+ /**
+ * Expand the network options within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The hook parameters.
+ */
+ public function expand_network_options( $args ) {
+ if ( $args[0] ) {
+ return $this->get_all_network_options();
+ }
+
+ return $args;
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return count( $this->network_options_whitelist );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-options.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-options.php
new file mode 100644
index 00000000..5c156512
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-options.php
@@ -0,0 +1,481 @@
+<?php
+/**
+ * Options sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Sync\Defaults;
+use Automattic\Jetpack\Sync\Settings;
+
+/**
+ * Class to handle sync for options.
+ */
+class Options extends Module {
+ /**
+ * Whitelist for options we want to sync.
+ *
+ * @access private
+ *
+ * @var array
+ */
+ private $options_whitelist;
+
+ /**
+ * Contentless options we want to sync.
+ *
+ * @access private
+ *
+ * @var array
+ */
+ private $options_contentless;
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'options';
+ }
+
+ /**
+ * Initialize options action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ // Options.
+ add_action( 'added_option', $callable, 10, 2 );
+ add_action( 'updated_option', $callable, 10, 3 );
+ add_action( 'deleted_option', $callable, 10, 1 );
+
+ // Sync Core Icon: Detect changes in Core's Site Icon and make it syncable.
+ add_action( 'add_option_site_icon', array( $this, 'jetpack_sync_core_icon' ) );
+ add_action( 'update_option_site_icon', array( $this, 'jetpack_sync_core_icon' ) );
+ add_action( 'delete_option_site_icon', array( $this, 'jetpack_sync_core_icon' ) );
+
+ // Handle deprecated options.
+ add_filter( 'jetpack_options_whitelist', array( $this, 'add_deprecated_options' ) );
+
+ $whitelist_option_handler = array( $this, 'whitelist_options' );
+ add_filter( 'jetpack_sync_before_enqueue_deleted_option', $whitelist_option_handler );
+ add_filter( 'jetpack_sync_before_enqueue_added_option', $whitelist_option_handler );
+ add_filter( 'jetpack_sync_before_enqueue_updated_option', $whitelist_option_handler );
+ }
+
+ /**
+ * Initialize options action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_options', $callable );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_options', array( $this, 'expand_options' ) );
+ }
+
+ /**
+ * Set module defaults.
+ * Define the options whitelist and contentless options.
+ *
+ * @access public
+ */
+ public function set_defaults() {
+ $this->update_options_whitelist();
+ $this->update_options_contentless();
+ }
+
+ /**
+ * Set module defaults at a later time.
+ *
+ * @access public
+ */
+ public function set_late_default() {
+ /** This filter is already documented in json-endpoints/jetpack/class.wpcom-json-api-get-option-endpoint.php */
+ $late_options = apply_filters( 'jetpack_options_whitelist', array() );
+ if ( ! empty( $late_options ) && is_array( $late_options ) ) {
+ $this->options_whitelist = array_merge( $this->options_whitelist, $late_options );
+ }
+ }
+
+ /**
+ * Add old deprecated options to the list of options to keep in sync.
+ *
+ * @since 1.14.0
+ *
+ * @access public
+ *
+ * @param array $options The default list of site options.
+ */
+ public function add_deprecated_options( $options ) {
+ global $wp_version;
+
+ $deprecated_options = array(
+ 'blacklist_keys' => '5.5-alpha', // Replaced by disallowed_keys.
+ 'comment_whitelist' => '5.5-alpha', // Replaced by comment_previously_approved.
+ );
+
+ foreach ( $deprecated_options as $option => $version ) {
+ if ( version_compare( $wp_version, $version, '<=' ) ) {
+ $options[] = $option;
+ }
+ }
+
+ return $options;
+ }
+
+ /**
+ * Enqueue the options actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ /**
+ * Tells the client to sync all options to the server
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param boolean Whether to expand options (should always be true)
+ */
+ do_action( 'jetpack_full_sync_options', true );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 1, true );
+ }
+
+ /**
+ * Send the options actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $send_until The timestamp until the current request can send.
+ * @param array $state This module Full Sync status.
+ *
+ * @return array This module Full Sync status.
+ */
+ public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // we call this instead of do_action when sending immediately.
+ $this->send_action( 'jetpack_full_sync_options', array( true ) );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 'finished' => true );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return int Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_options' );
+ }
+
+ /**
+ * Retrieve all options as per the current options whitelist.
+ * Public so that we don't have to store so much data all the options twice.
+ *
+ * @access public
+ *
+ * @return array All options.
+ */
+ public function get_all_options() {
+ $options = array();
+ $random_string = wp_generate_password();
+ foreach ( $this->options_whitelist as $option ) {
+ if ( 0 === strpos( $option, Settings::SETTINGS_OPTION_PREFIX ) ) {
+ $option_value = Settings::get_setting( str_replace( Settings::SETTINGS_OPTION_PREFIX, '', $option ) );
+ $options[ $option ] = $option_value;
+ } else {
+ $option_value = get_option( $option, $random_string );
+ if ( $option_value !== $random_string ) {
+ $options[ $option ] = $option_value;
+ }
+ }
+ }
+
+ // Add theme mods.
+ $theme_mods_option = 'theme_mods_' . get_option( 'stylesheet' );
+ $theme_mods_value = get_option( $theme_mods_option, $random_string );
+ if ( $theme_mods_value === $random_string ) {
+ return $options;
+ }
+ $this->filter_theme_mods( $theme_mods_value );
+ $options[ $theme_mods_option ] = $theme_mods_value;
+ return $options;
+ }
+
+ /**
+ * Update the options whitelist to the default one.
+ *
+ * @access public
+ */
+ public function update_options_whitelist() {
+ $this->options_whitelist = Defaults::get_options_whitelist();
+ }
+
+ /**
+ * Set the options whitelist.
+ *
+ * @access public
+ *
+ * @param array $options The new options whitelist.
+ */
+ public function set_options_whitelist( $options ) {
+ $this->options_whitelist = $options;
+ }
+
+ /**
+ * Get the options whitelist.
+ *
+ * @access public
+ *
+ * @return array The options whitelist.
+ */
+ public function get_options_whitelist() {
+ return $this->options_whitelist;
+ }
+
+ /**
+ * Update the contentless options to the defaults.
+ *
+ * @access public
+ */
+ public function update_options_contentless() {
+ $this->options_contentless = Defaults::get_options_contentless();
+ }
+
+ /**
+ * Get the contentless options.
+ *
+ * @access public
+ *
+ * @return array Array of the contentless options.
+ */
+ public function get_options_contentless() {
+ return $this->options_contentless;
+ }
+
+ /**
+ * Reject any options that aren't whitelisted or contentless.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The hook parameters.
+ */
+ public function whitelist_options( $args ) {
+ // Reject non-whitelisted options.
+ if ( ! $this->is_whitelisted_option( $args[0] ) ) {
+ return false;
+ }
+
+ // Filter our weird array( false ) value for theme_mods_*.
+ if ( 'theme_mods_' === substr( $args[0], 0, 11 ) ) {
+ $this->filter_theme_mods( $args[1] );
+ if ( isset( $args[2] ) ) {
+ $this->filter_theme_mods( $args[2] );
+ }
+ }
+
+ // Set value(s) of contentless option to empty string(s).
+ if ( $this->is_contentless_option( $args[0] ) ) {
+ // Create a new array matching length of $args, containing empty strings.
+ $empty = array_fill( 0, count( $args ), '' );
+ $empty[0] = $args[0];
+ return $empty;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Whether a certain option is whitelisted for sync.
+ *
+ * @access public
+ *
+ * @param string $option Option name.
+ * @return boolean Whether the option is whitelisted.
+ */
+ public function is_whitelisted_option( $option ) {
+ return in_array( $option, $this->options_whitelist, true ) || 'theme_mods_' === substr( $option, 0, 11 );
+ }
+
+ /**
+ * Whether a certain option is a contentless one.
+ *
+ * @access private
+ *
+ * @param string $option Option name.
+ * @return boolean Whether the option is contentless.
+ */
+ private function is_contentless_option( $option ) {
+ return in_array( $option, $this->options_contentless, true );
+ }
+
+ /**
+ * Filters out falsy values from theme mod options.
+ *
+ * @access private
+ *
+ * @param array $value Option value.
+ */
+ private function filter_theme_mods( &$value ) {
+ if ( is_array( $value ) && isset( $value[0] ) ) {
+ unset( $value[0] );
+ }
+ }
+
+ /**
+ * Handle changes in the core site icon and sync them.
+ *
+ * @access public
+ */
+ public function jetpack_sync_core_icon() {
+ $url = get_site_icon_url();
+
+ $jetpack_url = \Jetpack_Options::get_option( 'site_icon_url' );
+ if ( defined( 'JETPACK__PLUGIN_DIR' ) ) {
+ if ( ! function_exists( 'jetpack_site_icon_url' ) ) {
+ require_once JETPACK__PLUGIN_DIR . 'modules/site-icon/site-icon-functions.php';
+ }
+ $jetpack_url = jetpack_site_icon_url();
+ }
+
+ // If there's a core icon, maybe update the option. If not, fall back to Jetpack's.
+ if ( ! empty( $url ) && $jetpack_url !== $url ) {
+ // This is the option that is synced with dotcom.
+ \Jetpack_Options::update_option( 'site_icon_url', $url );
+ } elseif ( empty( $url ) ) {
+ \Jetpack_Options::delete_option( 'site_icon_url' );
+ }
+ }
+
+ /**
+ * Expand all options within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The hook parameters.
+ */
+ public function expand_options( $args ) {
+ if ( $args[0] ) {
+ return $this->get_all_options();
+ }
+
+ return $args;
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return count( Defaults::get_options_whitelist() );
+ }
+
+ /**
+ * Retrieve a set of options by their IDs.
+ *
+ * @access public
+ *
+ * @param string $object_type Object type.
+ * @param array $ids Object IDs.
+ * @return array Array of objects.
+ */
+ public function get_objects_by_id( $object_type, $ids ) {
+ if ( empty( $ids ) || empty( $object_type ) || 'option' !== $object_type ) {
+ return array();
+ }
+
+ $objects = array();
+ foreach ( (array) $ids as $id ) {
+ $object = $this->get_object_by_id( $object_type, $id );
+
+ // Only add object if we have the object.
+ if ( 'OPTION-DOES-NOT-EXIST' !== $object ) {
+ if ( 'all' === $id ) {
+ // If all was requested it contains all options and can simply be returned.
+ return $object;
+ }
+ $objects[ $id ] = $object;
+ }
+ }
+
+ return $objects;
+ }
+
+ /**
+ * Retrieve an option by its name.
+ *
+ * @access public
+ *
+ * @param string $object_type Type of the sync object.
+ * @param string $id ID of the sync object.
+ * @return mixed Value of Option or 'OPTION-DOES-NOT-EXIST' if not found.
+ */
+ public function get_object_by_id( $object_type, $id ) {
+ if ( 'option' === $object_type ) {
+ // Utilize Random string as default value to distinguish between false and not exist.
+ $random_string = wp_generate_password();
+ // Only whitelisted options can be returned.
+ if ( in_array( $id, $this->options_whitelist, true ) ) {
+ if ( 0 === strpos( $id, Settings::SETTINGS_OPTION_PREFIX ) ) {
+ $option_value = Settings::get_setting( str_replace( Settings::SETTINGS_OPTION_PREFIX, '', $id ) );
+ return $option_value;
+ } else {
+ $option_value = get_option( $id, $random_string );
+ if ( $option_value !== $random_string ) {
+ return $option_value;
+ }
+ }
+ } elseif ( 'all' === $id ) {
+ return $this->get_all_options();
+ }
+ }
+
+ return 'OPTION-DOES-NOT-EXIST';
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-plugins.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-plugins.php
new file mode 100644
index 00000000..b244834f
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-plugins.php
@@ -0,0 +1,420 @@
+<?php
+/**
+ * Plugins sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Constants as Jetpack_Constants;
+
+/**
+ * Class to handle sync for plugins.
+ */
+class Plugins extends Module {
+ /**
+ * Action handler callable.
+ *
+ * @access private
+ *
+ * @var callable
+ */
+ private $action_handler;
+
+ /**
+ * Information about plugins we store temporarily.
+ *
+ * @access private
+ *
+ * @var array
+ */
+ private $plugin_info = array();
+
+ /**
+ * List of all plugins in the installation.
+ *
+ * @access private
+ *
+ * @var array
+ */
+ private $plugins = array();
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'plugins';
+ }
+
+ /**
+ * Initialize plugins action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ $this->action_handler = $callable;
+
+ add_action( 'deleted_plugin', array( $this, 'deleted_plugin' ), 10, 2 );
+ add_action( 'activated_plugin', $callable, 10, 2 );
+ add_action( 'deactivated_plugin', $callable, 10, 2 );
+ add_action( 'delete_plugin', array( $this, 'delete_plugin' ) );
+ add_filter( 'upgrader_pre_install', array( $this, 'populate_plugins' ), 10, 1 );
+ add_action( 'upgrader_process_complete', array( $this, 'on_upgrader_completion' ), 10, 2 );
+ add_action( 'jetpack_plugin_installed', $callable, 10, 1 );
+ add_action( 'jetpack_plugin_update_failed', $callable, 10, 4 );
+ add_action( 'jetpack_plugins_updated', $callable, 10, 2 );
+ add_action( 'admin_action_update', array( $this, 'check_plugin_edit' ) );
+ add_action( 'jetpack_edited_plugin', $callable, 10, 2 );
+ add_action( 'wp_ajax_edit-theme-plugin-file', array( $this, 'plugin_edit_ajax' ), 0 );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_filter( 'jetpack_sync_before_send_activated_plugin', array( $this, 'expand_plugin_data' ) );
+ add_filter( 'jetpack_sync_before_send_deactivated_plugin', array( $this, 'expand_plugin_data' ) );
+ // Note that we don't simply 'expand_plugin_data' on the 'delete_plugin' action here because the plugin file is deleted when that action finishes.
+ }
+
+ /**
+ * Fetch and populate all current plugins before upgrader installation.
+ *
+ * @access public
+ *
+ * @param bool|WP_Error $response Install response, true if successful, WP_Error if not.
+ */
+ public function populate_plugins( $response ) {
+ if ( ! function_exists( 'get_plugins' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
+ }
+ $this->plugins = get_plugins();
+ return $response;
+ }
+
+ /**
+ * Handler for the upgrader success finishes.
+ *
+ * @access public
+ *
+ * @param \WP_Upgrader $upgrader Upgrader instance.
+ * @param array $details Array of bulk item update data.
+ */
+ public function on_upgrader_completion( $upgrader, $details ) {
+ if ( ! isset( $details['type'] ) ) {
+ return;
+ }
+ if ( 'plugin' !== $details['type'] ) {
+ return;
+ }
+
+ if ( ! isset( $details['action'] ) ) {
+ return;
+ }
+
+ $plugins = ( isset( $details['plugins'] ) ? $details['plugins'] : null );
+ if ( empty( $plugins ) ) {
+ $plugins = ( isset( $details['plugin'] ) ? array( $details['plugin'] ) : null );
+ }
+
+ // For plugin installer.
+ if ( empty( $plugins ) && method_exists( $upgrader, 'plugin_info' ) ) {
+ $plugins = array( $upgrader->plugin_info() );
+ }
+
+ if ( empty( $plugins ) ) {
+ return; // We shouldn't be here.
+ }
+
+ switch ( $details['action'] ) {
+ case 'update':
+ $state = array(
+ 'is_autoupdate' => Jetpack_Constants::is_true( 'JETPACK_PLUGIN_AUTOUPDATE' ),
+ );
+ $errors = $this->get_errors( $upgrader->skin );
+ if ( $errors ) {
+ foreach ( $plugins as $slug ) {
+ /**
+ * Sync that a plugin update failed
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.8.0
+ *
+ * @module sync
+ *
+ * @param string $plugin , Plugin slug
+ * @param string Error code
+ * @param string Error message
+ */
+ do_action( 'jetpack_plugin_update_failed', $this->get_plugin_info( $slug ), $errors['code'], $errors['message'], $state );
+ }
+
+ return;
+ }
+ /**
+ * Sync that a plugin update
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.8.0
+ *
+ * @module sync
+ *
+ * @param array () $plugin, Plugin Data
+ */
+ do_action( 'jetpack_plugins_updated', array_map( array( $this, 'get_plugin_info' ), $plugins ), $state );
+ break;
+ case 'install':
+ }
+
+ if ( 'install' === $details['action'] ) {
+ /**
+ * Signals to the sync listener that a plugin was installed and a sync action
+ * reflecting the installation and the plugin info should be sent
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.8.0
+ *
+ * @module sync
+ *
+ * @param array () $plugin, Plugin Data
+ */
+ do_action( 'jetpack_plugin_installed', array_map( array( $this, 'get_plugin_info' ), $plugins ) );
+
+ return;
+ }
+ }
+
+ /**
+ * Retrieve the plugin information by a plugin slug.
+ *
+ * @access private
+ *
+ * @param string $slug Plugin slug.
+ * @return array Plugin information.
+ */
+ private function get_plugin_info( $slug ) {
+ $plugins = get_plugins(); // Get the most up to date info.
+ if ( isset( $plugins[ $slug ] ) ) {
+ return array_merge( array( 'slug' => $slug ), $plugins[ $slug ] );
+ };
+ // Try grabbing the info from before the update.
+ return isset( $this->plugins[ $slug ] ) ? array_merge( array( 'slug' => $slug ), $this->plugins[ $slug ] ) : array( 'slug' => $slug );
+ }
+
+ /**
+ * Retrieve upgrade errors.
+ *
+ * @access private
+ *
+ * @param \Automatic_Upgrader_Skin|\WP_Upgrader_Skin $skin The upgrader skin being used.
+ * @return array|boolean Error on error, false otherwise.
+ */
+ private function get_errors( $skin ) {
+ $errors = method_exists( $skin, 'get_errors' ) ? $skin->get_errors() : null;
+ if ( is_wp_error( $errors ) ) {
+ $error_code = $errors->get_error_code();
+ if ( ! empty( $error_code ) ) {
+ return array(
+ 'code' => $error_code,
+ 'message' => $errors->get_error_message(),
+ );
+ }
+ }
+
+ if ( isset( $skin->result ) ) {
+ $errors = $skin->result;
+ if ( is_wp_error( $errors ) ) {
+ return array(
+ 'code' => $errors->get_error_code(),
+ 'message' => $errors->get_error_message(),
+ );
+ }
+
+ if ( empty( $skin->result ) ) {
+ return array(
+ 'code' => 'unknown',
+ 'message' => __( 'Unknown Plugin Update Failure', 'jetpack-sync' ),
+ );
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Handle plugin edit in the administration.
+ *
+ * @access public
+ *
+ * @todo The `admin_action_update` hook is called only for logged in users, but maybe implement nonce verification?
+ */
+ public function check_plugin_edit() {
+ $screen = get_current_screen();
+ // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ if ( 'plugin-editor' !== $screen->base || ! isset( $_POST['newcontent'] ) || ! isset( $_POST['plugin'] ) ) {
+ return;
+ }
+
+ // phpcs:ignore WordPress.Security.NonceVerification.Missing
+ $plugin = $_POST['plugin'];
+ $plugins = get_plugins();
+ if ( ! isset( $plugins[ $plugin ] ) ) {
+ return;
+ }
+
+ /**
+ * Helps Sync log that a plugin was edited
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.9.0
+ *
+ * @param string $plugin, Plugin slug
+ * @param mixed $plugins[ $plugin ], Array of plugin data
+ */
+ do_action( 'jetpack_edited_plugin', $plugin, $plugins[ $plugin ] );
+ }
+
+ /**
+ * Handle plugin ajax edit in the administration.
+ *
+ * @access public
+ *
+ * @todo Update this method to use WP_Filesystem instead of fopen/fclose.
+ */
+ public function plugin_edit_ajax() {
+ // This validation is based on wp_edit_theme_plugin_file().
+ $args = wp_unslash( $_POST );
+ if ( empty( $args['file'] ) ) {
+ return;
+ }
+
+ $file = $args['file'];
+ if ( 0 !== validate_file( $file ) ) {
+ return;
+ }
+
+ if ( ! isset( $args['newcontent'] ) ) {
+ return;
+ }
+
+ if ( ! isset( $args['nonce'] ) ) {
+ return;
+ }
+
+ if ( empty( $args['plugin'] ) ) {
+ return;
+ }
+
+ $plugin = $args['plugin'];
+ if ( ! current_user_can( 'edit_plugins' ) ) {
+ return;
+ }
+
+ if ( ! wp_verify_nonce( $args['nonce'], 'edit-plugin_' . $file ) ) {
+ return;
+ }
+ $plugins = get_plugins();
+ if ( ! array_key_exists( $plugin, $plugins ) ) {
+ return;
+ }
+
+ if ( 0 !== validate_file( $file, get_plugin_files( $plugin ) ) ) {
+ return;
+ }
+
+ $real_file = WP_PLUGIN_DIR . '/' . $file;
+
+ if ( ! is_writeable( $real_file ) ) {
+ return;
+ }
+
+ // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
+ $file_pointer = fopen( $real_file, 'w+' );
+ if ( false === $file_pointer ) {
+ return;
+ }
+ // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
+ fclose( $file_pointer );
+ /**
+ * This action is documented already in this file
+ */
+ do_action( 'jetpack_edited_plugin', $plugin, $plugins[ $plugin ] );
+ }
+
+ /**
+ * Handle plugin deletion.
+ *
+ * @access public
+ *
+ * @param string $plugin_path Path to the plugin main file.
+ */
+ public function delete_plugin( $plugin_path ) {
+ $full_plugin_path = WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $plugin_path;
+
+ // Checking for file existence because some sync plugin module tests simulate plugin installation and deletion without putting file on disk.
+ if ( file_exists( $full_plugin_path ) ) {
+ $all_plugin_data = get_plugin_data( $full_plugin_path );
+ $data = array(
+ 'name' => $all_plugin_data['Name'],
+ 'version' => $all_plugin_data['Version'],
+ );
+ } else {
+ $data = array(
+ 'name' => $plugin_path,
+ 'version' => 'unknown',
+ );
+ }
+
+ $this->plugin_info[ $plugin_path ] = $data;
+ }
+
+ /**
+ * Invoked after plugin deletion.
+ *
+ * @access public
+ *
+ * @param string $plugin_path Path to the plugin main file.
+ * @param boolean $is_deleted Whether the plugin was deleted successfully.
+ */
+ public function deleted_plugin( $plugin_path, $is_deleted ) {
+ call_user_func( $this->action_handler, $plugin_path, $is_deleted, $this->plugin_info[ $plugin_path ] );
+ unset( $this->plugin_info[ $plugin_path ] );
+ }
+
+ /**
+ * Expand the plugins within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The expanded hook parameters.
+ */
+ public function expand_plugin_data( $args ) {
+ $plugin_path = $args[0];
+ $plugin_data = array();
+
+ if ( ! function_exists( 'get_plugins' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
+ }
+ $all_plugins = get_plugins();
+ if ( isset( $all_plugins[ $plugin_path ] ) ) {
+ $all_plugin_data = $all_plugins[ $plugin_path ];
+ $plugin_data['name'] = $all_plugin_data['Name'];
+ $plugin_data['version'] = $all_plugin_data['Version'];
+ }
+
+ return array(
+ $args[0],
+ $args[1],
+ $plugin_data,
+ );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-posts.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-posts.php
new file mode 100644
index 00000000..b9ea21d1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-posts.php
@@ -0,0 +1,771 @@
+<?php
+/**
+ * Posts sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Constants as Jetpack_Constants;
+use Automattic\Jetpack\Roles;
+use Automattic\Jetpack\Sync\Settings;
+
+/**
+ * Class to handle sync for posts.
+ */
+class Posts extends Module {
+ /**
+ * The post IDs of posts that were just published but not synced yet.
+ *
+ * @access private
+ *
+ * @var array
+ */
+ private $just_published = array();
+
+ /**
+ * The previous status of posts that we use for calculating post status transitions.
+ *
+ * @access private
+ *
+ * @var array
+ */
+ private $previous_status = array();
+
+ /**
+ * Action handler callable.
+ *
+ * @access private
+ *
+ * @var callable
+ */
+ private $action_handler;
+
+ /**
+ * Import end.
+ *
+ * @access private
+ *
+ * @todo This appears to be unused - let's remove it.
+ *
+ * @var boolean
+ */
+ private $import_end = false;
+
+ /**
+ * Max bytes allowed for post_content => length.
+ * Current Setting : 5MB.
+ *
+ * @access public
+ *
+ * @var int
+ */
+ const MAX_POST_CONTENT_LENGTH = 5000000;
+
+ /**
+ * Max bytes allowed for post meta_value => length.
+ * Current Setting : 2MB.
+ *
+ * @access public
+ *
+ * @var int
+ */
+ const MAX_POST_META_LENGTH = 2000000;
+
+ /**
+ * Default previous post state.
+ * Used for default previous post status.
+ *
+ * @access public
+ *
+ * @var string
+ */
+ const DEFAULT_PREVIOUS_STATE = 'new';
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'posts';
+ }
+
+ /**
+ * The table in the database.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function table_name() {
+ return 'posts';
+ }
+
+ /**
+ * Retrieve a post by its ID.
+ *
+ * @access public
+ *
+ * @param string $object_type Type of the sync object.
+ * @param int $id ID of the sync object.
+ * @return \WP_Post|bool Filtered \WP_Post object, or false if the object is not a post.
+ */
+ public function get_object_by_id( $object_type, $id ) {
+ if ( 'post' === $object_type ) {
+ $post = get_post( (int) $id );
+ if ( $post ) {
+ return $this->filter_post_content_and_add_links( $post );
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Initialize posts action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ $this->action_handler = $callable;
+
+ add_action( 'wp_insert_post', array( $this, 'wp_insert_post' ), 11, 3 );
+ add_action( 'wp_after_insert_post', array( $this, 'wp_after_insert_post' ), 11, 2 );
+ add_action( 'jetpack_sync_save_post', $callable, 10, 4 );
+
+ add_action( 'deleted_post', $callable, 10 );
+ add_action( 'jetpack_published_post', $callable, 10, 2 );
+ add_filter( 'jetpack_sync_before_enqueue_deleted_post', array( $this, 'filter_blacklisted_post_types_deleted' ) );
+
+ add_action( 'transition_post_status', array( $this, 'save_published' ), 10, 3 );
+ add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_save_post', array( $this, 'filter_blacklisted_post_types' ) );
+
+ // Listen for meta changes.
+ $this->init_listeners_for_meta_type( 'post', $callable );
+ $this->init_meta_whitelist_handler( 'post', array( $this, 'filter_meta' ) );
+
+ add_action( 'jetpack_daily_akismet_meta_cleanup_before', array( $this, 'daily_akismet_meta_cleanup_before' ) );
+ add_action( 'jetpack_daily_akismet_meta_cleanup_after', array( $this, 'daily_akismet_meta_cleanup_after' ) );
+ add_action( 'jetpack_post_meta_batch_delete', $callable, 10, 2 );
+ }
+
+ /**
+ * Before Akismet's daily cleanup of spam detection metadata.
+ *
+ * @access public
+ *
+ * @param array $feedback_ids IDs of feedback posts.
+ */
+ public function daily_akismet_meta_cleanup_before( $feedback_ids ) {
+ remove_action( 'deleted_post_meta', $this->action_handler );
+
+ if ( ! is_array( $feedback_ids ) || count( $feedback_ids ) < 1 ) {
+ return;
+ }
+
+ $ids_chunks = array_chunk( $feedback_ids, 100, false );
+ foreach ( $ids_chunks as $chunk ) {
+ /**
+ * Used for syncing deletion of batch post meta
+ *
+ * @since 1.6.3
+ * @since-jetpack 6.1.0
+ *
+ * @module sync
+ *
+ * @param array $feedback_ids feedback post IDs
+ * @param string $meta_key to be deleted
+ */
+ do_action( 'jetpack_post_meta_batch_delete', $chunk, '_feedback_akismet_values' );
+ }
+ }
+
+ /**
+ * After Akismet's daily cleanup of spam detection metadata.
+ *
+ * @access public
+ *
+ * @param array $feedback_ids IDs of feedback posts.
+ */
+ public function daily_akismet_meta_cleanup_after( $feedback_ids ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ add_action( 'deleted_post_meta', $this->action_handler );
+ }
+
+ /**
+ * Initialize posts action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_posts', $callable ); // Also sends post meta.
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_filter( 'jetpack_sync_before_send_jetpack_sync_save_post', array( $this, 'expand_jetpack_sync_save_post' ) );
+
+ // meta.
+ add_filter( 'jetpack_sync_before_send_added_post_meta', array( $this, 'trim_post_meta' ) );
+ add_filter( 'jetpack_sync_before_send_updated_post_meta', array( $this, 'trim_post_meta' ) );
+ add_filter( 'jetpack_sync_before_send_deleted_post_meta', array( $this, 'trim_post_meta' ) );
+
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_posts', array( $this, 'expand_post_ids' ) );
+ }
+
+ /**
+ * Enqueue the posts actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
+ global $wpdb;
+
+ return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_posts', $wpdb->posts, 'ID', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @todo Use $wpdb->prepare for the SQL query.
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) {
+ global $wpdb;
+
+ $query = "SELECT count(*) FROM $wpdb->posts WHERE " . $this->get_where_sql( $config );
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $count = $wpdb->get_var( $query );
+
+ return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
+ }
+
+ /**
+ * Retrieve the WHERE SQL clause based on the module config.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return string WHERE SQL clause, or `null` if no comments are specified in the module config.
+ */
+ public function get_where_sql( $config ) {
+ $where_sql = Settings::get_blacklisted_post_types_sql();
+
+ // Config is a list of post IDs to sync.
+ if ( is_array( $config ) ) {
+ $where_sql .= ' AND ID IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
+ }
+
+ return $where_sql;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_posts' );
+ }
+
+ /**
+ * Filter meta arguments so that we don't sync meta_values over MAX_POST_META_LENGTH.
+ *
+ * @param array $args action arguments.
+ *
+ * @return array filtered action arguments.
+ */
+ public function trim_post_meta( $args ) {
+ list( $meta_id, $object_id, $meta_key, $meta_value ) = $args;
+ // Explicitly truncate meta_value when it exceeds limit.
+ // Large content will cause OOM issues and break Sync.
+ $serialized_value = maybe_serialize( $meta_value );
+ if ( strlen( $serialized_value ) >= self::MAX_POST_META_LENGTH ) {
+ $meta_value = '';
+ }
+ return array( $meta_id, $object_id, $meta_key, $meta_value );
+ }
+
+ /**
+ * Process content before send.
+ *
+ * @param array $args Arguments of the `wp_insert_post` hook.
+ *
+ * @return array
+ */
+ public function expand_jetpack_sync_save_post( $args ) {
+ list( $post_id, $post, $update, $previous_state ) = $args;
+ return array( $post_id, $this->filter_post_content_and_add_links( $post ), $update, $previous_state );
+ }
+
+ /**
+ * Filter all blacklisted post types.
+ *
+ * @param array $args Hook arguments.
+ * @return array|false Hook arguments, or false if the post type is a blacklisted one.
+ */
+ public function filter_blacklisted_post_types_deleted( $args ) {
+
+ // deleted_post is called after the SQL delete but before cache cleanup.
+ // There is the potential we can't detect post_type at this point.
+ if ( ! $this->is_post_type_allowed( $args[0] ) ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Filter all blacklisted post types.
+ *
+ * @param array $args Hook arguments.
+ * @return array|false Hook arguments, or false if the post type is a blacklisted one.
+ */
+ public function filter_blacklisted_post_types( $args ) {
+ $post = $args[1];
+
+ if ( in_array( $post->post_type, Settings::get_setting( 'post_types_blacklist' ), true ) ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Filter all meta that is not blacklisted, or is stored for a disallowed post type.
+ *
+ * @param array $args Hook arguments.
+ * @return array|false Hook arguments, or false if meta was filtered.
+ */
+ public function filter_meta( $args ) {
+ if ( $this->is_post_type_allowed( $args[1] ) && $this->is_whitelisted_post_meta( $args[2] ) ) {
+ return $args;
+ }
+
+ return false;
+ }
+
+ /**
+ * Whether a post meta key is whitelisted.
+ *
+ * @param string $meta_key Meta key.
+ * @return boolean Whether the post meta key is whitelisted.
+ */
+ public function is_whitelisted_post_meta( $meta_key ) {
+ // The _wpas_skip_ meta key is used by Publicize.
+ return in_array( $meta_key, Settings::get_setting( 'post_meta_whitelist' ), true ) || ( 0 === strpos( $meta_key, '_wpas_skip_' ) );
+ }
+
+ /**
+ * Whether a post type is allowed.
+ * A post type will be disallowed if it's present in the post type blacklist.
+ *
+ * @param int $post_id ID of the post.
+ * @return boolean Whether the post type is allowed.
+ */
+ public function is_post_type_allowed( $post_id ) {
+ $post = get_post( (int) $post_id );
+
+ if ( isset( $post->post_type ) ) {
+ return ! in_array( $post->post_type, Settings::get_setting( 'post_types_blacklist' ), true );
+ }
+ return false;
+ }
+
+ /**
+ * Remove the embed shortcode.
+ *
+ * @global $wp_embed
+ */
+ public function remove_embed() {
+ global $wp_embed;
+ remove_filter( 'the_content', array( $wp_embed, 'run_shortcode' ), 8 );
+ // remove the embed shortcode since we would do the part later.
+ remove_shortcode( 'embed' );
+ // Attempts to embed all URLs in a post.
+ remove_filter( 'the_content', array( $wp_embed, 'autoembed' ), 8 );
+ }
+
+ /**
+ * Add the embed shortcode.
+ *
+ * @global $wp_embed
+ */
+ public function add_embed() {
+ global $wp_embed;
+ add_filter( 'the_content', array( $wp_embed, 'run_shortcode' ), 8 );
+ // Shortcode placeholder for strip_shortcodes().
+ add_shortcode( 'embed', '__return_false' );
+ // Attempts to embed all URLs in a post.
+ add_filter( 'the_content', array( $wp_embed, 'autoembed' ), 8 );
+ }
+
+ /**
+ * Expands wp_insert_post to include filtered content
+ *
+ * @param \WP_Post $post_object Post object.
+ */
+ public function filter_post_content_and_add_links( $post_object ) {
+ global $post;
+ // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+ $post = $post_object;
+
+ // Return non existant post.
+ $post_type = get_post_type_object( $post->post_type );
+ if ( empty( $post_type ) || ! is_object( $post_type ) ) {
+ $non_existant_post = new \stdClass();
+ $non_existant_post->ID = $post->ID;
+ $non_existant_post->post_modified = $post->post_modified;
+ $non_existant_post->post_modified_gmt = $post->post_modified_gmt;
+ $non_existant_post->post_status = 'jetpack_sync_non_registered_post_type';
+ $non_existant_post->post_type = $post->post_type;
+
+ return $non_existant_post;
+ }
+ /**
+ * Filters whether to prevent sending post data to .com
+ *
+ * Passing true to the filter will prevent the post data from being sent
+ * to the WordPress.com.
+ * Instead we pass data that will still enable us to do a checksum against the
+ * Jetpacks data but will prevent us from displaying the data on in the API as well as
+ * other services.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param boolean false prevent post data from being synced to WordPress.com
+ * @param mixed $post \WP_Post object
+ */
+ if ( apply_filters( 'jetpack_sync_prevent_sending_post_data', false, $post ) ) {
+ // We only send the bare necessary object to be able to create a checksum.
+ $blocked_post = new \stdClass();
+ $blocked_post->ID = $post->ID;
+ $blocked_post->post_modified = $post->post_modified;
+ $blocked_post->post_modified_gmt = $post->post_modified_gmt;
+ $blocked_post->post_status = 'jetpack_sync_blocked';
+ $blocked_post->post_type = $post->post_type;
+
+ return $blocked_post;
+ }
+
+ // lets not do oembed just yet.
+ $this->remove_embed();
+
+ if ( 0 < strlen( $post->post_password ) ) {
+ $post->post_password = 'auto-' . wp_generate_password( 10, false );
+ }
+
+ // Explicitly omit post_content when it exceeds limit.
+ // Large content will cause OOM issues and break Sync.
+ if ( strlen( $post->post_content ) >= self::MAX_POST_CONTENT_LENGTH ) {
+ $post->post_content = '';
+ }
+
+ /** This filter is already documented in core. wp-includes/post-template.php */
+ if ( Settings::get_setting( 'render_filtered_content' ) && $post_type->public ) {
+ global $shortcode_tags;
+ /**
+ * Filter prevents some shortcodes from expanding.
+ *
+ * Since we can can expand some type of shortcode better on the .com side and make the
+ * expansion more relevant to contexts. For example [galleries] and subscription emails
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.5.0
+ *
+ * @param array of shortcode tags to remove.
+ */
+ $shortcodes_to_remove = apply_filters(
+ 'jetpack_sync_do_not_expand_shortcodes',
+ array(
+ 'gallery',
+ 'slideshow',
+ )
+ );
+ $removed_shortcode_callbacks = array();
+ foreach ( $shortcodes_to_remove as $shortcode ) {
+ if ( isset( $shortcode_tags[ $shortcode ] ) ) {
+ $removed_shortcode_callbacks[ $shortcode ] = $shortcode_tags[ $shortcode ];
+ }
+ }
+
+ array_map( 'remove_shortcode', array_keys( $removed_shortcode_callbacks ) );
+
+ $post->post_content_filtered = apply_filters( 'the_content', $post->post_content );
+ $post->post_excerpt_filtered = apply_filters( 'the_excerpt', $post->post_excerpt );
+
+ foreach ( $removed_shortcode_callbacks as $shortcode => $callback ) {
+ add_shortcode( $shortcode, $callback );
+ }
+ }
+
+ $this->add_embed();
+
+ if ( has_post_thumbnail( $post->ID ) ) {
+ $image_attributes = wp_get_attachment_image_src( get_post_thumbnail_id( $post->ID ), 'full' );
+ if ( is_array( $image_attributes ) && isset( $image_attributes[0] ) ) {
+ $post->featured_image = $image_attributes[0];
+ }
+ }
+
+ $post->permalink = get_permalink( $post->ID );
+ $post->shortlink = wp_get_shortlink( $post->ID );
+
+ if ( function_exists( 'amp_get_permalink' ) ) {
+ $post->amp_permalink = amp_get_permalink( $post->ID );
+ }
+
+ return $post;
+ }
+
+ /**
+ * Handle transition from another post status to a published one.
+ *
+ * @param string $new_status New post status.
+ * @param string $old_status Old post status.
+ * @param \WP_Post $post Post object.
+ */
+ public function save_published( $new_status, $old_status, $post ) {
+ if ( 'publish' === $new_status && 'publish' !== $old_status ) {
+ $this->just_published[ $post->ID ] = true;
+ }
+
+ $this->previous_status[ $post->ID ] = $old_status;
+ }
+
+ /**
+ * When publishing or updating a post, the Gutenberg editor sends two requests:
+ * 1. sent to WP REST API endpoint `wp-json/wp/v2/posts/$id`
+ * 2. sent to wp-admin/post.php `?post=$id&action=edit&classic-editor=1&meta_box=1`
+ *
+ * The 2nd request is to update post meta, which is not supported on WP REST API.
+ * When syncing post data, we will include if this was a meta box update.
+ *
+ * @todo Implement nonce verification.
+ *
+ * @return boolean Whether this is a Gutenberg meta box update.
+ */
+ public function is_gutenberg_meta_box_update() {
+ // phpcs:disable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
+ return (
+ isset( $_POST['action'], $_GET['classic-editor'], $_GET['meta_box'] ) &&
+ 'editpost' === $_POST['action'] &&
+ '1' === $_GET['classic-editor'] &&
+ '1' === $_GET['meta_box']
+ // phpcs:enable WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended
+ );
+ }
+
+ /**
+ * Handler for the wp_insert_post hook.
+ * Called upon creation of a new post.
+ *
+ * @param int $post_ID Post ID.
+ * @param \WP_Post $post Post object.
+ * @param boolean $update Whether this is an existing post being updated or not.
+ */
+ public function wp_insert_post( $post_ID, $post = null, $update = null ) {
+ if ( ! is_numeric( $post_ID ) || is_null( $post ) ) {
+ return;
+ }
+
+ // Workaround for https://github.com/woocommerce/woocommerce/issues/18007.
+ if ( $post && 'shop_order' === $post->post_type ) {
+ $post = get_post( $post_ID );
+ }
+
+ $previous_status = isset( $this->previous_status[ $post_ID ] ) ? $this->previous_status[ $post_ID ] : self::DEFAULT_PREVIOUS_STATE;
+
+ $just_published = isset( $this->just_published[ $post_ID ] ) ? $this->just_published[ $post_ID ] : false;
+
+ $state = array(
+ 'is_auto_save' => (bool) Jetpack_Constants::get_constant( 'DOING_AUTOSAVE' ),
+ 'previous_status' => $previous_status,
+ 'just_published' => $just_published,
+ 'is_gutenberg_meta_box_update' => $this->is_gutenberg_meta_box_update(),
+ );
+ /**
+ * Filter that is used to add to the post flags ( meta data ) when a post gets published
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.8.0
+ *
+ * @param int $post_ID the post ID
+ * @param mixed $post \WP_Post object
+ * @param bool $update Whether this is an existing post being updated or not.
+ * @param mixed $state state
+ *
+ * @module sync
+ */
+ do_action( 'jetpack_sync_save_post', $post_ID, $post, $update, $state );
+ unset( $this->previous_status[ $post_ID ] );
+ }
+
+ /**
+ * Handler for the wp_after_insert_post hook.
+ * Called after creation/update of a new post.
+ *
+ * @param int $post_ID Post ID.
+ * @param \WP_Post $post Post object.
+ **/
+ public function wp_after_insert_post( $post_ID, $post ) {
+ if ( ! is_numeric( $post_ID ) || is_null( $post ) ) {
+ return;
+ }
+
+ // Workaround for https://github.com/woocommerce/woocommerce/issues/18007.
+ if ( $post && 'shop_order' === $post->post_type ) {
+ $post = get_post( $post_ID );
+ }
+
+ $this->send_published( $post_ID, $post );
+ }
+
+ /**
+ * Send a published post for sync.
+ *
+ * @param int $post_ID Post ID.
+ * @param \WP_Post $post Post object.
+ */
+ public function send_published( $post_ID, $post ) {
+ if ( ! isset( $this->just_published[ $post_ID ] ) ) {
+ return;
+ }
+
+ // Post revisions cause race conditions where this send_published add the action before the actual post gets synced.
+ if ( wp_is_post_autosave( $post ) || wp_is_post_revision( $post ) ) {
+ return;
+ }
+
+ $post_flags = array(
+ 'post_type' => $post->post_type,
+ );
+
+ $author_user_object = get_user_by( 'id', $post->post_author );
+ if ( $author_user_object ) {
+ $roles = new Roles();
+
+ $post_flags['author'] = array(
+ 'id' => $post->post_author,
+ 'wpcom_user_id' => get_user_meta( $post->post_author, 'wpcom_user_id', true ),
+ 'display_name' => $author_user_object->display_name,
+ 'email' => $author_user_object->user_email,
+ 'translated_role' => $roles->translate_user_to_role( $author_user_object ),
+ );
+ }
+
+ /**
+ * Filter that is used to add to the post flags ( meta data ) when a post gets published
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.4.0
+ *
+ * @param mixed array post flags that are added to the post
+ * @param mixed $post \WP_Post object
+ */
+ $flags = apply_filters( 'jetpack_published_post_flags', $post_flags, $post );
+
+ // Only Send Pulished Post event if post_type is not blacklisted.
+ if ( ! in_array( $post->post_type, Settings::get_setting( 'post_types_blacklist' ), true ) ) {
+ /**
+ * Action that gets synced when a post type gets published.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.4.0
+ *
+ * @param int $post_ID
+ * @param mixed array $flags post flags that are added to the post
+ */
+ do_action( 'jetpack_published_post', $post_ID, $flags );
+ }
+ unset( $this->just_published[ $post_ID ] );
+
+ /**
+ * Send additional sync action for Activity Log when post is a Customizer publish
+ */
+ if ( 'customize_changeset' === $post->post_type ) {
+ $post_content = json_decode( $post->post_content, true );
+ foreach ( $post_content as $key => $value ) {
+ // Skip if it isn't a widget.
+ if ( 'widget_' !== substr( $key, 0, strlen( 'widget_' ) ) ) {
+ continue;
+ }
+ // Change key from "widget_archives[2]" to "archives-2".
+ $key = str_replace( 'widget_', '', $key );
+ $key = str_replace( '[', '-', $key );
+ $key = str_replace( ']', '', $key );
+
+ global $wp_registered_widgets;
+ if ( isset( $wp_registered_widgets[ $key ] ) ) {
+ $widget_data = array(
+ 'name' => $wp_registered_widgets[ $key ]['name'],
+ 'id' => $key,
+ 'title' => $value['value']['title'],
+ );
+ do_action( 'jetpack_widget_edited', $widget_data );
+ }
+ }
+ }
+ }
+
+ /**
+ * Expand post IDs to post objects within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The expanded hook parameters.
+ */
+ public function expand_post_ids( $args ) {
+ list( $post_ids, $previous_interval_end) = $args;
+
+ $posts = array_filter( array_map( array( 'WP_Post', 'get_instance' ), $post_ids ) );
+ $posts = array_map( array( $this, 'filter_post_content_and_add_links' ), $posts );
+ $posts = array_values( $posts ); // Reindex in case posts were deleted.
+
+ return array(
+ $posts,
+ $this->get_metadata( $post_ids, 'post', Settings::get_setting( 'post_meta_whitelist' ) ),
+ $this->get_term_relationships( $post_ids ),
+ $previous_interval_end,
+ );
+ }
+
+ /**
+ * Gets a list of minimum and maximum object ids for each batch based on the given batch size.
+ *
+ * @access public
+ *
+ * @param int $batch_size The batch size for objects.
+ * @param string|bool $where_sql The sql where clause minus 'WHERE', or false if no where clause is needed.
+ *
+ * @return array|bool An array of min and max ids for each batch. FALSE if no table can be found.
+ */
+ public function get_min_max_object_ids_for_batches( $batch_size, $where_sql = false ) {
+ return parent::get_min_max_object_ids_for_batches( $batch_size, $this->get_where_sql( $where_sql ) );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-protect.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-protect.php
new file mode 100644
index 00000000..ebd62ff8
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-protect.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * Protect sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Constants as Jetpack_Constants;
+
+/**
+ * Class to handle sync for Protect.
+ * Logs BruteProtect failed logins via sync.
+ */
+class Protect extends Module {
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'protect';
+ }
+
+ /**
+ * Initialize Protect action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callback Action handler callable.
+ */
+ public function init_listeners( $callback ) {
+ add_action( 'jpp_log_failed_attempt', array( $this, 'maybe_log_failed_login_attempt' ) );
+ add_action( 'jetpack_valid_failed_login_attempt', $callback );
+ }
+
+ /**
+ * Maybe log a failed login attempt.
+ *
+ * @access public
+ *
+ * @param array $failed_attempt Failed attempt data.
+ */
+ public function maybe_log_failed_login_attempt( $failed_attempt ) {
+ $protect = \Jetpack_Protect_Module::instance();
+ if ( $protect->has_login_ability() && ! Jetpack_Constants::is_true( 'XMLRPC_REQUEST' ) ) {
+ do_action( 'jetpack_valid_failed_login_attempt', $failed_attempt );
+ }
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-stats.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-stats.php
new file mode 100644
index 00000000..83479d1d
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-stats.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Stats sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Heartbeat;
+
+/**
+ * Class to handle sync for stats.
+ */
+class Stats extends Module {
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'stats';
+ }
+
+ /**
+ * Initialize stats action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callback Action handler callable.
+ */
+ public function init_listeners( $callback ) {
+ add_action( 'jetpack_heartbeat', array( $this, 'sync_site_stats' ), 20 );
+ add_action( 'jetpack_sync_heartbeat_stats', $callback );
+ }
+
+ /**
+ * This namespaces the action that we sync.
+ * So that we can differentiate it from future actions.
+ *
+ * @access public
+ */
+ public function sync_site_stats() {
+ do_action( 'jetpack_sync_heartbeat_stats' );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_filter( 'jetpack_sync_before_send_jetpack_sync_heartbeat_stats', array( $this, 'add_stats' ) );
+ }
+
+ /**
+ * Retrieve the stats data for the site.
+ *
+ * @access public
+ *
+ * @return array Stats data.
+ */
+ public function add_stats() {
+ return array( Heartbeat::generate_stats_array() );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-term-relationships.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-term-relationships.php
new file mode 100644
index 00000000..2a7c22e3
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-term-relationships.php
@@ -0,0 +1,244 @@
+<?php
+/**
+ * Term relationships sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Sync\Listener;
+use Automattic\Jetpack\Sync\Settings;
+
+/**
+ * Class to handle sync for term relationships.
+ */
+class Term_Relationships extends Module {
+
+ /**
+ * Max terms to return in one single query
+ *
+ * @access public
+ *
+ * @const int
+ */
+ const QUERY_LIMIT = 1000;
+
+ /**
+ * Max value for a signed INT in MySQL - https://dev.mysql.com/doc/refman/8.0/en/integer-types.html
+ *
+ * @access public
+ *
+ * @const int
+ */
+ const MAX_INT = 2147483647;
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'term_relationships';
+ }
+
+ /**
+ * The id field in the database.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function id_field() {
+ return 'object_id';
+ }
+
+ /**
+ * The table in the database.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function table_name() {
+ return 'term_relationships';
+ }
+
+ /**
+ * Initialize term relationships action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_term_relationships', $callable, 10, 2 );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_term_relationships', array( $this, 'expand_term_relationships' ) );
+ }
+
+ /**
+ * Enqueue the term relationships actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param object $last_object_enqueued Last object enqueued.
+ *
+ * @return array Number of actions enqueued, and next module state.
+ * @todo This method has similarities with Automattic\Jetpack\Sync\Modules\Module::enqueue_all_ids_as_action. Refactor to keep DRY.
+ * @see Automattic\Jetpack\Sync\Modules\Module::enqueue_all_ids_as_action
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $last_object_enqueued ) {
+ global $wpdb;
+ $term_relationships_full_sync_item_size = Settings::get_setting( 'term_relationships_full_sync_item_size' );
+ $limit = min( $max_items_to_enqueue * $term_relationships_full_sync_item_size, self::QUERY_LIMIT );
+ $items_enqueued_count = 0;
+ $last_object_enqueued = $last_object_enqueued ? $last_object_enqueued : array(
+ 'object_id' => self::MAX_INT,
+ 'term_taxonomy_id' => self::MAX_INT,
+ );
+
+ while ( $limit > 0 ) {
+ /*
+ * SELECT object_id, term_taxonomy_id
+ * FROM $wpdb->term_relationships
+ * WHERE ( object_id = 11 AND term_taxonomy_id < 14 ) OR ( object_id < 11 )
+ * ORDER BY object_id DESC, term_taxonomy_id DESC LIMIT 1000
+ */
+ $objects = $wpdb->get_results( $wpdb->prepare( "SELECT object_id, term_taxonomy_id FROM $wpdb->term_relationships WHERE ( object_id = %d AND term_taxonomy_id < %d ) OR ( object_id < %d ) ORDER BY object_id DESC, term_taxonomy_id DESC LIMIT %d", $last_object_enqueued['object_id'], $last_object_enqueued['term_taxonomy_id'], $last_object_enqueued['object_id'], $limit ), ARRAY_A );
+ // Request term relationships in groups of N for efficiency.
+ $objects_count = count( $objects );
+ if ( ! count( $objects ) ) {
+ return array( $items_enqueued_count, true );
+ }
+ $items = array_chunk( $objects, $term_relationships_full_sync_item_size );
+ $last_object_enqueued = $this->bulk_enqueue_full_sync_term_relationships( $items, $last_object_enqueued );
+ $items_enqueued_count += count( $items );
+ $limit = min( $limit - $objects_count, self::QUERY_LIMIT );
+ }
+
+ // We need to do this extra check in case $max_items_to_enqueue * $term_relationships_full_sync_item_size == relationships objects left.
+ $count = $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM $wpdb->term_relationships WHERE ( object_id = %d AND term_taxonomy_id < %d ) OR ( object_id < %d ) ORDER BY object_id DESC, term_taxonomy_id DESC LIMIT %d", $last_object_enqueued['object_id'], $last_object_enqueued['term_taxonomy_id'], $last_object_enqueued['object_id'], 1 ) );
+ if ( 0 === (int) $count ) {
+ return array( $items_enqueued_count, true );
+ }
+
+ return array( $items_enqueued_count, $last_object_enqueued );
+ }
+
+ /**
+ * Return the initial last sent object.
+ *
+ * @return string|array initial status.
+ */
+ public function get_initial_last_sent() {
+ return array(
+ 'object_id' => self::MAX_INT,
+ 'term_taxonomy_id' => self::MAX_INT,
+ );
+ }
+
+ /**
+ * Given the Module Full Sync Configuration and Status return the next chunk of items to send.
+ *
+ * @param array $config This module Full Sync configuration.
+ * @param array $status This module Full Sync status.
+ * @param int $chunk_size Chunk size.
+ *
+ * @return array|object|null
+ */
+ public function get_next_chunk( $config, $status, $chunk_size ) {
+ global $wpdb;
+
+ return $wpdb->get_results(
+ $wpdb->prepare(
+ "SELECT object_id, term_taxonomy_id
+ FROM $wpdb->term_relationships
+ WHERE ( object_id = %d AND term_taxonomy_id < %d ) OR ( object_id < %d )
+ ORDER BY object_id DESC, term_taxonomy_id
+ DESC LIMIT %d",
+ $status['last_sent']['object_id'],
+ $status['last_sent']['term_taxonomy_id'],
+ $status['last_sent']['object_id'],
+ $chunk_size
+ ),
+ ARRAY_A
+ );
+ }
+
+ /**
+ *
+ * Enqueue all $items within `jetpack_full_sync_term_relationships` actions.
+ *
+ * @param array $items Groups of objects to sync.
+ * @param array $previous_interval_end Last item enqueued.
+ *
+ * @return array Last enqueued object.
+ */
+ public function bulk_enqueue_full_sync_term_relationships( $items, $previous_interval_end ) {
+ $listener = Listener::get_instance();
+ $items_with_previous_interval_end = $this->get_chunks_with_preceding_end( $items, $previous_interval_end );
+ $listener->bulk_enqueue_full_sync_actions( 'jetpack_full_sync_term_relationships', $items_with_previous_interval_end );
+ $last_item = end( $items );
+ return end( $last_item );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return int Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ global $wpdb;
+
+ $query = "SELECT COUNT(*) FROM $wpdb->term_relationships";
+
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
+ $count = $wpdb->get_var( $query );
+
+ return (int) ceil( $count / Settings::get_setting( 'term_relationships_full_sync_item_size' ) );
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_term_relationships' );
+ }
+
+ /**
+ * Expand the term relationships within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The expanded hook parameters.
+ */
+ public function expand_term_relationships( $args ) {
+ list( $term_relationships, $previous_end ) = $args;
+
+ return array(
+ 'term_relationships' => $term_relationships,
+ 'previous_end' => $previous_end,
+ );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-terms.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-terms.php
new file mode 100644
index 00000000..6bc8c064
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-terms.php
@@ -0,0 +1,314 @@
+<?php
+/**
+ * Terms sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Sync\Defaults;
+use Automattic\Jetpack\Sync\Settings;
+
+/**
+ * Class to handle sync for terms.
+ */
+class Terms extends Module {
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'terms';
+ }
+
+ /**
+ * The id field in the database.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function id_field() {
+ return 'term_taxonomy_id';
+ }
+
+ /**
+ * The table in the database.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function table_name() {
+ return 'term_taxonomy';
+ }
+
+ /**
+ * Allows WordPress.com servers to retrieve term-related objects via the sync API.
+ *
+ * @param string $object_type The type of object.
+ * @param int $id The id of the object.
+ *
+ * @return bool|object A WP_Term object, or a row from term_taxonomy table depending on object type.
+ */
+ public function get_object_by_id( $object_type, $id ) {
+ global $wpdb;
+ $object = false;
+ if ( 'term' === $object_type ) {
+ $object = get_term( (int) $id );
+
+ if ( is_wp_error( $object ) && $object->get_error_code() === 'invalid_taxonomy' ) {
+ // Fetch raw term.
+ $columns = implode( ', ', array_unique( array_merge( Defaults::$default_term_checksum_columns, array( 'term_group' ) ) ) );
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $object = $wpdb->get_row( $wpdb->prepare( "SELECT $columns FROM $wpdb->terms WHERE term_id = %d", $id ) );
+ }
+ }
+
+ if ( 'term_taxonomy' === $object_type ) {
+ $columns = implode( ', ', array_unique( array_merge( Defaults::$default_term_taxonomy_checksum_columns, array( 'description' ) ) ) );
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $object = $wpdb->get_row( $wpdb->prepare( "SELECT $columns FROM $wpdb->term_taxonomy WHERE term_taxonomy_id = %d", $id ) );
+ }
+
+ if ( 'term_relationships' === $object_type ) {
+ $columns = implode( ', ', Defaults::$default_term_relationships_checksum_columns );
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $objects = $wpdb->get_results( $wpdb->prepare( "SELECT $columns FROM $wpdb->term_relationships WHERE object_id = %d", $id ) );
+ $object = (object) array(
+ 'object_id' => $id,
+ 'relationships' => array_map( array( $this, 'expand_terms_for_relationship' ), $objects ),
+ );
+ }
+
+ return $object ? $object : false;
+ }
+
+ /**
+ * Initialize terms action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ add_action( 'created_term', array( $this, 'save_term_handler' ), 10, 3 );
+ add_action( 'edited_term', array( $this, 'save_term_handler' ), 10, 3 );
+ add_action( 'jetpack_sync_save_term', $callable );
+ add_action( 'jetpack_sync_add_term', $callable );
+ add_action( 'delete_term', $callable, 10, 4 );
+ add_action( 'set_object_terms', $callable, 10, 6 );
+ add_action( 'deleted_term_relationships', $callable, 10, 2 );
+ add_filter( 'jetpack_sync_before_enqueue_set_object_terms', array( $this, 'filter_set_object_terms_no_update' ) );
+ add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_save_term', array( $this, 'filter_blacklisted_taxonomies' ) );
+ add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_add_term', array( $this, 'filter_blacklisted_taxonomies' ) );
+ }
+
+ /**
+ * Initialize terms action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_terms', $callable, 10, 2 );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_terms', array( $this, 'expand_term_taxonomy_id' ) );
+ }
+
+ /**
+ * Enqueue the terms actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
+ global $wpdb;
+ return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_terms', $wpdb->term_taxonomy, 'term_taxonomy_id', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
+ }
+
+ /**
+ * Retrieve the WHERE SQL clause based on the module config.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return string WHERE SQL clause, or `null` if no comments are specified in the module config.
+ */
+ public function get_where_sql( $config ) {
+ $where_sql = Settings::get_blacklisted_taxonomies_sql();
+
+ if ( is_array( $config ) ) {
+ $where_sql .= ' AND term_taxonomy_id IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
+ }
+
+ return $where_sql;
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return int Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) {
+ global $wpdb;
+
+ $query = "SELECT count(*) FROM $wpdb->term_taxonomy";
+
+ $where_sql = $this->get_where_sql( $config );
+ if ( $where_sql ) {
+ $query .= ' WHERE ' . $where_sql;
+ }
+
+ // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
+ $count = $wpdb->get_var( $query );
+
+ return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_terms' );
+ }
+
+ /**
+ * Handler for creating and updating terms.
+ *
+ * @access public
+ *
+ * @param int $term_id Term ID.
+ * @param int $tt_id Term taxonomy ID.
+ * @param string $taxonomy Taxonomy slug.
+ */
+ public function save_term_handler( $term_id, $tt_id, $taxonomy ) {
+ if ( class_exists( '\\WP_Term' ) ) {
+ $term_object = \WP_Term::get_instance( $term_id, $taxonomy );
+ } else {
+ $term_object = get_term_by( 'id', $term_id, $taxonomy );
+ }
+
+ $current_filter = current_filter();
+
+ if ( 'created_term' === $current_filter ) {
+ /**
+ * Fires when the client needs to add a new term
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.0.0
+ *
+ * @param object the Term object
+ */
+ do_action( 'jetpack_sync_add_term', $term_object );
+ return;
+ }
+
+ /**
+ * Fires when the client needs to update a term
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param object the Term object
+ */
+ do_action( 'jetpack_sync_save_term', $term_object );
+ }
+
+ /**
+ * Filter blacklisted taxonomies.
+ *
+ * @access public
+ *
+ * @param array $args Hook args.
+ * @return array|boolean False if not whitelisted, the original hook args otherwise.
+ */
+ public function filter_blacklisted_taxonomies( $args ) {
+ $term = $args[0];
+
+ if ( in_array( $term->taxonomy, Settings::get_setting( 'taxonomies_blacklist' ), true ) ) {
+ return false;
+ }
+
+ return $args;
+ }
+
+ /**
+ * Filter out set_object_terms actions where the terms have not changed.
+ *
+ * @param array $args Hook args.
+ * @return array|boolean False if no change in terms, the original hook args otherwise.
+ */
+ public function filter_set_object_terms_no_update( $args ) {
+ // There is potential for other plugins to modify args, therefore lets validate # of and types.
+ // $args[2] is $tt_ids, $args[5] is $old_tt_ids see wp-includes/taxonomy.php L2740.
+ if ( 6 === count( $args ) && is_array( $args[2] ) && is_array( $args[5] ) ) {
+ if ( empty( array_diff( $args[2], $args[5] ) ) && empty( array_diff( $args[5], $args[2] ) ) ) {
+ return false;
+ }
+ }
+ return $args;
+ }
+
+ /**
+ * Expand the term taxonomy IDs to terms within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The expanded hook parameters.
+ */
+ public function expand_term_taxonomy_id( $args ) {
+ list( $term_taxonomy_ids, $previous_end ) = $args;
+
+ return array(
+ 'terms' => get_terms(
+ array(
+ 'hide_empty' => false,
+ 'term_taxonomy_id' => $term_taxonomy_ids,
+ 'orderby' => 'term_taxonomy_id',
+ 'order' => 'DESC',
+ )
+ ),
+ 'previous_end' => $previous_end,
+ );
+ }
+
+ /**
+ * Gets a term object based on a given row from the term_relationships database table.
+ *
+ * @access public
+ *
+ * @param object $relationship A row object from the term_relationships table.
+ * @return object|bool A term object, or false if term taxonomy doesn't exist.
+ */
+ public function expand_terms_for_relationship( $relationship ) {
+ return get_term_by( 'term_taxonomy_id', $relationship->term_taxonomy_id );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-themes.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-themes.php
new file mode 100644
index 00000000..4b594eda
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-themes.php
@@ -0,0 +1,877 @@
+<?php
+/**
+ * Themes sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+/**
+ * Class to handle sync for themes.
+ */
+class Themes extends Module {
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'themes';
+ }
+
+ /**
+ * Initialize themes action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ add_action( 'switch_theme', array( $this, 'sync_theme_support' ), 10, 3 );
+ add_action( 'jetpack_sync_current_theme_support', $callable, 10, 2 );
+ add_action( 'upgrader_process_complete', array( $this, 'check_upgrader' ), 10, 2 );
+ add_action( 'jetpack_installed_theme', $callable, 10, 2 );
+ add_action( 'jetpack_updated_themes', $callable, 10, 2 );
+ add_filter( 'wp_redirect', array( $this, 'detect_theme_edit' ) );
+ add_action( 'jetpack_edited_theme', $callable, 10, 2 );
+ add_action( 'wp_ajax_edit-theme-plugin-file', array( $this, 'theme_edit_ajax' ), 0 );
+ add_action( 'update_site_option_allowedthemes', array( $this, 'sync_network_allowed_themes_change' ), 10, 4 );
+ add_action( 'jetpack_network_disabled_themes', $callable, 10, 2 );
+ add_action( 'jetpack_network_enabled_themes', $callable, 10, 2 );
+
+ // Theme deletions.
+ add_action( 'deleted_theme', array( $this, 'detect_theme_deletion' ), 10, 2 );
+ add_action( 'jetpack_deleted_theme', $callable, 10, 2 );
+
+ // Sidebar updates.
+ add_action( 'update_option_sidebars_widgets', array( $this, 'sync_sidebar_widgets_actions' ), 10, 2 );
+
+ add_action( 'jetpack_widget_added', $callable, 10, 4 );
+ add_action( 'jetpack_widget_removed', $callable, 10, 4 );
+ add_action( 'jetpack_widget_moved_to_inactive', $callable, 10, 2 );
+ add_action( 'jetpack_cleared_inactive_widgets', $callable );
+ add_action( 'jetpack_widget_reordered', $callable, 10, 2 );
+ add_filter( 'widget_update_callback', array( $this, 'sync_widget_edit' ), 10, 4 );
+ add_action( 'jetpack_widget_edited', $callable );
+ }
+
+ /**
+ * Sync handler for a widget edit.
+ *
+ * @access public
+ *
+ * @todo Implement nonce verification
+ *
+ * @param array $instance The current widget instance's settings.
+ * @param array $new_instance Array of new widget settings.
+ * @param array $old_instance Array of old widget settings.
+ * @param \WP_Widget $widget_object The current widget instance.
+ * @return array The current widget instance's settings.
+ */
+ public function sync_widget_edit( $instance, $new_instance, $old_instance, $widget_object ) {
+ if ( empty( $old_instance ) ) {
+ return $instance;
+ }
+
+ // Don't trigger sync action if this is an ajax request, because Customizer makes them during preview before saving changes.
+ // phpcs:disable WordPress.Security.NonceVerification.Missing
+ if ( defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_POST['customized'] ) ) {
+ return $instance;
+ }
+
+ $widget = array(
+ 'name' => $widget_object->name,
+ 'id' => $widget_object->id,
+ 'title' => isset( $new_instance['title'] ) ? $new_instance['title'] : '',
+ );
+ /**
+ * Trigger action to alert $callable sync listener that a widget was edited.
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.0.0
+ *
+ * @param string $widget_name , Name of edited widget
+ */
+ do_action( 'jetpack_widget_edited', $widget );
+
+ return $instance;
+ }
+
+ /**
+ * Sync handler for network allowed themes change.
+ *
+ * @access public
+ *
+ * @param string $option Name of the network option.
+ * @param mixed $value Current value of the network option.
+ * @param mixed $old_value Old value of the network option.
+ * @param int $network_id ID of the network.
+ */
+ public function sync_network_allowed_themes_change( $option, $value, $old_value, $network_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ $all_enabled_theme_slugs = array_keys( $value );
+
+ if ( count( $old_value ) > count( $value ) ) {
+
+ // Suppress jetpack_network_disabled_themes sync action when theme is deleted.
+ $delete_theme_call = $this->get_delete_theme_call();
+ if ( ! empty( $delete_theme_call ) ) {
+ return;
+ }
+
+ $newly_disabled_theme_names = array_keys( array_diff_key( $old_value, $value ) );
+ $newly_disabled_themes = $this->get_theme_details_for_slugs( $newly_disabled_theme_names );
+ /**
+ * Trigger action to alert $callable sync listener that network themes were disabled.
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.0.0
+ *
+ * @param mixed $newly_disabled_themes, Array of info about network disabled themes
+ * @param mixed $all_enabled_theme_slugs, Array of slugs of all enabled themes
+ */
+ do_action( 'jetpack_network_disabled_themes', $newly_disabled_themes, $all_enabled_theme_slugs );
+ return;
+ }
+
+ $newly_enabled_theme_names = array_keys( array_diff_key( $value, $old_value ) );
+ $newly_enabled_themes = $this->get_theme_details_for_slugs( $newly_enabled_theme_names );
+ /**
+ * Trigger action to alert $callable sync listener that network themes were enabled
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.0.0
+ *
+ * @param mixed $newly_enabled_themes , Array of info about network enabled themes
+ * @param mixed $all_enabled_theme_slugs, Array of slugs of all enabled themes
+ */
+ do_action( 'jetpack_network_enabled_themes', $newly_enabled_themes, $all_enabled_theme_slugs );
+ }
+
+ /**
+ * Retrieve details for one or more themes by their slugs.
+ *
+ * @access private
+ *
+ * @param array $theme_slugs Theme slugs.
+ * @return array Details for the themes.
+ */
+ private function get_theme_details_for_slugs( $theme_slugs ) {
+ $theme_data = array();
+ foreach ( $theme_slugs as $slug ) {
+ $theme = wp_get_theme( $slug );
+ $theme_data[ $slug ] = array(
+ 'name' => $theme->get( 'Name' ),
+ 'version' => $theme->get( 'Version' ),
+ 'uri' => $theme->get( 'ThemeURI' ),
+ 'slug' => $slug,
+ );
+ }
+ return $theme_data;
+ }
+
+ /**
+ * Detect a theme edit during a redirect.
+ *
+ * @access public
+ *
+ * @param string $redirect_url Redirect URL.
+ * @return string Redirect URL.
+ */
+ public function detect_theme_edit( $redirect_url ) {
+ $url = wp_parse_url( admin_url( $redirect_url ) );
+ $theme_editor_url = wp_parse_url( admin_url( 'theme-editor.php' ) );
+
+ if ( $theme_editor_url['path'] !== $url['path'] ) {
+ return $redirect_url;
+ }
+
+ $query_params = array();
+ wp_parse_str( $url['query'], $query_params );
+ if (
+ ! isset( $_POST['newcontent'] ) ||
+ ! isset( $query_params['file'] ) ||
+ ! isset( $query_params['theme'] ) ||
+ ! isset( $query_params['updated'] )
+ ) {
+ return $redirect_url;
+ }
+ $theme = wp_get_theme( $query_params['theme'] );
+ $theme_data = array(
+ 'name' => $theme->get( 'Name' ),
+ 'version' => $theme->get( 'Version' ),
+ 'uri' => $theme->get( 'ThemeURI' ),
+ );
+
+ /**
+ * Trigger action to alert $callable sync listener that a theme was edited.
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.0.0
+ *
+ * @param string $query_params['theme'], Slug of edited theme
+ * @param string $theme_data, Information about edited them
+ */
+ do_action( 'jetpack_edited_theme', $query_params['theme'], $theme_data );
+
+ return $redirect_url;
+ }
+
+ /**
+ * Handler for AJAX theme editing.
+ *
+ * @todo Refactor to use WP_Filesystem instead of fopen()/fclose().
+ */
+ public function theme_edit_ajax() {
+ $args = wp_unslash( $_POST );
+
+ if ( empty( $args['theme'] ) ) {
+ return;
+ }
+
+ if ( empty( $args['file'] ) ) {
+ return;
+ }
+ $file = $args['file'];
+ if ( 0 !== validate_file( $file ) ) {
+ return;
+ }
+
+ if ( ! isset( $args['newcontent'] ) ) {
+ return;
+ }
+
+ if ( ! isset( $args['nonce'] ) ) {
+ return;
+ }
+
+ $stylesheet = $args['theme'];
+ if ( 0 !== validate_file( $stylesheet ) ) {
+ return;
+ }
+
+ if ( ! current_user_can( 'edit_themes' ) ) {
+ return;
+ }
+
+ $theme = wp_get_theme( $stylesheet );
+ if ( ! $theme->exists() ) {
+ return;
+ }
+
+ if ( ! wp_verify_nonce( $args['nonce'], 'edit-theme_' . $stylesheet . '_' . $file ) ) {
+ return;
+ }
+
+ if ( $theme->errors() && 'theme_no_stylesheet' === $theme->errors()->get_error_code() ) {
+ return;
+ }
+
+ $editable_extensions = wp_get_theme_file_editable_extensions( $theme );
+
+ $allowed_files = array();
+ foreach ( $editable_extensions as $type ) {
+ switch ( $type ) {
+ case 'php':
+ $allowed_files = array_merge( $allowed_files, $theme->get_files( 'php', -1 ) );
+ break;
+ case 'css':
+ $style_files = $theme->get_files( 'css', -1 );
+ $allowed_files['style.css'] = $style_files['style.css'];
+ $allowed_files = array_merge( $allowed_files, $style_files );
+ break;
+ default:
+ $allowed_files = array_merge( $allowed_files, $theme->get_files( $type, -1 ) );
+ break;
+ }
+ }
+
+ $real_file = $theme->get_stylesheet_directory() . '/' . $file;
+ if ( 0 !== validate_file( $real_file, $allowed_files ) ) {
+ return;
+ }
+
+ // Ensure file is real.
+ if ( ! is_file( $real_file ) ) {
+ return;
+ }
+
+ // Ensure file extension is allowed.
+ $extension = null;
+ if ( preg_match( '/\.([^.]+)$/', $real_file, $matches ) ) {
+ $extension = strtolower( $matches[1] );
+ if ( ! in_array( $extension, $editable_extensions, true ) ) {
+ return;
+ }
+ }
+
+ if ( ! is_writeable( $real_file ) ) {
+ return;
+ }
+
+ // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fopen
+ $file_pointer = fopen( $real_file, 'w+' );
+ if ( false === $file_pointer ) {
+ return;
+ }
+ // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_fclose
+ fclose( $file_pointer );
+
+ $theme_data = array(
+ 'name' => $theme->get( 'Name' ),
+ 'version' => $theme->get( 'Version' ),
+ 'uri' => $theme->get( 'ThemeURI' ),
+ );
+
+ /**
+ * This action is documented already in this file.
+ */
+ do_action( 'jetpack_edited_theme', $stylesheet, $theme_data );
+ }
+
+ /**
+ * Detect a theme deletion.
+ *
+ * @access public
+ *
+ * @param string $stylesheet Stylesheet of the theme to delete.
+ * @param bool $deleted Whether the theme deletion was successful.
+ */
+ public function detect_theme_deletion( $stylesheet, $deleted ) {
+ $theme = wp_get_theme( $stylesheet );
+ $theme_data = array(
+ 'name' => $theme->get( 'Name' ),
+ 'version' => $theme->get( 'Version' ),
+ 'uri' => $theme->get( 'ThemeURI' ),
+ 'slug' => $stylesheet,
+ );
+
+ if ( $deleted ) {
+ /**
+ * Signals to the sync listener that a theme was deleted and a sync action
+ * reflecting the deletion and theme slug should be sent
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.0.0
+ *
+ * @param string $stylesheet Theme slug
+ * @param array $theme_data Theme info Since 5.3
+ */
+ do_action( 'jetpack_deleted_theme', $stylesheet, $theme_data );
+ }
+ }
+
+ /**
+ * Handle an upgrader completion action.
+ *
+ * @access public
+ *
+ * @param \WP_Upgrader $upgrader The upgrader instance.
+ * @param array $details Array of bulk item update data.
+ */
+ public function check_upgrader( $upgrader, $details ) {
+ if ( ! isset( $details['type'] ) ||
+ 'theme' !== $details['type'] ||
+ is_wp_error( $upgrader->skin->result ) ||
+ ! method_exists( $upgrader, 'theme_info' )
+ ) {
+ return;
+ }
+
+ if ( 'install' === $details['action'] ) {
+ $theme = $upgrader->theme_info();
+ if ( ! $theme instanceof \WP_Theme ) {
+ return;
+ }
+ $theme_info = array(
+ 'name' => $theme->get( 'Name' ),
+ 'version' => $theme->get( 'Version' ),
+ 'uri' => $theme->get( 'ThemeURI' ),
+ );
+
+ /**
+ * Signals to the sync listener that a theme was installed and a sync action
+ * reflecting the installation and the theme info should be sent
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.9.0
+ *
+ * @param string $theme->theme_root Text domain of the theme
+ * @param mixed $theme_info Array of abbreviated theme info
+ */
+ do_action( 'jetpack_installed_theme', $theme->stylesheet, $theme_info );
+ }
+
+ if ( 'update' === $details['action'] ) {
+ $themes = array();
+
+ if ( empty( $details['themes'] ) && isset( $details['theme'] ) ) {
+ $details['themes'] = array( $details['theme'] );
+ }
+
+ foreach ( $details['themes'] as $theme_slug ) {
+ $theme = wp_get_theme( $theme_slug );
+
+ if ( ! $theme instanceof \WP_Theme ) {
+ continue;
+ }
+
+ $themes[ $theme_slug ] = array(
+ 'name' => $theme->get( 'Name' ),
+ 'version' => $theme->get( 'Version' ),
+ 'uri' => $theme->get( 'ThemeURI' ),
+ 'stylesheet' => $theme->stylesheet,
+ );
+ }
+
+ if ( empty( $themes ) ) {
+ return;
+ }
+
+ /**
+ * Signals to the sync listener that one or more themes was updated and a sync action
+ * reflecting the update and the theme info should be sent
+ *
+ * @since 1.6.3
+ * @since-jetpack 6.2.0
+ *
+ * @param mixed $themes Array of abbreviated theme info
+ */
+ do_action( 'jetpack_updated_themes', $themes );
+ }
+ }
+
+ /**
+ * Initialize themes action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_theme_data', $callable );
+ }
+
+ /**
+ * Handle a theme switch.
+ *
+ * @access public
+ *
+ * @param string $new_name Name of the new theme.
+ * @param \WP_Theme $new_theme The new theme.
+ * @param \WP_Theme $old_theme The previous theme.
+ */
+ public function sync_theme_support( $new_name, $new_theme = null, $old_theme = null ) {
+ $previous_theme = $this->get_theme_info( $old_theme );
+
+ /**
+ * Fires when the client needs to sync theme support info
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param array the theme support array
+ * @param array the previous theme since Jetpack 6.5.0
+ */
+ do_action( 'jetpack_sync_current_theme_support', $this->get_theme_info(), $previous_theme );
+ }
+
+ /**
+ * Enqueue the themes actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ /**
+ * Tells the client to sync all theme data to the server
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param boolean Whether to expand theme data (should always be true)
+ */
+ do_action( 'jetpack_full_sync_theme_data', true );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 1, true );
+ }
+
+ /**
+ * Send the themes actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $send_until The timestamp until the current request can send.
+ * @param array $state This module Full Sync status.
+ *
+ * @return array This module Full Sync status.
+ */
+ public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // we call this instead of do_action when sending immediately.
+ $this->send_action( 'jetpack_full_sync_theme_data', array( true ) );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 'finished' => true );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_theme_data', array( $this, 'expand_theme_data' ) );
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_theme_data' );
+ }
+
+ /**
+ * Expand the theme within a hook before it is serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @return array Theme data.
+ */
+ public function expand_theme_data() {
+ return array( $this->get_theme_info() );
+ }
+
+ /**
+ * Retrieve the name of the widget by the widget ID.
+ *
+ * @access public
+ * @global $wp_registered_widgets
+ *
+ * @param string $widget_id Widget ID.
+ * @return string Name of the widget, or null if not found.
+ */
+ public function get_widget_name( $widget_id ) {
+ global $wp_registered_widgets;
+ return ( isset( $wp_registered_widgets[ $widget_id ] ) ? $wp_registered_widgets[ $widget_id ]['name'] : null );
+ }
+
+ /**
+ * Retrieve the name of the sidebar by the sidebar ID.
+ *
+ * @access public
+ * @global $wp_registered_sidebars
+ *
+ * @param string $sidebar_id Sidebar ID.
+ * @return string Name of the sidebar, or null if not found.
+ */
+ public function get_sidebar_name( $sidebar_id ) {
+ global $wp_registered_sidebars;
+ return ( isset( $wp_registered_sidebars[ $sidebar_id ] ) ? $wp_registered_sidebars[ $sidebar_id ]['name'] : null );
+ }
+
+ /**
+ * Sync addition of widgets to a sidebar.
+ *
+ * @access public
+ *
+ * @param array $new_widgets New widgets.
+ * @param array $old_widgets Old widgets.
+ * @param string $sidebar Sidebar ID.
+ * @return array All widgets that have been moved to the sidebar.
+ */
+ public function sync_add_widgets_to_sidebar( $new_widgets, $old_widgets, $sidebar ) {
+ $added_widgets = array_diff( $new_widgets, $old_widgets );
+ if ( empty( $added_widgets ) ) {
+ return array();
+ }
+ $moved_to_sidebar = array();
+ $sidebar_name = $this->get_sidebar_name( $sidebar );
+
+ // Don't sync jetpack_widget_added if theme was switched.
+ if ( $this->is_theme_switch() ) {
+ return array();
+ }
+
+ foreach ( $added_widgets as $added_widget ) {
+ $moved_to_sidebar[] = $added_widget;
+ $added_widget_name = $this->get_widget_name( $added_widget );
+ /**
+ * Helps Sync log that a widget got added
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.9.0
+ *
+ * @param string $sidebar, Sidebar id got changed
+ * @param string $added_widget, Widget id got added
+ * @param string $sidebar_name, Sidebar id got changed Since 5.0.0
+ * @param string $added_widget_name, Widget id got added Since 5.0.0
+ */
+ do_action( 'jetpack_widget_added', $sidebar, $added_widget, $sidebar_name, $added_widget_name );
+ }
+ return $moved_to_sidebar;
+ }
+
+ /**
+ * Sync removal of widgets from a sidebar.
+ *
+ * @access public
+ *
+ * @param array $new_widgets New widgets.
+ * @param array $old_widgets Old widgets.
+ * @param string $sidebar Sidebar ID.
+ * @param array $inactive_widgets Current inactive widgets.
+ * @return array All widgets that have been moved to inactive.
+ */
+ public function sync_remove_widgets_from_sidebar( $new_widgets, $old_widgets, $sidebar, $inactive_widgets ) {
+ $removed_widgets = array_diff( $old_widgets, $new_widgets );
+
+ if ( empty( $removed_widgets ) ) {
+ return array();
+ }
+
+ $moved_to_inactive = array();
+ $sidebar_name = $this->get_sidebar_name( $sidebar );
+
+ foreach ( $removed_widgets as $removed_widget ) {
+ // Lets check if we didn't move the widget to in_active_widgets.
+ if ( isset( $inactive_widgets ) && ! in_array( $removed_widget, $inactive_widgets, true ) ) {
+ $removed_widget_name = $this->get_widget_name( $removed_widget );
+ /**
+ * Helps Sync log that a widgte got removed
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.9.0
+ *
+ * @param string $sidebar, Sidebar id got changed
+ * @param string $removed_widget, Widget id got removed
+ * @param string $sidebar_name, Name of the sidebar that changed Since 5.0.0
+ * @param string $removed_widget_name, Name of the widget that got removed Since 5.0.0
+ */
+ do_action( 'jetpack_widget_removed', $sidebar, $removed_widget, $sidebar_name, $removed_widget_name );
+ } else {
+ $moved_to_inactive[] = $removed_widget;
+ }
+ }
+ return $moved_to_inactive;
+
+ }
+
+ /**
+ * Sync a reorder of widgets within a sidebar.
+ *
+ * @access public
+ *
+ * @todo Refactor serialize() to a json_encode().
+ *
+ * @param array $new_widgets New widgets.
+ * @param array $old_widgets Old widgets.
+ * @param string $sidebar Sidebar ID.
+ */
+ public function sync_widgets_reordered( $new_widgets, $old_widgets, $sidebar ) {
+ $added_widgets = array_diff( $new_widgets, $old_widgets );
+ if ( ! empty( $added_widgets ) ) {
+ return;
+ }
+ $removed_widgets = array_diff( $old_widgets, $new_widgets );
+ if ( ! empty( $removed_widgets ) ) {
+ return;
+ }
+
+ // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
+ if ( serialize( $old_widgets ) !== serialize( $new_widgets ) ) {
+ $sidebar_name = $this->get_sidebar_name( $sidebar );
+ /**
+ * Helps Sync log that a sidebar id got reordered
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.9.0
+ *
+ * @param string $sidebar, Sidebar id got changed
+ * @param string $sidebar_name, Name of the sidebar that changed Since 5.0.0
+ */
+ do_action( 'jetpack_widget_reordered', $sidebar, $sidebar_name );
+ }
+
+ }
+
+ /**
+ * Handle the update of the sidebars and widgets mapping option.
+ *
+ * @access public
+ *
+ * @param mixed $old_value The old option value.
+ * @param mixed $new_value The new option value.
+ */
+ public function sync_sidebar_widgets_actions( $old_value, $new_value ) {
+ // Don't really know how to deal with different array_values yet.
+ if (
+ ( isset( $old_value['array_version'] ) && 3 !== $old_value['array_version'] ) ||
+ ( isset( $new_value['array_version'] ) && 3 !== $new_value['array_version'] )
+ ) {
+ return;
+ }
+
+ $moved_to_inactive_ids = array();
+ $moved_to_sidebar = array();
+
+ foreach ( $new_value as $sidebar => $new_widgets ) {
+ if ( in_array( $sidebar, array( 'array_version', 'wp_inactive_widgets' ), true ) ) {
+ continue;
+ }
+ $old_widgets = isset( $old_value[ $sidebar ] )
+ ? $old_value[ $sidebar ]
+ : array();
+
+ if ( ! is_array( $new_widgets ) ) {
+ $new_widgets = array();
+ }
+
+ $moved_to_inactive_recently = $this->sync_remove_widgets_from_sidebar( $new_widgets, $old_widgets, $sidebar, $new_value['wp_inactive_widgets'] );
+ $moved_to_inactive_ids = array_merge( $moved_to_inactive_ids, $moved_to_inactive_recently );
+
+ $moved_to_sidebar_recently = $this->sync_add_widgets_to_sidebar( $new_widgets, $old_widgets, $sidebar );
+ $moved_to_sidebar = array_merge( $moved_to_sidebar, $moved_to_sidebar_recently );
+
+ $this->sync_widgets_reordered( $new_widgets, $old_widgets, $sidebar );
+
+ }
+
+ // Don't sync either jetpack_widget_moved_to_inactive or jetpack_cleared_inactive_widgets if theme was switched.
+ if ( $this->is_theme_switch() ) {
+ return;
+ }
+
+ // Treat inactive sidebar a bit differently.
+ if ( ! empty( $moved_to_inactive_ids ) ) {
+ $moved_to_inactive_name = array_map( array( $this, 'get_widget_name' ), $moved_to_inactive_ids );
+ /**
+ * Helps Sync log that a widgets IDs got moved to in active
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.9.0
+ *
+ * @param array $moved_to_inactive_ids, Array of widgets id that moved to inactive id got changed
+ * @param array $moved_to_inactive_names, Array of widgets names that moved to inactive id got changed Since 5.0.0
+ */
+ do_action( 'jetpack_widget_moved_to_inactive', $moved_to_inactive_ids, $moved_to_inactive_name );
+ } elseif ( empty( $moved_to_sidebar ) && empty( $new_value['wp_inactive_widgets'] ) && ! empty( $old_value['wp_inactive_widgets'] ) ) {
+ /**
+ * Helps Sync log that a got cleared from inactive.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.9.0
+ */
+ do_action( 'jetpack_cleared_inactive_widgets' );
+ }
+ }
+
+ /**
+ * Retrieve the theme data for the current or a specific theme.
+ *
+ * @access private
+ *
+ * @param \WP_Theme $theme Theme object. Optional, will default to the current theme.
+ *
+ * @return array Theme data.
+ */
+ private function get_theme_info( $theme = null ) {
+ $theme_support = array();
+
+ // We are trying to get the current theme info.
+ if ( null === $theme ) {
+ $theme = wp_get_theme();
+ }
+
+ $theme_support['name'] = $theme->get( 'Name' );
+ $theme_support['version'] = $theme->get( 'Version' );
+ $theme_support['slug'] = $theme->get_stylesheet();
+ $theme_support['uri'] = $theme->get( 'ThemeURI' );
+
+ return $theme_support;
+ }
+
+ /**
+ * Whether we've deleted a theme in the current request.
+ *
+ * @access private
+ *
+ * @return boolean True if this is a theme deletion request, false otherwise.
+ */
+ private function get_delete_theme_call() {
+ // Intentional usage of `debug_backtrace()` for production needs.
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
+ $backtrace = debug_backtrace();
+ $delete_theme_call = null;
+ foreach ( $backtrace as $call ) {
+ if ( isset( $call['function'] ) && 'delete_theme' === $call['function'] ) {
+ $delete_theme_call = $call;
+ break;
+ }
+ }
+ return $delete_theme_call;
+ }
+
+ /**
+ * Whether we've switched to another theme in the current request.
+ *
+ * @access private
+ *
+ * @return boolean True if this is a theme switch request, false otherwise.
+ */
+ private function is_theme_switch() {
+ return did_action( 'after_switch_theme' );
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+ /**
+ * Retrieve a set of constants by their IDs.
+ *
+ * @access public
+ *
+ * @param string $object_type Object type.
+ * @param array $ids Object IDs.
+ * @return array Array of objects.
+ */
+ public function get_objects_by_id( $object_type, $ids ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ if ( 'theme-info' !== $object_type ) {
+ return array();
+ }
+
+ return array( $this->get_theme_info() );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-updates.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-updates.php
new file mode 100644
index 00000000..8a2e8db3
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-updates.php
@@ -0,0 +1,585 @@
+<?php
+/**
+ * Updates sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Constants as Jetpack_Constants;
+
+/**
+ * Class to handle sync for updates.
+ */
+class Updates extends Module {
+ /**
+ * Name of the updates checksum option.
+ *
+ * @var string
+ */
+ const UPDATES_CHECKSUM_OPTION_NAME = 'jetpack_updates_sync_checksum';
+
+ /**
+ * WordPress Version.
+ *
+ * @access private
+ *
+ * @var string
+ */
+ private $old_wp_version = null;
+
+ /**
+ * The current updates.
+ *
+ * @access private
+ *
+ * @var array
+ */
+ private $updates = array();
+
+ /**
+ * Set module defaults.
+ *
+ * @access public
+ */
+ public function set_defaults() {
+ $this->updates = array();
+ }
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'updates';
+ }
+
+ /**
+ * Initialize updates action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ global $wp_version;
+ $this->old_wp_version = $wp_version;
+ add_action( 'set_site_transient_update_plugins', array( $this, 'validate_update_change' ), 10, 3 );
+ add_action( 'set_site_transient_update_themes', array( $this, 'validate_update_change' ), 10, 3 );
+ add_action( 'set_site_transient_update_core', array( $this, 'validate_update_change' ), 10, 3 );
+
+ add_action( 'jetpack_update_plugins_change', $callable );
+ add_action( 'jetpack_update_themes_change', $callable );
+ add_action( 'jetpack_update_core_change', $callable );
+
+ add_filter(
+ 'jetpack_sync_before_enqueue_jetpack_update_plugins_change',
+ array(
+ $this,
+ 'filter_update_keys',
+ ),
+ 10,
+ 2
+ );
+ add_filter(
+ 'jetpack_sync_before_enqueue_upgrader_process_complete',
+ array(
+ $this,
+ 'filter_upgrader_process_complete',
+ ),
+ 10,
+ 2
+ );
+
+ add_action( 'automatic_updates_complete', $callable );
+
+ if ( is_multisite() ) {
+ add_filter( 'pre_update_site_option_wpmu_upgrade_site', array( $this, 'update_core_network_event' ), 10, 2 );
+ add_action( 'jetpack_sync_core_update_network', $callable, 10, 3 );
+ }
+
+ // Send data when update completes.
+ add_action( '_core_updated_successfully', array( $this, 'update_core' ) );
+ add_action( 'jetpack_sync_core_reinstalled_successfully', $callable );
+ add_action( 'jetpack_sync_core_autoupdated_successfully', $callable, 10, 2 );
+ add_action( 'jetpack_sync_core_updated_successfully', $callable, 10, 2 );
+
+ }
+
+ /**
+ * Initialize updates action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_updates', $callable );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_updates', array( $this, 'expand_updates' ) );
+ add_filter( 'jetpack_sync_before_send_jetpack_update_themes_change', array( $this, 'expand_themes' ) );
+ }
+
+ /**
+ * Handle a core network update.
+ *
+ * @access public
+ *
+ * @param int $wp_db_version Current version of the WordPress database.
+ * @param int $old_wp_db_version Old version of the WordPress database.
+ * @return int Current version of the WordPress database.
+ */
+ public function update_core_network_event( $wp_db_version, $old_wp_db_version ) {
+ global $wp_version;
+ /**
+ * Sync event for when core wp network updates to a new db version
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.0.0
+ *
+ * @param int $wp_db_version the latest wp_db_version
+ * @param int $old_wp_db_version previous wp_db_version
+ * @param string $wp_version the latest wp_version
+ */
+ do_action( 'jetpack_sync_core_update_network', $wp_db_version, $old_wp_db_version, $wp_version );
+ return $wp_db_version;
+ }
+
+ /**
+ * Handle a core update.
+ *
+ * @access public
+ *
+ * @todo Implement nonce or refactor to use `admin_post_{$action}` hooks instead.
+ *
+ * @param string $new_wp_version The new WP core version.
+ */
+ public function update_core( $new_wp_version ) {
+ global $pagenow;
+
+ // // phpcs:ignore WordPress.Security.NonceVerification.Recommended
+ if ( isset( $_GET['action'] ) && 'do-core-reinstall' === $_GET['action'] ) {
+ /**
+ * Sync event that fires when core reinstall was successful
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.0.0
+ *
+ * @param string $new_wp_version the updated WordPress version
+ */
+ do_action( 'jetpack_sync_core_reinstalled_successfully', $new_wp_version );
+ return;
+ }
+
+ // Core was autoupdated.
+ if (
+ 'update-core.php' !== $pagenow &&
+ ! Jetpack_Constants::is_true( 'REST_API_REQUEST' ) // WP.com rest api calls should never be marked as a core autoupdate.
+ ) {
+ /**
+ * Sync event that fires when core autoupdate was successful
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.0.0
+ *
+ * @param string $new_wp_version the updated WordPress version
+ * @param string $old_wp_version the previous WordPress version
+ */
+ do_action( 'jetpack_sync_core_autoupdated_successfully', $new_wp_version, $this->old_wp_version );
+ return;
+ }
+ /**
+ * Sync event that fires when core update was successful
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.0.0
+ *
+ * @param string $new_wp_version the updated WordPress version
+ * @param string $old_wp_version the previous WordPress version
+ */
+ do_action( 'jetpack_sync_core_updated_successfully', $new_wp_version, $this->old_wp_version );
+ }
+
+ /**
+ * Retrieve the checksum for an update.
+ *
+ * @access public
+ *
+ * @param object $update The update object.
+ * @param string $transient The transient we're retrieving a checksum for.
+ * @return int The checksum.
+ */
+ public function get_update_checksum( $update, $transient ) {
+ $updates = array();
+ $no_updated = array();
+ switch ( $transient ) {
+ case 'update_plugins':
+ if ( ! empty( $update->response ) && is_array( $update->response ) ) {
+ foreach ( $update->response as $plugin_slug => $response ) {
+ if ( ! empty( $plugin_slug ) && isset( $response->new_version ) ) {
+ $updates[] = array( $plugin_slug => $response->new_version );
+ }
+ }
+ }
+ if ( ! empty( $update->no_update ) ) {
+ $no_updated = array_keys( $update->no_update );
+ }
+
+ if ( ! isset( $no_updated['jetpack/jetpack.php'] ) && isset( $updates['jetpack/jetpack.php'] ) ) {
+ return false;
+ }
+
+ break;
+ case 'update_themes':
+ if ( ! empty( $update->response ) && is_array( $update->response ) ) {
+ foreach ( $update->response as $theme_slug => $response ) {
+ if ( ! empty( $theme_slug ) && isset( $response['new_version'] ) ) {
+ $updates[] = array( $theme_slug => $response['new_version'] );
+ }
+ }
+ }
+
+ if ( ! empty( $update->checked ) ) {
+ $no_updated = $update->checked;
+ }
+
+ break;
+ case 'update_core':
+ if ( ! empty( $update->updates ) && is_array( $update->updates ) ) {
+ foreach ( $update->updates as $response ) {
+ if ( ! empty( $response->response ) && 'latest' === $response->response ) {
+ continue;
+ }
+ if ( ! empty( $response->response ) && isset( $response->packages->full ) ) {
+ $updates[] = array( $response->response => $response->packages->full );
+ }
+ }
+ }
+
+ if ( ! empty( $update->version_checked ) ) {
+ $no_updated = $update->version_checked;
+ }
+
+ if ( empty( $updates ) ) {
+ return false;
+ }
+ break;
+
+ }
+ if ( empty( $updates ) && empty( $no_updated ) ) {
+ return false;
+ }
+ return $this->get_check_sum( array( $no_updated, $updates ) );
+ }
+
+ /**
+ * Validate a change coming from an update before sending for sync.
+ *
+ * @access public
+ *
+ * @param mixed $value Site transient value.
+ * @param int $expiration Time until transient expiration in seconds.
+ * @param string $transient Transient name.
+ */
+ public function validate_update_change( $value, $expiration, $transient ) {
+ $new_checksum = $this->get_update_checksum( $value, $transient );
+
+ if ( false === $new_checksum ) {
+ return;
+ }
+
+ $checksums = get_option( self::UPDATES_CHECKSUM_OPTION_NAME, array() );
+
+ if ( isset( $checksums[ $transient ] ) && $checksums[ $transient ] === $new_checksum ) {
+ return;
+ }
+
+ $checksums[ $transient ] = $new_checksum;
+
+ update_option( self::UPDATES_CHECKSUM_OPTION_NAME, $checksums );
+ if ( 'update_core' === $transient ) {
+ /**
+ * Trigger a change to core update that we want to sync.
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.1.0
+ *
+ * @param array $value Contains info that tells us what needs updating.
+ */
+ do_action( 'jetpack_update_core_change', $value );
+ return;
+ }
+ if ( empty( $this->updates ) ) {
+ // Lets add the shutdown method once and only when the updates move from empty to filled with something.
+ add_action( 'shutdown', array( $this, 'sync_last_event' ), 9 );
+ }
+ if ( ! isset( $this->updates[ $transient ] ) ) {
+ $this->updates[ $transient ] = array();
+ }
+ $this->updates[ $transient ][] = $value;
+ }
+
+ /**
+ * Sync the last update only.
+ *
+ * @access public
+ */
+ public function sync_last_event() {
+ foreach ( $this->updates as $transient => $values ) {
+ $value = end( $values ); // Only send over the last value.
+ /**
+ * Trigger a change to a specific update that we want to sync.
+ * Triggers one of the following actions:
+ * - jetpack_{$transient}_change
+ * - jetpack_update_plugins_change
+ * - jetpack_update_themes_change
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.1.0
+ *
+ * @param array $value Contains info that tells us what needs updating.
+ */
+ do_action( "jetpack_{$transient}_change", $value );
+ }
+
+ }
+
+ /**
+ * Enqueue the updates actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ /**
+ * Tells the client to sync all updates to the server
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param boolean Whether to expand updates (should always be true)
+ */
+ do_action( 'jetpack_full_sync_updates', true );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 1, true );
+ }
+
+ /**
+ * Send the updates actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $send_until The timestamp until the current request can send.
+ * @param array $state This module Full Sync status.
+ *
+ * @return array This module Full Sync status.
+ */
+ public function send_full_sync_actions( $config, $send_until, $state ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // we call this instead of do_action when sending immediately.
+ $this->send_action( 'jetpack_full_sync_updates', array( true ) );
+
+ // The number of actions enqueued, and next module state (true == done).
+ return array( 'finished' => true );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 1;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_updates' );
+ }
+
+ /**
+ * Retrieve all updates that we're interested in.
+ *
+ * @access public
+ *
+ * @return array All updates.
+ */
+ public function get_all_updates() {
+ return array(
+ 'core' => get_site_transient( 'update_core' ),
+ 'plugins' => get_site_transient( 'update_plugins' ),
+ 'themes' => get_site_transient( 'update_themes' ),
+ );
+ }
+
+ /**
+ * Remove unnecessary keys from synced updates data.
+ *
+ * @access public
+ *
+ * @param array $args Hook arguments.
+ * @return array $args Hook arguments.
+ */
+ public function filter_update_keys( $args ) {
+ $updates = $args[0];
+
+ if ( isset( $updates->no_update ) ) {
+ unset( $updates->no_update );
+ }
+
+ return $args;
+ }
+
+ /**
+ * Filter out upgrader object from the completed upgrader action args.
+ *
+ * @access public
+ *
+ * @param array $args Hook arguments.
+ * @return array $args Filtered hook arguments.
+ */
+ public function filter_upgrader_process_complete( $args ) {
+ array_shift( $args );
+
+ return $args;
+ }
+
+ /**
+ * Expand the updates within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The hook parameters.
+ */
+ public function expand_updates( $args ) {
+ if ( $args[0] ) {
+ return $this->get_all_updates();
+ }
+
+ return $args;
+ }
+
+ /**
+ * Expand the themes within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook parameters.
+ * @return array $args The hook parameters.
+ */
+ public function expand_themes( $args ) {
+ if ( ! isset( $args[0], $args[0]->response ) ) {
+ return $args;
+ }
+ if ( ! is_array( $args[0]->response ) ) {
+ // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
+ trigger_error( 'Warning: Not an Array as expected but -> ' . wp_json_encode( $args[0]->response ) . ' instead', E_USER_WARNING );
+ return $args;
+ }
+ foreach ( $args[0]->response as $stylesheet => &$theme_data ) {
+ $theme = wp_get_theme( $stylesheet );
+ $theme_data['name'] = $theme->name;
+ }
+ return $args;
+ }
+
+ /**
+ * Perform module cleanup.
+ * Deletes any transients and options that this module uses.
+ * Usually triggered when uninstalling the plugin.
+ *
+ * @access public
+ */
+ public function reset_data() {
+ delete_option( self::UPDATES_CHECKSUM_OPTION_NAME );
+ }
+
+ /**
+ * Return Total number of objects.
+ *
+ * @param array $config Full Sync config.
+ *
+ * @return int total
+ */
+ public function total( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return 3;
+ }
+
+ /**
+ * Retrieve a set of updates by their IDs.
+ *
+ * @access public
+ *
+ * @param string $object_type Object type.
+ * @param array $ids Object IDs.
+ * @return array Array of objects.
+ */
+ public function get_objects_by_id( $object_type, $ids ) {
+ if ( empty( $ids ) || empty( $object_type ) || 'update' !== $object_type ) {
+ return array();
+ }
+
+ $objects = array();
+ foreach ( (array) $ids as $id ) {
+ $object = $this->get_object_by_id( $object_type, $id );
+
+ if ( 'all' === $id ) {
+ // If all was requested it contains all updates and can simply be returned.
+ return $object;
+ }
+ $objects[ $id ] = $object;
+ }
+
+ return $objects;
+ }
+
+ /**
+ * Retrieve a update by its id.
+ *
+ * @access public
+ *
+ * @param string $object_type Type of the sync object.
+ * @param string $id ID of the sync object.
+ * @return mixed Value of Update.
+ */
+ public function get_object_by_id( $object_type, $id ) {
+ if ( 'update' === $object_type ) {
+
+ // Only whitelisted constants can be returned.
+ if ( in_array( $id, array( 'core', 'plugins', 'themes' ), true ) ) {
+ return get_site_transient( 'update_' . $id );
+ } elseif ( 'all' === $id ) {
+ return $this->get_all_updates();
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-users.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-users.php
new file mode 100644
index 00000000..c53cfc5e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-users.php
@@ -0,0 +1,871 @@
+<?php
+/**
+ * Users sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use Automattic\Jetpack\Constants as Jetpack_Constants;
+use Automattic\Jetpack\Password_Checker;
+use Automattic\Jetpack\Sync\Defaults;
+
+/**
+ * Class to handle sync for users.
+ */
+class Users extends Module {
+ /**
+ * Maximum number of users to sync initially.
+ *
+ * @var int
+ */
+ const MAX_INITIAL_SYNC_USERS = 100;
+
+ /**
+ * User flags we care about.
+ *
+ * @access protected
+ *
+ * @var array
+ */
+ protected $flags = array();
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'users';
+ }
+
+ /**
+ * The table in the database.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function table_name() {
+ return 'usermeta';
+ }
+
+ /**
+ * The id field in the database.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function id_field() {
+ return 'user_id';
+ }
+
+ /**
+ * Retrieve a user by its ID.
+ * This is here to support the backfill API.
+ *
+ * @access public
+ *
+ * @param string $object_type Type of the sync object.
+ * @param int $id ID of the sync object.
+ * @return \WP_User|bool Filtered \WP_User object, or false if the object is not a user.
+ */
+ public function get_object_by_id( $object_type, $id ) {
+ if ( 'user' === $object_type ) {
+ $user = get_user_by( 'id', (int) $id );
+ if ( $user ) {
+ return $this->sanitize_user_and_expand( $user );
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Initialize users action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ // Users.
+ add_action( 'user_register', array( $this, 'user_register_handler' ) );
+ add_action( 'profile_update', array( $this, 'save_user_handler' ), 10, 2 );
+
+ add_action( 'add_user_to_blog', array( $this, 'add_user_to_blog_handler' ) );
+ add_action( 'jetpack_sync_add_user', $callable, 10, 2 );
+
+ add_action( 'jetpack_sync_register_user', $callable, 10, 2 );
+ add_action( 'jetpack_sync_save_user', $callable, 10, 2 );
+
+ add_action( 'jetpack_sync_user_locale', $callable, 10, 2 );
+ add_action( 'jetpack_sync_user_locale_delete', $callable, 10, 1 );
+
+ add_action( 'deleted_user', array( $this, 'deleted_user_handler' ), 10, 2 );
+ add_action( 'jetpack_deleted_user', $callable, 10, 3 );
+ add_action( 'remove_user_from_blog', array( $this, 'remove_user_from_blog_handler' ), 10, 2 );
+ add_action( 'jetpack_removed_user_from_blog', $callable, 10, 2 );
+
+ // User roles.
+ add_action( 'add_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
+ add_action( 'set_user_role', array( $this, 'save_user_role_handler' ), 10, 3 );
+ add_action( 'remove_user_role', array( $this, 'save_user_role_handler' ), 10, 2 );
+
+ // User capabilities.
+ add_action( 'added_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
+ add_action( 'updated_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
+ add_action( 'deleted_user_meta', array( $this, 'maybe_save_user_meta' ), 10, 4 );
+
+ // User authentication.
+ add_filter( 'authenticate', array( $this, 'authenticate_handler' ), 1000, 3 );
+ add_action( 'wp_login', array( $this, 'wp_login_handler' ), 10, 2 );
+
+ add_action( 'jetpack_wp_login', $callable, 10, 3 );
+
+ add_action( 'wp_logout', $callable, 10, 0 );
+ add_action( 'wp_masterbar_logout', $callable, 10, 1 );
+
+ // Add on init.
+ add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_add_user', array( $this, 'expand_action' ) );
+ add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_register_user', array( $this, 'expand_action' ) );
+ add_filter( 'jetpack_sync_before_enqueue_jetpack_sync_save_user', array( $this, 'expand_action' ) );
+ }
+
+ /**
+ * Initialize users action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_users', $callable );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ add_filter( 'jetpack_sync_before_send_jetpack_wp_login', array( $this, 'expand_login_username' ), 10, 1 );
+ add_filter( 'jetpack_sync_before_send_wp_logout', array( $this, 'expand_logout_username' ), 10, 2 );
+
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_users', array( $this, 'expand_users' ) );
+ }
+
+ /**
+ * Retrieve a user by a user ID or object.
+ *
+ * @access private
+ *
+ * @param mixed $user User object or ID.
+ * @return \WP_User User object, or `null` if user invalid/not found.
+ */
+ private function get_user( $user ) {
+ if ( is_numeric( $user ) ) {
+ $user = get_user_by( 'id', $user );
+ }
+ if ( $user instanceof \WP_User ) {
+ return $user;
+ }
+ return null;
+ }
+
+ /**
+ * Sanitize a user object.
+ * Removes the password from the user object because we don't want to sync it.
+ *
+ * @access public
+ *
+ * @todo Refactor `serialize`/`unserialize` to `wp_json_encode`/`wp_json_decode`.
+ *
+ * @param \WP_User $user User object.
+ * @return \WP_User Sanitized user object.
+ */
+ public function sanitize_user( $user ) {
+ $user = $this->get_user( $user );
+ // This creates a new user object and stops the passing of the object by reference.
+ // // phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize, WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
+ $user = unserialize( serialize( $user ) );
+
+ if ( is_object( $user ) && is_object( $user->data ) ) {
+ unset( $user->data->user_pass );
+ }
+ return $user;
+ }
+
+ /**
+ * Expand a particular user.
+ *
+ * @access public
+ *
+ * @param \WP_User $user User object.
+ * @return \WP_User Expanded user object.
+ */
+ public function expand_user( $user ) {
+ if ( ! is_object( $user ) ) {
+ return null;
+ }
+ $user->allowed_mime_types = get_allowed_mime_types( $user );
+ $user->allcaps = $this->get_real_user_capabilities( $user );
+
+ // Only set the user locale if it is different from the site locale.
+ if ( get_locale() !== get_user_locale( $user->ID ) ) {
+ $user->locale = get_user_locale( $user->ID );
+ }
+
+ return $user;
+ }
+
+ /**
+ * Retrieve capabilities we care about for a particular user.
+ *
+ * @access public
+ *
+ * @param \WP_User $user User object.
+ * @return array User capabilities.
+ */
+ public function get_real_user_capabilities( $user ) {
+ $user_capabilities = array();
+ if ( is_wp_error( $user ) ) {
+ return $user_capabilities;
+ }
+ foreach ( Defaults::get_capabilities_whitelist() as $capability ) {
+ if ( user_can( $user, $capability ) ) {
+ $user_capabilities[ $capability ] = true;
+ }
+ }
+ return $user_capabilities;
+ }
+
+ /**
+ * Retrieve, expand and sanitize a user.
+ * Can be directly used in the sync user action handlers.
+ *
+ * @access public
+ *
+ * @param mixed $user User ID or user object.
+ * @return \WP_User Expanded and sanitized user object.
+ */
+ public function sanitize_user_and_expand( $user ) {
+ $user = $this->get_user( $user );
+ $user = $this->expand_user( $user );
+ return $this->sanitize_user( $user );
+ }
+
+ /**
+ * Expand the user within a hook before it is serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook arguments.
+ * @return array $args The hook arguments.
+ */
+ public function expand_action( $args ) {
+ // The first argument is always the user.
+ list( $user ) = $args;
+ if ( $user ) {
+ $args[0] = $this->sanitize_user_and_expand( $user );
+ return $args;
+ }
+
+ return false;
+ }
+
+ /**
+ * Expand the user username at login before being sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook arguments.
+ * @return array $args Expanded hook arguments.
+ */
+ public function expand_login_username( $args ) {
+ list( $login, $user, $flags ) = $args;
+ $user = $this->sanitize_user( $user );
+
+ return array( $login, $user, $flags );
+ }
+
+ /**
+ * Expand the user username at logout before being sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook arguments.
+ * @param int $user_id ID of the user.
+ * @return array $args Expanded hook arguments.
+ */
+ public function expand_logout_username( $args, $user_id ) {
+ $user = get_userdata( $user_id );
+ $user = $this->sanitize_user( $user );
+
+ $login = '';
+ if ( is_object( $user ) && is_object( $user->data ) ) {
+ $login = $user->data->user_login;
+ }
+
+ // If we don't have a user here lets not send anything.
+ if ( empty( $login ) ) {
+ return false;
+ }
+
+ return array( $login, $user );
+ }
+
+ /**
+ * Additional processing is needed for wp_login so we introduce this wrapper handler.
+ *
+ * @access public
+ *
+ * @param string $user_login The user login.
+ * @param \WP_User $user The user object.
+ */
+ public function wp_login_handler( $user_login, $user ) {
+ /**
+ * Fires when a user is logged into a site.
+ *
+ * @since 1.6.3
+ * @since-jetpack 7.2.0
+ *
+ * @param int $user_id The user ID.
+ * @param \WP_User $user The User Object of the user that currently logged in.
+ * @param array $params Any Flags that have been added during login.
+ */
+ do_action( 'jetpack_wp_login', $user->ID, $user, $this->get_flags( $user->ID ) );
+ $this->clear_flags( $user->ID );
+ }
+
+ /**
+ * A hook for the authenticate event that checks the password strength.
+ *
+ * @access public
+ *
+ * @param \WP_Error|\WP_User $user The user object, or an error.
+ * @param string $username The username.
+ * @param string $password The password used to authenticate.
+ * @return \WP_Error|\WP_User the same object that was passed into the function.
+ */
+ public function authenticate_handler( $user, $username, $password ) {
+ // In case of cookie authentication we don't do anything here.
+ if ( empty( $password ) ) {
+ return $user;
+ }
+
+ // We are only interested in successful authentication events.
+ if ( is_wp_error( $user ) || ! ( $user instanceof \WP_User ) ) {
+ return $user;
+ }
+
+ $password_checker = new Password_Checker( $user->ID );
+
+ $test_results = $password_checker->test( $password, true );
+
+ // If the password passes tests, we don't do anything.
+ if ( empty( $test_results['test_results']['failed'] ) ) {
+ return $user;
+ }
+
+ $this->add_flags(
+ $user->ID,
+ array(
+ 'warning' => 'The password failed at least one strength test.',
+ 'failures' => $test_results['test_results']['failed'],
+ )
+ );
+
+ return $user;
+ }
+
+ /**
+ * Handler for after the user is deleted.
+ *
+ * @access public
+ *
+ * @param int $deleted_user_id ID of the deleted user.
+ * @param int $reassigned_user_id ID of the user the deleted user's posts are reassigned to (if any).
+ */
+ public function deleted_user_handler( $deleted_user_id, $reassigned_user_id = '' ) {
+ $is_multisite = is_multisite();
+ /**
+ * Fires when a user is deleted on a site
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.4.0
+ *
+ * @param int $deleted_user_id - ID of the deleted user.
+ * @param int $reassigned_user_id - ID of the user the deleted user's posts are reassigned to (if any).
+ * @param bool $is_multisite - Whether this site is a multisite installation.
+ */
+ do_action( 'jetpack_deleted_user', $deleted_user_id, $reassigned_user_id, $is_multisite );
+ }
+
+ /**
+ * Handler for user registration.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the deleted user.
+ */
+ public function user_register_handler( $user_id ) {
+ // Ensure we only sync users who are members of the current blog.
+ if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
+ return;
+ }
+
+ if ( Jetpack_Constants::is_true( 'JETPACK_INVITE_ACCEPTED' ) ) {
+ $this->add_flags( $user_id, array( 'invitation_accepted' => true ) );
+ }
+ /**
+ * Fires when a new user is registered on a site
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.9.0
+ *
+ * @param object The WP_User object
+ */
+ do_action( 'jetpack_sync_register_user', $user_id, $this->get_flags( $user_id ) );
+ $this->clear_flags( $user_id );
+
+ }
+
+ /**
+ * Handler for user addition to the current blog.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ */
+ public function add_user_to_blog_handler( $user_id ) {
+ // Ensure we only sync users who are members of the current blog.
+ if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
+ return;
+ }
+
+ if ( Jetpack_Constants::is_true( 'JETPACK_INVITE_ACCEPTED' ) ) {
+ $this->add_flags( $user_id, array( 'invitation_accepted' => true ) );
+ }
+
+ /**
+ * Fires when a user is added on a site
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.9.0
+ *
+ * @param object The WP_User object
+ */
+ do_action( 'jetpack_sync_add_user', $user_id, $this->get_flags( $user_id ) );
+ $this->clear_flags( $user_id );
+ }
+
+ /**
+ * Handler for user save.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ * @param \WP_User $old_user_data User object before the changes.
+ */
+ public function save_user_handler( $user_id, $old_user_data = null ) {
+ // Ensure we only sync users who are members of the current blog.
+ if ( ! is_user_member_of_blog( $user_id, get_current_blog_id() ) ) {
+ return;
+ }
+
+ $user = get_user_by( 'id', $user_id );
+
+ // Older versions of WP don't pass the old_user_data in ->data.
+ if ( isset( $old_user_data->data ) ) {
+ $old_user = $old_user_data->data;
+ } else {
+ $old_user = $old_user_data;
+ }
+
+ if ( null !== $old_user && $user->user_pass !== $old_user->user_pass ) {
+ $this->flags[ $user_id ]['password_changed'] = true;
+ }
+ if ( null !== $old_user && $user->data->user_email !== $old_user->user_email ) {
+ /**
+ * The '_new_email' user meta is deleted right after the call to wp_update_user
+ * that got us to this point so if it's still set then this was a user confirming
+ * their new email address.
+ */
+ if ( 1 === (int) get_user_meta( $user->ID, '_new_email', true ) ) {
+ $this->flags[ $user_id ]['email_changed'] = true;
+ }
+ }
+
+ /**
+ * Fires when the client needs to sync an updated user.
+ *
+ * @since 1.6.3
+ * @since-jetpack 4.2.0
+ *
+ * @param \WP_User The WP_User object
+ * @param array State - New since 5.8.0
+ */
+ do_action( 'jetpack_sync_save_user', $user_id, $this->get_flags( $user_id ) );
+ $this->clear_flags( $user_id );
+ }
+
+ /**
+ * Handler for user role change.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ * @param string $role New user role.
+ * @param array $old_roles Previous user roles.
+ */
+ public function save_user_role_handler( $user_id, $role, $old_roles = null ) {
+ $this->add_flags(
+ $user_id,
+ array(
+ 'role_changed' => true,
+ 'previous_role' => $old_roles,
+ )
+ );
+
+ // The jetpack_sync_register_user payload is identical to jetpack_sync_save_user, don't send both.
+ if ( $this->is_create_user() || $this->is_add_user_to_blog() ) {
+ return;
+ }
+ /**
+ * This action is documented already in this file
+ */
+ do_action( 'jetpack_sync_save_user', $user_id, $this->get_flags( $user_id ) );
+ $this->clear_flags( $user_id );
+ }
+
+ /**
+ * Retrieve current flags for a particular user.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ * @return array Current flags of the user.
+ */
+ public function get_flags( $user_id ) {
+ if ( isset( $this->flags[ $user_id ] ) ) {
+ return $this->flags[ $user_id ];
+ }
+ return array();
+ }
+
+ /**
+ * Clear the flags of a particular user.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ */
+ public function clear_flags( $user_id ) {
+ if ( isset( $this->flags[ $user_id ] ) ) {
+ unset( $this->flags[ $user_id ] );
+ }
+ }
+
+ /**
+ * Add flags to a particular user.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ * @param array $flags New flags to add for the user.
+ */
+ public function add_flags( $user_id, $flags ) {
+ $this->flags[ $user_id ] = wp_parse_args( $flags, $this->get_flags( $user_id ) );
+ }
+
+ /**
+ * Save the user meta, if we're interested in it.
+ * Also uses the time to add flags for the user.
+ *
+ * @access public
+ *
+ * @param int $meta_id ID of the meta object.
+ * @param int $user_id ID of the user.
+ * @param string $meta_key Meta key.
+ * @param mixed $value Meta value.
+ */
+ public function maybe_save_user_meta( $meta_id, $user_id, $meta_key, $value ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ if ( 'locale' === $meta_key ) {
+ $this->add_flags( $user_id, array( 'locale_changed' => true ) );
+ }
+
+ $user = get_user_by( 'id', $user_id );
+ if ( isset( $user->cap_key ) && $meta_key === $user->cap_key ) {
+ $this->add_flags( $user_id, array( 'capabilities_changed' => true ) );
+ }
+
+ if ( $this->is_create_user() || $this->is_add_user_to_blog() || $this->is_delete_user() ) {
+ return;
+ }
+
+ if ( isset( $this->flags[ $user_id ] ) ) {
+ /**
+ * This action is documented already in this file
+ */
+ do_action( 'jetpack_sync_save_user', $user_id, $this->get_flags( $user_id ) );
+ }
+ }
+
+ /**
+ * Enqueue the users actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
+ global $wpdb;
+
+ return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_users', $wpdb->usermeta, 'user_id', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @todo Refactor to prepare the SQL query before executing it.
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) {
+ global $wpdb;
+
+ $query = "SELECT count(*) FROM $wpdb->usermeta";
+
+ $where_sql = $this->get_where_sql( $config );
+ if ( $where_sql ) {
+ $query .= ' WHERE ' . $where_sql;
+ }
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $count = $wpdb->get_var( $query );
+
+ return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
+ }
+
+ /**
+ * Retrieve the WHERE SQL clause based on the module config.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return string WHERE SQL clause, or `null` if no comments are specified in the module config.
+ */
+ public function get_where_sql( $config ) {
+ global $wpdb;
+
+ $query = "meta_key = '{$wpdb->prefix}user_level' AND meta_value > 0";
+
+ // The $config variable is a list of user IDs to sync.
+ if ( is_array( $config ) ) {
+ $query .= ' AND user_id IN (' . implode( ',', array_map( 'intval', $config ) ) . ')';
+ }
+
+ return $query;
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_users' );
+ }
+
+ /**
+ * Retrieve initial sync user config.
+ *
+ * @access public
+ *
+ * @todo Refactor the SQL query to call $wpdb->prepare() before execution.
+ *
+ * @return array|boolean IDs of users to initially sync, or false if tbe number of users exceed the maximum.
+ */
+ public function get_initial_sync_user_config() {
+ global $wpdb;
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $user_ids = $wpdb->get_col( "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '{$wpdb->prefix}user_level' AND meta_value > 0 LIMIT " . ( self::MAX_INITIAL_SYNC_USERS + 1 ) );
+
+ if ( count( $user_ids ) <= self::MAX_INITIAL_SYNC_USERS ) {
+ return $user_ids;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Expand the users within a hook before they are serialized and sent to the server.
+ *
+ * @access public
+ *
+ * @param array $args The hook arguments.
+ * @return array $args The hook arguments.
+ */
+ public function expand_users( $args ) {
+ list( $user_ids, $previous_end ) = $args;
+
+ return array(
+ 'users' => array_map(
+ array( $this, 'sanitize_user_and_expand' ),
+ get_users(
+ array(
+ 'include' => $user_ids,
+ 'orderby' => 'ID',
+ 'order' => 'DESC',
+ )
+ )
+ ),
+ 'previous_end' => $previous_end,
+ );
+ }
+
+ /**
+ * Handler for user removal from a particular blog.
+ *
+ * @access public
+ *
+ * @param int $user_id ID of the user.
+ * @param int $blog_id ID of the blog.
+ */
+ public function remove_user_from_blog_handler( $user_id, $blog_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ // User is removed on add, see https://github.com/WordPress/WordPress/blob/0401cee8b36df3def8e807dd766adc02b359dfaf/wp-includes/ms-functions.php#L2114.
+ if ( $this->is_add_new_user_to_blog() ) {
+ return;
+ }
+
+ $reassigned_user_id = $this->get_reassigned_network_user_id();
+
+ // Note that we are in the context of the blog the user is removed from, see https://github.com/WordPress/WordPress/blob/473e1ba73bc5c18c72d7f288447503713d518790/wp-includes/ms-functions.php#L233.
+ /**
+ * Fires when a user is removed from a blog on a multisite installation
+ *
+ * @since 1.6.3
+ * @since-jetpack 5.4.0
+ *
+ * @param int $user_id - ID of the removed user
+ * @param int $reassigned_user_id - ID of the user the removed user's posts are reassigned to (if any).
+ */
+ do_action( 'jetpack_removed_user_from_blog', $user_id, $reassigned_user_id );
+ }
+
+ /**
+ * Whether we're adding a new user to a blog in this request.
+ *
+ * @access protected
+ *
+ * @return boolean
+ */
+ protected function is_add_new_user_to_blog() {
+ return $this->is_function_in_backtrace( 'add_new_user_to_blog' );
+ }
+
+ /**
+ * Whether we're adding an existing user to a blog in this request.
+ *
+ * @access protected
+ *
+ * @return boolean
+ */
+ protected function is_add_user_to_blog() {
+ return $this->is_function_in_backtrace( 'add_user_to_blog' );
+ }
+
+ /**
+ * Whether we're removing a user from a blog in this request.
+ *
+ * @access protected
+ *
+ * @return boolean
+ */
+ protected function is_delete_user() {
+ return $this->is_function_in_backtrace( array( 'wp_delete_user', 'remove_user_from_blog' ) );
+ }
+
+ /**
+ * Whether we're creating a user or adding a new user to a blog.
+ *
+ * @access protected
+ *
+ * @return boolean
+ */
+ protected function is_create_user() {
+ $functions = array(
+ 'add_new_user_to_blog', // Used to suppress jetpack_sync_save_user in save_user_cap_handler when user registered on multi site.
+ 'wp_create_user', // Used to suppress jetpack_sync_save_user in save_user_role_handler when user registered on multi site.
+ 'wp_insert_user', // Used to suppress jetpack_sync_save_user in save_user_cap_handler and save_user_role_handler when user registered on single site.
+ );
+
+ return $this->is_function_in_backtrace( $functions );
+ }
+
+ /**
+ * Retrieve the ID of the user the removed user's posts are reassigned to (if any).
+ *
+ * @return int ID of the user that got reassigned as the author of the posts.
+ */
+ protected function get_reassigned_network_user_id() {
+ $backtrace = debug_backtrace( false ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
+ foreach ( $backtrace as $call ) {
+ if (
+ 'remove_user_from_blog' === $call['function'] &&
+ 3 === count( $call['args'] )
+ ) {
+ return $call['args'][2];
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if one or more function names is in debug_backtrace.
+ *
+ * @access protected
+ *
+ * @param array|string $names Mixed string name of function or array of string names of functions.
+ * @return bool
+ */
+ protected function is_function_in_backtrace( $names ) {
+ $backtrace = debug_backtrace( false ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_debug_backtrace
+ if ( ! is_array( $names ) ) {
+ $names = array( $names );
+ }
+ $names_as_keys = array_flip( $names );
+
+ // Do check in constant O(1) time for PHP5.5+.
+ if ( function_exists( 'array_column' ) ) {
+ $backtrace_functions = array_column( $backtrace, 'function' ); // phpcs:ignore PHPCompatibility.FunctionUse.NewFunctions.array_columnFound
+ $backtrace_functions_as_keys = array_flip( $backtrace_functions );
+ $intersection = array_intersect_key( $backtrace_functions_as_keys, $names_as_keys );
+ return ! empty( $intersection );
+ }
+
+ // Do check in linear O(n) time for < PHP5.5 ( using isset at least prevents O(n^2) ).
+ foreach ( $backtrace as $call ) {
+ if ( isset( $names_as_keys[ $call['function'] ] ) ) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-woocommerce.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-woocommerce.php
new file mode 100644
index 00000000..493d9c43
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-woocommerce.php
@@ -0,0 +1,613 @@
+<?php
+/**
+ * WooCommerce sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+use WP_Error;
+
+/**
+ * Class to handle sync for WooCommerce.
+ */
+class WooCommerce extends Module {
+ /**
+ * Whitelist for order item meta we are interested to sync.
+ *
+ * @access private
+ *
+ * @var array
+ */
+ public static $order_item_meta_whitelist = array(
+ // See https://github.com/woocommerce/woocommerce/blob/master/includes/data-stores/class-wc-order-item-product-store.php#L20 .
+ '_product_id',
+ '_variation_id',
+ '_qty',
+ // Tax ones also included in below class
+ // See https://github.com/woocommerce/woocommerce/blob/master/includes/data-stores/class-wc-order-item-fee-data-store.php#L20 .
+ '_tax_class',
+ '_tax_status',
+ '_line_subtotal',
+ '_line_subtotal_tax',
+ '_line_total',
+ '_line_tax',
+ '_line_tax_data',
+ // See https://github.com/woocommerce/woocommerce/blob/master/includes/data-stores/class-wc-order-item-shipping-data-store.php#L20 .
+ 'method_id',
+ 'cost',
+ 'total_tax',
+ 'taxes',
+ // See https://github.com/woocommerce/woocommerce/blob/master/includes/data-stores/class-wc-order-item-tax-data-store.php#L20 .
+ 'rate_id',
+ 'label',
+ 'compound',
+ 'tax_amount',
+ 'shipping_tax_amount',
+ // See https://github.com/woocommerce/woocommerce/blob/master/includes/data-stores/class-wc-order-item-coupon-data-store.php .
+ 'discount_amount',
+ 'discount_amount_tax',
+ );
+
+ /**
+ * Name of the order item database table.
+ *
+ * @access private
+ *
+ * @var string
+ */
+ private $order_item_table_name;
+
+ /**
+ * The table in the database.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function table_name() {
+ return $this->order_item_table_name;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @global $wpdb
+ *
+ * @todo Should we refactor this to use $this->set_defaults() instead?
+ */
+ public function __construct() {
+ global $wpdb;
+ $this->order_item_table_name = $wpdb->prefix . 'woocommerce_order_items';
+
+ // Options, constants and post meta whitelists.
+ add_filter( 'jetpack_sync_options_whitelist', array( $this, 'add_woocommerce_options_whitelist' ), 10 );
+ add_filter( 'jetpack_sync_constants_whitelist', array( $this, 'add_woocommerce_constants_whitelist' ), 10 );
+ add_filter( 'jetpack_sync_post_meta_whitelist', array( $this, 'add_woocommerce_post_meta_whitelist' ), 10 );
+ add_filter( 'jetpack_sync_comment_meta_whitelist', array( $this, 'add_woocommerce_comment_meta_whitelist' ), 10 );
+
+ add_filter( 'jetpack_sync_before_enqueue_woocommerce_new_order_item', array( $this, 'filter_order_item' ) );
+ add_filter( 'jetpack_sync_before_enqueue_woocommerce_update_order_item', array( $this, 'filter_order_item' ) );
+ add_filter( 'jetpack_sync_whitelisted_comment_types', array( $this, 'add_review_comment_types' ) );
+
+ // Blacklist Action Scheduler comment types.
+ add_filter( 'jetpack_sync_prevent_sending_comment_data', array( $this, 'filter_action_scheduler_comments' ), 10, 2 );
+ }
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'woocommerce';
+ }
+
+ /**
+ * Initialize WooCommerce action listeners.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_listeners( $callable ) {
+ // Attributes.
+ add_action( 'woocommerce_attribute_added', $callable, 10, 2 );
+ add_action( 'woocommerce_attribute_updated', $callable, 10, 3 );
+ add_action( 'woocommerce_attribute_deleted', $callable, 10, 3 );
+
+ // Orders.
+ add_action( 'woocommerce_new_order', $callable, 10, 1 );
+ add_action( 'woocommerce_order_status_changed', $callable, 10, 3 );
+ add_action( 'woocommerce_payment_complete', $callable, 10, 1 );
+
+ // Order items.
+ add_action( 'woocommerce_new_order_item', $callable, 10, 4 );
+ add_action( 'woocommerce_update_order_item', $callable, 10, 4 );
+ add_action( 'woocommerce_delete_order_item', $callable, 10, 1 );
+ $this->init_listeners_for_meta_type( 'order_item', $callable );
+
+ // Payment tokens.
+ add_action( 'woocommerce_new_payment_token', $callable, 10, 1 );
+ add_action( 'woocommerce_payment_token_deleted', $callable, 10, 2 );
+ add_action( 'woocommerce_payment_token_updated', $callable, 10, 1 );
+ $this->init_listeners_for_meta_type( 'payment_token', $callable );
+
+ // Product downloads.
+ add_action( 'woocommerce_downloadable_product_download_log_insert', $callable, 10, 1 );
+ add_action( 'woocommerce_grant_product_download_access', $callable, 10, 1 );
+
+ // Tax rates.
+ add_action( 'woocommerce_tax_rate_added', $callable, 10, 2 );
+ add_action( 'woocommerce_tax_rate_updated', $callable, 10, 2 );
+ add_action( 'woocommerce_tax_rate_deleted', $callable, 10, 1 );
+
+ // Webhooks.
+ add_action( 'woocommerce_new_webhook', $callable, 10, 1 );
+ add_action( 'woocommerce_webhook_deleted', $callable, 10, 2 );
+ add_action( 'woocommerce_webhook_updated', $callable, 10, 1 );
+ }
+
+ /**
+ * Initialize WooCommerce action listeners for full sync.
+ *
+ * @access public
+ *
+ * @param callable $callable Action handler callable.
+ */
+ public function init_full_sync_listeners( $callable ) {
+ add_action( 'jetpack_full_sync_woocommerce_order_items', $callable ); // Also sends post meta.
+ }
+
+ /**
+ * Retrieve the actions that will be sent for this module during a full sync.
+ *
+ * @access public
+ *
+ * @return array Full sync actions of this module.
+ */
+ public function get_full_sync_actions() {
+ return array( 'jetpack_full_sync_woocommerce_order_items' );
+ }
+
+ /**
+ * Initialize the module in the sender.
+ *
+ * @access public
+ */
+ public function init_before_send() {
+ // Full sync.
+ add_filter( 'jetpack_sync_before_send_jetpack_full_sync_woocommerce_order_items', array( $this, 'expand_order_item_ids' ) );
+ }
+
+ /**
+ * Expand the order items properly.
+ *
+ * @access public
+ *
+ * @param array $args The hook arguments.
+ * @return array $args The hook arguments.
+ */
+ public function filter_order_item( $args ) {
+ // Make sure we always have all the data - prior to WooCommerce 3.0 we only have the user supplied data in the second argument and not the full details.
+ $args[1] = $this->build_order_item( $args[0] );
+ return $args;
+ }
+
+ /**
+ * Expand order item IDs to order items and their meta.
+ *
+ * @access public
+ *
+ * @todo Refactor table name to use a $wpdb->prepare placeholder.
+ *
+ * @param array $args The hook arguments.
+ * @return array $args Expanded order items with meta.
+ */
+ public function expand_order_item_ids( $args ) {
+ $order_item_ids = $args[0];
+
+ global $wpdb;
+
+ $order_item_ids_sql = implode( ', ', array_map( 'intval', $order_item_ids ) );
+
+ $order_items = $wpdb->get_results(
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ "SELECT * FROM $this->order_item_table_name WHERE order_item_id IN ( $order_item_ids_sql )"
+ );
+
+ return array(
+ $order_items,
+ $this->get_metadata( $order_item_ids, 'order_item', static::$order_item_meta_whitelist ),
+ );
+ }
+
+ /**
+ * Extract the full order item from the database by its ID.
+ *
+ * @access public
+ *
+ * @todo Refactor table name to use a $wpdb->prepare placeholder.
+ *
+ * @param int $order_item_id Order item ID.
+ * @return object Order item.
+ */
+ public function build_order_item( $order_item_id ) {
+ global $wpdb;
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ return $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $this->order_item_table_name WHERE order_item_id = %d", $order_item_id ) );
+ }
+
+ /**
+ * Enqueue the WooCommerce actions for full sync.
+ *
+ * @access public
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @param int $max_items_to_enqueue Maximum number of items to enqueue.
+ * @param boolean $state True if full sync has finished enqueueing this module, false otherwise.
+ * @return array Number of actions enqueued, and next module state.
+ */
+ public function enqueue_full_sync_actions( $config, $max_items_to_enqueue, $state ) {
+ return $this->enqueue_all_ids_as_action( 'jetpack_full_sync_woocommerce_order_items', $this->order_item_table_name, 'order_item_id', $this->get_where_sql( $config ), $max_items_to_enqueue, $state );
+ }
+
+ /**
+ * Retrieve an estimated number of actions that will be enqueued.
+ *
+ * @access public
+ *
+ * @todo Refactor the SQL query to use $wpdb->prepare().
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return array Number of items yet to be enqueued.
+ */
+ public function estimate_full_sync_actions( $config ) {
+ global $wpdb;
+
+ $query = "SELECT count(*) FROM $this->order_item_table_name WHERE " . $this->get_where_sql( $config );
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $count = $wpdb->get_var( $query );
+
+ return (int) ceil( $count / self::ARRAY_CHUNK_SIZE );
+ }
+
+ /**
+ * Retrieve the WHERE SQL clause based on the module config.
+ *
+ * @access private
+ *
+ * @param array $config Full sync configuration for this sync module.
+ * @return string WHERE SQL clause.
+ */
+ public function get_where_sql( $config ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+ return '1=1';
+ }
+
+ /**
+ * Add WooCommerce options to the options whitelist.
+ *
+ * @param array $list Existing options whitelist.
+ * @return array Updated options whitelist.
+ */
+ public function add_woocommerce_options_whitelist( $list ) {
+ return array_merge( $list, self::$wc_options_whitelist );
+ }
+
+ /**
+ * Add WooCommerce constants to the constants whitelist.
+ *
+ * @param array $list Existing constants whitelist.
+ * @return array Updated constants whitelist.
+ */
+ public function add_woocommerce_constants_whitelist( $list ) {
+ return array_merge( $list, self::$wc_constants_whitelist );
+ }
+
+ /**
+ * Add WooCommerce post meta to the post meta whitelist.
+ *
+ * @param array $list Existing post meta whitelist.
+ * @return array Updated post meta whitelist.
+ */
+ public function add_woocommerce_post_meta_whitelist( $list ) {
+ return array_merge( $list, self::$wc_post_meta_whitelist );
+ }
+
+ /**
+ * Add WooCommerce comment meta to the comment meta whitelist.
+ *
+ * @param array $list Existing comment meta whitelist.
+ * @return array Updated comment meta whitelist.
+ */
+ public function add_woocommerce_comment_meta_whitelist( $list ) {
+ return array_merge( $list, self::$wc_comment_meta_whitelist );
+ }
+
+ /**
+ * Adds 'revew' to the list of comment types so Sync will listen for status changes on 'reviews'.
+ *
+ * @access public
+ *
+ * @param array $comment_types The list of comment types prior to this filter.
+ * return array The list of comment types with 'review' added.
+ */
+ public function add_review_comment_types( $comment_types ) {
+ if ( is_array( $comment_types ) ) {
+ $comment_types[] = 'review';
+ }
+ return $comment_types;
+ }
+
+ /**
+ * Stop comments from the Action Scheduler from being synced.
+ * https://github.com/woocommerce/woocommerce/tree/e7762627c37ec1f7590e6cac4218ba0c6a20024d/includes/libraries/action-scheduler
+ *
+ * @since 1.6.3
+ * @since-jetpack 7.7.0
+ *
+ * @param boolean $can_sync Should we prevent comment data from bing synced to WordPress.com.
+ * @param mixed $comment WP_COMMENT object.
+ *
+ * @return bool
+ */
+ public function filter_action_scheduler_comments( $can_sync, $comment ) {
+ if ( isset( $comment->comment_agent ) && 'ActionScheduler' === $comment->comment_agent ) {
+ return true;
+ }
+ return $can_sync;
+ }
+
+ /**
+ * Whitelist for options we are interested to sync.
+ *
+ * @access private
+ * @static
+ *
+ * @var array
+ */
+ private static $wc_options_whitelist = array(
+ 'woocommerce_currency',
+ 'woocommerce_db_version',
+ 'woocommerce_weight_unit',
+ 'woocommerce_version',
+ 'woocommerce_unforce_ssl_checkout',
+ 'woocommerce_tax_total_display',
+ 'woocommerce_tax_round_at_subtotal',
+ 'woocommerce_tax_display_shop',
+ 'woocommerce_tax_display_cart',
+ 'woocommerce_prices_include_tax',
+ 'woocommerce_price_thousand_sep',
+ 'woocommerce_price_num_decimals',
+ 'woocommerce_price_decimal_sep',
+ 'woocommerce_notify_low_stock',
+ 'woocommerce_notify_low_stock_amount',
+ 'woocommerce_notify_no_stock',
+ 'woocommerce_notify_no_stock_amount',
+ 'woocommerce_manage_stock',
+ 'woocommerce_force_ssl_checkout',
+ 'woocommerce_hide_out_of_stock_items',
+ 'woocommerce_file_download_method',
+ 'woocommerce_enable_signup_and_login_from_checkout',
+ 'woocommerce_enable_shipping_calc',
+ 'woocommerce_enable_review_rating',
+ 'woocommerce_enable_guest_checkout',
+ 'woocommerce_enable_coupons',
+ 'woocommerce_enable_checkout_login_reminder',
+ 'woocommerce_enable_ajax_add_to_cart',
+ 'woocommerce_dimension_unit',
+ 'woocommerce_default_country',
+ 'woocommerce_default_customer_address',
+ 'woocommerce_currency_pos',
+ 'woocommerce_api_enabled',
+ 'woocommerce_allow_tracking',
+ 'woocommerce_task_list_hidden',
+ 'woocommerce_onboarding_profile',
+ );
+
+ /**
+ * Whitelist for constants we are interested to sync.
+ *
+ * @access private
+ * @static
+ *
+ * @var array
+ */
+ private static $wc_constants_whitelist = array(
+ // WooCommerce constants.
+ 'WC_PLUGIN_FILE',
+ 'WC_ABSPATH',
+ 'WC_PLUGIN_BASENAME',
+ 'WC_VERSION',
+ 'WOOCOMMERCE_VERSION',
+ 'WC_ROUNDING_PRECISION',
+ 'WC_DISCOUNT_ROUNDING_MODE',
+ 'WC_TAX_ROUNDING_MODE',
+ 'WC_DELIMITER',
+ 'WC_LOG_DIR',
+ 'WC_SESSION_CACHE_GROUP',
+ 'WC_TEMPLATE_DEBUG_MODE',
+ );
+
+ /**
+ * Whitelist for post meta we are interested to sync.
+ *
+ * @access private
+ * @static
+ *
+ * @var array
+ */
+ private static $wc_post_meta_whitelist = array(
+ // WooCommerce products.
+ // See https://github.com/woocommerce/woocommerce/blob/8ed6e7436ff87c2153ed30edd83c1ab8abbdd3e9/includes/data-stores/class-wc-product-data-store-cpt.php#L21 .
+ '_visibility',
+ '_sku',
+ '_price',
+ '_regular_price',
+ '_sale_price',
+ '_sale_price_dates_from',
+ '_sale_price_dates_to',
+ 'total_sales',
+ '_tax_status',
+ '_tax_class',
+ '_manage_stock',
+ '_backorders',
+ '_sold_individually',
+ '_weight',
+ '_length',
+ '_width',
+ '_height',
+ '_upsell_ids',
+ '_crosssell_ids',
+ '_purchase_note',
+ '_default_attributes',
+ '_product_attributes',
+ '_virtual',
+ '_downloadable',
+ '_download_limit',
+ '_download_expiry',
+ '_featured',
+ '_downloadable_files',
+ '_wc_rating_count',
+ '_wc_average_rating',
+ '_wc_review_count',
+ '_variation_description',
+ '_thumbnail_id',
+ '_file_paths',
+ '_product_image_gallery',
+ '_product_version',
+ '_wp_old_slug',
+
+ // Woocommerce orders.
+ // See https://github.com/woocommerce/woocommerce/blob/8ed6e7436ff87c2153ed30edd83c1ab8abbdd3e9/includes/data-stores/class-wc-order-data-store-cpt.php#L27 .
+ '_order_key',
+ '_order_currency',
+ // '_billing_first_name', do not sync these as they contain personal data
+ // '_billing_last_name',
+ // '_billing_company',
+ // '_billing_address_1',
+ // '_billing_address_2',
+ '_billing_city',
+ '_billing_state',
+ '_billing_postcode',
+ '_billing_country',
+ // '_billing_email', do not sync these as they contain personal data.
+ // '_billing_phone',
+ // '_shipping_first_name',
+ // '_shipping_last_name',
+ // '_shipping_company',
+ // '_shipping_address_1',
+ // '_shipping_address_2',
+ '_shipping_city',
+ '_shipping_state',
+ '_shipping_postcode',
+ '_shipping_country',
+ '_completed_date',
+ '_paid_date',
+ '_cart_discount',
+ '_cart_discount_tax',
+ '_order_shipping',
+ '_order_shipping_tax',
+ '_order_tax',
+ '_order_total',
+ '_payment_method',
+ '_payment_method_title',
+ // '_transaction_id', do not sync these as they contain personal data.
+ // '_customer_ip_address',
+ // '_customer_user_agent',
+ '_created_via',
+ '_order_version',
+ '_prices_include_tax',
+ '_date_completed',
+ '_date_paid',
+ '_payment_tokens',
+ '_billing_address_index',
+ '_shipping_address_index',
+ '_recorded_sales',
+ '_recorded_coupon_usage_counts',
+ // See https://github.com/woocommerce/woocommerce/blob/8ed6e7436ff87c2153ed30edd83c1ab8abbdd3e9/includes/data-stores/class-wc-order-data-store-cpt.php#L539 .
+ '_download_permissions_granted',
+ // See https://github.com/woocommerce/woocommerce/blob/8ed6e7436ff87c2153ed30edd83c1ab8abbdd3e9/includes/data-stores/class-wc-order-data-store-cpt.php#L594 .
+ '_order_stock_reduced',
+
+ // Woocommerce order refunds.
+ // See https://github.com/woocommerce/woocommerce/blob/b8a2815ae546c836467008739e7ff5150cb08e93/includes/data-stores/class-wc-order-refund-data-store-cpt.php#L20 .
+ '_order_currency',
+ '_refund_amount',
+ '_refunded_by',
+ '_refund_reason',
+ '_order_shipping',
+ '_order_shipping_tax',
+ '_order_tax',
+ '_order_total',
+ '_order_version',
+ '_prices_include_tax',
+ '_payment_tokens',
+ );
+
+ /**
+ * Whitelist for comment meta we are interested to sync.
+ *
+ * @access private
+ * @static
+ *
+ * @var array
+ */
+ private static $wc_comment_meta_whitelist = array(
+ 'rating',
+ );
+
+ /**
+ * Return a list of objects by their type and IDs
+ *
+ * @param string $object_type Object type.
+ * @param array $ids IDs of objects to return.
+ *
+ * @access public
+ *
+ * @return array|object|WP_Error|null
+ */
+ public function get_objects_by_id( $object_type, $ids ) {
+ switch ( $object_type ) {
+ case 'order_item':
+ return $this->get_order_item_by_ids( $ids );
+ }
+
+ return new WP_Error( 'unsupported_object_type', 'Unsupported object type' );
+ }
+
+ /**
+ * Returns a list of order_item objects by their IDs.
+ *
+ * @param array $ids List of order_item IDs to fetch.
+ *
+ * @access public
+ *
+ * @return array|object|null
+ */
+ public function get_order_item_by_ids( $ids ) {
+ global $wpdb;
+
+ if ( ! is_array( $ids ) ) {
+ return array();
+ }
+
+ // Make sure the IDs are numeric and are non-zero.
+ $ids = array_filter( array_map( 'intval', $ids ) );
+
+ if ( empty( $ids ) ) {
+ return array();
+ }
+
+ // Prepare the placeholders for the prepared query below.
+ $placeholders = implode( ',', array_fill( 0, count( $ids ), '%d' ) );
+
+ $query = "SELECT * FROM {$this->order_item_table_name} WHERE order_item_id IN ( $placeholders )";
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ return $wpdb->get_results( $wpdb->prepare( $query, $ids ), ARRAY_A );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-wp-super-cache.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-wp-super-cache.php
new file mode 100644
index 00000000..af4aec41
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/modules/class-wp-super-cache.php
@@ -0,0 +1,156 @@
+<?php
+/**
+ * WP_Super_Cache sync module.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Modules;
+
+/**
+ * Class to handle sync for WP_Super_Cache.
+ */
+class WP_Super_Cache extends Module {
+ /**
+ * Constructor.
+ *
+ * @todo Should we refactor this to use $this->set_defaults() instead?
+ */
+ public function __construct() {
+ add_filter( 'jetpack_sync_constants_whitelist', array( $this, 'add_wp_super_cache_constants_whitelist' ), 10 );
+ add_filter( 'jetpack_sync_callable_whitelist', array( $this, 'add_wp_super_cache_callable_whitelist' ), 10 );
+ }
+
+ /**
+ * Whitelist for constants we are interested to sync.
+ *
+ * @access public
+ * @static
+ *
+ * @var array
+ */
+ public static $wp_super_cache_constants = array(
+ 'WPLOCKDOWN',
+ 'WPSC_DISABLE_COMPRESSION',
+ 'WPSC_DISABLE_LOCKING',
+ 'WPSC_DISABLE_HTACCESS_UPDATE',
+ 'ADVANCEDCACHEPROBLEM',
+ );
+
+ /**
+ * Container for the whitelist for WP_Super_Cache callables we are interested to sync.
+ *
+ * @access public
+ * @static
+ *
+ * @var array
+ */
+ public static $wp_super_cache_callables = array(
+ 'wp_super_cache_globals' => array( __CLASS__, 'get_wp_super_cache_globals' ),
+ );
+
+ /**
+ * Sync module name.
+ *
+ * @access public
+ *
+ * @return string
+ */
+ public function name() {
+ return 'wp-super-cache';
+ }
+
+ /**
+ * Retrieve all WP_Super_Cache callables we are interested to sync.
+ *
+ * @access public
+ *
+ * @global $wp_cache_mod_rewrite;
+ * @global $cache_enabled;
+ * @global $super_cache_enabled;
+ * @global $ossdlcdn;
+ * @global $cache_rebuild_files;
+ * @global $wp_cache_mobile;
+ * @global $wp_super_cache_late_init;
+ * @global $wp_cache_anon_only;
+ * @global $wp_cache_not_logged_in;
+ * @global $wp_cache_clear_on_post_edit;
+ * @global $wp_cache_mobile_enabled;
+ * @global $wp_super_cache_debug;
+ * @global $cache_max_time;
+ * @global $wp_cache_refresh_single_only;
+ * @global $wp_cache_mfunc_enabled;
+ * @global $wp_supercache_304;
+ * @global $wp_cache_no_cache_for_get;
+ * @global $wp_cache_mutex_disabled;
+ * @global $cache_jetpack;
+ * @global $cache_domain_mapping;
+ *
+ * @return array All WP_Super_Cache callables.
+ */
+ public static function get_wp_super_cache_globals() {
+ global $wp_cache_mod_rewrite;
+ global $cache_enabled;
+ global $super_cache_enabled;
+ global $ossdlcdn;
+ global $cache_rebuild_files;
+ global $wp_cache_mobile;
+ global $wp_super_cache_late_init;
+ global $wp_cache_anon_only;
+ global $wp_cache_not_logged_in;
+ global $wp_cache_clear_on_post_edit;
+ global $wp_cache_mobile_enabled;
+ global $wp_super_cache_debug;
+ global $cache_max_time;
+ global $wp_cache_refresh_single_only;
+ global $wp_cache_mfunc_enabled;
+ global $wp_supercache_304;
+ global $wp_cache_no_cache_for_get;
+ global $wp_cache_mutex_disabled;
+ global $cache_jetpack;
+ global $cache_domain_mapping;
+
+ return array(
+ 'wp_cache_mod_rewrite' => $wp_cache_mod_rewrite,
+ 'cache_enabled' => $cache_enabled,
+ 'super_cache_enabled' => $super_cache_enabled,
+ 'ossdlcdn' => $ossdlcdn,
+ 'cache_rebuild_files' => $cache_rebuild_files,
+ 'wp_cache_mobile' => $wp_cache_mobile,
+ 'wp_super_cache_late_init' => $wp_super_cache_late_init,
+ 'wp_cache_anon_only' => $wp_cache_anon_only,
+ 'wp_cache_not_logged_in' => $wp_cache_not_logged_in,
+ 'wp_cache_clear_on_post_edit' => $wp_cache_clear_on_post_edit,
+ 'wp_cache_mobile_enabled' => $wp_cache_mobile_enabled,
+ 'wp_super_cache_debug' => $wp_super_cache_debug,
+ 'cache_max_time' => $cache_max_time,
+ 'wp_cache_refresh_single_only' => $wp_cache_refresh_single_only,
+ 'wp_cache_mfunc_enabled' => $wp_cache_mfunc_enabled,
+ 'wp_supercache_304' => $wp_supercache_304,
+ 'wp_cache_no_cache_for_get' => $wp_cache_no_cache_for_get,
+ 'wp_cache_mutex_disabled' => $wp_cache_mutex_disabled,
+ 'cache_jetpack' => $cache_jetpack,
+ 'cache_domain_mapping' => $cache_domain_mapping,
+ );
+ }
+
+ /**
+ * Add WP_Super_Cache constants to the constants whitelist.
+ *
+ * @param array $list Existing constants whitelist.
+ * @return array Updated constants whitelist.
+ */
+ public function add_wp_super_cache_constants_whitelist( $list ) {
+ return array_merge( $list, self::$wp_super_cache_constants );
+ }
+
+ /**
+ * Add WP_Super_Cache callables to the callables whitelist.
+ *
+ * @param array $list Existing callables whitelist.
+ * @return array Updated callables whitelist.
+ */
+ public function add_wp_super_cache_callable_whitelist( $list ) {
+ return array_merge( $list, self::$wp_super_cache_callables );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/replicastore/class-table-checksum-usermeta.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/replicastore/class-table-checksum-usermeta.php
new file mode 100644
index 00000000..20f686a6
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/replicastore/class-table-checksum-usermeta.php
@@ -0,0 +1,208 @@
+<?php
+/**
+ * Table Checksums Class.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Replicastore;
+
+use Automattic\Jetpack\Connection\Manager;
+use Automattic\Jetpack\Sync;
+use Automattic\Jetpack\Sync\Modules;
+use WP_Error;
+use WP_User_Query;
+
+/**
+ * Class to handle Table Checksums for the User Meta table.
+ */
+class Table_Checksum_Usermeta extends Table_Checksum_Users {
+ /**
+ * Calculate the checksum based on provided range and filters.
+ *
+ * @param int|null $range_from The start of the range.
+ * @param int|null $range_to The end of the range.
+ * @param array|null $filter_values Additional filter values. Not used at the moment.
+ * @param bool $granular_result If the returned result should be granular or only the checksum.
+ * @param bool $simple_return_value If we want to use a simple return value for non-granular results (return only the checksum, without wrappers).
+ *
+ * @return array|mixed|object|WP_Error|null
+ */
+ public function calculate_checksum( $range_from = null, $range_to = null, $filter_values = null, $granular_result = false, $simple_return_value = true ) {
+
+ if ( ! Sync\Settings::is_checksum_enabled() ) {
+ return new WP_Error( 'checksum_disabled', 'Checksums are currently disabled.' );
+ }
+
+ /**
+ * First we need to fetch the user IDs for the users that we want to include in the range.
+ *
+ * To keep things a bit simple and avoid filtering issues, let's reuse the `build_filter_statement` that already
+ * exists. Unfortunately we don't
+ */
+ global $wpdb;
+
+ // This call depends on the `range_field` pointing to the `ID` field of the `users` table. Currently, "ID".
+ $range_filter_statement = $this->build_filter_statement( $range_from, $range_to );
+
+ $query = "
+ SELECT
+ DISTINCT {$this->table}.{$this->range_field}
+ FROM
+ {$this->table}
+ JOIN {$wpdb->usermeta} as um_table ON um_table.user_id = {$this->table}.ID
+ WHERE
+ {$range_filter_statement}
+ AND um_table.meta_key = '{$wpdb->prefix}user_level'
+ AND um_table.meta_value > 0
+ ";
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $user_ids = $wpdb->get_col( $query );
+
+ // Chunk the array down to make sure we don't overload the database with queries that are too large.
+ $chunked_user_ids = array_chunk( $user_ids, 500 );
+
+ $checksum_entries = array();
+
+ foreach ( $chunked_user_ids as $user_ids_chunk ) {
+ $user_objects = $this->get_user_objects_by_ids( $user_ids_chunk );
+
+ foreach ( $user_objects as $user_object ) {
+ // expand and sanitize desired meta based on WP.com logic.
+ $user_object = $this->expand_and_sanitize_user_meta( $user_object );
+
+ // Generate checksum entry based on the serialized value if not empty.
+ $checksum_entry = 0;
+ if ( ! empty( $user_object->roles ) ) {
+ $checksum_entry = crc32( implode( '#', array( $this->salt, 'roles', maybe_serialize( $user_object->roles ) ) ) );
+ }
+
+ // Meta only persisted if user is connected to WP.com.
+ if ( ( new Manager( 'jetpack' ) )->is_user_connected( $user_object->ID ) ) {
+ if ( ! empty( $user_object->allcaps ) ) {
+ $checksum_entry += crc32(
+ implode(
+ '#',
+ array(
+ $this->salt,
+ 'capabilities',
+ maybe_serialize( $user_object->allcaps ),
+ )
+ )
+ );
+ }
+ // Explicitly check that locale is not same as site locale.
+ if ( ! empty( $user_object->locale ) && get_locale() !== $user_object->locale ) {
+ $checksum_entry += crc32(
+ implode(
+ '#',
+ array(
+ $this->salt,
+ 'locale',
+ maybe_serialize( $user_object->locale ),
+ )
+ )
+ );
+ }
+ if ( ! empty( $user_object->allowed_mime_types ) ) {
+ $checksum_entry += crc32(
+ implode(
+ '#',
+ array(
+ $this->salt,
+ 'allowed_mime_types',
+ maybe_serialize( $user_object->allowed_mime_types ),
+ )
+ )
+ );
+ }
+ }
+
+ $checksum_entries[ $user_object->ID ] = '' . $checksum_entry;
+ }
+ }
+
+ // Non-granular results need only to sum the different entries.
+ if ( ! $granular_result ) {
+ $checksum_sum = 0;
+ foreach ( $checksum_entries as $entry ) {
+ $checksum_sum += intval( $entry );
+ }
+
+ if ( $simple_return_value ) {
+ return '' . $checksum_sum;
+ }
+
+ return array(
+ 'range' => $range_from . '-' . $range_to,
+ 'checksum' => '' . $checksum_sum,
+ );
+
+ }
+
+ // Granular results.
+ $response = $checksum_entries;
+
+ // Sort the return value for easier comparisons and code flows further down the line.
+ ksort( $response );
+
+ return $response;
+ }
+
+ /**
+ * Expand the User Object with additional meta santized by WP.com logic.
+ *
+ * @param mixed $user_object User Object from WP_User_Query.
+ *
+ * @return mixed $user_object expanded User Object.
+ */
+ protected function expand_and_sanitize_user_meta( $user_object ) {
+ $user_module = Modules::get_module( 'users' );
+ // Expand User Objects based on Sync logic.
+ $user_object = $user_module->expand_user( $user_object );
+
+ // Sanitize location.
+ if ( ! empty( $user_object->locale ) ) {
+ $user_object->locale = wp_strip_all_tags( $user_object->locale, true );
+ }
+
+ // Sanitize allcaps.
+ if ( ! empty( $user_object->allcaps ) ) {
+ $user_object->allcaps = array_map(
+ function ( $cap ) {
+ return (bool) $cap;
+ },
+ $user_object->allcaps
+ );
+ }
+
+ // Sanitize allowed_mime_types.
+ $allowed_mime_types = $user_object->allowed_mime_types;
+ foreach ( $allowed_mime_types as $allowed_mime_type_short => $allowed_mime_type_long ) {
+ $allowed_mime_type_short = wp_strip_all_tags( (string) $allowed_mime_type_short, true );
+ $allowed_mime_type_long = wp_strip_all_tags( (string) $allowed_mime_type_long, true );
+ $allowed_mime_types[ $allowed_mime_type_short ] = $allowed_mime_type_long;
+ }
+ $user_object->allowed_mime_types = $allowed_mime_types;
+
+ // Sanitize roles.
+ if ( is_array( $user_object->roles ) ) {
+ $user_object->roles = array_map( 'sanitize_text_field', $user_object->roles );
+ }
+ return $user_object;
+ }
+
+ /**
+ * Gets a list of `WP_User` objects by their IDs
+ *
+ * @param array $ids List of IDs to fetch.
+ *
+ * @return array
+ */
+ protected function get_user_objects_by_ids( $ids ) {
+ $user_query = new WP_User_Query( array( 'include' => $ids ) );
+
+ return $user_query->get_results();
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/replicastore/class-table-checksum-users.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/replicastore/class-table-checksum-users.php
new file mode 100644
index 00000000..c38ed840
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/replicastore/class-table-checksum-users.php
@@ -0,0 +1,184 @@
+<?php
+/**
+ * Table Checksums Class.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Replicastore;
+
+/**
+ * Class to handle Table Checksums for the Users table.
+ */
+class Table_Checksum_Users extends Table_Checksum {
+
+ /**
+ * Returns the checksum query. All validation of fields and configurations are expected to occur prior to usage.
+ *
+ * @param int|null $range_from The start of the range.
+ * @param int|null $range_to The end of the range.
+ * @param array|null $filter_values Additional filter values. Not used at the moment.
+ * @param bool $granular_result If the function should return a granular result.
+ *
+ * @return string
+ *
+ * @throws Exception Throws an exception if validation fails in the internal function calls.
+ */
+ protected function build_checksum_query( $range_from = null, $range_to = null, $filter_values = null, $granular_result = false ) {
+ global $wpdb;
+
+ // Escape the salt.
+ $salt = $wpdb->prepare( '%s', $this->salt );
+
+ // Prepare the compound key.
+ $key_fields = array();
+
+ // Prefix the fields with the table name, to avoid clashes in queries with sub-queries (e.g. meta tables).
+ foreach ( $this->key_fields as $field ) {
+ $key_fields[] = $this->table . '.' . $field;
+ }
+
+ $key_fields = implode( ',', $key_fields );
+
+ // Prepare the checksum fields.
+ $checksum_fields = array();
+ // Prefix the fields with the table name, to avoid clashes in queries with sub-queries (e.g. meta tables).
+ foreach ( $this->checksum_fields as $field ) {
+ $checksum_fields[] = $this->table . '.' . $field;
+ }
+ // Apply latin1 conversion if enabled.
+ if ( $this->perform_text_conversion ) {
+ // Convert text fields to allow for encoding discrepancies as WP.com is latin1.
+ foreach ( $this->checksum_text_fields as $field ) {
+ $checksum_fields[] = 'CONVERT(' . $this->table . '.' . $field . ' using latin1 )';
+ }
+ } else {
+ // Conversion disabled, default to table prefixing.
+ foreach ( $this->checksum_text_fields as $field ) {
+ $checksum_fields[] = $this->table . '.' . $field;
+ }
+ }
+
+ $checksum_fields_string = implode( ',', array_merge( $checksum_fields, array( $salt ) ) );
+
+ $additional_fields = '';
+ if ( $granular_result ) {
+ // TODO uniq the fields as sometimes(most) range_index is the key and there's no need to select the same field twice.
+ $additional_fields = "
+ {$this->table}.{$this->range_field} as range_index,
+ {$key_fields},
+ ";
+ }
+
+ $filter_stamenet = $this->build_filter_statement( $range_from, $range_to, $filter_values );
+
+ // usermeta join to limit on user_level.
+ $join_statement = "JOIN {$wpdb->usermeta} as um_table ON um_table.user_id = {$this->table}.ID";
+
+ $query = "
+ SELECT
+ {$additional_fields}
+ SUM(
+ CRC32(
+ CONCAT_WS( '#', {$salt}, {$checksum_fields_string} )
+ )
+ ) AS checksum
+ FROM
+ {$this->table}
+ {$join_statement}
+ WHERE
+ {$filter_stamenet}
+ AND um_table.meta_key = '{$wpdb->prefix}user_level'
+ AND um_table.meta_value > 0
+ ";
+
+ /**
+ * We need the GROUP BY only for compound keys.
+ */
+ if ( $granular_result ) {
+ $query .= "
+ GROUP BY {$key_fields}
+ LIMIT 9999999
+ ";
+ }
+
+ return $query;
+ }
+
+ /**
+ * Obtain the min-max values (edges) of the range.
+ *
+ * @param int|null $range_from The start of the range.
+ * @param int|null $range_to The end of the range.
+ * @param int|null $limit How many values to return.
+ *
+ * @return array|object|void
+ * @throws Exception Throws an exception if validation fails on the internal function calls.
+ */
+ public function get_range_edges( $range_from = null, $range_to = null, $limit = null ) {
+ global $wpdb;
+
+ $this->validate_fields( array( $this->range_field ) );
+
+ // `trim()` to make sure we don't add the statement if it's empty.
+ $filters = trim( $this->build_filter_statement( $range_from, $range_to ) );
+
+ $filter_statement = '';
+ if ( ! empty( $filters ) ) {
+ $filter_statement = "
+ JOIN {$wpdb->usermeta} as um_table ON um_table.user_id = {$this->table}.ID
+ WHERE
+ {$filters}
+ AND um_table.meta_key = '{$wpdb->prefix}user_level'
+ AND um_table.meta_value > 0
+ ";
+ }
+
+ $query = "
+ SELECT
+ MIN({$this->range_field}) as min_range,
+ MAX({$this->range_field}) as max_range,
+ COUNT( {$this->range_field} ) as item_count
+ FROM
+ ";
+
+ /**
+ * If `$limit` is not specified, we can directly use the table.
+ */
+ if ( ! $limit ) {
+ $query .= "
+ {$this->table}
+ {$filter_statement}
+ ";
+ } else {
+ /**
+ * If there is `$limit` specified, we can't directly use `MIN/MAX()` as they don't work with `LIMIT`.
+ * That's why we will alter the query for this case.
+ */
+ $limit = intval( $limit );
+
+ $query .= "
+ (
+ SELECT
+ {$this->range_field}
+ FROM
+ {$this->table}
+ {$filter_statement}
+ ORDER BY
+ {$this->range_field} ASC
+ LIMIT {$limit}
+ ) as ids_query
+ ";
+ }
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $result = $wpdb->get_row( $query, ARRAY_A );
+
+ if ( ! $result || ! is_array( $result ) ) {
+ throw new Exception( 'Unable to get range edges' );
+ }
+
+ return $result;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/replicastore/class-table-checksum.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/replicastore/class-table-checksum.php
new file mode 100644
index 00000000..a62050eb
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-sync/src/replicastore/class-table-checksum.php
@@ -0,0 +1,826 @@
+<?php
+/**
+ * Table Checksums Class.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+namespace Automattic\Jetpack\Sync\Replicastore;
+
+use Automattic\Jetpack\Sync;
+use Exception;
+use WP_Error;
+
+// TODO add rest endpoints to work with this, hopefully in the same folder.
+/**
+ * Class to handle Table Checksums.
+ */
+class Table_Checksum {
+
+ /**
+ * Table to be checksummed.
+ *
+ * @var string
+ */
+ public $table = '';
+
+ /**
+ * Table Checksum Configuration.
+ *
+ * @var array
+ */
+ public $table_configuration = array();
+
+ /**
+ * Perform Text Conversion to latin1.
+ *
+ * @var boolean
+ */
+ protected $perform_text_conversion = false;
+
+ /**
+ * Field to be used for range queries.
+ *
+ * @var string
+ */
+ public $range_field = '';
+
+ /**
+ * ID Field(s) to be used.
+ *
+ * @var array
+ */
+ public $key_fields = array();
+
+ /**
+ * Field(s) to be used in generating the checksum value.
+ *
+ * @var array
+ */
+ public $checksum_fields = array();
+
+ /**
+ * Field(s) to be used in generating the checksum value that need latin1 conversion.
+ *
+ * @var array
+ */
+ public $checksum_text_fields = array();
+
+ /**
+ * Default filter values for the table
+ *
+ * @var array
+ */
+ public $filter_values = array();
+
+ /**
+ * SQL Query to be used to filter results (allow/disallow).
+ *
+ * @var string
+ */
+ public $additional_filter_sql = '';
+
+ /**
+ * Default Checksum Table Configurations.
+ *
+ * @var array
+ */
+ public $default_tables = array();
+
+ /**
+ * Salt to be used when generating checksum.
+ *
+ * @var string
+ */
+ public $salt = '';
+
+ /**
+ * Tables which are allowed to be checksummed.
+ *
+ * @var string
+ */
+ public $allowed_tables = array();
+
+ /**
+ * If the table has a "parent" table that it's related to.
+ *
+ * @var mixed|null
+ */
+ protected $parent_table = null;
+
+ /**
+ * What field to use for the parent table join, if it has a "parent" table.
+ *
+ * @var mixed|null
+ */
+ protected $parent_join_field = null;
+
+ /**
+ * What field to use for the table join, if it has a "parent" table.
+ *
+ * @var mixed|null
+ */
+ protected $table_join_field = null;
+
+ /**
+ * Some tables might not exist on the remote, and we want to verify they exist, before trying to query them.
+ *
+ * @var callable
+ */
+ protected $is_table_enabled_callback = false;
+
+ /**
+ * Table_Checksum constructor.
+ *
+ * @param string $table The table to calculate checksums for.
+ * @param string $salt Optional salt to add to the checksum.
+ * @param boolean $perform_text_conversion If text fields should be latin1 converted.
+ *
+ * @throws Exception Throws exception from inner functions.
+ */
+ public function __construct( $table, $salt = null, $perform_text_conversion = false ) {
+
+ if ( ! Sync\Settings::is_checksum_enabled() ) {
+ throw new Exception( 'Checksums are currently disabled.' );
+ }
+
+ $this->salt = $salt;
+
+ $this->default_tables = $this->get_default_tables();
+
+ $this->perform_text_conversion = $perform_text_conversion;
+
+ // TODO change filters to allow the array format.
+ // TODO add get_fields or similar method to get things out of the table.
+ // TODO extract this configuration in a better way, still make it work with `$wpdb` names.
+ // TODO take over the replicastore functions and move them over to this class.
+ // TODO make the API work.
+
+ $this->allowed_tables = apply_filters( 'jetpack_sync_checksum_allowed_tables', $this->default_tables );
+
+ $this->table = $this->validate_table_name( $table );
+ $this->table_configuration = $this->allowed_tables[ $table ];
+
+ $this->prepare_fields( $this->table_configuration );
+
+ // Run any callbacks to check if a table is enabled or not.
+ if (
+ is_callable( $this->is_table_enabled_callback )
+ && ! call_user_func( $this->is_table_enabled_callback, $table )
+ ) {
+ throw new Exception( "Unable to use table name: $table" );
+ }
+ }
+
+ /**
+ * Get Default Table configurations.
+ *
+ * @return array
+ */
+ protected function get_default_tables() {
+ global $wpdb;
+
+ return array(
+ 'posts' => array(
+ 'table' => $wpdb->posts,
+ 'range_field' => 'ID',
+ 'key_fields' => array( 'ID' ),
+ 'checksum_fields' => array( 'post_modified_gmt' ),
+ 'filter_values' => Sync\Settings::get_disallowed_post_types_structured(),
+ ),
+ 'postmeta' => array(
+ 'table' => $wpdb->postmeta,
+ 'range_field' => 'post_id',
+ 'key_fields' => array( 'post_id', 'meta_key' ),
+ 'checksum_text_fields' => array( 'meta_key', 'meta_value' ),
+ 'filter_values' => Sync\Settings::get_allowed_post_meta_structured(),
+ 'parent_table' => 'posts',
+ 'parent_join_field' => 'ID',
+ 'table_join_field' => 'post_id',
+ ),
+ 'comments' => array(
+ 'table' => $wpdb->comments,
+ 'range_field' => 'comment_ID',
+ 'key_fields' => array( 'comment_ID' ),
+ 'checksum_fields' => array( 'comment_date_gmt' ),
+ 'filter_values' => array(
+ 'comment_type' => array(
+ 'operator' => 'IN',
+ 'values' => apply_filters(
+ 'jetpack_sync_whitelisted_comment_types',
+ array( '', 'comment', 'trackback', 'pingback', 'review' )
+ ),
+ ),
+ 'comment_approved' => array(
+ 'operator' => 'NOT IN',
+ 'values' => array( 'spam' ),
+ ),
+ ),
+ ),
+ 'commentmeta' => array(
+ 'table' => $wpdb->commentmeta,
+ 'range_field' => 'comment_id',
+ 'key_fields' => array( 'comment_id', 'meta_key' ),
+ 'checksum_text_fields' => array( 'meta_key', 'meta_value' ),
+ 'filter_values' => Sync\Settings::get_allowed_comment_meta_structured(),
+ 'parent_table' => 'comments',
+ 'parent_join_field' => 'comment_ID',
+ 'table_join_field' => 'comment_id',
+ ),
+ 'terms' => array(
+ 'table' => $wpdb->terms,
+ 'range_field' => 'term_id',
+ 'key_fields' => array( 'term_id' ),
+ 'checksum_fields' => array( 'term_id' ),
+ 'checksum_text_fields' => array( 'name', 'slug' ),
+ 'parent_table' => 'term_taxonomy',
+ ),
+ 'termmeta' => array(
+ 'table' => $wpdb->termmeta,
+ 'range_field' => 'term_id',
+ 'key_fields' => array( 'term_id', 'meta_key' ),
+ 'checksum_text_fields' => array( 'meta_key', 'meta_value' ),
+ 'parent_table' => 'term_taxonomy',
+ ),
+ 'term_relationships' => array(
+ 'table' => $wpdb->term_relationships,
+ 'range_field' => 'object_id',
+ 'key_fields' => array( 'object_id' ),
+ 'checksum_fields' => array( 'object_id', 'term_taxonomy_id' ),
+ 'parent_table' => 'term_taxonomy',
+ 'parent_join_field' => 'term_taxonomy_id',
+ 'table_join_field' => 'term_taxonomy_id',
+ ),
+ 'term_taxonomy' => array(
+ 'table' => $wpdb->term_taxonomy,
+ 'range_field' => 'term_taxonomy_id',
+ 'key_fields' => array( 'term_taxonomy_id' ),
+ 'checksum_fields' => array( 'term_taxonomy_id', 'term_id', 'parent' ),
+ 'checksum_text_fields' => array( 'taxonomy', 'description' ),
+ 'filter_values' => Sync\Settings::get_allowed_taxonomies_structured(),
+ ),
+ 'links' => $wpdb->links, // TODO describe in the array format or add exceptions.
+ 'options' => $wpdb->options, // TODO describe in the array format or add exceptions.
+ 'woocommerce_order_items' => array(
+ 'table' => "{$wpdb->prefix}woocommerce_order_items",
+ 'range_field' => 'order_item_id',
+ 'key_fields' => array( 'order_item_id' ),
+ 'checksum_fields' => array( 'order_id' ),
+ 'checksum_text_fields' => array( 'order_item_name', 'order_item_type' ),
+ 'is_table_enabled_callback' => array( $this, 'enable_woocommerce_tables' ),
+ ),
+ 'woocommerce_order_itemmeta' => array(
+ 'table' => "{$wpdb->prefix}woocommerce_order_itemmeta",
+ 'range_field' => 'order_item_id',
+ 'key_fields' => array( 'order_item_id', 'meta_key' ),
+ 'checksum_text_fields' => array( 'meta_key', 'meta_value' ),
+ 'filter_values' => Sync\Settings::get_allowed_order_itemmeta_structured(),
+ 'parent_table' => 'woocommerce_order_items',
+ 'parent_join_field' => 'order_item_id',
+ 'table_join_field' => 'order_item_id',
+ 'is_table_enabled_callback' => array( $this, 'enable_woocommerce_tables' ),
+ ),
+ 'users' => array(
+ 'table' => $wpdb->users,
+ 'range_field' => 'ID',
+ 'key_fields' => array( 'ID' ),
+ 'checksum_text_fields' => array( 'user_login', 'user_nicename', 'user_email', 'user_url', 'user_registered', 'user_status', 'display_name' ),
+ 'filter_values' => array(),
+ ),
+
+ /**
+ * Usermeta is a special table, as it needs to use a custom override flow,
+ * as the user roles, capabilities, locale, mime types can be filtered by plugins.
+ * This prevents us from doing a direct comparison in the database.
+ */
+ 'usermeta' => array(
+ 'table' => $wpdb->users,
+ /**
+ * Range field points to ID, which in this case is the `WP_User` ID,
+ * since we're querying the whole WP_User objects, instead of meta entries in the DB.
+ */
+ 'range_field' => 'ID',
+ 'key_fields' => array(),
+ 'checksum_fields' => array(),
+ ),
+ );
+ }
+
+ /**
+ * Prepare field params based off provided configuration.
+ *
+ * @param array $table_configuration The table configuration array.
+ */
+ protected function prepare_fields( $table_configuration ) {
+ $this->key_fields = $table_configuration['key_fields'];
+ $this->range_field = $table_configuration['range_field'];
+ $this->checksum_fields = isset( $table_configuration['checksum_fields'] ) ? $table_configuration['checksum_fields'] : array();
+ $this->checksum_text_fields = isset( $table_configuration['checksum_text_fields'] ) ? $table_configuration['checksum_text_fields'] : array();
+ $this->filter_values = isset( $table_configuration['filter_values'] ) ? $table_configuration['filter_values'] : null;
+ $this->additional_filter_sql = ! empty( $table_configuration['filter_sql'] ) ? $table_configuration['filter_sql'] : '';
+ $this->parent_table = isset( $table_configuration['parent_table'] ) ? $table_configuration['parent_table'] : null;
+ $this->parent_join_field = isset( $table_configuration['parent_join_field'] ) ? $table_configuration['parent_join_field'] : $table_configuration['range_field'];
+ $this->table_join_field = isset( $table_configuration['table_join_field'] ) ? $table_configuration['table_join_field'] : $table_configuration['range_field'];
+ $this->is_table_enabled_callback = isset( $table_configuration['is_table_enabled_callback'] ) ? $table_configuration['is_table_enabled_callback'] : false;
+ }
+
+ /**
+ * Verify provided table name is valid for checksum processing.
+ *
+ * @param string $table Table name to validate.
+ *
+ * @return mixed|string
+ * @throws Exception Throw an exception on validation failure.
+ */
+ protected function validate_table_name( $table ) {
+ if ( empty( $table ) ) {
+ throw new Exception( 'Invalid table name: empty' );
+ }
+
+ if ( ! array_key_exists( $table, $this->allowed_tables ) ) {
+ throw new Exception( "Invalid table name: $table not allowed" );
+ }
+
+ return $this->allowed_tables[ $table ]['table'];
+ }
+
+ /**
+ * Verify provided fields are proper names.
+ *
+ * @param array $fields Array of field names to validate.
+ *
+ * @throws Exception Throw an exception on failure to validate.
+ */
+ protected function validate_fields( $fields ) {
+ foreach ( $fields as $field ) {
+ if ( ! preg_match( '/^[0-9,a-z,A-Z$_]+$/i', $field ) ) {
+ throw new Exception( "Invalid field name: $field is not allowed" );
+ }
+
+ // TODO other verifications of the field names.
+ }
+ }
+
+ /**
+ * Verify the fields exist in the table.
+ *
+ * @param array $fields Array of fields to validate.
+ *
+ * @return bool
+ * @throws Exception Throw an exception on failure to validate.
+ */
+ protected function validate_fields_against_table( $fields ) {
+ global $wpdb;
+
+ $valid_fields = array();
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $result = $wpdb->get_results( "SHOW COLUMNS FROM {$this->table}", ARRAY_A );
+
+ foreach ( $result as $result_row ) {
+ $valid_fields[] = $result_row['Field'];
+ }
+
+ // Check if the fields are actually contained in the table.
+ foreach ( $fields as $field_to_check ) {
+ if ( ! in_array( $field_to_check, $valid_fields, true ) ) {
+ throw new Exception( "Invalid field name: field '{$field_to_check}' doesn't exist in table {$this->table}" );
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Verify the configured fields.
+ *
+ * @throws Exception Throw an exception on failure to validate in the internal functions.
+ */
+ protected function validate_input() {
+ $fields = array_merge( array( $this->range_field ), $this->key_fields, $this->checksum_fields, $this->checksum_text_fields );
+
+ $this->validate_fields( $fields );
+ $this->validate_fields_against_table( $fields );
+ }
+
+ /**
+ * Prepare filter values as SQL statements to be added to the other filters.
+ *
+ * @param array $filter_values The filter values array.
+ * @param string $table_prefix If the values are going to be used in a sub-query, add a prefix with the table alias.
+ *
+ * @return array|null
+ */
+ protected function prepare_filter_values_as_sql( $filter_values = array(), $table_prefix = '' ) {
+ global $wpdb;
+
+ if ( ! is_array( $filter_values ) ) {
+ return null;
+ }
+
+ $result = array();
+
+ foreach ( $filter_values as $field => $filter ) {
+ $key = ( ! empty( $table_prefix ) ? $table_prefix : $this->table ) . '.' . $field;
+
+ switch ( $filter['operator'] ) {
+ case 'IN':
+ case 'NOT IN':
+ $values_placeholders = implode( ',', array_fill( 0, count( $filter['values'] ), '%s' ) );
+ $statement = "{$key} {$filter['operator']} ( $values_placeholders )";
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $prepared_statement = $wpdb->prepare( $statement, $filter['values'] );
+
+ $result[] = $prepared_statement;
+ break;
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Build the filter query baased off range fields and values and the additional sql.
+ *
+ * @param int|null $range_from Start of the range.
+ * @param int|null $range_to End of the range.
+ * @param array|null $filter_values Additional filter values. Not used at the moment.
+ * @param string $table_prefix Table name to be prefixed to the columns. Used in sub-queries where columns can clash.
+ *
+ * @return string
+ */
+ public function build_filter_statement( $range_from = null, $range_to = null, $filter_values = null, $table_prefix = '' ) {
+ global $wpdb;
+
+ // If there is a field prefix that we want to use with table aliases.
+ $parent_prefix = ( ! empty( $table_prefix ) ? $table_prefix : $this->table ) . '.';
+
+ /**
+ * Prepare the ranges.
+ */
+
+ $filter_array = array( '1 = 1' );
+ if ( null !== $range_from ) {
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $filter_array[] = $wpdb->prepare( "{$parent_prefix}{$this->range_field} >= %d", array( intval( $range_from ) ) );
+ }
+ if ( null !== $range_to ) {
+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
+ $filter_array[] = $wpdb->prepare( "{$parent_prefix}{$this->range_field} <= %d", array( intval( $range_to ) ) );
+ }
+
+ /**
+ * End prepare the ranges.
+ */
+
+ /**
+ * Prepare data filters.
+ */
+
+ // Default filters.
+ if ( $this->filter_values ) {
+ $prepared_values_statements = $this->prepare_filter_values_as_sql( $this->filter_values, $table_prefix );
+ if ( $prepared_values_statements ) {
+ $filter_array = array_merge( $filter_array, $prepared_values_statements );
+ }
+ }
+
+ // Additional filters.
+ if ( ! empty( $filter_values ) ) {
+ // Prepare filtering.
+ $prepared_values_statements = $this->prepare_filter_values_as_sql( $filter_values, $table_prefix );
+ if ( $prepared_values_statements ) {
+ $filter_array = array_merge( $filter_array, $prepared_values_statements );
+ }
+ }
+
+ // Add any additional filters via direct SQL statement.
+ // Currently used only because we haven't converted all filtering to happen via `filter_values`.
+ // This SQL is NOT prefixed and column clashes can occur when used in sub-queries.
+ if ( $this->additional_filter_sql ) {
+ $filter_array[] = $this->additional_filter_sql;
+ }
+
+ /**
+ * End prepare data filters.
+ */
+ return implode( ' AND ', $filter_array );
+ }
+
+ /**
+ * Returns the checksum query. All validation of fields and configurations are expected to occur prior to usage.
+ *
+ * @param int|null $range_from The start of the range.
+ * @param int|null $range_to The end of the range.
+ * @param array|null $filter_values Additional filter values. Not used at the moment.
+ * @param bool $granular_result If the function should return a granular result.
+ *
+ * @return string
+ *
+ * @throws Exception Throws an exception if validation fails in the internal function calls.
+ */
+ protected function build_checksum_query( $range_from = null, $range_to = null, $filter_values = null, $granular_result = false ) {
+ global $wpdb;
+
+ // Escape the salt.
+ $salt = $wpdb->prepare( '%s', $this->salt );
+
+ // Prepare the compound key.
+ $key_fields = array();
+
+ // Prefix the fields with the table name, to avoid clashes in queries with sub-queries (e.g. meta tables).
+ foreach ( $this->key_fields as $field ) {
+ $key_fields[] = $this->table . '.' . $field;
+ }
+
+ $key_fields = implode( ',', $key_fields );
+
+ // Prepare the checksum fields.
+ $checksum_fields = array();
+ // Prefix the fields with the table name, to avoid clashes in queries with sub-queries (e.g. meta tables).
+ foreach ( $this->checksum_fields as $field ) {
+ $checksum_fields[] = $this->table . '.' . $field;
+ }
+ // Apply latin1 conversion if enabled.
+ if ( $this->perform_text_conversion ) {
+ // Convert text fields to allow for encoding discrepancies as WP.com is latin1.
+ foreach ( $this->checksum_text_fields as $field ) {
+ $checksum_fields[] = 'CONVERT(' . $this->table . '.' . $field . ' using latin1 )';
+ }
+ } else {
+ // Conversion disabled, default to table prefixing.
+ foreach ( $this->checksum_text_fields as $field ) {
+ $checksum_fields[] = $this->table . '.' . $field;
+ }
+ }
+
+ $checksum_fields_string = implode( ',', array_merge( $checksum_fields, array( $salt ) ) );
+
+ $additional_fields = '';
+ if ( $granular_result ) {
+ // TODO uniq the fields as sometimes(most) range_index is the key and there's no need to select the same field twice.
+ $additional_fields = "
+ {$this->table}.{$this->range_field} as range_index,
+ {$key_fields},
+ ";
+ }
+
+ $filter_stamenet = $this->build_filter_statement( $range_from, $range_to, $filter_values );
+
+ $join_statement = '';
+ if ( $this->parent_table ) {
+ $parent_table_obj = new Table_Checksum( $this->parent_table );
+ $parent_filter_query = $parent_table_obj->build_filter_statement( null, null, null, 'parent_table' );
+
+ // It is possible to have the GROUP By cause multiple rows to be returned for the same row for term_taxonomy.
+ // To get distinct entries we use a correlatd subquery back on the parent table using the primary key.
+ $additional_unique_clause = '';
+ if ( 'term_taxonomy' === $this->parent_table ) {
+ $additional_unique_clause = "
+ AND parent_table.{$parent_table_obj->range_field} = (
+ SELECT min( parent_table_cs.{$parent_table_obj->range_field} )
+ FROM {$parent_table_obj->table} as parent_table_cs
+ WHERE parent_table_cs.{$this->parent_join_field} = {$this->table}.{$this->table_join_field}
+ )
+ ";
+ }
+
+ $join_statement = "
+ INNER JOIN {$parent_table_obj->table} as parent_table
+ ON (
+ {$this->table}.{$this->table_join_field} = parent_table.{$this->parent_join_field}
+ AND {$parent_filter_query}
+ $additional_unique_clause
+ )
+ ";
+ }
+
+ $query = "
+ SELECT
+ {$additional_fields}
+ SUM(
+ CRC32(
+ CONCAT_WS( '#', {$salt}, {$checksum_fields_string} )
+ )
+ ) AS checksum
+ FROM
+ {$this->table}
+ {$join_statement}
+ WHERE
+ {$filter_stamenet}
+ ";
+
+ /**
+ * We need the GROUP BY only for compound keys.
+ */
+ if ( $granular_result ) {
+ $query .= "
+ GROUP BY {$key_fields}
+ LIMIT 9999999
+ ";
+ }
+
+ return $query;
+ }
+
+ /**
+ * Obtain the min-max values (edges) of the range.
+ *
+ * @param int|null $range_from The start of the range.
+ * @param int|null $range_to The end of the range.
+ * @param int|null $limit How many values to return.
+ *
+ * @return array|object|void
+ * @throws Exception Throws an exception if validation fails on the internal function calls.
+ */
+ public function get_range_edges( $range_from = null, $range_to = null, $limit = null ) {
+ global $wpdb;
+
+ $this->validate_fields( array( $this->range_field ) );
+
+ // Performance :: When getting the postmeta range we do not want to filter by the whitelist.
+ // The reason for this is that it leads to a non-performant query that can timeout.
+ // Instead lets get the range based on posts regardless of meta.
+ $filter_values = $this->filter_values;
+ if ( 'postmeta' === $this->table ) {
+ $this->filter_values = null;
+ }
+
+ // `trim()` to make sure we don't add the statement if it's empty.
+ $filters = trim( $this->build_filter_statement( $range_from, $range_to ) );
+
+ // Reset Post meta filter.
+ if ( 'postmeta' === $this->table ) {
+ $this->filter_values = $filter_values;
+ }
+
+ $filter_statement = '';
+ if ( ! empty( $filters ) ) {
+ $filter_statement = "
+ WHERE
+ {$filters}
+ ";
+ }
+
+ // Only make the distinct count when we know there can be multiple entries for the range column.
+ $distinct_count = '';
+ if ( count( $this->key_fields ) > 1 || $wpdb->terms === $this->table || $wpdb->term_relationships === $this->table ) {
+ $distinct_count = 'DISTINCT';
+ }
+
+ $query = "
+ SELECT
+ MIN({$this->range_field}) as min_range,
+ MAX({$this->range_field}) as max_range,
+ COUNT( {$distinct_count} {$this->range_field}) as item_count
+ FROM
+ ";
+
+ /**
+ * If `$limit` is not specified, we can directly use the table.
+ */
+ if ( ! $limit ) {
+ $query .= "
+ {$this->table}
+ {$filter_statement}
+ ";
+ } else {
+ /**
+ * If there is `$limit` specified, we can't directly use `MIN/MAX()` as they don't work with `LIMIT`.
+ * That's why we will alter the query for this case.
+ */
+ $limit = intval( $limit );
+
+ $query .= "
+ (
+ SELECT
+ {$distinct_count} {$this->range_field}
+ FROM
+ {$this->table}
+ {$filter_statement}
+ ORDER BY
+ {$this->range_field} ASC
+ LIMIT {$limit}
+ ) as ids_query
+ ";
+ }
+
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $result = $wpdb->get_row( $query, ARRAY_A );
+
+ if ( ! $result || ! is_array( $result ) ) {
+ throw new Exception( 'Unable to get range edges' );
+ }
+
+ return $result;
+ }
+
+ /**
+ * Update the results to have key/checksum format.
+ *
+ * @param array $results Prepare the results for output of granular results.
+ */
+ protected function prepare_results_for_output( &$results ) {
+ // get the compound key.
+ // only return range and compound key for granular results.
+
+ $return_value = array();
+
+ foreach ( $results as &$result ) {
+ // Working on reference to save memory here.
+
+ $key = array();
+ foreach ( $this->key_fields as $field ) {
+ $key[] = $result[ $field ];
+ }
+
+ $return_value[ implode( '-', $key ) ] = $result['checksum'];
+ }
+
+ return $return_value;
+ }
+
+ /**
+ * Calculate the checksum based on provided range and filters.
+ *
+ * @param int|null $range_from The start of the range.
+ * @param int|null $range_to The end of the range.
+ * @param array|null $filter_values Additional filter values. Not used at the moment.
+ * @param bool $granular_result If the returned result should be granular or only the checksum.
+ * @param bool $simple_return_value If we want to use a simple return value for non-granular results (return only the checksum, without wrappers).
+ *
+ * @return array|mixed|object|WP_Error|null
+ */
+ public function calculate_checksum( $range_from = null, $range_to = null, $filter_values = null, $granular_result = false, $simple_return_value = true ) {
+
+ if ( ! Sync\Settings::is_checksum_enabled() ) {
+ return new WP_Error( 'checksum_disabled', 'Checksums are currently disabled.' );
+ }
+
+ try {
+ $this->validate_input();
+ } catch ( Exception $ex ) {
+ return new WP_Error( 'invalid_input', $ex->getMessage() );
+ }
+
+ $query = $this->build_checksum_query( $range_from, $range_to, $filter_values, $granular_result );
+
+ global $wpdb;
+
+ if ( ! $granular_result ) {
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $result = $wpdb->get_row( $query, ARRAY_A );
+
+ if ( ! is_array( $result ) ) {
+ return new WP_Error( 'invalid_query', "Result wasn't an array" );
+ }
+
+ if ( $simple_return_value ) {
+ return $result['checksum'];
+ }
+
+ return array(
+ 'range' => $range_from . '-' . $range_to,
+ 'checksum' => $result['checksum'],
+ );
+ } else {
+ // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+ $result = $wpdb->get_results( $query, ARRAY_A );
+ return $this->prepare_results_for_output( $result );
+ }
+ }
+
+ /**
+ * Make sure the WooCommerce tables should be enabled for Checksum/Fix.
+ *
+ * @return bool
+ */
+ protected function enable_woocommerce_tables() {
+ /**
+ * On WordPress.com, we can't directly check if the site has support for WooCommerce.
+ * Having the option to override the functionality here helps with syncing WooCommerce tables.
+ *
+ * @since 10.1
+ *
+ * @param bool If we should we force-enable WooCommerce tables support.
+ */
+ $force_woocommerce_support = apply_filters( 'jetpack_table_checksum_force_enable_woocommerce', false );
+
+ // If we're forcing WooCommerce tables support, there's no need to check further.
+ // This is used on WordPress.com.
+ if ( $force_woocommerce_support ) {
+ return true;
+ }
+
+ // No need to proceed if WooCommerce is not available.
+ if ( ! class_exists( 'WooCommerce' ) ) {
+ return false;
+ }
+
+ // TODO more checks if needed. Probably query the DB to make sure the tables exist.
+
+ return true;
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/CHANGELOG.md
new file mode 100644
index 00000000..f9b91333
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/CHANGELOG.md
@@ -0,0 +1,223 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.9.18] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies.
+
+## [1.9.17] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.9.16] - 2021-11-30
+### Changed
+- Updated package dependencies.
+
+## [1.9.15] - 2021-11-22
+### Changed
+- Updated package dependencies
+
+## [1.9.14] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.9.13] - 2021-10-26
+### Changed
+- Updated package dependencies.
+
+## [1.9.12] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.9.11] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.9.10] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.9.9] - 2021-08-30
+### Changed
+- Run composer update on test-php command instead of phpunit
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- update annotations versions
+
+## [1.9.8] - 2021-06-15
+
+- Updated package dependencies.
+
+## [1.9.7] - 2021-05-25
+### Changed
+- Updated package dependencies.
+
+## [1.9.6] - 2021-04-27
+### Changed
+- Updated package dependencies.
+
+## [1.9.5] - 2021-04-08
+### Changed
+- Packaging and build changes, no change to the package itself.
+
+## [1.9.4] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.9.3] - 2021-02-23
+
+- CI: Make tests more generic
+
+## [1.9.2] - 2021-01-28
+
+- Update dependencies to latest stable
+
+## [1.9.1] - 2021-01-26
+
+- Add mirror-repo information to all current composer packages
+- Monorepo: Reorganize all projects
+
+## [1.9.0] - 2021-01-05
+
+- Update dependency brain/monkey to v2.6.0
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.8.2] - 2020-11-24
+
+- Version packages for release
+
+## [1.8.1] - 2020-11-24
+
+- Updated PHPCS: Packages and Debugger
+
+## [1.8.0] - 2020-10-27
+
+- Updated dependencies to latest stable
+
+## [1.7.0] - 2020-09-29
+
+- Update dependencies to latest stable
+
+## [1.6.1] - 2020-09-22
+
+- Packages: update list of files distributed in production packages
+
+## [1.6.0] - 2020-08-26
+
+- ToS: Remove dependency to Jetpack Connection
+- CI: Try collect js coverage
+- Docker: Add package testing shortcut
+
+## [1.5.2] - 2020-08-10
+
+- Update dependencies to latest stable
+
+## [1.5.1] - 2020-08-10
+
+- Update dependencies to latest stable
+
+## [1.5.0] - 2020-07-28
+
+- Core Compat: Site Environment
+- Package Unit tests: update test file names to make sure they runs in Travis
+
+## [1.4.2] - 2020-07-06
+
+- Update dependencies to latest stable
+
+## [1.4.1] - 2020-07-01
+
+- Update dependencies to latest stable
+
+## [1.4.0] - 2020-06-30
+
+- PHPCS: Clean up the packages
+- PHPCS Updates after WPCS 2.3
+
+## [1.3.1] - 2020-06-01
+
+- Update dependencies to latest stable
+
+## [1.3.0] - 2020-05-26
+
+- Update dependencies to latest stable
+
+## [1.2.0] - 2020-04-28
+
+- Update dependencies to latest stable
+
+## [1.1.1] - 2020-03-31
+
+- Update dependencies to latest stable
+
+## [1.1.0] - 2020-03-31
+
+- Update dependencies to latest stable
+
+## [1.0.4] - 2019-11-15
+
+- Allow TOS agreement before Jetpack is fully active so we track…
+
+## [1.0.3] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## [1.0.1] - 2019-11-08
+
+- Deprecate Jetpack::is_development_mode() in favor of the packaged Status()-&gt;is_development_mode()
+
+## 1.0.0 - 2019-10-22
+
+- Package: Create new TOS package
+
+[1.9.18]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.17...v1.9.18
+[1.9.17]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.16...v1.9.17
+[1.9.16]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.15...v1.9.16
+[1.9.15]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.14...v1.9.15
+[1.9.14]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.13...v1.9.14
+[1.9.13]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.12...v1.9.13
+[1.9.12]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.11...v1.9.12
+[1.9.11]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.10...v1.9.11
+[1.9.10]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.9...v1.9.10
+[1.9.9]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.8...v1.9.9
+[1.9.8]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.7...v1.9.8
+[1.9.7]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.6...v1.9.7
+[1.9.6]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.5...v1.9.6
+[1.9.5]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.4...v1.9.5
+[1.9.4]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.3...v1.9.4
+[1.9.3]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.2...v1.9.3
+[1.9.2]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.1...v1.9.2
+[1.9.1]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.9.0...v1.9.1
+[1.9.0]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.8.2...v1.9.0
+[1.8.2]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.8.1...v1.8.2
+[1.8.1]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.8.0...v1.8.1
+[1.8.0]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.7.0...v1.8.0
+[1.7.0]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.6.1...v1.7.0
+[1.6.1]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.6.0...v1.6.1
+[1.6.0]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.5.2...v1.6.0
+[1.5.2]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.5.1...v1.5.2
+[1.5.1]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.5.0...v1.5.1
+[1.5.0]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.4.2...v1.5.0
+[1.4.2]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.4.1...v1.4.2
+[1.4.1]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.4.0...v1.4.1
+[1.4.0]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.3.1...v1.4.0
+[1.3.1]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.3.0...v1.3.1
+[1.3.0]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.2.0...v1.3.0
+[1.2.0]: https://github.com/Automattic/jetpack-terms-of-service/compare/1.1.1...v1.2.0
+[1.1.1]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.1.0...1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.0.4...v1.1.0
+[1.0.4]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.0.3...v1.0.4
+[1.0.3]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.0.1...v1.0.3
+[1.0.1]: https://github.com/Automattic/jetpack-terms-of-service/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/src/class-terms-of-service.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/src/class-terms-of-service.php
new file mode 100644
index 00000000..54bdbf1e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-terms-of-service/src/class-terms-of-service.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * A Terms of Service class for Jetpack.
+ *
+ * @package automattic/jetpack-terms-of-service
+ */
+
+namespace Automattic\Jetpack;
+
+/**
+ * Class Terms_Of_Service
+ *
+ * Helper class that is responsible for the state of agreement of the terms of service.
+ */
+class Terms_Of_Service {
+ /**
+ * Jetpack option name where the terms of service state is stored.
+ *
+ * @var string
+ */
+ const OPTION_NAME = 'tos_agreed';
+
+ /**
+ * Allow the site to agree to the terms of service.
+ */
+ public function agree() {
+ $this->set_agree();
+ /**
+ * Acton fired when the master user has agreed to the terms of service.
+ *
+ * @since 1.0.4
+ * @since-jetpack 7.9.0
+ */
+ do_action( 'jetpack_agreed_to_terms_of_service' );
+ }
+
+ /**
+ * Allow the site to reject to the terms of service.
+ */
+ public function reject() {
+ $this->set_reject();
+ /**
+ * Acton fired when the master user has revoked their agreement to the terms of service.
+ *
+ * @since 1.0.4
+ * @since-jetpack 7.9.1
+ */
+ do_action( 'jetpack_reject_terms_of_service' );
+ }
+
+ /**
+ * Returns whether the master user has agreed to the terms of service.
+ *
+ * The following conditions have to be met in order to agree to the terms of service.
+ * 1. The master user has gone though the connect flow.
+ * 2. The site is not in dev mode.
+ * 3. The master user of the site is still connected (deprecated @since 1.4.0).
+ *
+ * @return bool
+ */
+ public function has_agreed() {
+ if ( $this->is_offline_mode() ) {
+ return false;
+ }
+ /**
+ * Before 1.4.0 we used to also check if the master user of the site is connected
+ * by calling the Connection related `is_active` method.
+ * As of 1.4.0 we have removed this check in order to resolve the
+ * circular dependencies it was introducing to composer packages.
+ *
+ * @since 1.4.0
+ */
+ return $this->get_raw_has_agreed();
+ }
+
+ /**
+ * Abstracted for testing purposes.
+ * Tells us if the site is in dev mode.
+ *
+ * @return bool
+ */
+ protected function is_offline_mode() {
+ return ( new Status() )->is_offline_mode();
+ }
+
+ /**
+ * Gets just the Jetpack Option that contains the terms of service state.
+ * Abstracted for testing purposes.
+ *
+ * @return bool
+ */
+ protected function get_raw_has_agreed() {
+ return \Jetpack_Options::get_option( self::OPTION_NAME, false );
+ }
+
+ /**
+ * Sets the correct Jetpack Option to mark the that the site has agreed to the terms of service.
+ * Abstracted for testing purposes.
+ */
+ protected function set_agree() {
+ \Jetpack_Options::update_option( self::OPTION_NAME, true );
+ }
+
+ /**
+ * Sets the correct Jetpack Option to mark that the site has rejected the terms of service.
+ * Abstracted for testing purposes.
+ */
+ protected function set_reject() {
+ \Jetpack_Options::update_option( self::OPTION_NAME, false );
+ }
+
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/CHANGELOG.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/CHANGELOG.md
new file mode 100644
index 00000000..970ab757
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/CHANGELOG.md
@@ -0,0 +1,253 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.14.0] - 2022-01-04
+### Changed
+- Switch to pcov for code coverage.
+- Updated package dependencies
+- Updated package textdomain from `jetpack` to `jetpack-tracking`.
+
+## [1.13.19] - 2021-12-14
+### Changed
+- Updated package dependencies.
+
+## [1.13.18] - 2021-11-30
+### Changed
+- Updated package dependencies.
+
+## [1.13.17] - 2021-11-22
+### Changed
+- Updated package dependencies
+
+## [1.13.16] - 2021-11-16
+### Changed
+- Updated package dependencies.
+
+## [1.13.15] - 2021-11-02
+### Changed
+- Set `convertDeprecationsToExceptions` true in PHPUnit config.
+- Update PHPUnit configs to include just what needs coverage rather than include everything then try to exclude stuff that doesn't.
+
+## [1.13.14] - 2021-10-26
+### Changed
+- Updated package dependencies.
+
+## [1.13.13] - 2021-10-13
+### Changed
+- Updated package dependencies.
+
+## [1.13.12] - 2021-10-12
+### Changed
+- Updated package dependencies
+
+## [1.13.11] - 2021-09-30
+### Added
+- Set up the ajax hook in the Tracking class.
+
+## [1.13.10] - 2021-09-28
+### Changed
+- Updated package dependencies.
+
+## [1.13.9] - 2021-08-30
+### Changed
+- Run composer update on test-php command instead of phpunit
+- Tests: update PHPUnit polyfills dependency (yoast/phpunit-polyfills).
+- update annotations versions
+
+## [1.13.8] - 2021-08-10
+### Added
+- adding Readme to the tracking package
+
+## [1.13.7] - 2021-06-15
+### Changed
+- Updated package dependencies.
+
+## [1.13.6] - 2021-05-25
+### Added
+- Adding the tracks-callables.js file to the Tracking package.
+
+## [1.13.5] - 2021-04-27
+### Changed
+- Updated package dependencies.
+
+## [1.13.4] - 2021-04-08
+### Changed
+- Packaging and build changes, no change to the package itself.
+
+## [1.13.3] - 2021-03-30
+### Added
+- Composer alias for dev-master, to improve dependencies
+
+### Changed
+- Update package dependencies.
+
+### Fixed
+- Use `composer update` rather than `install` in scripts, as composer.lock isn't checked in.
+
+## [1.13.2] - 2021-02-23
+
+- CI: Make tests more generic
+- Jetpack: Normalize package names
+
+## [1.13.1] - 2021-01-28
+
+- Update dependencies to latest stable
+
+## [1.13.0] - 2021-01-26
+
+- Tracking: remove dependency to the Jetpack plugin
+- Add mirror-repo information to all current composer packages
+- Tracking: get connected user data from Connection package
+- Monorepo: Reorganize all projects
+
+## [1.12.0] - 2021-01-05
+
+- Pin dependencies
+- Packages: Update for PHP 8 testing
+
+## [1.11.1] - 2020-11-24
+
+- Version packages for release
+
+## [1.11.0] - 2020-10-27
+
+- Updated dependencies to latest stable
+
+## [1.10.0] - 2020-09-29
+
+- Packages: update list of files distributed in production packages
+- Tracking: fix the logic for determining when to enable tracking.
+
+## [1.9.1] - 2020-09-09
+
+- Tracking: fix the logic for determining when to enable tracking.
+
+## [1.9.0] - 2020-08-26
+
+- Tracking: Add the connection check.
+
+## [1.8.2] - 2020-08-10
+
+- Update dependencies to latest stable
+
+## [1.8.1] - 2020-08-10
+
+- Update dependencies to latest stable
+
+## [1.8.0] - 2020-07-28
+
+- Update dependencies to latest stable
+
+## [1.7.2] - 2020-07-06
+
+- Update dependencies to latest stable
+
+## [1.7.1] - 2020-07-01
+
+- Update dependencies to latest stable
+
+## [1.7.0] - 2020-06-30
+
+- Various: Update use of whitelist/blacklist
+
+## [1.6.1] - 2020-06-01
+
+- Update dependencies to latest stable
+
+## [1.6.0] - 2020-05-26
+
+- Update dependencies to latest stable
+
+## [1.5.0] - 2020-04-28
+
+- Update dependencies to latest stable
+
+## [1.4.0] - 2020-03-31
+
+- Update dependencies to latest stable
+
+## [1.3.0] - 2020-03-31
+
+- Update dependencies to latest stable
+
+## [1.2.2] - 2019-11-08
+
+- Packages: Use classmap instead of PSR-4
+
+## [1.2.1] - 2019-10-29
+
+- PHPCS: Rest of the packages
+
+## [1.2.0] - 2019-10-25
+
+- Update/Use the new Terms of Service package in Jetpack
+
+## [1.1.1] - 2019-10-16
+
+- Tracks: use filter instead of relying on Jetpack class
+
+## [1.1.0] - 2019-10-11
+
+- Tracks: Don't track users in dev mode or when opted out
+
+## [1.0.2] - 2019-10-07
+
+- Update dependency phpcompatibility/phpcompatibility-wp to v2.1.0
+
+## [1.0.1] - 2019-09-20
+
+- Docs: Unify usage of @package phpdoc tags
+
+## 1.0.0 - 2019-09-14
+
+- Create package for Jetpack Tracking
+
+[1.14.0]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.19...v1.14.0
+[1.13.19]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.18...v1.13.19
+[1.13.18]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.17...v1.13.18
+[1.13.17]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.16...v1.13.17
+[1.13.16]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.15...v1.13.16
+[1.13.15]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.14...v1.13.15
+[1.13.14]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.13...v1.13.14
+[1.13.13]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.12...v1.13.13
+[1.13.12]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.11...v1.13.12
+[1.13.11]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.10...v1.13.11
+[1.13.10]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.9...v1.13.10
+[1.13.9]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.8...v1.13.9
+[1.13.8]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.7...v1.13.8
+[1.13.7]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.6...v1.13.7
+[1.13.6]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.5...v1.13.6
+[1.13.5]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.4...v1.13.5
+[1.13.4]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.3...v1.13.4
+[1.13.3]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.2...v1.13.3
+[1.13.2]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.1...v1.13.2
+[1.13.1]: https://github.com/Automattic/jetpack-tracking/compare/v1.13.0...v1.13.1
+[1.13.0]: https://github.com/Automattic/jetpack-tracking/compare/v1.12.0...v1.13.0
+[1.12.0]: https://github.com/Automattic/jetpack-tracking/compare/v1.11.1...v1.12.0
+[1.11.1]: https://github.com/Automattic/jetpack-tracking/compare/v1.11.0...v1.11.1
+[1.11.0]: https://github.com/Automattic/jetpack-tracking/compare/v1.10.0...v1.11.0
+[1.10.0]: https://github.com/Automattic/jetpack-tracking/compare/v1.9.1...v1.10.0
+[1.9.1]: https://github.com/Automattic/jetpack-tracking/compare/v1.9.0...v1.9.1
+[1.9.0]: https://github.com/Automattic/jetpack-tracking/compare/v1.8.2...v1.9.0
+[1.8.2]: https://github.com/Automattic/jetpack-tracking/compare/v1.8.1...v1.8.2
+[1.8.1]: https://github.com/Automattic/jetpack-tracking/compare/v1.8.0...v1.8.1
+[1.8.0]: https://github.com/Automattic/jetpack-tracking/compare/v1.7.2...v1.8.0
+[1.7.2]: https://github.com/Automattic/jetpack-tracking/compare/v1.7.1...v1.7.2
+[1.7.1]: https://github.com/Automattic/jetpack-tracking/compare/v1.7.0...v1.7.1
+[1.7.0]: https://github.com/Automattic/jetpack-tracking/compare/v1.6.1...v1.7.0
+[1.6.1]: https://github.com/Automattic/jetpack-tracking/compare/v1.6.0...v1.6.1
+[1.6.0]: https://github.com/Automattic/jetpack-tracking/compare/v1.5.0...v1.6.0
+[1.5.0]: https://github.com/Automattic/jetpack-tracking/compare/1.4.0...v1.5.0
+[1.4.0]: https://github.com/Automattic/jetpack-tracking/compare/v1.3.0...1.4.0
+[1.3.0]: https://github.com/Automattic/jetpack-tracking/compare/v1.2.2...v1.3.0
+[1.2.2]: https://github.com/Automattic/jetpack-tracking/compare/v1.2.1...v1.2.2
+[1.2.1]: https://github.com/Automattic/jetpack-tracking/compare/v1.2.0...v1.2.1
+[1.2.0]: https://github.com/Automattic/jetpack-tracking/compare/v1.1.1...v1.2.0
+[1.1.1]: https://github.com/Automattic/jetpack-tracking/compare/v1.1.0...v1.1.1
+[1.1.0]: https://github.com/Automattic/jetpack-tracking/compare/v1.0.2...v1.1.0
+[1.0.2]: https://github.com/Automattic/jetpack-tracking/compare/v1.0.1...v1.0.2
+[1.0.1]: https://github.com/Automattic/jetpack-tracking/compare/v1.0.0...v1.0.1
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/LICENSE.txt b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/LICENSE.txt
new file mode 100644
index 00000000..e82774c1
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/LICENSE.txt
@@ -0,0 +1,357 @@
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+===================================
+
+
+GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and
+modification follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in
+whole or in part contains or is derived from the Program or any
+part thereof, to be licensed as a whole at no charge to all third
+parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively
+when run, you must cause it, when started running for such
+interactive use in the most ordinary way, to print or display an
+announcement including an appropriate copyright notice and a
+notice that there is no warranty (or else, saying that you provide
+a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this
+License. (Exception: if the Program itself is interactive but
+does not normally print such an announcement, your work based on
+the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable
+source code, which must be distributed under the terms of Sections
+1 and 2 above on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three
+years, to give any third party, for a charge no more than your
+cost of physically performing source distribution, a complete
+machine-readable copy of the corresponding source code, to be
+distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer
+to distribute corresponding source code. (This alternative is
+allowed only for noncommercial distribution and only if you
+received the program in object code or executable form with such
+an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+How to Apply These Terms to Your New Programs
+
+If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+<one line to give the program's name and a brief idea of what it does.>
+Copyright (C) <year> <name of author>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+Gnomovision version 69, Copyright (C) year name of author
+Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+This is free software, and you are welcome to redistribute it
+under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+`Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+<signature of Ty Coon>, 1 April 1989
+Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/SECURITY.md b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/SECURITY.md
new file mode 100644
index 00000000..b4b46c0e
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/SECURITY.md
@@ -0,0 +1,38 @@
+# Security Policy
+
+Full details of the Automattic Security Policy can be found on [automattic.com](https://automattic.com/security/).
+
+## Supported Versions
+
+Generally, only the latest version of Jetpack has continued support. If a critical vulnerability is found in the current version of Jetpack, we may opt to backport any patches to previous versions.
+
+## Reporting a Vulnerability
+
+[Jetpack](https://jetpack.com/) is an open-source plugin for WordPress. Our HackerOne program covers the plugin software, as well as a variety of related projects and infrastructure.
+
+**For responsible disclosure of security issues and to be eligible for our bug bounty program, please submit your report via the [HackerOne](https://hackerone.com/automattic) portal.**
+
+Our most critical targets are:
+
+* Jetpack and the Jetpack composer packages (all within this repo)
+* Jetpack.com -- the primary marketing site.
+* cloud.jetpack.com -- a management site.
+* wordpress.com -- the shared management site for both Jetpack and WordPress.com sites.
+
+For more targets, see the `In Scope` section on [HackerOne](https://hackerone.com/automattic).
+
+_Please note that the **WordPress software is a separate entity** from Automattic. Please report vulnerabilities for WordPress through [the WordPress Foundation's HackerOne page](https://hackerone.com/wordpress)._
+
+## Guidelines
+
+We're committed to working with security researchers to resolve the vulnerabilities they discover. You can help us by following these guidelines:
+
+* Follow [HackerOne's disclosure guidelines](https://www.hackerone.com/disclosure-guidelines).
+* Pen-testing Production:
+ * Please **setup a local environment** instead whenever possible. Most of our code is open source (see above).
+ * If that's not possible, **limit any data access/modification** to the bare minimum necessary to reproduce a PoC.
+ * **_Don't_ automate form submissions!** That's very annoying for us, because it adds extra work for the volunteers who manage those systems, and reduces the signal/noise ratio in our communication channels.
+ * To be eligible for a bounty, all of these guidelines must be followed.
+* Be Patient - Give us a reasonable time to correct the issue before you disclose the vulnerability.
+
+We also expect you to comply with all applicable laws. You're responsible to pay any taxes associated with your bounties.
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-client.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-client.php
new file mode 100644
index 00000000..07829b6c
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-client.php
@@ -0,0 +1,230 @@
+<?php
+/**
+ * Legacy Jetpack Tracks Client
+ *
+ * @package automattic/jetpack-tracking
+ */
+
+use Automattic\Jetpack\Connection\Manager;
+
+/**
+ * Jetpack_Tracks_Client
+ *
+ * @autounit nosara tracks-client
+ *
+ * Send Tracks events on behalf of a user
+ *
+ * Example Usage:
+```php
+ require( dirname(__FILE__).'path/to/tracks/class-jetpack-tracks-client.php' );
+
+ $result = Jetpack_Tracks_Client::record_event( array(
+ '_en' => $event_name, // required
+ '_ui' => $user_id, // required unless _ul is provided
+ '_ul' => $user_login, // required unless _ui is provided
+
+ // Optional, but recommended
+ '_ts' => $ts_in_ms, // Default: now
+ '_via_ip' => $client_ip, // we use it for geo, etc.
+
+ // Possibly useful to set some context for the event
+ '_via_ua' => $client_user_agent,
+ '_via_url' => $client_url,
+ '_via_ref' => $client_referrer,
+
+ // For user-targeted tests
+ 'abtest_name' => $abtest_name,
+ 'abtest_variation' => $abtest_variation,
+
+ // Your application-specific properties
+ 'custom_property' => $some_value,
+ ) );
+
+ if ( is_wp_error( $result ) ) {
+ // Handle the error in your app
+ }
+```
+ */
+class Jetpack_Tracks_Client {
+ const PIXEL = 'https://pixel.wp.com/t.gif';
+ const BROWSER_TYPE = 'php-agent';
+ const USER_AGENT_SLUG = 'tracks-client';
+ const VERSION = '0.3';
+
+ /**
+ * Stores the Terms of Service Object Reference.
+ *
+ * @var null
+ */
+ private static $terms_of_service = null;
+
+ /**
+ * Record an event.
+ *
+ * @param mixed $event Event object to send to Tracks. An array will be cast to object. Required.
+ * Properties are included directly in the pixel query string after light validation.
+ * @return mixed True on success, WP_Error on failure
+ */
+ public static function record_event( $event ) {
+ if ( ! self::$terms_of_service ) {
+ self::$terms_of_service = new \Automattic\Jetpack\Terms_Of_Service();
+ }
+
+ // Don't track users who have opted out or not agreed to our TOS, or are not running an active Jetpack.
+ if ( ! self::$terms_of_service->has_agreed() || ! empty( $_COOKIE['tk_opt-out'] ) ) {
+ return false;
+ }
+
+ if ( ! $event instanceof Jetpack_Tracks_Event ) {
+ $event = new Jetpack_Tracks_Event( $event );
+ }
+ if ( is_wp_error( $event ) ) {
+ return $event;
+ }
+
+ $pixel = $event->build_pixel_url( $event );
+
+ if ( ! $pixel ) {
+ return new WP_Error( 'invalid_pixel', 'cannot generate tracks pixel for given input', 400 );
+ }
+
+ return self::record_pixel( $pixel );
+ }
+
+ /**
+ * Synchronously request the pixel.
+ *
+ * @param string $pixel The wp.com tracking pixel.
+ * @return array|bool|WP_Error True if successful. wp_remote_get response or WP_Error if not.
+ */
+ public static function record_pixel( $pixel ) {
+ // Add the Request Timestamp and URL terminator just before the HTTP request.
+ $pixel .= '&_rt=' . self::build_timestamp() . '&_=_';
+
+ $response = wp_remote_get(
+ $pixel,
+ array(
+ 'blocking' => true, // The default, but being explicit here :).
+ 'timeout' => 1,
+ 'redirection' => 2,
+ 'httpversion' => '1.1',
+ 'user-agent' => self::get_user_agent(),
+ )
+ );
+
+ if ( is_wp_error( $response ) ) {
+ return $response;
+ }
+
+ $code = isset( $response['response']['code'] ) ? $response['response']['code'] : 0;
+
+ if ( 200 !== $code ) {
+ return new WP_Error( 'request_failed', 'Tracks pixel request failed', $code );
+ }
+
+ return true;
+ }
+
+ /**
+ * Get the user agent.
+ *
+ * @return string The user agent.
+ */
+ public static function get_user_agent() {
+ return self::USER_AGENT_SLUG . '-v' . self::VERSION;
+ }
+
+ /**
+ * Build an event and return its tracking URL
+ *
+ * @deprecated Call the `build_pixel_url` method on a Jetpack_Tracks_Event object instead.
+ * @param array $event Event keys and values.
+ * @return string URL of a tracking pixel.
+ */
+ public static function build_pixel_url( $event ) {
+ $_event = new Jetpack_Tracks_Event( $event );
+ return $_event->build_pixel_url();
+ }
+
+ /**
+ * Validate input for a tracks event.
+ *
+ * @deprecated Instantiate a Jetpack_Tracks_Event object instead
+ * @param array $event Event keys and values.
+ * @return mixed Validated keys and values or WP_Error on failure
+ */
+ private static function validate_and_sanitize( $event ) {
+ $_event = new Jetpack_Tracks_Event( $event );
+ if ( is_wp_error( $_event ) ) {
+ return $_event;
+ }
+ return get_object_vars( $_event );
+ }
+
+ /**
+ * Builds a timestamp.
+ *
+ * Milliseconds since 1970-01-01.
+ *
+ * @return string
+ */
+ public static function build_timestamp() {
+ $ts = round( microtime( true ) * 1000 );
+ return number_format( $ts, 0, '', '' );
+ }
+
+ /**
+ * Grabs the user's anon id from cookies, or generates and sets a new one
+ *
+ * @return string An anon id for the user
+ */
+ public static function get_anon_id() {
+ static $anon_id = null;
+
+ if ( ! isset( $anon_id ) ) {
+
+ // Did the browser send us a cookie?
+ if ( isset( $_COOKIE['tk_ai'] ) && preg_match( '#^[A-Za-z0-9+/=]{24}$#', $_COOKIE['tk_ai'] ) ) {
+ $anon_id = $_COOKIE['tk_ai'];
+ } else {
+
+ $binary = '';
+
+ // Generate a new anonId and try to save it in the browser's cookies.
+ // Note that base64-encoding an 18 character string generates a 24-character anon id.
+ for ( $i = 0; $i < 18; ++$i ) {
+ $binary .= chr( wp_rand( 0, 255 ) );
+ }
+
+ $anon_id = 'jetpack:' . base64_encode( $binary ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
+
+ if ( ! headers_sent()
+ && ! ( defined( 'REST_REQUEST' ) && REST_REQUEST )
+ && ! ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST )
+ ) {
+ setcookie( 'tk_ai', $anon_id );
+ }
+ }
+ }
+
+ return $anon_id;
+ }
+
+ /**
+ * Gets the WordPress.com user's Tracks identity, if connected.
+ *
+ * @return array|bool
+ */
+ public static function get_connected_user_tracks_identity() {
+ $user_data = ( new Manager() )->get_connected_user_data();
+ if ( ! $user_data ) {
+ return false;
+ }
+
+ return array(
+ 'blogid' => Jetpack_Options::get_option( 'id', 0 ),
+ 'userid' => $user_data['ID'],
+ 'username' => $user_data['login'],
+ );
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-event.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-event.php
new file mode 100644
index 00000000..be77a397
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/legacy/class-jetpack-tracks-event.php
@@ -0,0 +1,189 @@
+<?php
+/**
+ * Class Jetpack_Tracks_Event. Legacy.
+ *
+ * @package automattic/jetpack-sync
+ */
+
+/*
+ * @autounit nosara tracks-client
+ *
+ * Example Usage:
+```php
+ require_once( dirname(__FILE__) . 'path/to/tracks/class-jetpack-tracks-event.php' );
+
+ $event = new Jetpack_Tracks_Event( array(
+ '_en' => $event_name, // required
+ '_ui' => $user_id, // required unless _ul is provided
+ '_ul' => $user_login, // required unless _ui is provided
+
+ // Optional, but recommended
+ '_via_ip' => $client_ip, // for geo, etc.
+
+ // Possibly useful to set some context for the event
+ '_via_ua' => $client_user_agent,
+ '_via_url' => $client_url,
+ '_via_ref' => $client_referrer,
+
+ // For user-targeted tests
+ 'abtest_name' => $abtest_name,
+ 'abtest_variation' => $abtest_variation,
+
+ // Your application-specific properties
+ 'custom_property' => $some_value,
+ ) );
+
+ if ( is_wp_error( $event->error ) ) {
+ // Handle the error in your app
+ }
+
+ $bump_and_redirect_pixel = $event->build_signed_pixel_url();
+```
+ */
+
+/**
+ * Class Jetpack_Tracks_Event
+ */
+class Jetpack_Tracks_Event {
+ const EVENT_NAME_REGEX = '/^(([a-z0-9]+)_){2}([a-z0-9_]+)$/';
+ const PROP_NAME_REGEX = '/^[a-z_][a-z0-9_]*$/';
+
+ /**
+ * Tracks Event Error.
+ *
+ * @var mixed Error.
+ */
+ public $error;
+
+ /**
+ * Jetpack_Tracks_Event constructor.
+ *
+ * @param object $event Tracks event.
+ */
+ public function __construct( $event ) {
+ $_event = self::validate_and_sanitize( $event );
+ if ( is_wp_error( $_event ) ) {
+ $this->error = $_event;
+ return;
+ }
+
+ foreach ( $_event as $key => $value ) {
+ $this->{$key} = $value;
+ }
+ }
+
+ /**
+ * Record a track event.
+ */
+ public function record() {
+ return Jetpack_Tracks_Client::record_event( $this );
+ }
+
+ /**
+ * Annotate the event with all relevant info.
+ *
+ * @param mixed $event Object or (flat) array.
+ * @return mixed The transformed event array or WP_Error on failure.
+ */
+ public static function validate_and_sanitize( $event ) {
+ $event = (object) $event;
+
+ // Required.
+ if ( ! $event->_en ) {
+ return new WP_Error( 'invalid_event', 'A valid event must be specified via `_en`', 400 );
+ }
+
+ // delete non-routable addresses otherwise geoip will discard the record entirely.
+ if ( property_exists( $event, '_via_ip' ) && preg_match( '/^192\.168|^10\./', $event->_via_ip ) ) {
+ unset( $event->_via_ip );
+ }
+
+ $validated = array(
+ 'browser_type' => Jetpack_Tracks_Client::BROWSER_TYPE,
+ '_aua' => Jetpack_Tracks_Client::get_user_agent(),
+ );
+
+ $_event = (object) array_merge( (array) $event, $validated );
+
+ // If you want to block property names, do it here.
+
+ // Make sure we have an event timestamp.
+ if ( ! isset( $_event->_ts ) ) {
+ $_event->_ts = Jetpack_Tracks_Client::build_timestamp();
+ }
+
+ return $_event;
+ }
+
+ /**
+ * Build a pixel URL that will send a Tracks event when fired.
+ * On error, returns an empty string ('').
+ *
+ * @return string A pixel URL or empty string ('') if there were invalid args.
+ */
+ public function build_pixel_url() {
+ if ( $this->error ) {
+ return '';
+ }
+
+ $args = get_object_vars( $this );
+
+ // Request Timestamp and URL Terminator must be added just before the HTTP request or not at all.
+ unset( $args['_rt'] );
+ unset( $args['_'] );
+
+ $validated = self::validate_and_sanitize( $args );
+
+ if ( is_wp_error( $validated ) ) {
+ return '';
+ }
+
+ return Jetpack_Tracks_Client::PIXEL . '?' . http_build_query( $validated );
+ }
+
+ /**
+ * Validate the event name.
+ *
+ * @param string $name Event name.
+ * @return false|int
+ */
+ public static function event_name_is_valid( $name ) {
+ return preg_match( self::EVENT_NAME_REGEX, $name );
+ }
+
+ /**
+ * Validates prop name
+ *
+ * @param string $name Property name.
+ *
+ * @return false|int Truthy value.
+ */
+ public static function prop_name_is_valid( $name ) {
+ return preg_match( self::PROP_NAME_REGEX, $name );
+ }
+
+ /**
+ * Scrutinize event name.
+ *
+ * @param object $event Tracks event.
+ */
+ public static function scrutinize_event_names( $event ) {
+ if ( ! self::event_name_is_valid( $event->_en ) ) {
+ return;
+ }
+
+ $whitelisted_key_names = array(
+ 'anonId',
+ 'Browser_Type',
+ );
+
+ foreach ( array_keys( (array) $event ) as $key ) {
+ if ( in_array( $key, $whitelisted_key_names, true ) ) {
+ continue;
+ }
+ if ( ! self::prop_name_is_valid( $key ) ) {
+ return;
+ }
+ }
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/src/class-tracking.php b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/src/class-tracking.php
new file mode 100644
index 00000000..cc9d1de2
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/src/class-tracking.php
@@ -0,0 +1,325 @@
+<?php
+/**
+ * Nosara Tracks for Jetpack
+ *
+ * @package automattic/jetpack-tracking
+ */
+
+namespace Automattic\Jetpack;
+
+/**
+ * The Tracking class, used to record events in wpcom
+ */
+class Tracking {
+ /**
+ * The assets version.
+ *
+ * @since 1.13.1
+ *
+ * @var string Assets version.
+ */
+ const ASSETS_VERSION = '1.0.0';
+
+ /**
+ * Slug of the product that we are tracking.
+ *
+ * @var string
+ */
+ private $product_name;
+
+ /**
+ * Connection manager object.
+ *
+ * @var Object
+ */
+ private $connection;
+
+ /**
+ * Creates the Tracking object.
+ *
+ * @param String $product_name the slug of the product that we are tracking.
+ * @param Automattic\Jetpack\Connection\Manager $connection the connection manager object.
+ */
+ public function __construct( $product_name = 'jetpack', $connection = null ) {
+ $this->product_name = $product_name;
+ $this->connection = $connection;
+ if ( is_null( $this->connection ) ) {
+ // TODO We should always pass a Connection.
+ $this->connection = new Connection\Manager();
+ }
+
+ if ( ! did_action( 'jetpack_set_tracks_ajax_hook' ) ) {
+ add_action( 'wp_ajax_jetpack_tracks', array( $this, 'ajax_tracks' ) );
+
+ /**
+ * Fires when the Tracking::ajax_tracks() callback has been hooked to the
+ * wp_ajax_jetpack_tracks action. This action is used to ensure that
+ * the callback is hooked only once.
+ *
+ * @since 1.13.11
+ */
+ do_action( 'jetpack_set_tracks_ajax_hook' );
+ }
+ }
+
+ /**
+ * Universal method for for all tracking events triggered via the JavaScript client.
+ *
+ * @access public
+ */
+ public function ajax_tracks() {
+ // Check for nonce.
+ if (
+ empty( $_REQUEST['tracksNonce'] )
+ || ! wp_verify_nonce( $_REQUEST['tracksNonce'], 'jp-tracks-ajax-nonce' )
+ ) {
+ wp_send_json_error(
+ __( 'You aren’t authorized to do that.', 'jetpack-tracking' ),
+ 403
+ );
+ }
+
+ if ( ! isset( $_REQUEST['tracksEventName'] ) || ! isset( $_REQUEST['tracksEventType'] ) ) {
+ wp_send_json_error(
+ __( 'No valid event name or type.', 'jetpack-tracking' ),
+ 403
+ );
+ }
+
+ $tracks_data = array();
+ if ( 'click' === $_REQUEST['tracksEventType'] && isset( $_REQUEST['tracksEventProp'] ) ) {
+ if ( is_array( $_REQUEST['tracksEventProp'] ) ) {
+ $tracks_data = $_REQUEST['tracksEventProp'];
+ } else {
+ $tracks_data = array( 'clicked' => $_REQUEST['tracksEventProp'] );
+ }
+ }
+
+ $this->record_user_event( $_REQUEST['tracksEventName'], $tracks_data, null, false );
+
+ wp_send_json_success();
+ }
+
+ /**
+ * Register script necessary for tracking.
+ *
+ * @param boolean $enqueue Also enqueue? defaults to false.
+ */
+ public static function register_tracks_functions_scripts( $enqueue = false ) {
+
+ // Register jp-tracks as it is a dependency.
+ wp_register_script(
+ 'jp-tracks',
+ '//stats.wp.com/w.js',
+ array(),
+ gmdate( 'YW' ),
+ true
+ );
+
+ if ( $enqueue ) {
+ // Enqueue jp-tracks-functions script.
+ wp_enqueue_script(
+ 'jp-tracks-functions',
+ Assets::get_file_url_for_environment( 'js/tracks-callables.js', 'js/tracks-callables.js', __FILE__ ),
+ array( 'jp-tracks' ),
+ self::ASSETS_VERSION,
+ true
+ );
+ } else {
+ // Register jp-tracks-functions script.
+ wp_register_script(
+ 'jp-tracks-functions',
+ Assets::get_file_url_for_environment( 'js/tracks-callables.js', 'js/tracks-callables.js', __FILE__ ),
+ array( 'jp-tracks' ),
+ self::ASSETS_VERSION,
+ true
+ );
+ }
+
+ }
+
+ /**
+ * Enqueue script necessary for tracking.
+ */
+ public function enqueue_tracks_scripts() {
+ wp_enqueue_script(
+ 'jptracks',
+ Assets::get_file_url_for_environment( 'js/tracks-ajax.js', 'js/tracks-ajax.js', __FILE__ ),
+ array(),
+ self::ASSETS_VERSION,
+ true
+ );
+
+ wp_localize_script(
+ 'jptracks',
+ 'jpTracksAJAX',
+ array(
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
+ 'jpTracksAJAX_nonce' => wp_create_nonce( 'jp-tracks-ajax-nonce' ),
+ )
+ );
+ }
+
+ /**
+ * Send an event in Tracks.
+ *
+ * @param string $event_type Type of the event.
+ * @param array $data Data to send with the event.
+ * @param mixed $user Username, user_id, or WP_user object.
+ * @param bool $use_product_prefix Whether to use the object's product name as a prefix to the event type. If
+ * set to false, the prefix will be 'jetpack_'.
+ */
+ public function record_user_event( $event_type, $data = array(), $user = null, $use_product_prefix = true ) {
+ if ( ! $user ) {
+ $user = wp_get_current_user();
+ }
+ $site_url = get_option( 'siteurl' );
+
+ $data['_via_ua'] = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : '';
+ $data['_via_ip'] = isset( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : '';
+ $data['_lg'] = isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : '';
+ $data['blog_url'] = $site_url;
+ $data['blog_id'] = \Jetpack_Options::get_option( 'id' );
+
+ // Top level events should not be namespaced.
+ if ( '_aliasUser' !== $event_type ) {
+ $prefix = $use_product_prefix ? $this->product_name : 'jetpack';
+ $event_type = $prefix . '_' . $event_type;
+ }
+
+ $data['jetpack_version'] = defined( 'JETPACK__VERSION' ) ? JETPACK__VERSION : '0';
+
+ return $this->tracks_record_event( $user, $event_type, $data );
+ }
+
+ /**
+ * Record an event in Tracks - this is the preferred way to record events from PHP.
+ *
+ * @param mixed $user username, user_id, or WP_user object.
+ * @param string $event_name The name of the event.
+ * @param array $properties Custom properties to send with the event.
+ * @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
+ *
+ * @return bool true for success | \WP_Error if the event pixel could not be fired
+ */
+ public function tracks_record_event( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
+
+ // We don't want to track user events during unit tests/CI runs.
+ if ( $user instanceof \WP_User && 'wptests_capabilities' === $user->cap_key ) {
+ return false;
+ }
+ $terms_of_service = new Terms_Of_Service();
+ $status = new Status();
+ // Don't track users who have not agreed to our TOS.
+ if ( ! $this->should_enable_tracking( $terms_of_service, $status ) ) {
+ return false;
+ }
+
+ $event_obj = $this->tracks_build_event_obj( $user, $event_name, $properties, $event_timestamp_millis );
+
+ if ( is_wp_error( $event_obj->error ) ) {
+ return $event_obj->error;
+ }
+
+ return $event_obj->record();
+ }
+
+ /**
+ * Determines whether tracking should be enabled.
+ *
+ * @param Automattic\Jetpack\Terms_Of_Service $terms_of_service A Terms_Of_Service object.
+ * @param Automattic\Jetpack\Status $status A Status object.
+ *
+ * @return boolean True if tracking should be enabled, else false.
+ */
+ public function should_enable_tracking( $terms_of_service, $status ) {
+ if ( $status->is_offline_mode() ) {
+ return false;
+ }
+
+ return $terms_of_service->has_agreed() || $this->connection->is_user_connected();
+ }
+
+ /**
+ * Procedurally build a Tracks Event Object.
+ * NOTE: Use this only when the simpler Automattic\Jetpack\Tracking->jetpack_tracks_record_event() function won't work for you.
+ *
+ * @param WP_user $user WP_user object.
+ * @param string $event_name The name of the event.
+ * @param array $properties Custom properties to send with the event.
+ * @param int $event_timestamp_millis The time in millis since 1970-01-01 00:00:00 when the event occurred.
+ *
+ * @return \Jetpack_Tracks_Event|\WP_Error
+ */
+ private function tracks_build_event_obj( $user, $event_name, $properties = array(), $event_timestamp_millis = false ) {
+ $identity = $this->tracks_get_identity( $user->ID );
+
+ $properties['user_lang'] = $user->get( 'WPLANG' );
+
+ $blog_details = array(
+ 'blog_lang' => isset( $properties['blog_lang'] ) ? $properties['blog_lang'] : get_bloginfo( 'language' ),
+ );
+
+ $timestamp = ( false !== $event_timestamp_millis ) ? $event_timestamp_millis : round( microtime( true ) * 1000 );
+ $timestamp_string = is_string( $timestamp ) ? $timestamp : number_format( $timestamp, 0, '', '' );
+
+ return new \Jetpack_Tracks_Event(
+ array_merge(
+ $blog_details,
+ (array) $properties,
+ $identity,
+ array(
+ '_en' => $event_name,
+ '_ts' => $timestamp_string,
+ )
+ )
+ );
+ }
+
+ /**
+ * Get the identity to send to tracks.
+ *
+ * @param int $user_id The user id of the local user.
+ *
+ * @return array $identity
+ */
+ public function tracks_get_identity( $user_id ) {
+
+ // Meta is set, and user is still connected. Use WPCOM ID.
+ $wpcom_id = get_user_meta( $user_id, 'jetpack_tracks_wpcom_id', true );
+ if ( $wpcom_id && $this->connection->is_user_connected( $user_id ) ) {
+ return array(
+ '_ut' => 'wpcom:user_id',
+ '_ui' => $wpcom_id,
+ );
+ }
+
+ // User is connected, but no meta is set yet. Use WPCOM ID and set meta.
+ if ( $this->connection->is_user_connected( $user_id ) ) {
+ $wpcom_user_data = $this->connection->get_connected_user_data( $user_id );
+ update_user_meta( $user_id, 'jetpack_tracks_wpcom_id', $wpcom_user_data['ID'] );
+
+ return array(
+ '_ut' => 'wpcom:user_id',
+ '_ui' => $wpcom_user_data['ID'],
+ );
+ }
+
+ // User isn't linked at all. Fall back to anonymous ID.
+ $anon_id = get_user_meta( $user_id, 'jetpack_tracks_anon_id', true );
+ if ( ! $anon_id ) {
+ $anon_id = \Jetpack_Tracks_Client::get_anon_id();
+ add_user_meta( $user_id, 'jetpack_tracks_anon_id', $anon_id, false );
+ }
+
+ if ( ! isset( $_COOKIE['tk_ai'] ) && ! headers_sent() ) {
+ setcookie( 'tk_ai', $anon_id );
+ }
+
+ return array(
+ '_ut' => 'anon',
+ '_ui' => $anon_id,
+ );
+
+ }
+}
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/src/js/tracks-ajax.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/src/js/tracks-ajax.js
new file mode 100644
index 00000000..cea7cc04
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/src/js/tracks-ajax.js
@@ -0,0 +1,62 @@
+/* global jpTracksAJAX */
+( function ( $, jpTracksAJAX ) {
+ window.jpTracksAJAX = window.jpTracksAJAX || {};
+ var debugSet = localStorage.getItem( 'debug' ) === 'dops:analytics';
+
+ window.jpTracksAJAX.record_ajax_event = function ( eventName, eventType, eventProp ) {
+ var data = {
+ tracksNonce: jpTracksAJAX.jpTracksAJAX_nonce,
+ action: 'jetpack_tracks',
+ tracksEventType: eventType,
+ tracksEventName: eventName,
+ tracksEventProp: eventProp || false,
+ };
+
+ return $.ajax( {
+ type: 'POST',
+ url: jpTracksAJAX.ajaxurl,
+ data: data,
+ success: function ( response ) {
+ if ( debugSet ) {
+ // eslint-disable-next-line
+ console.log( 'AJAX tracks event recorded: ', data, response );
+ }
+ },
+ } );
+ };
+
+ $( document ).ready( function () {
+ $( 'body' ).on( 'click', '.jptracks a, a.jptracks', function ( event ) {
+ var $this = $( event.target );
+ // We know that the jptracks element is either this, or its ancestor
+ var $jptracks = $this.closest( '.jptracks' );
+ // We need an event name at least
+ var eventName = $jptracks.attr( 'data-jptracks-name' );
+ if ( undefined === eventName ) {
+ return;
+ }
+
+ var eventProp = $jptracks.attr( 'data-jptracks-prop' ) || false;
+
+ var url = $this.attr( 'href' );
+ var target = $this.get( 0 ).target;
+ if ( url && target && '_self' !== target ) {
+ var newTabWindow = window.open( '', target );
+ newTabWindow.opener = null;
+ }
+
+ event.preventDefault();
+
+ window.jpTracksAJAX.record_ajax_event( eventName, 'click', eventProp ).always( function () {
+ // Continue on to whatever url they were trying to get to.
+ if ( url && ! $this.hasClass( 'thickbox' ) ) {
+ if ( newTabWindow ) {
+ newTabWindow.location = url;
+ return;
+ }
+ window.location = url;
+ }
+ } );
+ } );
+ } );
+} )( jQuery, jpTracksAJAX );
diff --git a/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/src/js/tracks-callables.js b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/src/js/tracks-callables.js
new file mode 100644
index 00000000..c7fb85d6
--- /dev/null
+++ b/plugins/jetpack/jetpack_vendor/automattic/jetpack-tracking/src/js/tracks-callables.js
@@ -0,0 +1,79 @@
+/**
+ * This was abstracted from wp-calypso's analytics lib: https://github.com/Automattic/wp-calypso/blob/master/client/lib/analytics/README.md
+ * Some stuff was removed like GA tracking and other things not necessary for Jetpack tracking.
+ *
+ * This library should only be used and loaded if the Jetpack site is connected.
+ */
+
+// Load tracking scripts
+window._tkq = window._tkq || [];
+
+var _user;
+var debug = console.error; // eslint-disable-line no-console
+
+function buildQuerystring( group, name ) {
+ var uriComponent = '';
+
+ if ( 'object' === typeof group ) {
+ for ( var key in group ) {
+ uriComponent += '&x_' + encodeURIComponent( key ) + '=' + encodeURIComponent( group[ key ] );
+ }
+ } else {
+ uriComponent = '&x_' + encodeURIComponent( group ) + '=' + encodeURIComponent( name );
+ }
+
+ return uriComponent;
+}
+
+var analytics = {
+ initialize: function ( userId, username ) {
+ analytics.setUser( userId, username );
+ analytics.identifyUser();
+ },
+
+ mc: {
+ bumpStat: function ( group, name ) {
+ var uriComponent = buildQuerystring( group, name ); // prints debug info
+ new Image().src =
+ document.location.protocol +
+ '//pixel.wp.com/g.gif?v=wpcom-no-pv' +
+ uriComponent +
+ '&t=' +
+ Math.random();
+ },
+ },
+
+ tracks: {
+ recordEvent: function ( eventName, eventProperties ) {
+ eventProperties = eventProperties || {};
+
+ if ( eventName.indexOf( 'jetpack_' ) !== 0 ) {
+ debug( '- Event name must be prefixed by "jetpack_"' );
+ return;
+ }
+
+ window._tkq.push( [ 'recordEvent', eventName, eventProperties ] );
+ },
+
+ recordPageView: function ( urlPath ) {
+ analytics.tracks.recordEvent( 'jetpack_page_view', {
+ path: urlPath,
+ } );
+ },
+ },
+
+ setUser: function ( userId, username ) {
+ _user = { ID: userId, username: username };
+ },
+
+ identifyUser: function () {
+ // Don't identify the user if we don't have one
+ if ( _user ) {
+ window._tkq.push( [ 'identifyUser', _user.ID, _user.username ] );
+ }
+ },
+
+ clearedIdentity: function () {
+ window._tkq.push( [ 'clearIdentity' ] );
+ },
+};