epub_reader_framework-2.0.0-alpha2/src/Batch/ReaderChapterExtractorBatchProcessor.php
src/Batch/ReaderChapterExtractorBatchProcessor.php
<?php
namespace Drupal\epub_reader_framework\Batch;
use Drupal\Core\Url;
use Drupal\node\Entity\Node;
use Symfony\Component\HttpFoundation\RedirectResponse;
/**
* Handle the batch processing of the epub extraction process.
*
* @package Drupal\epub_reader_framework\Batch
*/
class ReaderChapterExtractorBatchProcessor {
/**
* Batch set operation callback.
*
* @param int $reader_publication_id
* Node ID of the parent reader_publication entity.
* @param array $context
* Batch context.
*
* @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
* @throws \Drupal\Component\Plugin\Exception\PluginNotFoundException
* @throws \Drupal\Core\Archiver\ArchiverException
* @throws \Drupal\Core\Entity\EntityStorageException
*/
public static function operationCallback($reader_publication_id, array &$context) {
if (empty($context['sandbox'])) {
self::initialiseSandbox($reader_publication_id, $context);
}
// Nothing to process.
if (!$context['sandbox']['max']) {
$context['finished'] = 1;
return;
}
// If we haven't yet processed all.
if ($context['sandbox']['progress'] < $context['sandbox']['max']) {
if (isset($context['sandbox']['spine'][$context['sandbox']['progress']])) {
$chapter = $context['sandbox']['spine'][$context['sandbox']['progress']];
if (isset($chapter['uri'])) {
if ($reader_publication = Node::load($context['sandbox']['reader_publication_id'])) {
/** @var \Drupal\node\NodeInterface $reader_publication */
/** @var \Drupal\epub_reader_framework\Epub\ReaderEpubXhtmlConverter $epub_xhtml_converter */
$epub_xhtml_converter = \Drupal::service('epub_reader_framework.reader_epub_xhtml_converter');
try {
$context['sandbox']['chapter_nids'][] = $epub_xhtml_converter->convertXhtmlToChapter(
$reader_publication,
$chapter['uri'],
$chapter['@id']
);
}
catch (Exception $e) {
\Drupal::messenger()->addWarning($e->getMessage());
}
// Store chapter in results. This is used both in the finish
// callback as well as the orphaned chapter cleanup.
$context['results']['chapters'][] = $chapter['@id'];
}
}
}
$context['sandbox']['progress']++;
}
// Mark reader EPUB as processed:
if ($reader_publication = Node::load($context['sandbox']['reader_publication_id'])) {
if ($reader_publication->hasField('field_reader_file_processed') && !$reader_publication->field_reader_file_processed->value) {
$reader_publication->set('field_reader_file_processed', TRUE);
$reader_publication->setNewRevision(TRUE);
$reader_publication->save();
}
}
// Extra step added for post-processing.
// @see self::extractEpub().
if ($context['sandbox']['progress'] == $context['sandbox']['max']) {
if ($context['results']['chapters'] && $reader_publication = Node::load($context['sandbox']['reader_publication_id'])) {
/** @var \Drupal\node\NodeInterface $reader_publication */
/** @var \Drupal\epub_reader_framework\Epub\ReaderEpubCompletedCleanup $epub_xhtml_converter */
$epub_xhtml_converter = \Drupal::service('epub_reader_framework.reader_epub_completed_cleanup');
$epub_xhtml_converter->cleanupChapters($reader_publication,
$context['results']['chapters']);
if ($context['sandbox']['chapter_nids']) {
/** @var \Drupal\epub_reader_framework\Entity\ReaderEntityCrossReference $reader_entity_cross_reference */
$reader_entity_cross_reference = \Drupal::service('epub_reader_framework.reader_entity_cross_reference');
$reader_entity_cross_reference->crossReferenceChaptersFromPublicationExtraction(
$reader_publication,
$context['sandbox']['chapter_nids']
);
}
}
$context['sandbox']['progress']++;
}
// When progress equals max, finished is '1' which means completed. Any
// decimal between '0' and '1' is used to determine the percentage of
// the progress bar.
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
}
/**
* Intialise sandbox on first operation callback.
*
* @param int $reader_publication_id
* Node ID of the parent reader_publication entity.
* @param array $context
* Batch context.
*
* @throws \Drupal\Core\Archiver\ArchiverException
*/
protected static function initialiseSandbox(
$reader_publication_id,
array &$context
) {
$context['sandbox']['progress'] = 0;
$context['sandbox']['max'] = 0;
$context['sandbox']['reader_publication_id'] = $reader_publication_id;
$context['sandbox']['chapter_nids'] = [];
$context['results'] = [];
$context['results']['chapters'] = [];
$context['results']['reader_publication_id'] = $reader_publication_id;
self::extractEpub($context);
}
/**
* Context to load per operation.
*
* Full context sandbox is stored and passed between each batch opeation
* callback occurrence. It is possible the publication epub content is too
* large for that storage so load on demand per run instead.
*
* @param array $context
* The batch processing context.
*
* @throws \Drupal\Core\Archiver\ArchiverException
*/
protected static function extractEpub(array &$context) {
if (isset($context['sandbox']['reader_publication_id']) && $context['sandbox']['reader_publication_id']) {
if ($reader_publication = Node::load($context['sandbox']['reader_publication_id'])) {
/** @var \Drupal\node\NodeInterface $reader_publication */
/** @var \Drupal\epub_reader_framework\Epub\ReaderEpubZipExtractor $epub_zip_extractor */
$epub_zip_extractor = \Drupal::service('epub_reader_framework.reader_epub_zip_extractor');
$epub_zip_extractor->extractEpubFiles($reader_publication);
$context['sandbox']['extracted_files'] = $epub_zip_extractor->getExtractedFiles($reader_publication);
$context['sandbox']['spine'] = $epub_zip_extractor->getSpineContents($reader_publication);
$context['sandbox']['max'] = (is_array($context['sandbox']['spine']) ? count($context['sandbox']['spine']) : 0);
// If we have chapters, add 1 post-processing step.
if ($context['sandbox']['max']) {
$context['sandbox']['max']++;
}
}
}
}
/**
* Batch finished callback.
*
* @param bool $success
* Batch encountered errors or not.
* @param array $results
* The processed chapters.
* @param array $operations
* The different batches that were run.
*/
public static function finishedCallback($success, array $results, array $operations) {
if ($success) {
// The 'success' parameter means no fatal PHP errors were detected.
$message = t('@count chapters were extracted successfully.', [
'@count' => (isset($results['chapters']) ? count($results['chapters']) : 0),
]);
\Drupal::messenger()->addStatus($message);
}
else {
// A fatal error occurred.
$message = t('Finished with an error.');
\Drupal::messenger()->addWarning($message);
}
if (isset($results['reader_publication_id'])) {
// Redirect to adding a publication.
$url = Url::fromRoute('entity.node.edit_form', [
'node' => $results['reader_publication_id'],
])->toString();
$response = new RedirectResponse($url);
$response->send();
}
}
}
