Printful Integration for WooCommerce - Version 2.0.5

Version Description

Improvements to Printful shipping carrier settings

Download this release

Release Info

Developer printful
Plugin Icon 128x128 Printful Integration for WooCommerce
Version 2.0.5
Comparing to
See all releases

Code changes from version 2.0.4 to 2.0.5

assets/js/settings.js CHANGED
@@ -29,6 +29,8 @@ var Printful_Settings;
29
  if (response === 'OK') {
30
  pass.show(0).delay(3000).hide(0);
31
  } else {
 
 
32
  fail.show(0).delay(3000).hide(0);
33
  }
34
  }
29
  if (response === 'OK') {
30
  pass.show(0).delay(3000).hide(0);
31
  } else {
32
+ fail.empty();
33
+ fail.append('<span class="dashicons dashicons-no"></span>' + response);
34
  fail.show(0).delay(3000).hide(0);
35
  }
36
  }
includes/class-printful-admin-settings.php CHANGED
@@ -6,7 +6,14 @@ class Printful_Admin_Settings {
6
  public static $_instance;
7
  const CARRIER_TYPE_STANDARD = 'standard';
8
  const CARRIER_TYPE_EXPEDITED = 'expedited';
9
-
 
 
 
 
 
 
 
10
  public static function getIntegrationFields()
11
  {
12
  return array(
@@ -128,78 +135,57 @@ class Printful_Admin_Settings {
128
  return $carrier_settings;
129
  }
130
 
131
- $carriers = Printful_Carriers::instance()->carriers;
132
- if ( empty( $carriers ) ) {
 
133
  return false;
134
  }
135
 
136
- $standard = array();
137
- $expedited = array();
138
-
139
- foreach ( $carriers as $carrier ) {
140
-
141
- $item = array(
142
- 'title' => false,
143
- 'type' => 'checkbox',
144
- 'label' => $carrier['title'] . ' <i>' . $carrier['subtitle'] . '</i>',
145
- 'default' => 'yes',
146
- 'value' => ( $carrier['status'] == 'on' ? 'yes' : 'no' ),
147
- );
148
-
149
- if ( $carrier['type'] == self::CARRIER_TYPE_STANDARD ) {
150
- $standard[ $carrier['carrier_id'] ] = $item;
151
- } else {
152
- $expedited[ $carrier['carrier_id'] ] = $item;
153
- }
154
- }
155
-
156
- $carrier_settings['settings'][ self::CARRIER_TYPE_STANDARD ] = array(
157
- 'title' => 'Standard shipping',
158
- 'type' => 'checkbox-group',
159
- 'items' => $standard,
160
- );
161
-
162
- $carrier_settings['settings'][ self::CARRIER_TYPE_EXPEDITED ] = array(
163
- 'title' => 'Expedited shipping',
164
- 'type' => 'checkbox-group',
165
- 'items' => $expedited,
166
- );
167
 
168
  return $carrier_settings;
169
  }
170
 
171
  /**
172
  * Prepare carrier data for posting to Printful API
173
- * @return array
174
  */
175
  public function prepare_carriers() {
176
 
177
- $carriers = Printful_Carriers::instance()->carriers;
178
 
179
- if ( empty( $carriers ) ) {
180
  return false;
181
  }
182
 
183
- $standard = ( ( ! empty( $_POST[ self::CARRIER_TYPE_STANDARD ] ) && wp_verify_nonce( $_POST['_wpnonce'], 'printful_settings' ) ) ? $_POST[ self::CARRIER_TYPE_STANDARD ] : array() );
184
- $expedited = ( ( ! empty( $_POST[ self::CARRIER_TYPE_EXPEDITED ] ) && wp_verify_nonce( $_POST['_wpnonce'], 'printful_settings' ) ) ? $_POST[ self::CARRIER_TYPE_EXPEDITED ] : array() );
185
 
186
- $saved_carriers = array_merge( $standard, $expedited );
 
 
187
 
188
- if ( empty( $saved_carriers ) ) {
189
  return false;
190
  }
191
 
 
192
  $request_body = array();
193
 
194
- foreach ( $carriers as $carrier ) {
195
- $status = 'off';
196
- if ( in_array( $carrier['carrier_id'], $saved_carriers ) ) {
197
- $status = 'on';
 
 
 
 
 
 
 
 
 
198
  }
199
- $request_body[] = array(
200
- 'carrier_id' => $carrier['carrier_id'],
201
- 'status' => $status,
202
- );
203
  }
204
 
205
  return $request_body;
@@ -223,7 +209,9 @@ class Printful_Admin_Settings {
223
 
224
  if ( ! $result ) {
225
  die( 'Error: failed to save carriers' );
226
- }
 
 
227
  }
228
 
229
  $options = array();
@@ -250,4 +238,91 @@ class Printful_Admin_Settings {
250
  die('OK');
251
  }
252
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
253
  }
6
  public static $_instance;
7
  const CARRIER_TYPE_STANDARD = 'standard';
8
  const CARRIER_TYPE_EXPEDITED = 'expedited';
9
+ const CARRIER_TYPE_DOMESTIC = 'domestic';
10
+ const CARRIER_TYPE_INTERNATIONAL = 'international';
11
+ const CARRIER_REGION_US = 'US';
12
+ const CARRIER_REGION_EU = 'LV';
13
+
14
+ /**
15
+ * @return array
16
+ */
17
  public static function getIntegrationFields()
18
  {
19
  return array(
135
  return $carrier_settings;
136
  }
137
 
138
+ $carrier_regions = Printful_Carriers::instance()->carriers;
139
+
140
+ if ( empty( $carrier_regions ) ) {
141
  return false;
142
  }
143
 
144
+ $carrier_settings[ 'settings' ] = $this->prepare_form_data( $carrier_regions );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
  return $carrier_settings;
147
  }
148
 
149
  /**
150
  * Prepare carrier data for posting to Printful API
151
+ * @return array|bool
152
  */
153
  public function prepare_carriers() {
154
 
155
+ $carrier_regions = Printful_Carriers::instance()->carriers;
156
 
157
+ if ( empty( $carrier_regions ) ) {
158
  return false;
159
  }
160
 
161
+ $us_carriers[ self::CARRIER_REGION_US ] = ( ( ! empty( $_POST[ self::CARRIER_REGION_US ] ) && wp_verify_nonce( $_POST['_wpnonce'], 'printful_settings' ) ) ? $_POST[ self::CARRIER_REGION_US ] : array() );
162
+ $eu_carriers[ self::CARRIER_REGION_EU ] = ( ( ! empty( $_POST[ self::CARRIER_REGION_EU ] ) && wp_verify_nonce( $_POST['_wpnonce'], 'printful_settings' ) ) ? $_POST[ self::CARRIER_REGION_EU ] : array() );
163
 
164
+ $parsed_carriers = $this->parse_region_carriers( $us_carriers, $eu_carriers );
165
+ $us_carriers = $parsed_carriers['us_carriers'];
166
+ $eu_carriers = $parsed_carriers['eu_carriers'];
167
 
168
+ if ( empty( $us_carriers ) && empty( $eu_carriers ) ) {
169
  return false;
170
  }
171
 
172
+ $saved_carriers = array_merge( $us_carriers, $eu_carriers );
173
  $request_body = array();
174
 
175
+ foreach ( $carrier_regions as $region => $carrier_region ) {
176
+ foreach ( $carrier_region as $carrier_type => $carrier_methods ) {
177
+ foreach ( $carrier_methods as $carrier_method => $carrier_data ) {
178
+ $is_active = false;
179
+
180
+ if ( in_array( $carrier_method, $saved_carriers[ $region ][ $carrier_type ] ) ) {
181
+ $is_active = true;
182
+ }
183
+
184
+ $request_body[ $region ][ $carrier_type ][ $carrier_method ] = array(
185
+ 'isActive' => $is_active
186
+ );
187
+ }
188
  }
 
 
 
 
189
  }
190
 
191
  return $request_body;
209
 
210
  if ( ! $result ) {
211
  die( 'Error: failed to save carriers' );
212
+ } else if (isset($result['error'])) {
213
+ die ( $result['error']);
214
+ }
215
  }
216
 
217
  $options = array();
238
  die('OK');
239
  }
240
  }
