Privacy and Action Logs

This content was generated with the assistance of AI and should be verified against the official Joomla documentation before being relied on in production.

This sub-section’s Joomla bibliography lists the reference material consulted while preparing these pages.

Joomla ships a small privacy toolkit in core: the Privacy component (com_privacy) that processes requests from people who want a copy of their data or want it removed, the Privacy Consent plugins that record consent at registration, and the User Actions Log (com_actionlogs) that audits what administrators and users do. This page was written against Joomla 6.2.x (PHP 8.3 or later) and checked on 2026-10-30 against the Joomla User Guide, the Developer Manual and the source tree. It is aimed at site owners and administrators first, with a developer section for extension authors. The running example is the Tallpine Hiking Club site with its com_tallpine component.

What the tools do, and what they do not

The Privacy component is based on email addresses. It mainly serves registered users, and people whose email addresses exist in the Contacts component. A request needs a valid email address; the subject does not need an account. The User Guide states plainly that the component does not implement the cookie or tracking consent that the GDPR requires. Joomla core sets a session cookie and, with "Remember Me", a longer-lived login cookie; the guide’s cookie page suggests mentioning them in the privacy policy or terms, in a custom HTML module, or in the login module message. A cookie banner with real consent management for third-party scripts is an extension or a custom-code concern.

Older books such as Joomla! 4 Masterclass (2023) describe the privacy and action log screens as they appeared in Joomla 4. The screens still exist, but the retention of action logs and the expiry of consents moved to scheduled tasks in 5.0 (see below), and the current end-user documentation lives at guide.joomla.org, not at the legacy wiki.

Privacy dashboard and checks

Open Users → Privacy. Two modules appear on the Privacy Dashboard: Privacy Requests (a summary of current requests) and Privacy Status, a checklist of things to fix:

  • Published Privacy Policy: choose a privacy policy article (or menu item) in the System - Privacy Consent plugin.

  • Published Request Form Menu Item: create a menu item that lets signed-in users submit requests.

  • Outstanding Urgent Requests: flags confirmed requests older than the component’s age parameter. The Privacy component option notify has a default of 14 days.

  • Mail Sending Enabled: the whole process is email driven, so the site must be able to send mail (see mail templates and email settings).

  • Database Encryption: relevant when the database is on another host.

A Privacy Dashboard module can also be added to the Home Dashboard.

Three plugins collect consent:

  • System - Privacy Consent adds a required consent checkbox (with a short note and a link) to the registration form and other personal-information forms. When it is enabled, existing users are redirected to their profile to consent.

  • User - Terms and Conditions does the same for the site terms. It is not enabled by default. Its consent is not shown in the Consents list; it appears only in the User Actions Log.

  • Content - Confirm Consent adds a required checkbox to a form such as the core contact form.

For the System plugin choose the Privacy Type: an article (privacy_article) or a menu item (privacy_menu_item), plus a short note (privacy_note) and the redirect message (messageOnRedirect). For multilingual sites, custom note and redirect text cannot be translated: leave them blank and override the language strings PLG_SYSTEM_PRIVACYCONSENT_NOTE_FIELD_DEFAULT and PLG_SYSTEM_PRIVACYCONSENT_REDIRECT_MESSAGE_DEFAULT (and PLG_USER_TERMS_NOTE_FIELD_DEFAULT for the terms plugin) in each language, and associate the policy articles or menu items across languages (see multilingual sites).

Do not confuse System - Privacy Consent with Privacy - Consents, which is a privacy plugin that exports a user’s recorded consents.

Users → Privacy → Consents lists recorded consents, most recent first, in the language of the form on which they were given. Search by name to find a person. The list can be filtered.

The expiry task (5.0 and later)

