Skip to content

Commit 7f52e34

Browse files
committed
Directly Remove Credentials on 401
1 parent 2fab768 commit 7f52e34

8 files changed

Lines changed: 406 additions & 15 deletions

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"type": "project",
55
"license": "GPLv3",
66
"require": {
7-
"convertkit/convertkit-wordpress-libraries": "2.1.2"
7+
"convertkit/convertkit-wordpress-libraries": "dev-add-invalid-access-token-to-hook"
88
},
99
"require-dev": {
1010
"php-webdriver/webdriver": "^1.0",
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
<?php
2+
/**
3+
* ConvertKit WPForms Admin Notices class.
4+
*
5+
* @package ConvertKit_WPForms
6+
* @author ConvertKit
7+
*/
8+
9+
/**
10+
* Add and remove persistent error messages across all
11+
* WordPress Administration screens.
12+
*
13+
* @package ConvertKit_WPForms
14+
* @author ConvertKit
15+
*/
16+
class Integrate_ConvertKit_WPForms_Admin_Notices {
17+
18+
/**
19+
* Holds the class object.
20+
*
21+
* @since 1.8.9
22+
*
23+
* @var object
24+
*/
25+
private static $instance;
26+
27+
/**
28+
* The key prefix to use for stored notices
29+
*
30+
* @since 1.8.9
31+
*
32+
* @var string
33+
*/
34+
private $key_prefix = 'integrate-convertkit-wpforms-admin-notices';
35+
36+
/**
37+
* Register output function to display persistent notices
38+
* in the WordPress Administration, if any exist.
39+
*
40+
* @since 1.8.9
41+
*/
42+
public function __construct() {
43+
44+
add_action( 'admin_notices', array( $this, 'output' ) );
45+
46+
}
47+
48+
/**
49+
* Output persistent notices in the WordPress Administration
50+
*
51+
* @since 1.8.9
52+
*/
53+
public function output() {
54+
55+
// Don't output if we don't have the required capabilities to fix the issue.
56+
if ( ! current_user_can( 'manage_options' ) ) {
57+
return;
58+
}
59+
60+
// Bail if no notices exist.
61+
$notices = get_option( $this->key_prefix );
62+
if ( ! $notices ) {
63+
return;
64+
}
65+
66+
// Output notices.
67+
foreach ( $notices as $notice ) {
68+
switch ( $notice ) {
69+
case 'authorization_failed':
70+
$output = sprintf(
71+
'%s %s',
72+
esc_html__( 'Kit for WPForms: Authorization failed. Please', 'integrate-convertkit-wpforms' ),
73+
sprintf(
74+
'<a href="%s">%s</a>',
75+
esc_url( admin_url( 'admin.php?page=wpforms-settings&view=integrations' ) ),
76+
esc_html__( 'reconnect your Kit account.', 'integrate-convertkit-wpforms' )
77+
)
78+
);
79+
break;
80+
81+
default:
82+
$output = '';
83+
84+
/**
85+
* Define the text to output in an admin error notice.
86+
*
87+
* @since 1.8.9
88+
*
89+
* @param string $notice Admin notice name.
90+
*/
91+
$output = apply_filters( 'integrate_convertkit_wpforms_admin_notices_output_' . $notice, $output );
92+
break;
93+
}
94+
95+
// If no output defined, skip.
96+
if ( empty( $output ) ) {
97+
continue;
98+
}
99+
?>
100+
<div class="notice notice-error">
101+
<p>
102+
<?php
103+
echo wp_kses(
104+
$output,
105+
wp_kses_allowed_html( 'post' )
106+
);
107+
?>
108+
</p>
109+
</div>
110+
<?php
111+
}
112+
113+
}
114+
115+
/**
116+
* Add a persistent notice for output in the WordPress Administration.
117+
*
118+
* @since 1.8.9
119+
*
120+
* @param string $notice Notice name.
121+
* @return bool Notice saved successfully
122+
*/
123+
public function add( $notice ) {
124+
125+
// If no other persistent notices exist, add one now.
126+
if ( ! $this->exist() ) {
127+
return update_option( $this->key_prefix, array( $notice ) );
128+
}
129+
130+
// Fetch existing persistent notices.
131+
$notices = $this->get();
132+
133+
// Add notice to existing notices.
134+
$notices[] = $notice;
135+
136+
// Remove any duplicate notices.
137+
$notices = array_values( array_unique( $notices ) );
138+
139+
// Update and return.
140+
return update_option( $this->key_prefix, $notices );
141+
142+
}
143+
144+
/**
145+
* Returns all notices stored in the options table.
146+
*
147+
* @since 1.8.9
148+
*
149+
* @return array
150+
*/
151+
public function get() {
152+
153+
// Fetch all notices from the options table.
154+
return get_option( $this->key_prefix );
155+
156+
}
157+
158+
/**
159+
* Whether any persistent notices are stored in the option table.
160+
*
161+
* @since 1.8.9
162+
*
163+
* @return bool
164+
*/
165+
public function exist() {
166+
167+
if ( ! $this->get() ) {
168+
return false;
169+
}
170+
171+
return true;
172+
173+
}
174+
175+
/**
176+
* Delete all persistent notices.
177+
*
178+
* @since 1.8.9
179+
*
180+
* @param string $notice Notice name.
181+
* @return bool Success
182+
*/
183+
public function delete( $notice ) {
184+
185+
// If no persistent notices exist, there's nothing to delete.
186+
if ( ! $this->exist() ) {
187+
return false;
188+
}
189+
190+
// Fetch existing persistent notices.
191+
$notices = $this->get();
192+
193+
// Remove notice from existing notices.
194+
$index = array_search( $notice, $notices, true );
195+
if ( $index !== false ) {
196+
unset( $notices[ $index ] );
197+
}
198+
199+
// Update and return.
200+
return update_option( $this->key_prefix, $notices );
201+
202+
}
203+
204+
/**
205+
* Returns the singleton instance of the class.
206+
*
207+
* @since 1.8.9
208+
*
209+
* @return object Class.
210+
*/
211+
public static function get_instance() {
212+
213+
if ( null === self::$instance ) {
214+
self::$instance = new self();
215+
}
216+
217+
return self::$instance;
218+
219+
}
220+
221+
}
222+
223+
new Integrate_ConvertKit_WPForms_Admin_Notices();

includes/class-integrate-convertkit-wpforms-api.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ class Integrate_ConvertKit_WPForms_API extends ConvertKit_API_V4 {
2929
*/
3030
public $error_messages = false;
3131

32+
/**
33+
* Access Token
34+
*
35+
* @var string
36+
*/
37+
public $access_token = '';
38+
3239
/**
3340
* Sets up the API with the required credentials.
3441
*

includes/class-integrate-convertkit-wpforms-resource.php

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,34 @@ public function __construct( $api_instance, $account_id = '' ) {
3333
$this->settings_name .= '_' . $account_id;
3434
}
3535

36-
// Call parent initialization function.
37-
parent::init();
36+
// Get last query time and existing resources.
37+
$this->last_queried = get_option( $this->settings_name . '_last_queried' );
38+
$this->resources = get_option( $this->settings_name );
39+
40+
}
41+
42+
/**
43+
* Fetches resources (custom fields, forms, sequences or tags) from the API, storing them in the options table
44+
* with a last queried timestamp.
45+
*
46+
* If the refresh results in a 401, removes the access and refresh tokens from the connection.
47+
*
48+
* @since 1.8.9
49+
*
50+
* @return WP_Error|array
51+
*/
52+
public function refresh() {
53+
54+
// Call parent refresh method.
55+
$result = parent::refresh();
56+
57+
// If an error occured, maybe delete credentials from the Plugin's settings
58+
// if the error is a 401 unauthorized.
59+
if ( is_wp_error( $result ) ) {
60+
integrate_convertkit_wpforms_maybe_delete_credentials( $result, INTEGRATE_CONVERTKIT_WPFORMS_OAUTH_CLIENT_ID, $this->api->access_token );
61+
}
62+
63+
return $result;
3864

3965
}
4066

includes/class-integrate-convertkit-wpforms.php

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -667,8 +667,8 @@ public function output_accounts( $connection_id = '', $connection = array() ) {
667667
}
668668

669669
/**
670-
* Outputs a <select> dropdown of ConvertKit Forms, allowing the user
671-
* to choose which ConvertKit Form to send form submissions to.
670+
* Outputs a <select> dropdown of Kit Forms, Sequences and Tags, allowing the user
671+
* to choose which resource to send form submissions to.
672672
*
673673
* @since 1.5.0
674674
*
@@ -691,28 +691,67 @@ public function output_lists( $connection_id = '', $connection = array() ) {
691691
return '';
692692
}
693693

694+
// Get the selected ConvertKit subscribe setting, if one was already defined.
695+
$value = ! empty( $connection['list_id'] ) ? $connection['list_id'] : '';
696+
697+
// Initialize resource classes.
698+
$forms = new Integrate_ConvertKit_WPForms_Resource_Forms( $api, $connection['account_id'] );
699+
$sequences = new Integrate_ConvertKit_WPForms_Resource_Sequences( $api, $connection['account_id'] );
700+
$tags = new Integrate_ConvertKit_WPForms_Resource_Tags( $api, $connection['account_id'] );
701+
694702
// Fetch Forms.
695703
// We use refresh() to ensure we get the latest data, as we're in the admin interface
696704
// and need to populate the select dropdown.
697-
$forms = new Integrate_ConvertKit_WPForms_Resource_Forms( $api, $connection['account_id'] );
698-
$forms->refresh();
705+
$result = $forms->refresh();
706+
707+
// Return the <select> dropdown if an error occurred, so the cached resources are
708+
// available for selection.
709+
if ( is_wp_error( $result ) ) {
710+
return $this->output_select_dropdown( $forms, $sequences, $tags, $value, $connection_id, $result->get_error_message() );
711+
}
699712

700713
// Fetch Sequences.
701714
// We use refresh() to ensure we get the latest data, as we're in the admin interface
702715
// and need to populate the select dropdown.
703-
$sequences = new Integrate_ConvertKit_WPForms_Resource_Sequences( $api, $connection['account_id'] );
704-
$sequences->refresh();
716+
$result = $sequences->refresh();
717+
718+
// Return the <select> dropdown if an error occurred, so the cached resources are
719+
// available for selection.
720+
if ( is_wp_error( $result ) ) {
721+
return $this->output_select_dropdown( $forms, $sequences, $tags, $value, $connection_id, $result->get_error_message() );
722+
}
705723

706724
// Fetch Tags.
707725
// We use refresh() to ensure we get the latest data, as we're in the admin interface
708726
// and need to populate the select dropdown.
709-
$tags = new Integrate_ConvertKit_WPForms_Resource_Tags( $api, $connection['account_id'] );
710-
$tags->refresh();
727+
$result = $tags->refresh();
711728

712-
// Get the selected ConvertKit subscribe setting, if one was already defined.
713-
$value = ! empty( $connection['list_id'] ) ? $connection['list_id'] : '';
729+
// Return the <select> dropdown if an error occurred, so the cached resources are
730+
// available for selection.
731+
if ( is_wp_error( $result ) ) {
732+
return $this->output_select_dropdown( $forms, $sequences, $tags, $value, $connection_id, $result->get_error_message() );
733+
}
714734

715735
// Output <select> dropdown.
736+
return $this->output_select_dropdown( $forms, $sequences, $tags, $value, $connection_id );
737+
738+
}
739+
740+
/**
741+
* Outputs the <select> dropdown.
742+
*
743+
* @since 1.8.9
744+
*
745+
* @param Integrate_ConvertKit_WPForms_Resource_Forms $forms Forms resource.
746+
* @param Integrate_ConvertKit_WPForms_Resource_Sequences $sequences Sequences resource.
747+
* @param Integrate_ConvertKit_WPForms_Resource_Tags $tags Tags resource.
748+
* @param string $value Selected value.
749+
* @param string $connection_id Connection ID.
750+
* @param string $error_message Error message.
751+
* @return string
752+
*/
753+
private function output_select_dropdown( $forms, $sequences, $tags, $value, $connection_id, $error_message = '' ) {
754+
716755
ob_start();
717756
require INTEGRATE_CONVERTKIT_WPFORMS_PATH . '/views/backend/settings-form-marketing-forms-dropdown.php';
718757
return ob_get_clean();
@@ -871,6 +910,10 @@ public function maybe_get_and_store_access_token() {
871910
)
872911
);
873912

913+
// Remove any existing persistent notice.
914+
$admin_notices = Integrate_ConvertKit_WPForms_Admin_Notices::get_instance();
915+
$admin_notices->delete( 'authorization_failed' );
916+
874917
// If this request is served in a popup window (i.e. from the form builder),
875918
// serve a view that will close the popup.
876919
if ( array_key_exists( 'convertkit-modal', $_REQUEST ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
@@ -974,6 +1017,11 @@ public function api_fields( $connection_id = '', $account_id = '', $list_id = ''
9741017
$resource_custom_fields = new Integrate_ConvertKit_WPForms_Resource_Custom_Fields( $api, $account_id );
9751018
$custom_fields = $resource_custom_fields->refresh();
9761019

1020+
// Just return fields if an error occurred.
1021+
if ( is_wp_error( $custom_fields ) ) {
1022+
return $provider_fields;
1023+
}
1024+
9771025
// Just return fields if no custom fields exist in ConvertKit.
9781026
if ( ! count( $custom_fields ) ) {
9791027
return $provider_fields;

0 commit comments

Comments
 (0)