Skip to content

Commit

Permalink
feat: api key ui in modal
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffersonrabb committed Apr 20, 2020
1 parent 60f81be commit e6f9a6a
Show file tree
Hide file tree
Showing 6 changed files with 348 additions and 84 deletions.
124 changes: 124 additions & 0 deletions includes/class-newspack-newsletters.php
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ public static function enqueue_block_editor_assets() {
return;
}

$mailchimp_api_key = self::mailchimp_api_key();
$mjml_api_key = get_option( 'newspack_newsletters_mjml_api_key', false );
$mjml_api_secret = get_option( 'newspack_newsletters_mjml_api_secret', false );

$has_keys = ! empty( $mailchimp_api_key ) && ! empty( $mjml_api_key ) && ! empty( $mjml_api_secret );

\wp_enqueue_script(
'newspack-newsletters',
plugins_url( '../dist/editor.js', __FILE__ ),
Expand All @@ -173,6 +179,7 @@ public static function enqueue_block_editor_assets() {
'newspack_newsletters_data',
[
'templates' => self::get_newsletter_templates(),
'has_keys' => $has_keys,
]
);

Expand Down Expand Up @@ -275,6 +282,123 @@ public static function rest_api_init() {
],
]
);
\register_rest_route(
'newspack-newsletters/v1/',
'keys',
[
'methods' => \WP_REST_Server::READABLE,
'callback' => [ __CLASS__, 'api_get_keys' ],
'permission_callback' => [ __CLASS__, 'api_permissions_check' ],
]
);
\register_rest_route(
'newspack-newsletters/v1/',
'keys',
[
'methods' => \WP_REST_Server::EDITABLE,
'callback' => [ __CLASS__, 'api_set_keys' ],
'permission_callback' => [ __CLASS__, 'api_permissions_check' ],
'args' => [
'mailchimp_api_key' => [
'sanitize_callback' => 'sanitize_text_field',
],
'mjml_application_id' => [
'sanitize_callback' => 'sanitize_text_field',
],
'mjml_api_secret' => [
'sanitize_callback' => 'sanitize_text_field',
],
],
]
);
}

/**
* Retrieve API keys.
*/
public static function api_get_keys() {
$mailchimp_api_key = self::mailchimp_api_key();
$mjml_api_key = get_option( 'newspack_newsletters_mjml_api_key', false );
$mjml_api_secret = get_option( 'newspack_newsletters_mjml_api_secret', false );

$keys = [
'mailchimp_api_key' => $mailchimp_api_key,
'mjml_api_key' => $mjml_api_key,
'mjml_api_secret' => $mjml_api_secret,
'status' => ! empty( $mailchimp_api_key ) && ! empty( $mjml_api_key ) && ! empty( $mjml_api_secret ),
];
return \rest_ensure_response( $keys );
}

/**
* Set API keys.
*
* @param WP_REST_Request $request API request object.
*/
public static function api_set_keys( $request ) {
$mailchimp_api_key = $request['mailchimp_api_key'];
$mjml_api_key = $request['mjml_api_key'];
$mjml_api_secret = $request['mjml_api_secret'];
$wp_error = new WP_Error();

$errors = [];

if ( empty( $mailchimp_api_key ) ) {
$wp_error->add(
'newspack_newsletters_invalid_keys_mailchimp',
__( 'Please input a Mailchimp API key.', 'newspack-newsletters' )
);
} else {
try {
$mc = new Mailchimp( $mailchimp_api_key );
$ping = $mc->get( 'ping' );
} catch ( Exception $e ) {
$ping = null;
}
if ( $ping ) {
update_option( 'newspack_newsletters_mailchimp_api_key', $mailchimp_api_key );
} else {
$wp_error->add(
'newspack_newsletters_invalid_keys_mailchimp',
__( 'Please input a valid Mailchimp API key.', 'newspack-newsletters' )
);
}
}

if ( empty( $mjml_api_key ) || empty( $mjml_api_secret ) ) {
$wp_error->add(
'newspack_newsletters_invalid_keys_mjml',
__( 'Please input MJML application ID and secret key.', 'newspack-newsletters' )
);
} else {
$credentials = "$mjml_api_key:$mjml_api_secret";
$url = 'https://api.mjml.io/v1/render';
$mjml_test = wp_remote_post(
$url,
[
'body' => wp_json_encode(
[
'mjml' => '<h1>test</h1>',
]
),
'headers' => array(
'Authorization' => 'Basic ' . base64_encode( $credentials ),
),
'timeout' => 10, // phpcs:ignore WordPressVIPMinimum.Performance.RemoteRequestTimeout.timeout_timeout
]
);
if ( 200 === $mjml_test['response']['code'] ) {
update_option( 'newspack_newsletters_mjml_api_key', $mjml_api_key );
update_option( 'newspack_newsletters_mjml_api_secret', $mjml_api_secret );
} else {
$wp_error->add(
'newspack_newsletters_invalid_keys_mjml',
__( 'Please input valid MJML application ID and secret key.', 'newspack-newsletters' )
);
}
}

return $wp_error->has_errors() ? $wp_error : self::api_get_keys();
}

