WooCommerce_Product_Vendors::__construct()   B
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 97
Code Lines 50

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 50
nc 1
nop 1
dl 0
loc 97
ccs 0
cts 51
cp 0
crap 2
rs 8.3604
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 21 and the first side effect is on line 12.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * Bootstraps the Vendor system
4
 *
5
 * @package     PrintCenter\Vendor
6
 * @since       1.0.0
7
 */
8
9
10
// Exit if accessed directly
11
if( ! defined( 'ABSPATH' ) ) {
12
	exit;
13
}
14
15
16
/**
17
 * The main WooCommerce_Product_Vendors class
18
 *
19
 * @since       1.0.0
20
 */
21
class WooCommerce_Product_Vendors {
22
23
24
	/**
25
	 * @access      private
26
	 * @since       1.0.0
27
	 * @var         string $dir The directory containing this file
28
	 */
29
	private $dir;
30
31
32
	/**
33
	 * @access      private
34
	 * @since       1.0.0
35
	 * @var         string $file This file
36
	 */
37
	private $file;
38
39
40
	/**
41
	 * @access      private
42
	 * @since       1.0.0
43
	 * @var         string $assets_dir The directory containing the vendor assets
44
	 */
45
	private $assets_dir;
46
47
48
	/**
49
	 * @access      private
50
	 * @since       1.0.0
51
	 * @var         string $assets_url The URL of the directory containing the vendor assets
52
	 */
53
	private $assets_url;
54
55
56
	/**
57
	 * @access      public
58
	 * @since       1.0.0
59
	 * @var         string $token The prefix for vendor related hooks
60
	 */
61
	public $token;
62
63
64
	/**
65
	 * Get things started
66
	 *
67
	 * @access      public
68
	 * @since       1.0.0
69
	 * @param       string $file This file
70
	 * @return      void
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

Loading history...
71
	 */
72
	public function __construct( $file ) {
73
		$this->dir = dirname( $file );
74
		$this->file = $file;
75
		$this->assets_dir = trailingslashit( $this->dir ) . 'assets';
76
		$this->assets_url = esc_url( trailingslashit( plugins_url( '/assets/', $file ) ) );
77
		$this->token = 'shop_vendor';
78
79
		// Register new taxonomy
80
		add_action( 'init', array( $this, 'register_vendors_taxonomy' ) );
81
82
		// Process commissions for order
83
		add_action( 'woocommerce_order_status_completed', array( $this, 'process_commissions' ), 10, 1 );
84
		add_action( 'woocommerce_order_status_refunded', array( $this, 'cancel_commission' ), 10, 1 );
85
		add_action( 'woocommerce_order_status_cancelled', array( $this, 'cancel_commission' ), 10, 1 );
86
87
		// Allow vendors access to the WP dashboard
88
		add_filter( 'woocommerce_prevent_admin_access', array( $this, 'allow_vendor_admin_access' ) );
89
90
		// Vendor report: Total earnings
91
		add_shortcode( 'product_vendors_total_earnings', array( $this, 'vendor_total_earnings_report' ) );
92
93
		// Vendor report: This month's earnings
94
		add_shortcode( 'product_vendors_month_earnings', array( $this, 'vendor_month_earnings' ) );
95
96
		// Setup vendor shop page (taxonomy archive)
97
		add_action( 'template_redirect', array( $this, 'load_product_archive_template' ) );
98
		add_filter( 'body_class', array( $this, 'set_product_archive_class' ) );
99
		add_action( 'woocommerce_archive_description', array( $this, 'product_archive_vendor_info' ) );
100
101
		// Handle commission setting on product edit page
102
		add_action( 'woocommerce_product_options_general_product_data', array( $this, 'add_product_settings' ) );
103
		add_action( 'woocommerce_process_product_meta', array( $this, 'process_product_settings' ), 10, 2 );
104
		add_action( 'woocommerce_product_after_variable_attributes', array( $this, 'add_variation_settings' ), 10, 2 );
105
		add_action( 'woocommerce_process_product_meta_variable', array( $this, 'process_variation_settings' ) );
106
107
		// Add fields to taxonomy
108
		add_action( $this->token . '_add_form_fields' , array( $this , 'add_vendor_fields' ) , 1 , 1 );
109
		add_action( $this->token . '_edit_form_fields' , array( $this , 'edit_vendor_fields' ) , 1 , 1 );
110
		add_action( 'edited_' . $this->token , array( $this , 'save_vendor_fields' ) , 10 , 2 );
111
		add_action( 'created_' . $this->token , array( $this , 'save_vendor_fields' ) , 10 , 2 );
112
113
		// Make vendor selection use checkboxes
114
		add_action( 'admin_menu', array( $this, 'remove_meta_box' ) );
115
		add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) );
116
		add_action( 'wp_ajax_add-' . $this->token , '_wp_ajax_add_non_hierarchical_term' );
117
118
		// Only show vendor's products in dashboard
119
		add_filter( 'request', array( $this, 'filter_product_list' ) );
120
		add_action( 'current_screen', array( $this, 'restrict_products' ) );
121
		add_filter( 'wp_count_posts', array( $this, 'list_table_product_counts' ), 10, 3 );
122
		add_filter( 'wp_count_attachments', array( $this, 'list_table_media_counts'), 10, 2 );
123
		add_filter( 'views_upload', array( $this, 'remove_unattached_attachments' ), 10, 1 );
124
125
		// Handle saving posts for vendors
126
		add_action( 'save_post', array( $this, 'add_vendor_to_product' ) );
127
128
		// Add pages to menu
129
		add_action( 'admin_menu', array( $this, 'vendor_menu_items' ) );
130
131
		// Handle media library restrictions
132
		add_filter( 'request', array( $this, 'restrict_media_library' ), 10, 1 );
133
		add_filter( 'ajax_query_attachments_args', array( $this, 'restrict_media_library_modal' ), 10, 1 );
134
135
		// Save vendor details
136
		add_action( 'admin_init', array( $this, 'vendor_details_page_save' ) );
137
138
		// Integration for WooCommerce Bookings
139
		add_action( 'woocommerce_new_booking', array( $this, 'add_vendor_to_booking' ), 10, 1 );
140
		add_filter( 'get_booking_products_args', array( $this, 'restrict_booking_products' ) );
141
		add_filter( 'request', array( $this, 'filter_booking_list' ) );
142
		add_filter( 'get_bookings_in_date_range_args', array( $this, 'filter_booking_calendar' ) );
143
		add_filter( 'wp_count_posts', array( $this, 'list_table_booking_counts' ), 10, 3 );
144
145
		// Add vendor pages to WooCommerce screen IDs
146
		add_filter( 'woocommerce_screen_ids', array( $this, 'screen_ids' ), 10, 1 );
147
148
		// Add reports to WP dashboard
149
		add_filter( 'woocommerce_reports_charts', array( $this, 'add_reports' ) );
150
151
		// Display admin page notices
152
		add_action( 'admin_notices', array( $this, 'admin_notices' ) );
153
154
		// Add settings link to plugin page
155
		add_filter( 'plugin_action_links_' . plugin_basename( $this->file ), array( $this, 'add_settings_link' ) );
156
157
		// Add extension settings to WooCommerce General settings tab
158
		add_filter( 'woocommerce_general_settings', array( $this, 'add_settings' ), 10, 1 );
159
160
		// Filter products in non-vendor shop loops
161
		add_action( 'pre_get_posts', array( $this, 'hide_vendor_products' ) );
162
163
		// If Product Enquiry Form is active then send enquiries to the product vendors too
164
		add_filter( 'product_enquiry_send_to', array( $this, 'wc_product_enquiry_to_vendor' ), 10, 2 );
165
166
		// Flush rewrite rules on plugin activation
167
		register_activation_hook( $this->file, array( $this, 'rewrite_flush' ) );
168
	}
169
170
171
	/**
172
	 * Register taxonomy for vendors
173
	 *
174
	 * @access      public
175
	 * @since       1.0.0
176
	 * @return      void
177
	 */
178
	public function register_vendors_taxonomy() {
179
		$labels = array(
180
			'name'                       => __( 'Product Vendors' , 'printcenter' ),
181
			'singular_name'              => __( 'Vendor', 'printcenter' ),
182
			'menu_name'                  => __( 'Vendors' , 'printcenter' ),
183
			'search_items'               => __( 'Search Vendors' , 'printcenter' ),
184
			'all_items'                  => __( 'All Vendors' , 'printcenter' ),
185
			'parent_item'                => __( 'Parent Vendor' , 'printcenter' ),
186
			'parent_item_colon'          => __( 'Parent Vendor:' , 'printcenter' ),
187
			'view_item'                  => __( 'View Vendor' , 'printcenter' ),
188
			'edit_item'                  => __( 'Edit Vendor' , 'printcenter' ),
189
			'update_item'                => __( 'Update Vendor' , 'printcenter' ),
190
			'add_new_item'               => __( 'Add New Vendor' , 'printcenter' ),
191
			'new_item_name'              => __( 'New Vendor Name' , 'printcenter' ),
192
			'popular_items'              => __( 'Popular Vendors' , 'printcenter' ),
193
			'separate_items_with_commas' => __( 'Separate vendors with commas' , 'printcenter' ),
194
			'add_or_remove_items'        => __( 'Add or remove vendors' , 'printcenter' ),
195
			'choose_from_most_used'      => __( 'Choose from most used vendors' , 'printcenter' ),
196
			'not_found'                  => __( 'No vendors found' , 'printcenter' ),
197
		);
198
199
		$vendor_slug = apply_filters( 'product_vendors_vendor_slug', 'vendor' );
200
201
		$args = array(
202
			'public'            => true,
203
			'hierarchical'      => false,
204
			'rewrite'           => array( 'slug' => $vendor_slug ),
205
			'show_admin_column' => true,
206
			'labels'            => $labels
207
		);
208
209
		register_taxonomy( $this->token, 'product', $args );
210
	}
