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
		}
	}

}