ordernumbers_woocommerce_basic.php 19.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
/**
 * This is the actual ordernumber plugin class for WooCommerce.
 * Copyright (C) 2015 Reinhold Kainhofer, Open Tools
 * Author: Open Tools, Reinhold Kainhofer
 * Author URI: http://open-tools.net
 * License: GPL2+
*/
if ( ! defined( 'ABSPATH' ) ) { 
	exit; // Exit if accessed directly
}
require_once( plugin_dir_path( __FILE__ ) . '/ordernumber_helper_woocommerce.php');

14
15
16



17
18
19
20
class OpenToolsOrdernumbersBasic {
	public $ordernumber_meta = "_oton_number_";
	public $ordernumber_new_placeholder = "[New Order]";
	public $plugin_basename = '';
21
22
23
24
25
	public $plugin_config_link = 'admin.php?page=wc-settings&tab=checkout&section=ordernumber';
	public $plugin_url = 'https://wordpress.org/plugins/woocommerce-basic-ordernumbers/';
	public $plugin_url_advanced = 'http://open-tools.net/woocommerce/advanced-ordernumbers-for-woocommerce.html';
	public $plugin_url_docs = 'http://open-tools.net/documentation/advanced-order-numbers-for-woocommerce.html';
	public $plugin_url_support = 'http://open-tools.net/support-forum/ordernumbers-for-woocommerce.html';
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
	
	protected $helper = null;
	protected $settings = array();
	protected $is_advanced = false;
	
	/**
	 * Construct the plugin object
	 */
	public function __construct($basename)
	{
		$this->helper = OrdernumberHelperWooCommerce::getHelper();
		$this->plugin_basename = $basename;
		$this->initializeBasicSettings();
		$this->initializeSettings();
		$this->initializeHooks();
	}
	
	/**
	 * Set up the functionality of the basic version of the plugin
	 */
	protected function initializeBasicSettings() {
		$this->helper->registerCallback('setupDateTimeReplacements',	array($this, 'setupDateTimeReplacements'));
		
		add_filter( 'plugin_row_meta', 									array( &$this, 'basic_ordernumber_plugin_row_meta' ), 30, 2 );
		add_filter( 'woocommerce_admin_field_opentools_ordernumbers_upgrade', array( &$this, 'admin_field_opentools_ordernumbers_upgrade') );
		$this->helper->setFlag('extract-counter-settings', false);
	}

	/**
	 * Install all neccessary filters and actions for this plugin
	 */
	protected function initializeHooks() {
		$helper = OrdernumberHelperWooCommerce::getHelper();
59
60
61
62
63
64
65
66
67
		// Information for other plugins
		add_filter( 'opentools_ordernumbers_activated', 		array( &$this, 'ordernumbers_activated'));
		add_filter( 'opentools_invoicenumbers_activated', 		array( &$this, 'invoicenumbers_activated'));
		add_filter ('woocommerce_invoice_number_by_plugin', array( &$this, 'invoicenumbers_activated'));
		add_filter ('woocommerce_order_number_by_plugin', array( &$this, 'ordernumbers_activated'));
		add_filter ('woocommerce_invoice_number_configuration_link', array( &$this, 'invoicenumbers_config_link'));
		add_filter ('woocommerce_order_number_configuration_link', array( &$this, 'ordernumbers_config_link'));
		
		
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
		// CONFIGURATION SCREENS
		add_filter( 'woocommerce_get_sections_checkout',				array( &$this, 'add_admin_section'));
		// The checkout settings page assumes all subpages are payment gateways, so we have to override this and manually pass our settings:
		add_action( 'woocommerce_settings_checkout',					array( &$this, 'settings_output' ) );
		add_action( 'woocommerce_settings_save_checkout',				array( &$this, 'settings_save' ) );
		add_action( 'woocommerce_admin_field_ordernumber_counters',		array( &$this, 'admin_field_counters' ) );
		// Add links to WordPress plugins page
		add_filter( 'plugin_action_links_'.$this->plugin_basename,		array( &$this, 'ordernumber_add_settings_link' ) );
		add_filter( 'plugin_row_meta', 									array( &$this, 'ordernumber_plugin_row_meta' ), 10, 2 );
		
		add_action( 'woocommerce_order_actions',						array( &$this, 'add_order_action_new_number' ) );
		add_action( 'woocommerce_order_action_assign_new_ordernumber',	array( &$this, 'order_action_assign_new_ordernumber' ) );

		add_action( 'admin_print_styles-woocommerce_page_wc-settings',	array( &$helper, 'print_admin_styles'));
		add_action( 'admin_print_styles-woocommerce_page_wc-settings',	array( &$this, 'print_admin_styles'));
		add_action( 'admin_print_scripts-woocommerce_page_wc-settings',	array( &$this, 'print_admin_scripts'));
		
		// AJAX counter modifications
		add_action( 'wp_ajax_setCounter',		array( &$this, 'counter_set_callback') );
		add_action( 'wp_ajax_addCounter',		array( &$this, 'counter_add_callback') );
		add_action( 'wp_ajax_deleteCounter',	array( &$this, 'counter_delete_callback') );

		// Add the ordernumber post meta to the search in the backend
		add_filter( 'woocommerce_shop_order_search_fields',		array( &$this, 'order_search_fields'));
		// Sort the order list in the backend by order number rather than ID, make sure this is called LAST so we modify the defaults passed as arguments
		add_filter( 'manage_edit-shop_order_sortable_columns',	array( &$this, 'modify_order_column_sortkey' ), 9999 );

		// When a new order is created, we immediately assign the order number:
		add_action( 'wp_insert_post',							array( &$this, 'check_assignNumber'), 10, 3);
		// The filter to actually return the order number for the given order
		add_filter ('woocommerce_order_number',					array( &$this, 'get_ordernumber'), 10, 2/*<= Also get the order object! */);
		
	}
	

