/** * @param Parser $parser * @return array */ public static function render($parser) { $html = ''; $html .= $parser->insertStripItem('<a href="#" class="custom-watchlist-wrapper" id="add-to-watchlist"></a>'); $parser->getOutput()->addModules('ext.customwatchlist.foo'); return array($html, 'markerType' => 'nowiki'); }
/** * Outputs the internal image wrapped in a link * @param Parser $parser Instance of running Parser. * @param String $image Name of image to display. * @param String $url External URL to which to link * @param String $alt Alternate text for image and link (optional) * @return String A parser strip flag which will be later replaced with raw html. */ function imageLink($parser, $image = null, $url = null, $alt = '') { # Short-circuit if requried params are missing if ($image === null || $url === null) { return $this->error('missing-params'); } # Prepare incomming params $image = trim($image); $url = trim($url); $alt = trim($alt); # Check for bad URLs if (!preg_match('/^(' . wfUrlProtocols() . ')/', $url) || preg_match('/\'"/', $url)) { $t = Title::newFromText($url); if (!$t) { return $this->error('bad-url', $url); } $url = $t->getFullURL(); } # Check to see that the selected image exists $imageObj = Image::newFromName($image); if (!$imageObj->exists()) { return $this->error('no-such-image', $image); } # Finally, since all checks passed, display it! return $parser->insertStripItem($this->msg('embed-clause', htmlentities($url, ENT_COMPAT), $imageObj->getURL(), htmlentities($alt, ENT_COMPAT)), $parser->mStripState); }
/** * @param Parser $parser * @return string */ public static function sprintbtn($parser) { $html = ''; $title = $parser->getTitle(); $link = $title->getFullURL('printable=yes'); $link = '#'; $parser->getOutput()->addModules('ext.stools.foo'); $html .= $parser->insertStripItem('<a id="print-button" href="' . $link . '" type="button" class="btn btn-primary"><i class="fa fa-print"></i> ' . wfMessage('stools-button-print')->plain() . '</a>'); return $html; }
/** * Handler for rendering the function hook. * * @since 0.4 * * @param Parser $parser * ... further arguments ... * * @return array */ public function renderFunction() { $args = func_get_args(); $this->parser = array_shift($args); $output = $this->validateAndRender($args, self::TYPE_FUNCTION); $options = $this->getFunctionOptions(); if (array_key_exists('isHTML', $options) && $options['isHTML']) { return $this->parser->insertStripItem($output, $this->parser->mStripState); } return array_merge(array($output), $options); }
/** * Creates a HTML link that will open in a new browser window or tab. * * @param Parser $parser Parser being used * @param string $target literal URI or a MediaWiki link for linked resource * @param string $label link text * @return string HTML */ function efParserCreateLink($parser, $target, $label = null) { // sanitise the input and set defaults if (is_null($label)) { $label = $target; $label = htmlspecialchars($label, ENT_NOQUOTES, 'UTF-8'); } else { $label = preg_replace("/\\b({$parser->mUrlProtocols})/", "\$1 ", $label); // Hack to not parse external links by breaking them $label = $parser->recursiveTagParse($label); $label = preg_replace("/\\b({$parser->mUrlProtocols}) /", "\$1", $label); // Put them back together } $attributes = array('target' => '_blank'); // WARNING: the order of the statements below does matter! // // Also, the Parser::insertStripItem is used to render the HTML inline. // See: http://www.mediawiki.org/wiki/Manual:Parser_functions#Parser_interface // Process (or rule out) external targets (literal URIs) if (preg_match($parser->mExtLinkBracketedRegex, "[{$target}]")) { return $parser->insertStripItem(Linker::makeExternalLink($target, $label, false, '', $attributes), $parser->mStripState); } // The target is not a literal URI. Create a Title object. $oTitle = Title::newFromText($target); // Title::newFromText may occasionally return null, which means something is really wrong with the $target. if (is_null($oTitle)) { return $parser->insertStripItem(wfMsg('newwindowlinks-invalid-target'), $parser->mStripState); } // Process (or rule out) existing local articles. if ($oTitle->exists()) { return $parser->insertStripItem(Linker::link($oTitle, $label, $attributes), $parser->mStripState); } // Process (or rule out) interwiki links. if (true !== $oTitle->isLocal()) { return $parser->insertStripItem(Linker::makeExternalLink($oTitle->getFullURL(), $label, false, '', $attributes), $parser->mStripState); } // Only non existing local articles remain. return $parser->insertStripItem(Linker::link($oTitle, $label, $attributes), $parser->mStripState); }
/** * Handler for rendering the function hook registered by Parser::setFunctionHook() * * @since 0.4 * * @param Parser &$parser * ... further arguments ... * * @return array */ public function renderFunction(Parser &$parser) { $args = func_get_args(); $this->parser = array_shift($args); $output = $this->validateAndRender($args, self::TYPE_FUNCTION); $options = $this->getFunctionOptions(); if (array_key_exists('isHTML', $options) && $options['isHTML']) { /** @ToDo: FIXME: Is this really necessary? The same thing is propably going to * happen in Parser::braceSubstitution() if 'isHTML' is set! * @ToDo: other options besides 'isHTML' like 'noparse' are ignored here! */ return $this->parser->insertStripItem($output, $this->parser->mStripState); } return array_merge(array($output), $options); }
/** * @param Parser $parser * @return string */ public static function render($parser) { $user = $parser->getUser(); $title = $parser->getTitle(); if (!$title || !$title->exists()) { return ''; } $author = self::getPageAuthor($title); if (!$author) { return ''; } $html = '<span class="author-rating-wrapper">'; // Add user link $html .= $parser->insertStripItem(' <a target="_blank" href="' . $author->getUserPage()->getFullURL() . '">' . $author->getName() . '</a>'); $html .= ' <i style="display: none;" class="fa fa-thumbs-o-up author-thumbs-up" title="' . wfMessage('authorrating-rate-title')->plain() . '"></i>'; $html .= ' <span class="label label-success">?</span>'; $html .= '</span>'; $parser->getOutput()->addModules('ext.authorrating.foo'); return array($html, 'markerType' => 'nowiki'); }
/** * Renders the #serieslink parser function. * * @param Parser $parser * @return string the unique tag which must be inserted into the stripped text */ public static function renderSeriesLink(&$parser) { global $wgTitle; $params = func_get_args(); array_shift($params); // We don't need the parser. // remove the target parameter should it be present foreach ($params as $key => $value) { $elements = explode('=', $value, 2); if ($elements[0] === 'target') { unset($params[$key]); } } // set the origin parameter // This will block it from use as iterator parameter. Oh well. $params[] = "origin=" . $parser->getTitle()->getArticleId(); // hack to remove newline from beginning of output, thanks to // http://jimbojw.com/wiki/index.php?title=Raw_HTML_Output_from_a_MediaWiki_Parser_Function return $parser->insertStripItem(SFUtils::createFormLink($parser, 'SeriesEdit', $params), $parser->mStripState); }
/** * Parse the text into proper poem format * @param string $in The text inside the poem tag * @param array $param * @param Parser $parser * @param boolean $frame * @return string */ public static function renderPoem($in, $param = array(), $parser = null, $frame = false) { // using newlines in the text will cause the parser to add <p> tags, // which may not be desired in some cases $newline = isset($param['compact']) ? '' : "\n"; $tag = $parser->insertStripItem("<br />", $parser->mStripState); // replace colons with indented spans $text = preg_replace_callback('/^(:+)(.+)$/m', array('Poem', 'indentVerse'), $in); // replace newlines with <br /> tags unless they are at the beginning or end // of the poem $text = preg_replace(array("/^\n/", "/\n\$/D", "/\n/"), array("", "", "{$tag}\n"), $text); // replace spaces at the beginning of a line with non-breaking spaces $text = preg_replace_callback('/^( +)/m', array('Poem', 'replaceSpaces'), $text); $text = $parser->recursiveTagParse($text, $frame); $attribs = Sanitizer::validateTagAttributes($param, 'div'); // Wrap output in a <div> with "poem" class. if (isset($attribs['class'])) { $attribs['class'] = 'poem ' . $attribs['class']; } else { $attribs['class'] = 'poem'; } return Html::rawElement('div', $attribs, $newline . trim($text) . $newline); }
/** * @throws MWException * @param string|PPNode_DOM|DOMDocument $root * @param int $flags * @return string */ public function expand($root, $flags = 0) { static $expansionDepth = 0; if (is_string($root)) { return $root; } if (++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount()) { $this->parser->limitationWarn('node-count-exceeded', $this->parser->mPPNodeCount, $this->parser->mOptions->getMaxPPNodeCount()); return '<span class="error">Node-count limit exceeded</span>'; } if ($expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth()) { $this->parser->limitationWarn('expansion-depth-exceeded', $expansionDepth, $this->parser->mOptions->getMaxPPExpandDepth()); return '<span class="error">Expansion depth limit exceeded</span>'; } ++$expansionDepth; if ($expansionDepth > $this->parser->mHighestExpansionDepth) { $this->parser->mHighestExpansionDepth = $expansionDepth; } if ($root instanceof PPNode_DOM) { $root = $root->node; } if ($root instanceof DOMDocument) { $root = $root->documentElement; } $outStack = array('', ''); $iteratorStack = array(false, $root); $indexStack = array(0, 0); while (count($iteratorStack) > 1) { $level = count($outStack) - 1; $iteratorNode =& $iteratorStack[$level]; $out =& $outStack[$level]; $index =& $indexStack[$level]; if ($iteratorNode instanceof PPNode_DOM) { $iteratorNode = $iteratorNode->node; } if (is_array($iteratorNode)) { if ($index >= count($iteratorNode)) { // All done with this iterator $iteratorStack[$level] = false; $contextNode = false; } else { $contextNode = $iteratorNode[$index]; $index++; } } elseif ($iteratorNode instanceof DOMNodeList) { if ($index >= $iteratorNode->length) { // All done with this iterator $iteratorStack[$level] = false; $contextNode = false; } else { $contextNode = $iteratorNode->item($index); $index++; } } else { // Copy to $contextNode and then delete from iterator stack, // because this is not an iterator but we do have to execute it once $contextNode = $iteratorStack[$level]; $iteratorStack[$level] = false; } if ($contextNode instanceof PPNode_DOM) { $contextNode = $contextNode->node; } $newIterator = false; if ($contextNode === false) { // nothing to do } elseif (is_string($contextNode)) { $out .= $contextNode; } elseif (is_array($contextNode) || $contextNode instanceof DOMNodeList) { $newIterator = $contextNode; } elseif ($contextNode instanceof DOMNode) { if ($contextNode->nodeType == XML_TEXT_NODE) { $out .= $contextNode->nodeValue; } elseif ($contextNode->nodeName == 'template') { # Double-brace expansion $xpath = new DOMXPath($contextNode->ownerDocument); $titles = $xpath->query('title', $contextNode); $title = $titles->item(0); $parts = $xpath->query('part', $contextNode); if ($flags & PPFrame::NO_TEMPLATES) { $newIterator = $this->virtualBracketedImplode('{{', '|', '}}', $title, $parts); } else { $lineStart = $contextNode->getAttribute('lineStart'); $params = array('title' => new PPNode_DOM($title), 'parts' => new PPNode_DOM($parts), 'lineStart' => $lineStart); $ret = $this->parser->braceSubstitution($params, $this); if (isset($ret['object'])) { $newIterator = $ret['object']; } else { $out .= $ret['text']; } } } elseif ($contextNode->nodeName == 'tplarg') { # Triple-brace expansion $xpath = new DOMXPath($contextNode->ownerDocument); $titles = $xpath->query('title', $contextNode); $title = $titles->item(0); $parts = $xpath->query('part', $contextNode); if ($flags & PPFrame::NO_ARGS) { $newIterator = $this->virtualBracketedImplode('{{{', '|', '}}}', $title, $parts); } else { $params = array('title' => new PPNode_DOM($title), 'parts' => new PPNode_DOM($parts)); $ret = $this->parser->argSubstitution($params, $this); if (isset($ret['object'])) { $newIterator = $ret['object']; } else { $out .= $ret['text']; } } } elseif ($contextNode->nodeName == 'comment') { # HTML-style comment # Remove it in HTML, pre+remove and STRIP_COMMENTS modes # Not in RECOVER_COMMENTS mode (msgnw) though. if (($this->parser->ot['html'] || $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() || $flags & PPFrame::STRIP_COMMENTS) && !($flags & PPFrame::RECOVER_COMMENTS)) { $out .= ''; } elseif ($this->parser->ot['wiki'] && !($flags & PPFrame::RECOVER_COMMENTS)) { # Add a strip marker in PST mode so that pstPass2() can # run some old-fashioned regexes on the result. # Not in RECOVER_COMMENTS mode (extractSections) though. $out .= $this->parser->insertStripItem($contextNode->textContent); } else { # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove $out .= $contextNode->textContent; } } elseif ($contextNode->nodeName == 'ignore') { # Output suppression used by <includeonly> etc. # OT_WIKI will only respect <ignore> in substed templates. # The other output types respect it unless NO_IGNORE is set. # extractSections() sets NO_IGNORE and so never respects it. if (!isset($this->parent) && $this->parser->ot['wiki'] || $flags & PPFrame::NO_IGNORE) { $out .= $contextNode->textContent; } else { $out .= ''; } } elseif ($contextNode->nodeName == 'ext') { # Extension tag $xpath = new DOMXPath($contextNode->ownerDocument); $names = $xpath->query('name', $contextNode); $attrs = $xpath->query('attr', $contextNode); $inners = $xpath->query('inner', $contextNode); $closes = $xpath->query('close', $contextNode); if ($flags & PPFrame::NO_TAGS) { $s = '<' . $this->expand($names->item(0), $flags); if ($attrs->length > 0) { $s .= $this->expand($attrs->item(0), $flags); } if ($inners->length > 0) { $s .= '>' . $this->expand($inners->item(0), $flags); if ($closes->length > 0) { $s .= $this->expand($closes->item(0), $flags); } } else { $s .= '/>'; } $out .= $s; } else { $params = array('name' => new PPNode_DOM($names->item(0)), 'attr' => $attrs->length > 0 ? new PPNode_DOM($attrs->item(0)) : null, 'inner' => $inners->length > 0 ? new PPNode_DOM($inners->item(0)) : null, 'close' => $closes->length > 0 ? new PPNode_DOM($closes->item(0)) : null); $out .= $this->parser->extensionSubstitution($params, $this); } } elseif ($contextNode->nodeName == 'h') { # Heading $s = $this->expand($contextNode->childNodes, $flags); # Insert a heading marker only for <h> children of <root> # This is to stop extractSections from going over multiple tree levels if ($contextNode->parentNode->nodeName == 'root' && $this->parser->ot['html']) { # Insert heading index marker $headingIndex = $contextNode->getAttribute('i'); $titleText = $this->title->getPrefixedDBkey(); $this->parser->mHeadings[] = array($titleText, $headingIndex); $serial = count($this->parser->mHeadings) - 1; $marker = Parser::MARKER_PREFIX . "-h-{$serial}-" . Parser::MARKER_SUFFIX; $count = $contextNode->getAttribute('level'); $s = substr($s, 0, $count) . $marker . substr($s, $count); $this->parser->mStripState->addGeneral($marker, ''); } $out .= $s; } else { # Generic recursive expansion $newIterator = $contextNode->childNodes; } } else { throw new MWException(__METHOD__ . ': Invalid parameter type'); } if ($newIterator !== false) { if ($newIterator instanceof PPNode_DOM) { $newIterator = $newIterator->node; } $outStack[] = ''; $iteratorStack[] = $newIterator; $indexStack[] = 0; } elseif ($iteratorStack[$level] === false) { // Return accumulated value to parent // With tail recursion while ($iteratorStack[$level] === false && $level > 0) { $outStack[$level - 1] .= $out; array_pop($outStack); array_pop($iteratorStack); array_pop($indexStack); $level--; } } } --$expansionDepth; return $outStack[0]; }
/** * @throws MWException * @param string|PPNode $root * @param int $flags * @return string */ public function expand($root, $flags = 0) { static $expansionDepth = 0; if (is_string($root)) { return $root; } if (++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount()) { $this->parser->limitationWarn('node-count-exceeded', $this->parser->mPPNodeCount, $this->parser->mOptions->getMaxPPNodeCount()); return '<span class="error">Node-count limit exceeded</span>'; } if ($expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth()) { $this->parser->limitationWarn('expansion-depth-exceeded', $expansionDepth, $this->parser->mOptions->getMaxPPExpandDepth()); return '<span class="error">Expansion depth limit exceeded</span>'; } ++$expansionDepth; if ($expansionDepth > $this->parser->mHighestExpansionDepth) { $this->parser->mHighestExpansionDepth = $expansionDepth; } $outStack = array('', ''); $iteratorStack = array(false, $root); $indexStack = array(0, 0); while (count($iteratorStack) > 1) { $level = count($outStack) - 1; $iteratorNode =& $iteratorStack[$level]; $out =& $outStack[$level]; $index =& $indexStack[$level]; if (is_array($iteratorNode)) { if ($index >= count($iteratorNode)) { // All done with this iterator $iteratorStack[$level] = false; $contextNode = false; } else { $contextNode = $iteratorNode[$index]; $index++; } } elseif ($iteratorNode instanceof PPNode_Hash_Array) { if ($index >= $iteratorNode->getLength()) { // All done with this iterator $iteratorStack[$level] = false; $contextNode = false; } else { $contextNode = $iteratorNode->item($index); $index++; } } else { // Copy to $contextNode and then delete from iterator stack, // because this is not an iterator but we do have to execute it once $contextNode = $iteratorStack[$level]; $iteratorStack[$level] = false; } $newIterator = false; if ($contextNode === false) { // nothing to do } elseif (is_string($contextNode)) { $out .= $contextNode; } elseif (is_array($contextNode) || $contextNode instanceof PPNode_Hash_Array) { $newIterator = $contextNode; } elseif ($contextNode instanceof PPNode_Hash_Attr) { // No output } elseif ($contextNode instanceof PPNode_Hash_Text) { $out .= $contextNode->value; } elseif ($contextNode instanceof PPNode_Hash_Tree) { if ($contextNode->name == 'template') { # Double-brace expansion $bits = $contextNode->splitTemplate(); if ($flags & PPFrame::NO_TEMPLATES) { $newIterator = $this->virtualBracketedImplode('{{', '|', '}}', $bits['title'], $bits['parts']); } else { $ret = $this->parser->braceSubstitution($bits, $this); if (isset($ret['object'])) { $newIterator = $ret['object']; } else { $out .= $ret['text']; } } } elseif ($contextNode->name == 'tplarg') { # Triple-brace expansion $bits = $contextNode->splitTemplate(); if ($flags & PPFrame::NO_ARGS) { $newIterator = $this->virtualBracketedImplode('{{{', '|', '}}}', $bits['title'], $bits['parts']); } else { $ret = $this->parser->argSubstitution($bits, $this); if (isset($ret['object'])) { $newIterator = $ret['object']; } else { $out .= $ret['text']; } } } elseif ($contextNode->name == 'comment') { # HTML-style comment # Remove it in HTML, pre+remove and STRIP_COMMENTS modes # Not in RECOVER_COMMENTS mode (msgnw) though. if (($this->parser->ot['html'] || $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() || $flags & PPFrame::STRIP_COMMENTS) && !($flags & PPFrame::RECOVER_COMMENTS)) { $out .= ''; } elseif ($this->parser->ot['wiki'] && !($flags & PPFrame::RECOVER_COMMENTS)) { # Add a strip marker in PST mode so that pstPass2() can # run some old-fashioned regexes on the result. # Not in RECOVER_COMMENTS mode (extractSections) though. $out .= $this->parser->insertStripItem($contextNode->firstChild->value); } else { # Recover the literal comment in RECOVER_COMMENTS and pre+no-remove $out .= $contextNode->firstChild->value; } } elseif ($contextNode->name == 'ignore') { # Output suppression used by <includeonly> etc. # OT_WIKI will only respect <ignore> in substed templates. # The other output types respect it unless NO_IGNORE is set. # extractSections() sets NO_IGNORE and so never respects it. if (!isset($this->parent) && $this->parser->ot['wiki'] || $flags & PPFrame::NO_IGNORE) { $out .= $contextNode->firstChild->value; } else { // $out .= ''; } } elseif ($contextNode->name == 'ext') { # Extension tag $bits = $contextNode->splitExt() + array('attr' => null, 'inner' => null, 'close' => null); if ($flags & PPFrame::NO_TAGS) { $s = '<' . $bits['name']->firstChild->value; if ($bits['attr']) { $s .= $bits['attr']->firstChild->value; } if ($bits['inner']) { $s .= '>' . $bits['inner']->firstChild->value; if ($bits['close']) { $s .= $bits['close']->firstChild->value; } } else { $s .= '/>'; } $out .= $s; } else { $out .= $this->parser->extensionSubstitution($bits, $this); } } elseif ($contextNode->name == 'h') { # Heading if ($this->parser->ot['html']) { # Expand immediately and insert heading index marker $s = ''; for ($node = $contextNode->firstChild; $node; $node = $node->nextSibling) { $s .= $this->expand($node, $flags); } $bits = $contextNode->splitHeading(); $titleText = $this->title->getPrefixedDBkey(); $this->parser->mHeadings[] = array($titleText, $bits['i']); $serial = count($this->parser->mHeadings) - 1; $marker = Parser::MARKER_PREFIX . "-h-{$serial}-" . Parser::MARKER_SUFFIX; $s = substr($s, 0, $bits['level']) . $marker . substr($s, $bits['level']); $this->parser->mStripState->addGeneral($marker, ''); $out .= $s; } else { # Expand in virtual stack $newIterator = $contextNode->getChildren(); } } else { # Generic recursive expansion $newIterator = $contextNode->getChildren(); } } else { throw new MWException(__METHOD__ . ': Invalid parameter type'); } if ($newIterator !== false) { $outStack[] = ''; $iteratorStack[] = $newIterator; $indexStack[] = 0; } elseif ($iteratorStack[$level] === false) { // Return accumulated value to parent // With tail recursion while ($iteratorStack[$level] === false && $level > 0) { $outStack[$level - 1] .= $out; array_pop($outStack); array_pop($iteratorStack); array_pop($indexStack); $level--; } } } --$expansionDepth; return $outStack[0]; }
/** * This expects to find: * {{#topic:Text Name}} * * @param Parser $parser * @param string $param1 Full text name of topic, must be converted to wiki topic name. * @return array * * TODO: Much of this function duplicates code above in efGetTitleFromMarkup(), can we DRY? * There really shouldn't be any real code in this file, just calls to class methods... */ function efTopicParserFunction_Render(&$parser, $param1 = '') { global $wgArticlePath, $wgTitle, $action; if (PonyDocsExtension::isSpeedProcessingEnabled()) { return TRUE; } /** * We ignore this parser function if not in a TOC management page. */ if (!preg_match('/' . PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':(.*):(.*)TOC(.*)/i', $wgTitle->__toString(), $matches)) { return FALSE; } $manualShortName = $matches[2]; $productShortName = $matches[1]; PonyDocsWiki::getInstance($productShortName); /** * Get the earliest tagged version of this TOC page and append it to the wiki page? * Ensure the manual is valid then use PonyDocsManual::getManualByShortName(). * Next attempt to get the version tags for this page -- which may be NONE -- * and from this determine the "earliest" version to which this page applies. * * TODO: This comment is duplicated above in efGetTitleFromMarkup, can we DRY? */ if (!PonyDocsProductManual::IsManual($productShortName, $manualShortName)) { return FALSE; } $pManual = PonyDocsProductManual::GetManualByShortName($productShortName, $manualShortName); $pTopic = new PonyDocsTopic(new Article($wgTitle)); /** * @FIXME: If TOC page is NOT tagged with any versions we cannot create the pages/links to the * topics, right? */ $manVersionList = $pTopic->getProductVersions(); if (!sizeof($manVersionList)) { return $parser->insertStripItem($param1, $parser->mStripState); } $earliestVersion = PonyDocsProductVersion::findEarliest($productShortName, $manVersionList); /** * Clean up the full text name into a wiki-form. This means remove spaces, #, ?, and a few other * characters which are not valid or wanted. It's not important HOW this is done as long as it is * consistent. */ $wikiTopic = preg_replace('/([^' . str_replace(' ', '', Title::legalChars()) . '])/', '', $param1); $wikiPath = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $productShortName . ':' . $manualShortName . ':' . $wikiTopic; $dbr = wfGetDB(DB_SLAVE); /** * Now look in the database for any instance of this topic name PLUS :<version>. * We need to look in categorylinks for it to find a record with a cl_to (version tag) * which is equal to the set of the versions for this TOC page. * For instance, if the TOC page was for versions 1.0 and 1.1 and our topic was 'How To Foo' * we need to find any cl_sortkey which is 'HowToFoo:%' and has a cl_to equal to 1.0 or 1.1. * There should only be 0 or 1, so we ignore anything beyond 1. * If found, we use THAT cl_sortkey as the link; * if NOT found we create a new topic, the name being the compressed topic name plus the earliest TOC version * ($earliestVersion->getName()). * We then need to ACTUALLY create it in the database, tag it with all the versions the TOC mgmt page is tagged with, * and set the H1 to the text inside the parser function. * * @fixme: Can we test if $action=save here so we don't do this on every page view? */ $versionIn = array(); foreach ($manVersionList as $pV) { $versionIn[] = $productShortName . ':' . $pV->getVersionName(); } $res = $dbr->select(array('categorylinks', 'page'), 'page_title', array('cl_from = page_id', 'page_namespace = "' . NS_PONYDOCS . '"', "cl_to IN ('V:" . implode("','V:", $versionIn) . "')", 'cl_type = "page"', "cl_sortkey LIKE '" . $dbr->strencode(strtoupper($productShortName . ':' . $manualShortName . ':' . $wikiTopic)) . ":%'"), __METHOD__); $topicName = ''; if (!$res->numRows()) { /** * No match -- so this is a "new" topic. Set name. */ $topicName = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ':' . $productShortName . ':' . $manualShortName . ':' . $wikiTopic . ':' . $earliestVersion->getVersionName(); } else { $row = $dbr->fetchObject($res); $topicName = PONYDOCS_DOCUMENTATION_NAMESPACE_NAME . ":{$row->page_title}"; } $output = '<a href="' . wfUrlencode(str_replace('$1', $topicName, $wgArticlePath)) . '">' . $param1 . '</a>'; return $parser->insertStripItem($output, $parser->mStripState); }
/** * Renders bs:whoisonline:popup output. * @param Parser $oParser MediaWiki parser object. * @param string $sLinkTitle Label of the link that is the anchor of the flyout * @return array Rendered HTML and flags. Used by magic word function hook as well as by onUsersLinkTag. */ public function onUsersLink($oParser, $sLinkTitle = '') { $oParser->disableCache(); wfProfileIn('BS::' . __METHOD__); $sLinkTitle = BsCore::sanitize($sLinkTitle, '', BsPARAMTYPE::STRING); if (empty($sLinkTitle)) { $sLinkTitle = wfMessage('bs-whoisonline-widget-title')->plain(); } $oWhoIsOnlineTagView = new ViewWhoIsOnlineTag(); $oWhoIsOnlineTagView->setOption('title', $sLinkTitle); $oWhoIsOnlineTagView->setPortlet($this->getPortlet()); $sOut = $oWhoIsOnlineTagView->execute(); wfProfileOut('BS::' . __METHOD__); return $oParser->insertStripItem($sOut, $oParser->mStripState); }
/** * @throws MWException * @param $root * @param $flags int * @return string */ function expand($root, $flags = 0) { static $expansionDepth = 0; if (is_string($root)) { return $root; } if (++$this->parser->mPPNodeCount > $this->parser->mOptions->getMaxPPNodeCount()) { return '<span class="error">Node-count limit exceeded</span>'; } if ($expansionDepth > $this->parser->mOptions->getMaxPPExpandDepth()) { return '<span class="error">Expansion depth limit exceeded</span>'; } wfProfileIn(__METHOD__); ++$expansionDepth; if ($root instanceof PPNode_DOM) { $root = $root->node; } if ($root instanceof DOMDocument) { $root = $root->documentElement; } $outStack = array('', ''); $iteratorStack = array(false, $root); $indexStack = array(0, 0); $RTEext_1 = false; $RTEext_2 = false; while (count($iteratorStack) > 1) { if ($RTEext_1) { $RTEext_1 = false; $RTEext_2 = true; } $level = count($outStack) - 1; $iteratorNode =& $iteratorStack[$level]; $out =& $outStack[$level]; $index =& $indexStack[$level]; if ($iteratorNode instanceof PPNode_DOM) { $iteratorNode = $iteratorNode->node; } if (is_array($iteratorNode)) { if ($index >= count($iteratorNode)) { // All done with this iterator $iteratorStack[$level] = false; $contextNode = false; } else { $contextNode = $iteratorNode[$index]; $index++; } } elseif ($iteratorNode instanceof DOMNodeList) { if ($index >= $iteratorNode->length) { // All done with this iterator $iteratorStack[$level] = false; $contextNode = false; } else { $contextNode = $iteratorNode->item($index); $index++; } } else { // Copy to $contextNode and then delete from iterator stack, // because this is not an iterator but we do have to execute it once $contextNode = $iteratorStack[$level]; $iteratorStack[$level] = false; } if ($contextNode instanceof PPNode_DOM) { $contextNode = $contextNode->node; } $newIterator = false; if ($contextNode === false) { // nothing to do } elseif (is_string($contextNode)) { $out .= $contextNode; } elseif (is_array($contextNode) || $contextNode instanceof DOMNodeList) { $newIterator = $contextNode; } elseif ($contextNode instanceof DOMNode) { if ($contextNode->nodeType == XML_TEXT_NODE) { # RTE (Rich Text Editor) - begin global $wgRTEParserEnabled; if (!empty($wgRTEParserEnabled)) { if ($RTEext_2) { if (strpos($contextNode->nodeValue, 'table') !== false) { RTE::$edgeCases[] = 'COMPLEX.11'; } } } # RTE - end $out .= $contextNode->nodeValue; } elseif ($contextNode->nodeName == 'template') { # Double-brace expansion $xpath = new DOMXPath($contextNode->ownerDocument); $titles = $xpath->query('title', $contextNode); $title = $titles->item(0); $parts = $xpath->query('part', $contextNode); # RTE (Rich Text Editor) - begin # @author: Inez Korczyński global $wgRTEParserEnabled; if (!empty($wgRTEParserEnabled)) { $dataIdx = RTEData::put('placeholder', array('type' => 'double-brackets', 'wikitextIdx' => $contextNode->getAttribute('_rte_wikitextidx'), 'lineStart' => $contextNode->getAttribute('lineStart'), 'title' => $title->textContent)); $out .= RTEMarker::generate(RTEMarker::PLACEHOLDER, $dataIdx); } else { if ($flags & PPFrame::NO_TEMPLATES) { $newIterator = $this->virtualBracketedImplode('{{', '|', '}}', $title, $parts); } else { $lineStart = $contextNode->getAttribute('lineStart'); $params = array('title' => new PPNode_DOM($title), 'parts' => new PPNode_DOM($parts), 'lineStart' => $lineStart); $ret = $this->parser->braceSubstitution($params, $this); if (isset($ret['object'])) { $newIterator = $ret['object']; } else { $out .= $ret['text']; } } } # RTE - end } elseif ($contextNode->nodeName == 'tplarg') { # Triple-brace expansion $xpath = new DOMXPath($contextNode->ownerDocument); $titles = $xpath->query('title', $contextNode); $title = $titles->item(0); $parts = $xpath->query('part', $contextNode); # RTE (Rich Text Editor) - begin # @author: Wladyslaw Bodzek global $wgRTEParserEnabled; if (!empty($wgRTEParserEnabled)) { //var_dump($contextNode->getAttribute('_rte_wikitextidx')); $dataIdx = RTEData::put('placeholder', array('type' => 'tplarg', 'wikitextIdx' => $contextNode->getAttribute('_rte_wikitextidx'), 'lineStart' => $contextNode->getAttribute('lineStart'), 'title' => $title->textContent)); $out .= RTEMarker::generate(RTEMarker::PLACEHOLDER, $dataIdx); } else { if ($flags & PPFrame::NO_ARGS) { $newIterator = $this->virtualBracketedImplode('{{{', '|', '}}}', $title, $parts); } else { $params = array('title' => new PPNode_DOM($title), 'parts' => new PPNode_DOM($parts)); $ret = $this->parser->argSubstitution($params, $this); if (isset($ret['object'])) { $newIterator = $ret['object']; } else { $out .= $ret['text']; } } } # RTE - end } elseif ($contextNode->nodeName == 'comment') { # HTML-style comment # Remove it in HTML, pre+remove and STRIP_COMMENTS modes if ($this->parser->ot['html'] || $this->parser->ot['pre'] && $this->parser->mOptions->getRemoveComments() || $flags & PPFrame::STRIP_COMMENTS) { # RTE (Rich Text Editor) - begin # @author: Inez Korczyński global $wgRTEParserEnabled; if (!empty($wgRTEParserEnabled)) { if (strlen($out) === 0 || substr($out, -1) == "\n") { if (substr($contextNode->textContent, -1) == "\n") { $add = "\n"; $text = substr($contextNode->textContent, 0, -1); } else { $add = ""; $text = $contextNode->textContent; } $dataIdx = RTEData::put('placeholder', array('type' => 'comment', 'wikitext' => $text)); $out .= RTEMarker::generate(RTEMarker::PLACEHOLDER, $dataIdx) . $add; } else { RTE::$edgeCases[] = 'COMMENT'; $out .= ''; } } else { $out .= ''; } # RTE - end } elseif ($this->parser->ot['wiki'] && !($flags & PPFrame::RECOVER_COMMENTS)) { $out .= $this->parser->insertStripItem($contextNode->textContent); } else { $out .= $contextNode->textContent; } } elseif ($contextNode->nodeName == 'ignore') { # Output suppression used by <includeonly> etc. # OT_WIKI will only respect <ignore> in substed templates. # The other output types respect it unless NO_IGNORE is set. # extractSections() sets NO_IGNORE and so never respects it. if (!isset($this->parent) && $this->parser->ot['wiki'] || $flags & PPFrame::NO_IGNORE) { $out .= $contextNode->textContent; } else { $out .= ''; } } elseif ($contextNode->nodeName == 'ext') { # Extension tag $xpath = new DOMXPath($contextNode->ownerDocument); $names = $xpath->query('name', $contextNode); $attrs = $xpath->query('attr', $contextNode); $inners = $xpath->query('inner', $contextNode); $closes = $xpath->query('close', $contextNode); $params = array('name' => new PPNode_DOM($names->item(0)), 'attr' => $attrs->length > 0 ? new PPNode_DOM($attrs->item(0)) : null, 'inner' => $inners->length > 0 ? new PPNode_DOM($inners->item(0)) : null, 'close' => $closes->length > 0 ? new PPNode_DOM($closes->item(0)) : null); $out .= $this->parser->extensionSubstitution($params, $this); $RTEext_1 = true; } elseif ($contextNode->nodeName == 'h') { # Heading $s = $this->expand($contextNode->childNodes, $flags); # Insert a heading marker only for <h> children of <root> # This is to stop extractSections from going over multiple tree levels if ($contextNode->parentNode->nodeName == 'root' && $this->parser->ot['html']) { # Insert heading index marker $headingIndex = $contextNode->getAttribute('i'); $titleText = $this->title->getPrefixedDBkey(); $this->parser->mHeadings[] = array($titleText, $headingIndex); $serial = count($this->parser->mHeadings) - 1; $marker = "{$this->parser->mUniqPrefix}-h-{$serial}-" . Parser::MARKER_SUFFIX; $count = $contextNode->getAttribute('level'); $s = substr($s, 0, $count) . $marker . substr($s, $count); $this->parser->mStripState->addGeneral($marker, ''); } $out .= $s; } else { # Generic recursive expansion $newIterator = $contextNode->childNodes; } } else { wfProfileOut(__METHOD__); throw new MWException(__METHOD__ . ': Invalid parameter type'); } if ($newIterator !== false) { if ($newIterator instanceof PPNode_DOM) { $newIterator = $newIterator->node; } $outStack[] = ''; $iteratorStack[] = $newIterator; $indexStack[] = 0; } elseif ($iteratorStack[$level] === false) { // Return accumulated value to parent // With tail recursion while ($iteratorStack[$level] === false && $level > 0) { $outStack[$level - 1] .= $out; array_pop($outStack); array_pop($iteratorStack); array_pop($indexStack); $level--; } } $RTEext_2 = false; } --$expansionDepth; wfProfileOut(__METHOD__); return $outStack[0]; }
/** * Parser function hook */ public static function translationDialogMagicWord( Parser $parser, $title = '', $linktext = '' ) { $title = Title::newFromText( $title ); if ( !$title ) return ''; $handle = new MessageHandle( $title ); if ( !$handle->isValid() ) return ''; $group = $handle->getGroup(); $callParams = array( $title->getPrefixedText(), $group->getId() ); $call = Xml::encodeJsCall( 'mw.translate.openDialog', $callParams ); $js = <<<JAVASCRIPT mw.loader.using( 'ext.translate.quickedit', function() { $call; } ); return false; JAVASCRIPT; $a = array( 'href' => $title->getFullUrl( array( 'action' => 'edit' ) ), 'onclick' => $js, ); if ( $linktext === '' ) { $linktext = wfMessage( 'translate-edit-jsopen' )->text(); } $output = Html::element( 'a', $a, $linktext ); return $parser->insertStripItem( $output, $parser->mStripState ); }
/** * Renrder the spark div. * * @since 0.1 * * @param Parser $parser * * @return string */ public function render(Parser $parser, $frame) { global $wgVersion; global $wgOut; global $egSparkScriptPath; global $wgResourceModules; // What is loaded already? static $loadedJsses = array(); wfDebugLog('myextension', 'Parameters alright? ' . print_r($this->parameters, true)); if (array_key_exists(egSparkQuery, $this->parameters)) { $query = htmlspecialchars($this->parameters[egSparkQuery]); // Before that, shall we allow internal parse, at least for the query? // We replace variables, templates etc. $query = $parser->replaceVariables($query, $frame); //$query = $parser->recursiveTagParse( $query ); // Replace special characters $query = str_replace(array('<', '>'), array('<', '>'), $query); unset($this->parameters[egSparkQuery]); // Depending on the format, we possibly need to add modules if (array_key_exists(egSparkFormat, $this->parameters)) { $format = htmlspecialchars($this->parameters[egSparkFormat]); // Remove everything before "spark.XXX" $format = substr($format, strpos($format, "spark.")); // Remove .js at the end $format = str_replace(array('.js'), array(''), $format); $module = 'ext.' . $format; // for older versions of MW, different if (version_compare($wgVersion, '1.17', '<')) { if (isset($wgResourceModules) && array_key_exists($module, $wgResourceModules)) { // only if not already loaded if (!isset($loadedJsses[$module])) { // scripts foreach ($wgResourceModules[$module]['scripts'] as $script) { $wgOut->addScript('<script src="' . $egSparkScriptPath . "/" . $script . '" type="text/javascript"></script>'); wfDebugLog('spark', "AddScript:" . ' <script src="' . $egSparkScriptPath . "/" . $script . '" type="text/javascript"></script>'); } // css foreach ($wgResourceModules[$module]['styles'] as $style) { $wgOut->addScript('<link rel="stylesheet" href="' . $egSparkScriptPath . "/" . $style . '" type="text/css" />'); wfDebugLog('spark', "AddLink:" . ' <link rel="stylesheet" href="' . $egSparkScriptPath . "/" . $style . '" type="text/css" />'); } $loadedJsses[$module] = true; } } } else { // $wgResourceModules might not exist if (isset($wgResourceModules) && array_key_exists($module, $wgResourceModules)) { // TODO: Do we need to check, whether module has been added already? $parser->getOutput()->addModules($module); } } } $html = '<div class="spark" data-spark-query="' . $query . '" ' . Html::expandAttributes($this->parameters) . ' >' . (is_null($this->contents) ? '' : htmlspecialchars($this->contents)) . '</div>'; // In MW 1.17 there seems to be the problem that ? after an empty space is replaced by a non-breaking space ( ) Therefore we remove all spaces before ? which should still make the SPARQL query work $html = preg_replace('/[ \\t]+(\\?)/', '$1', $html); // for older versions of MW, different if (version_compare($wgVersion, '1.17', '<')) { $parser->disableCache(); return $html; } else { return array($parser->insertStripItem($html, $parser->mStripState), 'noparse' => true, 'isHTML' => true); } } else { return Html::element('i', array(), wfMsg('spark-missing-query')); } }