/** * @see \wcf\system\event\listener\IParameterizedEventListener::execute() */ public function execute($eventObj, $className, $eventName, array &$parameters) { if (!$eventObj->text) { return; } // check if needed url BBCode is allowed if ($eventObj->allowedBBCodes !== null && !BBCode::isAllowedBBCode('url', $eventObj->allowedBBCodes)) { return; } static $userRegex = null; if ($userRegex === null) { $userRegex = new Regex("\n\t\t\t\t(?:^|(?<=\\s|\\]))\t\t\t\t\t# either at start of string, or after whitespace\n\t\t\t\t@\n\t\t\t\t(\n\t\t\t\t\t([^',\\s][^,\\s]{2,})(?:\\s[^,\\s]+)?\t# either at most two strings, not containing\n\t\t\t\t\t\t\t\t\t\t# whitespace or the comma, not starting with a single quote\n\t\t\t\t\t\t\t\t\t\t# separated by a single whitespace character\n\t\t\t\t|\n\t\t\t\t\t'(?:''|[^']){3,}'\t\t\t# or a string delimited by single quotes\n\t\t\t\t)\n\t\t\t", Regex::IGNORE_WHITESPACE); } // cache quotes // @see \wcf\system\bbcode\BBCodeParser::buildTagArray() $pattern = '~\\[(?:/(?:quote)|(?:quote) (?:= (?:\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^,\\]]*) (?:,(?:\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^,\\]]*))* )?)\\]~ix'; preg_match_all($pattern, $eventObj->text, $quoteMatches); $textArray = preg_split($pattern, $eventObj->text); $text = $textArray[0]; $openQuotes = 0; $quote = ''; foreach ($quoteMatches[0] as $i => $quoteTag) { if (mb_substr($quoteTag, 1, 1) == '/') { $openQuotes--; $quote .= $quoteTag; if ($openQuotes) { $quote .= $textArray[$i + 1]; } else { $text .= StringStack::pushToStringStack($quote, 'preParserUserMentions', '@@@') . $textArray[$i + 1]; $quote = ''; } } else { $openQuotes++; $quote .= $quoteTag . $textArray[$i + 1]; } } if ($quote) { $text .= $quote; } $userRegex->match($text, true, Regex::ORDER_MATCH_BY_SET); $matches = $userRegex->getMatches(); if (!empty($matches)) { $usernames = array(); foreach ($matches as $match) { // we don't care about the full match array_shift($match); foreach ($match as $username) { $username = self::getUsername($username); if (!in_array($username, $usernames)) { $usernames[] = $username; } } } if (!empty($usernames)) { // fetch users $userList = new UserList(); $userList->getConditionBuilder()->add('user_table.username IN (?)', array($usernames)); $userList->readObjects(); $users = array(); foreach ($userList as $user) { $users[mb_strtolower($user->username)] = $user; } $text = $userRegex->replace($text, new Callback(function ($matches) use($users) { // containing the full match $usernames = array($matches[1]); // containing only the part before the first space if (isset($matches[2])) { $usernames[] = $matches[2]; } $usernames = array_map(array('\\wcf\\system\\event\\listener\\PreParserAtUserListener', 'getUsername'), $usernames); foreach ($usernames as $username) { if (!isset($users[$username])) { continue; } $link = LinkHandler::getInstance()->getLink('User', array('appendSession' => false, 'object' => $users[$username])); $mention = "[url='" . $link . "']@" . $users[$username]->username . '[/url]'; // check if only the part before the first space matched, in that case append the second word if (isset($matches[2]) && strcasecmp($matches[2], $username) === 0) { $mention .= mb_substr($matches[1], strlen($matches[2])); } return $mention; } return $matches[0]; })); } } // reinsert cached quotes $eventObj->text = StringStack::reinsertStrings($text, 'preParserUserMentions'); }
/** * Handles pre-parsing of URLs. */ protected function parseURLs() { static $urlPattern = null; static $callback = null; if ($urlPattern === null) { $urlPattern = new Regex(' (?<!\\B|"|\'|=|/|,|\\?|\\.) (?: # hostname (?:ftp|https?)://' . static::$illegalChars . '(?:\\.' . static::$illegalChars . ')* | www\\.(?:' . static::$illegalChars . '\\.)+ (?:[a-z]{2,63}(?=\\b)) # tld ) (?::\\d+)? # port (?: / [^!.,?;"\'<>()\\[\\]{}\\s]* (?: [!.,?;(){}]+ [^!.,?;"\'<>()\\[\\]{}\\s]+ )* )?', Regex::IGNORE_WHITESPACE | Regex::CASE_INSENSITIVE); } if ($callback === null) { $callback = new Callback(function ($matches) { if ((PreParser::getInstance()->allowedBBCodes === null || BBCode::isAllowedBBCode('media', PreParser::getInstance()->allowedBBCodes)) && BBCodeMediaProvider::isMediaURL($matches[0])) { return '[media]' . $matches[0] . '[/media]'; } if (PreParser::getInstance()->allowedBBCodes === null || BBCode::isAllowedBBCode('url', PreParser::getInstance()->allowedBBCodes)) { return '[url]' . $matches[0] . '[/url]'; } return $matches[0]; }); } $this->text = $urlPattern->replace($this->text, $callback); }
/** * @see \wcf\form\IForm::validate() */ public function validate() { parent::validate(); // tag name must not be empty if (empty($this->bbcodeTag)) { throw new UserInputException('bbcodeTag'); } // tag may only contain alphanumeric chars if (!Regex::compile('^[a-z0-9]+$', Regex::CASE_INSENSITIVE)->match($this->bbcodeTag)) { throw new UserInputException('bbcodeTag', 'notValid'); } // disallow the Pseudo-BBCodes all and none if ($this->bbcodeTag == 'all' || $this->bbcodeTag == 'none') { throw new UserInputException('bbcodeTag', 'notValid'); } // check whether the tag is in use $bbcode = BBCode::getBBCodeByTag($this->bbcodeTag); if (!isset($this->bbcode) && $bbcode->bbcodeID || isset($this->bbcode) && $bbcode->bbcodeID != $this->bbcode->bbcodeID) { throw new UserInputException('bbcodeTag', 'inUse'); } // handle empty case first if (empty($this->allowedChildren)) { throw new UserInputException('allowedChildren'); } // validate syntax of allowedChildren: Optional all|none^ followed by a comma-separated list of bbcodes if (!empty($this->allowedChildren) && !Regex::compile('^(?:(?:all|none)\\^)?(?:[a-zA-Z0-9]+,)*[a-zA-Z0-9]+$')->match($this->allowedChildren)) { throw new UserInputException('allowedChildren', 'notValid'); } // validate class if (!empty($this->className) && !class_exists($this->className)) { throw new UserInputException('className', 'notFound'); } // validate attributes foreach ($this->attributes as $attribute) { // Check whether the pattern is a valid regex if (!Regex::compile($attribute->validationPattern)->isValid()) { throw new UserInputException('attributeValidationPattern' . $attribute->attributeNo, 'notValid'); } } // button if ($this->showButton) { // validate label if (!I18nHandler::getInstance()->validateValue('buttonLabel')) { if (I18nHandler::getInstance()->isPlainValue('buttonLabel')) { throw new UserInputException('buttonLabel'); } else { throw new UserInputException('buttonLabel', 'multilingual'); } } // validate image path if (empty($this->wysiwygIcon)) { throw new UserInputException('wysiwygIcon'); } } else { $this->buttonLabel = ''; } }