	/**
	 * Setup all options and the corresponding settings UI to configure this plugin, using the WP settings API
	 */
	protected function initializeSettings() {
		// TODO: Call some virtual function for the upgrade NAG text...
		$this->settings = array_merge(
			$this->initializeSettingsGeneral(),
			$this->initializeSettingsOrderNumbers(),
			$this->initializeSettingsOther()
		);
	}

	protected function initializeSettingsGeneral() {
		// TODO: Add some kind of NAG screen to advertize the premium version
		$settings = array(
			array(
				'name' 		=> $this->helper->__( 'Upgrade to the ADVANCED VERSION of the OpenTools Ordernumber plugin'),
				'desc'		=> $this->helper->__( 'This basic version has limited functionality...'),
				'desc_tip'	=> true,
				'id' 		=> 'opentools_ordernumbers_upgrade',
				'type' 		=> 'opentools_ordernumbers_upgrade',
124
				'link'		=> $this->plugin_url_advanced,
125
126
127
128
129
			),
		);
		return $settings;
	}
	protected function initializeSettingsOrderNumbers() {
130
131
		$settings = array();
		$settings[] = array(
132
				'name' 		=> $this->helper->__( 'Configure Order Numbers'),
133
				'desc'		=> sprintf( $this->helper->__( 'Configure the format and the counters of the order numbers in WooCommerce. For help, check out the plugin\'s <a href="%s">documentation at OpenTools</a>.'), esc_attr($this->plugin_url_docs)),
134
135
				'type' 		=> 'title',
				'id' 		=> 'ordernumber_options'
136
			);
137

138
		$settings[] = array(
139
140
141
142
143
				'name' 		=> $this->helper->__( 'Customize Order Numbers'),
				'desc' 		=> $this->helper->__( 'Check to use custom order numbers rather than the default wordpress post ID.'),
				'id' 		=> 'customize_ordernumber',
				'type' 		=> 'checkbox',
				'default'	=> 'no'
144
145
			);
		$settings[] = array(
146
147
148
149
150
151
152
				'title'		=> $this->helper->__( 'Order number format'),
				'desc' 		=> $this->getNumberFormatSettingsLabel(),
				'desc_tip'	=> true,
				'id' 		=> 'ordernumber_format',
				'default'	=> '#',
				'type' 		=> 'text',
				'css'		=> 'width: 100%',
153
154
155
			);
		$settings = $this->addGlobalCounterSettings($settings);
		$settings[] = array(
156
157
158
159
160
161
				'name' 		=> $this->helper->__( 'All order number counters'),
				'desc'		=> $this->helper->__( 'View and modify the current counter values. The counter value is the value used for the previous number. All changes are immediately applied!'),
				'desc_tip'	=> true,
				'id' 		=> 'ordernumber_counters',
				'type' 		=> 'ordernumber_counters',
				'nrtype' 	=> 'ordernumber',
162
163
164
			);
		$settings[] = array('type' => 'sectionend', 'id' => 'ordernumber_options' );

165
166
167
168
169
170
171
172
173
		add_option ('customize_ordernumber', 'no');
		add_option ('ordernumber_format',    "#");
		add_option ('ordernumber_global',    'no');
		return $settings;
	}
	/**
	 * Return the tooltip for the number format settings textinput (the two plugin versions have different features!)
	 */
	protected function getNumberFormatSettingsLabel() {
174
175
176
177
		return $this->helper->__( 'The format for the order numbers: You can choose any text string, where the counter is indicated by #. For example, a format "WC-#" will create order numbers "WC-376", "WC-377", "WC-378", ...<br>In the <b>advanced version</b> of the plugin, variables can be indicated [...], e.g. [year].');
	}
	protected function addGlobalCounterSettings($settings) {
		return $settings;
178
179
180
181
182
	}
	
