Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create additional download permissions on product save if needed #28521

Merged
merged 8 commits into from
Jan 18, 2021

Conversation

Konamiman
Copy link
Contributor

@Konamiman Konamiman commented Dec 9, 2020

All Submissions:

Changes proposed in this Pull Request:

When a simple product with downloads gets converted into a variable product all the existing download permissions for past orders become invalid. This commit adds an extra verification procedure to the product save code:

  1. Get all the existing download permissions for the product and all the children (variations).
  2. For each download permission for the parent product, if there's a variation that offers the same file for download (same file URL) AND an equivalent download permission doesn't exist (equivalent means same file URL, same order id and same user id), then create it. This is done in a scheduled action.

Additionally, a new WC_Customer_Download_Data_Store::create_from_data method is added.

Closes #26475.

How to test the changes in this Pull Request:

  1. Start in master.
  2. Create a simple product as "downloadable" with three downloadable files, let's call them file1, file2 and file3.
  3. Complete an order for the product, you should have access to all three files (in "My Account - Downloads").
  4. Convert the product to variable, with two downloadable variations. Make one variation offer file1 and the other one file2 (use the same file URLs, display names for the files can be different).
  5. Save the product, and verify that you have lost access to the files.
  6. Switch to this branch, go to the product edit page and save without changing anything.
  7. Go to WooCommerce - Status - Scheduled Actions - Pending, you should see one named adjust_download_permissions. Wait until it runs, or run it yourself.
  8. Verify that you got access to file1 and file2 again, but not to file3.
  9. Save the product again, and wait until the scheduled action runs again.
  10. Check the contents of the wp_woocommerce_downloadable_product_permissions table in the database, there should be no duplicate entries.

Other information:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes, as applicable?
  • Have you successfully run tests with your changes locally?

Changelog entry

Enhancement - Create additional download permissions for simple downloadable products that are converted to variable products provided that there are variations offering the same files.

When a simple product with downloads gets converted into a variable
product all the existing download permissions for past orders become
invalid. This commit adds an extra verification procedure to the
product save code:

- Get all the existing download permissions for the product and all
  the children (variations)
- For each download permission for the parent product, if there's a
  variation that offers the same file for download (same file URL)
  AND an equivalent download permission doesn't exist (equivalent means
  same file URL, same order id and same user id), then create it.

Additionally, a new WC_Customer_Download_Data_Store::create_from_data
method is added.
@Konamiman Konamiman self-assigned this Dec 9, 2020
Also, move all the new code from the 'WC_Product_Data_Store_CPT' class
to a new separate 'DownloadPermissionsAdjuster' class.
…load_permissions

Also replace direct invocations of functions with usages of the
LegacyProxy whenever needed, and code style ajustments.
The DownloadPermissionsAdjuster class hooks to adjust_download_permissions
from within its init method. However this method is executed only
if the class is resolved, otherwise the hooks doesn't get attached
and then the scheduled action is not serviced.

To solve this, the class is resolved from WooCommerce::init_hooks.
This requires a change in DownloadPermissionsAdjuster::init
to use wc_get_container()->get( LegacyProxy::class )->get_instance_of
instead of WC()->get_instance_of, since WC() can't be used from
WooCommerce::construct (which invokes init_hooks).
@Konamiman Konamiman marked this pull request as ready for review January 14, 2021 16:36
@Konamiman Konamiman requested review from a team and roykho and removed request for a team January 14, 2021 16:36
@roykho
Copy link
Member

roykho commented Jan 14, 2021

Was there no way to separate the product from the order once it was ordered? What about just storing the file URL within the order meta? This way you wouldn't need to re-generate permissions.

@Konamiman
Copy link
Contributor Author

Konamiman commented Jan 15, 2021

Hi @roykho. Thanks for your suggestion, but unless I'm missing/misunderstanding something that wouldn't help:

