diff --git a/css/cleantalk-admin.min.css b/css/cleantalk-admin.min.css
index daf049497..c2c7a6d27 100644
--- a/css/cleantalk-admin.min.css
+++ b/css/cleantalk-admin.min.css
@@ -1 +1 @@
-:disabled{cursor:not-allowed!important}.apbct_color--gray{color:gray}.apbct_display--none{display:none}.apbct_bottom_links--left{margin-right:2pc}.apbct_bottom_links--other{margin-right:2pc;margin-left:2pc}.ct_translate_links{color:#969614}.ct_support_links{color:#961414}.ct_faq_links{color:#149614}.ct_setting_links{color:#141496}.ct_translate_links:hover{color:#d2d214!important}.ct_support_links:hover{color:#fa1414!important}.ct_faq_links:hover{color:#14fa14!important}.ct_setting_links:hover{color:#1414fa!important}.ct_link_new_tab img{float:none!important;margin:0 2px;border:0}#negative_reports_table tr td{padding:7px 5px!important}#wp-admin-bar-cleantalk_admin_bar__parent_node{margin-right:5px}#wp-admin-bar-cleantalk_admin_bar__parent_node span{display:inline-block}#wp-admin-bar-cleantalk_admin_bar__parent_node .apbct-icon-attention-alt{background:#d63638;color:#fff;border-radius:50%;font-size:12px}#wp-admin-bar-cleantalk_admin_bar__parent_node img.cleantalk_admin_bar__spbc_icon{width:14px;height:17px;margin-top:7px}#wp-admin-bar-cleantalk_admin_bar__parent_node img.cleantalk_admin_bar__apbct_icon{width:18px;height:18px;margin-top:7px}#wp-admin-bar-cleantalk_admin_bar__parent_node div.cleantalk_admin_bar__sum_counter{color:#999;display:inline;padding:2px 5px!important}.cleantalk_admin_bar__blocked div{cursor:not-allowed!important}.cleantalk_admin_bar__blocked div a{color:#777!important}.cleantalk_admin_bar__title{vertical-align:top}.cleantalk_admin_bar__separator{height:0!important}.cleantalk-admin_bar--list_wrapper .ab-sub-wrapper ul:last-child{margin-bottom:5px!important}.apbct-plugin-errors{margin-left:0;margin-bottom:20px}#cleantalk_notice_review .caption{margin:0 0 15px;color:gray}#cleantalk_notice_review .button{margin-bottom:20px}.apbct-details-spam-order-button,.apbct-restore-spam-order-button{cursor:pointer}.apbct-details-spam-order-button{color:gray}.ct-modal-buttons{display:flex;align-items:center;padding:20px 0;justify-content:space-between}.ct-modal-message{font-weight:700;font-size:16px;line-height:2rem}.apbct-popup-fade:before{content:'';background:#000;position:fixed;left:0;top:0;width:100%;height:100%;opacity:.7;z-index:9999}.apbct-popup{position:fixed;top:20%;left:50%;padding:20px;width:360px;margin-left:-200px;background:#fff;border:1px solid;border-radius:4px;z-index:99999;opacity:1}.apbct-table-actions-wrapper{background:#fcfcfc;background:#fcfcfc;border-radius:2px;padding:5px!important;border:1px solid #d3d3d3;margin:0 5px 5px 0!important}.apbct_wc_details__table-container_header{text-align:center}.apbct_wc_details__wrapper{display:flex;flex-direction:column;scroll-behavior:smooth;overflow-y:auto;max-height:400px;min-width:50pc;position:relative}.apbct_wc_details__table-wrapper-inner{margin-bottom:25px;background:#f9f9f9;border-radius:8px;box-shadow:0 2px 4px rgba(0,0,0,.08);position:relative;padding:0 15px}.apbct_wc_details__table-wrapper-inner h4{margin:0 0 12px 0;color:#333;font-size:16px;font-weight:600;padding:10px 0 8px 0;border-bottom:2px solid #e0e0e0;position:sticky;top:0;background:#f9f9f9;z-index:10}.apbct_wc_details__table{width:100%;border-collapse:collapse;background:#fff;border-radius:6px;overflow:hidden}.apbct_wc_details__table-key-cell{border:1px solid #e8e8e8;padding:10px 12px;font-weight:600;background:#f5f7fa;color:#2c3e50;width:35%;font-size:13px}.apbct_wc_details__table-value-cell{border:1px solid #e8e8e8;padding:10px 12px;color:#34495e;font-size:13px;word-break:break-word}.apbct_wc_details__table tbody tr:hover{background:#fafbfc}.apbct_wc_details__table tbody tr:last-child td{border-bottom:none}.apbct_wc_details__table-value-cell--json{font-family:monospace;font-size:12px;white-space:pre-wrap}@supports (position:sticky){.apbct_wc_details__table th,.apbct_wc_details__table-wrapper-inner h4{position:sticky}}@media screen and (max-width:1120px){.apbct-tablenav{display:flex;flex-direction:column;flex-wrap:nowrap;height:100%;max-width:50%}}.apbct-notice.apbct-banner-success{padding:16px 32px!important;background-color:#fff!important;box-shadow:0 4px 15px #84848440!important;border-left-color:#00bb5d!important}.apbct-banner-content{display:flex;flex-wrap:wrap;justify-content:space-between;row-gap:16px}.apbct-banner-content-wrapper{display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between}.apbct-banner-text-wrapper{margin-top:16px;margin-left:32px}.apbct-banner-button{display:block;padding:17px 53px;color:#fff!important;border-radius:8px;font-size:16px;font-weight:500;text-decoration:none!important;text-align:center;align-content:center}.apbct-banner-button-wrapper{display:grid;gap:12px;align-content:center;text-align:center;margin-right:24px;margin-left:32px}.apbct-banner-title{font-size:24px;line-height:29px;font-weight:500;color:#000}.apbct-banner-subtitle{font-size:14px;line-height:22px;font-weight:400;color:#444;margin-top:8px}.apbct-banner-big-subtitle{font-size:16px}.apbct-banner-link{font-size:16px;line-height:24px;font-weight:400;color:#026e88!important}.apbct-banner-dismiss-link{font-weight:400;font-size:14px;line-height:22px;color:#646464!important}.apbct-notice.apbct-banner-error{padding:16px 32px!important;background-color:#fff!important;box-shadow:0 4px 15px #84848440!important;border-left-color:#bb3323!important}.apbct-notice.apbct-banner-error .notice-dismiss::before{color:#646464}.apbct-banner-button-green{background-color:#00bb5d!important}.apbct-banner-button-green:hover{background-color:#00a34f!important}.apbct-banner-button-red{background-color:#bb3323!important}.apbct-banner-button-red:hover{background-color:#a82e20!important}
\ No newline at end of file
+:disabled{cursor:not-allowed!important}.apbct_color--gray{color:gray}.apbct_display--none{display:none}.apbct_bottom_links--left{margin-right:2pc}.apbct_bottom_links--other{margin-right:2pc;margin-left:2pc}.ct_translate_links{color:#969614}.ct_support_links{color:#961414}.ct_faq_links{color:#149614}.ct_setting_links{color:#141496}.ct_translate_links:hover{color:#d2d214!important}.ct_support_links:hover{color:#fa1414!important}.ct_faq_links:hover{color:#14fa14!important}.ct_setting_links:hover{color:#1414fa!important}.ct_link_new_tab img{float:none!important;margin:0 2px;border:0}#negative_reports_table tr td{padding:7px 5px!important}#wp-admin-bar-cleantalk_admin_bar__parent_node{margin-right:5px}#wp-admin-bar-cleantalk_admin_bar__parent_node span{display:inline-block}#wp-admin-bar-cleantalk_admin_bar__parent_node .apbct-icon-attention-alt{background:#d63638;color:#fff;border-radius:50%;font-size:12px}#wp-admin-bar-cleantalk_admin_bar__parent_node img.cleantalk_admin_bar__spbc_icon{width:14px;height:17px;margin-top:7px}#wp-admin-bar-cleantalk_admin_bar__parent_node img.cleantalk_admin_bar__apbct_icon{width:18px;height:18px;margin-top:7px}#wp-admin-bar-cleantalk_admin_bar__parent_node div.cleantalk_admin_bar__sum_counter{color:#999;display:inline;padding:2px 5px!important}.cleantalk_admin_bar__blocked div{cursor:not-allowed!important}.cleantalk_admin_bar__blocked div a{color:#777!important}.cleantalk_admin_bar__title{vertical-align:top}.cleantalk_admin_bar__separator{height:0!important}.cleantalk-admin_bar--list_wrapper .ab-sub-wrapper ul:last-child{margin-bottom:5px!important}.apbct-plugin-errors{margin-left:0;margin-bottom:20px}#cleantalk_notice_review .caption{margin:0 0 15px;color:gray}#cleantalk_notice_review .button{margin-bottom:20px}.apbct-details-spam-order-button,.apbct-restore-spam-order-button{cursor:pointer}.apbct-details-spam-order-button{color:gray}.wp-list-table.wc_spam_orders .column-ct_order_date,.wp-list-table.wc_spam_orders .column-ct_status,.wp-list-table.wc_spam_orders .column-ct_total{width:15%}.wp-list-table.wc_spam_orders td{vertical-align:middle}.wp-list-table.wc_spam_orders .apbct-order-view{color:#2271b1;text-decoration:none}.wp-list-table.wc_spam_orders .apbct-order-view:hover{color:#135e96}.apbct-order-status{display:inline-flex;line-height:2.5em;color:#454545;background:#e5e5e5;border-radius:4px;border-bottom:1px solid rgba(0,0,0,.05);margin:-.25em 0;cursor:inherit!important;white-space:nowrap;max-width:100%}.apbct-order-status>span{margin:0 1em;overflow:hidden;text-overflow:ellipsis}.apbct-order-status--spam{background:#eba3a3;color:#570000}.ct-modal-buttons{display:flex;align-items:center;padding:20px 0;justify-content:space-between}.ct-modal-message{font-weight:700;font-size:16px;line-height:2rem}.apbct-popup-fade:before{content:'';background:#000;position:fixed;left:0;top:0;width:100%;height:100%;opacity:.7;z-index:9999}.apbct-popup{position:fixed;top:20%;left:50%;padding:20px;width:360px;margin-left:-200px;background:#fff;border:1px solid;border-radius:4px;z-index:99999;opacity:1}.apbct-table-actions-wrapper{background:#fcfcfc;background:#fcfcfc;border-radius:2px;padding:5px!important;border:1px solid #d3d3d3;margin:0 5px 5px 0!important}.apbct_wc_details__table-container_header{text-align:center}.apbct_wc_details__wrapper{display:flex;flex-direction:column;scroll-behavior:smooth;overflow-y:auto;max-height:400px;min-width:50pc;position:relative}.apbct_wc_details__table-wrapper-inner{margin-bottom:25px;background:#f9f9f9;border-radius:8px;box-shadow:0 2px 4px rgba(0,0,0,.08);position:relative;padding:0 15px}.apbct_wc_details__table-wrapper-inner h4{margin:0 0 12px 0;color:#333;font-size:16px;font-weight:600;padding:10px 0 8px 0;border-bottom:2px solid #e0e0e0;position:sticky;top:0;background:#f9f9f9;z-index:10}.apbct_wc_details__table{width:100%;border-collapse:collapse;background:#fff;border-radius:6px;overflow:hidden}.apbct_wc_details__table-key-cell{border:1px solid #e8e8e8;padding:10px 12px;font-weight:600;background:#f5f7fa;color:#2c3e50;width:35%;font-size:13px}.apbct_wc_details__table-value-cell{border:1px solid #e8e8e8;padding:10px 12px;color:#34495e;font-size:13px;word-break:break-word}.apbct_wc_details__table tbody tr:hover{background:#fafbfc}.apbct_wc_details__table tbody tr:last-child td{border-bottom:none}.apbct_wc_details__table-value-cell--json{font-family:monospace;font-size:12px;white-space:pre-wrap}@supports (position:sticky){.apbct_wc_details__table th,.apbct_wc_details__table-wrapper-inner h4{position:sticky}}@media screen and (max-width:1120px){.apbct-tablenav{display:flex;flex-direction:column;flex-wrap:nowrap;height:100%;max-width:50%}}.apbct-notice.apbct-banner-success{padding:16px 32px!important;background-color:#fff!important;box-shadow:0 4px 15px #84848440!important;border-left-color:#00bb5d!important}.apbct-banner-content{display:flex;flex-wrap:wrap;justify-content:space-between;row-gap:16px}.apbct-banner-content-wrapper{display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between}.apbct-banner-text-wrapper{margin-top:16px;margin-left:32px}.apbct-banner-button{display:block;padding:17px 53px;color:#fff!important;border-radius:8px;font-size:16px;font-weight:500;text-decoration:none!important;text-align:center;align-content:center}.apbct-banner-button-wrapper{display:grid;gap:12px;align-content:center;text-align:center;margin-right:24px;margin-left:32px}.apbct-banner-title{font-size:24px;line-height:29px;font-weight:500;color:#000}.apbct-banner-subtitle{font-size:14px;line-height:22px;font-weight:400;color:#444;margin-top:8px}.apbct-banner-big-subtitle{font-size:16px}.apbct-banner-link{font-size:16px;line-height:24px;font-weight:400;color:#026e88!important}.apbct-banner-dismiss-link{font-weight:400;font-size:14px;line-height:22px;color:#646464!important}.apbct-notice.apbct-banner-error{padding:16px 32px!important;background-color:#fff!important;box-shadow:0 4px 15px #84848440!important;border-left-color:#bb3323!important}.apbct-notice.apbct-banner-error .notice-dismiss::before{color:#646464}.apbct-banner-button-green{background-color:#00bb5d!important}.apbct-banner-button-green:hover{background-color:#00a34f!important}.apbct-banner-button-red{background-color:#bb3323!important}.apbct-banner-button-red:hover{background-color:#a82e20!important}
\ No newline at end of file
diff --git a/css/src/cleantalk-admin.css b/css/src/cleantalk-admin.css
index 815b371aa..09ff7c189 100644
--- a/css/src/cleantalk-admin.css
+++ b/css/src/cleantalk-admin.css
@@ -105,6 +105,51 @@
.apbct-details-spam-order-button {
color: gray;
}
+
+/* WooCommerce orders list alike view of the spam orders table.
+ The order column has no width, so it takes all the space left. */
+.wp-list-table.wc_spam_orders .column-ct_order_date,
+.wp-list-table.wc_spam_orders .column-ct_status,
+.wp-list-table.wc_spam_orders .column-ct_total {
+ width: 15%;
+}
+
+.wp-list-table.wc_spam_orders td {
+ vertical-align: middle;
+}
+
+.wp-list-table.wc_spam_orders .apbct-order-view {
+ color: #2271b1;
+ text-decoration: none;
+}
+
+.wp-list-table.wc_spam_orders .apbct-order-view:hover {
+ color: #135e96;
+}
+
+.apbct-order-status {
+ display: inline-flex;
+ line-height: 2.5em;
+ color: #454545;
+ background: #e5e5e5;
+ border-radius: 4px;
+ border-bottom: 1px solid rgba(0, 0, 0, .05);
+ margin: -.25em 0;
+ cursor: inherit !important;
+ white-space: nowrap;
+ max-width: 100%;
+}
+
+.apbct-order-status > span {
+ margin: 0 1em;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.apbct-order-status--spam {
+ background: #eba3a3;
+ color: #570000;
+}
.ct-modal-buttons {
display: flex;
align-items: center;
diff --git a/inc/cleantalk-admin.php b/inc/cleantalk-admin.php
index f52e3e67d..c245d2b31 100644
--- a/inc/cleantalk-admin.php
+++ b/inc/cleantalk-admin.php
@@ -1198,7 +1198,7 @@ function apbct_admin__admin_bar__add_child_nodes($wp_admin_bar)
array(
'parent' => 'apbct__parent_node',
'id' => 'ct_settings_bulk_orders',
- 'title' => ''
+ 'title' => ''
. __('WooCommerce spam orders', 'cleantalk-spam-protect') . '',
)
);
diff --git a/inc/cleantalk-ajax.php b/inc/cleantalk-ajax.php
index 48d12a792..e8b9be86a 100644
--- a/inc/cleantalk-ajax.php
+++ b/inc/cleantalk-ajax.php
@@ -894,7 +894,6 @@ function ct_ajax_hook($message_obj = null)
'message' => $ct_result->comment
)
);
- die();
}
// Plugin Name: eForm - WordPress Form Builder; ajax action ipt_fsqm_save_form
diff --git a/inc/cleantalk-public-integrations.php b/inc/cleantalk-public-integrations.php
index 5fd280d9c..b22c6df36 100644
--- a/inc/cleantalk-public-integrations.php
+++ b/inc/cleantalk-public-integrations.php
@@ -2448,9 +2448,6 @@ function apbct_form__the7_contact_form()
);
wp_send_json($response);
-
- // IMPORTANT: don't forget to "exit" @todo AG: Why? Exit does not terminate connection, but I can't see how it is applicable
- exit;
}
}
}
diff --git a/inc/cleantalk-settings.php b/inc/cleantalk-settings.php
index cea88f860..4dc05f059 100644
--- a/inc/cleantalk-settings.php
+++ b/inc/cleantalk-settings.php
@@ -2179,7 +2179,7 @@ function apbct_settings__field__action_buttons()
if ( apbct_is_plugin_active('woocommerce/woocommerce.php') ) {
add_filter('apbct_settings_action_buttons', function ($buttons_array) {
$buttons_array[] =
- ''
+ ''
. __('WooCommerce spam orders', 'cleantalk-spam-protect')
. '';
return $buttons_array;
diff --git a/lib/Cleantalk/Antispam/Integrations/ChatyContactForm.php b/lib/Cleantalk/Antispam/Integrations/ChatyContactForm.php
index 7836148e3..12599885b 100644
--- a/lib/Cleantalk/Antispam/Integrations/ChatyContactForm.php
+++ b/lib/Cleantalk/Antispam/Integrations/ChatyContactForm.php
@@ -41,6 +41,5 @@ public function doBlock($message)
'message' => $message,
];
wp_send_json($response);
- exit;
}
}
diff --git a/lib/Cleantalk/Antispam/Integrations/ElementorUltimateAddonsRegister.php b/lib/Cleantalk/Antispam/Integrations/ElementorUltimateAddonsRegister.php
index feaa89b16..f5a839cc0 100644
--- a/lib/Cleantalk/Antispam/Integrations/ElementorUltimateAddonsRegister.php
+++ b/lib/Cleantalk/Antispam/Integrations/ElementorUltimateAddonsRegister.php
@@ -24,7 +24,6 @@ public function doBlock($message)
)
)
);
- return false;
}
public function allow()
diff --git a/lib/Cleantalk/Antispam/Integrations/HivePress.php b/lib/Cleantalk/Antispam/Integrations/HivePress.php
index 50a1eda3c..c5f227594 100644
--- a/lib/Cleantalk/Antispam/Integrations/HivePress.php
+++ b/lib/Cleantalk/Antispam/Integrations/HivePress.php
@@ -39,6 +39,5 @@ public function doBlock($message)
'error' => ['message' => $message],
]
);
- die();
}
}
diff --git a/lib/Cleantalk/Antispam/Integrations/HivePressRegistration.php b/lib/Cleantalk/Antispam/Integrations/HivePressRegistration.php
index fc5044faa..0279910a2 100644
--- a/lib/Cleantalk/Antispam/Integrations/HivePressRegistration.php
+++ b/lib/Cleantalk/Antispam/Integrations/HivePressRegistration.php
@@ -35,6 +35,5 @@ public function doBlock($message)
'error' => ['message' => $message],
]
);
- die();
}
}
diff --git a/lib/Cleantalk/Antispam/Integrations/MailPoet.php b/lib/Cleantalk/Antispam/Integrations/MailPoet.php
index 5343bf60b..df1bb14e0 100644
--- a/lib/Cleantalk/Antispam/Integrations/MailPoet.php
+++ b/lib/Cleantalk/Antispam/Integrations/MailPoet.php
@@ -58,6 +58,5 @@ public function doBlock($message)
),
403
);
- die();
}
}
diff --git a/lib/Cleantalk/Antispam/Integrations/SureForms.php b/lib/Cleantalk/Antispam/Integrations/SureForms.php
index 8e9a83d5f..eeb997515 100644
--- a/lib/Cleantalk/Antispam/Integrations/SureForms.php
+++ b/lib/Cleantalk/Antispam/Integrations/SureForms.php
@@ -29,6 +29,5 @@ public function doBlock($message)
'message' => $message
)
);
- die();
}
}
diff --git a/lib/Cleantalk/Antispam/Integrations/UserRegistrationPro.php b/lib/Cleantalk/Antispam/Integrations/UserRegistrationPro.php
index d6011c211..d4719fecd 100644
--- a/lib/Cleantalk/Antispam/Integrations/UserRegistrationPro.php
+++ b/lib/Cleantalk/Antispam/Integrations/UserRegistrationPro.php
@@ -38,8 +38,6 @@ public function getDataForChecking($argument)
/**
* @param $message
- *
- * @psalm-suppress UnusedVariable
*/
public function doBlock($message)
{
@@ -48,6 +46,5 @@ public function doBlock($message)
'message' => $message
)
);
- die();
}
}
diff --git a/lib/Cleantalk/Antispam/IntegrationsByClass/Woocommerce.php b/lib/Cleantalk/Antispam/IntegrationsByClass/Woocommerce.php
index bafa1e415..7dcb87ee9 100644
--- a/lib/Cleantalk/Antispam/IntegrationsByClass/Woocommerce.php
+++ b/lib/Cleantalk/Antispam/IntegrationsByClass/Woocommerce.php
@@ -94,27 +94,7 @@ public function doAjaxWork()
public function doAdminWork()
{
- add_action('admin_menu', function () {
- add_submenu_page(
- 'woocommerce',
- __("WooCommerce spam orders", 'cleantalk-spam-protect'),
- __("WooCommerce spam orders", 'cleantalk-spam-protect'),
- 'activate_plugins',
- 'apbct_wc_spam_orders',
- function () {
- ?>
-
-
-
- settings['data__wc_store_blocked_orders'] ) {
+ // The checkout is left to WooCommerce as is: no check, no blocked order to store.
+ return;
+ }
+
if ( count($errors->errors) ) {
return;
}
@@ -230,14 +215,10 @@ public function checkoutCheck($_data, $errors)
ct_hash($ct_result->id);
if ( $ct_result->allow == 0 ) {
- if ( $apbct->settings['data__wc_store_blocked_orders'] ) {
- $this->storeBlockedOrder();
- }
+ $this->handleBlockedOrder();
wp_send_json(array(
- 'result' => 'failure',
- 'messages' => "
" . $ct_result->comment . "
",
- 'refresh' => 'false',
- 'reload' => 'false'
+ 'result' => 'success',
+ 'redirect' => $this->getBlockedOrderRedirectUrl(),
));
}
}
@@ -252,6 +233,11 @@ public function checkoutCheckFromRest($order)
{
global $apbct, $cleantalk_executed;
+ if ( ! $apbct->settings['data__wc_store_blocked_orders'] ) {
+ // The checkout is left to WooCommerce as is: no check, no blocked order to store.
+ return;
+ }
+
if ( is_null($order) || ! ($order instanceof \WC_Order) ) {
return;
}
@@ -286,11 +272,15 @@ public function checkoutCheckFromRest($order)
ct_hash($ct_result->id);
if ( $ct_result->allow == 0 ) {
- if ( $apbct->settings['data__wc_store_blocked_orders'] ) {
- $this->storeBlockedOrder();
- }
+ $response = $this->getStoreApiPassedResponse($order);
+
+ $this->handleBlockedOrder();
if ( $order->get_status() === 'pending' || $order->get_status() === 'checkout-draft' ) {
+ if ( function_exists('wc_release_stock_for_order') ) {
+ wc_release_stock_for_order($order);
+ }
+
try {
$order->delete(true);
} catch (\Exception $e) {
@@ -298,22 +288,67 @@ public function checkoutCheckFromRest($order)
}
}
- $response = [
- 'code' => 'woocommerce_store_api_checkout_order_processed',
- 'message' => $ct_result->comment,
- 'data' => [
- 'status' => 403
- ]
- ];
-
if ( ! headers_sent() ) {
- http_response_code(403);
+ header('Content-Type: application/json; charset=utf-8');
}
die(json_encode($response));
}
}
}
+ /**
+ * Common actions for an order blocked as spam.
+ *
+ * @return void
+ * @psalm-suppress UndefinedFunction
+ */
+ private function handleBlockedOrder()
+ {
+ $this->storeBlockedOrder();
+
+ if ( function_exists('wc') && ! is_null(wc()->cart) ) {
+ wc()->cart->empty_cart();
+ }
+ }
+
+ /**
+ * URL of the page shown to the visitor whose order was blocked.
+ *
+ * @return string
+ * @psalm-suppress UndefinedFunction
+ */
+ private function getBlockedOrderRedirectUrl()
+ {
+ return wc_get_endpoint_url('order-received', '', wc_get_checkout_url());
+ }
+
+ /**
+ * Response for the Store API checkout route imitating a passed checkout.
+ *
+ * @param \WC_Order $order
+ *
+ * @return array
+ * @psalm-suppress UndefinedClass, UndefinedFunction
+ */
+ private function getStoreApiPassedResponse($order)
+ {
+ return array(
+ 'order_id' => $order->get_id(),
+ 'status' => $order->get_status(),
+ 'order_key' => $order->get_order_key(),
+ 'customer_note' => $order->get_customer_note(),
+ 'customer_id' => $order->get_customer_id(),
+ 'billing_address' => $order->get_address('billing'),
+ 'shipping_address' => $order->get_address('shipping'),
+ 'payment_method' => $order->get_payment_method(),
+ 'payment_result' => array(
+ 'payment_status' => 'success',
+ 'payment_details' => array(),
+ 'redirect_url' => $this->getBlockedOrderRedirectUrl(),
+ ),
+ );
+ }
+
/**
* @return void
* @psalm-suppress UndefinedFunction
@@ -596,6 +631,209 @@ public function addOrdersSpamStatusHideFromList($query)
}
}
+ /**
+ * Enable the always visible status links on the HPOS orders list
+ *
+ * @param \WP_Screen $current_screen
+ *
+ * @return void
+ * @psalm-suppress PossiblyUnusedMethod
+ */
+ public function addOrdersListStatusViews($current_screen)
+ {
+ if ( ! isset($current_screen->id) || ! function_exists('wc_get_page_screen_id') ) {
+ return;
+ }
+
+ $orders_screen_id = wc_get_page_screen_id('shop_order');
+
+ // The legacy storage renders the orders on the posts list screen, the links there lead to another URL
+ if ( empty($orders_screen_id) || $orders_screen_id === 'shop_order' || $current_screen->id !== $orders_screen_id ) {
+ return;
+ }
+
+ add_filter('views_' . $orders_screen_id, [$this, 'addOrdersListStatusLinks']);
+
+ // The blocked orders are stored apart from the WooCommerce ones, so the 'Spam' view is rendered by the plugin
+ if ( Get::getString('status') === 'wc-spamorder' ) {
+ $this->replaceOrdersListRenderer($orders_screen_id);
+ }
+ }
+
+ /**
+ * Hand the orders page over to the spam orders table.
+ *
+ * The page content is printed by the WooCommerce page controller hooked to the page hook,
+ * so that callback is taken off and replaced. If it can not be found (the WooCommerce
+ * internals have changed), nothing is replaced to avoid rendering two tables at once.
+ *
+ * @param string $page_hook
+ *
+ * @return void
+ */
+ private function replaceOrdersListRenderer($page_hook)
+ {
+ global $wp_filter;
+
+ if ( ! isset($wp_filter[$page_hook]) || ! isset($wp_filter[$page_hook]->callbacks) ) {
+ return;
+ }
+
+ $removed = false;
+
+ foreach ( $wp_filter[$page_hook]->callbacks as $priority => $callbacks ) {
+ foreach ( $callbacks as $callback ) {
+ if ( ! isset($callback['function'][0]) || ! is_object($callback['function'][0]) ) {
+ continue;
+ }
+
+ if ( strpos(get_class($callback['function'][0]), 'Admin\\Orders\\PageController') === false ) {
+ continue;
+ }
+
+ $removed = remove_action($page_hook, $callback['function'], $priority) || $removed;
+ }
+ }
+
+ if ( $removed ) {
+ add_action($page_hook, [$this, 'renderSpamOrdersPage']);
+ }
+ }
+
+ /**
+ * The spam orders table shown in place of the WooCommerce orders list,
+ * keeping the page markup and the status links of the original page.
+ *
+ * @return void
+ * @psalm-suppress PossiblyUnusedMethod
+ */
+ public function renderSpamOrdersPage()
+ {
+ $list_table = new \Cleantalk\ApbctWP\WcSpamOrdersListTable($this->getOrdersListViews());
+ ?>
+
+
+
+ renderPageNotices(); ?>
+
+
+ $label ) {
+ $count = wc_orders_count($status, 'shop_order');
+
+ $status_object = get_post_status_object($status);
+ if ( $status_object && ! empty($status_object->show_in_admin_all_list) ) {
+ $all_count += $count;
+ }
+
+ if ( $count > 0 ) {
+ $views[$status] = $this->getOrdersListStatusLink($status, $label, false);
+ }
+ }
+
+ return array_merge(
+ array('all' => $this->getOrdersListStatusLink('', __('All', 'cleantalk-spam-protect'), false, $all_count)),
+ $views
+ );
+ }
+
+ /**
+ * The orders list shows the statuses having orders only, so the spam workflow statuses
+ * are unreachable until an order gets marked as spam. Both of them are added back:
+ * 'Spam' itself and 'On hold' the unmarked orders are moved to.
+ *
+ * @param array $views
+ *
+ * @return array
+ * @psalm-suppress PossiblyUnusedMethod, PossiblyUnusedReturnValue
+ */
+ public function addOrdersListStatusLinks($views)
+ {
+ if ( ! is_array($views) || ! function_exists('wc_get_order_statuses') ) {
+ return $views;
+ }
+
+ $order_statuses = wc_get_order_statuses();
+ $current_status = Get::getString('status');
+
+ foreach ( array('wc-on-hold', 'wc-spamorder') as $status ) {
+ if ( isset($views[$status]) || ! isset($order_statuses[$status]) ) {
+ continue;
+ }
+
+ $views[$status] = $this->getOrdersListStatusLink(
+ $status,
+ $order_statuses[$status],
+ $current_status === $status
+ );
+ }
+
+ return $views;
+ }
+
+ /**
+ * @param string $status Empty for the 'All' link
+ * @param string $label
+ * @param bool $is_current
+ * @param int|null $count Known count, counted by the status when not given
+ *
+ * @return string
+ */
+ private function getOrdersListStatusLink($status, $label, $is_current, $count = null)
+ {
+ if ( is_null($count) ) {
+ $count = $this->getOrdersListStatusCount($status);
+ }
+
+ $url = admin_url('admin.php?page=wc-orders');
+ if ( ! empty($status) ) {
+ $url = add_query_arg('status', $status, $url);
+ }
+
+ return sprintf(
+ '%s (%s)',
+ esc_url($url),
+ $is_current ? ' class="current"' : '',
+ esc_html($label),
+ number_format_i18n($count)
+ );
+ }
+
+ /**
+ * @param string $status
+ *
+ * @return int
+ */
+ private function getOrdersListStatusCount($status)
+ {
+ // The blocked orders never become WooCommerce ones, they are counted in the plugin table
+ if ( $status === 'wc-spamorder' ) {
+ return WcSpamOrdersFunctions::getSpamOrdersCount();
+ }
+
+ return function_exists('wc_orders_count') ? wc_orders_count($status, 'shop_order') : 0;
+ }
+
/**
* Add bulk actions: 'Mark as spam' and 'Unmark as spam'
*/
diff --git a/lib/Cleantalk/ApbctWP/State.php b/lib/Cleantalk/ApbctWP/State.php
index e0d637726..9e8ed2041 100644
--- a/lib/Cleantalk/ApbctWP/State.php
+++ b/lib/Cleantalk/ApbctWP/State.php
@@ -94,7 +94,7 @@ class State extends \Cleantalk\Common\State
'data__email_decoder_obfuscation_custom_text' => '',
'data__email_decoder_encode_phone_numbers' => 0,
'data__email_decoder_encode_email_addresses' => 1,
- 'data__wc_store_blocked_orders' => 0,
+ 'data__wc_store_blocked_orders' => 1,
// Exclusions
// Send to the cloud some excepted requests
diff --git a/lib/Cleantalk/ApbctWP/Variables/AltSessions.php b/lib/Cleantalk/ApbctWP/Variables/AltSessions.php
index 82e41d3ea..3694d7e62 100644
--- a/lib/Cleantalk/ApbctWP/Variables/AltSessions.php
+++ b/lib/Cleantalk/ApbctWP/Variables/AltSessions.php
@@ -192,7 +192,6 @@ public static function setFromRemote($request = null)
'error' => 'AltSessions: No cookies data provided.'
)
);
- die(); // Need to prevent psalm further processing checking
}
if ( ! wp_verify_nonce($nonce, $action) ) {
@@ -211,7 +210,6 @@ public static function setFromRemote($request = null)
try {
$cookies_array = json_decode($cookies_to_set, true);
} catch ( \Exception $e ) {
- $cookies_array = array();
unset($e);
wp_send_json(array(
'success' => false,
@@ -234,7 +232,6 @@ public static function setFromRemote($request = null)
//other versions json errors if json_decode returns null
if ( is_null($cookies_array) ) {
- $cookies_array = array();
wp_send_json(array(
'success' => false,
'error' => 'AltSessions: Internal JSON error: $cookies_array is null.'));
diff --git a/lib/Cleantalk/ApbctWP/WcSpamOrdersFunctions.php b/lib/Cleantalk/ApbctWP/WcSpamOrdersFunctions.php
index 327f28559..b75eabc35 100644
--- a/lib/Cleantalk/ApbctWP/WcSpamOrdersFunctions.php
+++ b/lib/Cleantalk/ApbctWP/WcSpamOrdersFunctions.php
@@ -6,6 +6,22 @@
class WcSpamOrdersFunctions
{
+ /**
+ * Count of the stored blocked orders, used for the 'Spam' status counter of the orders list.
+ *
+ * @return int
+ */
+ public static function getSpamOrdersCount()
+ {
+ global $wpdb;
+
+ if ( ! defined('APBCT_TBL_WC_SPAM_ORDERS') ) {
+ return 0;
+ }
+
+ return (int)$wpdb->get_var('SELECT COUNT(*) FROM ' . APBCT_TBL_WC_SPAM_ORDERS . ';');
+ }
+
public static function restoreOrderAction()
{
AJAXService::checkNonceRestrictingNonAdmins();
diff --git a/lib/Cleantalk/ApbctWP/WcSpamOrdersListTable.php b/lib/Cleantalk/ApbctWP/WcSpamOrdersListTable.php
index b5afb11d8..e244050f6 100644
--- a/lib/Cleantalk/ApbctWP/WcSpamOrdersListTable.php
+++ b/lib/Cleantalk/ApbctWP/WcSpamOrdersListTable.php
@@ -11,16 +11,28 @@ class WcSpamOrdersListTable extends CleantalkListTable
protected $apbct;
protected $wc_active = false;
- protected $page_title = '';
protected $wc_spam_orders_count = 0;
- public function __construct()
+ /**
+ * Status links of the hosting page when the table is embedded into it.
+ * Null means the table is rendered on its own page and builds the links itself.
+ *
+ * @var array|null
+ */
+ protected $embedded_views = null;
+
+ /**
+ * @param array|null $embedded_views Status links of the hosting page, see $embedded_views
+ */
+ public function __construct($embedded_views = null)
{
parent::__construct(array(
'singular' => 'wc_spam_orders',
'plural' => 'wc_spam_orders'
));
+ $this->embedded_views = is_array($embedded_views) ? $embedded_views : null;
+
$this->bulk_actions_handler();
$this->row_actions_handler();
@@ -32,10 +44,7 @@ public function __construct()
$this->prepare_items();
global $apbct;
- $this->apbct = $apbct;
- $this->page_title = 'WooCommerce spam orders';
-
- $this->generatePageHeader();
+ $this->apbct = $apbct;
}
/**
@@ -62,8 +71,13 @@ public function prepare_items() // phpcs:ignore PSR1.Methods.CamelCapsMethodNam
$wc_spam_orders = $this->getWcSpamOrders();
$this->wc_spam_orders_count = count($wc_spam_orders);
+ // Blocked orders are never put on hold, so the view is always empty
+ if ( $this->getCurrentStatus() === 'on-hold' ) {
+ $wc_spam_orders = array();
+ }
+
$this->set_pagination_args(array(
- 'total_items' => $this->wc_spam_orders_count,
+ 'total_items' => count($wc_spam_orders),
'per_page' => $per_page,
));
@@ -83,29 +97,40 @@ public function prepare_items() // phpcs:ignore PSR1.Methods.CamelCapsMethodNam
continue;
}
- $delete_url = wp_nonce_url(
- admin_url('admin.php?page=' . Get::getString('page') . '&action=delete&spam=' . $wc_spam_order->id),
- 'apbct_wc_spam_orders_row',
- '_wpnonce'
+ // The status has to be kept, the hosting page is chosen by it
+ $current_status = Get::getString('status');
+ $delete_url = admin_url('admin.php?page=' . Get::getString('page'));
+ $delete_url = add_query_arg(
+ array_filter(array(
+ 'status' => $current_status,
+ 'action' => 'delete',
+ 'spam' => $wc_spam_order->id,
+ )),
+ $delete_url
);
+ $delete_url = wp_nonce_url($delete_url, 'apbct_wc_spam_orders_row', '_wpnonce');
$actions = array(
'restore' => '' . esc_html__('Restore', 'cleantalk-spam-protect') . '',
'delete' => 'Delete',
'details' => '' . esc_html__('See details', 'cleantalk-spam-protect') . '',
);
- $order_id_column = sprintf('%1$s %2$s', $wc_spam_order->id, $this->row_actions($actions));
+ $order_column = sprintf(
+ '%1$s %2$s',
+ $this->renderOrderColumn($wc_spam_order->id, $wc_spam_order->customer_details),
+ $this->row_actions($actions)
+ );
- $order_details_column = $this->renderOrderDetailsColumn($wc_spam_order->order_details);
- $customer_details_column = $this->renderCustomerDetailsColumn($wc_spam_order->customer_details);
- $order_date_column = $this->renderOrderDateColumn($wc_spam_order->order_date);
+ $order_date_column = $this->renderOrderDateColumn($wc_spam_order->order_date);
+ $status_column = $this->renderStatusColumn();
+ $total_column = $this->renderTotalColumn($wc_spam_order->order_details);
$this->items[] = array(
- 'cb' => $wc_spam_order->id,
- 'ct_order_id' => $order_id_column,
- 'ct_order_details' => $order_details_column,
- 'ct_customer_details' => $customer_details_column,
- 'ct_order_date' => $order_date_column,
+ 'cb' => $wc_spam_order->id,
+ 'ct_order' => $order_column,
+ 'ct_order_date' => $order_date_column,
+ 'ct_status' => $status_column,
+ 'ct_total' => $total_column,
);
}
}
@@ -113,11 +138,11 @@ public function prepare_items() // phpcs:ignore PSR1.Methods.CamelCapsMethodNam
public function get_columns() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
{
$columns = array(
- 'cb' => '',
- 'ct_order_id' => esc_html__('ID', 'cleantalk-spam-protect'),
- 'ct_order_details' => esc_html__('Order details', 'cleantalk-spam-protect'),
- 'ct_customer_details' => esc_html__('Customer details', 'cleantalk-spam-protect'),
- 'ct_order_date' => esc_html__('Order date', 'cleantalk-spam-protect'),
+ 'cb' => '',
+ 'ct_order' => esc_html__('Order', 'cleantalk-spam-protect'),
+ 'ct_order_date' => esc_html__('Date', 'cleantalk-spam-protect'),
+ 'ct_status' => esc_html__('Status', 'cleantalk-spam-protect'),
+ 'ct_total' => esc_html__('Total', 'cleantalk-spam-protect'),
);
return $columns;
@@ -126,8 +151,79 @@ public function get_columns() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.N
protected function get_sortable_columns() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
{
return array(
+ 'ct_order' => array('id', false),
'ct_order_date' => array('order_date', false),
+ 'ct_total' => array('total', false),
+ );
+ }
+
+ /**
+ * Statuses row above the table, the same one as the WooCommerce orders list has.
+ * Every stored order is a blocked spam one, so the "On hold" view is always empty.
+ *
+ * @return array
+ */
+ protected function get_views() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
+ {
+ if ( ! is_null($this->embedded_views) ) {
+ return $this->embedded_views;
+ }
+
+ $current_status = $this->getCurrentStatus();
+
+ $statuses = array(
+ 'all' => array(esc_html__('All', 'cleantalk-spam-protect'), $this->wc_spam_orders_count),
+ 'on-hold' => array(esc_html__('On hold', 'cleantalk-spam-protect'), 0),
+ 'spam' => array(esc_html__('Spam', 'cleantalk-spam-protect'), $this->wc_spam_orders_count),
);
+
+ $views = array();
+
+ foreach ( $statuses as $status => $status_data ) {
+ list($title, $count) = $status_data;
+
+ $url = admin_url('admin.php?page=' . Get::getString('page'));
+ if ( $status !== 'all' ) {
+ $url = add_query_arg('status', $status, $url);
+ }
+
+ $views[$status] = sprintf(
+ '%3$s (%4$d)',
+ esc_url($url),
+ $status === $current_status ? ' class="current" aria-current="page"' : '',
+ $title,
+ $count
+ );
+ }
+
+ return $views;
+ }
+
+ /**
+ * Currently selected status view.
+ *
+ * @return string all|on-hold|spam
+ */
+ private function getCurrentStatus()
+ {
+ $status = Get::getString('status');
+
+ return in_array($status, array('on-hold', 'spam'), true) ? $status : 'all';
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function display() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
+ {
+ $this->views();
+
+ parent::display();
+ }
+
+ public function no_items() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
+ {
+ esc_html_e('No orders found.', 'cleantalk-spam-protect');
}
public function get_bulk_actions() // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps
@@ -256,6 +352,45 @@ private function renderCustomerDetailsColumn($customer_details)
return $result;
}
+ /**
+ * Order number and the customer name, the same way as WooCommerce orders list does it.
+ *
+ * @param int|string $spam_order_id
+ * @param string $customer_details
+ *
+ * @return string
+ */
+ private function renderOrderColumn($spam_order_id, $customer_details)
+ {
+ $customer_details = json_decode($customer_details, true);
+
+ $customer_name = '';
+
+ if ( is_array($customer_details) ) {
+ $customer_name = trim(
+ TT::getArrayValueAsString($customer_details, 'billing_first_name')
+ . ' '
+ . TT::getArrayValueAsString($customer_details, 'billing_last_name')
+ );
+
+ if ( $customer_name === '' ) {
+ $customer_name = TT::getArrayValueAsString($customer_details, 'billing_email');
+ }
+ }
+
+ if ( $customer_name === '' ) {
+ $customer_name = esc_html__('Guest', 'cleantalk-spam-protect');
+ } else {
+ $customer_name = esc_html($customer_name);
+ }
+
+ return sprintf(
+ '#%1$s %2$s',
+ esc_attr(TT::toString($spam_order_id)),
+ $customer_name
+ );
+ }
+
private function renderOrderDateColumn($order_date)
{
if ( ! $order_date ) {
@@ -268,14 +403,96 @@ private function renderOrderDateColumn($order_date)
return '-';
}
+ // Fresh orders are shown as "5 minutes ago", the older ones as a date. Same as WooCommerce does.
+ $diff = time() - $timestamp;
+ if ( $diff >= 0 && $diff < DAY_IN_SECONDS ) {
+ /* translators: %s: human-readable time difference */
+ $show_date = sprintf(__('%s ago', 'cleantalk-spam-protect'), human_time_diff($timestamp, time()));
+ } else {
+ $show_date = date_i18n('M j, Y', $timestamp); // Feb 15, 2023
+ }
+
return sprintf(
'',
esc_attr(date_i18n('c', $timestamp)), // 2023-02-15T20:25:06+00:00
- esc_html(date_i18n('d.m.Y H:i', $timestamp)), // 15.02.2023 20:25
- esc_html(date_i18n('M d, Y', $timestamp)) // Feb 15, 2023
+ esc_attr(date_i18n('d.m.Y H:i', $timestamp)), // 15.02.2023 20:25
+ esc_html($show_date)
);
}
+ /**
+ * Every stored order is a blocked spam one, so the status is always the same.
+ *
+ * @return string
+ */
+ private function renderStatusColumn()
+ {
+ return ''
+ . esc_html__('Spam', 'cleantalk-spam-protect')
+ . '';
+ }
+
+ /**
+ * @param string $order_details
+ *
+ * @return string Formatted order total or a dash if it can not be calculated.
+ */
+ private function renderTotalColumn($order_details)
+ {
+ $total = $this->calcOrderTotal($order_details);
+
+ if ( is_null($total) ) {
+ return '-';
+ }
+
+ /** @psalm-suppress UndefinedFunction */
+ return function_exists('wc_price')
+ ? wc_price($total)
+ : esc_html(number_format_i18n($total, 2));
+ }
+
+ /**
+ * Sums up the stored cart items. Cart data keeps the calculated line totals,
+ * the product price is used as a fallback only.
+ *
+ * @param string $order_details
+ *
+ * @return float|null Null if the order details can not be decoded.
+ *
+ * @psalm-suppress UndefinedFunction
+ */
+ private function calcOrderTotal($order_details)
+ {
+ $order_details = json_decode($order_details, true);
+
+ if ( ! is_array($order_details) ) {
+ return null;
+ }
+
+ $total = 0;
+
+ foreach ( $order_details as $order_detail ) {
+ if ( ! is_array($order_detail) ) {
+ continue;
+ }
+
+ if ( isset($order_detail['line_total']) ) {
+ $total += (float) $order_detail['line_total'] + (float) ($order_detail['line_tax'] ?? 0);
+ continue;
+ }
+
+ if ( isset($order_detail['product_id']) && function_exists('wc_get_product') && class_exists('\WC_Product') ) {
+ $wc_product = wc_get_product($order_detail['product_id']);
+ $wc_product_class = '\WC_Product';
+ if ( $wc_product instanceof $wc_product_class ) {
+ $total += (float) $wc_product->get_price() * (float) ($order_detail['quantity'] ?? 1);
+ }
+ }
+ }
+
+ return (float) $total;
+ }
+
/**
* @return array
*/
@@ -288,20 +505,69 @@ private function getWcSpamOrders()
$sql = 'SELECT * FROM ' . APBCT_TBL_WC_SPAM_ORDERS;
- if ($orderby) {
- $sql .= ' ORDER BY ' . $orderby . ' ' . $order;
- }
+ // The newest spam orders are shown first by default, the same way as WooCommerce orders list does it.
+ $sql .= ' ORDER BY ' . ($orderby ? $orderby : 'order_date') . ' ' . $order;
$result = $wpdb->get_results($sql, OBJECT);
- return is_array($result) ? $result : array();
+ $result = is_array($result) ? $result : array();
+
+ if ( Get::getString('orderby') === 'total' ) {
+ $result = $this->sortByTotal($result, $order);
+ }
+
+ return $result;
+ }
+
+ /**
+ * The order total is not stored as a column, so it has to be sorted after the fetch.
+ *
+ * @param array $wc_spam_orders
+ * @param string $order ASC|DESC
+ *
+ * @return array
+ */
+ private function sortByTotal($wc_spam_orders, $order)
+ {
+ $totals = array();
+
+ foreach ( $wc_spam_orders as $key => $wc_spam_order ) {
+ $calculated_total = is_string($wc_spam_order->order_details)
+ ? $this->calcOrderTotal($wc_spam_order->order_details)
+ : null;
+
+ if ( is_null($calculated_total) ) {
+ // Keep undecodable totals consistently at the end for both ASC and DESC.
+ $calculated_total = $order === 'DESC' ? -PHP_FLOAT_MAX : PHP_FLOAT_MAX;
+ }
+
+ $totals[$key] = (float) $calculated_total;
+ }
+
+ uasort($totals, static function ($a, $b) {
+ if ( $a === $b ) {
+ return 0;
+ }
+ return $a < $b ? -1 : 1;
+ });
+
+ if ( $order === 'DESC' ) {
+ $totals = array_reverse($totals, true);
+ }
+
+ $sorted = array();
+ foreach ( array_keys($totals) as $key ) {
+ $sorted[] = $wc_spam_orders[$key];
+ }
+
+ return $sorted;
}
private function getSqlOrderBy()
{
$order_by = Get::getString('orderby');
- $allowed_order_by = array_keys($this->get_sortable_columns());
- return in_array('ct_' . $order_by, $allowed_order_by) ? $order_by : '';
+ $allowed_order_by = array('id', 'order_date');
+ return in_array($order_by, $allowed_order_by, true) ? $order_by : '';
}
private function removeSpam($ids)
@@ -325,10 +591,16 @@ private function removeSpam($ids)
);
}
- private function generatePageHeader()
+ /**
+ * Notices shown above the table: the stored orders count and the warnings about the data.
+ *
+ * @return void
+ * @psalm-suppress PossiblyUnusedMethod
+ */
+ public function renderPageNotices()
{
if ( ! apbct_api_key__is_correct() ) {
- if ( 1 == $this->spam_checker->getApbct()->moderate_ip ) {
+ if ( 1 == $this->apbct->moderate_ip ) {
echo '