This page lists what the plugin records and how it is stored.
Action codes
Every entry has an action of the form <type>.<verb>.
| Type | Verbs | Source | Settings box |
|---|---|---|---|
item |
create, update, delete |
record hooks | Items |
file |
create, update, delete |
record hooks | Files |
collection |
create, update, delete |
record hooks | Collections |
item_type |
create, update, delete |
record hooks | Item types |
element, element_set |
create, update, delete |
record hooks | Elements and element sets |
tag |
update (renamed), delete |
record hooks | Tags |
user |
create, update, delete |
record hooks | Users |
user |
login, logout, login_failed |
the login and logout pages | Logins, Logouts, Failed logins |
api_key |
create, delete |
record hooks | API keys |
simple_page |
create, update, delete |
record hooks | Simple Pages |
exhibit, exhibit_page |
create, update, delete |
record hooks | Exhibits and exhibit pages |
plugin |
install, activate, deactivate, upgrade, uninstall, configure |
plugin hooks, Configure page | Plugins |
settings |
settings.update, appearance.update |
settings pages | Settings |
navigation |
navigation.update, navigation.reset |
Appearance > Navigation | Settings |
search_index |
search.reindex |
Settings > Search, Index Records | Settings |
theme |
theme.switch, theme.configure |
Appearance > Themes | Themes |
audit_trail |
audit_trail.export, audit_trail.purge |
this plugin | always |
| any | any | other plugins (audit_trail_log) |
Events reported by other plugins |
Records are logged from Omeka Classic's global record hooks (after_save_record, after_delete_record), so every save is covered whatever makes it: the admin forms, batch edit, the REST API, imports, background jobs and other plugins. A save that fails validation is not logged. Reads and searches are never logged.
What an update says
On update, a few columns of the record are compared with what was stored, and the entry says what changed (in Detail and in the data under changed):
| Type | Compared |
|---|---|
| Item | public, featured, collection_id, item_type_id |
| Collection | public, featured |
| File | item_id |
| Item type | name, description |
| Element | name, description, comment, order (an element is only logged when one of them changed) |
| Element set | name, description |
| Tag | name |
| User | username, name, email, role, active, and whether the password changed (never the password or its hash) |
| Simple page | title, slug, is_published, parent_id |
| Exhibit | title, slug, public, featured |
| Exhibit page | title, slug, parent_id |
Changes to an item's or file's metadata (its element texts) are logged as an update without a field list; the Resource Versioning plugin keeps the texts themselves. A create stores the compared columns' first values under values.
What is folded away
Omeka Classic saves some records again as a side effect. These saves are not separate entries:
- a new file stored and given thumbnails (part of
file.create), here or in a background job; - files reordered by saving their item, and pages reordered by saving their exhibit;
- a record saved again in the same request without a change to its compared columns;
- the password hash Omeka upgrades while someone logs in;
- the API key's "last used" time, saved on every REST API request (keys log only
createanddelete); - new tags made while tagging an item (part of the item's update).
Logins
A login is recorded when it succeeds. A failed login records the username typed (never the password), the address it came from, and whether an account has that username: the detail says No account with this username, Wrong password for an existing account or Login to an inactive account. The actor of a failed login is Visitor; the entry's resource is the account, when there is one, so it is in that user's Activity tab.
Settings, themes and plugins
Omeka Classic keeps settings as options. While an admin form is saved, the options it changes are collected and logged as one entry, with each option's old and new value (shortened to 200 characters). Options whose names look secret (containing key, secret, token, password, private, salt, credential or auth) are logged as changed, never with their values. Theme settings and plugin settings stored as lists are compared key by key; the navigation is logged as its visible pages before and after.
Saving a settings form without changing anything is not logged. Saving a plugin's Configure page is, since plugins may keep settings outside the options; it says Saved without changes when none changed. Plugins with their own settings pages are logged as "
Plugin installs, activations, deactivations, upgrades (with both versions) and uninstalls come from Omeka's plugin hooks. Audit Trail cannot log its own activation (it is off at that moment); it does log its deactivation.
Sources and actors
| Source | When | Actor | IP and user agent |
|---|---|---|---|
web |
A page or form in the admin or the public site | The signed-in user, or Visitor | The client's |
api |
The REST API, with a key | The key's user | The client's |
job |
A background job (background.php) |
The user who started the job, or System | None; the user agent names the job, e.g. Background job Job_ItemBatchEditAll (process 12) |
cli |
A command-line script | The user the script set, or System | None; the user agent names the script |
Jobs Omeka runs within the page (such as a batch edit of selected items) are web, with the user and address of the page.
Batches
Each background job and script is one batch; a batch edit is one from its start; any other request becomes one once it logs more than ten events (and the earlier ones join it). A batch's events share a 16-character batch_id. The log folds a batch into one row; see Viewing the Activity Log.
IP addresses
The address is the one the request came from. The X-Forwarded-For header is only believed when the request comes from a private or loopback address, that is, from a proxy or load balancer in front of the site; the address used is then the rightmost public one in the header. A site reached directly ignores the header, so nobody can forge their address by sending it.
With Redact IP addresses on, IPv4 addresses end in .0 and IPv6 addresses keep their first three groups and end in :****. Redaction happens when the entry is written.
Stored fields
Entries live in the audit_trail table, with your database prefix.
| Column | Type | Content |
|---|---|---|
id |
BIGINT | Entry ID |
timestamp |
DATETIME | When the entry was written, in UTC |
actor_id |
INT | User ID, or NULL |
actor_display |
VARCHAR(255) | The user's display name (or username), or System |
action |
VARCHAR(128) | Action code |
resource_type |
VARCHAR(64) | Type, from the table above |
resource_id |
VARCHAR(128) | Record ID (or a name, for plugins and themes) |
resource_label |
VARCHAR(512) | Title, filename, username, plugin or theme name |
resource_url |
VARCHAR(1024) | The record's admin page, relative to the admin (items/show/12), or a full address. Empty once a record is deleted. |
detail |
VARCHAR(1024) | One line about what happened |
source |
VARCHAR(8) | web, api, job or cli |
batch_id |
CHAR(16) | The batch, or NULL |
ip_address |
VARCHAR(45) | Client address, possibly redacted |
user_agent |
TEXT | Browser, or the job or script, up to 1,000 characters |
extra |
MEDIUMTEXT | Data as JSON (at most 60 KB), or NULL |
Indexes: timestamp; (actor_id, id); (action, id); (resource_type, resource_id); (batch_id, id). They serve the log's filters and its grouping.
Failure handling
Every part of the logging catches its own errors and writes a message starting [AuditTrail] to PHP's error log, once per message. A logging failure never interrupts the action being performed: a missing table or a database error means missing entries, not a broken admin.
Querying the table directly
The table is plain SQL, so reports beyond the built-in filters are a query away. For example, the most active users of the last 30 days:
SELECT actor_display, COUNT(*) AS events
FROM omeka_audit_trail
WHERE `timestamp` >= UTC_TIMESTAMP() - INTERVAL 30 DAY AND actor_id IS NOT NULL
GROUP BY actor_id, actor_display
ORDER BY events DESC;