Release Notes
Information¶
Releases use the following numbering system: {major}.{minor}.{incremental}
- major: Major refactoring or rewrite - make sure you read and test very carefully!
- minor: Breaking change in some circumstances, or a new feature. Read carefully and make sure you understand the impact of the change.
-
incremental: A "safe" change / improvement. Should always be safe to upgrade.
-
[BC]: Items marked with [BC] indicate a breaking change that will require updates to your code if you are using that code in your extension.
1.5.0 (2026-09-09)¶
- Requires CiviCRM 6.17 (was 6.16).
- Cart access is now a row-level ACL on cart ownership instead of a constraint bolted onto
CartItem.delete. A cart is reachable if you own it - bycontact_idwhen logged in, bysession_idwhen anonymous - and it is stillopen;administer CiviCRM datareaches every cart. The rule now coversgetandupdateas well asdelete, onCart,CartItemandCartItemDiscount. - This fixes three things the 1.4.0 constraint got wrong, all of which failed by deleting nothing and reporting success: server-side calls passing
checkPermissions => FALSEwere scoped too (so cron, queue workers and any headless test silently removed nothing); no administrator could act on another contact's cart, because everyone holding "access shopping cart" was scoped to their own; andCartItem.get/updatewere not scoped at all, so any holder of the permission could read or modify any cart item by id. access shopping cartis now declaredimplied_byadminister CiviCRM data, so a data administrator no longer has to be granted it explicitly. Requires CiviCRM 5.71+, well below the 6.17 minimum.- Add an index on
Cart.session_id. The row-level ACL matches an anonymous visitor's carts on that column, which was a full table scan ofcivicrm_cart- a table that grows with every abandoned cart.contact_idneeds nothing; its foreign key already indexes it. - Security:
Cart.submitnow refuses a cart the caller cannot reach. It reads the cart with permissions off - submitting has to see the whole cart whoever its items are for - so the row-level ACL never applied, and any holder of "access shopping cart" could submit another contact's open cart by id, creating an Order, Participants and Memberships billed to that contact and consuming their discount codes. Present since carts were introduced, not just in 1.4.0. - Security [BC]: a cart item may now only name a contact the user is allowed to act for. Reaching your own cart was not previously a constraint on whose item it was, so any holder of "access shopping cart" could add an item for an arbitrary contact - or re-attribute an existing one - and on submit that contact received a Participant or Membership. The rule is core's own: your own contact, "edit all contacts", a contact ACL, or a permissioned relationship, which is what keeps buying for an employee or family member working. Sites that relied on naming unrelated contacts will need a permissioned relationship, or
administer CiviCRM data. - Security:
Cart.submitno longer accepts acontactIDfrom an untrusted caller unless they may act for that contact. An anonymous cart carries no contact, so the caller had to supply one - and an unchecked id meant an anonymous visitor could raise the Order against any contact they guessed.afformShoppingCartCheckoutnow records the contact on the cart itself during submission, so anonymous checkout is unaffected. - Security: the "View My Cart" table no longer sets "Bypass permissions". That setting disabled permission checks for the table's own query and for its inline editing, and
SearchDisplay.inlineEditwrites the row id straight from the request - so any user of the public cart form could re-attribute any cart item in the database by id. Thecart_idfilter this extension injects did not cover it, applying only toSearchDisplay.run. The row-level ACL now scopes the table instead, which needs no bypass;docs/setup.mdpreviously said the bypass was required, and no longer does. - Add civilint, PHPCompatibility and jshint jobs to GitLab CI, from the shared
infra/extension-citemplates, so a merge request gets a real red/green status instead of only the CiviCARROT bot comment.
1.4.0 (2026-08-28)¶
- Requires CiviCRM 6.16 (was 6.15).
- Replace the
afform_paymentsdependency with core'scivi_contribute.afform_paymentswas merged into core and is obsolete from 6.14. Cart.Submitcan create aContributionRecuralongside the Contribution - setcontributionRecurValueson theSubmitCartEvent. See docs/cartsubmission.md. [BC]SubmitCartEvent::__construct()takes a fourth$contributionRecurValuesargument; update any code constructing the event directly.SubmitCartEvent::trigger()defaults it, so those callers are unaffected.- New
civi.shoppingcart.addcartitemsevent, dispatched byCartItems::add()before items are persisted, so a listener can append or modify them.PriceFieldValue.addToCartand Afform submission both route through it. See docs/cartitemadd.md. PriceFieldValue.addToCartadds one cart item per selected price option. A price set attached to several events previously produced one item per event.- Submitting an empty cart now fails instead of creating a zero-amount pending Contribution and closing the cart.
CartItem.deleteis now constrained to the requester's own open cart. A user holding "access shopping cart" could previously delete another user's cart items by id.- Add the missing
": "between the event title and the description on Participant cart items. - Add a PHPUnit test suite:
CIVICRM_UF=UnitTests phpunit9 tests/phpunit. - Correct the ContributionRecur docs: once
contributionRecurValuesis set,frequency_unitis mandatory andfinancial_type_idmust be a numeric id. - Fix a broken image path in the discounts docs, and add the "Adding cart items" page to the docs nav.
- Rewrite README.md, which still described cart submission and payment as unimplemented, and add the standard "Support and Maintenance" section to it and to the docs home page.
- Coding-standard fixes and missing docblock params throughout, and the
arrayreturn type on the lastgetSubscribedEvents()that was missing it - without which the extension is fatal on a Symfony 7 core.
1.3.0 (2026-08-05)¶
- Add support for anonymous (not logged in) users. The cart is tracked via a
session_idfield when there is no logged in contact, instead ofcontact_id. [BC]CRM_Shoppingcart_BAO_Cart::getCurrentCart()has been renamed togetCurrentOpenCartID()- update any code calling it directly. Cart.SubmitAPI now accepts acontactIDparam, for submitting an anonymous cart once a contact has been identified/created (eg. via an Afform submit handler).- The
cart_idfilter automatically applied to the "View My Cart" SearchDisplay is now configurable via the newshoppingcart_cart_display_namessetting, instead of being hardcoded toView_My_Cart_Table. See docs/setup.md. - Fix mapping of related entities (Participant/Event, Membership/MembershipType) so entity_table/entity_id and the Event title are looked up correctly.
- Don't apply discounts to zero-cost lineitems.
- Show full error details on the frontend when Cart.Submit fails.
1.2.1 (2026-02-18)¶
- Fix setting unit_price/line_total when there is no discount and quantity > 1. This changes CartItem.discounted_amount to CartItem.discounted_line_total and CartItem.discounted_unit_price - you need to update your searches!
- Increase priority of validate listener (for compatibility with Formbuilder Payments).
- Improve handling of lineitems.
- Add CiviDiscount dependency.