Docs MediaHub Version 1.x Custom Fields

Custom Fields

Add your own metadata fields to MediaHub assets and per-field drawers. Custom fields follow ProcessWire's own template model and support per-reference overrides with reset-to-master, introduced in 1.19.25.

Quick start

Custom fields follow ProcessWire's own model: create a field, attach it to a template, and MediaHub picks it up automatically.

  1. Create the field. Go to Setup > Fields > Add New Field. Choose a type (Text, Textarea, Options, etc.), give it a name, and save.
  2. Show system templates. Go to Setup > Templates and tick Show system templates in the top right. MediaHub's asset template is hidden by default.
  3. Add the field to the asset template. Edit the pkd-mediahub-asset template, drag your new field on to it, and save.
  4. Done. Open any MediaHub asset and your field appears at the bottom of the sidebar. The same field shows up in the InputfieldMediaHub drawer wherever editors pick assets on pages.

Naming your fields

MediaHub does not enforce a naming prefix. Any field name works. Because you edit pkd-mediahub-asset directly, it can be easy to confuse your own fields with MediaHub's built-in ones (pkd_mediahub_*, title, etc.).

A prefix like fieldCustom_credits or fieldCustom_suggested_caption is a good convention:

  • Your fields group visually apart from module infrastructure in Setup > Fields.
  • On the asset template you can see at a glance which fields you added versus which MediaHub installed.
  • You are less likely to remove a built-in field by mistake when tidying the template.

This is a convention only. MediaHub discovers any non-built-in field regardless of name. The module config Custom Fields panel lists everything it has found on your install.

Two tiers

Custom fields live in two tiers. Both use ordinary ProcessWire templates and fields. You can use either or both. The simplest setup uses Tier 1 only, which is the behaviour in all versions up to and including 1.19.1.

Tier Template Scope Master value Per-reference override
1. Asset-level (inherited) pkd-mediahub-asset Every asset, everywhere Yes, on the asset detail page Yes, override + reset-to-master in the drawer
2. Field-specific mediahub-field-{fieldName} (file-less, developer-created) One InputfieldMediaHub field No, value lives only on the reference Every value is per-reference by definition

Which tier to use depends on the metadata:

  • "Photo by Jane Doe / Reuters" is a property of the asset. Set it once and every page that references it sees it. Use Tier 1.
  • "OG description for the homepage hero" is a property of how the asset is used on a specific page, only meaningful for the hero_image field. Use Tier 2.
  • "Caption for this article, overriding the default" is a Tier 1 field where the editor has entered a per-reference override.

Per-field drawer scoping (Tier 2)

By default every Tier 1 custom field appears in every MediaHub field's drawer. You can filter what a specific drawer shows, and add fields that exist only for that drawer, by creating a file-less template named mediahub-field-{fieldName}, where {fieldName} matches the name of the InputfieldMediaHub field on your page template.

For example: a MediaHub field called hero_image can have its own curated drawer if you create a template named mediahub-field-hero_image.

This mirrors ProcessWire's native field-{fieldName} pattern for image and file fields (introduced in PW 3.0.142), adapted for shared library assets with per-reference overrides.

Drawer behaviour

Field exists on… Drawer shows? Master value? Reset button? Stored in
Asset template and field template Yes (inherited) Yes Yes meta['custom'][name] (override only)
Asset template only, and a field template exists No, filtered out of this drawer n/a n/a n/a
Asset template only, no field template defined Yes (inherited) Yes Yes meta['custom'][name] (pre-1.19.25 behaviour)
Field template only Yes (field-specific) No No meta['fieldCustom'][name]

Setting up a per-field template

  1. Decide which MediaHub input field to scope to (for example, hero_image).
  2. Go to Setup > Templates > Add New. Tick Don't create a template file, name it mediahub-field-hero_image, and save.
  3. Go to Setup > Fields and create any fields you want to show only in this drawer.
  4. Edit the mediahub-field-hero_image template and add the fields you want visible in hero_image's drawer. Include any existing asset-level fields you want to keep visible (they continue to inherit and override). Add any field-specific fields that should not appear on the universal asset template.
  5. Save.