211
212
213
	/**
214
	 * Add vendor settings to products
215
	 *
216
	 * @access      public
217
	 * @since       1.0.0
218
	 * @return      void
219
	 */
220
	public function add_product_settings() {
221
		global $post, $woocommerce;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
222
223
		if( ! printcenter_vendor_access() ) {
224
225
			// Get current commission setting
226
			$commission = get_post_meta( $post->ID , '_product_vendors_commission', true );
227
228
			// Print out options fields
229
			$html = '<div class="options_group">
230
						<p class="form-field _product_vendors_commission_field">
231
							<label for="_product_vendors_commission">' . __( 'Vendor Commission', 'printcenter' ) . '</label>
232
							<input class="short" size="6" placeholder="0" type="number" name="_product_vendors_commission" id="_product_vendors_commission" value="' . $commission . '" />&nbsp;&nbsp;%
233
							<span class="description">' . __( 'OPTIONAL: Enter the percentage of the sale price that will go to each product vendor. If no value is entered then the vendor\'s default commission will be used.', 'printcenter' ) . '</span>
234
						</p>
235
					</div>';
236
237
			echo $html;
238
		}
239
    }
240
241
242
	/**
243
	 * Update product settings
244
	 *
245
	 * @access      public
246
	 * @since       1.0.0
247
	 * @param       int $post_id ID of product
248
	 * @param       obj $post Product post object
249
	 * @return      void
250
	 */
251
	public function process_product_settings( $post_id, $post ) {
0 ignored issues
show
Unused Code introduced by
The parameter $post is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
252
		$commission = 0;
253
254
		if( isset( $_POST['_product_vendors_commission'] ) ) {
255
			$commission = $_POST['_product_vendors_commission'];
256
		}
257
258
		update_post_meta( $post_id , '_product_vendors_commission' , $commission );
259
	}
260
261
262
	/**
263
	 * Add vendor settings to product variations
264
	 *
265
	 * @access      public
266
	 * @since       1.0.0
267
	 * @param       int $loop Current variation loop
268
	 * @param       obj $variation_data Variation data object
269
	 * @return      void
270
	 */
271
	public function add_variation_settings( $loop, $variation_data ) {
272
		$commission = isset( $variation_data['_product_vendors_commission'] ) ? $variation_data['_product_vendors_commission'] : '';
273
274
		if( isset( $commission[0] ) ) {
275
			$commission = $commission[0];
276
		}
277
278
		$html = '<tr>
279
					<td>
280
						<div class="_product_vendors_commission">
281
							<label for="_product_vendors_commission_' . $loop . '">' . __( 'Vendor Commission', 'printcenter' ) . ':</label>
282
							<input size="4" type="text" name="variable_product_vendors_commission[' . $loop . ']" id="_product_vendors_commission_' . $loop . '" value="' . $commission . '" />
283
						</div>
284
					</td>
285
				</tr>';
286
287
		echo $html;
288
	}
289
290
291
	/**
292
	 * Update product variation settings
293
	 *
294
	 * @access      public
295
	 * @since       1.0.0
296
	 * @return      void
297
	 */
298
	public function process_variation_settings() {
299
		if( isset( $_POST['variable_post_id'] ) && is_array( $_POST['variable_post_id'] ) ) {
300
			foreach( $_POST['variable_post_id'] as $k => $id ) {
301
				$commission = $_POST['variable_product_vendors_commission'][$k];
302
				update_post_meta( $id , '_product_vendors_commission' , $commission );
303
			}
304
		}
305
	}
306
307
308
	/**
309
	 * Removing default vendor meta box
310
	 *
311
	 * @access      public
312
	 * @since       1.0.0
313
	 * @return      void
314
	 */
315
	public function remove_meta_box() {
316
		remove_meta_box( 'tagsdiv-' . $this->token, 'product', 'normal' );
317
	}
318
319
320
	/**
321
	 * Add new vendor meta box
322
	 *
323
	 * @access      public
324
	 * @since       1.0.0
325
	 * @return      void
326
	 */
327
	public function add_meta_box() {
328
		if( ! printcenter_vendor_access() ) {
329
			$tax = get_taxonomy( $this->token );
330
			add_meta_box( 'tagdiv-' . $this->token, $tax->labels->name, array( $this, 'metabox_content' ), 'product', 'side', 'core' );
331
		}
332
	}
333
334
	/**
335
	 * Generate metabox content
336
	 *
337
	 * @access      public
338
	 * @param       object $post Current post object
339
	 * @return      void
340
	 */
341
	public function metabox_content( $post ) {
342
		$taxonomy = $this->token;
343
		$tax = get_taxonomy( $taxonomy );
344
		?>
345
		<div id="taxonomy-<?php echo $taxonomy; ?>" class="categorydiv">
346
			<ul id="<?php echo $taxonomy; ?>-tabs" class="category-tabs">
347
				<li class="tabs"><a href="#<?php echo $taxonomy; ?>-all"><?php echo $tax->labels->all_items; ?></a></li>
348
				<li class="hide-if-no-js"><a href="#<?php echo $taxonomy; ?>-pop"><?php _e( 'Most Used' ); ?></a></li>
349
			</ul>
350
351
			<div id="<?php echo $taxonomy; ?>-pop" class="tabs-panel" style="display: none;">
352
				<ul id="<?php echo $taxonomy; ?>checklist-pop" class="categorychecklist form-no-clear">
353
					<?php $popular_ids = wp_popular_terms_checklist( $taxonomy ); ?>
354
				</ul>
355
			</div>
356
357
			<div id="<?php echo $taxonomy; ?>-all" class="tabs-panel">
358
				<input type="hidden" name="tax_input[<?php echo $taxonomy; ?>][]" value="0" />
359
				<?php
360
				if( class_exists( 'Walker_Tag_Checklist' ) ) {
361
					$walker = new Walker_Tag_Checklist;
362
				}
363
				?>
364
				<ul id="<?php echo $taxonomy; ?>checklist" data-wp-lists="list:<?php echo $taxonomy; ?>" class="categorychecklist form-no-clear">
365
					<?php wp_terms_checklist($post->ID, array( 'taxonomy' => $taxonomy, 'popular_cats' => $popular_ids , 'walker' => $walker ) ) ?>
0 ignored issues
show
Bug introduced by
The variable $walker does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
366
				</ul>
367
			</div>
368
			<?php if ( current_user_can($tax->cap->edit_terms) ) : ?>
369
				<div id="<?php echo $taxonomy; ?>-adder" class="wp-hidden-children">
370
					<h4>
371
						<a id="<?php echo $taxonomy; ?>-add-toggle" href="#<?php echo $taxonomy; ?>-add" class="hide-if-no-js">
372
							<?php
373
							/* translators: %s: add new taxonomy label */
374
							printf( __( '+ %s' ), $tax->labels->add_new_item );
375
							?>
376
						</a>
377
					</h4>
378
					<p id="<?php echo $taxonomy; ?>-add" class="category-add wp-hidden-child">
379
						<label class="screen-reader-text" for="new<?php echo $taxonomy; ?>"><?php echo $tax->labels->add_new_item; ?></label>
380
						<input type="text" name="new<?php echo $taxonomy; ?>" id="new<?php echo $taxonomy; ?>" class="form-required form-input-tip" value="<?php echo esc_attr( $tax->labels->new_item_name ); ?>" aria-required="true"/>
381
						<input type="button" id="<?php echo $taxonomy; ?>-add-submit" data-wp-lists="add:<?php echo $taxonomy ?>checklist:<?php echo $taxonomy ?>-add" class="button category-add-submit" value="<?php echo esc_attr( $tax->labels->add_new_item ); ?>" />
382
						<?php wp_nonce_field( 'add-' . $taxonomy, '_ajax_nonce-add-' . $taxonomy, false ); ?>
383
						<span id="<?php echo $taxonomy; ?>-ajax-response"></span>
384
					</p>
385
				</div>
386
			<?php endif; ?>
387
		</div>
388
		<?php
389
	}
390
391
392
	/**
393
	 * Add items to dashboard menu
394
	 *
395
	 * @access      public
396
	 * @since       1.0.0
397
	 * @return      void
398
	 */
399
	public function vendor_menu_items() {
400
		if( printcenter_vendor_access() ) {
401
			add_submenu_page( 'edit.php?post_type=product', __( 'Vendor Details', 'printcenter' ), __( 'Vendor Details', 'printcenter' ), 'edit_products', 'vendor_details', array( $this, 'vendor_details_page' ) );
402
		}
403
	}
404
405
	/**
406
	 * Only show current vendor's media in the media library
407
	 *
408
	 * @access      public
409
	 * @since       1.0.0
410
	 * @param       array $request Default request arguments
411
	 * @return      array Modified request arguments
412
	 */
413
	public function restrict_media_library( $request = array() ) {
414
		if( ! is_admin() ) {
415
			return $request;
416
		}
417
418
		$screen = get_current_screen();
419
420
		if( in_array( $screen->id, array( 'upload', 'product' ) ) ) {
421
			if( printcenter_vendor_access() ) {
422
				$vendor_id = printcenter_is_vendor();
423
424
				if( ! $vendor_id ) {
425
					return;
426
				}
427
428
				$vendor_admins = printcenter_get_vendor_admins( $vendor_id );
429
430
				if( ! $vendor_admins ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $vendor_admins of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
431
					return;
432
				}
433
434
				$admins = array();
435
436
				foreach( $vendor_admins as $admin ) {
437
					if( ! $admin->ID ) {
438
						continue;
439
					}
440
441
					$admins[] = $admin->ID;
442
				}
443
444
				if( 0 == count( $admins ) ) {
445
					return;
446
				}
447
448
				$request['author__in'] = $admins;
449
			}
450
		}
451
452
		return $request;
453
	}
