AfterShip – WooCommerce Tracking - Version 1.2.6

Version Description

  • Fix installation problem, php array
Download this release

Release Info

Developer aftership
Plugin Icon 128x128 AfterShip – WooCommerce Tracking
Version 1.2.6
Comparing to
See all releases

Code changes from version 1.2.5 to 1.2.6

aftership.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: AfterShip - WooCommerce Tracking
4
  Plugin URI: http://aftership.com/
5
  Description: Add tracking number and carrier name to WooCommerce, display tracking info at order history page, auto import tracking numbers to AfterShip.
6
- Version: 1.2.5
7
  Author: AfterShip
8
  Author URI: http://aftership.com
9
 
3
  Plugin Name: AfterShip - WooCommerce Tracking
4
  Plugin URI: http://aftership.com/
5
  Description: Add tracking number and carrier name to WooCommerce, display tracking info at order history page, auto import tracking numbers to AfterShip.
6
+ Version: 1.2.6
7
  Author: AfterShip
8
  Author URI: http://aftership.com
9
 
class-aftership-settings.php CHANGED
@@ -181,7 +181,7 @@ class AfterShip_Settings
181
 
182
  public function couriers_callback(){
183
 
184
- $couriers = [];
185
  if (isset($this->options['couriers'])){
186
  $couriers = explode(',', $this->options['couriers']);
187
  }
181
 
182
  public function couriers_callback(){
183
 
184
+ $couriers = array();
185
  if (isset($this->options['couriers'])){
186
  $couriers = explode(',', $this->options['couriers']);
187
  }
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.aftership.com/
4
  Tags: shipping, tracking, ups, usps, fedex, dhl, tnt, dpd, post, shipment, woocommerce, tracking number, aftership, package tracking, fulfilment, tracking link, carrier, courier, woo commerce, woocommerce shipment tracking, shipping details plugin, widget, shipstation, track, package
5
  Requires at least: 2.9
6
  Tested up to: 4.0
7
- Stable tag: 1.2.5
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -95,6 +95,9 @@ You'll find the FAQ on [AfterShip.com](https://aftership.uservoice.com/knowledge
95
 
96
  == Changelog ==
97
 
 
 
 
98
  = 1.2.5 =
99
  * No longer require to input AfterShip API key at Wordpress
100
  * Allow carrier selection at Wordpress
4
  Tags: shipping, tracking, ups, usps, fedex, dhl, tnt, dpd, post, shipment, woocommerce, tracking number, aftership, package tracking, fulfilment, tracking link, carrier, courier, woo commerce, woocommerce shipment tracking, shipping details plugin, widget, shipstation, track, package
5
  Requires at least: 2.9
6
  Tested up to: 4.0
7
+ Stable tag: 1.2.6
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
95
 
96
  == Changelog ==
97
 
98
+ = 1.2.6 =
99
+ * Fix installation problem, php array
100
+
101
  = 1.2.5 =
102
  * No longer require to input AfterShip API key at Wordpress
103
  * Allow carrier selection at Wordpress
trunk/aftership.php ADDED
@@ -0,0 +1,474 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: AfterShip - WooCommerce Tracking
4
+ Plugin URI: http://aftership.com/
5
+ Description: Add tracking number and carrier name to WooCommerce, display tracking info at order history page, auto import tracking numbers to AfterShip.
6
+ Version: 1.2.6
7
+ Author: AfterShip
8
+ Author URI: http://aftership.com
9
+
10
+ Copyright: © AfterShip
11
+ */
12
+
13
+ /**
14
+ * Security Note
15
+ */
16
+ defined('ABSPATH') or die("No script kiddies please!");
17
+
18
+ /**
19
+ * Required functions
20
+ */
21
+ if (!function_exists('is_woocommerce_active'))
22
+ require_once('woo-includes/woo-functions.php');
23
+
24
+
25
+ /**
26
+ * Plugin updates
27
+ */
28
+
29
+ if (is_woocommerce_active()) {
30
+
31
+ /**
32
+ * AfterShip class
33
+ */
34
+ if (!class_exists('AfterShip')) {
35
+
36
+ final class AfterShip
37
+ {
38
+
39
+ protected static $_instance = null;
40
+
41
+ public static function instance()
42
+ {
43
+ if (is_null(self::$_instance)) {
44
+ self::$_instance = new self();
45
+ }
46
+ return self::$_instance;
47
+ }
48
+
49
+
50
+ /**
51
+ * Constructor
52
+ */
53
+ public function __construct()
54
+ {
55
+ $this->includes();
56
+
57
+ $this->api = new AfterShip_API();
58
+
59
+ $options = get_option('aftership_option_name');
60
+ if ($options) {
61
+
62
+ if (isset($options['plugin'])) {
63
+ $plugin = $options['plugin'];
64
+ if ($plugin == 'aftership') {
65
+ add_action('admin_print_scripts', array(&$this, 'library_scripts'));
66
+ add_action('admin_print_styles', array(&$this, 'admin_styles'));
67
+ add_action('add_meta_boxes', array(&$this, 'add_meta_box'));
68
+ add_action('woocommerce_process_shop_order_meta', array(&$this, 'save_meta_box'), 0, 2);
69
+ add_action('plugins_loaded', array($this, 'load_plugin_textdomain'));
70
+
71
+ $this->couriers = $options['couriers'];
72
+ }
73
+
74
+ // View Order Page
75
+ $this->plugin = $plugin;
76
+ }else{
77
+ $this->plugin = '';
78
+ }
79
+
80
+ if (isset($options['use_track_button'])) {
81
+ $this->use_track_button = $options['use_track_button'];
82
+ }else{
83
+ $this->use_track_button = false;
84
+ }
85
+
86
+ add_action('woocommerce_view_order', array(&$this, 'display_tracking_info'));
87
+ add_action('woocommerce_email_before_order_table', array(&$this, 'email_display'));
88
+
89
+ }
90
+
91
+ // user profile api key
92
+ add_action('show_user_profile', array($this, 'add_api_key_field'));
93
+ add_action('edit_user_profile', array($this, 'add_api_key_field'));
94
+ add_action('personal_options_update', array($this, 'generate_api_key'));
95
+ add_action('edit_user_profile_update', array($this, 'generate_api_key'));
96
+
97
+ register_activation_hook(__FILE__, array($this, 'install'));
98
+ }
99
+
100
+ public function install()
101
+ {
102
+ global $wp_roles;
103
+
104
+ if (class_exists('WP_Roles')) {
105
+ if (!isset($wp_roles)) {
106
+ $wp_roles = new WP_Roles();
107
+ }
108
+ }
109
+
110
+ if (is_object($wp_roles)) {
111
+ $wp_roles->add_cap('administrator', 'manage_aftership');
112
+ }
113
+ }
114
+
115
+ private function includes()
116
+ {
117
+ include_once('class-aftership-api.php');
118
+ include_once('class-aftership-settings.php');
119
+ }
120
+
121
+ /**
122
+ * Localisation
123
+ */
124
+ public function load_plugin_textdomain()
125
+ {
126
+ load_plugin_textdomain('aftership', false, dirname(plugin_basename(__FILE__)) . '/languages/');
127
+ }
128
+
129
+ public function admin_styles()
130
+ {
131
+ wp_enqueue_style('aftership_styles_chosen', plugins_url(basename(dirname(__FILE__))) . '/assets/plugin/chosen/chosen.min.css');
132
+ wp_enqueue_style('aftership_styles', plugins_url(basename(dirname(__FILE__))) . '/assets/css/admin.css');
133
+ }
134
+
135
+ public function library_scripts()
136
+ {
137
+ wp_enqueue_script('aftership_styles_chosen_jquery', plugins_url(basename(dirname(__FILE__))) . '/assets/plugin/chosen/chosen.jquery.min.js');
138
+ wp_enqueue_script('aftership_styles_chosen_proto', plugins_url(basename(dirname(__FILE__))) . '/assets/plugin/chosen/chosen.proto.min.js');
139
+ wp_enqueue_script('aftership_script_util', plugins_url(basename(dirname(__FILE__))) . '/assets/js/util.js');
140
+ wp_enqueue_script('aftership_script_couriers', plugins_url(basename(dirname(__FILE__))) . '/assets/js/couriers.js');
141
+ wp_enqueue_script('aftership_script_admin', plugins_url(basename(dirname(__FILE__))) . '/assets/js/admin.js');
142
+ }
143
+
144
+ /**
145
+ * Add the meta box for shipment info on the order page
146
+ *
147
+ * @access public
148
+ */
149
+ public function add_meta_box()
150
+ {
151
+ add_meta_box('woocommerce-aftership', __('AfterShip', 'wc_aftership'), array(&$this, 'meta_box'), 'shop_order', 'side', 'high');
152
+ }
153
+
154
+ /**
155
+ * Show the meta box for shipment info on the order page
156
+ *
157
+ * @access public
158
+ */
159
+ public function meta_box()
160
+ {
161
+
162
+ // just draw the layout, no data
163
+ global $post;
164
+
165
+ $selected_provider = get_post_meta($post->ID, '_aftership_tracking_provider', true);
166
+
167
+ echo '<div id="aftership_wrapper">';
168
+
169
+ echo '<p class="form-field"><label for="aftership_tracking_provider">' . __('Carrier:', 'wc_aftership') . '</label><br/><select id="aftership_tracking_provider" name="aftership_tracking_provider" class="chosen_select" style="width:100%">';
170
+ if ($selected_provider == '') {
171
+ $selected_text = 'selected="selected"';
172
+ } else {
173
+ $selected_text = '';
174
+ }
175
+ echo '<option disabled ' . $selected_text . ' value="">Please Select</option>';
176
+ echo '</select>';
177
+ echo '<br><a href="options-general.php?page=aftership-setting-admin">Update carrier list</a>';
178
+ echo '<input type="hidden" id="aftership_tracking_provider_hidden" value="' . $selected_provider . '"/>';
179
+ echo '<input type="hidden" id="aftership_couriers_selected" value="' . $this->couriers . '"/>';
180
+
181
+
182
+
183
+ woocommerce_wp_text_input(array(
184
+ 'id' => 'aftership_tracking_provider_name',
185
+ 'label' => __('', 'wc_aftership'),
186
+ 'placeholder' => '',
187
+ 'description' => '',
188
+ 'class' => 'hidden',
189
+ 'value' => get_post_meta($post->ID, '_aftership_tracking_provider_name', true),
190
+ ));
191
+
192
+ woocommerce_wp_text_input(array(
193
+ 'id' => 'aftership_tracking_required_fields',
194
+ 'label' => __('', 'wc_aftership'),
195
+ 'placeholder' => '',
196
+ 'description' => '',
197
+ 'class' => 'hidden',
198
+ 'value' => get_post_meta($post->ID, '_aftership_tracking_required_fields', true),
199
+ ));
200
+
201
+ woocommerce_wp_text_input(array(
202
+ 'id' => 'aftership_tracking_number',
203
+ 'label' => __('Tracking number:', 'wc_aftership'),
204
+ 'placeholder' => '',
205
+ 'description' => '',
206
+ 'value' => get_post_meta($post->ID, '_aftership_tracking_number', true),
207
+ ));
208
+
209
+ woocommerce_wp_text_input(array(
210
+ 'id' => 'aftership_tracking_shipdate',
211
+ 'label' => __('Date shipped:', 'wc_aftership'),
212
+ 'placeholder' => 'YYYY-MM-DD',
213
+ 'description' => '',
214
+ 'class' => 'date-picker-field hidden-field',
215
+ 'value' => ($date = get_post_meta($post->ID, '_aftership_tracking_shipdate', true)) ? date('Y-m-d', $date) : ''
216
+ ));
217
+
218
+ woocommerce_wp_text_input(array(
219
+ 'id' => 'aftership_tracking_postal',
220
+ 'label' => __('Postal Code:', 'wc_aftership'),
221
+ 'placeholder' => '',
222
+ 'description' => '',
223
+ 'class' => 'hidden-field',
224
+ 'value' => get_post_meta($post->ID, '_aftership_tracking_postal', true),
225
+ ));
226
+
227
+ woocommerce_wp_text_input(array(
228
+ 'id' => 'aftership_tracking_account',
229
+ 'label' => __('Account name:', 'wc_aftership'),
230
+ 'placeholder' => '',
231
+ 'description' => '',
232
+ 'class' => 'hidden-field',
233
+ 'value' => get_post_meta($post->ID, '_aftership_tracking_account', true),
234
+ ));
235
+
236
+ echo '</div>';
237
+ }
238
+
239
+ /**
240
+ * Order Downloads Save
241
+ *
242
+ * Function for processing and storing all order downloads.
243
+ */
244
+ public function save_meta_box($post_id, $post)
245
+ {
246
+ if (isset($_POST['aftership_tracking_number'])) {
247
+
248
+ // Download data
249
+ $tracking_provider = woocommerce_clean($_POST['aftership_tracking_provider']);
250
+ $tracking_number = woocommerce_clean($_POST['aftership_tracking_number']);
251
+ $tracking_provider_name = woocommerce_clean($_POST['aftership_tracking_provider_name']);
252
+ $tracking_required_fields = woocommerce_clean($_POST['aftership_tracking_required_fields']);
253
+ $shipdate = woocommerce_clean(strtotime($_POST['aftership_tracking_shipdate']));
254
+ $postal = woocommerce_clean($_POST['aftership_tracking_postal']);
255
+ $account = woocommerce_clean($_POST['aftership_tracking_account']);
256
+
257
+ // Update order data
258
+ update_post_meta($post_id, '_aftership_tracking_provider', $tracking_provider);
259
+ update_post_meta($post_id, '_aftership_tracking_number', $tracking_number);
260
+ update_post_meta($post_id, '_aftership_tracking_provider_name', $tracking_provider_name);
261
+ update_post_meta($post_id, '_aftership_tracking_required_fields', $tracking_required_fields);
262
+ update_post_meta($post_id, '_aftership_tracking_shipdate', $shipdate);
263
+ update_post_meta($post_id, '_aftership_tracking_postal', $postal);
264
+ update_post_meta($post_id, '_aftership_tracking_account', $account);
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Display the API key info for a user
270
+ *
271
+ * @since 2.1
272
+ * @param WP_User $user
273
+ */
274
+ public function add_api_key_field($user)
275
+ {
276
+
277
+ if (!current_user_can('manage_aftership'))
278
+ return;
279
+
280
+ if (current_user_can('edit_user', $user->ID)) {
281
+ ?>
282
+ <h3>AfterShip</h3>
283
+ <table class="form-table">
284
+ <tbody>
285
+ <tr>
286
+ <th><label
287
+ for="aftership_wp_api_key"><?php _e('AfterShip\'s WordPress API Key', 'aftership'); ?></label>
288
+ </th>
289
+ <td>
290
+ <?php if (empty($user->aftership_wp_api_key)) : ?>
291
+ <input name="aftership_wp_generate_api_key" type="checkbox"
292
+ id="aftership_wp_generate_api_key" value="0"/>
293
+ <span class="description"><?php _e('Generate API Key', 'aftership'); ?></span>
294
+ <?php else : ?>
295
+ <code id="aftership_wp_api_key"><?php echo $user->aftership_wp_api_key ?></code>
296
+ <br/>
297
+ <input name="aftership_wp_generate_api_key" type="checkbox"
298
+ id="aftership_wp_generate_api_key" value="0"/>
299
+ <span class="description"><?php _e('Revoke API Key', 'aftership'); ?></span>
300
+ <?php endif; ?>
301
+ </td>
302
+ </tr>
303
+ </tbody>
304
+ </table>
305
+ <?php
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Generate and save (or delete) the API keys for a user
311
+ *
312
+ * @since 2.1
313
+ * @param int $user_id
314
+ */
315
+ public function generate_api_key($user_id)
316
+ {
317
+
318
+ if (current_user_can('edit_user', $user_id)) {
319
+
320
+ $user = get_userdata($user_id);
321
+
322
+ // creating/deleting key
323
+ if (isset($_POST['aftership_wp_generate_api_key'])) {
324
+
325
+ // consumer key
326
+ if (empty($user->aftership_wp_api_key)) {
327
+
328
+ $api_key = 'ck_' . hash('md5', $user->user_login . date('U') . mt_rand());
329
+
330
+ update_user_meta($user_id, 'aftership_wp_api_key', $api_key);
331
+
332
+ } else {
333
+
334
+ delete_user_meta($user_id, 'aftership_wp_api_key');
335
+ }
336
+
337
+ }
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Display Shipment info in the frontend (order view/tracking page).
343
+ *
344
+ * @access public
345
+ */
346
+ function display_tracking_info($order_id, $for_email = false)
347
+ {
348
+ if ($this->plugin == 'aftership') {
349
+ $this->display_order_aftership($order_id, $for_email);
350
+ } else if ($this->plugin == 'wc-shipment-tracking') { //$49
351
+ $this->display_order_wc_shipment_tracking($order_id, $for_email);
352
+ }
353
+ }
354
+
355
+ private function display_order_aftership($order_id, $for_email)
356
+ {
357
+ $tracking_provider = get_post_meta($order_id, '_aftership_tracking_provider', true);
358
+ $tracking_number = get_post_meta($order_id, '_aftership_tracking_number', true);
359
+ $tracking_provider_name = get_post_meta($order_id, '_aftership_tracking_provider_name', true);
360
+ $tracking_required_fields = get_post_meta($order_id, '_aftership_tracking_required_fields', true);
361
+ $date_shipped = get_post_meta($order_id, '_aftership_tracking_shipdate', true);
362
+ $postcode = get_post_meta($order_id, '_aftership_tracking_postal', true);
363
+ $account = get_post_meta($order_id, '_aftership_tracking_account', true);
364
+
365
+ if (!$tracking_provider)
366
+ return;
367
+
368
+ if (!$tracking_number)
369
+ return;
370
+
371
+ $provider_name = $tracking_provider_name;
372
+ $provider_required_fields = explode(",", $tracking_required_fields);
373
+
374
+ $date_shipped_str = '';
375
+ $postcode_str = '';
376
+ $account_str = '';
377
+
378
+ foreach ($provider_required_fields as $field) {
379
+ if ($field == 'tracking_ship_date') {
380
+ if ($date_shipped) {
381
+ $date_shipped_str = '&nbsp;' . sprintf(__('on %s', 'wc_shipment_tracking'), date_i18n(__('l jS F Y', 'wc_shipment_tracking'), $date_shipped));
382
+ }
383
+ } else if ($field == 'tracking_postal_code') {
384
+ if ($postcode) {
385
+ $postcode_str = '&nbsp;' . sprintf('The postal code is %s.', $postcode);
386
+ }
387
+ } else if ($field == 'tracking_account_number') {
388
+ if ($account) {
389
+ $account_str = '&nbsp;' . sprintf('The account is %s.', $account);
390
+ }
391
+ }
392
+ }
393
+
394
+ $provider_name = '&nbsp;' . __('via', 'wc_shipment_tracking') . ' <strong>' . $provider_name . '</strong>';
395
+
396
+ echo wpautop(sprintf(__('Your order was shipped%s%s. Tracking number is %s.%s%s', 'wc_shipment_tracking'), $date_shipped_str, $provider_name, $tracking_number, $postcode_str, $account_str));
397
+
398
+ if (!$for_email && $this->use_track_button) {
399
+ $this->display_track_button($tracking_provider, $tracking_number);
400
+ }
401
+
402
+ }
403
+
404
+ private function display_order_wc_shipment_tracking($order_id, $for_email)
405
+ {
406
+ if ($for_email || !$this->use_track_button) {
407
+ return;
408
+ }
409
+
410
+ $tracking = get_post_meta($order_id, '_tracking_number', true);
411
+ $sharp = strpos($tracking, '#');
412
+ $colon = strpos($tracking, ':');
413
+ if ($sharp && $colon && $sharp >= $colon) {
414
+ return;
415
+ } else if (!$sharp && $colon) {
416
+ return;
417
+ } else if ($sharp) {
418
+ $tracking_provider = substr($tracking, 0, $sharp);
419
+ if ($colon) {
420
+ $tracking_number = substr($tracking, $sharp + 1, $colon - $sharp - 1);
421
+ } else {
422
+ $tracking_number = substr($tracking, $sharp + 1, strlen($tracking));
423
+ }
424
+ } else {
425
+ $tracking_provider = '';
426
+ $tracking_number = $tracking;
427
+ }
428
+ if ($tracking_number) {
429
+ $this->display_track_button($tracking_provider, $tracking_number);
430
+ }
431
+ }
432
+
433
+ /**
434
+ * Display shipment info in customer emails.
435
+ *
436
+ * @access public
437
+ * @return void
438
+ */
439
+ function email_display($order)
440
+ {
441
+ $this->display_tracking_info($order->id, true);
442
+ }
443
+
444
+ private function display_track_button($tracking_provider, $tracking_number)
445
+ {
446
+
447
+ $js = '(function(e,t,n){var r,i=e.getElementsByTagName(t)[0];if(e.getElementById(n))return;r=e.createElement(t);r.id=n;r.src="//apps.aftership.com/all.js";i.parentNode.insertBefore(r,i)})(document,"script","aftership-jssdk")';
448
+ if (function_exists('wc_enqueue_js')) {
449
+ wc_enqueue_js($js);
450
+ } else {
451
+ global $woocommerce;
452
+ $woocommerce->add_inline_js($js);
453
+ }
454
+
455
+ $track_button = '<div id="as-root"></div><div class="as-track-button" data-slug="' . $tracking_provider . '" data-tracking-number="' . $tracking_number . '" data-support="true" data-width="400" data-size="normal" data-hide-tracking-number="true"></div>';
456
+ echo wpautop(sprintf('%s', $track_button));
457
+ echo "<br><br>";
458
+ }
459
+ }
460
+
461
+ if (!function_exists('getAfterShipInstance')) {
462
+ function getAfterShipInstance()
463
+ {
464
+ return AfterShip::Instance();
465
+ }
466
+ }
467
+ }
468
+
469
+ /**
470
+ * Register this class globally
471
+ */
472
+ $GLOBALS['aftership'] = getAfterShipInstance();
473
+
474
+ }
trunk/api/class-aftership-api-authentication.php ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AfterShip API Authentication Class
4
+ *
5
+ * @author AfterShip
6
+ * @category API
7
+ * @package AfterShip/API
8
+ * @since 1
9
+ */
10
+
11
+ if (!defined('ABSPATH')) exit; // Exit if accessed directly
12
+
13
+
14
+ if (!function_exists('getallheaders')) {
15
+ function getallheaders()
16
+ {
17
+ $headers = '';
18
+ foreach ($_SERVER as $name => $value) {
19
+ if (substr($name, 0, 5) == 'HTTP_') {
20
+ $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
21
+ }
22
+ }
23
+ return $headers;
24
+ }
25
+ }
26
+
27
+ class AfterShip_API_Authentication
28
+ {
29
+
30
+ /**
31
+ * Setup class
32
+ *
33
+ * @since 2.1
34
+ * @return WC_API_Authentication
35
+ */
36
+ public function __construct()
37
+ {
38
+
39
+ // to disable authentication, hook into this filter at a later priority and return a valid WP_User
40
+ add_filter('aftership_api_check_authentication', array($this, 'authenticate'), 0);
41
+ }
42
+
43
+ /**
44
+ * Authenticate the request. The authentication method varies based on whether the request was made over SSL or not.
45
+ *
46
+ * @since 2.1
47
+ * @param WP_User $user
48
+ * @return null|WP_Error|WP_User
49
+ */
50
+ public function authenticate($user)
51
+ {
52
+
53
+ // allow access to the index by default
54
+ if ('/' === getAfterShipInstance()->api->server->path)
55
+ return new WP_User(0);
56
+
57
+ try {
58
+ $user = $this->perform_authentication();
59
+
60
+ } catch (Exception $e) {
61
+
62
+ $user = new WP_Error('aftership_api_authentication_error', $e->getMessage(), array('status' => $e->getCode()));
63
+ }
64
+
65
+ return $user;
66
+ }
67
+
68
+ private function perform_authentication()
69
+ {
70
+ //$params = getAfterShipInstance()->api->server->params['GET'];
71
+
72
+ $headers = getallheaders();
73
+ $headers = json_decode(json_encode($headers), true);
74
+
75
+ // it dues to different kind of server configuration
76
+ $key = 'AFTERSHIP_WP_KEY';
77
+ $key1 = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $key))));
78
+ $key2 = 'AFTERSHIP-WP-KEY';
79
+
80
+ // get aftership wp key
81
+ if (!empty($headers[$key])) {
82
+ $api_key = $headers[$key];
83
+ } else if (!empty($headers[$key1])){
84
+ $api_key = $headers[$key1];
85
+ } else if (!empty($headers[$key2])){
86
+ $api_key = $headers[$key2];
87
+ } else {
88
+ throw new Exception(__('AfterShip\'s WordPress Key is missing', 'aftership'), 404);
89
+ }
90
+
91
+ $user = $this->get_user_by_api_key($api_key);
92
+
93
+ return $user;
94
+
95
+ }
96
+
97
+ /**
98
+ * Return the user for the given consumer key
99
+ *
100
+ * @since 2.1
101
+ * @param string $consumer_key
102
+ * @return WP_User
103
+ * @throws Exception
104
+ */
105
+ private function get_user_by_api_key($api_key)
106
+ {
107
+
108
+ $user_query = new WP_User_Query(
109
+ array(
110
+ //'meta_key' => 'woocommerce_api_consumer_key',
111
+ 'meta_key' => 'aftership_wp_api_key',
112
+ 'meta_value' => $api_key,
113
+ )
114
+ );
115
+
116
+ $users = $user_query->get_results();
117
+
118
+ if (empty($users[0]))
119
+ throw new Exception(__('AfterShip\'s WordPress API Key is invalid', 'aftership'), 401);
120
+
121
+ return $users[0];
122
+
123
+
124
+ }
125
+
126
+ }
trunk/api/class-aftership-api-json-handler.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AfterShip API
4
+ *
5
+ * Handles parsing JSON request bodies and generating JSON responses
6
+ *
7
+ * @author AfterShip
8
+ * @category API
9
+ * @package AfterShip/API
10
+ * @since 1.0
11
+ */
12
+
13
+ if (!defined('ABSPATH')) exit; // Exit if accessed directly
14
+
15
+ class AfterShip_API_JSON_Handler implements AfterShip_API_Handler
16
+ {
17
+
18
+ /**
19
+ * Get the content type for the response
20
+ *
21
+ * @since 2.1
22
+ * @return string
23
+ */
24
+ public function get_content_type()
25
+ {
26
+
27
+ return 'application/json; charset=' . get_option('blog_charset');
28
+ }
29
+
30
+ /**
31
+ * Parse the raw request body entity
32
+ *
33
+ * @since 2.1
34
+ * @param string $body the raw request body
35
+ * @return array|mixed
36
+ */
37
+ public function parse_body($body)
38
+ {
39
+
40
+ return json_decode($body, true);
41
+ }
42
+
43
+ /**
44
+ * Generate a JSON response given an array of data
45
+ *
46
+ * @since 2.1
47
+ * @param array $data the response data
48
+ * @return string
49
+ */
50
+ public function generate_response($data)
51
+ {
52
+
53
+ if (isset($_GET['_jsonp'])) {
54
+
55
+ // JSONP enabled by default
56
+ if (!apply_filters('aftership_api_jsonp_enabled', true)) {
57
+
58
+ WC()->api->server->send_status(400);
59
+
60
+ $data = array(array('code' => 'aftership_api_jsonp_disabled', 'message' => __('JSONP support is disabled on this site', 'aftership')));
61
+ }
62
+
63
+ // Check for invalid characters (only alphanumeric allowed)
64
+ if (preg_match('/\W/', $_GET['_jsonp'])) {
65
+
66
+ WC()->api->server->send_status(400);
67
+
68
+ $data = array(array('code' => 'aftership_api_jsonp_callback_invalid', __('The JSONP callback function is invalid', 'aftership')));
69
+ }
70
+
71
+ return $_GET['_jsonp'] . '(' . json_encode($data) . ')';
72
+ }
73
+
74
+ return json_encode($data);
75
+ }
76
+
77
+ }
trunk/api/class-aftership-api-orders.php ADDED
@@ -0,0 +1,462 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AfterShip API Orders Class
4
+ *
5
+ * Handles requests to the /orders endpoint
6
+ *
7
+ * @author AfterShip
8
+ * @category API
9
+ * @package AfterShip/API
10
+ * @since 1.0
11
+ */
12
+
13
+ if (!defined('ABSPATH')) exit; // Exit if accessed directly
14
+
15
+ class AfterShip_API_Orders extends AfterShip_API_Resource
16
+ {
17
+
18
+ /** @var string $base the route base */
19
+ protected $base = '/orders';
20
+
21
+ /**
22
+ * Register the routes for this class
23
+ *
24
+ * GET /orders
25
+ * GET /orders/count
26
+ * GET|PUT /orders/<id>
27
+ * GET /orders/<id>/notes
28
+ *
29
+ * @since 2.1
30
+ * @param array $routes
31
+ * @return array
32
+ */
33
+ public function register_routes($routes)
34
+ {
35
+
36
+ # GET /orders
37
+ $routes[$this->base] = array(
38
+ array(array($this, 'get_orders'), AfterShip_API_Server::READABLE),
39
+ );
40
+
41
+ # GET /orders/count
42
+ $routes[$this->base . '/count'] = array(
43
+ array(array($this, 'get_orders_count'), AfterShip_API_Server::READABLE),
44
+ );
45
+
46
+ # GET|PUT /orders/<id>
47
+ $routes[$this->base . '/(?P<id>\d+)'] = array(
48
+ array(array($this, 'get_order'), AfterShip_API_Server::READABLE),
49
+ array(array($this, 'edit_order'), AfterShip_API_Server::EDITABLE | AfterShip_API_Server::ACCEPT_DATA),
50
+ );
51
+
52
+ # GET /orders/<id>/notes
53
+ $routes[$this->base . '/(?P<id>\d+)/notes'] = array(
54
+ array(array($this, 'get_order_notes'), AfterShip_API_Server::READABLE),
55
+ );
56
+
57
+ return $routes;
58
+ }
59
+
60
+ /**
61
+ * Get all orders
62
+ *
63
+ * @since 2.1
64
+ * @param string $fields
65
+ * @param array $filter
66
+ * @param string $status
67
+ * @param int $page
68
+ * @return array
69
+ */
70
+ public function get_orders($fields = null, $filter = array(), $status = null, $page = 1)
71
+ {
72
+
73
+ if (!empty($status))
74
+ $filter['status'] = $status;
75
+
76
+ $filter['page'] = $page;
77
+
78
+ $query = $this->query_orders($filter);
79
+
80
+ $orders = array();
81
+
82
+ foreach ($query->posts as $order_id) {
83
+
84
+ if (!$this->is_readable($order_id))
85
+ continue;
86
+
87
+ $orders[] = current($this->get_order($order_id, $fields));
88
+ }
89
+
90
+ $this->server->add_pagination_headers($query);
91
+
92
+ return array('orders' => $orders);
93
+ }
94
+
95
+
96
+ /**
97
+ * Get the order for the given ID
98
+ *
99
+ * @since 2.1
100
+ * @param int $id the order ID
101
+ * @param array $fields
102
+ * @return array
103
+ */
104
+ public function get_order($id, $fields = null)
105
+ {
106
+
107
+ // ensure order ID is valid & user has permission to read
108
+ $id = $this->validate_request($id, 'shop_order', 'read');
109
+
110
+ if (is_wp_error($id))
111
+ return $id;
112
+
113
+ $order = new WC_Order($id);
114
+
115
+ $order_post = get_post($id);
116
+
117
+ $order_data = array(
118
+ 'id' => $order->id,
119
+ 'order_number' => $order->get_order_number(),
120
+ 'created_at' => $this->server->format_datetime($order_post->post_date_gmt),
121
+ 'updated_at' => $this->server->format_datetime($order_post->post_modified_gmt),
122
+ 'completed_at' => $this->server->format_datetime($order->completed_date, true),
123
+ 'status' => $order->status,
124
+ 'currency' => $order->order_currency,
125
+ 'total' => wc_format_decimal($order->get_total(), 2),
126
+ 'subtotal' => wc_format_decimal($this->get_order_subtotal($order), 2),
127
+ 'total_line_items_quantity' => $order->get_item_count(),
128
+ 'total_tax' => wc_format_decimal($order->get_total_tax(), 2),
129
+ 'total_shipping' => wc_format_decimal($order->get_total_shipping(), 2),
130
+ 'cart_tax' => wc_format_decimal($order->get_cart_tax(), 2),
131
+ 'shipping_tax' => wc_format_decimal($order->get_shipping_tax(), 2),
132
+ 'total_discount' => wc_format_decimal($order->get_total_discount(), 2),
133
+ 'cart_discount' => wc_format_decimal($order->get_cart_discount(), 2),
134
+ 'order_discount' => wc_format_decimal($order->get_order_discount(), 2),
135
+ 'shipping_methods' => $order->get_shipping_method(),
136
+ 'payment_details' => array(
137
+ 'method_id' => $order->payment_method,
138
+ 'method_title' => $order->payment_method_title,
139
+ 'paid' => isset($order->paid_date),
140
+ ),
141
+ 'billing_address' => array(
142
+ 'first_name' => $order->billing_first_name,
143
+ 'last_name' => $order->billing_last_name,
144
+ 'company' => $order->billing_company,
145
+ 'address_1' => $order->billing_address_1,
146
+ 'address_2' => $order->billing_address_2,
147
+ 'city' => $order->billing_city,
148
+ 'state' => $order->billing_state,
149
+ 'postcode' => $order->billing_postcode,
150
+ 'country' => $order->billing_country,
151
+ 'email' => $order->billing_email,
152
+ 'phone' => $order->billing_phone,
153
+ ),
154
+ 'shipping_address' => array(
155
+ 'first_name' => $order->shipping_first_name,
156
+ 'last_name' => $order->shipping_last_name,
157
+ 'company' => $order->shipping_company,
158
+ 'address_1' => $order->shipping_address_1,
159
+ 'address_2' => $order->shipping_address_2,
160
+ 'city' => $order->shipping_city,
161
+ 'state' => $order->shipping_state,
162
+ 'postcode' => $order->shipping_postcode,
163
+ 'country' => $order->shipping_country,
164
+ ),
165
+ 'note' => $order->customer_note,
166
+ 'customer_ip' => $order->customer_ip_address,
167
+ 'customer_user_agent' => $order->customer_user_agent,
168
+ 'customer_id' => $order->customer_user,
169
+ 'view_order_url' => $order->get_view_order_url(),
170
+ 'line_items' => array(),
171
+ 'shipping_lines' => array(),
172
+ 'tax_lines' => array(),
173
+ 'fee_lines' => array(),
174
+ 'coupon_lines' => array(),
175
+ );
176
+
177
+ // add line items
178
+ foreach ($order->get_items() as $item_id => $item) {
179
+
180
+ $product = $order->get_product_from_item($item);
181
+
182
+ $order_data['line_items'][] = array(
183
+ 'id' => $item_id,
184
+ 'subtotal' => wc_format_decimal($order->get_line_subtotal($item), 2),
185
+ 'total' => wc_format_decimal($order->get_line_total($item), 2),
186
+ 'total_tax' => wc_format_decimal($order->get_line_tax($item), 2),
187
+ 'price' => wc_format_decimal($order->get_item_total($item), 2),
188
+ 'quantity' => (int)$item['qty'],
189
+ 'tax_class' => (!empty($item['tax_class'])) ? $item['tax_class'] : null,
190
+ 'name' => $item['name'],
191
+ 'product_id' => (isset($product->variation_id)) ? $product->variation_id : $product->id,
192
+ 'sku' => is_object($product) ? $product->get_sku() : null,
193
+ );
194
+ }
195
+
196
+ // add shipping
197
+ foreach ($order->get_shipping_methods() as $shipping_item_id => $shipping_item) {
198
+
199
+ $order_data['shipping_lines'][] = array(
200
+ 'id' => $shipping_item_id,
201
+ 'method_id' => $shipping_item['method_id'],
202
+ 'method_title' => $shipping_item['name'],
203
+ 'total' => wc_format_decimal($shipping_item['cost'], 2),
204
+ );
205
+ }
206
+
207
+ // add taxes
208
+ foreach ($order->get_tax_totals() as $tax_code => $tax) {
209
+
210
+ $order_data['tax_lines'][] = array(
211
+ 'code' => $tax_code,
212
+ 'title' => $tax->label,
213
+ 'total' => wc_format_decimal($tax->amount, 2),
214
+ 'compound' => (bool)$tax->is_compound,
215
+ );
216
+ }
217
+
218
+ // add fees
219
+ foreach ($order->get_fees() as $fee_item_id => $fee_item) {
220
+
221
+ $order_data['fee_lines'][] = array(
222
+ 'id' => $fee_item_id,
223
+ 'title' => $fee_item['name'],
224
+ 'tax_class' => (!empty($fee_item['tax_class'])) ? $fee_item['tax_class'] : null,
225
+ 'total' => wc_format_decimal($order->get_line_total($fee_item), 2),
226
+ 'total_tax' => wc_format_decimal($order->get_line_tax($fee_item), 2),
227
+ );
228
+ }
229
+
230
+ // add coupons
231
+ foreach ($order->get_items('coupon') as $coupon_item_id => $coupon_item) {
232
+
233
+ $order_data['coupon_lines'][] = array(
234
+ 'id' => $coupon_item_id,
235
+ 'code' => $coupon_item['name'],
236
+ 'amount' => wc_format_decimal($coupon_item['discount_amount'], 2),
237
+ );
238
+ }
239
+
240
+ // aftership add
241
+ $options = get_option('aftership_option_name');
242
+ $plugin = $options['plugin'];
243
+ if ($plugin == 'aftership') {
244
+ $order_data['aftership']['woocommerce']['trackings'][] = array(
245
+ 'tracking_provider' => get_post_meta($order->id, '_aftership_tracking_provider', true),
246
+ 'tracking_number' => get_post_meta($order->id, '_aftership_tracking_number', true),
247
+ 'tracking_shipdate' => get_post_meta($order->id, '_aftership_tracking_shipdate', true),
248
+ 'tracking_postal' => get_post_meta($order->id, '_aftership_tracking_postal', true),
249
+ 'tracking_account' => get_post_meta($order->id, '_aftership_tracking_account', true),
250
+ );
251
+ } else if ($plugin == 'wc-shipment-tracking') { //$49
252
+ $order_data['aftership']['woocommerce']['trackings'][] = array(
253
+ 'tracking_number' => get_post_meta($order->id, '_tracking_number', true),
254
+ );
255
+ }
256
+ // aftership add finish
257
+
258
+ return array('order' => apply_filters('aftership_api_order_response', $order_data, $order, $fields, $this->server));
259
+ }
260
+
261
+ /**
262
+ * Get the total number of orders
263
+ *
264
+ * @since 2.1
265
+ * @param string $status
266
+ * @param array $filter
267
+ * @return array
268
+ */
269
+ public function get_orders_count($status = null, $filter = array())
270
+ {
271
+
272
+ if (!empty($status))
273
+ $filter['status'] = $status;
274
+
275
+ $query = $this->query_orders($filter);
276
+
277
+ if (!current_user_can('read_private_shop_orders'))
278
+ return new WP_Error('aftership_api_user_cannot_read_orders_count', __('You do not have permission to read the orders count', 'aftership'), array('status' => 401));
279
+
280
+ return array('count' => (int)$query->found_posts);
281
+ }
282
+
283
+ /**
284
+ * Edit an order
285
+ *
286
+ * API v1 only allows updating the status of an order
287
+ *
288
+ * @since 2.1
289
+ * @param int $id the order ID
290
+ * @param array $data
291
+ * @return array
292
+ */
293
+ public function edit_order($id, $data)
294
+ {
295
+
296
+ $id = $this->validate_request($id, 'shop_order', 'edit');
297
+
298
+ if (is_wp_error($id))
299
+ return $id;
300
+
301
+ $order = new WC_Order($id);
302
+
303
+ if (!empty($data['status'])) {
304
+
305
+ $order->update_status($data['status'], isset($data['note']) ? $data['note'] : '');
306
+ }
307
+
308
+ return $this->get_order($id);
309
+ }
310
+
311
+ /**
312
+ * Delete an order
313
+ *
314
+ * @TODO enable along with POST in 2.2
315
+ * @param int $id the order ID
316
+ * @param bool $force true to permanently delete order, false to move to trash
317
+ * @return array
318
+ */
319
+ public function delete_order($id, $force = false)
320
+ {
321
+
322
+ $id = $this->validate_request($id, 'shop_order', 'delete');
323
+
324
+ return $this->delete($id, 'order', ('true' === $force));
325
+ }
326
+
327
+ /**
328
+ * Get the admin order notes for an order
329
+ *
330
+ * @since 2.1
331
+ * @param int $id the order ID
332
+ * @param string $fields fields to include in response
333
+ * @return array
334
+ */
335
+ public function get_order_notes($id, $fields = null)
336
+ {
337
+
338
+ // ensure ID is valid order ID
339
+ $id = $this->validate_request($id, 'shop_order', 'read');
340
+
341
+ if (is_wp_error($id))
342
+ return $id;
343
+
344
+ $args = array(
345
+ 'post_id' => $id,
346
+ 'approve' => 'approve',
347
+ 'type' => 'order_note'
348
+ );
349
+
350
+ remove_filter('comments_clauses', array('WC_Comments', 'exclude_order_comments'), 10, 1);
351
+
352
+ $notes = get_comments($args);
353
+
354
+ add_filter('comments_clauses', array('WC_Comments', 'exclude_order_comments'), 10, 1);
355
+
356
+ $order_notes = array();
357
+
358
+ foreach ($notes as $note) {
359
+
360
+ $order_notes[] = array(
361
+ 'id' => $note->comment_ID,
362
+ 'created_at' => $this->server->format_datetime($note->comment_date_gmt),
363
+ 'note' => $note->comment_content,
364
+ 'customer_note' => get_comment_meta($note->comment_ID, 'is_customer_note', true) ? true : false,
365
+ );
366
+ }
367
+
368
+ return array('order_notes' => apply_filters('aftership_api_order_notes_response', $order_notes, $id, $fields, $notes, $this->server));
369
+ }
370
+
371
+ /**
372
+ * Helper method to get order post objects
373
+ *
374
+ * @since 2.1
375
+ * @param array $args request arguments for filtering query
376
+ * @return WP_Query
377
+ */
378
+ private function query_orders($args)
379
+ {
380
+
381
+ function wpbo_get_woo_version_number()
382
+ {
383
+ // If get_plugins() isn't available, require it
384
+ if (!function_exists('get_plugins'))
385
+ require_once(ABSPATH . 'wp-admin/includes/plugin.php');
386
+
387
+ // Create the plugins folder and file variables
388
+ $plugin_folder = get_plugins('/' . 'woocommerce');
389
+ $plugin_file = 'woocommerce.php';
390
+
391
+ // If the plugin version number is set, return it
392
+ if (isset($plugin_folder[$plugin_file]['Version'])) {
393
+ return $plugin_folder[$plugin_file]['Version'];
394
+
395
+ } else {
396
+ // Otherwise return null
397
+ return NULL;
398
+ }
399
+ }
400
+
401
+ $woo_version = wpbo_get_woo_version_number();
402
+
403
+ if ($woo_version >= 2.2) {
404
+ // set base query arguments
405
+ $query_args = array(
406
+ 'fields' => 'ids',
407
+ 'post_type' => 'shop_order',
408
+ // 'post_status' => 'publish',
409
+ 'post_status' => array_keys(wc_get_order_statuses())
410
+ );
411
+ } else {
412
+ // set base query arguments
413
+ $query_args = array(
414
+ 'fields' => 'ids',
415
+ 'post_type' => 'shop_order',
416
+ 'post_status' => 'publish',
417
+ );
418
+ }
419
+
420
+ // add status argument
421
+ if (!empty($args['status'])) {
422
+
423
+ $statuses = explode(',', $args['status']);
424
+
425
+ $query_args['tax_query'] = array(
426
+ array(
427
+ 'taxonomy' => 'shop_order_status',
428
+ 'field' => 'slug',
429
+ 'terms' => $statuses,
430
+ ),
431
+ );
432
+
433
+ unset($args['status']);
434
+ }
435
+
436
+ $query_args = $this->merge_query_args($query_args, $args);
437
+
438
+ return new WP_Query($query_args);
439
+ }
440
+
441
+ /**
442
+ * Helper method to get the order subtotal
443
+ *
444
+ * @since 2.1
445
+ * @param WC_Order $order
446
+ * @return float
447
+ */
448
+ private function get_order_subtotal($order)
449
+ {
450
+
451
+ $subtotal = 0;
452
+
453
+ // subtotal
454
+ foreach ($order->get_items() as $item) {
455
+
456
+ $subtotal += (isset($item['line_subtotal'])) ? $item['line_subtotal'] : 0;
457
+ }
458
+
459
+ return $subtotal;
460
+ }
461
+
462
+ }
trunk/api/class-aftership-api-resource.php ADDED
@@ -0,0 +1,410 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AfterShip API Resource class
4
+ *
5
+ * Provides shared functionality for resource-specific API classes
6
+ *
7
+ * @author AfterShip
8
+ * @category API
9
+ * @package AfterShip/API
10
+ * @since 1.0
11
+ */
12
+
13
+ if (!defined('ABSPATH')) exit; // Exit if accessed directly
14
+
15
+ class AfterShip_API_Resource
16
+ {
17
+
18
+ /** @var WC_API_Server the API server */
19
+ protected $server;
20
+
21
+ /** @var string sub-classes override this to set a resource-specific base route */
22
+ protected $base;
23
+
24
+ /**
25
+ * Setup class
26
+ *
27
+ * @since 2.1
28
+ * @param WC_API_Server $server
29
+ * @return WC_API_Resource
30
+ */
31
+ public function __construct(AfterShip_API_Server $server)
32
+ {
33
+
34
+ $this->server = $server;
35
+
36
+ // automatically register routes for sub-classes
37
+ add_filter('aftership_api_endpoints', array($this, 'register_routes'));
38
+
39
+ // remove fields from responses when requests specify certain fields
40
+ // note these are hooked at a later priority so data added via filters (e.g. customer data to the order response)
41
+ // still has the fields filtered properly
42
+ foreach (array('order', 'coupon', 'customer', 'product', 'report') as $resource) {
43
+
44
+ add_filter("aftership_api_{$resource}_response", array($this, 'maybe_add_meta'), 15, 2);
45
+ add_filter("aftership_api_{$resource}_response", array($this, 'filter_response_fields'), 20, 3);
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Validate the request by checking:
51
+ *
52
+ * 1) the ID is a valid integer
53
+ * 2) the ID returns a valid post object and matches the provided post type
54
+ * 3) the current user has the proper permissions to read/edit/delete the post
55
+ *
56
+ * @since 2.1
57
+ * @param string|int $id the post ID
58
+ * @param string $type the post type, either `shop_order`, `shop_coupon`, or `product`
59
+ * @param string $context the context of the request, either `read`, `edit` or `delete`
60
+ * @return int|WP_Error valid post ID or WP_Error if any of the checks fails
61
+ */
62
+ protected function validate_request($id, $type, $context)
63
+ {
64
+
65
+ if ('shop_order' === $type || 'shop_coupon' === $type)
66
+ $resource_name = str_replace('shop_', '', $type);
67
+ else
68
+ $resource_name = $type;
69
+
70
+ $id = absint($id);
71
+
72
+ // validate ID
73
+ if (empty($id))
74
+ return new WP_Error("aftership_api_invalid_{$resource_name}_id", sprintf(__('Invalid %s ID', 'aftership'), $type), array('status' => 404));
75
+
76
+ // only custom post types have per-post type/permission checks
77
+ if ('customer' !== $type) {
78
+
79
+ $post = get_post($id);
80
+
81
+ // for checking permissions, product variations are the same as the product post type
82
+ $post_type = ('product_variation' === $post->post_type) ? 'product' : $post->post_type;
83
+
84
+ // validate post type
85
+ if ($type !== $post_type)
86
+ return new WP_Error("aftership_api_invalid_{$resource_name}", sprintf(__('Invalid %s', 'aftership'), $resource_name), array('status' => 404));
87
+
88
+ // validate permissions
89
+ switch ($context) {
90
+
91
+ case 'read':
92
+ if (!$this->is_readable($post))
93
+ return new WP_Error("aftership_api_user_cannot_read_{$resource_name}", sprintf(__('You do not have permission to read this %s', 'aftership'), $resource_name), array('status' => 401));
94
+ break;
95
+
96
+ case 'edit':
97
+ if (!$this->is_editable($post))
98
+ return new WP_Error("aftership_api_user_cannot_edit_{$resource_name}", sprintf(__('You do not have permission to edit this %s', 'aftership'), $resource_name), array('status' => 401));
99
+ break;
100
+
101
+ case 'delete':
102
+ if (!$this->is_deletable($post))
103
+ return new WP_Error("aftership_api_user_cannot_delete_{$resource_name}", sprintf(__('You do not have permission to delete this %s', 'aftership'), $resource_name), array('status' => 401));
104
+ break;
105
+ }
106
+ }
107
+
108
+ return $id;
109
+ }
110
+
111
+ /**
112
+ * Add common request arguments to argument list before WP_Query is run
113
+ *
114
+ * @since 2.1
115
+ * @param array $base_args required arguments for the query (e.g. `post_type`, etc)
116
+ * @param array $request_args arguments provided in the request
117
+ * @return array
118
+ */
119
+ protected function merge_query_args($base_args, $request_args)
120
+ {
121
+
122
+ $args = array();
123
+
124
+ // date
125
+ if (!empty($request_args['created_at_min']) || !empty($request_args['created_at_max']) || !empty($request_args['updated_at_min']) || !empty($request_args['updated_at_max'])) {
126
+
127
+ $args['date_query'] = array();
128
+
129
+ // resources created after specified date
130
+ if (!empty($request_args['created_at_min']))
131
+ $args['date_query'][] = array('column' => 'post_date_gmt', 'after' => $this->server->parse_datetime($request_args['created_at_min']), 'inclusive' => true);
132
+
133
+ // resources created before specified date
134
+ if (!empty($request_args['created_at_max']))
135
+ $args['date_query'][] = array('column' => 'post_date_gmt', 'before' => $this->server->parse_datetime($request_args['created_at_max']), 'inclusive' => true);
136
+
137
+ // resources updated after specified date
138
+ if (!empty($request_args['updated_at_min']))
139
+ $args['date_query'][] = array('column' => 'post_modified_gmt', 'after' => $this->server->parse_datetime($request_args['updated_at_min']), 'inclusive' => true);
140
+
141
+ // resources updated before specified date
142
+ if (!empty($request_args['updated_at_max']))
143
+ $args['date_query'][] = array('column' => 'post_modified_gmt', 'before' => $this->server->parse_datetime($request_args['updated_at_max']), 'inclusive' => true);
144
+ }
145
+
146
+ // search
147
+ if (!empty($request_args['q']))
148
+ $args['s'] = $request_args['q'];
149
+
150
+ // resources per response
151
+ if (!empty($request_args['limit']))
152
+ $args['posts_per_page'] = $request_args['limit'];
153
+
154
+ // resource offset
155
+ if (!empty($request_args['offset']))
156
+ $args['offset'] = $request_args['offset'];
157
+
158
+ // resource page
159
+ $args['paged'] = (isset($request_args['page'])) ? absint($request_args['page']) : 1;
160
+
161
+ return array_merge($base_args, $args);
162
+ }
163
+
164
+ /**
165
+ * Add meta to resources when requested by the client. Meta is added as a top-level
166
+ * `<resource_name>_meta` attribute (e.g. `order_meta`) as a list of key/value pairs
167
+ *
168
+ * @since 2.1
169
+ * @param array $data the resource data
170
+ * @param object $resource the resource object (e.g WC_Order)
171
+ * @return mixed
172
+ */
173
+ public function maybe_add_meta($data, $resource)
174
+ {
175
+
176
+ if (isset($this->server->params['GET']['filter']['meta']) && 'true' === $this->server->params['GET']['filter']['meta'] && is_object($resource)) {
177
+
178
+ // don't attempt to add meta more than once
179
+ if (preg_grep('/[a-z]+_meta/', array_keys($data)))
180
+ return $data;
181
+
182
+ // define the top-level property name for the meta
183
+ switch (get_class($resource)) {
184
+
185
+ case 'WC_Order':
186
+ $meta_name = 'order_meta';
187
+ break;
188
+
189
+ case 'WC_Coupon':
190
+ $meta_name = 'coupon_meta';
191
+ break;
192
+
193
+ case 'WP_User':
194
+ $meta_name = 'customer_meta';
195
+ break;
196
+
197
+ default:
198
+ $meta_name = 'product_meta';
199
+ break;
200
+ }
201
+
202
+ if (is_a($resource, 'WP_User')) {
203
+
204
+ // customer meta
205
+ $meta = (array)get_user_meta($resource->ID);
206
+
207
+ } elseif (is_a($resource, 'WC_Product_Variation')) {
208
+
209
+ // product variation meta
210
+ $meta = (array)get_post_meta($resource->get_variation_id());
211
+
212
+ } else {
213
+
214
+ // coupon/order/product meta
215
+ $meta = (array)get_post_meta($resource->id);
216
+ }
217
+
218
+ foreach ($meta as $meta_key => $meta_value) {
219
+
220
+ // don't add hidden meta by default
221
+ if (!is_protected_meta($meta_key)) {
222
+ $data[$meta_name][$meta_key] = maybe_unserialize($meta_value[0]);
223
+ }
224
+ }
225
+
226
+ }
227
+
228
+ return $data;
229
+ }
230
+
231
+ /**
232
+ * Restrict the fields included in the response if the request specified certain only certain fields should be returned
233
+ *
234
+ * @since 2.1
235
+ * @param array $data the response data
236
+ * @param object $resource the object that provided the response data, e.g. WC_Coupon or WC_Order
237
+ * @param array|string the requested list of fields to include in the response
238
+ * @return array response data
239
+ */
240
+ public function filter_response_fields($data, $resource, $fields)
241
+ {
242
+
243
+ if (!is_array($data) || empty($fields))
244
+ return $data;
245
+
246
+ $fields = explode(',', $fields);
247
+ $sub_fields = array();
248
+
249
+ // get sub fields
250
+ foreach ($fields as $field) {
251
+
252
+ if (false !== strpos($field, '.')) {
253
+
254
+ list($name, $value) = explode('.', $field);
255
+
256
+ $sub_fields[$name] = $value;
257
+ }
258
+ }
259
+
260
+ // iterate through top-level fields
261
+ foreach ($data as $data_field => $data_value) {
262
+
263
+ // if a field has sub-fields and the top-level field has sub-fields to filter
264
+ if (is_array($data_value) && in_array($data_field, array_keys($sub_fields))) {
265
+
266
+ // iterate through each sub-field
267
+ foreach ($data_value as $sub_field => $sub_field_value) {
268
+
269
+ // remove non-matching sub-fields
270
+ if (!in_array($sub_field, $sub_fields)) {
271
+ unset($data[$data_field][$sub_field]);
272
+ }
273
+ }
274
+
275
+ } else {
276
+
277
+ // remove non-matching top-level fields
278
+ if (!in_array($data_field, $fields)) {
279
+ unset($data[$data_field]);
280
+ }
281
+ }
282
+ }
283
+
284
+ return $data;
285
+ }
286
+
287
+ /**
288
+ * Delete a given resource
289
+ *
290
+ * @since 2.1
291
+ * @param int $id the resource ID
292
+ * @param string $type the resource post type, or `customer`
293
+ * @param bool $force true to permanently delete resource, false to move to trash (not supported for `customer`)
294
+ * @return array|WP_Error
295
+ */
296
+ protected function delete($id, $type, $force = false)
297
+ {
298
+
299
+ if ('shop_order' === $type || 'shop_coupon' === $type)
300
+ $resource_name = str_replace('shop_', '', $type);
301
+ else
302
+ $resource_name = $type;
303
+
304
+ if ('customer' === $type) {
305
+
306
+ $result = wp_delete_user($id);
307
+
308
+ if ($result)
309
+ return array('message' => __('Permanently deleted customer', 'aftership'));
310
+ else
311
+ return new WP_Error('aftership_api_cannot_delete_customer', __('The customer cannot be deleted', 'aftership'), array('status' => 500));
312
+
313
+ } else {
314
+
315
+ // delete order/coupon/product
316
+
317
+ $result = ($force) ? wp_delete_post($id, true) : wp_trash_post($id);
318
+
319
+ if (!$result)
320
+ return new WP_Error("aftership_api_cannot_delete_{$resource_name}", sprintf(__('This %s cannot be deleted', 'aftership'), $resource_name), array('status' => 500));
321
+
322
+ if ($force) {
323
+ return array('message' => sprintf(__('Permanently deleted %s', 'aftership'), $resource_name));
324
+
325
+ } else {
326
+
327
+ $this->server->send_status('202');
328
+
329
+ return array('message' => sprintf(__('Deleted %s', 'aftership'), $resource_name));
330
+ }
331
+ }
332
+ }
333
+
334
+
335
+ /**
336
+ * Checks if the given post is readable by the current user
337
+ *
338
+ * @since 2.1
339
+ * @see WC_API_Resource::check_permission()
340
+ * @param WP_Post|int $post
341
+ * @return bool
342
+ */
343
+ protected function is_readable($post)
344
+ {
345
+
346
+ return $this->check_permission($post, 'read');
347
+ }
348
+
349
+ /**
350
+ * Checks if the given post is editable by the current user
351
+ *
352
+ * @since 2.1
353
+ * @see WC_API_Resource::check_permission()
354
+ * @param WP_Post|int $post
355
+ * @return bool
356
+ */
357
+ protected function is_editable($post)
358
+ {
359
+
360
+ return $this->check_permission($post, 'edit');
361
+
362
+ }
363
+
364
+ /**
365
+ * Checks if the given post is deletable by the current user
366
+ *
367
+ * @since 2.1
368
+ * @see WC_API_Resource::check_permission()
369
+ * @param WP_Post|int $post
370
+ * @return bool
371
+ */
372
+ protected function is_deletable($post)
373
+ {
374
+
375
+ return $this->check_permission($post, 'delete');
376
+ }
377
+
378
+ /**
379
+ * Checks the permissions for the current user given a post and context
380
+ *
381
+ * @since 2.1
382
+ * @param WP_Post|int $post
383
+ * @param string $context the type of permission to check, either `read`, `write`, or `delete`
384
+ * @return bool true if the current user has the permissions to perform the context on the post
385
+ */
386
+ private function check_permission($post, $context)
387
+ {
388
+
389
+ if (!is_a($post, 'WP_Post'))
390
+ $post = get_post($post);
391
+
392
+ if (is_null($post))
393
+ return false;
394
+
395
+ $post_type = get_post_type_object($post->post_type);
396
+
397
+ if ('read' === $context)
398
+ return current_user_can($post_type->cap->read_private_posts, $post->ID);
399
+
400
+ elseif ('edit' === $context)
401
+ return current_user_can($post_type->cap->edit_post, $post->ID);
402
+
403
+ elseif ('delete' === $context)
404
+ return current_user_can($post_type->cap->delete_post, $post->ID);
405
+
406
+ else
407
+ return false;
408
+ }
409
+
410
+ }
trunk/api/class-aftership-api-server.php ADDED
@@ -0,0 +1,766 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AfterShip API
4
+ *
5
+ * Handles REST API requests
6
+ *
7
+ * This class and related code (JSON response handler, resource classes) are based on WP-API v0.6 (https://github.com/WP-API/WP-API)
8
+ * Many thanks to Ryan McCue and any other contributors!
9
+ *
10
+ * @author AfterShip
11
+ * @category API
12
+ * @package AfterShip/API
13
+ * @since 1.0
14
+ */
15
+
16
+ if (!defined('ABSPATH')) exit; // Exit if accessed directly
17
+
18
+ require_once ABSPATH . 'wp-admin/includes/admin.php';
19
+
20
+ class AfterShip_API_Server
21
+ {
22
+
23
+ const METHOD_GET = 1;
24
+ const METHOD_POST = 2;
25
+ const METHOD_PUT = 4;
26
+ const METHOD_PATCH = 8;
27
+ const METHOD_DELETE = 16;
28
+
29
+ const READABLE = 1; // GET
30
+ const CREATABLE = 2; // POST
31
+ const EDITABLE = 14; // POST | PUT | PATCH
32
+ const DELETABLE = 16; // DELETE
33
+ const ALLMETHODS = 31; // GET | POST | PUT | PATCH | DELETE
34
+
35
+ /**
36
+ * Does the endpoint accept a raw request body?
37
+ */
38
+ const ACCEPT_RAW_DATA = 64;
39
+
40
+ /** Does the endpoint accept a request body? (either JSON or XML) */
41
+ const ACCEPT_DATA = 128;
42
+
43
+ /**
44
+ * Should we hide this endpoint from the index?
45
+ */
46
+ const HIDDEN_ENDPOINT = 256;
47
+
48
+ /**
49
+ * Map of HTTP verbs to constants
50
+ * @var array
51
+ */
52
+ public static $method_map = array(
53
+ 'HEAD' => self::METHOD_GET,
54
+ 'GET' => self::METHOD_GET,
55
+ 'POST' => self::METHOD_POST,
56
+ 'PUT' => self::METHOD_PUT,
57
+ 'PATCH' => self::METHOD_PATCH,
58
+ 'DELETE' => self::METHOD_DELETE,
59
+ );
60
+
61
+ /**
62
+ * Requested path (relative to the API root, wp-json.php)
63
+ *
64
+ * @var string
65
+ */
66
+ public $path = '';
67
+
68
+ /**
69
+ * Requested method (GET/HEAD/POST/PUT/PATCH/DELETE)
70
+ *
71
+ * @var string
72
+ */
73
+ public $method = 'HEAD';
74
+
75
+ /**
76
+ * Request parameters
77
+ *
78
+ * This acts as an abstraction of the superglobals
79
+ * (GET => $_GET, POST => $_POST)
80
+ *
81
+ * @var array
82
+ */
83
+ public $params = array('GET' => array(), 'POST' => array());
84
+
85
+ /**
86
+ * Request headers
87
+ *
88
+ * @var array
89
+ */
90
+ public $headers = array();
91
+
92
+ /**
93
+ * Request files (matches $_FILES)
94
+ *
95
+ * @var array
96
+ */
97
+ public $files = array();
98
+
99
+ /**
100
+ * Request/Response handler, either JSON by default
101
+ * or XML if requested by client
102
+ *
103
+ * @var WC_API_Handler
104
+ */
105
+ public $handler;
106
+
107
+
108
+ /**
109
+ * Setup class and set request/response handler
110
+ *
111
+ * @since 2.1
112
+ * @param $path
113
+ * @return WC_API_Server
114
+ */
115
+ public function __construct($path)
116
+ {
117
+
118
+ if (empty($path)) {
119
+ if (isset($_SERVER['PATH_INFO']))
120
+ $path = $_SERVER['PATH_INFO'];
121
+ else
122
+ $path = '/';
123
+ }
124
+
125
+ $this->path = $path;
126
+ $this->method = $_SERVER['REQUEST_METHOD'];
127
+ $this->params['GET'] = $_GET;
128
+ $this->params['POST'] = $_POST;
129
+ $this->headers = $this->get_headers($_SERVER);
130
+ $this->files = $_FILES;
131
+
132
+ // Compatibility for clients that can't use PUT/PATCH/DELETE
133
+ if (isset($_GET['_method'])) {
134
+ $this->method = strtoupper($_GET['_method']);
135
+ }
136
+
137
+ // determine type of request/response and load handler, JSON by default
138
+ if ($this->is_json_request())
139
+ $handler_class = 'AfterShip_API_JSON_Handler';
140
+
141
+ elseif ($this->is_xml_request())
142
+ $handler_class = 'WC_API_XML_Handler';
143
+
144
+ else
145
+ $handler_class = apply_filters('aftership_api_default_response_handler', 'AfterShip_API_JSON_Handler', $this->path, $this);
146
+
147
+ $this->handler = new $handler_class();
148
+ }
149
+
150
+ /**
151
+ * Check authentication for the request
152
+ *
153
+ * @since 2.1
154
+ * @return WP_User|WP_Error WP_User object indicates successful login, WP_Error indicates unsuccessful login
155
+ */
156
+ public function check_authentication()
157
+ {
158
+
159
+ // allow plugins to remove default authentication or add their own authentication
160
+ $user = apply_filters('aftership_api_check_authentication', null, $this);
161
+
162
+ // API requests run under the context of the authenticated user
163
+ if (is_a($user, 'WP_User'))
164
+ wp_set_current_user($user->ID);
165
+
166
+ // WP_Errors are handled in serve_request()
167
+ elseif (!is_wp_error($user))
168
+ $user = new WP_Error('aftership_api_authentication_error', __('Invalid authentication method', 'aftership'), array('code' => 500));
169
+
170
+ return $user;
171
+ }
172
+
173
+ /**
174
+ * Convert an error to an array
175
+ *
176
+ * This iterates over all error codes and messages to change it into a flat
177
+ * array. This enables simpler client behaviour, as it is represented as a
178
+ * list in JSON rather than an object/map
179
+ *
180
+ * @since 2.1
181
+ * @param WP_Error $error
182
+ * @return array List of associative arrays with code and message keys
183
+ */
184
+ protected function error_to_array($error)
185
+ {
186
+ $errors = array();
187
+ foreach ((array)$error->errors as $code => $messages) {
188
+ foreach ((array)$messages as $message) {
189
+ $errors[] = array('code' => $code, 'message' => $message);
190
+ }
191
+ }
192
+ return array('errors' => $errors);
193
+ }
194
+
195
+ /**
196
+ * Handle serving an API request
197
+ *
198
+ * Matches the current server URI to a route and runs the first matching
199
+ * callback then outputs a JSON representation of the returned value.
200
+ *
201
+ * @since 2.1
202
+ * @uses WC_API_Server::dispatch()
203
+ */
204
+ public function serve_request()
205
+ {
206
+
207
+ do_action('aftership_api_server_before_serve', $this);
208
+
209
+ $this->header('Content-Type', $this->handler->get_content_type(), true);
210
+
211
+ // the API is enabled by default
212
+ if (!apply_filters('aftership_api_enabled', true, $this) || ('no' === get_option('aftership_api_enabled'))) {
213
+
214
+ $this->send_status(404);
215
+
216
+ echo $this->handler->generate_response(array('errors' => array('code' => 'aftership_api_disabled', 'message' => 'The WooCommerce API is disabled on this site')));
217
+
218
+ return;
219
+ }
220
+
221
+ $result = $this->check_authentication();
222
+
223
+ // if authorization check was successful, dispatch the request
224
+ if (!is_wp_error($result)) {
225
+ $result = $this->dispatch();
226
+ }
227
+
228
+ // handle any dispatch errors
229
+ if (is_wp_error($result)) {
230
+ $data = $result->get_error_data();
231
+ if (is_array($data) && isset($data['status'])) {
232
+ $this->send_status($data['status']);
233
+ }
234
+
235
+ $result = $this->error_to_array($result);
236
+ }
237
+
238
+ // This is a filter rather than an action, since this is designed to be
239
+ // re-entrant if needed
240
+ $served = apply_filters('aftership_api_serve_request', false, $result, $this);
241
+
242
+ if (!$served) {
243
+
244
+ if ('HEAD' === $this->method)
245
+ return;
246
+
247
+ echo $this->handler->generate_response($result);
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Retrieve the route map
253
+ *
254
+ * The route map is an associative array with path regexes as the keys. The
255
+ * value is an indexed array with the callback function/method as the first
256
+ * item, and a bitmask of HTTP methods as the second item (see the class
257
+ * constants).
258
+ *
259
+ * Each route can be mapped to more than one callback by using an array of
260
+ * the indexed arrays. This allows mapping e.g. GET requests to one callback
261
+ * and POST requests to another.
262
+ *
263
+ * Note that the path regexes (array keys) must have @ escaped, as this is
264
+ * used as the delimiter with preg_match()
265
+ *
266
+ * @since 2.1
267
+ * @return array `'/path/regex' => array( $callback, $bitmask )` or `'/path/regex' => array( array( $callback, $bitmask ), ...)`
268
+ */
269
+ public function get_routes()
270
+ {
271
+
272
+ // index added by default
273
+ $endpoints = array(
274
+
275
+ '/' => array(array($this, 'get_index'), self::READABLE),
276
+ );
277
+
278
+ $endpoints = apply_filters('aftership_api_endpoints', $endpoints);
279
+
280
+ // Normalise the endpoints
281
+ foreach ($endpoints as $route => &$handlers) {
282
+ if (count($handlers) <= 2 && isset($handlers[1]) && !is_array($handlers[1])) {
283
+ $handlers = array($handlers);
284
+ }
285
+ }
286
+
287
+ return $endpoints;
288
+ }
289
+
290
+ /**
291
+ * Match the request to a callback and call it
292
+ *
293
+ * @since 2.1
294
+ * @return mixed The value returned by the callback, or a WP_Error instance
295
+ */
296
+ public function dispatch()
297
+ {
298
+
299
+ switch ($this->method) {
300
+
301
+ case 'HEAD':
302
+ case 'GET':
303
+ $method = self::METHOD_GET;
304
+ break;
305
+ /*
306
+ case 'POST':
307
+ $method = self::METHOD_POST;
308
+ break;
309
+
310
+ case 'PUT':
311
+ $method = self::METHOD_PUT;
312
+ break;
313
+
314
+ case 'PATCH':
315
+ $method = self::METHOD_PATCH;
316
+ break;
317
+
318
+ case 'DELETE':
319
+ $method = self::METHOD_DELETE;
320
+ break;
321
+ */
322
+ default:
323
+ return new WP_Error('aftership_api_unsupported_method', __('Unsupported request method', 'aftership'), array('status' => 400));
324
+ }
325
+
326
+ foreach ($this->get_routes() as $route => $handlers) {
327
+ foreach ($handlers as $handler) {
328
+ $callback = $handler[0];
329
+ $supported = isset($handler[1]) ? $handler[1] : self::METHOD_GET;
330
+
331
+ if (!($supported & $method))
332
+ continue;
333
+
334
+ $match = preg_match('@^' . $route . '$@i', urldecode($this->path), $args);
335
+
336
+ if (!$match)
337
+ continue;
338
+
339
+ if (!is_callable($callback))
340
+ return new WP_Error('aftership_api_invalid_handler', __('The handler for the route is invalid', 'aftership'), array('status' => 500));
341
+
342
+ $args = array_merge($args, $this->params['GET']);
343
+ if ($method & self::METHOD_POST) {
344
+ $args = array_merge($args, $this->params['POST']);
345
+ }
346
+ if ($supported & self::ACCEPT_DATA) {
347
+ $data = $this->handler->parse_body($this->get_raw_data());
348
+ $args = array_merge($args, array('data' => $data));
349
+ } elseif ($supported & self::ACCEPT_RAW_DATA) {
350
+ $data = $this->get_raw_data();
351
+ $args = array_merge($args, array('data' => $data));
352
+ }
353
+
354
+ $args['_method'] = $method;
355
+ $args['_route'] = $route;
356
+ $args['_path'] = $this->path;
357
+ $args['_headers'] = $this->headers;
358
+ $args['_files'] = $this->files;
359
+
360
+ $args = apply_filters('aftership_api_dispatch_args', $args, $callback);
361
+
362
+ // Allow plugins to halt the request via this filter
363
+ if (is_wp_error($args)) {
364
+ return $args;
365
+ }
366
+
367
+ $params = $this->sort_callback_params($callback, $args);
368
+ if (is_wp_error($params))
369
+ return $params;
370
+
371
+ return call_user_func_array($callback, $params);
372
+ }
373
+ }
374
+
375
+ return new WP_Error('aftership_api_no_route', __('No route was found matching the URL and request method', 'aftership'), array('status' => 404));
376
+ }
377
+
378
+ /**
379
+ * Sort parameters by order specified in method declaration
380
+ *
381
+ * Takes a callback and a list of available params, then filters and sorts
382
+ * by the parameters the method actually needs, using the Reflection API
383
+ *
384
+ * @since 2.1
385
+ * @param callable|array $callback the endpoint callback
386
+ * @param array $provided the provided request parameters
387
+ * @return array
388
+ */
389
+ protected function sort_callback_params($callback, $provided)
390
+ {
391
+ if (is_array($callback))
392
+ $ref_func = new ReflectionMethod($callback[0], $callback[1]);
393
+ else
394
+ $ref_func = new ReflectionFunction($callback);
395
+
396
+ $wanted = $ref_func->getParameters();
397
+ $ordered_parameters = array();
398
+
399
+ foreach ($wanted as $param) {
400
+ if (isset($provided[$param->getName()])) {
401
+ // We have this parameters in the list to choose from
402
+
403
+ $ordered_parameters[] = is_array($provided[$param->getName()]) ? array_map('urldecode', $provided[$param->getName()]) : urldecode($provided[$param->getName()]);
404
+ } elseif ($param->isDefaultValueAvailable()) {
405
+ // We don't have this parameter, but it's optional
406
+ $ordered_parameters[] = $param->getDefaultValue();
407
+ } else {
408
+ // We don't have this parameter and it wasn't optional, abort!
409
+ return new WP_Error('aftership_api_missing_callback_param', sprintf(__('Missing parameter %s', 'aftership'), $param->getName()), array('status' => 400));
410
+ }
411
+ }
412
+ return $ordered_parameters;
413
+ }
414
+
415
+ /**
416
+ * Get the site index.
417
+ *
418
+ * This endpoint describes the capabilities of the site.
419
+ *
420
+ * @since 2.1
421
+ * @return array Index entity
422
+ */
423
+ public function get_index()
424
+ {
425
+
426
+ // General site data
427
+ $available = array('store' => array(
428
+ 'name' => get_option('blogname'),
429
+ 'description' => get_option('blogdescription'),
430
+ 'URL' => get_option('siteurl'),
431
+ 'wc_version' => WC()->version,
432
+ 'routes' => array(),
433
+ 'meta' => array(
434
+ 'timezone' => wc_timezone_string(),
435
+ 'currency' => get_aftership_currency(),
436
+ 'currency_format' => get_aftership_currency_symbol(),
437
+ 'tax_included' => ('yes' === get_option('aftership_prices_include_tax')),
438
+ 'weight_unit' => get_option('aftership_weight_unit'),
439
+ 'dimension_unit' => get_option('aftership_dimension_unit'),
440
+ 'ssl_enabled' => ('yes' === get_option('aftership_force_ssl_checkout')),
441
+ 'permalinks_enabled' => ('' !== get_option('permalink_structure')),
442
+ 'links' => array(
443
+ 'help' => 'https://aftership.uservoice.com/knowledgebase',
444
+ ),
445
+ ),
446
+ ));
447
+
448
+ // Find the available routes
449
+ foreach ($this->get_routes() as $route => $callbacks) {
450
+ $data = array();
451
+
452
+ $route = preg_replace('#\(\?P(<\w+?>).*?\)#', '$1', $route);
453
+ $methods = array();
454
+ foreach (self::$method_map as $name => $bitmask) {
455
+ foreach ($callbacks as $callback) {
456
+ // Skip to the next route if any callback is hidden
457
+ if ($callback[1] & self::HIDDEN_ENDPOINT)
458
+ continue 3;
459
+
460
+ if ($callback[1] & $bitmask)
461
+ $data['supports'][] = $name;
462
+
463
+ if ($callback[1] & self::ACCEPT_DATA)
464
+ $data['accepts_data'] = true;
465
+
466
+ // For non-variable routes, generate links
467
+ if (strpos($route, '<') === false) {
468
+ $data['meta'] = array(
469
+ 'self' => get_aftership_api_url($route),
470
+ );
471
+ }
472
+ }
473
+ }
474
+ $available['store']['routes'][$route] = apply_filters('aftership_api_endpoints_description', $data);
475
+ }
476
+ return apply_filters('aftership_api_index', $available);
477
+ }
478
+
479
+ /**
480
+ * Send a HTTP status code
481
+ *
482
+ * @since 2.1
483
+ * @param int $code HTTP status
484
+ */
485
+ public function send_status($code)
486
+ {
487
+ status_header($code);
488
+ }
489
+
490
+ /**
491
+ * Send a HTTP header
492
+ *
493
+ * @since 2.1
494
+ * @param string $key Header key
495
+ * @param string $value Header value
496
+ * @param boolean $replace Should we replace the existing header?
497
+ */
498
+ public function header($key, $value, $replace = true)
499
+ {
500
+ header(sprintf('%s: %s', $key, $value), $replace);
501
+ }
502
+
503
+ /**
504
+ * Send a Link header
505
+ *
506
+ * @internal The $rel parameter is first, as this looks nicer when sending multiple
507
+ *
508
+ * @link http://tools.ietf.org/html/rfc5988
509
+ * @link http://www.iana.org/assignments/link-relations/link-relations.xml
510
+ *
511
+ * @since 2.1
512
+ * @param string $rel Link relation. Either a registered type, or an absolute URL
513
+ * @param string $link Target IRI for the link
514
+ * @param array $other Other parameters to send, as an associative array
515
+ */
516
+ public function link_header($rel, $link, $other = array())
517
+ {
518
+
519
+ $header = sprintf('<%s>; rel="%s"', $link, esc_attr($rel));
520
+
521
+ foreach ($other as $key => $value) {
522
+
523
+ if ('title' == $key) {
524
+
525
+ $value = '"' . $value . '"';
526
+ }
527
+
528
+ $header .= '; ' . $key . '=' . $value;
529
+ }
530
+
531
+ $this->header('Link', $header, false);
532
+ }
533
+
534
+ /**
535
+ * Send pagination headers for resources
536
+ *
537
+ * @since 2.1
538
+ * @param WP_Query|WP_User_Query $query
539
+ */
540
+ public function add_pagination_headers($query)
541
+ {
542
+
543
+ // WP_User_Query
544
+ if (is_a($query, 'WP_User_Query')) {
545
+
546
+ $page = $query->page;
547
+ $single = count($query->get_results()) > 1;
548
+ $total = $query->get_total();
549
+ $total_pages = $query->total_pages;
550
+
551
+ // WP_Query
552
+ } else {
553
+
554
+ $page = $query->get('paged');
555
+ $single = $query->is_single();
556
+ $total = $query->found_posts;
557
+ $total_pages = $query->max_num_pages;
558
+ }
559
+
560
+ if (!$page)
561
+ $page = 1;
562
+
563
+ $next_page = absint($page) + 1;
564
+
565
+ if (!$single) {
566
+
567
+ // first/prev
568
+ if ($page > 1) {
569
+ $this->link_header('first', $this->get_paginated_url(1));
570
+ $this->link_header('prev', $this->get_paginated_url($page - 1));
571
+ }
572
+
573
+ // next
574
+ if ($next_page <= $total_pages) {
575
+ $this->link_header('next', $this->get_paginated_url($next_page));
576
+ }
577
+
578
+ // last
579
+ if ($page != $total_pages)
580
+ $this->link_header('last', $this->get_paginated_url($total_pages));
581
+ }
582
+
583
+ $this->header('X-WC-Total', $total);
584
+ $this->header('X-WC-TotalPages', $total_pages);
585
+
586
+ do_action('aftership_api_pagination_headers', $this, $query);
587
+ }
588
+
589
+ /**
590
+ * Returns the request URL with the page query parameter set to the specified page
591
+ *
592
+ * @since 2.1
593
+ * @param int $page
594
+ * @return string
595
+ */
596
+ private function get_paginated_url($page)
597
+ {
598
+
599
+ // remove existing page query param
600
+ $request = remove_query_arg('page');
601
+
602
+ // add provided page query param
603
+ $request = urldecode(add_query_arg('page', $page, $request));
604
+
605
+ // get the home host
606
+ $host = parse_url(get_home_url(), PHP_URL_HOST);
607
+
608
+ return set_url_scheme("http://{$host}{$request}");
609
+ }
610
+
611
+ /**
612
+ * Retrieve the raw request entity (body)
613
+ *
614
+ * @since 2.1
615
+ * @return string
616
+ */
617
+ public function get_raw_data()
618
+ {
619
+ global $HTTP_RAW_POST_DATA;
620
+
621
+ // A bug in PHP < 5.2.2 makes $HTTP_RAW_POST_DATA not set by default,
622
+ // but we can do it ourself.
623
+ if (!isset($HTTP_RAW_POST_DATA)) {
624
+ $HTTP_RAW_POST_DATA = file_get_contents('php://input');
625
+ }
626
+
627
+ return $HTTP_RAW_POST_DATA;
628
+ }
629
+
630
+ /**
631
+ * Parse an RFC3339 datetime into a MySQl datetime
632
+ *
633
+ * Invalid dates default to unix epoch
634
+ *
635
+ * @since 2.1
636
+ * @param string $datetime RFC3339 datetime
637
+ * @return string MySQl datetime (YYYY-MM-DD HH:MM:SS)
638
+ */
639
+ public function parse_datetime($datetime)
640
+ {
641
+
642
+ // Strip millisecond precision (a full stop followed by one or more digits)
643
+ if (strpos($datetime, '.') !== false) {
644
+ $datetime = preg_replace('/\.\d+/', '', $datetime);
645
+ }
646
+
647
+ // default timezone to UTC
648
+ $datetime = preg_replace('/[+-]\d+:+\d+$/', '+00:00', $datetime);
649
+
650
+ try {
651
+
652
+ $datetime = new DateTime($datetime, new DateTimeZone('UTC'));
653
+
654
+ } catch (Exception $e) {
655
+
656
+ $datetime = new DateTime('@0');
657
+
658
+ }
659
+
660
+ return $datetime->format('Y-m-d H:i:s');
661
+ }
662
+
663
+ /**
664
+ * Format a unix timestamp or MySQL datetime into an RFC3339 datetime
665
+ *
666
+ * @since 2.1
667
+ * @param int|string $timestamp unix timestamp or MySQL datetime
668
+ * @param bool $convert_to_utc
669
+ * @return string RFC3339 datetime
670
+ */
671
+ public function format_datetime($timestamp, $convert_to_utc = false)
672
+ {
673
+
674
+ if ($convert_to_utc) {
675
+ $timezone = new DateTimeZone(wc_timezone_string());
676
+ } else {
677
+ $timezone = new DateTimeZone('UTC');
678
+ }
679
+
680
+ try {
681
+
682
+ if (is_numeric($timestamp)) {
683
+ $date = new DateTime("@{$timestamp}");
684
+ } else {
685
+ $date = new DateTime($timestamp, $timezone);
686
+ }
687
+
688
+ // convert to UTC by adjusting the time based on the offset of the site's timezone
689
+ if ($convert_to_utc) {
690
+ $date->modify(-1 * $date->getOffset() . ' seconds');
691
+ }
692
+
693
+ } catch (Exception $e) {
694
+
695
+ $date = new DateTime('@0');
696
+ }
697
+
698
+ return $date->format('Y-m-d\TH:i:s\Z');
699
+ }
700
+
701
+ /**
702
+ * Extract headers from a PHP-style $_SERVER array
703
+ *
704
+ * @since 2.1
705
+ * @param array $server Associative array similar to $_SERVER
706
+ * @return array Headers extracted from the input
707
+ */
708
+ public function get_headers($server)
709
+ {
710
+ $headers = array();
711
+ // CONTENT_* headers are not prefixed with HTTP_
712
+ $additional = array('CONTENT_LENGTH' => true, 'CONTENT_MD5' => true, 'CONTENT_TYPE' => true);
713
+
714
+ foreach ($server as $key => $value) {
715
+ if (strpos($key, 'HTTP_') === 0) {
716
+ $headers[substr($key, 5)] = $value;
717
+ } elseif (isset($additional[$key])) {
718
+ $headers[$key] = $value;
719
+ }
720
+ }
721
+
722
+ return $headers;
723
+ }
724
+
725
+ /**
726
+ * Check if the current request accepts a JSON response by checking the endpoint suffix (.json) or
727
+ * the HTTP ACCEPT header
728
+ *
729
+ * @since 2.1
730
+ * @return bool
731
+ */
732
+ private function is_json_request()
733
+ {
734
+
735
+ // check path
736
+ if (false !== stripos($this->path, '.json'))
737
+ return true;
738
+
739
+ // check ACCEPT header, only 'application/json' is acceptable, see RFC 4627
740
+ if (isset($this->headers['ACCEPT']) && 'application/json' == $this->headers['ACCEPT'])
741
+ return true;
742
+
743
+ return false;
744
+ }
745
+
746
+ /**
747
+ * Check if the current request accepts an XML response by checking the endpoint suffix (.xml) or
748
+ * the HTTP ACCEPT header
749
+ *
750
+ * @since 2.1
751
+ * @return bool
752
+ */
753
+ private function is_xml_request()
754
+ {
755
+
756
+ // check path
757
+ if (false !== stripos($this->path, '.xml'))
758
+ return true;
759
+
760
+ // check headers, 'application/xml' or 'text/xml' are acceptable, see RFC 2376
761
+ if (isset($this->headers['ACCEPT']) && ('application/xml' == $this->headers['ACCEPT'] || 'text/xml' == $this->headers['ACCEPT']))
762
+ return true;
763
+
764
+ return false;
765
+ }
766
+ }
trunk/api/interface-aftership-api-handler.php ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AfterShip API
4
+ *
5
+ * Defines an interface that API request/response handlers should implement
6
+ *
7
+ * @author AfterShip
8
+ * @category API
9
+ * @package AfterShip/API
10
+ * @since 1.0
11
+ */
12
+
13
+ if (!defined('ABSPATH')) exit; // Exit if accessed directly
14
+
15
+ interface AfterShip_API_Handler
16
+ {
17
+
18
+ /**
19
+ * Get the content type for the response
20
+ *
21
+ * This should return the proper HTTP content-type for the response
22
+ *
23
+ * @since 2.1
24
+ * @return string
25
+ */
26
+ public function get_content_type();
27
+
28
+ /**
29
+ * Parse the raw request body entity into an array
30
+ *
31
+ * @since 2.1
32
+ * @param string $data
33
+ * @return array
34
+ */
35
+ public function parse_body($data);
36
+
37
+ /**
38
+ * Generate a response from an array of data
39
+ *
40
+ * @since 2.1
41
+ * @param array $data
42
+ * @return string
43
+ */
44
+ public function generate_response($data);
45
+
46
+ }
trunk/assets/css/admin.css ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .hidden-field {
2
+ display: none;
3
+ }
4
+
5
+ #aftership_tracking_provider {
6
+ width: 100%;
7
+ }
8
+
9
+ .aftership_input_text {
10
+ width: 300px;
11
+ }
12
+
13
+ .aftership_dropdown {
14
+ width: 300px;
15
+ }
trunk/assets/js/admin.js ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function() {
2
+
3
+
4
+ var providers;
5
+
6
+
7
+ function set_aftership_tracking_provider() {
8
+ function hide_input_and_label(id) {
9
+ jQuery('#' + id).hide();
10
+ jQuery('label[for=' + id + ']').hide();
11
+ }
12
+
13
+ function show_input_and_label(id) {
14
+ jQuery('#' + id).show();
15
+ jQuery('label[for=' + id + ']').show();
16
+ }
17
+
18
+ jQuery('#aftership_tracking_provider').change(function() {
19
+ hide_input_and_label('aftership_tracking_shipdate');
20
+ hide_input_and_label('aftership_tracking_postal');
21
+ hide_input_and_label('aftership_tracking_account');
22
+
23
+ var slug = jQuery(this).val();
24
+ if (slug) {
25
+ var provider = providers[slug];
26
+ var fields = [];
27
+ if (jQuery.isArray(provider.required_fields)) {
28
+ fields = provider.required_fields;
29
+ } else {
30
+ fields.push(provider.required_fields);
31
+ }
32
+ jQuery.each(fields, function(index, item) {
33
+ if (item == 'tracking_ship_date') {
34
+ show_input_and_label('aftership_tracking_shipdate');
35
+ } else if (item == 'tracking_postal_code') {
36
+ show_input_and_label('aftership_tracking_postal');
37
+ } else if (item == 'tracking_account_number') {
38
+ show_input_and_label('aftership_tracking_account');
39
+ }
40
+ });
41
+ jQuery('#aftership_tracking_provider_name').val(provider.name);
42
+ jQuery('#aftership_tracking_required_fields').val(fields.join());
43
+ }
44
+ });
45
+ }
46
+
47
+
48
+ function fill_meta_box(couriers_selected) {
49
+ var response = get_couriers();
50
+ var couriers = [];
51
+ jQuery.each(response, function(index, courier) {
52
+ if (couriers_selected.indexOf(courier.slug) != -1) {
53
+ couriers.push(courier);
54
+ }
55
+ });
56
+
57
+ var selected_provider = jQuery('#aftership_tracking_provider_hidden').val();
58
+ var find_selected_provider = couriers_selected.indexOf(selected_provider) != -1;
59
+ if (!find_selected_provider) {
60
+ couriers.push({
61
+ slug: selected_provider,
62
+ name: jQuery("#aftership_tracking_provider_name").val(),
63
+ required_fields: jQuery("#aftership_tracking_required_fields").val()
64
+ });
65
+ }
66
+ // console.log(couriers);
67
+
68
+ couriers = sort_couriers(couriers);
69
+
70
+ jQuery.each(couriers, function(key, courier) {
71
+ var str = '<option ';
72
+ if (!find_selected_provider && courier['slug'] == selected_provider) {
73
+ str += 'style="display:none;" ';
74
+ }
75
+ str += 'value="' + courier['slug'] + '" ';
76
+ if (courier['slug'] == selected_provider) {
77
+ str += 'selected="selected"';
78
+ }
79
+ str += '>' + courier['name'] + '</option>';
80
+ jQuery('#aftership_tracking_provider').append(str);
81
+ });
82
+ // jQuery('#aftership_tracking_provider').val(selected_provider);
83
+ jQuery('#aftership_tracking_provider').trigger("chosen:updated");
84
+ jQuery('#aftership_tracking_provider_chosen').css({width: '100%'});
85
+
86
+ providers = {};
87
+ jQuery.each(couriers, function(index,courier){
88
+ providers[courier.slug] = courier;
89
+ });
90
+ set_aftership_tracking_provider();
91
+ jQuery('#aftership_tracking_provider').trigger('change');
92
+ }
93
+
94
+ if (jQuery('#aftership_tracking_provider')) {
95
+ var couriers_selected = jQuery('#aftership_couriers_selected').val();
96
+ var couriers_selected_arr = (couriers_selected) ? couriers_selected.split(',') : [];
97
+ fill_meta_box(couriers_selected_arr);
98
+ }
99
+ });
trunk/assets/js/couriers.js ADDED
@@ -0,0 +1,1779 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function get_couriers() {
2
+ var data = [
3
+ {
4
+ "slug": "india-post-int",
5
+ "name": "India Post International",
6
+ "phone": "+91 1800 11 2011",
7
+ "other_name": "भारतीय डाक, Speed Post & eMO, EMS, IPS Web",
8
+ "web_url": "http://www.indiapost.gov.in/",
9
+ "required_fields": []
10
+ },
11
+ {
12
+ "slug": "couriers-please",
13
+ "name": "Couriers Please",
14
+ "phone": "+61 1300 361 000",
15
+ "other_name": "CouriersPlease",
16
+ "web_url": "http://www.couriersplease.com.au/",
17
+ "required_fields": []
18
+ },
19
+ {
20
+ "slug": "italy-sda",
21
+ "name": "Italy SDA",
22
+ "phone": "+39 199 113366",
23
+ "other_name": "SDA Express Courier",
24
+ "web_url": "http://www.sda.it/",
25
+ "required_fields": []
26
+ },
27
+ {
28
+ "slug": "chronopost-portugal",
29
+ "name": "Chronopost Portugal",
30
+ "phone": "+351 707 20 28 28",
31
+ "other_name": "Chronopost pt",
32
+ "web_url": "http://chronopost.pt/",
33
+ "required_fields": []
34
+ },
35
+ {
36
+ "slug": "ptt-posta",
37
+ "name": "PTT Posta",
38
+ "phone": "+90 444 1 788",
39
+ "other_name": "Turkish Post",
40
+ "web_url": "http://www.ptt.gov.tr/",
41
+ "required_fields": []
42
+ },
43
+ {
44
+ "slug": "fastway-za",
45
+ "name": "Fastway South Africa",
46
+ "phone": "+27 0861 222 882",
47
+ "other_name": "Fastway Couriers",
48
+ "web_url": "http://www.fastway.co.za/",
49
+ "required_fields": []
50
+ },
51
+ {
52
+ "slug": "russian-post",
53
+ "name": "Russian Post",
54
+ "phone": "+7 (495) 956-20-67",
55
+ "other_name": "Почта России, EMS Post RU",
56
+ "web_url": "http://www.russianpost.ru/",
57
+ "required_fields": []
58
+ },
59
+ {
60
+ "slug": "spain-correos-es",
61
+ "name": "Correos de España",
62
+ "phone": "+34 902197197",
63
+ "other_name": "Spain Post, ChronoExpress",
64
+ "web_url": "http://www.correos.es",
65
+ "required_fields": []
66
+ },
67
+ {
68
+ "slug": "saudi-post",
69
+ "name": "Saudi Post",
70
+ "phone": "+966 9200 05700",
71
+ "other_name": "البريد السعودي",
72
+ "web_url": "http://www.sp.com.sa/",
73
+ "required_fields": []
74
+ },
75
+ {
76
+ "slug": "dpd-ireland",
77
+ "name": "DPD Ireland",
78
+ "phone": "+353 (0)90 64 20500",
79
+ "other_name": "DPD ie",
80
+ "web_url": "http://www.dpd.ie/",
81
+ "required_fields": []
82
+ },
83
+ {
84
+ "slug": "austrian-post-registered",
85
+ "name": "Austrian Post (Registered)",
86
+ "phone": "+43 810 010 100",
87
+ "other_name": "Österreichische Post AG",
88
+ "web_url": "http://www.post.at",
89
+ "required_fields": []
90
+ },
91
+ {
92
+ "slug": "kerry-logistics",
93
+ "name": "Kerry Express Thailand",
94
+ "phone": "+66 02-6868999",
95
+ "other_name": "嘉里物流, Kerry Logistics",
96
+ "web_url": "http://www.kerrylogistics.com/",
97
+ "required_fields": []
98
+ },
99
+ {
100
+ "slug": "posten-norge",
101
+ "name": "Posten Norge / Bring",
102
+ "phone": "+47 21316260",
103
+ "other_name": "Norway Post, Norska Posten",
104
+ "web_url": "http://www.posten.no/en/",
105
+ "required_fields": []
106
+ },
107
+ {
108
+ "slug": "hong-kong-post",
109
+ "name": "Hong Kong Post",
110
+ "phone": "+852 2921 2222",
111
+ "other_name": "香港郵政",
112
+ "web_url": "http://hongkongpost.com",
113
+ "required_fields": []
114
+ },
115
+ {
116
+ "slug": "portugal-seur",
117
+ "name": "Portugal Seur",
118
+ "phone": "+351 707 50 10 10",
119
+ "other_name": "SEUR",
120
+ "web_url": "http://www.seur.com/",
121
+ "required_fields": []
122
+ },
123
+ {
124
+ "slug": "international-seur",
125
+ "name": "International Seur",
126
+ "phone": "+34 902101010",
127
+ "other_name": "SEUR Internacional",
128
+ "web_url": "http://www.seur.com/",
129
+ "required_fields": []
130
+ },
131
+ {
132
+ "slug": "4px",
133
+ "name": "4PX",
134
+ "phone": "+86 755-33936349",
135
+ "other_name": "递四方",
136
+ "web_url": "http://express.4px.com/",
137
+ "required_fields": []
138
+ },
139
+ {
140
+ "slug": "yrc",
141
+ "name": "YRC",
142
+ "phone": "+1 800-468-5739",
143
+ "other_name": "YRC Freight",
144
+ "web_url": "http://www.yrc.com/",
145
+ "required_fields": []
146
+ },
147
+ {
148
+ "slug": "dpd-de",
149
+ "name": "DPD Germany",
150
+ "phone": "+49 01806 373 200",
151
+ "other_name": "DPD Germany",
152
+ "web_url": "https://www.dpd.com/",
153
+ "required_fields": []
154
+ },
155
+ {
156
+ "slug": "delhivery",
157
+ "name": "Delhivery",
158
+ "phone": "+91 (124) 4718900",
159
+ "other_name": "Gharpay",
160
+ "web_url": "http://www.delhivery.com/",
161
+ "required_fields": []
162
+ },
163
+ {
164
+ "slug": "canada-post",
165
+ "name": "Canada Post",
166
+ "phone": "+1 866 607 6301",
167
+ "other_name": "Postes Canada",
168
+ "web_url": "https://www.canadapost.ca/",
169
+ "required_fields": []
170
+ },
171
+ {
172
+ "slug": "aramex",
173
+ "name": "Aramex",
174
+ "phone": "+1 (718) 5538740",
175
+ "other_name": "ارامكس",
176
+ "web_url": "http://www.aramex.com/",
177
+ "required_fields": []
178
+ },
179
+ {
180
+ "slug": "gati-kwe",
181
+ "name": "Gati-KWE",
182
+ "phone": "+91 1800-180-4284",
183
+ "other_name": "Gati-Kintetsu Express",
184
+ "web_url": "http://www.gatikwe.com/",
185
+ "required_fields": []
186
+ },
187
+ {
188
+ "slug": "dhl-global-mail-asia",
189
+ "name": "DHL Global Mail Asia",
190
+ "phone": "+65 6883 0771",
191
+ "other_name": "DGM Asia",
192
+ "web_url": "http://www.dhlglobalmail.com/",
193
+ "required_fields": []
194
+ },
195
+ {
196
+ "slug": "dhl-poland",
197
+ "name": "DHL Poland",
198
+ "phone": "+48 42 6 345 345",
199
+ "other_name": "DHL Polska",
200
+ "web_url": "http://www.dhl.com.pl/",
201
+ "required_fields": []
202
+ },
203
+ {
204
+ "slug": "tnt-uk",
205
+ "name": "TNT UK",
206
+ "phone": "+44 0800 100 600",
207
+ "other_name": "TNT United Kingdom",
208
+ "web_url": "http://www.tnt.com/",
209
+ "required_fields": []
210
+ },
211
+ {
212
+ "slug": "asendia-usa",
213
+ "name": "Asendia USA",
214
+ "phone": "+1 610 461 3661",
215
+ "other_name": "Brokers Worldwide",
216
+ "web_url": "http://www.brokersworldwide.com/",
217
+ "required_fields": []
218
+ },
219
+ {
220
+ "slug": "posti",
221
+ "name": "Itella Posti",
222
+ "phone": "+358 200 71000",
223
+ "other_name": "Finland Post",
224
+ "web_url": "http://www.posti.fi/",
225
+ "required_fields": []
226
+ },
227
+ {
228
+ "slug": "tnt-it",
229
+ "name": "TNT Italy",
230
+ "phone": "+39 199 803 868",
231
+ "other_name": "TNT Express IT",
232
+ "web_url": "http://www.tnt.it/",
233
+ "required_fields": []
234
+ },
235
+ {
236
+ "slug": "nova-poshta",
237
+ "name": "Nova Poshta",
238
+ "phone": "+7 (0) 800 500 609",
239
+ "other_name": "Новая Почта",
240
+ "web_url": "http://novaposhta.ua/",
241
+ "required_fields": []
242
+ },
243
+ {
244
+ "slug": "cambodia-post",
245
+ "name": "Cambodia Post",
246
+ "phone": "+855 23 723 51",
247
+ "other_name": "Cambodia Post",
248
+ "web_url": "http://www.ems.com.kh/",
249
+ "required_fields": []
250
+ },
251
+ {
252
+ "slug": "fedex-uk",
253
+ "name": "FedEx UK",
254
+ "phone": "+ 44 2476 706 660",
255
+ "other_name": "FedEx United Kingdom",
256
+ "web_url": "http://www.fedex.com/ukservices",
257
+ "required_fields": []
258
+ },
259
+ {
260
+ "slug": "xend",
261
+ "name": "Xend",
262
+ "phone": "+63 2 806 9363",
263
+ "other_name": "Xend Business Solutions",
264
+ "web_url": "http://xendexpress.com/",
265
+ "required_fields": []
266
+ },
267
+ {
268
+ "slug": "2go",
269
+ "name": "2GO",
270
+ "phone": "+63 2 77-99-222",
271
+ "other_name": "Negros Navigation",
272
+ "web_url": "http://supplychain.2go.com.ph/",
273
+ "required_fields": []
274
+ },
275
+ {
276
+ "slug": "chronopost-france",
277
+ "name": "Chronopost France",
278
+ "phone": "+33 (0) 969 391 391",
279
+ "other_name": "La Poste EMS",
280
+ "web_url": "http://www.chronopost.fr/",
281
+ "required_fields": []
282
+ },
283
+ {
284
+ "slug": "tnt-au",
285
+ "name": "TNT Australia",
286
+ "phone": "+61 13 11 50",
287
+ "other_name": "TNT AU",
288
+ "web_url": "http://www.tntexpress.com.au/",
289
+ "required_fields": []
290
+ },
291
+ {
292
+ "slug": "acscourier",
293
+ "name": "ACS Courier",
294
+ "phone": "+30 210 81 90 000",
295
+ "other_name": "Αναζήτηση Καταστημάτων",
296
+ "web_url": "https://www.acscourier.net/",
297
+ "required_fields": []
298
+ },
299
+ {
300
+ "slug": "uk-mail",
301
+ "name": "UK Mail",
302
+ "phone": "+44 8451 554 455",
303
+ "other_name": "Business Post Group",
304
+ "web_url": "https://www.ukmail.com",
305
+ "required_fields": []
306
+ },
307
+ {
308
+ "slug": "siodemka",
309
+ "name": "Siodemka",
310
+ "phone": "+48 22 777 77 77 ext. 3",
311
+ "other_name": "Siodemka Kurier",
312
+ "web_url": "http://www.siodemka.com/",
313
+ "required_fields": []
314
+ },
315
+ {
316
+ "slug": "kn",
317
+ "name": "Kuehne + Nagel",
318
+ "phone": "+41-44-7869511",
319
+ "other_name": "KN",
320
+ "web_url": "http://www.kn-portal.com/",
321
+ "required_fields": []
322
+ },
323
+ {
324
+ "slug": "safexpress",
325
+ "name": "Safexpress",
326
+ "phone": "+91 1800 113 113",
327
+ "other_name": "Safexpress",
328
+ "web_url": "http://www.safexpress.com/",
329
+ "required_fields": []
330
+ },
331
+ {
332
+ "slug": "skynet",
333
+ "name": "SkyNet Malaysia",
334
+ "phone": "+60 3- 56239090",
335
+ "other_name": "SkyNet MY",
336
+ "web_url": "http://www.skynet.com.my/",
337
+ "required_fields": []
338
+ },
339
+ {
340
+ "slug": "nipost",
341
+ "name": "NiPost",
342
+ "phone": "+234 09-3149531",
343
+ "other_name": "Nigerian Postal Service",
344
+ "web_url": "http://www.emsng.com/",
345
+ "required_fields": []
346
+ },
347
+ {
348
+ "slug": "oca-ar",
349
+ "name": "OCA Argentina",
350
+ "phone": "+34 800-999-7700",
351
+ "other_name": "OCA e-Pak",
352
+ "web_url": "http://www.oca.com.ar/",
353
+ "required_fields": []
354
+ },
355
+ {
356
+ "slug": "aeroflash",
357
+ "name": "Mexico AeroFlash",
358
+ "phone": "+52 55 5445 2100",
359
+ "other_name": "AeroFlash",
360
+ "web_url": "http://www.aeroflash.com.mx/",
361
+ "required_fields": []
362
+ },
363
+ {
364
+ "slug": "sapo",
365
+ "name": "South African Post Office",
366
+ "phone": "+27 0860 111 502",
367
+ "other_name": "South African Post Office",
368
+ "web_url": "http://www.postoffice.co.za/",
369
+ "required_fields": []
370
+ },
371
+ {
372
+ "slug": "taqbin-hk",
373
+ "name": "TAQBIN Hong Kong",
374
+ "phone": "+852 2829 2222",
375
+ "other_name": "Yamat, 雅瑪多運輸- 宅急便",
376
+ "web_url": "http://hk.ta-q-bin.com/",
377
+ "required_fields": []
378
+ },
379
+ {
380
+ "slug": "deltec-courier",
381
+ "name": "Deltec Courier",
382
+ "phone": "+44 (0) 20 8569 6767",
383
+ "other_name": "Deltec Interntional Courier",
384
+ "web_url": "https://www.deltec-courier.com/",
385
+ "required_fields": []
386
+ },
387
+ {
388
+ "slug": "mexico-senda-express",
389
+ "name": "Mexico Senda Express",
390
+ "phone": "+52 1800 833 93 00",
391
+ "other_name": "Mexico Senda Express",
392
+ "web_url": "http://www.sendaexpress.com.mx/",
393
+ "required_fields": []
394
+ },
395
+ {
396
+ "slug": "mexico-redpack",
397
+ "name": "Mexico Redpack",
398
+ "phone": "+52 1800-013-3333",
399
+ "other_name": "TNT Mexico",
400
+ "web_url": "http://www.redpack.com.mx/",
401
+ "required_fields": []
402
+ },
403
+ {
404
+ "slug": "mexico-multipack",
405
+ "name": "Mexico Multipack",
406
+ "phone": "+52 1800 7023200",
407
+ "other_name": "Fedex Mexico",
408
+ "web_url": "http://www.multipack.com.mx/",
409
+ "required_fields": []
410
+ },
411
+ {
412
+ "slug": "japan-post",
413
+ "name": "Japan Post",
414
+ "phone": "+81 0570-046111",
415
+ "other_name": "日本郵便",
416
+ "web_url": "http://www.post.japanpost.jp/top.html",
417
+ "required_fields": []
418
+ },
419
+ {
420
+ "slug": "sagawa",
421
+ "name": "Sagawa",
422
+ "phone": "+81 0120-18-9595",
423
+ "other_name": "佐川急便",
424
+ "web_url": "http://www.sagawa-exp.co.jp/",
425
+ "required_fields": []
426
+ },
427
+ {
428
+ "slug": "tgx",
429
+ "name": "TGX",
430
+ "phone": "+852 3513 0888",
431
+ "other_name": "Top Gun Express, 精英速運",
432
+ "web_url": "http://www.tgxpress.com/",
433
+ "required_fields": []
434
+ },
435
+ {
436
+ "slug": "vnpost-ems",
437
+ "name": "Vietnam Post EMS",
438
+ "phone": "+84 1900 54 54 81",
439
+ "other_name": "VNPost EMS",
440
+ "web_url": "http://www.vnpost.vn/",
441
+ "required_fields": []
442
+ },
443
+ {
444
+ "slug": "viettelpost",
445
+ "name": "ViettelPost",
446
+ "phone": "+84-4 6266 2200",
447
+ "other_name": "Bưu chính Viettel",
448
+ "web_url": "http://www.viettelpost.com.vn/",
449
+ "required_fields": []
450
+ },
451
+ {
452
+ "slug": "postnl-3s",
453
+ "name": "PostNL International 3S",
454
+ "phone": "+31 (0)900 0990",
455
+ "other_name": "TNT Post parcel service United Kingdom",
456
+ "web_url": "http://www.postnl.nl/voorthuis/",
457
+ "required_fields": []
458
+ },
459
+ {
460
+ "slug": "tiki",
461
+ "name": "Tiki",
462
+ "phone": "+62 500 125",
463
+ "other_name": "Citra Van Titipan Kilat",
464
+ "web_url": "http://www.tiki-online.com/",
465
+ "required_fields": []
466
+ },
467
+ {
468
+ "slug": "opek",
469
+ "name": "OPEK",
470
+ "phone": "+48 22 732 79 99",
471
+ "other_name": "OPEK Sp. z oo",
472
+ "web_url": "http://www.opek.com.pl/",
473
+ "required_fields": []
474
+ },
475
+ {
476
+ "slug": "dpd-poland",
477
+ "name": "DPD Poland",
478
+ "phone": "+48 801 400 373",
479
+ "other_name": "Dynamic Parcel Distribution Poland",
480
+ "web_url": "http://www.dpd.com.pl/",
481
+ "required_fields": []
482
+ },
483
+ {
484
+ "slug": "ceska-posta",
485
+ "name": "Česká Pošta",
486
+ "phone": "+420 840 111 244",
487
+ "other_name": "Czech Post",
488
+ "web_url": "http://www.ceskaposta.cz/",
489
+ "required_fields": []
490
+ },
491
+ {
492
+ "slug": "geodis-calberson-fr",
493
+ "name": "Geodis Calberson France",
494
+ "phone": "+33 (0)1 70 15 16 17",
495
+ "other_name": "Geodiscalberson",
496
+ "web_url": "http://www.espacedestinataire.mobi/client/42228/mobile/index.html",
497
+ "required_fields": [
498
+ "tracking_postal_code"
499
+ ]
500
+ },
501
+ {
502
+ "slug": "ppbyb",
503
+ "name": "PayPal Package",
504
+ "phone": "+86 20 11185",
505
+ "other_name": "贝邮宝",
506
+ "web_url": "http://www.ppbyb.com/",
507
+ "required_fields": []
508
+ },
509
+ {
510
+ "slug": "apc",
511
+ "name": "APC Postal Logistics",
512
+ "phone": "+1 (888) 413-7300",
513
+ "other_name": "APC-PLI",
514
+ "web_url": "http://www.apc-pli.com/",
515
+ "required_fields": []
516
+ },
517
+ {
518
+ "slug": "red-express-wb",
519
+ "name": "Red Express Waybill",
520
+ "phone": "+91 1800-123-2400",
521
+ "other_name": "Red Express WayBill",
522
+ "web_url": "https://www.getsetred.net",
523
+ "required_fields": []
524
+ },
525
+ {
526
+ "slug": "professional-couriers",
527
+ "name": "Professional Couriers",
528
+ "phone": "+91 0124-4405730",
529
+ "other_name": "TPC India",
530
+ "web_url": "http://www.tpcindia.com/",
531
+ "required_fields": []
532
+ },
533
+ {
534
+ "slug": "gojavas",
535
+ "name": "GoJaVAS",
536
+ "phone": "+91 0124-4405730",
537
+ "other_name": "JaVAS",
538
+ "web_url": "http://gojavas.com/",
539
+ "required_fields": []
540
+ },
541
+ {
542
+ "slug": "cj-gls",
543
+ "name": "CJ GLS",
544
+ "phone": "+63-567-1320",
545
+ "other_name": "CJ Korea Express, 씨제이지엘에스주식회사",
546
+ "web_url": "http://www.cjgls.com/eng/",
547
+ "required_fields": []
548
+ },
549
+ {
550
+ "slug": "estafeta",
551
+ "name": "Estafeta",
552
+ "phone": "+52 1-800-378-2338",
553
+ "other_name": "Estafeta Mexicana",
554
+ "web_url": "http://www.estafeta.com/default.aspx",
555
+ "required_fields": []
556
+ },
557
+ {
558
+ "slug": "ups-freight",
559
+ "name": "UPS Freight",
560
+ "phone": "+1 800-333-7400",
561
+ "other_name": "UPS LTL and Truckload",
562
+ "web_url": "http://ltl.upsfreight.com/",
563
+ "required_fields": []
564
+ },
565
+ {
566
+ "slug": "singapore-speedpost",
567
+ "name": "Singapore Speedpost",
568
+ "phone": "+65 6222 5777",
569
+ "other_name": "Singapore EMS",
570
+ "web_url": "http://www.speedpost.com.sg/",
571
+ "required_fields": []
572
+ },
573
+ {
574
+ "slug": "purolator",
575
+ "name": "Purolator",
576
+ "phone": "+1-888-744-7123",
577
+ "other_name": "Purolator Freight",
578
+ "web_url": "http://www.purolator.com/",
579
+ "required_fields": []
580
+ },
581
+ {
582
+ "slug": "toll-global-express",
583
+ "name": "Toll Global Express",
584
+ "phone": "+61 13 15 31",
585
+ "other_name": "DPEX",
586
+ "web_url": "http://www.dpex.com/",
587
+ "required_fields": []
588
+ },
589
+ {
590
+ "slug": "ontrac",
591
+ "name": "OnTrac",
592
+ "phone": "+1 800 334 5000",
593
+ "other_name": "OnTrac Shipping",
594
+ "web_url": "https://www.ontrac.com",
595
+ "required_fields": []
596
+ },
597
+ {
598
+ "slug": "evergreen",
599
+ "name": "Evergreen",
600
+ "phone": "+1 201-7613000",
601
+ "other_name": "Evergreen Marine Corp",
602
+ "web_url": "http://www.evergreen-marine.com/",
603
+ "required_fields": []
604
+ },
605
+ {
606
+ "slug": "korea-post",
607
+ "name": "Korea Post",
608
+ "phone": "+82 2 2195 1114",
609
+ "other_name": "우정사업본부",
610
+ "web_url": "http://www.koreapost.go.kr",
611
+ "required_fields": []
612
+ },
613
+ {
614
+ "slug": "nacex-spain",
615
+ "name": "NACEX Spain",
616
+ "phone": "+34 900 100 000",
617
+ "other_name": "NACEX Logista",
618
+ "web_url": "http://nacex.es/",
619
+ "required_fields": [
620
+ "tracking_postal_code"
621
+ ]
622
+ },
623
+ {
624
+ "slug": "lasership",
625
+ "name": "LaserShip",
626
+ "phone": "+1 (800) 527-3764",
627
+ "other_name": "LaserShip",
628
+ "web_url": "http://www.lasership.com/",
629
+ "required_fields": []
630
+ },
631
+ {
632
+ "slug": "china-ems",
633
+ "name": "China EMS",
634
+ "phone": "+86 20 11183",
635
+ "other_name": "中国邮政速递物流",
636
+ "web_url": "http://www.11183.com.cn/english.html",
637
+ "required_fields": []
638
+ },
639
+ {
640
+ "slug": "china-post",
641
+ "name": "China Post",
642
+ "phone": "+86 20 11185",
643
+ "other_name": "中国邮政, ePacket, e-Packet",
644
+ "web_url": "http://www.chinapost.com.cn/",
645
+ "required_fields": []
646
+ },
647
+ {
648
+ "slug": "taqbin-sg",
649
+ "name": "TAQBIN Singapore",
650
+ "phone": "+65 6595 4640",
651
+ "other_name": "Yamato Singapore",
652
+ "web_url": "http://sg.ta-q-bin.com/",
653
+ "required_fields": []
654
+ },
655
+ {
656
+ "slug": "bgpost",
657
+ "name": "Bulgarian Posts",
658
+ "phone": "+3592/949 3280",
659
+ "other_name": "Български пощи",
660
+ "web_url": "http://www.bgpost.bg/",
661
+ "required_fields": []
662
+ },
663
+ {
664
+ "slug": "brazil-correios",
665
+ "name": "Brazil Correios",
666
+ "phone": "+55 61 3003 0100",
667
+ "other_name": "Brazilian Post",
668
+ "web_url": "http://correios.com.br/",
669
+ "required_fields": []
670
+ },
671
+ {
672
+ "slug": "fedex",
673
+ "name": "FedEx",
674
+ "phone": "+1 800 247 4747",
675
+ "other_name": "Federal Express",
676
+ "web_url": "http://www.fedex.com/",
677
+ "required_fields": []
678
+ },
679
+ {
680
+ "slug": "ukrposhta",
681
+ "name": "UkrPoshta",
682
+ "phone": "+380 (0) 800-500-440",
683
+ "other_name": "Укрпошта",
684
+ "web_url": "http://www.ukrposhta.com/",
685
+ "required_fields": []
686
+ },
687
+ {
688
+ "slug": "toll-priority",
689
+ "name": "Toll Priority",
690
+ "phone": "+61 13 15 31",
691
+ "other_name": "Toll Group, Toll Priority",
692
+ "web_url": "https://www.tollpriority.com.au/portal/page/portal/TOLL_PRIORITY/Home",
693
+ "required_fields": []
694
+ },
695
+ {
696
+ "slug": "jam-express",
697
+ "name": "Jam Express",
698
+ "phone": "+63 239 7502",
699
+ "other_name": "JAM Global Express",
700
+ "web_url": "http://www.myjamexpress.com/",
701
+ "required_fields": []
702
+ },
703
+ {
704
+ "slug": "estes",
705
+ "name": "Estes",
706
+ "phone": "+1-886-378-3748",
707
+ "other_name": "Estes Express Lines",
708
+ "web_url": "http://www.estes-express.com",
709
+ "required_fields": []
710
+ },
711
+ {
712
+ "slug": "flytexpress",
713
+ "name": "Flyt Express",
714
+ "phone": "+1 400-888-4003",
715
+ "other_name": "飞特物流",
716
+ "web_url": "http://www.flytexpress.com/",
717
+ "required_fields": []
718
+ },
719
+ {
720
+ "slug": "rpxonline",
721
+ "name": "RPX Online",
722
+ "phone": "+852 2620 0289",
723
+ "other_name": "Cathay Pacific",
724
+ "web_url": "http://www.rpxonline.com/",
725
+ "required_fields": []
726
+ },
727
+ {
728
+ "slug": "17postservice",
729
+ "name": "17 Post Service",
730
+ "phone": "+852 2620 0289",
731
+ "other_name": "17PostService",
732
+ "web_url": "http://17postservice.com/",
733
+ "required_fields": []
734
+ },
735
+ {
736
+ "slug": "ghn",
737
+ "name": "Giao hàng nhanh",
738
+ "phone": "+84 19006491",
739
+ "other_name": "Giaohangnhanh.vn, GHN",
740
+ "web_url": "http://giaohangnhanh.vn/",
741
+ "required_fields": []
742
+ },
743
+ {
744
+ "slug": "raf",
745
+ "name": "RAF Philippines",
746
+ "phone": "+632 820-2920 to 25",
747
+ "other_name": "RAF Int'l. Forwarding",
748
+ "web_url": "http://www.raf.ph/",
749
+ "required_fields": []
750
+ },
751
+ {
752
+ "slug": "trakpak",
753
+ "name": "TrakPak",
754
+ "phone": "+27 21 528 1000",
755
+ "other_name": "bpost international P2P Mailing Trak Pak",
756
+ "web_url": "http://www.trackmytrakpak.com/",
757
+ "required_fields": []
758
+ },
759
+ {
760
+ "slug": "lietuvos-pastas",
761
+ "name": "Lietuvos paštas",
762
+ "phone": "+370 8 700 55 400",
763
+ "other_name": "Lithuania Post, LP Express",
764
+ "web_url": "http://www.post.lt/",
765
+ "required_fields": []
766
+ },
767
+ {
768
+ "slug": "equick-cn",
769
+ "name": "Equick China",
770
+ "phone": "+1 400 706 6078",
771
+ "other_name": "北京网易速达",
772
+ "web_url": "http://www.equick.cn/",
773
+ "required_fields": []
774
+ },
775
+ {
776
+ "slug": "taqbin-jp",
777
+ "name": "Yamato Japan",
778
+ "phone": "+81 0120-17-9625",
779
+ "other_name": "ヤマト運輸, TAQBIN",
780
+ "web_url": "http://www.kuronekoyamato.co.jp/en/",
781
+ "required_fields": []
782
+ },
783
+ {
784
+ "slug": "courierit",
785
+ "name": "Courier IT",
786
+ "phone": "+27 21 555 6777",
787
+ "other_name": "Courierit",
788
+ "web_url": "http://www.courierit.co.za/",
789
+ "required_fields": []
790
+ },
791
+ {
792
+ "slug": "city-link-international",
793
+ "name": "City Link International",
794
+ "phone": "+44 (0) 8444 930 930",
795
+ "other_name": "citylink.norsknet.com",
796
+ "web_url": "http://citylink.norsknet.com/",
797
+ "required_fields": []
798
+ },
799
+ {
800
+ "slug": "red-express",
801
+ "name": "Red Express",
802
+ "phone": "+91 1800-123-2400",
803
+ "other_name": "Red Express",
804
+ "web_url": "https://www.getsetred.net",
805
+ "required_fields": []
806
+ },
807
+ {
808
+ "slug": "dtdc",
809
+ "name": "DTDC India",
810
+ "phone": "+91 33004444",
811
+ "other_name": "DTDC Courier & Cargo",
812
+ "web_url": "http://dtdc.com/",
813
+ "required_fields": []
814
+ },
815
+ {
816
+ "slug": "interlink-express",
817
+ "name": "Interlink Express",
818
+ "phone": "+44 8702 200 300",
819
+ "other_name": "Interlink UK",
820
+ "web_url": "http://www.interlinkexpress.com/",
821
+ "required_fields": []
822
+ },
823
+ {
824
+ "slug": "flash-courier",
825
+ "name": "Flash Courier",
826
+ "phone": "+55 011 5033 3260",
827
+ "other_name": "Flash Courier Brazil",
828
+ "web_url": "http://www.jall.com.br/",
829
+ "required_fields": []
830
+ },
831
+ {
832
+ "slug": "austrian-post",
833
+ "name": "Austrian Post (Express)",
834
+ "phone": "+43 810 010 100",
835
+ "other_name": "Österreichische Post AG",
836
+ "web_url": "http://www.post.at",
837
+ "required_fields": []
838
+ },
839
+ {
840
+ "slug": "star-track",
841
+ "name": "StarTrack",
842
+ "phone": "+61 13 2345",
843
+ "other_name": "Star Track",
844
+ "web_url": "http://startrack.com.au/",
845
+ "required_fields": []
846
+ },
847
+ {
848
+ "slug": "star-track-express",
849
+ "name": "Star Track Express",
850
+ "phone": "+61 13 2345",
851
+ "other_name": "AaE Australian air Express",
852
+ "web_url": "http://www.star-track.com.au/",
853
+ "required_fields": []
854
+ },
855
+ {
856
+ "slug": "jne",
857
+ "name": "JNE",
858
+ "phone": "+62 021-29278888",
859
+ "other_name": "Express Across Nation, Tiki Jalur Nugraha Ekakurir",
860
+ "web_url": "http://www.jne.co.id/",
861
+ "required_fields": []
862
+ },
863
+ {
864
+ "slug": "rpx",
865
+ "name": "RPX Indonesia",
866
+ "phone": "+62 0-800-1-888-900",
867
+ "other_name": "Repex Perdana International",
868
+ "web_url": "http://www.rpxholding.com/",
869
+ "required_fields": []
870
+ },
871
+ {
872
+ "slug": "first-logistics",
873
+ "name": "First Logistics",
874
+ "phone": "+62 021 - 73880707",
875
+ "other_name": "PT Synergy First Logistics",
876
+ "web_url": "http://www.jne.co.id/",
877
+ "required_fields": []
878
+ },
879
+ {
880
+ "slug": "brt-it",
881
+ "name": "BRT Bartolini",
882
+ "phone": "+39 011 397 411 1",
883
+ "other_name": "BRT Corriere Espresso, DPD Italy",
884
+ "web_url": "http://www.brt.it/",
885
+ "required_fields": []
886
+ },
887
+ {
888
+ "slug": "postnord",
889
+ "name": "PostNord Logistics",
890
+ "phone": "+46 771 33 33 10",
891
+ "other_name": "Posten Norden",
892
+ "web_url": "http://www.postnordlogistics.com/",
893
+ "required_fields": []
894
+ },
895
+ {
896
+ "slug": "belpost",
897
+ "name": "Belpost",
898
+ "phone": "+375 17 293 59 10",
899
+ "other_name": "Belposhta, Белпочта",
900
+ "web_url": "http://www.belpost.by",
901
+ "required_fields": []
902
+ },
903
+ {
904
+ "slug": "cyprus-post",
905
+ "name": "Cyprus Post",
906
+ "phone": "+357 22805802",
907
+ "other_name": "ΚΥΠΡΙΑΚΑ ΤΑΧΥΔΡΟΜΕΙΑ",
908
+ "web_url": "http://www.mcw.gov.cy/mcw/postal/dps.nsf/index_en/index_en",
909
+ "required_fields": []
910
+ },
911
+ {
912
+ "slug": "spanish-seur",
913
+ "name": "Spanish Seur",
914
+ "phone": "+34 902101010",
915
+ "other_name": "SEUR",
916
+ "web_url": "http://www.seur.com/",
917
+ "required_fields": []
918
+ },
919
+ {
920
+ "slug": "air21",
921
+ "name": "AIR21",
922
+ "phone": "+63 (02) 854-2100",
923
+ "other_name": "AIR 21 PH",
924
+ "web_url": "http://www.air21.com.ph",
925
+ "required_fields": []
926
+ },
927
+ {
928
+ "slug": "lbcexpress",
929
+ "name": "LBC Express",
930
+ "phone": "+63 800-10-8585999",
931
+ "other_name": "LBC Express",
932
+ "web_url": "http://www.lbcexpress.com/",
933
+ "required_fields": []
934
+ },
935
+ {
936
+ "slug": "israel-post",
937
+ "name": "Israel Post",
938
+ "phone": "+972 2 629 0691",
939
+ "other_name": "חברת דואר ישראל",
940
+ "web_url": "http://www.israelpost.co.il",
941
+ "required_fields": []
942
+ },
943
+ {
944
+ "slug": "vnpost",
945
+ "name": "Vietnam Post",
946
+ "phone": "+84 1900 54 54 81",
947
+ "other_name": "VNPost",
948
+ "web_url": "http://www.vnpost.vn/",
949
+ "required_fields": []
950
+ },
951
+ {
952
+ "slug": "rl-carriers",
953
+ "name": "RL Carriers",
954
+ "phone": "+1 800-543-5589",
955
+ "other_name": "R+L Carriers",
956
+ "web_url": "http://www.rlcarriers.com/",
957
+ "required_fields": []
958
+ },
959
+ {
960
+ "slug": "sf-express",
961
+ "name": "S.F. Express",
962
+ "phone": "+852 273 00 273 / +86 4008-111-111",
963
+ "other_name": "順豊快遞",
964
+ "web_url": "http://www.sf-express.com",
965
+ "required_fields": []
966
+ },
967
+ {
968
+ "slug": "selektvracht",
969
+ "name": "Selektvracht",
970
+ "phone": "+31 0900-2222120",
971
+ "other_name": "Selektvracht NL",
972
+ "web_url": "http://www.selektvracht.nl/",
973
+ "required_fields": []
974
+ },
975
+ {
976
+ "slug": "ec-firstclass",
977
+ "name": "EC-Firstclass",
978
+ "phone": "+86 4006 988 223",
979
+ "other_name": "ChuKou1, CK1",
980
+ "web_url": "http://www.ec-firstclass.org/",
981
+ "required_fields": []
982
+ },
983
+ {
984
+ "slug": "an-post",
985
+ "name": "An Post",
986
+ "phone": "+353 1850 57 58 59",
987
+ "other_name": "Ireland Post",
988
+ "web_url": "http://www.anpost.ie/AnPost/",
989
+ "required_fields": []
990
+ },
991
+ {
992
+ "slug": "usps",
993
+ "name": "USPS",
994
+ "phone": "+1 800-275-8777",
995
+ "other_name": "United States Postal Service",
996
+ "web_url": "https://www.usps.com",
997
+ "required_fields": []
998
+ },
999
+ {
1000
+ "slug": "swiss-post",
1001
+ "name": "Swiss Post",
1002
+ "phone": "+41 848 888 888",
1003
+ "other_name": "La Poste Suisse, Die Schweizerische Post, Die Post",
1004
+ "web_url": "https://www.post.ch/",
1005
+ "required_fields": []
1006
+ },
1007
+ {
1008
+ "slug": "india-post",
1009
+ "name": "India Post Domestic",
1010
+ "phone": "+91 1800 11 2011",
1011
+ "other_name": "भारतीय डाक",
1012
+ "web_url": "http://www.indiapost.gov.in/",
1013
+ "required_fields": []
1014
+ },
1015
+ {
1016
+ "slug": "correos-de-mexico",
1017
+ "name": "Correos de Mexico",
1018
+ "phone": "+52 01 800 701 7000",
1019
+ "other_name": "Mexico Post",
1020
+ "web_url": "http://www.sepomex.gob.mx/Paginas/Home.aspx",
1021
+ "required_fields": []
1022
+ },
1023
+ {
1024
+ "slug": "correos-chile",
1025
+ "name": "Correos Chile",
1026
+ "phone": "+562 600 950 2020",
1027
+ "other_name": "Chile Post",
1028
+ "web_url": "http://www.correos.cl",
1029
+ "required_fields": []
1030
+ },
1031
+ {
1032
+ "slug": "asm",
1033
+ "name": "ASM",
1034
+ "phone": "+34 902 11 33 00",
1035
+ "other_name": "Asm-Red",
1036
+ "web_url": "http://www.asmred.com/en/",
1037
+ "required_fields": [
1038
+ "tracking_postal_code"
1039
+ ]
1040
+ },
1041
+ {
1042
+ "slug": "aupost-china",
1043
+ "name": "AuPost China",
1044
+ "phone": "+86 4007005618",
1045
+ "other_name": "澳邮宝",
1046
+ "web_url": "http://www.aupost.org/",
1047
+ "required_fields": []
1048
+ },
1049
+ {
1050
+ "slug": "danmark-post",
1051
+ "name": "Post Danmark",
1052
+ "phone": "+45 80 20 70 30",
1053
+ "other_name": "Danske Post",
1054
+ "web_url": "http://www.postdanmark.dk/",
1055
+ "required_fields": []
1056
+ },
1057
+ {
1058
+ "slug": "gls-netherlands",
1059
+ "name": "GLS Netherlands",
1060
+ "phone": "+31 0900-1116660",
1061
+ "other_name": "GLS NL",
1062
+ "web_url": "http://www.gls-netherlands.com/",
1063
+ "required_fields": [
1064
+ "tracking_postal_code"
1065
+ ]
1066
+ },
1067
+ {
1068
+ "slug": "dpd-uk",
1069
+ "name": "DPD UK",
1070
+ "phone": "+44 845 9 300 350",
1071
+ "other_name": "Dynamic Parcel Distribution UK",
1072
+ "web_url": "http://www.dpd.co.uk/",
1073
+ "required_fields": []
1074
+ },
1075
+ {
1076
+ "slug": "mrw-spain",
1077
+ "name": "MRW",
1078
+ "phone": "+34 902 300 403",
1079
+ "other_name": "MRW Spain",
1080
+ "web_url": "http://www.mrw.es/",
1081
+ "required_fields": []
1082
+ },
1083
+ {
1084
+ "slug": "correo-argentino",
1085
+ "name": "Correo Argentino",
1086
+ "phone": "+54 11 4891-9191",
1087
+ "other_name": "Argentina Post",
1088
+ "web_url": "http://www.correoargentino.com.ar",
1089
+ "required_fields": []
1090
+ },
1091
+ {
1092
+ "slug": "hrvatska-posta",
1093
+ "name": "Hrvatska Pošta",
1094
+ "phone": "+385 0800 303 304",
1095
+ "other_name": "Croatia Post",
1096
+ "web_url": "http://www.posta.hr/",
1097
+ "required_fields": []
1098
+ },
1099
+ {
1100
+ "slug": "posta-romana",
1101
+ "name": "Poșta Română",
1102
+ "phone": "+40 021 9393 111",
1103
+ "other_name": "Romania Post",
1104
+ "web_url": "http://www.posta-romana.ro/",
1105
+ "required_fields": []
1106
+ },
1107
+ {
1108
+ "slug": "courierpost",
1109
+ "name": "CourierPost",
1110
+ "phone": "+64 0800 268 743",
1111
+ "other_name": "Express Couriers",
1112
+ "web_url": "http://www.courierpost.co.nz/",
1113
+ "required_fields": []
1114
+ },
1115
+ {
1116
+ "slug": "gdex",
1117
+ "name": "GDEX",
1118
+ "phone": "+60 03-77872222",
1119
+ "other_name": "GD Express",
1120
+ "web_url": "http://www.gdexpress.com",
1121
+ "required_fields": []
1122
+ },
1123
+ {
1124
+ "slug": "singapore-post",
1125
+ "name": "Singapore Post",
1126
+ "phone": "+65 6841 2000",
1127
+ "other_name": "SingPost",
1128
+ "web_url": "http://www.singpost.com",
1129
+ "required_fields": []
1130
+ },
1131
+ {
1132
+ "slug": "arrowxl",
1133
+ "name": "Arrow XL",
1134
+ "phone": "+44 0800 015 1509",
1135
+ "other_name": "Yodel XL",
1136
+ "web_url": "http://www.arrowxl.co.uk/",
1137
+ "required_fields": [
1138
+ "tracking_postal_code"
1139
+ ]
1140
+ },
1141
+ {
1142
+ "slug": "tnt-fr",
1143
+ "name": "TNT France",
1144
+ "phone": "+33 4 72 80 77 77",
1145
+ "other_name": "TNT Express FR",
1146
+ "web_url": "http://www.tnt.fr/",
1147
+ "required_fields": []
1148
+ },
1149
+ {
1150
+ "slug": "fastway-au",
1151
+ "name": "Fastway Australia",
1152
+ "phone": "+61 (0) 2 9737 8288",
1153
+ "other_name": "Fastway Couriers",
1154
+ "web_url": "http://www.fastway.com.au/",
1155
+ "required_fields": []
1156
+ },
1157
+ {
1158
+ "slug": "new-zealand-post",
1159
+ "name": "New Zealand Post",
1160
+ "phone": "+64 9 367 9710",
1161
+ "other_name": "NZ Post",
1162
+ "web_url": "http://www.nzpost.co.nz/",
1163
+ "required_fields": []
1164
+ },
1165
+ {
1166
+ "slug": "poste-italiane-paccocelere",
1167
+ "name": "Poste Italiane Paccocelere",
1168
+ "phone": "+39 803 160",
1169
+ "other_name": "Italian Post EMS / Express",
1170
+ "web_url": "http://www.poste.it/",
1171
+ "required_fields": []
1172
+ },
1173
+ {
1174
+ "slug": "poste-italiane",
1175
+ "name": "Poste Italiane",
1176
+ "phone": "+39 803 160",
1177
+ "other_name": "Italian Post",
1178
+ "web_url": "http://www.poste.it/",
1179
+ "required_fields": []
1180
+ },
1181
+ {
1182
+ "slug": "hermes-de",
1183
+ "name": "Hermes Germany",
1184
+ "phone": "+49 1806-311211",
1185
+ "other_name": "myhermes.de, Hermes Logistik Gruppe Deutschland",
1186
+ "web_url": "https://www.myhermes.de/wps/portal/paket/Home/privatkunden/home",
1187
+ "required_fields": []
1188
+ },
1189
+ {
1190
+ "slug": "wahana",
1191
+ "name": "Wahana",
1192
+ "phone": "+62 2171355152",
1193
+ "other_name": "Wahana Indonesia",
1194
+ "web_url": "http://www.wahana.com/",
1195
+ "required_fields": []
1196
+ },
1197
+ {
1198
+ "slug": "collectplus",
1199
+ "name": "Collect+",
1200
+ "phone": "+44 01923 601616",
1201
+ "other_name": "Collect Plus UK",
1202
+ "web_url": "https://www.collectplus.co.uk/",
1203
+ "required_fields": []
1204
+ },
1205
+ {
1206
+ "slug": "dynamic-logistics",
1207
+ "name": "Dynamic Logistics",
1208
+ "phone": "+66 02-688-6688",
1209
+ "other_name": "Dynamic Logistics Thailand",
1210
+ "web_url": "http://www.dynamic-logistics.com/",
1211
+ "required_fields": [
1212
+ "tracking_account_number"
1213
+ ]
1214
+ },
1215
+ {
1216
+ "slug": "dx",
1217
+ "name": "DX",
1218
+ "phone": "+44 0844 826 1178",
1219
+ "other_name": "DX Freight",
1220
+ "web_url": "https://www.thedx.co.uk/",
1221
+ "required_fields": [
1222
+ "tracking_postal_code"
1223
+ ]
1224
+ },
1225
+ {
1226
+ "slug": "taqbin-my",
1227
+ "name": "TAQBIN Malaysia",
1228
+ "phone": "+60 1800-8-827246",
1229
+ "other_name": "TAQBIN Malaysia",
1230
+ "web_url": "http://my.ta-q-bin.com/",
1231
+ "required_fields": []
1232
+ },
1233
+ {
1234
+ "slug": "dhl",
1235
+ "name": "DHL Express",
1236
+ "phone": "+1 800 225 5345",
1237
+ "other_name": "DHL International",
1238
+ "web_url": "http://www.dhl.com/",
1239
+ "required_fields": []
1240
+ },
1241
+ {
1242
+ "slug": "citylinkexpress",
1243
+ "name": "City-Link Express",
1244
+ "phone": "+60 603-5565 8399",
1245
+ "other_name": "Citylink Malaysia",
1246
+ "web_url": "http://www.citylinkexpress.com/",
1247
+ "required_fields": []
1248
+ },
1249
+ {
1250
+ "slug": "wedo",
1251
+ "name": "WeDo Logistics",
1252
+ "phone": "+86 (0779)2050300",
1253
+ "other_name": "運德物流",
1254
+ "web_url": "http://www.wedoexpress.com/",
1255
+ "required_fields": []
1256
+ },
1257
+ {
1258
+ "slug": "tntpost-it",
1259
+ "name": "Nexive (TNT Post Italy)",
1260
+ "phone": "+39 02 50720011",
1261
+ "other_name": "Postnl TNT",
1262
+ "web_url": "http://www.tntpost.it/",
1263
+ "required_fields": []
1264
+ },
1265
+ {
1266
+ "slug": "malaysia-post-posdaftar",
1267
+ "name": "Malaysia Post - Registered",
1268
+ "phone": "+603 27279100",
1269
+ "other_name": "PosDaftar",
1270
+ "web_url": "http://www.pos.com.my",
1271
+ "required_fields": []
1272
+ },
1273
+ {
1274
+ "slug": "pos-indonesia",
1275
+ "name": "Pos Indonesia Domestic",
1276
+ "phone": "+62 21 161",
1277
+ "other_name": "Indonesian Post Domestic",
1278
+ "web_url": "http://www.posindonesia.co.id",
1279
+ "required_fields": []
1280
+ },
1281
+ {
1282
+ "slug": "pos-indonesia-int",
1283
+ "name": "Pos Indonesia Int'l",
1284
+ "phone": "+62 21 161",
1285
+ "other_name": "Indonesian Post International EMS",
1286
+ "web_url": "http://www.posindonesia.co.id",
1287
+ "required_fields": []
1288
+ },
1289
+ {
1290
+ "slug": "xdp-uk",
1291
+ "name": "XDP Express",
1292
+ "phone": "+44 0843 1782222",
1293
+ "other_name": "XDP UK",
1294
+ "web_url": "http://www.xdp.co.uk/",
1295
+ "required_fields": [
1296
+ "tracking_postal_code"
1297
+ ]
1298
+ },
1299
+ {
1300
+ "slug": "portugal-ctt",
1301
+ "name": "Portugal CTT",
1302
+ "phone": "+351 707 26 26 26",
1303
+ "other_name": "Correios de Portugal",
1304
+ "web_url": "http://www.ctt.pt/",
1305
+ "required_fields": []
1306
+ },
1307
+ {
1308
+ "slug": "hermes",
1309
+ "name": "myHermes UK",
1310
+ "phone": "+44 844 543 7000",
1311
+ "other_name": "Hermes UK",
1312
+ "web_url": "https://www.myhermes.co.uk",
1313
+ "required_fields": []
1314
+ },
1315
+ {
1316
+ "slug": "colis-prive",
1317
+ "name": "Colis Privé",
1318
+ "phone": "+33 0826 82 83 84",
1319
+ "other_name": "ColisPrivé",
1320
+ "web_url": "https://www.colisprive.com/moncolis/",
1321
+ "required_fields": []
1322
+ },
1323
+ {
1324
+ "slug": "la-poste-colissimo",
1325
+ "name": "La Poste",
1326
+ "phone": "+33 3631",
1327
+ "other_name": "Coliposte",
1328
+ "web_url": "http://www.csuivi.courrier.laposte.fr",
1329
+ "required_fields": []
1330
+ },
1331
+ {
1332
+ "slug": "fastway-ireland",
1333
+ "name": "Fastway Ireland",
1334
+ "phone": "+353 1 4242 900",
1335
+ "other_name": "Fastway Couriers",
1336
+ "web_url": "http://www.fastway.ie/",
1337
+ "required_fields": []
1338
+ },
1339
+ {
1340
+ "slug": "malaysia-post",
1341
+ "name": "Malaysia Post EMS / Poslaju",
1342
+ "phone": "+603 27279100",
1343
+ "other_name": "Pos Ekspres, Pos Malaysia Express",
1344
+ "web_url": "http://www.pos.com.my",
1345
+ "required_fields": []
1346
+ },
1347
+ {
1348
+ "slug": "sto",
1349
+ "name": "STO Express",
1350
+ "phone": "+86 95543",
1351
+ "other_name": "申通快递, Shentong Express",
1352
+ "web_url": "http://www.sto.cn/",
1353
+ "required_fields": []
1354
+ },
1355
+ {
1356
+ "slug": "ups",
1357
+ "name": "UPS",
1358
+ "phone": "+1 800 742 5877",
1359
+ "other_name": "United Parcel Service",
1360
+ "web_url": "http://www.ups.com",
1361
+ "required_fields": []
1362
+ },
1363
+ {
1364
+ "slug": "postnl",
1365
+ "name": "PostNL Domestic",
1366
+ "phone": "+31 (0)900 0990",
1367
+ "other_name": "PostNL Pakketten, TNT Post Netherlands",
1368
+ "web_url": "http://www.postnl.nl/voorthuis/",
1369
+ "required_fields": [
1370
+ "tracking_postal_code"
1371
+ ]
1372
+ },
1373
+ {
1374
+ "slug": "deutsch-post",
1375
+ "name": "Deutsche Post Mail",
1376
+ "phone": "+49 (0) 180 2 000221",
1377
+ "other_name": "dpdhl",
1378
+ "web_url": "http://www.deutschepost.de/",
1379
+ "required_fields": [
1380
+ "tracking_ship_date"
1381
+ ]
1382
+ },
1383
+ {
1384
+ "slug": "elta-courier",
1385
+ "name": "ELTA Hellenic Post",
1386
+ "phone": "+30 801 11 83000",
1387
+ "other_name": "Greece Post, Ελληνικά Ταχυδρομεία, ELTA Courier, Ταχυμεταφορές ΕΛΤΑ",
1388
+ "web_url": "http://www.elta-courier.gr/",
1389
+ "required_fields": []
1390
+ },
1391
+ {
1392
+ "slug": "postnl-international",
1393
+ "name": "PostNL International",
1394
+ "phone": "+31 (0)900 0990",
1395
+ "other_name": "Netherlands Post, Spring Global Mail",
1396
+ "web_url": "http://www.postnl.nl/voorthuis/",
1397
+ "required_fields": []
1398
+ },
1399
+ {
1400
+ "slug": "toll-ipec",
1401
+ "name": "Toll IPEC",
1402
+ "phone": "+61 13 15 31",
1403
+ "other_name": "Toll Express",
1404
+ "web_url": "http://www.tollgroup.com/tollipec",
1405
+ "required_fields": []
1406
+ },
1407
+ {
1408
+ "slug": "jcex",
1409
+ "name": "JCEX",
1410
+ "phone": "+86 571-86436777",
1411
+ "other_name": "JiaCheng, 杭州佳成",
1412
+ "web_url": "http://www.jcex.com",
1413
+ "required_fields": []
1414
+ },
1415
+ {
1416
+ "slug": "israel-post-domestic",
1417
+ "name": "Israel Post Domestic",
1418
+ "phone": "+972 2 629 0691",
1419
+ "other_name": "חברת דואר ישראל מקומית",
1420
+ "web_url": "http://www.israelpost.co.il/",
1421
+ "required_fields": []
1422
+ },
1423
+ {
1424
+ "slug": "skynetworldwide",
1425
+ "name": "SkyNet Worldwide Express",
1426
+ "phone": "+44 (0) 20 8538 1988",
1427
+ "other_name": "Skynetwwe",
1428
+ "web_url": "http://www.skynetwwe.com/",
1429
+ "required_fields": []
1430
+ },
1431
+ {
1432
+ "slug": "tnt",
1433
+ "name": "TNT",
1434
+ "phone": "+1 800 558 5555",
1435
+ "other_name": "TNT Express",
1436
+ "web_url": "http://www.tnt.com/",
1437
+ "required_fields": []
1438
+ },
1439
+ {
1440
+ "slug": "royal-mail",
1441
+ "name": "Royal Mail",
1442
+ "phone": "+44 1752387112",
1443
+ "other_name": "Royal Mail United Kingdom",
1444
+ "web_url": "http://www.royalmail.com/",
1445
+ "required_fields": []
1446
+ },
1447
+ {
1448
+ "slug": "parcel-force",
1449
+ "name": "Parcel Force",
1450
+ "phone": "+44 844 800 44 66",
1451
+ "other_name": "Parcelforce UK",
1452
+ "web_url": "http://www.parcelforce.com",
1453
+ "required_fields": []
1454
+ },
1455
+ {
1456
+ "slug": "abf",
1457
+ "name": "ABF Freight",
1458
+ "phone": "+1 (800) 610-5544",
1459
+ "other_name": "Arkansas Best Corporation",
1460
+ "web_url": "http://www.abfs.com/",
1461
+ "required_fields": []
1462
+ },
1463
+ {
1464
+ "slug": "directlink",
1465
+ "name": "Direct Link",
1466
+ "phone": "+852 28504183",
1467
+ "other_name": "Direct Link",
1468
+ "web_url": "http://www.directlink.com/",
1469
+ "required_fields": []
1470
+ },
1471
+ {
1472
+ "slug": "dhlparcel-nl",
1473
+ "name": "DHL Parcel NL",
1474
+ "phone": "+31 0900 - 222 21 20",
1475
+ "other_name": "Selektvracht, dhlparcel.nl",
1476
+ "web_url": "https://www.dhlparcel.nl/",
1477
+ "required_fields": []
1478
+ },
1479
+ {
1480
+ "slug": "greyhound",
1481
+ "name": "Greyhound",
1482
+ "phone": "+1 800-739-5020",
1483
+ "other_name": "Greyhound Package Express",
1484
+ "web_url": "http://www.shipgreyhound.com/",
1485
+ "required_fields": []
1486
+ },
1487
+ {
1488
+ "slug": "dhl-germany",
1489
+ "name": "Deutsche Post DHL",
1490
+ "phone": "+49 228 28609898",
1491
+ "other_name": "DHL Germany",
1492
+ "web_url": "http://www.dhl.de/",
1493
+ "required_fields": []
1494
+ },
1495
+ {
1496
+ "slug": "gls",
1497
+ "name": "GLS",
1498
+ "phone": "+44 247 621 3455",
1499
+ "other_name": "General Logistics Systems",
1500
+ "web_url": "https://gls-group.eu/EU/en/home",
1501
+ "required_fields": []
1502
+ },
1503
+ {
1504
+ "slug": "i-parcel",
1505
+ "name": "i-parcel",
1506
+ "phone": "+44 (0) 1342 315 455",
1507
+ "other_name": "iparcel",
1508
+ "web_url": "https://www.i-parcel.com/",
1509
+ "required_fields": []
1510
+ },
1511
+ {
1512
+ "slug": "colissimo",
1513
+ "name": "Colissimo",
1514
+ "phone": "+33 3631",
1515
+ "other_name": "Colissimo fr",
1516
+ "web_url": "http://www.colissimo.fr",
1517
+ "required_fields": []
1518
+ },
1519
+ {
1520
+ "slug": "yanwen",
1521
+ "name": "Yanwen",
1522
+ "phone": "+86 10-64621881/64669779",
1523
+ "other_name": "燕文物流",
1524
+ "web_url": "http://www.yw56.com.cn/",
1525
+ "required_fields": []
1526
+ },
1527
+ {
1528
+ "slug": "poczta-polska",
1529
+ "name": "Poczta Polska",
1530
+ "phone": "+48 43-842-06-00",
1531
+ "other_name": "Poland Post",
1532
+ "web_url": "http://www.poczta-polska.pl/",
1533
+ "required_fields": []
1534
+ },
1535
+ {
1536
+ "slug": "bpost-international",
1537
+ "name": "bpost international",
1538
+ "phone": "+32 (0)2 278 50 90",
1539
+ "other_name": "Belgium Post International, Minipak DDU",
1540
+ "web_url": "http://www.bpostinternational.com/",
1541
+ "required_fields": []
1542
+ },
1543
+ {
1544
+ "slug": "first-flight",
1545
+ "name": "First Flight Couriers",
1546
+ "phone": "+91 022-39576666",
1547
+ "other_name": "FirstFlight India",
1548
+ "web_url": "http://www.firstflight.net/",
1549
+ "required_fields": []
1550
+ },
1551
+ {
1552
+ "slug": "australia-post",
1553
+ "name": "Australia Post",
1554
+ "phone": "+61 3 8847 9980",
1555
+ "other_name": "AusPost",
1556
+ "web_url": "http://auspost.com.au/",
1557
+ "required_fields": []
1558
+ },
1559
+ {
1560
+ "slug": "ups-mi",
1561
+ "name": "UPS Mail Innovations",
1562
+ "phone": "+1 800-500-2224",
1563
+ "other_name": "UPS MI",
1564
+ "web_url": "http://www.upsmailinnovations.com/",
1565
+ "required_fields": []
1566
+ },
1567
+ {
1568
+ "slug": "dhl-benelux",
1569
+ "name": "DHL Benelux",
1570
+ "phone": "+31 26-324 6700",
1571
+ "other_name": "DHL Benelux",
1572
+ "web_url": "http://www.dhl.nl/",
1573
+ "required_fields": []
1574
+ },
1575
+ {
1576
+ "slug": "taxydromiki",
1577
+ "name": "Geniki Taxydromiki",
1578
+ "phone": "+30 210 485110",
1579
+ "other_name": "ΓΕΝΙΚΗ ΤΑΧΥΔΡΟΜΙΚΗ",
1580
+ "web_url": "http://www.taxydromiki.gr/",
1581
+ "required_fields": []
1582
+ },
1583
+ {
1584
+ "slug": "skynetworldwide-uk",
1585
+ "name": "Skynet Worldwide Express UK",
1586
+ "phone": "+44 (0) 20 8538 1988",
1587
+ "other_name": "Skynet UK",
1588
+ "web_url": "https://www.skynetworldwide.com",
1589
+ "required_fields": []
1590
+ },
1591
+ {
1592
+ "slug": "gls-italy",
1593
+ "name": "GLS Italy",
1594
+ "phone": "+39 199 151188",
1595
+ "other_name": "GLS Corriere Espresso",
1596
+ "web_url": "http://www.gls-italy.com/index_noreg.asp",
1597
+ "required_fields": []
1598
+ },
1599
+ {
1600
+ "slug": "envialia",
1601
+ "name": "Envialia",
1602
+ "phone": "+34 902400909",
1603
+ "other_name": "Envialia Spain",
1604
+ "web_url": "http://www.envialia.com/",
1605
+ "required_fields": []
1606
+ },
1607
+ {
1608
+ "slug": "yundaex",
1609
+ "name": "Yunda Express",
1610
+ "phone": "+86 400-821-6789",
1611
+ "other_name": "韵达快递",
1612
+ "web_url": "http://www.yundaex.com/",
1613
+ "required_fields": []
1614
+ },
1615
+ {
1616
+ "slug": "specialisedfreight-za",
1617
+ "name": "Specialised Freight",
1618
+ "phone": "+27 21 528 1000",
1619
+ "other_name": "SFS",
1620
+ "web_url": "http://www.specialisedfreight.co.za/",
1621
+ "required_fields": []
1622
+ },
1623
+ {
1624
+ "slug": "yodel",
1625
+ "name": "Yodel Domestic",
1626
+ "phone": "+44 0844 453 7443",
1627
+ "other_name": "Home Delivery Network Limited (HDNL)",
1628
+ "web_url": "http://www.yodel.co.uk/",
1629
+ "required_fields": []
1630
+ },
1631
+ {
1632
+ "slug": "city-link",
1633
+ "name": "City Link",
1634
+ "phone": "+44 8444 930 914",
1635
+ "other_name": "City Link UK",
1636
+ "web_url": "http://www.city-link.co.uk/",
1637
+ "required_fields": []
1638
+ },
1639
+ {
1640
+ "slug": "bpost",
1641
+ "name": "Belgium Post",
1642
+ "phone": "+32 2 276 22 74",
1643
+ "other_name": "bpost, Belgian Post",
1644
+ "web_url": "http://www.bpost.be/",
1645
+ "required_fields": []
1646
+ },
1647
+ {
1648
+ "slug": "dpd",
1649
+ "name": "DPD",
1650
+ "phone": "+31 20 480 2900",
1651
+ "other_name": "Dynamic Parcel Distribution",
1652
+ "web_url": "http://www.dpd.com/",
1653
+ "required_fields": []
1654
+ },
1655
+ {
1656
+ "slug": "thailand-post",
1657
+ "name": "Thailand Thai Post",
1658
+ "phone": "+66 (0) 2831 3131",
1659
+ "other_name": "ไปรษณีย์ไทย",
1660
+ "web_url": "http://www.thailandpost.co.th/home.php",
1661
+ "required_fields": []
1662
+ },
1663
+ {
1664
+ "slug": "redur-es",
1665
+ "name": "Redur Spain",
1666
+ "phone": "+34 93 263 16 16",
1667
+ "other_name": "Eurodis",
1668
+ "web_url": "http://www.redur.es/",
1669
+ "required_fields": []
1670
+ },
1671
+ {
1672
+ "slug": "bluedart",
1673
+ "name": "Bluedart",
1674
+ "phone": "+91 (0) 40-64564307",
1675
+ "other_name": "Blue Dart Express",
1676
+ "web_url": "http://www.bluedart.com/",
1677
+ "required_fields": []
1678
+ },
1679
+ {
1680
+ "slug": "dpe-za",
1681
+ "name": "DPE South Africa",
1682
+ "phone": "+27 (011) 573 3000",
1683
+ "other_name": "DPE Worldwide Express",
1684
+ "web_url": "http://www.dpe.co.za/",
1685
+ "required_fields": []
1686
+ },
1687
+ {
1688
+ "slug": "emirates-post",
1689
+ "name": "Emirates Post",
1690
+ "phone": "+971 600 599999",
1691
+ "other_name": "مجموعة بريد الإمارات, UAE Post",
1692
+ "web_url": "http://www.epg.gov.ae/",
1693
+ "required_fields": []
1694
+ },
1695
+ {
1696
+ "slug": "dhl-nl",
1697
+ "name": "DHL Netherlands",
1698
+ "phone": "+31 26-324 6700",
1699
+ "other_name": "DHL Nederlands",
1700
+ "web_url": "http://www.dhl.nl/",
1701
+ "required_fields": []
1702
+ },
1703
+ {
1704
+ "slug": "szdpex",
1705
+ "name": "DPEX China",
1706
+ "phone": "+86 755-8829 7707",
1707
+ "other_name": "DPEX(深圳)国际物流, Toll China",
1708
+ "web_url": "http://www.szdpex.com.cn/",
1709
+ "required_fields": []
1710
+ },
1711
+ {
1712
+ "slug": "qxpress",
1713
+ "name": "Qxpress",
1714
+ "phone": "+86 755-8829 7707",
1715
+ "other_name": "Qxpress Qoo10",
1716
+ "web_url": "http://www.qxpress.asia/",
1717
+ "required_fields": []
1718
+ },
1719
+ {
1720
+ "slug": "courier-plus",
1721
+ "name": "Courier Plus",
1722
+ "phone": "+234-18102031",
1723
+ "other_name": "Courier Plus",
1724
+ "web_url": "http://www.courierplus-ng.com/",
1725
+ "required_fields": []
1726
+ },
1727
+ {
1728
+ "slug": "taiwan-post",
1729
+ "name": "Taiwan Post",
1730
+ "phone": "+886 (02)2703-7527",
1731
+ "other_name": "Chunghwa Post, 台灣中華郵政",
1732
+ "web_url": "http://www.post.gov.tw/",
1733
+ "required_fields": []
1734
+ },
1735
+ {
1736
+ "slug": "sweden-posten",
1737
+ "name": "Sweden Posten",
1738
+ "phone": "+46 8 23 22 20",
1739
+ "other_name": "Sweden Post",
1740
+ "web_url": "http://www.posten.se/",
1741
+ "required_fields": []
1742
+ },
1743
+ {
1744
+ "slug": "dhl-global-mail",
1745
+ "name": "DHL Global Mail",
1746
+ "phone": "+1 800 225 5345",
1747
+ "other_name": "DHL GM",
1748
+ "web_url": "http://us.dhlglobalmail.com/",
1749
+ "required_fields": []
1750
+ },
1751
+ {
1752
+ "slug": "800bestex",
1753
+ "name": "Best Express",
1754
+ "phone": "+86 4009565656",
1755
+ "other_name": "百世汇通",
1756
+ "web_url": "http://www.800bestex.com/",
1757
+ "required_fields": []
1758
+ },
1759
+ {
1760
+ "slug": "dhl-deliverit",
1761
+ "name": "DHL 2-Mann-Handling",
1762
+ "phone": "+49 228 28609898",
1763
+ "other_name": "DHL Deliver IT",
1764
+ "web_url": "https://www.dhl-deliverit.com/web/guest/webavisierung",
1765
+ "required_fields": [
1766
+ "tracking_postal_code"
1767
+ ]
1768
+ },
1769
+ {
1770
+ "slug": "dbschenker-se",
1771
+ "name": "DB Schenker Sweden",
1772
+ "phone": "+46 31 337 04 00",
1773
+ "other_name": "Deutsche Bahn",
1774
+ "web_url": "http://www.logistics.dbschenker.se/",
1775
+ "required_fields": []
1776
+ }
1777
+ ];
1778
+ return data;
1779
+ }
trunk/assets/js/setting.js ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function() {
2
+ function set_aftership_tracking_provider(selected_couriers) {
3
+ var couriers = sort_couriers(get_couriers());
4
+
5
+ // console.log(couriers);
6
+
7
+ jQuery.each(couriers, function(key, courier) {
8
+ // console.log(courier.name);
9
+ var str = '<option ';
10
+ str += 'value="' + courier['slug'] + '" ';
11
+ if (selected_couriers.hasOwnProperty(courier['slug'])) {
12
+ str += 'selected="selected"';
13
+ }
14
+ str += '>' + courier['name'] + '</option>';
15
+ jQuery('#couriers_select').append(str);
16
+ });
17
+
18
+ jQuery('#couriers_select').val(selected_couriers);
19
+ jQuery('#couriers_select').chosen();
20
+ }
21
+
22
+ jQuery('#couriers_select').change(function() {
23
+ var couriers_select = jQuery('#couriers_select').val();
24
+ var value = (couriers_select) ? couriers_select.join(',') : '';
25
+ jQuery('#couriers').val(value);
26
+ });
27
+
28
+ jQuery('#plugin').change(function() {
29
+ if (jQuery(this).val() == 'aftership') {
30
+ jQuery('#couriers').parent().parent().show();
31
+ } else {
32
+ jQuery('#couriers').parent().parent().hide();
33
+ }
34
+ })
35
+
36
+
37
+ if (jQuery('#couriers')) {
38
+ var couriers_select = jQuery('#couriers').val();
39
+ var couriers_select_array = (couriers_select) ? couriers_select.split(',') : [];
40
+ set_aftership_tracking_provider(couriers_select_array);
41
+
42
+ if (jQuery('#plugin').val() != 'aftership') {
43
+ jQuery('#couriers').parent().parent().hide();
44
+ }
45
+ }
46
+ });
trunk/assets/js/util.js ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function sort_couriers(data){
2
+ var n = data.length;
3
+ for (var i = 0; i < n - 1; i++) {
4
+ var find = false;
5
+ for (var j = i+1; j < n; j++) {
6
+ if (data[i].name.toLowerCase() > data[j].name.toLowerCase()) {
7
+ var tmp = data[i];
8
+ data[i] = data[j];
9
+ data[j] = tmp;
10
+ find = true;
11
+ }
12
+ }
13
+ if (!find) {
14
+ break;
15
+ }
16
+ }
17
+ return data;
18
+ }
trunk/assets/plugin/chosen/chosen-sprite.png ADDED
Binary file
trunk/assets/plugin/chosen/chosen-sprite@2x.png ADDED
Binary file
trunk/assets/plugin/chosen/chosen.jquery.min.js ADDED
@@ -0,0 +1,2 @@
 
 
1
+ /* Chosen v1.2.0 | (c) 2011-2014 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */
2
+ !function(){var a,AbstractChosen,Chosen,SelectParser,b,c={}.hasOwnProperty,d=function(a,b){function d(){this.constructor=a}for(var e in b)c.call(b,e)&&(a[e]=b[e]);return d.prototype=b.prototype,a.prototype=new d,a.__super__=b.prototype,a};SelectParser=function(){function SelectParser(){this.options_index=0,this.parsed=[]}return SelectParser.prototype.add_node=function(a){return"OPTGROUP"===a.nodeName.toUpperCase()?this.add_group(a):this.add_option(a)},SelectParser.prototype.add_group=function(a){var b,c,d,e,f,g;for(b=this.parsed.length,this.parsed.push({array_index:b,group:!0,label:this.escapeExpression(a.label),children:0,disabled:a.disabled}),f=a.childNodes,g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(this.add_option(c,b,a.disabled));return g},SelectParser.prototype.add_option=function(a,b,c){return"OPTION"===a.nodeName.toUpperCase()?(""!==a.text?(null!=b&&(this.parsed[b].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:a.value,text:a.text,html:a.innerHTML,selected:a.selected,disabled:c===!0?c:a.disabled,group_array_index:b,classes:a.className,style:a.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1):void 0},SelectParser.prototype.escapeExpression=function(a){var b,c;return null==a||a===!1?"":/[\&\<\>\"\'\`]/.test(a)?(b={"<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},c=/&(?!\w+;)|[\<\>\"\'\`]/g,a.replace(c,function(a){return b[a]||"&amp;"})):a},SelectParser}(),SelectParser.select_to_array=function(a){var b,c,d,e,f;for(c=new SelectParser,f=a.childNodes,d=0,e=f.length;e>d;d++)b=f[d],c.add_node(b);return c.parsed},AbstractChosen=function(){function AbstractChosen(a,b){this.form_field=a,this.options=null!=b?b:{},AbstractChosen.browser_is_supported()&&(this.is_multiple=this.form_field.multiple,this.set_default_text(),this.set_default_values(),this.setup(),this.set_up_html(),this.register_observers())}return AbstractChosen.prototype.set_default_values=function(){var a=this;return this.click_test_action=function(b){return a.test_active_click(b)},this.activate_action=function(b){return a.activate_field(b)},this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.allow_single_deselect=null!=this.options.allow_single_deselect&&null!=this.form_field.options[0]&&""===this.form_field.options[0].text?this.options.allow_single_deselect:!1,this.disable_search_threshold=this.options.disable_search_threshold||0,this.disable_search=this.options.disable_search||!1,this.enable_split_word_search=null!=this.options.enable_split_word_search?this.options.enable_split_word_search:!0,this.group_search=null!=this.options.group_search?this.options.group_search:!0,this.search_contains=this.options.search_contains||!1,this.single_backstroke_delete=null!=this.options.single_backstroke_delete?this.options.single_backstroke_delete:!0,this.max_selected_options=this.options.max_selected_options||1/0,this.inherit_select_classes=this.options.inherit_select_classes||!1,this.display_selected_options=null!=this.options.display_selected_options?this.options.display_selected_options:!0,this.display_disabled_options=null!=this.options.display_disabled_options?this.options.display_disabled_options:!0},AbstractChosen.prototype.set_default_text=function(){return this.default_text=this.form_field.getAttribute("data-placeholder")?this.form_field.getAttribute("data-placeholder"):this.is_multiple?this.options.placeholder_text_multiple||this.options.placeholder_text||AbstractChosen.default_multiple_text:this.options.placeholder_text_single||this.options.placeholder_text||AbstractChosen.default_single_text,this.results_none_found=this.form_field.getAttribute("data-no_results_text")||this.options.no_results_text||AbstractChosen.default_no_result_text},AbstractChosen.prototype.mouse_enter=function(){return this.mouse_on_container=!0},AbstractChosen.prototype.mouse_leave=function(){return this.mouse_on_container=!1},AbstractChosen.prototype.input_focus=function(){var a=this;if(this.is_multiple){if(!this.active_field)return setTimeout(function(){return a.container_mousedown()},50)}else if(!this.active_field)return this.activate_field()},AbstractChosen.prototype.input_blur=function(){var a=this;return this.mouse_on_container?void 0:(this.active_field=!1,setTimeout(function(){return a.blur_test()},100))},AbstractChosen.prototype.results_option_build=function(a){var b,c,d,e,f;for(b="",f=this.results_data,d=0,e=f.length;e>d;d++)c=f[d],b+=c.group?this.result_add_group(c):this.result_add_option(c),(null!=a?a.first:void 0)&&(c.selected&&this.is_multiple?this.choice_build(c):c.selected&&!this.is_multiple&&this.single_set_selected_text(c.text));return b},AbstractChosen.prototype.result_add_option=function(a){var b,c;return a.search_match?this.include_option_in_results(a)?(b=[],a.disabled||a.selected&&this.is_multiple||b.push("active-result"),!a.disabled||a.selected&&this.is_multiple||b.push("disabled-result"),a.selected&&b.push("result-selected"),null!=a.group_array_index&&b.push("group-option"),""!==a.classes&&b.push(a.classes),c=document.createElement("li"),c.className=b.join(" "),c.style.cssText=a.style,c.setAttribute("data-option-array-index",a.array_index),c.innerHTML=a.search_text,this.outerHTML(c)):"":""},AbstractChosen.prototype.result_add_group=function(a){var b;return a.search_match||a.group_match?a.active_options>0?(b=document.createElement("li"),b.className="group-result",b.innerHTML=a.search_text,this.outerHTML(b)):"":""},AbstractChosen.prototype.results_update_field=function(){return this.set_default_text(),this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.results_build(),this.results_showing?this.winnow_results():void 0},AbstractChosen.prototype.reset_single_select_options=function(){var a,b,c,d,e;for(d=this.results_data,e=[],b=0,c=d.length;c>b;b++)a=d[b],a.selected?e.push(a.selected=!1):e.push(void 0);return e},AbstractChosen.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},AbstractChosen.prototype.results_search=function(){return this.results_showing?this.winnow_results():this.results_show()},AbstractChosen.prototype.winnow_results=function(){var a,b,c,d,e,f,g,h,i,j,k,l;for(this.no_results_clear(),d=0,f=this.get_search_text(),a=f.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),i=new RegExp(a,"i"),c=this.get_search_regex(a),l=this.results_data,j=0,k=l.length;k>j;j++)b=l[j],b.search_match=!1,e=null,this.include_option_in_results(b)&&(b.group&&(b.group_match=!1,b.active_options=0),null!=b.group_array_index&&this.results_data[b.group_array_index]&&(e=this.results_data[b.group_array_index],0===e.active_options&&e.search_match&&(d+=1),e.active_options+=1),(!b.group||this.group_search)&&(b.search_text=b.group?b.label:b.text,b.search_match=this.search_string_match(b.search_text,c),b.search_match&&!b.group&&(d+=1),b.search_match?(f.length&&(g=b.search_text.search(i),h=b.search_text.substr(0,g+f.length)+"</em>"+b.search_text.substr(g+f.length),b.search_text=h.substr(0,g)+"<em>"+h.substr(g)),null!=e&&(e.group_match=!0)):null!=b.group_array_index&&this.results_data[b.group_array_index].search_match&&(b.search_match=!0)));return this.result_clear_highlight(),1>d&&f.length?(this.update_results_content(""),this.no_results(f)):(this.update_results_content(this.results_option_build()),this.winnow_results_set_highlight())},AbstractChosen.prototype.get_search_regex=function(a){var b;return b=this.search_contains?"":"^",new RegExp(b+a,"i")},AbstractChosen.prototype.search_string_match=function(a,b){var c,d,e,f;if(b.test(a))return!0;if(this.enable_split_word_search&&(a.indexOf(" ")>=0||0===a.indexOf("["))&&(d=a.replace(/\[|\]/g,"").split(" "),d.length))for(e=0,f=d.length;f>e;e++)if(c=d[e],b.test(c))return!0},AbstractChosen.prototype.choices_count=function(){var a,b,c,d;if(null!=this.selected_option_count)return this.selected_option_count;for(this.selected_option_count=0,d=this.form_field.options,b=0,c=d.length;c>b;b++)a=d[b],a.selected&&(this.selected_option_count+=1);return this.selected_option_count},AbstractChosen.prototype.choices_click=function(a){return a.preventDefault(),this.results_showing||this.is_disabled?void 0:this.results_show()},AbstractChosen.prototype.keyup_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),b){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices_count()>0)return this.keydown_backstroke();if(!this.pending_backstroke)return this.result_clear_highlight(),this.results_search();break;case 13:if(a.preventDefault(),this.results_showing)return this.result_select(a);break;case 27:return this.results_showing&&this.results_hide(),!0;case 9:case 38:case 40:case 16:case 91:case 17:break;default:return this.results_search()}},AbstractChosen.prototype.clipboard_event_checker=function(){var a=this;return setTimeout(function(){return a.results_search()},50)},AbstractChosen.prototype.container_width=function(){return null!=this.options.width?this.options.width:""+this.form_field.offsetWidth+"px"},AbstractChosen.prototype.include_option_in_results=function(a){return this.is_multiple&&!this.display_selected_options&&a.selected?!1:!this.display_disabled_options&&a.disabled?!1:a.empty?!1:!0},AbstractChosen.prototype.search_results_touchstart=function(a){return this.touch_started=!0,this.search_results_mouseover(a)},AbstractChosen.prototype.search_results_touchmove=function(a){return this.touch_started=!1,this.search_results_mouseout(a)},AbstractChosen.prototype.search_results_touchend=function(a){return this.touch_started?this.search_results_mouseup(a):void 0},AbstractChosen.prototype.outerHTML=function(a){var b;return a.outerHTML?a.outerHTML:(b=document.createElement("div"),b.appendChild(a),b.innerHTML)},AbstractChosen.browser_is_supported=function(){return"Microsoft Internet Explorer"===window.navigator.appName?document.documentMode>=8:/iP(od|hone)/i.test(window.navigator.userAgent)?!1:/Android/i.test(window.navigator.userAgent)&&/Mobile/i.test(window.navigator.userAgent)?!1:!0},AbstractChosen.default_multiple_text="Select Some Options",AbstractChosen.default_single_text="Select an Option",AbstractChosen.default_no_result_text="No results match",AbstractChosen}(),a=jQuery,a.fn.extend({chosen:function(b){return AbstractChosen.browser_is_supported()?this.each(function(){var c,d;c=a(this),d=c.data("chosen"),"destroy"===b&&d instanceof Chosen?d.destroy():d instanceof Chosen||c.data("chosen",new Chosen(this,b))}):this}}),Chosen=function(c){function Chosen(){return b=Chosen.__super__.constructor.apply(this,arguments)}return d(Chosen,c),Chosen.prototype.setup=function(){return this.form_field_jq=a(this.form_field),this.current_selectedIndex=this.form_field.selectedIndex,this.is_rtl=this.form_field_jq.hasClass("chosen-rtl")},Chosen.prototype.set_up_html=function(){var b,c;return b=["chosen-container"],b.push("chosen-container-"+(this.is_multiple?"multi":"single")),this.inherit_select_classes&&this.form_field.className&&b.push(this.form_field.className),this.is_rtl&&b.push("chosen-rtl"),c={"class":b.join(" "),style:"width: "+this.container_width()+";",title:this.form_field.title},this.form_field.id.length&&(c.id=this.form_field.id.replace(/[^\w]/g,"_")+"_chosen"),this.container=a("<div />",c),this.is_multiple?this.container.html('<ul class="chosen-choices"><li class="search-field"><input type="text" value="'+this.default_text+'" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chosen-drop"><ul class="chosen-results"></ul></div>'):this.container.html('<a class="chosen-single chosen-default" tabindex="-1"><span>'+this.default_text+'</span><div><b></b></div></a><div class="chosen-drop"><div class="chosen-search"><input type="text" autocomplete="off" /></div><ul class="chosen-results"></ul></div>'),this.form_field_jq.hide().after(this.container),this.dropdown=this.container.find("div.chosen-drop").first(),this.search_field=this.container.find("input").first(),this.search_results=this.container.find("ul.chosen-results").first(),this.search_field_scale(),this.search_no_results=this.container.find("li.no-results").first(),this.is_multiple?(this.search_choices=this.container.find("ul.chosen-choices").first(),this.search_container=this.container.find("li.search-field").first()):(this.search_container=this.container.find("div.chosen-search").first(),this.selected_item=this.container.find(".chosen-single").first()),this.results_build(),this.set_tab_index(),this.set_label_behavior(),this.form_field_jq.trigger("chosen:ready",{chosen:this})},Chosen.prototype.register_observers=function(){var a=this;return this.container.bind("touchstart.chosen",function(b){a.container_mousedown(b)}),this.container.bind("touchend.chosen",function(b){a.container_mouseup(b)}),this.container.bind("mousedown.chosen",function(b){a.container_mousedown(b)}),this.container.bind("mouseup.chosen",function(b){a.container_mouseup(b)}),this.container.bind("mouseenter.chosen",function(b){a.mouse_enter(b)}),this.container.bind("mouseleave.chosen",function(b){a.mouse_leave(b)}),this.search_results.bind("mouseup.chosen",function(b){a.search_results_mouseup(b)}),this.search_results.bind("mouseover.chosen",function(b){a.search_results_mouseover(b)}),this.search_results.bind("mouseout.chosen",function(b){a.search_results_mouseout(b)}),this.search_results.bind("mousewheel.chosen DOMMouseScroll.chosen",function(b){a.search_results_mousewheel(b)}),this.search_results.bind("touchstart.chosen",function(b){a.search_results_touchstart(b)}),this.search_results.bind("touchmove.chosen",function(b){a.search_results_touchmove(b)}),this.search_results.bind("touchend.chosen",function(b){a.search_results_touchend(b)}),this.form_field_jq.bind("chosen:updated.chosen",function(b){a.results_update_field(b)}),this.form_field_jq.bind("chosen:activate.chosen",function(b){a.activate_field(b)}),this.form_field_jq.bind("chosen:open.chosen",function(b){a.container_mousedown(b)}),this.form_field_jq.bind("chosen:close.chosen",function(b){a.input_blur(b)}),this.search_field.bind("blur.chosen",function(b){a.input_blur(b)}),this.search_field.bind("keyup.chosen",function(b){a.keyup_checker(b)}),this.search_field.bind("keydown.chosen",function(b){a.keydown_checker(b)}),this.search_field.bind("focus.chosen",function(b){a.input_focus(b)}),this.search_field.bind("cut.chosen",function(b){a.clipboard_event_checker(b)}),this.search_field.bind("paste.chosen",function(b){a.clipboard_event_checker(b)}),this.is_multiple?this.search_choices.bind("click.chosen",function(b){a.choices_click(b)}):this.container.bind("click.chosen",function(a){a.preventDefault()})},Chosen.prototype.destroy=function(){return a(this.container[0].ownerDocument).unbind("click.chosen",this.click_test_action),this.search_field[0].tabIndex&&(this.form_field_jq[0].tabIndex=this.search_field[0].tabIndex),this.container.remove(),this.form_field_jq.removeData("chosen"),this.form_field_jq.show()},Chosen.prototype.search_field_disabled=function(){return this.is_disabled=this.form_field_jq[0].disabled,this.is_disabled?(this.container.addClass("chosen-disabled"),this.search_field[0].disabled=!0,this.is_multiple||this.selected_item.unbind("focus.chosen",this.activate_action),this.close_field()):(this.container.removeClass("chosen-disabled"),this.search_field[0].disabled=!1,this.is_multiple?void 0:this.selected_item.bind("focus.chosen",this.activate_action))},Chosen.prototype.container_mousedown=function(b){return this.is_disabled||(b&&"mousedown"===b.type&&!this.results_showing&&b.preventDefault(),null!=b&&a(b.target).hasClass("search-choice-close"))?void 0:(this.active_field?this.is_multiple||!b||a(b.target)[0]!==this.selected_item[0]&&!a(b.target).parents("a.chosen-single").length||(b.preventDefault(),this.results_toggle()):(this.is_multiple&&this.search_field.val(""),a(this.container[0].ownerDocument).bind("click.chosen",this.click_test_action),this.results_show()),this.activate_field())},Chosen.prototype.container_mouseup=function(a){return"ABBR"!==a.target.nodeName||this.is_disabled?void 0:this.results_reset(a)},Chosen.prototype.search_results_mousewheel=function(a){var b;return a.originalEvent&&(b=a.originalEvent.deltaY||-a.originalEvent.wheelDelta||a.originalEvent.detail),null!=b?(a.preventDefault(),"DOMMouseScroll"===a.type&&(b=40*b),this.search_results.scrollTop(b+this.search_results.scrollTop())):void 0},Chosen.prototype.blur_test=function(){return!this.active_field&&this.container.hasClass("chosen-container-active")?this.close_field():void 0},Chosen.prototype.close_field=function(){return a(this.container[0].ownerDocument).unbind("click.chosen",this.click_test_action),this.active_field=!1,this.results_hide(),this.container.removeClass("chosen-container-active"),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale()},Chosen.prototype.activate_field=function(){return this.container.addClass("chosen-container-active"),this.active_field=!0,this.search_field.val(this.search_field.val()),this.search_field.focus()},Chosen.prototype.test_active_click=function(b){var c;return c=a(b.target).closest(".chosen-container"),c.length&&this.container[0]===c[0]?this.active_field=!0:this.close_field()},Chosen.prototype.results_build=function(){return this.parsing=!0,this.selected_option_count=null,this.results_data=SelectParser.select_to_array(this.form_field),this.is_multiple?this.search_choices.find("li.search-choice").remove():this.is_multiple||(this.single_set_selected_text(),this.disable_search||this.form_field.options.length<=this.disable_search_threshold?(this.search_field[0].readOnly=!0,this.container.addClass("chosen-container-single-nosearch")):(this.search_field[0].readOnly=!1,this.container.removeClass("chosen-container-single-nosearch"))),this.update_results_content(this.results_option_build({first:!0})),this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.parsing=!1},Chosen.prototype.result_do_highlight=function(a){var b,c,d,e,f;if(a.length){if(this.result_clear_highlight(),this.result_highlight=a,this.result_highlight.addClass("highlighted"),d=parseInt(this.search_results.css("maxHeight"),10),f=this.search_results.scrollTop(),e=d+f,c=this.result_highlight.position().top+this.search_results.scrollTop(),b=c+this.result_highlight.outerHeight(),b>=e)return this.search_results.scrollTop(b-d>0?b-d:0);if(f>c)return this.search_results.scrollTop(c)}},Chosen.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClass("highlighted"),this.result_highlight=null},Chosen.prototype.results_show=function(){return this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.container.addClass("chosen-with-drop"),this.results_showing=!0,this.search_field.focus(),this.search_field.val(this.search_field.val()),this.winnow_results(),this.form_field_jq.trigger("chosen:showing_dropdown",{chosen:this}))},Chosen.prototype.update_results_content=function(a){return this.search_results.html(a)},Chosen.prototype.results_hide=function(){return this.results_showing&&(this.result_clear_highlight(),this.container.removeClass("chosen-with-drop"),this.form_field_jq.trigger("chosen:hiding_dropdown",{chosen:this})),this.results_showing=!1},Chosen.prototype.set_tab_index=function(){var a;return this.form_field.tabIndex?(a=this.form_field.tabIndex,this.form_field.tabIndex=-1,this.search_field[0].tabIndex=a):void 0},Chosen.prototype.set_label_behavior=function(){var b=this;return this.form_field_label=this.form_field_jq.parents("label"),!this.form_field_label.length&&this.form_field.id.length&&(this.form_field_label=a("label[for='"+this.form_field.id+"']")),this.form_field_label.length>0?this.form_field_label.bind("click.chosen",function(a){return b.is_multiple?b.container_mousedown(a):b.activate_field()}):void 0},Chosen.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices_count()<1&&!this.active_field?(this.search_field.val(this.default_text),this.search_field.addClass("default")):(this.search_field.val(""),this.search_field.removeClass("default"))},Chosen.prototype.search_results_mouseup=function(b){var c;return c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first(),c.length?(this.result_highlight=c,this.result_select(b),this.search_field.focus()):void 0},Chosen.prototype.search_results_mouseover=function(b){var c;return c=a(b.target).hasClass("active-result")?a(b.target):a(b.target).parents(".active-result").first(),c?this.result_do_highlight(c):void 0},Chosen.prototype.search_results_mouseout=function(b){return a(b.target).hasClass("active-result")?this.result_clear_highlight():void 0},Chosen.prototype.choice_build=function(b){var c,d,e=this;return c=a("<li />",{"class":"search-choice"}).html("<span>"+b.html+"</span>"),b.disabled?c.addClass("search-choice-disabled"):(d=a("<a />",{"class":"search-choice-close","data-option-array-index":b.array_index}),d.bind("click.chosen",function(a){return e.choice_destroy_link_click(a)}),c.append(d)),this.search_container.before(c)},Chosen.prototype.choice_destroy_link_click=function(b){return b.preventDefault(),b.stopPropagation(),this.is_disabled?void 0:this.choice_destroy(a(b.target))},Chosen.prototype.choice_destroy=function(a){return this.result_deselect(a[0].getAttribute("data-option-array-index"))?(this.show_search_field_default(),this.is_multiple&&this.choices_count()>0&&this.search_field.val().length<1&&this.results_hide(),a.parents("li").first().remove(),this.search_field_scale()):void 0},Chosen.prototype.results_reset=function(){return this.reset_single_select_options(),this.form_field.options[0].selected=!0,this.single_set_selected_text(),this.show_search_field_default(),this.results_reset_cleanup(),this.form_field_jq.trigger("change"),this.active_field?this.results_hide():void 0},Chosen.prototype.results_reset_cleanup=function(){return this.current_selectedIndex=this.form_field.selectedIndex,this.selected_item.find("abbr").remove()},Chosen.prototype.result_select=function(a){var b,c;return this.result_highlight?(b=this.result_highlight,this.result_clear_highlight(),this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field_jq.trigger("chosen:maxselected",{chosen:this}),!1):(this.is_multiple?b.removeClass("active-result"):this.reset_single_select_options(),c=this.results_data[b[0].getAttribute("data-option-array-index")],c.selected=!0,this.form_field.options[c.options_index].selected=!0,this.selected_option_count=null,this.is_multiple?this.choice_build(c):this.single_set_selected_text(c.text),(a.metaKey||a.ctrlKey)&&this.is_multiple||this.results_hide(),this.search_field.val(""),(this.is_multiple||this.form_field.selectedIndex!==this.current_selectedIndex)&&this.form_field_jq.trigger("change",{selected:this.form_field.options[c.options_index].value}),this.current_selectedIndex=this.form_field.selectedIndex,this.search_field_scale())):void 0},Chosen.prototype.single_set_selected_text=function(a){return null==a&&(a=this.default_text),a===this.default_text?this.selected_item.addClass("chosen-default"):(this.single_deselect_control_build(),this.selected_item.removeClass("chosen-default")),this.selected_item.find("span").text(a)},Chosen.prototype.result_deselect=function(a){var b;return b=this.results_data[a],this.form_field.options[b.options_index].disabled?!1:(b.selected=!1,this.form_field.options[b.options_index].selected=!1,this.selected_option_count=null,this.result_clear_highlight(),this.results_showing&&this.winnow_results(),this.form_field_jq.trigger("change",{deselected:this.form_field.options[b.options_index].value}),this.search_field_scale(),!0)},Chosen.prototype.single_deselect_control_build=function(){return this.allow_single_deselect?(this.selected_item.find("abbr").length||this.selected_item.find("span").first().after('<abbr class="search-choice-close"></abbr>'),this.selected_item.addClass("chosen-single-with-deselect")):void 0},Chosen.prototype.get_search_text=function(){return this.search_field.val()===this.default_text?"":a("<div/>").text(a.trim(this.search_field.val())).html()},Chosen.prototype.winnow_results_set_highlight=function(){var a,b;return b=this.is_multiple?[]:this.search_results.find(".result-selected.active-result"),a=b.length?b.first():this.search_results.find(".active-result").first(),null!=a?this.result_do_highlight(a):void 0},Chosen.prototype.no_results=function(b){var c;return c=a('<li class="no-results">'+this.results_none_found+' "<span></span>"</li>'),c.find("span").first().html(b),this.search_results.append(c),this.form_field_jq.trigger("chosen:no_results",{chosen:this})},Chosen.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove()},Chosen.prototype.keydown_arrow=function(){var a;return this.results_showing&&this.result_highlight?(a=this.result_highlight.nextAll("li.active-result").first())?this.result_do_highlight(a):void 0:this.results_show()},Chosen.prototype.keyup_arrow=function(){var a;return this.results_showing||this.is_multiple?this.result_highlight?(a=this.result_highlight.prevAll("li.active-result"),a.length?this.result_do_highlight(a.first()):(this.choices_count()>0&&this.results_hide(),this.result_clear_highlight())):void 0:this.results_show()},Chosen.prototype.keydown_backstroke=function(){var a;return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.find("a").first()),this.clear_backstroke()):(a=this.search_container.siblings("li.search-choice").last(),a.length&&!a.hasClass("search-choice-disabled")?(this.pending_backstroke=a,this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClass("search-choice-focus")):void 0)},Chosen.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClass("search-choice-focus"),this.pending_backstroke=null},Chosen.prototype.keydown_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),8!==b&&this.pending_backstroke&&this.clear_backstroke(),b){case 8:this.backstroke_length=this.search_field.val().length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(a),this.mouse_on_container=!1;break;case 13:this.results_showing&&a.preventDefault();break;case 32:this.disable_search&&a.preventDefault();break;case 38:a.preventDefault(),this.keyup_arrow();break;case 40:a.preventDefault(),this.keydown_arrow()}},Chosen.prototype.search_field_scale=function(){var b,c,d,e,f,g,h,i,j;if(this.is_multiple){for(d=0,h=0,f="position:absolute; left: -1000px; top: -1000px; display:none;",g=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"],i=0,j=g.length;j>i;i++)e=g[i],f+=e+":"+this.search_field.css(e)+";";return b=a("<div />",{style:f}),b.text(this.search_field.val()),a("body").append(b),h=b.width()+25,b.remove(),c=this.container.outerWidth(),h>c-10&&(h=c-10),this.search_field.css({width:h+"px"})}},Chosen}(AbstractChosen)}.call(this);
trunk/assets/plugin/chosen/chosen.min.css ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ /* Chosen v1.2.0 | (c) 2011-2014 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */
2
+
3
+ .chosen-container{position:relative;display:inline-block;vertical-align:middle;font-size:13px;zoom:1;*display:inline;-webkit-user-select:none;-moz-user-select:none;user-select:none}.chosen-container *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.chosen-container .chosen-drop{position:absolute;top:100%;left:-9999px;z-index:1010;width:100%;border:1px solid #aaa;border-top:0;background:#fff;box-shadow:0 4px 5px rgba(0,0,0,.15)}.chosen-container.chosen-with-drop .chosen-drop{left:0}.chosen-container a{cursor:pointer}.chosen-container-single .chosen-single{position:relative;display:block;overflow:hidden;padding:0 0 0 8px;height:25px;border:1px solid #aaa;border-radius:5px;background-color:#fff;background:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#fff),color-stop(50%,#f6f6f6),color-stop(52%,#eee),color-stop(100%,#f4f4f4));background:-webkit-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background:-moz-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background:-o-linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background:linear-gradient(top,#fff 20%,#f6f6f6 50%,#eee 52%,#f4f4f4 100%);background-clip:padding-box;box-shadow:0 0 3px #fff inset,0 1px 1px rgba(0,0,0,.1);color:#444;text-decoration:none;white-space:nowrap;line-height:24px}.chosen-container-single .chosen-default{color:#999}.chosen-container-single .chosen-single span{display:block;overflow:hidden;margin-right:26px;text-overflow:ellipsis;white-space:nowrap}.chosen-container-single .chosen-single-with-deselect span{margin-right:38px}.chosen-container-single .chosen-single abbr{position:absolute;top:6px;right:26px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-single .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single.chosen-disabled .chosen-single abbr:hover{background-position:-42px -10px}.chosen-container-single .chosen-single div{position:absolute;top:0;right:0;display:block;width:18px;height:100%}.chosen-container-single .chosen-single div b{display:block;width:100%;height:100%;background:url(chosen-sprite.png) no-repeat 0 2px}.chosen-container-single .chosen-search{position:relative;z-index:1010;margin:0;padding:3px 4px;white-space:nowrap}.chosen-container-single .chosen-search input[type=text]{margin:1px 0;padding:4px 20px 4px 5px;width:100%;height:auto;outline:0;border:1px solid #aaa;background:#fff url(chosen-sprite.png) no-repeat 100% -20px;background:url(chosen-sprite.png) no-repeat 100% -20px;font-size:1em;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-single .chosen-drop{margin-top:-1px;border-radius:0 0 4px 4px;background-clip:padding-box}.chosen-container-single.chosen-container-single-nosearch .chosen-search{position:absolute;left:-9999px}.chosen-container .chosen-results{color:#444;position:relative;overflow-x:hidden;overflow-y:auto;margin:0 4px 4px 0;padding:0 0 0 4px;max-height:240px;-webkit-overflow-scrolling:touch}.chosen-container .chosen-results li{display:none;margin:0;padding:5px 6px;list-style:none;line-height:15px;word-wrap:break-word;-webkit-touch-callout:none}.chosen-container .chosen-results li.active-result{display:list-item;cursor:pointer}.chosen-container .chosen-results li.disabled-result{display:list-item;color:#ccc;cursor:default}.chosen-container .chosen-results li.highlighted{background-color:#3875d7;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#3875d7),color-stop(90%,#2a62bc));background-image:-webkit-linear-gradient(#3875d7 20%,#2a62bc 90%);background-image:-moz-linear-gradient(#3875d7 20%,#2a62bc 90%);background-image:-o-linear-gradient(#3875d7 20%,#2a62bc 90%);background-image:linear-gradient(#3875d7 20%,#2a62bc 90%);color:#fff}.chosen-container .chosen-results li.no-results{color:#777;display:list-item;background:#f4f4f4}.chosen-container .chosen-results li.group-result{display:list-item;font-weight:700;cursor:default}.chosen-container .chosen-results li.group-option{padding-left:15px}.chosen-container .chosen-results li em{font-style:normal;text-decoration:underline}.chosen-container-multi .chosen-choices{position:relative;overflow:hidden;margin:0;padding:0 5px;width:100%;height:auto!important;height:1%;border:1px solid #aaa;background-color:#fff;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(1%,#eee),color-stop(15%,#fff));background-image:-webkit-linear-gradient(#eee 1%,#fff 15%);background-image:-moz-linear-gradient(#eee 1%,#fff 15%);background-image:-o-linear-gradient(#eee 1%,#fff 15%);background-image:linear-gradient(#eee 1%,#fff 15%);cursor:text}.chosen-container-multi .chosen-choices li{float:left;list-style:none}.chosen-container-multi .chosen-choices li.search-field{margin:0;padding:0;white-space:nowrap}.chosen-container-multi .chosen-choices li.search-field input[type=text]{margin:1px 0;padding:0;height:25px;outline:0;border:0!important;background:transparent!important;box-shadow:none;color:#999;font-size:100%;font-family:sans-serif;line-height:normal;border-radius:0}.chosen-container-multi .chosen-choices li.search-choice{position:relative;margin:3px 5px 3px 0;padding:3px 20px 3px 5px;border:1px solid #aaa;max-width:100%;border-radius:3px;background-color:#eee;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#f4f4f4),color-stop(50%,#f0f0f0),color-stop(52%,#e8e8e8),color-stop(100%,#eee));background-image:-webkit-linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-moz-linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-o-linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:linear-gradient(#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-size:100% 19px;background-repeat:repeat-x;background-clip:padding-box;box-shadow:0 0 2px #fff inset,0 1px 0 rgba(0,0,0,.05);color:#333;line-height:13px;cursor:default}.chosen-container-multi .chosen-choices li.search-choice span{word-wrap:break-word}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close{position:absolute;top:4px;right:3px;display:block;width:12px;height:12px;background:url(chosen-sprite.png) -42px 1px no-repeat;font-size:1px}.chosen-container-multi .chosen-choices li.search-choice .search-choice-close:hover{background-position:-42px -10px}.chosen-container-multi .chosen-choices li.search-choice-disabled{padding-right:5px;border:1px solid #ccc;background-color:#e4e4e4;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#f4f4f4),color-stop(50%,#f0f0f0),color-stop(52%,#e8e8e8),color-stop(100%,#eee));background-image:-webkit-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-moz-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:-o-linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);background-image:linear-gradient(top,#f4f4f4 20%,#f0f0f0 50%,#e8e8e8 52%,#eee 100%);color:#666}.chosen-container-multi .chosen-choices li.search-choice-focus{background:#d4d4d4}.chosen-container-multi .chosen-choices li.search-choice-focus .search-choice-close{background-position:-42px -10px}.chosen-container-multi .chosen-results{margin:0;padding:0}.chosen-container-multi .chosen-drop .result-selected{display:list-item;color:#ccc;cursor:default}.chosen-container-active .chosen-single{border:1px solid #5897fb;box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active.chosen-with-drop .chosen-single{border:1px solid #aaa;-moz-border-radius-bottomright:0;border-bottom-right-radius:0;-moz-border-radius-bottomleft:0;border-bottom-left-radius:0;background-image:-webkit-gradient(linear,50% 0,50% 100%,color-stop(20%,#eee),color-stop(80%,#fff));background-image:-webkit-linear-gradient(#eee 20%,#fff 80%);background-image:-moz-linear-gradient(#eee 20%,#fff 80%);background-image:-o-linear-gradient(#eee 20%,#fff 80%);background-image:linear-gradient(#eee 20%,#fff 80%);box-shadow:0 1px 0 #fff inset}.chosen-container-active.chosen-with-drop .chosen-single div{border-left:0;background:transparent}.chosen-container-active.chosen-with-drop .chosen-single div b{background-position:-18px 2px}.chosen-container-active .chosen-choices{border:1px solid #5897fb;box-shadow:0 0 5px rgba(0,0,0,.3)}.chosen-container-active .chosen-choices li.search-field input[type=text]{color:#222!important}.chosen-disabled{opacity:.5!important;cursor:default}.chosen-disabled .chosen-single{cursor:default}.chosen-disabled .chosen-choices .search-choice .search-choice-close{cursor:default}.chosen-rtl{text-align:right}.chosen-rtl .chosen-single{overflow:visible;padding:0 8px 0 0}.chosen-rtl .chosen-single span{margin-right:0;margin-left:26px;direction:rtl}.chosen-rtl .chosen-single-with-deselect span{margin-left:38px}.chosen-rtl .chosen-single div{right:auto;left:3px}.chosen-rtl .chosen-single abbr{right:auto;left:26px}.chosen-rtl .chosen-choices li{float:right}.chosen-rtl .chosen-choices li.search-field input[type=text]{direction:rtl}.chosen-rtl .chosen-choices li.search-choice{margin:3px 5px 3px 0;padding:3px 5px 3px 19px}.chosen-rtl .chosen-choices li.search-choice .search-choice-close{right:auto;left:4px}.chosen-rtl.chosen-container-single-nosearch .chosen-search,.chosen-rtl .chosen-drop{left:9999px}.chosen-rtl.chosen-container-single .chosen-results{margin:0 0 4px 4px;padding:0 4px 0 0}.chosen-rtl .chosen-results li.group-option{padding-right:15px;padding-left:0}.chosen-rtl.chosen-container-active.chosen-with-drop .chosen-single div{border-right:0}.chosen-rtl .chosen-search input[type=text]{padding:4px 5px 4px 20px;background:#fff url(chosen-sprite.png) no-repeat -30px -20px;background:url(chosen-sprite.png) no-repeat -30px -20px;direction:rtl}.chosen-rtl.chosen-container-single .chosen-single div b{background-position:6px 2px}.chosen-rtl.chosen-container-single.chosen-with-drop .chosen-single div b{background-position:-12px 2px}@media only screen and (-webkit-min-device-pixel-ratio:2),only screen and (min-resolution:144dpi){.chosen-rtl .chosen-search input[type=text],.chosen-container-single .chosen-single abbr,.chosen-container-single .chosen-single div b,.chosen-container-single .chosen-search input[type=text],.chosen-container-multi .chosen-choices .search-choice .search-choice-close,.chosen-container .chosen-results-scroll-down span,.chosen-container .chosen-results-scroll-up span{background-image:url(chosen-sprite@2x.png)!important;background-size:52px 37px!important;background-repeat:no-repeat!important}}
trunk/assets/plugin/chosen/chosen.proto.min.js ADDED
@@ -0,0 +1,2 @@
 
 
1
+ /* Chosen v1.2.0 | (c) 2011-2014 by Harvest | MIT License, https://github.com/harvesthq/chosen/blob/master/LICENSE.md */
2
+ !function(){var AbstractChosen,SelectParser,a,b={}.hasOwnProperty,c=function(a,c){function d(){this.constructor=a}for(var e in c)b.call(c,e)&&(a[e]=c[e]);return d.prototype=c.prototype,a.prototype=new d,a.__super__=c.prototype,a};SelectParser=function(){function SelectParser(){this.options_index=0,this.parsed=[]}return SelectParser.prototype.add_node=function(a){return"OPTGROUP"===a.nodeName.toUpperCase()?this.add_group(a):this.add_option(a)},SelectParser.prototype.add_group=function(a){var b,c,d,e,f,g;for(b=this.parsed.length,this.parsed.push({array_index:b,group:!0,label:this.escapeExpression(a.label),children:0,disabled:a.disabled}),f=a.childNodes,g=[],d=0,e=f.length;e>d;d++)c=f[d],g.push(this.add_option(c,b,a.disabled));return g},SelectParser.prototype.add_option=function(a,b,c){return"OPTION"===a.nodeName.toUpperCase()?(""!==a.text?(null!=b&&(this.parsed[b].children+=1),this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:a.value,text:a.text,html:a.innerHTML,selected:a.selected,disabled:c===!0?c:a.disabled,group_array_index:b,classes:a.className,style:a.style.cssText})):this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:!0}),this.options_index+=1):void 0},SelectParser.prototype.escapeExpression=function(a){var b,c;return null==a||a===!1?"":/[\&\<\>\"\'\`]/.test(a)?(b={"<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},c=/&(?!\w+;)|[\<\>\"\'\`]/g,a.replace(c,function(a){return b[a]||"&amp;"})):a},SelectParser}(),SelectParser.select_to_array=function(a){var b,c,d,e,f;for(c=new SelectParser,f=a.childNodes,d=0,e=f.length;e>d;d++)b=f[d],c.add_node(b);return c.parsed},AbstractChosen=function(){function AbstractChosen(a,b){this.form_field=a,this.options=null!=b?b:{},AbstractChosen.browser_is_supported()&&(this.is_multiple=this.form_field.multiple,this.set_default_text(),this.set_default_values(),this.setup(),this.set_up_html(),this.register_observers())}return AbstractChosen.prototype.set_default_values=function(){var a=this;return this.click_test_action=function(b){return a.test_active_click(b)},this.activate_action=function(b){return a.activate_field(b)},this.active_field=!1,this.mouse_on_container=!1,this.results_showing=!1,this.result_highlighted=null,this.allow_single_deselect=null!=this.options.allow_single_deselect&&null!=this.form_field.options[0]&&""===this.form_field.options[0].text?this.options.allow_single_deselect:!1,this.disable_search_threshold=this.options.disable_search_threshold||0,this.disable_search=this.options.disable_search||!1,this.enable_split_word_search=null!=this.options.enable_split_word_search?this.options.enable_split_word_search:!0,this.group_search=null!=this.options.group_search?this.options.group_search:!0,this.search_contains=this.options.search_contains||!1,this.single_backstroke_delete=null!=this.options.single_backstroke_delete?this.options.single_backstroke_delete:!0,this.max_selected_options=this.options.max_selected_options||1/0,this.inherit_select_classes=this.options.inherit_select_classes||!1,this.display_selected_options=null!=this.options.display_selected_options?this.options.display_selected_options:!0,this.display_disabled_options=null!=this.options.display_disabled_options?this.options.display_disabled_options:!0},AbstractChosen.prototype.set_default_text=function(){return this.default_text=this.form_field.getAttribute("data-placeholder")?this.form_field.getAttribute("data-placeholder"):this.is_multiple?this.options.placeholder_text_multiple||this.options.placeholder_text||AbstractChosen.default_multiple_text:this.options.placeholder_text_single||this.options.placeholder_text||AbstractChosen.default_single_text,this.results_none_found=this.form_field.getAttribute("data-no_results_text")||this.options.no_results_text||AbstractChosen.default_no_result_text},AbstractChosen.prototype.mouse_enter=function(){return this.mouse_on_container=!0},AbstractChosen.prototype.mouse_leave=function(){return this.mouse_on_container=!1},AbstractChosen.prototype.input_focus=function(){var a=this;if(this.is_multiple){if(!this.active_field)return setTimeout(function(){return a.container_mousedown()},50)}else if(!this.active_field)return this.activate_field()},AbstractChosen.prototype.input_blur=function(){var a=this;return this.mouse_on_container?void 0:(this.active_field=!1,setTimeout(function(){return a.blur_test()},100))},AbstractChosen.prototype.results_option_build=function(a){var b,c,d,e,f;for(b="",f=this.results_data,d=0,e=f.length;e>d;d++)c=f[d],b+=c.group?this.result_add_group(c):this.result_add_option(c),(null!=a?a.first:void 0)&&(c.selected&&this.is_multiple?this.choice_build(c):c.selected&&!this.is_multiple&&this.single_set_selected_text(c.text));return b},AbstractChosen.prototype.result_add_option=function(a){var b,c;return a.search_match?this.include_option_in_results(a)?(b=[],a.disabled||a.selected&&this.is_multiple||b.push("active-result"),!a.disabled||a.selected&&this.is_multiple||b.push("disabled-result"),a.selected&&b.push("result-selected"),null!=a.group_array_index&&b.push("group-option"),""!==a.classes&&b.push(a.classes),c=document.createElement("li"),c.className=b.join(" "),c.style.cssText=a.style,c.setAttribute("data-option-array-index",a.array_index),c.innerHTML=a.search_text,this.outerHTML(c)):"":""},AbstractChosen.prototype.result_add_group=function(a){var b;return a.search_match||a.group_match?a.active_options>0?(b=document.createElement("li"),b.className="group-result",b.innerHTML=a.search_text,this.outerHTML(b)):"":""},AbstractChosen.prototype.results_update_field=function(){return this.set_default_text(),this.is_multiple||this.results_reset_cleanup(),this.result_clear_highlight(),this.results_build(),this.results_showing?this.winnow_results():void 0},AbstractChosen.prototype.reset_single_select_options=function(){var a,b,c,d,e;for(d=this.results_data,e=[],b=0,c=d.length;c>b;b++)a=d[b],a.selected?e.push(a.selected=!1):e.push(void 0);return e},AbstractChosen.prototype.results_toggle=function(){return this.results_showing?this.results_hide():this.results_show()},AbstractChosen.prototype.results_search=function(){return this.results_showing?this.winnow_results():this.results_show()},AbstractChosen.prototype.winnow_results=function(){var a,b,c,d,e,f,g,h,i,j,k,l;for(this.no_results_clear(),d=0,f=this.get_search_text(),a=f.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),i=new RegExp(a,"i"),c=this.get_search_regex(a),l=this.results_data,j=0,k=l.length;k>j;j++)b=l[j],b.search_match=!1,e=null,this.include_option_in_results(b)&&(b.group&&(b.group_match=!1,b.active_options=0),null!=b.group_array_index&&this.results_data[b.group_array_index]&&(e=this.results_data[b.group_array_index],0===e.active_options&&e.search_match&&(d+=1),e.active_options+=1),(!b.group||this.group_search)&&(b.search_text=b.group?b.label:b.text,b.search_match=this.search_string_match(b.search_text,c),b.search_match&&!b.group&&(d+=1),b.search_match?(f.length&&(g=b.search_text.search(i),h=b.search_text.substr(0,g+f.length)+"</em>"+b.search_text.substr(g+f.length),b.search_text=h.substr(0,g)+"<em>"+h.substr(g)),null!=e&&(e.group_match=!0)):null!=b.group_array_index&&this.results_data[b.group_array_index].search_match&&(b.search_match=!0)));return this.result_clear_highlight(),1>d&&f.length?(this.update_results_content(""),this.no_results(f)):(this.update_results_content(this.results_option_build()),this.winnow_results_set_highlight())},AbstractChosen.prototype.get_search_regex=function(a){var b;return b=this.search_contains?"":"^",new RegExp(b+a,"i")},AbstractChosen.prototype.search_string_match=function(a,b){var c,d,e,f;if(b.test(a))return!0;if(this.enable_split_word_search&&(a.indexOf(" ")>=0||0===a.indexOf("["))&&(d=a.replace(/\[|\]/g,"").split(" "),d.length))for(e=0,f=d.length;f>e;e++)if(c=d[e],b.test(c))return!0},AbstractChosen.prototype.choices_count=function(){var a,b,c,d;if(null!=this.selected_option_count)return this.selected_option_count;for(this.selected_option_count=0,d=this.form_field.options,b=0,c=d.length;c>b;b++)a=d[b],a.selected&&(this.selected_option_count+=1);return this.selected_option_count},AbstractChosen.prototype.choices_click=function(a){return a.preventDefault(),this.results_showing||this.is_disabled?void 0:this.results_show()},AbstractChosen.prototype.keyup_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),b){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices_count()>0)return this.keydown_backstroke();if(!this.pending_backstroke)return this.result_clear_highlight(),this.results_search();break;case 13:if(a.preventDefault(),this.results_showing)return this.result_select(a);break;case 27:return this.results_showing&&this.results_hide(),!0;case 9:case 38:case 40:case 16:case 91:case 17:break;default:return this.results_search()}},AbstractChosen.prototype.clipboard_event_checker=function(){var a=this;return setTimeout(function(){return a.results_search()},50)},AbstractChosen.prototype.container_width=function(){return null!=this.options.width?this.options.width:""+this.form_field.offsetWidth+"px"},AbstractChosen.prototype.include_option_in_results=function(a){return this.is_multiple&&!this.display_selected_options&&a.selected?!1:!this.display_disabled_options&&a.disabled?!1:a.empty?!1:!0},AbstractChosen.prototype.search_results_touchstart=function(a){return this.touch_started=!0,this.search_results_mouseover(a)},AbstractChosen.prototype.search_results_touchmove=function(a){return this.touch_started=!1,this.search_results_mouseout(a)},AbstractChosen.prototype.search_results_touchend=function(a){return this.touch_started?this.search_results_mouseup(a):void 0},AbstractChosen.prototype.outerHTML=function(a){var b;return a.outerHTML?a.outerHTML:(b=document.createElement("div"),b.appendChild(a),b.innerHTML)},AbstractChosen.browser_is_supported=function(){return"Microsoft Internet Explorer"===window.navigator.appName?document.documentMode>=8:/iP(od|hone)/i.test(window.navigator.userAgent)?!1:/Android/i.test(window.navigator.userAgent)&&/Mobile/i.test(window.navigator.userAgent)?!1:!0},AbstractChosen.default_multiple_text="Select Some Options",AbstractChosen.default_single_text="Select an Option",AbstractChosen.default_no_result_text="No results match",AbstractChosen}(),this.Chosen=function(b){function Chosen(){return a=Chosen.__super__.constructor.apply(this,arguments)}return c(Chosen,b),Chosen.prototype.setup=function(){return this.current_selectedIndex=this.form_field.selectedIndex,this.is_rtl=this.form_field.hasClassName("chosen-rtl")},Chosen.prototype.set_default_values=function(){return Chosen.__super__.set_default_values.call(this),this.single_temp=new Template('<a class="chosen-single chosen-default" tabindex="-1"><span>#{default}</span><div><b></b></div></a><div class="chosen-drop"><div class="chosen-search"><input type="text" autocomplete="off" /></div><ul class="chosen-results"></ul></div>'),this.multi_temp=new Template('<ul class="chosen-choices"><li class="search-field"><input type="text" value="#{default}" class="default" autocomplete="off" style="width:25px;" /></li></ul><div class="chosen-drop"><ul class="chosen-results"></ul></div>'),this.no_results_temp=new Template('<li class="no-results">'+this.results_none_found+' "<span>#{terms}</span>"</li>')},Chosen.prototype.set_up_html=function(){var a,b;return a=["chosen-container"],a.push("chosen-container-"+(this.is_multiple?"multi":"single")),this.inherit_select_classes&&this.form_field.className&&a.push(this.form_field.className),this.is_rtl&&a.push("chosen-rtl"),b={"class":a.join(" "),style:"width: "+this.container_width()+";",title:this.form_field.title},this.form_field.id.length&&(b.id=this.form_field.id.replace(/[^\w]/g,"_")+"_chosen"),this.container=this.is_multiple?new Element("div",b).update(this.multi_temp.evaluate({"default":this.default_text})):new Element("div",b).update(this.single_temp.evaluate({"default":this.default_text})),this.form_field.hide().insert({after:this.container}),this.dropdown=this.container.down("div.chosen-drop"),this.search_field=this.container.down("input"),this.search_results=this.container.down("ul.chosen-results"),this.search_field_scale(),this.search_no_results=this.container.down("li.no-results"),this.is_multiple?(this.search_choices=this.container.down("ul.chosen-choices"),this.search_container=this.container.down("li.search-field")):(this.search_container=this.container.down("div.chosen-search"),this.selected_item=this.container.down(".chosen-single")),this.results_build(),this.set_tab_index(),this.set_label_behavior(),this.form_field.fire("chosen:ready",{chosen:this})},Chosen.prototype.register_observers=function(){var a=this;return this.container.observe("touchstart",function(b){return a.container_mousedown(b)}),this.container.observe("touchend",function(b){return a.container_mouseup(b)}),this.container.observe("mousedown",function(b){return a.container_mousedown(b)}),this.container.observe("mouseup",function(b){return a.container_mouseup(b)}),this.container.observe("mouseenter",function(b){return a.mouse_enter(b)}),this.container.observe("mouseleave",function(b){return a.mouse_leave(b)}),this.search_results.observe("mouseup",function(b){return a.search_results_mouseup(b)}),this.search_results.observe("mouseover",function(b){return a.search_results_mouseover(b)}),this.search_results.observe("mouseout",function(b){return a.search_results_mouseout(b)}),this.search_results.observe("mousewheel",function(b){return a.search_results_mousewheel(b)}),this.search_results.observe("DOMMouseScroll",function(b){return a.search_results_mousewheel(b)}),this.search_results.observe("touchstart",function(b){return a.search_results_touchstart(b)}),this.search_results.observe("touchmove",function(b){return a.search_results_touchmove(b)}),this.search_results.observe("touchend",function(b){return a.search_results_touchend(b)}),this.form_field.observe("chosen:updated",function(b){return a.results_update_field(b)}),this.form_field.observe("chosen:activate",function(b){return a.activate_field(b)}),this.form_field.observe("chosen:open",function(b){return a.container_mousedown(b)}),this.form_field.observe("chosen:close",function(b){return a.input_blur(b)}),this.search_field.observe("blur",function(b){return a.input_blur(b)}),this.search_field.observe("keyup",function(b){return a.keyup_checker(b)}),this.search_field.observe("keydown",function(b){return a.keydown_checker(b)}),this.search_field.observe("focus",function(b){return a.input_focus(b)}),this.search_field.observe("cut",function(b){return a.clipboard_event_checker(b)}),this.search_field.observe("paste",function(b){return a.clipboard_event_checker(b)}),this.is_multiple?this.search_choices.observe("click",function(b){return a.choices_click(b)}):this.container.observe("click",function(a){return a.preventDefault()})},Chosen.prototype.destroy=function(){return this.container.ownerDocument.stopObserving("click",this.click_test_action),this.form_field.stopObserving(),this.container.stopObserving(),this.search_results.stopObserving(),this.search_field.stopObserving(),null!=this.form_field_label&&this.form_field_label.stopObserving(),this.is_multiple?(this.search_choices.stopObserving(),this.container.select(".search-choice-close").each(function(a){return a.stopObserving()})):this.selected_item.stopObserving(),this.search_field.tabIndex&&(this.form_field.tabIndex=this.search_field.tabIndex),this.container.remove(),this.form_field.show()},Chosen.prototype.search_field_disabled=function(){return this.is_disabled=this.form_field.disabled,this.is_disabled?(this.container.addClassName("chosen-disabled"),this.search_field.disabled=!0,this.is_multiple||this.selected_item.stopObserving("focus",this.activate_action),this.close_field()):(this.container.removeClassName("chosen-disabled"),this.search_field.disabled=!1,this.is_multiple?void 0:this.selected_item.observe("focus",this.activate_action))},Chosen.prototype.container_mousedown=function(a){return this.is_disabled||(a&&"mousedown"===a.type&&!this.results_showing&&a.stop(),null!=a&&a.target.hasClassName("search-choice-close"))?void 0:(this.active_field?this.is_multiple||!a||a.target!==this.selected_item&&!a.target.up("a.chosen-single")||this.results_toggle():(this.is_multiple&&this.search_field.clear(),this.container.ownerDocument.observe("click",this.click_test_action),this.results_show()),this.activate_field())},Chosen.prototype.container_mouseup=function(a){return"ABBR"!==a.target.nodeName||this.is_disabled?void 0:this.results_reset(a)},Chosen.prototype.search_results_mousewheel=function(a){var b;return b=a.deltaY||-a.wheelDelta||a.detail,null!=b?(a.preventDefault(),"DOMMouseScroll"===a.type&&(b=40*b),this.search_results.scrollTop=b+this.search_results.scrollTop):void 0},Chosen.prototype.blur_test=function(){return!this.active_field&&this.container.hasClassName("chosen-container-active")?this.close_field():void 0},Chosen.prototype.close_field=function(){return this.container.ownerDocument.stopObserving("click",this.click_test_action),this.active_field=!1,this.results_hide(),this.container.removeClassName("chosen-container-active"),this.clear_backstroke(),this.show_search_field_default(),this.search_field_scale()},Chosen.prototype.activate_field=function(){return this.container.addClassName("chosen-container-active"),this.active_field=!0,this.search_field.value=this.search_field.value,this.search_field.focus()},Chosen.prototype.test_active_click=function(a){return a.target.up(".chosen-container")===this.container?this.active_field=!0:this.close_field()},Chosen.prototype.results_build=function(){return this.parsing=!0,this.selected_option_count=null,this.results_data=SelectParser.select_to_array(this.form_field),this.is_multiple?this.search_choices.select("li.search-choice").invoke("remove"):this.is_multiple||(this.single_set_selected_text(),this.disable_search||this.form_field.options.length<=this.disable_search_threshold?(this.search_field.readOnly=!0,this.container.addClassName("chosen-container-single-nosearch")):(this.search_field.readOnly=!1,this.container.removeClassName("chosen-container-single-nosearch"))),this.update_results_content(this.results_option_build({first:!0})),this.search_field_disabled(),this.show_search_field_default(),this.search_field_scale(),this.parsing=!1},Chosen.prototype.result_do_highlight=function(a){var b,c,d,e,f;return this.result_clear_highlight(),this.result_highlight=a,this.result_highlight.addClassName("highlighted"),d=parseInt(this.search_results.getStyle("maxHeight"),10),f=this.search_results.scrollTop,e=d+f,c=this.result_highlight.positionedOffset().top,b=c+this.result_highlight.getHeight(),b>=e?this.search_results.scrollTop=b-d>0?b-d:0:f>c?this.search_results.scrollTop=c:void 0},Chosen.prototype.result_clear_highlight=function(){return this.result_highlight&&this.result_highlight.removeClassName("highlighted"),this.result_highlight=null},Chosen.prototype.results_show=function(){return this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field.fire("chosen:maxselected",{chosen:this}),!1):(this.container.addClassName("chosen-with-drop"),this.results_showing=!0,this.search_field.focus(),this.search_field.value=this.search_field.value,this.winnow_results(),this.form_field.fire("chosen:showing_dropdown",{chosen:this}))},Chosen.prototype.update_results_content=function(a){return this.search_results.update(a)},Chosen.prototype.results_hide=function(){return this.results_showing&&(this.result_clear_highlight(),this.container.removeClassName("chosen-with-drop"),this.form_field.fire("chosen:hiding_dropdown",{chosen:this})),this.results_showing=!1},Chosen.prototype.set_tab_index=function(){var a;return this.form_field.tabIndex?(a=this.form_field.tabIndex,this.form_field.tabIndex=-1,this.search_field.tabIndex=a):void 0},Chosen.prototype.set_label_behavior=function(){var a=this;return this.form_field_label=this.form_field.up("label"),null==this.form_field_label&&(this.form_field_label=$$("label[for='"+this.form_field.id+"']").first()),null!=this.form_field_label?this.form_field_label.observe("click",function(b){return a.is_multiple?a.container_mousedown(b):a.activate_field()}):void 0},Chosen.prototype.show_search_field_default=function(){return this.is_multiple&&this.choices_count()<1&&!this.active_field?(this.search_field.value=this.default_text,this.search_field.addClassName("default")):(this.search_field.value="",this.search_field.removeClassName("default"))},Chosen.prototype.search_results_mouseup=function(a){var b;return b=a.target.hasClassName("active-result")?a.target:a.target.up(".active-result"),b?(this.result_highlight=b,this.result_select(a),this.search_field.focus()):void 0},Chosen.prototype.search_results_mouseover=function(a){var b;return b=a.target.hasClassName("active-result")?a.target:a.target.up(".active-result"),b?this.result_do_highlight(b):void 0},Chosen.prototype.search_results_mouseout=function(a){return a.target.hasClassName("active-result")||a.target.up(".active-result")?this.result_clear_highlight():void 0},Chosen.prototype.choice_build=function(a){var b,c,d=this;return b=new Element("li",{"class":"search-choice"}).update("<span>"+a.html+"</span>"),a.disabled?b.addClassName("search-choice-disabled"):(c=new Element("a",{href:"#","class":"search-choice-close",rel:a.array_index}),c.observe("click",function(a){return d.choice_destroy_link_click(a)}),b.insert(c)),this.search_container.insert({before:b})},Chosen.prototype.choice_destroy_link_click=function(a){return a.preventDefault(),a.stopPropagation(),this.is_disabled?void 0:this.choice_destroy(a.target)},Chosen.prototype.choice_destroy=function(a){return this.result_deselect(a.readAttribute("rel"))?(this.show_search_field_default(),this.is_multiple&&this.choices_count()>0&&this.search_field.value.length<1&&this.results_hide(),a.up("li").remove(),this.search_field_scale()):void 0},Chosen.prototype.results_reset=function(){return this.reset_single_select_options(),this.form_field.options[0].selected=!0,this.single_set_selected_text(),this.show_search_field_default(),this.results_reset_cleanup(),"function"==typeof Event.simulate&&this.form_field.simulate("change"),this.active_field?this.results_hide():void 0},Chosen.prototype.results_reset_cleanup=function(){var a;return this.current_selectedIndex=this.form_field.selectedIndex,a=this.selected_item.down("abbr"),a?a.remove():void 0},Chosen.prototype.result_select=function(a){var b,c;return this.result_highlight?(b=this.result_highlight,this.result_clear_highlight(),this.is_multiple&&this.max_selected_options<=this.choices_count()?(this.form_field.fire("chosen:maxselected",{chosen:this}),!1):(this.is_multiple?b.removeClassName("active-result"):this.reset_single_select_options(),b.addClassName("result-selected"),c=this.results_data[b.getAttribute("data-option-array-index")],c.selected=!0,this.form_field.options[c.options_index].selected=!0,this.selected_option_count=null,this.is_multiple?this.choice_build(c):this.single_set_selected_text(c.text),(a.metaKey||a.ctrlKey)&&this.is_multiple||this.results_hide(),this.search_field.value="","function"!=typeof Event.simulate||!this.is_multiple&&this.form_field.selectedIndex===this.current_selectedIndex||this.form_field.simulate("change"),this.current_selectedIndex=this.form_field.selectedIndex,this.search_field_scale())):void 0},Chosen.prototype.single_set_selected_text=function(a){return null==a&&(a=this.default_text),a===this.default_text?this.selected_item.addClassName("chosen-default"):(this.single_deselect_control_build(),this.selected_item.removeClassName("chosen-default")),this.selected_item.down("span").update(a)},Chosen.prototype.result_deselect=function(a){var b;return b=this.results_data[a],this.form_field.options[b.options_index].disabled?!1:(b.selected=!1,this.form_field.options[b.options_index].selected=!1,this.selected_option_count=null,this.result_clear_highlight(),this.results_showing&&this.winnow_results(),"function"==typeof Event.simulate&&this.form_field.simulate("change"),this.search_field_scale(),!0)},Chosen.prototype.single_deselect_control_build=function(){return this.allow_single_deselect?(this.selected_item.down("abbr")||this.selected_item.down("span").insert({after:'<abbr class="search-choice-close"></abbr>'}),this.selected_item.addClassName("chosen-single-with-deselect")):void 0},Chosen.prototype.get_search_text=function(){return this.search_field.value===this.default_text?"":this.search_field.value.strip().escapeHTML()},Chosen.prototype.winnow_results_set_highlight=function(){var a;return this.is_multiple||(a=this.search_results.down(".result-selected.active-result")),null==a&&(a=this.search_results.down(".active-result")),null!=a?this.result_do_highlight(a):void 0},Chosen.prototype.no_results=function(a){return this.search_results.insert(this.no_results_temp.evaluate({terms:a})),this.form_field.fire("chosen:no_results",{chosen:this})},Chosen.prototype.no_results_clear=function(){var a,b;for(a=null,b=[];a=this.search_results.down(".no-results");)b.push(a.remove());return b},Chosen.prototype.keydown_arrow=function(){var a;return this.results_showing&&this.result_highlight?(a=this.result_highlight.next(".active-result"))?this.result_do_highlight(a):void 0:this.results_show()},Chosen.prototype.keyup_arrow=function(){var a,b,c;return this.results_showing||this.is_multiple?this.result_highlight?(c=this.result_highlight.previousSiblings(),a=this.search_results.select("li.active-result"),b=c.intersect(a),b.length?this.result_do_highlight(b.first()):(this.choices_count()>0&&this.results_hide(),this.result_clear_highlight())):void 0:this.results_show()},Chosen.prototype.keydown_backstroke=function(){var a;return this.pending_backstroke?(this.choice_destroy(this.pending_backstroke.down("a")),this.clear_backstroke()):(a=this.search_container.siblings().last(),a&&a.hasClassName("search-choice")&&!a.hasClassName("search-choice-disabled")?(this.pending_backstroke=a,this.pending_backstroke&&this.pending_backstroke.addClassName("search-choice-focus"),this.single_backstroke_delete?this.keydown_backstroke():this.pending_backstroke.addClassName("search-choice-focus")):void 0)},Chosen.prototype.clear_backstroke=function(){return this.pending_backstroke&&this.pending_backstroke.removeClassName("search-choice-focus"),this.pending_backstroke=null},Chosen.prototype.keydown_checker=function(a){var b,c;switch(b=null!=(c=a.which)?c:a.keyCode,this.search_field_scale(),8!==b&&this.pending_backstroke&&this.clear_backstroke(),b){case 8:this.backstroke_length=this.search_field.value.length;break;case 9:this.results_showing&&!this.is_multiple&&this.result_select(a),this.mouse_on_container=!1;break;case 13:this.results_showing&&a.preventDefault();break;case 32:this.disable_search&&a.preventDefault();break;case 38:a.preventDefault(),this.keyup_arrow();break;case 40:a.preventDefault(),this.keydown_arrow()}},Chosen.prototype.search_field_scale=function(){var a,b,c,d,e,f,g,h,i;if(this.is_multiple){for(c=0,g=0,e="position:absolute; left: -1000px; top: -1000px; display:none;",f=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"],h=0,i=f.length;i>h;h++)d=f[h],e+=d+":"+this.search_field.getStyle(d)+";";return a=new Element("div",{style:e}).update(this.search_field.value.escapeHTML()),document.body.appendChild(a),g=Element.measure(a,"width")+25,a.remove(),b=this.container.getWidth(),g>b-10&&(g=b-10),this.search_field.setStyle({width:g+"px"})}},Chosen}(AbstractChosen)}.call(this);
trunk/class-aftership-api.php ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AfterShip API
4
+ *
5
+ * Handles AfterShip-API endpoint requests
6
+ *
7
+ * @author AfterShip
8
+ * @category API
9
+ * @package AfterShip
10
+ * @since 1.0
11
+ */
12
+
13
+ if (!defined('ABSPATH')) exit; // Exit if accessed directly
14
+
15
+ class AfterShip_API
16
+ {
17
+
18
+ /** This is the major version for the REST API and takes
19
+ * first-order position in endpoint URLs
20
+ */
21
+ const VERSION = 1;
22
+
23
+ /** @var WC_API_Server the REST API server */
24
+ public $server;
25
+
26
+ /**
27
+ * Setup class
28
+ *
29
+ * @access public
30
+ * @since 2.0
31
+ * @return WC_API
32
+ */
33
+ public function __construct()
34
+ {
35
+
36
+ // add query vars
37
+ add_filter('query_vars', array($this, 'add_query_vars'), 0);
38
+
39
+ // register API endpoints
40
+ add_action('init', array($this, 'add_endpoint'), 0);
41
+
42
+ // handle REST/legacy API request
43
+ add_action('parse_request', array($this, 'handle_api_requests'), 0);
44
+ }
45
+
46
+ /**
47
+ * add_query_vars function.
48
+ *
49
+ * @access public
50
+ * @since 2.0
51
+ * @param $vars
52
+ * @return array
53
+ */
54
+ public function add_query_vars($vars)
55
+ {
56
+ $vars[] = 'aftership-api';
57
+ $vars[] = 'aftership-api-route';
58
+ return $vars;
59
+ }
60
+
61
+ /**
62
+ * add_endpoint function.
63
+ *
64
+ * @access public
65
+ * @since 2.0
66
+ * @return void
67
+ */
68
+ public function add_endpoint()
69
+ {
70
+
71
+ // REST API
72
+ add_rewrite_rule('^aftership-api\/v' . self::VERSION . '/?$', 'index.php?aftership-api-route=/', 'top');
73
+ add_rewrite_rule('^aftership-api\/v' . self::VERSION . '(.*)?', 'index.php?aftership-api-route=$matches[1]', 'top');
74
+
75
+ // legacy API for payment gateway IPNs
76
+ add_rewrite_endpoint('aftership-api', EP_ALL);
77
+ }
78
+
79
+
80
+ /**
81
+ * API request - Trigger any API requests
82
+ *
83
+ * @access public
84
+ * @since 2.0
85
+ * @return void
86
+ */
87
+ public function handle_api_requests()
88
+ {
89
+ global $wp;
90
+
91
+ if (!empty($_GET['aftership-api']))
92
+ $wp->query_vars['aftership-api'] = $_GET['aftership-api'];
93
+
94
+ if (!empty($_GET['aftership-api-route']))
95
+ $wp->query_vars['aftership-api-route'] = $_GET['aftership-api-route'];
96
+
97
+ // REST API request
98
+ if (!empty($wp->query_vars['aftership-api-route'])) {
99
+
100
+ define('AFTERSHIP_API_REQUEST', true);
101
+
102
+ // load required files
103
+ $this->includes();
104
+
105
+ $this->server = new AfterShip_API_Server($wp->query_vars['aftership-api-route']);
106
+
107
+ // load API resource classes
108
+ $this->register_resources($this->server);
109
+
110
+ // Fire off the request
111
+ $this->server->serve_request();
112
+
113
+ exit;
114
+ }
115
+
116
+ // legacy API requests
117
+ if (!empty($wp->query_vars['aftership-api'])) {
118
+
119
+ // Buffer, we won't want any output here
120
+ ob_start();
121
+
122
+ // Get API trigger
123
+ $api = strtolower(esc_attr($wp->query_vars['aftership-api']));
124
+
125
+ // Load class if exists
126
+ if (class_exists($api))
127
+ $api_class = new $api();
128
+
129
+ // Trigger actions
130
+ do_action('woocommerce_api_' . $api);
131
+
132
+ // Done, clear buffer and exit
133
+ ob_end_clean();
134
+ die('1');
135
+ }
136
+ }
137
+
138
+
139
+ /**
140
+ * Include required files for REST API request
141
+ *
142
+ * @since 2.1
143
+ */
144
+ private function includes()
145
+ {
146
+
147
+
148
+ // API server / response handlers
149
+ include_once('api/class-aftership-api-server.php');
150
+ include_once('api/interface-aftership-api-handler.php');
151
+ include_once('api/class-aftership-api-json-handler.php');
152
+
153
+ // authentication
154
+ include_once('api/class-aftership-api-authentication.php');
155
+ $this->authentication = new AfterShip_API_Authentication();
156
+
157
+ include_once('api/class-aftership-api-resource.php');
158
+
159
+ // self api
160
+ include_once('api/class-aftership-api-orders.php');
161
+
162
+ // allow plugins to load other response handlers or resource classes
163
+ do_action('woocommerce_api_loaded');
164
+ }
165
+
166
+ /**
167
+ * Register available API resources
168
+ *
169
+ * @since 2.1
170
+ * @param object $server the REST server
171
+ */
172
+ public function register_resources($server)
173
+ {
174
+
175
+ $api_classes = apply_filters('aftership_api_classes',
176
+ array(
177
+ 'AfterShip_API_Orders',
178
+ )
179
+ );
180
+
181
+ foreach ($api_classes as $api_class) {
182
+ $this->$api_class = new $api_class($server);
183
+ }
184
+ }
185
+
186
+ }
trunk/class-aftership-settings.php ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * AfterShip Admin
4
+ *
5
+ * Handles AfterShip-Admin endpoint requests
6
+ *
7
+ * @author AfterShip
8
+ * @category Admin
9
+ * @package AfterShip
10
+ * @since 1.0
11
+ */
12
+
13
+ if (!defined('ABSPATH')) exit; // Exit if accessed directly
14
+
15
+ /**
16
+ * Required functions
17
+ */
18
+ if ( ! class_exists( 'WC_Dependencies' ) )
19
+ require_once 'woo-includes/class-wc-dependencies.php';
20
+
21
+ class AfterShip_Settings
22
+ {
23
+ /**
24
+ * Holds the values to be used in the fields callbacks
25
+ */
26
+ private $options;
27
+
28
+ private $plugins;
29
+
30
+ /**
31
+ * Start up
32
+ */
33
+ public function __construct()
34
+ {
35
+ $this->plugins[] = array(
36
+ 'value' => 'aftership',
37
+ 'label' => 'AfterShip',
38
+ 'path' => 'aftership-woocommerce-tracking/aftership.php'
39
+ );
40
+ $this->plugins[] = array(
41
+ 'value' => 'wc-shipment-tracking',
42
+ 'label' => 'WooCommerce Shipment Tracking',
43
+ 'path' => 'woocommerce-shipment-tracking/shipment-tracking.php'
44
+ );
45
+
46
+ add_action('admin_menu', array($this, 'add_plugin_page'));
47
+ add_action('admin_init', array($this, 'page_init'));
48
+ add_action('admin_print_styles', array($this, 'admin_styles'));
49
+ add_action('admin_print_scripts', array(&$this, 'library_scripts'));
50
+ }
51
+
52
+
53
+ public function admin_styles()
54
+ {
55
+ wp_enqueue_style('aftership_styles_chosen', plugins_url(basename(dirname(__FILE__))) . '/assets/plugin/chosen/chosen.min.css');
56
+ wp_enqueue_style('aftership_styles', plugins_url(basename(dirname(__FILE__))) . '/assets/css/admin.css');
57
+ }
58
+
59
+ public function library_scripts()
60
+ {
61
+ wp_enqueue_script('aftership_styles_chosen_jquery', plugins_url(basename(dirname(__FILE__))) . '/assets/plugin/chosen/chosen.jquery.min.js');
62
+ wp_enqueue_script('aftership_styles_chosen_proto', plugins_url(basename(dirname(__FILE__))) . '/assets/plugin/chosen/chosen.proto.min.js');
63
+ wp_enqueue_script('aftership_script_util', plugins_url(basename(dirname(__FILE__))) . '/assets/js/util.js');
64
+ wp_enqueue_script('aftership_script_couriers', plugins_url(basename(dirname(__FILE__))) . '/assets/js/couriers.js');
65
+ wp_enqueue_script('aftership_script_setting', plugins_url(basename(dirname(__FILE__))) . '/assets/js/setting.js');
66
+ }
67
+
68
+ /**
69
+ * Add options page
70
+ */
71
+ public function add_plugin_page()
72
+ {
73
+ // This page will be under "Settings"
74
+ add_options_page(
75
+ 'AfterShip Settings Admin',
76
+ 'AfterShip',
77
+ 'manage_options',
78
+ 'aftership-setting-admin',
79
+ array($this, 'create_admin_page')
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Options page callback
85
+ */
86
+ public function create_admin_page()
87
+ {
88
+ // Set class property
89
+ $this->options = get_option('aftership_option_name');
90
+ ?>
91
+ <div class="wrap">
92
+ <?php screen_icon(); ?>
93
+ <h2>AfterShip Settings</h2>
94
+
95
+ <form method="post" action="options.php">
96
+ <?php
97
+ // This prints out all hidden setting fields
98
+ settings_fields('aftership_option_group');
99
+ do_settings_sections('aftership-setting-admin');
100
+ submit_button();
101
+ ?>
102
+ </form>
103
+ </div>
104
+ <?php
105
+ }
106
+
107
+ /**
108
+ * Register and add settings
109
+ */
110
+ public function page_init()
111
+ {
112
+ register_setting(
113
+ 'aftership_option_group', // Option group
114
+ 'aftership_option_name', // Option name
115
+ array($this, 'sanitize') // Sanitize
116
+ );
117
+
118
+ add_settings_section(
119
+ 'aftership_setting_section_id', // ID
120
+ '', // Title
121
+ array($this, 'print_section_info'), // Callback
122
+ 'aftership-setting-admin' // Page
123
+ );
124
+
125
+ add_settings_field(
126
+ 'plugin',
127
+ 'Plugin',
128
+ array($this, 'plugin_callback'),
129
+ 'aftership-setting-admin',
130
+ 'aftership_setting_section_id'
131
+ );
132
+
133
+ add_settings_field(
134
+ 'couriers',
135
+ 'Couriers',
136
+ array($this, 'couriers_callback'),
137
+ 'aftership-setting-admin',
138
+ 'aftership_setting_section_id'
139
+ );
140
+
141
+ add_settings_field(
142
+ 'use_track_button',
143
+ 'Display Track Button at Order History Page',
144
+ array($this, 'track_button_callback'),
145
+ 'aftership-setting-admin',
146
+ 'aftership_setting_section_id'
147
+ );
148
+ }
149
+
150
+ /**
151
+ * Sanitize each setting field as needed
152
+ *
153
+ * @param array $input Contains all settings fields as array keys
154
+ */
155
+ public function sanitize($input)
156
+ {
157
+ $new_input = array();
158
+
159
+ if (isset($input['couriers'])) {
160
+ $new_input['couriers'] = sanitize_text_field($input['couriers']);
161
+ }
162
+
163
+ if (isset($input['plugin'])) {
164
+ $new_input['plugin'] = sanitize_text_field($input['plugin']);
165
+ }
166
+
167
+ if (isset($input['use_track_button'])) {
168
+ $new_input['use_track_button'] = true;
169
+ }
170
+
171
+ return $new_input;
172
+ }
173
+
174
+ /**
175
+ * Print the Section text
176
+ */
177
+ public function print_section_info()
178
+ {
179
+ //print 'Enter your settings below:';
180
+ }
181
+
182
+ public function couriers_callback(){
183
+
184
+ $couriers = array();
185
+ if (isset($this->options['couriers'])){
186
+ $couriers = explode(',', $this->options['couriers']);
187
+ }
188
+
189
+ // print_r($couriers);
190
+ echo '<select data-placeholder="Please select couriers" id="couriers_select" class="chosen-select " multiple style="width:100%">';
191
+ echo '</select>';
192
+ // echo '<br><a href="https://www.aftership.com/settings/courier" target="_blank">Update carrier list</a>';
193
+ echo '<input type="hidden" id="couriers" name="aftership_option_name[couriers]" value="' . implode(",", $couriers) . '"/>';
194
+
195
+ }
196
+
197
+ public function plugin_callback()
198
+ {
199
+
200
+ $options = "";
201
+ foreach ($this->plugins as $plugin) {
202
+ //print_r($plugin);
203
+ if (WC_Dependencies::plugin_active_check($plugin['path'])) {
204
+ $option = '<option value="' . $plugin['value'] . '"';
205
+
206
+ if (isset($this->options['plugin']) && esc_attr($this->options['plugin']) == $plugin['value']) {
207
+ $option .= ' selected="selected"';
208
+ }
209
+
210
+ $option .= '>' . $plugin['label'] . '</option>';
211
+ $options .= $option;
212
+ }
213
+ }
214
+
215
+ printf(
216
+ '<select id="plugin" name="aftership_option_name[plugin]" class="aftership_dropdown">' . $options . '</select>'
217
+ );
218
+ }
219
+
220
+ public function track_button_callback()
221
+ {
222
+ printf(
223
+ '<label><input type="checkbox" id="use_track_button" name="aftership_option_name[use_track_button]" %s>Use Track Button</label>',
224
+ (isset($this->options['use_track_button']) && $this->options['use_track_button'] === true) ? 'checked="checked"' : ''
225
+ );
226
+ }
227
+ }
228
+
229
+
230
+ if (is_admin())
231
+ $aftership_settings = new AfterShip_Settings();
trunk/readme.txt ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === Plugin Name ===
2
+ Contributors: aftership
3
+ Donate link: https://www.aftership.com/
4
+ Tags: shipping, tracking, ups, usps, fedex, dhl, tnt, dpd, post, shipment, woocommerce, tracking number, aftership, package tracking, fulfilment, tracking link, carrier, courier, woo commerce, woocommerce shipment tracking, shipping details plugin, widget, shipstation, track, package
5
+ Requires at least: 2.9
6
+ Tested up to: 4.0
7
+ Stable tag: 1.2.6
8
+ License: GPLv2 or later
9
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
+
11
+ Add tracking number and carrier name to WooCommerce, display tracking info at order history page, auto import tracking numbers to AfterShip.
12
+
13
+ == Description ==
14
+
15
+ ###Add tracking number and carrier name fields (Free)
16
+ By installing AfterShip plugin, you can automatically add Tracking Number and Carrier fields to your admin panel. After you fulfill an order, simply enter the tracking number and select a carrier at WooCommerce, the same info will be displayed at customer's order history page. AfterShip supports over 200 carriers worldwide, you can select carriers by [signing up a free AfterShip account](https://www.aftership.com/signup).
17
+
18
+ ###Display tracking info at order history page (Free)
19
+ The plugin allows you to insert the AfterShip Track Button to order history page, so that your customers can track the latest order status in one click.
20
+
21
+ ###Support 200+ International Carrier (Free)
22
+ AfterShip supports UPS tracking, FedEx tracking, USPS tracking, DHL tracking and shipment tracking of over 200+ carriers worlwide. Please scroll down to see the full list of our supported carriers.
23
+
24
+
25
+ ###Track all shipments in one place (Free)
26
+ By setting up an auto import of tracking numbers to AfterShip, you can get the latest tracking info of all shipments in one place. Each free account comes with a Dashboard to monitor current statuses of all shipments. You can also filter your shipments by dates, statuses, couriers and destination. Find out if all your shipments are delivered on time and discover any exceptions. Support [WooCommerce Shipment Tracking Plugin](http://www.woothemes.com/products/shipment-tracking/) as well.
27
+
28
+ ###Notify customers of delivery updates (Premium)
29
+ Upgrade to [Premium](https://www.aftership.com/premium) to automatically send out delivery notifications to customers or yourself. Your choice of notification triggers - In transit, Out for delivery, Delivered, Failed delivery attempt or Exceptions. Use your own email to send out notifications, and customize messages to add store logo, URL, a tracking link to get more returned customers after shipping!
30
+
31
+ [youtube http://www.youtube.com/watch?v=1zCCx8Ap9ms]
32
+
33
+ ###Full list of supported carriers (200+):
34
+ **①. US, Canada & Global Tracking (23)**
35
+
36
+ FedEx • UPS • DHL Express • USPS • UPS Mail Innovations • TNT • DPD • Aramex • DHL Global Mail • Toll Global Express • UPS Freight • Canada Post • Purolator • APC Postal Logistics • OnTrac • YRC • Asendia USA • LaserShip • i-parcel • ABF Freight • Estes • RL Carriers • Greyhound
37
+
38
+ **②. UK & Ireland Tracking (19)**
39
+
40
+ Royal Mail • Parcel Force • FedEx UK • DPD UK • SkyNet Worldwide Express • TNT UK • Interlink Express • UK Mail • YODEL • myHermes UK • City Link • Collect+ • DX • Deltec Courier • XDP Express • An Post • DPD Ireland • Fastway Ireland • Arrow XL
41
+
42
+ **③. Europe Tracking (66)**
43
+
44
+ Direct Link • International Seur • GLS • DHL Benelux • PostNord Logistics • Deutsche Post Mail • Deutsche Post DHL • Hermes Germany • DPD Germany • Austrian Post (Registered) • Austrian Post (Express) • Swiss Post • Correos de España • NACEX Spain • Spanish Seur • ASM • MRW • Redur Spain • Portugal CTT • Chronopost Portugal • Portugal Seur • La Poste • Colissimo • Chronopost France • Colis Privé • TNT France • Geodis Calberson France • PostNL Domestic • PostNL International • PostNL International 3S • DHL Netherlands • GLS Netherlands • Selektvracht • DHL Parcel NL • Belgium Post • bpost international • Itella Posti • Posten Norge • Sweden Posten • Post Danmark • Italy SDA • Poste Italiane Paccocelere • Poste Italiane • BRT Bartolini • GLS Italy • TNT Italy • Nexive (TNT Post Italy) • Russian Post • Nova Poshta • DHL Poland • Poczta Polska • DPD Poland • Siodemka • OPEK • Lietuvos paštas • Česká Pošta • ELTA Hellenic Post • ACS Courier • PTT Posta • Belpost • Bulgarian Posts • Hrvatska PoÅ¡ta • Kuehne + Nagel • Poșta Română • Cyprus Post • UkrPoshta
45
+
46
+ **④. Asia & China Tracking (57)**
47
+
48
+ DHL Global Mail Asia • Kerry Express Thailand • Singapore Post • Singapore Speedpost • TAQBIN Singapore • Korea Post • CJ GLS • India Post Domestic • India Post International • Delhivery • Bluedart • DTDC India • Professional Couriers • Safexpress • Red Express • Red Express Waybill • First Flight Couriers • Gati-KWE • GoJaVAS • Japan Post • Yamato Japan • Sagawa • Taiwan Post • Malaysia Post EMS / Poslaju • Malaysia Post - Registered • TAQBIN Malaysia • GDEX • SkyNet Malaysia • City-Link Express • Thailand Thai Post • Dynamic Logistics • 2GO • Xend • AIR21 • Jam Express • Pos Indonesia Domestic • Pos Indonesia Int'l • JNE • RPX Indonesia • Tiki • Wahana • LBC Express • First Logistics • Vietnam Post • Vietnam Post EMS • ViettelPost • Cambodia Post • S.F. Express • China Post • China EMS • 4PX • Yanwen • EC-Firstclass • AuPost China • WeDo Logistics • PayPal Package • JCEX • STO Express • Flyt Express • Hong Kong Post • TAQBIN Hong Kong • TGX
49
+
50
+ **⑤. Australia & New Zealand Tracking (10)**
51
+
52
+ Australia Post • Toll Priority • Toll IPEC • TNT Australia • Fastway Australia • StarTrack • Star Track Express • Couriers Please • New Zealand Post • CourierPost
53
+
54
+ **⑥. Latin America, Middle East & Other Tracking (18)**
55
+
56
+ Correos de Mexico • Estafeta • Mexico Senda Express • Mexico Redpack • Mexico Multipack • Mexico AeroFlash • Brazil Correios • Flash Courier • Correos Chile • Correo Argentino • OCA Argentina • Israel Post • Israel Post Domestic • South African Post Office • Fastway South Africa • Saudi Post • NiPost • Evergreen
57
+
58
+
59
+ == Installation ==
60
+
61
+ 1. [Sign up AfterShip account for FREE](https://www.aftership.com/signup)
62
+ 2. [Download AfterShip plugin](http://downloads.wordpress.org/plugin/aftership-woocommerce-tracking.zip), then install and activate at wordpress.
63
+ 3. Select AfterShip Plugin or [WooCommerce Shipment Tracking Plugin](http://www.woothemes.com/products/shipment-tracking/) for entering tracking number
64
+ 4. Check `Use Track Button` for displaying tracking info at order history page
65
+ 5. Generate [AfterShip's WordPress API Key](http://aftership.uservoice.com/knowledgebase/articles/405909) under `Users` > `Your Profile`
66
+ 6. Input `Store URL` and `AfterShip's WordPress API Key` at [AfterShip WooCommerce page](https://www.aftership.com/apps/woocommerce)
67
+
68
+ *** This plugin requires at least PHP 5.2.4
69
+ *** This plugin requires at least WooCommerce 2.1
70
+
71
+ ###Further Reading
72
+
73
+ * [How to insert AfterShip Track Button to WordPress or WooCommerce](https://aftership.uservoice.com/knowledgebase/articles/268665)
74
+ * [Premium account pricing](https://www.aftership.com/premium)
75
+ * [List of supported couriers](https://www.aftership.com/courier)
76
+ * [Free vs Premium](https://www.aftership.com/free-vs-premium)
77
+ * [SMS Charges](https://www.aftership.com/sms-pricing)
78
+ * [Isn’t my store or courier sending notifications already?](https://aftership.uservoice.com/knowledgebase/articles/169634-isn-t-my-store-or-courier-sending-notifications-al)
79
+
80
+
81
+ == Frequently Asked Questions ==
82
+
83
+ You'll find the FAQ on [AfterShip.com](https://aftership.uservoice.com/knowledgebase)
84
+
85
+
86
+ == Screenshots ==
87
+
88
+ 1. Add tracking number and carrier to WooCommerce
89
+ 2. Display tracking info at order history page
90
+ 3. Support tracking of UPS, DHL, FedEx, USPS and 200+ carriers
91
+ 4. AfterShip dashboard
92
+ 5. Track all shipments in one place
93
+ 6. View exception, and filter by status
94
+ 7. Automatically send out delivery notifications
95
+
96
+ == Changelog ==
97
+
98
+ = 1.2.6 =
99
+ * Fix installation problem, php array
100
+
101
+ = 1.2.5 =
102
+ * No longer require to input AfterShip API key at Wordpress
103
+ * Allow carrier selection at Wordpress
104
+ * Support PHP 5.2.4 or greater
105
+
106
+
107
+ = 1.2.4 =
108
+ * Fix enable track button issues
109
+ * Support at least WooCommerce 2.1
110
+
111
+ = 1.2.3 =
112
+ * Fix undefined variables issue
113
+ * Add Icon
114
+ * Support Wordpress 4.0
115
+
116
+ = 1.2.2 =
117
+ * Fix the header issues of different server
118
+
119
+ = 1.2.1 =
120
+ * Fix the header issues of different server
121
+
122
+ = 1.2.0 =
123
+ * Fix the header issues of different server
124
+
125
+ = 1.1.9 =
126
+ * Fix the header issues of different server
127
+
128
+ = 1.1.8 =
129
+ * Fix the email display issues
130
+
131
+ = 1.1.7 =
132
+ * Fix the headers issues
133
+
134
+ = 1.1.6 =
135
+ * Fix the apache request headers issues
136
+
137
+ = 1.1.5 =
138
+ * Fix Bug - Order page - couriers callback
139
+ * Fix Bug - Avoid duplicate getAfterShipInstance()
140
+
141
+ = 1.1.4 =
142
+ * Fix Bug - assets location
143
+ * Fix Bug - WC Dependency
144
+
145
+ = 1.1.3 =
146
+ * Fix Bug - WC Dependency
147
+ * i18N change to AfterShip
148
+
149
+ = 1.1.2 =
150
+ * Updated the plugin name
151
+
152
+ = 1.1.1 =
153
+ * Updated the version number in aftership.php !
154
+
155
+ = 1.1.0 =
156
+ * Released
157
+
158
+ = 1.0.5 =
159
+ * Update Readme - add a video
160
+
161
+ = 1.0.4 =
162
+ * Delete changelog.txt
163
+
164
+ = 1.0.3 =
165
+ * Fix the plugin name
166
+ * Remove unused 3rd party plugin
167
+
168
+ = 1.0.2 =
169
+ * Remove the unnecessary plugin updater
170
+
171
+ = 1.0.1 =
172
+ * Fix the apache request headers issues
173
+ * Readme
174
+
175
+ = 1.0.0 =
176
+ * AfterShip plugin launched
177
+
178
+
179
+ == Upgrade Notice ==
180
+
181
+ No Upgrade Yet
trunk/woo-includes/class-wc-dependencies.php ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WC Dependency Checker
4
+ *
5
+ * Checks if WooCommerce is enabled
6
+ */
7
+ class WC_Dependencies {
8
+
9
+ private static $active_plugins;
10
+
11
+ public static function init() {
12
+
13
+ self::$active_plugins = (array) get_option( 'active_plugins', array() );
14
+
15
+ if ( is_multisite() )
16
+ self::$active_plugins = array_merge( self::$active_plugins, get_site_option( 'active_sitewide_plugins', array() ) );
17
+ }
18
+
19
+ public static function plugin_active_check($plugin){
20
+ if ( ! self::$active_plugins ) self::init();
21
+ return in_array( $plugin, self::$active_plugins ) || array_key_exists( $plugin, self::$active_plugins );
22
+ }
23
+
24
+ public static function woocommerce_active_check() {
25
+ return self::plugin_active_check('woocommerce/woocommerce.php');
26
+ }
27
+
28
+ }
29
+
30
+
trunk/woo-includes/woo-functions.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Functions used by plugins
4
+ */
5
+ if ( ! class_exists( 'WC_Dependencies' ) )
6
+ require_once 'class-wc-dependencies.php';
7
+
8
+ /**
9
+ * WC Detection
10
+ */
11
+ if ( ! function_exists( 'is_woocommerce_active' ) ) {
12
+ function is_woocommerce_active() {
13
+ return WC_Dependencies::woocommerce_active_check();
14
+ }
15
+ }