GDPR & Cookie Consent
A complete GDPR compliance toolkit for your Larapen site. Display a customizable cookie consent banner, log visitor consent decisions for audit compliance, and manage cookie categories from the admin panel.
Cookie Consent Banner
A polished, responsive banner with Accept All, Reject All, and Customize options. Seven position variants including modal mode.
Granular Categories
Four cookie categories (Essential, Analytics, Marketing, Preferences) with per-category toggle controls for visitors.
Consent Logging
Full audit trail of every consent decision: IP address, user agent, action taken, and categories selected. GDPR Article 7 compliant.
Auto-injection
The banner is automatically injected into all front-end pages via middleware. No template changes needed. Skips admin panel and AJAX requests.
Use Cases
EU/EEA Website Compliance
Your site serves visitors in the European Union and must comply with the GDPR and ePrivacy Directive.
- Enable the cookie consent banner to inform visitors about cookie usage.
- Enable consent logging to maintain an auditable record of all consent decisions.
- Link your privacy policy page so visitors can review your data practices.
- Configure category toggles so visitors can opt in/out of analytics and marketing cookies.
Analytics-Heavy Site
You use Google Analytics, Hotjar, or similar tools and need user consent before loading tracking scripts.
- Enable the Analytics category toggle.
- Read the consent cookie value (
gdpr_consent) in your JavaScript before loading analytics scripts. - Only load analytics when
categories.analytics === true.
Marketing & Ad Retargeting
You run Facebook Pixel, Google Ads, or email marketing tools that set cookies.
- Enable the Marketing category toggle.
- Gate all marketing/advertising scripts behind the
marketingconsent flag. - Visitors can revoke consent at any time via the withdraw endpoint.
Requirements
- Larapen CMS v1.0.0 or later
- PHP 8.3+
- MySQL 8.0+
Installation
Step 1: Place the Add-on
Copy or symlink the gdpr folder into your Larapen "extensions/addons" directory:
Step 2: Activate the Add-on
Go to Admin → Add-ons → Installed Add-ons and activate GDPR & Cookie Consent.
Step 3: Run Migrations
This creates the gdpr_consent_logs table for storing consent audit records.
Step 4: Set Permissions
The add-on registers 4 permissions (see Permissions). Assign them to admin roles via Admin → Users → Roles & Permissions.
Step 5: Configure
Navigate to Admin → GDPR → Settings to configure the banner position, cookie lifetime, consent logging, and cookie categories. See Configuration for details.
Configuration
All settings are managed in Admin → GDPR → Settings
(stored in the settings table, group gdpr).
Default values come from config/gdpr.php.
| Setting | Description | Default |
|---|---|---|
gdpr_enabled |
Enable or disable the cookie consent banner globally. | true |
gdpr_cookie_lifetime |
How many days the consent preference cookie is remembered (1–3650). | 365 |
gdpr_log_consents |
Record each visitor’s consent decision to the database for compliance auditing. | true |
gdpr_banner_position |
Where the banner appears on the page (7 positions available). | bottom-center |
gdpr_privacy_policy_url |
URL to your privacy policy page, displayed as a link in the banner. | (empty) |
gdpr_category_analytics |
Show the Analytics cookie category toggle in the preferences panel. | false |
gdpr_category_marketing |
Show the Marketing cookie category toggle in the preferences panel. | false |
gdpr_category_preferences |
Show the Preferences cookie category toggle in the preferences panel. | false |
Config File Defaults
The config/gdpr.php file provides default values that are used when no database setting exists:
Admin: Settings
The settings page (GDPR → Settings) is organized into three main areas:
General Settings
The left column (7/12 width) contains the core configuration options:
- Enable Cookie Banner: toggle switch to enable/disable the banner globally. When disabled, no banner is shown and no middleware injection occurs.
- Cookie Lifetime (Days): numeric input (1–3650 days) controlling how long the consent cookie persists in the visitor’s browser.
- Banner Position: visual position picker with 7 positions displayed on a browser mockup. Click a position to select it; the label updates in real time.
- Log Consent Actions: toggle to enable/disable consent logging to the database. When enabled, every Accept/Reject/Customize/Withdraw action is recorded.
- Privacy Policy URL: text input for the URL to your privacy policy page. If provided, a “Privacy Policy” link appears in the banner.
Banner Position Picker
The position picker is an interactive visual element showing a miniature browser window. Seven clickable dots represent the available positions:
| Position | Layout | Description |
|---|---|---|
top-left |
Compact card | Floating card in the top-left corner (max 420px wide) |
top-center |
Full-width bar | Horizontal bar across the top of the page |
top-right |
Compact card | Floating card in the top-right corner (max 420px wide) |
middle-center |
Modal dialog | Centered modal with a semi-transparent backdrop overlay |
bottom-left |
Compact card | Floating card in the bottom-left corner (max 420px wide) |
bottom-center |
Full-width bar | Horizontal bar across the bottom of the page (default) |
bottom-right |
Compact card | Floating card in the bottom-right corner (max 420px wide) |
Cookie Categories
The right column (5/12 width) displays the four cookie categories with toggle switches:
- Essential: always on, cannot be disabled (displayed with an “Always On” badge). These are cookies required for the website to function (sessions, CSRF tokens, etc.).
- Analytics: toggleable. When enabled, visitors can opt in/out of analytics cookies (Google Analytics, Hotjar, etc.).
- Marketing: toggleable. When enabled, visitors can opt in/out of marketing cookies (Facebook Pixel, ad retargeting, etc.).
- Preferences: toggleable. When enabled, visitors can opt in/out of preference cookies (language, theme, personalization, etc.).
Consent Overview Stats
At the top of the settings page, five stat cards show consent metrics for the last 30 days:
- Total Consents: total number of consent actions recorded
- Accepted All: visitors who clicked “Accept All”
- Rejected All: visitors who clicked “Reject All”
- Customized: visitors who saved custom preferences
- Acceptance Rate: percentage of “Accepted All” vs total consents
Admin: Consent Logs
The Consent Logs page (GDPR → Consent Logs) provides a full audit trail of every visitor consent decision, as required by GDPR Article 7.
Statistics
Four mini stat cards at the top of the page:
- Total Consents (neutral color)
- Accepted All (success/green color)
- Rejected All (danger/red color)
- Acceptance Rate (accent color, shown as percentage)
Log Table
A paginated table (25 records per page) with the following columns:
| Column | Description |
|---|---|
| Visitor | User avatar + name/email (if authenticated) or “Guest” label (if anonymous) |
| IP Address | Visitor’s IP address, displayed in monospace/code formatting |
| Action | Color-coded badge: Accepted All (green), Rejected All (red), Customized (yellow), Withdrew (gray) |
| Categories | Inline badges for each category showing enabled (filled) or disabled (outline) state |
| Date | Human-readable relative time (e.g. “2 hours ago”) with full date in a tooltip |
Filters
- Search: filter by IP address, user name, or user email
- Action: dropdown to filter by consent action type
(
accepted_all,rejected_all,customized,withdrew)
Empty State
When no logs exist, a friendly empty state is displayed with a large icon and a message explaining that records will appear once visitors interact with the banner.
Front-end: Cookie Banner
The cookie consent banner is a self-contained HTML/CSS/JS component that is automatically injected
into every front-end page via the InjectCookieBanner middleware.
Main Banner View
The main view displays:
- Cookie icon: an inline SVG cookie illustration
- Title: “Cookie Preferences”
- Message: a brief explanation of cookie usage
- Privacy Policy link: shown only if a URL is configured in settings
- Three action buttons:
- Accept All (primary dark button): accepts all cookie categories
- Reject All (secondary gray button): rejects all non-essential cookies
- Customize (ghost/text button): opens the preferences panel
Banner Positions
The banner adapts its layout based on the configured position:
- Full-width positions (
top-center,bottom-center) use a horizontal flex layout with content and buttons side by side. Max width 860px, centered. - Corner positions (
top-left,top-right,bottom-left,bottom-right) use a compact card layout (max 420px) with stacked content. - Modal position (
middle-center) centers the banner with a semi-transparent backdrop overlay, functioning as a modal dialog (max 520px).
Preferences Panel
Clicking “Customize” transitions from the main view to the preferences panel (no page reload). The panel shows:
- Back button with chevron icon: returns to the main banner view
- Category list (scrollable, max height 280px) with one row per enabled category:
- Category name and description
- Required categories: display an “Always On” badge (no toggle)
- Optional categories: display a custom CSS toggle switch
- Save Preferences button: saves the selected categories and dismisses the banner
Animations
The banner uses smooth CSS animations:
- Slide in from bottom for bottom positions
- Slide in from top for top positions
- Fade + scale for the center modal position
- Reverse animations when the banner is dismissed
Responsive Design
On screens below 768px:
- Full-width bars switch to vertical (stacked) layout
- Corner cards expand to full width
- Action buttons stack vertically and fill the width
Translations
Translation files are provided for English (en) and French (fr).
All strings use the gdpr:: namespace prefix.
Translation Keys
| Section | Key Pattern | Description |
|---|---|---|
| Categories | gdpr::gdpr.categories.* |
Category names and descriptions (necessary, analytics, marketing, preferences) |
| Actions | gdpr::gdpr.actions.* |
Consent action labels (accepted_all, rejected_all, customized, withdrew) |
| Positions | gdpr::gdpr.positions.* |
Banner position labels for the admin picker |
| Front-end | gdpr::gdpr.front.* |
Banner UI strings (title, message, button labels, privacy link) |
| Admin Settings | gdpr::gdpr.admin.settings.* |
Settings page labels, hints, and section headings |
| Admin Consent Logs | gdpr::gdpr.admin.consent_logs.* |
Consent logs page labels, column headers, empty state |
| Admin Stats | gdpr::gdpr.admin.stats.* |
Stat card labels (total_consents, accepted_all, etc.) |
| Messages | gdpr::gdpr.admin.messages.* |
Flash messages (settings_updated) |
Updating
Step 1: Replace Files
Replace the add-on directory with the new version.
Step 2: Run Migrations
Step 3: Clear Caches
Step 4: Verify
Visit GDPR → Settings in the admin panel to confirm all settings are intact and the banner is working correctly on the front-end.
Troubleshooting
Banner not appearing on the front-end
Check the following:
- The add-on is activated in Admin → Add-ons.
- The
gdpr_enabledsetting is set to1(enabled) in settings. - You don’t already have a
gdpr_consentcookie set. Clear cookies or use an incognito window to test. - The page is a standard HTML response with a
</body>tag (the middleware looks for this tag to inject the banner). - You are viewing a front-end page, not an admin page (admin routes are excluded).
Banner appears on admin pages
This should not happen: the middleware explicitly skips admin routes via is_admin_panel_route().
If it does occur, check that:
- The
is_admin_panel_route()helper function is defined and working in your Larapen core installation. - Your admin route prefix matches the configured value in
config('larapen.core.admin_prefix').
Consent cookie is encrypted / unreadable by JavaScript
The add-on explicitly excludes the gdpr_consent cookie from Laravel’s EncryptCookies middleware.
If the cookie is still encrypted:
- Ensure the
GdprServiceProvideris properly registered (add-on is active). - Check that no custom middleware or package is re-encrypting the cookie.
- Clear your browser cookies and consent again: old encrypted cookies from a previous configuration may persist.
Consent logs not being recorded
- Check that
gdpr_log_consentsis set to1(enabled) in the admin settings. - Verify the
gdpr_consent_logstable exists (runphp artisan migrate). - Check your server logs for database errors (e.g. permission issues).
Banner position not changing
- After changing the position in admin settings, make sure to clear the
gdpr_consentcookie (or use an incognito window): the banner only appears for visitors without a consent cookie. - Clear the view cache:
php artisan view:clear.
Categories not showing in preferences panel
Only categories that are enabled in the admin settings appear in the preferences panel. The “Essential” category always appears. For optional categories:
- Check that
gdpr_category_analytics,gdpr_category_marketing, and/orgdpr_category_preferencesare set to1in settings. - Disabled categories are filtered out by the
InjectCookieBannermiddleware before rendering the view.
Log retention / purging
The GdprService::purgeOldLogs() method deletes logs older than the configured retention period
(log_retention_days, default: 730 days / 2 years). This method is not currently hooked to a
scheduled task. To purge old logs:
- Call it manually from an Artisan command or tinker:
app(GdprService::class)->purgeOldLogs() - Or add it to your application’s scheduler in
routes/console.php:
CSRF token mismatch on consent endpoints
All consent endpoints (/gdpr/accept-all, etc.) require a valid CSRF token.
The banner JavaScript includes the token from the rendered Blade view. If you see 419 errors:
- Ensure your Laravel session is configured correctly.
- Check that the
webmiddleware group (which includesVerifyCsrfToken) is applied to the consent routes. - If using a custom banner view, make sure it passes the CSRF token in the
X-CSRF-TOKENheader of fetch requests.