454
455
456
	/**
457
	 * Only show current vendor's media in the media library modal on the product edit screen
458
	 *
459
	 * @access      public
460
	 * @since       1.0.0
461
	 * @param       array $query Default query arguments
462
	 * @return      array Modified query arguments
463
	 */
464
	public function restrict_media_library_modal( $query = array() ) {
465
		if( ! is_admin() ) {
466
			return $query;
467
		}
468
469
		$screen = get_current_screen();
470
471
		if( printcenter_vendor_access() ) {
472
			$vendor_id = printcenter_is_vendor();
473
474
			if( ! $vendor_id ) {
475
				return;
476
			}
477
478
			$vendor_admins = printcenter_get_vendor_admins( $vendor_id );
479
480
			if( ! $vendor_admins ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $vendor_admins of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
481
				return;
482
			}
483
484
			$admins = array();
485
486
			foreach( $vendor_admins as $admin ) {
487
				if( ! $admin->ID ) {
488
					continue;
489
				}
490
491
				$admins[] = $admin->ID;
492
			}
493
494
			if( 0 == count( $admins ) ) {
495
				return;
496
			}
497
498
			$query['author__in'] = $admins;
499
		}
500
501
		return $query;
502
	}
503
504
505
	/**
506
	 * Create vendor details page for vendors to edit their own details
507
	 *
508
	 * @access      public
509
	 * @since       1.0.0
510
	 * @return      void
511
	 */
512
	public function vendor_details_page() {
513
		$vendor         = printcenter_get_user_vendor();
514
		$vendor_data    = get_option( $this->token . '_' . $vendor->ID );
515
		$vendor_info    = printcenter_get_vendor( $vendor->ID );
516
		$paypal_address = '';
517
518
		if( isset( $vendor_data['paypal_email'] ) ) {
519
			$paypal_address = $vendor_data['paypal_email'];
520
		}
521
522
		echo '<div class="wrap" id="vendor_details">
523
				<div class="icon32" id="icon-options-general"><br/></div>
524
				<h2>' . __( 'Vendor Details', 'printcenter' ) . '</h2>
525
				<form method="post" action="" enctype="multipart/form-data">
526
					<input type="hidden" name="update_vendor_id" value="' . esc_attr( $vendor->ID ) . '" />
527
					' . wp_nonce_field( 'vendor_update', 'vendor_update_nonce', true, false ) . '
528
					<p class="form-field"><label for="vendor_paypal_address">' . __( 'PayPal account email address:', 'printcenter' ) . '</label> <input id="vendor_paypal_address" type="text" name="woo_vendors_paypal_address_' . $vendor->ID . '" value="' . $paypal_address . '" class="regular-text" style="width:auto;"/></p>
529
					<p class="form-field">
530
						<label for="vendor_description" style="vertical-align:top">' . __( 'Vendor description:', 'printcenter' ) . '</label>
531
						<textarea id="vendor_description" name="woo_vendors_description_' . $vendor->ID . '" rows="10" cols="50" class="large-text">' . $vendor_info->description . '</textarea>
532
					</p>';
533
534
		do_action( 'product_vendors_details_fields', $vendor->ID );
535
536
		echo '      <p class="submit">
537
						<input name="Submit" type="submit" class="button-primary" value="' . esc_attr( __( 'Save Details', 'printcenter' ) ) . '" />
538
					</p>
539
				</form>
540
			</div>';
541
	}
542
543
544
	/**
545
	 * Process save on vendor details submit
546
	 *
547
	 * @access      public
548
	 * @since       1.0.0
549
	 * @return      void
550
	 */
551
	public function vendor_details_page_save() {
552
		if( isset( $_POST[ 'vendor_update_nonce' ] ) && isset( $_POST['update_vendor_id'] ) ) {
553
			if( ! wp_verify_nonce( $_POST['vendor_update_nonce'], 'vendor_update' ) ) {
554
				wp_die( __( 'Cheatin&#8217; uh?', 'printcenter' ) );
555
			}
556
557
			$vendor_id = $_POST['update_vendor_id'];
558
559
			if( ! $vendor_id ) {
560
				return;
561
			}
562
563
			// PayPal account email address
564
			$paypal_address              = $_POST[ 'woo_vendors_paypal_address_' . $vendor_id ];
565
			$vendor_data['paypal_email'] = $paypal_address;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$vendor_data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $vendor_data = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
566
567
			update_option( $this->token . '_' . $vendor_id, $vendor_data );
568
569
			// Vendor description
570
			$args = array(
571
				'description' => $_POST[ 'woo_vendors_description_' . $vendor_id ]
572
			);
573
			wp_update_term( $vendor_id, $this->token, $args );
574
575
			do_action( 'product_vendors_details_fields_save', $vendor_id, $_POST );
576
577
			$redirect = add_query_arg( 'message', 1, $_POST['_wp_http_referer'] );
578
			wp_safe_redirect( $redirect );
579
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method vendor_details_page_save() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
580
		}
581
	}
582
583
584
	/**
585
	 * Add fields to vendor taxonomy (add new vendor screen)
586
	 *
587
	 * @access      public
588
	 * @since       1.0.0
589
	 * @param       str $taxonomy Current taxonomy name
590
	 * @return      void
591
	 */
592
	public function add_vendor_fields( $taxonomy ) {
0 ignored issues
show
Unused Code introduced by
The parameter $taxonomy is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
593
		global $woocommerce;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
594
595
		wp_enqueue_script( 'chosen' );
596
		wp_enqueue_script( 'ajax-chosen' );
597
598
		$commission = 50;
599
600
		?>
601
		<div class="form-field">
602
			<label for="vendor_admins"><?php _e( 'Vendor admins (optional)', 'printcenter' ); ?></label>
603
			<select name="vendor_data[admins][]" id="vendor_admins" class="ajax_chosen_select_customer" multiple="multiple" style="width:95%;" placeholder="Search for users"></select><br/>
604
			<span class="description"><?php _e( 'A list of users who can manage this vendor\'s products and view the sales reports.', 'printcenter' ); ?></span>
605
		</div>
606
607
		<div class="form-field">
608
			<label for="vendor_commission"><?php _e( 'Commission', 'printcenter' ); ?></label>
609
			<input type="number" class="regular-text" name="vendor_data[commission]" id="vendor_commission" value="<?php echo esc_attr( $commission ); ?>" /> %<br/>
610
			<span class="description"><?php _e( 'The percent of the total sale price that this vendor will receive - can be modified per product.', 'printcenter' ); ?></span>
611
		</div>
612
613
		<div class="form-field">
614
			<label for="vendor_paypal_email"><?php _e( 'PayPal email address', 'printcenter' ); ?></label>
615
			<input type="text" class="regular-text" name="vendor_data[paypal_email]" id="vendor_paypal_email" value="" /><br/>
616
			<span class="description"><?php _e( 'The PayPal email address of the vendor where their profits will be delivered.', 'printcenter' ); ?></span>
617
		</div>
618
		<?php
619
		$inline_js = "
620
			jQuery('select.ajax_chosen_select_customer').ajaxChosen({
621
				method: 		'GET',
622
				url: 			'" . admin_url('admin-ajax.php') . "',
623
				dataType: 		'json',
624
				afterTypeDelay: 100,
625
				minTermLength: 	1,
626
				data:		{
627
					action: 	'woocommerce_json_search_customers',
628
					security: 	'" . wp_create_nonce("search-customers") . "',
629
					default: 	''
630
				}
631
			}, function (data) {
632
				var terms = {};
633
634
				$.each(data, function (i, val) {
635
					terms[i] = val;
636
				});
637
638
				return terms;
639
			});
640
		";
641
642
		// Check WC version for backwards compatibility
643
		if( version_compare( $woocommerce->version, '2.1-beta-1', ">=" ) ) {
644
			wc_enqueue_js( $inline_js );
645
		} else {
646
			$woocommerce->add_inline_js( $inline_js );
647
		}
648
	}
649
650
651
	/**
652
	 * Add fields to vendor taxonomy (edit vendor screen)
653
	 *
654
	 * @access      public
655
	 * @since       1.0.0
656
	 * @param       obj $vendor Vendor taxonomy object
657
	 * @return      void
658
	 */
