/** * More or less "markup-safe" explode() * Ignores any instances of the separator inside <...> * @param string $separator * @param string $text * @return array */ static function explodeMarkup($separator, $text) { $placeholder = ""; // Remove placeholder instances $text = str_replace($placeholder, '', $text); // Replace instances of the separator inside HTML-like tags with the placeholder $replacer = new DoubleReplacer($separator, $placeholder); $cleaned = StringUtils::delimiterReplaceCallback('<', '>', $replacer->cb(), $text); // Explode, then put the replaced separators back in $items = explode($separator, $cleaned); foreach ($items as $i => $str) { $items[$i] = str_replace($placeholder, $separator, $str); } return $items; }
function expand($string) { return StringUtils::delimiterReplaceCallback("<!-- LINKMARKER ", " -->", array(&$this, 'callback'), $string); }
/** * Return the text of a template, after recursively * replacing any variables or templates within the template. * * @param array $piece The parts of the template * $piece['text']: matched text * $piece['title']: the title, i.e. the part before the | * $piece['parts']: the parameter array * @return string the text of the template * @private */ function braceSubstitution($piece) { global $wgContLang, $wgLang, $wgAllowDisplayTitle, $wgNonincludableNamespaces; $fname = __METHOD__; wfProfileIn($fname); wfProfileIn(__METHOD__ . '-setup'); # Flags $found = false; # $text has been filled $nowiki = false; # wiki markup in $text should be escaped $noparse = false; # Unsafe HTML tags should not be stripped, etc. $noargs = false; # Don't replace triple-brace arguments in $text $replaceHeadings = false; # Make the edit section links go to the template not the article $headingOffset = 0; # Skip headings when number, to account for those that weren't transcluded. $isHTML = false; # $text is HTML, armour it against wikitext transformation $forceRawInterwiki = false; # Force interwiki transclusion to be done in raw mode not rendered # Title object, where $text came from $title = NULL; $linestart = ''; # $part1 is the bit before the first |, and must contain only title characters # $args is a list of arguments, starting from index 0, not including $part1 $titleText = $part1 = $piece['title']; # If the third subpattern matched anything, it will start with | if (null == $piece['parts']) { $replaceWith = $this->variableSubstitution(array($piece['text'], $piece['title'])); if ($replaceWith != $piece['text']) { $text = $replaceWith; $found = true; $noparse = true; $noargs = true; } } $args = null == $piece['parts'] ? array() : $piece['parts']; wfProfileOut(__METHOD__ . '-setup'); # SUBST wfProfileIn(__METHOD__ . '-modifiers'); if (!$found) { $mwSubst =& MagicWord::get('subst'); if ($mwSubst->matchStartAndRemove($part1) xor $this->ot['wiki']) { # One of two possibilities is true: # 1) Found SUBST but not in the PST phase # 2) Didn't find SUBST and in the PST phase # In either case, return without further processing $text = $piece['text']; $found = true; $noparse = true; $noargs = true; } } # MSG, MSGNW and RAW if (!$found) { # Check for MSGNW: $mwMsgnw =& MagicWord::get('msgnw'); if ($mwMsgnw->matchStartAndRemove($part1)) { $nowiki = true; } else { # Remove obsolete MSG: $mwMsg =& MagicWord::get('msg'); $mwMsg->matchStartAndRemove($part1); } # Check for RAW: $mwRaw =& MagicWord::get('raw'); if ($mwRaw->matchStartAndRemove($part1)) { $forceRawInterwiki = true; } } wfProfileOut(__METHOD__ . '-modifiers'); //save path level before recursing into functions & templates. $lastPathLevel = $this->mTemplatePath; # Parser functions if (!$found) { wfProfileIn(__METHOD__ . '-pfunc'); $colonPos = strpos($part1, ':'); if ($colonPos !== false) { # Case sensitive functions $function = substr($part1, 0, $colonPos); if (isset($this->mFunctionSynonyms[1][$function])) { $function = $this->mFunctionSynonyms[1][$function]; } else { # Case insensitive functions $function = strtolower($function); if (isset($this->mFunctionSynonyms[0][$function])) { $function = $this->mFunctionSynonyms[0][$function]; } else { $function = false; } } if ($function) { $funcArgs = array_map('trim', $args); $funcArgs = array_merge(array(&$this, trim(substr($part1, $colonPos + 1))), $funcArgs); $result = call_user_func_array($this->mFunctionHooks[$function], $funcArgs); $found = true; // The text is usually already parsed, doesn't need triple-brace tags expanded, etc. //$noargs = true; //$noparse = true; if (is_array($result)) { if (isset($result[0])) { $text = $linestart . $result[0]; unset($result[0]); } // Extract flags into the local scope // This allows callers to set flags such as nowiki, noparse, found, etc. extract($result); } else { $text = $linestart . $result; } } } wfProfileOut(__METHOD__ . '-pfunc'); } # Template table test # Did we encounter this template already? If yes, it is in the cache # and we need to check for loops. if (!$found && isset($this->mTemplates[$piece['title']])) { $found = true; # Infinite loop test if (isset($this->mTemplatePath[$part1])) { $noparse = true; $noargs = true; $found = true; $text = $linestart . "[[{$part1}]]<!-- WARNING: template loop detected -->"; wfDebug(__METHOD__ . ": template loop broken at '{$part1}'\n"); } else { # set $text to cached message. $text = $linestart . $this->mTemplates[$piece['title']]; #treat title for cached page the same as others $ns = NS_TEMPLATE; $subpage = ''; $part1 = $this->maybeDoSubpageLink($part1, $subpage); if ($subpage !== '') { $ns = $this->mTitle->getNamespace(); } $title = Title::newFromText($part1, $ns); //used by include size checking $titleText = $title->getPrefixedText(); //used by edit section links $replaceHeadings = true; } } # Load from database if (!$found) { wfProfileIn(__METHOD__ . '-loadtpl'); $ns = NS_TEMPLATE; # declaring $subpage directly in the function call # does not work correctly with references and breaks # {{/subpage}}-style inclusions $subpage = ''; $part1 = $this->maybeDoSubpageLink($part1, $subpage); if ($subpage !== '') { $ns = $this->mTitle->getNamespace(); } $title = Title::newFromText($part1, $ns); if (!is_null($title)) { $titleText = $title->getPrefixedText(); # Check for language variants if the template is not found if ($wgContLang->hasVariants() && $title->getArticleID() == 0) { $wgContLang->findVariantLink($part1, $title); } if (!$title->isExternal()) { if ($title->getNamespace() == NS_SPECIAL && $this->mOptions->getAllowSpecialInclusion() && $this->ot['html']) { $text = SpecialPage::capturePath($title); if (is_string($text)) { $found = true; $noparse = true; $noargs = true; $isHTML = true; $this->disableCache(); } } else { if ($wgNonincludableNamespaces && in_array($title->getNamespace(), $wgNonincludableNamespaces)) { $found = false; //access denied wfDebug("{$fname}: template inclusion denied for " . $title->getPrefixedDBkey()); } else { list($articleContent, $title) = $this->fetchTemplateAndtitle($title); if ($articleContent !== false) { $found = true; $text = $articleContent; $replaceHeadings = true; } } } # If the title is valid but undisplayable, make a link to it if (!$found && ($this->ot['html'] || $this->ot['pre'])) { $text = "[[:{$titleText}]]"; $found = true; } } elseif ($title->isTrans()) { // Interwiki transclusion if ($this->ot['html'] && !$forceRawInterwiki) { $text = $this->interwikiTransclude($title, 'render'); $isHTML = true; $noparse = true; } else { $text = $this->interwikiTransclude($title, 'raw'); $replaceHeadings = true; } $found = true; } # Template cache array insertion # Use the original $piece['title'] not the mangled $part1, so that # modifiers such as RAW: produce separate cache entries if ($found) { if ($isHTML) { // A special page; don't store it in the template cache. } else { $this->mTemplates[$piece['title']] = $text; } $text = $linestart . $text; } } wfProfileOut(__METHOD__ . '-loadtpl'); } if ($found && !$this->incrementIncludeSize('pre-expand', strlen($text))) { # Error, oversize inclusion $text = $linestart . "[[{$titleText}]]<!-- WARNING: template omitted, pre-expand include size too large -->"; $noparse = true; $noargs = true; } # Recursive parsing, escaping and link table handling # Only for HTML output if ($nowiki && $found && ($this->ot['html'] || $this->ot['pre'])) { $text = wfEscapeWikiText($text); } elseif (!$this->ot['msg'] && $found) { if ($noargs) { $assocArgs = array(); } else { # Clean up argument array $assocArgs = self::createAssocArgs($args); # Add a new element to the templace recursion path $this->mTemplatePath[$part1] = 1; } if (!$noparse) { # If there are any <onlyinclude> tags, only include them if (in_string('<onlyinclude>', $text) && in_string('</onlyinclude>', $text)) { $replacer = new OnlyIncludeReplacer(); StringUtils::delimiterReplaceCallback('<onlyinclude>', '</onlyinclude>', array(&$replacer, 'replace'), $text); $text = $replacer->output; } # Remove <noinclude> sections and <includeonly> tags $text = StringUtils::delimiterReplace('<noinclude>', '</noinclude>', '', $text); $text = strtr($text, array('<includeonly>' => '', '</includeonly>' => '')); if ($this->ot['html'] || $this->ot['pre']) { # Strip <nowiki>, <pre>, etc. $text = $this->strip($text, $this->mStripState); if ($this->ot['html']) { $text = Sanitizer::removeHTMLtags($text, array(&$this, 'replaceVariables'), $assocArgs); } elseif ($this->ot['pre'] && $this->mOptions->getRemoveComments()) { $text = Sanitizer::removeHTMLcomments($text); } } $text = $this->replaceVariables($text, $assocArgs); # If the template begins with a table or block-level # element, it should be treated as beginning a new line. if (!$piece['lineStart'] && preg_match('/^(?:{\\||:|;|#|\\*)/', $text)) { $text = "\n" . $text; } } elseif (!$noargs) { # $noparse and !$noargs # Just replace the arguments, not any double-brace items # This is used for rendered interwiki transclusion $text = $this->replaceVariables($text, $assocArgs, true); } } # Prune lower levels off the recursion check path $this->mTemplatePath = $lastPathLevel; if ($found && !$this->incrementIncludeSize('post-expand', strlen($text))) { # Error, oversize inclusion $text = $linestart . "[[{$titleText}]]<!-- WARNING: template omitted, post-expand include size too large -->"; $noparse = true; $noargs = true; } if (!$found) { wfProfileOut($fname); return $piece['text']; } else { wfProfileIn(__METHOD__ . '-placeholders'); if ($isHTML) { # Replace raw HTML by a placeholder # Add a blank line preceding, to prevent it from mucking up # immediately preceding headings $text = "\n\n" . $this->insertStripItem($text, $this->mStripState); } else { # replace ==section headers== # XXX this needs to go away once we have a better parser. if (!$this->ot['wiki'] && !$this->ot['pre'] && $replaceHeadings) { if (!is_null($title)) { $encodedname = base64_encode($title->getPrefixedDBkey()); } else { $encodedname = base64_encode(""); } $m = preg_split('/(^={1,6}.*?={1,6}\\s*?$)/m', $text, -1, PREG_SPLIT_DELIM_CAPTURE); $text = ''; $nsec = $headingOffset; for ($i = 0; $i < count($m); $i += 2) { $text .= $m[$i]; if (!isset($m[$i + 1]) || $m[$i + 1] == "") { continue; } $hl = $m[$i + 1]; if (strstr($hl, "<!--MWTEMPLATESECTION")) { $text .= $hl; continue; } $m2 = array(); preg_match('/^(={1,6})(.*?)(={1,6}\\s*?)$/m', $hl, $m2); $text .= $m2[1] . $m2[2] . "<!--MWTEMPLATESECTION=" . $encodedname . "&" . base64_encode("{$nsec}") . "-->" . $m2[3]; $nsec++; } } } wfProfileOut(__METHOD__ . '-placeholders'); } # Prune lower levels off the recursion check path $this->mTemplatePath = $lastPathLevel; if (!$found) { wfProfileOut($fname); return $piece['text']; } else { wfProfileOut($fname); return $text; } }
function internalParse($text, $isMain = true, $frame = false) { $this->fck_internal_parse_text =& $text; /* // these three tags should remain unchanged $text = StringUtils::delimiterReplaceCallback( '<includeonly>', '</includeonly>', array( $this, 'fck_includeonly' ), $text ); $text = StringUtils::delimiterReplaceCallback( '<noinclude>', '</noinclude>', array( $this, 'fck_noinclude' ), $text ); $text = StringUtils::delimiterReplaceCallback( '<onlyinclude>', '</onlyinclude>', array( $this, 'fck_onlyinclude' ), $text ); */ // also replace all custom tags foreach ($this->FCKeditorWikiTags as $tag) { $tag = $this->guessCaseSensitiveTag($tag, $text); $this->fck_allTagsCurrentTagReplaced = $tag; $text = StringUtils::delimiterReplaceCallback("<{$tag}", "</{$tag}>", array($this, 'fck_allTags'), $text); } // Rule tags (the tag is not registered as a official wiki tag, therefore // not treated by the parser before if (defined('SEMANTIC_RULES_VERSION')) { $text = $this->replaceRules($text); } // __TOC__ etc. must be replaced $text = $this->stripToc($text); // HTML comments shouldn't be stripped $text = $this->fck_replaceHTMLcomments($text); // as well as templates $text = $this->fck_replaceTemplates($text); // as well as properties $text = $this->fck_replaceSpecialLinks($text); // preserve linebreaks //$text = strtr($text, array("\n" => "\nFCKLR_fcklr_FCKLR", "\r" => '')); // this doesn't work when inside tables. So leave this for later. $text = $this->parseExternalLinksWithTmpl($text); $finalString = parent::internalParse($text, $isMain); return $finalString; }
function internalParse($text, $isMain = true, $frame = false) { $this->fck_internal_parse_text =& $text; // these three tags should remain unchanged $text = StringUtils::delimiterReplaceCallback('<includeonly>', '</includeonly>', array($this, 'fck_includeonly'), $text); $text = StringUtils::delimiterReplaceCallback('<noinclude>', '</noinclude>', array($this, 'fck_noinclude'), $text); $text = StringUtils::delimiterReplaceCallback('<onlyinclude>', '</onlyinclude>', array($this, 'fck_onlyinclude'), $text); // HTML comments shouldn't be stripped $text = $this->fck_replaceHTMLcomments($text); // as well as templates $text = $this->fck_replaceTemplates($text); $finalString = parent::internalParse($text, $isMain); return $finalString; }