Merge remote-tracking branch 'woothemes/master'

This commit is contained in:
Ewout Fernhout 2015-10-15 09:38:07 +02:00
commit 08b32953d9
204 changed files with 11098 additions and 10547 deletions

2
.gitignore vendored
View File

@ -5,12 +5,10 @@ project.properties
.buildpath
.project
.settings*
sftp-config.json
.idea
# Grunt
/node_modules/
/deploy/
# Sass
.sass-cache/

View File

@ -6,7 +6,7 @@ When contributing please ensure you follow the guidelines below to help us keep
__Please Note:__
GitHub and Transifex are for *bug reports and contributions only* - if you have a support question or a request for a customization this is not the right place to post it. Use [WooThemes Support](http://support.woothemes.com) for customer support, [WordPress.org](http://wordpress.org/support/plugin/woocommerce) for community support, and for customizations we recommend one of the following services:
GitHub is for *bug reports and contributions only* - if you have a support question or a request for a customization this is not the right place to post it. Use [WooThemes Support](http://support.woothemes.com) for customer support, [WordPress.org](http://wordpress.org/support/plugin/woocommerce) for community support, and for customizations we recommend one of the following services:
- [Codeable](https://codeable.io/)
- [Affiliated Woo Workers](http://www.woothemes.com/affiliated-woo-workers/)
@ -42,7 +42,7 @@ If you think something could be improved and you're able to do so, make your cha
* When committing, reference your issue number (#1234) and include a note about the fix
* Push the changes to your fork and submit a pull request on the master branch of the WooCommerce repository. Existing maintenance branches will be maintained by WooCommerce developers
* Please **don't** modify the changelog - this will be maintained by the WooCommerce developers.
* Please **don't** add your localizations or update the .pot files - these will also be maintained by the WooCommerce developers. To contribute to the localization of WooCommerce, please join the [WooCommerce Transifex project](https://www.transifex.com/projects/p/woocommerce/). This is much needed, if you speak a language that needs translating consider yourself officially invited to the party.
* Please **don't** add your localizations or update the .pot files - these will also be maintained by the WooCommerce developers. To contribute to the localization of WooCommerce, please join the [translate.wordpress.org project](https://translate.wordpress.org/projects/wp-plugins/woocommerce). This is much needed, if you speak a language that needs translating consider yourself officially invited to the party.
After you follow the step above, the next stage will be waiting on us to merge your Pull Request. We review them all, and make suggestions and changes as and if necessary.
@ -52,7 +52,7 @@ Localization is a very important part of WooCommerce. We believe in net neutrali
### Translating The Core
We have a [public project on Transifex](https://www.transifex.com/projects/p/woocommerce/). You can join the localization team of your language and help by translating WooCommerce.
We have a [project on translate.wordpress.org](https://translate.wordpress.org/projects/wp-plugins/woocommerce). You can join the localization team of your language and help by translating WooCommerce.
If WooCommerce is already 100% translated for your language, join the team anyway! We regularly update our language files and there will definitely be need of your help soon.
### Translating Video Tutorials
@ -65,7 +65,5 @@ By translating video tutorials you'll be helping non-English speaking users to g
* [General GitHub documentation](http://help.github.com/)
* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
* [General Transifex documentation](http://docs.transifex.com/)
* [Translate using Transifex](http://docs.transifex.com/introduction/translators/)
* [WooCommerce Docs](http://docs.woothemes.com/)
* [WooThemes Support](http://support.woothemes.com)

View File

@ -149,31 +149,14 @@ module.exports = function( grunt ) {
'language-team': 'LANGUAGE <EMAIL@ADDRESS>'
}
},
frontend: {
dist: {
options: {
potFilename: 'woocommerce.pot',
exclude: [
'includes/admin/.*',
'apigen/.*',
'tests/.*',
'tmp/.*'
],
processPot: function ( pot ) {
pot.headers['project-id-version'] += ' Frontend';
return pot;
}
}
},
admin: {
options: {
potFilename: 'woocommerce-admin.pot',
include: [
'includes/admin/.*'
],
processPot: function ( pot ) {
pot.headers['project-id-version'] += ' Admin';
return pot;
}
]
}
}
},

File diff suppressed because one or more lines are too long

View File

@ -1683,11 +1683,11 @@ table.wp-list-table {
td.column-thumb img {
margin: 0;
vertical-align: middle;
width: auto;
height: auto;
max-width: 40px;
max-height: 40px;
vertical-align: middle;
}
span.na {
@ -1842,6 +1842,14 @@ a.import_rates {
margin-bottom: 0;
}
#rates-search {
float: right;
input.wc-tax-rates-search-field {
padding: 4px 8px;
font-size: 1.2em;
}
}
table.wc_tax_rates,
table.wc_input_table {
width: 100%;
@ -1897,31 +1905,36 @@ table.wc_input_table {
th.sort {
width: 17px;
padding: 0;
padding: 0 4px;
}
td.sort {
padding: 0 4px;
cursor: move;
background: #f9f9f9;
text-align: center;
vertical-align: middle;
}
&:before {
content: "\e032";
font-family: 'WooCommerce';
.ui-sortable:not(.ui-sortable-disabled) {
td.sort {
cursor: move;
background: #f9f9f9;
text-align: center;
line-height: 1;
color: #999;
display: block;
width: 17px;
float: left;
height: 100%;
}
vertical-align: middle;
&:hover {
&:before {
color: #333;
content: "\e032";
font-family: 'WooCommerce';
text-align: center;
line-height: 1;
color: #999;
display: block;
width: 17px;
float: left;
height: 100%;
}
&:hover {
&:before {
color: #333;
}
}
}
}
@ -2043,7 +2056,7 @@ img.help_tip {
}
.postbox img.help_tip {
margin-top: -4px;
margin-top: 0px;
}
.status-enabled,
@ -2606,15 +2619,15 @@ img.help_tip {
#woocommerce-order-downloads,
#woocommerce-coupon-data {
.inside {
padding: 0;
margin: 0;
padding: 0;
}
}
.woocommerce_options_panel,
.panel {
padding: 9px 9px 9px 9px;
color: #555;
padding: 9px;
color: #555;
}
.woocommerce_page_settings .woocommerce_options_panel,
@ -2624,8 +2637,8 @@ img.help_tip {
#woocommerce-product-type-options .panel,
#woocommerce-product-specs .inside {
padding: 9px;
margin: 0;
padding: 9px;
}
.woocommerce_options_panel p,
@ -2648,8 +2661,8 @@ img.help_tip {
.woocommerce_options_panel .checkbox,
.woocommerce_variable_attributes .checkbox {
width: auto;
margin: 3px 0;
vertical-align: middle;
margin: 7px 0;
}
.woocommerce_variations,
@ -2771,6 +2784,7 @@ img.help_tip {
}
.woocommerce_options_panel {
min-height: 175px;
box-sizing: border-box;
.downloadable_files {
@ -2787,7 +2801,7 @@ img.help_tip {
}
p {
margin: 9px 0 9px;
margin: 9px 0;
}
p.form-field,
@ -2838,9 +2852,9 @@ img.help_tip {
}
textarea {
vertical-align: top;
height: 3.5em;
line-height: 1.5em;
vertical-align: top;
}
input[type="text"],
@ -2874,48 +2888,48 @@ img.help_tip {
.options_group {
border-top: 1px solid white;
border-bottom: 1px solid #eee;
}
.options_group:first-child {
border-top: 0;
}
.options_group:last-child {
border-bottom: 0;
}
.options_group fieldset {
margin: 9px 0;
font-size: 12px;
padding: 5px 9px;
line-height: 24px;
label {
width: auto;
float: none;
&:first-child {
border-top: 0;
}
ul {
float: left;
width: 50%;
margin: 0;
padding: 0;
&:last-child {
border-bottom: 0;
}
li {
margin: 0;
fieldset {
margin: 9px 0;
font-size: 12px;
padding: 5px 9px;
line-height: 24px;
label {
width: auto;
float: none;
}
input {
ul {
float: left;
width: 50%;
margin: 0;
padding: 0;
li {
margin: 0;
width: auto;
float: none;
margin-right: 4px;
input {
width: auto;
float: none;
margin-right: 4px;
}
}
}
}
ul.wc-radios {
label {
margin-left: 0;
ul.wc-radios {
label {
margin-left: 0;
}
}
}
}

File diff suppressed because one or more lines are too long

View File

@ -274,7 +274,7 @@ body {
}
}
}
.woocommerce-tracker, .woocommerce-language-pack, .updated {
.woocommerce-tracker, .updated {
padding: 24px 24px 0;
margin: 0 0 24px 0;
overflow: hidden;

File diff suppressed because one or more lines are too long

View File

@ -147,7 +147,7 @@ p.demo_store {
margin-top: 10px;
}
.woocommerce-breadcrumb{
.woocommerce-breadcrumb {
@include clearfix();
margin: 0 0 1em;
padding: 0;
@ -913,102 +913,56 @@ p.demo_store {
}
p.stars {
position: relative;
font-size: 1em;
a {
display: inline-block;
font-weight: 700;
margin-right: 1em;
text-indent: -9999px;
position: relative;
border-bottom: 0 !important;
outline: 0;
height: 1em;
width: 1em;
text-indent: -999em;
display: inline-block;
text-decoration: none;
&:last-child {
border-right: 0;
&:before {
display: block;
position: absolute;
top: 0;
left: 0;
width: 1em;
height: 1em;
line-height: 1;
font-family: "WooCommerce";
content: "\e021";
text-indent: 0;
}
&.star-1,
&.star-2,
&.star-3,
&.star-4,
&.star-5 {
border-right: 1px solid #ccc;
&:after {
font-family: "WooCommerce";
text-indent: 0;
position: absolute;
top: 0;
left: 0;
}
}
&.star-1 {
width: 2em;
&:after {
&:hover {
~ a:before {
content: "\e021";
}
}
}
&:hover:after,
&.active:after {
content: "\e020"
&:hover {
a {
&:before {
content: "\e020";
}
}
}
&.selected {
a.active {
&:before {
content: "\e020";
}
~ a:before {
content: "\e021";
}
}
&.star-2 {
width: 3em;
&:after {
content: "\e021\e021";
}
&:hover:after,
&.active:after {
content: "\e020\e020"
}
}
&.star-3 {
width: 4em;
&:after {
content: "\e021\e021\e021";
}
&:hover:after,
&.active:after {
content: "\e020\e020\e020"
}
}
&.star-4 {
width: 5em;
&:after {
content: "\e021\e021\e021\e021";
}
&:hover:after,
&.active:after {
content: "\e020\e020\e020\e020"
}
}
&.star-5 {
width: 6em;
border: 0;
&:after {
content: "\e021\e021\e021\e021\e021";
}
&:hover:after,
&.active:after {
content: "\e020\e020\e020\e020\e020"
a:not(.active) {
&:before {
content: "\e020";
}
}
}
@ -1925,6 +1879,42 @@ p.demo_store {
}
}
/**
* Password strength meter
*/
.woocommerce-password-strength {
text-align: center;
font-weight: 600;
padding: 3px 0px 3px 0px;
&.strong {
background-color: #c1e1b9;
border-color: #83c373;
}
&.short {
background-color: #f1adad;
border-color: #e35b5b;
}
&.bad {
background-color: #fbc5a9;
border-color: #f78b53;
}
&.good {
background-color: #ffe399;
border-color: #ffc733;
}
}
/* added to get around variation image flicker issue */
.product.has-default-attributes {
> .images {
opacity: 0;
}
}
/**
* Twenty Eleven specific styles
*/

View File

@ -523,20 +523,20 @@ jQuery( function ( $ ) {
if ( window.confirm( woocommerce_admin_meta_boxes.calc_line_taxes ) ) {
wc_meta_boxes_order_items.block();
var shipping_country = $( '#_shipping_country' ).val();
var billing_country = $( '#_billing_country' ).val();
var country = woocommerce_admin_meta_boxes.base_country;
var country = '';
var state = '';
var postcode = '';
var city = '';
if ( shipping_country ) {
country = shipping_country;
if ( 'shipping' === woocommerce_admin_meta_boxes.tax_based_on ) {
country = $( '#_shipping_country' ).val();
state = $( '#_shipping_state' ).val();
postcode = $( '#_shipping_postcode' ).val();
city = $( '#_shipping_city' ).val();
} else if ( billing_country ) {
country = billing_country;
}
if ( 'billing' === woocommerce_admin_meta_boxes.tax_based_on || ! country ) {
country = $( '#_billing_country' ).val();
state = $( '#_billing_state' ).val();
postcode = $( '#_billing_postcode' ).val();
city = $( '#_billing_city' ).val();

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,418 @@
/* global htmlSettingsTaxLocalizeScript, ajaxurl */
/**
* Used by woocommerce/includes/admin/settings/views/html-settings-tax.php
*/
(function($, data, wp, ajaxurl){
$(function() {
if ( ! String.prototype.trim ) {
String.prototype.trim = function () {
return this.replace( /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '' );
};
}
var rowTemplate = wp.template( 'wc-tax-table-row' ),
rowTemplateEmpty = wp.template( 'wc-tax-table-row-empty' ),
paginationTemplate = wp.template( 'wc-tax-table-pagination' ),
$table = $( '.wc_tax_rates' ),
$tbody = $( '#rates' ),
$save_button = $( 'input[name="save"]' ),
$pagination = $( '#rates-pagination' ),
$search_field = $( '#rates-search .wc-tax-rates-search-field' ),
$submit = $( '.submit .button-primary[type=submit]' ),
WCTaxTableModelConstructor = Backbone.Model.extend({
changes : {},
setRateAttribute : function( rateID, attribute, value ) {
var rates = _.indexBy( this.get( 'rates' ), 'tax_rate_id' ),
changes = {};
if ( rates[ rateID ][ attribute ] !== value ) {
changes[ rateID ] = {};
changes[ rateID ][ attribute ] = value;
rates[ rateID ][ attribute ] = value;
}
this.logChanges( changes );
},
logChanges : function( changedRows ) {
var changes = this.changes || {};
_.each( changedRows, function( row, id ) {
changes[ id ] = _.extend( changes[ id ] || { tax_rate_id : id }, row );
} );
this.changes = changes;
this.trigger( 'change:rates' );
},
getFilteredRates : function() {
var rates = this.get( 'rates' ),
search = $search_field.val().toLowerCase();
if ( search.length ) {
rates = _.filter( rates, function( rate ) {
var search_text = _.toArray( rate ).join( ' ' ).toLowerCase();
return ( -1 !== search_text.indexOf( search ) );
} );
}
rates = _.sortBy( rates, function( rate ) {
return parseInt( rate.tax_rate_order, 10 );
} );
return rates;
},
save : function() {
$.post( ajaxurl + '?action=woocommerce_tax_rates_save_changes', {
current_class : data.current_class,
wc_tax_nonce : data.wc_tax_nonce,
changes : this.changes
}, this.onSaveResponse, 'json' );
},
onSaveResponse : function( response, textStatus ) {
if ( 'success' === textStatus ) {
WCTaxTableModelInstance.set( 'rates', response.data.rates );
WCTaxTableModelInstance.trigger( 'change:rates' );
WCTaxTableModelInstance.changes = {};
WCTaxTableModelInstance.trigger( 'saved:rates' );
}
}
} ),
WCTaxTableViewConstructor = Backbone.View.extend({
rowTemplate : rowTemplate,
per_page : data.limit,
page : data.page,
initialize : function() {
this.qty_pages = Math.ceil( _.toArray( this.model.get( 'rates' ) ).length / this.per_page );
this.page = this.sanitizePage( data.page );
this.listenTo( this.model, 'change:rates', this.setUnloadConfirmation );
this.listenTo( this.model, 'saved:rates', this.clearUnloadConfirmation );
$tbody.on( 'change', { view : this }, this.updateModelOnChange );
$tbody.on( 'sortupdate', { view : this }, this.updateModelOnSort );
$search_field.on( 'keyup search', { view : this }, this.onSearchField );
$pagination.on( 'click', 'a', { view : this }, this.onPageChange );
$pagination.on( 'change', 'input', { view : this }, this.onPageChange );
$(window).on( 'beforeunload', { view : this }, this.unloadConfirmation );
$submit.on( 'click', { view : this }, this.onSubmit );
$save_button.attr('disabled','disabled');
// Can bind these directly to the buttons, as they won't get overwritten.
$table.find('.insert').on( 'click', { view : this }, this.onAddNewRow );
$table.find('.remove_tax_rates').on( 'click', { view : this }, this.onDeleteRow );
$table.find('.export').on( 'click', { view : this }, this.onExport );
},
render : function() {
var rates = this.model.getFilteredRates(),
qty_rates = _.size( rates ),
qty_pages = Math.ceil( qty_rates / this.per_page ),
first_index = this.per_page * ( this.page - 1),
last_index = this.per_page * this.page,
paged_rates = _.toArray( rates ).slice( first_index, last_index ),
view = this;
// Blank out the contents.
this.$el.empty();
if ( paged_rates.length ) {
// Populate $tbody with the current page of results.
$.each( paged_rates, function ( id, rowData ) {
view.$el.append( view.rowTemplate( rowData ) );
} );
} else {
view.$el.append( rowTemplateEmpty() );
}
// Initialize autocomplete for countries.
this.$el.find( 'td.country input' ).autocomplete({
source: data.countries,
minLength: 2
});
// Initialize autocomplete for states.
this.$el.find( 'td.state input' ).autocomplete({
source: data.states,
minLength: 3
});
// Postcode and city don't have `name` values by default. They're only created if the contents changes, to save on database queries (I think)
this.$el.find( 'td.postcode input, td.city input' ).change(function() {
$(this).attr( 'name', $(this).data( 'name' ) );
});
if ( qty_pages > 1 ) {
// We've now displayed our initial page, time to render the pagination box.
$pagination.html( paginationTemplate( {
qty_rates : qty_rates,
current_page : this.page,
qty_pages : qty_pages
} ) );
}
// Disable sorting if there is a search term filtering the items.
if ( $search_field.val() ) {
$tbody.sortable( 'disable' );
} else {
$tbody.sortable( 'enable' );
}
},
updateUrl : function() {
if ( ! window.history.replaceState ) {
return;
}
var url = data.base_url,
search = $search_field.val();
if ( 1 < this.page ) {
url += '&p=' + encodeURIComponent( this.page );
}
if ( search.length ) {
url += '&s=' + encodeURIComponent( search );
}
window.history.replaceState( {}, '', url );
},
onSubmit : function( event ) {
event.data.view.model.save();
event.preventDefault();
},
onAddNewRow : function( event ) {
var view = event.data.view,
model = view.model,
rates = _.indexBy( model.get('rates'), 'tax_rate_id' ),
changes = {},
size = _.size( rates ),
newRow = _.extend( {}, data.default_rate, {
tax_rate_id : 'new-' + size + '-' + Date.now(),
newRow : true
} ),
$current, current_id, current_order, rates_to_reorder, reordered_rates;
$current = $tbody.children( '.current' );
if ( $current.length ) {
current_id = $current.last().data( 'id' );
current_order = parseInt( rates[ current_id ].tax_rate_order, 10 );
newRow.tax_rate_order = 1 + current_order;
rates_to_reorder = _.filter( rates, function( rate ) {
if ( parseInt( rate.tax_rate_order, 10 ) > current_order ) {
return true;
}
return false;
} );
reordered_rates = _.map( rates_to_reorder, function( rate ) {
rate.tax_rate_order++;
changes[ rate.tax_rate_id ] = _.extend( changes[ rate.tax_rate_id ] || {}, { tax_rate_order : rate.tax_rate_order } );
return rate;
} );
} else {
newRow.tax_rate_order = 1 + _.max(
_.pluck( rates, 'tax_rate_order' ),
function ( val ) {
// Cast them all to integers, because strings compare funky. Sighhh.
return parseInt( val, 10 );
}
);
}
rates[ newRow.tax_rate_id ] = newRow;
changes[ newRow.tax_rate_id ] = newRow;
model.set( 'rates', rates );
model.logChanges( changes );
view.render();
},
onDeleteRow : function( event ) {
var view = event.data.view,
model = view.model,
rates = _.indexBy( model.get('rates'), 'tax_rate_id' ),
changes = {},
$current, current_id, current_order, rates_to_reorder, reordered_rates;
event.preventDefault();
if ( $current = $tbody.children( '.current' ) ) {
$current.each(function(){
current_id = $( this ).data('id');
current_order = parseInt( rates[ current_id ].tax_rate_order, 10 );
rates_to_reorder = _.filter( rates, function( rate ) {
if ( parseInt( rate.tax_rate_order, 10 ) > current_order ) {
return true;
}
return false;
} );
reordered_rates = _.map( rates_to_reorder, function( rate ) {
rate.tax_rate_order--;
changes[ rate.tax_rate_id ] = _.extend( changes[ rate.tax_rate_id ] || {}, { tax_rate_order : rate.tax_rate_order } );
return rate;
} );
delete rates[ current_id ];
changes[ current_id ] = _.extend( changes[ current_id ] || {}, { deleted : 'deleted' } );
});
model.set( 'rates', rates );
model.logChanges( changes );
view.render();
} else {
window.alert( data.strings.no_rows_selected );
}
},
onSearchField : function( event ){
event.data.view.updateUrl();
event.data.view.render();
},
onPageChange : function( event ) {
var $target = $( event.currentTarget );
event.preventDefault();
event.data.view.page = $target.data('goto') ? $target.data('goto') : $target.val();
event.data.view.render();
event.data.view.updateUrl();
},
onExport : function( event ) {
var csv_data = 'data:application/csv;charset=utf-8,' + data.strings.csv_data_cols.join(',') + '\n';
$.each( event.data.view.model.getFilteredRates(), function( id, rowData ) {
var row = '';
row += rowData.tax_rate_country + ',';
row += rowData.tax_rate_state + ',';
row += ( rowData.postcode ? rowData.postcode.join( '; ' ) : '' ) + ',';
row += ( rowData.city ? rowData.city.join( '; ' ) : '' ) + ',';
row += rowData.tax_rate + ',';
row += rowData.tax_rate_name + ',';
row += rowData.tax_rate_priority + ',';
row += rowData.tax_rate_compound + ',';
row += rowData.tax_rate_shipping + ',';
row += data.current_class;
csv_data += row + '\n';
});
$(this).attr( 'href', encodeURI( csv_data ) );
return true;
},
setUnloadConfirmation : function() {
this.needsUnloadConfirm = true;
$save_button.removeAttr('disabled');
},
clearUnloadConfirmation : function() {
this.needsUnloadConfirm = false;
$save_button.attr('disabled','disabled');
},
unloadConfirmation : function(event) {
if ( event.data.view.needsUnloadConfirm ) {
event.returnValue = data.strings.unload_confirmation_msg;
window.event.returnValue = data.strings.unload_confirmation_msg;
return data.strings.unload_confirmation_msg;
}
},
updateModelOnChange : function( event ) {
var model = event.data.view.model,
$target = $( event.target ),
id = $target.closest('tr').data('id'),
attribute = $target.data('attribute'),
val = $target.val();
if ( 'city' === attribute || 'postcode' === attribute ) {
val = val.split(';');
val = $.map( val, function( thing ) {
return thing.trim();
});
}
if ( 'tax_rate_compound' === attribute || 'tax_rate_shipping' === attribute ) {
if ( $target.is(':checked') ) {
val = 1;
} else {
val = 0;
}
}
model.setRateAttribute( id, attribute, val );
},
updateModelOnSort : function( event, ui ) {
var view = event.data.view,
model = view.model,
$tr = ui.item,
tax_rate_id = $tr.data( 'id' ),
rates = _.indexBy( model.get('rates'), 'tax_rate_id' ),
old_position = rates[ tax_rate_id ].tax_rate_order,
new_position = $tr.index() + ( ( view.page - 1 ) * view.per_page ),
which_way = ( new_position > old_position ) ? 'higher' : 'lower',
changes = {},
rates_to_reorder, reordered_rates;
rates_to_reorder = _.filter( rates, function( rate ) {
var order = parseInt( rate.tax_rate_order, 10 ),
limits = [ old_position, new_position ];
if ( parseInt( rate.tax_rate_id, 10 ) === parseInt( tax_rate_id, 10 ) ) {
return true;
} else if ( order > _.min( limits ) && order < _.max( limits ) ) {
return true;
} else if ( 'higher' === which_way && order === _.max( limits ) ) {
return true;
} else if ( 'lower' === which_way && order === _.min( limits ) ) {
return true;
}
return false;
} );
reordered_rates = _.map( rates_to_reorder, function( rate ) {
var order = parseInt( rate.tax_rate_order, 10 );
if ( parseInt( rate.tax_rate_id, 10 ) === parseInt( tax_rate_id, 10 ) ) {
rate.tax_rate_order = new_position;
} else if ( 'higher' === which_way ) {
rate.tax_rate_order = order - 1;
} else if ( 'lower' === which_way ) {
rate.tax_rate_order = order + 1;
}
changes[ rate.tax_rate_id ] = _.extend( changes[ rate.tax_rate_id ] || {}, { tax_rate_order : rate.tax_rate_order } );
return rate;
} );
if ( reordered_rates.length ) {
model.logChanges( changes );
view.render(); // temporary, probably should get yanked.
}
},
sanitizePage : function( page_num ) {
page_num = parseInt( page_num, 10 );
if ( page_num < 1 ) {
page_num = 1;
} else if ( page_num > this.qty_pages ) {
page_num = this.qty_pages;
}
return page_num;
}
} ),
WCTaxTableModelInstance = new WCTaxTableModelConstructor({
rates : data.rates
} ),
WCTaxTableInstance = new WCTaxTableViewConstructor({
model : WCTaxTableModelInstance,
// page : data.page, // I'd prefer to have these two specified down here in the instance,
// per_page : data.limit, // but it doesn't seem to recognize them in render if I do. :\
el : '#rates'
} );
WCTaxTableInstance.render();
});
})(jQuery, htmlSettingsTaxLocalizeScript, wp, ajaxurl);

File diff suppressed because one or more lines are too long

View File

@ -147,7 +147,7 @@ jQuery( function ( $ ) {
$this_row.addClass( 'selected_now' ).addClass( 'current' );
if ( $( 'tr.last_selected', $this_table ).size() > 0 ) {
if ( $this_row.index() > $( 'tr.last_selected, $this_table' ).index() ) {
if ( $this_row.index() > $( 'tr.last_selected', $this_table ).index() ) {
$( 'tr', $this_table ).slice( $( 'tr.last_selected', $this_table ).index(), $this_row.index() ).addClass( 'current' );
} else {
$( 'tr', $this_table ).slice( $this_row.index(), $( 'tr.last_selected', $this_table ).index() + 1 ).addClass( 'current' );
@ -221,4 +221,18 @@ jQuery( function ( $ ) {
// Attribute term table
$( 'table.attributes-table tbody tr:nth-child(odd)' ).addClass( 'alternate' );
// Add js validation for product quick edit panel.
$( '#woocommerce-fields .regular_price[type=text], #woocommerce-fields .sale_price[type=text]' ).keyup( function() {
var value = $( this ).val();
var regex = new RegExp( '[^\-0-9\%\\' + woocommerce_admin.mon_decimal_point + ']+', 'gi' );
var newvalue = value.replace( regex, '' );
if ( value !== newvalue ) {
$( this ).val( newvalue );
$( document.body ).triggerHandler( 'wc_add_error_tip', [ $( this ), 'i18n_mon_decimal_error' ] );
} else {
$( document.body ).triggerHandler( 'wc_remove_error_tip', [ $( this ), 'i18n_mon_decimal_error' ] );
}
});
});

File diff suppressed because one or more lines are too long

View File

@ -140,6 +140,9 @@
$( this ).blur();
}
// added to get around variation image flicker issue
$( '.product.has-default-attributes > .images' ).fadeTo( 200, 1 );
// Custom event for when variation selection has been changed
$form.trigger( 'woocommerce_variation_has_changed' );
} )

File diff suppressed because one or more lines are too long

View File

@ -17,6 +17,13 @@ jQuery( function( $ ) {
$supports_html5_storage = false;
}
/* Cart session creation time to base expiration on */
function set_cart_creation_timestamp() {
if ( $supports_html5_storage ) {
sessionStorage.setItem( 'wc_cart_created', ( new Date() ).getTime() );
}
}
var $fragment_refresh = {
url: wc_cart_fragments_params.wc_ajax_url.toString().replace( '%%endpoint%%', 'get_refreshed_fragments' ),
type: 'POST',
@ -30,6 +37,10 @@ jQuery( function( $ ) {
if ( $supports_html5_storage ) {
sessionStorage.setItem( wc_cart_fragments_params.fragment_name, JSON.stringify( data.fragments ) );
sessionStorage.setItem( 'wc_cart_hash', data.cart_hash );
if ( data.cart_hash ) {
set_cart_creation_timestamp();
}
}
$( document.body ).trigger( 'wc_fragments_refreshed' );
@ -37,18 +48,38 @@ jQuery( function( $ ) {
}
};
/* Named callback for refreshing cart fragment */
function refresh_cart_fragment() {
$.ajax( $fragment_refresh );
}
/* Cart Handling */
if ( $supports_html5_storage ) {
var cart_timeout = null,
day_in_ms = ( 24 * 60 * 60 * 1000 );
$( document.body ).bind( 'added_to_cart', function( event, fragments, cart_hash ) {
var prev_cart_hash = sessionStorage.getItem( 'wc_cart_hash' );
if ( prev_cart_hash === null || prev_cart_hash === undefined || prev_cart_hash === '' ) {
set_cart_creation_timestamp();
}
sessionStorage.setItem( wc_cart_fragments_params.fragment_name, JSON.stringify( fragments ) );
sessionStorage.setItem( 'wc_cart_hash', cart_hash );
});
$( document.body ).bind( 'wc_fragments_refreshed', function() {
clearTimeout( cart_timeout );
cart_timeout = setTimeout( refresh_cart_fragment, day_in_ms );
} );
try {
var wc_fragments = $.parseJSON( sessionStorage.getItem( wc_cart_fragments_params.fragment_name ) ),
cart_hash = sessionStorage.getItem( 'wc_cart_hash' ),
cookie_hash = $.cookie( 'woocommerce_cart_hash' );
cookie_hash = $.cookie( 'woocommerce_cart_hash'),
cart_created = sessionStorage.getItem( 'wc_cart_created' );
if ( cart_hash === null || cart_hash === undefined || cart_hash === '' ) {
cart_hash = '';
@ -58,6 +89,19 @@ jQuery( function( $ ) {
cookie_hash = '';
}
if ( cart_hash && ( cart_created === null || cart_created === undefined || cart_created === '' ) ) {
throw 'No cart_created';
}
if ( cart_created ) {
var cart_expiration = ( ( 1 * cart_created ) + day_in_ms ),
timestamp_now = ( new Date() ).getTime();
if ( cart_expiration < timestamp_now ) {
throw 'Fragment expired';
}
cart_timeout = setTimeout( refresh_cart_fragment, ( cart_expiration - timestamp_now ) );
}
if ( wc_fragments && wc_fragments['div.widget_shopping_cart_content'] && cart_hash === cookie_hash ) {
$.each( wc_fragments, function( key, value ) {
@ -70,11 +114,11 @@ jQuery( function( $ ) {
}
} catch( err ) {
$.ajax( $fragment_refresh );
refresh_cart_fragment();
}
} else {
$.ajax( $fragment_refresh );
refresh_cart_fragment();
}
/* Cart Hiding */

View File

@ -1 +1 @@
jQuery(function(a){if("undefined"==typeof wc_cart_fragments_params)return!1;var b;try{b="sessionStorage"in window&&null!==window.sessionStorage,window.sessionStorage.setItem("wc","test"),window.sessionStorage.removeItem("wc")}catch(c){b=!1}var d={url:wc_cart_fragments_params.wc_ajax_url.toString().replace("%%endpoint%%","get_refreshed_fragments"),type:"POST",success:function(c){c&&c.fragments&&(a.each(c.fragments,function(b,c){a(b).replaceWith(c)}),b&&(sessionStorage.setItem(wc_cart_fragments_params.fragment_name,JSON.stringify(c.fragments)),sessionStorage.setItem("wc_cart_hash",c.cart_hash)),a(document.body).trigger("wc_fragments_refreshed"))}};if(b){a(document.body).bind("added_to_cart",function(a,b,c){sessionStorage.setItem(wc_cart_fragments_params.fragment_name,JSON.stringify(b)),sessionStorage.setItem("wc_cart_hash",c)});try{var e=a.parseJSON(sessionStorage.getItem(wc_cart_fragments_params.fragment_name)),f=sessionStorage.getItem("wc_cart_hash"),g=a.cookie("woocommerce_cart_hash");if((null===f||void 0===f||""===f)&&(f=""),(null===g||void 0===g||""===g)&&(g=""),!e||!e["div.widget_shopping_cart_content"]||f!==g)throw"No fragment";a.each(e,function(b,c){a(b).replaceWith(c)}),a(document.body).trigger("wc_fragments_loaded")}catch(c){a.ajax(d)}}else a.ajax(d);a.cookie("woocommerce_items_in_cart")>0?a(".hide_cart_widget_if_empty").closest(".widget_shopping_cart").show():a(".hide_cart_widget_if_empty").closest(".widget_shopping_cart").hide(),a(document.body).bind("adding_to_cart",function(){a(".hide_cart_widget_if_empty").closest(".widget_shopping_cart").show()})});
jQuery(function(a){function b(){d&&sessionStorage.setItem("wc_cart_created",(new Date).getTime())}function c(){a.ajax(f)}if("undefined"==typeof wc_cart_fragments_params)return!1;var d;try{d="sessionStorage"in window&&null!==window.sessionStorage,window.sessionStorage.setItem("wc","test"),window.sessionStorage.removeItem("wc")}catch(e){d=!1}var f={url:wc_cart_fragments_params.wc_ajax_url.toString().replace("%%endpoint%%","get_refreshed_fragments"),type:"POST",success:function(c){c&&c.fragments&&(a.each(c.fragments,function(b,c){a(b).replaceWith(c)}),d&&(sessionStorage.setItem(wc_cart_fragments_params.fragment_name,JSON.stringify(c.fragments)),sessionStorage.setItem("wc_cart_hash",c.cart_hash),c.cart_hash&&b()),a(document.body).trigger("wc_fragments_refreshed"))}};if(d){var g=null,h=864e5;a(document.body).bind("added_to_cart",function(a,c,d){var e=sessionStorage.getItem("wc_cart_hash");(null===e||void 0===e||""===e)&&b(),sessionStorage.setItem(wc_cart_fragments_params.fragment_name,JSON.stringify(c)),sessionStorage.setItem("wc_cart_hash",d)}),a(document.body).bind("wc_fragments_refreshed",function(){clearTimeout(g),g=setTimeout(c,h)});try{var i=a.parseJSON(sessionStorage.getItem(wc_cart_fragments_params.fragment_name)),j=sessionStorage.getItem("wc_cart_hash"),k=a.cookie("woocommerce_cart_hash"),l=sessionStorage.getItem("wc_cart_created");if((null===j||void 0===j||""===j)&&(j=""),(null===k||void 0===k||""===k)&&(k=""),j&&(null===l||void 0===l||""===l))throw"No cart_created";if(l){var m=1*l+h,n=(new Date).getTime();if(n>m)throw"Fragment expired";g=setTimeout(c,m-n)}if(!i||!i["div.widget_shopping_cart_content"]||j!==k)throw"No fragment";a.each(i,function(b,c){a(b).replaceWith(c)}),a(document.body).trigger("wc_fragments_loaded")}catch(e){c()}}else c();a.cookie("woocommerce_items_in_cart")>0?a(".hide_cart_widget_if_empty").closest(".widget_shopping_cart").show():a(".hide_cart_widget_if_empty").closest(".widget_shopping_cart").hide(),a(document.body).bind("adding_to_cart",function(){a(".hide_cart_widget_if_empty").closest(".widget_shopping_cart").show()})});

View File

@ -19,7 +19,7 @@ jQuery( function( $ ) {
$( document.body ).bind( 'init_checkout', this.init_checkout );
// Payment methods
this.$order_review.on( 'click', 'input[name=payment_method]', this.payment_method_selected );
this.$checkout_form.on( 'click', 'input[name=payment_method]', this.payment_method_selected );
// Form submission
this.$checkout_form.on( 'submit', this.submit );
@ -27,6 +27,9 @@ jQuery( function( $ ) {
// Inline validation
this.$checkout_form.on( 'blur change', '.input-text, select', this.validate_field );
// Manual trigger
this.$checkout_form.on( 'update', this.trigger_update_checkout );
// Inputs/selects which update totals
this.$checkout_form.on( 'change', 'select.shipping_method, input[name^=shipping_method], #ship-to-different-address input, .update_totals_on_change select, .update_totals_on_change input[type=radio]', this.trigger_update_checkout );
this.$checkout_form.on( 'change', '.address-field select', this.input_changed );
@ -37,7 +40,7 @@ jQuery( function( $ ) {
this.$checkout_form.on( 'change', '#ship-to-different-address input', this.ship_to_different_address );
// Trigger events
this.$order_review.find( 'input[name=payment_method]:checked' ).trigger( 'click' );
this.$checkout_form.find( 'input[name=payment_method]:checked' ).trigger( 'click' );
this.$checkout_form.find( '#ship-to-different-address input' ).change();
// Update on page load

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,106 @@
/* global wp, pwsL10n */
jQuery( function( $ ) {
/**
* Password Strength Meter class
*/
var wc_password_strength_meter = {
/**
* Initialize strength meter actions
*/
init: function() {
$( document.body )
.on( 'keyup', 'form.register #reg_password, form.checkout #account_password', this.strengthMeter )
.on( 'change', 'form.checkout #createaccount', this.checkoutNeedsRegistration );
$( 'form.checkout #createaccount' ).change();
},
/**
* Strength Meter
*/
strengthMeter: function() {
var wrapper = $( 'form.register, form.checkout' ),
submit = $( 'input[type="submit"]', wrapper ),
field = $( '#reg_password, #account_password', wrapper ),
strength = 1;
wc_password_strength_meter.includeMeter( wrapper, field );
strength = wc_password_strength_meter.checkPasswordStrength( field );
if ( 3 === strength || 4 === strength ) {
submit.removeAttr( 'disabled' );
} else {
submit.attr( 'disabled', 'disabled' );
}
},
/**
* Include meter HTML
*
* @param {Object} wrapper
* @param {Object} field
*/
includeMeter: function( wrapper, field ) {
var meter = wrapper.find( '.woocommerce-password-strength' );
if ( 0 === meter.length ) {
field.after( '<div class="woocommerce-password-strength" aria-live="polite"></div>' );
} else if ( '' === field.val() ) {
meter.remove();
}
},
/**
* Check password strength
*
* @param {Object} field
*
* @return {Int}
*/
checkPasswordStrength: function( field ) {
var meter = $( '.woocommerce-password-strength' );
var strength = wp.passwordStrength.meter( field.val(), wp.passwordStrength.userInputBlacklist() );
// Reset classes
meter.removeClass( 'short bad good strong' );
switch ( strength ) {
case 2 :
meter.addClass( 'bad' ).html( pwsL10n.bad );
break;
case 3 :
meter.addClass( 'good' ).html( pwsL10n.good );
break;
case 4 :
meter.addClass( 'strong' ).html( pwsL10n.strong );
break;
case 5 :
meter.addClass( 'short' ).html( pwsL10n.mismatch );
break;
default :
meter.addClass( 'short' ).html( pwsL10n['short'] );
}
return strength;
},
/**
* Check if user wants register on checkout.
*/
checkoutNeedsRegistration: function() {
var submit = $( 'form.checkout input[type="submit"]' );
if ( $( this ).is( ':checked' ) ) {
submit.attr( 'disabled', 'disabled' );
} else {
submit.removeAttr( 'disabled' );
}
}
};
wc_password_strength_meter.init();
});

View File

@ -0,0 +1 @@
jQuery(function(a){var b={init:function(){a(document.body).on("keyup","form.register #reg_password, form.checkout #account_password",this.strengthMeter).on("change","form.checkout #createaccount",this.checkoutNeedsRegistration),a("form.checkout #createaccount").change()},strengthMeter:function(){var c=a("form.register, form.checkout"),d=a('input[type="submit"]',c),e=a("#reg_password, #account_password",c),f=1;b.includeMeter(c,e),f=b.checkPasswordStrength(e),3===f||4===f?d.removeAttr("disabled"):d.attr("disabled","disabled")},includeMeter:function(a,b){var c=a.find(".woocommerce-password-strength");0===c.length?b.after('<div class="woocommerce-password-strength" aria-live="polite"></div>'):""===b.val()&&c.remove()},checkPasswordStrength:function(b){var c=a(".woocommerce-password-strength"),d=wp.passwordStrength.meter(b.val(),wp.passwordStrength.userInputBlacklist());switch(c.removeClass("short bad good strong"),d){case 2:c.addClass("bad").html(pwsL10n.bad);break;case 3:c.addClass("good").html(pwsL10n.good);break;case 4:c.addClass("strong").html(pwsL10n.strong);break;case 5:c.addClass("short").html(pwsL10n.mismatch);break;default:c.addClass("short").html(pwsL10n["short"])}return d},checkoutNeedsRegistration:function(){var b=a('form.checkout input[type="submit"]');a(this).is(":checked")?b.attr("disabled","disabled"):b.removeAttr("disabled")}};b.init()});

View File

@ -48,12 +48,14 @@ jQuery( function( $ ) {
$( 'body' )
.on( 'click', '#respond p.stars a', function() {
var $star = $( this ),
$rating = $( this ).closest( '#respond' ).find( '#rating' );
var $star = $( this ),
$rating = $( this ).closest( '#respond' ).find( '#rating' ),
$container = $( this ).closest( '.stars' );
$rating.val( $star.text() );
$star.siblings( 'a' ).removeClass( 'active' );
$star.addClass( 'active' );
$container.addClass( 'selected' );
return false;
})

View File

@ -1 +1 @@
jQuery(function(a){return"undefined"==typeof wc_single_product_params?!1:(a(".wc-tabs-wrapper, .woocommerce-tabs").on("init",function(){a(".wc-tab, .woocommerce-tabs .panel:not(.panel .panel)").hide();var b=window.location.hash,c=window.location.href,d=a(this).find(".wc-tabs, ul.tabs").first();b.toLowerCase().indexOf("comment-")>=0||"#reviews"===b?d.find("li.reviews_tab a").click():c.indexOf("comment-page-")>0||c.indexOf("cpage=")>0?d.find("li.reviews_tab a").click():d.find("li:first a").click()}).on("click",".wc-tabs li a, ul.tabs li a",function(){var b=a(this),c=b.closest(".wc-tabs-wrapper, .woocommerce-tabs"),d=c.find(".wc-tabs, ul.tabs");return d.find("li").removeClass("active"),c.find(".wc-tab, .panel:not(.panel .panel)").hide(),b.closest("li").addClass("active"),c.find(b.attr("href")).show(),!1}).trigger("init"),a("a.woocommerce-review-link").click(function(){return a(".reviews_tab a").click(),!0}),a("#rating").hide().before('<p class="stars"><span><a class="star-1" href="#">1</a><a class="star-2" href="#">2</a><a class="star-3" href="#">3</a><a class="star-4" href="#">4</a><a class="star-5" href="#">5</a></span></p>'),void a("body").on("click","#respond p.stars a",function(){var b=a(this),c=a(this).closest("#respond").find("#rating");return c.val(b.text()),b.siblings("a").removeClass("active"),b.addClass("active"),!1}).on("click","#respond #submit",function(){var b=a(this).closest("#respond").find("#rating"),c=b.val();return b.size()>0&&!c&&"yes"===wc_single_product_params.review_rating_required?(window.alert(wc_single_product_params.i18n_required_rating_text),!1):void 0}))});
jQuery(function(a){return"undefined"==typeof wc_single_product_params?!1:(a(".wc-tabs-wrapper, .woocommerce-tabs").on("init",function(){a(".wc-tab, .woocommerce-tabs .panel:not(.panel .panel)").hide();var b=window.location.hash,c=window.location.href,d=a(this).find(".wc-tabs, ul.tabs").first();b.toLowerCase().indexOf("comment-")>=0||"#reviews"===b?d.find("li.reviews_tab a").click():c.indexOf("comment-page-")>0||c.indexOf("cpage=")>0?d.find("li.reviews_tab a").click():d.find("li:first a").click()}).on("click",".wc-tabs li a, ul.tabs li a",function(){var b=a(this),c=b.closest(".wc-tabs-wrapper, .woocommerce-tabs"),d=c.find(".wc-tabs, ul.tabs");return d.find("li").removeClass("active"),c.find(".wc-tab, .panel:not(.panel .panel)").hide(),b.closest("li").addClass("active"),c.find(b.attr("href")).show(),!1}).trigger("init"),a("a.woocommerce-review-link").click(function(){return a(".reviews_tab a").click(),!0}),a("#rating").hide().before('<p class="stars"><span><a class="star-1" href="#">1</a><a class="star-2" href="#">2</a><a class="star-3" href="#">3</a><a class="star-4" href="#">4</a><a class="star-5" href="#">5</a></span></p>'),void a("body").on("click","#respond p.stars a",function(){var b=a(this),c=a(this).closest("#respond").find("#rating"),d=a(this).closest(".stars");return c.val(b.text()),b.siblings("a").removeClass("active"),b.addClass("active"),d.addClass("selected"),!1}).on("click","#respond #submit",function(){var b=a(this).closest("#respond").find("#rating"),c=b.val();return b.size()>0&&!c&&"yes"===wc_single_product_params.review_rating_required?(window.alert(wc_single_product_params.i18n_required_rating_text),!1):void 0}))});

View File

@ -6,4 +6,4 @@ WooCommerce will delete all custom translations placed in this directory.
Put your custom WooCommerce translations in your WordPress language directory, located at: WP_LANG_DIR . "/woocommerce/{$textdomain}-{$locale}.mo";
## Contributing your translating to WooCommerce
If you want to help translate WooCommerce, please visit our [translation page](https://www.transifex.com/projects/p/woocommerce/).
If you want to help translate WooCommerce, please visit our [translation page](https://translate.wordpress.org/projects/wp-plugins/woocommerce).

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,41 +5,41 @@
* @author WooThemes
* @category i18n
* @package WooCommerce/i18n
* @version 2.2.3
* @version 2.2.9
*/
global $states;
$states['MX'] = array(
'Distrito Federal' => __( 'Distrito Federal', 'woocommerce' ),
'Jalisco' => __( 'Jalisco', 'woocommerce' ),
'Nuevo Leon' => __( 'Nuevo León', 'woocommerce' ),
'Aguascalientes' => __( 'Aguascalientes', 'woocommerce' ),
'Baja California' => __( 'Baja California', 'woocommerce' ),
'Distrito Federal' => __( 'Distrito Federal', 'woocommerce' ),
'Jalisco' => __( 'Jalisco', 'woocommerce' ),
'Nuevo Leon' => __( 'Nuevo León', 'woocommerce' ),
'Aguascalientes' => __( 'Aguascalientes', 'woocommerce' ),
'Baja California' => __( 'Baja California', 'woocommerce' ),
'Baja California Sur' => __( 'Baja California Sur', 'woocommerce' ),
'Campeche' => __( 'Campeche', 'woocommerce' ),
'Chiapas' => __( 'Chiapas', 'woocommerce' ),
'Chihuahua' => __( 'Chihuahua', 'woocommerce' ),
'Coahuila' => __( 'Coahuila', 'woocommerce' ),
'Colima' => __( 'Colima', 'woocommerce' ),
'Durango' => __( 'Durango', 'woocommerce' ),
'Guanajuato' => __( 'Guanajuato', 'woocommerce' ),
'Guerrero' => __( 'Guerrero', 'woocommerce' ),
'Hidalgo' => __( 'Hidalgo', 'woocommerce' ),
'Estado de Mexico' => __( 'Edo. de México', 'woocommerce' ),
'Michoacan' => __( 'Michoacán', 'woocommerce' ),
'Morelos' => __( 'Morelos', 'woocommerce' ),
'Nayarit' => __( 'Nayarit', 'woocommerce' ),
'Oaxaca' => __( 'Oaxaca', 'woocommerce' ),
'Puebla' => __( 'Puebla', 'woocommerce' ),
'Queretaro' => __( 'Querétaro', 'woocommerce' ),
'Quintana Roo' => __( 'Quintana Roo', 'woocommerce' ),
'San Luis Potosi' => __( 'San Luis Potosí', 'woocommerce' ),
'Sinaloa' => __( 'Sinaloa', 'woocommerce' ),
'Sonora' => __( 'Sonora', 'woocommerce' ),
'Tabasco' => __( 'Tabasco', 'woocommerce' ),
'Tamaulipas' => __( 'Tamaulipas', 'woocommerce' ),
'Tlaxcala' => __( 'Tlaxcala', 'woocommerce' ),
'Veracruz' => __( 'Veracruz', 'woocommerce' ),
'Yucatan' => __( 'Yucatán', 'woocommerce' ),
'Zacatecas' => __( 'Zacatecas', 'woocommerce' )
'Campeche' => __( 'Campeche', 'woocommerce' ),
'Chiapas' => __( 'Chiapas', 'woocommerce' ),
'Chihuahua' => __( 'Chihuahua', 'woocommerce' ),
'Coahuila' => __( 'Coahuila', 'woocommerce' ),
'Colima' => __( 'Colima', 'woocommerce' ),
'Durango' => __( 'Durango', 'woocommerce' ),
'Guanajuato' => __( 'Guanajuato', 'woocommerce' ),
'Guerrero' => __( 'Guerrero', 'woocommerce' ),
'Hidalgo' => __( 'Hidalgo', 'woocommerce' ),
'Estado de Mexico' => __( 'Edo. de México', 'woocommerce' ),
'Michoacan' => __( 'Michoacán', 'woocommerce' ),
'Morelos' => __( 'Morelos', 'woocommerce' ),
'Nayarit' => __( 'Nayarit', 'woocommerce' ),
'Oaxaca' => __( 'Oaxaca', 'woocommerce' ),
'Puebla' => __( 'Puebla', 'woocommerce' ),
'Queretaro' => __( 'Querétaro', 'woocommerce' ),
'Quintana Roo' => __( 'Quintana Roo', 'woocommerce' ),
'San Luis Potosi' => __( 'San Luis Potosí', 'woocommerce' ),
'Sinaloa' => __( 'Sinaloa', 'woocommerce' ),
'Sonora' => __( 'Sonora', 'woocommerce' ),
'Tabasco' => __( 'Tabasco', 'woocommerce' ),
'Tamaulipas' => __( 'Tamaulipas', 'woocommerce' ),
'Tlaxcala' => __( 'Tlaxcala', 'woocommerce' ),
'Veracruz' => __( 'Veracruz', 'woocommerce' ),
'Yucatan' => __( 'Yucatán', 'woocommerce' ),
'Zacatecas' => __( 'Zacatecas', 'woocommerce' )
);

View File

@ -2456,11 +2456,12 @@ abstract class WC_Abstract_Order {
if ( $_product && $_product->exists() && $_product->managing_stock() ) {
$qty = apply_filters( 'woocommerce_order_item_quantity', $item['qty'], $this, $item );
$new_stock = $_product->reduce_stock( $qty );
$item_name = $_product->get_sku() ? $_product->get_sku(): $item['product_id'];
if ( isset( $item['variation_id'] ) && $item['variation_id'] ) {
$this->add_order_note( sprintf( __( 'Item #%s variation #%s stock reduced from %s to %s.', 'woocommerce' ), $item['product_id'], $item['variation_id'], $new_stock + $qty, $new_stock) );
$this->add_order_note( sprintf( __( 'Item %s variation #%s stock reduced from %s to %s.', 'woocommerce' ), $item_name, $item['variation_id'], $new_stock + $qty, $new_stock) );
} else {
$this->add_order_note( sprintf( __( 'Item #%s stock reduced from %s to %s.', 'woocommerce' ), $item['product_id'], $new_stock + $qty, $new_stock) );
$this->add_order_note( sprintf( __( 'Item %s stock reduced from %s to %s.', 'woocommerce' ), $item_name, $new_stock + $qty, $new_stock) );
}
$this->send_stock_notifications( $_product, $new_stock, $item['qty'] );
}

View File

@ -1310,7 +1310,19 @@ class WC_Product {
* @return array
*/
public function get_attributes() {
return apply_filters( 'woocommerce_get_product_attributes', (array) maybe_unserialize( $this->product_attributes ) );
$attributes = array_filter( (array) maybe_unserialize( $this->product_attributes ) );
$taxonomies = wp_list_pluck( wc_get_attribute_taxonomies(), 'attribute_name' );
// Check for any attributes which have been removed globally
foreach ( $attributes as $key => $attribute ) {
if ( $attribute['is_taxonomy'] ) {
if ( ! in_array( substr( $attribute['name'], 3 ), $taxonomies ) ) {
unset( $attributes[ $key ] );
}
}
}
return apply_filters( 'woocommerce_get_product_attributes', $attributes );
}
/**

View File

@ -185,7 +185,7 @@ abstract class WC_Settings_API {
$this->settings[ $key ] = isset( $form_fields[ $key ]['default'] ) ? $form_fields[ $key ]['default'] : '';
}
if ( ! is_null( $empty_value ) && empty( $this->settings[ $key ] ) ) {
if ( ! is_null( $empty_value ) && empty( $this->settings[ $key ] ) && '' === $this->settings[ $key ] ) {
$this->settings[ $key ] = $empty_value;
}
@ -846,16 +846,10 @@ abstract class WC_Settings_API {
* @return string
*/
public function validate_password_field( $key ) {
$text = $this->get_option( $key );
$field = $this->get_field_key( $key );
$value = trim( stripslashes( $_POST[ $field ] ) );
if ( isset( $_POST[ $field ] ) ) {
$text = wp_kses_post( $value );
}
return $text === $value ? $text : '';
$value = wp_kses_post( trim( stripslashes( $_POST[ $field ] ) ) );
return $value;
}
/**

View File

@ -9,7 +9,7 @@
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
exit;
}
if ( ! class_exists( 'WC_Admin_Assets' ) ) :
@ -29,7 +29,7 @@ class WC_Admin_Assets {
}
/**
* Enqueue styles
* Enqueue styles.
*/
public function admin_styles() {
global $wp_scripts;
@ -47,11 +47,8 @@ class WC_Admin_Assets {
// Sitewide menu CSS
wp_enqueue_style( 'woocommerce_admin_menu_styles' );
// Admin styles for WC pages only
if ( in_array( $screen->id, wc_get_screen_ids() ) ) {
$jquery_version = isset( $wp_scripts->registered['jquery-ui-core']->ver ) ? $wp_scripts->registered['jquery-ui-core']->ver : '1.9.2';
// Admin styles for WC pages only
wp_enqueue_style( 'woocommerce_admin_styles' );
wp_enqueue_style( 'jquery-ui-style' );
wp_enqueue_style( 'wp-color-picker' );
@ -77,7 +74,7 @@ class WC_Admin_Assets {
/**
* Enqueue scripts
* Enqueue scripts.
*/
public function admin_scripts() {
global $wp_query, $post, $current_user;
@ -104,6 +101,7 @@ class WC_Admin_Assets {
wp_register_script( 'flot-time', WC()->plugin_url() . '/assets/js/jquery-flot/jquery.flot.time' . $suffix . '.js', array( 'jquery', 'flot' ), WC_VERSION );
wp_register_script( 'flot-pie', WC()->plugin_url() . '/assets/js/jquery-flot/jquery.flot.pie' . $suffix . '.js', array( 'jquery', 'flot' ), WC_VERSION );
wp_register_script( 'flot-stack', WC()->plugin_url() . '/assets/js/jquery-flot/jquery.flot.stack' . $suffix . '.js', array( 'jquery', 'flot' ), WC_VERSION );
wp_register_script( 'wc-settings-tax', WC()->plugin_url() . '/assets/js/admin/settings-views-html-settings-tax' . $suffix . '.js', array( 'jquery', 'wp-util', 'underscore', 'backbone' ), WC_VERSION );
// Chosen is @deprecated (2.3) in favour of select2, but is registered for backwards compat
wp_register_script( 'ajax-chosen', WC()->plugin_url() . '/assets/js/chosen/ajax-chosen.jquery' . $suffix . '.js', array( 'jquery', 'chosen' ), WC_VERSION );
@ -137,9 +135,8 @@ class WC_Admin_Assets {
// WooCommerce admin pages
if ( in_array( $screen->id, wc_get_screen_ids() ) ) {
wp_enqueue_script( 'woocommerce_admin' );
wp_enqueue_script( 'iris' );
wp_enqueue_script( 'woocommerce_admin' );
wp_enqueue_script( 'wc-enhanced-select' );
wp_enqueue_script( 'jquery-ui-sortable' );
wp_enqueue_script( 'jquery-ui-autocomplete' );
@ -254,6 +251,7 @@ class WC_Admin_Assets {
'load_shipping' => __( 'Load the customer\'s shipping information? This will remove any currently entered shipping information.', 'woocommerce' ),
'featured_label' => __( 'Featured', 'woocommerce' ),
'prices_include_tax' => esc_attr( get_option( 'woocommerce_prices_include_tax' ) ),
'tax_based_on' => esc_attr( get_option( 'woocommerce_tax_based_on' ) ),
'round_at_subtotal' => esc_attr( get_option( 'woocommerce_tax_round_at_subtotal' ) ),
'no_customer_selected' => __( 'No customer selected', 'woocommerce' ),
'plugin_url' => WC()->plugin_url(),

View File

@ -163,7 +163,7 @@ class WC_Admin_Dashboard {
<?php printf( _n( "<strong>%s product</strong> out of stock", "<strong>%s products</strong> out of stock", $outofstock_count, 'woocommerce' ), $outofstock_count ); ?>
</a>
</li>
<?php do_action( 'woocommerce_after_dashboard_status_widget', $reports ); ?>
</ul>
<?php
@ -195,9 +195,9 @@ class WC_Admin_Dashboard {
$rating = intval( get_comment_meta( $comment->comment_ID, 'rating', true ) );
echo '<div class="star-rating" title="' . esc_attr( $rating ) . '">
<span style="width:'. ( $rating * 20 ) . '%">' . $rating . ' ' . __( 'out of 5', 'woocommerce' ) . '</span></div>';
<span style="width:' . ( $rating * 20 ) . '%">' . $rating . ' ' . __( 'out of 5', 'woocommerce' ) . '</span></div>';
echo '<h4 class="meta"><a href="' . get_permalink( $comment->ID ) . '#comment-' . absint( $comment->comment_ID ) .'">' . esc_html__( apply_filters( 'woocommerce_admin_dashboard_recent_reviews', $comment->post_title, $comment ) ) . '</a> ' . __( 'reviewed by', 'woocommerce' ) . ' ' . esc_html( $comment->comment_author ) .'</h4>';
echo '<h4 class="meta"><a href="' . get_permalink( $comment->ID ) . '#comment-' . absint( $comment->comment_ID ) .'">' . esc_html( apply_filters( 'woocommerce_admin_dashboard_recent_reviews', $comment->post_title, $comment ) ) . '</a> ' . __( 'reviewed by', 'woocommerce' ) . ' ' . esc_html( $comment->comment_author ) .'</h4>';
echo '<blockquote>' . wp_kses_data( $comment->comment_excerpt ) . ' [...]</blockquote></li>';
}

View File

@ -203,7 +203,17 @@ class WC_Admin_Help {
'content' =>
'<h2>' . __( 'Found a bug?', 'woocommerce' ) . '</h2>' .
'<p>' . sprintf( __( 'If you find a bug within WooCommerce core you can create a ticket via <a href="%s">Github issues</a>. Ensure you read the <a href="%s">contribution guide</a> prior to submitting your report. To help us solve your issue, please be as descriptive as possible and include your <a href="%s">system status report</a>.', 'woocommerce' ), 'https://github.com/woothemes/woocommerce/issues?state=open', 'https://github.com/woothemes/woocommerce/blob/master/CONTRIBUTING.md', admin_url( 'admin.php?page=wc-status' ) ) . '</p>' .
'<p><a href="' . 'https://github.com/woothemes/woocommerce/issues?state=open' . '" class="button button-primary">' . __( 'Report a bug', 'woocommerce' ) . '</a> <a href="' . admin_url('admin.php?page=wc-status') . '" class="button">' . __( 'System Status', 'woocommerce' ) . '</a></p>'
'<p><a href="' . 'https://github.com/woothemes/woocommerce/issues?state=open' . '" class="button button-primary">' . __( 'Report a bug', 'woocommerce' ) . '</a> <a href="' . admin_url( 'admin.php?page=wc-status' ) . '" class="button">' . __( 'System Status', 'woocommerce' ) . '</a></p>'
) );
$screen->add_help_tab( array(
'id' => 'woocommerce_onboard_tab',
'title' => __( 'Onboarding Wizard', 'woocommerce' ),
'content' =>
'<h2>' . __( 'Onboarding Wizard', 'woocommerce' ) . '</h2>' .
'<p>' . __( 'If you need to access the onboarding wizard again, please click on the button below.', 'woocommerce' ) . '</p>' .
'<p><a href="' . admin_url( 'index.php?page=wc-setup' ) . '" class="button button-primary">' . __( 'Onboarding Wizard', 'woocommerce' ) . '</a></p>'
) );

View File

@ -137,9 +137,9 @@ class WC_Admin_Meta_Boxes {
remove_meta_box( 'pageparentdiv', 'product', 'side' );
remove_meta_box( 'commentstatusdiv', 'product', 'normal' );
remove_meta_box( 'commentstatusdiv', 'product', 'side' );
remove_meta_box( 'woothemes-settings', 'shop_coupon' , 'normal' );
remove_meta_box( 'commentstatusdiv', 'shop_coupon' , 'normal' );
remove_meta_box( 'slugdiv', 'shop_coupon' , 'normal' );
remove_meta_box( 'woothemes-settings', 'shop_coupon', 'normal' );
remove_meta_box( 'commentstatusdiv', 'shop_coupon', 'normal' );
remove_meta_box( 'slugdiv', 'shop_coupon', 'normal' );
foreach ( wc_get_order_types( 'order-meta-boxes' ) as $type ) {
remove_meta_box( 'commentsdiv', $type, 'normal' );

View File

@ -22,11 +22,10 @@ class WC_Admin_Notices {
* @var array
*/
private $core_notices = array(
'install' => 'install_notice',
'update' => 'update_notice',
'template_files' => 'template_file_check_notice',
'theme_support' => 'theme_check_notice',
'translation_upgrade' => 'translation_upgrade_notice'
'install' => 'install_notice',
'update' => 'update_notice',
'template_files' => 'template_file_check_notice',
'theme_support' => 'theme_check_notice'
);
/**
@ -36,7 +35,6 @@ class WC_Admin_Notices {
add_action( 'switch_theme', array( $this, 'reset_admin_notices' ) );
add_action( 'woocommerce_installed', array( $this, 'reset_admin_notices' ) );
add_action( 'wp_loaded', array( $this, 'hide_notices' ) );
add_action( 'woocommerce_hide_translation_upgrade_notice', array( $this, 'hide_translation_upgrade_notice' ) );
if ( current_user_can( 'manage_woocommerce' ) ) {
add_action( 'admin_print_styles', array( $this, 'add_notices' ) );
@ -106,13 +104,6 @@ class WC_Admin_Notices {
}
}
/**
* Hide translation upgrade message
*/
public function hide_translation_upgrade_notice() {
update_option( 'woocommerce_language_pack_version', array( WC_VERSION, get_locale() ) );
}
/**
* Add notices + styles if needed.
*/
@ -154,22 +145,6 @@ class WC_Admin_Notices {
}
}
/**
* Show the translation upgrade notice
*/
public function translation_upgrade_notice() {
$screen = get_current_screen();
$locale = get_locale();
if ( 'en_US' === $locale ) {
self::hide_translation_upgrade_notice();
}
if ( 'update-core' !== $screen->id && 'en_US' !== $locale ) {
include( 'views/html-notice-translation-upgrade.php' );
}
}
/**
* Show a notice highlighting bad template files
*/

View File

@ -33,7 +33,7 @@ class WC_Admin_Permalink_Settings {
*/
public function settings_init() {
// Add a section to the permalinks page
add_settings_section( 'woocommerce-permalink', __( 'Product permalink base', 'woocommerce' ), array( $this, 'settings' ), 'permalink' );
add_settings_section( 'woocommerce-permalink', __( 'Product Permalinks', 'woocommerce' ), array( $this, 'settings' ), 'permalink' );
// Add our settings
add_settings_field(
@ -93,9 +93,9 @@ class WC_Admin_Permalink_Settings {
* Show the settings.
*/
public function settings() {
echo wpautop( __( 'These settings control the permalinks used for products. These settings only apply when <strong>not using "default" permalinks above</strong>.', 'woocommerce' ) );
echo wpautop( __( 'These settings control the permalinks used specifically for products.', 'woocommerce' ) );
$permalinks = get_option( 'woocommerce_permalinks' );
$permalinks = get_option( 'woocommerce_permalinks' );
$product_permalink = $permalinks['product_base'];
// Get shop page
@ -105,28 +105,23 @@ class WC_Admin_Permalink_Settings {
$structures = array(
0 => '',
1 => '/' . trailingslashit( $product_base ),
2 => '/' . trailingslashit( $base_slug ),
3 => '/' . trailingslashit( $base_slug ) . trailingslashit( '%product_cat%' )
1 => '/' . trailingslashit( $base_slug ),
2 => '/' . trailingslashit( $base_slug ) . trailingslashit( '%product_cat%' )
);
?>
<table class="form-table">
<table class="form-table wc-permalink-structure">
<tbody>
<tr>
<th><label><input name="product_permalink" type="radio" value="<?php echo esc_attr( $structures[0] ); ?>" class="wctog" <?php checked( $structures[0], $product_permalink ); ?> /> <?php _e( 'Default', 'woocommerce' ); ?></label></th>
<td><code><?php echo esc_html( home_url() ); ?>/?product=sample-product</code></td>
</tr>
<tr>
<th><label><input name="product_permalink" type="radio" value="<?php echo esc_attr( $structures[1] ); ?>" class="wctog" <?php checked( $structures[1], $product_permalink ); ?> /> <?php _e( 'Product', 'woocommerce' ); ?></label></th>
<td><code><?php echo esc_html( home_url() ); ?>/<?php echo esc_html( $product_base ); ?>/sample-product/</code></td>
<td><code class="default-example"><?php echo esc_html( home_url() ); ?>/?product=sample-product</code> <code class="non-default-example"><?php echo esc_html( home_url() ); ?>/<?php echo esc_html( $product_base ); ?>/sample-product/</code></td>
</tr>
<?php if ( $shop_page_id ) : ?>
<tr>
<th><label><input name="product_permalink" type="radio" value="<?php echo esc_attr( $structures[2] ); ?>" class="wctog" <?php checked( $structures[2], $product_permalink ); ?> /> <?php _e( 'Shop base', 'woocommerce' ); ?></label></th>
<th><label><input name="product_permalink" type="radio" value="<?php echo esc_attr( $structures[1] ); ?>" class="wctog" <?php checked( $structures[1], $product_permalink ); ?> /> <?php _e( 'Shop base', 'woocommerce' ); ?></label></th>
<td><code><?php echo esc_html( home_url() ); ?>/<?php echo esc_html( $base_slug ); ?>/sample-product/</code></td>
</tr>
<tr>
<th><label><input name="product_permalink" type="radio" value="<?php echo esc_attr( $structures[3] ); ?>" class="wctog" <?php checked( $structures[3], $product_permalink ); ?> /> <?php _e( 'Shop base with category', 'woocommerce' ); ?></label></th>
<th><label><input name="product_permalink" type="radio" value="<?php echo esc_attr( $structures[2] ); ?>" class="wctog" <?php checked( $structures[2], $product_permalink ); ?> /> <?php _e( 'Shop base with category', 'woocommerce' ); ?></label></th>
<td><code><?php echo esc_html( home_url() ); ?>/<?php echo esc_html( $base_slug ); ?>/product-category/sample-product/</code></td>
</tr>
<?php endif; ?>
@ -144,7 +139,18 @@ class WC_Admin_Permalink_Settings {
jQuery('input.wctog').change(function() {
jQuery('#woocommerce_permalink_structure').val( jQuery( this ).val() );
});
jQuery('.permalink-structure input').change(function() {
jQuery('.wc-permalink-structure').find('code.non-default-example, code.default-example').hide();
if ( jQuery(this).val() ) {
jQuery('.wc-permalink-structure code.non-default-example').show();
jQuery('.wc-permalink-structure input').removeAttr('disabled');
} else {
jQuery('.wc-permalink-structure code.default-example').show();
jQuery('.wc-permalink-structure input:eq(0)').click();
jQuery('.wc-permalink-structure input').attr('disabled', 'disabled');
}
});
jQuery('.permalink-structure input:checked').change();
jQuery('#woocommerce_permalink_structure').focus( function(){
jQuery('#woocommerce_custom_selection').click();
} );
@ -168,8 +174,7 @@ class WC_Admin_Permalink_Settings {
$woocommerce_product_category_slug = wc_clean( $_POST['woocommerce_product_category_slug'] );
$woocommerce_product_tag_slug = wc_clean( $_POST['woocommerce_product_tag_slug'] );
$woocommerce_product_attribute_slug = wc_clean( $_POST['woocommerce_product_attribute_slug'] );
$permalinks = get_option( 'woocommerce_permalinks' );
$permalinks = get_option( 'woocommerce_permalinks' );
if ( ! $permalinks ) {
$permalinks = array();
@ -182,7 +187,7 @@ class WC_Admin_Permalink_Settings {
// Product base
$product_permalink = wc_clean( $_POST['product_permalink'] );
if ( $product_permalink == 'custom' ) {
if ( 'custom' === $product_permalink ) {
// Get permalink without slashes
$product_permalink = trim( wc_clean( $_POST['product_permalink_structure'] ), '/' );

View File

@ -76,6 +76,7 @@ class WC_Admin_Post_Types {
add_action( 'wp_trash_post', array( $this, 'trash_post' ) );
add_action( 'untrash_post', array( $this, 'untrash_post' ) );
add_action( 'before_delete_post', array( $this, 'delete_order_items' ) );
add_action( 'before_delete_post', array( $this, 'delete_order_downloadable_permissions' ) );
// Edit post screens
add_filter( 'enter_title_here', array( $this, 'enter_title_here' ), 1, 2 );
@ -215,7 +216,6 @@ class WC_Admin_Post_Types {
$columns = array();
$columns['cb'] = '<input type="checkbox" />';
$columns['thumb'] = '<span class="wc-image tips" data-tip="' . esc_attr__( 'Image', 'woocommerce' ) . '">' . __( 'Image', 'woocommerce' ) . '</span>';
$columns['thumb'] = '<span class="wc-image tips" data-tip="' . esc_attr__( 'Image', 'woocommerce' ) . '">' . __( 'Image', 'woocommerce' ) . '</span>';
$columns['name'] = __( 'Name', 'woocommerce' );
if ( wc_product_sku_enabled() ) {
@ -298,7 +298,7 @@ class WC_Admin_Post_Types {
$edit_link = get_edit_post_link( $post->ID );
$title = _draft_or_post_title();
echo '<strong><a class="row-title" href="' . esc_url( $edit_link ) .'">' . $title .'</a>';
echo '<strong><a class="row-title" href="' . esc_url( $edit_link ) . '">' . esc_html( $title ) . '</a>';
_post_states( $post );
@ -487,10 +487,12 @@ class WC_Admin_Post_Types {
$edit_link = get_edit_post_link( $post->ID );
$title = _draft_or_post_title();
echo '<strong><a href="' . esc_attr( $edit_link ) . '" class="row-title">' . esc_html( $title ). '</a></strong>';
echo '<strong><a class="row-title" href="' . esc_url( $edit_link ) . '">' . esc_html( $title ) . '</a>';
_post_states( $post );
echo '</strong>';
$this->_render_shop_coupon_row_actions( $post, $title );
break;
case 'type' :
@ -1608,7 +1610,7 @@ class WC_Admin_Post_Types {
* Filters for post types
*/
public function restrict_manage_posts() {
global $typenow, $wp_query;
global $typenow;
if ( in_array( $typenow, wc_get_order_types( 'order-meta-boxes' ) ) ) {
$this->shop_order_filters();
@ -1720,7 +1722,7 @@ class WC_Admin_Post_Types {
if ( ! empty( $_GET['_customer_user'] ) ) {
$user_id = absint( $_GET['_customer_user'] );
$user = get_user_by( 'id', $user_id );
$user_string = esc_html( $user->display_name ) . ' (#' . absint( $user->ID ) . ' &ndash; ' . esc_html( $user->user_email );
$user_string = esc_html( $user->display_name ) . ' (#' . absint( $user->ID ) . ' &ndash; ' . esc_html( $user->user_email ) . ')';
}
?>
<input type="hidden" class="wc-customer-search" name="_customer_user" data-placeholder="<?php esc_attr_e( 'Search for a customer&hellip;', 'woocommerce' ); ?>" data-selected="<?php echo htmlspecialchars( $user_string ); ?>" value="<?php echo $user_id; ?>" data-allow_clear="true" />
@ -1863,15 +1865,17 @@ class WC_Admin_Post_Types {
if ( is_numeric( $term ) ) {
$search_ids[] = $term;
}
// Attempt to get a SKU
$sku_to_id = $wpdb->get_col( $wpdb->prepare( "SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='_sku' AND meta_value LIKE '%%%s%%';", wc_clean( $term ) ) );
$sku_to_id = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_parent FROM {$wpdb->posts} LEFT JOIN {$wpdb->postmeta} ON {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id WHERE meta_key='_sku' AND meta_value LIKE %s;", '%' . $wpdb->esc_like( wc_clean( $term ) ) . '%' ) );
$sku_to_id = array_merge( wp_list_pluck( $sku_to_id, 'ID' ), wp_list_pluck( $sku_to_id, 'post_parent' ) );
if ( $sku_to_id && sizeof( $sku_to_id ) > 0 ) {
$search_ids = array_merge( $search_ids, $sku_to_id );
}
}
$search_ids = array_filter( array_map( 'absint', $search_ids ) );
$search_ids = array_filter( array_unique( array_map( 'absint', $search_ids ) ) );
if ( sizeof( $search_ids ) > 0 ) {
$where = str_replace( 'AND (((', "AND ( ({$wpdb->posts}.ID IN (" . implode( ',', $search_ids ) . ")) OR ((", $where );
@ -2039,6 +2043,24 @@ class WC_Admin_Post_Types {
}
}
/**
* Remove downloadable permissions on permanent order deletion
*/
public function delete_order_downloadable_permissions( $postid ) {
global $wpdb;
if ( in_array( get_post_type( $postid ), wc_get_order_types() ) ) {
do_action( 'woocommerce_delete_order_downloadable_permissions', $postid );
$wpdb->query( $wpdb->prepare( "
DELETE FROM {$wpdb->prefix}woocommerce_downloadable_product_permissions
WHERE order_id = %d
", $postid ) );
do_action( 'woocommerce_deleted_order_downloadable_permissions', $postid );
}
}
/**
* Change title boxes in admin.
* @param string $text
@ -2065,7 +2087,7 @@ class WC_Admin_Post_Types {
public function edit_form_after_title( $post ) {
if ( 'shop_coupon' === $post->post_type ) {
?>
<textarea id="woocommerce-coupon-description" name="excerpt" cols="5" rows="2" placeholder="<?php esc_attr_e( 'Description (optional)', 'woocommerce' ); ?>"><?php echo esc_textarea( $post->post_excerpt ); ?></textarea>
<textarea id="woocommerce-coupon-description" name="excerpt" cols="5" rows="2" placeholder="<?php esc_attr_e( 'Description (optional)', 'woocommerce' ); ?>"><?php echo $post->post_excerpt; // This is already escaped in core ?></textarea>
<?php
}
}

View File

@ -131,7 +131,7 @@ class WC_Admin_Setup_Wizard {
public function get_next_step_link() {
$keys = array_keys( $this->steps );
return add_query_arg( 'step', $keys[ array_search( $this->step, array_keys( $this->steps ) ) + 1 ], remove_query_arg( 'translation_updated' ) );
return add_query_arg( 'step', $keys[ array_search( $this->step, array_keys( $this->steps ) ) + 1 ] );
}
/**
@ -201,20 +201,6 @@ class WC_Admin_Setup_Wizard {
* Introduction step
*/
public function wc_setup_introduction() {
$locale = get_locale();
if ( isset( $_GET['translation_updated'] ) ) {
WC_Language_Pack_Upgrader::language_update_messages();
} elseif( 'en_US' !== $locale && WC_Language_Pack_Upgrader::has_available_update( $locale ) ) {
?>
<div class="woocommerce-message woocommerce-language-pack">
<p><?php printf( esc_html__( 'WooCommerce is available in %s. Would you like to use this translation?', 'woocommerce' ), $locale ); ?></p>
<p class="submit">
<a href="<?php echo esc_url( wp_nonce_url( admin_url( 'index.php?page=wc-setup&action=translation_upgrade' ), 'setup_language' ) ); ?>" class="button-primary"><?php _e( 'Install Translation', 'woocommerce' ); ?></a>
</p>
</div>
<?php
}
?>
<h1><?php _e( 'Welcome to the world of WooCommerce!', 'woocommerce' ); ?></h1>
<p><?php _e( 'Thank you for choosing WooCommerce to power your online store! This quick setup wizard will help you configure the basic settings. <strong>Its completely optional and shouldnt take longer than five minutes.</strong>', 'woocommerce' ); ?></p>

View File

@ -96,10 +96,7 @@ class WC_Admin_Status {
break;
case 'clear_sessions' :
$wpdb->query( "
DELETE FROM {$wpdb->options}
WHERE option_name LIKE '_wc_session_%' OR option_name LIKE '_wc_session_expires_%'
" );
$wpdb->query( "DELETE FROM {$wpdb->prefix}woocommerce_sessions" );
wp_cache_flush();
@ -141,11 +138,6 @@ class WC_Admin_Status {
}
}
// Manual translation update messages
if ( isset( $_GET['translation_updated'] ) ) {
WC_Language_Pack_Upgrader::language_update_messages();
}
// Display message if settings settings have been saved
if ( isset( $_REQUEST['settings-updated'] ) ) {
echo '<div class="updated"><p>' . __( 'Your changes have been saved.', 'woocommerce' ) . '</p></div>';
@ -202,14 +194,6 @@ class WC_Admin_Status {
)
);
if ( get_locale() !== 'en_US' ) {
$tools['translation_upgrade'] = array(
'name' => __( 'Translation Upgrade', 'woocommerce' ),
'button' => __( 'Force Translation Upgrade', 'woocommerce' ),
'desc' => __( '<strong class="red">Note:</strong> This option will force the translation upgrade for your language if a translation is available.', 'woocommerce' ),
);
}
return apply_filters( 'woocommerce_debug_tools', $tools );
}

File diff suppressed because one or more lines are too long

View File

@ -56,12 +56,7 @@ class WC_Admin {
switch ( $_GET['page'] ) {
case 'wc-setup' :
include_once( 'class-wc-admin-setup-wizard.php' );
break;
case 'wc-about' :
case 'wc-credits' :
case 'wc-translators' :
include_once( 'class-wc-admin-welcome.php' );
break;
break;
}
}
@ -105,7 +100,7 @@ class WC_Admin {
delete_transient( '_wc_activation_redirect' );
if ( ! empty( $_GET['page'] ) && in_array( $_GET['page'], array( 'wc-setup', 'wc-about' ) ) ) {
if ( ! empty( $_GET['page'] ) && in_array( $_GET['page'], array( 'wc-setup' ) ) ) {
return;
}
@ -113,11 +108,6 @@ class WC_Admin {
if ( WC_Admin_Notices::has_notice( 'install' ) ) {
wp_safe_redirect( admin_url( 'index.php?page=wc-setup' ) );
exit;
// Otherwise, the welcome page
} else {
wp_safe_redirect( admin_url( 'index.php?page=wc-about' ) );
exit;
}
}
@ -198,11 +188,6 @@ class WC_Admin {
}
$wc_pages = array_flip( $wc_pages );
// Add the dashboard pages
$wc_pages[] = 'dashboard_page_wc-about';
$wc_pages[] = 'dashboard_page_wc-credits';
$wc_pages[] = 'dashboard_page_wc-translators';
// Check to make sure we're on a WooCommerce admin page
if ( isset( $current_screen->id ) && apply_filters( 'woocommerce_display_admin_footer_text', in_array( $current_screen->id, $wc_pages ) ) ) {
// Change the footer text

View File

@ -120,6 +120,7 @@ class WC_Meta_Box_Order_Actions {
foreach ( $mails as $mail ) {
if ( $mail->id == $email_to_send ) {
$mail->trigger( $order->id );
$order->add_order_note( sprintf( __( '%s email notification manually sent.', 'woocommerce' ), $mail->title ), false, true );
}
}
}

View File

@ -1177,7 +1177,7 @@ class WC_Meta_Box_Product_Data {
// Validate the file extension
if ( in_array( $file_is, array( 'absolute', 'relative' ) ) ) {
$file_type = wp_check_filetype( strtok( $file_url, '?' ) );
$file_type = wp_check_filetype( strtok( $file_url, '?' ), $allowed_file_types );
$parsed_url = parse_url( $file_url, PHP_URL_PATH );
$extension = pathinfo( $parsed_url, PATHINFO_EXTENSION );
@ -1465,7 +1465,7 @@ class WC_Meta_Box_Product_Data {
// Validate the file extension
if ( in_array( $file_is, array( 'absolute', 'relative' ) ) ) {
$file_type = wp_check_filetype( strtok( $file_url, '?' ) );
$file_type = wp_check_filetype( strtok( $file_url, '?' ), $allowed_file_types );
$parsed_url = parse_url( $file_url, PHP_URL_PATH );
$extension = pathinfo( $parsed_url, PATHINFO_EXTENSION );

View File

@ -17,10 +17,10 @@ if ( ! defined( 'ABSPATH' ) ) {
<td class="name">
<div class="view">
<?php echo ! empty( $item['name'] ) ? esc_html( $item['name'] ) : __( 'Shipping', 'woocommerce' ); ?>
<?php echo ! empty( $item['name'] ) ? wc_clean( $item['name'] ) : __( 'Shipping', 'woocommerce' ); ?>
</div>
<div class="edit" style="display: none;">
<input type="text" placeholder="<?php esc_attr_e( 'Shipping Name', 'woocommerce' ); ?>" name="shipping_method_title[<?php echo $item_id; ?>]" value="<?php echo ( isset( $item['name'] ) ) ? esc_attr( $item['name'] ) : ''; ?>" />
<input type="text" placeholder="<?php esc_attr_e( 'Shipping Name', 'woocommerce' ); ?>" name="shipping_method_title[<?php echo $item_id; ?>]" value="<?php echo ( isset( $item['name'] ) ) ? wc_clean( $item['name'] ) : ''; ?>" />
<select name="shipping_method[<?php echo $item_id; ?>]">
<optgroup label="<?php esc_attr_e( 'Shipping Method', 'woocommerce' ); ?>">
<option value=""><?php _e( 'N/A', 'woocommerce' ); ?></option>

View File

@ -317,7 +317,8 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
$this->report_data->net_sales = wc_format_decimal( $this->report_data->total_sales - $this->report_data->total_shipping - $this->report_data->total_tax - $this->report_data->total_shipping_tax, 2 );
// Calculate average based on net
$this->report_data->average_sales = wc_format_decimal( $this->report_data->net_sales / ( $this->chart_interval + 1 ), 2 );
$this->report_data->average_sales = wc_format_decimal( $this->report_data->net_sales / ( $this->chart_interval + 1 ), 2 );
$this->report_data->average_total_sales = wc_format_decimal( $this->report_data->total_sales / ( $this->chart_interval + 1 ), 2 );
// Total orders and discounts also includes those which have been refunded at some point
$this->report_data->total_orders = absint( array_sum( wp_list_pluck( $this->report_data->order_counts, 'count' ) ) );
@ -338,11 +339,13 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
switch ( $this->chart_groupby ) {
case 'day' :
$average_sales_title = sprintf( __( '%s average daily sales', 'woocommerce' ), '<strong>' . wc_price( $data->average_sales ) . '</strong>' );
$average_total_sales_title = sprintf( __( '%s average gross daily sales', 'woocommerce' ), '<strong>' . wc_price( $data->average_total_sales ) . '</strong>' );
$average_sales_title = sprintf( __( '%s average net daily sales', 'woocommerce' ), '<strong>' . wc_price( $data->average_sales ) . '</strong>' );
break;
case 'month' :
default :
$average_sales_title = sprintf( __( '%s average monthly sales', 'woocommerce' ), '<strong>' . wc_price( $data->average_sales ) . '</strong>' );
$average_total_sales_title = sprintf( __( '%s average gross monthly sales', 'woocommerce' ), '<strong>' . wc_price( $data->average_total_sales ) . '</strong>' );
$average_sales_title = sprintf( __( '%s average net monthly sales', 'woocommerce' ), '<strong>' . wc_price( $data->average_sales ) . '</strong>' );
break;
}
@ -350,21 +353,30 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
'title' => sprintf( __( '%s gross sales in this period', 'woocommerce' ), '<strong>' . wc_price( $data->total_sales ) . '</strong>' ),
'placeholder' => __( 'This is the sum of the order totals after any refunds and including shipping and taxes.', 'woocommerce' ),
'color' => $this->chart_colours['sales_amount'],
'highlight_series' => 5
);
$legend[] = array(
'title' => sprintf( __( '%s net sales in this period', 'woocommerce' ), '<strong>' . wc_price( $data->net_sales ) . '</strong>' ),
'placeholder' => __( 'This is the sum of the order totals after any refunds and excluding shipping and taxes.', 'woocommerce' ),
'color' => $this->chart_colours['net_sales_amount'],
'highlight_series' => 6
);
if ( $data->average_sales > 0 ) {
if ( $data->average_total_sales > 0 ) {
$legend[] = array(
'title' => $average_sales_title,
'title' => $average_total_sales_title,
'color' => $this->chart_colours['average'],
'highlight_series' => 2
);
}
$legend[] = array(
'title' => sprintf( __( '%s net sales in this period', 'woocommerce' ), '<strong>' . wc_price( $data->net_sales ) . '</strong>' ),
'placeholder' => __( 'This is the sum of the order totals after any refunds and excluding shipping and taxes.', 'woocommerce' ),
'color' => $this->chart_colours['net_sales_amount'],
'highlight_series' => 7
);
if ( $data->average_sales > 0 ) {
$legend[] = array(
'title' => $average_sales_title,
'color' => $this->chart_colours['net_average'],
'highlight_series' => 3
);
}
$legend[] = array(
'title' => sprintf( __( '%s orders placed', 'woocommerce' ), '<strong>' . $data->total_orders . '</strong>' ),
'color' => $this->chart_colours['order_count'],
@ -379,17 +391,17 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
$legend[] = array(
'title' => sprintf( _nx( '%s refunded %d order', '%s refunded %d orders', $this->report_data->total_refunded_orders, '%s = amount of the refunds, %d = number of refunded orders.', 'woocommerce' ), '<strong>' . wc_price( $data->total_refunds ) . '</strong>', $this->report_data->total_refunded_orders ) . ' (' . sprintf( _n( '%d item', '%d items', $this->report_data->refunded_order_items, 'woocommerce' ), $this->report_data->refunded_order_items ) . ')',
'color' => $this->chart_colours['refund_amount'],
'highlight_series' => 7
'highlight_series' => 8
);
$legend[] = array(
'title' => sprintf( __( '%s charged for shipping', 'woocommerce' ), '<strong>' . wc_price( $data->total_shipping ) . '</strong>' ),
'color' => $this->chart_colours['shipping_amount'],
'highlight_series' => 4
'highlight_series' => 5
);
$legend[] = array(
'title' => sprintf( __( '%s worth of coupons used', 'woocommerce' ), '<strong>' . wc_price( $data->total_coupons ) . '</strong>' ),
'color' => $this->chart_colours['coupon_amount'],
'highlight_series' => 3
'highlight_series' => 2
);
return $legend;
@ -409,7 +421,8 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
$this->chart_colours = array(
'sales_amount' => '#b1d4ea',
'net_sales_amount' => '#3498db',
'average' => '#95a5a6',
'average' => '#b1d4ea',
'net_average' => '#3498db',
'order_count' => '#dbe1e3',
'item_count' => '#ecf0f1',
'shipping_amount' => '#5cc488',
@ -479,22 +492,27 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
$shipping_tax_amounts = $this->prepare_chart_data( $this->report_data->orders, 'post_date', 'total_shipping_tax', $this->chart_interval, $this->start_date, $this->chart_groupby );
$tax_amounts = $this->prepare_chart_data( $this->report_data->orders, 'post_date', 'total_tax', $this->chart_interval, $this->start_date, $this->chart_groupby );
$net_order_amounts = array();
$net_order_amounts = array();
$gross_order_amounts = array();
foreach ( $order_amounts as $order_amount_key => $order_amount_value ) {
$gross_order_amounts[ $order_amount_key ] = $order_amount_value;
$gross_order_amounts[ $order_amount_key ][1] = $gross_order_amounts[ $order_amount_key ][1] - $refund_amounts[ $order_amount_key ][1];
$net_order_amounts[ $order_amount_key ] = $order_amount_value;
$net_order_amounts[ $order_amount_key ][1] = $net_order_amounts[ $order_amount_key ][1] - $shipping_amounts[ $order_amount_key ][1] - $shipping_tax_amounts[ $order_amount_key ][1] - $tax_amounts[ $order_amount_key ][1];
$net_order_amounts[ $order_amount_key ][1] = $net_order_amounts[ $order_amount_key ][1] - $refund_amounts[ $order_amount_key ][1] - $shipping_amounts[ $order_amount_key ][1] - $shipping_tax_amounts[ $order_amount_key ][1] - $tax_amounts[ $order_amount_key ][1];
}
// Encode in json format
$chart_data = json_encode( array(
'order_counts' => array_values( $order_counts ),
'order_item_counts' => array_values( $order_item_counts ),
'order_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $order_amounts ) ),
'net_order_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $net_order_amounts ) ),
'shipping_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $shipping_amounts ) ),
'coupon_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $coupon_amounts ) ),
'refund_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $refund_amounts ) )
'order_counts' => array_values( $order_counts ),
'order_item_counts' => array_values( $order_item_counts ),
'order_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $order_amounts ) ),
'gross_order_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $gross_order_amounts ) ),
'net_order_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $net_order_amounts ) ),
'shipping_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $shipping_amounts ) ),
'coupon_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $coupon_amounts ) ),
'refund_amounts' => array_map( array( $this, 'round_chart_totals' ), array_values( $refund_amounts ) )
) );
?>
<div class="chart-container">
@ -525,8 +543,8 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
hoverable: false
},
{
label: "<?php echo esc_js( __( 'Average sales amount', 'woocommerce' ) ) ?>",
data: [ [ <?php echo min( array_keys( $order_amounts ) ); ?>, <?php echo $this->report_data->average_sales; ?> ], [ <?php echo max( array_keys( $order_amounts ) ); ?>, <?php echo $this->report_data->average_sales; ?> ] ],
label: "<?php echo esc_js( __( 'Average gross sales amount', 'woocommerce' ) ) ?>",
data: [ [ <?php echo min( array_keys( $order_amounts ) ); ?>, <?php echo $this->report_data->average_total_sales; ?> ], [ <?php echo max( array_keys( $order_amounts ) ); ?>, <?php echo $this->report_data->average_total_sales; ?> ] ],
yaxis: 2,
color: '<?php echo $this->chart_colours['average']; ?>',
points: { show: false },
@ -534,6 +552,16 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
shadowSize: 0,
hoverable: false
},
{
label: "<?php echo esc_js( __( 'Average net sales amount', 'woocommerce' ) ) ?>",
data: [ [ <?php echo min( array_keys( $order_amounts ) ); ?>, <?php echo $this->report_data->average_sales; ?> ], [ <?php echo max( array_keys( $order_amounts ) ); ?>, <?php echo $this->report_data->average_sales; ?> ] ],
yaxis: 2,
color: '<?php echo $this->chart_colours['net_average']; ?>',
points: { show: false },
lines: { show: true, lineWidth: 2, fill: false },
shadowSize: 0,
hoverable: false
},
{
label: "<?php echo esc_js( __( 'Coupon amount', 'woocommerce' ) ) ?>",
data: order_data.coupon_amounts,
@ -556,7 +584,7 @@ class WC_Report_Sales_By_Date extends WC_Admin_Report {
},
{
label: "<?php echo esc_js( __( 'Gross Sales amount', 'woocommerce' ) ) ?>",
data: order_data.order_amounts,
data: order_data.gross_order_amounts,
yaxis: 2,
color: '<?php echo $this->chart_colours['sales_amount']; ?>',
points: { show: true, radius: 5, lineWidth: 2, fillColor: '#fff', fill: true },

View File

@ -104,12 +104,76 @@ class WC_Settings_Tax extends WC_Settings_Page {
* Output tax rate tables
*/
public function output_tax_rates() {
global $wpdb;
global $wpdb,
$current_section;
$page = ! empty( $_GET['p'] ) ? absint( $_GET['p'] ) : 1;
$limit = 100;
$current_class = $this->get_current_tax_class();
$countries = array();
foreach ( WC()->countries->get_allowed_countries() as $value => $label ) {
$countries[] = array(
'label' => $label,
'value' => $value,
);
}
$states = array();
foreach ( WC()->countries->get_allowed_country_states() as $label ) {
foreach ( $label as $code => $state ) {
$states[] = array(
'label' => $state,
'value' => $code,
);
}
}
$base_url = admin_url( add_query_arg( array(
'page' => 'wc-settings',
'tab' => 'tax',
'section' => $current_section,
), 'admin.php' ) );
// Localize and enqueue our js.
wp_localize_script( 'wc-settings-tax', 'htmlSettingsTaxLocalizeScript', array(
'current_class' => $current_class,
'wc_tax_nonce' => wp_create_nonce( 'wc_tax_nonce-class:' . $current_class ),
'base_url' => $base_url,
'rates' => array_values( WC_Tax::get_rates_for_tax_class( $current_class ) ),
'page' => ! empty( $_GET['p'] ) ? absint( $_GET['p'] ) : 1,
'limit' => 100,
'countries' => $countries,
'states' => $states,
'default_rate' => array(
'tax_rate_id' => 0,
'tax_rate_country' => '',
'tax_rate_state' => '',
'tax_rate' => '',
'tax_rate_name' => '',
'tax_rate_priority' => 1,
'tax_rate_compound' => 0,
'tax_rate_shipping' => 1,
'tax_rate_order' => null,
'tax_rate_class' => $current_class,
),
'strings' => array(
'no_rows_selected' => __( 'No row(s) selected', 'woocommerce' ),
'unload_confirmation_msg' => __( 'Your changed data will be lost if you leave this page without saving.', 'woocommerce' ),
'csv_data_cols' => array(
__( 'Country Code', 'woocommerce' ),
__( 'State Code', 'woocommerce' ),
__( 'ZIP/Postcode', 'woocommerce' ),
__( 'City', 'woocommerce' ),
__( 'Rate %', 'woocommerce' ),
__( 'Tax Name', 'woocommerce' ),
__( 'Priority', 'woocommerce' ),
__( 'Compound', 'woocommerce' ),
__( 'Shipping', 'woocommerce' ),
__( 'Tax Class', 'woocommerce' ),
),
),
) );
wp_enqueue_script( 'wc-settings-tax' );
include( 'views/html-settings-tax.php' );
}
@ -117,7 +181,7 @@ class WC_Settings_Tax extends WC_Settings_Page {
* Get tax class being edited
* @return string
*/
private function get_current_tax_class() {
private static function get_current_tax_class() {
global $current_section;
$tax_classes = WC_Tax::get_tax_classes();

View File

@ -3,273 +3,125 @@ if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<h3><?php printf( __( 'Tax Rates for the "%s" Class', 'woocommerce' ), $current_class ? esc_html( $current_class ) : __( 'Standard', 'woocommerce' ) ); ?></h3>
<p><?php printf( __( 'Define tax rates for countries and states below. <a href="%s">See here</a> for available alpha-2 country codes.', 'woocommerce' ), 'http://en.wikipedia.org/wiki/ISO_3166-1#Current_codes' ); ?></p>
<div class="wc-tax-rates-search" id="rates-search">
<input type="search" class="wc-tax-rates-search-field" placeholder="<?php esc_attr_e( 'Search…', 'woocommerce' ); ?>" value="<?php if ( isset( $_GET['s'] ) ) { echo esc_attr( $_GET['s'] ); } ?>" />
</div>
<h3><?php printf( __( '"%s" Tax Rates', 'woocommerce' ), $current_class ? esc_html( $current_class ) : __( 'Standard', 'woocommerce' ) ); ?></h3>
<table class="wc_tax_rates wc_input_table sortable widefat">
<thead>
<tr>
<th class="sort">&nbsp;</th>
<th width="8%"><?php _e( 'Country&nbsp;Code', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php esc_attr_e('A 2 digit country code, e.g. US. Leave blank to apply to all.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><a href="http://en.wikipedia.org/wiki/ISO_3166-1#Current_codes" target="_blank"><?php _e( 'Country&nbsp;Code', 'woocommerce' ); ?></a>&nbsp;<span class="tips" data-tip="<?php esc_attr_e('A 2 digit country code, e.g. US. Leave blank to apply to all.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'State&nbsp;Code', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php esc_attr_e('A 2 digit state code, e.g. AL. Leave blank to apply to all.', 'woocommerce'); ?>">[?]</span></th>
<th><?php _e( 'ZIP/Postcode', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php esc_attr_e('Postcode for this rule. Semi-colon (;) separate multiple values. Leave blank to apply to all areas. Wildcards (*) can be used. Ranges for numeric postcodes (e.g. 12345-12350) will be expanded into individual postcodes.', 'woocommerce'); ?>">[?]</span></th>
<th><?php _e( 'City', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php esc_attr_e('Cities for this rule. Semi-colon (;) separate multiple values. Leave blank to apply to all cities.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Rate&nbsp;%', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php esc_attr_e( 'Enter a tax rate (percentage) to 4 decimal places.', 'woocommerce' ); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Tax&nbsp;Name', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php esc_attr_e('Enter a name for this tax rate.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Priority', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php esc_attr_e('Choose a priority for this tax rate. Only 1 matching rate per priority will be used. To define multiple tax rates for a single area you need to specify a different priority per rate.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Compound', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php esc_attr_e('Choose whether or not this is a compound rate. Compound tax rates are applied on top of other tax rates.', 'woocommerce'); ?>">[?]</span></th>
<th width="8%"><?php _e( 'Shipping', 'woocommerce' ); ?>&nbsp;<span class="tips" data-tip="<?php esc_attr_e('Choose whether or not this tax rate also gets applied to shipping.', 'woocommerce'); ?>">[?]</span></th>
</tr>
</thead>
<tbody id="rates">
<?php
$rates = $wpdb->get_results( $wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}woocommerce_tax_rates
WHERE tax_rate_class = %s
ORDER BY tax_rate_order
LIMIT %d, %d
" ,
sanitize_title( $current_class ),
( $page - 1 ) * $limit,
$limit
) );
foreach ( $rates as $rate ) {
?>
<tr class="tips" data-tip="<?php echo __( 'Tax rate ID', 'woocommerce' ) . ': ' . $rate->tax_rate_id; ?>">
<td class="sort"><input type="hidden" class="remove_tax_rate" name="remove_tax_rate[<?php echo $rate->tax_rate_id ?>]" value="0" /></td>
<td class="country" width="8%">
<input type="text" value="<?php echo esc_attr( $rate->tax_rate_country ) ?>" placeholder="*" name="tax_rate_country[<?php echo $rate->tax_rate_id ?>]" class="wc_input_country_iso" />
</td>
<td class="state" width="8%">
<input type="text" value="<?php echo esc_attr( $rate->tax_rate_state ) ?>" placeholder="*" name="tax_rate_state[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="postcode">
<input type="text" value="<?php
$locations = $wpdb->get_col( $wpdb->prepare( "SELECT location_code FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE location_type='postcode' AND tax_rate_id = %d ORDER BY location_code", $rate->tax_rate_id ) );
echo esc_attr( implode( '; ', $locations ) );
?>" placeholder="*" data-name="tax_rate_postcode[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="city">
<input type="text" value="<?php
$locations = $wpdb->get_col( $wpdb->prepare( "SELECT location_code FROM {$wpdb->prefix}woocommerce_tax_rate_locations WHERE location_type='city' AND tax_rate_id = %d ORDER BY location_code", $rate->tax_rate_id ) );
echo esc_attr( implode( '; ', $locations ) );
?>" placeholder="*" data-name="tax_rate_city[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="rate" width="8%">
<input type="number" step="any" min="0" value="<?php echo esc_attr( $rate->tax_rate ) ?>" placeholder="0" name="tax_rate[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="name" width="8%">
<input type="text" value="<?php echo esc_attr( $rate->tax_rate_name ) ?>" name="tax_rate_name[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="priority" width="8%">
<input type="number" step="1" min="1" value="<?php echo esc_attr( $rate->tax_rate_priority ) ?>" name="tax_rate_priority[<?php echo $rate->tax_rate_id ?>]" />
</td>
<td class="compound" width="8%">
<input type="checkbox" class="checkbox" name="tax_rate_compound[<?php echo $rate->tax_rate_id ?>]" <?php checked( $rate->tax_rate_compound, '1' ); ?> />
</td>
<td class="apply_to_shipping" width="8%">
<input type="checkbox" class="checkbox" name="tax_rate_shipping[<?php echo $rate->tax_rate_id ?>]" <?php checked($rate->tax_rate_shipping, '1' ); ?> />
</td>
</tr>
<?php
}
?>
</tbody>
<tfoot>
<tr>
<th colspan="10">
<a href="#" class="button plus insert"><?php _e( 'Insert row', 'woocommerce' ); ?></a>
<a href="#" class="button minus remove_tax_rates"><?php _e( 'Remove selected row(s)', 'woocommerce' ); ?></a>
<div class="pagination">
<?php
echo str_replace( 'page-numbers', 'page-numbers button', paginate_links( array(
'base' => esc_url_raw( add_query_arg( 'p', '%#%', remove_query_arg( 'p' ) ) ),
'format' => '',
'add_args' => '',
'type' => 'plain',
'prev_text' => '&laquo;',
'next_text' => '&raquo;',
'current' => $page,
'total' => ceil( absint( $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(tax_rate_id) FROM {$wpdb->prefix}woocommerce_tax_rates WHERE tax_rate_class = %s;", sanitize_title( $current_class ) ) ) ) / $limit )
) ) );
?>
</div>
<a href="#" download="tax_rates.csv" class="button export"><?php _e( 'Export CSV', 'woocommerce' ); ?></a>
<a href="<?php echo admin_url( 'admin.php?import=woocommerce_tax_rate_csv' ); ?>" class="button import"><?php _e( 'Import CSV', 'woocommerce' ); ?></a>
</th>
</tr>
</tfoot>
<tbody id="rates">
<tr>
<th colspan="10" style="text-align: center;"><?php esc_html_e( 'Loading…', 'woocommerce' ); ?></th>
</tr>
</tbody>
</table>
<script type="text/javascript">
jQuery( function() {
jQuery('.wc_tax_rates .remove_tax_rates').click(function() {
var $tbody = jQuery('.wc_tax_rates').find('tbody');
if ( $tbody.find('tr.current').size() > 0 ) {
$current = $tbody.find('tr.current');
$current.find('input').val('');
$current.find('input.remove_tax_rate').val('1');
$current.each(function(){
if ( jQuery(this).is('.new') )
jQuery(this).remove();
else
jQuery(this).hide();
});
} else {
alert('<?php echo esc_js( __( 'No row(s) selected', 'woocommerce' ) ); ?>');
}
return false;
});
<script type="text/html" id="tmpl-wc-tax-table-row">
<tr class="tips" data-tip="<?php echo esc_attr( sprintf( __( 'Tax rate ID: %s', 'woocommerce' ), '{{ data.tax_rate_id }}' ) ); ?>" data-id="{{ data.tax_rate_id }}">
<td class="sort"></td>
jQuery('.wc_tax_rates .export').click(function() {
<td class="country">
<input type="text" value="{{ data.tax_rate_country }}" placeholder="*" name="tax_rate_country[{{ data.tax_rate_id }}]" class="wc_input_country_iso" data-attribute="tax_rate_country" />
</td>
var csv_data = "data:application/csv;charset=utf-8,<?php echo esc_js( implode( ',', array(
__( 'Country Code', 'woocommerce' ),
__( 'State Code', 'woocommerce' ),
__( 'ZIP/Postcode', 'woocommerce' ),
__( 'City', 'woocommerce' ),
__( 'Rate %', 'woocommerce' ),
__( 'Tax Name', 'woocommerce' ),
__( 'Priority', 'woocommerce' ),
__( 'Compound', 'woocommerce' ),
__( 'Shipping', 'woocommerce' ),
__( 'Tax Class', 'woocommerce' ),
) ) ); ?>\n";
<td class="state">
<input type="text" value="{{ data.tax_rate_state }}" placeholder="*" name="tax_rate_state[{{ data.tax_rate_id }}]" data-attribute="tax_rate_state" />
</td>
jQuery('#rates').find('tr:visible').each(function() {
var row = '';
jQuery(this).find('td:not(.sort) input').each(function() {
<td class="postcode">
<input type="text" value="<# if ( data.postcode ) print( data.postcode.join( '; ' ) ); #>" placeholder="*" data-name="tax_rate_postcode[{{ data.tax_rate_id }}]" data-attribute="postcode" />
</td>
if ( jQuery(this).is('.checkbox') ) {
<td class="city">
<input type="text" value="<# if ( data.city ) print( data.city.join( '; ' ) ); #>" placeholder="*" data-name="tax_rate_city[{{ data.tax_rate_id }}]" data-attribute="city" />
</td>
if ( jQuery(this).is(':checked') ) {
val = 1;
} else {
val = 0;
}
<td class="rate">
<input type="number" step="any" min="0" value="{{ data.tax_rate }}" placeholder="0" name="tax_rate[{{ data.tax_rate_id }}]" data-attribute="tax_rate" />
</td>
} else {
<td class="name">
<input type="text" value="{{ data.tax_rate_name }}" name="tax_rate_name[{{ data.tax_rate_id }}]" data-attribute="tax_rate_name" />
</td>
var val = jQuery(this).val();
<td class="priority">
<input type="number" step="1" min="1" value="{{ data.tax_rate_priority }}" name="tax_rate_priority[{{ data.tax_rate_id }}]" data-attribute="tax_rate_priority" />
</td>
if ( ! val )
val = jQuery(this).attr('placeholder');
}
<td class="compound">
<input type="checkbox" class="checkbox" name="tax_rate_compound[{{ data.tax_rate_id }}]" <# if ( parseInt( data.tax_rate_compound, 10 ) ) { #> checked="checked" <# } #> data-attribute="tax_rate_compound" />
</td>
row = row + val + ',';
});
row = row + '<?php echo $current_class; ?>';
//row.substring( 0, row.length - 1 );
csv_data = csv_data + row + "\n";
});
jQuery(this).attr( 'href', encodeURI( csv_data ) );
return true;
});
jQuery('.wc_tax_rates .insert').click(function() {
var $tbody = jQuery('.wc_tax_rates').find('tbody');
var size = $tbody.find('tr').size();
var code = '<tr class="new">\
<td class="sort"></td>\
<td class="country" width="8%">\
<input type="text" placeholder="*" name="tax_rate_country[new-' + size + ']" class="wc_input_country_iso" />\
</td>\
<td class="state" width="8%">\
<input type="text" placeholder="*" name="tax_rate_state[new-' + size + ']" />\
</td>\
<td class="postcode">\
<input type="text" placeholder="*" name="tax_rate_postcode[new-' + size + ']" />\
</td>\
<td class="city">\
<input type="text" placeholder="*" name="tax_rate_city[new-' + size + ']" />\
</td>\
<td class="rate" width="8%">\
<input type="number" step="any" min="0" placeholder="0" name="tax_rate[new-' + size + ']" />\
</td>\
<td class="name" width="8%">\
<input type="text" name="tax_rate_name[new-' + size + ']" />\
</td>\
<td class="priority" width="8%">\
<input type="number" step="1" min="1" value="1" name="tax_rate_priority[new-' + size + ']" />\
</td>\
<td class="compound" width="8%">\
<input type="checkbox" class="checkbox" name="tax_rate_compound[new-' + size + ']" />\
</td>\
<td class="apply_to_shipping" width="8%">\
<input type="checkbox" class="checkbox" name="tax_rate_shipping[new-' + size + ']" checked="checked" />\
</td>\
</tr>';
if ( $tbody.find('tr.current').size() > 0 ) {
$tbody.find('tr.current').after( code );
} else {
$tbody.append( code );
}
jQuery( "td.country input" ).autocomplete({
source: availableCountries,
minLength: 3
});
jQuery( "td.state input" ).autocomplete({
source: availableStates,
minLength: 3
});
return false;
});
jQuery('.wc_tax_rates td.postcode, .wc_tax_rates td.city').find('input').change(function() {
jQuery(this).attr( 'name', jQuery(this).attr( 'data-name' ) );
});
var availableCountries = [<?php
$countries = array();
foreach ( WC()->countries->get_allowed_countries() as $value => $label )
$countries[] = '{ label: "' . esc_attr( $label ) . '", value: "' . $value . '" }';
echo implode( ', ', $countries );
?>];
var availableStates = [<?php
$countries = array();
foreach ( WC()->countries->get_allowed_country_states() as $value => $label )
foreach ( $label as $code => $state )
$countries[] = '{ label: "' . esc_attr( $state ) . '", value: "' . $code . '" }';
echo implode( ', ', $countries );
?>];
jQuery( "td.country input" ).autocomplete({
source: availableCountries,
minLength: 3
});
jQuery( "td.state input" ).autocomplete({
source: availableStates,
minLength: 3
});
});
<td class="apply_to_shipping">
<input type="checkbox" class="checkbox" name="tax_rate_shipping[{{ data.tax_rate_id }}]" <# if ( parseInt( data.tax_rate_shipping, 10 ) ) { #> checked="checked" <# } #> data-attribute="tax_rate_shipping" />
</td>
</tr>
</script>
<script type="text/html" id="tmpl-wc-tax-table-row-empty">
<tr>
<th colspan="10" style="text-align:center"><?php esc_html_e( 'No Matching Tax Rates Found.', 'woocommerce' ); ?></th>
</tr>
</script>
<script type="text/html" id="tmpl-wc-tax-table-pagination">
<div class="tablenav">
<div class="tablenav-pages">
<span class="displaying-num"><?php printf( _x( '%s items', '%s will be a number eventually, but must be a string for now.', 'woocommerce' ), '{{ data.qty_rates }}' ); ?></span>
<span class="pagination-links">
<a class="tablenav-pages-navspan" data-goto="1">
<span class="screen-reader-text"><?php esc_html_e( 'First page', 'woocommerce' ); ?></span>
<span aria-hidden="true">&laquo;</span>
</a>
<a class="tablenav-pages-navspan" data-goto="<# print( Math.max( 1, parseInt( data.current_page, 10 ) - 1 ) ) #>">
<span class="screen-reader-text"><?php esc_html_e( 'Previous page', 'woocommerce' ); ?></span>
<span aria-hidden="true">&lsaquo;</span>
</a>
<span class="paging-input">
<label for="current-page-selector" class="screen-reader-text"><?php esc_html_e( 'Current Page', 'woocommerce' ); ?></label>
<?php printf( esc_html_x( '%1$s of %2$s', 'Pagination, like `1 of 3`', 'woocommerce' ),
'<input class="current-page" id="current-page-selector" type="text" name="paged" value="{{ data.current_page }}" size="<# print( data.qty_pages.toString().length ) #>" aria-describedby="table-paging">',
'<span class="total-pages">{{ data.qty_pages }}</span>' ); ?>
</span>
<a class="tablenav-pages-navspan" data-goto="<# print( Math.min( data.qty_pages, parseInt( data.current_page, 10 ) + 1 ) ) #>">
<span class="screen-reader-text"><?php esc_html_e( 'Next page', 'woocommerce' ); ?></span>
<span aria-hidden="true">&rsaquo;</span>
</a>
<a class="tablenav-pages-navspan" data-goto="{{ data.qty_pages }}">
<span class="screen-reader-text"><?php esc_html_e( 'Last page', 'woocommerce' ); ?></span>
<span aria-hidden="true">&raquo;</span>
</a>
</span>
</div>
</div>
</script>

View File

@ -288,6 +288,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<tr>
<?php
$tables = array(
'woocommerce_sessions',
'woocommerce_api_keys',
'woocommerce_attribute_taxonomies',
'woocommerce_termmeta',
@ -661,11 +662,10 @@ if ( ! defined( 'ABSPATH' ) ) {
$outdated_templates = false;
foreach ( $template_paths as $plugin_name => $template_path ) {
$scanned_files[ $plugin_name ] = WC_Admin_Status::scan_template_files( $template_path );
}
foreach ( $scanned_files as $plugin_name => $files ) {
foreach ( $files as $file ) {
$scanned_files = WC_Admin_Status::scan_template_files( $template_path );
foreach ( $scanned_files as $file ) {
if ( file_exists( get_stylesheet_directory() . '/' . $file ) ) {
$theme_file = get_stylesheet_directory() . '/' . $file;
} elseif ( file_exists( get_stylesheet_directory() . '/woocommerce/' . $file ) ) {
@ -679,7 +679,7 @@ if ( ! defined( 'ABSPATH' ) ) {
}
if ( ! empty( $theme_file ) ) {
$core_version = WC_Admin_Status::get_file_version( WC()->plugin_path() . '/templates/' . $file );
$core_version = WC_Admin_Status::get_file_version( $template_path . $file );
$theme_version = WC_Admin_Status::get_file_version( $theme_file );
if ( $core_version && ( empty( $theme_version ) || version_compare( $theme_version, $core_version, '<' ) ) ) {

View File

@ -1,23 +0,0 @@
<?php
/**
* Admin View: Notice - Translation Upgrade
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<div id="message" class="updated woocommerce-message wc-connect">
<p><?php printf( __( '<strong>WooCommerce Translation Available</strong> &#8211; Install or update your <code>%s</code> translation to version <code>%s</code>.', 'woocommerce' ), get_locale(), WC_VERSION ); ?></p>
<p>
<?php if ( is_multisite() ) : ?>
<a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin.php?page=wc-status&tab=tools&action=translation_upgrade' ), 'debug_action' ) ); ?>" class="button-primary"><?php _e( 'Update Translation', 'woocommerce' ); ?></a>
<?php else : ?>
<a href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'action' => 'do-translation-upgrade' ), admin_url( 'update-core.php' ) ), 'upgrade-translations' ) ); ?>" class="button-primary"><?php _e( 'Update Translation', 'woocommerce' ); ?></a>
<a href="<?php echo esc_url( wp_nonce_url( admin_url( 'admin.php?page=wc-status&tab=tools&action=translation_upgrade' ), 'debug_action' ) ); ?>" class="button-secondary"><?php _e( 'Force Update Translation', 'woocommerce' ); ?></a>
<?php endif; ?>
<a href="<?php echo esc_url( wp_nonce_url( add_query_arg( 'wc-hide-notice', 'translation_upgrade' ), 'woocommerce_hide_notices_nonce', '_wc_notice_nonce' ) ); ?>" class="button-secondary skip"><?php _e( 'Hide This Message', 'woocommerce' ); ?></a>
</p>
</div>

View File

@ -874,7 +874,8 @@ class WC_API_Orders extends WC_API_Resource {
*/
protected function set_line_item( $order, $item, $action ) {
$creating = ( 'create' === $action );
$creating = ( 'create' === $action );
$item_args = array();
// product is always required
if ( ! isset( $item['product_id'] ) && ! isset( $item['sku'] ) ) {
@ -927,8 +928,6 @@ class WC_API_Orders extends WC_API_Resource {
throw new WC_API_Exception( 'woocommerce_api_invalid_product_quantity', __( 'Product quantity is required', 'woocommerce' ), 400 );
}
$item_args = array();
// quantity
if ( isset( $item['quantity'] ) ) {
$item_args['qty'] = $item['quantity'];

View File

@ -1040,6 +1040,8 @@ class WC_API_Products extends WC_API_Resource {
'value' => $args['sku'],
'compare' => '='
);
$query_args['post_type'] = array( 'product', 'product_variation' );
}
$query_args = $this->merge_query_args( $query_args, $args );
@ -1055,8 +1057,6 @@ class WC_API_Products extends WC_API_Resource {
* @return WC_Product
*/
private function get_product_data( $product ) {
$prices_precision = wc_get_price_decimals();
return array(
'title' => $product->get_title(),
'id' => (int) $product->is_type( 'variation' ) ? $product->get_variation_id() : $product->id,
@ -1068,9 +1068,9 @@ class WC_API_Products extends WC_API_Resource {
'virtual' => $product->is_virtual(),
'permalink' => $product->get_permalink(),
'sku' => $product->get_sku(),
'price' => wc_format_decimal( $product->get_price(), $prices_precision ),
'regular_price' => wc_format_decimal( $product->get_regular_price(), $prices_precision ),
'sale_price' => $product->get_sale_price() ? wc_format_decimal( $product->get_sale_price(), $prices_precision ) : null,
'price' => $product->get_price(),
'regular_price' => $product->get_regular_price(),
'sale_price' => $product->get_sale_price() ? $product->get_sale_price() : null,
'price_html' => $product->get_price_html(),
'taxable' => $product->is_taxable(),
'tax_status' => $product->get_tax_status(),
@ -1088,7 +1088,7 @@ class WC_API_Products extends WC_API_Resource {
'on_sale' => $product->is_on_sale(),
'product_url' => $product->is_type( 'external' ) ? $product->get_product_url() : '',
'button_text' => $product->is_type( 'external' ) ? $product->get_button_text() : '',
'weight' => $product->get_weight() ? wc_format_decimal( $product->get_weight(), 2 ) : null,
'weight' => $product->get_weight() ? $product->get_weight() : null,
'dimensions' => array(
'length' => $product->length,
'width' => $product->width,
@ -1133,8 +1133,7 @@ class WC_API_Products extends WC_API_Resource {
* @return array
*/
private function get_variation_data( $product ) {
$prices_precision = wc_get_price_decimals();
$variations = array();
$variations = array();
foreach ( $product->get_children() as $child_id ) {
@ -1152,9 +1151,9 @@ class WC_API_Products extends WC_API_Resource {
'virtual' => $variation->is_virtual(),
'permalink' => $variation->get_permalink(),
'sku' => $variation->get_sku(),
'price' => wc_format_decimal( $variation->get_price(), $prices_precision ),
'regular_price' => wc_format_decimal( $variation->get_regular_price(), $prices_precision ),
'sale_price' => $variation->get_sale_price() ? wc_format_decimal( $variation->get_sale_price(), $prices_precision ) : null,
'price' => $variation->get_price(),
'regular_price' => $variation->get_regular_price(),
'sale_price' => $variation->get_sale_price() ? $variation->get_sale_price() : null,
'taxable' => $variation->is_taxable(),
'tax_status' => $variation->get_tax_status(),
'tax_class' => $variation->get_tax_class(),
@ -1165,7 +1164,7 @@ class WC_API_Products extends WC_API_Resource {
'purchaseable' => $variation->is_purchasable(),
'visible' => $variation->variation_is_visible(),
'on_sale' => $variation->is_on_sale(),
'weight' => $variation->get_weight() ? wc_format_decimal( $variation->get_weight(), 2 ) : null,
'weight' => $variation->get_weight() ? $variation->get_weight() : null,
'dimensions' => array(
'length' => $variation->length,
'width' => $variation->width,

View File

@ -43,7 +43,7 @@ class WC_API_Resource {
$response_names = array( 'order', 'coupon', 'customer', 'product', 'report',
'customer_orders', 'customer_downloads', 'order_note', 'order_refund',
'product_reviews', 'product_category'
'product_reviews', 'product_category', 'tax', 'tax_class'
);
foreach ( $response_names as $name ) {

View File

@ -560,7 +560,7 @@ class WC_API_Server {
* Send pagination headers for resources
*
* @since 2.1
* @param WP_Query|WP_User_Query $query
* @param WP_Query|WP_User_Query|stdClass $query
*/
public function add_pagination_headers( $query ) {
@ -577,6 +577,11 @@ class WC_API_Server {
$page = 1;
$total_pages = 1;
}
} else if ( is_a( $query, 'stdClass' ) ) {
$page = $query->page;
$single = $query->is_single;
$total = $query->total;
$total_pages = $query->total_pages;
// WP_Query
} else {

View File

@ -0,0 +1,678 @@
<?php
/**
* WooCommerce API Taxes Class
*
* Handles requests to the /taxes endpoint
*
* @author WooThemes
* @category API
* @package WooCommerce/API
* @since 2.5.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WC_API_Taxes extends WC_API_Resource {
/** @var string $base the route base */
protected $base = '/taxes';
/**
* Register the routes for this class
*
* GET /taxes
* GET /taxes/count
* GET /taxes/<id>
*
* @since 2.1
* @param array $routes
* @return array
*/
public function register_routes( $routes ) {
# GET/POST /taxes
$routes[ $this->base ] = array(
array( array( $this, 'get_taxes' ), WC_API_Server::READABLE ),
array( array( $this, 'create_tax' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
);
# GET /taxes/count
$routes[ $this->base . '/count'] = array(
array( array( $this, 'get_taxes_count' ), WC_API_Server::READABLE ),
);
# GET/PUT/DELETE /taxes/<id>
$routes[ $this->base . '/(?P<id>\d+)' ] = array(
array( array( $this, 'get_tax' ), WC_API_Server::READABLE ),
array( array( $this, 'edit_tax' ), WC_API_SERVER::EDITABLE | WC_API_SERVER::ACCEPT_DATA ),
array( array( $this, 'delete_tax' ), WC_API_SERVER::DELETABLE ),
);
# GET/POST /taxes/classes
$routes[ $this->base . '/classes' ] = array(
array( array( $this, 'get_tax_classes' ), WC_API_Server::READABLE ),
array( array( $this, 'create_tax_class' ), WC_API_Server::CREATABLE | WC_API_Server::ACCEPT_DATA ),
);
# GET /taxes/classes/count
$routes[ $this->base . '/classes/count'] = array(
array( array( $this, 'get_tax_classes_count' ), WC_API_Server::READABLE ),
);
# GET /taxes/classes/<slug>
$routes[ $this->base . '/classes/(?P<slug>\w[\w\s\-]*)' ] = array(
array( array( $this, 'delete_tax_class' ), WC_API_SERVER::DELETABLE ),
);
# POST|PUT /taxes/bulk
$routes[ $this->base . '/bulk' ] = array(
array( array( $this, 'bulk' ), WC_API_Server::EDITABLE | WC_API_Server::ACCEPT_DATA ),
);
return $routes;
}
/**
* Get all taxes
*
* @since 2.5.0
*
* @param string $fields
* @param array $filter
* @param string $class
* @param int $page
*
* @return array
*/
public function get_taxes( $fields = null, $filter = array(), $class = null, $page = 1 ) {
if ( ! empty( $class ) ) {
$filter['tax_rate_class'] = $class;
}
$filter['page'] = $page;
$query = $this->query_tax_rates( $filter );
$taxes = array();
foreach ( $query['results'] as $tax ) {
$taxes[] = current( $this->get_tax( $tax->tax_rate_id, $fields ) );
}
// Set pagination headers
$this->server->add_pagination_headers( $query['headers'] );
return array( 'taxes' => $taxes );
}
/**
* Get the tax for the given ID
*
* @since 2.5.0
*
* @param int $id The tax ID
* @param string $fields fields to include in response
*
* @return array|WP_Error
*/
public function get_tax( $id, $fields = null ) {
global $wpdb;
try {
$id = absint( $id );
// Permissions check
if ( ! current_user_can( 'manage_woocommerce' ) ) {
throw new WC_API_Exception( 'woocommerce_api_user_cannot_read_tax', __( 'You do not have permission to read tax rate', 'woocommerce' ), 401 );
}
// Get tax rate details
$tax = WC_Tax::_get_tax_rate( $id );
if ( is_wp_error( $tax ) || empty( $tax ) ) {
throw new WC_API_Exception( 'woocommerce_api_invalid_tax_id', __( 'A tax rate with the provided ID could not be found', 'woocommerce' ), 404 );
}
$tax_data = array(
'id' => $tax['tax_rate_id'],
'country' => $tax['tax_rate_country'],
'state' => $tax['tax_rate_state'],
'postcode' => '',
'city' => '',
'rate' => $tax['tax_rate'],
'name' => $tax['tax_rate_name'],
'priority' => (int) $tax['tax_rate_priority'],
'compound' => (bool) $tax['tax_rate_compound'],
'shipping' => (bool) $tax['tax_rate_shipping'],
'order' => (int) $tax['tax_rate_order'],
'class' => $tax['tax_rate_class'] ? $tax['tax_rate_class'] : 'standard'
);
// Get locales from a tax rate
$locales = $wpdb->get_results( $wpdb->prepare( "
SELECT location_code, location_type
FROM {$wpdb->prefix}woocommerce_tax_rate_locations
WHERE tax_rate_id = %d
", $id ) );
if ( ! is_wp_error( $tax ) && ! is_null( $tax ) ) {
foreach ( $locales as $locale ) {
$tax_data[ $locale->location_type ] = $locale->location_code;
}
}
return array( 'tax' => apply_filters( 'woocommerce_api_tax_response', $tax_data, $tax, $fields, $this ) );
} catch ( WC_API_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
}
}
/**
* Create a tax
*
* @since 2.5.0
*
* @param array $data
*
* @return array
*/
public function create_tax( $data ) {
try {
if ( ! isset( $data['tax'] ) ) {
throw new WC_API_Exception( 'woocommerce_api_missing_tax_data', sprintf( __( 'No %1$s data specified to create %1$s', 'woocommerce' ), 'tax' ), 400 );
}
// Check permissions
if ( ! current_user_can( 'manage_woocommerce' ) ) {
throw new WC_API_Exception( 'woocommerce_api_user_cannot_create_tax', __( 'You do not have permission to create tax rates', 'woocommerce' ), 401 );
}
$data = apply_filters( 'woocommerce_api_create_tax_data', $data['tax'], $this );
$tax_data = array(
'tax_rate_country' => '',
'tax_rate_state' => '',
'tax_rate' => '',
'tax_rate_name' => '',
'tax_rate_priority' => 1,
'tax_rate_compound' => 0,
'tax_rate_shipping' => 1,
'tax_rate_order' => 0,
'tax_rate_class' => '',
);
foreach ( $tax_data as $key => $value ) {
$new_key = str_replace( 'tax_rate_', '', $key );
$new_key = 'tax_rate' === $new_key ? 'rate' : $new_key;
if ( isset( $data[ $new_key ] ) ) {
if ( in_array( $new_key, array( 'compound', 'shipping' ) ) ) {
$tax_data[ $key ] = $data[ $new_key ] ? 1 : 0;
} else {
$tax_data[ $key ] = $data[ $new_key ];
}
}
}
// Create tax rate
$id = WC_Tax::_insert_tax_rate( $tax_data );
// Add locales
if ( ! empty( $data['postcode'] ) ) {
WC_Tax::_update_tax_rate_postcodes( $id, wc_clean( $data['postcode'] ) );
}
if ( ! empty( $data['city'] ) ) {
WC_Tax::_update_tax_rate_cities( $id, wc_clean( $data['city'] ) );
}
do_action( 'woocommerce_api_create_tax_rate', $id, $data );
$this->server->send_status( 201 );
return $this->get_tax( $id );
} catch ( WC_API_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
}
}
/**
* Edit a tax
*
* @since 2.5.0
*
* @param int $id The tax ID
* @param array $data
*
* @return array
*/
public function edit_tax( $id, $data ) {
try {
if ( ! isset( $data['tax'] ) ) {
throw new WC_API_Exception( 'woocommerce_api_missing_tax_data', sprintf( __( 'No %1$s data specified to edit %1$s', 'woocommerce' ), 'tax' ), 400 );
}
// Check permissions
if ( ! current_user_can( 'manage_woocommerce' ) ) {
throw new WC_API_Exception( 'woocommerce_api_user_cannot_edit_tax', __( 'You do not have permission to edit tax rates', 'woocommerce' ), 401 );
}
$data = $data['tax'];
// Get current tax rate data
$tax = $this->get_tax( $id );
if ( is_wp_error( $tax ) ) {
$error_data = $tax->get_error_data();
throw new WC_API_Exception( $tax->get_error_code(), $tax->get_error_message(), $error_data['status'] );
}
$current_data = $tax['tax'];
$data = apply_filters( 'woocommerce_api_edit_tax_data', $data, $this );
$tax_data = array();
$default_fields = array(
'tax_rate_country',
'tax_rate_state',
'tax_rate',
'tax_rate_name',
'tax_rate_priority',
'tax_rate_compound',
'tax_rate_shipping',
'tax_rate_order',
'tax_rate_class'
);
foreach ( $data as $key => $value ) {
$new_key = 'rate' === $key ? 'tax_rate' : 'tax_rate_' . $key;
// Check if the key is valid
if ( ! in_array( $new_key, $default_fields ) ) {
continue;
}
// Test new data against current data
if ( $value === $current_data[ $key ] ) {
continue;
}
// Fix compund and shipping values
if ( in_array( $key, array( 'compound', 'shipping' ) ) ) {
$value = $value ? 1 : 0;
}
$tax_data[ $new_key ] = $value;
}
// Update tax rate
WC_Tax::_update_tax_rate( $id, $tax_data );
// Update locales
if ( ! empty( $data['postcode'] ) && $current_data['postcode'] != $data['postcode'] ) {
WC_Tax::_update_tax_rate_postcodes( $id, wc_clean( $data['postcode'] ) );
}
if ( ! empty( $data['city'] ) && $current_data['city'] != $data['city'] ) {
WC_Tax::_update_tax_rate_cities( $id, wc_clean( $data['city'] ) );
}
do_action( 'woocommerce_api_edit_product', $id, $data );
// Clear cache/transients
wc_delete_product_transients( $id );
return $this->get_tax( $id );
} catch ( WC_API_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
}
}
/**
* Delete a tax
*
* @since 2.5.0
*
* @param int $id The tax ID
*
* @return array
*/
public function delete_tax( $id ) {
global $wpdb;
try {
// Check permissions
if ( ! current_user_can( 'manage_woocommerce' ) ) {
throw new WC_API_Exception( 'woocommerce_api_user_cannot_delete_tax', __( 'You do not have permission to delete tax rates', 'woocommerce' ), 401 );
}
$id = absint( $id );
WC_Tax::_delete_tax_rate( $id );
if ( 0 === $wpdb->rows_affected ) {
throw new WC_API_Exception( 'woocommerce_api_cannot_delete_tax', __( 'Could not delete the tax rate', 'woocommerce' ), 401 );
}
return array( 'message' => sprintf( __( 'Deleted %s', 'woocommerce' ), 'tax' ) );
} catch ( WC_API_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
}
}
/**
* Get the total number of taxes
*
* @since 2.5.0
*
* @param string $class
* @param array $filter
*
* @return array
*/
public function get_taxes_count( $class = null, $filter = array() ) {
try {
if ( ! current_user_can( 'manage_woocommerce' ) ) {
throw new WC_API_Exception( 'woocommerce_api_user_cannot_read_taxes_count', __( 'You do not have permission to read the taxes count', 'woocommerce' ), 401 );
}
if ( ! empty( $class ) ) {
$filter['tax_rate_class'] = $class;
}
$query = $this->query_tax_rates( $filter, true );
return array( 'count' => (int) $query['headers']->total );
} catch ( WC_API_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
}
}
/**
* Helper method to get tax rates objects
*
* @since 2.5.0
*
* @param array $args
*
* @return array
*/
protected function query_tax_rates( $args, $count_only = false ) {
global $wpdb;
$results = '';
// Set args
$args = $this->merge_query_args( $args, array() );
$query = "
SELECT tax_rate_id
FROM {$wpdb->prefix}woocommerce_tax_rates
WHERE 1 = 1
";
// Filter by tax class
if ( ! empty( $args['tax_rate_class'] ) ) {
$tax_rate_class = 'standard' !== $args['tax_rate_class'] ? sanitize_title( $args['tax_rate_class'] ) : '';
$query .= " AND tax_rate_class = '$tax_rate_class'";
}
// Order tax rates
$order_by = ' ORDER BY tax_rate_order';
// Pagination
$per_page = isset( $args['posts_per_page'] ) ? $args['posts_per_page'] : get_option( 'posts_per_page' );
$offset = 1 < $args['paged'] ? ( $args['paged'] - 1 ) * $per_page : 0;
$pagination = sprintf( ' LIMIT %d, %d', $offset, $per_page );
if ( ! $count_only ) {
$results = $wpdb->get_results( $query . $order_by . $pagination );
}
$wpdb->get_results( $query );
$headers = new stdClass;
$headers->page = $args['paged'];
$headers->total = (int) $wpdb->num_rows;
$headers->is_single = $per_page > $headers->total;
$headers->total_pages = ceil( $headers->total / $per_page );
return array(
'results' => $results,
'headers' => $headers
);
}
/**
* Bulk update or insert taxes
* Accepts an array with taxes in the formats supported by
* WC_API_Taxes->create_tax() and WC_API_Taxes->edit_tax()
*
* @since 2.5.0
*
* @param array $data
*
* @return array
*/
public function bulk( $data ) {
try {
if ( ! isset( $data['taxes'] ) ) {
throw new WC_API_Exception( 'woocommerce_api_missing_taxes_data', sprintf( __( 'No %1$s data specified to create/edit %1$s', 'woocommerce' ), 'taxes' ), 400 );
}
$data = $data['taxes'];
$limit = apply_filters( 'woocommerce_api_bulk_limit', 100, 'taxes' );
// Limit bulk operation
if ( count( $data ) > $limit ) {
throw new WC_API_Exception( 'woocommerce_api_taxes_request_entity_too_large', sprintf( __( 'Unable to accept more than %s items for this request', 'woocommerce' ), $limit ), 413 );
}
$taxes = array();
foreach ( $data as $_tax ) {
$tax_id = 0;
// Try to get the tax rate ID
if ( isset( $_tax['id'] ) ) {
$tax_id = intval( $_tax['id'] );
}
// Tax rate exists / edit tax rate
if ( $tax_id ) {
$edit = $this->edit_tax( $tax_id, array( 'tax' => $_tax ) );
if ( is_wp_error( $edit ) ) {
$taxes[] = array(
'id' => $tax_id,
'error' => array( 'code' => $edit->get_error_code(), 'message' => $edit->get_error_message() )
);
} else {
$taxes[] = $edit['tax'];
}
}
// Tax rate don't exists / create tax rate
else {
$new = $this->create_tax( array( 'tax' => $_tax ) );
if ( is_wp_error( $new ) ) {
$taxes[] = array(
'id' => $tax_id,
'error' => array( 'code' => $new->get_error_code(), 'message' => $new->get_error_message() )
);
} else {
$taxes[] = $new['tax'];
}
}
}
return array( 'taxes' => apply_filters( 'woocommerce_api_taxes_bulk_response', $orders, $this ) );
} catch ( WC_API_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
}
}
/**
* Get all tax classes
*
* @since 2.5.0
*
* @param string $fields
*
* @return array
*/
public function get_tax_classes( $fields = null ) {
try {
// Permissions check
if ( ! current_user_can( 'manage_woocommerce' ) ) {
throw new WC_API_Exception( 'woocommerce_api_user_cannot_read_tax_classes', __( 'You do not have permission to read tax classes', 'woocommerce' ), 401 );
}
$tax_classes = array();
// Add standard class
$tax_classes[] = array(
'slug' => 'standard',
'name' => __( 'Standard Rate', 'woocommerce' )
);
$classes = WC_Tax::get_tax_classes();
foreach ( $classes as $class ) {
$tax_classes[] = apply_filters( 'woocommerce_api_tax_class_response', array(
'slug' => sanitize_title( $class ),
'name' => $class
), $class, $fields, $this );
}
return array( 'tax_classes' => apply_filters( 'woocommerce_api_tax_classes_response', $tax_classes, $classes, $fields, $this ) );
} catch ( WC_API_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
}
}
/**
* Create a tax class
*
* @since 2.5.0
*
* @param array $data
*
* @return array
*/
public function create_tax_class( $data ) {
try {
if ( ! isset( $data['tax_class'] ) ) {
throw new WC_API_Exception( 'woocommerce_api_missing_tax_class_data', sprintf( __( 'No %1$s data specified to create %1$s', 'woocommerce' ), 'tax_class' ), 400 );
}
// Check permissions
if ( ! current_user_can( 'manage_woocommerce' ) ) {
throw new WC_API_Exception( 'woocommerce_api_user_cannot_create_tax_class', __( 'You do not have permission to create tax classes', 'woocommerce' ), 401 );
}
$data = $data['tax_class'];
if ( empty( $data['name'] ) ) {
throw new WC_API_Exception( 'woocommerce_api_missing_tax_class_name', sprintf( __( 'Missing parameter %s', 'woocommerce' ), 'name' ), 400 );
}
$name = sanitize_text_field( $data['name'] );
$slug = sanitize_title( $name );
$classes = WC_Tax::get_tax_classes();
$exists = false;
// Check if class exists
foreach ( $classes as $key => $class ) {
if ( sanitize_title( $class ) === $slug ) {
$exists = true;
break;
}
}
// Return error if tax class already exists
if ( $exists ) {
throw new WC_API_Exception( 'woocommerce_api_cannot_create_tax_class', __( 'Tax class already exists', 'woocommerce' ), 401 );
}
// Add the new class
$classes[] = $name;
update_option( 'woocommerce_tax_classes', implode( "\n", $classes ) );
do_action( 'woocommerce_api_create_tax_class', $slug, $data );
$this->server->send_status( 201 );
return array(
'tax_class' => array(
'slug' => $slug,
'name' => $name
)
);
} catch ( WC_API_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
}
}
/**
* Delete a tax class
*
* @since 2.5.0
*
* @param int $slug The tax class slug
*
* @return array
*/
public function delete_tax_class( $slug ) {
try {
// Check permissions
if ( ! current_user_can( 'manage_woocommerce' ) ) {
throw new WC_API_Exception( 'woocommerce_api_user_cannot_delete_tax_class', __( 'You do not have permission to delete tax classes', 'woocommerce' ), 401 );
}
$slug = sanitize_title( $slug );
$classes = WC_Tax::get_tax_classes();
$deleted = false;
foreach ( $classes as $key => $class ) {
if ( sanitize_title( $class ) === $slug ) {
unset( $classes[ $key ] );
$deleted = true;
break;
}
}
if ( ! $deleted ) {
throw new WC_API_Exception( 'woocommerce_api_cannot_delete_tax_class', __( 'Could not delete the tax class', 'woocommerce' ), 401 );
}
update_option( 'woocommerce_tax_classes', implode( "\n", $classes ) );
return array( 'message' => sprintf( __( 'Deleted %s', 'woocommerce' ), 'tax_class' ) );
} catch ( WC_API_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
}
}
/**
* Get the total number of tax classes
*
* @since 2.5.0
*
* @return array
*/
public function get_tax_classes_count() {
try {
if ( ! current_user_can( 'manage_woocommerce' ) ) {
throw new WC_API_Exception( 'woocommerce_api_user_cannot_read_tax_classes_count', __( 'You do not have permission to read the tax classes count', 'woocommerce' ), 401 );
}
$total = count( WC_Tax::get_tax_classes() ) + 1; // +1 for Standard Rate
return array( 'count' => $total );
} catch ( WC_API_Exception $e ) {
return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
}
}
}

View File

@ -866,7 +866,8 @@ class WC_API_Orders extends WC_API_Resource {
*/
protected function set_line_item( $order, $item, $action ) {
$creating = ( 'create' === $action );
$creating = ( 'create' === $action );
$item_args = array();
// product is always required
if ( ! isset( $item['product_id'] ) && ! isset( $item['sku'] ) ) {
@ -919,8 +920,6 @@ class WC_API_Orders extends WC_API_Resource {
throw new WC_API_Exception( 'woocommerce_api_invalid_product_quantity', __( 'Product quantity is required', 'woocommerce' ), 400 );
}
$item_args = array();
// quantity
if ( isset( $item['quantity'] ) ) {
$item_args['qty'] = $item['quantity'];

View File

@ -610,6 +610,8 @@ class WC_API_Products extends WC_API_Resource {
'value' => $args['sku'],
'compare' => '='
);
$query_args['post_type'] = array( 'product', 'product_variation' );
}
$query_args = $this->merge_query_args( $query_args, $args );

View File

@ -130,7 +130,8 @@ class WC_AJAX {
'get_customer_location' => true,
'load_variations' => false,
'save_variations' => false,
'bulk_edit_variations' => false
'bulk_edit_variations' => false,
'tax_rates_save_changes' => false,
);
foreach ( $ajax_events as $ajax_event => $nopriv ) {
@ -1414,7 +1415,8 @@ class WC_AJAX {
if ( $_product->exists() && $_product->managing_stock() && isset( $order_item_qty[ $item_id ] ) && $order_item_qty[ $item_id ] > 0 ) {
$stock_change = apply_filters( 'woocommerce_reduce_order_stock_quantity', $order_item_qty[ $item_id ], $item_id );
$new_stock = $_product->reduce_stock( $stock_change );
$note = sprintf( __( 'Item #%s stock reduced from %s to %s.', 'woocommerce' ), $order_item['product_id'], $new_stock + $stock_change, $new_stock );
$item_name = $_product->get_sku() ? $_product->get_sku() : $order_item['product_id'];
$note = sprintf( __( 'Item %s stock reduced from %s to %s.', 'woocommerce' ), $item_name, $new_stock + $stock_change, $new_stock );
$return[] = $note;
$order->add_order_note( $note );
@ -1463,10 +1465,11 @@ class WC_AJAX {
$_product = $order->get_product_from_item( $order_item );
if ( $_product->exists() && $_product->managing_stock() && isset( $order_item_qty[ $item_id ] ) && $order_item_qty[ $item_id ] > 0 ) {
$old_stock = $_product->stock;
$old_stock = $_product->get_stock_quantity();
$stock_change = apply_filters( 'woocommerce_restore_order_stock_quantity', $order_item_qty[ $item_id ], $item_id );
$new_quantity = $_product->increase_stock( $stock_change );
$note = sprintf( __( 'Item #%s stock increased from %s to %s.', 'woocommerce' ), $order_item['product_id'], $old_stock, $new_quantity );
$item_name = $_product->get_sku() ? $_product->get_sku(): $order_item['product_id'];
$note = sprintf( __( 'Item %s stock increased from %s to %s.', 'woocommerce' ), $item_name, $old_stock, $new_quantity );
$return[] = $note;
$order->add_order_note( $note );
@ -1536,6 +1539,7 @@ class WC_AJAX {
}
$tax = new WC_Tax();
$tax_based_on = get_option( 'woocommerce_tax_based_on' );
$order_id = absint( $_POST['order_id'] );
$items = array();
$country = strtoupper( esc_attr( $_POST['country'] ) );
@ -1546,6 +1550,15 @@ class WC_AJAX {
$taxes = array();
$shipping_taxes = array();
// Default to base
if ( 'base' === $tax_based_on || empty( $country ) ) {
$default = wc_get_base_location();
$country = $default['country'];
$state = $default['state'];
$postcode = '';
$city = '';
}
// Parse the jQuery serialized items
parse_str( $_POST['items'], $items );
@ -1781,6 +1794,8 @@ class WC_AJAX {
* @param string $post_types (default: array('product'))
*/
public static function json_search_products( $x = '', $post_types = array( 'product' ) ) {
global $wpdb;
ob_start();
check_ajax_referer( 'search-products', 'security' );
@ -1792,80 +1807,42 @@ class WC_AJAX {
die();
}
if ( ! empty( $_GET['exclude'] ) ) {
$exclude = array_map( 'intval', explode( ',', $_GET['exclude'] ) );
}
$args = array(
'post_type' => $post_types,
'post_status' => 'publish',
'posts_per_page' => -1,
's' => $term,
'fields' => 'ids',
'exclude' => $exclude
);
$like_term = '%' . $wpdb->esc_like( $term ) . '%';
if ( is_numeric( $term ) ) {
if ( false === array_search( $term, $exclude ) ) {
$posts2 = get_posts( array(
'post_type' => $post_types,
'post_status' => 'publish',
'posts_per_page' => -1,
'post__in' => array( 0, $term ),
'fields' => 'ids'
) );
} else {
$posts2 = array();
}
$posts3 = get_posts( array(
'post_type' => $post_types,
'post_status' => 'publish',
'posts_per_page' => -1,
'post_parent' => $term,
'fields' => 'ids',
'exclude' => $exclude
) );
$posts4 = get_posts( array(
'post_type' => $post_types,
'post_status' => 'publish',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => '_sku',
'value' => $term,
'compare' => 'LIKE'
$query = $wpdb->prepare( "
SELECT ID FROM {$wpdb->posts} posts LEFT JOIN {$wpdb->postmeta} postmeta ON posts.ID = postmeta.post_id
WHERE posts.post_status = 'publish'
AND (
posts.post_parent = %s
OR posts.ID = %s
OR posts.post_title LIKE %s
OR (
postmeta.meta_key = '_sku' AND postmeta.meta_value LIKE %s
)
),
'fields' => 'ids',
'exclude' => $exclude
) );
$posts = array_unique( array_merge( get_posts( $args ), $posts2, $posts3, $posts4 ) );
)
", $term, $term, $term, $like_term );
} else {
$args2 = array(
'post_type' => $post_types,
'post_status' => 'publish',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => '_sku',
'value' => $term,
'compare' => 'LIKE'
$query = $wpdb->prepare( "
SELECT ID FROM {$wpdb->posts} posts LEFT JOIN {$wpdb->postmeta} postmeta ON posts.ID = postmeta.post_id
WHERE posts.post_status = 'publish'
AND (
posts.post_title LIKE %s
or posts.post_content LIKE %s
OR (
postmeta.meta_key = '_sku' AND postmeta.meta_value LIKE %s
)
),
'fields' => 'ids',
'exclude' => $exclude
);
$posts = array_unique( array_merge( get_posts( $args ), get_posts( $args2 ) ) );
)
", $like_term, $like_term, $like_term );
}
$query .= " AND posts.post_type IN ('" . implode( "','", array_map( 'esc_sql', $post_types ) ) . "')";
if ( ! empty( $_GET['exclude'] ) ) {
$query .= " AND posts.ID NOT IN (" . implode( ',', array_map( 'intval', explode( ',', $_GET['exclude'] ) ) ) . ")";
}
$posts = array_unique( $wpdb->get_col( $query ) );
$found_products = array();
if ( ! empty( $posts ) ) {
@ -2242,7 +2219,7 @@ class WC_AJAX {
'amount' => $refund_amount,
'reason' => $refund_reason,
'order_id' => $order_id,
'line_items' => $line_items
'line_items' => $line_items,
) );
if ( is_wp_error( $refund ) ) {
@ -2284,10 +2261,18 @@ class WC_AJAX {
}
}
// Check if items are refunded fully
$max_remaining_items = absint( $order->get_item_count() - $order->get_item_count_refunded() );
// Trigger notifications and status changes
if ( $order->get_remaining_refund_amount() > 0 || $order->get_remaining_refund_items() > 0 ) {
/**
* woocommerce_order_partially_refunded
*
* @since 2.4.0
* Note: 3rd arg was added in err. Kept for bw compat. 2.4.3
*/
do_action( 'woocommerce_order_partially_refunded', $order_id, $refund->id, $refund->id );
} else {
do_action( 'woocommerce_order_fully_refunded', $order_id, $refund->id );
if ( $refund_amount == $max_refund && 0 === $max_remaining_items ) {
$order->update_status( apply_filters( 'woocommerce_order_fully_refunded_status', 'refunded', $order_id, $refund->id ) );
$response_data['status'] = 'fully_refunded';
}
@ -2296,7 +2281,6 @@ class WC_AJAX {
// Clear transients
wc_delete_shop_order_transients( $order_id );
wp_send_json_success( $response_data );
} catch ( Exception $e ) {
@ -3016,6 +3000,76 @@ class WC_AJAX {
wc_delete_product_transients( $product_id );
die();
}
/**
* Handle submissions from assets/js/settings-views-html-settings-tax.js Backbone model.
*/
public static function tax_rates_save_changes() {
if ( ! isset( $_POST['current_class'], $_POST['wc_tax_nonce'], $_POST['changes'] ) ) {
wp_send_json_error( 'missing_fields' );
exit;
}
$current_class = $_POST['current_class']; // This is sanitized seven lines later.
if ( ! wp_verify_nonce( $_POST['wc_tax_nonce'], 'wc_tax_nonce-class:' . $current_class ) ) {
wp_send_json_error( 'bad_nonce' );
exit;
}
$current_class = WC_Tax::format_tax_rate_class( $current_class );
// Check User Caps
if ( ! current_user_can( 'manage_woocommerce' ) ) {
wp_send_json_error( 'missing_capabilities' );
exit;
}
$changes = $_POST['changes'];
foreach ( $changes as $tax_rate_id => $data ) {
if ( isset( $data['deleted'] ) ) {
if ( isset( $data['newRow'] ) ) {
// So the user added and deleted a new row.
// That's fine, it's not in the database anyways. NEXT!
continue;
}
WC_Tax::_delete_tax_rate( $tax_rate_id );
}
$tax_rate = array_intersect_key( $data, array(
'tax_rate_country' => 1,
'tax_rate_state' => 1,
'tax_rate' => 1,
'tax_rate_name' => 1,
'tax_rate_priority' => 1,
'tax_rate_compound' => 1,
'tax_rate_shipping' => 1,
'tax_rate_order' => 1,
) );
if ( isset( $data['newRow'] ) ) {
// Hurrah, shiny and new!
$tax_rate['tax_rate_class'] = $current_class;
$tax_rate_id = WC_Tax::_insert_tax_rate( $tax_rate );
} else {
// Updating an existing rate ...
if ( ! empty( $tax_rate ) ) {
WC_Tax::_update_tax_rate( $tax_rate_id, $tax_rate );
}
}
if ( isset( $data['postcode'] ) ) {
WC_Tax::_update_tax_rate_postcodes( $tax_rate_id, array_map( 'wc_clean', $data['postcode'] ) );
}
if ( isset( $data['city'] ) ) {
WC_Tax::_update_tax_rate_cities( $tax_rate_id, array_map( 'wc_clean', $data['city'] ) );
}
}
wp_send_json_success( array(
'rates' => WC_Tax::get_rates_for_tax_class( $current_class ),
) );
}
}
WC_AJAX::init();

View File

@ -143,11 +143,12 @@ class WC_API {
$this->authentication = new WC_API_Authentication();
include_once( 'api/class-wc-api-resource.php' );
include_once( 'api/class-wc-api-orders.php' );
include_once( 'api/class-wc-api-products.php' );
include_once( 'api/class-wc-api-coupons.php' );
include_once( 'api/class-wc-api-customers.php' );
include_once( 'api/class-wc-api-orders.php' );
include_once( 'api/class-wc-api-products.php' );
include_once( 'api/class-wc-api-reports.php' );
include_once( 'api/class-wc-api-taxes.php' );
include_once( 'api/class-wc-api-webhooks.php' );
// allow plugins to load other response handlers or resource classes
@ -164,11 +165,12 @@ class WC_API {
$api_classes = apply_filters( 'woocommerce_api_classes',
array(
'WC_API_Coupons',
'WC_API_Customers',
'WC_API_Orders',
'WC_API_Products',
'WC_API_Coupons',
'WC_API_Reports',
'WC_API_Taxes',
'WC_API_Webhooks',
)
);
@ -196,10 +198,10 @@ class WC_API {
$this->authentication = new WC_API_Authentication();
include_once( 'api/v1/class-wc-api-resource.php' );
include_once( 'api/v1/class-wc-api-orders.php' );
include_once( 'api/v1/class-wc-api-products.php' );
include_once( 'api/v1/class-wc-api-coupons.php' );
include_once( 'api/v1/class-wc-api-customers.php' );
include_once( 'api/v1/class-wc-api-orders.php' );
include_once( 'api/v1/class-wc-api-products.php' );
include_once( 'api/v1/class-wc-api-reports.php' );
// allow plugins to load other response handlers or resource classes
@ -241,12 +243,12 @@ class WC_API {
$this->authentication = new WC_API_Authentication();
include_once( 'api/v2/class-wc-api-resource.php' );
include_once( 'api/v2/class-wc-api-orders.php' );
include_once( 'api/v2/class-wc-api-products.php' );
include_once( 'api/v2/class-wc-api-coupons.php' );
include_once( 'api/v2/class-wc-api-customers.php' );
include_once( 'api/v2/class-wc-api-orders.php' );
include_once( 'api/v2/class-wc-api-products.php' );
include_once( 'api/v2/class-wc-api-reports.php' );
include_once( 'api/class-wc-api-webhooks.php' );
include_once( 'api/v2/class-wc-api-webhooks.php' );
// allow plugins to load other response handlers or resource classes
do_action( 'woocommerce_api_loaded' );

View File

@ -969,6 +969,7 @@ class WC_Cart {
public function remove_cart_item( $cart_item_key ) {
if ( isset( $this->cart_contents[ $cart_item_key ] ) ) {
$this->removed_cart_contents[ $cart_item_key ] = $this->cart_contents[ $cart_item_key ];
unset( $this->removed_cart_contents[ $cart_item_key ]['data'] );
do_action( 'woocommerce_remove_cart_item', $cart_item_key, $this );
@ -993,6 +994,7 @@ class WC_Cart {
public function restore_cart_item( $cart_item_key ) {
if ( isset( $this->removed_cart_contents[ $cart_item_key ] ) ) {
$this->cart_contents[ $cart_item_key ] = $this->removed_cart_contents[ $cart_item_key ];
$this->cart_contents[ $cart_item_key ]['data'] = wc_get_product( $this->cart_contents[ $cart_item_key ]['variation_id'] ? $this->cart_contents[ $cart_item_key ]['variation_id'] : $this->cart_contents[ $cart_item_key ]['product_id'] );
do_action( 'woocommerce_restore_cart_item', $cart_item_key, $this );

View File

@ -536,7 +536,7 @@ class WC_Checkout {
WC()->cart->calculate_totals();
// Terms
if ( ! isset( $_POST['woocommerce_checkout_update_totals'] ) && empty( $this->posted['terms'] ) && wc_get_page_id( 'terms' ) > 0 ) {
if ( ! isset( $_POST['woocommerce_checkout_update_totals'] ) && empty( $this->posted['terms'] ) && wc_get_page_id( 'terms' ) > 0 && apply_filters( 'woocommerce_checkout_show_terms', true ) ) {
wc_add_notice( __( 'You must accept our Terms &amp; Conditions.', 'woocommerce' ), 'error' );
}

View File

@ -289,12 +289,22 @@ class WC_Form_Handler {
WC()->customer->set_city( $order->billing_city );
}
// Terms
if ( ! empty( $_POST['terms-field'] ) && empty( $_POST['terms'] ) ) {
wc_add_notice( __( 'You must accept our Terms &amp; Conditions.', 'woocommerce' ), 'error' );
return;
}
// Update payment method
if ( $order->needs_payment() ) {
$payment_method = wc_clean( $_POST['payment_method'] );
$payment_method = isset( $_POST['payment_method'] ) ? wc_clean( $_POST['payment_method'] ) : false;
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
if ( ! $payment_method ) {
wc_add_notice( __( 'Invalid payment method.', 'woocommerce' ), 'error' );
return;
}
// Update meta
update_post_meta( $order_id, '_payment_method', $payment_method );
@ -319,7 +329,6 @@ class WC_Form_Handler {
wp_redirect( $result['redirect'] );
exit;
}
}
} else {
@ -328,7 +337,6 @@ class WC_Form_Handler {
wp_safe_redirect( $order->get_checkout_order_received_url() );
exit;
}
}
}

View File

@ -147,6 +147,10 @@ class WC_Frontend_Scripts {
public static function load_scripts() {
global $post;
if ( ! did_action( 'before_woocommerce_init' ) ) {
return;
}
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';
$lightbox_en = 'yes' === get_option( 'woocommerce_enable_lightbox' );
$ajax_cart_en = 'yes' === get_option( 'woocommerce_enable_ajax_add_to_cart' );
@ -166,6 +170,7 @@ class WC_Frontend_Scripts {
self::register_script( 'wc-single-product', $frontend_script_path . 'single-product' . $suffix . '.js' );
self::register_script( 'wc-country-select', $frontend_script_path . 'country-select' . $suffix . '.js' );
self::register_script( 'wc-address-i18n', $frontend_script_path . 'address-i18n' . $suffix . '.js' );
self::register_script( 'wc-password-strength-meter', $frontend_script_path . 'password-strength-meter' . $suffix . '.js', array( 'jquery', 'password-strength-meter' ) );
// Register frontend scripts conditionally
if ( $ajax_cart_en ) {
@ -174,9 +179,14 @@ class WC_Frontend_Scripts {
if ( is_cart() ) {
self::enqueue_script( 'wc-cart', $frontend_script_path . 'cart' . $suffix . '.js', array( 'jquery', 'wc-country-select', 'wc-address-i18n' ) );
}
if ( is_checkout() || is_page( get_option( 'woocommerce_myaccount_page_id' ) ) ) {
if ( is_checkout() || is_account_page() ) {
self::enqueue_script( 'select2' );
self::enqueue_style( 'select2', $assets_path . 'css/select2.css' );
// Password strength meter js called for checkout page.
if ( 'no' === get_option( 'woocommerce_registration_generate_password' ) && ! is_user_logged_in() ) {
self::enqueue_script( 'wc-password-strength-meter' );
}
}
if ( is_checkout() ) {
self::enqueue_script( 'wc-checkout', $frontend_script_path . 'checkout' . $suffix . '.js', array( 'jquery', 'woocommerce', 'wc-country-select', 'wc-address-i18n' ) );

View File

@ -57,17 +57,22 @@ class WC_Install {
public static function install_actions() {
if ( ! empty( $_GET['do_update_woocommerce'] ) ) {
self::update();
// Update complete
WC_Admin_Notices::remove_notice( 'update' );
// What's new redirect
delete_transient( '_wc_activation_redirect' );
wp_redirect( admin_url( 'index.php?page=wc-about&wc-updated=true' ) );
exit;
add_action( 'admin_notices', array( __CLASS__, 'updated_notice' ) );
}
}
/**
* Show notice stating update was successful.
*/
public static function updated_notice() {
?>
<div id="message" class="updated woocommerce-message wc-connect">
<p><?php _e( 'WooCommerce data update complete. Thank you for updating to the latest version!', 'woocommerce' ); ?></p>
</div>
<?php
}
/**
* Install WC
*/
@ -114,10 +119,6 @@ class WC_Install {
// No page? Let user run wizard again..
} elseif ( ! get_option( 'woocommerce_cart_page_id' ) ) {
WC_Admin_Notices::add_notice( 'install' );
// Show welcome screen for major updates only
} elseif ( version_compare( $current_wc_version, $major_wc_version, '<' ) ) {
set_transient( '_wc_activation_redirect', 1, 30 );
}
if ( ! is_null( $current_db_version ) && version_compare( $current_db_version, max( array_keys( self::$db_updates ) ), '<' ) ) {
@ -361,6 +362,14 @@ class WC_Install {
}
return "
CREATE TABLE {$wpdb->prefix}woocommerce_sessions (
session_id bigint(20) NOT NULL AUTO_INCREMENT,
session_key char(32) NOT NULL,
session_value longtext NOT NULL,
session_expiry bigint(20) NOT NULL,
UNIQUE KEY session_id (session_id),
PRIMARY KEY session_key (session_key)
) $collate;
CREATE TABLE {$wpdb->prefix}woocommerce_api_keys (
key_id bigint(20) NOT NULL auto_increment,
user_id bigint(20) NOT NULL,
@ -473,9 +482,7 @@ CREATE TABLE {$wpdb->prefix}woocommerce_tax_rate_locations (
// Customer role
add_role( 'customer', __( 'Customer', 'woocommerce' ), array(
'read' => true,
'edit_posts' => false,
'delete_posts' => false
'read' => true
) );
// Shop manager role
@ -738,6 +745,7 @@ CREATE TABLE {$wpdb->prefix}woocommerce_tax_rate_locations (
public static function wpmu_drop_tables( $tables ) {
global $wpdb;
$tables[] = $wpdb->prefix . 'woocommerce_sessions';
$tables[] = $wpdb->prefix . 'woocommerce_api_keys';
$tables[] = $wpdb->prefix . 'woocommerce_attribute_taxonomies';
$tables[] = $wpdb->prefix . 'woocommerce_downloadable_product_permissions';

View File

@ -1,273 +0,0 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
/**
* WooCommerce Language Pack Upgrader class
*
* Downloads the last language pack.
*
* @class WC_Language_Pack_Upgrader
* @version 2.4.0
* @package WooCommerce/Classes/Language
* @category Class
* @author WooThemes
*/
class WC_Language_Pack_Upgrader {
/**
* Languages repository
*
* @var string
*/
protected static $repo = 'https://github.com/woothemes/woocommerce-language-packs/raw/v';
/**
* Initialize the language pack upgrader
*/
public function __construct() {
add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'check_for_update' ) );
add_filter( 'upgrader_pre_download', array( $this, 'version_update' ), 10, 2 );
add_action( 'woocommerce_installed', array( __CLASS__, 'has_available_update' ) );
add_action( 'update_option_WPLANG', array( $this, 'updated_language_option' ), 10, 2 );
add_filter( 'admin_init', array( $this, 'manual_language_update' ), 10 );
}
/**
* Get language package URI.
*
* @return string
*/
public static function get_language_package_uri( $locale = null ) {
if ( is_null( $locale ) ) {
$locale = get_locale();
}
return self::$repo . WC_VERSION . '/packages/' . $locale . '.zip';
}
/**
* Check for language updates
*
* @param object $data Transient update data
*
* @return object
*/
public function check_for_update( $data ) {
if ( self::has_available_update() ) {
$locale = get_locale();
$data->translations[] = array(
'type' => 'plugin',
'slug' => 'woocommerce',
'language' => $locale,
'version' => WC_VERSION,
'updated' => date( 'Y-m-d H:i:s' ),
'package' => self::get_language_package_uri( $locale ),
'autoupdate' => 1
);
}
return $data;
}
/**
* Triggered when WPLANG is changed
*
* @param string $old
* @param string $new
*/
public function updated_language_option( $old, $new ) {
self::has_available_update( $new );
}
/**
* Check if has available translation update
*
* @return bool
*/
public static function has_available_update( $locale = null ) {
if ( is_null( $locale ) ) {
$locale = get_locale();
}
if ( 'en_US' === $locale ) {
return false;
}
$version = get_option( 'woocommerce_language_pack_version', array( '0', $locale ) );
if ( ! is_array( $version ) || version_compare( $version[0], WC_VERSION, '<' ) || $version[1] !== $locale ) {
if ( self::check_if_language_pack_exists( $locale ) ) {
self::configure_woocommerce_upgrade_notice();
return true;
} else {
// Updated the woocommerce_language_pack_version to avoid searching translations for this release again
update_option( 'woocommerce_language_pack_version', array( WC_VERSION, $locale ) );
}
}
return false;
}
/**
* Configure the WooCommerce translation upgrade notice
*/
public static function configure_woocommerce_upgrade_notice() {
WC_Admin_Notices::add_notice( 'translation_upgrade' );
}
/**
* Check if language pack exists
*
* @return bool
*/
public static function check_if_language_pack_exists( $locale ) {
$response = wp_safe_remote_get( self::get_language_package_uri( $locale ), array( 'timeout' => 60 ) );
if ( ! is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 ) {
return true;
} else {
return false;
}
}
/**
* Update the language version in database
*
* This updates the database while the download the translation package and ensures that not generate download loop
* If the installation fails you can redo it in: WooCommerce > Sistem Status > Tools > Force Translation Upgrade
*
* @param bool $reply Whether to bail without returning the package (default: false)
* @param string $package Package URL
*
* @return bool
*/
public function version_update( $reply, $package ) {
if ( $package === self::get_language_package_uri() ) {
$this->save_language_version();
}
return $reply;
}
/**
* Save language version
*/
protected function save_language_version() {
// Update the language pack version
update_option( 'woocommerce_language_pack_version', array( WC_VERSION, get_locale() ) );
// Remove the translation upgrade notice
$notices = get_option( 'woocommerce_admin_notices', array() );
$notices = array_diff( $notices, array( 'translation_upgrade' ) );
update_option( 'woocommerce_admin_notices', $notices );
}
/**
* Manual language update
*/
public function manual_language_update() {
if (
is_admin()
&& current_user_can( 'update_plugins' )
&& isset( $_GET['page'] )
&& in_array( $_GET['page'], array( 'wc-status', 'wc-setup' ) )
&& isset( $_GET['action'] )
&& 'translation_upgrade' == $_GET['action']
) {
$page = 'wc-status&tab=tools';
$wpnonce = 'debug_action';
if ( 'wc-setup' == $_GET['page'] ) {
$page = 'wc-setup';
$wpnonce = 'setup_language';
}
$url = wp_nonce_url( admin_url( 'admin.php?page=' . $page . '&action=translation_upgrade' ), 'language_update' );
$tools_url = admin_url( 'admin.php?page=' . $page );
if ( ! isset( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], $wpnonce ) ) {
wp_redirect( add_query_arg( array( 'translation_updated' => 2 ), $tools_url ) );
exit;
}
if ( false === ( $creds = request_filesystem_credentials( $url, '', false, false, null ) ) ) {
wp_redirect( add_query_arg( array( 'translation_updated' => 3 ), $tools_url ) );
exit;
}
if ( ! WP_Filesystem( $creds ) ) {
request_filesystem_credentials( $url, '', true, false, null );
wp_redirect( add_query_arg( array( 'translation_updated' => 3 ), $tools_url ) );
exit;
}
// Download the language pack
$response = wp_safe_remote_get( self::get_language_package_uri(), array( 'timeout' => 60 ) );
if ( ! is_wp_error( $response ) && $response['response']['code'] >= 200 && $response['response']['code'] < 300 ) {
global $wp_filesystem;
$upload_dir = wp_upload_dir();
$file = trailingslashit( $upload_dir['path'] ) . get_locale() . '.zip';
// Save the zip file
if ( ! $wp_filesystem->put_contents( $file, $response['body'], FS_CHMOD_FILE ) ) {
wp_redirect( add_query_arg( array( 'translation_updated' => 3 ), $tools_url ) );
exit;
}
// Unzip the file to wp-content/languages/plugins directory
$dir = trailingslashit( WP_LANG_DIR ) . 'plugins/';
$unzip = unzip_file( $file, $dir );
if ( true !== $unzip ) {
wp_redirect( add_query_arg( array( 'translation_updated' => 3 ), $tools_url ) );
exit;
}
// Delete the package file
$wp_filesystem->delete( $file );
// Update the language pack version
$this->save_language_version();
// Redirect and show a success message
wp_redirect( add_query_arg( array( 'translation_updated' => 1 ), $tools_url ) );
exit;
} else {
// Don't have a valid package for the current language!
wp_redirect( add_query_arg( array( 'translation_updated' => 4 ), $tools_url ) );
exit;
}
}
}
/**
* Language update messages
*
* @since 2.4.5
*/
public static function language_update_messages() {
switch ( $_GET['translation_updated'] ) {
case 2 :
echo '<div class="error"><p>' . __( 'Failed to install/update the translation:', 'woocommerce' ) . ' ' . __( 'Seems you don\'t have permission to do this!', 'woocommerce' ) . '</p></div>';
break;
case 3 :
echo '<div class="error"><p>' . __( 'Failed to install/update the translation:', 'woocommerce' ) . ' ' . sprintf( __( 'An authentication error occurred while updating the translation. Please try again or configure your %sUpgrade Constants%s.', 'woocommerce' ), '<a href="http://codex.wordpress.org/Editing_wp-config.php#WordPress_Upgrade_Constants">', '</a>' ) . '</p></div>';
break;
case 4 :
echo '<div class="error"><p>' . __( 'Failed to install/update the translation:', 'woocommerce' ) . ' ' . __( 'Sorry but there is no translation available for your language =/', 'woocommerce' ) . '</p></div>';
break;
default :
// Force WordPress find for new updates and hide the WooCommerce translation update
set_site_transient( 'update_plugins', null );
echo '<div class="updated"><p>' . __( 'Translations installed/updated successfully!', 'woocommerce' ) . '</p></div>';
break;
}
}
}
new WC_Language_Pack_Upgrader();

View File

@ -18,7 +18,7 @@ class WC_Order extends WC_Abstract_Order {
*
* @return string
*/
public function get_formatted_order_total( $tax_display = '' ) {
public function get_formatted_order_total( $tax_display = '', $display_refunded = true ) {
$formatted_total = wc_price( $this->get_total(), array( 'currency' => $this->get_order_currency() ) );
$order_total = $this->get_total();
$total_refunded = $this->get_total_refunded();
@ -30,18 +30,19 @@ class WC_Order extends WC_Abstract_Order {
if ( 'itemized' == get_option( 'woocommerce_tax_total_display' ) ) {
foreach ( $this->get_tax_totals() as $code => $tax ) {
$tax_amount = $total_refunded ? wc_price( WC_Tax::round( $tax->amount - $this->get_total_tax_refunded_by_rate_id( $tax->rate_id ) ), array( 'currency' => $this->get_order_currency() ) ) : $tax->formatted_amount;
$tax_amount = ( $total_refunded && $display_refunded ) ? wc_price( WC_Tax::round( $tax->amount - $this->get_total_tax_refunded_by_rate_id( $tax->rate_id ) ), array( 'currency' => $this->get_order_currency() ) ) : $tax->formatted_amount;
$tax_string_array[] = sprintf( '%s %s', $tax_amount, $tax->label );
}
} else {
$tax_string_array[] = sprintf( '%s %s', wc_price( $this->get_total_tax() - $this->get_total_tax_refunded(), array( 'currency' => $this->get_order_currency() ) ), WC()->countries->tax_or_vat() );
$tax_amount = ( $total_refunded && $display_refunded ) ? $this->get_total_tax() - $this->get_total_tax_refunded() : $this->get_total_tax();
$tax_string_array[] = sprintf( '%s %s', wc_price( $tax_amount, array( 'currency' => $this->get_order_currency() ) ), WC()->countries->tax_or_vat() );
}
if ( ! empty( $tax_string_array ) ) {
$tax_string = ' ' . sprintf( __( '(Includes %s)', 'woocommerce' ), implode( ', ', $tax_string_array ) );
}
}
if ( $total_refunded ) {
if ( $total_refunded && $display_refunded ) {
$formatted_total = '<del>' . strip_tags( $formatted_total ) . '</del> <ins>' . wc_price( $order_total - $total_refunded, array( 'currency' => $this->get_order_currency() ) ) . $tax_string . '</ins>';
} else {
$formatted_total .= $tax_string;
@ -279,4 +280,20 @@ class WC_Order extends WC_Abstract_Order {
return $total;
}
/**
* How much money is left to refund?
* @return string
*/
public function get_remaining_refund_amount() {
return wc_format_decimal( $this->get_total() - $this->get_total_refunded() );
}
/**
* How many items are left to refund?
* @return int
*/
public function get_remaining_refund_items() {
return absint( $this->get_item_count() - $this->get_item_count_refunded() );
}
}

View File

@ -24,7 +24,7 @@ class WC_Product_Variable extends WC_Product {
public $total_stock;
/** @private array Array of variation prices. */
private $prices_array;
private $prices_array = array();
/**
* Constructor
@ -265,31 +265,46 @@ class WC_Product_Variable extends WC_Product {
global $wp_filter;
/**
* Create unique cache key based on the tax location (affects displayed/cached prices), product version and active price filters.
* Transient name for storing prices for this product.
* Max transient length is 45, -10 for get_transient_version.
* @var string
* @since 2.5.0 a single transient is used per product for all prices, rather than many transients per product.
*/
$hash = array( $this->id, $display, $display ? WC_Tax::get_rates() : array() );
$transient_name = 'wc_var_prices' . $this->id . '_' . WC_Cache_Helper::get_transient_version( 'product' );
/**
* Create unique cache key based on the tax location (affects displayed/cached prices), product version and active price filters.
* DEVELOPERS should filter this hash if offering conditonal pricing to keep it unique.
* @var string
*/
if ( $display ) {
$price_hash = array( true, WC_Tax::get_rates(), get_option( 'woocommerce_tax_display_shop' ) );
} else {
$price_hash = array( false );
}
foreach ( $wp_filter as $key => $val ) {
if ( in_array( $key, array( 'woocommerce_variation_prices_price', 'woocommerce_variation_prices_regular_price', 'woocommerce_variation_prices_sale_price' ) ) ) {
$hash[ $key ] = $val;
$price_hash[ $key ] = $val;
}
}
/**
* DEVELOPERS should filter this hash if offering conditonal pricing to keep it unique.
*/
$hash = apply_filters( 'woocommerce_get_variation_prices_hash', $hash, $this, $display );
$cache_key = 'wc_var_prices' . substr( md5( json_encode( $hash ) ), 0, 22 ) . WC_Cache_Helper::get_transient_version( 'product' );
$this->prices_array = get_transient( $cache_key );
$price_hash = md5( json_encode( apply_filters( 'woocommerce_get_variation_prices_hash', $price_hash, $this, $display ) ) );
if ( empty( $this->prices_array ) ) {
$prices = array();
$regular_prices = array();
$sale_prices = array();
$tax_display_mode = get_option( 'woocommerce_tax_display_shop' );
$variation_ids = $this->get_children( true );
// If the value has already been generated, return it now
if ( ! empty( $this->prices_array[ $price_hash ] ) ) {
return $this->prices_array[ $price_hash ];
}
// Get value of transient
$this->prices_array = array_filter( (array) get_transient( $transient_name ) );
// If the prices are not stored for this hash, generate them
if ( empty( $this->prices_array[ $price_hash ] ) ) {
$prices = array();
$regular_prices = array();
$sale_prices = array();
$variation_ids = $this->get_children( true );
foreach ( $variation_ids as $variation_id ) {
if ( $variation = $this->get_child( $variation_id ) ) {
@ -304,7 +319,7 @@ class WC_Product_Variable extends WC_Product {
// If we are getting prices for display, we need to account for taxes
if ( $display ) {
if ( 'incl' === $tax_display_mode ) {
if ( 'incl' === get_option( 'woocommerce_tax_display_shop' ) ) {
$price = '' === $price ? '' : $variation->get_price_including_tax( 1, $price );
$regular_price = '' === $regular_price ? '' : $variation->get_price_including_tax( 1, $regular_price );
$sale_price = '' === $sale_price ? '' : $variation->get_price_including_tax( 1, $sale_price );
@ -325,19 +340,19 @@ class WC_Product_Variable extends WC_Product {
asort( $regular_prices );
asort( $sale_prices );
$this->prices_array = array(
$this->prices_array[ $price_hash ] = array(
'price' => $prices,
'regular_price' => $regular_prices,
'sale_price' => $sale_prices
);
set_transient( $cache_key, $this->prices_array, DAY_IN_SECONDS * 30 );
set_transient( $transient_name, $this->prices_array, DAY_IN_SECONDS * 30 );
}
/**
* Give plugins one last chance to filter the variation prices array.
* Give plugins one last chance to filter the variation prices array which is being returned.
*/
return $this->prices_array = apply_filters( 'woocommerce_variation_prices', $this->prices_array, $this, $display );
return $this->prices_array[ $price_hash ] = apply_filters( 'woocommerce_variation_prices', $this->prices_array[ $price_hash ], $this, $display );
}
/**
@ -454,7 +469,20 @@ class WC_Product_Variable extends WC_Product {
*/
public function get_variation_default_attributes() {
$default = isset( $this->default_attributes ) ? $this->default_attributes : '';
return apply_filters( 'woocommerce_product_default_attributes', (array) maybe_unserialize( $default ), $this );
return apply_filters( 'woocommerce_product_default_attributes', array_filter( (array) maybe_unserialize( $default ) ), $this );
}
/**
* Check if variable product has default attributes set
*
* @access public
* @return bool
*/
public function has_default_attributes() {
if ( ! $this->get_variation_default_attributes() ) {
return true;
}
return false;
}
/**

View File

@ -640,6 +640,86 @@ class WC_Product_Variation extends WC_Product {
return absint( $this->variation_shipping_class_id );
}
/**
* Get formatted variation data with WC < 2.4 back compat and proper formatting of text-based attribute names.
*
* @return string
*/
public function get_formatted_variation_attributes( $flat = false ) {
$variation_data = $this->get_variation_attributes();
$attributes = $this->parent->get_attributes();
$description = array();
$return = '';
if ( is_array( $variation_data ) ) {
if ( ! $flat ) {
$return = '<dl class="variation">';
}
foreach ( $attributes as $attribute ) {
// Only deal with attributes that are variations
if ( ! $attribute[ 'is_variation' ] ) {
continue;
}
$variation_selected_value = isset( $variation_data[ 'attribute_' . sanitize_title( $attribute[ 'name' ] ) ] ) ? $variation_data[ 'attribute_' . sanitize_title( $attribute[ 'name' ] ) ] : '';
$description_name = esc_html( wc_attribute_label( $attribute[ 'name' ] ) );
$description_value = __( 'Any', 'woocommerce' );
// Get terms for attribute taxonomy or value if its a custom attribute
if ( $attribute[ 'is_taxonomy' ] ) {
$post_terms = wp_get_post_terms( $this->id, $attribute[ 'name' ] );
foreach ( $post_terms as $term ) {
if ( $variation_selected_value === $term->slug ) {
$description_value = apply_filters( 'woocommerce_variation_option_name', esc_html( $term->name ) );
}
}
} else {
$options = wc_get_text_attributes( $attribute[ 'value' ] );
foreach ( $options as $option ) {
if ( sanitize_title( $variation_selected_value ) === $variation_selected_value ) {
if ( $variation_selected_value !== sanitize_title( $option ) ) {
continue;
}
} else {
if ( $variation_selected_value !== $option ) {
continue;
}
}
$description_value = esc_html( apply_filters( 'woocommerce_variation_option_name', $option ) );
}
}
if ( $flat ) {
$description[] = $description_name . ': ' . rawurldecode( $description_value );
} else {
$description[] = '<dt>' . $description_name . ':</dt><dd>' . rawurldecode( $description_value ) . '</dd>';
}
}
if ( $flat ) {
$return .= implode( ', ', $description );
} else {
$return .= implode( '', $description );
}
if ( ! $flat ) {
$return .= '</dl>';
}
}
return $return;
}
/**
* Get product name with extra details such as SKU, price and attributes. Used within admin.
*
@ -652,8 +732,8 @@ class WC_Product_Variation extends WC_Product {
$identifier = '#' . $this->variation_id;
}
$attributes = $this->get_variation_attributes();
$extra_data = ' &ndash; ' . implode( ', ', $attributes ) . ' &ndash; ' . wc_price( $this->get_price() );
$formatted_attributes = $this->get_formatted_variation_attributes( true );
$extra_data = ' &ndash; ' . $formatted_attributes . ' &ndash; ' . wc_price( $this->get_price() );
return sprintf( __( '%s &ndash; %s%s', 'woocommerce' ), $identifier, $this->get_title(), $extra_data );
}

View File

@ -819,7 +819,7 @@ class WC_Query {
* Price filter Init
*/
public function price_filter_init() {
if ( is_active_widget( false, false, 'woocommerce_price_filter', true ) && ! is_admin() ) {
if ( apply_filters( 'woocommerce_is_price_filter_active', is_active_widget( false, false, 'woocommerce_price_filter', true ) ) && ! is_admin() ) {
$suffix = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ? '' : '.min';

View File

@ -5,38 +5,41 @@ if ( ! defined( 'ABSPATH' ) ) {
/**
* Handle data for the current customers session.
* Implements the WC_Session abstract class
* Implements the WC_Session abstract class.
*
* Long term plan will be, if https://github.com/ericmann/wp-session-manager/ gains traction
* in WP core, this will be switched out to use it and maintain backwards compatibility :)
* From 2.5 this uses a custom table for session storage. Based on https://github.com/kloon/woocommerce-large-sessions.
*
* Partly based on WP SESSION by Eric Mann.
*
* @class WC_Session_Handler
* @version 2.0.0
* @package WooCommerce/Classes
* @category Class
* @author WooThemes
* @class WC_Session_Handler
* @version 2.5.0
* @package WooCommerce/Classes
* @category Class
* @author WooThemes
*/
class WC_Session_Handler extends WC_Session {
/** cookie name */
/** @var string cookie name */
private $_cookie;
/** session due to expire timestamp */
/** @var string session due to expire timestamp */
private $_session_expiring;
/** session expiration timestamp */
/** @var string session expiration timestamp */
private $_session_expiration;
/** Bool based on whether a cookie exists **/
/** $var bool Bool based on whether a cookie exists **/
private $_has_cookie = false;
/** @var string Custom session table name */
private $_table;
/**
* Constructor for the session class.
* Constructor for the session class
*/
public function __construct() {
global $wpdb;
$this->_cookie = 'wp_woocommerce_session_' . COOKIEHASH;
$this->_table = $wpdb->prefix . 'woocommerce_sessions';
if ( $cookie = $this->get_session_cookie() ) {
$this->_customer_id = $cookie[0];
@ -47,13 +50,7 @@ class WC_Session_Handler extends WC_Session {
// Update session if its close to expiring
if ( time() > $this->_session_expiring ) {
$this->set_session_expiration();
$session_expiry_option = '_wc_session_expires_' . $this->_customer_id;
// Check if option exists first to avoid auloading cleaned up sessions
if ( false === get_option( $session_expiry_option ) ) {
add_option( $session_expiry_option, $this->_session_expiration, '', 'no' );
} else {
update_option( $session_expiry_option, $this->_session_expiration );
}
$this->update_session_timestamp( $this->_customer_id, $this->_session_expiration );
}
} else {
@ -63,57 +60,58 @@ class WC_Session_Handler extends WC_Session {
$this->_data = $this->get_session_data();
// Actions
add_action( 'woocommerce_set_cart_cookies', array( $this, 'set_customer_session_cookie' ), 10 );
add_action( 'woocommerce_cleanup_sessions', array( $this, 'cleanup_sessions' ), 10 );
add_action( 'shutdown', array( $this, 'save_data' ), 20 );
add_action( 'wp_logout', array( $this, 'destroy_session' ) );
if ( ! is_user_logged_in() ) {
add_action( 'woocommerce_thankyou', array( $this, 'destroy_session' ) );
add_filter( 'nonce_user_logged_out', array( $this, 'nonce_user_logged_out' ) );
}
}
// Actions
add_action( 'woocommerce_set_cart_cookies', array( $this, 'set_customer_session_cookie' ), 10 );
add_action( 'woocommerce_cleanup_sessions', array( $this, 'cleanup_sessions' ), 10 );
add_action( 'shutdown', array( $this, 'save_data' ), 20 );
add_action( 'wp_logout', array( $this, 'destroy_session' ) );
if ( ! is_user_logged_in() ) {
add_action( 'woocommerce_thankyou', array( $this, 'destroy_session' ) );
add_filter( 'nonce_user_logged_out', array( $this, 'nonce_user_logged_out' ) );
}
}
/**
* Sets the session cookie on-demand (usually after adding an item to the cart).
*
* Since the cookie name (as of 2.1) is prepended with wp, cache systems like batcache will not cache pages when set.
*
* Warning: Cookies will only be set if this is called before the headers are sent.
*/
public function set_customer_session_cookie( $set ) {
if ( $set ) {
// Set/renew our cookie
/**
* Sets the session cookie on-demand (usually after adding an item to the cart)
*
* Since the cookie name (as of 2.1) is prepended with wp, cache systems like batcache will not cache pages when set
*
* Warning: Cookies will only be set if this is called before the headers are sent
*/
public function set_customer_session_cookie( $set ) {
if ( $set ) {
// Set/renew our cookie
$to_hash = $this->_customer_id . $this->_session_expiration;
$cookie_hash = hash_hmac( 'md5', $to_hash, wp_hash( $to_hash ) );
$cookie_value = $this->_customer_id . '||' . $this->_session_expiration . '||' . $this->_session_expiring . '||' . $cookie_hash;
$this->_has_cookie = true;
// Set the cookie
wc_setcookie( $this->_cookie, $cookie_value, $this->_session_expiration, apply_filters( 'wc_session_use_secure_cookie', false ) );
}
}
/**
* Return true if the current user has an active session, i.e. a cookie to retrieve values
* @return boolean
*/
public function has_session() {
return isset( $_COOKIE[ $this->_cookie ] ) || $this->_has_cookie || is_user_logged_in();
}
/**
* set_session_expiration function.
*/
public function set_session_expiration() {
$this->_session_expiring = time() + intval( apply_filters( 'wc_session_expiring', 60 * 60 * 47 ) ); // 47 Hours
$this->_session_expiration = time() + intval( apply_filters( 'wc_session_expiration', 60 * 60 * 48 ) ); // 48 Hours
}
// Set the cookie
wc_setcookie( $this->_cookie, $cookie_value, $this->_session_expiration, apply_filters( 'wc_session_use_secure_cookie', false ) );
}
}
/**
* Generate a unique customer ID for guests, or return user ID if logged in.
* Return true if the current user has an active session, i.e. a cookie to retrieve values
*
* Uses Portable PHP password hashing framework to generate a unique cryptographically strong ID.
* @return bool
*/
public function has_session() {
return isset( $_COOKIE[ $this->_cookie ] ) || $this->_has_cookie || is_user_logged_in();
}
/**
* Set session expiration.
*/
public function set_session_expiration() {
$this->_session_expiring = time() + intval( apply_filters( 'wc_session_expiring', 60 * 60 * 47 ) ); // 47 Hours
$this->_session_expiration = time() + intval( apply_filters( 'wc_session_expiration', 60 * 60 * 48 ) ); // 48 Hours
}
/**
* Generate a unique customer ID for guests, or return user ID if logged in
*
* Uses Portable PHP password hashing framework to generate a unique cryptographically strong ID
*
* @return int|string
*/
@ -128,7 +126,7 @@ class WC_Session_Handler extends WC_Session {
}
/**
* get_session_cookie function.
* Get session cookie
*
* @return bool|array
*/
@ -151,48 +149,87 @@ class WC_Session_Handler extends WC_Session {
}
/**
* get_session_data function.
* Get session data
*
* @return array
*/
public function get_session_data() {
return $this->has_session() ? (array) get_option( '_wc_session_' . $this->_customer_id, array() ) : array();
return $this->has_session() ? (array) $this->get_session( $this->_customer_id, array() ) : array();
}
/**
* save_data function.
*/
public function save_data() {
// Dirty if something changed - prevents saving nothing new
if ( $this->_dirty && $this->has_session() ) {
/**
* Gets a cache prefix. This is used in session names so the entire cache can be invalidated with 1 function call
*
* @return string
*/
private function get_cache_prefix() {
$prefix_num = wp_cache_get( 'wc_session_prefix', WC_SESSION_CACHE_GROUP );
$session_option = '_wc_session_' . $this->_customer_id;
$session_expiry_option = '_wc_session_expires_' . $this->_customer_id;
if ( $prefix_num === false ) {
wp_cache_set( 'wc_session_prefix', 1, WC_SESSION_CACHE_GROUP );
}
if ( false === get_option( $session_option ) ) {
add_option( $session_option, $this->_data, '', 'no' );
add_option( $session_expiry_option, $this->_session_expiration, '', 'no' );
} else {
update_option( $session_option, $this->_data );
}
// Mark session clean after saving
$this->_dirty = false;
}
}
return 'wc_session_' . $prefix_num . '_';
}
/**
* Destroy all session data
*/
public function destroy_session() {
/**
* Save data
*/
public function save_data() {
// Dirty if something changed - prevents saving nothing new
if ( $this->_dirty && $this->has_session() ) {
global $wpdb;
$session_id = $wpdb->get_var( $wpdb->prepare( "SELECT session_id FROM $this->_table WHERE session_key = %s;", $this->_customer_id ) );
if ( $session_id ) {
$wpdb->update(
$this->_table,
array(
'session_key' => $this->_customer_id,
'session_value' => maybe_serialize( $this->_data ),
'session_expiry' => $this->_session_expiration
),
array( 'session_id' => $session_id ),
array(
'%s',
'%s',
'%d'
),
array( '%d' )
);
} else {
$wpdb->insert(
$this->_table,
array(
'session_key' => $this->_customer_id,
'session_value' => maybe_serialize( $this->_data ),
'session_expiry' => $this->_session_expiration
),
array(
'%s',
'%s',
'%d'
)
);
}
// Set cache
wp_cache_set( $this->get_cache_prefix() . $this->_customer_id, $this->_data, WC_SESSION_CACHE_GROUP, $this->_session_expiration - time() );
// Mark session clean after saving
$this->_dirty = false;
}
}
/**
* Destroy all session data
*/
public function destroy_session() {
// Clear cookie
wc_setcookie( $this->_cookie, '', time() - YEAR_IN_SECONDS, apply_filters( 'wc_session_use_secure_cookie', false ) );
// Delete session
$session_option = '_wc_session_' . $this->_customer_id;
$session_expiry_option = '_wc_session_expires_' . $this->_customer_id;
delete_option( $session_option );
delete_option( $session_expiry_option );
$this->delete_session( $this->_customer_id );
// Clear cart
wc_empty_cart();
@ -204,45 +241,98 @@ class WC_Session_Handler extends WC_Session {
}
/**
* When a user is logged out, ensure they have a unique nonce by using the customer/session ID.
* When a user is logged out, ensure they have a unique nonce by using the customer/session ID
*
* @return string
*/
public function nonce_user_logged_out( $uid ) {
return $this->has_session() && $this->_customer_id ? $this->_customer_id : $uid;
}
/**
* cleanup_sessions function.
/**
* Cleanup sessions
*/
public function cleanup_sessions() {
global $wpdb;
if ( ! defined( 'WP_SETUP_CONFIG' ) && ! defined( 'WP_INSTALLING' ) ) {
$now = time();
$expired_sessions = array();
$wc_session_expires = $wpdb->get_col( "SELECT option_name FROM $wpdb->options WHERE option_name LIKE '\_wc\_session\_expires\_%' AND option_value < '$now'" );
foreach ( $wc_session_expires as $option_name ) {
$session_id = substr( $option_name, 20 );
$expired_sessions[] = $option_name; // Expires key
$expired_sessions[] = "_wc_session_$session_id"; // Session key
}
// Delete expired sessions
$wpdb->query( $wpdb->prepare( "DELETE FROM $this->_table WHERE session_expiry < %d", time() ) );
if ( ! empty( $expired_sessions ) ) {
$expired_sessions_chunked = array_chunk( $expired_sessions, 100 );
foreach ( $expired_sessions_chunked as $chunk ) {
if ( wp_using_ext_object_cache() ) {
// delete from object cache first, to avoid cached but deleted options
foreach ( $chunk as $option ) {
wp_cache_delete( $option, 'options' );
}
}
// delete from options table
$option_names = implode( "','", $chunk );
$wpdb->query( "DELETE FROM $wpdb->options WHERE option_name IN ('$option_names')" );
}
}
// Invalidate cache
wp_cache_incr( 'wc_session_prefix', 1, WC_SESSION_CACHE_GROUP );
}
}
/**
* Returns the session
*
* @param string $customer_id
* @param mixed $default
* @return string|array
*/
public function get_session( $customer_id, $default = false ) {
global $wpdb;
if ( defined( 'WP_SETUP_CONFIG' ) ) {
return false;
}
// Try get it from the cache, it will return false if not present or if object cache not in use
$value = wp_cache_get( $this->get_cache_prefix() . $customer_id, WC_SESSION_CACHE_GROUP );
if ( false === $value ) {
$value = $wpdb->get_var( $wpdb->prepare( "SELECT session_value FROM $this->_table WHERE session_key = %s", $customer_id ) );
if ( is_null( $value ) ) {
$value = $default;
}
wp_cache_add( $this->get_cache_prefix() . $customer_id, $value, WC_SESSION_CACHE_GROUP, $this->_session_expiration - time() );
}
return maybe_unserialize( $value );
}
/**
* Delete the session from the cache and database
*
* @param int $customer_id
*/
public function delete_session( $customer_id ) {
global $wpdb;
wp_cache_delete( $this->get_cache_prefix() . $customer_id, WC_SESSION_CACHE_GROUP );
$wpdb->delete(
$this->_table,
array(
'session_key' => $customer_id
)
);
}
/**
* Update the session expiry timestamp
*
* @param string $customer_id
* @param int $timestamp
*/
public function update_session_timestamp( $customer_id, $timestamp ) {
global $wpdb;
$wpdb->update(
$this->_table,
array(
'session_expiry' => $timestamp
),
array(
'session_key' => $customer_id
),
array(
'%d'
)
);
}
}

View File

@ -61,4 +61,13 @@ class WC_Shipping_Rate {
}
return apply_filters( 'woocommerce_get_shipping_tax', $taxes, $this );
}
/**
* Get label
*
* @return string
*/
public function get_label() {
return apply_filters( 'woocommerce_shipping_rate_label', $this->label );
}
}

View File

@ -240,7 +240,9 @@ class WC_Shipping {
$prioritized_methods = array();
foreach ( $available_methods as $method_id => $method ) {
$priority = isset( $selection_priority[ $method_id ] ) ? absint( $selection_priority[ $method_id ] ) : 1;
// Some IDs contain : if they have multiple rates
$method_id = current( explode( ':', $method_id ) );
$priority = isset( $selection_priority[ $method_id ] ) ? absint( $selection_priority[ $method_id ] ): 1;
if ( empty( $prioritized_methods[ $priority ] ) ) {
$prioritized_methods[ $priority ] = array();
}

View File

@ -786,14 +786,15 @@ class WC_Shortcodes {
}
/**
* List related products.
* @param array $atts
* @return string
*/
public static function related_products( $atts ) {
$atts = shortcode_atts( array(
'per_page' => '4',
'columns' => '4',
'orderby' => 'rand'
'columns' => '4',
'orderby' => 'rand'
), $atts );
ob_start();

View File

@ -654,8 +654,12 @@ class WC_Tax {
* @param string $class
* @return string
*/
private static function format_tax_rate_class( $class ) {
public static function format_tax_rate_class( $class ) {
$class = sanitize_title( $class );
$sanitized_classes = array_map( 'sanitize_title', self::get_tax_classes() );
if ( ! in_array( $class, $sanitized_classes ) ) {
$class = '';
}
return $class === 'standard' ? '' : $class;
}
@ -682,7 +686,8 @@ class WC_Tax {
* @access private
*
* @param array $tax_rate
* @return int tax rate id
*
* @return int tax rate id
*/
public static function _insert_tax_rate( $tax_rate ) {
global $wpdb;
@ -694,6 +699,28 @@ class WC_Tax {
return $wpdb->insert_id;
}
/**
* Get tax rate
*
* Internal use only.
*
* @since 2.5.0
* @access private
*
* @param int $tax_rate_id
*
* @return array
*/
public static function _get_tax_rate( $tax_rate_id ) {
global $wpdb;
return $wpdb->get_row( $wpdb->prepare( "
SELECT *
FROM {$wpdb->prefix}woocommerce_tax_rates
WHERE tax_rate_id = %d
", $tax_rate_id ), ARRAY_A );
}
/**
* Update a tax rate
*
@ -702,8 +729,8 @@ class WC_Tax {
* @since 2.3.0
* @access private
*
* @param int $tax_rate_id
* @param array $tax_rate
* @param int $tax_rate_id
* @param array $tax_rate
*/
public static function _update_tax_rate( $tax_rate_id, $tax_rate ) {
global $wpdb;
@ -753,7 +780,10 @@ class WC_Tax {
* @return string
*/
public static function _update_tax_rate_postcodes( $tax_rate_id, $postcodes ) {
$postcodes = array_filter( array_diff( array_map( array( __CLASS__, 'format_tax_rate_postcode' ), explode( ';', $postcodes ) ), array( '*' ) ) );
if ( ! is_array( $postcodes ) ) {
$postcodes = explode( ';', $postcodes );
}
$postcodes = array_filter( array_diff( array_map( array( __CLASS__, 'format_tax_rate_postcode' ), $postcodes ), array( '*' ) ) );
$postcodes = self::_get_expanded_numeric_ranges_from_array( $postcodes );
self::_update_tax_rate_locations( $tax_rate_id, $postcodes, 'postcode' );
@ -772,7 +802,10 @@ class WC_Tax {
* @return string
*/
public static function _update_tax_rate_cities( $tax_rate_id, $cities ) {
$cities = array_filter( array_diff( array_map( array( __CLASS__, 'format_tax_rate_city' ), explode( ';', $cities ) ), array( '*' ) ) );
if ( ! is_array( $cities ) ) {
$cities = explode( ';', $cities );
}
$cities = array_filter( array_diff( array_map( array( __CLASS__, 'format_tax_rate_city' ), $cities ), array( '*' ) ) );
self::_update_tax_rate_locations( $tax_rate_id, $cities, 'city' );
}
@ -862,5 +895,38 @@ class WC_Tax {
}
return $postcodes;
}
/**
* Used by admin settings page.
*
* @param $tax_class
*
* @return array|null|object
*/
public static function get_rates_for_tax_class( $tax_class ) {
global $wpdb;
// Get all the rates and locations. Snagging all at once should significantly cut down on the number of queries.
$rates = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$wpdb->prefix}woocommerce_tax_rates` WHERE `tax_rate_class` = %s ORDER BY `tax_rate_order`;", sanitize_title( $tax_class ) ) );
$locations = $wpdb->get_results( "SELECT * FROM `{$wpdb->prefix}woocommerce_tax_rate_locations`" );
// Set the rates keys equal to their ids.
$rates = array_combine( wp_list_pluck( $rates, 'tax_rate_id' ), $rates );
// Drop the locations into the rates array.
foreach ( $locations as $location ) {
// Don't set them for unexistent rates.
if ( ! isset( $rates[ $location->tax_rate_id ] ) ) {
continue;
}
// If the rate exists, initialize the array before appending to it.
if ( ! isset( $rates[ $location->tax_rate_id ]->{$location->location_type} ) ) {
$rates[ $location->tax_rate_id ]->{$location->location_type} = array();
}
$rates[ $location->tax_rate_id ]->{$location->location_type}[] = $location->location_code;
}
return $rates;
}
}
WC_Tax::init();

View File

@ -53,6 +53,9 @@ class WC_Validation {
case 'CH' :
$valid = (bool) preg_match( '/^([0-9]{4})$/i', $postcode );
break;
case 'DE' :
$valid = (bool) preg_match( '/^([0]{1}[1-9]{1}|[1-9]{1}[0-9]{1})[0-9]{3}$/', $postcode );
break;
case 'GB' :
$valid = self::is_GB_postcode( $postcode );
break;

View File

@ -752,7 +752,6 @@ class WC_CLI_Product extends WC_CLI_Command {
* @return array
*/
private function get_product_data( $product ) {
$prices_precision = wc_get_price_decimals();
// Add data that applies to every product type.
$product_data = array(
@ -766,9 +765,9 @@ class WC_CLI_Product extends WC_CLI_Command {
'virtual' => $product->is_virtual(),
'permalink' => $product->get_permalink(),
'sku' => $product->get_sku(),
'price' => wc_format_decimal( $product->get_price(), $prices_precision ),
'regular_price' => wc_format_decimal( $product->get_regular_price(), $prices_precision ),
'sale_price' => $product->get_sale_price() ? wc_format_decimal( $product->get_sale_price(), $prices_precision ) : null,
'price' => $product->get_price(),
'regular_price' => $product->get_regular_price(),
'sale_price' => $product->get_sale_price() ? $product->get_sale_price() : null,
'price_html' => $product->get_price_html(),
'taxable' => $product->is_taxable(),
'tax_status' => $product->get_tax_status(),
@ -786,7 +785,7 @@ class WC_CLI_Product extends WC_CLI_Command {
'on_sale' => $product->is_on_sale(),
'product_url' => $product->is_type( 'external' ) ? $product->get_product_url() : '',
'button_text' => $product->is_type( 'external' ) ? $product->get_button_text() : '',
'weight' => $product->get_weight() ? wc_format_decimal( $product->get_weight(), 2 ) : null,
'weight' => $product->get_weight() ? $product->get_weight() : null,
'dimensions' => array(
'length' => $product->length,
'width' => $product->width,
@ -995,8 +994,7 @@ class WC_CLI_Product extends WC_CLI_Command {
* @return array
*/
private function get_variation_data( $product ) {
$prices_precision = wc_get_price_decimals();
$variations = array();
$variations = array();
foreach ( $product->get_children() as $child_id ) {
@ -1014,9 +1012,9 @@ class WC_CLI_Product extends WC_CLI_Command {
'virtual' => $variation->is_virtual(),
'permalink' => $variation->get_permalink(),
'sku' => $variation->get_sku(),
'price' => wc_format_decimal( $variation->get_price(), $prices_precision ),
'regular_price' => wc_format_decimal( $variation->get_regular_price(), $prices_precision ),
'sale_price' => $variation->get_sale_price() ? wc_format_decimal( $variation->get_sale_price(), $prices_precision ) : null,
'price' => $variation->get_price(),
'regular_price' => $variation->get_regular_price(),
'sale_price' => $variation->get_sale_price() ? $variation->get_sale_price() : null,
'taxable' => $variation->is_taxable(),
'tax_status' => $variation->get_tax_status(),
'tax_class' => $variation->get_tax_class(),
@ -1027,7 +1025,7 @@ class WC_CLI_Product extends WC_CLI_Command {
'purchaseable' => $variation->is_purchasable(),
'visible' => $variation->variation_is_visible(),
'on_sale' => $variation->is_on_sale(),
'weight' => $variation->get_weight() ? wc_format_decimal( $variation->get_weight(), 2 ) : null,
'weight' => $variation->get_weight() ? $variation->get_weight() : null,
'dimensions' => array(
'length' => $variation->length,
'width' => $variation->width,

View File

@ -266,7 +266,7 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
if ( ! $this->can_refund_order( $order ) ) {
$this->log( 'Refund Failed: No transaction ID' );
return false;
return new WP_Error( 'error', __( 'Refund Failed: No transaction ID', 'woocommerce' ) );
}
include_once( 'includes/class-wc-gateway-paypal-refund.php' );
@ -279,7 +279,7 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
if ( is_wp_error( $result ) ) {
$this->log( 'Refund Failed: ' . $result->get_error_message() );
return false;
return new WP_Error( 'error', $result->get_error_message() );
}
$this->log( 'Refund Result: ' . print_r( $result, true ) );
@ -292,6 +292,6 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
break;
}
return false;
return isset( $result['L_LONGMESSAGE0'] ) ? new WP_Error( 'error', $result['L_LONGMESSAGE0'] ) : false;
}
}

View File

@ -84,7 +84,7 @@ class WC_Shortcode_Checkout {
// Pay for existing order
$order_key = $_GET[ 'key' ];
$order = wc_get_order( $order_id );
if ( ! current_user_can( 'pay_for_order', $order_id ) ) {
echo '<div class="woocommerce-error">' . __( 'Invalid order. If you have an account please log in and try again.', 'woocommerce' ) . ' <a href="' . wc_get_page_permalink( 'myaccount' ) . '" class="wc-forward">' . __( 'My Account', 'woocommerce' ) . '</a>' . '</div>';
return;
@ -95,14 +95,27 @@ class WC_Shortcode_Checkout {
if ( $order->needs_payment() ) {
// Set customer location to order location
if ( $order->billing_country )
if ( $order->billing_country ) {
WC()->customer->set_country( $order->billing_country );
if ( $order->billing_state )
}
if ( $order->billing_state ) {
WC()->customer->set_state( $order->billing_state );
if ( $order->billing_postcode )
}
if ( $order->billing_postcode ) {
WC()->customer->set_postcode( $order->billing_postcode );
}
wc_get_template( 'checkout/form-pay.php', array( 'order' => $order ) );
$available_gateways = WC()->payment_gateways->get_available_payment_gateways();
if ( sizeof( $available_gateways ) ) {
current( $available_gateways )->set_current();
}
wc_get_template( 'checkout/form-pay.php', array(
'order' => $order,
'available_gateways' => $available_gateways,
'order_button_text' => apply_filters( 'woocommerce_pay_order_button_text', __( 'Pay for order', 'woocommerce' ) )
) );
} else {
wc_add_notice( sprintf( __( 'This order&rsquo;s status is &ldquo;%s&rdquo;&mdash;it cannot be paid for. Please contact us if you need assistance.', 'woocommerce' ), wc_get_order_status_name( $order->get_status() ) ), 'error' );
@ -117,7 +130,7 @@ class WC_Shortcode_Checkout {
// Pay for order after checkout step
$order_key = isset( $_GET['key'] ) ? wc_clean( $_GET['key'] ) : '';
$order = wc_get_order( $order_id );
if ( $order->id == $order_id && $order->order_key == $order_key ) {
if ( $order->needs_payment() ) {

View File

@ -165,7 +165,13 @@ function wc_cart_totals_shipping_html() {
foreach ( $packages as $i => $package ) {
$chosen_method = isset( WC()->session->chosen_shipping_methods[ $i ] ) ? WC()->session->chosen_shipping_methods[ $i ] : '';
wc_get_template( 'cart/cart-shipping.php', array( 'package' => $package, 'available_methods' => $package['rates'], 'show_package_details' => ( sizeof( $packages ) > 1 ), 'index' => $i, 'chosen_method' => $chosen_method ) );
wc_get_template( 'cart/cart-shipping.php', array(
'package' => $package,
'available_methods' => $package['rates'],
'show_package_details' => sizeof( $packages ) > 1,
'index' => $i,
'chosen_method' => $chosen_method
) );
}
}
@ -263,11 +269,11 @@ function wc_cart_totals_fee_html( $fee ) {
/**
* Get a shipping methods full label including price
* @param object $method
* @param WC_Shipping_Rate $method
* @return string
*/
function wc_cart_totals_shipping_method_label( $method ) {
$label = $method->label;
$label = $method->get_label();
if ( $method->cost > 0 ) {
if ( WC()->cart->tax_display_cart == 'excl' ) {

View File

@ -33,6 +33,7 @@ add_filter( 'woocommerce_coupon_code', 'html_entity_decode' );
add_filter( 'woocommerce_coupon_code', 'sanitize_text_field' );
add_filter( 'woocommerce_coupon_code', 'strtolower' ); // Coupons case-insensitive by default
add_filter( 'woocommerce_stock_amount', 'intval' ); // Stock amounts are integers by default
add_filter( 'woocommerce_shipping_rate_label', 'sanitize_text_field' ); // Shipping rate label
/**
* Short Description (excerpt)

View File

@ -244,13 +244,12 @@ function wc_format_localized_decimal( $value ) {
}
/**
* Clean variables
*
* @param string $var
* @return string
* Clean variables using sanitize_text_field
* @param string|array $var
* @return string|array
*/
function wc_clean( $var ) {
return sanitize_text_field( $var );
return is_array( $var ) ? array_map( 'wc_clean', $var ) : sanitize_text_field( $var );
}
/**

View File

@ -717,22 +717,6 @@ function wc_create_refund( $args = array() ) {
// Set total to total refunded which may vary from order items
$refund->set_total( wc_format_decimal( $args['amount'] ) * -1, 'total' );
// Figure out if this is just a partial refund
$max_remaining_refund = wc_format_decimal( $order->get_total() - $order->get_total_refunded() );
$max_remaining_items = absint( $order->get_item_count() - $order->get_item_count_refunded() );
if ( $max_remaining_refund > 0 || $max_remaining_items > 0 ) {
/**
* woocommerce_order_partially_refunded
*
* @since 2.4.0
* Note: 3rd arg was added in err. Kept for bw compat. 2.4.3
*/
do_action( 'woocommerce_order_partially_refunded', $args['order_id'], $refund_id, $refund_id );
} else {
do_action( 'woocommerce_order_fully_refunded', $args['order_id'], $refund_id );
}
do_action( 'woocommerce_refund_created', $refund_id, $args );
}

View File

@ -163,9 +163,11 @@ function wc_nav_menu_items( $items ) {
if ( ! is_user_logged_in() ) {
$customer_logout = get_option( 'woocommerce_logout_endpoint', 'customer-logout' );
foreach ( $items as $key => $item ) {
if ( strstr( $item->url, $customer_logout ) ) {
unset( $items[ $key ] );
if ( ! empty( $customer_logout ) ) {
foreach ( $items as $key => $item ) {
if ( strstr( $item->url, $customer_logout ) ) {
unset( $items[ $key ] );
}
}
}
}

View File

@ -7,7 +7,7 @@
* @author WooThemes
* @category Core
* @package WooCommerce/Functions
* @version 2.4.0
* @version 2.5.0
*/
if ( ! defined( 'ABSPATH' ) ) {
@ -308,6 +308,10 @@ function wc_product_post_class( $classes, $class = '', $post_id = '' ) {
}
}
if ( is_product() && 'variable' === $product->product_type && $product->has_default_attributes() ) {
$classes[] = 'has-default-attributes';
}
$classes[] = $product->stock_status;
}
@ -513,7 +517,7 @@ if ( ! function_exists( 'woocommerce_template_loop_product_title' ) ) {
* Show the product title in the product loop. By default this is an H3
*/
function woocommerce_template_loop_product_title() {
wc_get_template( 'loop/title.php' );
echo '<h3>' . get_the_title() . '</h3>';
}
}
if ( ! function_exists( 'woocommerce_template_loop_subcategory_title' ) ) {
@ -1385,14 +1389,17 @@ if ( ! function_exists( 'woocommerce_products_will_display' ) ) {
* @return bool
*/
function woocommerce_products_will_display() {
if ( is_shop() )
return get_option( 'woocommerce_shop_page_display' ) != 'subcategories';
if ( is_shop() ) {
return 'subcategories' !== get_option( 'woocommerce_shop_page_display' ) || is_search();
}
if ( ! is_product_taxonomy() )
if ( ! is_product_taxonomy() ) {
return false;
}
if ( is_search() || is_filtered() || is_paged() )
if ( is_search() || is_filtered() || is_paged() ) {
return true;
}
$term = get_queried_object();

View File

@ -537,3 +537,15 @@ function wc_get_customer_order_count( $user_id ) {
return absint( $count );
}
/**
* Reset _customer_user on orders when a user is deleted.
* @param int $user_id
*/
function wc_reset_order_customer_id_on_deleted_user( $user_id ) {
global $wpdb;
$wpdb->update( $wpdb->postmeta, array( '_customer_user' => 0 ), array( '_customer_user' => $user_id ) );
}
add_action( 'deleted_user', 'wc_reset_order_customer_id_on_deleted_user' );

View File

@ -193,7 +193,7 @@ class WC_Widget_Product_Categories extends WC_Widget {
// Dropdown
if ( $d ) {
$dropdown_defaults = array(
'show_counts' => $c,
'show_count' => $c,
'hierarchical' => $h,
'show_uncategorized' => 0,
'orderby' => $o,

View File

@ -80,7 +80,7 @@ When you download WooCommerce, you join a community of more than a million store
If youre interested in contributing to WooCommerce weve got more than 350 contributors, and theres always room for more. Head over to the [WooCommerce GitHub Repository](https://github.com/woothemes/woocommerce) to find out how you can pitch in.
Want to add a new language to WooCommerce? Swell! You can contribute through [Transifex](https://www.transifex.com/woothemes/woocommerce/).
Want to add a new language to WooCommerce? Swell! You can contribute via [translate.wordpress.org](https://translate.wordpress.org/projects/wp-plugins/woocommerce).
And, finally, consider joining or spearheading a WooCommerce Meetup locally, more about those [here](http://www.woothemes.com/woocommerce/meetups/).
@ -159,10 +159,40 @@ Yes you can! Join in on our [GitHub repository](http://github.com/woothemes/wooc
== Changelog ==
= 2.5.0 - TBD =
* Feature - New default session handler. Uses custom table to store data rather than the options table for performance and scalability reasons. https://woocommerce.wordpress.com/2015/10/07/new-session-handler-in-2-5/
* Feature - New tax settings UI - faster, enhanced with ajax, searchable.
* Feature - WP CLI Support. https://woocommerce.wordpress.com/2015/10/01/sneak-peek-wp-cli-support-in-woocommerce/
* Feature - Added terms and conditions checkbox to pay page.
* Fix - Check for existence of global attribute when you get_attributes() for a product.
* Fix - Show order by template on product search.
* Fix - Search variation skus in backend search.
* Tweak - Default customer role capabilities.
* Tweak - Expire mini-cart cache after 24 hours.
* Tweak - Improved refund error messages in PayPal standard.
* Tweak - Removed language pack downloader in favour of translate.wordpress.org.
* Tweak - Added onboarding wizard button to the contextual help so it can be accessed again.
* Tweak - When a WordPress user is deleted, turn any orders they have into Guest orders.
* Tweak - When calculating order taxes, respect tax settings and default to base country.
* Tweak - Fade in variation images to avoid flicker during load.
* Tweak - Display 2 averages on report (net and gross).
* Tweak - Improve product search and use WPDB instead of several get_posts queries for performance.
* Tweak - Use SKU for stock order notes.
* Tweak - Added order notes for manual email sends.
* Tweak - Sanitize shipping method labels/titles.
* Tweak - Only display the coupon form on the checkout if a coupon hasn't been applied.
* Tweak - Added billing address column to order screen (off for new users).
* Dev - API - Added /products/shipping_classes endpoint.
* Dev - API - Added support to POST, PUT, and DELETE categories and tags.
* Dev - API - Added support to filter products by tag, category, shipping class, and attribute.
* Dev - API - Added tax and tax_class endpoints.
* Dev - Template - New star ratings. The old one was 5 separate buttons. This new one consolidates the 5 options into one element making it leaner visually and more intuitive. Works in IE9+ with a graceful degradation for IE8.
* Dev - Template - Added `data-title` attribute to cart table.
* Dev - Template - Product archive anchors are now hooked into templates rather than hard coded.
* Dev - Allow wc_clean to support arrays.
* Dev - Added a manual update trigger for checkout.
* Dev - Added woocommerce_is_price_filter_active filter to Query class.
= 2.4.7 - 21/09/2015 =
* Fix - Handle Switzerland in get_european_union_countries.
* Fix - For geolocation with static cache support, ensure hash is appended during form submission.
* Fix - To prevent discounts being applied in 'random' order (based on order added to cart), sort cart items based on subtotal during calculate_totals.
@ -174,7 +204,15 @@ Yes you can! Join in on our [GitHub repository](http://github.com/woothemes/wooc
* Fix - Check specifically for Post IDs in WC Query verbose rules fix.
* Fix - Only run maybe_set_cart_cookies if cart was loaded to prevent notices.
* Fix - Variation loading/refresh after attribute saving.
* Fix - Add monthly cron schedule.
* Fix - Added monthly cron schedule.
* Fix - Remove use of 'input' event in checkout scripts to prevent IE11 triggering updates on placeholder change.
* Fix - AJAX variations not being found in some cases when product version was < 2.4, but attributes were updated after sync().
* Fix - Changed the way variable product prices get cached for greater plugin compatibility. See http://wp.me/p6wtcw-5x
* Fix - Highlighting of reports chart.
* Fix - Network activated plugins not showing up in system status report.
* Fix - Tax fields showing on bulk/quick edit when disabled the tax system.
* Fix - Tax status and tax class values within bulk edit.
* Tweak - Allow bulk edit price to 0.
* Tweak - Add filters to control "shipped via" text.
* Tweak - Allow line breaks in non-variation attributes.
* Tweak - Renamed wc_var_prices transient to allow them to flush on product save.
@ -185,9 +223,12 @@ Yes you can! Join in on our [GitHub repository](http://github.com/woothemes/wooc
* Tweak - Use the needs_payment function (DRY).
* Tweak - Tweak wc_create_page to work with trashed pages.
* Tweak - Redirect 'not right now' to referer in onboarding wizard.
* Tweak - woocommerce_update_new_customer_past_order action.
* Tweak - Prevent empty terms when using `wc_get_formatted_variation()`.
* Tweak - Unslash shipping label on orders admin screen.
* Tweak - Prevent wrong phone numbers on PayPal for CA and US when users add the prefix `+1`.
* Template - Removed 'Payment' heading in `templates/checkout/form-pay.php`.
* Template - Removed unnecessary clearing div in `templates/checkout/payment.php`.
* Template - Anchors and titles are now hooked in, rather than hard coded into `templates/content-product.php` and `templates/content-product_cat.php`
= 2.4.6 - 24/08/2015 =
* Fix - menu_order notices on IIS.

View File

@ -2,7 +2,7 @@
/**
* The Template for displaying product archives, including the main shop page which is a post type archive.
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product.php
* This template can be overridden by copying it to yourtheme/woocommerce/archive-product.php
*
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer)
* will need to copy the new files to your theme to maintain compatibility. We try to do this

View File

@ -2,7 +2,7 @@
/**
* Auth footer
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product.php
* This template can be overridden by copying it to yourtheme/woocommerce/auth/footer.php
*
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer)
* will need to copy the new files to your theme to maintain compatibility. We try to do this

View File

@ -2,7 +2,7 @@
/**
* Auth form grant access
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product.php
* This template can be overridden by copying it to yourtheme/woocommerce/auth/form-grant-access.php
*
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer)
* will need to copy the new files to your theme to maintain compatibility. We try to do this

View File

@ -2,7 +2,7 @@
/**
* Auth form login
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product.php
* This template can be overridden by copying it to yourtheme/woocommerce/auth/form-login.php
*
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer)
* will need to copy the new files to your theme to maintain compatibility. We try to do this

View File

@ -2,7 +2,7 @@
/**
* Auth header
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product.php
* This template can be overridden by copying it to yourtheme/woocommerce/auth/header.php
*
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer)
* will need to copy the new files to your theme to maintain compatibility. We try to do this

View File

@ -2,7 +2,7 @@
/**
* Empty cart page
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product.php
* This template can be overridden by copying it to yourtheme/woocommerce/cart/cart-empty.php
*
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer)
* will need to copy the new files to your theme to maintain compatibility. We try to do this

View File

@ -2,7 +2,7 @@
/**
* Cart item data (when outputting non-flat)
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product.php
* This template can be overridden by copying it to yourtheme/woocommerce/cart/cart-item-data.php
*
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer)
* will need to copy the new files to your theme to maintain compatibility. We try to do this

View File

@ -4,7 +4,7 @@
*
* In 2.1 we show methods per package. This allows for multiple methods per order if so desired.
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product.php
* This template can be overridden by copying it to yourtheme/woocommerce/cart/cart-shipping.php
*
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer)
* will need to copy the new files to your theme to maintain compatibility. We try to do this
@ -14,99 +14,64 @@
* @see http://docs.woothemes.com/document/template-structure/
* @author WooThemes
* @package WooCommerce/Templates
* @version 2.3.0
* @version 2.5.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<tr class="shipping">
<th><?php
if ( $show_package_details ) {
printf( __( 'Shipping #%d', 'woocommerce' ), $index + 1 );
} else {
_e( 'Shipping', 'woocommerce' );
}
?></th>
<th><?php echo $show_package_details && $index ? sprintf( __( 'Shipping #%d', 'woocommerce' ), $index + 1 ) : __( 'Shipping', 'woocommerce' ); ?></th>
<td>
<?php if ( ! empty( $available_methods ) ) : ?>
<?php if ( empty( $available_methods ) ) : ?>
<?php if ( 1 === count( $available_methods ) ) :
$method = current( $available_methods );
<?php if ( ( WC()->countries->get_states( WC()->customer->get_shipping_country() ) && ! WC()->customer->get_shipping_state() ) || ! WC()->customer->get_shipping_postcode() ) : ?>
echo wp_kses_post( wc_cart_totals_shipping_method_label( $method ) ); ?>
<input type="hidden" name="shipping_method[<?php echo $index; ?>]" data-index="<?php echo $index; ?>" id="shipping_method_<?php echo $index; ?>" value="<?php echo esc_attr( $method->id ); ?>" class="shipping_method" />
<?php elseif ( get_option( 'woocommerce_shipping_method_format' ) === 'select' ) : ?>
<select name="shipping_method[<?php echo $index; ?>]" data-index="<?php echo $index; ?>" id="shipping_method_<?php echo $index; ?>" class="shipping_method">
<?php foreach ( $available_methods as $method ) : ?>
<option value="<?php echo esc_attr( $method->id ); ?>" <?php selected( $method->id, $chosen_method ); ?>><?php echo wp_kses_post( wc_cart_totals_shipping_method_label( $method ) ); ?></option>
<?php endforeach; ?>
</select>
<?php echo wpautop( __( 'Shipping costs will be calculated once you have provided your address.', 'woocommerce' ) ); ?>
<?php else : ?>
<ul id="shipping_method">
<?php foreach ( $available_methods as $method ) : ?>
<li>
<input type="radio" name="shipping_method[<?php echo $index; ?>]" data-index="<?php echo $index; ?>" id="shipping_method_<?php echo $index; ?>_<?php echo sanitize_title( $method->id ); ?>" value="<?php echo esc_attr( $method->id ); ?>" <?php checked( $method->id, $chosen_method ); ?> class="shipping_method" />
<label for="shipping_method_<?php echo $index; ?>_<?php echo sanitize_title( $method->id ); ?>"><?php echo wp_kses_post( wc_cart_totals_shipping_method_label( $method ) ); ?></label>
</li>
<?php endforeach; ?>
</ul>
<?php echo apply_filters( is_cart() ? 'woocommerce_cart_no_shipping_available_html' : 'woocommerce_no_shipping_available_html', wpautop( __( 'There are no shipping methods available. Please double check your address, or contact us if you need any help.', 'woocommerce' ) ) ); ?>
<?php endif; ?>
<?php elseif ( ( WC()->countries->get_states( WC()->customer->get_shipping_country() ) && ! WC()->customer->get_shipping_state() ) || ! WC()->customer->get_shipping_postcode() ) : ?>
<?php elseif ( 1 === count( $available_methods ) ) : ?>
<?php if ( is_cart() && get_option( 'woocommerce_enable_shipping_calc' ) === 'yes' ) : ?>
<?php $method = current( $available_methods ); ?>
<?php echo wc_cart_totals_shipping_method_label( $method ); ?>
<input type="hidden" name="shipping_method[<?php echo $index; ?>]" data-index="<?php echo $index; ?>" id="shipping_method_<?php echo $index; ?>" value="<?php echo esc_attr( $method->id ); ?>" class="shipping_method" />
<p><?php _e( 'Please use the shipping calculator to see available shipping methods.', 'woocommerce' ); ?></p>
<?php elseif ( 'select' === get_option( 'woocommerce_shipping_method_format' ) ) : ?>
<?php elseif ( is_cart() ) : ?>
<p><?php _e( 'Please continue to the checkout and enter your full address to see if there are any available shipping methods.', 'woocommerce' ); ?></p>
<?php else : ?>
<p><?php _e( 'Please fill in your details to see available shipping methods.', 'woocommerce' ); ?></p>
<?php endif; ?>
<select name="shipping_method[<?php echo $index; ?>]" data-index="<?php echo $index; ?>" id="shipping_method_<?php echo $index; ?>" class="shipping_method">
<?php foreach ( $available_methods as $method ) : ?>
<option value="<?php echo esc_attr( $method->id ); ?>" <?php selected( $method->id, $chosen_method ); ?>><?php echo wc_cart_totals_shipping_method_label( $method ); ?></option>
<?php endforeach; ?>
</select>
<?php else : ?>
<?php if ( is_cart() ) : ?>
<?php echo apply_filters( 'woocommerce_cart_no_shipping_available_html',
'<p>' . __( 'There are no shipping methods available. Please double check your address, or contact us if you need any help.', 'woocommerce' ) . '</p>'
); ?>
<?php else : ?>
<?php echo apply_filters( 'woocommerce_no_shipping_available_html',
'<p>' . __( 'There are no shipping methods available. Please double check your address, or contact us if you need any help.', 'woocommerce' ) . '</p>'
); ?>
<?php endif; ?>
<ul id="shipping_method">
<?php foreach ( $available_methods as $method ) : ?>
<li>
<input type="radio" name="shipping_method[<?php echo $index; ?>]" data-index="<?php echo $index; ?>" id="shipping_method_<?php echo $index; ?>_<?php echo sanitize_title( $method->id ); ?>" value="<?php echo esc_attr( $method->id ); ?>" <?php checked( $method->id, $chosen_method ); ?> class="shipping_method" />
<label for="shipping_method_<?php echo $index; ?>_<?php echo sanitize_title( $method->id ); ?>"><?php echo wc_cart_totals_shipping_method_label( $method ); ?></label>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
<?php if ( $show_package_details ) : ?>
<?php
foreach ( $package['contents'] as $item_id => $values ) {
if ( $values['data']->needs_shipping() ) {
$product_names[] = $values['data']->get_title() . ' &times;' . $values['quantity'];
}
$product_names[] = $values['data']->get_title() . ' &times;' . $values['quantity'];
}
echo '<p class="woocommerce-shipping-contents"><small>' . __( 'Shipping', 'woocommerce' ) . ': ' . implode( ', ', $product_names ) . '</small></p>';
?>
<?php endif; ?>
<?php if ( is_cart() ) : ?>
<?php if ( is_cart() && ! $index ) : ?>
<?php woocommerce_shipping_calculator(); ?>
<?php endif; ?>
</td>

View File

@ -2,7 +2,7 @@
/**
* Cart totals
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product.php
* This template can be overridden by copying it to yourtheme/woocommerce/cart/cart-totals.php
*
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer)
* will need to copy the new files to your theme to maintain compatibility. We try to do this

View File

@ -2,7 +2,7 @@
/**
* Cart Page
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product.php
* This template can be overridden by copying it to yourtheme/woocommerce/cart/cart.php
*
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer)
* will need to copy the new files to your theme to maintain compatibility. We try to do this
@ -74,7 +74,7 @@ do_action( 'woocommerce_before_cart' ); ?>
?>
</td>
<td class="product-name">
<td class="product-name" data-title="<?php _e( 'Product', 'woocommerce' ); ?>">
<?php
if ( ! $_product->is_visible() ) {
echo apply_filters( 'woocommerce_cart_item_name', $_product->get_title(), $cart_item, $cart_item_key ) . '&nbsp;';
@ -92,13 +92,13 @@ do_action( 'woocommerce_before_cart' ); ?>
?>
</td>
<td class="product-price">
<td class="product-price" data-title="<?php _e( 'Price', 'woocommerce' ); ?>">
<?php
echo apply_filters( 'woocommerce_cart_item_price', WC()->cart->get_product_price( $_product ), $cart_item, $cart_item_key );
?>
</td>
<td class="product-quantity">
<td class="product-quantity" data-title="<?php _e( 'Quantity', 'woocommerce' ); ?>">
<?php
if ( $_product->is_sold_individually() ) {
$product_quantity = sprintf( '1 <input type="hidden" name="cart[%s][qty]" value="1" />', $cart_item_key );
@ -115,7 +115,7 @@ do_action( 'woocommerce_before_cart' ); ?>
?>
</td>
<td class="product-subtotal">
<td class="product-subtotal" data-title="<?php _e( 'Total', 'woocommerce' ); ?>">
<?php
echo apply_filters( 'woocommerce_cart_item_subtotal', WC()->cart->get_product_subtotal( $_product, $cart_item['quantity'] ), $cart_item, $cart_item_key );
?>

View File

@ -2,7 +2,7 @@
/**
* Cross-sells
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product.php
* This template can be overridden by copying it to yourtheme/woocommerce/cart/cross-sells.php
*
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer)
* will need to copy the new files to your theme to maintain compatibility. We try to do this

View File

@ -4,7 +4,7 @@
*
* Contains the markup for the mini-cart, used by the cart widget
*
* This template can be overridden by copying it to yourtheme/woocommerce/single-product.php
* This template can be overridden by copying it to yourtheme/woocommerce/cart/mini-cart.php
*
* HOWEVER, on occasion WooCommerce will need to update template files and you (the theme developer)
* will need to copy the new files to your theme to maintain compatibility. We try to do this

Some files were not shown because too many files have changed in this diff Show More