use DOMDocument;
use DOMElement;
-use DOMNode;
use Friendica\Model\Photo;
use Friendica\Model\Post;
*/
class NPF
{
- static public function fromBBCode(string $bbcode, int $uri_id): array
+ private static $heading_subtype = [];
+
+ /**
+ * Convert BBCode into NPF (Tumblr Neue Post Format)
+ *
+ * @param string $bbcode
+ * @param integer $uri_id
+ * @return array NPF
+ */
+ public static function fromBBCode(string $bbcode, int $uri_id): array
{
- $npf = [];
-
$bbcode = self::prepareBody($bbcode);
- $html = BBCode::convert($bbcode, false, BBCode::CONNECTORS);
+ $html = BBCode::convertForUriId($uri_id, $bbcode, BBCode::NPF);
if (empty($html)) {
return [];
}
$doc = new DOMDocument();
+
+ $doc->formatOutput = true;
if (!@$doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'))) {
return [];
}
- $node = $doc->getElementsByTagName('body')->item(0);
- foreach ($node->childNodes as $child) {
- $npf = self::routeElements($child, $uri_id, $npf);
- }
+ self::setHeadingSubStyles($doc);
+
+ $element = $doc->getElementsByTagName('body')->item(0);
+
+ list($npf, $text, $formatting) = self::routeChildren($element, $uri_id, true, []);
- return self::addLinkBlock($uri_id, $npf);
+ return self::addLinkBlockForUriId($uri_id, 0, $npf);
}
- public static function prepareBody(string $body): string
+ /**
+ * Fetch the heading types
+ *
+ * @param DOMDocument $doc
+ * @return void
+ */
+ private static function setHeadingSubStyles(DOMDocument $doc)
{
- $shared = BBCode::fetchShareAttributes($body);
- if (!empty($shared)) {
- $body = $shared['shared'];
+ self::$heading_subtype = [];
+ foreach (['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] as $element) {
+ if ($doc->getElementsByTagName($element)->count() > 0) {
+ if (empty(self::$heading_subtype)) {
+ self::$heading_subtype[$element] = 'heading1';
+ } else {
+ self::$heading_subtype[$element] = 'heading2';
+ }
+ }
}
+ }
- $body = BBCode::removeAttachment($body);
+ /**
+ * Prepare the BBCode for the NPF conversion
+ *
+ * @param string $bbcode
+ * @return string
+ */
+ private static function prepareBody(string $bbcode): string
+ {
+ $shared = BBCode::fetchShareAttributes($bbcode);
+ if (!empty($shared)) {
+ $bbcode = $shared['shared'];
+ }
- $body = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $body);
+ $bbcode = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $bbcode);
- if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img=([^\[\]]*)\]([^\[\]]*)\[\/img\]\s*\[/url\]#ism", $body, $pictures, PREG_SET_ORDER)) {
+ if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img=([^\[\]]*)\]([^\[\]]*)\[\/img\]\s*\[/url\]#ism", $bbcode, $pictures, PREG_SET_ORDER)) {
foreach ($pictures as $picture) {
if (preg_match('#/photo/.*-[01]\.#ism', $picture[2]) && (preg_match('#/photo/.*-0\.#ism', $picture[1]) || preg_match('#/photos/.*/image/#ism', $picture[1]))) {
- $body = str_replace($picture[0], "\n\n[img=" . str_replace('-1.', '-0.', $picture[2]) . "]" . $picture[3] . "[/img]\n\n", $body);
+ $bbcode = str_replace($picture[0], "\n\n[img=" . str_replace('-1.', '-0.', $picture[2]) . "]" . $picture[3] . "[/img]\n\n", $bbcode);
}
}
}
- $body = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", "\n\n[img=$1]$2[/img]\n\n", $body);
+ $bbcode = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", "\n\n[img=$1]$2[/img]\n\n", $bbcode);
- if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img\]([^\[]+?)\[/img\]\s*\[/url\]#ism", $body, $pictures, PREG_SET_ORDER)) {
+ if (preg_match_all("#\[url=([^\]]+?)\]\s*\[img\]([^\[]+?)\[/img\]\s*\[/url\]#ism", $bbcode, $pictures, PREG_SET_ORDER)) {
foreach ($pictures as $picture) {
if (preg_match('#/photo/.*-[01]\.#ism', $picture[2]) && (preg_match('#/photo/.*-0\.#ism', $picture[1]) || preg_match('#/photos/.*/image/#ism', $picture[1]))) {
- $body = str_replace($picture[0], "\n\n[img]" . str_replace('-1.', '-0.', $picture[2]) . "[/img]\n\n", $body);
+ $bbcode = str_replace($picture[0], "\n\n[img]" . str_replace('-1.', '-0.', $picture[2]) . "[/img]\n\n", $bbcode);
}
}
}
- $body = preg_replace("/\[img\](.*?)\[\/img\]/ism", "\n\n[img]$1[/img]\n\n", $body);
- $body = preg_replace("/\[audio\](.*?)\[\/audio\]/ism", "\n\n[audio]$1[/audio]\n\n", $body);
- $body = preg_replace("/\[video\](.*?)\[\/video\]/ism", "\n\n[video]$1[/video]\n\n", $body);
+ $bbcode = preg_replace("/\[img\](.*?)\[\/img\]/ism", "\n\n[img]$1[/img]\n\n", $bbcode);
do {
- $oldbody = $body;
- $body = str_replace(["\n\n\n"], ["\n\n"], $body);
- } while ($oldbody != $body);
+ $oldbbcode = $bbcode;
+ $bbcode = str_replace(["\n\n\n"], ["\n\n"], $bbcode);
+ } while ($oldbbcode != $bbcode);
+
+ return trim($bbcode);
+ }
+
+ /**
+ * Walk recursively through the HTML
+ *
+ * @param DOMElement $element
+ * @param integer $uri_id
+ * @param boolean $parse_structure
+ * @param array $callstack
+ * @param array $npf
+ * @param string $text
+ * @param array $formatting
+ * @return array
+ */
+ private static function routeChildren(DOMElement $element, int $uri_id, bool $parse_structure, array $callstack, array $npf = [], string $text = '', array $formatting = []): array
+ {
+ if ($parse_structure && $text) {
+ list($npf, $text, $formatting) = self::addBlock($text, $formatting, $npf, $callstack);
+ }
+
+ $callstack[] = $element->nodeName;
+ $level = self::getLevelByCallstack($callstack);
+
+ foreach ($element->childNodes as $child) {
+ switch ($child->nodeName) {
+ case 'b':
+ case 'strong':
+ list($npf, $text, $formatting) = self::addFormatting($child, $uri_id, 'bold', $callstack, $npf, $text, $formatting);
+ break;
+
+ case 'i':
+ case 'em':
+ list($npf, $text, $formatting) = self::addFormatting($child, $uri_id, 'italic', $callstack, $npf, $text, $formatting);
+ break;
+
+ case 's':
+ list($npf, $text, $formatting) = self::addFormatting($child, $uri_id, 'strikethrough', $callstack, $npf, $text, $formatting);
+ break;
+
+ case 'u':
+ case 'span':
+ list($npf, $text, $formatting) = self::addFormatting($child, $uri_id, '', $callstack, $npf, $text, $formatting);
+ break;
+
+ case 'hr':
+ case 'br':
+ if (!empty($text)) {
+ $text .= "\n";
+ }
+ break;
+
+ case '#text':
+ $text .= $child->textContent;
+ break;
+
+ case 'table':
+ case 'summary':
+ // Ignore tables and spoilers
+ break;
+
+ case 'a':
+ list($npf, $text, $formatting) = self::addInlineLink($child, $uri_id, $callstack, $npf, $text, $formatting);
+ break;
+
+ case 'img':
+ $npf = self::addImageBlock($child, $uri_id, $level, $npf);
+ break;
+
+ case 'audio':
+ case 'video':
+ $npf = self::addMediaBlock($child, $uri_id, $level, $npf);
+ break;
+
+ default:
+ list($npf, $text, $formatting) = self::routeChildren($child, $uri_id, true, $callstack, $npf, $text, $formatting);
+ break;
+ }
+ }
+
+ if ($parse_structure && $text) {
+ list($npf, $text, $formatting) = self::addBlock($text, $formatting, $npf, $callstack);
+ }
+ return [$npf, $text, $formatting];
+ }
+
+ /**
+ * Return the correct indent level
+ *
+ * @param array $callstack
+ * @return integer
+ */
+ private static function getLevelByCallstack(array $callstack): int
+ {
+ $level = 0;
+ foreach ($callstack as $entry) {
+ if (in_array($entry, ['ol', 'ul', 'blockquote'])) {
+ ++$level;
+ }
+ }
+ return max(0, $level - 1);
+ }
+
+ /**
+ * Detect the subtype via the HTML element callstack
+ *
+ * @param array $callstack
+ * @param string $text
+ * @return string
+ */
+ private static function getSubTypeByCallstack(array $callstack, string $text): string
+ {
+ $subtype = '';
+ foreach ($callstack as $entry) {
+ switch ($entry) {
+ case 'ol':
+ $subtype = 'ordered-list-item';
+ break;
+
+ case 'ul':
+ $subtype = 'unordered-list-item';
+ break;
- return trim($body);
+ case 'h1':
+ $subtype = self::$heading_subtype[$entry];
+ break;
+
+ case 'h2':
+ $subtype = self::$heading_subtype[$entry];
+ break;
+
+ case 'h3':
+ $subtype = self::$heading_subtype[$entry];
+ break;
+
+ case 'h4':
+ $subtype = self::$heading_subtype[$entry];
+ break;
+
+ case 'h5':
+ $subtype = self::$heading_subtype[$entry];
+ break;
+
+ case 'h6':
+ $subtype = self::$heading_subtype[$entry];
+ break;
+
+ case 'blockquote':
+ $subtype = mb_strlen($text) < 100 ? 'quote' : 'indented';
+ break;
+
+ case 'pre':
+ $subtype = 'indented';
+ break;
+
+ case 'code':
+ $subtype = 'chat';
+ break;
+ }
+ }
+ return $subtype;
}
- static private function routeElements(DOMElement|DOMNode $child, int $uri_id, array $npf): array
+ /**
+ * Add formatting for a text block
+ *
+ * @param DOMElement $element
+ * @param integer $uri_id
+ * @param string $type
+ * @param array $callstack
+ * @param array $npf
+ * @param string $text
+ * @param array $formatting
+ * @return array
+ */
+ private static function addFormatting(DOMElement $element, int $uri_id, string $type, array $callstack, array $npf, string $text, array $formatting): array
{
- switch ($child->tagName ?? '') {
- case 'blockquote':
- $npf = self::addTextBlock($child, $uri_id, $npf, 'indented');
- break;
+ $start = mb_strlen($text);
- case 'h1':
- $npf = self::addTextBlock($child, $uri_id, $npf, 'heading1');
- break;
+ list($npf, $text, $formatting) = self::routeChildren($element, $uri_id, false, $callstack, $npf, $text, $formatting);
- case 'h2':
- $npf = self::addTextBlock($child, $uri_id, $npf, 'heading1');
- break;
+ if (!empty($type)) {
+ $formatting[] = [
+ 'start' => $start,
+ 'end' => mb_strlen($text),
+ 'type' => $type
+ ];
+ }
+ return [$npf, $text, $formatting];
+ }
- case 'h3':
- $npf = self::addTextBlock($child, $uri_id, $npf, 'heading1');
- break;
+ /**
+ * Add an inline link for a text block
+ *
+ * @param DOMElement $element
+ * @param integer $uri_id
+ * @param array $callstack
+ * @param array $npf
+ * @param string $text
+ * @param array $formatting
+ * @return array
+ */
+ private static function addInlineLink(DOMElement $element, int $uri_id, array $callstack, array $npf, string $text, array $formatting): array
+ {
+ $start = mb_strlen($text);
- case 'h4':
- $npf = self::addTextBlock($child, $uri_id, $npf, 'heading2');
- break;
+ list($npf, $text, $formatting) = self::routeChildren($element, $uri_id, false, $callstack, $npf, $text, $formatting);
- case 'h5':
- $npf = self::addTextBlock($child, $uri_id, $npf, 'heading2');
- break;
+ $attributes = [];
+ foreach ($element->attributes as $key => $attribute) {
+ $attributes[$key] = trim($attribute->value);
+ }
+ if (!empty($attributes['href'])) {
+ $formatting[] = [
+ 'start' => $start,
+ 'end' => mb_strlen($text),
+ 'type' => 'link',
+ 'url' => $attributes['href']
+ ];
+ }
+ return [$npf, $text, $formatting];
+ }
- case 'h6':
- $npf = self::addTextBlock($child, $uri_id, $npf, 'heading2');
- break;
+ /**
+ * Add a text block
+ *
+ * @param string $text
+ * @param array $formatting
+ * @param array $npf
+ * @param array $callstack
+ * @return array
+ */
+ private static function addBlock(string $text, array $formatting, array $npf, array $callstack): array
+ {
+ $block = [
+ 'type' => 'text',
+ 'subtype' => '',
+ 'text' => $text,
+ ];
- case 'ul':
- $npf = self::addListBlock($child, $uri_id, $npf, false, 0);
- break;
+ if (!empty($formatting)) {
+ $block['formatting'] = $formatting;
+ }
- case 'ol':
- $npf = self::addListBlock($child, $uri_id, $npf, true, 0);
- break;
+ $level = self::getLevelByCallstack($callstack);
+ if ($level > 0) {
+ $block['indent_level'] = $level;
+ }
- case 'hr':
- case 'br':
- break;
+ $subtype = self::getSubTypeByCallstack($callstack, $text);
+ if ($subtype) {
+ $block['subtype'] = $subtype;
+ } else {
+ unset($block['subtype']);
+ }
- case 'pre':
- case 'code':
- $npf = self::addTextBlock($child, $uri_id, $npf, 'indented');
- break;
+ $npf[] = $block;
+ return [$npf, '', []];
+ }
- case 'a':
- $npf = self::addMediaBlock($child, $uri_id, $npf);
- break;
+ /**
+ * Add a block for a preview picture
+ *
+ * @param array $media
+ * @param array $block
+ * @return array
+ */
+ private static function addPoster(array $media, array $block): array
+ {
+ $poster = [];
+ if (!empty($media['preview'])) {
+ $poster['url'] = $media['preview'];
+ }
+ if (!empty($media['preview-width'])) {
+ $poster['width'] = $media['preview-width'];
+ }
+ if (!empty($media['preview-height'])) {
+ $poster['height'] = $media['preview-height'];
+ }
+ if (!empty($poster)) {
+ $block['poster'] = [$poster];
+ }
+ return $block;
+ }
- case 'table':
- // $child->ownerDocument->saveHTML($child)
- break;
+ /**
+ * Add a link block from the HTML attachment of a given post uri-id
+ *
+ * @param integer $uri_id
+ * @param integer $level
+ * @param array $npf
+ * @return array
+ */
+ private static function addLinkBlockForUriId(int $uri_id, int $level, array $npf): array
+ {
+ foreach (Post\Media::getByURIId($uri_id, [Post\Media::HTML]) as $link) {
+ $host = parse_url($link['url'], PHP_URL_HOST);
+ if (in_array($host, ['www.youtube.com', 'youtu.be'])) {
+ $block = [
+ 'type' => 'video',
+ 'provider' => 'youtube',
+ 'url' => $link['url'],
+ ];
+ } elseif (in_array($host, ['vimeo.com'])) {
+ $block = [
+ 'type' => 'video',
+ 'provider' => 'vimeo',
+ 'url' => $link['url'],
+ ];
+ } elseif (in_array($host, ['open.spotify.com'])) {
+ $block = [
+ 'type' => 'audio',
+ 'provider' => 'spotify',
+ 'url' => $link['url'],
+ ];
+ } else {
+ $block = [
+ 'type' => 'link',
+ 'url' => $link['url'],
+ ];
+ if (!empty($link['name'])) {
+ $block['title'] = $link['name'];
+ }
+ if (!empty($link['description'])) {
+ $block['description'] = $link['description'];
+ }
+ if (!empty($link['author-name'])) {
+ $block['author'] = $link['author-name'];
+ }
+ if (!empty($link['publisher-name'])) {
+ $block['site_name'] = $link['publisher-name'];
+ }
+ }
- case 'img':
- $npf = self::addImageBlock($child, $uri_id, $npf);
- break;
+ if ($level > 0) {
+ $block['indent_level'] = $level;
+ }
- default:
- $npf = self::addTextBlock($child, $uri_id, $npf);
- break;
+ $npf[] = self::addPoster($link, $block);
}
return $npf;
}
- static private function addImageBlock(DOMElement|DOMNode $child, int $uri_id, array $npf): array
+ /**
+ * Add an image block
+ *
+ * @param DOMElement $element
+ * @param integer $uri_id
+ * @param integer $level
+ * @param array $npf
+ * @return array
+ */
+ private static function addImageBlock(DOMElement $element, int $uri_id, int $level, array $npf): array
{
$attributes = [];
- foreach ($child->attributes as $key => $attribute) {
- $attributes[$key] = $attribute->value;
+ foreach ($element->attributes as $key => $attribute) {
+ $attributes[$key] = trim($attribute->value);
}
if (empty($attributes['src'])) {
return $npf;
}
- $entry = [
+ $block = [
'type' => 'image',
'media' => [],
];
if (!empty($attributes['alt'])) {
- $entry['alt_text'] = $attributes['alt'];
+ $block['alt_text'] = $attributes['alt'];
}
- if (!empty($attributes['title']) && ($attributes['alt'] ?? '' != $attributes['title'])) {
- $entry['caption'] = $attributes['title'];
+ if (!empty($attributes['title']) && (($attributes['alt'] ?? '') != $attributes['title'])) {
+ $block['caption'] = $attributes['title'];
}
$rid = Photo::ridFromURI($attributes['src']);
if (!empty($rid)) {
$photos = Photo::selectToArray([], ['resource-id' => $rid]);
foreach ($photos as $photo) {
- $entry['media'][] = [
+ $block['media'][] = [
'type' => $photo['type'],
'url' => str_replace('-0.', '-' . $photo['scale'] . '.', $attributes['src']),
'width' => $photo['width'],
];
}
if (empty($attributes['alt']) && !empty($photos[0]['desc'])) {
- $entry['alt_text'] = $photos[0]['desc'];
+ $block['alt_text'] = $photos[0]['desc'];
}
} elseif ($media = Post\Media::getByURL($uri_id, $attributes['src'], [Post\Media::IMAGE])) {
- $entry['media'][] = [
+ $block['media'][] = [
'type' => $media['mimetype'],
'url' => $media['url'],
'width' => $media['width'],
'height' => $media['height'],
];
if (empty($attributes['alt']) && !empty($media['description'])) {
- $entry['alt_text'] = $media['description'];
+ $block['alt_text'] = $media['description'];
}
} else {
- $entry['media'][] = ['url' => $attributes['src']];
+ $block['media'][] = ['url' => $attributes['src']];
}
- $npf[] = $entry;
+ if ($level > 0) {
+ $block['indent_level'] = $level;
+ }
+
+ $npf[] = $block;
return $npf;
}
- static private function addMediaBlock(DOMElement|DOMNode $child, int $uri_id, array $npf): array
+ /**
+ * Add an audio or video block
+ *
+ * @param DOMElement $element
+ * @param integer $uri_id
+ * @param integer $level
+ * @param array $npf
+ * @return array
+ */
+ private static function addMediaBlock(DOMElement $element, int $uri_id, int $level, array $npf): array
{
$attributes = [];
- foreach ($child->attributes as $key => $attribute) {
- $attributes[$key] = $attribute->value;
+ foreach ($element->attributes as $key => $attribute) {
+ $attributes[$key] = trim($attribute->value);
}
- if (empty($attributes['href'])) {
+ if (empty($attributes['src'])) {
return $npf;
}
- $media = Post\Media::getByURL($uri_id, $attributes['href'], [Post\Media::AUDIO, Post\Media::VIDEO]);
+ $media = Post\Media::getByURL($uri_id, $attributes['src'], [Post\Media::AUDIO, Post\Media::VIDEO]);
if (!empty($media)) {
switch ($media['type']) {
case Post\Media::AUDIO:
- $entry = [
- 'type' => 'audio',
+ $block = [
+ 'type' => 'audio',
'media' => [
'type' => $media['mimetype'],
'url' => $media['url'],
];
if (!empty($media['name'])) {
- $entry['title'] = $media['name'];
+ $block['title'] = $media['name'];
} elseif (!empty($media['description'])) {
- $entry['title'] = $media['description'];
+ $block['title'] = $media['description'];
}
- $npf[] = self::addPoster($media, $entry);
+ $block = self::addPoster($media, $block);
break;
case Post\Media::VIDEO:
- $entry = [
- 'type' => 'video',
+ $block = [
+ 'type' => 'video',
'media' => [
'type' => $media['mimetype'],
'url' => $media['url'],
]
];
- $npf[] = self::addPoster($media, $entry);
+ $block = self::addPoster($media, $block);
break;
}
} else {
- $npf[] = [
- 'type' => 'text',
- 'text' => $child->textContent,
+ $block = [
+ 'type' => 'text',
+ 'text' => $element->textContent,
'formatting' => [
- 'start' => 0,
- 'end' => strlen($child->textContent),
- 'type' => 'link',
- 'url' => $attributes['href']
+ [
+ 'start' => 0,
+ 'end' => mb_strlen($element->textContent),
+ 'type' => 'link',
+ 'url' => $attributes['src']
+ ]
]
];
}
- return $npf;
- }
-
- static private function addPoster(array $media, array $entry): array
- {
- $poster = [];
- if (!empty($media['preview'])) {
- $poster['url'] = $media['preview'];
- }
- if (!empty($media['preview-width'])) {
- $poster['width'] = $media['preview-width'];
- }
- if (!empty($media['preview-height'])) {
- $poster['height'] = $media['preview-height'];
- }
- if (!empty($poster)) {
- $entry['poster'] = $poster;
- }
- return $entry;
- }
-
- static private function fetchText(DOMElement|DOMNode $child, array $text = ['text' => '', 'formatting' => []]): array
- {
- foreach ($child->childNodes as $node) {
- $start = strlen($text['text']);
-
- switch ($node->nodeName) {
- case 'b':
- case 'strong':
- $type = 'bold';
- break;
-
- case 'i':
- case 'em':
- $type = 'italic';
- break;
-
- case 's':
- $type = 'strikethrough';
- break;
-
- default:
- $type = '';
- break;
- }
- if ($node->nodeName == 'br') {
- $text['text'] .= "\n";
- } else {
- $text['text'] .= $node->textContent;
- }
- if (!empty($type)) {
- $text['formatting'][] = ['start' => $start, 'end' => strlen($text['text']), 'type' => $type];
- }
- }
- return $text;
- }
-
- static private function addTextBlock(DOMElement|DOMNode $child, int $uri_id, array $npf, string $subtype = ''): array
- {
- if (empty($subtype) && (($child->childElementCount) ?? 0 == 1) && ($child->textContent == $child->firstChild->textContent)) {
- return self::routeElements($child->firstChild, $uri_id, $npf);
- }
-
- $element = ['type' => 'text'];
-
- if (!empty($subtype)) {
- $element['subtype'] = $subtype;
- }
-
- $text = self::fetchText($child);
-
- $element['text'] = $text['text'];
- $element['formatting'] = $text['formatting'];
-
- if (empty($subtype)) {
- switch ($child->tagName ?? '') {
- case 'b':
- case 'strong':
- $element['formatting'][] = ['start' => 0, 'end' => strlen($element['text']), 'type' => 'bold'];
- break;
-
- case 'i':
- case 'em':
- $element['formatting'][] = ['start' => 0, 'end' => strlen($element['text']), 'type' => 'italic'];
- break;
-
- case 's':
- $element['formatting'][] = ['start' => 0, 'end' => strlen($element['text']), 'type' => 'strikethrough'];
- break;
-
- case 'span':
- case 'p':
- case 'div':
- case 'details';
- case '':
- break;
- default:
- print_r($element);
- die($child->tagName . "\n");
- break;
- }
- }
- if (empty($element['formatting'])) {
- unset($element['formatting']);
+ if ($level > 0) {
+ $block['indent_level'] = $level;
}
- $npf[] = $element;
+ $npf[] = $block;
return $npf;
}
-
- static private function addListBlock(DOMElement|DOMNode $child, int $uri_id, array $npf, bool $ordered, int $level): array
- {
- foreach ($child->childNodes as $node) {
- switch ($node->nodeName) {
- case 'ul':
- $npf = self::addListBlock($node, $uri_id, $npf, false, $level++);
- case 'ol':
- $npf = self::addListBlock($node, $uri_id, $npf, true, $level++);
- case 'li':
- $text = self::fetchText($node);
-
- $entry = [
- 'type' => 'text',
- 'subtype' => $ordered ? 'ordered-list-item' : 'unordered-list-item',
- 'text' => $text['text']
- ];
- if ($level > 0) {
- $entry['indent_level'] = $level;
- }
- if (!empty($text['formatting'])) {
- $entry['formatting'] = $text['formatting'];
- }
- $npf[] = $entry;
- }
- }
-
- return $npf;
- }
-
- static private function addLinkBlock(int $uri_id, array $npf): array
- {
- foreach (Post\Media::getByURIId($uri_id, [Post\Media::HTML]) as $link) {
- $host = parse_url($link['url'], PHP_URL_HOST);
- if (in_array($host, ['www.youtube.com', 'youtu.be'])) {
- $entry = [
- 'type' => 'video',
- 'provider' => 'youtube',
- 'url' => $link['url'],
- ];
- } elseif (in_array($host, ['vimeo.com'])) {
- $entry = [
- 'type' => 'video',
- 'provider' => 'vimeo',
- 'url' => $link['url'],
- ];
- } elseif (in_array($host, ['open.spotify.com'])) {
- $entry = [
- 'type' => 'audio',
- 'provider' => 'spotify',
- 'url' => $link['url'],
- ];
- } else {
- $entry = [
- 'type' => 'link',
- 'url' => $link['url'],
- ];
- if (!empty($link['name'])) {
- $entry['title'] = $link['name'];
- }
- if (!empty($link['description'])) {
- $entry['description'] = $link['description'];
- }
- if (!empty($link['author-name'])) {
- $entry['author'] = $link['author-name'];
- }
- if (!empty($link['publisher-name'])) {
- $entry['site_name'] = $link['publisher-name'];
- }
- }
-
- $npf[] = self::addPoster($link, $entry);
- }
- return $npf;
- }
}