659
	public function edit_vendor_fields( $vendor ) {
660
		global $woocommerce;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
661
662
		wp_enqueue_script( 'chosen' );
663
		wp_enqueue_script( 'ajax-chosen' );
664
665
		$vendor_id     = $vendor->term_id;
666
		$vendor_data   = get_option( $this->token . '_' . $vendor_id );
667
		$vendor_admins = '';
668
669
		if( isset( $vendor_data['admins'] ) && count( $vendor_data['admins'] ) > 0 ) {
670
			$admins = $vendor_data['admins'];
671
672
			foreach( $admins as $k => $user_id ) {
673
				$user           = get_userdata( $user_id );
674
				$user_display   = $user->display_name . '(#' . $user_id . ' - ' . $user->user_email . ')';
675
				$vendor_admins .= '<option value="' . esc_attr( $user_id ) . '" selected="selected">' . $user_display . '</option>';
676
			}
677
		}
678
679
		$commission = 0;
680
		if( $vendor_data['commission'] || strlen( $vendor_data['commission'] ) > 0 || $vendor_data['commission'] != '' ) {
681
			$commission = $vendor_data['commission'];
682
		}
683
684
		$paypal_email = '';
685
		if( $vendor_data['paypal_email'] || strlen( $vendor_data['paypal_email'] ) > 0 || $vendor_data['paypal_email'] != '' ) {
686
			$paypal_email = $vendor_data['paypal_email'];
687
		}
688
		?>
689
		<tr class="form-field">
690
			<th scope="row" valign="top"><label for="vendor_admins"><?php _e( 'Vendor admins (optional)', 'printcenter' ); ?></label></th>
691
			<td>
692
				<select name="vendor_data[admins][]" id="vendor_admins" class="ajax_chosen_select_customer" multiple="multiple" style="width:95%;" placeholder="Search for users"><?php echo $vendor_admins; ?></select><br/>
693
				<span class="description"><?php _e( 'A list of users who can manage this vendor\'s products and view the sales reports.', 'printcenter' ); ?></span>
694
			</td>
695
		</tr>
696
697
		<tr class="form-field">
698
			<th scope="row" valign="top"><label for="vendor_commission"><?php _e( 'Commission', 'printcenter' ); ?></label></th>
699
			<td>
700
				<input type="number" class="regular-text" name="vendor_data[commission]" id="vendor_commission" value="<?php echo esc_attr( $commission ); ?>" /> %<br/>
701
				<span class="description"><?php _e( 'The percent of the total sale price that this vendor will receive - can be modified per product.', 'printcenter' ); ?></span>
702
			</td>
703
		</tr>
704
705
		<tr class="form-field">
706
			<th scope="row" valign="top"><label for="vendor_paypal_email"><?php _e( 'PayPal email address', 'printcenter' ); ?></label></th>
707
			<td>
708
				<input type="text" class="regular-text" name="vendor_data[paypal_email]" id="vendor_paypal_email" value="<?php echo esc_attr( $paypal_email ); ?>" /><br/>
709
				<span class="description"><?php _e( 'The PayPal email address of the vendor where their profits will be delivered.', 'printcenter' ); ?></span>
710
			</td>
711
		</tr>
712
		<?php
713
714
		$inline_js = "
715
			jQuery('select.ajax_chosen_select_customer').ajaxChosen({
716
				method:         'GET',
717
				url:            '" . admin_url('admin-ajax.php') . "',
718
				dataType:       'json',
719
				afterTypeDelay: 100,
720
				minTermLength:  1,
721
				data:       {
722
					action:     'woocommerce_json_search_customers',
723
					security:   '" . wp_create_nonce("search-customers") . "',
724
					default:    ''
725
				}
726
			}, function (data) {
727
				var terms = {};
728
729
				$.each(data, function (i, val) {
730
					terms[i] = val;
731
				});
732
733
				return terms;
734
			});
735
		";
736
737
		// Check WC version for backwards compatibility
738
		if( version_compare( $woocommerce->version, '2.1-beta-1', ">=" ) ) {
739
			wc_enqueue_js( $inline_js );
740
		} else {
741
			$woocommerce->add_inline_js( $inline_js );
742
		}
743
	}
744
745
746
	/**
747
	 * Save vendor taxonomy fields
748
	 *
749
	 * @access      public
750
	 * @since       1.0.0
751
	 * @param       int $vendor_id ID of vendor
752
	 * @return      void
753
	 */
754
	public function save_vendor_fields( $vendor_id ) {
755
		if ( isset( $_POST['vendor_data'] ) ) {
756
			$vendor_data = get_option( $this->token . '_' . $vendor_id );
757
			$keys        = array_keys( $_POST['vendor_data'] );
758
759
			foreach( $keys as $key ) {
760
				if( isset( $_POST['vendor_data'][$key] ) ) {
761
					$vendor_data[$key] = $_POST['vendor_data'][$key];
762
				}
763
			}
764
765
			// Get current vendor admins
766
			$args = array(
767
				'meta_key'     => 'product_vendor',
768
				'meta_value'   => $vendor_id,
769
				'meta_compare' => '='
770
			);
771
772
			$current_vendors = get_users( $args );
773
774
			// Remove all current admins (user meta)
775
			foreach( $current_vendors as $vendor ) {
776
				delete_user_meta( $vendor->ID, 'product_vendor' );
777
				$this->remove_vendor_caps( $vendor->ID );
778
			}
779
780
			// Remove all current admins (vendor meta)
781
			$vendor_data['admins'] = array();
782
			update_option( $this->token . '_' . $vendor_id, $vendor_data );
783
784
			// Only add selected admins
785
			if( isset( $_POST['vendor_data']['admins'] ) && count( $_POST['vendor_data']['admins'] > 0 ) ) {
786
787
				// Add selected admins to vendor
788
				$vendor_data['admins'] = $_POST['vendor_data']['admins'];
789
790
				// Get all vendors
791
				$vendors = printcenter_get_vendors();
792
793
				foreach( $_POST['vendor_data']['admins'] as $user_id ) {
794
					update_user_meta( $user_id, 'product_vendor', $vendor_id );
795
					$this->add_vendor_caps( $user_id );
796
797
					// Remove user from all other vendors
798
					if( is_array( $vendors ) && count( $vendors ) > 0 ) {
799
						foreach( $vendors as $v ) {
800
							$this_vendor = get_option( $this->token . '_' . $v->ID );
801
802
							if( isset( $this_vendor['admins'] ) && is_array( $this_vendor['admins'] ) ) {
803
								foreach( $this_vendor['admins'] as $k => $admin ) {
804
									if( $admin == $user_id ) {
805
										unset( $this_vendor['admins'][ $k ] );
806
										break;
807
									}
808
								}
809
							}
810
811
							update_option( $this->token . '_' . $v->ID, $this_vendor );
812
						}
813
					}
814
				}
815
			}
816
817
			// Update vendor
818
			update_option( $this->token . '_' . $vendor_id, $vendor_data );
819
		}
820
	}
821
822
	/**
823
	 * Add capabilities to vendor admins
824
	 *
825
	 * @access      public
826
	 * @since       1.0.0
827
	 * @param       int $user_id User ID of vendor admin
828
	 * @return      void
829
	 */
830
	private function add_vendor_caps( $user_id = 0 ) {
831
		if( $user_id > 0 ) {
832
			$caps = $this->vendor_caps();
833
			$user = new WP_User( $user_id );
834
835
			foreach( $caps as $cap ) {
836
				$user->add_cap( $cap );
837
			}
838
		}
839
	}
840
841
842
	/**
843
	 * Remove capabilities from vendor admins
844
	 *
845
	 * @access      public
846
	 * @since       1.0.0
847
	 * @param       int $user_id User ID of vendor admin
848
	 * @return      void
849
	 */
850
	private function remove_vendor_caps( $user_id = 0 ) {
851
		if( $user_id > 0 ) {
852
			$caps = $this->vendor_caps();
853
			$user = new WP_User( $user_id );
854
855
			foreach( $caps as $cap ) {
856
				$user->remove_cap( $cap );
857
			}
858
		}
859
	}
860
861
862
	/**
863
	 * Set up array of vendor admin capabilities
864
	 *
865
	 * @access      public
866
	 * @since       1.0.0
867
	 * @return      array $caps Vendor capabilities
868
	 */
869
	private function vendor_caps() {
870
		$caps = array(
871
			"edit_product",
872
			"read_product",
873
			"delete_product",
874
			"edit_products",
875
			"edit_others_products",
876
			"delete_products",
877
			"delete_published_products",
878
			"delete_others_products",
879
			"edit_published_products",
880
			"assign_product_terms",
881
			"upload_files",
882
			"manage_bookings",
883
		);
884
885
		$skip_review = get_option( 'woocommerce_product_vendors_skip_review' ) == 'yes' ? true : false;
886
887
		if( $skip_review ) {
888
			$caps[] = 'publish_products';
889
		}
890
891
		$caps = apply_filters( 'product_vendors_admin_caps', $caps );
892
893
		return $caps;
894
	}
895
896
897
	/**
898
	 * Process commission for order
899
	 *
900
	 * @access      public
901
	 * @since       1.0.0
902
	 * @param       int $order_id ID of order
903
	 * @return      void
904
	 */
