Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 128 |
|
0.00% |
0 / 9 |
CRAP | |
0.00% |
0 / 1 |
HtmlUtils | |
0.00% |
0 / 128 |
|
0.00% |
0 / 9 |
380 | |
0.00% |
0 / 1 |
renderMarkdown | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
2 | |||
postprocess | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
replaceEmailAdresses | |
0.00% |
0 / 86 |
|
0.00% |
0 / 1 |
90 | |||
getImageSrcHtml | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
12 | |||
getPrefix | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getSubject | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getSuffix | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
escapeDollar | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
fromEnv | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace Olz\Utils; |
4 | |
5 | use League\CommonMark\Environment\Environment; |
6 | use League\CommonMark\Extension\Attributes\AttributesExtension; |
7 | use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; |
8 | use League\CommonMark\Extension\GithubFlavoredMarkdownExtension; |
9 | use League\CommonMark\MarkdownConverter; |
10 | use Olz\Entity\Roles\Role; |
11 | use Olz\Roles\Components\OlzRoleInfoModal\OlzRoleInfoModal; |
12 | |
13 | class HtmlUtils { |
14 | use WithUtilsTrait; |
15 | |
16 | public string $prefix_regex = '<a ([^>]*)href=[\'"]mailto:'; |
17 | public string $subject_regex = '\?subject=([^\'"]*)'; |
18 | public string $suffix_regex = '[\'"]([^>]*)>([^<@]*)([^<]*)<\/a>'; |
19 | public string $olz_email_regex = ''; |
20 | public string $email_regex = '([A-Z0-9a-z._%+-]+)@([A-Za-z0-9.-]+\.[A-Za-z]{2,64})'; |
21 | |
22 | /** @param array<string, mixed> $override_config */ |
23 | public function renderMarkdown(string $markdown, array $override_config = []): string { |
24 | $default_config = [ |
25 | 'html_input' => 'escape', |
26 | 'allow_unsafe_links' => false, |
27 | 'max_nesting_level' => 100, |
28 | ]; |
29 | $config = array_merge($default_config, $override_config); |
30 | |
31 | $environment = new Environment($config); |
32 | $environment->addExtension(new CommonMarkCoreExtension()); |
33 | $environment->addExtension(new GithubFlavoredMarkdownExtension()); |
34 | $environment->addExtension(new AttributesExtension()); |
35 | $converter = new MarkdownConverter($environment); |
36 | $rendered = $converter->convert($markdown); |
37 | $postprocessed = $this->postprocess(strval($rendered)); |
38 | return "<div class='rendered-markdown'>{$postprocessed}</div>"; |
39 | } |
40 | |
41 | public function postprocess(string $html): string { |
42 | return $this->replaceEmailAdresses($html); |
43 | } |
44 | |
45 | public function replaceEmailAdresses(string $html): string { |
46 | $role_repo = $this->entityManager()->getRepository(Role::class); |
47 | $host = $this->envUtils()->getEmailForwardingHost(); |
48 | $esc_host = preg_quote($host); |
49 | $this->olz_email_regex = '([A-Z0-9a-z._%+-]+)@'.$esc_host; |
50 | |
51 | $html = str_replace(['<p>', '<p ', '</p>'], ['<div>', '<div ', '</div>'], $html); |
52 | |
53 | preg_match_all( |
54 | "/{$this->prefix_regex}{$this->olz_email_regex}{$this->subject_regex}{$this->suffix_regex}/", |
55 | $html, |
56 | $matches, |
57 | ); |
58 | for ($i = 0; $i < count($matches[0]); $i++) { |
59 | $username = $matches[2][$i]; |
60 | // TODO: Only active roles! |
61 | $role = $role_repo->findOneBy(['username' => $username]); |
62 | if ($role) { |
63 | $prefix = $this->getPrefix($matches[1][$i]); |
64 | $username = preg_quote($username); |
65 | $email = "{$username}@{$host}"; |
66 | $subject = $this->getSubject($matches[3][$i]); |
67 | $suffix = $this->getSuffix($matches[4][$i], $matches[5][$i], $matches[6][$i]); |
68 | $html = preg_replace( |
69 | "/{$prefix}{$email}{$subject}{$suffix}/", |
70 | $this->escapeDollar(OlzRoleInfoModal::render([ |
71 | 'role' => $role, |
72 | 'text' => $matches[5][$i] ?: null, |
73 | ])), |
74 | $html |
75 | ); |
76 | $this->generalUtils()->checkNotNull($html, "String replacement failed"); |
77 | } |
78 | } |
79 | |
80 | preg_match_all( |
81 | "/{$this->prefix_regex}{$this->olz_email_regex}{$this->suffix_regex}/", |
82 | $html, |
83 | $matches, |
84 | ); |
85 | for ($i = 0; $i < count($matches[0]); $i++) { |
86 | $username = $matches[2][$i]; |
87 | // TODO: Only active roles! |
88 | $role = $role_repo->findOneBy(['username' => $username]); |
89 | if ($role) { |
90 | $prefix = $this->getPrefix($matches[1][$i]); |
91 | $username = preg_quote($username); |
92 | $email = "{$username}@{$host}"; |
93 | $suffix = $this->getSuffix($matches[3][$i], $matches[4][$i], $matches[5][$i]); |
94 | $html = preg_replace( |
95 | "/{$prefix}{$email}{$suffix}/", |
96 | $this->escapeDollar(OlzRoleInfoModal::render([ |
97 | 'role' => $role, |
98 | 'text' => $matches[4][$i] ?: null, |
99 | ])), |
100 | $html |
101 | ); |
102 | $this->generalUtils()->checkNotNull($html, "String replacement failed"); |
103 | } |
104 | } |
105 | |
106 | preg_match_all( |
107 | "/(\\s|^){$this->olz_email_regex}([\\s,\\.!\\?]|$)/", |
108 | $html, |
109 | $matches, |
110 | ); |
111 | for ($i = 0; $i < count($matches[0]); $i++) { |
112 | $username = $matches[2][$i]; |
113 | // TODO: Only active roles! |
114 | $role = $role_repo->findOneBy(['username' => $username]); |
115 | if ($role) { |
116 | $username = preg_quote($username); |
117 | $email = "{$username}@{$host}"; |
118 | $html = preg_replace( |
119 | "/{$email}/", |
120 | $this->escapeDollar(OlzRoleInfoModal::render(['role' => $role])), |
121 | $html |
122 | ); |
123 | $this->generalUtils()->checkNotNull($html, "String replacement failed"); |
124 | } |
125 | } |
126 | |
127 | $html = preg_replace( |
128 | "/{$this->prefix_regex}{$this->email_regex}{$this->subject_regex}{$this->suffix_regex}/", |
129 | "<script>olz.MailTo(\"\$2\", \"\$3\", \"\$6\" + \"\$7\", \"\$4\")</script>", |
130 | $html |
131 | ); |
132 | $this->generalUtils()->checkNotNull($html, "String replacement failed"); |
133 | $html = preg_replace( |
134 | "/{$this->prefix_regex}{$this->email_regex}{$this->suffix_regex}/", |
135 | "<script>olz.MailTo(\"\$2\", \"\$3\", \"\$5\" + \"\$6\")</script>", |
136 | $html |
137 | ); |
138 | $this->generalUtils()->checkNotNull($html, "String replacement failed"); |
139 | $html = preg_replace( |
140 | "/(\\s|^){$this->email_regex}([\\s,\\.!\\?]|$)/", |
141 | "\$1<script>olz.MailTo(\"\$2\", \"\$3\", \"E-Mail\")</script>\$4", |
142 | $html |
143 | ); |
144 | $this->generalUtils()->checkNotNull($html, "String replacement failed"); |
145 | return $html; |
146 | } |
147 | |
148 | /** @param array<string, string> $image_hrefs */ |
149 | public function getImageSrcHtml(array $image_hrefs): string { |
150 | $keys = array_keys($image_hrefs); |
151 | if (count($keys) < 1) { |
152 | return ''; |
153 | } |
154 | $default_src = $image_hrefs['1x'] ?? $image_hrefs[$keys[0]]; |
155 | if (count($keys) < 2) { |
156 | return <<<ZZZZZZZZZZ |
157 | src='{$default_src}' |
158 | ZZZZZZZZZZ; |
159 | } |
160 | $srcset = implode(",\n ", array_map(function ($key) use ($image_hrefs) { |
161 | $value = $image_hrefs[$key]; |
162 | return "{$value} {$key}"; |
163 | }, $keys)); |
164 | return <<<ZZZZZZZZZZ |
165 | srcset=' |
166 | {$srcset} |
167 | ' |
168 | src='{$default_src}' |
169 | ZZZZZZZZZZ; |
170 | } |
171 | |
172 | protected function getPrefix(string $match): string { |
173 | $esc_match = preg_quote($match); |
174 | return "<a {$esc_match}href=['\"]mailto:"; |
175 | } |
176 | |
177 | protected function getSubject(string $match): string { |
178 | $esc_match = preg_quote($match); |
179 | return "\\?subject={$esc_match}"; |
180 | } |
181 | |
182 | protected function getSuffix(string $match1, string $match2, string $match3): string { |
183 | $esc_match1 = preg_quote($match1); |
184 | $esc_match2 = preg_quote($match2); |
185 | $esc_match3 = preg_quote($match3); |
186 | return "['\"]{$esc_match1}>{$esc_match2}{$esc_match3}<\\/a>"; |
187 | } |
188 | |
189 | protected function escapeDollar(string $replacement): string { |
190 | return str_replace('$', '\$', $replacement); |
191 | } |
192 | |
193 | public static function fromEnv(): self { |
194 | return new self(); |
195 | } |
196 | } |