	protected function initializeSettingsOther() {
		return array();
	}
183
184
185
186
187
188
189
190
191
192
193
194
	
	
	/**
	 * Filters for other plugins to get information about this one, e.g.
	 * indicating whether invoice/order numbers are to be created by this plugin
	 * and getting the link to the configuration page.
	 */
	
	public function numbers_activated($type) {
		return (get_option('customize_'.$type, 'no')!='no');
	}
	public function ordernumbers_activated($default=false) {
195
		return $default || $this->numbers_activated('ordernumber');
196
197
	}
	public function invoicenumbers_activated($default=false) {
198
		return $default || $this->numbers_activated('invoice');
199
200
201
202
203
204
205
206
207
208
209
210
211
212
	}
	
	public function invoicenumbers_config_link($default=null) {
// 		if ($this->invoicenumbers_activated())
			return $this->plugin_config_link;
// 		else
// 			return $default;
	}
	public function ordernumbers_config_link($default=null) {
// 		if ($this->invoicenumbers_activated())
			return $this->plugin_config_link;
// 		else
// 			return $default;
	}
213
214
215
216
217

	/**
	 * Add settings link to plugins page
	 */
	public function ordernumber_add_settings_link( $links ) {
218
		$link = '<a href="'.esc_attr($this->plugin_config_link).'">'. $this->helper->__( 'Settings' ) . '</a>';
219
220
221
		// Prepend the settings link:
		array_unshift( $links, $link );
// 		$links['settings'] = $link;
222
223
224
225
		return $links;
	}
	public function ordernumber_plugin_row_meta( $links, $file ) {
		if ($file==$this->plugin_basename) {
226
227
			$links['docs'] = '<a href="' . esc_url( $this->plugin_url_docs ) . '" title="' . esc_attr( $this->helper->__( 'Plugin Documentation' ) ) . '">' . $this->helper->__( 'Plugin Documentation' ) . '</a>';
			$links['support'] = '<a href="' . esc_url( $this->plugin_url_support ) . '" title="' . esc_attr( $this->helper->__( 'Support Forum' ) ) . '">' . $this->helper->__( 'Support Forum' ) . '</a>';
228
229
230
231
232
233
		}
		return (array)$links;
	}
	
	public function basic_ordernumber_plugin_row_meta( $links, $file ) {
		if ($file==$this->plugin_basename && !$this->is_advanced) {
234
			$links['advanced'] = '<a href="' . esc_url( $this->plugin_url_advanced ) . '" title="' . esc_attr( $this->helper->__('Purchase Advanced Version')) . '">' . $this->helper->__('Purchase Advanced Version') . '</a>';
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
		}
		return (array)$links;
	}
	
	/**
	 * Insert our own section in the checkout setting page. Rearrange the sections array to make sure our settings
	 * come second place, directly after the default page with the '' key and before all the payment gateways
	 */
	function add_admin_section($sections) {
		$newsections = array();
		foreach ($sections as $sec => $name ) {
			$newsections[$sec] = $name;
			if ($sec == '') {
				$newsections['ordernumber'] = $this->helper->__('Order Numbers');
			}
		}
		return $newsections;
	}
	
	public function settings_output() {
		global $current_section;
		if ($current_section == 'ordernumber') {
			$settings = $this->settings;
			WC_Admin_Settings::output_fields( $settings );
		}
	}

	public function settings_save() {
		global $current_section;
		if ($current_section == 'ordernumber') {
			$settings = $this->settings;
			WC_Admin_Settings::save_fields( $settings );
		}
	}
	
	/** 
	 * Print the JS for the counter values and counter variables tables to the page header in the WC backend admin setting page
	 */
	public function print_admin_scripts() {
		$this->helper->print_admin_scripts();
		wp_register_script( 'ordernumber-admin', plugins_url('assets/js/ordernumber-config.js', __FILE__), array('jquery'));
		wp_enqueue_script( 'ordernumber-admin');
	}
    public function print_admin_styles() {
		wp_register_style('ordernumber-wc-styles',  plugins_url('assets/css/ordernumber-config.css', __FILE__));
		wp_enqueue_style('ordernumber-wc-styles');
	}
	



	/**
	 * Render the Counter Values modification table
	 */
	public function admin_field_counters ($settings) {
		// Description handling
		$field_description = WC_Admin_Settings::get_field_description( $settings );
		extract( $field_description );
		?>

		
		<tr valign="top">
			<th scope="row" class="titledesc">
				<label for="<?php echo esc_attr( $settings['id'] ); ?>"><?php echo esc_html( $settings['title'] ); ?></label>
				<?php echo $tooltip_html; ?>
			</th>
		    <td class="forminp forminp-<?php echo sanitize_title( $settings['nrtype'] ) ?>">
				<?php 
					$counters = $this->helper->getAllCounters($settings['nrtype']);
					echo $this->helper->counter_modification_create_table($settings['nrtype'], $counters);
				?>
			</td>
		</tr> 
		<?php
	}
	

