Skip to content

Developer Notes

Module Architecture

The GDPR Compliance Suite consists of 21 independent modules organized into two main categories:

  • 15 Core Modules - Essential GDPR functionality for all Magento installations
  • 6 Hyvä Compatibility Modules - Native Hyvä theme integration

Module Dependencies

GdprAdmin (base - required by all)
├── GdprConsent
│   └── GdprConsentHyva
├── GdprCookie
│   ├── GdprCookieHyva
│   └── GdprCookieTemplates
├── GdprDataSubject
│   ├── GdprDataSubjectHyva
│   └── GdprAutomation
├── GdprPrivacyCenter
│   └── GdprPrivacyCenterHyva
├── GdprPolicy
│   └── GdprPolicyHyva
├── GdprAnalytics
├── GdprGtm
├── GdprMarketing
├── GdprApi
├── GdprGeo
├── GdprIntegration
└── GdprFrontend
    └── GdprFrontendHyva

GdprConsent Module

Database Schema

Stores consent type definitions.

Column Type Description
consent_id INT UNSIGNED Primary key
code VARCHAR(100) Unique consent code
title VARCHAR(255) Consent title
description TEXT Consent description
checkbox_text TEXT Checkbox label text
display_type VARCHAR(50) Display type (text, cms_page_link)
link_url VARCHAR(500) Link URL (optional)
cms_page_id INT UNSIGNED CMS page ID (optional)
link_target VARCHAR(20) Link target (_self, _blank, modal)
is_required BOOLEAN Is consent required
is_enabled BOOLEAN Is consent enabled
form_locations TEXT Form locations (JSON array)
sort_order INT UNSIGNED Display sort order
consent_type VARCHAR(50) Consent type (checkbox, implicit)
validation_rules TEXT Validation rules (JSON)
created_at TIMESTAMP Creation timestamp
updated_at TIMESTAMP Update timestamp

Indexes: - PRIMARY KEY (consent_id) - UNIQUE KEY (code) - INDEX (is_enabled) - INDEX (sort_order)

Multi-language support for consent labels.

Column Type Description
label_id INT UNSIGNED Primary key
consent_id INT UNSIGNED Foreign key to consent_definition
store_id SMALLINT UNSIGNED Foreign key to store
title VARCHAR(255) Store-specific title
description TEXT Store-specific description
checkbox_text TEXT Store-specific checkbox text

Foreign Keys: - consent_idqoliber_gdpr_consent_definition.consent_id (CASCADE) - store_idstore.store_id (CASCADE)

Indexes: - UNIQUE KEY (consent_id, store_id)

Logs all consent actions by customers.

Column Type Description
log_id INT UNSIGNED Primary key
consent_id INT UNSIGNED Foreign key to consent_definition
consent_code VARCHAR(100) Consent code (preserved if definition deleted)
consent_snapshot TEXT Full consent data snapshot (JSON)
customer_id INT UNSIGNED Customer ID (null for guests)
guest_email VARCHAR(255) Guest email
form_location VARCHAR(100) Form location identifier
is_accepted BOOLEAN Acceptance status
ip_address VARCHAR(45) IP address
user_agent TEXT Browser user agent
store_id SMALLINT UNSIGNED Store ID
accepted_at TIMESTAMP When consent was given
revoked_at TIMESTAMP When consent was revoked
order_entity_id INT UNSIGNED Associated order ID
additional_data TEXT Additional data (JSON)

Foreign Keys: - consent_idqoliber_gdpr_consent_definition.consent_id (SET NULL) - customer_idcustomer_entity.entity_id (SET NULL) - store_idstore.store_id (CASCADE) - order_entity_idsales_order.entity_id (SET NULL)

Indexes: - INDEX (customer_id) - INDEX (guest_email) - INDEX (consent_code) - INDEX (accepted_at) - INDEX (order_entity_id)

Preferences

<!-- Repository Interface -->
<preference for="Qoliber\GdprConsent\Api\ConsentDefinitionRepositoryInterface"
            type="Qoliber\GdprConsent\Model\ConsentDefinitionRepository"/>

<!-- Data Interface -->
<preference for="Qoliber\GdprConsent\Api\Data\ConsentDefinitionInterface"
            type="Qoliber\GdprConsent\Model\ConsentDefinition"/>

<!-- Search Results -->
<preference for="Qoliber\GdprConsent\Api\Data\ConsentDefinitionSearchResultsInterface"
            type="Qoliber\GdprConsent\Model\ConsentDefinitionSearchResults"/>

<!-- Consent Log Interfaces -->
<preference for="Qoliber\GdprConsent\Api\ConsentLogRepositoryInterface"
            type="Qoliber\GdprConsent\Model\ConsentLogRepository"/>