/**
Expand Down
105 changes: 26 additions & 79 deletions src/components/template-modal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,88 +5,35 @@
/**
* WordPress dependencies
*/
import { parse } from '@wordpress/blocks';
import { __, sprintf } from '@wordpress/i18n';
import { Component } from '@wordpress/element';
import { Button, Modal } from '@wordpress/components';
import { ENTER, SPACE } from '@wordpress/keycodes';
import { BlockPreview } from '@wordpress/block-editor';
import { Modal } from '@wordpress/components';
import { __ } from '@wordpress/i18n';

/**
* Internal dependencies
*/
import TemplatePicker from './screens/template-picker';
import APIKeys from './screens/api-keys';
import './style.scss';

class TemplateModal extends Component {
generateBlockPreview = () => {
const { selectedTemplate, templates } = this.props;
return templates && templates[ selectedTemplate ]
? parse( templates[ selectedTemplate ].content )
: null;
};
render = () => {
const { onInsertTemplate, onSelectTemplate, selectedTemplate, templates } = this.props;
const blockPreview = this.generateBlockPreview();
return (
<Modal
className="newspack-newsletters-modal__frame"
isDismissible={ false }
overlayClassName="newspack-newsletters-modal__screen-overlay"
shouldCloseOnClickOutside={ false }
shouldCloseOnEsc={ false }
title={ __( 'Select a layout', 'newspack-newsletters' ) }
>
<div className="newspack-newsletters-modal__content">
<div className="newspack-newsletters-modal__patterns">
<div className="block-editor-patterns">
{ ( templates || [] ).map( ( { title, content }, index ) => (
<div
key={ index }
className={
selectedTemplate === index
? 'selected block-editor-patterns__item'
: 'block-editor-patterns__item'
}
onClick={ () => onSelectTemplate( index ) }
onKeyDown={ event => {
if ( ENTER === event.keyCode || SPACE === event.keyCode ) {
event.preventDefault();
onSelectTemplate( index );
}
} }
role="button"
tabIndex="0"
aria-label={ title }
>
<div className="block-editor-patterns__item-preview">
<BlockPreview blocks={ parse( content ) } viewportWidth={ 810 } />
</div>
<div className="block-editor-patterns__item-title">{ title }</div>
</div>
) ) }
</div>
</div>

<div className="newspack-newsletters-modal__preview">
{ blockPreview && blockPreview.length > 0 ? (
<BlockPreview blocks={ blockPreview } viewportWidth={ 810 } />
) : (
<p>{ __( 'Select a layout to preview.', 'newspack-newsletters' ) }</p>
) }
</div>
</div>

{ selectedTemplate !== null && (
<Button isPrimary onClick={ () => onInsertTemplate( selectedTemplate ) }>
{ sprintf(
__( 'Use %s layout', 'newspack-newsletter' ),
templates[ selectedTemplate ].title
) }
</Button>
) }
</Modal>
);
};
}

