Commit 43edb2af authored by Franco nextime Lanza's avatar Franco nextime Lanza

Forket WooCommerce Product Add-ons

parents
<?php
/**
* Product_Addon_Admin class.
*/
class Product_Addon_Admin {
/**
* __construct function.
*
* @access public
* @return void
*/
public function __construct() {
add_action( 'woocommerce_admin_css', array( $this, 'styles' ) );
add_action( 'admin_menu', array( $this, 'admin_menu' ), 9 );
add_filter( 'woocommerce_screen_ids', array( $this, 'add_screen_id' ) );
add_action( 'woocommerce_product_write_panel_tabs', array( $this, 'tab' ) );
add_action( 'woocommerce_product_write_panels', array( $this, 'panel' ) );
add_action( 'woocommerce_process_product_meta', array( $this, 'process_meta_box'), 1, 2 );
}
/**
* Add menus
*/
public function admin_menu() {
$page = add_submenu_page( 'edit.php?post_type=product', __( 'Global Add-ons', 'wc_product_addons' ), __( 'Global Add-ons', 'wc_product_addons' ), 'manage_woocommerce', 'global_addons', array( $this, 'global_addons_admin' ) );
add_action( 'admin_print_styles-'. $page, array( &$this, 'admin_enqueue' ) );
}
/**
* admin_enqueue function.
*
* @access public
* @return void
*/
public function admin_enqueue() {
wp_enqueue_script( 'chosen' );
}
/**
* styles function.
*
* @access public
* @return void
*/
public function styles() {
wp_enqueue_style( 'woocommerce_product_addons_css', plugins_url( basename( dirname( dirname( __FILE__ ) ) ) ) . '/assets/css/admin.css' );
}
/**
* Add screen id to WooCommerce
*
* @param array $screen_ids
*/
public function add_screen_id( $screen_ids ) {
$screen_ids[] = 'product_page_global_addons';
return $screen_ids;
}
/**
* Controls the global addons admin page
* @return void
*/
public function global_addons_admin() {
if ( ! empty( $_GET['add'] ) || ! empty( $_GET['edit'] ) ) {
if ( $_POST ) {
if ( $this->save_global_addons() ) {
echo '<div class="updated"><p>' . __( 'Add-on saved successfully', 'wc_product_addons' ) . '</p></div>';
}
$reference = woocommerce_clean( $_POST['addon-reference'] );
$priority = absint( $_POST['addon-priority'] );
$objects = ! empty( $_POST['addon-objects'] ) ? array_map( 'absint', $_POST['addon-objects'] ) : array();
$product_addons = array_filter( (array) $this->get_posted_product_addons() );
}
if ( ! empty( $_GET['edit'] ) ) {
$edit_id = absint( $_GET['edit'] );
$global_addon = get_post( $edit_id );
if ( ! $global_addon ) {
echo '<div class="error">' . __( 'Error: Global Add-on not found', 'wc_product_addons' ) . '</div>';
return;
}
$reference = $global_addon->post_title;
$priority = get_post_meta( $global_addon->ID, '_priority', true );
$objects = (array) wp_get_post_terms( $global_addon->ID, 'product_cat', array( 'fields' => 'ids' ) );
$product_addons = array_filter( (array) get_post_meta( $global_addon->ID, '_product_addons', true ) );
if ( get_post_meta( $global_addon->ID, '_all_products', true ) == 1 )
$objects[] = 0;
} else {
$global_addons_count = wp_count_posts( 'global_product_addon' );
$reference = __( 'Global Add-on Group' ) . ' #' . ( $global_addons_count->publish + 1 );
$priority = 10;
$objects = array( 0 );
$product_addons = array();
}
include( 'html-global-admin-add.php' );
} else {
if ( ! empty( $_GET['delete'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'delete_addon' ) ) {
wp_delete_post( absint( $_GET['delete'] ), true );
echo '<div class="updated"><p>' . __( 'Add-on deleted successfully', 'wc_product_addons' ) . '</p></div>';
}
include( 'html-global-admin.php' );
}
}
/**
* tab function.
*
* @access public
* @return void
*/
public function tab() {
?><li class="addons_tab product_addons"><a href="#product_addons_data"><?php _e( 'Add-ons', 'wc_product_addons' ); ?></a></li><?php
}
/**
* panel function.
*
* @access public
* @return void
*/
public function panel() {
global $post;
$product_addons = array_filter( (array) get_post_meta( $post->ID, '_product_addons', true ) );
include( 'html-addon-panel.php' );
}
/**
* Save global addons
*
* @return bool success or failure
*/
public function save_global_addons() {
$edit_id = ! empty( $_POST['edit_id'] ) ? absint( $_POST['edit_id'] ) : '';
$reference = woocommerce_clean( $_POST['addon-reference'] );
$priority = absint( $_POST['addon-priority'] );
$objects = ! empty( $_POST['addon-objects'] ) ? array_map( 'absint', $_POST['addon-objects'] ) : array();
$product_addons = $this->get_posted_product_addons();
if ( ! $reference ) {
$global_addons_count = wp_count_posts( 'global_product_addon' );
$reference = __( 'Global Add-on Group' ) . ' #' . ( $global_addons_count->publish + 1 );
}
if ( ! $priority && $priority !== 0 )
$priority = 10;
if ( $edit_id ) {
$edit_post = array();
$edit_post['ID'] = $edit_id;
$edit_post['post_title'] = $reference;
wp_update_post( $edit_post );
wp_set_post_terms( $edit_id, $objects, 'product_cat', false );
} else {
$edit_id = wp_insert_post( array(
'post_title' => $reference,
'post_status' => 'publish',
'post_type' => 'global_product_addon',
'tax_input' => array(
'product_cat' => $objects
)
) );
}
if ( in_array( 0, $objects ) )
update_post_meta( $edit_id, '_all_products', 1 );
else
update_post_meta( $edit_id, '_all_products', 0 );
update_post_meta( $edit_id, '_priority', $priority );
update_post_meta( $edit_id, '_product_addons', $product_addons );
return true;
}
/**
* process_meta_box function.
*
* @access public
* @param mixed $post_id
* @param mixed $post
* @return void
*/
public function process_meta_box( $post_id, $post ) {
// Save addons as serialised array
$product_addons = $this->get_posted_product_addons();
$product_addons_exclude_global = isset( $_POST['_product_addons_exclude_global'] ) ? 1 : 0;
update_post_meta( $post_id, '_product_addons', $product_addons );
update_post_meta( $post_id, '_product_addons_exclude_global', $product_addons_exclude_global );
}
/**
* Put posted addon data into an array
*
* @return [type] [description]
*/
private function get_posted_product_addons() {
$product_addons = array();
if ( isset( $_POST[ 'product_addon_name' ] ) ) {
$addon_name = $_POST['product_addon_name'];
$addon_description = $_POST['product_addon_description'];
$addon_type = $_POST['product_addon_type'];
$addon_position = $_POST['product_addon_position'];
$addon_required = isset( $_POST['product_addon_required'] ) ? $_POST['product_addon_required'] : array();
$addon_option_label = $_POST['product_addon_option_label'];
$addon_option_price = $_POST['product_addon_option_price'];
$addon_option_min = $_POST['product_addon_option_min'];
$addon_option_max = $_POST['product_addon_option_max'];
for ( $i = 0; $i < sizeof( $addon_name ); $i++ ) {
if ( ! isset( $addon_name[ $i ] ) || ( '' == $addon_name[ $i ] ) ) continue;
$addon_options = array();
$option_label = $addon_option_label[ $i ];
$option_price = $addon_option_price[ $i ];
$option_min = $addon_option_min[ $i ];
$option_max = $addon_option_max[ $i ];
for ( $ii = 0; $ii < sizeof( $option_label ); $ii++ ) {
$label = sanitize_text_field( stripslashes( $option_label[ $ii ] ) );
$price = sanitize_text_field( stripslashes( $option_price[ $ii ] ) );
$min = sanitize_text_field( stripslashes( $option_min[ $ii ] ) );
$max = sanitize_text_field( stripslashes( $option_max[ $ii ] ) );
$addon_options[] = array(
'label' => $label,
'price' => $price,
'min' => $min,
'max' => $max
);
}
if ( sizeof( $addon_options ) == 0 )
continue; // Needs options
// Add to array
$product_addons[] = array(
'name' => sanitize_text_field( stripslashes( $addon_name[ $i ] ) ),
'description' => wp_kses_post( stripslashes( $addon_description[ $i ] ) ),
'type' => sanitize_text_field( stripslashes( $addon_type[ $i ] ) ),
'position' => absint( $addon_position[ $i ] ),
'options' => $addon_options,
'required' => isset( $addon_required[ $i ] ) ? 1 : 0,
);
}
}
if ( ! empty( $_POST['import_product_addon'] ) ) {
$import_addons = maybe_unserialize( maybe_unserialize( stripslashes( trim( $_POST['import_product_addon'] ) ) ) );
if ( is_array( $import_addons ) && sizeof( $import_addons ) > 0 ) {
$valid = true;
foreach ( $import_addons as $addon ) {
if ( ! isset( $addon['name'] ) || ! $addon['name'] ) $valid = false;
if ( ! isset( $addon['description'] ) ) $valid = false;
if ( ! isset( $addon['type'] ) ) $valid = false;
if ( ! isset( $addon['position'] ) ) $valid = false;
if ( ! isset( $addon['options'] ) ) $valid = false;
if ( ! isset( $addon['required'] ) ) $valid = false;
}
if ( $valid ) {
$product_addons = array_merge( $product_addons, $import_addons );
}
}
}
uasort( $product_addons, array( $this, 'addons_cmp' ) );
return $product_addons;
}
/**
* Sort addons
*
* @param [type] $a [description]
* @param [type] $b [description]
* @return [type] [description]
*/
private function addons_cmp( $a, $b ) {
if ( $a['position'] == $b['position'] ) {
return 0;
}
return ( $a['position'] < $b['position'] ) ? -1 : 1;
}
}
\ No newline at end of file
<?php
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
?>
<tr>
<td><input type="text" name="product_addon_option_label[<?php echo $loop; ?>][]" value="<?php echo esc_attr($option['label']) ?>" placeholder="<?php _e('Label', 'wc_product_addons'); ?>" /></td>
<td class="price_column"><input type="number" name="product_addon_option_price[<?php echo $loop; ?>][]" value="<?php echo esc_attr($option['price']) ?>" placeholder="0.00" min="0" step="any" /></td>
<td class="minmax_column"><input type="number" name="product_addon_option_min[<?php echo $loop; ?>][]" value="<?php echo esc_attr($option['min']) ?>" placeholder="N/A" min="0" step="any" /></td>
<td class="minmax_column"><input type="number" name="product_addon_option_max[<?php echo $loop; ?>][]" value="<?php echo esc_attr($option['max']) ?>" placeholder="N/A" min="0" step="any" /></td>
<td class="actions"><button type="button" class="remove_addon_option button">x</button></td>
</tr>
\ No newline at end of file
<div id="product_addons_data" class="panel woocommerce_options_panel">
<div class="wc-metaboxes-wrapper">
<p class="toolbar">
<a href="#" class="close_all"><?php _e( 'Close all', 'woocommerce' ); ?></a><a href="#" class="expand_all"><?php _e( 'Expand all', 'woocommerce' ); ?></a>
</p>
<div class="woocommerce_product_addons wc-metaboxes">
<?php
$loop = 0;
foreach ( $product_addons as $addon ) {
include( 'html-addon.php' );
$loop++;
}
?>
</div>
<div class="toolbar">
<button type="button" class="button add_new_addon button-primary"><?php _e( 'New Addon Group', 'wc_product_addons' ); ?></button>
<button type="button" class="button import_addons"><?php _e( 'Import', 'wc_product_addons' ); ?></button>
<button type="button" class="button export_addons"><?php _e( 'Export', 'wc_product_addons' ); ?></button>
<textarea name="export_product_addon" class="export" cols="20" rows="5" readonly="readonly"><?php echo esc_textarea( serialize( $product_addons ) ); ?></textarea>
<textarea name="import_product_addon" class="import" cols="20" rows="5" placeholder="<?php _e('Paste exported form data here and then save to import fields. The imported fields will be appended.', 'wc_product_addons'); ?>"></textarea>
</div>
<?php if( isset($post->ID) ):?>
<div class="options_group">
<p class="form-field">
<label for="_product_addons_exclude_global"><?php _e( 'Global Addon Exclusion', 'wc_product_addons' ); ?></label>
<input id="_product_addons_exclude_global" name="_product_addons_exclude_global" class="checkbox" type="checkbox" value="1" <?php checked( get_post_meta( $post->ID, '_product_addons_exclude_global', TRUE ), 1 ); ?>/><span class="description"><?php _e( 'Check this to exclude this product from all Global Addons', 'wc_product_addons' ); ?></span>
</p>
</div>
<?php endif; ?>
</div>
</div>
<script type="text/javascript">
jQuery(function(){
jQuery( 'select.chosen_select' ).chosen();
jQuery('#product_addons_data')
.on( 'change', '.addon_name input', function() {
if ( jQuery(this).val() )
jQuery(this).closest('.woocommerce_product_addon').find('span.group_name').text( '"' + jQuery(this).val() + '"' );
else
jQuery(this).closest('.woocommerce_product_addon').find('span.group_name').text('');
})
.on( 'change', 'select.product_addon_type', function() {
var value = jQuery(this).val();
if ( value == 'custom' || value == 'custom_price' || value == 'custom_textarea' || value == 'input_multiplier' ) {
jQuery(this).closest('.woocommerce_product_addon').find('td.minmax_column, th.minmax_column').show();
} else {
jQuery(this).closest('.woocommerce_product_addon').find('td.minmax_column, th.minmax_column').hide();
}
if ( value == 'custom_price' ) {
jQuery(this).closest('.woocommerce_product_addon').find('td.price_column, th.price_column').hide();
} else {
jQuery(this).closest('.woocommerce_product_addon').find('td.price_column, th.price_column').show();
}
})
.on( 'click', 'button.add_addon_option', function() {
var loop = jQuery(this).closest('.woocommerce_product_addon').index('.woocommerce_product_addon');
var html = '<?php
ob_start();
$option['label'] = '';
$option['price'] = '';
$option['min'] = '';
$option['max'] = '';
$loop = "{loop}";
include( 'html-addon-option.php' );
$html = ob_get_clean();
echo str_replace( array( "\n", "\r" ), '', str_replace( "'", '"', $html ) );
?>';
html = html.replace( /{loop}/g, loop );
jQuery(this).closest('.woocommerce_product_addon .data').find('tbody').append( html );
jQuery('select.product_addon_type').change();
return false;
})
.on( 'click', '.add_new_addon', function() {
var loop = jQuery('.woocommerce_product_addons .woocommerce_product_addon').size();
var html = '<?php
ob_start();
$addon['name'] = '';
$addon['description'] = '';
$addon['required'] = '';
$addon['type'] = 'checkbox';
$addon['options'] = array();
$loop = "{loop}";
include( 'html-addon.php' );
$html = ob_get_clean();
echo str_replace( array( "\n", "\r" ), '', str_replace( "'", '"', $html ) );
?>';
html = html.replace( /{loop}/g, loop );
jQuery('.woocommerce_product_addons').append( html );
jQuery('select.product_addon_type').change();
return false;
})
.on( 'click', '.remove_addon', function() {
var answer = confirm('<?php _e('Are you sure you want remove this add-on?', 'wc_product_addons'); ?>');
if (answer) {
var addon = jQuery(this).closest('.woocommerce_product_addon');
jQuery(addon).find('input').val('');
jQuery(addon).hide();
}
return false;
})
.find('select.product_addon_type').change();
// Import / Export
jQuery('#product_addons_data').on('click', '.export_addons', function() {
jQuery('#product_addons_data textarea.import').hide();
jQuery('#product_addons_data textarea.export').slideToggle('500', function() {
jQuery(this).select();
});
return false;
});
jQuery('#product_addons_data').on('click', '.import_addons', function() {
jQuery('#product_addons_data textarea.export').hide();
jQuery('#product_addons_data textarea.import').slideToggle('500', function() {
jQuery(this).val('');
});
return false;
});
// Sortable
jQuery('.woocommerce_product_addons').sortable({
items:'.woocommerce_product_addon',
cursor:'move',
axis:'y',
handle:'h3',
scrollSensitivity:40,
helper:function(e,ui){
return ui;
},
start:function(event,ui){
ui.item.css('border-style','dashed');
},
stop:function(event,ui){
ui.item.removeAttr('style');
addon_row_indexes();
}
});
function addon_row_indexes() {
jQuery('.woocommerce_product_addons .woocommerce_product_addon').each(function(index, el){ jQuery('.product_addon_position', el).val( parseInt( jQuery(el).index('.woocommerce_product_addons .woocommerce_product_addon') ) ); });
};
// Sortable options
jQuery('.woocommerce_product_addon .data table tbody').sortable({
items:'tr',
cursor:'move',
axis:'y',
scrollSensitivity:40,
helper:function(e,ui){
ui.children().each(function(){
jQuery(this).width(jQuery(this).width());
});
return ui;
},
start:function(event,ui){
ui.item.css('background-color','#f6f6f6');
},
stop:function(event,ui){
ui.item.removeAttr('style');
}
});
// Remove option
jQuery('button.remove_addon_option').live('click', function(){
var answer = confirm('<?php _e('Are you sure you want delete this option?', 'wc_product_addons'); ?>');
if (answer) {
jQuery(this).closest('tr').remove();
}
return false;
});
});
</script>
\ No newline at end of file
<?php
if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
?>
<div class="woocommerce_product_addon wc-metabox closed">
<h3>
<button type="button" class="remove_addon button"><?php _e( 'Remove', 'woocommerce' ); ?></button>
<div class="handlediv" title="<?php _e( 'Click to toggle', 'woocommerce' ); ?>"></div>
<strong><?php _e( 'Group', 'woocommerce' ); ?> <span class="group_name"><?php if ( $addon['name'] ) echo '"' . esc_attr( $addon['name'] ) . '"'; ?></span> &mdash; </strong>
<select name="product_addon_type[<?php echo $loop; ?>]" class="product_addon_type">
<option <?php selected('checkbox', $addon['type']); ?> value="checkbox"><?php _e('Checkboxes', 'wc_product_addons'); ?></option>
<option <?php selected('radiobutton', $addon['type']); ?> value="radiobutton"><?php _e('Radio buttons', 'wc_product_addons'); ?></option>
<option <?php selected('select', $addon['type']); ?> value="select"><?php _e('Select box', 'wc_product_addons'); ?></option>
<option <?php selected('custom', $addon['type']); ?> value="custom"><?php _e('Custom input (text)', 'wc_product_addons'); ?></option>
<option <?php selected('custom_textarea', $addon['type']); ?> value="custom_textarea"><?php _e('Custom input (textarea)', 'wc_product_addons'); ?></option>
<option <?php selected('file_upload', $addon['type']); ?> value="file_upload"><?php _e('File upload', 'wc_product_addons'); ?></option>
<option <?php selected('custom_price', $addon['type']); ?> value="custom_price"><?php _e('Custom price input', 'wc_product_addons'); ?></option>
<option <?php selected('input_multiplier', $addon['type']); ?> value="input_multiplier"><?php _e('Custom Input Multipler', 'wc_product_addons'); ?></option>
</select>
<input type="hidden" name="product_addon_position[<?php echo $loop; ?>]" class="product_addon_position" value="<?php echo $loop; ?>" />
</h3>
<table cellpadding="0" cellspacing="0" class="wc-metabox-content">
<tbody>
<tr>
<td class="addon_name" width="50%">
<label for="addon_name_<?php echo $loop; ?>"><?php _e( 'Group Name', 'woocommerce' ); ?></label>
<input type="text" id="addon_name_<?php echo $loop; ?>" name="product_addon_name[<?php echo $loop; ?>]" value="<?php echo esc_attr( $addon['name'] ) ?>" />
</td>
<td class="addon_required" width="50%">
<label for="addon_required_<?php echo $loop; ?>"><?php _e( 'Required fields?', 'wc_product_addons' ); ?></label>
<input type="checkbox" id="addon_required_<?php echo $loop; ?>" name="product_addon_required[<?php echo $loop; ?>]" <?php checked( $addon['required'], 1 ) ?> />
</td>
</tr>
<tr>
<td class="addon_description" colspan="2">
<label for="addon_description_<?php echo $loop; ?>"><?php _e( 'Group Description', 'woocommerce' ); ?></label>
<textarea cols="20" id="addon_description_<?php echo $loop; ?>" rows="3" name="product_addon_description[<?php echo $loop; ?>]"><?php echo esc_textarea( $addon['description'] ) ?></textarea>
</td>
</tr>
<tr>
<td class="data" colspan="3">
<table cellspacing="0" cellpadding="0">
<thead>
<tr>
<th><?php _e('Option Label', 'wc_product_addons'); ?></th>
<th class="price_column"><?php _e('Option Price', 'wc_product_addons'); ?></th>
<th class="minmax_column"><?php _e('Min', 'wc_product_addons'); ?></th>
<th class="minmax_column"><?php _e('Max', 'wc_product_addons'); ?></th>
<th width="1%"></th>
</tr>
</thead>
<tfoot>
<tr>
<td colspan="5"><button type="button" class="add_addon_option button"><?php _e('New&nbsp;Option', 'wc_product_addons'); ?></button></td>
</tr>
</tfoot>
<tbody>
<?php
foreach ( $addon['options'] as $option )
include( 'html-addon-option.php' );
?>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
<div class="wrap woocommerce">
<div class="icon32 icon32-posts-product" id="icon-woocommerce"><br/></div>
<h2><?php _e( 'Add/Edit Global Add-on', 'wc_product_addons' ) ?></h2><br/>
<form method="POST" action="">
<table class="form-table global-addons-form">
<tr>
<th>
<label for="addon-reference"><?php _e( 'Global Add-on Reference', 'wc_product_addons' ); ?></label>
</th>
<td>
<input type="text" name="addon-reference" id="addon-reference" style="width:50%;" value="<?php echo esc_attr( $reference ); ?>" />
<p class="description"><?php _e( 'Give this global add-on a reference/name to make it recognisable.', 'wc_product_addons' ); ?></p>
</td>
</tr>
<tr>
<th>
<label for="addon-priority"><?php _e( 'Priority', 'wc_product_addons' ); ?></label>
</th>
<td>
<input type="text" name="addon-priority" id="addon-priority" style="width:50%;" value="<?php echo esc_attr( $priority ); ?>" />
<p class="description"><?php _e( 'Give this global addon a priority - this will deternmine the order in which multiple groups of addons get displayed on the frontend. Per-product add-ons will always have priority 10.', 'wc_product_addons' ); ?></p>
</td>
</tr>
<tr>
<th>
<label for="addon-objects"><?php _e( 'Applied to...', 'wc_product_addons' ); ?></label>
</th>
<td>
<select id="addon-objects" name="addon-objects[]" multiple="multiple" style="width:50%;" data-placeholder="<?php _e('Choose some options&hellip;', 'wc_product_addons'); ?>" class="chosen_select">
<option value="0" <?php selected( in_array( '0', $objects ), true ); ?>><?php _e( 'All Products', 'wc_product_addons' ); ?></option>
<optgroup label="<?php _e( 'Product category notifications', 'wc_product_addons' ); ?>">
<?php
$terms = get_terms( 'product_cat', array( 'hide_empty' => 0 ) );
foreach( $terms as $term )
echo '<option value="' . $term->term_id . '" ' . selected( in_array( $term->term_id, $objects ), true, false ) . '>' . __( 'Category:', 'wc_product_addons' ) . ' ' . $term->name . '</option>';
?>
</optgroup>
</select>
<p class="description"><?php _e( 'Choose categories which should show these addons (or apply to all products).', 'wc_product_addons' ); ?></p>
</td>
</tr>
<tr>
<th>
<label for="addon-objects"><?php _e( 'Add-ons', 'wc_product_addons' ); ?></label>
</th>
<td id="poststuff" class="postbox">
<?php include( 'html-addon-panel.php' ); ?>
</td>
</tr>
</table>
<p class="submit">
<input type="hidden" name="edit_id" value="<?php if ( ! empty( $edit_id ) ) echo $edit_id; ?>" />
<input type="submit" name="submit" id="submit" class="button button-primary" value="<?php _e( 'Save Global Add-on', 'wc_product_addons' ); ?>">
</p>
</form>
</div>
<script type="text/javascript">
// Open/close
jQuery('.wc-metaboxes-wrapper').on('click', '.wc-metabox h3', function(event){
// If the user clicks on some form input inside the h3, like a select list (for variations), the box should not be toggled
if (jQuery(event.target).filter(':input, option').length) return;
jQuery(this).next('.wc-metabox-content').toggle();
})
.on('click', '.expand_all', function(event){
jQuery(this).closest('.wc-metaboxes-wrapper').find('.wc-metabox > table').show();
return false;
})
.on('click', '.close_all', function(event){
jQuery(this).closest('.wc-metaboxes-wrapper').find('.wc-metabox > table').hide();
return false;
});
jQuery('.wc-metabox.closed').each(function(){
jQuery(this).find('.wc-metabox-content').hide();
});
</script>
\ No newline at end of file
<div class="wrap woocommerce">
<div class="icon32 icon32-posts-product" id="icon-woocommerce"><br/></div>
<h2><?php _e( 'Global Add-ons', 'wc_product_addons' ) ?> <a href="<?php echo add_query_arg( 'add', true, admin_url( 'edit.php?post_type=product&page=global_addons' ) ); ?>" class="add-new-h2"><?php _e( 'Add Global Add-on', 'wc_product_addons' ); ?></a></h2><br/>
<table id="global-addons-table" class="wp-list-table widefat" cellspacing="0">
<thead>
<tr>
<th scope="col"><?php _e( 'Reference', 'wc_product_addons' ); ?></th>
<th><?php _e( 'Number of Fields', 'wc_product_addons' ); ?></th>
<th><?php _e( 'Priority', 'wc_product_addons' ); ?></th>
<th><?php _e( 'Applies to...', 'wc_product_addons' ); ?></th>
<th><?php _e( 'Actions', 'wc_product_addons' ); ?></th>
</tr>
</thead>
<tbody id="the-list">
<?php
$args = array(
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC',
'post_type' => 'global_product_addon',
'post_status' => 'any',
'suppress_filters' => true
);
$global_addons = get_posts( $args );
if ( $global_addons ) {
foreach ( $global_addons as $global_addon ) {
$reference = $global_addon->post_title;
$priority = get_post_meta( $global_addon->ID, '_priority', true );
$objects = (array) wp_get_post_terms( $global_addon->ID, 'product_cat', array( 'fields' => 'ids' ) );
$product_addons = array_filter( (array) get_post_meta( $global_addon->ID, '_product_addons', true ) );
if ( get_post_meta( $global_addon->ID, '_all_products', true ) == 1 )
$objects[] = 0;
?>
<tr>
<td><?php echo $reference; ?></td>
<td><?php echo sizeof( $product_addons ); ?></td>
<td><?php echo $priority; ?></td>
<td><?php
if ( in_array( 0, $objects ) )
_e( 'All Products', 'wc_product_addons' );
else {
$term_names = array();
foreach ( $objects as $object_id ) {
$term = get_term_by( 'id', $object_id, 'product_cat' );
$term_names[] = $term->name;
}
echo implode( ', ', $term_names );
}
?></td>
<td>
<a href="<?php echo add_query_arg( 'edit', $global_addon->ID, admin_url( 'edit.php?post_type=product&page=global_addons' ) ); ?>" class="button"><?php _e( 'Edit', 'wc_product_addons' ); ?></a> <a href="<?php echo wp_nonce_url( add_query_arg( 'delete', $global_addon->ID, admin_url( 'edit.php?post_type=product&page=global_addons' ) ), 'delete_addon' ); ?>" class="button"><?php _e( 'Delete', 'wc_product_addons' ); ?></a>
</td>
</tr>
<?php
}
} else {
?>
<tr>
<td colspan="5"><?php _e( 'No global add-ons exists yet.', 'wc_product_addons' ); ?> <a href="<?php echo add_query_arg( 'add', true, admin_url( 'edit.php?post_type=product&page=global_addons' ) ); ?>"><?php _e( 'Add one?', 'wc_product_addons' ); ?></a></td>
</tr>
<?php
}
?>
</tbody>
</table>
</div>
\ No newline at end of file
#global-addons-table td{padding:7px 9px;vertical-align:middle}#global-addons-table th{padding:7px 9px}.global-addons-form td.postbox,.woocommerce_product_addons td.postbox{padding:0!important}.global-addons-form td label,.woocommerce_product_addons td label{margin:0}.global-addons-form .addon_required label,.woocommerce_product_addons .addon_required label{display:block;float:none}.global-addons-form .addon_required input,.woocommerce_product_addons .addon_required input{width:16px!important;min-width:0!important}#woocommerce-product-data ul.product_data_tabs li.addons_tab a:before{content:"\e007"}#product_addons_data .add_new_addon{float:right}#product_addons_data textarea.import,#product_addons_data textarea.export{width:100%;display:none;margin:10px 0 0}#product_addons_data .woocommerce_product_addon .product_addon_type{float:none}#product_addons_data .woocommerce_product_addon .data table{padding:0;border:1px solid #ececec}#product_addons_data .woocommerce_product_addon .data table thead th{background:#ececec;padding:5px}#product_addons_data .woocommerce_product_addon .data table tfoot td{border-top:1px solid #ececec}#product_addons_data .woocommerce_product_addon .data input{min-width:50px}
\ No newline at end of file
#global-addons-table {
td {
padding: 7px 9px;
vertical-align: middle;
}
th {
padding: 7px 9px;
}
}
.global-addons-form, .woocommerce_product_addons {
td.postbox {
padding: 0 !important;
}
td {
label {
margin: 0;
}
}
.addon_required {
label {
display: block;
float: none;
}
input {
width: 16px !important;
min-width: 0 !important;
}
}
}
#woocommerce-product-data ul.product_data_tabs {
li.addons_tab {
a {
&:before {
content: "\e007";
}
}
}
}
#product_addons_data {
.add_new_addon {
float: right;
}
textarea.import, textarea.export {
width: 100%;
display: none;
margin: 10px 0 0;
}
.woocommerce_product_addon {
.product_addon_type {
float: none;
}
.data {
table {
padding: 0;
border: 1px solid #ececec;
thead {
th {
background: #ececec;
padding: 5px;
}
}
tfoot {
td {
border-top: 1px solid #ececec;
}
}
}
input {
min-width: 50px;
}
}
}
}
\ No newline at end of file
div.product-addon {
margin: 1em 0;
}
h3.addon-name {
margin-top: 0;
}
div.addon-description p {
margin-bottom: .5em;
font-style: italic
}
dl.product-addon-totals {
margin-bottom: 1em;
overflow: hidden;
}
dl.product-addon-totals dt {
width: 50%;
float: left;
clear: left;
}
dl.product-addon-totals dd {
float: left;
}
.addon-alert{
display: none;
color: red;
}
\ No newline at end of file
/*!
* accounting.js v0.3.2
* Copyright 2011, Joss Crowcroft
*
* Freely distributable under the MIT license.
* Portions of accounting.js are inspired or borrowed from underscore.js
*
* Full details and documentation:
* http://josscrowcroft.github.com/accounting.js/
*/
(function(root, undefined) {
/* --- Setup --- */
// Create the local library object, to be exported or referenced globally later
var lib = {};
// Current version
lib.version = '0.3.2';
/* --- Exposed settings --- */
// The library's settings configuration object. Contains default parameters for
// currency and number formatting
lib.settings = {
currency: {
symbol : "$", // default currency symbol is '$'
format : "%s%v", // controls output: %s = symbol, %v = value (can be object, see docs)
decimal : ".", // decimal point separator
thousand : ",", // thousands separator
precision : 2, // decimal places
grouping : 3 // digit grouping (not implemented yet)
},
number: {
precision : 0, // default precision on numbers is 0
grouping : 3, // digit grouping (not implemented yet)
thousand : ",",
decimal : "."
}
};
/* --- Internal Helper Methods --- */
// Store reference to possibly-available ECMAScript 5 methods for later
var nativeMap = Array.prototype.map,
nativeIsArray = Array.isArray,
toString = Object.prototype.toString;
/**
* Tests whether supplied parameter is a string
* from underscore.js
*/
function isString(obj) {
return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
}
/**
* Tests whether supplied parameter is a string
* from underscore.js, delegates to ECMA5's native Array.isArray
*/
function isArray(obj) {
return nativeIsArray ? nativeIsArray(obj) : toString.call(obj) === '[object Array]';
}
/**
* Tests whether supplied parameter is a true object
*/
function isObject(obj) {
return obj && toString.call(obj) === '[object Object]';
}
/**
* Extends an object with a defaults object, similar to underscore's _.defaults
*
* Used for abstracting parameter handling from API methods
*/
function defaults(object, defs) {
var key;
object = object || {};
defs = defs || {};
// Iterate over object non-prototype properties:
for (key in defs) {
if (defs.hasOwnProperty(key)) {
// Replace values with defaults only if undefined (allow empty/zero values):
if (object[key] == null) object[key] = defs[key];
}
}
return object;
}
/**
* Implementation of `Array.map()` for iteration loops
*
* Returns a new Array as a result of calling `iterator` on each array value.
* Defers to native Array.map if available
*/
function map(obj, iterator, context) {
var results = [], i, j;
if (!obj) return results;
// Use native .map method if it exists:
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
// Fallback for native .map:
for (i = 0, j = obj.length; i < j; i++ ) {
results[i] = iterator.call(context, obj[i], i, obj);
}
return results;
}
/**
* Check and normalise the value of precision (must be positive integer)
*/
function checkPrecision(val, base) {
val = Math.round(Math.abs(val));
return isNaN(val)? base : val;
}
/**
* Parses a format string or object and returns format obj for use in rendering
*
* `format` is either a string with the default (positive) format, or object
* containing `pos` (required), `neg` and `zero` values (or a function returning
* either a string or object)
*
* Either string or format.pos must contain "%v" (value) to be valid
*/
function checkCurrencyFormat(format) {
var defaults = lib.settings.currency.format;
// Allow function as format parameter (should return string or object):
if ( typeof format === "function" ) format = format();
// Format can be a string, in which case `value` ("%v") must be present:
if ( isString( format ) && format.match("%v") ) {
// Create and return positive, negative and zero formats:
return {
pos : format,
neg : format.replace("-", "").replace("%v", "-%v"),
zero : format
};
// If no format, or object is missing valid positive value, use defaults:
} else if ( !format || !format.pos || !format.pos.match("%v") ) {
// If defaults is a string, casts it to an object for faster checking next time:
return ( !isString( defaults ) ) ? defaults : lib.settings.currency.format = {
pos : defaults,
neg : defaults.replace("%v", "-%v"),
zero : defaults
};
}
// Otherwise, assume format was fine:
return format;
}
/* --- API Methods --- */
/**
* Takes a string/array of strings, removes all formatting/cruft and returns the raw float value
* alias: accounting.`parse(string)`
*
* Decimal must be included in the regular expression to match floats (defaults to
* accounting.settings.number.decimal), so if the number uses a non-standard decimal
* separator, provide it as the second argument.
*
* Also matches bracketed negatives (eg. "$ (1.99)" => -1.99)
*
* Doesn't throw any errors (`NaN`s become 0) but this may change in future
*/
var unformat = lib.unformat = lib.parse = function(value, decimal) {
// Recursively unformat arrays:
if (isArray(value)) {
return map(value, function(val) {
return unformat(val, decimal);
});
}
// Fails silently (need decent errors):
value = value || 0;
// Return the value as-is if it's already a number:
if (typeof value === "number") return value;
// Default decimal point comes from settings, but could be set to eg. "," in opts:
decimal = decimal || lib.settings.number.decimal;
// Build regex to strip out everything except digits, decimal point and minus sign:
var regex = new RegExp("[^0-9-" + decimal + "]", ["g"]),
unformatted = parseFloat(
("" + value)
.replace(/\((.*)\)/, "-$1") // replace bracketed values with negatives
.replace(regex, '') // strip out any cruft
.replace(decimal, '.') // make sure decimal point is standard
);
// This will fail silently which may cause trouble, let's wait and see:
return !isNaN(unformatted) ? unformatted : 0;
};
/**
* Implementation of toFixed() that treats floats more like decimals
*
* Fixes binary rounding issues (eg. (0.615).toFixed(2) === "0.61") that present
* problems for accounting- and finance-related software.
*/
var toFixed = lib.toFixed = function(value, precision) {
precision = checkPrecision(precision, lib.settings.number.precision);
var power = Math.pow(10, precision);
// Multiply up by precision, round accurately, then divide and use native toFixed():
return (Math.round(lib.unformat(value) * power) / power).toFixed(precision);
};
/**
* Format a number, with comma-separated thousands and custom precision/decimal places
*
* Localise by overriding the precision and thousand / decimal separators
* 2nd parameter `precision` can be an object matching `settings.number`
*/
var formatNumber = lib.formatNumber = function(number, precision, thousand, decimal) {
// Resursively format arrays:
if (isArray(number)) {
return map(number, function(val) {
return formatNumber(val, precision, thousand, decimal);
});
}
// Clean up number:
number = unformat(number);
// Build options object from second param (if object) or all params, extending defaults:
var opts = defaults(
(isObject(precision) ? precision : {
precision : precision,
thousand : thousand,
decimal : decimal
}),
lib.settings.number
),
// Clean up precision
usePrecision = checkPrecision(opts.precision),
// Do some calc:
negative = number < 0 ? "-" : "",
base = parseInt(toFixed(Math.abs(number || 0), usePrecision), 10) + "",
mod = base.length > 3 ? base.length % 3 : 0;
// Format the number:
return negative + (mod ? base.substr(0, mod) + opts.thousand : "") + base.substr(mod).replace(/(\d{3})(?=\d)/g, "$1" + opts.thousand) + (usePrecision ? opts.decimal + toFixed(Math.abs(number), usePrecision).split('.')[1] : "");
};
/**
* Format a number into currency
*
* Usage: accounting.formatMoney(number, symbol, precision, thousandsSep, decimalSep, format)
* defaults: (0, "$", 2, ",", ".", "%s%v")
*
* Localise by overriding the symbol, precision, thousand / decimal separators and format
* Second param can be an object matching `settings.currency` which is the easiest way.
*
* To do: tidy up the parameters
*/
var formatMoney = lib.formatMoney = function(number, symbol, precision, thousand, decimal, format) {
// Resursively format arrays:
if (isArray(number)) {
return map(number, function(val){
return formatMoney(val, symbol, precision, thousand, decimal, format);
});
}
// Clean up number:
number = unformat(number);
// Build options object from second param (if object) or all params, extending defaults:
var opts = defaults(
(isObject(symbol) ? symbol : {
symbol : symbol,
precision : precision,
thousand : thousand,
decimal : decimal,
format : format
}),
lib.settings.currency
),
// Check format (returns object with pos, neg and zero):
formats = checkCurrencyFormat(opts.format),
// Choose which format to use for this value:
useFormat = number > 0 ? formats.pos : number < 0 ? formats.neg : formats.zero;
// Return with currency symbol added:
return useFormat.replace('%s', opts.symbol).replace('%v', formatNumber(Math.abs(number), checkPrecision(opts.precision), opts.thousand, opts.decimal));
};
/**
* Format a list of numbers into an accounting column, padding with whitespace
* to line up currency symbols, thousand separators and decimals places
*
* List should be an array of numbers
* Second parameter can be an object containing keys that match the params
*
* Returns array of accouting-formatted number strings of same length
*
* NB: `white-space:pre` CSS rule is required on the list container to prevent
* browsers from collapsing the whitespace in the output strings.
*/
lib.formatColumn = function(list, symbol, precision, thousand, decimal, format) {
if (!list) return [];
// Build options object from second param (if object) or all params, extending defaults:
var opts = defaults(
(isObject(symbol) ? symbol : {
symbol : symbol,
precision : precision,
thousand : thousand,
decimal : decimal,
format : format
}),
lib.settings.currency
),
// Check format (returns object with pos, neg and zero), only need pos for now:
formats = checkCurrencyFormat(opts.format),
// Whether to pad at start of string or after currency symbol:
padAfterSymbol = formats.pos.indexOf("%s") < formats.pos.indexOf("%v") ? true : false,
// Store value for the length of the longest string in the column:
maxLength = 0,
// Format the list according to options, store the length of the longest string:
formatted = map(list, function(val, i) {
if (isArray(val)) {
// Recursively format columns if list is a multi-dimensional array:
return lib.formatColumn(val, opts);
} else {
// Clean up the value
val = unformat(val);
// Choose which format to use for this value (pos, neg or zero):
var useFormat = val > 0 ? formats.pos : val < 0 ? formats.neg : formats.zero,
// Format this value, push into formatted list and save the length:
fVal = useFormat.replace('%s', opts.symbol).replace('%v', formatNumber(Math.abs(val), checkPrecision(opts.precision), opts.thousand, opts.decimal));
if (fVal.length > maxLength) maxLength = fVal.length;
return fVal;
}
});
// Pad each number in the list and send back the column of numbers:
return map(formatted, function(val, i) {
// Only if this is a string (not a nested array, which would have already been padded):
if (isString(val) && val.length < maxLength) {
// Depending on symbol position, pad after symbol or at index 0:
return padAfterSymbol ? val.replace(opts.symbol, opts.symbol+(new Array(maxLength - val.length + 1).join(" "))) : (new Array(maxLength - val.length + 1).join(" ")) + val;
}
return val;
});
};
/* --- Module Definition --- */
// Export accounting for CommonJS. If being loaded as an AMD module, define it as such.
// Otherwise, just add `accounting` to the global object
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = lib;
}
exports.accounting = lib;
} else if (typeof define === 'function' && define.amd) {
// Return the library as an AMD module:
define([], function() {
return lib;
});
} else {
// Use accounting.noConflict to restore `accounting` back to its original value.
// Returns a reference to the library's `accounting` object;
// e.g. `var numbers = accounting.noConflict();`
lib.noConflict = (function(oldAccounting) {
return function() {
// Reset the value of the root's `accounting` variable:
root.accounting = oldAccounting;
// Delete the noConflict method:
lib.noConflict = undefined;
// Return reference to the library to re-assign it:
return lib;
};
})(root.accounting);
// Declare `fx` on the root (global/window) object:
root['accounting'] = lib;
}
// Root will be `window` in browser or `global` on the server:
}(this));
/*!
* accounting.js v0.3.2
* Copyright 2011, Joss Crowcroft
*
* Freely distributable under the MIT license.
* Portions of accounting.js are inspired or borrowed from underscore.js
*
* Full details and documentation:
* http://josscrowcroft.github.com/accounting.js/
*/(function(e,t){function o(e){return!!(e===""||e&&e.charCodeAt&&e.substr)}function u(e){return i?i(e):s.call(e)==="[object Array]"}function a(e){return e&&s.call(e)==="[object Object]"}function f(e,t){var n;e=e||{};t=t||{};for(n in t)t.hasOwnProperty(n)&&e[n]==null&&(e[n]=t[n]);return e}function l(e,t,n){var i=[],s,o;if(!e)return i;if(r&&e.map===r)return e.map(t,n);for(s=0,o=e.length;s<o;s++)i[s]=t.call(n,e[s],s,e);return i}function c(e,t){e=Math.round(Math.abs(e));return isNaN(e)?t:e}function h(e){var t=n.settings.currency.format;typeof e=="function"&&(e=e());return o(e)&&e.match("%v")?{pos:e,neg:e.replace("-","").replace("%v","-%v"),zero:e}:!e||!e.pos||!e.pos.match("%v")?o(t)?n.settings.currency.format={pos:t,neg:t.replace("%v","-%v"),zero:t}:t:e}var n={};n.version="0.3.2";n.settings={currency:{symbol:"$",format:"%s%v",decimal:".",thousand:",",precision:2,grouping:3},number:{precision:0,grouping:3,thousand:",",decimal:"."}};var r=Array.prototype.map,i=Array.isArray,s=Object.prototype.toString,p=n.unformat=n.parse=function(e,t){if(u(e))return l(e,function(e){return p(e,t)});e=e||0;if(typeof e=="number")return e;t=t||n.settings.number.decimal;var r=new RegExp("[^0-9-"+t+"]",["g"]),i=parseFloat((""+e).replace(/\((.*)\)/,"-$1").replace(r,"").replace(t,"."));return isNaN(i)?0:i},d=n.toFixed=function(e,t){t=c(t,n.settings.number.precision);var r=Math.pow(10,t);return(Math.round(n.unformat(e)*r)/r).toFixed(t)},v=n.formatNumber=function(e,t,r,i){if(u(e))return l(e,function(e){return v(e,t,r,i)});e=p(e);var s=f(a(t)?t:{precision:t,thousand:r,decimal:i},n.settings.number),o=c(s.precision),h=e<0?"-":"",m=parseInt(d(Math.abs(e||0),o),10)+"",g=m.length>3?m.length%3:0;return h+(g?m.substr(0,g)+s.thousand:"")+m.substr(g).replace(/(\d{3})(?=\d)/g,"$1"+s.thousand)+(o?s.decimal+d(Math.abs(e),o).split(".")[1]:"")},m=n.formatMoney=function(e,t,r,i,s,o){if(u(e))return l(e,function(e){return m(e,t,r,i,s,o)});e=p(e);var d=f(a(t)?t:{symbol:t,precision:r,thousand:i,decimal:s,format:o},n.settings.currency),g=h(d.format),y=e>0?g.pos:e<0?g.neg:g.zero;return y.replace("%s",d.symbol).replace("%v",v(Math.abs(e),c(d.precision),d.thousand,d.decimal))};n.formatColumn=function(e,t,r,i,s,d){if(!e)return[];var m=f(a(t)?t:{symbol:t,precision:r,thousand:i,decimal:s,format:d},n.settings.currency),g=h(m.format),y=g.pos.indexOf("%s")<g.pos.indexOf("%v")?!0:!1,b=0,w=l(e,function(e,t){if(u(e))return n.formatColumn(e,m);e=p(e);var r=e>0?g.pos:e<0?g.neg:g.zero,i=r.replace("%s",m.symbol).replace("%v",v(Math.abs(e),c(m.precision),m.thousand,m.decimal));i.length>b&&(b=i.length);return i});return l(w,function(e,t){return o(e)&&e.length<b?y?e.replace(m.symbol,m.symbol+(new Array(b-e.length+1)).join(" ")):(new Array(b-e.length+1)).join(" ")+e:e})};if(typeof exports!="undefined"){typeof module!="undefined"&&module.exports&&(exports=module.exports=n);exports.accounting=n}else if(typeof define=="function"&&define.amd)define([],function(){return n});else{n.noConflict=function(r){return function(){e.accounting=r;n.noConflict=t;return n}}(e.accounting);e.accounting=n}})(this);
\ No newline at end of file
jQuery(document).ready(function($) {
function init_addon_totals() {
$('.cart').on( 'keyup change', '.product-addon input, .product-addon textarea', function() {
if ( $(this).attr('maxlength') > 0 ) {
var value = $(this).val();
var remaining = $(this).attr('maxlength') - value.length;
$(this).next('.chars_remaining').find('span').text( remaining );
}
} );
$('.cart').find('.addon-custom, .addon-custom-textarea').each(function(){
if ( $(this).attr('maxlength') > 0 ) {
$(this).after('<small class="chars_remaining"><span>' + $(this).attr('maxlength') + '</span> ' + woocommerce_addons_params.i18n_remaining + '</small>' );
}
} );
$('.cart').on( 'change', '.product-addon input, .product-addon textarea, .product-addon select, input.qty', function() {
var $cart = $(this).closest('.cart');
$cart.trigger('woocommerce-product-addons-update');
} );
$('.variations_form').on('found_variation', function( event, variation ) {
var $variation_form = $(this);
var $totals = $variation_form.find('#product-addons-total');
if ( $( variation.price_html ).find('.amount:last').size() ) {
product_price = $( variation.price_html ).find('.amount:last').text();
product_price = product_price.replace( woocommerce_addons_params.currency_format_thousand_sep, '' );
product_price = product_price.replace( woocommerce_addons_params.currency_format_decimal_sep, '.' );
product_price = product_price.replace(/[^0-9\.]/g, '');
product_price = parseFloat( product_price );
$totals.data( 'price', product_price );
}
$variation_form.trigger('woocommerce-product-addons-update');
});
$('.cart').bind( 'woocommerce-product-addons-update', function() {
var total = 0;
var $cart = $(this);
var $totals = $cart.find('#product-addons-total');
var product_price = $totals.data( 'price' );
var product_type = $totals.data( 'type' );
// Move totals
if ( product_type == 'variable' ) {
$cart.find('.single_variation').after( $totals );
}
$cart.find('.addon').each(function() {
var addon_cost = 0;
if ( $(this).is('.addon-custom-price') ) {
addon_cost = $(this).val();
} else if ( $(this).is('.addon-input_multiplier') ) {
if( isNaN( $(this).val() ) || $(this).val() == "" ) { // Number inputs return blank when invalid
$(this).val('');
$(this).closest('p').find('.addon-alert').show();
} else {
if( $(this).val() != "" ){
$(this).val( Math.ceil( $(this).val() ) );
}
$(this).closest('p').find('.addon-alert').hide();
}
addon_cost = $(this).data('price') * $(this).val();
} else if ( $(this).is('.addon-checkbox, .addon-radio') ) {
if ( $(this).is(':checked') )
addon_cost = $(this).data('price');
} else if ( $(this).is('.addon-select') ) {
if ( $(this).val() )
addon_cost = $(this).find('option:selected').data('price');
} else {
if ( $(this).val() )
addon_cost = $(this).data('price');
}
if ( ! addon_cost )
addon_cost = 0;
total = parseFloat( total ) + parseFloat( addon_cost );
} );
var qty = parseFloat( $cart.find('input.qty').val() );
if ( total > 0 && qty > 0 ) {
total = parseFloat( total * qty );
var formatted_addon_total = accounting.formatMoney( total, {
symbol : woocommerce_addons_params.currency_format_symbol,
decimal : woocommerce_addons_params.currency_format_decimal_sep,
thousand : woocommerce_addons_params.currency_format_thousand_sep,
precision : woocommerce_addons_params.currency_format_num_decimals,
format : woocommerce_addons_params.currency_format
} );
if ( product_price ) {
product_total_price = parseFloat( product_price * qty );
var formatted_grand_total = accounting.formatMoney( product_total_price + total, {
symbol : woocommerce_addons_params.currency_format_symbol,
decimal : woocommerce_addons_params.currency_format_decimal_sep,
thousand : woocommerce_addons_params.currency_format_thousand_sep,
precision : woocommerce_addons_params.currency_format_num_decimals,
format : woocommerce_addons_params.currency_format
} );
}
html = '<dl class="product-addon-totals"><dt>' + woocommerce_addons_params.i18n_addon_total + '</dt><dd><strong><span class="amount">' + formatted_addon_total + '</span></strong></dd>';
if ( formatted_grand_total ) {
html = html + '<dt>' + woocommerce_addons_params.i18n_grand_total + '</dt><dd><strong><span class="amount">' + formatted_grand_total + '</span></strong></dd>';
}
html = html + '</dl>';
$totals.html( html );
} else {
$totals.empty();
}
$('body').trigger('updated_addons');
} );
$('.cart').find('.addon-custom, .addon-custom-textarea, .product-addon input, .product-addon textarea, .product-addon select, input.qty').change();
}
init_addon_totals();
$( '.variations_form .product-addon' ).closest( '.cart' ).find( '.variations select' ).change();
// Quick view
$('body').on('quick-view-displayed', function() {
init_addon_totals();
});
});
\ No newline at end of file
jQuery(document).ready(function(e){function t(){e("form.cart").on("keyup change",".product-addon input, .product-addon textarea",function(){if(e(this).attr("maxlength")>0){var t=e(this).val(),n=e(this).attr("maxlength")-t.length;e(this).next(".chars_remaining").find("span").text(n)}});e("form.cart").find(".addon-custom, .addon-custom-textarea").each(function(){e(this).attr("maxlength")>0&&e(this).after('<small class="chars_remaining"><span>'+e(this).attr("maxlength")+"</span> "+woocommerce_addons_params.i18n_remaining+"</small>")});e("form.cart").on("change",".product-addon input, .product-addon textarea, .product-addon select, input.qty",function(){var t=e(this).closest("form.cart");t.trigger("woocommerce-product-addons-update")});e("form.variations_form").on("found_variation",function(t,n){var r=e(this),i=r.find("#product-addons-total");if(e(n.price_html).find(".amount:last").size()){product_price=e(n.price_html).find(".amount:last").text();product_price=product_price.replace(woocommerce_addons_params.currency_format_thousand_sep,"");product_price=product_price.replace(woocommerce_addons_params.currency_format_decimal_sep,".");product_price=product_price.replace(/[^0-9\.]/g,"");product_price=parseFloat(product_price);i.data("price",product_price)}r.trigger("woocommerce-product-addons-update")});e("form.cart").bind("woocommerce-product-addons-update",function(){var t=0,n=e(this),r=n.find("#product-addons-total"),i=r.data("price"),s=r.data("type");s=="variable"&&n.find(".single_variation").after(r);n.find(".addon").each(function(){var n=0;if(e(this).is(".addon-custom-price"))n=e(this).val();else if(e(this).is(".addon-input_multiplier")){if(isNaN(e(this).val())||e(this).val()==""){e(this).val("");e(this).closest("p").find(".addon-alert").show()}else{e(this).val()!=""&&e(this).val(Math.ceil(e(this).val()));e(this).closest("p").find(".addon-alert").hide()}n=e(this).data("price")*e(this).val()}else e(this).is(".addon-checkbox, .addon-radio")?e(this).is(":checked")&&(n=e(this).data("price")):e(this).is(".addon-select")?e(this).val()&&(n=e(this).find("option:selected").data("price")):e(this).val()&&(n=e(this).data("price"));n||(n=0);t=parseFloat(t)+parseFloat(n)});var o=parseFloat(n.find("input.qty").val());if(t>0&&o>0){t=parseFloat(t*o);var u=accounting.formatMoney(t,{symbol:woocommerce_addons_params.currency_format_symbol,decimal:woocommerce_addons_params.currency_format_decimal_sep,thousand:woocommerce_addons_params.currency_format_thousand_sep,precision:woocommerce_addons_params.currency_format_num_decimals,format:woocommerce_addons_params.currency_format});if(i){product_total_price=parseFloat(i*o);var a=accounting.formatMoney(product_total_price+t,{symbol:woocommerce_addons_params.currency_format_symbol,decimal:woocommerce_addons_params.currency_format_decimal_sep,thousand:woocommerce_addons_params.currency_format_thousand_sep,precision:woocommerce_addons_params.currency_format_num_decimals,format:woocommerce_addons_params.currency_format})}html='<dl class="product-addon-totals"><dt>'+woocommerce_addons_params.i18n_addon_total+'</dt><dd><strong><span class="amount">'+u+"</span></strong></dd>";a&&(html=html+"<dt>"+woocommerce_addons_params.i18n_grand_total+'</dt><dd><strong><span class="amount">'+a+"</span></strong></dd>");html+="</dl>";r.html(html)}else r.empty();e("body").trigger("updated_addons")});e("form.cart").find(".addon-custom, .addon-custom-textarea, .product-addon input, .product-addon textarea, .product-addon select, input.qty").change()}t();e("form.variations_form .product-addon").closest("form.cart").find(".variations select").change();e("body").on("quick-view-displayed",function(){t()})});
\ No newline at end of file
*** Product Addons Changelog ***
2014.03.21 - version 2.5.6
* Fix upload dir when "subdir" is not set.
2014.02.23 - version 2.5.5
* Fix cart button text for required addons in 2.1
2014.02.20 - version 2.5.4
* Styling fixes
2014.01.16 - version 2.5.3
* 2.1 compat
2013.12.06 - verison 2.5.2
* Check for subscription product type for add to cart urls
2013.12.06 - version 2.5.1
* Increased add_to_cart_text hook priority so that it applies to subscriptions too
2013.12.02 - version 2.5.0
* Removed legacy support
* Order again - restore addons
2013.10.10 - version 2.4.5
* Allow zero value for custom text inputs
2013.10.10 - version 2.4.4
* Only set addons to array when not set already
2013.10.10 - version 2.4.3
* Added min attribute when min is 0
2013.10.10 - version 2.4.2
* Prevent negative multiplier
2013.10.10 - version 2.4.1
* get_product_addons filter
2013.09.16 - version 2.4.0
* New custom input multipler field type which multiples an input value by the price. Thanks Andrew Beeken.
2013.09.08 - version 2.3.0
* Exclude products from Global Addons option
2013.08.22 - version 2.2.0
* Various tweaks to support composite products
2013.07.31 - version 2.1.3
* Corrected product type checking
* Unique class per addon
2013.07.23 - version 2.1.2
* Better compatibility with quick view
2013.07.18 - version 2.1.0
* Allow HTML in the addon descriptions
* Improved sanitization of posted options
2013.06.28 - version 2.0.9
* Global addons - include_children false so sibling category addons are not found.
2013.06.27 - version 2.0.8
* Rename addon totals string
2013.06.07 - version 2.0.7
* Change event on page load to show totals if needed
* Currency converter compatibility
2013.06.07 - version 2.0.6
* Labels are optional
* Select first radio element by default
2013.06.07 - version 2.0.5
* Variable product support when variations all cost the same
2013.06.07 - version 2.0.4
* Variable product support
2013.06.03 - version 2.0.3
* Fix global import
2013.05.31 - version 2.0.2
* get_woocommerce_price_format fallback
* Stripslashes fix
2013.05.29 - version 2.0.1
* Fix escaping of user defined data
2013.02.01 - version 2.0.0
* Complete rewrite for WooCommerce 2.0 with new interface.
* Global forms which can be applied to any product.
* Custom price inputs.
* More hooks for extending the plugin.
* Template based overrides for fields.
* Dynamic total cost display
* Removed upload size filter for the benefit of WP MU
* When required, select box default is "Select an option" rather than "None"
* Add to cart button on archives links to product page for products with required add ons
* Removed colon for text fields when label is empty
* required-product-addon class on wrapper div for required fields
* Add option to provide id for product_addons function
2013.02.09 - version 1.2.0
* WC 2.0 Compat
2012.12.04 - version 1.1.1
* New updater
2012.01.26 - version 1.1
* Required fields
* Textarea field type
* File upload field type
* Import/export forms between products
* Validation hook - woocommerce_validate_posted_addon_data
2012.01.26 - version 1.0.2
* Small plugin header update
* Strip tags from prices/meta
2011.12.1 - version 1.0.1
* Woo Updater
* Hook change
2011.11.17 - version 1.0
* First Release
\ No newline at end of file
<?php
/**
* Product_Addon_cart class.
*/
class Product_Addon_Cart {
/**
* __construct function.
*
* @access public
* @return void
*/
function __construct() {
// Add to cart
add_filter( 'woocommerce_add_cart_item', array( $this, 'add_cart_item' ), 20, 1 );
// Load cart data per page load
add_filter( 'woocommerce_get_cart_item_from_session', array( $this, 'get_cart_item_from_session' ), 20, 2 );
// Get item data to display
add_filter( 'woocommerce_get_item_data', array( $this, 'get_item_data' ), 10, 2 );
// Add item data to the cart
add_filter( 'woocommerce_add_cart_item_data', array( $this, 'add_cart_item_data' ), 10, 2 );
// Validate when adding to cart
add_filter( 'woocommerce_add_to_cart_validation', array( $this, 'validate_add_cart_item' ), 10, 3 );
// Add meta to order
add_action( 'woocommerce_add_order_item_meta', array( $this, 'order_item_meta' ), 10, 2 );
// order again functionality
add_filter( 'woocommerce_order_again_cart_item_data', array( $this, 're_add_cart_item_data' ), 10, 3 );
}
/**
* Add an error
*/
public function add_error( $error ) {
if ( function_exists( 'wc_add_notice' ) ) {
wc_add_notice( $error, 'error' );
} else {
global $woocommerce;
$woocommerce->add_error( $error );
}
}
/**
* add_cart_item function.
*
* @access public
* @param mixed $cart_item
* @return void
*/
public function add_cart_item( $cart_item ) {
// Adjust price if addons are set
if ( ! empty( $cart_item['addons'] ) ) {
$extra_cost = 0;
foreach ( $cart_item['addons'] as $addon )
if ( $addon['price'] > 0 )
$extra_cost += $addon['price'];
$cart_item['data']->adjust_price( $extra_cost );
}
return $cart_item;
}
/**
* get_cart_item_from_session function.
*
* @access public
* @param mixed $cart_item
* @param mixed $values
* @return void
*/
public function get_cart_item_from_session( $cart_item, $values ) {
if ( ! empty( $values['addons'] ) ) {
$cart_item['addons'] = $values['addons'];
$cart_item = $this->add_cart_item( $cart_item );
}
return $cart_item;
}
/**
* get_item_data function.
*
* @access public
* @param mixed $other_data
* @param mixed $cart_item
* @return void
*/
public function get_item_data( $other_data, $cart_item ) {
if ( ! empty( $cart_item['addons'] ) ) {
foreach ( $cart_item['addons'] as $addon ) {
$name = $addon['name'];
if ( $addon['price'] > 0 && apply_filters( 'woocommerce_addons_add_price_to_name', '__return_true' ) )
$name .= ' (' . woocommerce_price( $addon['price'] ) . ')';
$other_data[] = array(
'name' => $name,
'value' => $addon['value'],
'display' => isset( $addon['display'] ) ? $addon['display'] : ''
);
}
}
return $other_data;
}
/**
* add_cart_item_data function.
*
* @access public
* @param mixed $cart_item_meta
* @param mixed $product_id
* @return void
*/
public function add_cart_item_data( $cart_item_meta, $product_id ) {
global $woocommerce;
$product_addons = get_product_addons( $product_id );
if ( empty( $cart_item_meta['addons'] ) )
$cart_item_meta['addons'] = array();
if ( ! empty( $product_addons ) && is_array( $product_addons ) && sizeof( $product_addons ) > 0 ) {
foreach ( $product_addons as $addon ) {
if ( empty( $addon['field-name'] ) )
continue;
switch ( $addon['type'] ) {
case "checkbox" :
case "radiobutton" :
// Posted var = name, value = label
$posted = isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) ] ) ? $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) ] : '';
if ( is_array( $posted ) )
$posted = array_map( 'stripslashes', $posted );
else
$posted = stripslashes( $posted );
if ( empty( $posted ) )
continue;
foreach ( $addon['options'] as $option ) {
if ( array_search( sanitize_title( $option['label'] ), $posted ) !== FALSE ) {
// Set
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $addon['name'] ),
'value' => esc_attr( $option['label'] ),
'price' => esc_attr( $option['price'] )
);
}
}
break;
case "select" :
// Posted var = name, value = label
$posted = isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) ] ) ? $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) ] : '';
if ( is_array( $posted ) )
$posted = array_map( 'stripslashes', $posted );
else
$posted = stripslashes( $posted );
if ( empty( $posted ) )
continue;
$chosen_option = '';
$loop = 0;
foreach ( $addon['options'] as $option ) {
$loop++;
if ( sanitize_title( $option['label'] . '-' . $loop ) == $posted ) {
$chosen_option = $option;
break;
}
}
if ( ! $chosen_option )
continue;
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $addon['name'] ),
'value' => esc_attr( $chosen_option['label'] ),
'price' => esc_attr( $chosen_option['price'] )
);
break;
case "custom" :
case "custom_textarea" :
case "custom_price" :
case "input_multiplier" :
// Posted var = label, value = custom
foreach ( $addon['options'] as $option ) {
$posted = isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] ) ? $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] : '';
if ( is_array( $posted ) )
$posted = array_map( 'stripslashes', $posted );
else
$posted = stripslashes( $posted );
if ( empty( $posted ) )
continue;
$label = ! empty( $option['label'] ) ? trim( $option['label'] ) : trim( $addon['name'] );
if ( $addon['type'] == "custom_price" ) {
$price = floatval( woocommerce_clean( $posted ) );
if ( $price >= 0 ) {
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $label ),
'value' => esc_attr( strip_tags( $price ) ),
'price' => esc_attr( $price ),
'display' => esc_attr( strip_tags( woocommerce_price( $price ) ) ),
);
}
} elseif ( $addon['type'] == "input_multiplier" ) {
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $label ),
'value' => esc_attr( woocommerce_clean( $posted ) ),
'price' => esc_attr( woocommerce_clean( $posted )*$option['price'] )
);
} elseif ( $addon['type'] == "custom_textarea" ) {
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $label ),
'value' => esc_attr( wp_kses_post( $posted ) ),
'price' => esc_attr( $option['price'] )
);
} else {
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $label ),
'value' => esc_attr( woocommerce_clean( $posted ) ),
'price' => esc_attr( $option['price'] )
);
}
}
break;
case "file_upload" :
include_once( ABSPATH . 'wp-admin/includes/file.php' );
include_once( ABSPATH . 'wp-admin/includes/media.php' );
add_filter( 'upload_dir', array( $this, 'upload_dir' ) );
foreach ( $addon['options'] as $option ) {
$field_name = 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] );
if ( ! empty( $_FILES[ $field_name ] ) && ! empty( $_FILES[ $field_name ]['name'] ) ) {
$file = $_FILES[ $field_name ];
$upload = wp_handle_upload( $file, array( 'test_form' => false ) );
$label = ! empty( $option['label'] ) ? trim( $option['label'] ) : trim( $addon['name'] );
if ( empty( $upload['error'] ) && ! empty( $upload['file'] ) ) {
$file_path = $upload['url'];
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $label ),
'value' => esc_attr( woocommerce_clean( $file_path ) ),
'display' => esc_attr( basename( woocommerce_clean( $file_path ) ) ),
'price' => esc_attr( $option['price'] )
);
} else {
$this->add_error( $upload['error'] );
}
}
}
remove_filter( 'upload_dir', array( $this, 'upload_dir' ) );
break;
}
}
}
return $cart_item_meta;
}
/**
* upload_dir function.
*
* @access public
* @param mixed $pathdata
* @return void
*/
public function upload_dir( $pathdata ) {
global $woocommerce;
if ( empty( $pathdata['subdir'] ) ) {
$pathdata['path'] = $pathdata['path'] . '/product_addons_uploads/' . md5( $woocommerce->session->get_customer_id() );
$pathdata['url'] = $pathdata['url']. '/product_addons_uploads/' . md5( $woocommerce->session->get_customer_id() );
$pathdata['subdir'] = '/product_addons_uploads/' . md5( $woocommerce->session->get_customer_id() );
} else {
$subdir = '/product_addons_uploads/' . md5( $woocommerce->session->get_customer_id() );
$pathdata['path'] = str_replace( $pathdata['subdir'], $subdir, $pathdata['path'] );
$pathdata['url'] = str_replace( $pathdata['subdir'], $subdir, $pathdata['url'] );
$pathdata['subdir'] = str_replace( $pathdata['subdir'], $subdir, $pathdata['subdir'] );
}
return $pathdata;
}
/**
* validate_add_cart_item function.
*
* @access public
* @param mixed $passed
* @param mixed $product_id
* @param mixed $qty
* @return void
*/
public function validate_add_cart_item( $passed, $product_id, $qty ) {
global $woocommerce;
$product_addons = get_product_addons( $product_id );
if ( ! empty( $product_addons ) && is_array( $product_addons ) && sizeof( $product_addons ) > 0 ) {
foreach ( $product_addons as $addon ) {
if ( empty( $addon['field-name'] ) || empty( $addon['required'] ) )
continue;
if ( $addon['required'] ) {
switch ( $addon['type'] ) {
case "checkbox" :
case "radiobutton" :
case "select" :
$posted = isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) ] ) ? $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) ] : '';
if ( ! $posted || sizeof( $posted ) == 0 )
$passed = false;
break;
case "custom" :
case "custom_price" :
case "custom_textarea" :
case "input_multiplier" :
foreach ( $addon['options'] as $option ) {
$posted = isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] ) ? $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] : "";
if ( $posted === "" || sizeof( $posted ) == 0 ) {
$passed = false;
break;
}
if ( $addon['type'] == "custom_price" ) {
$price = floatval( woocommerce_clean( $posted ) );
if ( $price < 0 ) {
$this->add_error( sprintf( __( 'Invalid price entered for "%s".', 'woocommerce' ), $addon['name'] ) );
return false;
}
}
}
break;
case "file_upload" :
foreach ( $addon['options'] as $option ) {
$field_name = 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] );
if ( empty( $_FILES[ $field_name ] ) || empty( $_FILES[ $field_name ]['name'] ) ) {
$passed = false;
break;
}
}
break;
}
if ( ! $passed ) {
$this->add_error( sprintf( __( '"%s" is a required field.', 'woocommerce' ), $addon['name'] ) );
break;
}
}
// Min max
if ( $addon['options'] ) {
foreach ( $addon['options'] as $option ) {
switch ( $addon['type'] ) {
case "custom" :
case "custom_textarea" :
$posted = isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] ) ? $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] : '';
if ( ! empty( $option['min'] ) && ! empty( $posted ) && strlen( $posted ) < $option['min'] ) {
$this->add_error( sprintf( __( 'The minimum allowed length for "%s - %s" is %s.', 'woocommerce' ), $addon['name'], $option['label'], $option['min'] ) );
return false;
}
if ( ! empty( $option['max'] ) && ! empty( $posted ) && strlen( $posted ) > $option['max'] ) {
$this->add_error( sprintf( __( 'The maximum allowed length for "%s - %s" is %s.', 'woocommerce' ), $addon['name'], $option['label'], $option['max'] ) );
return false;
}
break;
case "input_multiplier" :
$posted = isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] ) ? intval( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] ) : '';
if ( $posted < 0 ) {
$this->add_error( sprintf( __( 'Please enter a value greater than 0 for "%s - %s".', 'woocommerce' ), $addon['name'], $option['label'] ) );
return false;
}
if ( ! empty( $option['min'] ) && ! empty( $posted ) && $posted < $option['min'] ) {
$this->add_error( sprintf( __( 'The minimum allowed value for "%s - %s" is %s.', 'woocommerce' ), $addon['name'], $option['label'], $option['min'] ) );
return false;
}
if ( ! empty( $option['max'] ) && ! empty( $posted ) && $posted > $option['max'] ) {
$this->add_error( sprintf( __( 'The maximum allowed value for "%s - %s" is %s.', 'woocommerce' ), $addon['name'], $option['label'], $option['max'] ) );
return false;
}
break;
case "custom_price" :
$posted = isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] ) ? $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] : '';
if ( ! empty( $option['min'] ) && ! empty( $posted ) && $posted < $option['min'] ) {
$this->add_error( sprintf( __( 'The minimum allowed amount for "%s - %s" is %s.', 'woocommerce' ), $addon['name'], $option['label'], $option['min'] ) );
return false;
}
if ( ! empty( $option['max'] ) && ! empty( $posted ) && $posted > $option['max'] ) {
$this->add_error( sprintf( __( 'The maximum allowed amount for "%s - %s" is %s.', 'woocommerce' ), $addon['name'], $option['label'], $option['max'] ) );
return false;
}
break;
}
}
}
do_action( 'woocommerce_validate_posted_addon_data', $addon );
}
}
return $passed;
}
/**
* Add meta to orders
*
* @access public
* @param mixed $item_id
* @param mixed $values
* @return void
*/
public function order_item_meta( $item_id, $values ) {
if ( ! empty( $values['addons'] ) ) {
foreach ( $values['addons'] as $addon ) {
$name = $addon['name'];
if ( $addon['price'] > 0 && apply_filters( 'woocommerce_addons_add_price_to_name', true ) )
$name .= ' (' . strip_tags(woocommerce_price($addon['price'])) . ')';
woocommerce_add_order_item_meta( $item_id, $name, $addon['value'] );
}
}
}
public function re_add_cart_item_data( $cart_item_meta, $product, $order ) {
global $woocommerce;
// Disable validation
remove_filter( 'woocommerce_add_to_cart_validation', array( $this, 'validate_add_cart_item' ), 10, 3 );
// Get addon data
$product_addons = get_product_addons( $product['product_id'] );
if ( empty( $cart_item_meta['addons'] ) )
$cart_item_meta['addons'] = array();
if ( ! empty( $product_addons ) && is_array( $product_addons ) && sizeof( $product_addons ) > 0 ) {
foreach ( $product_addons as $addon ) {
$posted = '';
if ( empty( $addon['field-name'] ) )
continue;
switch ( $addon['type'] ) {
case "checkbox" :
case "radiobutton" :
$posted = array();
foreach ( $product['item_meta'] as $key => $meta ) {
if ( strpos( $key, $addon['name'] ) === 0 ) {
$posted[] = sanitize_title( $meta[0] );
}
}
if ( empty( $posted ) )
continue;
foreach ( $addon['options'] as $option ) {
if ( array_search( sanitize_title( $option['label'] ), $posted ) !== FALSE ) {
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $addon['name'] ),
'value' => esc_attr( $option['label'] ),
'price' => esc_attr( $option['price'] )
);
}
}
break;
case "select" :
foreach ( $product['item_meta'] as $key => $meta ) {
if ( strpos( $key, $addon['name'] ) === 0 ) {
$posted = sanitize_title( $meta[0] );
}
}
if ( empty( $posted ) )
continue;
$chosen_option = '';
$loop = 0;
foreach ( $addon['options'] as $option ) {
$loop++;
if ( sanitize_title( $option['label'] ) == $posted ) {
$chosen_option = $option;
break;
}
}
if ( ! $chosen_option )
continue;
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $addon['name'] ),
'value' => esc_attr( $chosen_option['label'] ),
'price' => esc_attr( $chosen_option['price'] )
);
break;
case "custom" :
case "custom_textarea" :
case "custom_price" :
case "input_multiplier" :
// Posted var = label, value = custom
foreach ( $addon['options'] as $option ) {
$label = ! empty( $option['label'] ) ? trim( $option['label'] ) : trim( $addon['name'] );
foreach ( $product['item_meta'] as $key => $meta ) {
if ( strpos( $key, $label ) === 0 ) {
$posted = $meta[0];
}
}
if ( empty( $posted ) )
continue;
if ( $addon['type'] == "custom_price" ) {
$price = floatval( woocommerce_clean( $posted ) );
if ( $price >= 0 ) {
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $label ),
'value' => esc_attr( strip_tags( $price ) ),
'price' => esc_attr( $price ),
'display' => esc_attr( strip_tags( woocommerce_price( $price ) ) ),
);
}
} elseif ( $addon['type'] == "input_multiplier" ) {
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $label ),
'value' => esc_attr( woocommerce_clean( $posted ) ),
'price' => esc_attr( woocommerce_clean( $posted )*$option['price'] )
);
} elseif ( $addon['type'] == "custom_textarea" ) {
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $label ),
'value' => esc_attr( wp_kses_post( $posted ) ),
'price' => esc_attr( $option['price'] )
);
} else {
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $label ),
'value' => esc_attr( woocommerce_clean( $posted ) ),
'price' => esc_attr( $option['price'] )
);
}
}
break;
case "file_upload" :
$label = ! empty( $option['label'] ) ? trim( $option['label'] ) : trim( $addon['name'] );
foreach ( $product['item_meta'] as $key => $meta ) {
if ( strpos( $key, $addon['name'] ) === 0 ) {
$posted = $meta[0];
}
}
$cart_item_meta['addons'][] = array(
'name' => esc_attr( $label ),
'value' => esc_attr( $posted ),
'display' => esc_attr( basename( $posted ) ),
'price' => esc_attr( $option['price'] )
);
break;
}
}
}
return $cart_item_meta;
}
}
\ No newline at end of file
<?php
/**
* Product_Addon_Display class.
*/
class Product_Addon_Display {
var $version = '2.2.0';
/**
* __construct function.
*
* @access public
* @return void
*/
public function __construct() {
// Styles
add_action( 'get_header', array( $this, 'styles' ) );
add_action( 'wc_quick_view_enqueue_scripts', array( $this, 'addon_scripts' ) );
// Addon display
add_action( 'woocommerce_before_add_to_cart_button', array( $this, 'display' ), 10 );
add_action( 'wc_product_addons_end', array( $this, 'totals' ), 10 );
// Change buttons/cart urls
add_filter( 'add_to_cart_text', array( $this, 'add_to_cart_text'), 15 );
add_filter( 'woocommerce_product_add_to_cart_text', array( $this, 'add_to_cart_text'), 15 );
add_filter( 'woocommerce_add_to_cart_url', array( $this, 'add_to_cart_url' ), 10, 1 );
add_filter( 'woocommerce_product_add_to_cart_url', array( $this, 'add_to_cart_url' ), 10, 1 );
}
/**
* styles function.
*
* @access public
* @return void
*/
function styles() {
if ( is_singular( 'product' ) || class_exists( 'WC_Quick_View' ) )
wp_enqueue_style( 'woocommerce-addons-css', plugins_url( basename( dirname( dirname( __FILE__ ) ) ) ) . '/assets/css/frontend.css' );
}
/**
* Get the plugin path
*/
function plugin_path() {
return $this->plugin_path = untrailingslashit( plugin_dir_path( dirname( __FILE__ ) ) );
}
/**
* Enqueue addon scripts
*/
function addon_scripts() {
wp_register_script( 'accounting', plugins_url( basename( dirname( dirname( __FILE__ ) ) ) ) . '/assets/js/accounting.js', '', '0.3.2' );
wp_enqueue_script( 'woocommerce-addons', plugins_url( basename( dirname( dirname( __FILE__ ) ) ) ) . '/assets/js/addons.js', array( 'jquery', 'accounting' ), '1.0', true );
$params = array(
'i18n_addon_total' => __( 'Options total:', 'wc_product_addons' ),
'i18n_grand_total' => __( 'Grand total:', 'wc_product_addons' ),
'i18n_remaining' => __( 'characters remaining', 'wc_product_addons' ),
'currency_format_num_decimals' => absint( get_option( 'woocommerce_price_num_decimals' ) ),
'currency_format_symbol' => get_woocommerce_currency_symbol(),
'currency_format_decimal_sep' => esc_attr( stripslashes( get_option( 'woocommerce_price_decimal_sep' ) ) ),
'currency_format_thousand_sep' => esc_attr( stripslashes( get_option( 'woocommerce_price_thousand_sep' ) ) )
);
if ( ! function_exists( 'get_woocommerce_price_format' ) ) {
$currency_pos = get_option( 'woocommerce_currency_pos' );
switch ( $currency_pos ) {
case 'left' :
$format = '%1$s%2$s';
break;
case 'right' :
$format = '%2$s%1$s';
break;
case 'left_space' :
$format = '%1$s&nbsp;%2$s';
break;
case 'right_space' :
$format = '%2$s&nbsp;%1$s';
break;
}
$params['currency_format'] = esc_attr( str_replace( array( '%1$s', '%2$s' ), array( '%s', '%v' ), $format ) );
} else {
$params['currency_format'] = esc_attr( str_replace( array( '%1$s', '%2$s' ), array( '%s', '%v' ), get_woocommerce_price_format() ) );
}
wp_localize_script( 'woocommerce-addons', 'woocommerce_addons_params', $params );
}
/**
* display function.
*
* @access public
* @param bool $post_id (default: false)
* @return void
*/
function display( $post_id = false, $prefix = false ) {
global $product;
if ( ! $post_id ) {
global $post;
$post_id = $post->ID;
}
$this->addon_scripts();
$product_addons = get_product_addons( $post_id, $prefix );
if ( is_array( $product_addons ) && sizeof( $product_addons ) > 0 ) {
do_action( 'wc_product_addons_start', $post_id );
foreach ( $product_addons as $addon ) {
if ( ! isset( $addon['field-name'] ) )
continue;
woocommerce_get_template( 'addons/addon-start.php', array(
'addon' => $addon,
'required' => $addon['required'],
'name' => $addon['name'],
'description' => $addon['description'],
'type' => $addon['type'],
), 'woocommerce-product-addons', $this->plugin_path() . '/templates/' );
echo $this->get_addon_html( $addon );
woocommerce_get_template( 'addons/addon-end.php', array(
'addon' => $addon,
), 'woocommerce-product-addons', $this->plugin_path() . '/templates/' );
}
do_action( 'wc_product_addons_end', $post_id );
}
}
/**
* totals function.
*
* @access public
* @return void
*/
function totals( $post_id ) {
global $product;
if ( ! isset( $product ) || $product->id != $post_id )
$the_product = get_product( $post_id );
else
$the_product = $product;
echo '<div id="product-addons-total" data-type="' . $the_product->product_type . '" data-price="' . ( is_object( $the_product ) ? $the_product->get_price() : '' ) . '"></div>';
}
/**
* get_addon_html function.
*
* @access public
* @param mixed $addon
* @return void
*/
function get_addon_html( $addon ) {
ob_start();
$method_name = 'get_' . $addon['type'] . '_html';
if ( method_exists( $this, $method_name ) ) {
$this->$method_name( $addon );
}
do_action( 'wc_product_addons_get_' . $addon['type'] . '_html', $addon );
return ob_get_clean();
}
/**
* get_checkbox_html function.
*
* @access public
* @return void
*/
function get_checkbox_html( $addon ) {
woocommerce_get_template( 'addons/checkbox.php', array(
'addon' => $addon,
), 'woocommerce-product-addons', $this->plugin_path() . '/templates/' );
}
/**
* get_radiobutton_html function.
*
* @access public
* @param mixed $addon
* @return void
*/
function get_radiobutton_html( $addon ) {
woocommerce_get_template( 'addons/radiobutton.php', array(
'addon' => $addon,
), 'woocommerce-product-addons', $this->plugin_path() . '/templates/' );
}
/**
* get_select_html function.
*
* @access public
* @return void
*/
function get_select_html( $addon ) {
woocommerce_get_template( 'addons/select.php', array(
'addon' => $addon,
), 'woocommerce-product-addons', $this->plugin_path() . '/templates/' );
}
/**
* get_custom_html function.
*
* @access public
* @return void
*/
function get_custom_html( $addon ) {
woocommerce_get_template( 'addons/custom.php', array(
'addon' => $addon,
), 'woocommerce-product-addons', $this->plugin_path() . '/templates/' );
}
/**
* get_custom_textarea function.
*
* @access public
* @return void
*/
function get_custom_textarea_html( $addon ) {
woocommerce_get_template( 'addons/custom_textarea.php', array(
'addon' => $addon,
), 'woocommerce-product-addons', $this->plugin_path() . '/templates/' );
}
/**
* get_file_upload_html function.
*
* @access public
* @return void
*/
function get_file_upload_html( $addon ) {
woocommerce_get_template( 'addons/file_upload.php', array(
'addon' => $addon,
'max_size' => $this->max_upload_size()
), 'woocommerce-product-addons', $this->plugin_path() . '/templates/' );
}
/**
* get_custom_price_html function.
*
* @access public
* @return void
*/
function get_custom_price_html( $addon ) {
woocommerce_get_template( 'addons/custom_price.php', array(
'addon' => $addon,
), 'woocommerce-product-addons', $this->plugin_path() . '/templates/' );
}
/**
* get_input_multiplier_html function.
*
* @access public
* @return void
*/
function get_input_multiplier_html( $addon ) {
woocommerce_get_template( 'addons/input_multiplier.php', array(
'addon' => $addon,
), 'woocommerce-product-addons', $this->plugin_path() . '/templates/' );
}
/**
* check_required_addons function.
*
* @access private
* @param mixed $product_id
* @return void
*/
private function check_required_addons( $product_id ) {
$addons = get_product_addons( $product_id );
if ( $addons && ! empty( $addons ) ) {
foreach ( $addons as $addon ) {
if ( '1' == $addon['required'] ) {
return true;
}
}
}
return false;
}
/**
* add_to_cart_text function.
*
* @access public
* @param mixed $text
* @return void
*/
public function add_to_cart_text( $text ) {
global $product;
if ( ! is_single( $product->id ) ) {
if ( $this->check_required_addons( $product->id ) ) {
$product->product_type = 'addons';
$text = apply_filters( 'addons_add_to_cart_text', __( 'Select options', 'wc_product_addons' ) );
}
}
return $text;
}
/**
* add_to_cart_url function.
*
* @access public
* @param mixed $url
* @return void
*/
function add_to_cart_url( $url ) {
global $product;
if ( ! is_single( $product->id ) && in_array( $product->product_type, array( 'subscription', 'simple' ) ) && ( ! isset( $_GET['wc-api'] ) || $_GET['wc-api'] !== 'WC_Quick_View' ) ) {
if ( $this->check_required_addons( $product->id ) ) {
$product->product_type = 'addons';
$url = apply_filters( 'addons_add_to_cart_url', get_permalink( $product->id ) );
}
}
return $url;
}
/**
* max_upload_size function.
*
* @access public
* @return void
*/
function max_upload_size() {
$u_bytes = $this->convert_hr_to_bytes( ini_get( 'upload_max_filesize' ) );
$p_bytes = $this->convert_hr_to_bytes( ini_get( 'post_max_size' ) );
$bytes = min($u_bytes, $p_bytes);
return $this->convert_bytes_to_hr( $bytes );
}
/**
* convert_hr_to_bytes function.
*
* @access public
* @param mixed $size
* @return void
*/
function convert_hr_to_bytes( $size ) {
$size = strtolower($size);
$bytes = (int) $size;
if ( strpos($size, 'k') !== false )
$bytes = intval($size) * 1024;
elseif ( strpos($size, 'm') !== false )
$bytes = intval($size) * 1024 * 1024;
elseif ( strpos($size, 'g') !== false )
$bytes = intval($size) * 1024 * 1024 * 1024;
return $bytes;
}
/**
* convert_bytes_to_hr function.
*
* @access public
* @param mixed $bytes
* @return void
*/
function convert_bytes_to_hr( $bytes ) {
$units = array( 0 => 'B', 1 => 'kB', 2 => 'MB', 3 => 'GB' );
$log = log( $bytes, 1024 );
$power = (int) $log;
$size = pow(1024, $log - $power);
return $size . $units[$power];
}
}
\ No newline at end of file
<?php
/*
Plugin Name: WooCommerce Better Product Add-ons
Plugin URI: https://github.com/nextime/woocommerce-better-product-addons
Description: WooCommerce Better Product Add-ons is a fork of WooCommerce Product Add-ons 2.5.6. It lets you add extra options to products which the user can select. Add-ons can be checkboxes, a select box, or custom input. Each option can optionally be given a price which is added to the cost of the product.
Version: 2.5.7
Original Author: WooThemes
Fork Author: nextime
Original Author URI: http://woothemes.com
Fork Author URI: https://github.com/nextime/
Requires at least: 3.1
Tested up to: 3.2
Copyright for original WooCommerce Product Add-ons: © 2009-2011 WooThemes.
Copyright: © 2014 nextime.
License: GNU General Public License v3.0
License URI: http://www.gnu.org/licenses/gpl-3.0.html
*/
/**
* Required functions
*/
if ( ! function_exists( 'woothemes_queue_update' ) )
require_once( 'woo-includes/woo-functions.php' );
/**
* Plugin updates
*/
woothemes_queue_update( plugin_basename( __FILE__ ), '147d0077e591e16db9d0d67daeb8c484', '18618' );
if ( is_woocommerce_active() ) {
/**
* Localisation
*/
load_plugin_textdomain( 'wc_product_addons', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
/**
* Init
*/
if ( ! class_exists( 'Product_Addon_Display' ) ) {
if ( is_admin() ) {
include_once( 'admin/class-product-addon-admin.php' );
$GLOBALS['Product_Addon_Admin'] = new Product_Addon_Admin();
}
include_once( 'classes/class-product-addon-display.php' );
$GLOBALS['Product_Addon_Display'] = new Product_Addon_Display();
include_once( 'classes/class-product-addon-cart.php' );
$GLOBALS['Product_Addon_Cart'] = new Product_Addon_Cart();
}
/**
* Gets addons assigned to a product by ID
*
* @param int $post_id ID of the product to get addons for
* @return array array of addons
*/
function get_product_addons( $post_id, $prefix = false ) {
if ( ! $post_id )
return array();
$addons = array();
$raw_addons = array();
$product_terms = wp_get_post_terms( $post_id, 'product_cat', array( 'fields' => 'ids' ) );
$exclude = get_post_meta( $post_id, '_product_addons_exclude_global', TRUE );
// Product level addons
$raw_addons[10][0] = array_filter( (array) get_post_meta( $post_id, '_product_addons', true ) );
if ( !isset($exclude) || $exclude != '1' ) :
// Global level addons (all products)
$args = array(
'posts_per_page' => -1,
'orderby' => 'meta_value',
'order' => 'ASC',
'meta_key' => '_priority',
'post_type' => 'global_product_addon',
'post_status' => 'publish',
'suppress_filters' => true,
'meta_query' => array(
array(
'key' => '_all_products',
'value' => '1',
)
)
);
$global_addons = get_posts( $args );
if ( $global_addons ) :
foreach ( $global_addons as $global_addon ) {
$priority = get_post_meta( $global_addon->ID, '_priority', true );
$raw_addons[ $priority ][ $global_addon->ID ] = array_filter( (array) get_post_meta( $global_addon->ID, '_product_addons', true ) );
}
endif;
// Global level addons (categories)
if ( $product_terms ) :
$args = array(
'posts_per_page' => -1,
'orderby' => 'meta_value',
'order' => 'ASC',
'meta_key' => '_priority',
'post_type' => 'global_product_addon',
'post_status' => 'publish',
'suppress_filters' => true,
'tax_query' => array(
array(
'taxonomy' => 'product_cat',
'field' => 'id',
'terms' => $product_terms,
'include_children' => false
)
)
);
$global_addons = get_posts( $args );
if ( $global_addons ) :
foreach ( $global_addons as $global_addon ) {
$priority = get_post_meta( $global_addon->ID, '_priority', true );
$raw_addons[ $priority ][ $global_addon->ID ] = array_filter( (array) get_post_meta( $global_addon->ID, '_product_addons', true ) );
}
endif;
endif;
endif; // exclude from global addons
ksort( $raw_addons );
foreach ( $raw_addons as $addon_group ) {
if ( $addon_group ) {
foreach ( $addon_group as $addon )
$addons = array_merge( $addons, $addon );
}
}
// Generate field names with unqiue prefixes
if ( ! $prefix )
$prefix = apply_filters( 'product_addons_field_prefix', "{$post_id}-", $post_id );
foreach ( $addons as $addon_key => $addon ) {
$addons[ $addon_key ]['field-name'] = $prefix . $addon['name'];
}
return apply_filters( 'wc_get_product_addons', $addons );
}
/**
* Register post types for global addons
*/
function product_addons_post_types() {
register_post_type( "global_product_addon",
array(
'public' => false,
'show_ui' => false,
'capability_type' => 'product',
'map_meta_cap' => true,
'publicly_queryable' => false,
'exclude_from_search' => true,
'hierarchical' => false,
'rewrite' => false,
'query_var' => false,
'supports' => array( 'title' ),
'show_in_nav_menus' => false
)
);
register_taxonomy_for_object_type( 'product_cat', 'global_product_addon' );
}
add_action( 'init', 'product_addons_post_types', 20 );
}
<?php do_action( 'wc_product_addon_end', $addon ); ?>
<div class="clear"></div>
</div>
\ No newline at end of file
<div class="<?php if ( 1 == $required ) echo 'required-product-addon'; ?> product-addon product-addon-<?php echo sanitize_title( $name ); ?>">
<?php do_action( 'wc_product_addon_start', $addon ); ?>
<?php if ( $name ) : ?>
<h3 class="addon-name"><?php echo wptexturize( $name ); ?> <?php if ( 1 == $required ) echo '<abbr class="required" title="required">*</abbr>'; ?></h3>
<?php endif; ?>
<?php if ( $description ) : ?>
<?php echo '<div class="addon-description">' . wpautop( wptexturize( $description ) ) . '</div>'; ?>
<?php endif; ?>
<?php do_action( 'wc_product_addon_options', $addon ); ?>
<?php foreach ( $addon['options'] as $i => $option ) :
$price = $option['price'] > 0 ? '(' . woocommerce_price( $option['price'] ) . ')' : '';
$current_value = (
isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) ] ) &&
in_array( sanitize_title( $option['label'] ), $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) ] )
) ? 1 : 0;
?>
<p class="form-row form-row-wide addon-wrap-<?php echo sanitize_title( $addon['field-name'] ) . '-' . $i; ?>">
<label><input type="checkbox" class="addon addon-checkbox" name="addon-<?php echo sanitize_title( $addon['field-name'] ); ?>[]" data-price="<?php echo $option['price']; ?>" value="<?php echo sanitize_title( $option['label'] ); ?>" <?php checked( $current_value, 1 ); ?> /> <?php echo wptexturize( $option['label'] . ' ' . $price ); ?></label>
</p>
<?php endforeach; ?>
\ No newline at end of file
<?php foreach ( $addon['options'] as $key => $option ) :
$current_value = isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] ) ? $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] : '';
$price = $option['price'] > 0 ? '(' . woocommerce_price( $option['price'] ) . ')' : '';
if ( empty( $option['label'] ) ) : ?>
<p class="form-row form-row-wide addon-wrap-<?php echo sanitize_title( $addon['field-name'] ); ?>">
<input type="text" class="input-text addon addon-custom" data-price="<?php echo $option['price']; ?>" name="addon-<?php echo sanitize_title( $addon['field-name'] ); ?>-<?php echo sanitize_title( $option['label'] ); ?>" value="<?php echo esc_attr( $current_value ); ?>" <?php if ( ! empty( $option['max'] ) ) echo 'maxlength="' . $option['max'] .'"'; ?> />
</p>
<?php else : ?>
<p class="form-row form-row-wide addon-wrap-<?php echo sanitize_title( $addon['field-name'] ); ?>">
<label><?php echo wptexturize( $option['label'] ) . ' ' . $price; ?> <input type="text" class="input-text addon addon-custom" data-price="<?php echo $option['price']; ?>" name="addon-<?php echo sanitize_title( $addon['field-name'] ); ?>-<?php echo sanitize_title( $option['label'] ); ?>" value="<?php echo esc_attr( $current_value ); ?>" <?php if ( ! empty( $option['max'] ) ) echo 'maxlength="' . $option['max'] .'"'; ?> /></label>
</p>
<?php endif; ?>
<?php endforeach; ?>
\ No newline at end of file
<?php foreach ( $addon['options'] as $key => $option ) :
$current_value = isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] ) ? $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] : '';
$price = $option['price'] > 0 ? '(' . woocommerce_price( $option['price'] ) . ')' : '';
if ( empty( $option['label'] ) ) : ?>
<p class="form-row form-row-wide addon-wrap-<?php echo sanitize_title( $addon['field-name'] ); ?>">
<input type="number" step="any" class="input-text addon addon-custom-price" name="addon-<?php echo sanitize_title( $addon['field-name'] ); ?>-<?php echo sanitize_title( $option['label'] ); ?>" value="<?php echo esc_attr( $current_value ); ?>" <?php if ( ! empty( $option['min'] ) ) echo 'min="' . $option['min'] .'"'; ?> <?php if ( ! empty( $option['max'] ) ) echo 'max="' . $option['max'] .'"'; ?> />
</p>
<?php else : ?>
<p class="form-row form-row-wide addon-wrap-<?php echo sanitize_title( $addon['field-name'] ); ?>">
<label><?php echo wptexturize( $option['label'] ) . ' ' . $price; ?> <input type="number" step="any" class="input-text addon addon-custom-price" name="addon-<?php echo sanitize_title( $addon['field-name'] ); ?>-<?php echo sanitize_title( $option['label'] ); ?>" value="<?php echo esc_attr( $current_value ); ?>" <?php if ( ! empty( $option['min'] ) ) echo 'min="' . $option['min'] .'"'; ?> <?php if ( ! empty( $option['max'] ) ) echo 'max="' . $option['max'] .'"'; ?> /></label>
</p>
<?php endif; ?>
<?php endforeach; ?>
\ No newline at end of file
<?php foreach ( $addon['options'] as $key => $option ) :
$current_value = isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] ) ? $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] : '';
$price = $option['price'] > 0 ? '(' . woocommerce_price( $option['price'] ) . ')' : '';
if ( empty( $option['label'] ) ) : ?>
<p class="form-row form-row-wide addon-wrap-<?php echo sanitize_title( $addon['field-name'] ); ?>">
<textarea type="text" class="input-text addon addon-custom-textarea" data-price="<?php echo $option['price']; ?>" name="addon-<?php echo sanitize_title( $addon['field-name'] ); ?>-<?php echo sanitize_title( $option['label'] ); ?>" rows="4" cols="20" <?php if ( ! empty( $option['max'] ) ) echo 'maxlength="' . $option['max'] .'"'; ?>><?php echo esc_textarea( $current_value ); ?></textarea>
</p>
<?php else : ?>
<p class="form-row form-row-wide addon-wrap-<?php echo sanitize_title( $addon['field-name'] ); ?>">
<label><?php echo wptexturize( $option['label'] ) . ' ' . $price; ?> <textarea type="text" class="input-text addon addon-custom-textarea" data-price="<?php echo $option['price']; ?>" name="addon-<?php echo sanitize_title( $addon['field-name'] ); ?>-<?php echo sanitize_title( $option['label'] ); ?>" rows="4" cols="20" <?php if ( ! empty( $option['max'] ) ) echo 'maxlength="' . $option['max'] .'"'; ?>><?php echo esc_textarea( $current_value ); ?></textarea></label>
</p>
<?php endif; ?>
<?php endforeach; ?>
\ No newline at end of file
<?php foreach ( $addon['options'] as $key => $option ) :
$price = ($option['price']>0) ? ' (' . woocommerce_price( $option['price'] ) . ')' : '';
if ( empty( $option['label'] ) ) : ?>
<p class="form-row form-row-wide addon-wrap-<?php echo sanitize_title( $addon['field-name'] ); ?>">
<input type="file" class="input-text addon" data-price="<?php echo $option['price']; ?>" name="addon-<?php echo sanitize_title( $addon['field-name'] ); ?>-<?php echo sanitize_title( $option['label'] ); ?>" /> <small><?php echo sprintf( __( '(max file size %s)', 'wc_product_addons' ), $max_size ) ?></small>
</p>
<?php else : ?>
<p class="form-row form-row-wide addon-wrap-<?php echo sanitize_title( $addon['field-name'] ); ?>">
<label><?php echo wptexturize( $option['label'] ) . ' ' . $price; ?> <input type="file" class="input-text addon" data-price="<?php echo $option['price']; ?>" name="addon-<?php echo sanitize_title( $addon['field-name'] ); ?>-<?php echo sanitize_title( $option['label'] ); ?>" /> <small><?php echo sprintf( __( '(max file size %s)', 'wc_product_addons' ), $max_size ) ?></small></label>
</p>
<?php endif; ?>
<?php endforeach; ?>
\ No newline at end of file
<?php foreach ( $addon['options'] as $key => $option ) :
$current_value = max( 0, $option['min'], isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] ) ? $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) . '-' . sanitize_title( $option['label'] ) ] : '' );
$price = $option['price'] > 0 ? '(' . woocommerce_price( $option['price'] ) . ')' : '';
if ( empty( $option['label'] ) ) : ?>
<p class="form-row form-row-wide addon-wrap-<?php echo sanitize_title( $addon['field-name'] ); ?>">
<input type="number" step="" class="input-text addon addon-input_multiplier" data-price="<?php echo $option['price']; ?>" name="addon-<?php echo sanitize_title( $addon['field-name'] ); ?>-<?php echo sanitize_title( $option['label'] ); ?>" value="<?php echo esc_attr( $current_value ); ?>" <?php if ( ! empty( $option['min'] ) || $option['min'] === '0' ) echo 'min="' . $option['min'] .'"'; ?> <?php if ( ! empty( $option['max'] ) ) echo 'max="' . $option['max'] .'"'; ?> />
<span class="addon-alert"><?php _e( 'This must be a number!', 'wc_product_addons' ); ?></span>
</p>
<?php else : ?>
<p class="form-row form-row-wide addon-wrap-<?php echo sanitize_title( $addon['field-name'] ); ?>">
<label><?php echo wptexturize( $option['label'] ) . ' ' . $price; ?> <input type="number" step="" class="input-text addon addon-input_multiplier" data-price="<?php echo $option['price']; ?>" name="addon-<?php echo sanitize_title( $addon['field-name'] ); ?>-<?php echo sanitize_title( $option['label'] ); ?>" value="<?php echo esc_attr( $current_value ); ?>" <?php if ( ! empty( $option['min'] ) || $option['min'] === '0' ) echo 'min="' . $option['min'] .'"'; ?> <?php if ( ! empty( $option['max'] ) ) echo 'max="' . $option['max'] .'"'; ?> /></label>
<span class="addon-alert"><?php _e( 'This must be a number!', 'wc_product_addons' ); ?></span>
</p>
<?php endif; ?>
<?php endforeach; ?>
\ No newline at end of file
<?php $first = true; foreach ( $addon['options'] as $i => $option ) :
$price = $option['price'] > 0 ? '(' . woocommerce_price( $option['price'] ) . ')' : '';
if ( isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) ] ) ) {
$current_value = (
isset( $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) ] ) &&
in_array( sanitize_title( $option['label'] ), $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) ] )
) ? 1 : 0;
} else {
$current_value = $first ? 1 : 0;
$first = false;
}
?>
<p class="form-row form-row-wide addon-wrap-<?php echo sanitize_title( $addon['field-name'] ) . '-' . $i; ?>">
<label><input type="radio" class="addon addon-radio" name="addon-<?php echo sanitize_title( $addon['field-name'] ); ?>[]" data-price="<?php echo $option['price']; ?>" value="<?php echo sanitize_title( $option['label'] ); ?>" <?php checked( $current_value, 1 ); ?> /> <?php echo wptexturize( $option['label'] . ' ' . $price ); ?></label>
</p>
<?php endforeach; ?>
\ No newline at end of file
<?php
$loop = 0;
$current_value = isset( $_POST['addon-' . sanitize_title( $addon['field-name'] ) ] ) ? $_POST[ 'addon-' . sanitize_title( $addon['field-name'] ) ] : '';
?>
<p class="form-row form-row-wide addon-wrap-<?php echo sanitize_title( $addon['field-name'] ); ?>">
<select class="addon addon-select" name="addon-<?php echo sanitize_title( $addon['field-name'] ); ?>">
<?php if ( ! isset( $addon['required'] ) ) : ?>
<option value=""><?php _e('None', 'wc_product_addons'); ?></option>
<?php else : ?>
<option value=""><?php _e('Select an option...', 'wc_product_addons'); ?></option>
<?php endif; ?>
<?php foreach ( $addon['options'] as $option ) :
$loop ++;
$price = $option['price'] > 0 ? ' (' . woocommerce_price( $option['price'] ) . ')' : '';
?>
<option data-price="<?php echo $option['price']; ?>" value="<?php echo sanitize_title( $option['label'] ) . '-' . $loop; ?>" <?php selected( $current_value, sanitize_title( $option['label'] ) . '-' . $loop ); ?>><?php echo wptexturize( $option['label'] ) . $price ?></option>
<?php endforeach; ?>
</select>
</p>
\ No newline at end of file
<?php
/**
* WC Dependency Checker
*
* Checks if WooCommerce is enabled
*/
class WC_Dependencies {
private static $active_plugins;
public static function init() {
self::$active_plugins = (array) get_option( 'active_plugins', array() );
if ( is_multisite() )
self::$active_plugins = array_merge( self::$active_plugins, get_site_option( 'active_sitewide_plugins', array() ) );
}
public static function woocommerce_active_check() {
if ( ! self::$active_plugins ) self::init();
return in_array( 'woocommerce/woocommerce.php', self::$active_plugins ) || array_key_exists( 'woocommerce/woocommerce.php', self::$active_plugins );
}
}
<?php
/**
* Functions used by plugins
*/
if ( ! class_exists( 'WC_Dependencies' ) )
require_once 'class-wc-dependencies.php';
/**
* WC Detection
*/
if ( ! function_exists( 'is_woocommerce_active' ) ) {
function is_woocommerce_active() {
return WC_Dependencies::woocommerce_active_check();
}
}
/**
* Queue updates for the WooUpdater
*/
if ( ! function_exists( 'woothemes_queue_update' ) ) {
function woothemes_queue_update( $file, $file_id, $product_id ) {
global $woothemes_queued_updates;
if ( ! isset( $woothemes_queued_updates ) )
$woothemes_queued_updates = array();
$plugin = new stdClass();
$plugin->file = $file;
$plugin->file_id = $file_id;
$plugin->product_id = $product_id;
$woothemes_queued_updates[] = $plugin;
}
}
/**
* Load installer for the WooThemes Updater.
* @return $api Object
*/
if ( ! class_exists( 'WooThemes_Updater' ) && ! function_exists( 'woothemes_updater_install' ) ) {
function woothemes_updater_install( $api, $action, $args ) {
$download_url = 'http://woodojo.s3.amazonaws.com/downloads/woothemes-updater/woothemes-updater.zip';
if ( 'plugin_information' != $action ||
false !== $api ||
! isset( $args->slug ) ||
'woothemes-updater' != $args->slug
) return $api;
$api = new stdClass();
$api->name = 'WooThemes Updater';
$api->version = '1.0.0';
$api->download_link = esc_url( $download_url );
return $api;
}
add_filter( 'plugins_api', 'woothemes_updater_install', 10, 3 );
}
/**
* WooUpdater Installation Prompts
*/
if ( ! class_exists( 'WooThemes_Updater' ) && ! function_exists( 'woothemes_updater_notice' ) ) {
/**
* Display a notice if the "WooThemes Updater" plugin hasn't been installed.
* @return void
*/
function woothemes_updater_notice() {
$active_plugins = apply_filters( 'active_plugins', get_option('active_plugins' ) );
if ( in_array( 'woothemes-updater/woothemes-updater.php', $active_plugins ) ) return;
$slug = 'woothemes-updater';
$install_url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $slug ), 'install-plugin_' . $slug );
$activate_url = 'plugins.php?action=activate&plugin=' . urlencode( 'woothemes-updater/woothemes-updater.php' ) . '&plugin_status=all&paged=1&s&_wpnonce=' . urlencode( wp_create_nonce( 'activate-plugin_woothemes-updater/woothemes-updater.php' ) );
$message = '<a href="' . esc_url( $install_url ) . '">Install the WooThemes Updater plugin</a> to get updates for your WooThemes plugins.';
$is_downloaded = false;
$plugins = array_keys( get_plugins() );
foreach ( $plugins as $plugin ) {
if ( strpos( $plugin, 'woothemes-updater.php' ) !== false ) {
$is_downloaded = true;
$message = '<a href="' . esc_url( admin_url( $activate_url ) ) . '">Activate the WooThemes Updater plugin</a> to get updates for your WooThemes plugins.';
}
}
echo '<div class="updated fade"><p>' . $message . '</p></div>' . "\n";
}
add_action( 'admin_notices', 'woothemes_updater_notice' );
}
/**
* Prevent conflicts with older versions
*/
if ( ! class_exists( 'WooThemes_Plugin_Updater' ) ) {
class WooThemes_Plugin_Updater { function init() {} }
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment