From ed4c44a78a38159e017ac3dc78970f3f719cc0bf Mon Sep 17 00:00:00 2001 From: Miguel Peixe Date: Wed, 29 Jan 2025 17:09:39 -0300 Subject: [PATCH] chore(ia): clean up connections --- includes/class-blocks.php | 2 +- includes/class-newspack.php | 1 - includes/class-wizards.php | 1 - includes/oauth/class-fivetran-connection.php | 2 +- includes/wizards/class-connections-wizard.php | 82 --- .../components/onboarding/index.js | 2 +- src/wizards/connections/index.js | 44 -- src/wizards/connections/style.scss | 113 ---- src/wizards/connections/views/index.js | 1 - .../connections/views/main/fivetran.js | 137 ---- src/wizards/connections/views/main/google.js | 178 ------ src/wizards/connections/views/main/index.js | 48 -- .../connections/views/main/mailchimp.js | 173 ----- src/wizards/connections/views/main/plugins.js | 207 ------ .../connections/views/main/recaptcha.js | 218 ------- .../connections/views/main/webhooks.js | 591 ------------------ tests/unit-tests/oauth.php | 2 +- 17 files changed, 4 insertions(+), 1798 deletions(-) delete mode 100644 includes/wizards/class-connections-wizard.php delete mode 100644 src/wizards/connections/index.js delete mode 100644 src/wizards/connections/style.scss delete mode 100644 src/wizards/connections/views/index.js delete mode 100644 src/wizards/connections/views/main/fivetran.js delete mode 100644 src/wizards/connections/views/main/google.js delete mode 100644 src/wizards/connections/views/main/index.js delete mode 100644 src/wizards/connections/views/main/mailchimp.js delete mode 100644 src/wizards/connections/views/main/plugins.js delete mode 100644 src/wizards/connections/views/main/recaptcha.js delete mode 100644 src/wizards/connections/views/main/webhooks.js diff --git a/includes/class-blocks.php b/includes/class-blocks.php index 02ed0fb026..04afcb09f1 100644 --- a/includes/class-blocks.php +++ b/includes/class-blocks.php @@ -46,7 +46,7 @@ public static function enqueue_block_editor_assets() { 'reader_activation_terms' => Reader_Activation::get_setting( 'terms_text' ), 'reader_activation_url' => Reader_Activation::get_setting( 'terms_url' ), 'has_recaptcha' => Recaptcha::can_use_captcha(), - 'recaptcha_url' => admin_url( 'admin.php?page=newspack-connections-wizard' ), + 'recaptcha_url' => admin_url( 'admin.php?page=newspack-settings' ), ] ); \wp_enqueue_style( diff --git a/includes/class-newspack.php b/includes/class-newspack.php index 04fea9ecac..b750254537 100644 --- a/includes/class-newspack.php +++ b/includes/class-newspack.php @@ -174,7 +174,6 @@ private function includes() { include_once NEWSPACK_ABSPATH . 'includes/wizards/class-analytics-wizard.php'; include_once NEWSPACK_ABSPATH . 'includes/wizards/class-engagement-wizard.php'; include_once NEWSPACK_ABSPATH . 'includes/wizards/class-health-check-wizard.php'; - include_once NEWSPACK_ABSPATH . 'includes/wizards/class-connections-wizard.php'; include_once NEWSPACK_ABSPATH . 'includes/class-wizards.php'; include_once NEWSPACK_ABSPATH . 'includes/class-handoff-banner.php'; diff --git a/includes/class-wizards.php b/includes/class-wizards.php index e6ccadff16..f626a2b8e3 100644 --- a/includes/class-wizards.php +++ b/includes/class-wizards.php @@ -34,7 +34,6 @@ public static function init() { 'seo' => new SEO_Wizard(), 'health-check' => new Health_Check_Wizard(), 'engagement' => new Engagement_Wizard(), - 'connections' => new Connections_Wizard(), // v2 Information Architecture. 'newspack-dashboard' => new Newspack_Dashboard(), 'setup' => new Setup_Wizard(), diff --git a/includes/oauth/class-fivetran-connection.php b/includes/oauth/class-fivetran-connection.php index 5b0e0e98d9..c596751379 100644 --- a/includes/oauth/class-fivetran-connection.php +++ b/includes/oauth/class-fivetran-connection.php @@ -114,7 +114,7 @@ public static function api_create_connection( $request ) { [ 'service' => $service, 'service_data' => $service_data, - 'redirect_after' => admin_url( 'admin.php?page=newspack-connections-wizard' ), + 'redirect_after' => admin_url( 'admin.php?page=newspack-settings' ), ] ); $response = self::process_proxy_response( \wp_safe_remote_post( $url, [ 'timeout' => 30 ] ) ); // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout diff --git a/includes/wizards/class-connections-wizard.php b/includes/wizards/class-connections-wizard.php deleted file mode 100644 index ef5ed8f0ef..0000000000 --- a/includes/wizards/class-connections-wizard.php +++ /dev/null @@ -1,82 +0,0 @@ -slug ) { - return; - } - - \wp_register_script( - 'newspack-connections-wizard', - Newspack::plugin_url() . '/dist/connections.js', - $this->get_script_dependencies( [] ), - NEWSPACK_PLUGIN_VERSION, - true - ); - \wp_localize_script( - 'newspack-connections-wizard', - 'newspack_connections_data', - [ - 'can_connect_google' => OAuth::is_proxy_configured( 'google' ), - 'can_connect_fivetran' => OAuth::is_proxy_configured( 'fivetran' ), - 'can_use_webhooks' => defined( 'NEWSPACK_EXPERIMENTAL_WEBHOOKS' ) && NEWSPACK_EXPERIMENTAL_WEBHOOKS, - 'can_use_everlit' => Everlit_Configuration_Manager::is_enabled(), - ] - ); - \wp_enqueue_script( 'newspack-connections-wizard' ); - - \wp_register_style( - 'newspack-connections-wizard', - Newspack::plugin_url() . '/dist/connections.css', - $this->get_style_dependencies(), - NEWSPACK_PLUGIN_VERSION - ); - \wp_style_add_data( 'newspack-connections-wizard', 'rtl', 'replace' ); - \wp_enqueue_style( 'newspack-connections-wizard' ); - } -} diff --git a/src/wizards/advertising/components/onboarding/index.js b/src/wizards/advertising/components/onboarding/index.js index abe7c6a49e..d3ad6cbfc5 100644 --- a/src/wizards/advertising/components/onboarding/index.js +++ b/src/wizards/advertising/components/onboarding/index.js @@ -11,7 +11,7 @@ import { useEffect, useState, useRef, Fragment } from '@wordpress/element'; * Internal dependencies. */ import { Card, ButtonCard, Notice, TextControl } from '../../../../components/src'; -import GoogleOAuth from '../../../connections/views/main/google'; +import GoogleOAuth from '../../../newspack/views/settings/connections/google-oauth'; import { handleJSONFile } from '../utils'; export default function AdsOnboarding( { onUpdate, onSuccess } ) { diff --git a/src/wizards/connections/index.js b/src/wizards/connections/index.js deleted file mode 100644 index 9cc651d955..0000000000 --- a/src/wizards/connections/index.js +++ /dev/null @@ -1,44 +0,0 @@ -import '../../shared/js/public-path'; - -/** - * WordPress dependencies. - */ -import { render, createElement } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal dependencies. - */ -import { withWizard, withWizardScreen } from '../../components/src'; -import Router from '../../components/src/proxied-imports/router'; -import { Main } from './views'; - -import './style.scss'; - -const { HashRouter, Redirect, Route, Switch } = Router; - -const MainScreen = withWizardScreen( Main ); - -const ConnectionsWizard = ( { pluginRequirements, wizardApiFetch, startLoading, doneLoading } ) => { - const wizardScreenProps = { - headerText: __( 'Connections', 'newspack-plugin' ), - subHeaderText: __( 'Connections to third-party services', 'newspack-plugin' ), - wizardApiFetch, - startLoading, - doneLoading, - }; - return ( - - - { pluginRequirements } - } /> - - - - ); -}; - -render( - createElement( withWizard( ConnectionsWizard ) ), - document.getElementById( 'newspack-connections-wizard' ) -); diff --git a/src/wizards/connections/style.scss b/src/wizards/connections/style.scss deleted file mode 100644 index c2b8ccb7b8..0000000000 --- a/src/wizards/connections/style.scss +++ /dev/null @@ -1,113 +0,0 @@ -@use "sass:color"; -@use "~@wordpress/base-styles/colors" as wp-colors; - -.newspack-webhooks { - &__endpoint { - &__action { - font-weight: 700; - font-family: Consolas, monaco, monospace; - background: wp-colors.$gray-100; - color: wp-colors.$gray-900; - display: inline-block; - margin-right: 4px; - padding: 2px 4px; - font-size: 0.8em; - } - - &__label { - margin-right: 8px; - } - &__url { - font-family: Consolas, monaco, monospace; - font-size: 0.8em; - color: wp-colors.$gray-900; - } - - .newspack-action-card__title { - display: flex; - align-items: center; - } - } - &__test-response { - display: flex; - align-items: center; - justify-content: center; - font-family: Consolas, monaco, monospace; - font-size: 12px; - &.status { - &--success { - .code { - color: wp-colors.$alert-green; - } - } - &--error { - .code { - color: wp-colors.$alert-red; - } - } - } - .code { - margin-left: 8px; - } - } - &__requests { - border-collapse: collapse; - white-space: nowrap; - width: 100%; - th { - text-align: left; - } - tr:nth-child(odd) td { - background-color: wp-colors.$gray-100; - } - th, - td { - border-bottom: 1px solid wp-colors.$gray-300; - padding: 6px; - } - td { - color: wp-colors.$gray-900; - font-size: 12px; - &:last-child { - text-align: right; - } - } - .status { - &--finished { - fill: wp-colors.$alert-green; - } - &--killed { - fill: wp-colors.$alert-red; - } - &--pending { - fill: wp-colors.$alert-yellow; - } - svg { - display: block; - } - } - .action-name { - font-weight: 700; - font-family: Consolas, monaco, monospace; - } - &.has-error { - .error { - width: 100%; - white-space: normal; - } - .error-count { - background: rgba(black, 0.025); - color: color.adjust(wp-colors.$gray-700, $lightness: -0.75%); - padding: 1px 3px; - margin-right: 4px; - border-radius: 1px; - display: inline-block; - } - } - &:not(.has-error) { - .action-name { - width: 100%; - } - } - } -} diff --git a/src/wizards/connections/views/index.js b/src/wizards/connections/views/index.js deleted file mode 100644 index dc91e6bec3..0000000000 --- a/src/wizards/connections/views/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as Main } from './main'; diff --git a/src/wizards/connections/views/main/fivetran.js b/src/wizards/connections/views/main/fivetran.js deleted file mode 100644 index ba478068b3..0000000000 --- a/src/wizards/connections/views/main/fivetran.js +++ /dev/null @@ -1,137 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { useEffect, useState } from '@wordpress/element'; -import apiFetch from '@wordpress/api-fetch'; -import { Button, CheckboxControl } from '@wordpress/components'; - -/** - * Internal dependencies - */ -import { ActionCard } from '../../../../components/src'; - -/** - * External dependencies - */ -import classnames from 'classnames'; -import get from 'lodash/get'; - -const CONNECTORS = [ - { - service: 'stripe', - label: __( 'Stripe', 'newspack-plugin' ), - }, -]; - -const getConnectionStatus = ( item, connections ) => { - const hasConnections = connections !== undefined; - const setupState = get( connections, [ item.service, 'setup_state' ] ); - const syncState = get( connections, [ item.service, 'sync_state' ] ); - const schemaStatus = get( connections, [ item.service, 'schema_status' ] ); - const isPending = ( schemaStatus && 'ready' !== schemaStatus ) || 'paused' === syncState; - let label = '-'; - if ( setupState ) { - if ( 'ready' === schemaStatus ) { - label = `${ setupState }, ${ syncState }`; - } else if ( isPending ) { - label = `${ setupState }, ${ syncState }. ${ __( - 'Sync is in progress – please check back in a while.', - 'newspack-plugin' - ) }`; - } - } else if ( hasConnections ) { - label = __( 'Not connected', 'newspack-plugin' ); - } - return { - label, - isConnected: setupState === 'connected', - isPending, - }; -}; - -const FivetranConnection = ( { setError } ) => { - const [ connections, setConnections ] = useState(); - const [ inFlight, setInFlight ] = useState( false ); - const [ hasAcceptedTOS, setHasAcceptedTOS ] = useState( null ); - - const handleError = err => - setError( err.message || __( 'Something went wrong.', 'newspack-plugin' ) ); - - const hasConnections = connections !== undefined; - const isDisabled = inFlight || ! hasConnections || ! hasAcceptedTOS; - - useEffect( () => { - setInFlight( true ); - apiFetch( { path: '/newspack/v1/oauth/fivetran' } ) - .then( response => { - setConnections( response.connections_statuses ); - setHasAcceptedTOS( response.has_accepted_tos ); - } ) - .catch( handleError ) - .finally( () => setInFlight( false ) ); - }, [] ); - - const createConnection = ( { service } ) => { - setInFlight( true ); - apiFetch( { - path: `/newspack/v1/oauth/fivetran/${ service }`, - method: 'POST', - data: { - service, - }, - } ) - .then( ( { url } ) => ( window.location = url ) ) - .catch( handleError ); - }; - - return ( - <> -
- { __( 'In order to use the this features, you must read and accept', 'newspack-plugin' ) }{ ' ' } - - { __( 'Newspack Terms of Service', 'newspack-plugin' ) } - - : -
- { - apiFetch( { - path: `/newspack/v1/oauth/fivetran-tos`, - method: 'POST', - data: { - has_accepted, - }, - } ); - setHasAcceptedTOS( has_accepted ); - } } - label={ __( "I've read and accept Newspack Terms of Service", 'newspack-plugin' ) } - /> - { CONNECTORS.map( item => { - const status = getConnectionStatus( item, connections ); - return ( - createConnection( item ) } isLink> - { status.isConnected - ? __( 'Re-connect', 'newspack-plugin' ) - : __( 'Connect', 'newspack-plugin' ) } - - } - checkbox={ status.isConnected ? 'checked' : 'unchecked' } - isMedium - /> - ); - } ) } - - ); -}; - -export default FivetranConnection; diff --git a/src/wizards/connections/views/main/google.js b/src/wizards/connections/views/main/google.js deleted file mode 100644 index 800bf417fe..0000000000 --- a/src/wizards/connections/views/main/google.js +++ /dev/null @@ -1,178 +0,0 @@ -/** - * External dependencies. - */ -import qs from 'qs'; - -/** - * WordPress dependencies. - */ -import { useEffect, useState } from '@wordpress/element'; -import { __, sprintf } from '@wordpress/i18n'; -import apiFetch from '@wordpress/api-fetch'; - -/** - * Internal dependencies. - */ -import { ActionCard, Button } from '../../../../components/src'; - -const getURLParams = () => { - return qs.parse( window.location.search.replace( /^\?/, '' ) ); -}; - -const GoogleOAuth = ( { setError, onInit, onSuccess, isOnboarding } ) => { - const [ authState, setAuthState ] = useState( {} ); - - const userBasicInfo = authState.user_basic_info; - - const [ inFlight, setInFlight ] = useState( false ); - const [ localError, setLocalError ] = useState( null ); - const handleError = res => { - const message = res.message || __( 'Something went wrong.', 'newspack-plugin' ); - setLocalError( message ); - if ( typeof setError === 'function' ) { - setError( message ); - } - }; - - const isConnected = Boolean( userBasicInfo && userBasicInfo.email ); - - useEffect( () => { - if ( isConnected && ! userBasicInfo.has_refresh_token ) { - setError( [ - __( 'Missing Google refresh token. Please', 'newspack-plugin' ), - ' ', - - { __( 'revoke credentials', 'newspack-plugin' ) } - , - ' ', - __( 'and authorize the site again.', 'newspack-plugin' ), - ] ); - } - }, [ isConnected ] ); - - const getCurrentAuth = () => { - const params = getURLParams(); - if ( ! params.access_token ) { - let error = null; - setInFlight( true ); - apiFetch( { path: '/newspack/v1/oauth/google' } ) - .then( data => { - setAuthState( data ); - setError(); - setLocalError(); - if ( data?.user_basic_info && typeof onSuccess === 'function' ) { - onSuccess( data ); - } - } ) - .catch( err => { - error = err; - handleError( err ); - } ) - .finally( () => { - setInFlight( false ); - if ( typeof onInit === 'function' ) { - onInit( error ); - } - } ); - } - }; - - // We only want to autofetch the current auth state if we're not onboarding. - useEffect( () => { - if ( ! isOnboarding ) { - getCurrentAuth(); - } - }, [] ); - - const openAuth = () => { - const authWindow = window.open( - 'about:blank', - 'newspack_google_oauth', - 'width=500,height=600' - ); - setInFlight( true ); - apiFetch( { - path: '/newspack/v1/oauth/google/start', - } ) - .then( url => { - /** authWindow can be 'null' due to browser's popup blocker. */ - if ( authWindow ) { - authWindow.location = url; - const interval = setInterval( () => { - if ( authWindow?.closed ) { - clearInterval( interval ); - getCurrentAuth(); - } - }, 500 ); - } - } ) - .catch( err => { - if ( authWindow ) { - authWindow.close(); - } - handleError( err ); - setInFlight( false ); - } ); - }; - - // Redirect user to Google auth screen. - const disconnect = () => { - setInFlight( true ); - apiFetch( { - path: '/newspack/v1/oauth/google/revoke', - method: 'DELETE', - } ) - .then( () => { - setAuthState( {} ); - setError(); - setLocalError(); - } ) - .catch( handleError ) - .finally( () => setInFlight( false ) ); - }; - - const getDescription = () => { - if ( localError ) { - return localError; - } - if ( inFlight ) { - return __( 'Loading…', 'newspack-plugin' ); - } - if ( isConnected ) { - return sprintf( - // Translators: connected user's email address. - __( 'Connected as %s', 'newspack-plugin' ), - userBasicInfo.email - ); - } - return __( 'Not connected', 'newspack-plugin' ); - }; - - return ( - - { isConnected - ? __( 'Disconnect', 'newspack-plugin' ) - : __( 'Connect', 'newspack-plugin' ) } - - } - isMedium - /> - ); -}; - -export default GoogleOAuth; diff --git a/src/wizards/connections/views/main/index.js b/src/wizards/connections/views/main/index.js deleted file mode 100644 index f17e15b1b6..0000000000 --- a/src/wizards/connections/views/main/index.js +++ /dev/null @@ -1,48 +0,0 @@ -/* global newspack_connections_data */ - -/** - * WordPress dependencies. - */ -import { __ } from '@wordpress/i18n'; -import { useState } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import { Notice, SectionHeader } from '../../../../components/src'; -import Plugins from './plugins'; -import GoogleAuth from './google'; -import Mailchimp from './mailchimp'; -import FivetranConnection from './fivetran'; -import Recaptcha from './recaptcha'; -import Webhooks from './webhooks'; - -const Main = () => { - const [ error, setError ] = useState(); - const setErrorWithPrefix = prefix => err => setError( err ? prefix + err : null ); - - return ( - <> - { error && } - - - - { newspack_connections_data.can_connect_google && ( - - ) } - - { newspack_connections_data.can_connect_fivetran && ( - <> - - - - ) } - - { newspack_connections_data.can_use_webhooks && } - - ); -}; - -export default Main; diff --git a/src/wizards/connections/views/main/mailchimp.js b/src/wizards/connections/views/main/mailchimp.js deleted file mode 100644 index f8c5c360d2..0000000000 --- a/src/wizards/connections/views/main/mailchimp.js +++ /dev/null @@ -1,173 +0,0 @@ -/** - * WordPress dependencies. - */ -import { useEffect, useState, useRef } from '@wordpress/element'; -import { __, sprintf } from '@wordpress/i18n'; -import apiFetch from '@wordpress/api-fetch'; -import { ENTER } from '@wordpress/keycodes'; -import { ExternalLink } from '@wordpress/components'; - -/** - * Internal dependencies. - */ -import { ActionCard, Button, Card, Grid, Modal, TextControl } from '../../../../components/src'; - -const Mailchimp = ( { setError } ) => { - const [ authState, setAuthState ] = useState( {} ); - const [ isModalOpen, setisModalOpen ] = useState( false ); - const [ apiKey, setAPIKey ] = useState(); - const [ isLoading, setIsLoading ] = useState( false ); - - const modalTextRef = useRef( null ); - const isConnected = Boolean( authState && authState.username ); - - const handleError = res => - setError( res.message || __( 'Something went wrong.', 'newspack-plugin' ) ); - - const openModal = () => setisModalOpen( true ); - const closeModal = () => { - setisModalOpen( false ); - setAPIKey(); - }; - - // Check the Mailchimp connectivity status. - useEffect( () => { - setIsLoading( true ); - apiFetch( { path: '/newspack/v1/oauth/mailchimp' } ) - .then( res => { - setAuthState( res ); - } ) - .catch( handleError ) - .finally( () => setIsLoading( false ) ); - }, [] ); - - useEffect( () => { - if ( isModalOpen ) { - modalTextRef.current.querySelector( 'input' ).focus(); - } - }, [ isModalOpen ] ); - - const submitAPIKey = () => { - setError(); - setIsLoading( true ); - apiFetch( { - path: '/newspack/v1/oauth/mailchimp', - method: 'POST', - data: { - api_key: apiKey, - }, - } ) - .then( res => { - setAuthState( res ); - } ) - .catch( e => { - setError( - e.message || - __( - 'Something went wrong during verification of your Mailchimp API key.', - 'newspack-plugin' - ) - ); - } ) - .finally( () => { - setIsLoading( false ); - closeModal(); - } ); - }; - - const disconnect = () => { - setIsLoading( true ); - apiFetch( { - path: '/newspack/v1/oauth/mailchimp', - method: 'DELETE', - } ) - .then( () => { - setAuthState( {} ); - setIsLoading( false ); - } ) - .catch( handleError ); - }; - - const getDescription = () => { - if ( isLoading ) { - return __( 'Loading…', 'newspack-plugin' ); - } - if ( isConnected ) { - // Translators: user connection status message. - return sprintf( __( 'Connected as %s', 'newspack-plugin' ), authState.username ); - } - return __( 'Not connected', 'newspack-plugin' ); - }; - - const getModalButtonText = () => { - if ( isLoading ) { - return __( 'Connecting…', 'newspack-plugin' ); - } - if ( isConnected ) { - return __( 'Connected', 'newspack-plugin' ); - } - return __( 'Connect', 'newspack-plugin' ); - }; - - return ( - <> - - { isConnected - ? __( 'Disconnect', 'newspack-plugin' ) - : __( 'Connect', 'newspack-plugin' ) } - - } - isMedium - /> - { isModalOpen && ( - -
- - { - if ( ENTER === event.keyCode && '' !== apiKey ) { - event.preventDefault(); - submitAPIKey(); - } - } } - /> -

