@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
at upstream/main 153 lines 3.8 kB view raw
1<?php 2 3final class PhutilRemarkupTableBlockRule extends PhutilRemarkupBlockRule { 4 5 public function getMatchingLineCount(array $lines, $cursor) { 6 $num_lines = 0; 7 8 if (preg_match('/^\s*<table>/i', $lines[$cursor])) { 9 $num_lines++; 10 $cursor++; 11 12 while (isset($lines[$cursor])) { 13 $num_lines++; 14 if (preg_match('@</table>\s*$@i', $lines[$cursor])) { 15 break; 16 } 17 $cursor++; 18 } 19 } 20 21 return $num_lines; 22 } 23 24 public function markupText($text, $children) { 25 $root = id(new PhutilHTMLParser()) 26 ->parseDocument($text); 27 28 $nodes = $root->selectChildrenWithTags(array('table')); 29 30 $out = array(); 31 $seen_table = false; 32 foreach ($nodes as $node) { 33 if ($node->isContentNode()) { 34 $content = $node->getContent(); 35 36 if (!strlen(trim($content))) { 37 // Ignore whitespace. 38 continue; 39 } 40 41 // If we find other content, fail the rule. This can happen if the 42 // input is two consecutive table tags on one line with some text 43 // in between them, which we currently forbid. 44 return $text; 45 } else { 46 // If we have multiple table tags, just return the raw text. 47 if ($seen_table) { 48 return $text; 49 } 50 $seen_table = true; 51 52 $out[] = $this->newTable($node); 53 } 54 } 55 56 if ($this->getEngine()->isTextMode()) { 57 return implode('', $out); 58 } else { 59 return phutil_implode_html('', $out); 60 } 61 } 62 63 private function newTable(PhutilDOMNode $table) { 64 $nodes = $table->selectChildrenWithTags( 65 array( 66 'colgroup', 67 'tr', 68 )); 69 70 $colgroup = null; 71 $rows = array(); 72 73 foreach ($nodes as $node) { 74 if ($node->isContentNode()) { 75 $content = $node->getContent(); 76 77 // If this is whitespace, ignore it. 78 if (!strlen(trim($content))) { 79 continue; 80 } 81 82 // If we have nonempty content between the rows, this isn't a valid 83 // table. We can't really do anything reasonable with this, so just 84 // fail out and render the raw text. 85 return $table->newRawString(); 86 } 87 88 if ($node->getTagName() === 'colgroup') { 89 // This table has multiple "<colgroup />" tags. Just bail out. 90 if ($colgroup !== null) { 91 return $table->newRawString(); 92 } 93 94 // This table has a "<colgroup />" after a "<tr />". We could parse 95 // this, but just reject it out of an abundance of caution. 96 if ($rows) { 97 return $table->newRawString(); 98 } 99 100 $colgroup = $node; 101 continue; 102 } 103 104 $rows[] = $node; 105 } 106 107 $row_specs = array(); 108 109 foreach ($rows as $row) { 110 $cells = $row->selectChildrenWithTags(array('td', 'th')); 111 112 $cell_specs = array(); 113 foreach ($cells as $cell) { 114 if ($cell->isContentNode()) { 115 $content = $node->getContent(); 116 117 if ($content === null || trim($content) === '') { 118 continue; 119 } 120 121 return $table->newRawString(); 122 } 123 124 // Respect newlines in table cells as literal linebreaks. 125 126 $content = $cell->newRawContentString(); 127 $content = trim($content, "\r\n"); 128 129 $lines = phutil_split_lines($content, $retain_endings = false); 130 foreach ($lines as $key => $line) { 131 $lines[$key] = $this->applyRules($line); 132 } 133 134 $content = phutil_implode_html( 135 phutil_tag('br'), 136 $lines); 137 138 $cell_specs[] = array( 139 'type' => $cell->getTagName(), 140 'content' => $content, 141 ); 142 } 143 144 $row_specs[] = array( 145 'type' => 'tr', 146 'content' => $cell_specs, 147 ); 148 } 149 150 return $this->renderRemarkupTable($row_specs); 151 } 152 153}