<?php
namespace wcf\system\bbcode;
-use wcf\system\Regex;
/**
* Parses the [table] bbcode tag.
*
- * @author Tim Duesterhus, Marcel Werk
+ * @author Alexander Ebert
* @copyright 2001-2016 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @package WoltLabSuite\Core\System\Bbcode
* @inheritDoc
*/
public function getParsedTag(array $openingTag, $content, array $closingTag, BBCodeParser $parser) {
- if ($parser->getOutputType() == 'text/html') {
- $parsedContent = Regex::compile('(?:\s|<br>)*(\[tr\].*\[/tr\])(?:\s|<br>)*', Regex::CASE_INSENSITIVE | Regex::DOT_ALL)->replace($content, '\\1');
-
- // check syntax
- $regex = new Regex('\[/?t[rd]\]', Regex::CASE_INSENSITIVE);
- if ($regex->match($parsedContent, true)) {
- $matches = $regex->getMatches();
-
- $openTags = [];
- $openTDs = 0;
- $firstRowTDs = 0;
-
- // parse tags
- foreach ($matches[0] as $match) {
- switch ($match) {
- case '[td]':
- if (end($openTags) !== '[tr]') return '';
- $openTags[] = $match;
- $openTDs++;
- break;
- case '[/td]':
- if (end($openTags) !== '[td]') return '';
- array_pop($openTags);
- break;
- case '[tr]':
- if (!empty($openTags)) return '';
- $openTags[] = $match;
- break;
- case '[/tr]':
- if (end($openTags) !== '[tr]') return '';
-
- array_pop($openTags);
-
- // check that every row has got the same number of tds
- if ($firstRowTDs === 0) $firstRowTDs = $openTDs;
- if ($openTDs !== $firstRowTDs) return '';
-
- $openTDs = 0;
- break;
- }
- }
-
- if (!empty($openTags)) return '';
- }
- else {
- return '';
- }
-
- // tr
- $parsedContent = Regex::compile('\[tr\](?:\s|<br>)*', Regex::CASE_INSENSITIVE)->replace($parsedContent, '<tr>');
- // td
- $parsedContent = str_ireplace('[td]', '<td>', $parsedContent);
- // /td
- $parsedContent = Regex::compile('\[/td\](?:\s|<br>)*', Regex::CASE_INSENSITIVE)->replace($parsedContent, '</td>');
- // /tr
- $parsedContent = Regex::compile('\[/tr\](?:\s|<br>)*', Regex::CASE_INSENSITIVE)->replace($parsedContent, '</tr>');
-
- return '<div class="container bbcodeTable"><table class="table responsiveTable"><tbody>'.$parsedContent.'</tbody></table></div>';
- }
- else if ($parser->getOutputType() == 'text/simplified-html') {
- // remove table tags
- $content = str_ireplace('[td]', '* ', $content);
- $content = str_ireplace('[/td]', ' ', $content);
- $content = str_ireplace('[tr]', '', $content);
- $content = str_ireplace('[/tr]', '', $content);
-
- return $content;
- }
-
- return '';
+ // fake output
+ return '[table]' . $content . '[/table]';
}
}
--- /dev/null
+<?php
+namespace wcf\system\html\metacode\converter;
+use wcf\util\DOMUtil;
+use wcf\util\StringUtil;
+
+/**
+ * Converts table bbcodes into `<table>`, `<tr>` and `<td>`.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2016 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Html\Metacode\Converter
+ * @since 3.0
+ */
+class TableMetacodeConverter extends AbstractMetacodeConverter {
+ /**
+ * @inheritDoc
+ */
+ public function convert(\DOMDocumentFragment $fragment, array $attributes) {
+ $element = $fragment->ownerDocument->createElement('table');
+ $tbody = $fragment->ownerDocument->createElement('tbody');
+ $element->appendChild($tbody);
+ $tbody->appendChild($fragment);
+
+ // get all table rows
+ $rows = [];
+ $nodes = $tbody->getElementsByTagName('woltlab-metacode');
+ /** @var \DOMElement $node */
+ foreach ($nodes as $node) {
+ if ($node->getAttribute('data-name') === 'tr' && !$this->isInsideTable($node)) {
+ $rows[] = $node;
+ }
+ }
+
+ // fix markup for table rows
+ /** @var \DOMElement $row */
+ foreach ($rows as $row) {
+ if ($row->parentNode !== $tbody) {
+ $parent = DOMUtil::getParentBefore($row, $tbody);
+ $tbody->insertBefore($row, $parent);
+ }
+
+ DOMUtil::replaceElement($row, $row->ownerDocument->createElement('tr'));
+ }
+
+ // drop everything except for <tr> elements
+ $childNodes = DOMUtil::getChildNodes($tbody);
+ foreach ($childNodes as $childNode) {
+ if ($childNode->nodeType === XML_ELEMENT_NODE && $childNode->nodeName === 'tr') {
+ continue;
+ }
+
+ DOMUtil::removeNode($childNode);
+ }
+
+ // get columns for each tr
+ /** @var \DOMElement $childNode */
+ foreach ($tbody->childNodes as $childNode) {
+ $this->handleRow($childNode);
+ }
+
+ return $element;
+ }
+
+ protected function handleRow(\DOMElement $row) {
+ // get all table columns
+ $cols = [];
+ $nodes = $row->getElementsByTagName('woltlab-metacode');
+ /** @var \DOMElement $node */
+ foreach ($nodes as $node) {
+ if ($node->getAttribute('data-name') === 'td' && !$this->isInsideTable($node)) {
+ $cols[] = $node;
+ }
+ }
+
+ // move tds
+ /** @var \DOMElement $col */
+ foreach ($cols as $col) {
+ if (false && $col->parentNode !== $row) {
+ $parent = DOMUtil::getParentBefore($col, $row);
+ $row->insertBefore($col, $parent);
+ }
+
+ DOMUtil::replaceElement($col, $row->ownerDocument->createElement('td'));
+ }
+
+ // drop everything except for <td> elements and removing
+ // <p> inside columns
+ $childNodes = DOMUtil::getChildNodes($row);
+ /** @var \DOMElement $childNode */
+ foreach ($childNodes as $childNode) {
+ if ($childNode->nodeType === XML_ELEMENT_NODE && $childNode->nodeName === 'td') {
+ // convert <p>...</p> to ...<br><br>
+ $nodes = DOMUtil::getChildNodes($childNode);
+ /** @var \DOMElement $node */
+ foreach ($nodes as $node) {
+ if ($node->nodeName === 'p') {
+ for ($i = 0; $i < 2; $i++) {
+ DOMUtil::insertAfter($node->ownerDocument->createElement('br'), $node);
+ }
+
+ DOMUtil::removeNode($node, true);
+ }
+ }
+
+ // removing leading whitespace / <br>
+ $nodes = DOMUtil::getChildNodes($childNode);
+ foreach ($nodes as $node) {
+ if ($node->nodeType === XML_TEXT_NODE) {
+ if (StringUtil::trim($node->textContent) !== '') {
+ break;
+ }
+ }
+ else if ($node->nodeType === XML_ELEMENT_NODE && $node->nodeName !== 'br') {
+ break;
+ }
+
+ DOMUtil::removeNode($node);
+ }
+
+ // removing trailing whitespace / <br>
+ $nodes = DOMUtil::getChildNodes($childNode);
+ $i = count($nodes);
+ while ($i--) {
+ $node = $nodes[$i];
+ if ($node->nodeType === XML_TEXT_NODE) {
+ if (StringUtil::trim($node->textContent) !== '') {
+ break;
+ }
+ }
+ else if ($node->nodeType === XML_ELEMENT_NODE && $node->nodeName !== 'br') {
+ break;
+ }
+
+ DOMUtil::removeNode($node);
+ }
+
+ continue;
+ }
+
+ DOMUtil::removeNode($childNode);
+ }
+ }
+
+ /**
+ * Returns true if provided node is within another table, prevents issues
+ * with nested tables handled in the wrong order.
+ *
+ * @param \DOMNode $node target node
+ * @return boolean true if provided node is within another table
+ */
+ protected function isInsideTable(\DOMNode $node) {
+ /** @var \DOMElement $parent */
+ $parent = $node;
+ while ($parent = $parent->parentNode) {
+ if ($parent->nodeName === 'woltlab-metacode' && $parent->getAttribute('data-name') === 'table') {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}