The User Guide’s setup page still mentions an Expiration tab inside the System plugin. In the 6.2 source that tab is gone: the System - Privacy Consent plugin form only holds the privacy type, article, menu item, note and redirect message. Consent expiry and reminders belong to the Task - Privacy Consent plugin (plg_task_privacyconsent, present since 5.0.0), which offers one routine, privacy.consent, titled "Expiration of privacy consents". Its parameters are:

Parameter Form default Meaning

consentexpiration

360

Days after which a consent is invalidated. The code falls back to 365 if the value is missing.

remind

30

Days before expiry at which a reminder email is sent. Each consent is reminded once.

When it runs, the routine first invalidates expired consents (notifying the users) and then emails reminders to people whose consents are about to expire, using the mail template plg_task_privacyconsent.request.reminder and a link to the com_privacy remind view. Create the task under System → Scheduled Tasks → New and choose "Expiration of privacy consents". Like every task it only runs when the scheduler is triggered by lazy scheduling or by a real cron job; see scheduled tasks and mail templates.

Information requests

The two request types

  • Export: the subject’s data can be exported as XML.

  • Remove: the subject’s data is anonymised and removed where possible.

Statuses stored on a request are invalid, pending, confirmed and complete. A newly created request is pending until the subject confirms it, which turns it into Confirmed; an administrator then acts on it and marks it Complete, or Invalidates it.

Creating a request

Signed-in users submit a request through a menu item. Create it in Menus, choose the Create Request item type under Privacy, set Access to Registered and place it near the privacy policy link, for example in the footer menu. If the user already has an active request for the same email address and type, Joomla refuses with "There is already an active information request for this email address and request type." A second hidden menu item of type Confirm Request gives the confirmation link a clean SEF URL; put it in a menu with no module position, or with Display in Menu set to No.

Super Users create requests under Users → Privacy → Requests → New: enter the email address, pick Export or Remove and save. This is the only way to handle people without an account. A created request cannot be edited, only processed or invalidated.

Confirmation

Whichever way it was created, the requester receives an email with a link to a confirmation form and a token. The token is valid for 24 hours; after that the request is marked invalid and a new one is needed. After confirmation the Super Users get an "action required" email and see the request as Confirmed in the list.

sequenceDiagram participant M as Tallpine member participant S as Joomla site participant A as Super User M->>S: Submit Export or Remove request S->>M: Email with confirmation link and token M->>S: Confirm within 24 hours S->>A: Action required email A->>S: Export, Email or Delete Data A->>S: Complete (or Invalidate) S-->>M: Optional email with XML export

Processing an export

On a confirmed export request two actions are available. Export Data downloads an XML file for review. Email Data Export sends the same file to the requester as an attachment. The export contains only what extensions with privacy support contribute, so the administrator checks it and adds data from other extensions by hand.

Processing a removal

On a confirmed removal request the single action is Delete Data. For a registered account the name, username and email are anonymised, the account is blocked and a logged-in user is logged out. A "Data Removed" message appears but the list does not change; the administrator then opens the request from the list, checks it and clicks Complete (or Invalidate). Each extension decides whether its data can be removed: a privacy plugin can refuse and give a reason.

The removal does not touch the action logs, private messages or the privacy requests table. Afterwards, delete the person’s entries in Users → User Actions Log (search by username or email, select all, Delete) and remove messages and request rows yourself, for example with SQL. The guide notes that Joomla has no batch tool for these.

The Extension Capabilities report

Users → Privacy → Capabilities lists what installed extensions report about the personal data they handle, grouped by categories such as Authentication, Captcha, Installer, Privacy, System and User, in the administrator’s language. The text comes from core language strings and from plugins answering the onPrivacyCollectAdminCapabilities event. Use it as raw material for the privacy policy and terms of service; third-party extensions that stay silent are not covered.

Privacy plugins for your own extension

Core plugins in the privacy group (actionlogs, consents, contact, content, message, user) contribute to exports and removals. An extension that stores personal data, like the com_tallpine trail-booking table, ships its own. The classes extend PrivacyPlugin (which provides createDomain(), createItemFromArray() and createCustomFieldsDomain()), implement SubscriberInterface and use the concrete event classes from Joomla\CMS\Event\Privacy.

