4 changed files with 179 additions and 45 deletions
@ -0,0 +1,7 @@ |
|||||||
|
services: |
||||||
|
caption_linker.caption_linker: |
||||||
|
class: Drupal\caption_linker\Service\CaptionLinker |
||||||
|
arguments: |
||||||
|
- '@entity_type.manager' |
||||||
|
- '@islandora.utils' |
||||||
|
- '@logger.channel.islandora' |
||||||
@ -0,0 +1,67 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\caption_linker\Service; |
||||||
|
|
||||||
|
use Drupal\Core\Entity\ContentEntityInterface; |
||||||
|
use Drupal\Core\Entity\EntityTypeManagerInterface; |
||||||
|
use Drupal\Core\Logger\LoggerChannelInterface; |
||||||
|
use Drupal\islandora\IslandoraUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Service to link transcript media items to host media items as captions. |
||||||
|
*/ |
||||||
|
class CaptionLinker { |
||||||
|
|
||||||
|
/** |
||||||
|
* Constructs a new CaptionLinker service. |
||||||
|
* |
||||||
|
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager |
||||||
|
* The entity type manager service. |
||||||
|
* @param \Drupal\islandora\IslandoraUtils $islandoraUtils |
||||||
|
* The Islandora utils service. |
||||||
|
* @param \Drupal\Core\Logger\LoggerChannelInterface $logger |
||||||
|
* The logger for the Islandora channel. |
||||||
|
*/ |
||||||
|
public function __construct( |
||||||
|
protected readonly EntityTypeManagerInterface $entityTypeManager, |
||||||
|
protected readonly IslandoraUtils $islandoraUtils, |
||||||
|
protected readonly LoggerChannelInterface $logger |
||||||
|
) {} |
||||||
|
|
||||||
|
/** |
||||||
|
* Links a transcript media item to a host media item as a caption. |
||||||
|
* |
||||||
|
* @param \Drupal\Core\Entity\ContentEntityInterface $entity |
||||||
|
* The node entity to process. |
||||||
|
*/ |
||||||
|
public function linkCaption(ContentEntityInterface $entity): void { |
||||||
|
$transcript_media = $this->getMediaByUri($entity, 'http://pcdm.org/use#Transcript'); |
||||||
|
$host_media = $this->getMediaByUri($entity, 'http://pcdm.org/use#ServiceFile') |
||||||
|
?? $this->getMediaByUri($entity, 'http://pcdm.org/use#OriginalFile'); |
||||||
|
|
||||||
|
if ($transcript_media && $host_media |
||||||
|
&& $transcript_media->bundle() === 'able_player_caption' |
||||||
|
&& $host_media->hasField('field_ableplayer_media_caption') |
||||||
|
&& $host_media->get('field_ableplayer_media_caption')->isEmpty()) { |
||||||
|
$host_media->set('field_ableplayer_media_caption', ['target_id' => $transcript_media->id()]); |
||||||
|
$host_media->save(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Gets a media item from an entity by its RDF use URI. |
||||||
|
* |
||||||
|
* @param \Drupal\Core\Entity\ContentEntityInterface $entity |
||||||
|
* The node or media entity. |
||||||
|
* @param string $uri |
||||||
|
* The RDF use URI. |
||||||
|
* |
||||||
|
* @return \Drupal\media\MediaInterface|null |
||||||
|
* The matching media item or NULL if not found. |
||||||
|
*/ |
||||||
|
protected function getMediaByUri(ContentEntityInterface $entity, string $uri) { |
||||||
|
$term = $this->islandoraUtils->getTermForUri($uri); |
||||||
|
return $this->islandoraUtils->getMediaWithTerm($entity, $term); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,101 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace Drupal\Tests\caption_linker\Unit\Service; |
||||||
|
|
||||||
|
use Drupal\caption_linker\Service\CaptionLinker; |
||||||
|
use Drupal\Core\Entity\EntityTypeManagerInterface; |
||||||
|
use Drupal\Core\Logger\LoggerChannelInterface; |
||||||
|
use Drupal\islandora\IslandoraUtils; |
||||||
|
use Drupal\media\MediaInterface; |
||||||
|
use Drupal\node\NodeInterface; |
||||||
|
use PHPUnit\Framework\MockObject\MockObject; |
||||||
|
use PHPUnit\Framework\TestCase; |
||||||
|
|
||||||
|
/** |
||||||
|
* @coversDefaultClass \Drupal\caption_linker\Service\CaptionLinker |
||||||
|
* @group caption_linker |
||||||
|
*/ |
||||||
|
class CaptionLinkerTest extends TestCase { |
||||||
|
|
||||||
|
/** |
||||||
|
* The service under test. |
||||||
|
*/ |
||||||
|
protected CaptionLinker $captionLinker; |
||||||
|
|
||||||
|
protected MockObject $islandoraUtils; |
||||||
|
protected MockObject $entityTypeManager; |
||||||
|
protected MockObject $logger; |
||||||
|
|
||||||
|
protected function setUp(): void { |
||||||
|
$this->islandoraUtils = $this->createMock(IslandoraUtils::class); |
||||||
|
$this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class); |
||||||
|
$this->logger = $this->createMock(LoggerChannelInterface::class); |
||||||
|
|
||||||
|
$this->captionLinker = new CaptionLinker( |
||||||
|
$this->entityTypeManager, |
||||||
|
$this->islandoraUtils, |
||||||
|
$this->logger |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests linking the caption when all conditions are met. |
||||||
|
*/ |
||||||
|
public function testLinkCaption(): void { |
||||||
|
$node = $this->createMock(NodeInterface::class); |
||||||
|
$transcript = $this->createMock(MediaInterface::class); |
||||||
|
$host = $this->createMock(MediaInterface::class); |
||||||
|
|
||||||
|
// Mock the Islandora methods. |
||||||
|
$this->islandoraUtils->method('getTermForUri')->willReturn('mock_term'); |
||||||
|
$this->islandoraUtils->method('getMediaWithTerm') |
||||||
|
->withConsecutive([$node, 'mock_term'], [$node, 'mock_term'], [$node, 'mock_term']) |
||||||
|
->willReturnOnConsecutiveCalls($transcript, $host, null); |
||||||
|
|
||||||
|
$transcript->method('bundle')->willReturn('able_player_caption'); |
||||||
|
|
||||||
|
// Field checks and save expectations. |
||||||
|
$host->method('hasField')->with('field_ableplayer_media_caption')->willReturn(TRUE); |
||||||
|
$host->method('get')->with('field_ableplayer_media_caption')->willReturn( |
||||||
|
new class { |
||||||
|
public function isEmpty() { return TRUE; } |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
$host->expects($this->once())->method('set')->with( |
||||||
|
'field_ableplayer_media_caption', |
||||||
|
['target_id' => $transcript->id()] |
||||||
|
); |
||||||
|
$host->expects($this->once())->method('save'); |
||||||
|
|
||||||
|
$transcript->method('id')->willReturn(42); |
||||||
|
|
||||||
|
$this->captionLinker->linkCaption($node); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Tests that no linking occurs if caption field is not empty. |
||||||
|
*/ |
||||||
|
public function testNoLinkIfFieldPopulated(): void { |
||||||
|
$node = $this->createMock(NodeInterface::class); |
||||||
|
$transcript = $this->createMock(MediaInterface::class); |
||||||
|
$host = $this->createMock(MediaInterface::class); |
||||||
|
|
||||||
|
$this->islandoraUtils->method('getTermForUri')->willReturn('mock_term'); |
||||||
|
$this->islandoraUtils->method('getMediaWithTerm')->willReturnOnConsecutiveCalls($transcript, $host); |
||||||
|
|
||||||
|
$transcript->method('bundle')->willReturn('able_player_caption'); |
||||||
|
$host->method('hasField')->willReturn(TRUE); |
||||||
|
$host->method('get')->willReturn( |
||||||
|
new class { |
||||||
|
public function isEmpty() { return FALSE; } |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
$host->expects($this->never())->method('set'); |
||||||
|
$host->expects($this->never())->method('save'); |
||||||
|
|
||||||
|
$this->captionLinker->linkCaption($node); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue