feat: WooCommerce Business Central integration plugin
Native PHP plugin (no Composer) that syncs: - Product stock and pricing from BC to WooCommerce (scheduled cron) - Orders from WooCommerce to BC (on payment received) - Auto-creates customers in BC from WooCommerce billing data Product matching: WooCommerce SKU → BC Item Number, fallback to GTIN (EAN). OAuth2 client credentials auth with encrypted secret storage. Admin settings page with connection test, manual sync, and log viewer. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
332
woo-business-central/admin/class-wbc-admin.php
Normal file
332
woo-business-central/admin/class-wbc-admin.php
Normal file
@@ -0,0 +1,332 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin Settings for WooCommerce Business Central Integration
|
||||
*
|
||||
* @package WooBusinessCentral
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class WBC_Admin
|
||||
*
|
||||
* Handles admin settings page and AJAX actions.
|
||||
*/
|
||||
class WBC_Admin {
|
||||
|
||||
/**
|
||||
* Settings page slug
|
||||
*/
|
||||
const PAGE_SLUG = 'wbc-settings';
|
||||
|
||||
/**
|
||||
* Add admin menu
|
||||
*/
|
||||
public function add_admin_menu() {
|
||||
add_submenu_page(
|
||||
'woocommerce',
|
||||
__( 'Business Central', 'woo-business-central' ),
|
||||
__( 'Business Central', 'woo-business-central' ),
|
||||
'manage_woocommerce',
|
||||
self::PAGE_SLUG,
|
||||
array( $this, 'render_settings_page' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register settings
|
||||
*/
|
||||
public function register_settings() {
|
||||
// Connection settings
|
||||
register_setting( 'wbc_settings', 'wbc_tenant_id', array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
) );
|
||||
register_setting( 'wbc_settings', 'wbc_client_id', array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
) );
|
||||
register_setting( 'wbc_settings', 'wbc_client_secret', array(
|
||||
'sanitize_callback' => array( $this, 'sanitize_client_secret' ),
|
||||
) );
|
||||
register_setting( 'wbc_settings', 'wbc_environment', array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
) );
|
||||
register_setting( 'wbc_settings', 'wbc_company_id', array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
) );
|
||||
|
||||
// Sync settings
|
||||
register_setting( 'wbc_settings', 'wbc_sync_frequency', array(
|
||||
'sanitize_callback' => array( $this, 'sanitize_frequency' ),
|
||||
) );
|
||||
register_setting( 'wbc_settings', 'wbc_enable_stock_sync', array(
|
||||
'sanitize_callback' => array( $this, 'sanitize_checkbox' ),
|
||||
) );
|
||||
register_setting( 'wbc_settings', 'wbc_enable_price_sync', array(
|
||||
'sanitize_callback' => array( $this, 'sanitize_checkbox' ),
|
||||
) );
|
||||
|
||||
// Order settings
|
||||
register_setting( 'wbc_settings', 'wbc_enable_order_sync', array(
|
||||
'sanitize_callback' => array( $this, 'sanitize_checkbox' ),
|
||||
) );
|
||||
register_setting( 'wbc_settings', 'wbc_default_payment_terms_id', array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
) );
|
||||
register_setting( 'wbc_settings', 'wbc_default_shipment_method_id', array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
) );
|
||||
register_setting( 'wbc_settings', 'wbc_shipping_item_number', array(
|
||||
'sanitize_callback' => 'sanitize_text_field',
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize client secret
|
||||
*
|
||||
* @param string $value Input value.
|
||||
* @return string Sanitized value.
|
||||
*/
|
||||
public function sanitize_client_secret( $value ) {
|
||||
if ( empty( $value ) ) {
|
||||
// Keep existing value if empty (masked field)
|
||||
return get_option( 'wbc_client_secret', '' );
|
||||
}
|
||||
|
||||
// If it looks like a masked value, keep existing
|
||||
if ( strpos( $value, '***' ) !== false ) {
|
||||
return get_option( 'wbc_client_secret', '' );
|
||||
}
|
||||
|
||||
// Encrypt new value
|
||||
return WBC_OAuth::encrypt( sanitize_text_field( $value ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize frequency
|
||||
*
|
||||
* @param string $value Input value.
|
||||
* @return string Sanitized value.
|
||||
*/
|
||||
public function sanitize_frequency( $value ) {
|
||||
$allowed = array( 'hourly', 'twice_daily', 'daily' );
|
||||
$value = sanitize_text_field( $value );
|
||||
|
||||
if ( ! in_array( $value, $allowed, true ) ) {
|
||||
return 'daily';
|
||||
}
|
||||
|
||||
// Reschedule cron if frequency changed
|
||||
$old_frequency = get_option( 'wbc_sync_frequency', 'daily' );
|
||||
if ( $value !== $old_frequency ) {
|
||||
WBC_Cron::reschedule_sync( $value );
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize checkbox
|
||||
*
|
||||
* @param string $value Input value.
|
||||
* @return string Sanitized value.
|
||||
*/
|
||||
public function sanitize_checkbox( $value ) {
|
||||
return $value === 'yes' ? 'yes' : 'no';
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin scripts
|
||||
*
|
||||
* @param string $hook Current admin page hook.
|
||||
*/
|
||||
public function enqueue_scripts( $hook ) {
|
||||
if ( strpos( $hook, self::PAGE_SLUG ) === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
wp_enqueue_style(
|
||||
'wbc-admin-style',
|
||||
WBC_PLUGIN_URL . 'admin/css/wbc-admin.css',
|
||||
array(),
|
||||
WBC_VERSION
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'wbc-admin-script',
|
||||
WBC_PLUGIN_URL . 'admin/js/wbc-admin.js',
|
||||
array( 'jquery' ),
|
||||
WBC_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script( 'wbc-admin-script', 'wbc_admin', array(
|
||||
'ajax_url' => admin_url( 'admin-ajax.php' ),
|
||||
'nonce' => wp_create_nonce( 'wbc_admin_nonce' ),
|
||||
'strings' => array(
|
||||
'testing' => __( 'Testing connection...', 'woo-business-central' ),
|
||||
'syncing' => __( 'Syncing products...', 'woo-business-central' ),
|
||||
'clearing' => __( 'Clearing logs...', 'woo-business-central' ),
|
||||
'loading' => __( 'Loading...', 'woo-business-central' ),
|
||||
'confirm_clear' => __( 'Are you sure you want to clear all logs?', 'woo-business-central' ),
|
||||
),
|
||||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add settings link to plugin list
|
||||
*
|
||||
* @param array $links Existing links.
|
||||
* @return array Modified links.
|
||||
*/
|
||||
public function add_settings_link( $links ) {
|
||||
$settings_link = sprintf(
|
||||
'<a href="%s">%s</a>',
|
||||
admin_url( 'admin.php?page=' . self::PAGE_SLUG ),
|
||||
__( 'Settings', 'woo-business-central' )
|
||||
);
|
||||
|
||||
array_unshift( $links, $settings_link );
|
||||
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render settings page
|
||||
*/
|
||||
public function render_settings_page() {
|
||||
// Check user capabilities
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_die( __( 'You do not have sufficient permissions to access this page.', 'woo-business-central' ) );
|
||||
}
|
||||
|
||||
// Handle tab
|
||||
$current_tab = isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : 'connection';
|
||||
|
||||
include WBC_PLUGIN_DIR . 'admin/partials/wbc-admin-display.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: Test connection
|
||||
*/
|
||||
public function ajax_test_connection() {
|
||||
check_ajax_referer( 'wbc_admin_nonce', 'nonce' );
|
||||
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'woo-business-central' ) ) );
|
||||
}
|
||||
|
||||
$result = WBC_OAuth::test_connection();
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
|
||||
}
|
||||
|
||||
wp_send_json_success( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: Manual sync
|
||||
*/
|
||||
public function ajax_manual_sync() {
|
||||
check_ajax_referer( 'wbc_admin_nonce', 'nonce' );
|
||||
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'woo-business-central' ) ) );
|
||||
}
|
||||
|
||||
$result = WBC_Cron::run_sync_now();
|
||||
|
||||
if ( isset( $result['success'] ) && $result['success'] ) {
|
||||
wp_send_json_success( $result );
|
||||
} else {
|
||||
wp_send_json_error( $result );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: Clear logs
|
||||
*/
|
||||
public function ajax_clear_logs() {
|
||||
check_ajax_referer( 'wbc_admin_nonce', 'nonce' );
|
||||
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'woo-business-central' ) ) );
|
||||
}
|
||||
|
||||
WBC_Logger::clear_logs();
|
||||
|
||||
wp_send_json_success( array( 'message' => __( 'Logs cleared successfully.', 'woo-business-central' ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX: Get companies
|
||||
*/
|
||||
public function ajax_get_companies() {
|
||||
check_ajax_referer( 'wbc_admin_nonce', 'nonce' );
|
||||
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_send_json_error( array( 'message' => __( 'Permission denied.', 'woo-business-central' ) ) );
|
||||
}
|
||||
|
||||
$result = WBC_API_Client::get_companies();
|
||||
|
||||
if ( is_wp_error( $result ) ) {
|
||||
wp_send_json_error( array( 'message' => $result->get_error_message() ) );
|
||||
}
|
||||
|
||||
$companies = isset( $result['value'] ) ? $result['value'] : array();
|
||||
|
||||
wp_send_json_success( array( 'companies' => $companies ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle CSV export of logs
|
||||
*/
|
||||
public function handle_csv_export() {
|
||||
if ( ! isset( $_GET['wbc_export_logs'] ) || $_GET['wbc_export_logs'] !== '1' ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $_GET['_wpnonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_wpnonce'] ) ), 'wbc_export_logs' ) ) {
|
||||
wp_die( esc_html__( 'Security check failed.', 'woo-business-central' ) );
|
||||
}
|
||||
|
||||
if ( ! current_user_can( 'manage_woocommerce' ) ) {
|
||||
wp_die( esc_html__( 'Permission denied.', 'woo-business-central' ) );
|
||||
}
|
||||
|
||||
$csv = WBC_Logger::export_to_csv();
|
||||
|
||||
header( 'Content-Type: text/csv; charset=utf-8' );
|
||||
header( 'Content-Disposition: attachment; filename=wbc-logs-' . gmdate( 'Y-m-d' ) . '.csv' );
|
||||
header( 'Pragma: no-cache' );
|
||||
header( 'Expires: 0' );
|
||||
|
||||
echo $csv; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- CSV output
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get logs for display
|
||||
*
|
||||
* @param array $args Query arguments.
|
||||
* @return array Logs data.
|
||||
*/
|
||||
public static function get_logs_for_display( $args = array() ) {
|
||||
$defaults = array(
|
||||
'limit' => 50,
|
||||
'offset' => 0,
|
||||
'level' => '',
|
||||
);
|
||||
|
||||
$args = wp_parse_args( $args, $defaults );
|
||||
|
||||
return array(
|
||||
'logs' => WBC_Logger::get_logs( $args ),
|
||||
'total' => WBC_Logger::get_log_count( $args ),
|
||||
);
|
||||
}
|
||||
}
|
||||
233
woo-business-central/admin/css/wbc-admin.css
Normal file
233
woo-business-central/admin/css/wbc-admin.css
Normal file
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* WooCommerce Business Central - Admin Styles
|
||||
*/
|
||||
|
||||
/* General Layout */
|
||||
.wbc-admin-wrap {
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.wbc-nav-tabs {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Cards */
|
||||
.wbc-card {
|
||||
background: #fff;
|
||||
border: 1px solid #c3c4c7;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
margin-top: 20px;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
.wbc-card h2 {
|
||||
margin-top: 0;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.wbc-card h2:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Status Messages */
|
||||
.wbc-status {
|
||||
display: inline-block;
|
||||
margin-left: 10px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.wbc-status.success {
|
||||
color: #00a32a;
|
||||
}
|
||||
|
||||
.wbc-status.error {
|
||||
color: #d63638;
|
||||
}
|
||||
|
||||
.wbc-status.loading {
|
||||
color: #2271b1;
|
||||
}
|
||||
|
||||
/* Status Table */
|
||||
.wbc-status-table {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.wbc-status-table th {
|
||||
text-align: left;
|
||||
padding: 8px 20px 8px 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.wbc-status-table td {
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
/* Logs Table */
|
||||
.wbc-logs-table {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.wbc-logs-table .column-timestamp {
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.wbc-logs-table .column-level {
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.wbc-logs-table .column-context {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
.wbc-logs-table .column-message {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
/* Log Level Badges */
|
||||
.wbc-log-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 3px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.wbc-log-badge-debug {
|
||||
background: #f0f0f1;
|
||||
color: #50575e;
|
||||
}
|
||||
|
||||
.wbc-log-badge-info {
|
||||
background: #d1e4f6;
|
||||
color: #0a4b78;
|
||||
}
|
||||
|
||||
.wbc-log-badge-warning {
|
||||
background: #fcf9e8;
|
||||
color: #bd8600;
|
||||
}
|
||||
|
||||
.wbc-log-badge-error {
|
||||
background: #facfd2;
|
||||
color: #8a1f1f;
|
||||
}
|
||||
|
||||
/* Log Row Colors */
|
||||
.wbc-log-level-error {
|
||||
background-color: #fff5f5 !important;
|
||||
}
|
||||
|
||||
.wbc-log-level-warning {
|
||||
background-color: #fffbe8 !important;
|
||||
}
|
||||
|
||||
/* Log Data Display */
|
||||
.wbc-log-data {
|
||||
background: #f6f7f7;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
font-size: 12px;
|
||||
max-height: 200px;
|
||||
overflow: auto;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 3px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* Log Filters */
|
||||
.wbc-log-filters {
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
background: #f6f7f7;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.wbc-log-filters label {
|
||||
font-weight: 500;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* Toggle Data Link */
|
||||
.wbc-toggle-data {
|
||||
margin-left: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Form Improvements */
|
||||
.wbc-settings-form .form-table th {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.wbc-settings-form .description {
|
||||
color: #646970;
|
||||
font-style: normal;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* Button Spacing */
|
||||
.wbc-card .button + .button,
|
||||
.wbc-card .button-primary + .button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
/* Connection Status */
|
||||
#wbc-connection-status {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Companies List */
|
||||
#wbc-companies-list {
|
||||
margin-top: 10px;
|
||||
padding: 10px;
|
||||
background: #f6f7f7;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
#wbc-company-select {
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
/* Responsive Adjustments */
|
||||
@media screen and (max-width: 782px) {
|
||||
.wbc-logs-table .column-timestamp,
|
||||
.wbc-logs-table .column-context {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wbc-logs-table .column-level {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
#wbc-company-select {
|
||||
min-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loading State */
|
||||
.wbc-loading {
|
||||
opacity: 0.5;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.wbc-logs-table + .tablenav {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.tablenav-pages .pagination-links {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.tablenav-pages .button {
|
||||
padding: 0 8px;
|
||||
min-height: 28px;
|
||||
line-height: 26px;
|
||||
}
|
||||
239
woo-business-central/admin/js/wbc-admin.js
Normal file
239
woo-business-central/admin/js/wbc-admin.js
Normal file
@@ -0,0 +1,239 @@
|
||||
/**
|
||||
* WooCommerce Business Central - Admin JavaScript
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
'use strict';
|
||||
|
||||
var WBC_Admin = {
|
||||
/**
|
||||
* Initialize
|
||||
*/
|
||||
init: function() {
|
||||
this.bindEvents();
|
||||
},
|
||||
|
||||
/**
|
||||
* Bind event handlers
|
||||
*/
|
||||
bindEvents: function() {
|
||||
// Test connection
|
||||
$('#wbc-test-connection').on('click', this.testConnection);
|
||||
|
||||
// Manual sync
|
||||
$('#wbc-manual-sync').on('click', this.manualSync);
|
||||
|
||||
// Clear logs
|
||||
$('#wbc-clear-logs').on('click', this.clearLogs);
|
||||
|
||||
// Load companies
|
||||
$('#wbc-load-companies').on('click', this.loadCompanies);
|
||||
|
||||
// Select company
|
||||
$('#wbc-company-select').on('change', this.selectCompany);
|
||||
|
||||
// Toggle log data
|
||||
$(document).on('click', '.wbc-toggle-data', this.toggleLogData);
|
||||
},
|
||||
|
||||
/**
|
||||
* Test connection to Business Central
|
||||
*/
|
||||
testConnection: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $btn = $(this);
|
||||
var $status = $('#wbc-connection-status');
|
||||
|
||||
$btn.prop('disabled', true);
|
||||
$status.removeClass('success error').addClass('loading').text(wbc_admin.strings.testing);
|
||||
|
||||
$.ajax({
|
||||
url: wbc_admin.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'wbc_test_connection',
|
||||
nonce: wbc_admin.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
$btn.prop('disabled', false);
|
||||
$status.removeClass('loading');
|
||||
|
||||
if (response.success) {
|
||||
$status.addClass('success').text(response.data.message);
|
||||
} else {
|
||||
$status.addClass('error').text(response.data.message || 'Connection failed');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
$btn.prop('disabled', false);
|
||||
$status.removeClass('loading').addClass('error').text('Request failed: ' + error);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Run manual sync
|
||||
*/
|
||||
manualSync: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $btn = $(this);
|
||||
var $status = $('#wbc-sync-status');
|
||||
|
||||
$btn.prop('disabled', true);
|
||||
$status.removeClass('success error').addClass('loading').text(wbc_admin.strings.syncing);
|
||||
|
||||
$.ajax({
|
||||
url: wbc_admin.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'wbc_manual_sync',
|
||||
nonce: wbc_admin.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
$btn.prop('disabled', false);
|
||||
$status.removeClass('loading');
|
||||
|
||||
if (response.success) {
|
||||
$status.addClass('success').text(response.data.message);
|
||||
} else {
|
||||
$status.addClass('error').text(response.data.message || 'Sync failed');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
$btn.prop('disabled', false);
|
||||
$status.removeClass('loading').addClass('error').text('Request failed: ' + error);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear all logs
|
||||
*/
|
||||
clearLogs: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
if (!confirm(wbc_admin.strings.confirm_clear)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $btn = $(this);
|
||||
var originalText = $btn.text();
|
||||
|
||||
$btn.prop('disabled', true).text(wbc_admin.strings.clearing);
|
||||
|
||||
$.ajax({
|
||||
url: wbc_admin.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'wbc_clear_logs',
|
||||
nonce: wbc_admin.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
// Reload the page to show empty logs
|
||||
location.reload();
|
||||
} else {
|
||||
alert(response.data.message || 'Failed to clear logs');
|
||||
$btn.prop('disabled', false).text(originalText);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
alert('Request failed: ' + error);
|
||||
$btn.prop('disabled', false).text(originalText);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Load companies from Business Central
|
||||
*/
|
||||
loadCompanies: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $btn = $(this);
|
||||
var $select = $('#wbc-company-select');
|
||||
var $list = $('#wbc-companies-list');
|
||||
var originalText = $btn.text();
|
||||
|
||||
$btn.prop('disabled', true).text(wbc_admin.strings.loading);
|
||||
|
||||
$.ajax({
|
||||
url: wbc_admin.ajax_url,
|
||||
type: 'POST',
|
||||
data: {
|
||||
action: 'wbc_get_companies',
|
||||
nonce: wbc_admin.nonce
|
||||
},
|
||||
success: function(response) {
|
||||
$btn.prop('disabled', false).text(originalText);
|
||||
|
||||
if (response.success && response.data.companies) {
|
||||
// Clear existing options except the first one
|
||||
$select.find('option:not(:first)').remove();
|
||||
|
||||
// Add companies
|
||||
$.each(response.data.companies, function(i, company) {
|
||||
$select.append(
|
||||
$('<option>', {
|
||||
value: company.id,
|
||||
text: company.displayName + ' (' + company.id.substring(0, 8) + '...)'
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
// Show the select
|
||||
$list.slideDown();
|
||||
|
||||
// Pre-select current company if set
|
||||
var currentCompany = $('#wbc_company_id').val();
|
||||
if (currentCompany) {
|
||||
$select.val(currentCompany);
|
||||
}
|
||||
} else {
|
||||
alert(response.data.message || 'Failed to load companies. Make sure credentials are saved and correct.');
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
$btn.prop('disabled', false).text(originalText);
|
||||
alert('Request failed: ' + error);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Select company from dropdown
|
||||
*/
|
||||
selectCompany: function() {
|
||||
var companyId = $(this).val();
|
||||
if (companyId) {
|
||||
$('#wbc_company_id').val(companyId);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggle log data visibility
|
||||
*/
|
||||
toggleLogData: function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var $btn = $(this);
|
||||
var $data = $btn.siblings('.wbc-log-data');
|
||||
|
||||
$data.slideToggle(200, function() {
|
||||
if ($data.is(':visible')) {
|
||||
$btn.text($btn.data('hide-text') || 'Hide data');
|
||||
} else {
|
||||
$btn.text($btn.data('show-text') || 'Show data');
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize on document ready
|
||||
$(document).ready(function() {
|
||||
WBC_Admin.init();
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
399
woo-business-central/admin/partials/wbc-admin-display.php
Normal file
399
woo-business-central/admin/partials/wbc-admin-display.php
Normal file
@@ -0,0 +1,399 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin settings page template
|
||||
*
|
||||
* @package WooBusinessCentral
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$tabs = array(
|
||||
'connection' => __( 'Connection', 'woo-business-central' ),
|
||||
'sync' => __( 'Sync Settings', 'woo-business-central' ),
|
||||
'orders' => __( 'Order Settings', 'woo-business-central' ),
|
||||
'logs' => __( 'Logs', 'woo-business-central' ),
|
||||
);
|
||||
?>
|
||||
|
||||
<div class="wrap wbc-admin-wrap">
|
||||
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
|
||||
|
||||
<nav class="nav-tab-wrapper wbc-nav-tabs">
|
||||
<?php foreach ( $tabs as $tab_id => $tab_name ) : ?>
|
||||
<a href="<?php echo esc_url( add_query_arg( 'tab', $tab_id ) ); ?>"
|
||||
class="nav-tab <?php echo $current_tab === $tab_id ? 'nav-tab-active' : ''; ?>">
|
||||
<?php echo esc_html( $tab_name ); ?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
|
||||
<div class="wbc-admin-content">
|
||||
<?php if ( $current_tab === 'connection' ) : ?>
|
||||
<!-- Connection Settings -->
|
||||
<form method="post" action="options.php" class="wbc-settings-form">
|
||||
<?php settings_fields( 'wbc_settings' ); ?>
|
||||
|
||||
<div class="wbc-card">
|
||||
<h2><?php esc_html_e( 'Microsoft Azure AD Credentials', 'woo-business-central' ); ?></h2>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Enter your Azure AD application credentials to connect to Business Central.', 'woo-business-central' ); ?>
|
||||
</p>
|
||||
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="wbc_tenant_id"><?php esc_html_e( 'Tenant ID', 'woo-business-central' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" id="wbc_tenant_id" name="wbc_tenant_id"
|
||||
value="<?php echo esc_attr( get_option( 'wbc_tenant_id', '' ) ); ?>"
|
||||
class="regular-text" />
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Your Azure AD tenant ID (GUID format).', 'woo-business-central' ); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="wbc_client_id"><?php esc_html_e( 'Client ID', 'woo-business-central' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" id="wbc_client_id" name="wbc_client_id"
|
||||
value="<?php echo esc_attr( get_option( 'wbc_client_id', '' ) ); ?>"
|
||||
class="regular-text" />
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Application (client) ID from Azure AD.', 'woo-business-central' ); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="wbc_client_secret"><?php esc_html_e( 'Client Secret', 'woo-business-central' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<?php
|
||||
$has_secret = ! empty( WBC_OAuth::get_client_secret() );
|
||||
$placeholder = $has_secret ? '********' : '';
|
||||
?>
|
||||
<input type="password" id="wbc_client_secret" name="wbc_client_secret"
|
||||
value="" placeholder="<?php echo esc_attr( $placeholder ); ?>"
|
||||
class="regular-text" autocomplete="new-password" />
|
||||
<p class="description">
|
||||
<?php if ( $has_secret ) : ?>
|
||||
<?php esc_html_e( 'Leave blank to keep existing secret. Enter a new value to update.', 'woo-business-central' ); ?>
|
||||
<?php else : ?>
|
||||
<?php esc_html_e( 'Client secret from Azure AD application.', 'woo-business-central' ); ?>
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="wbc_environment"><?php esc_html_e( 'Environment', 'woo-business-central' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<select id="wbc_environment" name="wbc_environment">
|
||||
<option value="production" <?php selected( get_option( 'wbc_environment', 'production' ), 'production' ); ?>>
|
||||
<?php esc_html_e( 'Production', 'woo-business-central' ); ?>
|
||||
</option>
|
||||
<option value="sandbox" <?php selected( get_option( 'wbc_environment' ), 'sandbox' ); ?>>
|
||||
<?php esc_html_e( 'Sandbox', 'woo-business-central' ); ?>
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="wbc_company_id"><?php esc_html_e( 'Company ID', 'woo-business-central' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" id="wbc_company_id" name="wbc_company_id"
|
||||
value="<?php echo esc_attr( get_option( 'wbc_company_id', '' ) ); ?>"
|
||||
class="regular-text" />
|
||||
<button type="button" id="wbc-load-companies" class="button">
|
||||
<?php esc_html_e( 'Load Companies', 'woo-business-central' ); ?>
|
||||
</button>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Business Central company ID (GUID). Click "Load Companies" after saving credentials.', 'woo-business-central' ); ?>
|
||||
</p>
|
||||
<div id="wbc-companies-list" style="display: none; margin-top: 10px;">
|
||||
<select id="wbc-company-select">
|
||||
<option value=""><?php esc_html_e( 'Select a company...', 'woo-business-central' ); ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="submit">
|
||||
<?php submit_button( __( 'Save Settings', 'woo-business-central' ), 'primary', 'submit', false ); ?>
|
||||
<button type="button" id="wbc-test-connection" class="button button-secondary">
|
||||
<?php esc_html_e( 'Test Connection', 'woo-business-central' ); ?>
|
||||
</button>
|
||||
<span id="wbc-connection-status" class="wbc-status"></span>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php elseif ( $current_tab === 'sync' ) : ?>
|
||||
<!-- Sync Settings -->
|
||||
<form method="post" action="options.php" class="wbc-settings-form">
|
||||
<?php settings_fields( 'wbc_settings' ); ?>
|
||||
|
||||
<div class="wbc-card">
|
||||
<h2><?php esc_html_e( 'Product Sync Settings', 'woo-business-central' ); ?></h2>
|
||||
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="wbc_sync_frequency"><?php esc_html_e( 'Sync Frequency', 'woo-business-central' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<select id="wbc_sync_frequency" name="wbc_sync_frequency">
|
||||
<option value="hourly" <?php selected( get_option( 'wbc_sync_frequency', 'daily' ), 'hourly' ); ?>>
|
||||
<?php esc_html_e( 'Hourly', 'woo-business-central' ); ?>
|
||||
</option>
|
||||
<option value="twice_daily" <?php selected( get_option( 'wbc_sync_frequency' ), 'twice_daily' ); ?>>
|
||||
<?php esc_html_e( 'Twice Daily', 'woo-business-central' ); ?>
|
||||
</option>
|
||||
<option value="daily" <?php selected( get_option( 'wbc_sync_frequency' ), 'daily' ); ?>>
|
||||
<?php esc_html_e( 'Daily', 'woo-business-central' ); ?>
|
||||
</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php esc_html_e( 'Enable Stock Sync', 'woo-business-central' ); ?></th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="wbc_enable_stock_sync" value="yes"
|
||||
<?php checked( get_option( 'wbc_enable_stock_sync', 'yes' ), 'yes' ); ?> />
|
||||
<?php esc_html_e( 'Sync stock levels from Business Central to WooCommerce', 'woo-business-central' ); ?>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row"><?php esc_html_e( 'Enable Price Sync', 'woo-business-central' ); ?></th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="wbc_enable_price_sync" value="yes"
|
||||
<?php checked( get_option( 'wbc_enable_price_sync', 'yes' ), 'yes' ); ?> />
|
||||
<?php esc_html_e( 'Sync prices from Business Central to WooCommerce', 'woo-business-central' ); ?>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="submit">
|
||||
<?php submit_button( __( 'Save Settings', 'woo-business-central' ), 'primary', 'submit', false ); ?>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="wbc-card">
|
||||
<h2><?php esc_html_e( 'Sync Status', 'woo-business-central' ); ?></h2>
|
||||
|
||||
<table class="wbc-status-table">
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Last Sync:', 'woo-business-central' ); ?></th>
|
||||
<td><?php echo esc_html( WBC_Cron::get_last_sync_formatted() ); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php esc_html_e( 'Next Scheduled Sync:', 'woo-business-central' ); ?></th>
|
||||
<td><?php echo esc_html( WBC_Cron::get_next_sync_formatted() ); ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
<button type="button" id="wbc-manual-sync" class="button button-primary">
|
||||
<?php esc_html_e( 'Sync Now', 'woo-business-central' ); ?>
|
||||
</button>
|
||||
<span id="wbc-sync-status" class="wbc-status"></span>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php elseif ( $current_tab === 'orders' ) : ?>
|
||||
<!-- Order Settings -->
|
||||
<form method="post" action="options.php" class="wbc-settings-form">
|
||||
<?php settings_fields( 'wbc_settings' ); ?>
|
||||
|
||||
<div class="wbc-card">
|
||||
<h2><?php esc_html_e( 'Order Sync Settings', 'woo-business-central' ); ?></h2>
|
||||
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row"><?php esc_html_e( 'Enable Order Sync', 'woo-business-central' ); ?></th>
|
||||
<td>
|
||||
<label>
|
||||
<input type="checkbox" name="wbc_enable_order_sync" value="yes"
|
||||
<?php checked( get_option( 'wbc_enable_order_sync', 'yes' ), 'yes' ); ?> />
|
||||
<?php esc_html_e( 'Sync orders to Business Central when payment is received', 'woo-business-central' ); ?>
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="wbc_default_payment_terms_id"><?php esc_html_e( 'Default Payment Terms ID', 'woo-business-central' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" id="wbc_default_payment_terms_id" name="wbc_default_payment_terms_id"
|
||||
value="<?php echo esc_attr( get_option( 'wbc_default_payment_terms_id', '' ) ); ?>"
|
||||
class="regular-text" />
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Optional. Business Central payment terms ID to use for new orders.', 'woo-business-central' ); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="wbc_default_shipment_method_id"><?php esc_html_e( 'Default Shipment Method ID', 'woo-business-central' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" id="wbc_default_shipment_method_id" name="wbc_default_shipment_method_id"
|
||||
value="<?php echo esc_attr( get_option( 'wbc_default_shipment_method_id', '' ) ); ?>"
|
||||
class="regular-text" />
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Optional. Business Central shipment method ID to use for new orders.', 'woo-business-central' ); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="wbc_shipping_item_number"><?php esc_html_e( 'Shipping Item Number', 'woo-business-central' ); ?></label>
|
||||
</th>
|
||||
<td>
|
||||
<input type="text" id="wbc_shipping_item_number" name="wbc_shipping_item_number"
|
||||
value="<?php echo esc_attr( get_option( 'wbc_shipping_item_number', '' ) ); ?>"
|
||||
class="regular-text" />
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Optional. Business Central item number to use for shipping charges.', 'woo-business-central' ); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="submit">
|
||||
<?php submit_button( __( 'Save Settings', 'woo-business-central' ), 'primary', 'submit', false ); ?>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<?php elseif ( $current_tab === 'logs' ) : ?>
|
||||
<!-- Logs -->
|
||||
<div class="wbc-card">
|
||||
<h2><?php esc_html_e( 'Sync Logs', 'woo-business-central' ); ?></h2>
|
||||
|
||||
<p>
|
||||
<button type="button" id="wbc-clear-logs" class="button">
|
||||
<?php esc_html_e( 'Clear All Logs', 'woo-business-central' ); ?>
|
||||
</button>
|
||||
<a href="<?php echo esc_url( add_query_arg( array( 'wbc_export_logs' => '1', '_wpnonce' => wp_create_nonce( 'wbc_export_logs' ) ) ) ); ?>"
|
||||
class="button">
|
||||
<?php esc_html_e( 'Download CSV', 'woo-business-central' ); ?>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<?php
|
||||
$log_level = isset( $_GET['log_level'] ) ? sanitize_text_field( wp_unslash( $_GET['log_level'] ) ) : '';
|
||||
$page_num = isset( $_GET['log_page'] ) ? max( 1, absint( $_GET['log_page'] ) ) : 1;
|
||||
$per_page = 50;
|
||||
|
||||
$logs_data = WBC_Admin::get_logs_for_display( array(
|
||||
'level' => $log_level,
|
||||
'limit' => $per_page,
|
||||
'offset' => ( $page_num - 1 ) * $per_page,
|
||||
) );
|
||||
|
||||
$logs = $logs_data['logs'];
|
||||
$total = $logs_data['total'];
|
||||
$total_pages = ceil( $total / $per_page );
|
||||
?>
|
||||
|
||||
<div class="wbc-log-filters">
|
||||
<label for="wbc-log-level-filter"><?php esc_html_e( 'Filter by level:', 'woo-business-central' ); ?></label>
|
||||
<select id="wbc-log-level-filter" onchange="location = this.value;">
|
||||
<option value="<?php echo esc_url( remove_query_arg( 'log_level' ) ); ?>"><?php esc_html_e( 'All', 'woo-business-central' ); ?></option>
|
||||
<?php foreach ( array( 'DEBUG', 'INFO', 'WARNING', 'ERROR' ) as $level ) : ?>
|
||||
<option value="<?php echo esc_url( add_query_arg( 'log_level', $level ) ); ?>" <?php selected( $log_level, $level ); ?>>
|
||||
<?php echo esc_html( $level ); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<table class="wp-list-table widefat fixed striped wbc-logs-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="column-timestamp"><?php esc_html_e( 'Timestamp', 'woo-business-central' ); ?></th>
|
||||
<th class="column-level"><?php esc_html_e( 'Level', 'woo-business-central' ); ?></th>
|
||||
<th class="column-context"><?php esc_html_e( 'Context', 'woo-business-central' ); ?></th>
|
||||
<th class="column-message"><?php esc_html_e( 'Message', 'woo-business-central' ); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if ( empty( $logs ) ) : ?>
|
||||
<tr>
|
||||
<td colspan="4"><?php esc_html_e( 'No logs found.', 'woo-business-central' ); ?></td>
|
||||
</tr>
|
||||
<?php else : ?>
|
||||
<?php foreach ( $logs as $log ) : ?>
|
||||
<tr class="wbc-log-level-<?php echo esc_attr( strtolower( $log['level'] ) ); ?>">
|
||||
<td class="column-timestamp">
|
||||
<?php echo esc_html( wp_date( 'Y-m-d H:i:s', strtotime( $log['timestamp'] ) ) ); ?>
|
||||
</td>
|
||||
<td class="column-level">
|
||||
<span class="wbc-log-badge wbc-log-badge-<?php echo esc_attr( strtolower( $log['level'] ) ); ?>">
|
||||
<?php echo esc_html( $log['level'] ); ?>
|
||||
</span>
|
||||
</td>
|
||||
<td class="column-context"><?php echo esc_html( $log['context'] ); ?></td>
|
||||
<td class="column-message">
|
||||
<?php echo esc_html( $log['message'] ); ?>
|
||||
<?php if ( ! empty( $log['data'] ) ) : ?>
|
||||
<button type="button" class="button-link wbc-toggle-data"
|
||||
data-show-text="<?php esc_attr_e( 'Show data', 'woo-business-central' ); ?>"
|
||||
data-hide-text="<?php esc_attr_e( 'Hide data', 'woo-business-central' ); ?>">
|
||||
<?php esc_html_e( 'Show data', 'woo-business-central' ); ?>
|
||||
</button>
|
||||
<pre class="wbc-log-data" style="display: none;"><?php echo esc_html( $log['data'] ); ?></pre>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php if ( $total_pages > 1 ) : ?>
|
||||
<div class="tablenav bottom">
|
||||
<div class="tablenav-pages">
|
||||
<span class="displaying-num">
|
||||
<?php printf( esc_html__( '%d items', 'woo-business-central' ), $total ); ?>
|
||||
</span>
|
||||
<span class="pagination-links">
|
||||
<?php if ( $page_num > 1 ) : ?>
|
||||
<a class="prev-page button" href="<?php echo esc_url( add_query_arg( 'log_page', $page_num - 1 ) ); ?>">
|
||||
‹
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<span class="paging-input">
|
||||
<?php echo esc_html( $page_num ); ?> / <?php echo esc_html( $total_pages ); ?>
|
||||
</span>
|
||||
<?php if ( $page_num < $total_pages ) : ?>
|
||||
<a class="next-page button" href="<?php echo esc_url( add_query_arg( 'log_page', $page_num + 1 ) ); ?>">
|
||||
›
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user