905
	public function process_commissions( $order_id ) {
906
907
		// Only process commissions once
908
		$processed = get_post_meta( $order_id, '_commissions_processed', $single = false );
909
910
		if( $processed && $processed == 'yes' ) {
911
			return;
912
		}
913
914
		$order           = new WC_Order( $order_id );
915
		$include_coupons = get_option( 'woocommerce_product_vendors_include_coupons' ) == 'yes' ? true : false;
916
		$discounts       = array();
917
918
		if( $include_coupons ) {
919
920
			// Get order total with tax subtracted and discounts added
921
			$order_total    = $order->get_total();
922
			$order_tax      = $order->get_total_tax();
923
			$order_discount = $order->get_total_discount();
924
			$order_total    = (float) ( $order_total - $order_tax + $order_discount );
925
			$order_coupons  = $order->get_used_coupons();
926
927
			foreach( $order_coupons as $code ) {
928
				$coupon = new WC_Coupon( $code );
929
930
				if( ! $coupon->id ) {
931
					continue;
932
				}
933
934
				$coupon_amount   = (float) $coupon->amount;
935
				$coupon_discount = 0;
936
937
				// Work out discount based on coupon type, amount and products specified
938
				switch( $coupon->type ) {
939
					case 'fixed_cart':
940
						if( $coupon_amount >= $order_total ) {
941
							$coupon_amount = $order_total;
942
						}
943
944
						$coupon_discount = ( $coupon_amount / $order_total ) * 100;
945
946
						if( isset( $discounts['all']['percent'] ) ) {
947
							$coupon_discount += $discounts['all']['percent'];
948
						}
949
950
						$discounts['all']['percent'] = $coupon_discount;
951
						break;
952
					case 'percent':
953
						$coupon_discount = $coupon_amount;
954
955
						if( isset( $discounts['all']['percent'] ) ) {
956
							$coupon_discount += $discounts['all']['percent'];
957
						}
958
959
						$discounts['all']['percent'] = $coupon_discount;
960
						break;
961
					case 'fixed_product':
962
						$products = $coupon->product_ids;
963
964
						foreach( $products as $product_id ) {
965
							$coupon_discount = $coupon_amount;
966
967
							if( isset( $discounts[ $product_id ]['fixed'] ) ) {
968
								$coupon_discount += $discounts[ $product_id ]['fixed'];
969
							}
970
971
							$discounts[ $product_id ]['fixed'] = $coupon_amount;
972
						}
973
						break;
974
					case 'percent_product':
975
						$products = $coupon->product_ids;
976
977
						foreach( $products as $product_id ) {
978
							$coupon_discount = $coupon_amount;
979
980
							if( isset( $discounts[ $product_id ]['percent'] ) ) {
981
								$coupon_discount += $discounts[ $product_id ]['percent'];
982
							}
983
984
							$discounts[ $product_id ]['percent'] = $coupon_discount;
985
						}
986
						break;
987
				}
988
			}
989
		}
990
991
		// Get all products in order
992
		$items = $order->get_items( 'line_item' );
993
994
		foreach( $items as $item_id => $item ) {
995
			$discount_amount = 0;
996
			$product_id      = $order->get_item_meta( $item_id, '_product_id', true );
997
			$line_total      = (float) $order->get_line_subtotal( $item, false, true );
998
999
			foreach( $discounts as $product => $data ) {
1000
				if( ! in_array( $product, array( 'all', $product_id ) ) ) {
1001
					continue;
1002
				}
1003
1004
				foreach( $data as $type => $amount ) {
1005
					$amount = (float) $amount;
1006
1007
					switch( $type ) {
1008
						case 'fixed':
1009
							$discount_amount += (float) $amount;
1010
							break;
1011
						case 'percent':
1012
							$discount_amount += (float) ( $line_total * ( $amount / 100 ) );
1013
							break;
1014
					}
1015
				}
1016
			}
1017
1018
			if( $product_id && $line_total ) {
1019
				$this->record_commission( $product_id, $line_total, $discount_amount, $order_id );
1020
			}
1021
		}
1022
1023
		// Mark commissions as processed
1024
		update_post_meta( $order_id, '_commissions_processed', 'yes' );
1025
1026
		do_action( 'product_vendors_commissions_processed', $order_id );
1027
1028
	}
1029
1030
1031
	/**
1032
	 * Record individual commission
1033
	 *
1034
	 * @access      public
1035
	 * @since       1.0.0
1036
	 * @param       int $product_id ID of product for commission
1037
	 * @param       int $line_total Line total of product
1038
	 * @param       int $discount   Total discount applied to order
1039
	 * @param       int $order_id   ID of order
1040
	 * @return      void
1041
	 */
1042
	public function record_commission( $product_id = 0, $line_total = 0, $discount = 0, $order_id = 0 ) {
1043
		if( $product_id > 0 && $line_total > 0 ) {
1044
			$vendors = printcenter_get_product_vendors( $product_id );
1045
1046
			if( $vendors ) {
1047
				foreach( $vendors as $vendor ) {
0 ignored issues
show
Bug introduced by
The expression $vendors of type boolean is not traversable.
Loading history...
1048
					// Get percentage for commission
1049
					$commission = (float) printcenter_get_commission_percent( $product_id, $vendor->ID );
1050
1051
					// If commission is 0% then go no further
1052
					if( ! $commission ) {
1053
						continue;
1054
					}
1055
1056
					// Subtract total discount for this line item
1057
					$discounted_total = (float) $line_total - $discount;
1058
1059
					// If total is 0 after discounts then go no further
1060
					if( ! $discounted_total ) {
1061
						continue;
1062
					}
1063
1064
					// Get total amount for commission
1065
					$amount = (float) $discounted_total * ( $commission / 100 );
1066
1067
					// If commission amount is 0 then go no further
1068
					if( ! $amount ) {
1069
						continue;
1070
					}
1071
1072
					$this->create_commission( $vendor->ID, $product_id, $amount, $order_id );
1073
				}
1074
			}
1075
		}
1076
	}
1077
1078
1079
	/**
1080
	 * Create new commission post
1081
	 *
1082
	 * @access      public
1083
	 * @since       1.0.0
1084
	 * @param       int $vendor_id  ID of vendor for commission
1085
	 * @param       int $product_id ID of product for commission
1086
	 * @param       int $amount     Commission total
1087
	 * @param       int $order_id   ID of order
1088
	 * @return      void
1089
	 */
1090
	public function create_commission( $vendor_id = 0, $product_id = 0, $amount = 0, $order_id = 0 ) {
1091
		$commission_data = array(
1092
			'post_type'    => 'shop_commission',
1093
			'post_title'   => sprintf( __( 'Commission - %s', 'printcenter' ), strftime( _x( '%B %e, %Y @ %I:%M %p', 'Commission date parsed by strftime', 'printcenter' ) ) ),
1094
			'post_status'  => 'private',
1095
			'ping_status'  => 'closed',
1096
			'post_excerpt' => '',
1097
			'post_author'  => 1
1098
		);
1099
1100
		$commission_id = wp_insert_post( $commission_data );
1101
1102
		// Add meta data
1103
		if( $vendor_id ) { add_post_meta( $commission_id, '_commission_vendor', $vendor_id ); }
1104
		if( $product_id ) { add_post_meta( $commission_id, '_commission_product', $product_id ); }
1105
		if( $amount ) { add_post_meta( $commission_id, '_commission_amount', $amount ); }
1106
		if( $order_id ) { add_post_meta( $commission_id, '_commission_order', $order_id ); }
1107
1108
		// Mark commission as unpaid
1109
		add_post_meta( $commission_id, '_paid_status', 'unpaid' );
1110
1111
		do_action( 'product_vendors_commission_created', $commission_id );
1112
	}
1113
1114
1115
	/**
1116
	 * Cancel commission (move to trash) - will only work for commissions created in v1.1+
1117
	 *
1118
	 * @access      public
1119
	 * @since       1.0.0
1120
	 * @param       int $order_id ID of orders for commissions to cancal
1121
	 * @return      void
1122
	 */
1123
	public function cancel_commission( $order_id ) {
1124
		// Get all commissions for order
1125
		$args = array(
1126
			'post_type'      => 'shop_commission',
1127
			'posts_per_page' => -1,
1128
			'post_status'    => 'any',
1129
			'meta_key'       => '_commission_order',
1130
			'meta_value_num' => $order_id,
1131
		);
1132
1133
		$commissions = get_posts( $args );
1134
1135
		// Move all commission posts to trash
1136
		foreach( $commissions as $commission ) {
1137
			wp_trash_post( $commission->ID, false );
1138
		}
1139
	}
1140
1141
1142
	/**
1143
	 * Allow vendor admins to access the WP dashboard
1144
	 *
1145
	 * @access      public
1146
	 * @since       1.0.0
1147
	 * @param       bool $prevent_access Current setting
1148
	 * @return      bool Modified setitng
1149
	 */
1150
	public function allow_vendor_admin_access( $prevent_access ) {
1151
		if( printcenter_vendor_access() ) {
1152
			$prevent_access = false;
1153
		}
1154
1155
		return apply_filters( 'product_vendors_prevent_admin_access', $prevent_access );
1156
	}
1157
1158
1159
	/**
1160
	 * Only show vendor's products
1161
	 *
1162
	 * @access      public
1163
	 * @since       1.0.0
1164
	 * @param       array $request Current request
1165
	 * @return      array Modified request
1166
	 */
1167
	public function filter_product_list( $request ) {
1168
		global $typenow, $current_user;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1169
1170
		if( is_admin() ) {
1171
			if( ! current_user_can( 'manage_woocommerce' ) ) {
1172
				if( printcenter_vendor_access() ) {
1173
					if( $typenow == 'product' ) {
1174
						wp_get_current_user();
1175
1176
						if( isset( $current_user->ID ) && $current_user->ID > 0 ) {
1177
							$vendor = printcenter_get_user_vendor( $current_user->ID );
1178
1179
							if( $vendor->slug && strlen( $vendor->slug ) > 0 ) {
1180
								$request[ $this->token ] = $vendor->slug;
1181
							}
1182
						}
1183
					}
1184
				}
1185
			}
1186
		}
1187
1188
		return $request;
1189
	}
1190
1191
1192
	/**
1193
	 * Restrict vendors from editing other vendors' products
1194
	 *
1195
	 * @access      public
1196
	 * @since       1.0.0
1197
	 * @return      void
1198
	 */
1199
	public function restrict_products() {
1200
		global $typenow, $pagenow, $current_user;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1201
1202
		if( printcenter_is_vendor() && printcenter_vendor_access() ) {
1203
			if( $pagenow == 'post.php' && $typenow == 'product' ) {
1204
				if( isset( $_POST['post_ID'] ) ) {
1205
					return;
1206
				}
1207
1208
				wp_get_current_user();
1209
				$show_product = false;
1210
1211
				if( isset( $current_user->ID ) && $current_user->ID > 0 ) {
1212
					$vendors = printcenter_get_product_vendors( $_GET['post'] );
1213
1214
					if( isset( $vendors ) && is_array( $vendors ) ) {
1215
						foreach( $vendors as $vendor ) {
1216
							$show_product = printcenter_is_vendor_admin( $vendor->ID, $current_user->ID );
1217
1218
							if( $show_product ) {
1219
								break;
1220
							}
1221
						}
1222
					}
1223
				}
1224
1225
				if( ! $show_product ) {
1226
					wp_die( sprintf( __( 'You do not have permission to edit this product. %1$sClick here to view and edit your products%2$s.', 'printcenter' ), '<a href="' . esc_url( 'edit.php?post_type=product' ) . '">', '</a>' ) );
1227
				}
1228
			}
1229
		}
1230
1231
		return;
1232
	}
