Skip to content

The icon position of a file

The corner the icon sits in is set for the site, and for a burned-in label once per image style. An editor can also choose it for one file, see The position of a single image. ai_disclosure.icon_position stores that choice and tells the position that applies. It is a Drupal\ai_disclosure\IconPositionStorageInterface.

The choice is keyed by file ID, in the ai_disclosure_icon_position table. The disclosure field is not involved.

Method Does
get($fid) the position stored for the file, or NULL
set($fid, $position) stores a position, or removes it with NULL. Throws \InvalidArgumentException for a value that is not a corner
effective($fid, $style_position = NULL) the position that applies: the file, else $style_position, else the site setting
forUri($uri) the stored position of the file at a URI, with its cacheability, for a caller that has no file ID
cacheTag($fid) the cache tag of the file's position
deleteForFile($fid) removes the row of a file that is gone, without an event

The corners are MediaOverlay::POSITIONS: top_right, top_left, bottom_right and bottom_left.

$positions = \Drupal::service('ai_disclosure.icon_position');
$positions->set($file->id(), 'bottom_left');

// Inheritance: the file, then the style, then the site.
$corner = $positions->effective($file->id(), 'top_right');

Output that depends on the position needs the cache tag of the file:

$build['#cache']['tags'][] = $positions->cacheTag($file->id());

set() invalidates that tag. The row of a file is removed when the file is deleted.

The event

set() dispatches AiDisclosureEvents::ICON_POSITION_CHANGED (ai_disclosure.icon_position_changed) after it has stored the new value, and only when the value differs from the old one. The event is a Drupal\ai_disclosure\Event\IconPositionChangedEvent:

Method Returns
getFileId() the ID of the file
getOldPosition() the position before, or NULL
getNewPosition() the position after, or NULL when it was removed

The module that owns the event does not depend on anything that listens. The ai_disclosure_image submodule listens, and flushes the burned-in derivatives of the file. A module that keeps its own copies of a labelled image can do the same:

public static function getSubscribedEvents(): array {
  return [AiDisclosureEvents::ICON_POSITION_CHANGED => 'onChanged'];
}

public function onChanged(IconPositionChangedEvent $event): void {
  $this->myCache->purgeFile($event->getFileId());
}