export default TemplateModal;
export default ( { hasKeys, onInsertTemplate, onSetupStatus, templates } ) => {
return (
<Modal
className="newspack-newsletters-modal__frame"
isDismissible={ false }
overlayClassName="newspack-newsletters-modal__screen-overlay"
shouldCloseOnClickOutside={ false }
shouldCloseOnEsc={ false }
title={
hasKeys
? __( 'Select a layout', 'newspack-newsletters' )
: __( 'Set up', 'newspack-newsletters' )
}
>
{ hasKeys ? (
<TemplatePicker templates={ templates } onInsertTemplate={ onInsertTemplate } />
) : (
<APIKeys onSetupStatus={ onSetupStatus } />
) }
</Modal>
);
};
117 changes: 117 additions & 0 deletions src/components/template-modal/screens/api-keys/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/**
* WordPress dependencies
*/
import apiFetch from '@wordpress/api-fetch';
import { Button, ExternalLink, Spinner, TextControl } from '@wordpress/components';
import { Fragment, useEffect, useState } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { ENTER } from '@wordpress/keycodes';

export default ( { onSetupStatus } ) => {
const [ keys, setKeys ] = useState( {} );
const [ inFlight, setInFlight ] = useState( false );
const [ errors, setErrors ] = useState( {} );
const commitSettings = () => {
setInFlight( true );
setErrors( {} );
apiFetch( {
path: '/newspack-newsletters/v1/keys',
method: 'POST',
data: keys,
} )
.then( results => {
setInFlight( false );
setKeys( results );
onSetupStatus( results.status );
} )
.catch( handleErrors );
};
const handleErrors = error => {
const allErrors = { [ error.code ]: error.message };
( error.additional_errors || [] ).forEach(
additionalError => ( allErrors[ additionalError.code ] = additionalError.message )
);
setErrors( allErrors );
setInFlight( false );
};
useEffect(() => {
setInFlight( true );
apiFetch( { path: `/newspack-newsletters/v1/keys` } )
.then( results => {
setInFlight( false );
setKeys( results );
onSetupStatus( results.status );
} )
.catch( handleErrors );
}, []);

const {
mailchimp_api_key: mailchimpAPIKey = '',
mjml_api_key: mjmlApplicationId = '',
mjml_api_secret: mjmlAPISecret = '',
} = keys;
return (
<Fragment>
<div className="newspack-newsletters-modal__content">
<div>
<h4>{ __( 'Enter your Mailchimp API key', 'newspack-newsletters' ) }</h4>
{ errors.newspack_newsletters_invalid_keys_mailchimp && (
<p className="error">{ errors.newspack_newsletters_invalid_keys_mailchimp }</p>
) }
<TextControl
label={ __( 'Mailchimp API Key', 'newspack-newsletters' ) }
value={ mailchimpAPIKey }
onChange={ value => setKeys( { ...keys, mailchimp_api_key: value } ) }
disabled={ inFlight }
onKeyDown={ event => {
if ( ENTER === event.keyCode ) {
event.preventDefault();
commitSettings();
}
} }
/>
{ inFlight && <Spinner /> }
<ExternalLink href="https://mailchimp.com/help/about-api-keys/">
{ __( 'About Mailchimp API keys', 'newspack-newsletters' ) }
</ExternalLink>
<h4>{ __( 'Enter your MJML API keys', 'newspack-newsletters' ) }</h4>
{ errors.newspack_newsletters_invalid_keys_mjml && (
<p className="error">{ errors.newspack_newsletters_invalid_keys_mjml }</p>
) }
<TextControl
label={ __( 'MJML Application ID', 'newspack-newsletters' ) }
value={ mjmlApplicationId }
onChange={ value => setKeys( { ...keys, mjml_api_key: value } ) }
disabled={ inFlight }
onKeyDown={ event => {
if ( ENTER === event.keyCode ) {
event.preventDefault();
commitSettings();
}
} }
/>
{ inFlight && <Spinner /> }
<TextControl
label={ __( 'MJML Secret Key', 'newspack-newsletters' ) }
value={ mjmlAPISecret }
onChange={ value => setKeys( { ...keys, mjml_api_secret: value } ) }
disabled={ inFlight }
onKeyDown={ event => {
if ( ENTER === event.keyCode ) {
event.preventDefault();
commitSettings();
}
} }
/>
{ inFlight && <Spinner /> }
<ExternalLink href="https://mjml.io/api">
{ __( 'Request MJML API keys', 'newspack-newsletters' ) }
</ExternalLink>
</div>
</div>
<Button isPrimary onClick={ commitSettings }>
{ __( 'Save Settings', 'newspack-newsletter' ) }
</Button>
</Fragment>
);
};
Loading

0 comments on commit e6f9a6a

Please sign in to comment.