<preference for="Qoliber\GdprConsent\Api\Data\ConsentLogInterface"
            type="Qoliber\GdprConsent\Model\ConsentLog"/>

Virtual Types

<!-- Grid Collection for Admin Listing -->
<virtualType name="Qoliber\GdprConsent\Model\ResourceModel\ConsentDefinition\Grid\Collection"
             type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult">
    <arguments>
        <argument name="mainTable" xsi:type="string">qoliber_gdpr_consent_definition</argument>
        <argument name="resourceModel" xsi:type="string">Qoliber\GdprConsent\Model\ResourceModel\ConsentDefinition</argument>
    </arguments>
</virtualType>

<!-- Consent Log Grid Collection -->
<virtualType name="Qoliber\GdprConsent\Model\ResourceModel\ConsentCustomerLog\Grid\Collection"
             type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult">
    <arguments>
        <argument name="mainTable" xsi:type="string">qoliber_gdpr_consent_customer_log</argument>
        <argument name="resourceModel" xsi:type="string">Qoliber\GdprConsent\Model\ResourceModel\ConsentCustomerLog</argument>
    </arguments>
</virtualType>

Plugins

None. The module uses event observers and service classes.

Console Commands

Command: gdpr:consent:list Class: Qoliber\GdprConsent\Console\Command\ListConsentsCommand Purpose: List all consent definitions

php bin/magento gdpr:consent:list

GdprCookie Module

Database Schema

Stores cookie categories (Essential, Analytics, Marketing, Functional).

Column Type Description
category_id INT UNSIGNED Primary key
code VARCHAR(100) Unique category code
name VARCHAR(255) Category name
description TEXT Category description
is_required BOOLEAN Is category essential
is_enabled BOOLEAN Is category enabled
sort_order INT UNSIGNED Display order
created_at TIMESTAMP Creation timestamp
updated_at TIMESTAMP Update timestamp

Stores individual cookie information.

Column Type Description
cookie_id INT UNSIGNED Primary key
category_id INT UNSIGNED Foreign key to cookie_category
name VARCHAR(255) Cookie name
provider VARCHAR(255) Cookie provider/service
purpose TEXT Cookie purpose description
expiry VARCHAR(100) Cookie expiry period
type VARCHAR(50) Cookie type (session, persistent)
created_at TIMESTAMP Creation timestamp
updated_at TIMESTAMP Update timestamp

Foreign Keys: - category_idqoliber_gdpr_cookie_category.category_id (CASCADE)

Preferences

<preference for="Qoliber\GdprCookie\Api\CookieCategoryRepositoryInterface"
            type="Qoliber\GdprCookie\Model\CookieCategoryRepository"/>
<preference for="Qoliber\GdprCookie\Api\Data\CookieCategoryInterface"
            type="Qoliber\GdprCookie\Model\CookieCategory"/>

<preference for="Qoliber\GdprCookie\Api\CookieDetailRepositoryInterface"
            type="Qoliber\GdprCookie\Model\CookieDetailRepository"/>
<preference for="Qoliber\GdprCookie\Api\Data\CookieDetailInterface"
            type="Qoliber\GdprCookie\Model\CookieDetail"/>

GdprDataSubject Module

Database Schema

Table: qoliber_gdpr_requests

Unified table for all GDPR data requests.

Column Type Description
request_id INT UNSIGNED Primary key
customer_id INT UNSIGNED Customer ID (null for guests)
customer_email VARCHAR(255) Customer email
request_type VARCHAR(50) Request type (fetch_data, anonymize, delete, rectification)
request_source VARCHAR(50) Request source (customer, guest, admin)
status VARCHAR(50) Status (pending, processing, completed, failed, expired)
request_data TEXT Request data (JSON)
response_data TEXT Response data (JSON)
is_blocked BOOLEAN Whether request is blocked
blocked_reason VARCHAR(255) Reason for blocking
blocked_at TIMESTAMP When blocked
blocked_by VARCHAR(255) Admin who blocked
token VARCHAR(255) Verification token for guests
ip_address VARCHAR(45) IP address
user_agent TEXT User agent
admin_user VARCHAR(255) Admin user (if admin action)
store_id INT UNSIGNED Store ID
email_sent_at TIMESTAMP Email sent timestamp
accessed_at TIMESTAMP When data was accessed
verified_at TIMESTAMP Email verification timestamp
reviewed_at TIMESTAMP When reviewed by admin
reviewed_by VARCHAR(255) Admin who reviewed
created_at TIMESTAMP Creation timestamp
updated_at TIMESTAMP Update timestamp
completed_at TIMESTAMP Completion timestamp
expires_at TIMESTAMP Expiration timestamp

