In an environment with persistent object caching, concurrent calls
to delete_option() + add_option() can result in the option value
leaking out of the alloptions cache key, and into its own cache
item under the options group, while deleting the value from the
database.
This causes future function calls to add_option() to fail, since
the value already exists in cache (under the wrong key). It also
causes calls to delete_option() to fail, since the value is not
in the database.
This commit forces update_option() instead of the delete + add
combination, as well as removes multiple unnecessary calls to
update the woocommerce_db_version from admin notes and notices.
This commit reverts the functionality introduced in PR #26260
(later refined by #27175, #27190, #27508) in which filtering by
attribute using the layered nav widget was improved to handle the
cases of variations out of stock. The revert is a response to the
numerous problems reported by users in Woo 4.4 and 4.5
Not all the code has been reverted, only the code that resulted in
visible functionality changes. Thus, the code that generates
term relationships for variations is still in place to keep database
consistency and to keep the reverting changes to the minimum needed.
We were doing state and postcode even for countries where its not required, but unfortunately as an unintended effect we were ending up not checking shipping requirements if this was not met.
This commit changes the order of the error handling check to protect the code against a possible fatal error if wp_safe_remote_post() returns an instance of WP_Error().
This will introduce two new filters:
woocommerce_from_product_type_changed
woocommerce_to_product_type_changed
to filter the $from and $to variables respectively.
This will be useful in WCS pr_3732!
This commit fixes a bug that made it impossible to assign to a product a tax class that contained non-ASCII characters that are URL encoded by sanitize_title().
WooCommerce uses sanitize_title() to generate a slug when creating a tax class (d48f1d4e2e/includes/class-wc-tax.php (L808)). sanitize_title() converts some non-ASCII to ASCII equivalents (those handled by remove_accents()) and URL encodes others (like some Greek characters, for example).
The code was using wc_clean() to sanitize the tax class when the user edited a product. The problem is that wc_clean() removes URL encoded characters, changing the slug of some tax class, causing WooCommerce to use the standard tax class instead without any errors. To fix this issue, this commit replaces wc_clean() with sanitize_title(). This should be enough for security purposes and should not cause any issues with non-ASCII characters.
Implements the following expected behaviour:
1. Switch from variable subscription to non-variable one will delete variations.
2. Switch from variable subscription to variable product and vice-versa will NOT delete any variations.
This shall have no side-effects in WooCommerce Core.
By changing to auto invoking call the `c` variable will be encapsulated in its scope therefore not polluting the global scope and will continue to function as previously.
Fixes conflict and reassigning of already used variables when using other minified scripts. In my case the problem occurred with the Speed Booster Pack plugin.
The previous query was counting variable products twice when they
had a variation with a concrete value for the attribute and also
a variation with "Any..." value for the same attribute.
The new query fixes a bug where variations were being counted twice:
if a product was included in both the queries then it would be counted
differently and added; e.g. when a product had two variations,
one with "Any" attribute and other with a attribute that has a value.
The new query also optimizes performance, so that filter conditions
can be improved and better indexes can be used.
For performance reasons the query is split in two: one for simple
products and variations with a concrete attribute value, and another
one for variations having "Any..." as the attribute value.
Previously, we were using the `$formatted_meta_data` to build the final array.
However, this does not consider the fact that
`WC_Order_Item->get_formatted_meta_data` can exclude `meta_data` from the
result. There would be less `meta_data` objects return than the previous
implementation.
This fixes the issue by using the `$data['meta_data']` value as the main list of
meta data and only using `$formatted_meta_data` to optionally apply the
`display_key` and `display_value` properties.
The new query doesn't need empty attribute entries in the meta table,
therefore the code that generates them and the migration to backfill
the missing existing ones have been removed.
When set_attributes is used on WC_Product to remove existing attributes
the wc_product_attribute_uasort_comparison ends up being called
with a null argument, and this breaks tests in PHP 7.4 since
null is used as an array. This commit modifies the function so that
if null is passed no array access is attempted.
Right now the filter by attributes widget counts available variations
(for variation products). This is confusing since the counter shows
numbers that are higher than the actual count of products displayed.
This commit changes the query used by the widget so that instead
of counting variations it returns the parent product ids, and then
counts the distinct values. This also covers the case of products
where some of the variations have concrete values and some have
"Any..." values.
When "Create variations from all attributes" is used to create
variations it generates term relationship entries for all the generated
variations, however that doesn't happen when the term can be interpreted
as a numeric value. This is because in that case product->get_attributes
returns the attribute values as numbers, but the code that generates
the term relationships expect those to always be strings.
When manually adding a given variation this doesn't happen.
The fix is to simply strval-ize the value before using it, but it might
be worth investigating why this is happening.
The previous commit fixes a bug that causes the "attribute_" metadata
with an empty value to not be created when a new variation attribute
is added to the product (so that all variations have the attribute
with a value of "Any..."). This commit adds a data migration to
backfill the missing metadata for existing variations.
When a new variation attribute is added, the corresponding 'attribute_'
meta entries are added for all variations with an empty value;
and when an existing variation attribute is removed, the existing
'attribute_' meta entries are deleted for all variations.
This is necessary for the filter by attribute widget to work properly
when variations exist with a value of "Any..." for attributes.
When a variation product has an attribute with a value of "Any...",
and there's a filter by attribute widget for that attribute, then
that product won't be included in the counts displayed in the widget
(and if the count ends up being zero, the attribute won't be shown
in the widget).
This happens before since Woo 4.4, this widget works by looking at
entries in the term relationships table for varitions too
(used to do so for simple products and for "main" variable products
only), see PR #26260; but there are no such entries for
"Any..." attributes.
This commit fixes that by extending the SQL query used by the widget
to look for variations that have empty attribute values in the meta
table too.
Previously 'dirname( __FILE__ )' was used to import files, however, the directory separator was missing.
This commit replaces 'dirname( __FILE__ )' that was introduced in 5370d02484 with __DIR__ and added DIRECTORY_SEPARATOR
Relative include paths in PHP can break whenever the server is running opcache. As such, WordPress.com deploy system refuses to include WooCommerce because of that issue.
This commit changes the relative include paths to absolute include paths.
Relates to #27269
This bug was introduced in #26260. The sequence is:
1. WC_Query::adjust_posts_count runs, to handle found_posts filter,
this indirectly executes wc_setup_loop.
2. At this point $GLOBALS['wp_query']->max_num_pages hasn't been set
yet, and has a value of 0. Thus the loop variable total_pages
is set to 0.
3. Later wc_setup_loop runs again and this time
$GLOBALS['wp_query']->max_num_pages is already set, but since
the loop variable total_pages already exists, it keeps its
value of 0.
4. The pagination controls never show if total_pages is less than 2.
The fix consists of hooking into the_posts to set the value of
total_pages again, at that point $GLOBALS['wp_query']->max_num_pages
is already set.
PR #26260 introduced a handler for 'found_posts' filter in WC_Query
class in order to adjust the count depending on the visibility
of variation products. However the handler incorrectly assumed
that the filter was triggered only when listing products, when
actually it's also triggered for any post type e.g. pages.
In these cases the post count was set to zero, which caused bugs.
Now the handler starts with the originally supplied posts count,
and only decrements it when a post is a product AND is not visible.
- Fix a call to get_available_variations with incorrect optional arguments.
- Add code that has been removed in WC_Product_Variable::get_available_variations.
Now, if there are filters present the logic is as follows:
- For multiple filtering values of the same attribute:
the product is visible if there's at least one variation
that has one of the filtering values associated to the attribute,
or if there's at least one variation having the attribute
with a value of "Any".
- For filtering by more than one attribute:
the product is visible if there's at least one variation that
is visible for ALL the attributes according to the above rule.
Note that this is irrespective of the type of logic configured for
the filter (OR or AND).
Two adjustments were needed:
- Adjust the count even when there's no nav filtering in the query.
This is necessary to present the proper products count.
even when the woocommerce_product_is_visible filter is used.
- Account for the case where $GLOBALS['wp_query']->posts
returns objects instead of ids (for example when viewing
a product page).
After the change that registers variation attributes as terms
(in addition to reigstering them as post meta) it is now time
to modify the get_filtered_term_product_counts methods in
WC_Widget_Layered_Nav so that it works consistently for both
variable and non-variable products. The logic for the counters
is now as follows:
with OR operator:
- Simple products: count the attributes of all visible products
(unchanged behavior).
- Variable products: count attributes corresponding to
visible variations.
with AND operator:
- Simple products: count the attributes of visible products but only
for products that have all the selected (unchanged behavior).
- Variable products: find all the products for which all the variations
corresponding to the selected attributes exist and are visible,
then count the attributes corresponding to the visible variations
of those products.
A product is "visible" if it's published, not excluded for catalog,
and has stock. Additionally, a variable product will not be considered
visible if the parent product is not.
Product attributes are currently recorded as terms in
wp_term_relationships (product attributes are actually taxonomies).
In the case of variable products this is true for the main product,
but not for the variations. The attributes used to define variations
are stored as post meta, but nothing is recorded in the term
relationships table.
This is a problem when using the layered nav filtering plugin,
since the attribute counters displayed are calculated based solely
on the contents of the term relationships table. Adding meta queries
would be really messy (especially when the widget is configured
with AND operator) and would probably also hurt performance.
This commit adds a change to store the attributes for variations
as term relationships, additionally to storing them as post meta.
Terms are stored on variation creation, and updated/deleted together
with the variation as appropriate. "Any" variations (stored in meta
as empty values) are not stored as terms.
Additionally, a database upgrade is included in order to backfill
terms for already existing products.
The layered nav filtering doesn't work well with variable products
when some variations have stock and other don't. When a term is
selected in the widget, a variable product having no stock for
the variation corresponding to that term but having stock for
other variations will be displayed, but it shouldn't.
This commit fixes that by introducing two changes:
- A new override of "is_visible" for WC_Product_Variable that
looks at the supplied filters, compares them against the corresponding
available variations and calculates the visibility based on
the query type (OR or AND).
- A hook on the "found_posts" filter in WC_Query, that adjusts
the posts count based on the found products visibility
when there are filters available; this is needed to sync the
"displaying X posts" messages and the paging when variable
products are hidden due to stock status.
Additionally, the visibility calculated in "found_posts" is cached
as loop variables so that it isn't calculated again when actually
displaying the products.
Using WooCommerce on a multisite network using the hyperdb drop-in, the foreign key check fails although there is a foreign key.
32037e37dd/includes/class-wc-install.php (L745-L763)
The first problem is that hyperdb wasn't designed to route `information_schema` queries. After patching hyperdb, the query is routed to the correct database.
The second problem is that the query still finds zero foreign keys because `$wpdb->dbname` is blank when the query is generated. Hyperdb only has a dbname in the context of a query being executed; hyperdb extracts the table name, then maps that to the correct database.
Although we could find a way to support such `information_schema` queries, they are also not preferred because they are notoriously slow. On WordPress.com they take 300ms, versus 3ms for a `SHOW CREATE TABLE` which similarly reveals the existence of the foreign key.
The proposed changes simplify the check, make it faster, and make it work with hyperdb.
This makes match with WP sanitization for post_title.
WP sanitize post_title using kses_init_filters() when
the current user can't use unfiltered HTML.
It is possible for a later duplicate webhook to be fired too early if
the same webhook triggers in one request more than once with the updated
changes from the second one missing if it happens too quickly.
This queues all webhook to be register on shutdown instead of just
syncronous ones to make sure all data from the request is updated first
before the webhook gets queued.
`wc_round_tax_total` is for when prices are inclusive of taxes, however, shipping taxes can never include taxes so they should always use `round( $price, wc_price_get_decimals() )` instead.
When rounding at subtotal is enabled, line items are already rounded where they need to, so we only should do any further rounding when displaying values and no where else.
This is part of our larger attempt to remove individual rounding as much as possible.
- src/README.md largely expanded
- tests/README.md expanded
- includes/README.md added
- src/Internal/REAMDE.md added
src/README.md and includes/README.md have TODO placeholders to add
guidelines regarding to actions and filters.
The following methods are added and can be invoked using `WC()`,
they just redirect to the same methods in LegacyProxy:
call_function
call_static
get_instance_of
- Method and class renames.
- Removed unnecessary autoloader registration.
- Add a unit test for classes with non-object type hints
in constructor arguments.
- The `Container` class now implements `Psr\Container\ContainerInterface`
(and registers itself as such), holding a private instance of the
real container. This way it's a read-only container from the point
of view of plugins (which should use their own containers, but
can still use this to get WooCommerce classes).
- All registrations are now done in the `Container` constructor via
service providers.
- The container instance is now held in a global variable, set in
`woocommerce.php`
- Added the `wc_get_container` function for old code.
- Added the `AbstractServiceProvider` class, which inherits with the
corresponding League's class and adds some utility methods,
most notably `add/shareWithAutoArguments`.
- Added the `ActionsProxy` and `LegacyProxy` classes, they are
registered via a dedicated service provider.
- `WC_Queue_Interface` is no longer resolvable via the container
(which is for classes inside `src` only).
- All the method names in the new classes have the format `fooBarFizz`
to be PSR4 compliant, so the MethodNameInvalid error has been
disabled in phpcs.xml for the `src` directory.
- Introduced the `@public` annotation for public API classes
(classes that plugins can use and whose backwards compatibility
we guarantee), applied to `ActionsProxy` and to `LegacyProxy` for now.
- Removed the hack for the autoloader as now it doesn't work anyway.
For the changes in this branch to work, now WP_DEBUG must be false.
- Renamed from `ObjectContainer` to `Container`.
- It now inherits from PHP League's `Container`.
- It has now a `defineAsSharedAutowired` method.
- Initialization moved to the `WooCommerce::init_container` method.
- The static method for object resolution is now `WooCommerce::get_instance_of`.
- Add PHP League's Container package via Composer.
- Add an ObjectContainer class that encapsulates all the configuration
and insulates the codebase from the concrete DI engine used.
- Add an improved ReflectionContainer class that will allow to
register individual classes as singletons while autowiring.
- Use ObjectContainer to resolve the WooCommerce class, everything
instantiated with "new" inside it, and all singletons that are
usually obtained via WC() function.
- Introduce the CustomerProvider class.
- Introduce a service provider to resolve WC_Queue_Interface,
this replaces the WC_Queue class.
- Mark as obsolete all the replaced "instance()" methods,
and the entire WC_Queue class.
While variations only uses "published" and "private" statuses when
exporting we should display the variations as "draft" in case the parent
product it's also a draft.
At some point the 'change_stock' key is assumed to be present
in the request data, but it might not. Fixed to test for existence
before using the value.
Create a new `request_data` method in WC_Admin_Post_Types that
just returns $_REQUEST. This is intended to ease unit testing,
as this method can be easily mocked to return test data.
For bulk edit: even if stock status was left as "No change", the
status of all variations was being changed to whatever the status
of the product was before it was converted to variable. Now
no change is performed when "No change" is selected, and all
variations change to whatever is selected otherwise.
For quick edit: a new "No change" option is added that will be
preselected when the product is variable. Previously, whatever
status the product had before being converted to variable was being
shown, and that's the status that would be set when saving.
Also, a "This will change the stock status of all variations"
message is displayed before the selector.
Two methods have been created:
- update_stock_status, replaces code that was duplicated in the
quick_edit_save and bulk_edit_save methods.
- set_new_price, replaces code that was duplicated-ish in the
bulk_edit_save for setting the new regular and sales prices
(code was not identical but very similar).
Also, `round` is now used on sale price calculations that involve
multiplying by a percent, the same was as it was done already
to calculate the regular price.
Some of our endpoints don't have an "<id>" parameter but we're expecting one in the CLI. Since the `id` is already part of the supported IDs we don't actually need this since it will pull it from the route.
Fix the error message shown when the max setting for rows is shown. before it stated "The minimum allowed setting is 8" when it should be "The maximum allowed setting is 8"
Methods `wc_maybe_increase_stock_levels` and `wc_maybe_reduce_stock_levels` already reduce/increase stock levels when statuses are changed, so no need to do this here.
When a product is saved its validate_props method is invoked,
and this recalculates the stock_status property based on whether
the product manages stock or not, the stock quantity, and the
value of the woocommerce_notify_no_stock_amount option.
In the case of variable products, and when stock is managed, the stock
was set to "instock" when the current stock was enough, but only
if the "stock_quantity" property was in the list of changed properties
for the object (the method in the base product class doen't check
for changed properties). This is a problem because the
wc_update_product_stock function updates stock_quantity but via direct
database modification, and thus stock_quantity isn't considered
modified. Therefore stock modifications via wc_update_product_stock
don't update stock_status on the product (e.g. when going from 0 to 1
after a refund the stock status will remain as "outofstock").
The fix consists of removing the check for changed properties since
it's not done anyway in the other cases (when stock is below the
woocommerce_notify_no_stock_amount threshold) nor in the base class.
Also, validate_props is refactored for readabiliyy, and an useless
set_stock_status() call placed right before save()
in wc_update_product_stock is removed.
One of the problems with synchronous webhooks is that they are executed as soon as the related action is. Since we may call an action multiple times in the process of updating something, this causes only the first action to trigger the hook. This differs from asynchronous execution because in that case, the web hook will be executed after the entire request has completed.
When a logged-out user tries to check out with an email address associated with an existing account, they are prompted to log in. This PR updates that prompt with a link to the login form. Though other opportunities to log in should already exist elsewhere on the checkout page (such as `form-login.php`'s "Returning customer?" prompt), this change makes it more explicit and intuitive.
1. Use '0000-00-00 00:00:00' instead of CURRENT_TIMESTAMP as default value to support MySQL 5.6
2. Return early if DB version is less than 430 because then it would mean that required wc_reserved_stock table might not be present.
With the increased cadence of releases it becomes necessary that we address the `WC tested up to` header's usefulness. It isn't practical to require everyone to update their extensions every month, especially given that we are only doing backwards compatible minor releases. The only case I can think of where we might want to check the minor version is if the Stable tag on Core is downgraded, but due to the naming of the header, this doesn't make any sense.
I considered making this a wildcard of some kind but I think most would bind to a full major version anyway and so this isn't worth the time to add it. As an aside, the tests in `plugin-updates.php` seem to indicate that a header of `WC tested up to: 4` would apply to the entire major version cycle, so wildcards already exist!