Elgg  Version master
HtmlFormatter.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg\Views;
4 
10 
15 
16  use Loggable;
17 
21  protected $views;
22 
26  protected $events;
27 
31  protected $autop;
32 
40  public function __construct(
44  ) {
45  $this->views = $views;
46  $this->events = $events;
47  $this->autop = $autop;
48  }
49 
63  public function formatBlock(string $html, array $options = []): string {
64  $options = array_merge([
65  'parse_urls' => true,
66  'parse_emails' => true,
67  'sanitize' => true,
68  'autop' => true,
69  ], $options);
70 
71  $params = [
72  'options' => $options,
73  'html' => $html,
74  ];
75 
76  $params = $this->events->triggerResults('prepare', 'html', [], $params);
77 
78  $html = (string) elgg_extract('html', $params);
79  $options = (array) elgg_extract('options', $params);
80 
81  if (elgg_extract('parse_urls', $options)) {
82  $html = $this->parseUrls($html);
83  }
84 
85  if (elgg_extract('parse_emails', $options)) {
86  $html = $this->parseEmails($html);
87  }
88 
89  if (elgg_extract('sanitize', $options)) {
90  $html = elgg_sanitize_input($html);
91  }
92 
93  if (elgg_extract('autop', $options)) {
94  $html = $this->addParagaraphs($html);
95  }
96 
97  return $html;
98  }
99 
107  public function parseUrls(string $text): string {
108 
109  $linkify = new \Misd\Linkify\Linkify();
110 
111  return $linkify->processUrls($text, ['attr' => ['rel' => 'nofollow']]);
112  }
113 
122  public function parseEmails(string $text): string {
123  $linkify = new \Misd\Linkify\Linkify();
124 
125  return $linkify->processEmails($text, ['attr' => ['rel' => 'nofollow']]);
126  }
127 
135  public function addParagaraphs(string $string): string {
136  try {
137  $result = $this->autop->process($string);
138  if ($result !== false) {
139  return $result;
140  }
141  } catch (\RuntimeException $e) {
142  $this->getLogger()->warning('AutoParagraph failed to process the string: ' . $e->getMessage());
143  }
144 
145  return $string;
146  }
147 
172  public function formatAttributes(array $attrs = []): string {
173  if (empty($attrs)) {
174  return '';
175  }
176 
177  $attributes = [];
178 
179  foreach ($attrs as $attr => $val) {
180  if (!str_starts_with($attr, 'data-') && str_contains($attr, '_')) {
181  // this is probably a view $vars variable not meant for output
182  continue;
183  }
184 
185  $attr = strtolower($attr);
186 
187  if (!isset($val) || $val === false) {
188  continue;
189  }
190 
191  if ($val === true) {
192  $val = $attr; //e.g. checked => true ==> checked="checked"
193  }
194 
195  if (is_array($val) && empty($val)) {
196  //e.g. ['class' => []]
197  continue;
198  }
199 
200  if (is_scalar($val)) {
201  $val = [$val];
202  }
203 
204  if (!is_array($val)) {
205  continue;
206  }
207 
208  // Check if array contains non-scalar values and bail if so
209  $filtered_val = array_filter($val, function($e) {
210  return is_scalar($e);
211  });
212 
213  if (count($val) != count($filtered_val)) {
214  continue;
215  }
216 
217  $val = implode(' ', $val);
218 
219  $val = htmlspecialchars($val, ENT_QUOTES, 'UTF-8', false);
220  $attributes[] = "$attr=\"$val\"";
221  }
222 
223  return implode(' ', $attributes);
224  }
225 
253  public function formatElement(string $tag_name, array $attributes = [], string $text = '', array $options = []): string {
254  if ($tag_name === '') {
255  throw new InvalidArgumentException('$tag_name is required');
256  }
257 
258  // from http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
259  $is_void = $options['is_void'] ?? in_array(strtolower($tag_name), [
260  'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem',
261  'meta', 'param', 'source', 'track', 'wbr'
262  ]);
263 
264  if (!empty($options['encode_text']) && is_string($text)) {
265  $double_encode = !empty($options['double_encode']);
266  $text = htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', $double_encode);
267  }
268 
269  $attrs = '';
270  if (!empty($attributes)) {
272  if ($attrs !== '') {
273  $attrs = " $attrs";
274  }
275  }
276 
277  if ($is_void) {
278  return empty($options['is_xml']) ? "<{$tag_name}{$attrs}>" : "<{$tag_name}{$attrs} />";
279  }
280 
281  return "<{$tag_name}{$attrs}>$text</$tag_name>";
282  }
283 
294  public function stripTags(string $string, string $allowable_tags = null): string {
295  $params = [
296  'original_string' => $string,
297  'allowable_tags' => $allowable_tags,
298  ];
299 
300  $string = strip_tags($string, $allowable_tags);
301  return (string) $this->events->triggerResults('format', 'strip_tags', $params, $string);
302  }
303 
331  public function decode(string $string): string {
332  $string = str_replace(
333  ['&gt;', '&lt;', '&amp;', '&quot;', '&#039;'],
334  ['&amp;gt;', '&amp;lt;', '&amp;amp;', '&amp;quot;', '&amp;#039;'],
335  $string
336  );
337  $string = html_entity_decode($string, ENT_NOQUOTES, 'UTF-8');
338  return str_replace(
339  ['&amp;gt;', '&amp;lt;', '&amp;amp;', '&amp;quot;', '&amp;#039;'],
340  ['&gt;', '&lt;', '&amp;', '&quot;', '&#039;'],
341  $string
342  );
343  }
344 
356  public function inlineCss(string $html, string $css, bool $body_only = false): string {
357  if (empty($html) || empty($css)) {
358  return $html;
359  }
360 
361  $inliner = CssInliner::fromHtml($html)->disableStyleBlocksParsing()->inlineCss($css);
362 
363  return $body_only ? $inliner->renderBodyContent() : $inliner->render();
364  }
365 
375  public function normalizeUrls(string $text): string {
376  $pattern = '/\s(?:href|src)=([\'"]\S+[\'"])/i';
377 
378  // find all matches
379  $matches = [];
380  preg_match_all($pattern, $text, $matches);
381 
382  if (empty($matches) || !isset($matches[1])) {
383  return $text;
384  }
385 
386  // go through all the matches
387  $urls = $matches[1];
388  $urls = array_unique($urls);
389 
390  foreach ($urls as $url) {
391  // remove wrapping quotes from the url
392  $real_url = substr($url, 1, -1);
393  // normalize url
394  $new_url = elgg_normalize_url($real_url);
395  // make the correct replacement string
396  $replacement = str_replace($real_url, $new_url, $url);
397 
398  // replace the url in the content
399  $text = str_replace($url, $replacement, $text);
400  }
401 
402  return $text;
403  }
404 }
Exception thrown if an argument is not of the expected type.
$params
Saves global plugin settings.
Definition: save.php:13
Exception thrown if an error which can only be found on runtime occurs.
formatAttributes(array $attrs=[])
Converts an associative array into a string of well-formed HTML/XML attributes Returns a concatenated...
decode(string $string)
Decode HTML markup into a raw text string.
Events service.
normalizeUrls(string $text)
Replaces relative urls in href or src attributes in text.
elgg_sanitize_input($input)
Filter input from a given string based on registered events.
Definition: input.php:77
Create wrapper P and BR elements in HTML depending on newlines.
$options
Elgg admin footer.
Definition: footer.php:6
$html
Definition: section.php:10
elgg_extract($key, $array, $default=null, bool $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:254
trait Loggable
Enables adding a logger.
Definition: Loggable.php:14
stripTags(string $string, string $allowable_tags=null)
Strip tags and offer plugins the chance.
formatElement(string $tag_name, array $attributes=[], string $text= '', array $options=[])
Format an HTML element.
Views service.
Various helper method for formatting and sanitizing output.
$css
Definition: install.css.php:5
parseEmails(string $text)
Takes a string and turns any email addresses into formatted links.
addParagaraphs(string $string)
Create paragraphs from text with line spacing.
formatBlock(string $html, array $options=[])
Prepare HTML output.
getLogger()
Returns logger.
Definition: Loggable.php:37
__construct(ViewsService $views, EventsService $events, AutoParagraph $autop)
Output constructor.
foreach($plugin_guids as $guid) if(empty($deactivated_plugins)) $url
Definition: deactivate.php:39
inlineCss(string $html, string $css, bool $body_only=false)
Adds inline style to html content.
elgg_normalize_url(string $url)
Definition: output.php:148
$text
Definition: button.php:32
$attributes
Elgg AJAX loader.
Definition: ajax_loader.php:10
if(empty($title)&&empty($body)) if(!empty($link)) $attrs
Definition: message.php:28
parseUrls(string $text)
Takes a string and turns any URLs into formatted links.