Indexes: - INDEX (customer_id) - INDEX (customer_email) - INDEX (request_type) - INDEX (status) - INDEX (token) - INDEX (created_at) - INDEX (expires_at)

Table: qoliber_gdpr_request_notes

Internal notes for GDPR requests.

Column Type Description
note_id INT UNSIGNED Primary key
request_id INT UNSIGNED Foreign key to gdpr_requests
note_type VARCHAR(50) Note type (internal, customer, system, compliance)
note_content TEXT Note content
created_by VARCHAR(255) Admin username
created_at TIMESTAMP Creation timestamp

Foreign Keys: - request_idqoliber_gdpr_requests.request_id (CASCADE)

Table: qoliber_gdpr_email_queue

Email queue for asynchronous email processing.

Column Type Description
queue_id INT UNSIGNED Primary key
request_id INT UNSIGNED Foreign key to gdpr_requests
email_type VARCHAR(50) Email template type
recipient_email VARCHAR(255) Recipient email
recipient_name VARCHAR(255) Recipient name
status VARCHAR(50) Status (pending, processing, sent, failed)
template_vars TEXT Template variables (JSON)
retry_count INT UNSIGNED Number of retry attempts
error_message TEXT Error message if failed
scheduled_at TIMESTAMP When scheduled
sent_at TIMESTAMP When sent
created_at TIMESTAMP Creation timestamp

Foreign Keys: - request_idqoliber_gdpr_requests.request_id (CASCADE)

Indexes: - INDEX (status) - INDEX (scheduled_at)


GdprPolicy Module

Database Schema

Table: qoliber_gdpr_privacy_policy

Privacy policy versions.

Column Type Description
policy_id INT UNSIGNED Primary key
version VARCHAR(50) Policy version number
title VARCHAR(255) Policy title
content TEXT Policy content (HTML)
is_active BOOLEAN Is this the active version
effective_date DATE When policy becomes effective
store_id SMALLINT UNSIGNED Store ID
created_at TIMESTAMP Creation timestamp
updated_at TIMESTAMP Update timestamp

Table: qoliber_gdpr_policy_acceptance

Customer policy acceptance log.

Column Type Description
acceptance_id INT UNSIGNED Primary key
policy_id INT UNSIGNED Foreign key to privacy_policy
customer_id INT UNSIGNED Customer ID
accepted_at TIMESTAMP Acceptance timestamp
ip_address VARCHAR(45) IP address
user_agent TEXT User agent

Events

The GDPR suite dispatches the following events for customization:

Event: gdpr_consent_before_save Parameters: - consent - ConsentDefinition model - customer_id - Customer ID

Event: gdpr_consent_after_save Parameters: - consent - ConsentDefinition model - is_accepted - Boolean

Event: gdpr_consent_withdrawn Parameters: - consent_identifier - Consent code - customer_id - Customer ID

Data Request Events

Event: gdpr_data_request_submitted Parameters: - request - Request model - type - Request type (export, delete, rectify)

Event: gdpr_data_request_approved Parameters: - request - Request model

Event: gdpr_data_request_completed Parameters: - request - Request model - result - Result data

Event: gdpr_customer_anonymized Parameters: - customer_id - Customer ID - anonymization_data - Anonymization details

Event: gdpr_cookie_consent_saved Parameters: - categories - Array of accepted categories - customer_id - Customer ID (if logged in)


Routers

Frontend Router: GdprDataSubject

Router ID: gdpr Area: Frontend Purpose: Handle GDPR data subject request URLs

Routes: - /gdpr/customer/* - Logged-in customer requests - /gdpr/guest/* - Guest user requests - /gdpr/data-request/* - Request submission and status


Console Commands

GdprConsent

# List all consent definitions
php bin/magento gdpr:consent:list

# Export consent logs
php bin/magento gdpr:consent:export --from=2024-01-01 --to=2024-12-31

GdprDataSubject

# Export customer data
php bin/magento gdpr:export:customer <customer_id>

# Anonymize customer
php bin/magento gdpr:anonymize <customer_id>

# Delete customer (hard delete)
php bin/magento gdpr:delete:customer <customer_id>

# List pending requests
php bin/magento gdpr:requests:list --status=pending

GdprAutomation

# Run anonymization manually
php bin/magento gdpr:anonymize:run

# Check accounts eligible for anonymization
php bin/magento gdpr:anonymize:check --dry-run

Cron Jobs

GdprAutomation

Cron Group: default

Job: gdpr_anonymize_inactive_accounts Schedule: Daily at 2:00 AM Class: Qoliber\GdprAutomation\Cron\AnonymizeInactiveAccounts Purpose: Automatically anonymize inactive customer accounts

Job: gdpr_cleanup_old_logs Schedule: Weekly (Sunday 3:00 AM) Class: Qoliber\GdprAutomation\Cron\CleanupOldLogs Purpose: Clean up old consent logs and request data

GdprDataSubject

Job: gdpr_process_email_queue Schedule: Every 5 minutes Class: Qoliber\GdprDataSubject\Cron\ProcessEmailQueue Purpose: Process queued GDPR notification emails

Job: gdpr_expire_old_requests Schedule: Daily at 4:00 AM Class: Qoliber\GdprDataSubject\Cron\ExpireOldRequests Purpose: Mark expired requests as expired


API Endpoints

REST API

Currently implemented endpoints:

POST /rest/V1/gdpr/policy/consent/log Authentication: Anonymous Purpose: Log policy consent acceptance

GET /rest/V1/gdpr/policy/consent/customer/:customerId/type/:policyType Authentication: Self (customer) Purpose: Get latest consent for customer Note: customerId is automatically set from customer token

GET /rest/V1/gdpr/policy/consent/check Authentication: Self (customer) Purpose: Check if customer has accepted current policy version

GET /rest/V1/gdpr/policy/consent/:consentId Authentication: Admin ACL (Qoliber_GdprPolicy::policy_consent_view) Purpose: Get specific consent record

GET /rest/V1/gdpr/policy/consent/search Authentication: Admin ACL (Qoliber_GdprPolicy::policy_consent_view) Purpose: Search consent records

Future API Development

Additional API endpoints are planned for future releases to support: - Consent tracking and management - Data export/deletion requests - Cookie consent preferences - Customer privacy data access


Extending the Module

  1. Create renderer class implementing ConsentRendererInterface
  2. Register in di.xml:
<type name="Qoliber\GdprConsent\Model\ConsentRendererPool">
    <arguments>
        <argument name="renderers" xsi:type="array">
            <item name="custom_type" xsi:type="object">Vendor\Module\Model\Renderer\CustomRenderer</item>
        </argument>
    </arguments>
</type>

Adding Custom Form Location

Add to di.xml:

<type name="Qoliber\GdprConsent\Model\Source\FormLocation">
    <arguments>
        <argument name="formLocations" xsi:type="array">
            <item name="custom_form" xsi:type="array">
                <item name="label" xsi:type="string">Custom Form</item>
                <item name="sort_order" xsi:type="number">50</item>
            </item>
        </argument>
    </arguments>
</type>

Adding Custom Export Data

Implement ExportDataProviderInterface:

<?php
namespace Vendor\Module\Model\Export;

use Qoliber\GdprDataSubject\Api\ExportDataProviderInterface;

class CustomDataProvider implements ExportDataProviderInterface
{
    public function getData(int $customerId): array
    {
        return [
            'section_name' => 'Custom Data',
            'data' => $this->getCustomerCustomData($customerId)
        ];
    }

    public function getSectionName(): string
    {
        return 'custom_section';
    }
}

Register in di.xml:

<type name="Qoliber\GdprDataSubject\Model\Export\DataProviderPool">
    <arguments>
        <argument name="providers" xsi:type="array">
            <item name="custom" xsi:type="object">Vendor\Module\Model\Export\CustomDataProvider</item>
        </argument>
    </arguments>
</type>

Testing

Unit Tests

php bin/magento dev:tests:run unit Qoliber_GdprConsent
php bin/magento dev:tests:run unit Qoliber_GdprDataSubject

Integration Tests

php bin/magento dev:tests:run integration Qoliber_GdprConsent

Functional Tests (MFTF)

php vendor/bin/mftf run:test GdprConsentTrackingTest

Performance Optimization

Database Indexes

All critical tables have appropriate indexes. For large installations, consider:

-- Add composite index for frequent queries
ALTER TABLE qoliber_gdpr_consent_customer_log
ADD INDEX idx_customer_consent_date (customer_id, consent_code, accepted_at);

-- Add index for admin reporting
ALTER TABLE qoliber_gdpr_requests
ADD INDEX idx_type_status_created (request_type, status, created_at);

Caching

Consent definitions are cached automatically. Cache key format:

gdpr_consent_definition_{consent_code}
gdpr_consent_list_store_{store_id}

Clear cache:

php bin/magento cache:clean gdpr_consent


Security Considerations

  1. Always validate customer ownership before processing data requests
  2. Use tokenized URLs for guest data access
  3. Rate limit API endpoints to prevent abuse
  4. Encrypt sensitive data in database where applicable
  5. Audit log all admin actions related to GDPR
  6. Implement proper ACL for admin users

Support & Resources

  • Documentation: https://docs.qoliber.com
  • Support Email: [email protected]
  • GitHub Issues: (if applicable)
  • Developer Slack: (if applicable)