Downloadable files are ultimately tied to products, not (or not only) orders. If a product ceases to exist or ceases to have the downloadable file then the associated download permissions are no longer valid. That's why the wp_woocommerce_downloadable_product_permissions table holds both the order id and the product id.

So if we were to store the file id (probably that and not the URL, as in wp_woocommerce_downloadable_product_permissions) in order meta then we would have to either store the product id there as well, or join the product permissions table to get it; in any case the original problem (missing permissions when converting to variable product) would still be present.

A seconday concern is that we are planning to move towards dedicated tables, so storing yet more stuff in post meta should be avoided whenever possible. Again, unless there's something I'm missing.

@roykho
Copy link
Member

roykho commented Jan 15, 2021

Downloadable files are ultimately tied to products, not (or not only) orders.

What you're saying is currently how it works and I understand that however I believe that is not the best approach. What I am suggesting is change how WC works in terms of what is ordered and the products.

Ideally, after an order is made, I can delete the product completely and the order should still have all the information necessary to fulfill the order obligation. That should include the original price of the product, taxes and any downloadable files attached to that product...etc.

So if we were to store the file id (probably that and not the URL, as in wp_woocommerce_downloadable_product_permissions) in order meta then we would have to either store the product id there as well, or join the product permissions table to get it; in any case the original problem (missing permissions when converting to variable product) would still be present.

We would not need the product ID because the download permission table has order product id/order id already so it knows which permissions are tied to the order.

As I said in the comment of the original issue, I believe to make it work correctly will be a heavy lift. Your PR can be used as an interim however if you feel strongly about getting this in.

@Konamiman
Copy link
Contributor Author

Ideally, after an order is made, I can delete the product completely and the order should still have all the information necessary to fulfill the order obligation. That should include the original price of the product, taxes and any downloadable files attached to that product...etc.

That makes sense but it's also a little bit tricky. I'm thinking of a situation where I want to completely discontinue a software product I used to sell because it's too old and maybe it even has security issues; on one hand if a customer has purchased something it seems logical to give him access to the download "at eternum", on the other hand it may be better to remove access (because you provided an alternative or the product is really useless by now). Although this is already kind of contemplated in the "access expires" field.

Anyway, I agree with what you say next:

I believe to make it work correctly will be a heavy lift.

Yep. This would need some extended discussion.

Your PR can be used as an interim however if you feel strongly about getting this in.

I think that if this works as I intend it to work, it's indeed a good solution until we can figure out a better mechanism for download permissions in general.

Copy link
Member

@roykho roykho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested well, good job! Since this is a pretty big PR, I just skim through the parts and make sure they made sense. I just left some small comments.

* Assumes that all the keys in the passed data are valid.
*
* @param array $data Data to create the permission for.
* @returns int The database id of the created permission, or false if the permission creation failed.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be @return here. Without the s

return $adjusted_date;
}

throw new Exception( "I don't know how to get a date from a " . is_object( $date ) ? get_class( $date ) : gettype( $date ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this not need to be translatable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding was that since it's an exception message it should rarely be displayed and if it is, the viewer will probably be a developer. But anyway, I see that our convention in the code is to translate the exception messages so I'll do the same.

@Konamiman Konamiman requested a review from roykho January 18, 2021 14:31
Copy link
Member

@roykho roykho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks!

@roykho roykho merged commit 4c54895 into master Jan 18, 2021
@roykho roykho deleted the fix/26475 branch January 18, 2021 14:51
@woocommercebot woocommercebot added release: add changelog Mark all PRs that have not had their changelog entries added. [auto] release: add testing instructions PRs that have not had testing instructions added to the wiki. [auto] labels Jan 18, 2021
@roykho roykho removed the release: add testing instructions PRs that have not had testing instructions added to the wiki. [auto] label Jan 18, 2021
@Konamiman Konamiman added this to the 5.0.0 milestone Jan 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release: add changelog Mark all PRs that have not had their changelog entries added. [auto]
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add warning on changing downloadable products
3 participants