Search by

therealworld / cookieconsent-module

therealworld

Cookie Consent Solution for OXID Shops

Package info

bitbucket.org/therealworld/cookieconsent-module

Homepage

Issues

Type:oxideshop-module

pkg:composer/therealworld/cookieconsent-module

Statistics

Installs: 738

Dependents: 0

Suggesters: 0


README

Deutsche Fassung: README.de.md

vendor-logo the-real-world.de

description

A cookie consent banner for the OXID shop. The shop operator maintains the cookies in the admin and assigns each one a type (necessary, performance, functional, marketing). Visitors accept all cookies, only the necessary ones, or choose per cookie. Templates ask oViewConf.isAllowedCookie('<ident>') and only output a script when the visitor agreed to it.

  • Composer package: therealworld/cookieconsent-module
  • OXID module id: trwcookieconsent

Requirements

  • OXID eShop CE >= 7.4, PHP ^8.3, ext-json
  • therealworld/tools-plugin >= 3.9.0
  • A Bootstrap 5 based theme: the banner is a Bootstrap modal with an accordion and is opened through the global bootstrap.Modal (data-bs-* attributes)

Installation

composer require therealworld/cookieconsent-module
vendor/bin/oe-console trw:module:setup trwcookieconsent

trw:module:setup (from therealworld/clirun-plugin) installs the module, runs its database migration and activates it. Without the clirun-plugin: vendor/bin/oe-console oe:module:install vendor/therealworld/cookieconsent-module, vendor/bin/oe-eshop-db_migrate migrations:migrate trwcookieconsent and vendor/bin/oe-console oe:module:activate trwcookieconsent.

On activation the module

  • switches on OXID's own option "show cookie notification" (blShowCookiesNotification) for every shop — the banner is rendered through that core widget, so it stays hidden while the option is off
  • creates the CMS page trwcookieconsentpopup (folder "user information") with placeholder title and text, unless it exists already

Configuration

Module setting

Setting (admin)NameDefaultEffect
Own cookies expiry timenumTRWCookieConsentExpires31536000 (1 year)Lifetime in seconds of the module's own cookie trwcookieconsentallowedcookies, which stores the visitor's choice

Banner text

Title and text of the banner come from the CMS page trwcookieconsentpopup (Customer Info > CMS Pages). Replace the placeholder content in every language. The title is also used as the label of the button that reopens the banner.

Cookies

Service > Cookie-Admin lists the cookies; New Cookie creates one. Per cookie:

FieldMeaning
ActiveOnly active cookies are shown in the banner. The banner appears at all only when at least one active cookie exists in the current shop
Cookie IDThe ident the templates ask for (isAllowedCookie('googleanalytics'))
Cookie titleLabel shown to the visitor (per language)
Cookie Typenecessary, performance, functional or advertising (shown as "Marketing") — the accordion groups of the banner
SortingOrder within the banner
Cache-relevantFolds the consent state of this cookie into the key of the static page cache (see below)

Cookies of type necessary are always checked and cannot be deselected.

In multishop editions the shop's Mall tab gets the option "Inherit all (TRW) cookies from parent shop".

Frontend behaviour

  • The stylesheet goes into the <head> (block head_css), the banner itself is rendered at the end of the body (block base_js), so on a slow connection the page is painted first. It is not rendered on clearcookies, mallstart and plain popup pages (?plain=1).
  • As long as the visitor has not decided, the banner opens on its own. Afterwards a cookie button next to the mini basket reopens it.
  • "Accept all", "select only Necessary" and "Save" send the choice in the background to cl=CookieConsentDetailsController&fnc=setCookiesToSession; it is stored as JSON ({"<ident>": true|false}) in the cookie trwcookieconsentallowedcookies.
  • If the saved choice differs from the previous one, the page reloads, so server-side isAllowedCookie() checks take effect immediately. An unchanged choice just closes the banner.

Using the consent in templates

{% if oViewConf.isAllowedCookie('googleanalytics') %}
    {# tracking code #}
{% endif %}

oViewConf.showAllowedCookie() returns the full map, oViewConf.isCookieConsentActive() tells whether the banner is in use in the current shop.

Static page cache

When a page is cached statically (e.g. by therealworld/staticcache-module), server-side isAllowedCookie() checks would be frozen into the cache. Flag such cookies as Cache-relevant: the module then contributes the accepted cache-relevant cookies to the cache key, and each combination gets its own cache variant. Cookies whose scripts are switched on client-side do not need the flag — every flagged cookie multiplies the number of variants.

Consent for other modules (consent contract)

The module implements the consent contract of the tools-plugin (TheRealWorld\ToolsPlugin\Contract\ConsentProviderInterface). Modules that ask for consent before tracking (e.g. therealworld/matomo-module) therefore need no dependency on this module - and vice versa.

  • The banner counts as in use as soon as the current shop has at least one active cookie (isConsentRequired()). Without an active cookie the module passes both questions on to the default provider: no consent required, everything allowed.
  • hasConsent('<ident>', '<type>') looks up the cookie with this cookie ID and returns the visitor's choice (cookies of type necessary always true). If there is no cookie with that ID, consent to at least one cookie of the given type (performance, functional, advertising) is enough; without a type the answer is no.
  • For Matomo, create an active cookie with the ID matomo and the type performance; with a static page cache also flag it Cache-relevant.

For developers

  • Chain extensions: Core\Language (registers the table trwcookieconsent as multi-language), Core\ViewConfig (isAllowedCookie(), showAllowedCookie(), isCookieConsentActive(), getTrwCookieConsentModuleId()), Application\Component\Widget\CookieNote (renders only when called with trwcookieconsentposition=bodyend; the themes' own calls in the header render nothing).
  • Controllers: admin cookieconsentcontroller, cookieconsentlist, cookieconsentmain; frontend CookieConsentDetailsController (fnc=setCookiesToSession, request array aAllowedCookies[<ident>]=0|1).
  • Services: CookieConsentCacheKeyContributor decorates TheRealWorld\ToolsPlugin\Contract\CacheKeyCookieContributorInterface, CookieConsentProvider decorates TheRealWorld\ToolsPlugin\Contract\ConsentProviderInterface.
  • Templates/blocks: @trwcookieconsent/frontend/widget/header/cookieconsentnote.html.twig with the blocks cookieconsent_note, cookieconsent_note_head, cookieconsent_note_body, cookieconsent_note_body_welcome, cookieconsent_note_body_accordion, cookieconsent_note_body_accordion_item(_header|_body), cookieconsent_note_footer, cookieconsent_button; asset blocks cookieconsent_assets_css, cookieconsent_assets_js, cookieconsent_assets_svg. Theme extensions: layout/base.html.twig, widget/minibasket/minibasket_button.html.twig, admin bottomnaviitem.html.twig, shop_mall.html.twig.
  • Database: table trwcookieconsent (OXIDENT, OXTITLE multi-language, OXTYPE enum, OXSORT, OXACTIVE, OXCACHERELEVANT), in PE/EE additionally trwcookieconsent2shop and OXMAPID. Migration migration/data/Version20260711093100.php, migration table oxmigrations_trwcookieconsent. The cookie types in the banner are read from the enum of OXTYPE; a new type needs a migration plus a language constant TRWCOOKIECONSENT_TYPE_<TYPE>.
  • Assets: sources in resources/ (Vite, SCSS, vanilla JS), build output in assets/src/; see resources/README.md.