WooCommerce_Product_Vendors_Commissions   D
last analyzed

Complexity

Total Complexity 89

Size/Duplication

Total Lines 836
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 836
ccs 0
cts 400
cp 0
rs 4.4444
c 0
b 0
f 0
wmc 89
lcom 1
cbo 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 45 2
B register_post_type() 0 36 1
A paid_filter_option() 0 15 3
A paid_filter_action() 0 15 4
B register_custom_column_headings() 0 28 5
C register_custom_columns() 0 36 11
A updated_messages() 0 19 2
A custom_actions_content() 0 14 3
A custom_actions_save() 0 11 4
A meta_box_setup() 0 3 1
D meta_box_content() 0 111 15
D meta_box_save() 0 38 9
A enter_title_here() 0 7 2
B get_custom_fields_settings() 0 37 1
A add_bulk_action_options() 0 14 2
D generate_commissions_csv() 0 112 9
B mark_all_commissions_paid() 0 36 4
A admin_enqueue_scripts() 0 17 4
B admin_notices() 0 21 7

How to fix   Complexity   

Complex Class

Complex classes like WooCommerce_Product_Vendors_Commissions often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use WooCommerce_Product_Vendors_Commissions, and based on these observations, apply Extract Interface, too.

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
 * Vendor commissions
4
 *
5
 * @package     PrintCenter\Vendor\Commissions
6
 * @since       1.0.0
7
 */
8
9
10
// Exit if accessed directly
11
if( ! defined( 'ABSPATH' ) ) {
12
	exit;
13
}
14
15
16
/**
17
 * Commission management
18
 *
19
 * @since       1.0.0
20
 */
21
class WooCommerce_Product_Vendors_Commissions {
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 The plugin 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_commission';
78
79
		// Regsiter post type
80
		add_action( 'init' , array( $this , 'register_post_type' ) );
81
82
		if ( is_admin() ) {
83
			// Enqueue scripts and styles where needed
84
			add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ), 11 );
85
86
			// Handle custom fields for post
87
			add_action( 'admin_menu', array( $this, 'meta_box_setup' ), 20 );
88
			add_action( 'save_post', array( $this, 'meta_box_save' ) );
89
90
			// Handle commission paid status
91
			add_action( 'post_submitbox_misc_actions', array( $this, 'custom_actions_content' ) );
92
			add_action( 'save_post', array( $this, 'custom_actions_save' ) );
93
94
			// Modify text in main title text box
95
			add_filter( 'enter_title_here', array( $this, 'enter_title_here' ) );
96
97
			// Display custom update messages for posts edits
98
			add_filter( 'post_updated_messages', array( $this, 'updated_messages' ) );
99
100
			// Handle post columns
101
			add_filter( 'manage_edit-' . $this->token . '_columns', array( $this, 'register_custom_column_headings' ), 10, 1 );
102
			add_action( 'manage_pages_custom_column', array( $this, 'register_custom_columns' ), 10, 2 );
103
104
			// Handle commissions table filtering for vendors
105
			add_action( 'restrict_manage_posts', array( $this, 'paid_filter_option' ) );
106
			add_filter( 'request', array( $this, 'paid_filter_action' ) );
107
108
			// Add bulk actions to commissions table
109
			add_action( 'admin_footer-edit.php', array( $this, 'add_bulk_action_options' ) );
110
			add_action( 'load-edit.php', array( $this, 'generate_commissions_csv' ) );
111
			add_action( 'load-edit.php', array( $this, 'mark_all_commissions_paid' ) );
112
113
			// Display admin page notices
114
			add_action( 'admin_notices', array( $this, 'admin_notices' ) );
115
		}
116
	}
117
118
119
	/**
120
	 * Register commissions post type
121
	 *
122
	 * @access      public
123
	 * @since       1.0.0
124
	 * @return      void
125
	 */
