Skip to content

Commit 9cad7b3

Browse files
authored
Merge pull request #287 from wpengine/feat-webhooks-ui
fix: inability to delete webhooks via UI
2 parents afc0ccb + 9069946 commit 9cad7b3

File tree

4 files changed

+88
-83
lines changed

4 files changed

+88
-83
lines changed

plugins/wp-graphql-headless-webhooks/assets/js/admin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
'click',
1212
function () {
1313
var headerRow = $( wpGraphQLWebhooks.headerTemplate || wpGraphQLWebhooks.headerRowTemplate );
14-
$( '#webhook-headers-container, #webhook-headers' ).append( headerRow );
14+
$( '#webhook-headers-container' ).append( headerRow );
1515
}
1616
);
1717

plugins/wp-graphql-headless-webhooks/src/Admin/WebhooksAdmin.php

Lines changed: 51 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -41,38 +41,34 @@ class WebhooksAdmin {
4141
*/
4242
public function __construct( WebhookRepositoryInterface $repository ) {
4343
$this->repository = $repository;
44-
45-
add_action( 'admin_menu', [ $this, 'add_admin_menu' ] );
46-
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
47-
add_action( 'admin_post_graphql_webhook_save', [ $this, 'handle_webhook_save' ] );
48-
add_action( 'admin_post_graphql_webhook_delete', [ $this, 'handle_webhook_delete' ] );
49-
add_action( 'admin_init', [ $this, 'handle_admin_actions' ] );
50-
add_action( 'wp_ajax_test_webhook', [ $this, 'ajax_test_webhook' ] );
5144
}
5245

5346
/**
54-
* Optionally initialize additional admin hooks.
47+
* Initialize the admin functionality.
5548
*
5649
* @return void
5750
*/
5851
public function init(): void {
52+
add_action( 'admin_menu', [ $this, 'add_admin_menu' ] );
53+
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_assets' ] );
5954
add_action( 'admin_init', [ $this, 'handle_actions' ] );
55+
add_action( 'wp_ajax_test_webhook', [ $this, 'ajax_test_webhook' ] );
6056
}
6157

6258
/**
63-
* Registers the top-level "Webhooks" admin menu.
59+
* Registers the webhooks submenu under the GraphQL admin menu.
6460
*
6561
* @return void
6662
*/
6763
public function add_admin_menu(): void {
68-
add_menu_page(
69-
__( 'Webhooks', 'wp-graphql-headless-webhooks' ),
64+
// Add submenu under GraphQL menu using the correct parent slug
65+
add_submenu_page(
66+
'graphiql-ide',
67+
__( 'GraphQL Webhooks', 'wp-graphql-headless-webhooks' ),
7068
__( 'Webhooks', 'wp-graphql-headless-webhooks' ),
7169
'manage_options',
7270
self::ADMIN_PAGE_SLUG,
73-
[ $this, 'render_admin_page' ],
74-
'dashicons-rss',
75-
25
71+
[ $this, 'render_admin_page' ]
7672
);
7773
}
7874

@@ -97,6 +93,11 @@ public function get_admin_url( array $args = [] ): string {
9793
* @return void
9894
*/
9995
public function enqueue_assets( string $hook ): void {
96+
// Only enqueue on our admin page
97+
if ( false === strpos( $hook, self::ADMIN_PAGE_SLUG ) ) {
98+
return;
99+
}
100+
100101
wp_enqueue_style(
101102
'graphql-webhooks-admin',
102103
WPGRAPHQL_HEADLESS_WEBHOOKS_PLUGIN_URL . 'assets/css/admin.css',
@@ -136,24 +137,6 @@ private function get_header_row_template(): string {
136137
return ob_get_clean();
137138
}
138139

139-
/**
140-
* Handles admin actions from the webhooks page.
141-
*
142-
* @return void
143-
*/
144-
public function handle_actions(): void {
145-
if ( ! isset( $_GET['page'] ) || self::ADMIN_PAGE_SLUG !== $_GET['page'] ) {
146-
return;
147-
}
148-
149-
if ( isset( $_POST['action'] ) && 'save_webhook' === $_POST['action'] ) {
150-
$this->handle_webhook_save();
151-
}
152-
153-
if ( isset( $_GET['action'] ) && 'delete' === $_GET['action'] && isset( $_GET['webhook_id'] ) ) {
154-
$this->handle_webhook_delete();
155-
}
156-
}
157140

158141
/**
159142
* Checks if the current user has permission to manage options.
@@ -227,44 +210,55 @@ public function handle_webhook_save() {
227210
}
228211

229212
/**
230-
* Handles deleting a webhook.
213+
* Handles admin actions from the webhooks page.
231214
*
232215
* @return void
233216
*/
234-
public function handle_webhook_delete() {
235-
// To be implemented: Individual deletes are handled through the list table's handle_row_actions.
217+
public function handle_actions(): void {
218+
if ( ! isset( $_REQUEST['page'] ) || self::ADMIN_PAGE_SLUG !== $_REQUEST['page'] ) {
219+
return;
220+
}
221+
222+
// Handle save action
223+
if ( isset( $_POST['action'] ) && 'save_webhook' === $_POST['action'] ) {
224+
$this->handle_webhook_save();
225+
}
226+
227+
// Handle delete action
228+
if ( isset( $_GET['action'] ) && 'delete' === $_GET['action'] && isset( $_GET['webhook'] ) ) {
229+
$this->handle_webhook_delete();
230+
}
236231
}
237232

238233
/**
239-
* Handles bulk admin actions (such as bulk delete).
234+
* Handles single webhook deletion.
240235
*
241236
* @return void
242237
*/
243-
public function handle_admin_actions() {
244-
if (
245-
( isset( $_REQUEST['action'] ) && 'delete' === $_REQUEST['action'] ) ||
246-
( isset( $_REQUEST['action2'] ) && 'delete' === $_REQUEST['action2'] )
247-
) {
248-
if ( ! $this->verify_admin_permission() || ! $this->verify_nonce( 'bulk-webhooks', '_wpnonce' ) ) {
249-
return;
250-
}
251-
252-
$webhook_ids = isset( $_REQUEST['webhook'] ) ? array_map( 'intval', (array) $_REQUEST['webhook'] ) : [];
253-
$deleted = 0;
254-
255-
foreach ( $webhook_ids as $webhook_id ) {
256-
if ( $this->repository->delete( $webhook_id ) ) {
257-
$deleted++;
258-
}
259-
}
238+
private function handle_webhook_delete(): void {
239+
// Verify permissions
240+
if ( ! $this->verify_admin_permission() ) {
241+
return;
242+
}
260243

261-
if ( $deleted > 0 ) {
262-
wp_redirect( add_query_arg( [ 'deleted' => $deleted ], $this->get_admin_url() ) );
263-
exit;
264-
}
244+
// Get webhook ID
245+
$webhook_id = intval( $_GET['webhook'] );
246+
$nonce = isset( $_GET['_wpnonce'] ) ? $_GET['_wpnonce'] : '';
247+
248+
// Verify nonce
249+
if ( ! wp_verify_nonce( $nonce, 'delete-webhook-' . $webhook_id ) ) {
250+
wp_die( __( 'Security check failed.', 'wp-graphql-headless-webhooks' ) );
265251
}
252+
253+
// Delete webhook
254+
$deleted = $this->repository->delete( $webhook_id ) ? 1 : 0;
255+
256+
// Redirect with result
257+
wp_redirect( add_query_arg( [ 'deleted' => $deleted ], remove_query_arg( [ 'action', 'webhook', '_wpnonce' ], $this->get_admin_url() ) ) );
258+
exit;
266259
}
267260

261+
268262
/**
269263
* Renders the webhooks admin page.
270264
*

plugins/wp-graphql-headless-webhooks/src/Admin/WebhooksListTable.php

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -77,45 +77,56 @@ public function get_sortable_columns() {
7777
*/
7878
public function get_bulk_actions() {
7979
return [
80-
'bulk-delete' => __( 'Delete', 'wp-graphql-webhooks' ),
80+
'delete' => __( 'Delete', 'wp-graphql-webhooks' ),
8181
];
8282
}
8383

84+
8485
/**
8586
* Process bulk actions
8687
*/
8788
public function process_bulk_action() {
88-
// Handle bulk delete
89-
if ( 'bulk-delete' === $this->current_action() ) {
90-
$webhook_ids = isset( $_POST['webhook'] ) ? array_map( 'intval', $_POST['webhook'] ) : [];
91-
92-
if ( ! empty( $webhook_ids ) && isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'bulk-' . $this->_args['plural'] ) ) {
93-
foreach ( $webhook_ids as $id ) {
94-
$this->repository->delete( $id );
95-
}
96-
97-
wp_redirect( add_query_arg( 'deleted', count( $webhook_ids ), remove_query_arg( [ 'action', 'webhook', '_wpnonce' ] ) ) );
98-
exit;
99-
}
89+
// Only handle delete action
90+
if ( 'delete' !== $this->current_action() ) {
91+
return;
10092
}
101-
102-
// Handle single delete
103-
if ( 'delete' === $this->current_action() ) {
104-
$webhook_id = isset( $_GET['webhook'] ) ? intval( $_GET['webhook'] ) : 0;
105-
$nonce = isset( $_GET['_wpnonce'] ) ? $_GET['_wpnonce'] : '';
106-
107-
if ( $webhook_id && wp_verify_nonce( $nonce, 'delete-webhook-' . $webhook_id ) ) {
108-
$this->repository->delete( $webhook_id );
109-
wp_redirect( add_query_arg( 'deleted', 1, remove_query_arg( [ 'action', 'webhook', '_wpnonce' ] ) ) );
110-
exit;
93+
94+
// Verify nonce
95+
if ( ! isset( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'bulk-' . $this->_args['plural'] ) ) {
96+
wp_die( __( 'Security check failed.', 'wp-graphql-webhooks' ) );
97+
}
98+
99+
// Check permissions
100+
if ( ! current_user_can( 'manage_options' ) ) {
101+
wp_die( __( 'You do not have sufficient permissions to access this page.', 'wp-graphql-headless-webhooks' ) );
102+
}
103+
104+
// Get selected webhooks
105+
$webhook_ids = isset( $_REQUEST['webhook'] ) ? array_map( 'intval', (array) $_REQUEST['webhook'] ) : [];
106+
if ( empty( $webhook_ids ) ) {
107+
return;
108+
}
109+
110+
// Delete webhooks
111+
$deleted = 0;
112+
foreach ( $webhook_ids as $webhook_id ) {
113+
if ( $this->repository->delete( $webhook_id ) ) {
114+
$deleted++;
111115
}
112116
}
117+
118+
// Redirect with success message
119+
if ( $deleted > 0 ) {
120+
wp_redirect( add_query_arg( [ 'deleted' => $deleted ], remove_query_arg( [ 'action', 'action2', 'webhook', '_wpnonce' ] ) ) );
121+
exit;
122+
}
113123
}
114124

115125
/**
116126
* Prepare items for display
117127
*/
118128
public function prepare_items() {
129+
// Process bulk actions first
119130
$this->process_bulk_action();
120131

121132
$per_page = $this->get_items_per_page( 'webhooks_per_page', 20 );

plugins/wp-graphql-headless-webhooks/src/Admin/views/webhook-form.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@
2828
<div class="wrap">
2929
<h1><?php echo esc_html( $form_title ); ?></h1>
3030

31-
<form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>">
31+
<form method="post">
3232
<?php wp_nonce_field( 'webhook_save', 'webhook_nonce' ); ?>
33-
<input type="hidden" name="action" value="graphql_webhook_save">
33+
<input type="hidden" name="action" value="save_webhook">
3434
<?php if ( $webhook ) : ?>
3535
<input type="hidden" name="webhook_id" value="<?php echo esc_attr( $webhook->id ); ?>">
3636
<?php endif; ?>

0 commit comments

Comments
 (0)