- - { __( 'Find or generate your API key', 'newspack-plugin' ) } - -

-
-
- - - - -
- ) } - - ); -}; - -export default Mailchimp; diff --git a/src/wizards/connections/views/main/plugins.js b/src/wizards/connections/views/main/plugins.js deleted file mode 100644 index 55257930af..0000000000 --- a/src/wizards/connections/views/main/plugins.js +++ /dev/null @@ -1,207 +0,0 @@ -/** - * WordPress dependencies - */ -import { __, sprintf } from '@wordpress/i18n'; -import { useEffect } from '@wordpress/element'; -import apiFetch from '@wordpress/api-fetch'; - -/** - * Internal dependencies - */ -import { ActionCard, Button, hooks, Waiting } from '../../../../components/src'; - -async function fetchHandler( slug, action = '' ) { - const path = action - ? `/newspack/v1/plugins/${ slug }/${ action }` - : `/newspack/v1/plugins/${ slug }`; - const method = action ? 'POST' : 'GET'; - const result = await apiFetch( { path, method } ); - return { - status: result.Status, - configured: result.Configured, - }; -} - -const PLUGINS = { - jetpack: { - pluginSlug: 'jetpack', - editLink: 'admin.php?page=jetpack#/settings', - title: 'Jetpack', - init: () => fetchHandler( 'jetpack' ), - activate: () => fetchHandler( 'jetpack', 'activate' ), - install: () => fetchHandler( 'jetpack', 'install' ), - }, - 'google-site-kit': { - pluginSlug: 'google-site-kit', - editLink: 'admin.php?page=googlesitekit-splash', - title: __( 'Site Kit by Google', 'newspack-plugin' ), - statusDescription: { - notConfigured: __( 'Not connected for this user', 'newspack-plugin' ), - }, - init: () => fetchHandler( 'google-site-kit' ), - activate: () => fetchHandler( 'google-site-kit', 'activate' ), - install: () => fetchHandler( 'google-site-kit', 'install' ), - }, - everlit: { - pluginSlug: 'everlit', - editLink: 'admin.php?page=everlit_settings', - title: __( 'Everlit', 'newspack-plugin' ), - subTitle: __( 'AI-Generated Audio Stories', 'newspack-plugin' ), - hidden: window.newspack_connections_data.can_use_everlit !== '1', - description: ( - <> - { __( - 'Complete setup and licensing agreement to unlock 5 free audio stories per month.', - 'newspack-plugin' - ) }{ ' ' } - - { __( 'Learn more', 'newspack-plugin' ) } - - - ), - statusDescription: { - uninstalled: __( 'Not installed.', 'newspack-plugin' ), - inactive: __( 'Inactive.', 'newspack-plugin' ), - notConfigured: __( 'Pending.', 'newspack-plugin' ), - }, - init: () => fetchHandler( 'everlit' ), - activate: () => fetchHandler( 'everlit', 'activate' ), - install: () => fetchHandler( 'everlit', 'install' ), - }, -}; - -const pluginConnectButton = ( { - isLoading, - isSetup, - isActive, - onActivate, - onInstall, - isInstalled, - ...plugin -} ) => { - if ( plugin.status === 'page-reload' ) { - return { __( 'Page reloading…', 'newspack-plugin' ) }; - } - if ( isLoading ) { - return ; - } - if ( ! isInstalled ) { - return ( - - ); - } - if ( ! isActive ) { - return ( - - ); - } - if ( ! isSetup ) { - return { __( 'Complete Setup', 'newspack-plugin' ) }; - } -}; - -const Plugin = ( { plugin, setError } ) => { - const [ pluginState, setPluginState ] = hooks.useObjectState( plugin ); - const { title, subTitle } = pluginState; - const isActive = pluginState.status === 'active'; - const isInstalled = pluginState.status !== 'uninstalled'; - const isConfigured = pluginState.configured; - const isSetup = isActive && isConfigured; - const isLoading = ! pluginState.status; - - useEffect( () => { - plugin - .init() - .then( update => setPluginState( update ) ) - .catch( setError ); - }, [] ); - - const getDescription = () => { - if ( isLoading ) { - return __( 'Loading…', 'newspack-plugin' ); - } - const descriptionSuffix = plugin.description ?? ''; - let description = ''; - if ( ! isInstalled ) { - description = - pluginState.statusDescription?.uninstalled ?? __( 'Uninstalled.', 'newspack-plugin' ); - } else if ( ! isActive ) { - description = pluginState.statusDescription?.inactive ?? __( 'Inactive.', 'newspack-plugin' ); - } else if ( ! isConfigured ) { - description = - pluginState.statusDescription?.notConfigured ?? __( 'Not connected.', 'newspack-plugin' ); - } else { - description = __( 'Connected', 'newspack-plugin' ); - } - return ( - <> - { __( 'Status:', 'newspack-plugin' ) } { description }{ ' ' } - { ! isSetup ? descriptionSuffix : '' } - - ); - }; - - const onActivate = () => { - setPluginState( { status: '' } ); - pluginState - .activate() - .then( () => setPluginState( { status: 'page-reload' } ) ) - .finally( () => { - window.location.reload(); - } ); - }; - - const onInstall = () => { - setPluginState( { status: '' } ); - pluginState.install().then( update => setPluginState( update ) ); - }; - - return ( - - ); -}; - -const Plugins = ( { setError } ) => { - return ( - <> - { Object.entries( PLUGINS ).map( ( [ slug, plugin ] ) => { - return ( - ! Boolean( plugin.hidden ) && ( - - ) - ); - } ) } - - ); -}; - -export default Plugins; diff --git a/src/wizards/connections/views/main/recaptcha.js b/src/wizards/connections/views/main/recaptcha.js deleted file mode 100644 index ad55e0b9d8..0000000000 --- a/src/wizards/connections/views/main/recaptcha.js +++ /dev/null @@ -1,218 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { BaseControl, ExternalLink } from '@wordpress/components'; -import { useEffect, useState } from '@wordpress/element'; -import apiFetch from '@wordpress/api-fetch'; - -/** - * Internal dependencies - */ -import { - ActionCard, - Button, - Grid, - Notice, - SectionHeader, - SelectControl, - TextControl, -} from '../../../../components/src'; - -const Recaptcha = () => { - const [ error, setError ] = useState( null ); - const [ isLoading, setIsLoading ] = useState( false ); - const [ settings, setSettings ] = useState( {} ); - const [ settingsToUpdate, setSettingsToUpdate ] = useState( {} ); - const credentials = settingsToUpdate?.credentials || {}; - const versionCredentials = credentials[ settingsToUpdate?.version ]; - - // Check the reCAPTCHA connectivity status. - useEffect( () => { - const fetchSettings = async () => { - setIsLoading( true ); - try { - const fetchedSettings = await apiFetch( { path: '/newspack/v1/recaptcha' } ); - setSettings( fetchedSettings ); - setSettingsToUpdate( fetchedSettings ); - } catch ( e ) { - setError( e.message || __( 'Error fetching settings.', 'newspack-plugin' ) ); - } finally { - setIsLoading( false ); - } - }; - fetchSettings(); - }, [] ); - - // Clear out site key + secret if changing the version. - useEffect( () => { - if ( settingsToUpdate?.version !== settings?.version ) { - const newCredentials = versionCredentials || {}; - if ( ! newCredentials.site_key || ! newCredentials.site_secret ) { - setError( - __( - 'Your site key and secret must match the selected reCAPTCHA version. Please enter new credentials.', - 'newspack-plugin' - ) - ); - } - } - }, [ settingsToUpdate?.version ] ); - - const updateSettings = async data => { - setError( null ); - setIsLoading( true ); - try { - const newSettings = await apiFetch( { - path: '/newspack/v1/recaptcha', - method: 'POST', - data, - } ); - setSettings( newSettings ); - setSettingsToUpdate( newSettings ); - } catch ( e ) { - setError( e?.message || __( 'Error updating settings.', 'newspack-plugin' ) ); - } finally { - setIsLoading( false ); - } - }; - - const isV3 = 'v3' === settingsToUpdate?.version; - const hasRequiredSettings = versionCredentials - ? versionCredentials.site_key && versionCredentials.site_secret - : false; - - return ( - <> - - ( - <> - { __( - 'Enabling reCAPTCHA can help protect your site against bot attacks and credit card testing.', - 'newspack-plugin' - ) }{ ' ' } - - { __( 'Get started', 'newspack-plugin' ) } - - - ) } - hasGreyHeader={ !! settings.use_captcha } - toggleChecked={ !! settings.use_captcha } - toggleOnChange={ () => updateSettings( { use_captcha: ! settings.use_captcha } ) } - actionContent={ - settings.use_captcha && ( - - ) - } - disabled={ isLoading } - > - { settings.use_captcha && ( - <> - { error && } - { ! hasRequiredSettings && ( - - ) } - - - { __( 'Learn more about reCAPTCHA versions', 'newspack-plugin' ) } - - } - > - - setSettingsToUpdate( { ...settingsToUpdate, version: value } ) - } - // Note: add 'v2_checkbox' here and in Recaptcha::SUPPORTED_VERSIONS to add support for the Checkbox flavor of reCAPTCHA v2. - options={ [ - { value: 'v3', label: __( 'Score based (v3)', 'newspack-plugin' ) }, - { - value: 'v2_invisible', - label: __( 'Challenge (v2) - invisible reCAPTCHA badge', 'newspack-plugin' ), - }, - ] } - /> - - - - { - const newSettings = { ...settingsToUpdate }; - const newCredentials = { ...credentials }; - newCredentials[ newSettings.version ] = - newCredentials[ newSettings.version ] || {}; - newCredentials[ newSettings.version ].site_key = value; - newSettings.credentials = newCredentials; - - setSettingsToUpdate( newSettings ); - } } - disabled={ isLoading } - autoComplete="off" - /> - { - const newSettings = { ...settingsToUpdate }; - const newCredentials = { ...credentials }; - newCredentials[ newSettings.version ] = - newCredentials[ newSettings.version ] || {}; - newCredentials[ newSettings.version ].site_secret = value; - newSettings.credentials = newCredentials; - - setSettingsToUpdate( newSettings ); - } } - disabled={ isLoading } - autoComplete="off" - /> - { isV3 && ( - - setSettingsToUpdate( { ...settingsToUpdate, threshold: value } ) - } - disabled={ isLoading } - help={ - - { __( 'Learn more about the threshold value', 'newspack-plugin' ) } - - } - /> - ) } - - - ) } - - - ); -}; - -export default Recaptcha; diff --git a/src/wizards/connections/views/main/webhooks.js b/src/wizards/connections/views/main/webhooks.js deleted file mode 100644 index c37fd6e7a7..0000000000 --- a/src/wizards/connections/views/main/webhooks.js +++ /dev/null @@ -1,591 +0,0 @@ -/** - * External dependencies - */ -import moment from 'moment'; - -/** - * WordPress dependencies - */ -import { sprintf, __ } from '@wordpress/i18n'; -import { CheckboxControl, MenuItem } from '@wordpress/components'; -import { useEffect, useState, useRef } from '@wordpress/element'; -import apiFetch from '@wordpress/api-fetch'; -import { Icon, settings, check, close, reusableBlock, moreVertical } from '@wordpress/icons'; -import { ESCAPE } from '@wordpress/keycodes'; - -/** - * Internal dependencies - */ -import { - Card, - ActionCard, - Button, - Grid, - Notice, - SectionHeader, - Modal, - TextControl, - Popover, -} from '../../../../components/src'; - -const getDisplayUrl = url => { - let displayUrl = url.slice( 8 ); - if ( url.length > 45 ) { - displayUrl = `${ url.slice( 8, 38 ) }...${ url.slice( -10 ) }`; - } - return displayUrl; -}; - -const getEndpointLabel = endpoint => { - const { label, url } = endpoint; - return label || getDisplayUrl( url ); -}; - -const getEndpointTitle = endpoint => { - const { label, url } = endpoint; - return ( - <> - { label && { label } } - { getDisplayUrl( url ) } - - ); -}; - -const getRequestStatusIcon = status => { - const icons = { - pending: reusableBlock, - finished: check, - killed: close, - }; - return icons[ status ] || settings; -}; - -const hasEndpointErrors = endpoint => { - return endpoint.requests.some( request => request.errors.length ); -}; - -const EndpointActions = ( { - disabled, - position = 'bottom left', - isSystem, - onEdit = () => {}, - onDelete = () => {}, - onView = () => {}, -} ) => { - const [ popoverVisible, setPopoverVisible ] = useState( false ); - useEffect( () => { - setPopoverVisible( false ); - }, [ disabled ] ); - return ( - <> - - - - - ); -}; - -const Webhooks = () => { - const [ inFlight, setInFlight ] = useState( false ); - const [ error, setError ] = useState( false ); - - const [ actions, setActions ] = useState( [] ); - const fetchActions = () => { - apiFetch( { path: '/newspack/v1/data-events/actions' } ) - .then( response => { - setActions( response ); - } ) - .catch( err => { - setError( err ); - } ); - }; - - const [ endpoints, setEndpoints ] = useState( [] ); - const [ deleting, setDeleting ] = useState( false ); - const [ toggling, setToggling ] = useState( false ); - const [ viewing, setViewing ] = useState( false ); - const [ editing, setEditing ] = useState( false ); - const [ editingError, setEditingError ] = useState( false ); - - const modalRef = useRef( null ); - - const fetchEndpoints = () => { - setInFlight( true ); - apiFetch( { path: '/newspack/v1/webhooks/endpoints' } ) - .then( response => { - setEndpoints( response ); - } ) - .catch( err => { - setError( err ); - } ) - .finally( () => { - setInFlight( false ); - } ); - }; - const toggleEndpoint = endpoint => { - setInFlight( true ); - apiFetch( { - path: `/newspack/v1/webhooks/endpoints/${ endpoint.id }`, - method: 'POST', - data: { disabled: ! endpoint.disabled }, - } ) - .then( response => { - setEndpoints( response ); - } ) - .catch( err => { - setError( err ); - } ) - .finally( () => { - setInFlight( false ); - setToggling( false ); - } ); - }; - const deleteEndpoint = endpoint => { - setInFlight( true ); - apiFetch( { - path: `/newspack/v1/webhooks/endpoints/${ endpoint.id }`, - method: 'DELETE', - } ) - .then( response => { - setEndpoints( response ); - } ) - .catch( err => { - setError( err ); - } ) - .finally( () => { - setInFlight( false ); - setDeleting( false ); - } ); - }; - const validateEndpoint = endpoint => { - const errors = []; - if ( ! endpoint.url ) { - errors.push( __( 'URL is required.', 'newspack-plugin' ) ); - } - if ( ! endpoint.actions || ! endpoint.actions.length ) { - errors.push( __( 'At least one action is required.', 'newspack-plugin' ) ); - } - if ( errors.length ) { - setEditingError( { message: errors.join( ' ' ) } ); - } else { - setEditingError( false ); - } - return errors; - } - const upsertEndpoint = endpoint => { - const errors = validateEndpoint( endpoint ); - if ( errors.length ) { - return; - } - setInFlight( true ); - apiFetch( { - path: `/newspack/v1/webhooks/endpoints/${ endpoint.id || '' }`, - method: 'POST', - data: endpoint, - } ) - .then( response => { - setEndpoints( response ); - setEditing( false ); - } ) - .catch( err => { - setEditingError( err ); - } ) - .finally( () => { - setInFlight( false ); - } ); - }; - - const [ testResponse, setTestResponse ] = useState( false ); - const [ testError, setTestError ] = useState( false ); - const sendTestRequest = ( url, bearer_token ) => { - setInFlight( true ); - setTestError( false ); - setTestResponse( false ); - apiFetch( { - path: '/newspack/v1/webhooks/endpoints/test', - method: 'POST', - data: { url, bearer_token }, - } ) - .then( response => { - setTestResponse( response ); - } ) - .catch( err => { - setTestError( err ); - } ) - .finally( () => { - setInFlight( false ); - } ); - }; - - useEffect( fetchActions, [] ); - useEffect( fetchEndpoints, [] ); - - useEffect( () => { - setTestResponse( false ); - setEditingError( false ); - setTestError( false ); - }, [ editing ] ); - - useEffect( () => { - if ( editingError ) { - modalRef?.current?.querySelector('.components-modal__content')?.scrollTo( { top: 0, left: 0, behavior: 'smooth' } ); - } - }, [ editingError ] ); - - return ( - - { false !== error && } - -
- - -
- - { endpoints.length > 0 && ( - <> - { endpoints.map( endpoint => ( - setToggling( endpoint ) } - key={ endpoint.id } - title={ getEndpointTitle( endpoint ) } - disabled={ endpoint.system } - description={ () => { - if ( endpoint.disabled && endpoint.disabled_error ) { - return ( - __( - 'This endpoint is disabled due to excessive request errors', - 'newspack-plugin' - ) + - ': ' + - endpoint.disabled_error - ); - } - return ( - <> - { __( 'Actions:', 'newspack-plugin' ) }{ ' ' } - { endpoint.actions.map( action => ( - - { action } - ) ) } - - ); - } } - actionText={ - setEditing( endpoint ) } - onDelete={ () => setDeleting( endpoint ) } - onView={ () => setViewing( endpoint ) } - isSystem={ endpoint.system } - /> - } - /> - ) ) } - - ) } - { false !== deleting && ( - setDeleting( false ) } - onConfirm={ () => deleteEndpoint( deleting ) } - disabled={ inFlight } - /> - ) } - { false !== toggling && ( - setToggling( false ) } - onConfirm={ () => toggleEndpoint( toggling ) } - disabled={ inFlight } - /> - ) } - { false !== viewing && ( - setViewing( false ) } - > -

- { sprintf( - // translators: %s is the endpoint title (shortened URL). - __( 'Most recent requests for %s', 'newspack-plugin' ), - getEndpointLabel( viewing ) - ) } -

- { viewing.requests.length > 0 ? ( - - - - { hasEndpointErrors( viewing ) && ( - - ) } - - { viewing.requests.map( request => ( - - - - - { hasEndpointErrors( viewing ) && ( - <> - - - - ) } - - ) ) } -
- { __( 'Action', 'newspack-plugin' ) }{ __( 'Error', 'newspack-plugin' ) }
- - { request.action_name } - { 'pending' === request.status - ? sprintf( - // translators: %s is a human-readable time difference. - __( 'sending in %s', 'newspack-plugin' ), - moment( parseInt( request.scheduled ) * 1000 ).fromNow( true ) - ) - : sprintf( - // translators: %s is a human-readable time difference. - __( 'processed %s', 'newspack-plugin' ), - moment( parseInt( request.scheduled ) * 1000 ).fromNow() - ) } - - { request.errors && request.errors.length > 0 - ? request.errors[ request.errors.length - 1 ] - : '--' } - - - { sprintf( - // translators: %s is the number of errors. - __( 'Attempt #%s', 'newspack-plugin' ), - request.errors.length - ) } - -
- ) : ( - - ) } -
- ) } - { false !== editing && ( - { - setEditing( false ); - setEditingError( false ); - } } - > - { false !== editingError && } - { true === editing.disabled && ( - - ) } - { editing.disabled && editing.disabled_error && ( - - ) } - { testError && ( - - ) } - - setEditing( { ...editing, url: value } ) } - disabled={ inFlight } - /> - setEditing( { ...editing, bearer_token: value } ) } - disabled={ inFlight } - /> - - { testResponse && ( -
- { testResponse.message } - { testResponse.code } -
- ) } - -
-
-
- setEditing( { ...editing, label: value } ) } - disabled={ inFlight } - /> - -

{ __( 'Actions', 'newspack-plugin' ) }

- { actions.length > 0 && ( - <> -

- { __( - 'Select which actions should trigger this endpoint:', - 'newspack-plugin' - ) } -

- - { actions.map( ( action, i ) => ( - { - const currentActions = editing.actions || []; - if ( currentActions.includes( action ) ) { - currentActions.splice( currentActions.indexOf( action ), 1 ); - } else { - currentActions.push( action ); - } - setEditing( { ...editing, actions: currentActions } ); - } } - /> - ) ) } - - - ) } - - - -
-
- ) } -
- ); -}; - -export default Webhooks; diff --git a/tests/unit-tests/oauth.php b/tests/unit-tests/oauth.php index 07ff314249..e9b657b934 100644 --- a/tests/unit-tests/oauth.php +++ b/tests/unit-tests/oauth.php @@ -66,7 +66,7 @@ public function test_oauth_google() { $consent_page_params, [ 'scope' => 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/dfp https://www.googleapis.com/auth/analytics https://www.googleapis.com/auth/analytics.edit', - 'redirect_after' => 'http://example.org/wp-admin/admin.php?page=newspack-connections-wizard', + 'redirect_after' => 'http://example.org/wp-admin/admin.php?page=newspack-settings', 'csrf_token' => $csrf_token, ], 'The consent page request params are as expected.'