1233
1234
1235
	/**
1236
	 * Show correct post counts on list table for products
1237
	 *
1238
	 * @access      public
1239
	 * @since       1.0.0
1240
	 * @param       object $counts Default status counts
1241
	 * @param       string $type Current post type
1242
	 * @param       string $perm User permission level
1243
	 * @return      object Modified status counts
1244
	 */
1245
	public function list_table_product_counts( $counts, $type, $perm ) {
0 ignored issues
show
Unused Code introduced by
The parameter $perm is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1246
		if( 'product' != $type ) {
1247
			return $counts;
1248
		}
1249
1250
		if( printcenter_vendor_access() ) {
1251
			$vendor_id = printcenter_is_vendor();
1252
1253
			if( ! $vendor_id ) {
1254
				return $counts;
1255
			}
1256
1257
			$vendor_admins = printcenter_get_vendor_admins( $vendor_id );
1258
1259
			if( ! $vendor_admins ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $vendor_admins of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1260
				return $counts;
1261
			}
1262
1263
			$admins = array();
1264
1265
			foreach( $vendor_admins as $admin ) {
1266
				if( ! $admin->ID ) {
1267
					continue;
1268
				}
1269
1270
				$admins[] = $admin->ID;
1271
			}
1272
1273
			if( 0 == count( $admins ) ) {
1274
				return;
1275
			}
1276
1277
			$args = array(
1278
				'post_type'      => $type,
1279
				'author__in'     => $admins,
1280
				'posts_per_page' => -1
1281
			);
1282
1283
			 // Get all available statuses
1284
			$stati = get_post_stati();
1285
1286
			// Update count object
1287
			foreach( $stati as $status ) {
1288
				$args['post_status'] = $status;
1289
				$posts               = get_posts( $args );
1290
				$counts->$status     = count( $posts );
1291
			}
1292
		}
1293
1294
		return $counts;
1295
	}
1296
1297
1298
	/**
1299
	 * Show correct post counts on list tables for attachments
1300
	 *
1301
	 * @access      public
1302
	 * @since       1.0.0
1303
	 * @param       object $counts Default counts
1304
	 * @param       string $mime_type Current MIME type
1305
	 * @return      object Modified counts
1306
	 */
1307
	public function list_table_media_counts( $counts, $mime_type = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $mime_type is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1308
		if( printcenter_vendor_access() ) {
1309
			$vendor_id = printcenter_is_vendor();
1310
1311
			if( ! $vendor_id ) {
1312
				return $counts;
1313
			}
1314
1315
			$vendor_admins = printcenter_get_vendor_admins( $vendor_id );
1316
1317
			if( ! $vendor_admins ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $vendor_admins of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1318
				return $counts;
1319
			}
1320
1321
			$admins = array();
1322
1323
			foreach( $vendor_admins as $admin ) {
1324
				if( ! $admin->ID ) {
1325
					continue;
1326
				}
1327
1328
				$admins[] = $admin->ID;
1329
			}
1330
1331
			if( 0 == count( $admins ) ) {
1332
				return $counts;
1333
			}
1334
1335
			$args = array(
1336
				'post_parent' => null,
1337
				'numberposts' => -1,
1338
				'post_type'   => 'attachment',
1339
				'post_status' => 'any',
1340
				'author__in'  => $admins,
1341
			);
1342
1343
			// Update count object
1344
			foreach( $counts as $mimetype => $num ) {
1345
				$args['post_mime_type'] = $mimetype;
1346
				$attachments            = get_children( $args );
1347
				$counts->$mimetype      = count( $attachments );
1348
			}
1349
		}
1350
1351
		return $counts;
1352
	}
1353
1354
1355
	/**
1356
	 * Remove the 'Unattached' attachment view for vendors
1357
	 *
1358
	 * @access      public
1359
	 * @since       1.0.0
1360
	 * @param       array $views Default views
1361
	 * @return      array Modified views
1362
	 */
1363
	public function remove_unattached_attachments( $views = array() ) {
1364
		if( printcenter_vendor_access() ) {
1365
			unset( $views['detached'] );
1366
		}
1367
1368
		return $views;
1369
	}
1370
1371
1372
	/**
1373
	 * Add vendor to product
1374
	 *
1375
	 * @access      public
1376
	 * @since       1.0.0
1377
	 * @param       int $post_id Product ID
1378
	 * @return      void
1379
	 */
1380
	public function add_vendor_to_product( $post_id ) {
1381
		if( get_post_type( $post_id ) == 'product' ) {
1382
			if ( ! current_user_can( 'edit_post', $post_id ) ) {
1383
				return;
1384
			}
1385
1386
			if( printcenter_vendor_access() ) {
1387
				$vendor = printcenter_get_user_vendor();
1388
1389
				if( isset( $vendor->slug ) && strlen( $vendor->slug ) > 0 ) {
1390
					wp_set_object_terms( $post_id, $vendor->slug, $this->token, false );
1391
				}
1392
			}
1393
		}
1394
	}
1395
1396
1397
	/**
1398
	 * Add Product Vendor reports to WC reports
1399
	 *
1400
	 * @access      public
1401
	 * @since       1.0.0
1402
	 * @param       array $charts Existing reports
1403
	 * @return      array Modified reports
1404
	 */
1405
	public function add_reports( $charts ) {
1406
		$charts['product_vendors'] = array(
1407
			'title'  => __( 'Product Vendors', 'printcenter' ),
1408
			'charts' => array(
1409
				array(
1410
					'title'       => __( 'Overview', 'printcenter' ),
1411
					'description' => '',
1412
					'hide_title'  => true,
1413
					'function'    => 'woocommerce_product_vendors_report_overview'
1414
				),
1415
				array(
1416
					'title'       => __( 'Vendor Sales', 'printcenter' ),
1417
					'description' => '',
1418
					'hide_title'  => false,
1419
					'function'    => 'woocommerce_product_vendors_report_vendor_sales'
1420
				)
1421
			)
1422
		);
1423
1424
		return $charts;
1425
	}
1426
1427
1428
	/**
1429
	 * Detailed report of total vendor earnings for logged in user
1430
	 *
1431
	 * @access      public
1432
	 * @since       1.0.0
1433
	 * @return      string Report HTML
1434
	 */
1435
	public function vendor_total_earnings_report() {
1436
		$html      = '';
1437
		$vendor_id = printcenter_is_vendor();
1438
1439
		if( $vendor_id ) {
1440
			$selected_year  = ( isset( $_POST['report_year'] ) && $_POST['report_year'] != 'all' ) ? $_POST['report_year'] : false;
1441
			$selected_month = ( isset( $_POST['report_month'] ) && $_POST['report_month'] != 'all' ) ? $_POST['report_month'] : false;
1442
			$commissions    = printcenter_get_vendor_commissions( $vendor_id, $selected_year, $selected_month, false );
1443
			$total_earnings = 0;
1444
1445
			foreach( $commissions as $commission ) {
1446
				$earnings   = get_post_meta( $commission->ID, '_commission_amount', true );
1447
				$product_id = get_post_meta( $commission->ID, '_commission_product', true );
1448
				$product    = get_product( $product_id );
1449
1450
				if( ! isset( $product ) || ! $product || is_wp_error( $product ) || ! is_object( $product ) ) {
1451
					continue;
1452
				}
1453
1454
				if( ! isset( $data[ $product_id ]['product'] ) ) {
1455
					$data[ $product_id ]['product'] = $product->get_title();
0 ignored issues
show
Coding Style Comprehensibility introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1456
				}
1457
1458
				if( ! isset( $data[ $product_id ]['product_url'] ) ) {
1459
					$data[ $product_id ]['product_url'] = get_permalink( $product_id );
0 ignored issues
show
Bug introduced by
The variable $data does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1460
				}
1461
1462
				if( isset( $data[ $product_id ]['sales'] ) ) {
1463
					++$data[ $product_id ]['sales'];
1464
				} else {
1465
					$data[ $product_id ]['sales'] = 1;
1466
				}
1467
1468
				if( isset( $data[ $product_id ]['earnings'] ) ) {
1469
					$data[ $product_id ]['earnings'] += $earnings;
1470
				} else {
1471
					$data[ $product_id ]['earnings'] = $earnings;
1472
				}
1473
1474
				$total_earnings += $earnings;
1475
			}
1476
1477
			$month_options = '<option value="all">' . __( 'All months', 'printcenter' ) . '</option>';
1478
1479
			for( $i = 1; $i <= 12; $i++ ) {
1480
				$month_num      = str_pad( $i, 2, 0, STR_PAD_LEFT );
1481
				$month_name     = date( 'F', mktime( 0, 0, 0, $i + 1, 0, 0 ) );
1482
				$month_options .= '<option value="' . esc_attr( $month_num ) . '" ' . selected( $selected_month, $month_num, false ) . '>' . $month_name . '</option>';
1483
			}
1484
1485
			$year_options = '<option value="all">' . __( 'All years', 'printcenter' ) . '</option>';
1486
			$current_year = date( 'Y' );
1487
1488
			for( $i = $current_year; $i >= ( $current_year - 5 ); $i-- ) {
1489
				$year_options .= '<option value="' . $i . '" ' . selected( $selected_year, $i, false ) . '>' . $i . '</option>';
1490
			}
1491
1492
			$html .= '<div class="product_vendors_report_form">
1493
						<form name="product_vendors_report" action="' . get_permalink() . '" method="post">
1494
							' . __( 'Select report date:', 'printcenter' ) . '
1495
							<select name="report_month">' . $month_options . '</select>
1496
							<select name="report_year">' . $year_options . '</select>
1497
							<input type="submit" class="button" value="Submit" />
1498
						</form>
1499
					</div>';
1500
1501
			$html .= '<table class="shop_table" cellspacing="0">
1502
						<thead>
1503
							<tr>
1504
								<th>' . __( 'Product', 'printcenter' ) . '</th>
1505
								<th>' . __( 'Sales', 'printcenter' ) . '</th>
1506
								<th>' . __( 'Earnings', 'printcenter' ) . '</th>
1507
							</tr>
1508
						</thead>
1509
						<tbody>';
1510
1511
			if( isset( $data ) && is_array( $data ) ) {
1512
				foreach( $data as $product_id => $product ) {
1513
					$html .= '<tr>
1514
								<td><a href="' . esc_url( $product['product_url'] ) . '">' . $product['product'] . '</a></td>
1515
								<td>' . $product['sales'] . '</td>
1516
								<td>' . get_woocommerce_currency_symbol() . number_format( $product['earnings'], 2 ) . '</td>
1517
							</tr>';
1518
				}
1519
1520
				$html .= '<tr>
1521
							<td colspan="2"><b>' . __( 'Total', 'printcenter' ) . '</b></td>
1522
							<td>' . get_woocommerce_currency_symbol() . number_format( $total_earnings, 2 ) . '</td>
1523
						</tr>';
1524
			} else {
1525
				$html .= '<tr><td colspan="3"><em>' . __( 'No sales found', 'printcenter' ) . '</em></td></tr>';
1526
			}
1527
1528
			$html .= '</tbody>
1529
					</table>';
1530
		}
1531
1532
		return $html;
1533
	}