	/**
	 * Render the UPGRADE TO ADVANCED VERSION nag table in the settings page
	 */
	public function admin_field_opentools_ordernumbers_upgrade ($settings) {
		// Description handling
		$field_description = WC_Admin_Settings::get_field_description( $settings );
		extract( $field_description );
		?>

		
		<tr valign="top">
			<td colspan="2">
				<div id="opentools-ordernumber-upgrade" class="postbox">
					<h3><?php echo esc_html($settings['title']); ?></h3>
					<div class="contents">
						<div class="logoleft"><a href="<?php echo esc_html($settings['link']); ?>"><img src="<?php echo plugins_url('assets/images/advlogo100.png', __FILE__); ?>"></a></div>
					<p>Advanced features not included in the free plugin include:</p>
					<ul>
						<li><b>Counter formatting</b>: initial value, counter steps, counter padding</li>
						<li><b>Many variables</b>: month, day, time, address fields, order properties (amount, # of articles, shipping methods), product categories and tags, etc.</li>
						<li>Multiple <b>concurrent counters</b>: Separate counters per country, per product category, etc.</li>
						<li><b>Different order number formats</b> for orders with specific properties (e.g. numbers "FREE-#" for free orders)</li>
						<li><b>Flexible counter resets</b>: Counter "resets" when any variable changes</li>
						<li>Customize <b>invoice numbers</b> (only for the <a href="https://wordpress.org/plugins/woocommerce-pdf-invoices-packing-slips/">"WooCommerce PDF Invoices and Package Slips"</a> plugin)</li>
						<li>...</li>
					</ul>
					<p>More information and purchase: <a class="button-primary" href="<?php echo esc_html($settings['link']); ?>" target="_blank">Get Support and advanced features</a></p>
					</div>
				</div>
			</td>
		</tr>
		<?php
	}
	
	
	/** 
	 * Hook to add the order numer post meta field to the searchable field 
	 * (so the admin can search for the order number in the backend)
	 */
	public function order_search_fields($fields) {
		$fields[] = $this->ordernumber_meta;
		return $fields;
	}
	
	/**
	 * Sort the order list's "Order" column by our post meta rather than by ID
	 */
	public function modify_order_column_sortkey($columns) {
		$columns['order_title'] = $this->ordernumber_meta;
		return $columns;
	}
	
	/**
	 * Add the "create new order number" action to the edit order page in the Backend
	 */
	public function add_order_action_new_number($actions) {
		$actions['assign_new_ordernumber'] = $this->helper->__('Assign a new order number');
		return $actions;
	}
	/**
	 * Handle the "Assign a new order number" action from the edit order page in the backend
	 */
	public function order_action_assign_new_ordernumber( $order ) {
		$number = $this->generateNumber($order->id, $order, 'ordernumber');
	}
		
	/** 
	 * Handle new posts created in the frontend. This action will be called for all posts, 
	 * not only for orders, so we need to check explicitly. Also, this function will be called
	 * for order updates, so we need to check the update argument, too.
	 */
	public function check_assignNumber($post_id, $post, $update) {
		// Is the post really an order?
		// Order numbers are only assigned to orders on creation, not when updating!
		if ($post->post_type != 'shop_order') {
			return;
		} else {
			// Handle new admin-created orders, where the address is entered later on!
			// Assign an order number:
			$number = $this->assign_new_ordernumber($post_id, $post, $update);
		}
	}

	/**
	 * AJAX Counter handling (simple loading/storing counters), storing them as options
	 */
	
	public function counter_delete_callback() {
		$json = $this->helper->ajax_counter_delete($_POST['nrtype'], $_POST['counter']);
		wp_send_json($json);
	}

	public function counter_add_callback () {
		$json = $this->helper->ajax_counter_add($_POST['nrtype'], $_POST['counter'], isset($_POST['value'])?$_POST['value']:"0");
		wp_send_json($json);
	}
	
	public function counter_set_callback () {
		$json = $this->helper->ajax_counter_set($_POST['nrtype'], $_POST['counter'], $_POST['value']);
		wp_send_json($json);
	}
	
	
	
	/** ***********************************************************
	 * 
	 *  REPLACEMENT FUNCTIONS
	 *
	 **************************************************************/
	
	/* Restrict date variables to years */
	public function setupDateTimeReplacements (&$reps, $details, $nrtype) {
424
425
426
// 		$utime = microtime(true);
// 		$reps["[year]"] = date ("Y", $utime);
// 		$reps["[year2]"] = date ("y", $utime);
427
428
	}

429
	function generateNumber($default, $order, $type='ordernumber') {
430
		if ($this->numbers_activated($type)) {
431
432
433
434
435
436
437
438
439
440
441
442
			$fmt     = get_option ($type.'_format',  "#");
			$ctrsettings = array(
				"${type}_format"  => '',
				"${type}_counter" => '',
				"${type}_global"  => get_option ($type.'_global',  'no'),
				"${type}_padding" => 1,
				"${type}_step"    => 1,
				"${type}_start"   => 1,
			);
			$customvars = get_option ('ordernumber_variables',   array());

			$number = $this->helper->createNumber ($fmt, $type, $order, $customvars, $ctrsettings);
443
			update_post_meta( $order->id, $this->ordernumber_meta.$type, $number );
444
445
			return $number;
		} else {
446
			return $default;
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
		}
	}
	
	/** 
	 * The hook to assign a customized order number (unless the order already has one assigned)
	 */
	function assign_new_ordernumber($orderid, $order, $update=true) {
		if ((!$update) /*&& ($order->post_status == 'auto-draft')*/) {
			// New order => assign placeholder, which will later be overwritten the real order number
			update_post_meta( $orderid, $this->ordernumber_meta.'ordernumber', $this->ordernumber_new_placeholder );
		}
		// If we do not have an order (yet), we cannot proceed. But we probably have created the 
		// ordernumber placeholder for that post, so this function has done its job and we can return
		if (!$order instanceof WC_Order) {
			return;
		}
		$number = get_post_meta( $orderid, $this->ordernumber_meta.'ordernumber', 'true');
		if ($number == $this->ordernumber_new_placeholder && $order->post_status != 'auto-draft') {
			$number = $this->generateNumber($orderid, $order, 'ordernumber');
			// Assign a new number
		}
		return $number;
	}

471
472
	function get_or_create_number($default, $order, $type = 'ordernumber') {
		$stored_number = get_post_meta( $order->id, $this->ordernumber_meta.$type, true);
473
474
475
		if (!empty($stored_number)) {
			return $stored_number;
		} else {
476
			return $this->generateNumber($order->id, $order, $type);
477
478
479
		}
	}
	
480
481
482
483
	/**
	 * Callback function for Woocommerce to retrieve the ordernumber for an order
	 * The hook to customize order numbers (requests the order number from the database; 
	 * creates a new ordernumber if no entry exists in the database)
484
	 */
485
486
487
	function get_ordernumber($orderid, $order) {
		$type = 'ordernumber';
		$stored_number = get_post_meta( $orderid, $this->ordernumber_meta.$type, 'true');
488
489
		if ($stored_number == $this->ordernumber_new_placeholder) {
			// Check whether the order was now really created => create order number now
490
			return $this->assign_new_ordernumber($orderid, $order);
491
492
493
494
495
		} elseif (!empty($stored_number)) {
			// Order number already exists => simply return it
			return $stored_number;
		} else {
			// No order number was created for this order, so simply use the orderid as default.
496
			return $default;
497
498
499
500
		}
	}

}