WooLentor – Elementor WooCommerce Builder Addons, Variation Swatches Addons, Gutenberg WooCommerce Product block, grid, slider, templates, widgets, Quick View, Wishlist, Products Compare, Product Filter – All in One Solution - Version 2.1.8

Version Description

Download this release

Release Info

Developer devitemsllc
Plugin Icon 128x128 WooLentor – Elementor WooCommerce Builder Addons, Variation Swatches Addons, Gutenberg WooCommerce Product block, grid, slider, templates, widgets, Quick View, Wishlist, Products Compare, Product Filter – All in One Solution
Version 2.1.8
Comparing to
See all releases

Code changes from version 2.1.7 to 2.1.8

assets/css/woolentor-widgets.css CHANGED
@@ -377,6 +377,10 @@ li.woolentor-cart .button:before {
377
  .ht-product-action ul li.woolentor-cart a.wc-forward.added_to_cart{
378
  padding: 0;
379
  }
 
 
 
 
380
 
381
  /*Product Wrap*/
382
  .ht-products {
@@ -2261,6 +2265,10 @@ span.wlvideo-button {
2261
  background-color: transparent !important;
2262
  }
2263
 
 
 
 
 
2264
  .woolentor-order-review-product span.product-thumbnail {
2265
  display: inline-block;
2266
  padding-right: 10px;
377
  .ht-product-action ul li.woolentor-cart a.wc-forward.added_to_cart{
378
  padding: 0;
379
  }
380
+ .woolentor-short-desc ul, .woolentor-short-desc ol,
381
+ .woolentor-products .woocommerce-product-details__short-description ul,.woolentor-products .woocommerce-product-details__short-description ol{
382
+ margin-left: 15px;
383
+ }
384
 
385
  /*Product Wrap*/
386
  .ht-products {
2265
  background-color: transparent !important;
2266
  }
2267
 
2268
+ span.woolentor-order-item-title .product-thumbnail{
2269
+ display: none;
2270
+ }
2271
+
2272
  .woolentor-order-review-product span.product-thumbnail {
2273
  display: inline-block;
2274
  padding-right: 10px;
includes/addons/wb_archive_product.php CHANGED
@@ -928,7 +928,7 @@ class Woolentor_Elementor_Widget_Archive_Product extends Widget_Base {
928
 
929
  $settings = $this->get_settings_for_display();
930
 
931
- if ( WC()->session ) {
932
  wc_print_notices();
933
  }
934
 
928
 
929
  $settings = $this->get_settings_for_display();
930
 
931
+ if ( WC()->session && function_exists('wc_print_notices') ) {
932
  wc_print_notices();
933
  }
934
 
includes/admin/include/admin_fields.php CHANGED
@@ -1543,6 +1543,53 @@ class Woolentor_Admin_Fields {
1543
 
1544
  ),
1545
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1546
  array(
1547
  'name' => 'wishlist',
1548
  'label' => esc_html__( 'Wishlist', 'woolentor' ),
1543
 
1544
  ),
1545
 
1546
+ array(
1547
+ 'name' => 'woolentor_backorder_settings',
1548
+ 'label' => esc_html__( 'Backorder', 'woolentor' ),
1549
+ 'type' => 'module',
1550
+ 'default' => 'off',
1551
+ 'section' => 'woolentor_backorder_settings',
1552
+ 'option_id'=> 'enable',
1553
+ 'require_settings' => true,
1554
+ 'setting_fields' => array(
1555
+
1556
+ array(
1557
+ 'name' => 'enable',
1558
+ 'label' => esc_html__( 'Enable / Disable', 'woolentor' ),
1559
+ 'desc' => esc_html__( 'You can enable / disable backorder module from here.', 'woolentor' ),
1560
+ 'type' => 'checkbox',
1561
+ 'default' => 'off',
1562
+ 'class' => 'woolentor-action-field-left'
1563
+ ),
1564
+
1565
+ array(
1566
+ 'name' => 'backorder_limit',
1567
+ 'label' => esc_html__( 'Backorder Limit', 'woolentor' ),
1568
+ 'desc' => esc_html__( 'Set "Backorder Limit" on all "Backorder" products across the entire website. You can also set limits for each product individually from the "Inventory" tab.', 'woolentor' ),
1569
+ 'type' => 'number',
1570
+ 'class' => 'woolentor-action-field-left'
1571
+ ),
1572
+
1573
+ array(
1574
+ 'name' => 'backorder_availability_date',
1575
+ 'label' => esc_html__( 'Availability Date', 'woolentor' ),
1576
+ 'type' => 'date',
1577
+ 'class' => 'woolentor-action-field-left'
1578
+ ),
1579
+
1580
+ array(
1581
+ 'name' => 'backorder_availability_message',
1582
+ 'label' => esc_html__( 'Availability Message', 'woolentor' ),
1583
+ 'desc' => esc_html__( 'Manage how you want the "Message" to appear. Use this {availability_date} placeholder to display the date you set. ', 'woolentor' ),
1584
+ 'type' => 'text',
1585
+ 'default' => esc_html__( 'On Backorder: Will be available on {availability_date}', 'woolentor' ),
1586
+ 'class' => 'woolentor-action-field-left',
1587
+ ),
1588
+
1589
+ )
1590
+
1591
+ ),
1592
+
1593
  array(
1594
  'name' => 'wishlist',
1595
  'label' => esc_html__( 'Wishlist', 'woolentor' ),
includes/modules/backorder/assets/css/backorder-admin.css ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ .woolentor-backorder-fields p{
2
+ display: none;
3
+ }
4
+ .wl_manage_stock--no.wl_stock_status--onbackorder p,
5
+ .wl_manage_stock--yes.wl_allow_backorder--yes p,
6
+ .wl_manage_stock--yes.wl_allow_backorder--notify p{
7
+ display: block;
8
+ }
9
+ #_woolentor_backorder_availability_date{
10
+ float: left;
11
+ }
includes/modules/backorder/assets/css/backorder.css ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
1
+ td.product-name .backorder_notification{
2
+ display: none;
3
+ }
4
+ td.product-name .woolentor-backorder-notification.backorder_notification{
5
+ display: block;
6
+ }
includes/modules/backorder/assets/js/backorder-admin.js ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ;(function($){
2
+ "use strict";
3
+
4
+ // Stock options.
5
+ $(document).ready(function(){
6
+ // On change manage stock checkbox
7
+ $( '#inventory_product_data input#_manage_stock' ).on( 'change', function() {
8
+ if ( $( this ).is( ':checked' ) ) {
9
+ $('#inventory_product_data .woolentor-backorder-fields').removeClass('wl_manage_stock--no').addClass('wl_manage_stock--yes');
10
+ } else {
11
+ $('#inventory_product_data .woolentor-backorder-fields').removeClass('wl_manage_stock--yes').addClass('wl_manage_stock--no');
12
+ }
13
+ });
14
+
15
+ // On change stock status
16
+ $( '#inventory_product_data select#_stock_status' ).on( 'change', function() {
17
+ if( $(this).val() == 'onbackorder' ){
18
+ $('#inventory_product_data .woolentor-backorder-fields').addClass( 'wl_stock_status--onbackorder' );
19
+ } else{
20
+ $('#inventory_product_data .woolentor-backorder-fields').removeClass('wl_stock_status--onbackorder' );
21
+ }
22
+ });
23
+
24
+ // On change allow backorder
25
+ $( '#inventory_product_data select#_backorders' ).on( 'change', function() {
26
+ $('#inventory_product_data .woolentor-backorder-fields').removeClass('wl_allow_backorder--yes wl_allow_backorder--notify wl_allow_backorder--no');
27
+ $('#inventory_product_data .woolentor-backorder-fields').addClass('wl_allow_backorder--' + $(this).val());
28
+ });
29
+ });
30
+
31
+ })(jQuery);
includes/modules/backorder/class.backorder.php ADDED
@@ -0,0 +1,481 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3
+
4
+ class Woolentor_Backorder extends WC_Product{
5
+
6
+ private static $_instance = null;
7
+
8
+ /**
9
+ * Get Instance
10
+ */
11
+ public static function get_instance(){
12
+ if( is_null( self::$_instance ) ){
13
+ self::$_instance = new self();
14
+ }
15
+ return self::$_instance;
16
+ }
17
+
18
+ /**
19
+ * Constructor
20
+ */
21
+ function __construct(){
22
+
23
+ // Frontend scripts
24
+ add_action('wp_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
25
+
26
+ // Admin scripts
27
+ add_action('admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] );
28
+
29
+ // Save order meta data while placing order
30
+ add_action( 'woocommerce_checkout_create_order_line_item', [ $this, 'line_item_save' ], 99, 4 );
31
+
32
+ // Through limit notice for product details page
33
+ add_filter( 'woocommerce_add_cart_item_data', [ $this, 'render_single_product_notice' ], 99, 4 );
34
+
35
+ // Through limit notice for cart page
36
+ add_action('woocommerce_check_cart_items', [ $this, 'check_cart_item_backorder_limit' ] );
37
+
38
+ // Add and save meta fields
39
+ add_action( 'woocommerce_product_options_stock_status', [ $this, 'add_product_meta_fields' ] );
40
+ add_action( 'woocommerce_process_product_meta', [ $this, 'save_product_metabox'], 10, 2 );
41
+
42
+ // Render backorder availability text on product page
43
+ add_filter('woocommerce_get_availability_text', [ $this, 'filter_get_availability_text'], 10, 2 );
44
+
45
+ // Render backorder label to the cart page
46
+ add_action('woocommerce_after_cart_item_name', [ $this, 'render_backorder_availability_cart_page'], 10, 2 );
47
+
48
+ // Meta data display
49
+ add_filter( 'woocommerce_order_item_get_formatted_meta_data', [ $this, 'item_get_formatted_meta_data' ], 10, 4 );
50
+
51
+ }
52
+
53
+
54
+ /**
55
+ * Enqueue scripts
56
+ */
57
+ public function enqueue_scripts(){
58
+ if( is_cart() ){
59
+ wp_enqueue_style( 'woolentor-backorder', plugin_dir_url( __FILE__ ) . 'assets/css/backorder.css', '', WOOLENTOR_VERSION, 'all' );
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Enqueue scripts admin
65
+ */
66
+ public function admin_enqueue_scripts(){
67
+ global $typenow;
68
+
69
+ if( $typenow == 'product' ){
70
+ wp_enqueue_style( 'woolentor-backorder-admin', plugin_dir_url( __FILE__ ) . 'assets/css/backorder-admin.css', '', WOOLENTOR_VERSION, 'all' );
71
+ wp_enqueue_script( 'woolentor-backorder-admin', plugin_dir_url( __FILE__ ) . 'assets/js/backorder-admin.js', array('jquery'), WOOLENTOR_VERSION, true );
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Save line items custom metadata
77
+ * Line items refers to the individual item of an order
78
+ */
79
+ public function line_item_save( $item, $cart_item_key, $values, $order ) {
80
+ $product = $values['data'];
81
+
82
+ if( $product->is_on_backorder($values['quantity']) ){
83
+ $backorder_qty = $values['quantity'] - max( 0, $product->get_stock_quantity() );
84
+ $item->add_meta_data( 'woolentor_backordered', $backorder_qty, true );
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Manage Order Item in thank you page || admin order page
90
+ *
91
+ * @param [array] $formatted_meta
92
+ * @param [type] $item
93
+ * @return void
94
+ */
95
+ public function item_get_formatted_meta_data( $formatted_meta, $item ) {
96
+
97
+ foreach ( $formatted_meta as $key => $meta ) {
98
+ if ( $meta->key == 'woolentor_backordered' ) {
99
+ $meta->display_key = esc_html__('Backordered','woolentor');
100
+ }
101
+ }
102
+
103
+ return $formatted_meta;
104
+ }
105
+
106
+ /**
107
+ * Looks through the cart to check each item is within backorder limit. If not, add an error.
108
+ */
109
+ public function check_cart_item_backorder_limit() {
110
+ $result = true;
111
+ $error = new WP_Error();
112
+
113
+ foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
114
+ $product_data = $values['data'];
115
+ $variation_id = '';
116
+ $product_id = '';
117
+
118
+ if( $product_data->is_type('variation') ){
119
+ $variation_id = $product_data->get_id();
120
+ } else {
121
+ $product_id = $product_data->get_id();
122
+ }
123
+
124
+ if(
125
+ !$product_data->managing_stock() && $product_data->get_stock_status() == 'onbackorder' ||
126
+ $product_data->managing_stock() && $product_data->backorders_allowed()
127
+ ){
128
+
129
+ $limit_status = $this->get_limit_crossed_status( $product_id, $variation_id, 0 );
130
+
131
+ if( $limit_status ){
132
+ $can_buy_max = ((int) $limit_status['backorder_limit'] + (int) $limit_status['stock_qty']) - (int) $limit_status['qty_already_backordered'];
133
+
134
+ $error->add( 'woolentor_out_of_backorder_limit', sprintf( __( 'Sorry, "%s" has reached its maximum backorder limit. Orders can be placed for up to <b>%s</b> units.', 'woolentor' ), $product_data->get_name(), $can_buy_max ) );
135
+
136
+ $result = $error;
137
+ }
138
+ }
139
+ }
140
+
141
+
142
+ if ( is_wp_error( $result ) ) {
143
+ wc_add_notice( $result->get_error_message(), 'error' );
144
+ $return = false;
145
+ }
146
+
147
+ return $result;
148
+ }
149
+
150
+ /**
151
+ * Render backorder limit notice for single product page
152
+ */
153
+ public function render_single_product_notice( $cart_item_data, $product_id, $variation_id, $quantity ){
154
+ $product_id = absint( $product_id );
155
+ $variation_id = absint( $variation_id );
156
+
157
+ // Ensure we don't add a variation to the cart directly by variation ID.
158
+ if ( 'product_variation' === get_post_type( $product_id ) ) {
159
+ $variation_id = $product_id;
160
+ $product_id = wp_get_post_parent_id( $variation_id );
161
+ }
162
+
163
+ $product_data = wc_get_product( $variation_id ? $variation_id : $product_id );
164
+
165
+ $quantity = apply_filters( 'woocommerce_add_to_cart_quantity', $quantity, $product_id );
166
+
167
+ if(
168
+ !$product_data->managing_stock() && $product_data->get_stock_status() == 'onbackorder' ||
169
+ $product_data->managing_stock() && $product_data->backorders_allowed()
170
+ ){
171
+ $limit_status = $this->get_limit_crossed_status($product_id, $variation_id, $quantity);
172
+
173
+ if($limit_status ){
174
+
175
+ $backorder_limit = (int) $limit_status['backorder_limit'];
176
+ $add_to_cart_qty = $quantity;
177
+ $stock_qty = (int) $limit_status['stock_qty'];
178
+ $qty_already_backordered = (int) $limit_status['qty_already_backordered'];
179
+ $qty_already_on_cart = (int) $limit_status['qty_on_cart'];
180
+
181
+ $can_add_to_cart_max = ($backorder_limit + $stock_qty) - ($qty_already_backordered + $qty_already_on_cart);
182
+ $can_add_to_cart_max = $can_add_to_cart_max < 0 ? 0 : $can_add_to_cart_max;
183
+
184
+ // If this product already has on cart && user allowed add on "Cart" at least 1 qty
185
+ if( $qty_already_on_cart > 0 ){
186
+
187
+ $message = sprintf(
188
+ '<a href="%s" class="button wc-forward">%s</a> %s',
189
+ wc_get_cart_url(),
190
+ __( 'View cart', 'woolentor' ),
191
+ /* translators: 1: quantity in stock 2: current quantity */
192
+ sprintf( __( 'Sorry, "%s" has reached its maximum backorder limit — (%s available). You already have %s in your cart.', 'woolentor' ), $product_data->get_name(), $can_add_to_cart_max, $qty_already_on_cart )
193
+ );
194
+
195
+ $message = apply_filters( 'wlbackorder_cart_product_not_enough_stock_already_in_cart_message', $message, $product_data, $can_add_to_cart_max, $qty_already_on_cart );
196
+
197
+ } else {
198
+ $message = sprintf( __( 'Sorry, "%s" was not added to cart because it has reached the maximum backorder limit. (%s available).', 'woolentor' ), $product_data->get_name(), $can_add_to_cart_max );
199
+
200
+ $message = apply_filters( 'wlbackorder_cart_product_not_enough_stock_message', $message, $product_data, $can_add_to_cart_max );
201
+ }
202
+
203
+
204
+ throw new Exception( $message );
205
+ }
206
+
207
+ }
208
+
209
+ return $quantity;
210
+ }
211
+
212
+ /**
213
+ * Look for the respected product into the cart and get the total quantities if it is available to the cart table
214
+ */
215
+ public function get_qty_in_cart( $product_id = '', $variation_id = '' ){
216
+ $product_id = absint( $product_id );
217
+ $variation_id = absint( $variation_id );
218
+
219
+ $p_id = $variation_id ? $variation_id : $product_id;
220
+ $product_data = wc_get_product($p_id);
221
+
222
+ if( $product_data->is_type('variation') && $product_data->managing_stock() == 'parent' ){
223
+ $p_id = $product_data->get_parent_id();
224
+ }
225
+
226
+ $products_qty_in_cart = WC()->cart->get_cart_item_quantities();
227
+ $qty_in_cart = !empty($products_qty_in_cart[$p_id]) ? $products_qty_in_cart[$p_id] : 0;
228
+
229
+ return $qty_in_cart;
230
+ }
231
+
232
+ /**
233
+ * Look for the respected product if it is already reached the backorder limit
234
+ */
235
+ public function get_limit_crossed_status( $product_id = '', $variation_id = '', $qty = 0 ){
236
+ $product_id = absint( $product_id );
237
+ $variation_id = absint( $variation_id );
238
+
239
+ // Ensure we don't add a variation to the cart directly by variation ID.
240
+ if ( 'product_variation' === get_post_type( $product_id ) ) {
241
+ $variation_id = $product_id;
242
+ $product_id = wp_get_post_parent_id( $variation_id );
243
+ }
244
+
245
+ $p_id = $variation_id ? $variation_id : $product_id;
246
+ $product_data = wc_get_product( $p_id );
247
+ $backorder_limit = (int) $this->get_option('backorder_limit', $product_id, $variation_id );
248
+
249
+ // Get the order count of a product/variation product
250
+ $qty_already_backordered = (int) $this->get_total_qty_already_backordered( $product_id, $variation_id );
251
+
252
+ // Get qty count from the cart page
253
+ $qty_on_cart = (int) $this->get_qty_in_cart( $product_id, $variation_id );
254
+
255
+ // Sum with qty_already_backordered and qty_in_cart
256
+ $qunatities = $qty + $qty_already_backordered + $qty_on_cart;
257
+
258
+ // Deduct stock qty
259
+ $stock_qty = $product_data->get_stock_quantity() > 0 ? $product_data->get_stock_quantity() : 0;
260
+
261
+ if( $product_data->is_on_backorder($qty + $qty_on_cart) && $stock_qty ){
262
+ $qunatities = $qunatities - $stock_qty;
263
+ }
264
+
265
+ if( $qunatities > $backorder_limit ){
266
+ return array(
267
+ 'status' => true,
268
+ 'backorder_limit' => $backorder_limit,
269
+ 'qty_already_backordered' => $qty_already_backordered,
270
+ 'qty_on_cart' => $qty_on_cart,
271
+ 'stock_qty' => $stock_qty
272
+ );
273
+ }
274
+
275
+ return false;
276
+ }
277
+
278
+ /**
279
+ * Loop trhough the orders and get the total quantities ordered of the respected product
280
+ */
281
+ public function get_total_qty_already_backordered( $product_id = '', $variation_id = '' ){
282
+ $product_id = absint( $product_id );
283
+ $variation_id = absint( $variation_id );
284
+ $p_id = $variation_id ? $variation_id : $product_id;
285
+
286
+ $qty_already_backordered = 0;
287
+
288
+ $status = wc_get_order_statuses();
289
+ unset($status['wc-pending']);
290
+
291
+ $limit = apply_filters('wlbackorder_order_query_limit', 250);
292
+
293
+ $query = new WC_Order_Query( array(
294
+ 'status' => array('wc-pending', 'wc-processing', 'wc-on-hold', 'wc-completed'),
295
+ 'limit' => $limit,
296
+ 'orderby' => 'date',
297
+ 'order' => 'DESC',
298
+ ) );
299
+ $orders = $query->get_orders();
300
+
301
+ // Loop through all orders
302
+ foreach($orders as $order){
303
+ $line_items = $order->get_items();
304
+
305
+ // Loop throgh on the current order items
306
+ foreach($line_items as $item){
307
+ $item_p_id = $item->get_variation_id() ? $item->get_variation_id() : $item->get_product_id();
308
+
309
+ if( $p_id == $item_p_id ){
310
+
311
+ // check if this line item is under woolentor backorder
312
+ if($item->meta_exists('woolentor_backordered') && $item->get_meta('woolentor_backordered')){
313
+ $backorder_qty = (int) $item->get_meta('woolentor_backordered');
314
+ $qty_already_backordered += $backorder_qty;
315
+ break;
316
+ }
317
+ }
318
+ }
319
+ }
320
+
321
+ return $qty_already_backordered;
322
+ }
323
+
324
+ /**
325
+ * Generate and return backorder availability message
326
+ */
327
+ public function get_availability_message( $product_id ){
328
+ $availability_date = $this->get_option('backorder_availability_date', $product_id);
329
+ $timestamp = strtotime($availability_date);
330
+ if($timestamp){
331
+ $availability_date = date(get_option('date_format'), $timestamp);
332
+ }
333
+
334
+ $backorder_limit = $this->get_option('backorder_limit', $product_id);
335
+
336
+ $availability_message = woolentor_get_option('backorder_availability_message', 'woolentor_backorder_settings');
337
+ $availability_message = str_replace( '{availability_date}', '<span class="woolentor-backorder-availability">'.$availability_date.'</span>', $availability_message );
338
+
339
+ if( $backorder_limit && $availability_message ){
340
+ $availability_message = $availability_message;
341
+ } elseif( $backorder_limit && $availability_date && empty($availability_message) ){
342
+ $availability_message = __( 'On Backorder. Will be available on: '. $availability_date, 'woolentor' );
343
+ }
344
+
345
+ return $availability_message;
346
+ }
347
+
348
+ /**
349
+ * Render the backorder availability message
350
+ */
351
+ public function filter_get_availability_text( $availability, $product ){
352
+
353
+ $product_id = $product->get_id();
354
+ if( $product->is_type('variation') ){
355
+ $product_id = $product->get_parent_id();
356
+ }
357
+
358
+ $availability_message = $this->get_availability_message( $product_id ) ? $this->get_availability_message( $product_id ) : $availability;
359
+
360
+ if ( $product->managing_stock() && $product->is_on_backorder( 1 ) ) {
361
+ $availability = $product->backorders_require_notification() ? $availability_message : '';
362
+ } elseif ( ! $product->managing_stock() && $product->is_on_backorder( 1 ) ) {
363
+ $availability = $availability_message;
364
+ }
365
+
366
+ return $availability;
367
+ }
368
+
369
+ /**
370
+ * Render the backorder availability message on cart page
371
+ */
372
+ public function render_backorder_availability_cart_page( $cart_item, $cart_item_key ){
373
+ $product_data = $cart_item['data'];
374
+
375
+ if($product_data->is_type('simple')){
376
+ $product_id = $product_data->get_id();
377
+ } elseif( $product_data->is_type('variation') ){
378
+ $product_id = $product_data->get_parent_id();
379
+ }
380
+
381
+ if( $product_data->is_on_backorder() ){
382
+ echo '<p class="woolentor-backorder-notification backorder_notification">';
383
+ echo wp_kses_post($this->get_availability_message( $product_id ));
384
+ echo '</p>';
385
+ }
386
+ }
387
+
388
+
389
+ /**
390
+ * Get the option value either from metadata or the global settings
391
+ */
392
+ public function get_option( $option_name = '', $product_id = '', $variation_id = '' ){
393
+
394
+ $product_id = absint( $product_id );
395
+ $variation_id = absint( $variation_id );
396
+ $p_id = $variation_id ? $variation_id : $product_id;
397
+
398
+ $product_data = wc_get_product( $p_id );
399
+ if( $product_data->is_type('variation') ){
400
+ $product_id = $product_data->get_parent_id();
401
+ }
402
+
403
+ $global_settings_value = woolentor_get_option( $option_name, 'woolentor_backorder_settings');
404
+ $meta_value = get_post_meta( $product_id, '_woolentor_'. $option_name, true );
405
+
406
+ if( $meta_value ){
407
+ $global_settings_value = $meta_value;
408
+ }
409
+
410
+ return $global_settings_value;
411
+ }
412
+
413
+ /**
414
+ * Render backorder meta fields into the inventory tab
415
+ */
416
+ public function add_product_meta_fields(){
417
+ $product_id = get_the_id();
418
+ $product = wc_get_product($product_id);
419
+
420
+ $backorder_limit_global = woolentor_get_option('backorder_limit', 'woolentor_backorder_settings');
421
+ $backorder_limit_global = $backorder_limit_global ? __( "Store-wide backorder limit ($backorder_limit_global)", "woolentor") : '';
422
+
423
+ $availability_date_global = woolentor_get_option('backorder_availability_date', 'woolentor_backorder_settings');
424
+ $availability_date_global = $availability_date_global ? __( "Store-wide availability ($availability_date_global)", "woolentor") : '';
425
+
426
+ $backorder_limit = get_post_meta( $product_id, '_woolentor_backorder_limit', true );
427
+ $backorder_availability = get_post_meta( $product_id, '_woolentor_backorder_availability_date', true );
428
+
429
+ // Stock status
430
+ $manage_stock = get_post_meta($product_id, '_manage_stock', true);
431
+ $stock_status = $product->is_type('simple') ? get_post_meta($product_id, '_stock_status', true) : '';
432
+ $allow_backorder = get_post_meta($product_id, '_backorders', true);
433
+ ?>
434
+
435
+ <div class="woolentor-backorder-fields show_if_simple show_if_variable wl_manage_stock--<?php echo esc_attr($manage_stock); ?> wl_stock_status--<?php echo esc_attr($stock_status); ?> wl_allow_backorder--<?php echo esc_attr($allow_backorder); ?>">
436
+
437
+ <?php
438
+ woocommerce_wp_text_input(
439
+ array(
440
+ 'id' => '_woolentor_backorder_limit',
441
+ 'value' => $backorder_limit,
442
+ 'label' => __( 'Backorder Limit', 'woolentor' ),
443
+ 'placeholder' => $backorder_limit_global,
444
+ 'wrapper_class' => '',
445
+ 'desc_tip' => true,
446
+ 'description' => __( 'Backorder limit. If this is a variable product this value will be used to control backorder limit for all variations, unless you define backorder limit at variation level.', 'woolentor' ),
447
+ 'type' => 'number',
448
+ 'custom_attributes' => array(
449
+ 'step' => 'any',
450
+ ),
451
+ )
452
+ );
453
+ ?>
454
+ <p class="form-field">
455
+ <label for="_woolentor_backorder_availability_date"><?php echo esc_html__('Backorder Availability', 'woolentor') ?></label>
456
+ <?php echo wc_help_tip( 'The selected date will show as a message to customer. You can customize the message as you need from the module settings.', 'woolentor' ); ?>
457
+ <input type="date" class="short hasDatepicker" name="_woolentor_backorder_availability_date" id="_woolentor_backorder_availability_date" value="<?php echo esc_attr($backorder_availability); ?>" placeholder="<?php echo esc_attr($availability_date_global); ?>">
458
+ </p>
459
+ </div> <!-- .woolentor-backorder-fields -->
460
+ <?php
461
+ }
462
+
463
+ /**
464
+ * Save backorder meta fields into the inventory tab
465
+ */
466
+ public function save_product_metabox( $post_id, $post ){
467
+ $posted_data = wp_unslash( $_REQUEST );
468
+ $backorder_limit = $posted_data['_woolentor_backorder_limit'];
469
+ $backorder_availability = $posted_data['_woolentor_backorder_availability_date'];
470
+
471
+ if( isset($posted_data['_woolentor_backorder_limit']) ){
472
+ update_post_meta( $post_id, '_woolentor_backorder_limit', $backorder_limit );
473
+ }
474
+
475
+ if( isset($posted_data['_woolentor_backorder_availability_date']) ){
476
+ update_post_meta( $post_id, '_woolentor_backorder_availability_date', $backorder_availability );
477
+ }
478
+ }
479
+ }
480
+
481
+ Woolentor_Backorder::get_instance();
includes/modules/class.module-manager.php CHANGED
@@ -54,6 +54,11 @@ class Woolentor_Module_Manager{
54
  require_once( WOOLENTOR_ADDONS_PL_PATH .'includes/modules/flash-sale/class.flash-sale.php' );
55
  }
56
 
 
 
 
 
 
57
  if( is_plugin_active('woolentor-addons-pro/woolentor_addons_pro.php') ){
58
 
59
  // Partial payment
54
  require_once( WOOLENTOR_ADDONS_PL_PATH .'includes/modules/flash-sale/class.flash-sale.php' );
55
  }
56
 
57
+ // Backorder
58
+ if( woolentor_get_option( 'enable', 'woolentor_backorder_settings', 'off' ) == 'on' ){
59
+ require_once( WOOLENTOR_ADDONS_PL_PATH .'includes/modules/backorder/class.backorder.php' );
60
+ }
61
+
62
  if( is_plugin_active('woolentor-addons-pro/woolentor_addons_pro.php') ){
63
 
64
  // Partial payment
includes/modules/shopify-like-checkout/class.shopify-like-checkout.php CHANGED
@@ -68,12 +68,12 @@ class Woolentor_Shopify_Like_Checkout extends \WC_Checkout{
68
  }
69
 
70
  // Styles
71
- wp_enqueue_style( 'woolentor-shopify-like-checkout', plugin_dir_url( __FILE__ ) . '/assets/shopify-like-checkout.css' ,'', WOOLENTOR_VERSION, 'all' );
72
 
73
  // Scripts
74
  $suffix = Automattic\Jetpack\Constants::is_true( 'SCRIPT_DEBUG' ) ? '' : '.min';
75
  wp_enqueue_script( 'serializejson', WC()->plugin_url() . '/assets/js/jquery-serializejson/jquery.serializejson' . $suffix . '.js', array( 'jquery' ), '2.8.1' );
76
- wp_enqueue_script( 'woolentor-shopify-like-checkout', plugin_dir_url( __FILE__ ) . '/assets/shopify-like-checkout.js', array('jquery', 'wc-checkout'), WOOLENTOR_VERSION, 'all' );
77
  wp_localize_script( 'woolentor-shopify-like-checkout', 'woolentor_slc_params',
78
  array(
79
  'ajax_url' => admin_url( 'admin-ajax.php' ),
68
  }
69
 
70
  // Styles
71
+ wp_enqueue_style( 'woolentor-shopify-like-checkout', plugin_dir_url( __FILE__ ) . 'assets/shopify-like-checkout.css' ,'', WOOLENTOR_VERSION, 'all' );
72
 
73
  // Scripts
74
  $suffix = Automattic\Jetpack\Constants::is_true( 'SCRIPT_DEBUG' ) ? '' : '.min';
75
  wp_enqueue_script( 'serializejson', WC()->plugin_url() . '/assets/js/jquery-serializejson/jquery.serializejson' . $suffix . '.js', array( 'jquery' ), '2.8.1' );
76
+ wp_enqueue_script( 'woolentor-shopify-like-checkout', plugin_dir_url( __FILE__ ) . 'assets/shopify-like-checkout.js', array('jquery', 'wc-checkout'), WOOLENTOR_VERSION, 'all' );
77
  wp_localize_script( 'woolentor-shopify-like-checkout', 'woolentor_slc_params',
78
  array(
79
  'ajax_url' => admin_url( 'admin-ajax.php' ),
readme.txt CHANGED
@@ -3,15 +3,20 @@ Contributors: hasthemes, htplugins, devitemsllc, tarekht
3
  Tags: Elementor, WooCommerce, WooCommerce Elementor, WooCommerce Builder, WooCommerce Product
4
  Requires at least: 4.7
5
  Tested up to: 5.9
6
- Stable tag: 2.1.7
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
10
  All-in-one solution to enhance your WooCommerce website including WooCommerce Elementor Addons, splendid features, and a WooCommerce builder for Elementor.
11
 
12
  == Description ==
13
- WooLentor is a WooCommerce Addons plugin for Elementor Page Builder. WooCommerce Builder is included in this plugin to build custom product page and archive pages.
14
- Equipped with 42 product layouts, awesome style options, it can show latest products, Best Selling products, On Sale Products, Featured products, Category products. This plugin has the options to add unlimited background colors and images to make your product slider/Tab more professional.
 
 
 
 
 
15
 
16
  [Live Demo](https://woolentor.com/?utm_source=wprepo&utm_medium=freeplugin&utm_campaign=demo) | [Documentation](https://woolentor.com/documentation/?utm_source=wprepo&utm_medium=freeplugin&utm_campaign=doc) | [Purchase Pro](https://woolentor.com/pricing/?utm_source=wprepo&utm_medium=freeplugin&utm_campaign=purchasepro)
17
 
@@ -242,6 +247,9 @@ Flash Sale Countdown module allows you to show discounts available for a limited
242
  <strong>[Shopify Style Checkout Page in WooCommerce](https://woolentor.com/shopify-style-checkout-page-in-woocommerce/)</strong>
243
  When it comes to the Checkout page, customers always prefer a clean and simple one rather than a complicated one with lots of fields. A lengthy Checkout form can even increase cart abandonment while also negatively affecting the conversion rates. With this in mind, we have added a fantastic feature, which will enable you to create a Shopify style Checkout page that is pretty straightforward. Furthermore, it will provide the customers with an amazing Checkout experience.
244
 
 
 
 
245
  <strong>Multi-Step Checkout (Pro)</strong>
246
  The checkout process is the most important step in your customer’s journey, and it should be as easy and seamless as possible. That’s why we created our Multi-Step Checkout feature that allows you to create a more effective and organized checkout page by dividing the process into several simpler steps.
247
 
@@ -337,6 +345,10 @@ Elementor Pro is not required. But you can use wooLentor with Elementor free & P
337
 
338
  == Changelog ==
339
 
 
 
 
 
340
  = Version: 2.1.7 - Date: 2022-01-24 =
341
  * Added : Wishlist and compare module.
342
  * Added : Flash Sale Addon.
3
  Tags: Elementor, WooCommerce, WooCommerce Elementor, WooCommerce Builder, WooCommerce Product
4
  Requires at least: 4.7
5
  Tested up to: 5.9
6
+ Stable tag: 2.1.8
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
10
  All-in-one solution to enhance your WooCommerce website including WooCommerce Elementor Addons, splendid features, and a WooCommerce builder for Elementor.
11
 
12
  == Description ==
13
+ Have you ever searched for a WooCommerce Addons plugin for Elementor Page Builder to build an online store that does not look ordinary like most of the stores out there? WooLentor is an all-in-one solution that will not only give you complete control over the WooCommerce page designs but also provide you with several features.
14
+
15
+ The plugin comes with a powerful WooCommerce page builder, allowing you to effortlessly design all WooCommerce pages from scratch using the Elementor page builder. As a result, you no longer have to rely on the default page designs of WooCommerce.
16
+
17
+ WooLentor gives you the flexibility to design a custom Shop and Product details page using the free version of the plugin. The rest of the pages are also customizable with the pro version. Furthermore, WooLentor offers a plethora of Elementor widgets with extensive customization options that you can leverage for creating your store as per your needs.
18
+
19
+ Apart from a large number of widgets, there are plenty of useful functionalities available in the form of modules. For example, product comparison, wishlist, quick view, Shopify style checkout, flash sale countdown, and so on. The main purpose of these modules is to offer the users as many options as users would need to enhance their website further. That way, they don't have to look for another third-party plugin just for a single feature.
20
 
21
  [Live Demo](https://woolentor.com/?utm_source=wprepo&utm_medium=freeplugin&utm_campaign=demo) | [Documentation](https://woolentor.com/documentation/?utm_source=wprepo&utm_medium=freeplugin&utm_campaign=doc) | [Purchase Pro](https://woolentor.com/pricing/?utm_source=wprepo&utm_medium=freeplugin&utm_campaign=purchasepro)
22
 
247
  <strong>[Shopify Style Checkout Page in WooCommerce](https://woolentor.com/shopify-style-checkout-page-in-woocommerce/)</strong>
248
  When it comes to the Checkout page, customers always prefer a clean and simple one rather than a complicated one with lots of fields. A lengthy Checkout form can even increase cart abandonment while also negatively affecting the conversion rates. With this in mind, we have added a fantastic feature, which will enable you to create a Shopify style Checkout page that is pretty straightforward. Furthermore, it will provide the customers with an amazing Checkout experience.
249
 
250
+ <strong>Backorder Module</strong>
251
+ You may use a Backorder module to enable your consumers to make purchases from you that you can't currently fulfill. Cross-docking strategies, such as those used by organized firms, can help them fill orders quickly once goods arrive, saving time and resources. Backorders offer flexibility to warehouse management when products take up a lot of room or when aa customer needs to pay and receive the product at a later date.
252
+
253
  <strong>Multi-Step Checkout (Pro)</strong>
254
  The checkout process is the most important step in your customer’s journey, and it should be as easy and seamless as possible. That’s why we created our Multi-Step Checkout feature that allows you to create a more effective and organized checkout page by dividing the process into several simpler steps.
255
 
345
 
346
  == Changelog ==
347
 
348
+ = Version: 2.1.8 - Date: 02-02-2022 =
349
+ * Added : Backorder Module.
350
+ * Fixed : Print notices issue in editor mode.
351
+
352
  = Version: 2.1.7 - Date: 2022-01-24 =
353
  * Added : Wishlist and compare module.
354
  * Added : Flash Sale Addon.
woolentor-blocks/src/blocks/brand-logo/index.php CHANGED
@@ -45,6 +45,7 @@ class WooLentorBlocks_Brand_Logo{
45
  register_block_type(
46
  $metadata['name'],
47
  array(
 
48
  'attributes' => $metadata['attributes'],
49
  'render_callback' => [ $this, 'render_content' ]
50
  )
45
  register_block_type(
46
  $metadata['name'],
47
  array(
48
+ 'title' => __('WL: Brand Logo', 'woolentor'),
49
  'attributes' => $metadata['attributes'],
50
  'render_callback' => [ $this, 'render_content' ]
51
  )
woolentor-blocks/src/blocks/category-grid/index.php CHANGED
@@ -45,6 +45,7 @@ class WooLentorBlocks_Category_Grid{
45
  register_block_type(
46
  $metadata['name'],
47
  array(
 
48
  'attributes' => $metadata['attributes'],
49
  'render_callback' => [ $this, 'render_content' ],
50
  'script' => 'slick'
45
  register_block_type(
46
  $metadata['name'],
47
  array(
48
+ 'title' => __('WL: Category Grid', 'woolentor'),
49
  'attributes' => $metadata['attributes'],
50
  'render_callback' => [ $this, 'render_content' ],
51
  'script' => 'slick'
woolentor-blocks/src/blocks/faq/index.php CHANGED
@@ -45,6 +45,7 @@ class WooLentorBlocks_Faq{
45
  register_block_type(
46
  $metadata['name'],
47
  array(
 
48
  'attributes' => $metadata['attributes'],
49
  'render_callback' => [ $this, 'render_content' ],
50
  'script' => 'woolentor-accordion-min',
45
  register_block_type(
46
  $metadata['name'],
47
  array(
48
+ 'title' => __('WL: FAQ', 'woolentor'),
49
  'attributes' => $metadata['attributes'],
50
  'render_callback' => [ $this, 'render_content' ],
51
  'script' => 'woolentor-accordion-min',
woolentor-blocks/src/blocks/image-marker/index.php CHANGED
@@ -45,6 +45,7 @@ class WooLentorBlocks_Image_Marker{
45
  register_block_type(
46
  $metadata['name'],
47
  array(
 
48
  'attributes' => $metadata['attributes'],
49
  'render_callback' => [ $this, 'render_content' ]
50
  )
45
  register_block_type(
46
  $metadata['name'],
47
  array(
48
+ 'title' => __('WL: Image Marker', 'woolentor'),
49
  'attributes' => $metadata['attributes'],
50
  'render_callback' => [ $this, 'render_content' ]
51
  )
woolentor-blocks/src/blocks/product-curvy/index.php CHANGED
@@ -45,6 +45,7 @@ class WooLentorBlocks_Product_Curvy{
45
  register_block_type(
46
  $metadata['name'],
47
  array(
 
48
  'attributes' => $metadata['attributes'],
49
  'render_callback' => [ $this, 'render_content' ]
50
  )
45
  register_block_type(
46
  $metadata['name'],
47
  array(
48
+ 'title' => __('WL: Product Curvy', 'woolentor'),
49
  'attributes' => $metadata['attributes'],
50
  'render_callback' => [ $this, 'render_content' ]
51
  )
woolentor-blocks/src/blocks/product-tab/index.php CHANGED
@@ -43,18 +43,11 @@ class WooLentorBlocks_Product_Tab{
43
  register_block_type(
44
  $metadata['name'],
45
  array(
46
- 'attributes' => $metadata['attributes'],
 
47
  'render_callback' => [ $this,'product_tab_render' ],
48
  'script' => 'slick',
49
  )
50
-
51
- // array(
52
- // 'attributes' => $metadata['attributes'],
53
- // 'render_callback' => [ $this,'product_tab_render' ],
54
- // 'editor_script' => 'slick',
55
- // 'editor_style' => 'woolentor-widgets',
56
- // 'script' => 'slick',
57
- // )
58
  );
59
 
60
  }
43
  register_block_type(
44
  $metadata['name'],
45
  array(
46
+ 'title' => __('WL: Product Tab', 'woolentor'),
47
+ 'attributes' => $metadata['attributes'],
48
  'render_callback' => [ $this,'product_tab_render' ],
49
  'script' => 'slick',
50
  )
 
 
 
 
 
 
 
 
51
  );
52
 
53
  }
woolentor-blocks/src/blocks/special-day-offer/index.php CHANGED
@@ -45,7 +45,8 @@ class WooLentorBlocks_Special_Day_Offer_Banner{
45
  register_block_type(
46
  $metadata['name'],
47
  array(
48
- 'attributes' => $metadata['attributes'],
 
49
  'render_callback' => [ $this, 'render_content' ],
50
  // 'editor_style' => 'woolentor-widgets',
51
  )
45
  register_block_type(
46
  $metadata['name'],
47
  array(
48
+ 'title' => __('WL: Special Day Offer', 'woolentor'),
49
+ 'attributes' => $metadata['attributes'],
50
  'render_callback' => [ $this, 'render_content' ],
51
  // 'editor_style' => 'woolentor-widgets',
52
  )
woolentor-blocks/src/blocks/store-feature/index.php CHANGED
@@ -45,7 +45,8 @@ class WooLentorBlocks_Store_Feature{
45
  register_block_type(
46
  $metadata['name'],
47
  array(
48
- 'attributes' => $metadata['attributes'],
 
49
  'render_callback' => [ $this, 'render_content' ]
50
  )
51
  );
45
  register_block_type(
46
  $metadata['name'],
47
  array(
48
+ 'title' => __('WL: Store Feature', 'woolentor'),
49
+ 'attributes' => $metadata['attributes'],
50
  'render_callback' => [ $this, 'render_content' ]
51
  )
52
  );
woolentor_addons_elementor.php CHANGED
@@ -3,7 +3,7 @@
3
  * Plugin Name: WooLentor - WooCommerce Elementor Addons + Builder
4
  * Description: The WooCommerce elements library for Elementor page builder plugin for WordPress.
5
  * Plugin URI: https://woolentor.com/
6
- * Version: 2.1.7
7
  * Author: HasThemes
8
  * Author URI: https://hasthemes.com/plugins/woolentor-pro/
9
  * License: GPL-2.0+
@@ -11,13 +11,13 @@
11
  * Text Domain: woolentor
12
  * Domain Path: /languages
13
  * WC tested up to: 6.1.1
14
- * Elementor tested up to: 3.5.3
15
  * Elementor Pro tested up to: 3.5.2
16
  */
17
 
18
  if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
19
 
20
- define( 'WOOLENTOR_VERSION', '2.1.7' );
21
  define( 'WOOLENTOR_ADDONS_PL_ROOT', __FILE__ );
22
  define( 'WOOLENTOR_ADDONS_PL_URL', plugins_url( '/', WOOLENTOR_ADDONS_PL_ROOT ) );
23
  define( 'WOOLENTOR_ADDONS_PL_PATH', plugin_dir_path( WOOLENTOR_ADDONS_PL_ROOT ) );
3
  * Plugin Name: WooLentor - WooCommerce Elementor Addons + Builder
4
  * Description: The WooCommerce elements library for Elementor page builder plugin for WordPress.
5
  * Plugin URI: https://woolentor.com/
6
+ * Version: 2.1.8
7
  * Author: HasThemes
8
  * Author URI: https://hasthemes.com/plugins/woolentor-pro/
9
  * License: GPL-2.0+
11
  * Text Domain: woolentor
12
  * Domain Path: /languages
13
  * WC tested up to: 6.1.1
14
+ * Elementor tested up to: 3.5.4
15
  * Elementor Pro tested up to: 3.5.2
16
  */
17
 
18
  if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
19
 
20
+ define( 'WOOLENTOR_VERSION', '2.1.8' );
21
  define( 'WOOLENTOR_ADDONS_PL_ROOT', __FILE__ );
22
  define( 'WOOLENTOR_ADDONS_PL_URL', plugins_url( '/', WOOLENTOR_ADDONS_PL_ROOT ) );
23
  define( 'WOOLENTOR_ADDONS_PL_PATH', plugin_dir_path( WOOLENTOR_ADDONS_PL_ROOT ) );