WooCommerce Weight Based Shipping - Version 4.2.3

Version Description

  • Fix links to premium plugins
Download this release

Release Info

Developer dangoodman
Plugin Icon 128x128 WooCommerce Weight Based Shipping
Version 4.2.3
Comparing to
See all releases

Version 4.2.3

Model/WbsBucketRate.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WbsBucketRate
3
+ {
4
+ public function __construct($id, $flatRate, WbsProgressiveRate $progressiveRate)
5
+ {
6
+ $this->setId($id);
7
+ $this->setFlatRate($flatRate);
8
+ $this->setProgressiveRate($progressiveRate);
9
+ }
10
+
11
+ public function rate($amount)
12
+ {
13
+ return $this->flatRate + $this->progressiveRate->rate($amount);
14
+ }
15
+
16
+ public function getId()
17
+ {
18
+ return $this->id;
19
+ }
20
+
21
+ public function getFlatRate()
22
+ {
23
+ return $this->flatRate;
24
+ }
25
+
26
+ public function getProgressiveRate()
27
+ {
28
+ return $this->progressiveRate;
29
+ }
30
+
31
+
32
+ private $id;
33
+ private $flatRate;
34
+ /** @var WbsProgressiveRate */
35
+ private $progressiveRate;
36
+
37
+ private function setId($id)
38
+ {
39
+ if (empty($id)) {
40
+ throw new InvalidArgumentException("Please provide id for bucket rate");
41
+ }
42
+
43
+ $this->id = $id;
44
+ }
45
+
46
+ private function setFlatRate($rate)
47
+ {
48
+ $this->flatRate = (float)$rate;
49
+ }
50
+
51
+ private function setProgressiveRate(WbsProgressiveRate $rate)
52
+ {
53
+ $this->progressiveRate = $rate;
54
+ }
55
+ }
56
+ ?>
Model/WbsBucketRates.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WbsBucketRates
3
+ {
4
+ /** @var WbsBucketRate[] */
5
+ private $rates;
6
+
7
+ public function __construct()
8
+ {
9
+ $this->rates = array();
10
+ }
11
+
12
+ public function add(WbsBucketRate $rate)
13
+ {
14
+ $this->rates[$rate->getId()] = $rate;
15
+ }
16
+
17
+ public function findById($class)
18
+ {
19
+ return @$this->rates[$class];
20
+ }
21
+
22
+ public function listAll()
23
+ {
24
+ return $this->rates;
25
+ }
26
+ }
27
+ ?>
Model/WbsItemBucket.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WbsItemBucket
3
+ {
4
+ /** @var WbsBucketRate */
5
+ private $rate;
6
+ private $quantity;
7
+
8
+ public function __construct($quantity, WbsBucketRate $rate)
9
+ {
10
+ $this->rate = $rate;
11
+ $this->quantity = (float)$quantity;
12
+ }
13
+
14
+ public function calculate()
15
+ {
16
+ return $this->rate->rate($this->quantity);
17
+ }
18
+
19
+ public function add($quantity)
20
+ {
21
+ $this->quantity += $quantity;
22
+ }
23
+
24
+ public function getRate()
25
+ {
26
+ return $this->rate;
27
+ }
28
+
29
+ public function getQuantity()
30
+ {
31
+ return $this->quantity;
32
+ }
33
+ }
34
+ ?>
Model/WbsPackage.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WbsPackage
4
+ {
5
+ public function __construct(array $lines)
6
+ {
7
+ $this->lines = $lines;
8
+ }
9
+
10
+ public static function fromWcPackage(array $wcPackageData)
11
+ {
12
+ $lines = array();
13
+ foreach ($wcPackageData['contents'] as $wcLineData) {
14
+ $lines[] = new WbsPackageLine(
15
+ $wcLineData['data'],
16
+ $wcLineData['quantity'],
17
+ $wcLineData['line_subtotal'],
18
+ $wcLineData['line_subtotal_tax']
19
+ );
20
+ }
21
+
22
+ return new self($lines);
23
+ }
24
+
25
+ public function getLines()
26
+ {
27
+ return $this->lines;
28
+ }
29
+
30
+ public function getPrice($withTax = true)
31
+ {
32
+ $price = 0;
33
+
34
+ foreach ($this->lines as $line) {
35
+ $price += $line->getPrice($withTax);
36
+ }
37
+
38
+ return $price;
39
+ }
40
+
41
+ public function getWeight()
42
+ {
43
+ $weight = 0;
44
+
45
+ foreach ($this->lines as $line) {
46
+ $weight += $line->getWeight();
47
+ }
48
+
49
+ return $weight;
50
+ }
51
+
52
+ /** @var WbsPackageLine[] */
53
+ private $lines;
54
+ }
Model/WbsPackageLine.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WbsPackageLine
4
+ {
5
+ public function __construct(WC_Product $product, $quantity, $subtotal, $tax)
6
+ {
7
+ $this->product = $product;
8
+ $this->quantity = $quantity;
9
+ $this->subtotal = $subtotal;
10
+ $this->tax = $tax;
11
+ }
12
+
13
+ public function getQuantity()
14
+ {
15
+ return $this->quantity;
16
+ }
17
+
18
+ public function getProduct()
19
+ {
20
+ return $this->product;
21
+ }
22
+
23
+ public function getWeight()
24
+ {
25
+ return (float)$this->product->get_weight() * $this->quantity;
26
+ }
27
+
28
+ public function getPrice($withTax = false)
29
+ {
30
+ return $this->subtotal + ($withTax ? $this->tax : 0);
31
+ }
32
+
33
+ private $product;
34
+ private $subtotal;
35
+ private $tax;
36
+ }
Model/WbsProgressiveRate.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WbsProgressiveRate
3
+ {
4
+ public function __construct($cost = 0, $step = 0, $skip = 0)
5
+ {
6
+ $cost = isset($cost) ? $cost : 0;
7
+ $step = isset($step) ? $step : 0;
8
+ $skip = isset($skip) ? $skip : 0;
9
+
10
+ if (!is_numeric($cost) || !is_numeric($step) || !is_numeric($skip)) {
11
+ throw new InvalidArgumentException(sprintf(
12
+ "%s: invalid argument value(s): '%s', '%s', '%s'.",
13
+ get_class(), var_export($cost, true), var_export($step, true), var_export($skip, true)
14
+ ));
15
+ }
16
+
17
+ $this->cost = $cost;
18
+ $this->step = $step;
19
+ $this->skip = $skip;
20
+ }
21
+
22
+ public static function fromArray(array $input)
23
+ {
24
+ return new self(
25
+ @$input['cost'],
26
+ @$input['step'],
27
+ @$input['skip']
28
+ );
29
+ }
30
+
31
+ public function toArray()
32
+ {
33
+ return array(
34
+ 'cost' => $this->cost,
35
+ 'step' => $this->step,
36
+ 'skip' => $this->skip,
37
+ );
38
+ }
39
+
40
+ public function rate($amount)
41
+ {
42
+ $amount = max(0, $amount - $this->skip);
43
+
44
+ if ($this->step != 0) {
45
+ $amount = ceil(round($amount / $this->step, 5));
46
+ }
47
+
48
+ return $amount * $this->cost;
49
+ }
50
+
51
+ public function getCost()
52
+ {
53
+ return $this->cost;
54
+ }
55
+
56
+ public function getStep()
57
+ {
58
+ return $this->step;
59
+ }
60
+
61
+ public function getSkip()
62
+ {
63
+ return $this->skip;
64
+ }
65
+
66
+ private $cost;
67
+ private $step;
68
+ private $skip;
69
+ }
Model/WbsRange.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * @property-read float $min
5
+ * @property-read float $max
6
+ * @property-read bool $minInclusive
7
+ * @property-read bool $maxInclusive
8
+ */
9
+ class WbsRange
10
+ {
11
+ public function __construct($min = null, $max = null, $minInclusive = true, $maxInclusive = false)
12
+ {
13
+ $this->min = isset($min) ? (float)$min : null;
14
+ $this->max = isset($max) ? (float)$max : null;
15
+ $this->minInclusive = isset($minInclusive) ? !!$minInclusive : true;
16
+ $this->maxInclusive = isset($maxInclusive) ? !!$maxInclusive : false;
17
+ }
18
+
19
+ public static function fromArray(array $range)
20
+ {
21
+ return new self(
22
+ @$range['min']['value'],
23
+ @$range['max']['value'],
24
+ @$range['min']['inclusive'],
25
+ @$range['max']['inclusive']
26
+ );
27
+ }
28
+
29
+ public function toArray()
30
+ {
31
+ return array(
32
+ 'min' => array(
33
+ 'value' => $this->min,
34
+ 'inclusive' => $this->minInclusive,
35
+ ),
36
+ 'max' => array(
37
+ 'value' => $this->max,
38
+ 'inclusive' => $this->maxInclusive,
39
+ ),
40
+ );
41
+ }
42
+
43
+ public function includes($value)
44
+ {
45
+ if (isset($this->min) && ($value == $this->min && !$this->minInclusive || $value < $this->min) ||
46
+ isset($this->max) && ($value == $this->max && !$this->maxInclusive || $value > $this->max) ) {
47
+ return false;
48
+ }
49
+
50
+ return true;
51
+ }
52
+
53
+ public function clamp($value)
54
+ {
55
+ if (isset($this->min) && $value < $this->min) {
56
+ $value = $this->min;
57
+ }
58
+
59
+ if (isset($this->max) && $value > $this->max) {
60
+ $value = $this->max;
61
+ }
62
+
63
+ return $value;
64
+ }
65
+
66
+ public function __get($property)
67
+ {
68
+ return $this->{$property};
69
+ }
70
+
71
+ public function __isset($property)
72
+ {
73
+ return isset($this->{$property});
74
+ }
75
+
76
+ private $min;
77
+ private $max;
78
+ private $minInclusive;
79
+ private $maxInclusive;
80
+ }
Upgrade/WBS_Upgrade_Notice.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WBS_Upgrade_Notice
3
+ {
4
+ private $shortHtml;
5
+ private $longHtml;
6
+
7
+ public static function createBehaviourChangeNotice($since, $message)
8
+ {
9
+ $shortHtml =
10
+ "Behavior of WooCommerce Weight Based Shipping changed since {$since}.";
11
+
12
+ $longHtml =
13
+ '<p>'.
14
+ esc_html($message).
15
+ '</p>
16
+ <p>'.
17
+ 'Please <a href="'.esc_html(WbsRuleUrls::generic()).'">review settings</a>
18
+ and make appropriate changes if it\'s needed.'.
19
+ '</p>';
20
+
21
+ return new self($shortHtml, $longHtml);
22
+ }
23
+
24
+ public function __construct($shortHtml, $longHtml)
25
+ {
26
+ if (empty($shortHtml) || empty($longHtml)) {
27
+ throw new InvalidArgumentException();
28
+ }
29
+
30
+ $this->shortHtml = $shortHtml;
31
+ $this->longHtml = $longHtml;
32
+ }
33
+
34
+ public function getShortHtml()
35
+ {
36
+ return $this->shortHtml;
37
+ }
38
+
39
+ public function getLongHtml()
40
+ {
41
+ return $this->longHtml;
42
+ }
43
+ }
44
+ ?>
Upgrade/WBS_Upgrade_Notices.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WBS_Upgrade_Notices
3
+ {
4
+ public function __construct($storageOptionName, $removeNoticeUrlFlagName)
5
+ {
6
+ $this->storageOptionName = $storageOptionName;
7
+ $this->removeNoticeUrlFlagName = $removeNoticeUrlFlagName;
8
+ }
9
+
10
+ public function add(WBS_Upgrade_Notice $notice)
11
+ {
12
+ $data = $this->load();
13
+
14
+ $data['last_id']++;
15
+ $data['notices'][$data['last_id']] = $notice;
16
+
17
+ $this->save($data);
18
+
19
+ return $data['last_id'];
20
+ }
21
+
22
+ public function remove($id)
23
+ {
24
+ $data = $this->load();
25
+ if (!isset($data['notices'][$id])) {
26
+ return false;
27
+ }
28
+
29
+ unset($data['notices'][$id]);
30
+
31
+ $this->save($data);
32
+
33
+ return true;
34
+ }
35
+
36
+ public function show()
37
+ {
38
+ static $firstRun = true;
39
+
40
+ $data = $this->load();
41
+
42
+ /** @var WBS_Upgrade_Notice $notice */
43
+ foreach ($data['notices'] as $id => $notice) {
44
+ $hideNoticeUrl = esc_html(
45
+ WbsRuleUrls::generic(array($this->removeNoticeUrlFlagName => $id))
46
+ );
47
+
48
+ echo '
49
+ <div class="woowbs-upgrade-notice highlight" style="padding: 1em; border: 1px solid red;">
50
+ <big>'.
51
+ $notice->getShortHtml().
52
+ ' <a class="woowbs-upgrade-notice-switcher" href="#">Less</a>
53
+ </big>
54
+ <div class="woowbs-upgrade-notice-long-message">'.
55
+ $notice->getLongHtml().
56
+ ' <p><a class="button" href="'.$hideNoticeUrl.'">Don\'t show this message again</a></p>
57
+ </div>
58
+ </div>
59
+ ';
60
+
61
+ if ($firstRun) {
62
+ $firstRun = false;
63
+ echo '
64
+ <script>
65
+ jQuery(function($) {
66
+ var toggleSpeed = 0;
67
+ var $collapsers = $(".woowbs-upgrade-notice-switcher");
68
+
69
+ $collapsers.click(function() {
70
+ var $collapser = $(this);
71
+
72
+ var $content = $collapser
73
+ .closest(".woowbs-upgrade-notice")
74
+ .find(".woowbs-upgrade-notice-long-message");
75
+
76
+ $content.toggle(toggleSpeed, function() {
77
+ $collapser.text($content.is(":visible") ? "Less" : "More");
78
+ });
79
+
80
+ return false;
81
+ });
82
+
83
+ $collapsers.click();
84
+ toggleSpeed = "fast";
85
+ });
86
+ </script>
87
+ ';
88
+ }
89
+ }
90
+ }
91
+
92
+ public function getRemoveNoticeUrlFlagName()
93
+ {
94
+ return $this->removeNoticeUrlFlagName;
95
+ }
96
+
97
+ private $storageOptionName;
98
+ private $removeNoticeUrlFlagName;
99
+
100
+ private function load()
101
+ {
102
+ $data = get_option($this->storageOptionName, null);
103
+
104
+ if (!isset($data)) {
105
+ $data = array(
106
+ 'last_id' => 0,
107
+ 'notices' => array(),
108
+ );
109
+ }
110
+
111
+ return $data;
112
+ }
113
+
114
+ private function save(array $data)
115
+ {
116
+ update_option($this->storageOptionName, $data);
117
+ }
118
+ }
119
+ ?>
WBS_Loader.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WBS_Loader
3
+ {
4
+ const ADMIN_SECTION_NAME = 'wc_weight_based_shipping';
5
+
6
+ public static function loadWbs($pluginFile)
7
+ {
8
+ if (!self::$loaded) {
9
+ self::$loaded = true;
10
+ new WBS_Loader($pluginFile);
11
+ }
12
+ }
13
+
14
+ public function __construct($pluginFile)
15
+ {
16
+ $this->pluginFile = wp_normalize_path($pluginFile);
17
+ add_action('plugins_loaded', array($this, 'load'), 0);
18
+ add_filter('plugin_action_links_' . plugin_basename($this->pluginFile), array($this, '_outputSettingsLink'));
19
+ }
20
+
21
+ public function load()
22
+ {
23
+ if (!$this->woocommerceAvailable()) return;
24
+ $this->loadLanguage();
25
+ $this->loadFunctions();
26
+ $this->loadClasses();
27
+ WBS_Profile_Manager::setup();
28
+ WBS_Upgrader::setup($this->pluginFile);
29
+ add_filter('woocommerce_get_sections_shipping', array($this, '_fixShippingMethodsLinks'));
30
+
31
+ if (is_admin()) {
32
+
33
+ add_action('wp_ajax_woowbs_update_rules_order', array($this, '_updateRulesOrder'));
34
+
35
+ if (@$_GET['section'] === self::ADMIN_SECTION_NAME) {
36
+ add_action('admin_enqueue_scripts', array($this, '_enqueueScripts'));
37
+ }
38
+ }
39
+ }
40
+
41
+ public function _outputSettingsLink($links)
42
+ {
43
+ array_unshift($links, '<a href="'.esc_html(WbsRuleUrls::generic()).'">'.__('Settings', 'woowbs').'</a>');
44
+ return $links;
45
+ }
46
+
47
+ public function _fixShippingMethodsLinks($sections)
48
+ {
49
+ foreach (WBS_Profile_Manager::instance()->profiles() as $profile) {
50
+ unset($sections[$profile->id]);
51
+ }
52
+
53
+ $sections[self::ADMIN_SECTION_NAME] = WC_Weight_Based_Shipping::getTitle();
54
+
55
+ return $sections;
56
+ }
57
+
58
+ public function _loadClass($class)
59
+ {
60
+ foreach ($this->classDirs as $dir) {
61
+ if (file_exists($file = "{$dir}/{$class}.php")) {
62
+ require_once($file);
63
+ return true;
64
+ }
65
+ }
66
+
67
+ if (in_array($class, array('WBS_Shipping_Rate_Override', 'WBS_Shipping_Class_Override_Set'))) {
68
+ /** @noinspection PhpIncludeInspection */
69
+ require_once($this->legacyClassesFile);
70
+ }
71
+
72
+ return false;
73
+ }
74
+
75
+ public function _enqueueScripts()
76
+ {
77
+ wp_enqueue_script('woowbs-admin', plugins_url('assets/admin.js', $this->pluginFile), array('jquery-ui-sortable'));
78
+ wp_localize_script('woowbs-admin', 'woowbs_ajax_vars', array('ajax_url' => admin_url('admin-ajax.php')));
79
+ }
80
+
81
+ public function _updateRulesOrder() {
82
+ $order = WbsFactory::getRulesOrderStorage();
83
+ $order->set($_POST['profiles']);
84
+ WbsWcTools::purgeWoocommerceShippingCache();
85
+ wp_die();
86
+ }
87
+
88
+ private static $loaded;
89
+ private $pluginFile;
90
+ private $classDirs;
91
+ private $legacyClassesFile;
92
+
93
+ private function woocommerceAvailable()
94
+ {
95
+ return class_exists('WC_Shipping_Method');
96
+ }
97
+
98
+ private function loadLanguage()
99
+ {
100
+ load_plugin_textdomain('woowbs', false, dirname(plugin_basename($this->pluginFile)).'/lang/');
101
+ }
102
+
103
+ private function loadFunctions()
104
+ {
105
+ require_once(dirname(__FILE__) . "/functions.php");
106
+ }
107
+
108
+ private function loadClasses()
109
+ {
110
+ $wbsdir = dirname(__FILE__);
111
+ $this->classDirs = array($wbsdir, "{$wbsdir}/Model", "{$wbsdir}/Upgrade");
112
+ $this->legacyClassesFile = "{$wbsdir}/legacy.php";
113
+ spl_autoload_register(array($this, '_loadClass'));
114
+ }
115
+ }
WBS_Profile_Manager.php ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WBS_Profile_Manager
3
+ {
4
+ public static function setup()
5
+ {
6
+ self::instance();
7
+ }
8
+
9
+ public static function instance($resetCache = false)
10
+ {
11
+ if (!isset(self::$instance)) {
12
+ self::$instance = new self();
13
+ add_filter('woocommerce_shipping_methods', array(self::$instance, '_registerProfiles'));
14
+ }
15
+
16
+ if ($resetCache) {
17
+ unset(self::$instance->orderedProfiles);
18
+ unset(self::$instance->profileInstances);
19
+ }
20
+
21
+ return self::$instance;
22
+ }
23
+
24
+ /** @return WC_Weight_Based_Shipping[] */
25
+ public function profiles()
26
+ {
27
+ if (!isset($this->orderedProfiles)) {
28
+
29
+ $this->orderedProfiles = array();
30
+
31
+ /** @var WC_Shipping $shipping */
32
+ $shipping = WC()->shipping;
33
+ foreach ($shipping->load_shipping_methods() as $method)
34
+ {
35
+ if ($method instanceof WC_Weight_Based_Shipping)
36
+ {
37
+ $this->orderedProfiles[] = $method;
38
+ }
39
+ }
40
+ }
41
+
42
+ return $this->orderedProfiles;
43
+ }
44
+
45
+ public function profile($name = null)
46
+ {
47
+ $this->find_suitable_id($name);
48
+ $profiles = $this->instantiateProfiles();
49
+ return @$profiles[$name];
50
+ }
51
+
52
+ public function profile_exists($name)
53
+ {
54
+ $profiles = $this->instantiateProfiles();
55
+ return isset($profiles[$name]);
56
+ }
57
+
58
+ public function find_suitable_id(&$profileId)
59
+ {
60
+ if (!$profileId && !($profileId = $this->current_profile_id())) {
61
+ return $profileId = null;
62
+ }
63
+
64
+ return $profileId;
65
+ }
66
+
67
+ public function current_profile_id()
68
+ {
69
+ $profile_id = null;
70
+
71
+ if (is_admin()) {
72
+
73
+ if (empty($profile_id)) {
74
+ $profile_id = @$_GET['wbs_profile'];
75
+ }
76
+
77
+ if (empty($profile_id) && ($profiles = $this->profiles())) {
78
+ $profile_id = $profiles[0]->profile_id;
79
+ }
80
+
81
+ if (empty($profile_id)) {
82
+ $profile_id = 'main';
83
+ }
84
+ }
85
+
86
+ return $profile_id;
87
+ }
88
+
89
+ public function new_profile_id()
90
+ {
91
+ if (!$this->profile_exists('main')) {
92
+ return 'main';
93
+ }
94
+
95
+ $timestamp = time();
96
+
97
+ $i = null;
98
+ do {
99
+ $new_profile_id = trim($timestamp.'-'.$i++, '-');
100
+ } while ($this->profile_exists($new_profile_id));
101
+
102
+ return $new_profile_id;
103
+ }
104
+
105
+
106
+
107
+ public function _registerProfiles($methods)
108
+ {
109
+ return array_merge($methods, $this->instantiateProfiles());
110
+ }
111
+
112
+ public static function listAvailableProfileIds($pluginPrefix = WC_Weight_Based_Shipping::PLUGIN_PREFIX, $idPrefix = null)
113
+ {
114
+ $ids = array();
115
+
116
+ $settingsOptionNamePattern = sprintf('/^%s%s(\\w+)_settings$/',
117
+ preg_quote($pluginPrefix, '/'), preg_quote($idPrefix, '/')
118
+ );
119
+
120
+ foreach (array_keys(wp_load_alloptions()) as $option) {
121
+ $matches = array();
122
+ if (preg_match($settingsOptionNamePattern, $option, $matches)) {
123
+ $ids[] = $matches[1];
124
+ }
125
+ }
126
+
127
+ $ids = WbsFactory::getRulesOrderStorage()->sort($ids);
128
+
129
+ return $ids;
130
+ }
131
+
132
+ public static function getRuleSettingsOptionName($ruleId, $pluginPrefix = WC_Weight_Based_Shipping::PLUGIN_PREFIX)
133
+ {
134
+ return sprintf('%s%s_settings', $pluginPrefix, $ruleId);
135
+ }
136
+
137
+
138
+ private static $instance;
139
+
140
+ private $orderedProfiles;
141
+
142
+ /** @var WC_Weight_Based_Shipping[] */
143
+ private $profileInstances;
144
+
145
+ private function instantiateProfiles()
146
+ {
147
+ if (!isset($this->profileInstances)) {
148
+
149
+ $this->profileInstances = array();
150
+
151
+ $profileIds = self::listAvailableProfileIds();
152
+ if (empty($profileIds)) {
153
+ $profileIds[] = $this->new_profile_id();
154
+ }
155
+
156
+ foreach ($profileIds as $profileId) {
157
+ $this->profileInstances[$profileId] = new WC_Weight_Based_Shipping($profileId);
158
+ }
159
+
160
+ if (is_admin() &&
161
+ ($editingProfileId = @$_GET['wbs_profile']) &&
162
+ !isset($this->profileInstances[$editingProfileId])) {
163
+
164
+ $editingProfile = new WC_Weight_Based_Shipping($editingProfileId);
165
+ $editingProfile->_stub = true;
166
+ $this->profileInstances[$editingProfileId] = $editingProfile;
167
+ }
168
+
169
+ if ($currentProfile = $this->profile()) {
170
+ add_action(
171
+ 'woocommerce_update_options_shipping_' . $currentProfile->id,
172
+ array($currentProfile, 'process_admin_options')
173
+ );
174
+ }
175
+ }
176
+
177
+ return $this->profileInstances;
178
+ }
179
+ }
180
+ ?>
WBS_Upgrader.php ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WBS_Upgrader
3
+ {
4
+ public static function setup($pluginFile)
5
+ {
6
+ if (!isset(self::$instance)) {
7
+ $upgrader = new self($pluginFile);
8
+ $upgrader->onLoad();
9
+ self::$instance = $upgrader;
10
+ }
11
+ }
12
+
13
+ public static function instance()
14
+ {
15
+ return self::$instance;
16
+ }
17
+
18
+ public function __construct($pluginFile)
19
+ {
20
+ $this->pluginFile = $pluginFile;
21
+ $this->upgradeNotices = new WBS_Upgrade_Notices('woowbs_upgrade_notices', 'woowbs_remove_upgrade_notice');
22
+ }
23
+
24
+ public function onLoad()
25
+ {
26
+ $this->setupHooks();
27
+ }
28
+
29
+ public function onAdminInit()
30
+ {
31
+ $this->checkForUpgrade();
32
+ }
33
+
34
+ public function onAdminNotices()
35
+ {
36
+ $this->upgradeNotices->show();
37
+ }
38
+
39
+ public function removeUpgradeNotices()
40
+ {
41
+ $id = @$_GET[$this->upgradeNotices->getRemoveNoticeUrlFlagName()];
42
+ if (!isset($id)) {
43
+ return false;
44
+ }
45
+
46
+ return $this->upgradeNotices->remove($id);
47
+ }
48
+
49
+ /** @var WBS_Upgrader */
50
+ private static $instance;
51
+ private $pluginFile;
52
+ private $upgradeNotices;
53
+
54
+ private function checkForUpgrade()
55
+ {
56
+ $updateCurrentVersion = false;
57
+
58
+ $currentVersion = null; {
59
+ $pluginData = get_plugin_data($this->pluginFile, false, false);
60
+ $currentVersion = $pluginData['Version'];
61
+ }
62
+
63
+ $previousVersion = get_option('woowbs_version');
64
+ if (empty($previousVersion)) {
65
+ $hasSomeRules = !!self::listAvailableProfileIds();
66
+ $hasSomeRules = $hasSomeRules || !!self::listAvailableProfileIds('woocommerce_', 'WC_Weight_Based_Shipping_');
67
+ $previousVersion = $hasSomeRules ? '2.6.8' : $currentVersion;
68
+ $updateCurrentVersion = true;
69
+ }
70
+
71
+ if ($previousVersion !== $currentVersion) {
72
+
73
+ $updateCurrentVersion = true;
74
+
75
+ if (version_compare($previousVersion, '2.2.1') < 0) {
76
+ $this->upgradeNotices->add(WBS_Upgrade_Notice::createBehaviourChangeNotice('2.2.1', '
77
+ Previously, weight-based shipping option has not been shown to user if total
78
+ weight of their cart is zero. Since version 2.2.1 this is changed so shipping
79
+ option is available to user with price set to Handling Fee. If it does not
80
+ suite your needs well you can return previous behavior by setting Min Weight
81
+ to something a bit greater zero, e.g. 0.001, so that zero-weight orders will
82
+ not match constraints and the shipping option will not be shown.
83
+ '));
84
+ }
85
+
86
+ if (version_compare($previousVersion, '2.4.0') < 0) {
87
+ $profiles = WBS_Profile_Manager::instance()->profiles();
88
+ foreach ($profiles as $profile) {
89
+ $option = $profile->get_wp_option_name();
90
+ $config = get_option($option);
91
+ $config['extra_weight_only'] = 'no';
92
+ update_option($option, $config);
93
+ }
94
+ }
95
+
96
+ if (version_compare($previousVersion, '2.6.3') < 0) {
97
+ $this->upgradeNotices->add(WBS_Upgrade_Notice::createBehaviourChangeNotice('2.6.2', '
98
+ Previously, base Handling Fee has not been added to the shipping price if user
99
+ cart contained only items with shipping classes specified in the Shipping
100
+ Classes Overrides section. Starting from version 2.6.2 base Handling Fee is
101
+ applied in any case. This behavior change is only affecting you if you use
102
+ Shipping Classes Overrides. If you don\'t use it just ignore this message.
103
+ '));
104
+ }
105
+
106
+ if (version_compare($previousVersion, '4.0.0') < 0) {
107
+
108
+ // Rename settings options
109
+ if ($ruleIdsToUpdate = self::listAvailableProfileIds('woocommerce_', 'WC_Weight_Based_Shipping_')) {
110
+
111
+ foreach ($ruleIdsToUpdate as $oldSettingsName => $id) {
112
+
113
+ $settings = get_option($oldSettingsName);
114
+
115
+ # Don't remove old settings entry. Keep it as a backup.
116
+ //delete_option($oldSettingsName);
117
+
118
+ $newSettingsName = WBS_Profile_Manager::getRuleSettingsOptionName($id);
119
+ if (!get_option($newSettingsName)) {
120
+ update_option($newSettingsName, $settings);
121
+ }
122
+ }
123
+
124
+ // Reset cache to load renamed rules
125
+ WBS_Profile_Manager::instance(true);
126
+ }
127
+
128
+ $rangeFieldsMap = array(
129
+ 'weight' => array( 'min_weight', 'max_weight', false ),
130
+ 'subtotal' => array( 'min_subtotal', 'max_subtotal', false ),
131
+ 'price_clamp' => array( 'min_price', 'max_price', true ),
132
+ );
133
+
134
+ foreach (WBS_Profile_Manager::instance()->profiles() as $profile) {
135
+
136
+ $option = $profile->get_wp_option_name();
137
+ $config = get_option($option);
138
+
139
+ // Convert conditions
140
+ foreach ($rangeFieldsMap as $newField => $oldFields) {
141
+
142
+ if (!isset($config[$newField])) {
143
+
144
+ $range = null;
145
+
146
+ $min = (float)@$config[$oldFields[0]];
147
+ $max = (float)@$config[$oldFields[1]];
148
+ if ($min || $max) {
149
+ $range = new WbsRange(
150
+ $min ? $min : null,
151
+ $max ? $max : null,
152
+ true,
153
+ $oldFields[2] || !!$max
154
+ );
155
+ }
156
+
157
+ if (!isset($range)) {
158
+ $range = new WbsRange();
159
+ }
160
+
161
+ $config[$newField] = $range->toArray();
162
+ }
163
+
164
+ unset($config[$oldFields[0]]);
165
+ unset($config[$oldFields[1]]);
166
+ }
167
+
168
+ $weightRange = WbsRange::fromArray(@$config['weight']);
169
+ $extraWeightOnly = @$config['extra_weight_only'];
170
+ unset($config['extra_weight_only']);
171
+
172
+ // Convert progressive rate
173
+ if (!isset($config['weight_rate'])) {
174
+
175
+ $config['weight_rate'] = self::convertToProgressiveRate(
176
+ @$config['rate'],
177
+ @$config['weight_step'],
178
+ $extraWeightOnly,
179
+ $weightRange->min
180
+ )->toArray();
181
+
182
+ unset($config['weight_step']);
183
+ unset($config['rate']);
184
+ }
185
+
186
+ // Convert shipping class rates
187
+ if (!isset($config['shipping_class_rates'])) {
188
+
189
+ /** @var WBS_Shipping_Class_Override_Set $oldRates */
190
+ if (($oldRates = @$config['shipping_class_overrides']) &&
191
+ ($oldRates = $oldRates->getOverrides())) {
192
+
193
+ $rates = new WbsBucketRates();
194
+
195
+ /** @var WBS_Shipping_Rate_Override $oldRate */
196
+ foreach ($oldRates as $oldRate) {
197
+
198
+ $rates->add(new WbsBucketRate(
199
+ $oldRate->getClass(),
200
+ $oldRate->getFee(),
201
+ self::convertToProgressiveRate(
202
+ $oldRate->getRate(),
203
+ @$oldRate->getWeightStep(),
204
+ $extraWeightOnly,
205
+ $weightRange->min
206
+ )
207
+ ));
208
+ }
209
+
210
+ $config['shipping_class_rates'] = $rates;
211
+ }
212
+
213
+ unset($config['shipping_class_overrides']);
214
+ }
215
+
216
+ update_option($option, $config);
217
+ }
218
+ }
219
+ }
220
+
221
+ if ($updateCurrentVersion) {
222
+ update_option('woowbs_version', $currentVersion);
223
+ }
224
+ }
225
+
226
+ private function setupHooks()
227
+ {
228
+ add_action('admin_init', array($this, 'onAdminInit'));
229
+ add_action('admin_notices', array($this, 'onAdminNotices'));
230
+ }
231
+
232
+ private static function convertToProgressiveRate($rate, $weightStep, $extraWeightOnly, $minWeightRange)
233
+ {
234
+ $rate = $rate * ($weightStep ? $weightStep : 1);
235
+ $weightStep = $rate ? $weightStep : 0;
236
+ $skip = $rate && $extraWeightOnly !== 'no' ? $minWeightRange : 0;
237
+ return new WbsProgressiveRate($rate, $weightStep, $skip);
238
+ }
239
+
240
+ private static function listAvailableProfileIds($pluginPrefix = WC_Weight_Based_Shipping::PLUGIN_PREFIX, $idPrefix = null)
241
+ {
242
+ $ids = array();
243
+
244
+ $settingsOptionNamePattern = sprintf('/^%s%s(\\w+)_settings$/',
245
+ preg_quote($pluginPrefix, '/'), preg_quote($idPrefix, '/')
246
+ );
247
+
248
+ foreach (array_keys(wp_load_alloptions()) as $option) {
249
+ $matches = array();
250
+ if (preg_match($settingsOptionNamePattern, $option, $matches)) {
251
+ $ids[$matches[0]] = $matches[1];
252
+ }
253
+ }
254
+
255
+ return $ids;
256
+ }
257
+ }
WC_Weight_Based_Shipping.php ADDED
@@ -0,0 +1,797 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WC_Weight_Based_Shipping extends WC_Shipping_Method
3
+ {
4
+ const PLUGIN_PREFIX = 'woowbs_';
5
+
6
+ public $plugin_id = self::PLUGIN_PREFIX;
7
+
8
+ public $name;
9
+ public $profile_id;
10
+
11
+ /** @var WbsRange */
12
+ public $weight;
13
+ /** @var WbsRange */
14
+ public $subtotal;
15
+ public $subtotalWithTax;
16
+
17
+ /** @var WbsProgressiveRate */
18
+ public $weightRate;
19
+
20
+ /** @var WbsBucketRates */
21
+ public $shippingClassRates;
22
+
23
+ /** @var WbsRange */
24
+ public $priceClamp;
25
+
26
+ public $valid = false;
27
+
28
+ public $_stub = false;
29
+
30
+
31
+ static public function getTitle()
32
+ {
33
+ return __('Weight Based', 'woowbs');
34
+ }
35
+
36
+ /** @noinspection PhpMissingParentConstructorInspection */
37
+ public function __construct($profileId = null)
38
+ {
39
+ $manager = WBS_Profile_Manager::instance();
40
+
41
+ // Force loading profiles when called from WooCommerce 2.3.9- save handler
42
+ // to activate process_admin_option() with appropriate hook
43
+ if (!isset($profileId)) {
44
+ $manager->profiles();
45
+ }
46
+
47
+ $this->id = $manager->find_suitable_id($profileId);
48
+ $this->profile_id = $profileId;
49
+
50
+ $this->method_title = self::getTitle();
51
+
52
+ $this->settingsHelper = new WbsSettingsHtmlTools($this);
53
+
54
+ $this->safeInit();
55
+ }
56
+
57
+ public function __clone()
58
+ {
59
+ $manager = WBS_Profile_Manager::instance();
60
+
61
+ $this->profile_id = $manager->new_profile_id();
62
+ $this->id = $manager->find_suitable_id($this->profile_id);
63
+
64
+ $this->name .= ' ('._x('copy', 'noun', 'woowbs').')';
65
+ $this->settings['name'] = $this->name;
66
+
67
+ $this->shippingClassRates = clone($this->shippingClassRates);
68
+
69
+ $this->settingsHelper = new WbsSettingsHtmlTools($this);
70
+ }
71
+
72
+
73
+ public function init_form_fields()
74
+ {
75
+ $woocommerce = WC();
76
+ $shippingCountries = method_exists($woocommerce->countries, 'get_shipping_countries')
77
+ ? $woocommerce->countries->get_shipping_countries()
78
+ : $woocommerce->countries->{'countries'};
79
+
80
+ $this->form_fields = array(
81
+ ### Meta ###
82
+ array(
83
+ 'type' => 'title',
84
+ 'title' => __('Rule Settings', 'woowbs'),
85
+ ),
86
+ 'enabled' => array(
87
+ 'title' => __('Enable/Disable', 'woowbs'),
88
+ 'type' => 'checkbox',
89
+ 'label' => __('Enable this rule', 'woowbs'),
90
+ 'default' => 'yes',
91
+ ),
92
+ 'name' => array(
93
+ 'title' => __('Label', 'woowbs'),
94
+ 'description' => __('This is an internal rule label, just for you. Customers don\'t see this.', 'woowbs'),
95
+ 'type' => 'text',
96
+ 'default' => sprintf($this->profile_id != 'main' ? __('rule #%s') : '%s', $this->profile_id),
97
+ ),
98
+ 'title' => array(
99
+ 'title' => __('Title', 'woowbs'),
100
+ 'type' => 'text',
101
+ 'description' => __('This controls the title which the customer sees during checkout.', 'woowbs'),
102
+ 'default' => __('Weight Based Shipping', 'woowbs'),
103
+ ),
104
+ 'tax_status' => array(
105
+ 'title' => __('Tax Status', 'woowbs'),
106
+ 'type' => 'select',
107
+ 'default' => 'taxable',
108
+ 'class' => 'availability wc-enhanced-select',
109
+ 'options' => array(
110
+ 'taxable' => __('Taxable', 'woowbs'),
111
+ 'none' => __('None', 'woowbs'),
112
+ ),
113
+ ),
114
+ ### Conditions ###
115
+ array(
116
+ 'type' => 'title',
117
+ 'title' => __('Conditions', 'woowbs'),
118
+ 'description' => __('Define when the delivery option should be shown to the customer. All the following conditions must be met to activate the rule.', 'woowbs'),
119
+ ),
120
+ 'availability' => array(
121
+ 'title' => __('Destination', 'woowbs'),
122
+ 'type' => 'select',
123
+ 'default' => 'all',
124
+ 'class' => 'availability wc-enhanced-select',
125
+ 'options' => array(
126
+ 'all' => __('All allowed countries', 'woowbs'),
127
+ 'specific' => __('Specific countries', 'woowbs'),
128
+ 'excluding' => __('All countries except specific', 'woowbs'),
129
+ ),
130
+ ),
131
+ 'countries' => array(
132
+ 'title' => __('Specific Countries', 'woowbs'),
133
+ 'type' => 'wbs_custom',
134
+ 'wbs_real_type' => 'multiselect',
135
+ 'wbs_row_class' => 'wbs-destination',
136
+ 'class' => 'chosen_select',
137
+ 'default' => '',
138
+ 'options' => $shippingCountries,
139
+ 'custom_attributes' => array(
140
+ 'data-placeholder' => __('Select some countries', 'woowbs'),
141
+ ),
142
+ 'html' =>
143
+ '<a class="select_all button" href="#">'.__('Select all', 'woowbs').'</a> '.
144
+ '<a class="select_none button" href="#">'.__('Select none', 'woowbs').'</a>'.
145
+ $this->settingsHelper->premiumPromotionHtml('States/counties targeting'),
146
+ ),
147
+ 'weight' => array(
148
+ 'title' => __('Order Weight', 'woowbs'),
149
+ 'type' => 'wbs_range',
150
+ ),
151
+ 'subtotal' => array(
152
+ 'title' => __('Order Subtotal', 'woowbs'),
153
+ 'type' => 'wbs_custom',
154
+ 'wbs_real_type' => 'wbs_range',
155
+ 'wbs_row_class' => 'wbs-subtotal',
156
+ ),
157
+ 'subtotal_with_tax' => array(
158
+ 'title' => __('Subtotal With Tax', 'woowbs'),
159
+ 'type' => 'checkbox',
160
+ 'label' => __('After tax included', 'woowbs'),
161
+ ),
162
+
163
+ ### Calculations ###
164
+ array(
165
+ 'type' => 'title',
166
+ 'title' => __('Costs', 'woowbs'),
167
+ 'description' => __('This controls shipping price when this rule is active.', 'woowbs'),
168
+ ),
169
+ 'fee' => array(
170
+ 'title' => __('Base Cost', 'woowbs'),
171
+ 'type' => 'decimal',
172
+ 'description' => __('Leave empty or zero if your shipping price has no flat part.', 'woowbs'),
173
+ ),
174
+ 'weight_rate' => array(
175
+ 'title' => __('Weight Rate', 'woowbs'),
176
+ 'type' => 'wbs_weight_rate',
177
+ 'description' =>
178
+ __('Leave <code class="wbs-code">charge</code> field empty if your shipping price is flat.', 'woowbs').'<br>'.
179
+ __('Use <code class="wbs-code">over</code> field to skip weight part covered with Base Cost or leave it empty to charge for entire order weight.', 'woowbs'),
180
+ ),
181
+ 'shipping_class_rates' => array(
182
+ 'title' => __('Shipping Classes', 'woowbs'),
183
+ 'type' => 'shipping_class_rates',
184
+ 'description' => __('You can override some options for specific shipping classes', 'woowbs'),
185
+ ),
186
+
187
+ ### Modificators ###
188
+ array(
189
+ 'type' => 'title',
190
+ 'title' => __('Modificators', 'woowbs'),
191
+ 'description' => __('With the following you can modify resulting shipping price', 'woowbs'),
192
+ ),
193
+ 'price_clamp' => array(
194
+ 'title' => __('Limit Total Cost', 'woowbs'),
195
+ 'type' => 'wbs_range',
196
+ 'wbs_range_type' => 'simple',
197
+ 'description' => __('If total shipping price (Base Cost + Weight Rate) exceeds specified range it will be changed to the either lower or upper bound appropriately.', 'woowbs'),
198
+ ),
199
+ );
200
+
201
+ $placeholders = array
202
+ (
203
+ 'weight_unit' => __(get_option('woocommerce_weight_unit'), 'woowbs'),
204
+ 'currency' => get_woocommerce_currency_symbol(),
205
+ );
206
+
207
+ foreach ($this->form_fields as &$field)
208
+ {
209
+ $field['description'] = wbst(@$field['description'], $placeholders);
210
+ }
211
+
212
+ $this->form_fields = apply_filters('wbs_profile_settings_form', $this->form_fields, $this);
213
+ }
214
+
215
+ public function calculate_shipping($package = array())
216
+ {
217
+ if (!$this->valid) {
218
+ wc_add_notice("Price of shipping method '{$this->name}' couldn't be calculated");
219
+ return;
220
+ }
221
+
222
+ $package = WbsPackage::fromWcPackage($package);
223
+
224
+ if (!$this->weight->includes($package->getWeight()) ||
225
+ !$this->subtotal->includes($package->getPrice($this->subtotalWithTax))) {
226
+ return;
227
+ }
228
+
229
+ $defaultRate = new WbsBucketRate(
230
+ '___wbs_base_pseudo_class',
231
+ $this->fee,
232
+ $this->weightRate
233
+ );
234
+
235
+ /** @var WbsItemBucket[] $buckets */
236
+ $buckets = array(); {
237
+
238
+ foreach ($package->getLines() as $line) {
239
+
240
+ $product = $line->getProduct();
241
+ $class = $product->get_shipping_class();
242
+
243
+ $rate = $this->shippingClassRates->findById($class);
244
+ if ($rate == null) {
245
+ $rate = $defaultRate;
246
+ }
247
+
248
+ $class = $rate->getId();
249
+ if (!isset($buckets[$class])) {
250
+ $buckets[$class] = new WbsItemBucket(0, $rate);
251
+ }
252
+
253
+ $buckets[$class]->add($line->getWeight());
254
+ }
255
+
256
+ $defaultClass = $defaultRate->getId();
257
+ if (!isset($buckets[$defaultClass])) {
258
+ $buckets[$defaultClass] = new WbsItemBucket(0, $defaultRate);
259
+ }
260
+ }
261
+
262
+ $price = 0;
263
+ foreach ($buckets as $bucket) {
264
+ $price += $bucket->calculate();
265
+ }
266
+
267
+ $price = $this->priceClamp->clamp($price);
268
+
269
+ $this->add_rate(array(
270
+ 'id' => $this->id,
271
+ 'label' => $this->title,
272
+ 'cost' => $price,
273
+ 'taxes' => '',
274
+ 'calc_tax' => 'per_order'
275
+ ));
276
+ }
277
+
278
+ public function admin_options()
279
+ {
280
+ static $already = false;
281
+
282
+ /** @noinspection PhpUndefinedConstantInspection */
283
+ if (version_compare(WC_VERSION, '2.6', '>=')) {
284
+ if (!$already) {
285
+ $already = true;
286
+ } else {
287
+ return;
288
+ }
289
+ }
290
+
291
+ if (WBS_Upgrader::instance()->removeUpgradeNotices()) {
292
+ $this->refresh();
293
+ }
294
+
295
+ $manager = WBS_Profile_Manager::instance(true);
296
+ $profiles = $manager->profiles();
297
+ $profile = $manager->profile();
298
+
299
+ if (!empty($_GET['delete'])) {
300
+
301
+ if (isset($profile)) {
302
+ delete_option($profile->get_wp_option_name());
303
+ WbsFactory::getRulesOrderStorage()->remove($profile->profile_id);
304
+ }
305
+
306
+ $this->refresh();
307
+ }
308
+
309
+ if (!isset($profile)) {
310
+ $profile = new self();
311
+ $profile->_stub = true;
312
+ $profiles[] = $profile;
313
+ }
314
+
315
+ if ($profile->_stub &&
316
+ ($sourceProfileId = @$_GET['duplicate']) != null &&
317
+ ($sourceProfile = $manager->profile($sourceProfileId)) != null) {
318
+
319
+ $duplicate = clone($sourceProfile);
320
+ $duplicate->id = $profile->id;
321
+ $duplicate->profile_id = $profile->profile_id;
322
+
323
+ $profiles[array_search($profile, $profiles, true)] = $duplicate;
324
+ $profile = $duplicate;
325
+ }
326
+
327
+ $create_profile_link_html =
328
+ '<a class="add-new-h2" href="'.esc_html(WbsRuleUrls::create()).'">'.
329
+ esc_html__('Add New', 'woowbs').
330
+ '</a>';
331
+
332
+ ?>
333
+ <h3><?php esc_html_e('Weight-based shipping', 'woowbs'); ?></h3>
334
+ <p><?php esc_html_e('Lets you calculate shipping based on total weight of the cart. You can have multiple rules active.', 'woowbs'); ?></p>
335
+ <?php echo $this->settingsHelper->trsPromotionHtml() ?>
336
+
337
+
338
+ <table class="form-table">
339
+
340
+ <tr class="wbs-title">
341
+ <th colspan="2">
342
+ <h4><?php esc_html_e('Rules', 'woowbs'); echo $create_profile_link_html; ?></h4>
343
+ </th>
344
+ </tr>
345
+
346
+ <tr class="wbs-profiles">
347
+ <td colspan="2">
348
+ <?php self::listProfiles($profiles); ?>
349
+ </td>
350
+ </tr>
351
+
352
+ <?php $profile->generate_settings_html(); ?>
353
+ </table>
354
+ <?php
355
+ }
356
+
357
+ public function process_admin_options()
358
+ {
359
+ $result = parent::process_admin_options();
360
+
361
+ $this->safeInit(true);
362
+
363
+ $clone = WBS_Profile_Manager::instance()->profile($this->profile_id);
364
+ if (isset($clone) && $clone !== $this) {
365
+ $clone->init();
366
+ }
367
+
368
+ if ($result) {
369
+ WbsWcTools::purgeWoocommerceShippingCache();
370
+ }
371
+
372
+ return $result;
373
+ }
374
+
375
+ public function display_errors()
376
+ {
377
+ foreach ($this->errors as $error) {
378
+ WC_Admin_Settings::add_error($error);
379
+ }
380
+ }
381
+
382
+ public function validate_positive_decimal_field($key)
383
+ {
384
+ return max(0, (float)$this->validate('decimal', $key));
385
+ }
386
+
387
+ public function validate_wbs_range_field($key)
388
+ {
389
+ return $this->settingsHelper->validateRangeHtml($key);
390
+ }
391
+
392
+ public function validate_wbs_weight_rate_field($key)
393
+ {
394
+ return $this->settingsHelper->validateWeightRateHtml($key);
395
+ }
396
+
397
+ public function validate_wbs_custom_field($key)
398
+ {
399
+ return $this->validate(@$this->form_fields[$key]['wbs_real_type'], $key);
400
+ }
401
+
402
+ public function validate_shipping_class_rates_field($key)
403
+ {
404
+ return $this->settingsHelper->validateShippingClasses($key);
405
+ }
406
+
407
+ public function generate_positive_decimal_html($key, $data)
408
+ {
409
+ return $this->generate_decimal_html($key, $data);
410
+ }
411
+
412
+ public function generate_wbs_range_html($key, $data)
413
+ {
414
+ return $this->settingsHelper->generateRangeHtml($key, $data);
415
+ }
416
+
417
+ public function generate_wbs_weight_rate_html($key, $data)
418
+ {
419
+ return $this->settingsHelper->generateWeightRateHtml($key, $data);
420
+ }
421
+
422
+ public function generate_wbs_custom_html($key, $data)
423
+ {
424
+ $realType = @$data['wbs_real_type'];
425
+ $data['type'] = $realType;
426
+ unset($data['wbs_real_type']);
427
+
428
+ $rowClass = @$data['wbs_row_class'];
429
+ unset($data['wbs_row_class']);
430
+
431
+ $generator = 'generate_text_html';
432
+ if ($realType) {
433
+ $realTypeGenerator = "generate_{$realType}_html";
434
+ if (method_exists($this, $realTypeGenerator)) {
435
+ $generator = $realTypeGenerator;
436
+ }
437
+ }
438
+
439
+ $html = $this->{$generator}($key, $data);
440
+
441
+ if ($rowClass) {
442
+ $html = preg_replace('/\<tr(.*?)(?:class="(.*?)")?(.*?)>/i', '<tr $1 class="$2 '.esc_html($rowClass).'" $3>', $html, 1);
443
+ }
444
+
445
+ return $html;
446
+ }
447
+
448
+ public function generate_shipping_class_rates_html($key, $data)
449
+ {
450
+ return $this->settingsHelper->generateShippingClassesHtml($key, $data);
451
+ }
452
+
453
+ public function get_description_html($data)
454
+ {
455
+ return parent::get_description_html($data) . @$data['html'];
456
+ }
457
+
458
+ public function get_wp_option_name()
459
+ {
460
+ return WBS_Profile_Manager::getRuleSettingsOptionName($this->id, $this->plugin_id);
461
+ }
462
+
463
+ public function getPostKey($key)
464
+ {
465
+ return method_exists($this, 'get_field_key')
466
+ ? $this->get_field_key($key)
467
+ : "{$this->plugin_id}{$this->id}_{$key}";
468
+ }
469
+
470
+
471
+ private $settingsHelper;
472
+
473
+ private function safeInit($showMessage = false)
474
+ {
475
+ try {
476
+
477
+ $this->init();
478
+ $this->valid = true;
479
+
480
+ } catch (Exception $e) {
481
+
482
+ $this->valid = false;
483
+ $this->enabled = 'no';
484
+
485
+ if ($showMessage) {
486
+ WC_Admin_Settings::add_error(
487
+ "Shipping rule '{$this->name}' has been deactivated due to an error. Double-check its settings and fix errors to make it active.
488
+ Unerlying error: \"{$e->getMessage()}\" at {$e->getFile()}:{$e->getLine()}."
489
+ );
490
+ }
491
+ }
492
+ }
493
+
494
+ private function init()
495
+ {
496
+ $this->init_form_fields();
497
+ $this->init_settings();
498
+
499
+ $this->enabled = $this->get_option('enabled');
500
+ $this->name = $this->get_option('name');
501
+ $this->title = $this->get_option('title');
502
+ $this->{'type'} = 'order';
503
+ $this->tax_status = $this->get_option('tax_status');
504
+
505
+ $this->availability = $this->get_option('availability');
506
+ $this->countries = $this->get_option('countries');
507
+ $this->weight = $this->getRangeOption('weight');
508
+ $this->subtotal = $this->getRangeOption('subtotal');
509
+ $this->subtotalWithTax = $this->get_option('subtotal_with_tax') === 'yes';
510
+
511
+ $this->fee = (float)$this->get_option('fee');
512
+ $this->settings['fee'] = $this->formatFloat($this->fee);
513
+
514
+ $this->weightRate = WbsProgressiveRate::fromArray($this->get_option('weight_rate', array()));
515
+
516
+ if (empty($this->countries)) {
517
+ $this->availability = $this->settings['availability'] = 'all';
518
+ }
519
+
520
+ $this->shippingClassRates = $this->get_option('shipping_class_rates', new WbsBucketRates());
521
+
522
+ $this->priceClamp = $this->getRangeOption('price_clamp');
523
+ }
524
+
525
+ private function formatFloat($value, $zeroReplacement = '')
526
+ {
527
+ if ($value == 0) {
528
+ return $zeroReplacement;
529
+ }
530
+
531
+ return wc_float_to_string($value);
532
+ }
533
+
534
+ private function makeCostString()
535
+ {
536
+ $baseCost = $this->fee ? wc_format_localized_price($this->fee) : null;
537
+
538
+ $weightRate = null;
539
+ if ($cost = $this->weightRate->getCost()) {
540
+
541
+ $weightRate .= wc_format_localized_price($cost);
542
+
543
+ $weightUnit = get_option('woocommerce_weight_unit');
544
+
545
+ $step = $this->weightRate->getStep();
546
+ $weightRate = sprintf(__('%s per %s %s', 'woowbs'), $weightRate, $step ? wc_format_localized_decimal($step) : null, $weightUnit);
547
+
548
+ if ($skip = $this->weightRate->getSkip()) {
549
+ $weightRate = sprintf(__('%s (from %s %s)', 'woowbs'), $weightRate, $skip, $weightUnit);
550
+ }
551
+ }
552
+
553
+ $cost = null;
554
+ if ($baseCost && $weightRate) {
555
+ $cost = sprintf(__('%s + %s'), $baseCost, $weightRate);
556
+ } else if ($baseCost || $weightRate) {
557
+ $cost = $baseCost . $weightRate;
558
+ } else {
559
+ $cost = __('Free', 'woowbs');
560
+ }
561
+
562
+ return $cost;
563
+ }
564
+
565
+ private function refresh()
566
+ {
567
+ echo '<script>location.href = ', json_encode(WbsRuleUrls::generic()), ';</script>';
568
+ die();
569
+ }
570
+
571
+ private function getRangeOption($name)
572
+ {
573
+ return WbsRange::fromArray($this->get_option($name, array()));
574
+ }
575
+
576
+ private function validate($type, $key)
577
+ {
578
+ $result = null;
579
+
580
+ $method = "validate_{$type}_field";
581
+ if (!$type || !method_exists($this, $method)) {
582
+ $method = 'validate_text_field';
583
+ }
584
+
585
+ /** @noinspection PhpUndefinedConstantInspection */
586
+ if (version_compare(WC_VERSION, '2.6', '<')) {
587
+ $result = $this->$method($key);
588
+ } else {
589
+ $result = $this->$method($key, @$_POST[$this->getPostKey($key)]);
590
+ }
591
+
592
+ return $result;
593
+ }
594
+
595
+ private static function listProfiles(array $profiles)
596
+ {
597
+ $current_profile_id = WBS_Profile_Manager::instance()->current_profile_id();
598
+
599
+ ?>
600
+ <table id="woowbs_shipping_methods" class="wc_shipping widefat">
601
+ <thead>
602
+ <tr>
603
+ <th class="sort"><i class="spinner"></i></th>
604
+ <th class="name"> <?php esc_html_e('Name', 'woowbs'); ?> </th>
605
+ <th> <?php esc_html_e('Destination', 'woowbs'); ?> </th>
606
+ <th> <?php esc_html_e('Weight', 'woowbs'); ?> </th>
607
+ <th> <?php esc_html_e('Subtotal', 'woowbs'); ?> </th>
608
+ <th> <?php esc_html_e('Cost', 'woowbs'); ?> </th>
609
+ <th class="status"> <?php esc_html_e('Active', 'woowbs'); ?> </th>
610
+ <th> <?php esc_html_e('Actions'); ?> </th>
611
+ </tr>
612
+ </thead>
613
+ <tbody>
614
+ <?php /** @var WC_Weight_Based_Shipping[] $profiles */ ?>
615
+ <?php foreach ($profiles as $profile): ?>
616
+ <tr
617
+ class="<?php echo ($profile->profile_id === $current_profile_id ? 'wbs-current' : null) ?>"
618
+ data-settings-url="<?php echo esc_html(WbsRuleUrls::edit($profile)) ?>"
619
+ data-profile-id="<?php echo esc_html($profile->id) ?>"
620
+ >
621
+ <td class="sort"></td>
622
+
623
+ <td class="name"><?php echo esc_html($profile->name)?></td>
624
+
625
+ <?php if ($profile->valid): ?>
626
+ <td class="countries">
627
+ <?php if ($profile->availability === 'all'): ?>
628
+ <?php esc_html_e('Any', 'woowbs') ?>
629
+ <?php else: ?>
630
+ <?php if ($profile->availability === 'excluding'): ?>
631
+ <?php esc_html_e('All except', 'woowbs') ?>
632
+ <?php endif; ?>
633
+ <?php echo esc_html(join(', ', $profile->countries))?>
634
+ <?php endif; ?>
635
+ </td>
636
+
637
+ <?php foreach (array($profile->weight, $profile->subtotal) as $range): ?>
638
+ <td>
639
+ <?php
640
+ echo
641
+ isset($range->min) || isset($range->max)
642
+ ?
643
+ ($range->minInclusive || !isset($range->min) ? '[' : '(') .
644
+ esc_html(wc_format_localized_decimal((float)$range->min)) .
645
+ '&nbsp;–&nbsp;' .
646
+ (isset($range->max) ? esc_html(wc_format_localized_decimal($range->max)) : '<span style="font-family:monospace">&infin;</span>') .
647
+ ($range->maxInclusive ? ']' : ')')
648
+ :
649
+ 'Any';
650
+ ?>
651
+ </td>
652
+ <?php endforeach; ?>
653
+
654
+ <td>
655
+ <?php echo esc_html($profile->makeCostString()); ?>
656
+ </td>
657
+ <?php else: ?>
658
+ <td colspan="4" align="center">
659
+ <?php esc_html_e('Invalid state', 'woowbs') ?>
660
+ </td>
661
+ <?php endif; ?>
662
+
663
+ <td class="status">
664
+ <?php if ($profile->enabled == 'yes'): ?>
665
+ <span class="status-enabled tips" data-tip="<?php esc_html_e('Enabled', 'woowbs')?>"><?php esc_html_e('Enabled', 'woowbs')?></span>
666
+ <?php else: ?>
667
+ -
668
+ <?php endif; ?>
669
+ </td>
670
+
671
+ <td class="actions">
672
+ <a class="button" href="<?php echo esc_html(WbsRuleUrls::duplicate($profile)) ?>">
673
+ <?php esc_html_e('Duplicate', 'woowbs') ?>
674
+ </a>
675
+
676
+ <a class="button" href="<?php echo esc_html(WbsRuleUrls::delete($profile)) ?>"
677
+ onclick="return confirm('<?php esc_html_e('Are you sure you want to delete this rule?', 'woowbs') ?>');">
678
+ <?php esc_html_e('Delete') ?>
679
+ </a>
680
+ </td>
681
+ </tr>
682
+ <?php endforeach; ?>
683
+ </tbody>
684
+ </table>
685
+
686
+ <!--suppress CssUnusedSymbol -->
687
+ <style>
688
+ #woowbs_shipping_methods td { cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
689
+ #woowbs_shipping_methods .sort { width: 1em; }
690
+ #woowbs_shipping_methods td.sort { cursor: move; }
691
+ #woowbs_shipping_methods th.sort { padding: 0; }
692
+ #woowbs_shipping_methods .actions { white-space: nowrap; }
693
+ #woowbs_shipping_methods .countries { max-width: 15em; }
694
+ #woowbs_shipping_methods .wbs-current td { background-color: #eee; }
695
+ #woowbs_shipping_methods tr:hover td { background-color: #ddd; }
696
+ #woowbs_shipping_methods .spinner { display: none; visibility: visible; margin: 0; width: 17px; height: 17px; background-size: 17px 17px; }
697
+ #woowbs_shipping_methods.in-progress .spinner { display: block; }
698
+ tr.wbs-title th { padding: 2em 0 0 0; }
699
+ tr.wbs-title h4 { font-size: 1.2em; }
700
+ .wc-settings-sub-title { padding-top: 2em; }
701
+ .form-table { border-top: 1px solid #aaa; }
702
+ tr.wbs-profiles > td { padding: 0; }
703
+
704
+ .wbs-destination label { display: none; }
705
+ .wbs-destination .forminp { padding-top: 0; }
706
+ .wbs-destination fieldset { margin-top: -15px !important; }
707
+
708
+ .wbs-subtotal .forminp { padding-bottom: 0}
709
+ .wbs-subtotal + tr > * { padding-top: 0 }
710
+ .wbs-subtotal + tr label { display: none; }
711
+ .wbs-minifield { width: 15.7em; }
712
+ .wbs-minifield.wc_input_decimal { text-align: right; }
713
+ .wbs-minifield-container { display: block ! important; }
714
+ .wbs-minifield-label { display: inline-block; min-width: 3em; }
715
+ .wbs-range-simple .wbs-minifield-label { min-width: 5em; }
716
+ .wbs-range-simple .wbs-minifield { min-width: 19.7em; }
717
+ .wbs-weight-rate .wbs-minifield-label { min-width: 5em; }
718
+ .wbs-weight-rate .wbs-minifield { width: 17.3em; }
719
+
720
+ .wbs-input-group {
721
+ display: inline-block;
722
+ height: 29px;
723
+ box-sizing: border-box;
724
+ }
725
+
726
+ .wbs-input-group-addon {
727
+ display: inline-block;
728
+ box-sizing: border-box;
729
+ height: 100%;
730
+ width: 2.5em;
731
+ text-align: center;
732
+ border: 1px solid #ddd;
733
+ padding: 3px 0;
734
+ }
735
+
736
+ .wbs-input-group-addon:first-child {
737
+ border-right: 0;
738
+ float: left;
739
+ }
740
+
741
+ .wbs-input-group-addon:last-child {
742
+ border-left: 0;
743
+ float: right;
744
+ }
745
+
746
+ .wbs-input-group input[type="text"] {
747
+ display: inline-block;
748
+ height: 100%;
749
+ box-sizing: border-box;
750
+ margin: 0;
751
+ }
752
+
753
+ .wbs-code {
754
+ font-size: inherit;
755
+ padding: 0 0.5em;
756
+ font-family: monospace;
757
+ }
758
+
759
+ .shippingrows {
760
+ overflow: hidden;
761
+ }
762
+
763
+ .shippingrows th, .shippingrows td {
764
+ width: auto;
765
+ white-space: nowrap;
766
+ padding: 1em;
767
+ }
768
+
769
+ .shippingrows .wc_input_decimal {
770
+ width: 7em;
771
+ }
772
+
773
+ .flat_rate .wbs-minifield-container {
774
+ display: inline !important;
775
+ margin-left: 1em;
776
+ }
777
+
778
+ .flat_rate .wbs-minifield-container:first-child {
779
+ margin-left: 0;
780
+ }
781
+
782
+ .flat_rate .wbs-minifield-label {
783
+ display: inline;
784
+ }
785
+
786
+ .flat_rate .wbs-minifield {
787
+ width: 6em;
788
+ }
789
+
790
+ .flat_rate_tpl {
791
+ display: none;
792
+ }
793
+ </style>
794
+ <?php
795
+ }
796
+ }
797
+ ?>
WbsFactory.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WbsFactory
3
+ {
4
+ public static function getRulesOrderStorage()
5
+ {
6
+ if (!isset(self::$rulesOrderStorage)) {
7
+ self::$rulesOrderStorage = new WbsRulesOrderStorage(WC_Weight_Based_Shipping::PLUGIN_PREFIX.'rules_order');
8
+ }
9
+
10
+ return self::$rulesOrderStorage;
11
+ }
12
+
13
+ private static $rulesOrderStorage;
14
+ }
WbsRuleUrls.php ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WbsRuleUrls
3
+ {
4
+ public static function generic($parameters = array())
5
+ {
6
+ $query = build_query(self::arrayFilterNull($parameters + array(
7
+ "page" => (version_compare(WC()->version, '2.1', '>=') ? "wc-settings" : "woocommerce_settings"),
8
+ "tab" => "shipping",
9
+ "section" => "wc_weight_based_shipping",
10
+ )));
11
+
12
+ $url = admin_url("admin.php?{$query}");
13
+
14
+ return $url;
15
+ }
16
+
17
+ public static function create(array $additionals = array())
18
+ {
19
+ return self::genericWithProfile(WBS_Profile_Manager::instance()->new_profile_id(), $additionals);
20
+ }
21
+
22
+ public static function edit(WC_Weight_Based_Shipping $rule, array $parameters = array())
23
+ {
24
+ return self::genericWithProfile($rule->profile_id, $parameters);
25
+ }
26
+
27
+ public static function duplicate(WC_Weight_Based_Shipping $rule)
28
+ {
29
+ return self::create(array('duplicate' => $rule->profile_id));
30
+ }
31
+
32
+ public static function delete(WC_Weight_Based_Shipping $rule)
33
+ {
34
+ return self::edit($rule, array('delete' => 'yes'));
35
+ }
36
+
37
+
38
+ private static function genericWithProfile($profileId, array $parameters = array())
39
+ {
40
+ $parameters['wbs_profile'] = $profileId;
41
+ $url = self::generic($parameters);
42
+ return $url;
43
+ }
44
+
45
+ public static function arrayFilterNull($array)
46
+ {
47
+ foreach ($array as $key => $value) {
48
+ if ($value === null) {
49
+ unset($array[$key]);
50
+ }
51
+ }
52
+
53
+ return $array;
54
+ }
55
+ }
WbsRulesOrderStorage.php ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WbsRulesOrderStorage
3
+ {
4
+ public function __construct($storageKeyName)
5
+ {
6
+ $this->storageKeyName = $storageKeyName;
7
+ }
8
+
9
+ public function sort(array $profiles)
10
+ {
11
+ $profilesWithDefinedOrder = array();
12
+ $profilesWithNoDefineOrder = array();
13
+
14
+ $weights = $this->getProfilesSortWeights();
15
+ foreach ($profiles as $profile) {
16
+ if (isset($weights[$profile])) {
17
+ $profilesWithDefinedOrder[$weights[$profile]] = $profile;
18
+ } else {
19
+ $profilesWithNoDefineOrder[] = $profile;
20
+ }
21
+ }
22
+
23
+ ksort($profilesWithDefinedOrder);
24
+
25
+ $sortedProfiles = array_merge($profilesWithDefinedOrder, $profilesWithNoDefineOrder);
26
+
27
+ return $sortedProfiles;
28
+ }
29
+
30
+ public function add($profile)
31
+ {
32
+ $weights = $this->getProfilesSortWeights();
33
+ $weights[$profile] = max($weights)+1;
34
+ $this->setProfileSortWeights($weights);
35
+ }
36
+
37
+ public function remove($profile)
38
+ {
39
+ $weights = $this->getProfilesSortWeights();
40
+ unset($weights[$profile]);
41
+ $this->setProfileSortWeights($weights);
42
+ }
43
+
44
+ public function set(array $profiles)
45
+ {
46
+ $this->setProfileSortWeights(array_flip(array_values($profiles)));
47
+ }
48
+
49
+ private $storageKeyName;
50
+
51
+ private function setProfileSortWeights(array $profileWeights)
52
+ {
53
+ update_option($this->storageKeyName, $profileWeights);
54
+ }
55
+
56
+ private function getProfilesSortWeights()
57
+ {
58
+ return get_option($this->storageKeyName, array());
59
+ }
60
+ }
WbsSettingsHtmlTools.php ADDED
@@ -0,0 +1,483 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WbsSettingsHtmlTools
3
+ {
4
+ public function __construct(WC_Weight_Based_Shipping $settings)
5
+ {
6
+ $this->settings = $settings;
7
+ }
8
+
9
+ public function generateRangeHtml($key, array $data = array())
10
+ {
11
+ $field = $this->settings->getPostKey($key);
12
+
13
+ $range = $this->settings->get_option($key);
14
+ if ($range === '' || $range === false) {
15
+ $range = null;
16
+ }
17
+
18
+ $data['wbs_range_type'] = @$data['wbs_range_type'] !== 'simple' ? 'default' : $data['wbs_range_type'];
19
+ $simple = @$data['wbs_range_type'] == 'simple';
20
+
21
+ return $this->formRow('
22
+ <label class="wbs-minifield-container">
23
+ <span class="wbs-minifield-label">' . ($simple ? 'minimum' : 'above') . '</span>
24
+ ' . $this->rangeInput('min', @$range['min'], true, $simple, $field) . '
25
+ </label>
26
+ <label class="wbs-minifield-container">
27
+ <span class="wbs-minifield-label">' . ($simple ? 'maximum' : 'below') . '</span>
28
+ ' . $this->rangeInput('max', @$range['max'], false, $simple, $field) . '
29
+ </label>
30
+ ',
31
+ "{$field}_min",
32
+ array('wbs-range', "wbs-range-{$data['wbs_range_type']}"),
33
+ $data
34
+ );
35
+ }
36
+
37
+ public function validateRangeHtml($key)
38
+ {
39
+ $range = array();
40
+
41
+ $input = @$_POST[$this->settings->getPostKey($key)];
42
+ foreach(array('min', 'max') as $limit) {
43
+ $range[$limit]['value'] = $this->receiveDecimal(@$input[$limit]['value']);
44
+ $range[$limit]['inclusive'] = (bool)(int)@$input[$limit]['inclusive'];
45
+ }
46
+
47
+ if (isset($range['min']['value']) && isset($range['max']['value'])) {
48
+ if ($range['min']['value'] > $range['max']['value']) {
49
+ $tmp = $range['max']['value'];
50
+ $range['max']['value'] = $range['min']['value'];
51
+ $range['min']['value'] = $tmp;
52
+ } else if ($range['min']['value'] === $range['max']['value']) {
53
+ $range['min']['inclusive'] = $range['max']['inclusive'] = true;
54
+ }
55
+ }
56
+
57
+ return $range;
58
+ }
59
+
60
+ public function generateWeightRateHtml($key, array $data = array())
61
+ {
62
+ $inputNamePrefix = $this->settings->getPostKey($key);
63
+ $id = "{$inputNamePrefix}_cost";
64
+ $value = $this->settings->get_option($key);
65
+
66
+ return $this->formRow(
67
+ $this->weightRate($inputNamePrefix, $value),
68
+ $id, 'wbs-weight-rate', $data
69
+ );
70
+ }
71
+
72
+ public function validateWeightRateHtml($key)
73
+ {
74
+ return $this->receiveWeightRate(@$_POST[$this->settings->getPostKey($key)]);
75
+ }
76
+
77
+ public function generateShippingClassesHtml($key, $data)
78
+ {
79
+ $prefix = $this->settings->getPostKey($key);
80
+
81
+ $data = wp_parse_args($data, array(
82
+ 'title' => '',
83
+ 'desc_tip' => '',
84
+ 'description' => '',
85
+ ));
86
+
87
+ $rates = $this->settings->get_option($key);
88
+ if (!$rates) {
89
+ $rates = new WbsBucketRates();
90
+ }
91
+
92
+ if (!$rates->listAll()) {
93
+ ob_start();
94
+ ?>
95
+ <tr valign="top">
96
+ <th scope="row" class="titledesc">
97
+ <?php echo wp_kses_post($data['title']); ?>
98
+ <?php echo $this->settings->get_description_html($data); ?>
99
+ </th>
100
+ <td>
101
+ <?php echo self::premiumPromotionHtml() ?>
102
+ </td>
103
+ </tr>
104
+ <?php
105
+ return ob_get_clean();
106
+ }
107
+
108
+ $tableId = "{$this->settings->id}_flat_rates";
109
+
110
+ ob_start();
111
+ ?>
112
+ <tr valign="top">
113
+ <th scope="row" class="titledesc">
114
+ <?php echo wp_kses_post($data['title']); ?>
115
+ <?php echo $this->settings->get_description_html($data); ?>
116
+ </th>
117
+ <td class="forminp" id="<?php echo esc_html($tableId); ?>">
118
+ <table class="shippingrows widefat" cellspacing="0">
119
+ <thead>
120
+ <tr>
121
+ <th class="check-column"><input type="checkbox"></th>
122
+ <th class="shipping_class"><?php _e('Shipping Class', 'woowbs'); ?></th>
123
+ <th><?php _e('Additional Cost', 'woowbs'); ?></th>
124
+ <th><?php _e('Weight Rate', 'woowbs'); ?></th>
125
+ </tr>
126
+ </thead>
127
+ <tbody class="flat_rates">
128
+ <?php
129
+ foreach (array_values($rates->listAll()) as $i => $rate) {
130
+ echo $this->shippingClassRateRow($prefix, $i, $rate);
131
+ }
132
+
133
+ echo $this->shippingClassRateRow($prefix, '{{{rate_id}}}', null, 'flat_rate_tpl');
134
+ ?>
135
+ </tbody>
136
+ <tfoot>
137
+ <tr>
138
+ <th colspan="5">
139
+ <a href="#" class="add button"><?php _e('Add', 'woowbs'); ?></a>
140
+ <a href="#" class="remove button"><?php _e('Delete selected costs', 'woowbs'); ?></a>
141
+ </th>
142
+ </tr>
143
+ </tfoot>
144
+ </table>
145
+ <script type="text/javascript">
146
+ jQuery(function($) {
147
+
148
+ var $table = $("#<?php echo $tableId ?>");
149
+ var $templateRow = $table.find('.flat_rate_tpl');
150
+
151
+ $table
152
+
153
+ .on('click', '.add', function() {
154
+
155
+ var rateCount = $table.find('.flat_rate').size();
156
+ var newRateHtml = $('<div/>').append($templateRow.clone()).html().replace(/\{\{\{rate_id}}}/g, rateCount);
157
+ $(newRateHtml).insertBefore($templateRow).removeClass('flat_rate_tpl');
158
+
159
+ return false;
160
+ })
161
+
162
+ .on('click', '.remove', function() {
163
+
164
+ var answer = confirm("<?php _e('Delete the selected rates?', 'woowbs'); ?>");
165
+
166
+ if (answer) {
167
+ $table.find('.check-column input:checked').each(function(i, el) {
168
+ $(el).closest('.flat_rate:not(.flat_rate_tpl)').remove();
169
+ });
170
+ }
171
+
172
+ return false;
173
+ })
174
+
175
+ .closest('form').on('submit', function() {
176
+ $templateRow.remove();
177
+ })
178
+ ;
179
+ });
180
+ </script>
181
+ </td>
182
+ </tr>
183
+ <?php
184
+ return ob_get_clean();
185
+ }
186
+
187
+ public function validateShippingClasses($key)
188
+ {
189
+ $rates = new WbsBucketRates();
190
+
191
+ $rows = (array)@$_POST[$this->settings->getPostKey($key)];
192
+ foreach ($rows as $i => $row) {
193
+ $rates->add(new WbsBucketRate(
194
+ @$row['class'],
195
+ $this->receiveDecimal(@$row['fee']),
196
+ WbsProgressiveRate::fromArray($this->receiveWeightRate(@$row['weight_rate']))
197
+ ));
198
+ }
199
+
200
+ return $rates;
201
+ }
202
+
203
+ public function premiumPromotionHtml($feature = 'The function')
204
+ {
205
+ return '
206
+ <p>
207
+ '.esc_html($feature).' is available in the
208
+ <a href="https://codecanyon.net/item/woocommerce-weight-based-shipping/10099013?ref=dangoodman" target="_blank">Plus version</a>.
209
+ Try out a <a href="https://codecanyon.net/item/woocommerce-weight-based-shipping/full_screen_preview/10099013?ref=dangoodman" target="_blank">live demo</a>.
210
+ </p>';
211
+ }
212
+
213
+ public function trsPromotionHtml()
214
+ {
215
+ return '
216
+ <p>
217
+ In case you need a more flexible shipping solution take a look at our <a href="http://tablerateshipping.com/" target="_blank">advanced shipping plugin</a>.
218
+ </p>
219
+ ';
220
+ }
221
+
222
+ private $settings;
223
+
224
+ private function shippingClassSelect($name, $value = null)
225
+ {
226
+ $html = '<select name="' . esc_attr($name) . '" class="select">';
227
+
228
+ if ($classes = WC()->shipping->get_shipping_classes()) {
229
+ foreach ($classes as $shipping_class) {
230
+ $html .=
231
+ '<option value="' . esc_attr($shipping_class->slug) . '" ' . selected($shipping_class->slug, $value, false) . '>' .
232
+ $shipping_class->name .
233
+ '</option>';
234
+ }
235
+ } else {
236
+ $html .= '<option value="">' . __('Select a class&hellip;', 'woowbs') . '</option>';
237
+ }
238
+
239
+ return $html;
240
+ }
241
+
242
+ private function shippingClassRateRow($inputNamePrefix, $rateId, WbsBucketRate $rate = null, $class = null)
243
+ {
244
+ return '
245
+ <tr class="flat_rate ' . esc_html($class) . '">
246
+ <th class="check-column">
247
+ <input type="checkbox" name="select" />
248
+ </th>
249
+ <td class="flat_rate_class">
250
+ ' . $this->shippingClassSelect(
251
+ "{$inputNamePrefix}[{$rateId}][class]",
252
+ $rate ? $rate->getId() : null
253
+ ) . '
254
+ </td>
255
+ <td>' .
256
+ $this->decimalInput(array(
257
+ 'value' => $rate ? $rate->getFlatRate() : null,
258
+ 'name' => "{$inputNamePrefix}[{$rateId}][fee]"
259
+ )) . '
260
+ </td>
261
+ <td>' .
262
+ $this->weightRate(
263
+ "{$inputNamePrefix}[{$rateId}][weight_rate]",
264
+ $rate ? $rate->getProgressiveRate()->toArray() : null,
265
+ false
266
+ ) . '
267
+ </td>
268
+ </tr>
269
+ ';
270
+ }
271
+
272
+ private function weightRate($inputNamePrefix, array $value = null, $placeholders = true)
273
+ {
274
+ $id = "{$inputNamePrefix}_cost";
275
+
276
+ $weightUnit = get_option('woocommerce_weight_unit');
277
+
278
+ $fields = array(
279
+ array(
280
+ 'id' => $id,
281
+ 'name' => 'cost',
282
+ 'label' => 'charge',
283
+ 'placeholder' => sprintf(__('e.g. %s'), strip_tags(wc_price(2.5))),
284
+ 'decorator' => get_woocommerce_currency_symbol(),
285
+ 'decorator_left' => substr(get_option('woocommerce_currency_pos'), 0, 1) === 'l',
286
+ ),
287
+ array(
288
+ 'id' => null,
289
+ 'name' => 'step',
290
+ 'label' => 'per each',
291
+ 'placeholder' => sprintf(__('e.g. %s %s'), wc_format_localized_decimal(0.5), $weightUnit),
292
+ 'decorator' => $weightUnit,
293
+ 'decorator_left' => false,
294
+ ),
295
+ array(
296
+ 'id' => null,
297
+ 'name' => 'skip',
298
+ 'label' => 'over',
299
+ 'placeholder' => sprintf(__('e.g. %s %s'), wc_format_localized_decimal(3), $weightUnit),
300
+ 'decorator' => $weightUnit,
301
+ 'decorator_left' => false,
302
+ )
303
+ );
304
+
305
+ $html = '';
306
+
307
+ foreach ($fields as $field) {
308
+
309
+ $decorator = '<span class="wbs-input-group-addon">' . esc_html($field['decorator']) . '</span>';
310
+
311
+ $html .= '
312
+ <label class="wbs-minifield-container">
313
+ <span class="wbs-minifield-label">' . esc_html($field['label']) . '</span>
314
+ <div class="wbs-input-group">
315
+ ' . ($field['decorator_left'] ? $decorator : null) . '
316
+ ' . $this->decimalInput(array(
317
+ 'id' => $field['id'],
318
+ 'class' => 'wbs-minifield',
319
+ 'name' => "{$inputNamePrefix}[{$field['name']}]",
320
+ 'value' => (float)($v = @$value[$field['name']]) === 0.0 ? null : $v,
321
+ 'placeholder' => $placeholders ? $field['placeholder'] : null,
322
+ )) . '
323
+ ' . ($field['decorator_left'] ? null : $decorator) . '
324
+ </div>
325
+ </label>
326
+ ';
327
+ }
328
+
329
+ return $html;
330
+ }
331
+
332
+ private function rangeInput($name, $current, $defaultInclusive, $simple, $field)
333
+ {
334
+ $html = $this->decimalInput(array(
335
+ 'id' => "{$field}_{$name}",
336
+ 'name' => "{$field}[{$name}][value]",
337
+ 'class' => "wbs-minifield",
338
+ 'value' => @$current['value'],
339
+ 'placeholder' => $simple ? esc_html($name) : null,
340
+ ));
341
+
342
+ if (!$simple) {
343
+ $html .= '
344
+ <label> ' .
345
+ $this->input(array(
346
+ 'type' => 'checkbox',
347
+ 'name' =>"{$field}[{$name}][inclusive]",
348
+ 'value' => 1,
349
+ 'checked' => isset($current['inclusive']) ? $current['inclusive'] : $defaultInclusive,
350
+ )) .
351
+ ' or equal
352
+ </label>
353
+ ';
354
+ }
355
+
356
+ return $html;
357
+ }
358
+
359
+ private function input(array $attrs = array())
360
+ {
361
+ $attrs += array(
362
+ 'type' => 'text',
363
+ );
364
+
365
+ $html = '<input';
366
+
367
+ foreach ($attrs as $name => $value) {
368
+
369
+ if (!isset($value)) {
370
+ continue;
371
+ }
372
+
373
+ if (is_bool($value)) {
374
+
375
+ if ($value) {
376
+ $html .= ' '.esc_html($name);
377
+ }
378
+
379
+ continue;
380
+ }
381
+
382
+ if (is_array($value)) {
383
+ $value = join(' ', $value);
384
+ }
385
+
386
+ $html .= ' ' . esc_html($name) . '="' . esc_html($value) . '"';
387
+ }
388
+
389
+ $html .= '>';
390
+
391
+ return $html;
392
+ }
393
+
394
+ private function decimalInput(array $attrs = array())
395
+ {
396
+ if (isset($attrs['value']) && (string)$attrs['value'] !== '') {
397
+ $attrs['value'] = wc_format_localized_decimal($attrs['value']);
398
+ }
399
+
400
+ $attrs['class'] = (array)@$attrs['class'];
401
+ $attrs['class'][] = 'wc_input_decimal input-text';
402
+
403
+ return $this->input($attrs);
404
+ }
405
+
406
+ private function formRow($innerHtml, $fieldId, $classes, $data)
407
+ {
408
+ if (is_array($classes)) {
409
+ $classes = join(' ', $classes);
410
+ }
411
+
412
+ $ksesTitle = wp_kses_post(@$data['title']);
413
+
414
+ $data += array(
415
+ 'desc_tip' => false,
416
+ 'description' => '',
417
+ );
418
+
419
+ ob_start();
420
+ ?>
421
+ <tr valign="top" class="<?php echo esc_html($classes) ?>">
422
+ <th scope="row" class="titledesc">
423
+ <?php if ($ksesTitle): ?>
424
+ <label for="<?php echo esc_attr($fieldId) ?>"><?php echo $ksesTitle ?></label>
425
+ <?php endif; ?>
426
+ <?php echo $this->settings->get_tooltip_html($data); ?>
427
+ </th>
428
+ <td class="forminp">
429
+ <fieldset>
430
+ <?php if ($ksesTitle): ?>
431
+ <legend class="screen-reader-text">
432
+ <span><?php echo $ksesTitle; ?></span>
433
+ </legend>
434
+ <?php endif; ?>
435
+ <?php echo $innerHtml ?>
436
+ <?php echo $this->settings->get_description_html($data); ?>
437
+ </fieldset>
438
+ </td>
439
+ </tr>
440
+ <?php
441
+ return ob_get_clean();
442
+ }
443
+
444
+ private function receiveString($value)
445
+ {
446
+ if (isset($value)) {
447
+
448
+ $value = stripslashes(trim($value));
449
+
450
+ if ((string)$value === '') {
451
+ $value = null;
452
+ }
453
+ }
454
+
455
+ return $value;
456
+ }
457
+
458
+ private function receiveDecimal($value, $defaultValue = null)
459
+ {
460
+ $value = $this->receiveString($value);
461
+
462
+ $value = isset($value) ? wc_format_decimal($value) : $defaultValue;
463
+
464
+ if (isset($value) && !is_numeric($value)) {
465
+ $this->settings->errors[] = "'{$value}' is not a valid decimal value";
466
+ $value = null;
467
+ }
468
+
469
+ return $value;
470
+ }
471
+
472
+
473
+ private function receiveWeightRate($input)
474
+ {
475
+ $input = (array)$input;
476
+
477
+ return array(
478
+ 'cost' => $this->receiveDecimal(@$input['cost']),
479
+ 'step' => $this->receiveDecimal(@$input['step']),
480
+ 'skip' => $this->receiveDecimal(@$input['skip']),
481
+ );
482
+ }
483
+ }
WbsWcTools.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class WbsWcTools
4
+ {
5
+ public static function purgeWoocommerceShippingCache()
6
+ {
7
+ global $wpdb;
8
+
9
+ $transients = $wpdb->get_col("
10
+ SELECT SUBSTR(option_name, LENGTH('_transient_') + 1)
11
+ FROM `{$wpdb->options}`
12
+ WHERE option_name LIKE '_transient_wc_ship_%'
13
+ ");
14
+
15
+ foreach ($transients as $transient) {
16
+ delete_transient($transient);
17
+ }
18
+ }
19
+ }
assets/admin.js ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery(function($) {
2
+ 'use strict';
3
+
4
+ var $table = $("#woowbs_shipping_methods");
5
+
6
+ $table.find('td').click(function(e) {
7
+ if (e.target == this) {
8
+ location.href = $(this).parent().data('settingsUrl')
9
+ }
10
+ });
11
+
12
+ var orderUpdatingRequest = null;
13
+
14
+ $table.find('tbody').on("sortupdate", function(event, ui) {
15
+
16
+ var ids = $(this).find('[data-profile-id]').map(function() {
17
+ return $(this).attr('data-profile-id');
18
+ }).get();
19
+
20
+ if (orderUpdatingRequest) {
21
+ orderUpdatingRequest.abort();
22
+ }
23
+
24
+ $table.addClass('in-progress');
25
+
26
+ //noinspection JSUnresolvedVariable
27
+ orderUpdatingRequest =
28
+ jQuery.post(woowbs_ajax_vars.ajax_url, {
29
+ 'action': 'woowbs_update_rules_order',
30
+ 'profiles': ids
31
+ })
32
+ .always(function() {
33
+ $table.removeClass('in-progress');
34
+ orderUpdatingRequest = null;
35
+ });
36
+ });
37
+ });
bootstrap.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: WooCommerce Weight Based Shipping
4
+ * Plugin URI: http://wordpress.org/plugins/weight-based-shipping-for-woocommerce/
5
+ * Description: Simple yet flexible shipping method for WooCommerce.
6
+ * Version: 4.2.3
7
+ * Author: dangoodman
8
+ * Author URI: http://tablerateshipping.com
9
+ */
10
+
11
+ require_once(dirname(__FILE__).'/WBS_Loader.php');
12
+ WBS_Loader::loadWbs(__FILE__);
functions.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if (!function_exists('WC'))
3
+ {
4
+ /**
5
+ * @return Woocommerce
6
+ */
7
+ function WC()
8
+ {
9
+ return $GLOBALS['woocommerce'];
10
+ }
11
+ }
12
+
13
+ function wbst($template, $args)
14
+ {
15
+ $result = null;
16
+
17
+ if (!empty($template))
18
+ {
19
+ if (is_array($args))
20
+ {
21
+ $replacements = array();
22
+ foreach ($args as $key => $value)
23
+ {
24
+ $replacements["{{{$key}}}"] = $value;
25
+ }
26
+
27
+ $result = strtr($template, $replacements);
28
+ }
29
+ else
30
+ {
31
+ $result = preg_replace('/\{\{.*?\}\}/', $args, $template);
32
+ }
33
+ }
34
+
35
+ return $result;
36
+ }
37
+ ?>
legacy.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class WBS_Shipping_Rate_Override
3
+ {
4
+ private $class;
5
+ private $fee;
6
+ private $rate;
7
+ private $weightStep;
8
+
9
+ public function getWeightStep()
10
+ {
11
+ return $this->weightStep;
12
+ }
13
+
14
+ public function getClass()
15
+ {
16
+ return $this->class;
17
+ }
18
+
19
+ public function getRate()
20
+ {
21
+ return $this->rate;
22
+ }
23
+
24
+ public function getFee()
25
+ {
26
+ return $this->fee;
27
+ }
28
+ }
29
+
30
+ class WBS_Shipping_Class_Override_Set
31
+ {
32
+ private $overrides;
33
+
34
+ public function getOverrides()
35
+ {
36
+ return $this->overrides;
37
+ }
38
+ }
readme.txt ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WooCommerce Weight Based Shipping ===
2
+ Contributors: dangoodman
3
+ Tags: ecommerce, woocommerce, shipping, woocommerce shipping, weight-based shipping, conditional free shipping,
4
+ conditional flat rate, table rate shipping, weight, subtotal, country, shipping classes
5
+ Requires at least: 3.8
6
+ Tested up to: 4.7
7
+ WC requires at least: 2.1
8
+ WC tested up to: 2.7
9
+ Stable tag: trunk
10
+
11
+
12
+ Simple yet flexible weight-based shipping for WooCommerce
13
+
14
+ == Description ==
15
+
16
+ Weight Based Shipping is a simple yet flexible shipping method for WooCommerce focused mainly on order weight (but not limited to) to calculate shipping cost. Plugin allows you to add multiple rules based on various conditions.
17
+
18
+ <br>
19
+
20
+ = Features =
21
+
22
+ <p></p>
23
+ <ul>
24
+ <li>
25
+ <b>Order weight, subtotal and destination</b><br>
26
+ Create as many shipping rules as you need for different order destinations, weight and subtotal ranges.<br><br>
27
+ </li>
28
+ <li>
29
+ <b>Flexible Price Calculation</b><br>
30
+ Each rule can be configured to expose a constant price (like Flat Rate) or a progressive price based on cart weight, or both.<br><br>
31
+ </li>
32
+ <li>
33
+ <b>Conditional Free Shipping</b><br>
34
+ In some cases you want to ship for free depending on subtotal, total weight or some other condition. That can be achieved in a moment with the plugin.<br><br>
35
+ </li>
36
+ <li>
37
+ <b>Shipping Classes Support</b><br>
38
+ For each shipping class you have you can override the way shipping price is calculated for it.<br><br>
39
+ </li>
40
+ </ul>
41
+
42
+ See <a href="https://wordpress.org/plugins/weight-based-shipping-for-woocommerce/screenshots/">screenshots</a> for the list of all supported options.
43
+ <br><br>
44
+
45
+ <blockquote>
46
+ Also, check out our <a href="http://tablerateshipping.com">advanced table rate shipping plugin for WooCommerce</a>.<br>
47
+ <br>
48
+ </blockquote>
49
+
50
+ == Changelog ==
51
+
52
+ = 4.2.3 =
53
+ * Fix links to premium plugins
54
+
55
+ = 4.2.2 =
56
+ * Fix rules not imported from an older version when updating from pre-4.0 to 4.2.0 or 4.2.1
57
+
58
+ = 4.2.1 =
59
+ * Fix saving rules order
60
+
61
+ = 4.2.0 =
62
+ * Allow sorting rules by drag'n'drop in admin panel
63
+
64
+ = 4.1.4 =
65
+ * WooCommerce 2.6 compatibility fixes
66
+
67
+ = 4.1.3 =
68
+ * Minimize chances of a float-point rounding error in the weight step count calculation (https://wordpress.org/support/topic/weight-rate-charge-skip-calculate)
69
+
70
+ = 4.1.2 =
71
+ * Don't fail on invalid settings, allow editing them instead
72
+
73
+ = 4.1.1 =
74
+ * Backup old settings on upgrade from pre-4.0 versions
75
+
76
+ = 4.1.0 =
77
+ * Fix WC_Settings_API->get_field_key() missing method usage on WC 2.3.x
78
+ * Use package passed to calculate_shipping() funciton instead of global cart object for better integration with 3d-party plugins
79
+ * Get rid of wbs_remap_shipping_class hook
80
+ * Use class autoloader for better performance and code readability
81
+
82
+ = 4.0.0 =
83
+ * Admin UI redesign
84
+
85
+ = 3.0.0 =
86
+ * Country states/regions targeting support
87
+
88
+ = 2.6.9 =
89
+ * Fixed: inconsistent decimal input handling in Shipping Classes section (https://wordpress.org/support/topic/please-enter-in-monetary-decimal-issue)
90
+
91
+ = 2.6.8 =
92
+ * Fixed: plugin settings are not changed on save with WooCommerce 2.3.10 (WooCommerce 2.3.10 compatibility issue)
93
+
94
+ = 2.6.6 =
95
+ * Introduced 'wbs_profile_settings_form' filter for better 3d-party extensions support
96
+ * Removed partial localization
97
+
98
+ = 2.6.5 =
99
+ * Min/Max Shipping Price options
100
+
101
+ = 2.6.3 =
102
+ * Improved upgrade warning system
103
+ * Fixed warning about Shipping Classes Overrides changes
104
+
105
+ = 2.6.2 =
106
+ * Fixed Shipping Classes Overrides: always apply base Handling Fee
107
+
108
+ = 2.6.1 =
109
+ * Introduced "Subtotal With Tax" option
110
+
111
+ = 2.6.0 =
112
+ * Min/Max Subtotal condition support
113
+
114
+ = 2.5.1 =
115
+ * Introduce "wbs_remap_shipping_class" filter to provide 3dparty plugins an ability to alter shipping cost calculation
116
+ * Wordpress 4.1 compatibility testing
117
+
118
+ = 2.5.0 =
119
+
120
+ * Shipping classes support
121
+ * Ability to choose all countries except specified
122
+ * Select All/None buttons for countries
123
+ * Purge shipping price calculations cache on configuration changes to reflect actual config immediatelly
124
+ * Profiles table look tweaks
125
+ * Other small tweaks
126
+
127
+ = 2.4.2 =
128
+
129
+ * Fixed: deleting non-currently selected configuration deletes first configuration from the list
130
+
131
+ = 2.4.1 =
132
+
133
+ * Updated pot-file required for translations
134
+ * Added three nice buttons to plugin settings page
135
+ * Prevent buttons in Actions column from wrapping on multiple lines
136
+
137
+ = 2.4.0 =
138
+
139
+ * By default, apply Shipping Rate to the extra weight part exceeding Min Weight. Also a checkbox added to switch off this feature.
140
+
141
+ = 2.3.0 =
142
+
143
+ * Duplicate profile feature
144
+ * New 'Weight Step' option for rough gradual shipping price calculation
145
+ * Added more detailed description to the Handling Fee and Shipping Rate fields to make their purpose clear
146
+ * Plugin prepared for localization
147
+ * Refactoring
148
+
149
+ = 2.2.3 =
150
+
151
+ * Fixed: first time saving settings with fresh install does not save anything while reporting successful saving.
152
+ * Replace short php tags with their full equivalents to make code more portable.
153
+
154
+ = 2.2.2 =
155
+
156
+ Fix "parse error: syntax error, unexpected T_FUNCTION in woocommerce-weight-based-shipping.php on line 610" http://wordpress.org/support/topic/fatal-error-1164.
157
+
158
+ = 2.2.1 =
159
+
160
+ Allow zero weight shipping. Thus only Handling Fee is added to the final price.
161
+
162
+ Previously, weight based shipping option has not been shown to user if total weight of their cart is zero. Since version 2.2.1 this is changed so shipping option is available to user with price set to Handling Fee. If it does not suite your needs well you can return previous behavior by setting Min Weight to something a bit greater zero, e.g. 0.001, so that zero-weight orders will not match constraints and the shipping option will not be shown.
163
+
164
+ == Upgrade Notice ==
165
+
166
+ = 2.2.1 =
167
+
168
+ Allow zero weight shipping. Thus only Handling Fee is added to the final price.
169
+
170
+ Previously, weight based shipping option has not been shown to user if total weight of their cart is zero. Since version 2.2.1 this is changed so shipping option is available to user with price set to Handling Fee. If it does not suite your needs well you can return previous behavior by setting Min Weight to something a bit greater zero, e.g. 0.001, so that zero-weight orders will not match constraints and the shipping option will not be shown.
171
+
172
+
173
+ == Screenshots ==
174
+
175
+ 1. A configuration example
176
+ 2. Another rule settings
177
+ 3. How that could look to customer