The next time an editor opens the hero_image drawer, they see only the curated list.

Note: do not add built-in drawer fields (title, alt, etc.) to a per-field template. They are excluded by design to avoid duplicating the built-in drawer inputs.

Use cases

Below are four areas where the inherit-with-override model genuinely earns its keep. Mix and match as needed.

Editorial attribution and captioning

For newsrooms, publishers, and any site where an asset's provenance matters. Set the canonical credit once on the master; per-page overrides handle exceptions.

FieldTypePurpose
creditsText"Photo: Jane Doe / Reuters" — single source of truth for attribution.
suggested_captionTextareaDefault caption copy; editors fine-tune per-article.
embargo_notesTextareaInternal usage notes (e.g. "Do not publish before 2026-07-01").

Distribution and rights control

FieldTypePurpose
approved_channelsOptions (Checkboxes)Platforms cleared for this asset (Instagram, X, LinkedIn, etc.).
usage_rightsOptions (Select)editorial-only, web-only, print-and-web, all-rights-cleared.
license_expiresDatetimeLicense or embargo expiry date.
source_urlURLOrigin URL for stock or syndicated assets.

Social graph / OpenGraph defaults

Probably the strongest real-world fit for the inherit-with-override model. The master asset stores sensible OG defaults set once by the marketing team; individual article authors override the social copy per-page. The same asset on five different articles can carry five different OG descriptions without ever cloning the asset.

$mh   = wire('modules')->get('MediaHub');
$hero = $page->hero_image->first();
if ($hero) {
$ogDesc = $mh->getRefValue($page, 'hero_image', $hero, 'og_description');
echo '<meta property="og:description" content="' . htmlspecialchars($ogDesc) . '">';
}

Workflow and internal metadata

FieldTypePurpose
internal_statusOptions (Select)draft, in-review, approved, archived.
internal_notesTextareaPrivate editorial notes, not visible to the public.
shoot_dateDateWhen the photo was taken (distinct from published).
ownerPage Reference (Users)Who is responsible for this asset.

The inherit-with-override model

Custom fields behave exactly like the Title and Alt fields already do in MediaHub:

  • The master asset holds the canonical value, the single source of truth.
  • When the asset is referenced from a MediaHub field on a page, the drawer shows the master's value by default.
  • The editor can override the value per-reference. That override applies only on that one page.
  • A small reset-to-master button appears next to any overridden field, so it is always easy to drop back to the canonical value.

Overrides are saved when the page editor saves the page. There is no separate save button on the custom fields section, which matches how Title and Alt already work.

Crops

Crops share the master asset's content. The drawer for a crop reference inherits the master's custom field defaults. Per-reference overrides are still possible if you need a different value for a specific crop in a specific page context. The override is keyed by the crop's page ID, exactly as Title and Alt overrides on crop references already work.

Where custom fields appear

SurfaceWhat you see
Asset detail pageAn Additional fields section in the right sidebar. Edits here update the master asset.
InputfieldMediaHub drawerEach custom field appears as its own inline-labelled input below Tags, with the master's value as the default. Edits stage as per-reference overrides, saved when the page is saved. A per-field reset button restores the master's value.
Module configA read-only Custom Fields panel in Modules > Site > MediaHub listing every discovered field with its name, label, and type. Tier 2 per-field templates are also listed with tier badges.
Uninstall PreparationA Custom fields row marked Preserve, not Remove. Custom field definitions are never deleted on uninstall.

Reading custom field values

Direct field access returns the master value, the same as any other ProcessWire field. This is intentional: it never silently changes behaviour for existing templates.

$asset = $pages->get(1234); // a MediaHub asset page
echo $asset->copyright;     // master value

When iterating a MediaHub field on a page, direct field access still returns the master:

foreach ($page->images as $asset) {
echo $asset->copyright; // master value, not override-aware
}

Getting the effective (override-aware) value

Use getRefValue() when you want the per-reference override applied:

$mh = wire('modules')->get('MediaHub');
foreach ($page->images as $asset) {
$effectiveCopyright = $mh->getRefValue($page, 'images', $asset, 'copyright');
echo $effectiveCopyright; // override if present, else master
}