126
	public function register_post_type() {
127
		$labels = array(
128
			'name'               => _x( 'Commissions', 'post type general name' , 'printcenter' ),
129
			'singular_name'      => _x( 'Commission', 'post type singular name' , 'printcenter' ),
130
			'add_new'            => _x( 'New Commission', $this->token , 'printcenter' ),
131
			'add_new_item'       => sprintf( __( 'Add New %s' , 'printcenter' ), __( 'Commission' , 'printcenter' ) ),
132
			'edit_item'          => sprintf( __( 'Edit %s' , 'printcenter' ), __( 'Commission' , 'printcenter' ) ),
133
			'new_item'           => sprintf( __( 'New %s' , 'printcenter' ), __( 'Commission' , 'printcenter' ) ),
134
			'all_items'          => sprintf( __( 'All %s' , 'printcenter' ), __( 'Commissions' , 'printcenter' ) ),
135
			'view_item'          => sprintf( __( 'View %s' , 'printcenter' ), __( 'Commission' , 'printcenter' ) ),
136
			'search_items'       => sprintf( __( 'Search %a' , 'printcenter' ), __( 'Commissions' , 'printcenter' ) ),
137
			'not_found'          => sprintf( __( 'No %s Found' , 'printcenter' ), __( 'Commissions' , 'printcenter' ) ),
138
			'not_found_in_trash' => sprintf( __( 'No %s Found In Trash' , 'printcenter' ), __( 'Commissions' , 'printcenter' ) ),
139
			'parent_item_colon'  => '',
140
			'menu_name'          => __( 'PrintCenter' , 'printcenter' )
141
		);
142
143
		$args = array(
144
			'labels'              => $labels,
145
			'public'              => false,
146
			'publicly_queryable'  => true,
147
			'exclude_from_search' => true,
148
			'show_ui'             => true,
149
			'show_in_menu'        => false,
150
			'show_in_nav_menus'   => false,
151
			'query_var'           => false,
152
			'rewrite'             => true,
153
			'capability_type'     => 'post',
154
			'has_archive'         => true,
155
			'hierarchical'        => true,
156
			'supports'            => array( 'title' ),
157
			'menu_position'       => 1227
158
		);
159
160
		register_post_type( $this->token, $args );
161
	}
162
163
164
	/**
165
	 * Add options to filter commissions by paid status
166
	 *
167
	 * @access      public
168
	 * @since       1.0.0
169
	 * @return      void
170
	 */
171
	public function paid_filter_option() {
172
		global $typenow;
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...
173
174
		if( $typenow == $this->token ) {
175
			$selected = isset( $_GET['paid_status'] ) ? $_GET['paid_status'] : '';
176
177
			$output  = '<select name="paid_status" id="dropdown_product_type">';
178
			$output .= '<option value="">'.__( 'Show all paid statuses', 'printcenter' ).'</option>';
179
			$output .= '<option value="paid" ' . selected( $selected, 'paid', false ) . '>'.__( 'Paid', 'printcenter' ).'</option>';
180
			$output .= '<option value="unpaid" ' . selected( $selected, 'unpaid', false ) . '>'.__( 'Unpaid', 'printcenter' ).'</option>';
181
			$output .= '</select>';
182
183
			echo $output;
184
		}
185
	}
186
187
188
	/**
189
	 * Filter commissions by paid status
190
	 *
191
	 * @access      public
192
	 * @since       1.0.0
193
	 * @param       array $request Current request
194
	 * @return      array Modified request
195
	 */
196
	public function paid_filter_action( $request ) {
197
		global $typenow;
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...
198
199
		if( $typenow == $this->token ) {
200
			$paid_status = isset( $_GET['paid_status'] ) ? $_GET['paid_status'] : '';
201
202
			if( $paid_status ) {
203
				$request['meta_key']     = '_paid_status';
204
				$request['meta_value']   = $paid_status;
205
				$request['meta_compare'] = '=';
206
			}
207
		}
208
209
		return $request;
210
	}
211
212
213
	/**
214
	 * Add columns to commissions list table
215
	 *
216
	 * @access      public
217
	 * @since       1.0.0
218
	 * @param       array $defaults Default columns
219
	 * @return      array New columns
220
	 */
