init
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Esun\BlogImport\Model;
|
||||
|
||||
use Magento\Framework\Exception\LocalizedException;
|
||||
|
||||
class HtmlProcessor
|
||||
{
|
||||
private const ESUN_MEDIA_HOSTS = ['esun3d.com', 'www.esun3d.com'];
|
||||
|
||||
private const MEDIA_EXTENSIONS = [
|
||||
'avif', 'bmp', 'gif', 'jpeg', 'jpg', 'm4v', 'mov', 'mp4', 'ogg', 'png', 'svg', 'tif', 'tiff', 'webm', 'webp',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return array{html: string, media: string[]}
|
||||
* @throws LocalizedException
|
||||
*/
|
||||
public function process(string $htmlPath, string $mediaSubdirectory): array
|
||||
{
|
||||
$html = file_get_contents($htmlPath);
|
||||
if ($html === false || trim($html) === '') {
|
||||
throw new LocalizedException(__('HTML file is empty or unreadable: %1', $htmlPath));
|
||||
}
|
||||
|
||||
$previous = libxml_use_internal_errors(true);
|
||||
$document = new \DOMDocument('1.0', 'UTF-8');
|
||||
$loaded = $document->loadHTML(
|
||||
'<?xml encoding="UTF-8"><div id="esun-import-root">' . $html . '</div>',
|
||||
LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD
|
||||
);
|
||||
libxml_clear_errors();
|
||||
libxml_use_internal_errors($previous);
|
||||
if (!$loaded) {
|
||||
throw new LocalizedException(__('Unable to parse HTML file: %1', $htmlPath));
|
||||
}
|
||||
|
||||
$xpath = new \DOMXPath($document);
|
||||
$article = $xpath->query(
|
||||
'//*[contains(concat(" ", normalize-space(@class), " "), " blog-article ")]'
|
||||
)->item(0);
|
||||
$contentRoot = $article instanceof \DOMElement
|
||||
? $article
|
||||
: $xpath->query('//*[@id="esun-import-root"]')->item(0);
|
||||
if (!$contentRoot instanceof \DOMElement) {
|
||||
throw new LocalizedException(__('No importable HTML content found in %1.', $htmlPath));
|
||||
}
|
||||
|
||||
$removeQueries = [
|
||||
'.//h1[contains(concat(" ", normalize-space(@class), " "), " news-title ")]',
|
||||
'.//*[contains(concat(" ", normalize-space(@class), " "), " news-time ")]',
|
||||
'.//script',
|
||||
'.//*[contains(concat(" ", normalize-space(@class), " "), " picker ")]',
|
||||
'./*[contains(concat(" ", normalize-space(@class), " "), " clear ")]',
|
||||
];
|
||||
foreach ($removeQueries as $query) {
|
||||
$nodes = iterator_to_array($xpath->query($query, $contentRoot));
|
||||
foreach ($nodes as $node) {
|
||||
$node->parentNode?->removeChild($node);
|
||||
}
|
||||
}
|
||||
|
||||
$media = [];
|
||||
$mediaReplacements = [];
|
||||
foreach (iterator_to_array($xpath->query('.//*', $contentRoot)) as $element) {
|
||||
if (!$element instanceof \DOMElement) {
|
||||
continue;
|
||||
}
|
||||
foreach (['src', 'href', 'poster'] as $attribute) {
|
||||
if (!$element->hasAttribute($attribute)) {
|
||||
continue;
|
||||
}
|
||||
$relativeMediaPath = $this->getLegacyMediaPath($element->getAttribute($attribute));
|
||||
if ($relativeMediaPath === null) {
|
||||
continue;
|
||||
}
|
||||
$media[$relativeMediaPath] = $relativeMediaPath;
|
||||
$placeholder = sprintf('__ESUN_IMPORTED_MEDIA_%d__', count($mediaReplacements));
|
||||
$mediaReplacements[$placeholder] = sprintf(
|
||||
"{{media url='%s/%s'}}",
|
||||
$mediaSubdirectory,
|
||||
str_replace("'", ''', $relativeMediaPath)
|
||||
);
|
||||
$element->setAttribute($attribute, $placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
$output = '';
|
||||
foreach ($contentRoot->childNodes as $child) {
|
||||
$output .= $document->saveHTML($child);
|
||||
}
|
||||
$output = trim(strtr($output, $mediaReplacements));
|
||||
if ($output === '') {
|
||||
throw new LocalizedException(__('HTML content became empty after processing: %1', $htmlPath));
|
||||
}
|
||||
|
||||
return ['html' => $output, 'media' => array_values($media)];
|
||||
}
|
||||
|
||||
private function getLegacyMediaPath(string $url): ?string
|
||||
{
|
||||
$url = html_entity_decode(trim($url), ENT_QUOTES | ENT_HTML5);
|
||||
$path = '';
|
||||
if (str_starts_with($url, '/uploads/')) {
|
||||
$embeddedUrl = rawurldecode(substr($url, strlen('/uploads/')));
|
||||
if (preg_match('#^(?:https?:)?//#iu', $embeddedUrl)) {
|
||||
return null;
|
||||
}
|
||||
$path = $url;
|
||||
} elseif (preg_match(
|
||||
'#^(?:https?:)?//([^/]+)(/.*)$#iu',
|
||||
$url,
|
||||
$matches
|
||||
)) {
|
||||
if (!in_array(strtolower($matches[1]), self::ESUN_MEDIA_HOSTS, true)) {
|
||||
return null;
|
||||
}
|
||||
$path = $matches[2];
|
||||
}
|
||||
if ($path === '') {
|
||||
return null;
|
||||
}
|
||||
$path = (string)preg_split('/[?#]/', $path, 2)[0];
|
||||
$filename = rawurldecode(basename($path));
|
||||
if ($filename === '' || $filename === '.' || $filename === '..') {
|
||||
return null;
|
||||
}
|
||||
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
|
||||
if (!in_array($extension, self::MEDIA_EXTENSIONS, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $filename;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user