Skip to content

Commit 7556aae

Browse files
committedSep 11, 2024·
SP-940 Adding BitPay Support Package download button
1 parent 69609bb commit 7556aae

8 files changed

+279
-4
lines changed
 

‎.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ codeception.yml
44
logs/*.*
55
/.idea/
66
/vendor
7-
/build
7+
/build
8+
.phpunit.result.cache
9+
tests/.env
10+
tests/Unit/wp-config.php

‎BitPayLib/class-bitpaylogger.php

+6-2
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ class BitPayLogger {
1616

1717
public function execute( $msg, string $type, bool $is_array = false, $error = false ): void {
1818
$bitpay_checkout_options = get_option( 'woocommerce_bitpay_checkout_gateway_settings' );
19-
$log_directory = plugin_dir_path( __FILE__ ) . '..' . DIRECTORY_SEPARATOR . '..'
20-
. DIRECTORY_SEPARATOR . 'logs/';
19+
$log_directory = $this->get_log_directory();
2120
if ( ! file_exists( $log_directory ) && ! mkdir( $log_directory ) && ! is_dir( $log_directory ) ) {
2221
throw new \RuntimeException( sprintf( 'Directory "%s" was not created', esc_html( $log_directory ) ) );
2322
}
@@ -47,4 +46,9 @@ public function execute( $msg, string $type, bool $is_array = false, $error = fa
4746
}
4847
// @codingStandardsIgnoreEnd
4948
}
49+
50+
public function get_log_directory(): string {
51+
return plugin_dir_path( __FILE__ ) . '..' . DIRECTORY_SEPARATOR . '..'
52+
. DIRECTORY_SEPARATOR . 'logs/';
53+
}
5054
}

‎BitPayLib/class-bitpaypluginsetup.php

+16
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ class BitPayPluginSetup {
2525
private BitPayPaymentSettings $bitpay_payment_settings;
2626
private BitPayInvoiceCreate $bitpay_invoice_create;
2727
private BitPayCheckoutTransactions $bitpay_checkout_transactions;
28+
private BitPaySupportPackage $bitpay_support_package;
2829

2930
public function __construct() {
3031
$this->bitpay_payment_settings = new BitPayPaymentSettings();
@@ -42,6 +43,10 @@ public function __construct() {
4243
$wordpress_helper,
4344
$logger
4445
);
46+
$this->bitpay_support_package = new BitPaySupportPackage(
47+
$wordpress_helper,
48+
$logger
49+
);
4550
}
4651

4752
public function execute(): void {
@@ -82,6 +87,17 @@ function () {
8287
'permission_callback' => '__return_true',
8388
)
8489
);
90+
register_rest_route(
91+
'bitpay/site',
92+
'/health-status',
93+
array(
94+
'methods' => 'GET',
95+
'callback' => array( $this->bitpay_support_package, 'get_zip' ),
96+
'permission_callback' => function () {
97+
return current_user_can( 'manage_woocommerce' );
98+
},
99+
)
100+
);
85101
}
86102
);
87103
}
+198
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace BitPayLib;
6+
7+
use WP_HTTP_Response;
8+
use WP_REST_Response;
9+
10+
class BitPaySupportPackage {
11+
12+
private BitPayWordpressHelper $bitpay_wordpress;
13+
private BitPayLogger $bitpay_logger;
14+
15+
public function __construct(
16+
BitPayWordpressHelper $bitpay_wordpress,
17+
BitPayLogger $bitpay_logger
18+
) {
19+
$this->bitpay_wordpress = $bitpay_wordpress;
20+
$this->bitpay_logger = $bitpay_logger;
21+
}
22+
23+
public function get_zip(): WP_REST_Response {
24+
$zipfile_string = $this->create_site_info_zip();
25+
26+
return $this->get_zip_rest_response( $zipfile_string );
27+
}
28+
29+
private function create_site_info_zip(): string {
30+
$json_data = $this->get_site_data_as_json();
31+
$tmp_file = tmpfile();
32+
$tmp_location = stream_get_meta_data( $tmp_file )['uri'];
33+
34+
$zip = new \ZipArchive();
35+
36+
if ( true !== $zip->open( $tmp_location, \ZipArchive::CREATE ) ) {
37+
throw new \RuntimeException( 'Could not create zip file' );
38+
}
39+
40+
$zip->addFromString( 'site-info.json', $json_data );
41+
$log_directory = $this->bitpay_logger->get_log_directory();
42+
43+
if ( is_readable( $log_directory ) ) {
44+
$zip->addGlob(
45+
$log_directory . '*.log',
46+
0,
47+
array(
48+
'remove_all_path' => true,
49+
)
50+
);
51+
}
52+
53+
$zip->close();
54+
$file_contents = file_get_contents( $tmp_location );
55+
56+
// This removes the file.
57+
fclose( $tmp_file );
58+
59+
return $file_contents;
60+
}
61+
62+
private function get_site_data_as_json(): bool|string {
63+
$active_plugins = get_plugins();
64+
$active_plugin_data = array();
65+
66+
foreach ( $active_plugins as $plugin_path => $plugin_data ) {
67+
if ( is_plugin_active( $plugin_path ) ) {
68+
$active_plugin_data[] = array(
69+
'name' => $plugin_data['Name'],
70+
'version' => $plugin_data['Version'],
71+
);
72+
}
73+
}
74+
75+
$wpdb = $this->bitpay_wordpress->get_wpdb();
76+
$extension = null;
77+
78+
// Populate the database debug fields.
79+
if ( is_object( $wpdb->dbh ) ) {
80+
// mysqli or PDO.
81+
$extension = get_class( $wpdb->dbh );
82+
}
83+
84+
$json_data = array(
85+
'bitpay_plugin_version' => BitPayPluginSetup::VERSION,
86+
'plugins' => $active_plugin_data,
87+
'database' => array(
88+
array(
89+
'dbms' => $extension,
90+
'dbms_version' => $wpdb->get_var( 'SELECT VERSION()' ), // phpcs:ignore
91+
'char_set' => $wpdb->charset,
92+
'collation' => $wpdb->collate,
93+
'tables' => array(
94+
array(
95+
'table' => '_bitpay_checkout_transactions',
96+
'exists' => $wpdb->get_var( "SHOW TABLES LIKE '{$wpdb->prefix}_bitpay_checkout_transactions'" ) ? 'yes' : 'no',
97+
),
98+
),
99+
),
100+
),
101+
'wordpress' => array(
102+
array(
103+
'url' => home_url(),
104+
'version' => get_bloginfo( 'version' ),
105+
),
106+
),
107+
'server' => array(
108+
array(
109+
'software' => $_SERVER['SERVER_SOFTWARE'], // phpcs:ignore
110+
'document_root' => $_SERVER['DOCUMENT_ROOT'], // phpcs:ignore
111+
),
112+
),
113+
'php' => array(
114+
'version' => phpversion(),
115+
'memory_limit' => ini_get( 'memory_limit' ),
116+
'max_execution_time' => ini_get( 'max_execution_time' ),
117+
'max_file_upload_size' => ini_get( 'upload_max_filesize' ),
118+
'max_post_size' => ini_get( 'post_max_size' ),
119+
'max_input_variables' => ini_get( 'max_input_vars' ),
120+
'curl_enabled' => function_exists( 'curl_version' ) ? 'yes' : 'no',
121+
'curl_version' => function_exists( 'curl_version' ) ? curl_version()['version'] : '',
122+
'openssl_version' => defined( 'OPENSSL_VERSION_TEXT' ) ? OPENSSL_VERSION_TEXT : '',
123+
'mcrypt_enabled' => function_exists( 'mcrypt_encrypt' ) ? 'yes' : 'no',
124+
'mbstring_enabled' => function_exists( 'mb_detect_encoding' ) ? 'yes' : 'no',
125+
'extensions' => get_loaded_extensions(),
126+
),
127+
);
128+
129+
return json_encode( $json_data, JSON_PRETTY_PRINT );
130+
}
131+
132+
/**
133+
* Serves a zip via the REST endpoint.
134+
*
135+
* By default, every REST response is passed through json_encode(), as the
136+
* typical REST response contains JSON data.
137+
*
138+
* This method hooks into the REST server to return a binary zip.
139+
*
140+
* @param string $data Data of the ZIP to serve.
141+
*
142+
* @return WP_REST_Response The REST response object to serve the zip.
143+
*/
144+
private function get_zip_rest_response( string $data ): WP_REST_Response {
145+
$response = new WP_REST_Response();
146+
147+
$response->set_data( $data );
148+
$response->set_headers(
149+
array(
150+
'Content-Type' => 'application/zip',
151+
'Content-Length' => strlen( $data ),
152+
)
153+
);
154+
155+
// This filter will return our binary zip.
156+
add_filter( 'rest_pre_serve_request', array( $this, 'serve_zip_action_handler' ), 0, 2 );
157+
158+
return $response;
159+
}
160+
161+
/**
162+
* Action handler that is used by `get_zip_rest_response()` to serve a binary image
163+
* instead of a JSON string.
164+
*
165+
* @param bool $served Whether the request has already been served. Default false.
166+
* @param WP_HTTP_Response $result Result to send to the client. Usually a WP_REST_Response.
167+
*
168+
* @return bool Returns true, if the image was served; this will skip the
169+
* default REST response logic.
170+
*
171+
* @see https://developer.wordpress.org/reference/hooks/rest_pre_serve_request/
172+
* */
173+
public function serve_zip_action_handler( bool $served, WP_HTTP_Response $result ): bool {
174+
$is_zip = false;
175+
$zip_data = null;
176+
177+
// Check the "Content-Type" header to confirm that we really want to return
178+
// binary zip data.
179+
foreach ( $result->get_headers() as $header => $value ) {
180+
if ( 'content-type' === strtolower( $header ) ) {
181+
$is_zip = 0 === strpos( $value, 'application/zip' );
182+
$zip_data = $result->get_data();
183+
break;
184+
}
185+
}
186+
187+
// Output the binary data and tell the REST server to not send any other
188+
// details (via "return true").
189+
if ( $is_zip && is_string( $zip_data ) ) {
190+
// phpcs:ignore
191+
echo $zip_data;
192+
193+
return true;
194+
}
195+
196+
return $served;
197+
}
198+
}