221
	public function register_custom_column_headings( $defaults ) {
222
		$new_columns = array(
223
			'_commission_product' => __( 'Product' , 'printcenter' ),
224
			'_commission_vendor'  => __( 'Vendor' , 'printcenter' ),
225
			'_commission_amount'  => __( 'Amount' , 'printcenter' ),
226
			'_paid_status'        => __( 'Status' , 'printcenter' )
227
		);
228
229
		$last_item = '';
230
231
		if ( isset( $defaults['date'] ) ) { unset( $defaults['date'] ); }
232
233
		if ( count( $defaults ) > 2 ) {
234
			$last_item = array_slice( $defaults, -1 );
235
			array_pop( $defaults );
236
		}
237
238
		$defaults = array_merge( $defaults, $new_columns );
239
240
		if ( $last_item != '' ) {
241
			foreach ( $last_item as $k => $v ) {
0 ignored issues
show
Bug introduced by
The expression $last_item of type array<string,?,{"date":"?"}>|string is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
242
				$defaults[$k] = $v;
243
				break;
244
			}
245
		}
246
247
		return $defaults;
248
	}
249
250
251
	/**
252
	 * Register new columns for commissions list table
253
	 *
254
	 * @access      public
255
	 * @since       1.0.0
256
	 * @param       string $column_name Name of column
257
	 * @param       int $id ID of commission
258
	 * @return      void
259
	 */
260
	public function register_custom_columns( $column_name, $id ) {
261
		global $woo_vendors;
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...
262
263
		$data = get_post_meta( $id , $column_name , true );
264
265
		switch ( $column_name ) {
266
			case '_commission_product':
267
				if( $data && strlen( $data ) > 0 ) {
268
					if( function_exists( 'get_product' ) ) {
269
						$product = get_product( $data );
270
					} else {
271
						$product = new WC_Product( $data );
272
					}
273
					$edit_url = 'post.php?post=' . $data . '&action=edit';
274
					echo '<a href="' . esc_url( $edit_url ) . '">' . $product->get_title() . '</a>';
275
				}
276
				break;
277
			case '_commission_vendor':
278
				if( $data && strlen( $data ) > 0 ) {
279
					$vendor = printcenter_get_vendor( $data );
280
					if( $vendor ) {
281
						$edit_url = 'edit-tags.php?action=edit&taxonomy=' . $woo_vendors->token . '&tag_ID=' . $vendor->ID . '&post_type=product';
282
						echo '<a href="' . esc_url( $edit_url ) . '">' . $vendor->title . '</a>';
283
					}
284
				}
285
				break;
286
			case '_commission_amount':
287
				echo get_woocommerce_currency_symbol() . number_format( (double) $data, 2 );
288
				break;
289
			case '_paid_status':
290
				echo ucfirst( $data );
291
				break;
292
			default:
293
				break;
294
		}
295
	}
296
297
298
	/**
299
	 * Update messages for commission posts
300
	 *
301
	 * @access      public
302
	 * @since       1.0.0
303
	 * @param       array $messages Current messages
304
	 * @return      array Modified messages
305
	 */
306
	public function updated_messages( $messages ) {
307
		global $post, $post_ID;
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...
308
309
		$messages[ $this->token ] = array(
310
			0  => '', // Unused. Messages start at index 1.
311
			1  => __( 'Commission updated.' , 'printcenter' ),
312
			2  => __( 'Custom field updated.' , 'printcenter' ),
313
			3  => __( 'Custom field deleted.' , 'printcenter' ),
314
			4  => __( 'Commission updated.' , 'printcenter' ),
315
			5  => isset($_GET['revision']) ? sprintf( __( 'Commission restored to revision from %s.' , 'printcenter' ), wp_post_revision_title( (int) $_GET['revision'], false ) ) : false,
316
			6  => __( 'Commission published.' , 'printcenter' ),
317
			7  => __( 'Commission saved.' , 'printcenter' ),
318
			8  => __( 'Commission submitted.' , 'printcenter' ),
319
			9  => sprintf( __( 'Commission scheduled for: %1$s.' , 'printcenter' ), '<strong>' . date_i18n( __( 'M j, Y @ G:i' , 'printcenter' ), strtotime( $post->post_date ) ) . '</strong>' ),
320
			10 => __( 'Commission draft updated.' , 'printcenter' ),
321
		);
322
323
		return $messages;
324
	}
325
326
327
	/**
328
	 * Add custom actions to commission posts
329
	 *
330
	 * @access      public
331
	 * @since       1.0.0
332
	 * @return      void
333
	 */
