ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilWikiUtil.php
Go to the documentation of this file.
1 <?php
2 
32 // From include/Unicode/UtfNormal.php
33 if (!defined('UTF8_REPLACEMENT')) {
34  define('UTF8_REPLACEMENT', "\xef\xbf\xbd" /*codepointToUtf8( UNICODE_REPLACEMENT )*/);
35 }
36 
37 const IL_WIKI_MODE_REPLACE = "replace";
38 const IL_WIKI_MODE_COLLECT = "collect";
39 const IL_WIKI_MODE_EXT_COLLECT = "ext_collect";
40 
47 {
52  public static function replaceInternalLinks(
53  string $s,
54  int $a_wiki_id,
55  bool $a_offline = false,
56  string $lang = "-"
57  ): string {
58  return self::processInternalLinks(
59  $s,
60  $a_wiki_id,
62  false,
63  $a_offline,
64  $lang
65  );
66  }
67 
71  public static function collectInternalLinks(
72  string $s,
73  int $a_wiki_id,
74  bool $a_collect_non_ex = false,
75  string $mode = IL_WIKI_MODE_COLLECT
76  ): array {
78 
79  $log->debug("collect interna links wiki id: " . $a_wiki_id . ", collect nonex: " . $a_collect_non_ex);
80 
81  $result = self::processInternalLinks(
82  $s,
83  $a_wiki_id,
84  $mode,
85  $a_collect_non_ex
86  );
87  $log->debug("content: " . $s);
88  $log->debug("found: " . print_r($result, true));
89  return $result;
90  }
91 
97  protected static function processInternalLinks(
98  string $s,
99  int $a_wiki_id,
100  string $a_mode = IL_WIKI_MODE_REPLACE,
101  bool $a_collect_non_ex = false,
102  bool $a_offline = false,
103  string $lang = "-"
104  ) {
105  global $DIC;
106  $page_repo = $DIC->wiki()->internal()->repo()->page();
107 
108  include_once("./Modules/Wiki/libs/Sanitizer.php");
109  $collect = array();
110  // both from mediawiki DefaulSettings.php
111  $wgLegalTitleChars = " %!\"$&'()*,\\-.\\/0-9:;=?@A-Z\\\\^_`a-z~\\x80-\\xFF+";
112 
113  // dummies for wiki globals
114  $GLOBALS["wgContLang"] = new class () {
115  public function getNsIndex($a_p): bool
116  {
117  return false;
118  }
119  public function lc($a_key): bool
120  {
121  return false;
122  }
123  };
124  $GLOBALS["wgInterWikiCache"] = false;
125 
126  # the % is needed to support urlencoded titles as well
127  //$tc = Title::legalChars().'#%';
128  $tc = $wgLegalTitleChars . '#%';
129 
130  //$sk = $this->mOptions->getSkin();
131 
132  #split the entire text string on occurences of [[
133  $a = explode('[[', ' ' . $s);
134  #get the first element (all text up to first [[), and remove the space we added
135  $s = array_shift($a);
136  $s = substr($s, 1);
137 
138  # Match a link having the form [[namespace:link|alternate]]trail
139  $e1 = "/^([{$tc}]+)(?:\\|(.+?))?]](.*)\$/sD";
140 
141  # Match cases where there is no "]]", which might still be images
142  // static $e1_img = FALSE;
143  // if ( !$e1_img ) { $e1_img = "/^([{$tc}]+)\\|(.*)\$/sD"; }
144 
145  # Match the end of a line for a word that's not followed by whitespace,
146  # e.g. in the case of 'The Arab al[[Razi]]', 'al' will be matched
147  // $e2 = wfMsgForContent( 'linkprefix' );
148 
149  /* $useLinkPrefixExtension = $wgContLang->linkPrefixExtension();
150  if( is_null( $this->mTitle ) ) {
151  throw new MWException( __METHOD__.": \$this->mTitle is null\n" );
152  }
153  $nottalk = !$this->mTitle->isTalkPage();*/
154  $nottalk = true;
155 
156  /* if ( $useLinkPrefixExtension ) {
157  $m = array();
158  if ( preg_match( $e2, $s, $m ) ) {
159  $first_prefix = $m[2];
160  } else {
161  $first_prefix = false;
162  }
163  } else {*/
164  $prefix = '';
165  // }
166 
167  $useSubpages = false;
168 
169  # Loop for each link
170  for ($k = 0; isset($a[$k]); $k++) {
171  $line = $a[$k];
172 
173 
174  $might_be_img = false;
175 
176  //wfProfileIn( "$fname-e1" );
177  if (preg_match($e1, $line, $m)) { # page with normal text or alt
178  $text = $m[2];
179  # If we get a ] at the beginning of $m[3] that means we have a link that's something like:
180  # [[Image:Foo.jpg|[http://example.com desc]]] <- having three ] in a row fucks up,
181  # the real problem is with the $e1 regex
182  # See bug 1300.
183  #
184  # Still some problems for cases where the ] is meant to be outside punctuation,
185  # and no image is in sight. See bug 2095.
186  #
187  if ($text !== '' &&
188  strpos($m[3], ']') === 0 &&
189  strpos($text, '[') !== false
190  ) {
191  $text .= ']'; # so that replaceExternalLinks($text) works later
192  $m[3] = substr($m[3], 1);
193  }
194  # fix up urlencoded title texts
195  if (strpos($m[1], '%') !== false) {
196  # Should anchors '#' also be rejected?
197  $m[1] = str_replace(array('<', '>'), array('&lt;', '&gt;'), urldecode($m[1]));
198  }
199  $trail = $m[3];
200  /* } elseif( preg_match($e1_img, $line, $m) ) { # Invalid, but might be an image with a link in its caption
201  $might_be_img = true;
202  $text = $m[2];
203  if ( strpos( $m[1], '%' ) !== false ) {
204  $m[1] = urldecode($m[1]);
205  }
206  $trail = "";*/
207  } else { # Invalid form; output directly
208  $s .= $prefix . '[[' . $line ;
209  //wfProfileOut( "$fname-e1" );
210  continue;
211  }
212  //wfProfileOut( "$fname-e1" );
213  //wfProfileIn( "$fname-misc" );
214 
215  # Don't allow internal links to pages containing
216  # PROTO: where PROTO is a valid URL protocol; these
217  # should be external links.
218  if (preg_match('/^\b%na' . self::wfUrlProtocols() . 'me/', $m[1])) {
219  $s .= $prefix . '[[' . $line ;
220  continue;
221  }
222 
223  # Make subpage if necessary
224  /* if( $useSubpages ) {
225  $link = $this->maybeDoSubpageLink( $m[1], $text );
226  } else {*/
227  $link = $m[1];
228  // }
229 
230  $noforce = (strpos($m[1], ':') !== 0);
231  if (!$noforce) {
232  # Strip off leading ':'
233  $link = substr($link, 1);
234  }
235 
236  // wfProfileOut( "$fname-misc" );
237  // wfProfileIn( "$fname-title" );
238 
239  $nt = Title::newFromText($link);
240 
241  if (!$nt) {
242  $s .= $prefix . '[[' . $line;
243  continue;
244  }
245 
246  $wasblank = ('' == $text);
247  if ($wasblank) {
248  $text = $link;
249  }
250 
251  // Media wiki performs an intermediate step here (Parser->makeLinkHolder)
252  if ($a_mode === IL_WIKI_MODE_REPLACE) {
253  $s .= self::makeLink(
254  $nt,
255  $a_wiki_id,
256  $text,
257  '',
258  $trail,
259  $prefix,
260  $a_offline,
261  $lang
262  );
263  }
264  if ($a_mode === IL_WIKI_MODE_EXT_COLLECT) {
265  if (is_object($nt)) {
266  $url_title = self::makeUrlTitle($nt->mTextform);
267  $db_title = self::makeDbTitle($nt->mTextform);
268  [$inside, $trail] = self::splitTrail($trail);
269  $collect[] = array("nt" => $nt, "text" => $text,
270  "trail" => $trail, "db_title" => $db_title,
271  "url_title" => $url_title);
272  }
273  } else {
274  $db_title = self::makeDbTitle($nt->mTextform);
275 
276  if (($a_collect_non_ex || $page_repo->existsByTitle($a_wiki_id, $db_title, $lang))
277  &&
278  !in_array($db_title, $collect)) {
279  $collect[] = $db_title;
280  }
281  }
282  }
283 
284  //wfProfileOut( $fname );
285 
286  if ($a_mode === IL_WIKI_MODE_COLLECT ||
287  $a_mode === IL_WIKI_MODE_EXT_COLLECT) {
288  return $collect;
289  } else {
290  return $s;
291  }
292  }
293 
294  protected static function removeUnsafeCharacters(
295  string $a_str
296  ): string {
297  return str_replace(array("\x00", "\n", "\r", "\\", "'", '"', "\x1a"), "", $a_str);
298  }
299 
309  protected static function makeLink(
310  object $nt,
311  int $a_wiki_id,
312  string $text = '',
313  string $query = '',
314  string $trail = '',
315  string $prefix = '',
316  bool $a_offline = false,
317  string $lang = "-"
318  ): string {
319  global $DIC;
320  $request = $DIC
321  ->wiki()
322  ->internal()
323  ->gui()
324  ->request();
325  $page_repo = $DIC->wiki()->internal()->repo()->page();
326 
327  $ilCtrl = $DIC->ctrl();
328 
329  if (!is_object($nt)) {
330  # Fail gracefully
331  $retVal = "<!-- ERROR -->{$prefix}{$text}{$trail}";
332  } else {
333  // remove anchor from text, define anchor
334  $anc = "";
335  if ($nt->mFragment != "") {
336  if (substr($text, strlen($text) - strlen("#" . $nt->mFragment)) === "#" . $nt->mFragment) {
337  $text = substr($text, 0, strlen($text) - strlen("#" . $nt->mFragment));
338  }
339  $anc = "#copganc_" . $nt->mFragment;
340  }
341 
342  # Separate the link trail from the rest of the link
343  // outcommented due to bug #14590
344  // list( $inside, $trail ) = ilWikiUtil::splitTrail( $trail );
345 
346  $retVal = '***' . $text . "***" . $trail;
347  $url_title = self::makeUrlTitle($nt->mTextform);
348  $db_title = self::makeDbTitle($nt->mTextform);
349  if ($db_title != "") {
350  $pg_exists = $page_repo->existsByTitle($a_wiki_id, $db_title, $lang);
351  } else {
352  // links on same page (only anchor used)
353  $pg_exists = true;
354  }
355 
356  //var_dump($nt);
357  //var_dump($inside);
358  //var_dump($trail);
359  $wiki_link_class = (!$pg_exists)
360  ? ' class="ilc_link_IntLink ilWikiPageMissing" '
361  : ' class="ilc_link_IntLink" ';
362 
363  if (!$a_offline) {
364  if ($url_title != "") {
365  $ilCtrl->setParameterByClass("ilobjwikigui", "wpg_id", null);
366  $ilCtrl->setParameterByClass("ilwikipagegui", "wpg_id", null);
367  $ilCtrl->setParameterByClass("ilobjwikigui", "page", $url_title);
368  $retVal = '<a ' . $wiki_link_class . ' href="' .
369  $ilCtrl->getLinkTargetByClass("ilobjwikigui", "gotoPage") . $anc .
370  '">' . $text . '</a>' . $trail;
371  $ilCtrl->setParameterByClass(
372  "ilobjwikigui",
373  "page",
374  $request->getPage()
375  );
376  } else {
377  $retVal = '<a ' . $wiki_link_class . ' href="' .
378  $anc .
379  '">' . $text . '</a>' . $trail;
380  }
381  } else {
382  if ($pg_exists) {
383  if ($db_title != "") {
384  $pg_id = ilWikiPage::getPageIdForTitle($a_wiki_id, $db_title);
385  $retVal = '<a ' . $wiki_link_class . ' href="' .
386  "wpg_" . $pg_id . ".html" . $anc .
387  '">' . $text . '</a>' . $trail;
388  } else {
389  $retVal = '<a ' . $wiki_link_class . ' href="' .
390  $anc .
391  '">' . $text . '</a>' . $trail;
392  }
393  } else {
394  $retVal = $text . $trail;
395  }
396  }
397  }
398  return $retVal;
399  }
400 
405  public static function wfUrlProtocols(): string
406  {
407  $wgUrlProtocols = array(
408  'http://',
409  'https://',
410  'ftp://',
411  'irc://',
412  'gopher://',
413  'telnet://', // Well if we're going to support the above.. -ævar
414  'nntp://', // @bug 3808 RFC 1738
415  'worldwind://',
416  'mailto:',
417  'news:'
418  );
419 
420  // Support old-style $wgUrlProtocols strings, for backwards compatibility
421  // with LocalSettings files from 1.5
422  $protocols = array();
423  foreach ($wgUrlProtocols as $protocol) {
424  $protocols[] = preg_quote($protocol, '/');
425  }
426 
427  return implode('|', $protocols);
428  }
429 
430  public static function wfUrlencode(
431  string $s
432  ): string {
433  $s = urlencode($s);
434  return $s;
435  }
436 
437  public static function makeDbTitle(
438  string $a_par
439  ): string {
440  $a_par = self::removeUnsafeCharacters($a_par);
441  return str_replace("_", " ", $a_par);
442  }
443 
444  public static function makeUrlTitle(
445  string $a_par
446  ): string {
447  $a_par = self::removeUnsafeCharacters($a_par);
448  $a_par = str_replace(" ", "_", $a_par);
449  return self::wfUrlencode($a_par);
450  }
451 
452  public static function splitTrail(
453  string $trail
454  ): array {
455  $regex = '/^([a-z]+)(.*)$/sD';
456 
457  $inside = '';
458  if ('' != $trail) {
459  $m = array();
460 
461  if (preg_match($regex, $trail, $m)) {
462  $inside = $m[1];
463  $trail = $m[2];
464  }
465  }
466 
467  return array( $inside, $trail );
468  }
469 
470  public static function sendNotification(
471  string $a_action,
472  int $a_type,
473  int $a_wiki_ref_id,
474  int $a_page_id,
475  ?string $a_comment = null
476  ): void {
477  global $DIC;
478 
480  $log->debug("start... vvvvvvvvvvvvvvvvvvvvvvvvvvv");
481 
482  $ilUser = $DIC->user();
483  $ilObjDataCache = $DIC["ilObjDataCache"];
484  $ilAccess = $DIC->access();
485 
486  if ($a_wiki_ref_id === 0) {
487  return;
488  }
489 
490  $wiki_id = $ilObjDataCache->lookupObjId($a_wiki_ref_id);
491  $wiki = new ilObjWiki($a_wiki_ref_id, true);
492  $page = new ilWikiPage($a_page_id);
493 
494  // #11138
495  $ignore_threshold = ($a_action === "comment");
496 
497  // 1st update will be converted to new - see below
498  if ($a_action === "new") {
499  return;
500  }
501 
502  $log->debug("-- get notifications");
503  if ($a_type == ilNotification::TYPE_WIKI_PAGE) {
504  $users = ilNotification::getNotificationsForObject($a_type, $a_page_id, null, $ignore_threshold);
505  $wiki_users = ilNotification::getNotificationsForObject(ilNotification::TYPE_WIKI, $wiki_id, $a_page_id, $ignore_threshold);
506  $users = array_merge($users, $wiki_users);
507  if (!count($users)) {
508  $log->debug("no notifications... ^^^^^^^^^^^^^^^^^^");
509  return;
510  }
511  } else {
512  $users = ilNotification::getNotificationsForObject(ilNotification::TYPE_WIKI, $wiki_id, $a_page_id, $ignore_threshold);
513  $log->debug("--->" . print_r($users));
514  if (!count($users)) {
515  $log->debug("no notifications... ^^^^^^^^^^^^^^^^^^");
516  return;
517  }
518  }
521 
522  // #15192 - should always be present
523  if ($a_page_id) {
524  // #18804 - see ilWikiPageGUI::preview()
525  $link = ilLink::_getLink(null, "wiki", [], "wpage_" . $a_page_id . "_" . $a_wiki_ref_id);
526  } else {
527  $link = ilLink::_getLink($a_wiki_ref_id);
528  }
529 
530  $log->debug("-- prepare content");
531  $pgui = new ilWikiPageGUI($page->getId());
532  $pgui->setRawPageContent(true);
533  $pgui->setAbstractOnly(true);
534  $pgui->setFileDownloadLink(".");
535  $pgui->setFullscreenLink(".");
536  $pgui->setSourcecodeDownloadScript(".");
537  $snippet = $pgui->showPage();
538  $snippet = ilPageObject::truncateHTML($snippet, 500, "...");
539 
540  // making things more readable
541  $snippet = str_replace(['<br/>', '<br />', '</p>', '</div>'], "\n", $snippet);
542 
543  $snippet = trim(strip_tags($snippet));
544 
545  // "fake" new (to enable snippet - if any)
546  $hist = $page->getHistoryEntries();
547  $current_version = array_shift($hist);
548  $current_version = $current_version["nr"] ?? 0;
549  if (!$current_version && $a_action !== "comment") {
550  $a_type = ilNotification::TYPE_WIKI;
551  $a_action = "new";
552  }
553 
554  $log->debug("-- sending mails");
555  $mails = [];
556  foreach (array_unique($users) as $idx => $user_id) {
557  if ($user_id != $ilUser->getId() &&
558  $ilAccess->checkAccessOfUser($user_id, 'read', '', $a_wiki_ref_id)) {
559  // use language of recipient to compose message
560  $ulng = ilLanguageFactory::_getLanguageOfUser($user_id);
561  $ulng->loadLanguageModule('wiki');
562 
563  if ($a_action === "comment") {
564  $subject = sprintf($ulng->txt('wiki_notification_comment_subject'), $wiki->getTitle(), $page->getTitle());
565  $message = sprintf($ulng->txt('wiki_change_notification_salutation'), ilObjUser::_lookupFullname($user_id)) . "\n\n";
566 
567  $message .= $ulng->txt('wiki_notification_' . $a_action) . ":\n\n";
568  $message .= $ulng->txt('wiki') . ": " . $wiki->getTitle() . "\n";
569  $message .= $ulng->txt('page') . ": " . $page->getTitle() . "\n";
570  $message .= $ulng->txt('wiki_commented_by') . ": " . ilUserUtil::getNamePresentation($ilUser->getId()) . "\n";
571 
572  // include comment/note text
573  if ($a_comment) {
574  $message .= "\n" . $ulng->txt('comment') . ":\n\"" . trim($a_comment) . "\"\n";
575  }
576 
577  $message .= "\n" . $ulng->txt('wiki_change_notification_page_link') . ": " . $link;
578  } else {
579  $subject = sprintf($ulng->txt('wiki_change_notification_subject'), $wiki->getTitle(), $page->getTitle());
580  $message = sprintf($ulng->txt('wiki_change_notification_salutation'), ilObjUser::_lookupFullname($user_id)) . "\n\n";
581 
582  if ($a_type == ilNotification::TYPE_WIKI_PAGE) {
583  // update/delete
584  $message .= $ulng->txt('wiki_change_notification_page_body_' . $a_action) . ":\n\n";
585  $message .= $ulng->txt('wiki') . ": " . $wiki->getTitle() . "\n";
586  $message .= $ulng->txt('page') . ": " . $page->getTitle() . "\n";
587  $message .= $ulng->txt('wiki_changed_by') . ": " . ilUserUtil::getNamePresentation($ilUser->getId()) . "\n";
588 
589  if ($snippet) {
590  $message .= "\n" . $ulng->txt('content') . "\n" .
591  "----------------------------------------\n" .
592  $snippet . "\n" .
593  "----------------------------------------\n";
594  }
595 
596  // include comment/note text
597  if ($a_comment) {
598  $message .= "\n" . $ulng->txt('comment') . ":\n\"" . trim($a_comment) . "\"\n";
599  }
600 
601  $message .= "\n" . $ulng->txt('wiki_change_notification_page_link') . ": " . $link;
602  } else {
603  // new
604  $message .= $ulng->txt('wiki_change_notification_body_' . $a_action) . ":\n\n";
605  $message .= $ulng->txt('wiki') . ": " . $wiki->getTitle() . "\n";
606  $message .= $ulng->txt('page') . ": " . $page->getTitle() . "\n";
607  $message .= $ulng->txt('wiki_changed_by') . ": " . ilUserUtil::getNamePresentation($ilUser->getId()) . "\n\n";
608 
609  if ($snippet) {
610  $message .= $ulng->txt('content') . "\n" .
611  "----------------------------------------\n" .
612  $snippet . "\n" .
613  "----------------------------------------\n\n";
614  }
615 
616  $message .= $ulng->txt('wiki_change_notification_link') . ": " . $link;
617  }
618  }
619 
620  $mail_obj = new ilMail(ANONYMOUS_USER_ID);
621  $mail_obj->appendInstallationSignature(true);
622  $log->debug("before enqueue ($user_id)");
623  /*
624  $mail_obj->enqueue(
625  ilObjUser::_lookupLogin($user_id),
626  "",
627  "",
628  $subject,
629  $message,
630  array()
631  );*/
633  $mails[] = new ilMailValueObject(
634  '',
635  ilObjUser::_lookupLogin($user_id),
636  '',
637  '',
638  $subject,
639  $message,
640  [],
641  false,
642  false
643  );
644  $log->debug("after enqueue");
645  } else {
646  unset($users[$idx]);
647  }
648  }
649  if (count($mails) > 0) {
650  $processor = new ilMassMailTaskProcessor();
651  $processor->run(
652  $mails,
654  "",
655  []
656  );
657  }
658  $log->debug("end... ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
659  }
660 }
static makeLink(object $nt, int $a_wiki_id, string $text='', string $query='', string $trail='', string $prefix='', bool $a_offline=false, string $lang="-")
Make a wiki link, the following formats are supported:
const ANONYMOUS_USER_ID
Definition: constants.php:27
static getLogger(string $a_component_id)
Get component logger.
static _lookupFullname(int $a_user_id)
static newFromText($text, $defaultNamespace=NS_MAIN)
Create a new Title from text, such as what one would find in a link.
Definition: Title.php:100
if(!defined('UTF8_REPLACEMENT')) const IL_WIKI_MODE_REPLACE
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static removeUnsafeCharacters(string $a_str)
static processInternalLinks(string $s, int $a_wiki_id, string $a_mode=IL_WIKI_MODE_REPLACE, bool $a_collect_non_ex=false, bool $a_offline=false, string $lang="-")
Process internal links (internal)
static getNotificationsForObject(int $type, int $id, ?int $page_id=null, bool $ignore_threshold=false)
Get all users/recipients for given object.
static replaceInternalLinks(string $s, int $a_wiki_id, bool $a_offline=false, string $lang="-")
This one is based on Mediawiki Parser->replaceInternalLinks since we display images in another way...
const IL_WIKI_MODE_COLLECT
static sendNotification(string $a_action, int $a_type, int $a_wiki_ref_id, int $a_page_id, ?string $a_comment=null)
global $DIC
Definition: feed.php:28
static truncateHTML(string $a_text, int $a_length=100, string $a_ending='...', bool $a_exact=false, bool $a_consider_html=true)
Truncate (html) string.
static getNamePresentation( $a_user_id, bool $a_user_image=false, bool $a_profile_link=false, string $a_profile_back_link='', bool $a_force_first_lastname=false, bool $a_omit_login=false, bool $a_sortable=true, bool $a_return_data_array=false, $a_ctrl_path='ilpublicuserprofilegui')
Default behaviour is:
$GLOBALS["DIC"]
Definition: wac.php:31
static _getLanguageOfUser(int $a_usr_id)
Get language object of user.
$log
Definition: result.php:33
setRawPageContent(bool $a_rawpagecontent)
Set Get raw page content only.
static collectInternalLinks(string $s, int $a_wiki_id, bool $a_collect_non_ex=false, string $mode=IL_WIKI_MODE_COLLECT)
Collect internal wiki links of a string.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Class ilWikiPage GUI class.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static updateNotificationTime(int $type, int $id, array $user_ids, ?int $page_id=null, bool $activate_new_entries=true)
Update the last mail timestamp for given object and users.
Utility class for wiki.
$lang
Definition: xapiexit.php:26
static makeUrlTitle(string $a_par)
form( $class_path, string $cmd, string $submit_caption="")
static wfUrlProtocols()
From mediawiki GlobalFunctions.php.
static makeDbTitle(string $a_par)
static getPageIdForTitle(int $a_wiki_id, string $a_title, string $lang="-")
Get wiki page object for id and title.
const IL_WIKI_MODE_EXT_COLLECT
$a
thx to https://mlocati.github.io/php-cs-fixer-configurator for the examples
$message
Definition: xapiexit.php:32
static wfUrlencode(string $s)
static splitTrail(string $trail)
static _getInstallationSignature()
static _lookupLogin(int $a_user_id)