1534
1535
1536
	/**
1537
	 * Quick report of total vendor earnings for the current month
1538
	 *
1539
	 * @access      public
1540
	 * @since       1.0.0
1541
	 * @return      string Report HTML
1542
	 */
1543
	public function vendor_month_earnings() {
1544
		$html      = '';
1545
		$vendor_id = printcenter_is_vendor();
1546
1547
		if( $vendor_id ) {
1548
			$commissions = printcenter_get_vendor_commissions( $vendor_id, date( 'Y' ), date( 'm' ), false );
1549
1550
			if( $commissions ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $commissions of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1551
				$month_earnings = 0;
1552
1553
				foreach( $commissions as $commission ) {
1554
					$earnings        = get_post_meta( $commission->ID, '_commission_amount', true );
1555
					$month_earnings += $earnings;
1556
				}
1557
1558
				$html .= sprintf( __( 'This month\'s earnings: %1$s', 'printcenter' ), '<b>' . get_woocommerce_currency_symbol() . number_format( $month_earnings, 2 ) . '</b>' );
1559
			}
1560
		}
1561
1562
		return $html;
1563
	}
1564
1565
1566
	/**
1567
	 * Load product archive template for vendor pages
1568
	 *
1569
	 * @access      public
1570
	 * @since       1.0.0
1571
	 * @return      void
1572
	 */
1573
	public function load_product_archive_template() {
1574
		if( is_tax( $this->token ) ) {
1575
			woocommerce_get_template( 'archive-product.php' );
1576
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method load_product_archive_template() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1577
		}
1578
	}
1579
1580
1581
	/**
1582
	 * Add 'woocommerce' class to body tag for vendor pages
1583
	 *
1584
	 * @access      public
1585
	 * @since       1.0.0
1586
	 * @param       array $classes Existing classes
1587
	 * @return      array Modified classes
1588
	 */
1589
	public function set_product_archive_class( $classes ) {
1590
		if( is_tax( $this->token ) ) {
1591
			// Add generic classes
1592
			$classes[] = 'woocommerce';
1593
			$classes[] = 'product-vendor';
1594
1595
			// Get vendor ID
1596
			$vendor_id = get_queried_object()->term_id;
1597
1598
			// Get vendor info
1599
			$vendor = printcenter_get_vendor( $vendor_id );
1600
1601
			// Add vendor slug as class
1602
			if( '' != $vendor->slug ) {
1603
				$classes[] = $vendor->slug;
1604
			}
1605
		}
1606
1607
		return $classes;
1608
	}
1609
1610
1611
	/**
1612
	 * Add vendor info to top of vendor pages
1613
	 *
1614
	 * @access      public
1615
	 * @since       1.0.0
1616
	 * @return      void
1617
	 */
1618
	public function product_archive_vendor_info() {
1619
		if( is_tax( $this->token ) ) {
1620
			// Get vendor ID
1621
			$vendor_id = get_queried_object()->term_id;
1622
1623
			// Get vendor info
1624
			$vendor = printcenter_get_vendor( $vendor_id );
1625
1626
			do_action( 'product_vendors_page_description_before', $vendor_id );
1627
1628
			if( '' != $vendor->description ) {
1629
				echo '<p class="archive-description">' . apply_filters( 'product_vendors_page_description', nl2br( $vendor->description ), $vendor_id ) . '</p>';
1630
			}
1631
1632
			do_action( 'product_vendors_page_description_after', $vendor_id );
1633
		}
1634
	}
1635
1636
1637
	/**
1638
	 * Add vendor ID to booking
1639
	 *
1640
	 * @access      public
1641
	 * @since       1.0.0
1642
	 * @param       int $booking_id Booking ID
1643
	 * @return      void
1644
	 */
1645
	public function add_vendor_to_booking( $booking_id = 0 ) {
1646
		if( ! $booking_id ) {
1647
			return;
1648
		}
1649
1650
		$product_id = get_post_meta( $booking_id, '_booking_product_id', true );
1651
1652
		if( ! $product_id ) {
1653
			return;
1654
		}
1655
1656
		$vendors = printcenter_get_product_vendors( $product_id );
1657
1658
		if( ! $vendors ) {
1659
			return;
1660
		}
1661
1662
		foreach( $vendors as $vendor ) {
0 ignored issues
show
Bug introduced by
The expression $vendors of type boolean is not traversable.
Loading history...
1663
			if( ! isset( $vendor->ID ) ) {
1664
				continue;
1665
			}
1666
1667
			update_post_meta( $booking_id, '_booking_vendor', $vendor->ID );
1668
		}
1669
	}
1670
1671
1672
	/**
1673
	 * Restrict booking products lists to current vendor's products
1674
	 *
1675
	 * @access      public
1676
	 * @since       1.0.0
1677
	 * @param       array $args Default query args
1678
	 * @return      array Modified query args
1679
	 */
1680
	public function restrict_booking_products( $args = array() ) {
1681
		if( current_user_can( 'manage_woocommerce' ) ) {
1682
			return $args;
1683
		}
1684
1685
		$vendor_id = printcenter_is_vendor();
1686
1687
		if( $vendor_id ) {
1688
			$args['tax_query'][] = array(
1689
				'taxonomy' => $this->token,
1690
				'field'    => 'id',
1691
				'terms'    => $vendor_id,
1692
			);
1693
		}
1694
1695
		return $args;
1696
	}
1697
1698
1699
	/**
1700
	 * Only show vendor's bookings
1701
	 *
1702
	 * @access      public
1703
	 * @since       1.0.0
1704
	 * @param       array $request Current request
1705
	 * @return      array Modified request
1706
	 */
1707
	public function filter_booking_list( $request ) {
1708
		global $typenow, $current_user;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1709
1710
		if( is_admin() ) {
1711
			if( ! current_user_can( 'manage_woocommerce' ) ) {
1712
				if( printcenter_vendor_access() ) {
1713
					if( $typenow == 'wc_booking' ) {
1714
						wp_get_current_user();
1715
1716
						if( isset( $current_user->ID ) && $current_user->ID > 0 ) {
1717
							$vendor = printcenter_get_user_vendor( $current_user->ID );
1718
1719
							if( $vendor->slug && strlen( $vendor->slug ) > 0 ) {
1720
								$request['meta_query'][] = array(
1721
									'key'   => '_booking_vendor',
1722
									'value' => $vendor->ID,
1723
								);
1724
							}
1725
						}
1726
					}
1727
				}
1728
			}
1729
		}
1730
1731
		return $request;
1732
	}
1733
1734
1735
	/**
1736
	 * Restrict booking calendar to current vendor's bookings
1737
	 *
1738
	 * @access      public
1739
	 * @since       1.0.0
1740
	 * @param       array $args Default query args
1741
	 * @return      array Modified query args
1742
	 */
1743
	public function filter_booking_calendar( $args = array() ) {
1744
		if( current_user_can( 'manage_woocommerce' ) ) {
1745
			return $args;
1746
		}
1747
1748
		$vendor_id = printcenter_is_vendor();
1749
1750
		if( $vendor_id ) {
1751
			$args['meta_query'][] = array(
1752
				'key'   => '_booking_vendor',
1753
				'value' => $vendor_id,
1754
			);
1755
		}
1756
1757
		return $args;
1758
	}
1759
1760
1761
	/**
1762
	 * Show correct post counts on list table for bookings
1763
	 *
1764
	 * @access      public
1765
	 * @since       1.0.0
1766
	 * @param       object $counts Default status counts
1767
	 * @param       string $type Current post type
1768
	 * @param       string $perm User permission level
1769
	 * @return      object Modified status counts
1770
	 */