<?php

namespace Tallpine\Plugin\Privacy\Tallpine\Extension;

use Joomla\CMS\Event\Privacy\CanRemoveDataEvent;
use Joomla\CMS\Event\Privacy\ExportRequestEvent;
use Joomla\CMS\Event\Privacy\RemoveDataEvent;
use Joomla\Component\Privacy\Administrator\Plugin\PrivacyPlugin;
use Joomla\Component\Privacy\Administrator\Removal\Status;
use Joomla\Database\DatabaseAwareTrait;
use Joomla\Database\ParameterType;
use Joomla\Event\SubscriberInterface;

\defined('_JEXEC') or die;

final class Tallpine extends PrivacyPlugin implements SubscriberInterface
{
    use DatabaseAwareTrait;

    public static function getSubscribedEvents(): array
    {
        return [
            'onPrivacyExportRequest'  => 'onPrivacyExportRequest',
            'onPrivacyCanRemoveData'  => 'onPrivacyCanRemoveData',
            'onPrivacyRemoveData'     => 'onPrivacyRemoveData',
        ];
    }

    public function onPrivacyExportRequest(ExportRequestEvent $event): void
    {
        $request = $event->getRequest();
        $user    = $event->getUser();

        if (!$user && !$request->email) {
            return;
        }

        $domain = $this->createDomain('tallpine_bookings', 'tallpine_trail_bookings');
        $db     = $this->getDatabase();
        $query  = $db->createQuery()
            ->select('*')
            ->from($db->quoteName('#__tallpine_bookings'));

        if ($user) {
            $query->where($db->quoteName('user_id') . ' = :id')
                ->bind(':id', $user->id, ParameterType::INTEGER);
        } else {
            $query->where($db->quoteName('email') . ' = :email')
                ->bind(':email', $request->email);
        }

        foreach ($db->setQuery($query)->loadObjectList() as $row) {
            $domain->addItem($this->createItemFromArray((array) $row));
        }

        $event->addResult([$domain]);
    }

    public function onPrivacyCanRemoveData(CanRemoveDataEvent $event): void
    {
        $status = new Status();

        // Keep bookings that are part of an unpaid season pass.
        // (Business rule for the example; set $status->canRemove = false and a reason to block.)
        $event->addResult($status);
    }

    public function onPrivacyRemoveData(RemoveDataEvent $event): void
    {
        $user = $event->getUser();

        if (!$user) {
            return;
        }

        $db    = $this->getDatabase();
        $query = $db->createQuery()
            ->update($db->quoteName('#__tallpine_bookings'))
            ->set($db->quoteName('email') . ' = ' . $db->quote('anonymous@example.invalid'))
            ->set($db->quoteName('name') . ' = ' . $db->quote('Anonymous'))
            ->where($db->quoteName('user_id') . ' = :id')
            ->bind(':id', $user->id, ParameterType::INTEGER);

        $db->setQuery($query)->execute();
    }
}

Source: Joomla Manual: How plugins work and the core plg_privacy_contact class in plugins/privacy in the source tree.

The manifest is the usual plugin one:

<?xml version="1.0" encoding="UTF-8"?>
<extension type="plugin" group="privacy" method="upgrade">
    <name>plg_privacy_tallpine</name>
    <version>1.0.0</version>
    <namespace path="src">Tallpine\Plugin\Privacy\Tallpine</namespace>
    <files>
        <folder plugin="tallpine">services</folder>
        <folder>src</folder>
    </files>
</extension>

Common pitfalls: exporting rows by email only exposes data to anyone who can forge a request, so rely on the confirmed subject; forgetting quoteName() and bind() around the email; returning nothing when no domain applies is fine, but throwing aborts the whole export; and writing to a table in onPrivacyRemoveData without first answering onPrivacyCanRemoveData leaves the administrator no way to see why data must stay. Plugins in the privacy group are enabled by default and have no significant settings.