getRefValue() works for built-in keys too ('title', 'alt', 'tags'). Call-site code does not need to know which tier a key belongs to. Field-specific (Tier 2) keys return null when no per-reference value exists, as there is no master for Tier 2.

$mh->getRefValue($page, 'images', $asset, 'alt');                  // built-in
$mh->getRefValue($page, 'images', $asset, 'fieldCustom_credits');  // Tier 1 (override > master)
$mh->getRefValue($page, 'images', $asset, 'og_description');       // Tier 2 (reference-only)

Via findRaw()

findRaw() is context-aware. When called against a MediaHubPageArray (the value of a MediaHub field on a page), it already knows the page and field context, so it returns the effective override-aware value for any opted-in custom field:

$rows = $mediahub->findRaw($page->images, [
'fields' => ['title', 'url', 'copyright', 'photographer'],
]);
// 'copyright' in each row is the per-ref override if present, else the master value

For Tier 2 (field-specific) fields, opt in by name when calling against a MediaHubPageArray that has the right page and field context:

// $page->blog_images has a mediahub-field-blog_images per-field template
$rows = $mediahub->findRaw($page->blog_images, [
'fields' => ['fieldCustom_suggested_caption', 'fieldCustom_approved_for'],
]);
foreach ($rows as $row) {
echo $row['fieldCustom_suggested_caption']; // per-reference value, or null
}

Requesting Tier 2 keys against a bare selector or a list without page and field context will not return per-reference values. Use getRefValue() in that case instead.

See Lightweight asset retrieval (findRaw) for the full findRaw() reference.

Storage schema

Per-reference values for both tiers are stored in the pkd_mediahub_ref_meta table as a JSON blob per page and field and asset. Two sub-keys keep the tiers separate:

{
"title":  "Per-reference title override",
"alt":    "Per-reference alt override",
"tags":   [],
"custom": {
"fieldCustom_credits": "Photo by Jane Doe / Reuters"
},
"fieldCustom": {
"og_description": "A stunning landscape, featured on the homepage."
}
}
  • custom holds overrides of Tier 1 (asset-level) fields. Absent when no override exists; the master value applies.
  • fieldCustom holds values of Tier 2 (field-specific) fields. Absent when nothing has been set; getRefValue() returns null.

There is no schema migration when adopting per-field templates. Only the JSON shape inside the existing column gains a sub-key.

Field types

MediaHub passes custom fields through ProcessWire's normal field and inputfield stack. Most standard fieldtypes work on the asset detail page. The InputfieldMediaHub drawer is more sensitive to fieldtype complexity.

Verified in 1.19.25

FieldtypeInputfieldNotes
TextSingle-line textFully verified end-to-end.
TextareaPlain textarea (not CKEditor)Fully verified end-to-end.
OptionsSelect, Radio, or CheckboxesCheckboxes verified; findRaw() returns { id, value, title } arrays.

Expected to work (not yet verified)

These standard ProcessWire fieldtypes should behave like Text. Drawer and override behaviour has not been tested for 1.19.25:

  • Email, URL
  • Integer, Float
  • Datetime
  • Checkbox, Toggle (single-value)
  • Page Reference (single or multiple)
  • Multi-language variants (TextLanguage, TextareaLanguage, etc.)

Detail page only (drawer may not render cleanly)

Complex inputfields may not render cleanly inside the InputfieldMediaHub drawer. They will usually work on the asset detail page. If a field type misbehaves in the drawer, edit it via the asset's detail page in the library:

  • CKEditor / Textarea with rich text
  • Repeater, RepeaterMatrix
  • Files, Images
  • Matrix and other heavy composite types

Language support

If a custom field uses a multi-language fieldtype (e.g. FieldtypeTextLanguage), ProcessWire renders language tabs automatically. No extra configuration in MediaHub is needed.

Note: this applies only to custom fields in 1.19.25. MediaHub's built-in Alt and About fields are single-language for now. Multi-language support for those is on the roadmap.

Import and migration

MediaHub's import tools copy image files and map a few built-in metadata fields. They do not migrate custom asset field values.

Import pathWhat is importedCustom asset fields
Import page images (field button)Image file; title, alt, tags from PageimageNot imported. Fill in on the asset after import.
Scan for Existing Images (Library import)Image files from image fields across the siteNot imported.
Import by URLImage file and basic metadataNot imported.

Migrating values from legacy Pageimage custom fields to MediaHub asset custom fields is not supported in 1.19.25. It is on the roadmap as a separate migration feature. Plan to re-enter editorial metadata on imported assets, or script a one-off migration if you have many files.

How custom fields survive upgrades

Custom fields are stored in ProcessWire's own field and template tables, not inside MediaHub. MediaHub's install routines are additive: they only ever check for and create built-in fields. They never iterate the asset fieldgroup looking for things to remove. Your custom fields stay attached to the asset template across every upgrade.

How custom fields survive uninstall

Uninstalling MediaHub does not delete your custom field definitions or their data:

  1. Before deleting the asset template's fieldgroup, MediaHub detaches every custom field from it. A confirmation message lists the fields preserved.
  2. The asset fieldgroup and built-in MediaHub fields are then removed normally.
  3. Your custom field definitions remain in Setup > Fields. Their data tables remain in the database with all values intact.

If you reinstall MediaHub, the asset template is recreated with only the built-in fields. To restore your custom fields, re-add them to the pkd-mediahub-asset template via Setup > Templates. No data loss.

The Uninstall Preparation UI in module settings flags this clearly: custom fields are shown as Preserve N rather than the red Remove label used for built-in module infrastructure.

Hookable discovery

The custom field discovery method is hookable, so you can extend or filter the list from a site module:

$wire->addHookAfter('MediaHub::getCustomAssetFields', function($event) {
$fields = $event->return;
// Remove a sensitive field from the drawer and detail page
$event->return = array_filter($fields, function($f) {
return $f->name !== 'internal_notes';
});
});

This affects both the asset detail page render and the drawer, since both go through the same discovery method.

Troubleshooting

My field isn't showing up

  • Make sure it's added to the pkd-mediahub-asset template, not just defined in Setup > Fields.
  • Verify it isn't in the built-in list: title, pkd_mediahub_image, pkd_mediahub_file, pkd_mediahub_alt, pkd_mediahub_about, pkd_mediahub_labels, pkd_mediahub_mime, pkd_mediahub_variants, pkd_mediahub_collections, pkd_mediahub_favourite, pkd_mediahub_dominant_color. Anything else is treated as a custom field.
  • Check the Custom Fields panel in the module config. If your field appears there, MediaHub has discovered it correctly.

My field shows on the detail page but not in the drawer

This is expected for complex input types such as CKEditor, Repeater, etc. Use the asset detail page to edit them. The detail page can host any fieldtype ProcessWire supports.

My per-field template isn't filtering the drawer

  • Check the template name exactly: mediahub-field-{yourMhFieldName}. A single typo will prevent discovery.
  • Confirm the MediaHub field name on your page template matches the suffix (e.g. hero_image maps to mediahub-field-hero_image).
  • Try a hard browser refresh after creating the template, as the drawer is loaded via AJAX.
  • Check the Custom Fields panel in the module config. Discovered per-field templates are listed there with Tier 2 badges.

I edited a value in the drawer but $asset->copyright still returns the old master value

Correct. Direct field access always returns the master value, never a per-reference override. Use $mh->getRefValue($page, 'fieldName', $asset, 'copyright') to get the effective value, or use findRaw($page->images, ['fields' => ['copyright']]), which is override-aware by default.

Overrides aren't saving

Did you save the page itself? Drawer edits stage as per-reference overrides and are committed when the page-edit form is saved, the same as Title and Alt. There is no separate save button on the custom fields section by design.

I can't find the pkd-mediahub-asset template

It is hidden by the system flag. In Setup > Templates, tick Show system templates in the top right.

I uninstalled MediaHub and reinstalled — where are my custom fields?

The definitions and data are preserved. Go to Setup > Templates > pkd-mediahub-asset and re-add them to the template. See Uninstalling for full details of what is preserved and what is removed.

Last updated