1771
	public function list_table_booking_counts( $counts, $type, $perm ) {
0 ignored issues
show
Unused Code introduced by
The parameter $perm is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1772
		if( 'wc_booking' != $type ) {
1773
			return $counts;
1774
		}
1775
1776
		if( printcenter_vendor_access() ) {
1777
			$vendor_id = printcenter_is_vendor();
1778
1779
			if( ! $vendor_id ) {
1780
				return $counts;
1781
			}
1782
1783
			// Build up query arguments
1784
			$args = array(
1785
				'post_type'      => $type,
1786
				'posts_per_page' => -1,
1787
				'meta_query'     => array(
1788
					array(
1789
						'key'   => '_booking_vendor',
1790
						'value' => $vendor_id,
1791
					),
1792
				),
1793
			);
1794
1795
			// Get all available booking statuses
1796
			$stati = array( 'unpaid', 'pending', 'confirmed', 'paid', 'cancelled', 'complete' );
1797
1798
			// Update count object
1799
			foreach( $stati as $status ) {
1800
				$args['post_status'] = $status;
1801
				$posts               = get_posts( $args );
1802
				$counts->$status     = count( $posts );
1803
			}
1804
		}
1805
1806
		return $counts;
1807
	}
1808
1809
1810
	/**
1811
	 * Hide vendor products from other product loops
1812
	 *
1813
	 * @access      public
1814
	 * @since       1.0.0
1815
	 * @param       object $query WP_Query object
1816
	 * @return      void
1817
	 */
1818
	public function hide_vendor_products( $query ) {
1819
		$hide_vendor_products = get_option( 'woocommerce_product_vendors_hide_vendor_products' ) == 'yes' ? true : false;
1820
1821
		if( $hide_vendor_products && ! is_admin() ) {
1822
			if( is_post_type_archive( 'product' ) && ! is_tax( $this->token ) ) {
1823
				// Get all shop_vendor terms
1824
				$vendor_terms_array = get_terms( $this->token, array( 'hide_empty' => false ) );
1825
				$vendor_terms       = array();
1826
1827
				foreach( $vendor_terms_array as $term ) {
1828
					$vendor_terms[] = $term->slug;
1829
				}
1830
1831
				// Set up custom tax query
1832
				$tax_query = array(
1833
					'taxonomy' => $this->token,
1834
					'field'    => 'slug',
1835
					'terms'    => $vendor_terms,
1836
					'operator' => 'NOT IN',
1837
				);
1838
1839
				// Add custom tax query to query
1840
				$query->tax_query->queries[]    = $tax_query;
1841
				$query->query_vars['tax_query'] = $query->tax_query->queries;
1842
			}
1843
		}
1844
	}
1845
1846
1847
	/**
1848
	 * Send product enquiries directly to vendors
1849
	 *
1850
	 * @access      public
1851
	 * @since       1.0.0
1852
	 * @param       string $email Default email address for form
1853
	 * @param       int $product_id ID of product
1854
	 * @return      mixed String if no vendors exist for product, otherwise array of vendor email addresses
1855
	 */
1856
	function wc_product_enquiry_to_vendor( $email = '', $product_id = 0 ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
1857
		// Get all vendors for product
1858
		$vendors = printcenter_get_product_vendors( $product_id );
1859
1860
		if ( ! empty( $vendors ) ) {
1861
			$emails = array();
1862
1863
			// Send to default email address
1864
			$emails[] = $email;
1865
1866
			// Also send to vendor email addresses
1867
			foreach( $vendors as $vendor ) {
0 ignored issues
show
Bug introduced by
The expression $vendors of type boolean is not traversable.
Loading history...
1868
1869
				// Get all admins for vendor
1870
				foreach( $vendor->admins as $admin ) {
1871
					$emails[] = $admin->data->user_email;
1872
				}
1873
			}
1874
1875
			return $emails;
1876
		}
1877
1878
		return $email;
1879
	}
1880
1881
1882
	/**
1883
	 * Add extension settings
1884
	 *
1885
	 * @access      public
1886
	 * @since       1.0.0
1887
	 * @param       array $settings Existing settings
1888
	 * @return      array Modified settings
1889
	 */
1890
	public function add_settings( $settings ) {
1891
		$vendors_settings = array(
1892
			array(
1893
				'title' => __( 'Product Vendors', 'printcenter' ),
1894
				'type'  => 'title',
1895
				'id'    => 'product_vendors_options'
1896
			),
1897
			array(
1898
				'title'         => __( 'Product listings', 'printcenter' ),
1899
				'desc'          => __( 'Hide vendor products from other product listings', 'printcenter' ),
1900
				'id'            => 'woocommerce_product_vendors_hide_vendor_products',
1901
				'default'       => 'no',
1902
				'desc_tip'      => __( 'Hide products belonging to vendors from other product listings - this means that vendor products will only be visible on the individual vendor pages.', 'printcenter' ),
1903
				'type'          => 'checkbox',
1904
				'checkboxgroup' => 'start'
1905
			),
1906
			array(
1907
				'title'         => __( 'Admin review', 'printcenter' ),
1908
				'desc'          => __( 'Skip admin review for vendors', 'printcenter' ),
1909
				'id'            => 'woocommerce_product_vendors_skip_review',
1910
				'default'       => 'no',
1911
				'desc_tip'      => __( 'Allow vendors to add products without the need for admin review - after changing this option you will need to resave each vendor.', 'printcenter' ),
1912
				'type'          => 'checkbox',
1913
				'checkboxgroup' => 'start'
1914
			),
1915
			array(
1916
				'title'             => __( 'Base commission', 'woocommerce' ),
1917
				'desc'              => __( 'This sets the default commission percentage that will be applied if no product or vendor commission is specified.', 'printcenter' ),
1918
				'id'                => 'woocommerce_product_vendors_base_commission',
1919
				'css'               => 'width:50px;',
1920
				'default'           => '50',
1921
				'desc_tip'          => true,
1922
				'type'              => 'number',
1923
				'custom_attributes' => array(
1924
					'min'  => 0,
1925
					'max'  => 100,
1926
					'step' => 1
1927
				)
1928
			),
1929
			array(
1930
				'title'         => __( 'Coupon handling', 'printcenter' ),
1931
				'desc'          => __( 'Include coupons in commission calculations', 'printcenter' ),
1932
				'id'            => 'woocommerce_product_vendors_include_coupons',
1933
				'default'       => 'no',
1934
				'desc_tip'      => __( 'Decide whether vendor commissions should take coupons into account or not.', 'printcenter' ),
1935
				'type'          => 'checkbox',
1936
				'checkboxgroup' => 'start'
1937
			),
1938
			array(
1939
				'type' => 'sectionend',
1940
				'id'   => 'product_vendors_options'
1941
			),
1942
		);
1943
1944
		$settings = array_merge( $settings, $vendors_settings );
1945
1946
		return $settings;
1947
	}
1948
1949
1950
	/**
1951
	 * Add Product Vendors pages to WooCommerce screen IDs
1952
	 *
1953
	 * @access      public
1954
	 * @since       1.0.0
1955
	 * @param       array $screen_ids Existing IDs
1956
	 * @return      array Modified IDs
1957
	 */
1958
	public function screen_ids( $screen_ids = array() ) {
1959
		$screen_ids[] = 'edit-shop_vendor';
1960
		$screen_ids[] = 'shop_commission';
1961
		$screen_ids[] = 'edit-shop_commission';
1962
		$screen_ids[] = 'product_page_vendor_details';
1963
1964
		return $screen_ids;
1965
	}
1966
1967
1968
	/**
1969
	 * Display admin notices in the WP dashboard
1970
	 *
1971
	 * @access      public
1972
	 * @since       1.0.0
1973
	 * @return      void
1974
	 */
1975
	public function admin_notices() {
1976
		global $current_screen;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1977
1978
		$message = false;
1979
1980
		if( $current_screen->base == 'product_page_vendor_details' ) {
1981
			if( isset( $_GET['message'] ) ) {
1982
				switch( $_GET['message'] ) {
1983
					case 1: $message = sprintf( __( '%1$sVendor details have been updated.%2$s', 'printcenter' ), '<div id="message" class="updated"><p>', '</p></div>' );
1984
				}
1985
			}
1986
		}
1987
1988
		if( $message ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $message of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1989
			echo $message;
1990
		}
1991
	}
1992
1993
1994
	/**
1995
	 * Flush rewrite rules on plugin activation
1996
	 *
1997
	 * @access      public
1998
	 * @since       1.0.0
1999
	 * @return      void
2000
	 */
2001
	public function rewrite_flush() {
2002
		$this->register_vendors_taxonomy();
2003
		flush_rewrite_rules();
2004
	}
2005
2006
2007
	/**
2008
	 * Add 'Configure' link to plugin listing
2009
	 *
2010
	 * @access      public
2011
	 * @since       1.0.0
2012
	 * @param       array $links Existing links
2013
	 * @return      array Modified links
2014
	 */
2015
	public function add_settings_link( $links ) {
2016
		$custom_links[] = '<a href="' . admin_url( 'edit-tags.php?taxonomy=shop_vendor&post_type=product' ) . '">' . __( 'Vendors', 'printcenter' ) . '</a>';
0 ignored issues
show
Coding Style Comprehensibility introduced by
$custom_links was never initialized. Although not strictly required by PHP, it is generally a good practice to add $custom_links = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
2017
		$custom_links[] = '<a href="' . admin_url( 'edit.php?post_type=shop_commission' ) . '">' . __( 'Commissions', 'printcenter' ) . '</a>';
2018
2019
		return array_merge( $links, $custom_links );
2020
	}
2021
}
2022