‎BitPayLib/class-wcgatewaybitpay.php

+20
Original file line numberDiff line numberDiff line change
@@ -344,4 +344,24 @@ private function get_bitpay_version_info(): string {
344344

345345
return $plugin_name . ' ' . $plugin_data['Version'];
346346
}
347+
348+
public function admin_options(): void {
349+
parent::admin_options();
350+
$this->add_support_package_download_button();
351+
}
352+
353+
private function add_support_package_download_button() {
354+
?>
355+
<div style="display: flex; align-items: center">
356+
<div style="padding-right: 24px;">
357+
<p style="color: #1d2327; font-weight: 600; font-size: 14px; padding: 20px 10px 20px 0; width: 200px">Support Package </p>
358+
</div>
359+
<div style="padding: 15px 0; margin-top: 9px;">
360+
<button type="button" style="height: 36px;" class="button button-secondary" id="download_support_package">Download</button>
361+
<p style="margin-top: 2px; margin-bottom: 0;">Select to download a package of files that can be used for technical support. No personal
362+
information will be captured.</p>
363+
</div>
364+
</div>
365+
<?php
366+
}
347367
}

‎js/wc_gateway_bitpay.js

+25
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,30 @@ jQuery( document ).ready(
1717
dark.html( '<img src="' + url + '" style="background-color: black"/>' );
1818
}
1919
)
20+
21+
function downloadZipFile(blob, name) {
22+
const a = document.createElement( 'a' );
23+
a.href = URL.createObjectURL( blob );
24+
a.download = name;
25+
a.click();
26+
}
27+
28+
document.getElementById( 'download_support_package' ).addEventListener(
29+
'click',
30+
async function () {
31+
const nonce = document.getElementById( '_wpnonce' );
32+
wp.apiFetch.use( wp.apiFetch.createNonceMiddleware( nonce ) );
33+
34+
const response = await wp.apiFetch(
35+
{
36+
path: '/bitpay/site/health-status',
37+
parse: false
38+
}
39+
);
40+
const blob = await response.blob();
41+
42+
downloadZipFile( blob, 'bitpay-support-package.zip' );
43+
}
44+
);
2045
}
2146
);

‎phpcs.xml

+8-1
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,11 @@
1010
<exclude name="WordPress.WP.AlternativeFunctions"/>
1111
<exclude name="WordPress.DateTime.RestrictedFunctions"/>
1212
</rule>
13-
</ruleset>
13+
<rule ref="WordPress.WP.Capabilities">
14+
<properties>
15+
<property name="custom_capabilities" type="array">
16+
<element value="manage_woocommerce" />
17+
</property>
18+
</properties>
19+
</rule>
20+
</ruleset>

‎scoper.inc.php

+2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ static function (string $filePath, string $prefix, string $contents): string {
3939
'WP_User',
4040
'WC_Order',
4141
'WP_REST_Request',
42+
'WP_Rest_Response',
43+
'WP_Http_Response',
4244
'WC_Admin_Settings',
4345
'Automattic\WooCommerce\Blocks\Package',
4446
'Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry',

0 commit comments

Comments
 (0)
Please sign in to comment.