334
	public function custom_actions_content() {
335
		global $post;
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...
336
337
		if( get_post_type( $post ) == $this->token ) {
338
			echo '<div class="misc-pub-section misc-pub-section-last">';
339
			wp_nonce_field( plugin_basename( $this->file ), 'paid_status_nonce' );
340
341
			$status = get_post_meta( $post->ID, '_paid_status', true ) ? get_post_meta( $post->ID, '_paid_status', true ) : 'unpaid';
342
343
			echo '<input type="radio" name="_paid_status" id="_paid_status-unpaid" value="unpaid" ' . checked( $status, 'unpaid', false ) . ' /> <label for="_paid_status-unpaid" class="select-it">Unpaid</label>&nbsp;&nbsp;&nbsp;&nbsp;';
344
			echo '<input type="radio" name="_paid_status" id="_paid_status-paid" value="paid" ' . checked( $status, 'paid', false ) . '/> <label for="_paid_status-paid" class="select-it">Paid</label>';
345
			echo '</div>';
346
		}
347
	}
348
349
350
	/**
351
	 * Save custom actions for commission posts
352
	 *
353
	 * @access      public
354
	 * @since       1.0.0
355
	 * @param       int $post_id Commission ID
356
	 * @return      void
357
	 */
358
	public function custom_actions_save( $post_id ) {
359
		if( isset( $_POST['paid_status_nonce'] ) ) {
360
			if ( ! wp_verify_nonce( $_POST['paid_status_nonce'], plugin_basename( $this->file ) ) ) {
361
				return $post_id;
362
			}
363
364
			if( isset( $_POST['_paid_status'] ) ) {
365
				update_post_meta( $post_id, '_paid_status', $_POST['_paid_status'] );
366
			}
367
		}
368
	}
369
370
371
	/**
372
	 * Add meta box to commission posts
373
	 *
374
	 * @access      public
375
	 * @since       1.0.0
376
	 * @return      void
377
	 */
378
	public function meta_box_setup() {
379
		add_meta_box( 'commission-data', __( 'Commission Details' , 'printcenter' ), array( &$this, 'meta_box_content' ), $this->token, 'normal', 'high' );
380
	}
381
382
383
	/**
384
	 * Add content to meta box on commission posts
385
	 *
386
	 * @access      public
387
	 * @since       1.0.0
388
	 * @return      void
389
	 */
