epub_reader_framework-2.0.0-alpha2/src/Entity/ReaderEntityChapterHeadingsSave.php
src/Entity/ReaderEntityChapterHeadingsSave.php
<?php
namespace Drupal\epub_reader_framework\Entity;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Component\Utility\DeprecationHelper;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Wa72\HtmlPageDom\HtmlPageCrawler;
/**
* Class ReaderEntityChapterHeadingsSave.
*/
class ReaderEntityChapterHeadingsSave {
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Storage for heading order.
*
* @var int
*/
protected $order = 1;
/**
* Storage for found headings.
*
* @var array
*/
protected $headings = [];
/**
* Constructor.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager service.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager) {
$this->entityTypeManager = $entity_type_manager;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity_type.manager')
);
}
/**
* Save all h2s into the reader chapter heading storage.
*
* @param \Drupal\node\NodeInterface $reader_publication
* The reader publication node.
* @param \Drupal\node\NodeInterface $reader_chapter
* The reader chapter node.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
* @throws \Drupal\Core\Theme\MissingThemeDependencyException
*/
public function saveHeadings(
NodeInterface $reader_publication,
NodeInterface $reader_chapter
) {
if ($output = $this->getRenderedNodeOutput($reader_chapter)) {
$this->determineHeadings($output, $reader_chapter);
$this->deleteExistingHeadings($reader_publication, $reader_chapter);
$this->saveNewHeadings($reader_publication, $reader_chapter);
}
}
/**
* Get the rendered markup for the chapter.
*
* @param \Drupal\node\NodeInterface $reader_chapter
* The reader chapter node.
*
* @return string
* The markup of the rendered node.
*
* @throws \Drupal\Core\Theme\MissingThemeDependencyException
*/
protected function getRenderedNodeOutput(NodeInterface $reader_chapter) {
$render = \Drupal::entityTypeManager()->getViewBuilder('node')->view($reader_chapter, 'full');
/** @var \Drupal\Core\Render\Renderer $renderer */
$renderer = \Drupal::service('renderer');
// Set the active theme to the default theme.
/** @var \Drupal\Core\Theme\ThemeInitialization $theme_initialization */
$theme_initialization = \Drupal::service('theme.initialization');
$default_theme = \Drupal::config('system.theme')->get('default');
$original_active_theme = \Drupal::theme()->getActiveTheme();
\Drupal::theme()->setActiveTheme($theme_initialization->getActiveThemeByName($default_theme));
// Render the node with the default theme.
$output = DeprecationHelper::backwardsCompatibleCall(
\Drupal::VERSION,
'10.3',
fn() => $renderer->renderInIsolation($render),
fn() => $renderer->renderPlain($render),
);
if ($output instanceof MarkupInterface) {
$output = $output->__toString();
}
// Reset the active theme.
\Drupal::theme()->setActiveTheme($original_active_theme);
return (is_string($output) ? $output : '');
}
/**
* Determine all h2s from the chapter.
*
* @param string $output
* The rendered markup of the chapter.
* @param \Drupal\node\NodeInterface $reader_chapter
* The reader chapter node.
*/
public function determineHeadings($output, NodeInterface $reader_chapter) {
// Get all headings within the content area.
$this->headings = [];
$this->order = 1;
$self = $this;
$crawler = new HtmlPageCrawler($output);
$crawler->filter('#js-epub-reader-framework-content h2')
->each(function (HtmlPageCrawler $node, $i) use ($self, $reader_chapter) {
$text = $node->getCombinedText();
$text = preg_replace('/[ ]{2,}|[\t]/', ' ', trim($text));
$text = trim($text);
if ($reader_chapter->label() != $text) {
$self->headings[$self->order] = $text;
$self->order++;
}
});
}
/**
* Delete existing reader chapter headings for this chapter.
*
* @param \Drupal\node\NodeInterface $reader_publication
* The reader publication node.
* @param \Drupal\node\NodeInterface $reader_chapter
* The reader chapter node.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function deleteExistingHeadings(
NodeInterface $reader_publication,
NodeInterface $reader_chapter
) {
// Delete existing headings within this chapter and publication.
$storage = $this->entityTypeManager->getStorage('reader_chapter_heading');
$query = $storage->getQuery();
$query->condition('reader_publication_id', $reader_publication->id());
$query->condition('reader_chapter_id', $reader_chapter->id());
$query->accessCheck();
$chapter_heading_ids = $query->execute();
if ($chapter_heading_ids && $chapter_headings = $storage->loadMultiple($chapter_heading_ids)) {
foreach ($chapter_headings as $chapter_heading) {
$chapter_heading->delete();
}
}
}
/**
* Save the new h2s into the reader chapter heading storage.
*
* @param \Drupal\node\NodeInterface $reader_publication
* The reader publication node.
* @param \Drupal\node\NodeInterface $reader_chapter
* The reader chapter node.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public function saveNewHeadings(
NodeInterface $reader_publication,
NodeInterface $reader_chapter
) {
// Save new headings.
if ($this->headings) {
$storage = $this->entityTypeManager->getStorage('reader_chapter_heading');
foreach ($this->headings as $order => $heading) {
$chapter_heading = $storage->create([
'reader_publication_id' => $reader_publication->id(),
'reader_chapter_id' => $reader_chapter->id(),
'order' => $order,
'heading' => $heading,
]);
$chapter_heading->save();
}
}
}
}