User Actions Log

Users → User Actions Log is an audit trail of activity. Only Super Users can use the component. The list can be filtered, and the toolbar offers:

  • Export Selected as CSV (only the selected rows) and Export All as CSV (all records, filters ignored)

  • Delete (selected rows) and Clear Log (everything)

  • Options

A Latest Actions module on the Home Dashboard shows recent entries to Super Users.

Options

The Options form (defaults from com_actionlogs) holds:

Option Default Meaning

ip_logging

0 (off)

Store the IP address with each entry. An IP address is personal data, so enable it only with a reason.

csv_delimiter

,

Delimiter for CSV exports.

loggable_extensions

core list

Which components are logged (banners, cache, categories, checkin, config, contact, content, fields, guided tours, installer, media, menus, messages, modules, newsfeeds, plugins, redirect, scheduler, tags, templates, users).

loggable_api

0 (off)

Also log Web Services API calls.

loggable_verbs

GET

HTTP verbs logged when API logging is on.

date_relative

1

Show dates relative to now.

Plugins

  • Action Log - Joomla records core actions (login, logout, article changes, module creation) and has no parameters.

  • System - User Actions Log handles the per-user notification options on the user profile.

  • Privacy - Action Logs includes a user’s log entries in an export.

Retention: the Delete Action Logs task

The User Guide says the System - User Actions Log plugin sets a number of days after which logs are deleted. In the 6.2 source that plugin has no retention parameter. Retention is a scheduled task: Task - Delete Action Logs (plg_task_deleteactionlogs, since 5.0.0) with routine delete.actionlogs, titled "Delete ActionLogs". Its single parameter is logDeletePeriod, the days to keep logs (form default 7, minimum 1; a value of 0 deletes nothing). The routine deletes rows whose log_date is older than that many days from the action log table.

Create it under System → Scheduled Tasks → New, for example daily at 03:15, and make sure the scheduler runs (see scheduled tasks and scheduled task plugins). On a fresh install no such task exists, so without creating it logs grow without limit. To run tasks from the shell on the Docker development site of local development with Docker:

docker compose exec joomla php cli/joomla.php scheduler:list
docker compose exec joomla php cli/joomla.php scheduler:run

See the command-line interface for the other console commands.

Extensions that write to the log

Extensions register with the log through the #__action_logs_extensions and #__action_log_config tables and record entries through the action log model, supplying a language string such as "User {username} created a transaction ( {type} )". The loggable_extensions option lists only registered components, so com_tallpine appears there after its installer script registers it. See plugin development and events for the event side.

GDPR considerations

Joomla gives you the mechanics; the legal basis, policy text and retention periods are yours:

  • publish a privacy policy article and link it from the registration form and the footer menu

  • keep consent records, and set the consent expiry and reminder task to your policy

  • keep IP logging off unless needed, and set an action log retention period

  • answer requests within your legal deadline: the 24 hour token and the 14 day urgent-request flag are only the technical clocks

  • after a removal request, clean the User Actions Log, private messages and request rows by hand

  • review the Extension Capabilities report after each installation, and record third-party processors

  • back up and restore with care, since a restored backup can bring removed data back; see backups and recovery

  • protect administrators who can see exports with MFA; see security hardening, MFA and passkeys

This page is not legal advice.

Common errors

  • No confirmation email arrives: outgoing mail is not configured, or the confirm-request menu item is missing.

  • "There is already an active information request": finish or invalidate the earlier request first.

  • Token expired after 24 hours: the request is invalid, so create a new one.

  • The export lacks an extension’s data: that extension has no privacy plugin.

  • Consent never expires: the Task - Privacy Consent plugin is disabled or no task exists, or the scheduler never runs.

  • The Terms and Conditions consent is missing from the Consents list: it is only recorded in the User Actions Log.