390
	public function meta_box_content() {
391
		global $post_id, $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...
392
393
		$fields     = get_post_custom( $post_id );
394
		$field_data = $this->get_custom_fields_settings();
395
396
		$html  = '';
397
		$html .= '<input type="hidden" name="' . $this->token . '_nonce" id="' . $this->token . '_nonce" value="' . wp_create_nonce( plugin_basename( $this->dir ) ) . '" />';
398
399
		if( 0 < count( $field_data ) ) {
400
			$html .= '<table class="form-table">' . "\n";
401
			$html .= '<tbody>' . "\n";
402
403
			foreach ( $field_data as $k => $v ) {
404
				$data = $v['default'];
405
406
				if ( isset( $fields[$k] ) && isset( $fields[$k][0] ) ) {
407
					$data = $fields[$k][0];
408
				}
409
410
				if( $k == '_commission_product' ) {
411
					$option = '<option value=""></option>';
412
413
					if( $data && strlen( $data ) > 0 ) {
414
						if( function_exists( 'get_product' ) ) {
415
							$product = get_product( $data );
416
						} else {
417
							$product = new WC_Product( $data );
418
						}
419
						$option = '<option value="' . $data . '" selected="selected">' . $product->get_title() . '</option>';
420
					}
421
422
					$html .= '<tr valign="top"><th scope="row"><label for="' . esc_attr( $k ) . '">' . $v['name'] . '</label></th><td><select name="' . esc_attr( $k ) . '" id="' . esc_attr( $k ) . '" class="ajax_chosen_select_products_and_variations" data-placeholder="Search for product&hellip;" style="min-width:300px;">' . $option . '</select>' . "\n";
423
					$html .= '<p class="description">' . $v['description'] . '</p>' . "\n";
424
					$html .= '</td><tr/>' . "\n";
425
				} elseif( $k == '_commission_vendor' ) {
426
					$option = '<option value=""></option>';
427
					if( $data && strlen( $data ) > 0 ) {
428
						$vendor = printcenter_get_vendor( $data );
429
						$option = '<option value="' . $vendor->ID . '" selected="selected">' . $vendor->title . '</option>';
430
					}
431
432
					$html .= '<tr valign="top"><th scope="row"><label for="' . esc_attr( $k ) . '">' . $v['name'] . '</label></th><td><select name="' . esc_attr( $k ) . '" id="' . esc_attr( $k ) . '" class="ajax_chosen_select_vendor" data-placeholder="Search for vendor&hellip;" style="min-width:300px;">' . $option . '</select>' . "\n";
433
					$html .= '<p class="description">' . $v['description'] . '</p>' . "\n";
434
					$html .= '</td><tr/>' . "\n";
435
				} else {
436
					if( $v['type'] == 'checkbox' ) {
437
						$html .= '<tr valign="top"><th scope="row">' . $v['name'] . '</th><td><input name="' . esc_attr( $k ) . '" type="checkbox" id="' . esc_attr( $k ) . '" ' . checked( 'on' , $data , false ) . ' /> <label for="' . esc_attr( $k ) . '"><span class="description">' . $v['description'] . '</span></label>' . "\n";
438
						$html .= '</td><tr/>' . "\n";
439
					} elseif ( $v['type'] == 'post_edit_link' ) {
440
						if( $data ) {
441
							$post_title = get_the_title( $data );
442
							$html .= '<tr valign="top"><th scope="row"><label for="' . esc_attr( $k ) . '">' . $v['name'] . '</label></th><td><a href="' . admin_url( 'post.php?post=' . $data . '&action=edit' ) . '">' . $post_title . '</a>' . "\n";
443
							$html .= '<p class="description">' . $v['description'] . '</p>' . "\n";
444
							$html .= '</td><tr/>' . "\n";
445
						}
446
					} else {
447
						$html .= '<tr valign="top"><th scope="row"><label for="' . esc_attr( $k ) . '">' . $v['name'] . '</label></th><td><input name="' . esc_attr( $k ) . '" type="text" id="' . esc_attr( $k ) . '" class="regular-text" value="' . esc_attr( $data ) . '" />' . "\n";
448
						$html .= '<p class="description">' . $v['description'] . '</p>' . "\n";
449
						$html .= '</td><tr/>' . "\n";
450
					}
451
				}
452
			}
453
454
			$html .= '</tbody>' . "\n";
455
			$html .= '</table>' . "\n";
456
		}
457
458
		wc_enqueue_js( "
459
			jQuery('select.ajax_chosen_select_products_and_variations').ajaxChosen({
460
				method: 	'GET',
461
				url: 		'" . admin_url('admin-ajax.php') . "',
462
				dataType: 	'json',
463
				afterTypeDelay: 100,
464
				data:		{
465
					action: 		'woocommerce_json_search_products_and_variations',
466
					security: 		'" . wp_create_nonce("search-products") . "'
467
				}
468
			}, function (data) {
469
				var terms = {};
470
471
				$.each(data, function (i, val) {
472
					terms[i] = val;
473
				});
474
475
				return terms;
476
			});
477
478
			jQuery('select.ajax_chosen_select_vendor').ajaxChosen({
479
				method: 		'GET',
480
				url: 			'" . admin_url( 'admin-ajax.php' ) . "',
481
				dataType: 		'json',
482
				afterTypeDelay: 100,
483
				minTermLength: 	1,
484
				data:		{
485
					action: 	'woocommerce_json_search_vendors',
486
					security: 	'" . wp_create_nonce( 'search-vendors' ) . "'
487
				}
488
			}, function (data) {
489
				var terms = {};
490
491
				$.each(data, function (i, val) {
492
					terms[i] = val;
493
				});
494
495
				return terms;
496
			});
497
		" );
498
499
		echo $html;
500
	}
501
502
503
	/**
504
	 * Save meta box on commission posts
505
	 *
506
	 * @access      public
507
	 * @since       1.0.0
508
	 * @param       int $post_id Commission ID
509
	 * @return      void
510
	 */
511
	public function meta_box_save( $post_id ) {
512
		global $post, $messages;
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...
513
514
		// Verify nonce
515
		if( ( get_post_type() != $this->token ) || ! wp_verify_nonce( $_POST[ $this->token . '_nonce'], plugin_basename( $this->dir ) ) ) {
516
			return $post_id;
517
		}
518
519
		// Verify user permissions
520
		if( ! current_user_can( 'edit_post', $post_id ) ) {
521
			return $post_id;
522
		}
523
524
		// Handle custom fields
525
		$field_data = $this->get_custom_fields_settings();
526
		$fields = array_keys( $field_data );
527
528
		foreach( $fields as $f ) {
529
			if( 'post_edit_link' == $field_data[$f]['type'] ) {
530
				continue;
531
			}
532
533
			if( isset( $_POST[$f] ) ) {
534
				${$f} = strip_tags( trim( $_POST[$f] ) );
535
			}
536
537
			// Escape the URLs.
538
			if( 'url' == $field_data[$f]['type'] ) {
539
				${$f} = esc_url( ${$f} );
540
			}
541
542
			if( ${$f} == '' ) {
543
				delete_post_meta( $post_id , $f , get_post_meta( $post_id , $f , true ) );
544
			} else {
545
				update_post_meta( $post_id , $f , ${$f} );
546
			}
547
		}
548
	}
549
550
551
	/**
552
	 * Change 'Enter title here' text for commission posts
553
	 *
554
	 * @access      public
555
	 * @since       1.0.0
556
	 * @param       string $title Existing text
557
	 * @return      string Modified text
558
	 */
559
	public function enter_title_here( $title ) {
560
		if ( get_post_type() == $this->token ) {
561
			$title = __( 'Enter the commission title here (optional)' , 'woocommerce' );
562
		}
563
564
		return $title;
565
	}
566
567
568
	/**
569
	 * Add custom field to commission posts
570
	 *
571
	 * @access      public
572
	 * @since       1.0.0
573
	 * @return      array Array of custom fields
574
	 */
575
	public function get_custom_fields_settings() {
576
		$fields = array();
577
578
		$fields['_commission_product'] = array(
579
			'name'        => __( 'Product:' , 'woocommerce' ),
580
			'description' => __( 'The product purchased that generated this commission.' , 'woocommerce' ),
581
			'type'        => 'select',
582
			'default'     => '',
583
			'section'     => 'commission-data'
584
		);
585
586
		$fields['_commission_vendor'] = array(
587
			'name'        => __( 'Vendor:' , 'woocommerce' ),
588
			'description' => __( 'The vendor who receives this commission.' , 'woocommerce' ),
589
			'type'        => 'select',
590
			'default'     => '',
591
			'section'     => 'commission-data'
592
		);
593
594
		$fields['_commission_amount'] = array(
595
			'name'        => __( 'Amount:' , 'woocommerce' ),
596
			'description' => __( 'The total value of this commission (' . get_woocommerce_currency_symbol() . ').' , 'woocommerce' ),
597
			'type'        => 'text',
598
			'default'     => 0.00,
599
			'section'     => 'commission-data'
600
		);
601
602
		$fields['_commission_order'] = array(
603
			'name'        => __( 'Order:' , 'woocommerce' ),
604
			'description' => __( 'The order from which this commission was generated.' , 'woocommerce' ),
605
			'type'        => 'post_edit_link',
606
			'default'     => '',
607
			'section'     => 'commission-data'
608
		);
609
610
		return $fields;
611
	}
612
613
614
	/**
615
	 * Add bulk action options to commission list table
616
	 *
617
	 * @access      public
618
	 * @since       1.0.0
619
	 * @return      void
620
	 */
621
	public function add_bulk_action_options() {
622
		global $post_type;
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...
623
624
		if( $post_type == $this->token ) { ?>
625
			<script type="text/javascript">
626
				jQuery(document).ready(function() {
627
					jQuery('<option>').val('export').text('<?php _e('Export unpaid commissions (CSV)', 'printcenter' ); ?>').appendTo("select[name='action']");
628
					jQuery('<option>').val('export').text('<?php _e('Export unpaid commissions (CSV)', 'printcenter' ); ?>').appendTo("select[name='action2']");
629
					jQuery('<option>').val('mark_paid').text('<?php _e('Mark all commissions as paid', 'printcenter' ); ?>').appendTo("select[name='action']");
630
					jQuery('<option>').val('mark_paid').text('<?php _e('Mark all commissions as paid', 'printcenter' ); ?>').appendTo("select[name='action2']");
631
				});
632
			</script>
633
		<?php }
634
	}
635
636
637
	/**
638
	 * Create export CSV for unpaid commissions
639
	 *
640
	 * @access      public
641
	 * @since       1.0.0
642
	 * @return      void
643
	 */
644
	public function generate_commissions_csv() {
645
		global $woo_vendors, $typenow;
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...
646
647
		if( $typenow == $this->token ) {
648
649
			// Confirm list table action
650
			$wp_list_table = _get_list_table( 'WP_Posts_List_Table' );
651
			$action        = $wp_list_table->current_action();
652
653
			if( $action != 'export' ) {
654
				return;
655
			}
656
657
			// Security check
658
			check_admin_referer( 'bulk-posts' );
659
660
			// Set filename
661
			$date     = date( 'd-m-Y H:i:s' );
662
			$filename = 'Commissions ' . $date . '.csv';
663
664
			// Set page headers to force download of CSV
665
			header("Pragma: public");
666
			header("Expires: 0");
667
			header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
668
			header("Content-Type: application/force-download");
669
			header("Content-Type: application/octet-stream");
670
			header("Content-Type: application/download");
671
			header("Content-Disposition: attachment;filename={$filename}");
672
			header("Content-Transfer-Encoding: binary");
673
674
			// Set CSV headers
675
			$headers = array(
676
				'Recipient',
677
				'Payment',
678
				'Currency',
679
				'Customer ID',
680
				'Note'
681
			);
682
683
			// Get data for CSV
684
			$args = array(
685
				'post_type'      => $this->token,
686
				'post_status'    => array( 'publish', 'private' ),
687
				'meta_key'       => '_paid_status',
688
				'meta_value'     => 'unpaid',
689
				'posts_per_page' => -1
690
			);
691
692
			$commissions = get_posts( $args );
693
694
			// Get total commissions for each vendor
695
			$commission_totals = array();
696
			foreach( $commissions as $commission ) {
697
				// Get commission data
698
				$commission_data = printcenter_get_commission( $commission->ID );
699
700
				// Increment total amount for each vendor
701
				if( ! isset( $commission_totals[ $commission_data->vendor->ID ] ) ) {
702
					$commission_totals[ $commission_data->vendor->ID ] = (float) 0;
703
				}
704
705
				$commission_totals[ $commission_data->vendor->ID ] += (float) $commission_data->amount;
706
			}
707
708
			// Set info for all payouts
709
			$currency = get_woocommerce_currency();
710
			$payout_note = sprintf( __( 'Total commissions earned from %1$s as at %2$s on %3$s', 'printcenter' ), get_bloginfo( 'name' ), date( 'H:i:s' ), date( 'd-m-Y' ) );
711
712
			// Set up data for CSV
713
			$commissions_data = array();
714
715
			foreach( $commission_totals as $vendor_id => $total ) {
716
				// Get vendor data
717
				$vendor = printcenter_get_vendor( $vendor_id );
718
719
				// Set vendor recipient field
720
				if( isset( $vendor->paypal_email ) && strlen( $vendor->paypal_email ) > 0 ) {
721
					$recipient = $vendor->paypal_email;
722
				} else {
723
					$recipient = $vendor->title;
724
				}
725
726
				$commissions_data[] = array(
727
					$recipient,
728
					$total,
729
					$currency,
730
					$vendor_id,
731
					$payout_note
732
				);
733
			}
734
735
			// Initiate output buffer and open file
736
			ob_start();
737
			$file = fopen( "php://output", 'w' );
738
739
			// Add headers to file
740
			fputcsv( $file, $headers );
741
742
			// Add data to file
743
			foreach ( $commissions_data as $commission ) {
744
				fputcsv( $file, $commission );
745
			}
746
747
			// Close file and get data from output buffer
748
			fclose( $file );
749
			$csv = ob_get_clean();
750
751
			// Send CSV to browser for download
752
			echo $csv;
753
			die();
0 ignored issues
show
Coding Style Compatibility introduced by
The method generate_commissions_csv() 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...
754
		}
755
	}
756
757
	/**
758
	 * Mark all unpaid commissions as paid
759
	 *
760
	 * @access      public
761
	 * @since       1.0.0
762
	 * @return      void
763
	 */
764
	public function mark_all_commissions_paid() {
765
		global $woo_vendors, $typenow;
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...
766
767
		if( $typenow == $this->token ) {
768
			// Confirm list table action
769
			$wp_list_table = _get_list_table( 'WP_Posts_List_Table' );
770
			$action        = $wp_list_table->current_action();
771
772
			if( $action != 'mark_paid' ) {
773
				return;
774
			}
775
776
			// Security check
777
			check_admin_referer( 'bulk-posts' );
778
779
			// Get all unpaid commissions
780
			$args = array(
781
				'post_type'      => $this->token,
782
				'post_status'    => array( 'publish', 'private' ),
783
				'meta_key'       => '_paid_status',
784
				'meta_value'     => 'unpaid',
785
				'posts_per_page' => -1
786
			);
787
788
			$commissions = get_posts( $args );
789
790
			foreach( $commissions as $commission ) {
791
				update_post_meta( $commission->ID, '_paid_status', 'paid', 'unpaid' );
792
			}
793
794
			$redirect = add_query_arg( 'message', 'paid', $_REQUEST['_wp_http_referer'] );
795
796
			wp_safe_redirect( $redirect );
797
			exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method mark_all_commissions_paid() 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...
798
		}
799
	}
800
801
802
	/**
803
	 * Load scripts and styles for the WP dashboard
804
	 *
805
	 * @access      public
806
	 * @since       1.0.0
807
	 * @return      void
808
	 */
809
	public function admin_enqueue_scripts() {
810
		global $woocommerce, $pagenow, $typenow;
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...
811
812
		// Load admin CSS
813
		//wp_enqueue_style( 'product_vendors_admin', $this->assets_url . 'css/admin.css' );
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
814
815
		if( in_array( $pagenow, array( 'post.php', 'post-new.php' ) ) && $typenow == $this->token ) {
816
			// Load Chosen CSS
817
			if( ! wp_style_is( 'woocommerce_chosen_styles', 'queue' ) ) {
818
				wp_enqueue_style( 'woocommerce_chosen_styles', $woocommerce->plugin_url() . '/assets/css/chosen.css' );
819
			}
820
821
			// Load Chosen JS
822
			wp_enqueue_script( 'chosen' );
823
			wp_enqueue_script( 'ajax-chosen' );
824
		}
825
	}
826
827
828
	/**
829
	 * Display admin notices in the WP dashboard
830
	 *
831
	 * @access      public
832
	 * @since       1.0.0
833
	 * @return      void
834
	 */
835
	public function admin_notices() {
836
		global $current_screen, $pagenow, $post_type;
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...
837
838
		$message = false;
839
840
		if( in_array( $pagenow, array( 'edit.php', 'post.php', 'post-new.php' ) ) && $post_type == $this->token ) {
841
			if( isset( $_GET['message'] ) && $_GET['message'] == 'paid' ) {
842
				$message = sprintf( __( '%1$sAll commissions have been marked as %2$spaid%3$s.%4$s', 'printcenter' ), '<div id="message" class="updated"><p>', '<b>', '</b>', '</p></div>' );
843
			} else {
844
				$vendors = printcenter_get_vendors();
845
846
				if( ! $vendors ) {
847
					$message = sprintf( __( '%1$s%2$sYou need to add vendors before commissions can be created.%3$s %4$sClick here to add your first vendor%5$s.%6$s', 'printcenter' ), '<div id="message" class="updated"><p>', '<b>', '</b>', '<a href="' . esc_url( admin_url( 'edit-tags.php?taxonomy=shop_vendor&post_type=product' ) ) . '">', '</a>', '</p></div>' );
848
				}
849
			}
850
		}
851
852
		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...
853
			echo $message;
854
		}
855
	}
856
}