241
+
242
+ /**
243
+ * @param array $carrier_regions
244
+ * @return array|bool
245
+ */
246
+ private function prepare_form_data( $carrier_regions )
247
+ {
248
+ $carrier_settings = array();
249
+
250
+ foreach ( $carrier_regions as $region => $carrier_types ) {
251
+ foreach ($carrier_types as $carrier_type => $carrier_data) {
252
+ foreach ($carrier_data as $key => $carrier) {
253
+ $carrier_item = array(
254
+ 'label' => $carrier[ 'label' ] . ' <i>' . $carrier[ 'subtitle' ] . '</i>',
255
+ 'value' => ( $carrier[ 'isActive' ] == true ? 'yes' : 'no' ),
256
+ );
257
+
258
+ $carrier_regions[ $region ][ $carrier_type ][ $carrier['key'] ] = $carrier_item;
259
+ }
260
+ }
261
+ }
262
+
263
+ $item_array = $this->format_carrier_items( $carrier_regions );
264
+
265
+ $carrier_settings[ self::CARRIER_REGION_US ] = array(
266
+ 'title' => __( 'International from USA', 'printful' ),
267
+ 'type' => 'checkbox-group',
268
+ 'items' => $item_array[ self::CARRIER_REGION_US ]
269
+ );
270
+
271
+ $carrier_settings[ self::CARRIER_REGION_EU ] = array(
272
+ 'title' => __( 'International from EU', 'printful' ),
273
+ 'type' => 'checkbox-group',
274
+ 'items' => $item_array[ self::CARRIER_REGION_EU ]
275
+ );
276
+
277
+ return $carrier_settings;
278
+ }
279
+
280
+ /**
281
+ * Prepare carrier item data for form
282
+ * @param array $carrier_regions
283
+ * @return array
284
+ */
285
+ private function format_carrier_items( $carrier_regions )
286
+ {
287
+ $item_array = array();
288
+
289
+ foreach ( $carrier_regions as $region => $carrier_types ) {
290
+ foreach ( $carrier_types as $carrier_type => $carrier_data ) {
291
+ $item_array[ $region ][ $carrier_type ] = array(
292
+ 'subtitle' => ucfirst($carrier_type) . ' shipping',
293
+ 'carriers' => $carrier_data
294
+ );
295
+ }
296
+ }
297
+
298
+ return array(
299
+ self::CARRIER_REGION_US => $item_array[ self::CARRIER_REGION_US ],
300
+ self::CARRIER_REGION_EU => $item_array[ self::CARRIER_REGION_EU ]
301
+ );
302
+ }
303
+
304
+ /**
305
+ * @param array $us_carriers
306
+ * @param array $eu_carriers
307
+ * @return array
308
+ */
309
+ private function parse_region_carriers( $us_carriers, $eu_carriers )
310
+ {
311
+ foreach ( $us_carriers[ self::CARRIER_REGION_US ] as $carrier_type => $carrier_methods ) {
312
+ foreach ( $carrier_methods as $key => $carrier_method ) {
313
+ $us_carriers[ self::CARRIER_REGION_US ][ $carrier_type ][ $key ] = str_replace( self::CARRIER_REGION_US . '_' . $carrier_type . '_', '', $carrier_method );
314
+ }
315
+ }
316
+
317
+ foreach ( $eu_carriers[ self::CARRIER_REGION_EU ] as $carrier_type => $carrier_methods ) {
318
+ foreach ( $carrier_methods as $key => $carrier_method ) {
319
+ $eu_carriers[ self::CARRIER_REGION_EU ][ $carrier_type ][ $key ] = str_replace( self::CARRIER_REGION_EU . '_' . $carrier_type.'_', '', $carrier_method );
320
+ }
321
+ }
322
+
323
+ return array(
324
+ 'eu_carriers' => $eu_carriers,
325
+ 'us_carriers' => $us_carriers
326
+ );
327
+ }
328
  }
includes/class-printful-admin-status.php CHANGED
@@ -113,6 +113,9 @@ class Printful_Admin_Status {
113
  * @return Printful_Admin_Status
114
  */
115
  public static function instance() {
 
 
 
116
 
117
  if ( is_null( self::$_instance ) ) {
118
  self::$_instance = new self();
113
  * @return Printful_Admin_Status
114
  */
115
  public static function instance() {
116
+ if ( ! function_exists( 'get_plugins' ) ) {
117
+ include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
118
+ }
119
 
120
  if ( is_null( self::$_instance ) ) {
121
  self::$_instance = new self();
includes/class-printful-carriers.php CHANGED
@@ -25,7 +25,6 @@ class Printful_Carriers {
25
  * @return mixed
26
  */
27
  public function get_carriers() {
28
-
29
  $carriers = get_transient( 'printful_carriers' );
30
  if ( ! $carriers ) {
31
  $carriers = $this->refresh_carriers();
@@ -41,7 +40,7 @@ class Printful_Carriers {
41
  public function refresh_carriers() {
42
 
43
  try {
44
- $carriers = Printful_Integration::instance()->get_client()->get( 'store/carriers-services' );
45
  $this->update_carrier_cache( $carriers );
46
  } catch (PrintfulApiException $e) {
47
  $carriers = array();
@@ -74,7 +73,7 @@ class Printful_Carriers {
74
 
75
  $shipping = new self;
76
  try {
77
- $carriers = Printful_Integration::instance()->get_client()->post( 'store/carriers-services', $data );
78
  $shipping->update_carrier_cache( $carriers );
79
  } catch ( PrintfulApiException $e ) {
80
  $carriers = false;
25
  * @return mixed
26
  */
27
  public function get_carriers() {
 
28
  $carriers = get_transient( 'printful_carriers' );
29
  if ( ! $carriers ) {
30
  $carriers = $this->refresh_carriers();
40
  public function refresh_carriers() {
41
 
42
  try {
43
+ $carriers = Printful_Integration::instance()->get_client()->get( 'store/get-shipping-methods' );
44
  $this->update_carrier_cache( $carriers );
45
  } catch (PrintfulApiException $e) {
46
  $carriers = array();
73
 
74
  $shipping = new self;
75
  try {
76
+ $carriers = Printful_Integration::instance()->get_client()->patch( 'store/update-shipping-methods', $data );
77
  $shipping->update_carrier_cache( $carriers );
78
  } catch ( PrintfulApiException $e ) {
79
  $carriers = false;
includes/class-printful-client.php CHANGED
@@ -98,6 +98,21 @@ class Printful_Client {
98
  return $this->request( 'PUT', $path, $params, $data );
99
  }
100
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  /**
102
  * Return raw response data from the last request
103
  * @return string|null Response data
98
  return $this->request( 'PUT', $path, $params, $data );
99
  }
100
 
101
+
102
+ /**
103
+ * Perform a PATCH request to the API
104
+ * @param string $path Request path
105
+ * @param array $data Request body data as an associative array
106
+ * @param array $params
107
+ * @return mixed API response
108
+ * @throws PrintfulApiException if the API call status code is not in the 2xx range
109
+ * @throws PrintfulException if the API call has failed or the response is invalid
110
+ */
111
+ public function patch( $path, $data = array(), $params = array() )
112
+ {
113
+ return $this->request( 'PATCH', $path, $params, $data );
114
+ }
115
+
116
  /**
117
  * Return raw response data from the last request
118
  * @return string|null Response data
includes/templates/setting-group.php CHANGED
@@ -1,3 +1,11 @@
 
 
 
 
 
 
 
 
1
  <div class="printful-setting-group">
2
 
3
  <h2><?php echo esc_html($title); ?></h2>
@@ -39,8 +47,22 @@
39
 
40
  <?php foreach ( $setting['items'] as $checkbox_key => $item ) : ?>
41
 
42
- <label for="<?php echo esc_attr($checkbox_key); ?>" class="checkbox-group-item">
43
- <input class="" type="checkbox" name="<?php echo esc_attr($key) . '[]'; ?>" id="<?php echo esc_attr($checkbox_key); ?>" value="<?php echo esc_attr($checkbox_key); ?>" <?php if ( $item['value'] == 'yes' ) {echo 'checked="checked"';} ?>><?php echo wp_kses_post( $item['label'] ); ?></label><br>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
  <?php endforeach; ?>
46
 
1
+ <?php
2
+ /**
3
+ * @var string $title
4
+ * @var string $description
5
+ * @var string $carrier_version
6
+ * @var array $settings
7
+ */
8
+ ?>
9
  <div class="printful-setting-group">
10
 
11
  <h2><?php echo esc_html($title); ?></h2>
47
 
48
  <?php foreach ( $setting['items'] as $checkbox_key => $item ) : ?>
49
 
50
+ <label class="carrier-type"><?php echo $item['subtitle']; ?></label>
51
+ <?php foreach ( $item['carriers'] as $carrier_name => $carrier ): ?>
52
+
53
+ <label for="<?php echo esc_attr( $key ) . '_' . esc_attr( $checkbox_key ).'_'.esc_attr( $carrier_name ); ?>" class="checkbox-group-item">
54
+ <input class="" type="checkbox" name="<?php echo esc_attr( $key ) . '[' . esc_attr( $checkbox_key ) . '][]'; ?>"
55
+ id="<?php echo esc_attr( $key ) . '_' .esc_attr( $checkbox_key ) . '_' . esc_attr( $carrier_name ); ?>"
56
+ value="<?php echo esc_attr( $key ) . '_' . esc_attr( $checkbox_key ) . '_' . esc_attr( $carrier_name ); ?>"
57
+ <?php if ( $carrier['value'] == 'yes' ) {
58
+ echo 'checked="checked"';
59
+ } ?>
60
+ >
61
+ <?php echo wp_kses_post( $carrier['label'] ); ?>
62
+ </label>
63
+ <br>
64
+
65
+ <?php endforeach; ?>
66
 
67
  <?php endforeach; ?>
68
 
includes/templates/setting-submit.php CHANGED
@@ -1,3 +1,8 @@
 
 
 
 
 
1
  <p class="printful-submit">
2
  <input name="save" class="button-primary woocommerce-save-button <?php if($disabled) { echo 'disabled'; } ?>" type="submit" value="<?php esc_attr_e('Save changes', 'printful'); ?>" <?php if($disabled) { echo 'disabled'; } ?>/>
3
  <input type="hidden" id="_wpnonce" name="_wpnonce" value="<?php echo esc_attr($nonce); ?>">
@@ -9,8 +14,6 @@
9
  <?php esc_html_e('Saved successfully', 'printful'); ?>
10
  </span>
11
  <span class="fail">
12
- <span class="dashicons dashicons-no"></span>
13
- <?php esc_html_e('Saving settings failed', 'printful'); ?>
14
  </span>
15
  </span>
16
  </p>
1
+ <?php
2
+ /**
3
+ * @var bool $disabled
4
+ */
5
+ ?>
6
  <p class="printful-submit">
7
  <input name="save" class="button-primary woocommerce-save-button <?php if($disabled) { echo 'disabled'; } ?>" type="submit" value="<?php esc_attr_e('Save changes', 'printful'); ?>" <?php if($disabled) { echo 'disabled'; } ?>/>
8
  <input type="hidden" id="_wpnonce" name="_wpnonce" value="<?php echo esc_attr($nonce); ?>">
14
  <?php esc_html_e('Saved successfully', 'printful'); ?>
15
  </span>
16
  <span class="fail">
 
 
17
  </span>
18
  </span>
19
  </p>
includes/templates/support-info.php CHANGED
@@ -4,7 +4,7 @@
4
  <h3><?php esc_html_e('Need help? Get in touch!', 'printful') ?></h3>
5
  <p><?php esc_html_e('Email, call, text, or visit us. We\'ve got support teams in the USA and Europe to make sure we\'re there when you need us.', 'printful') ?><br/><?php esc_html_e('We\'d love to hear from you.', 'printful') ?></p>
6
  <div class="btn-wrap">
7
- <a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>support" class="button button-primary button-large" target="_blank"><?php esc_html_e('Contact support', 'printful') ?></a>
8
  </div>
9
  </div>
10
 
4
  <h3><?php esc_html_e('Need help? Get in touch!', 'printful') ?></h3>
5
  <p><?php esc_html_e('Email, call, text, or visit us. We\'ve got support teams in the USA and Europe to make sure we\'re there when you need us.', 'printful') ?><br/><?php esc_html_e('We\'d love to hear from you.', 'printful') ?></p>
6
  <div class="btn-wrap">
7
+ <a href="<?php echo esc_url(Printful_Base::get_printful_host()); ?>contacts" class="button button-primary button-large" target="_blank"><?php esc_html_e('Contact support', 'printful') ?></a>
8
  </div>
9
  </div>
10
 
printful-shipping.php CHANGED
@@ -3,13 +3,13 @@
3
  Plugin Name: Printful Integration for WooCommerce
4
  Plugin URI: https://wordpress.org/plugins/printful-shipping-for-woocommerce/
5
  Description: Calculate correct shipping and tax rates for your Printful-Woocommerce integration.
6
- Version: 2.0.4
7
  Author: Printful
8
  Author URI: http://www.printful.com
9
  License: GPL2 http://www.gnu.org/licenses/gpl-2.0.html
10
  Text Domain: printful
11
  WC requires at least: 3.0.0
12
- WC tested up to: 3.4.3
13
  */
14
 
15
  if ( ! defined( 'ABSPATH' ) ) exit;
@@ -20,7 +20,7 @@ if ( ! defined( 'PF_PLUGIN_FILE' ) ) {
20
 
21
  class Printful_Base {
22
 
23
- const VERSION = '2.0.4';
24
  const PF_HOST = 'https://www.printful.com/';
25
  const PF_API_HOST = 'https://api.printful.com/';
26
 
3
  Plugin Name: Printful Integration for WooCommerce
4
  Plugin URI: https://wordpress.org/plugins/printful-shipping-for-woocommerce/
5
  Description: Calculate correct shipping and tax rates for your Printful-Woocommerce integration.
6
+ Version: 2.0.5
7
  Author: Printful
8
  Author URI: http://www.printful.com
9
  License: GPL2 http://www.gnu.org/licenses/gpl-2.0.html
10
  Text Domain: printful
11
  WC requires at least: 3.0.0
12
+ WC tested up to: 3.4.5
13
  */
14
 
15
  if ( ! defined( 'ABSPATH' ) ) exit;
20
 
21
  class Printful_Base {
22
 
23
+ const VERSION = '2.0.5';
24
  const PF_HOST = 'https://www.printful.com/';
25
  const PF_API_HOST = 'https://api.printful.com/';
26
 
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: girts_u, kievins, kberzins
3
  Tags: woocommerce, printful, drop shipping, shipping, shipping rates, fulfillment, printing, fedex, carriers, checkout, t-shirts
4
  Requires at least: 3.8
5
  Tested up to: 4.9
6
- Stable tag: 2.0.4
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -38,6 +38,9 @@ Go to https://www.printful.com/dashboard/store , select your WooCommerce store,
38
 
39
  == Upgrade Notice ==
40
 
 
 
 
41
  = 2.0.4 =
42
  Improvements and fixed various minor issues
43
 
@@ -100,6 +103,9 @@ First release
100
 
101
  == Changelog ==
102
 
 
 
 
103
  = 2.0.4 =
104
  * Improved support for older PHP versions
105
  * Other minor bug fixes
3
  Tags: woocommerce, printful, drop shipping, shipping, shipping rates, fulfillment, printing, fedex, carriers, checkout, t-shirts
4
  Requires at least: 3.8
5
  Tested up to: 4.9
6
+ Stable tag: 2.0.5
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
38
 
39
  == Upgrade Notice ==
40
 
41
+ = 2.0.5 =
42
+ Improvements to Printful shipping carrier settings
43
+
44
  = 2.0.4 =
45
  Improvements and fixed various minor issues
46
 
103
 
104
  == Changelog ==
105
 
106
+ = 2.0.5 =
107
+ * Improvements to Printful shipping carrier settings
108
+
109
  = 2.0.4 =
110
  * Improved support for older PHP versions
111
  * Other minor bug fixes