/** * Render <place> tag * * @param string $content tag content (will be ignored) * @param array $attributes tag attributes * @param Parser $parser MW parser instance * @param PPFrame $frame parent frame with the context * @return string HTML output of the tag */ public static function renderPlaceTag($content, array $attributes, Parser $parser, PPFrame $frame) { wfProfileIn(__METHOD__); // wrap data in a model object $placeModel = PlaceModel::newFromAttributes($attributes); // are we rendering for RTE? $inRTE = !empty(F::app()->wg->RTEParserEnabled); if ($inRTE) { $wikitext = RTEData::get('wikitext', self::$lastWikitextId); $data = array('wikitext' => $wikitext, 'placeholder' => 1); $rteData = RTEData::convertDataToAttributes($data); } else { $rteData = false; } // render parser hook $html = F::app()->sendRequest('Places', 'placeFromModel', array('model' => $placeModel, 'rteData' => $rteData))->toString(); // add JS snippets code if (!$inRTE) { $html .= self::getJSSnippet(); } // add model to be stored in database (new PlacesHooks())->setModelToSave($placeModel); $html = self::cleanHTML($html); wfProfileOut(__METHOD__); return $html; }
public static function replaceIdxByData($var) { $data = RTEData::get('data', intval($var[1])); if (isset($data['type'])) { if (isset($data['wikitextIdx'])) { $data['wikitext'] = RTEData::get('wikitext', $data['wikitextIdx']); // macbre: correctly handle and unmark entities inside links wikitext (RT #38844) $data['wikitext'] = htmlspecialchars_decode($data['wikitext']); $data['wikitext'] = RTEParser::unmarkEntities($data['wikitext']); unset($data['wikitextIdx']); if (strpos($data['wikitext'], '_rte_wikitextidx') !== false) { RTE::$edgeCases[] = 'COMPLEX.01'; } else { if (strpos($data['wikitext'], '_rte_dataidx') !== false) { RTE::$edgeCases[] = 'COMPLEX.02'; } else { if ($data['type'] == 'double-brackets') { if (strrpos($data['wikitext'], '{{') !== 0 && strpos($data['wikitext'], '{{') !== strlen($data['wikitext']) - 2) { RTE::$edgeCases[] = 'COMPLEX.03'; } } else { if (strpos($data['wikitext'], "") !== false) { RTE::$edgeCases[] = 'COMPLEX.07'; } } } } } } return self::convertDataToAttributes($data); }
/** * Render HTML for given placeholder * * @author: Macbre */ public static function renderPlaceholder($label, $data) { // this is placeholder $data['placeholder'] = 1; // store data $dataIdx = RTEData::put('data', $data); // render placeholder global $wgBlankImgUrl; return Xml::element('img', array('_rte_dataidx' => sprintf('%04d', $dataIdx), 'class' => "placeholder placeholder-{$data['type']}", 'src' => $wgBlankImgUrl, 'type' => $data['type'])); }
public function renderTag($input, $params, Parser $parser, PPFrame $frame) { //$this->frame = $frame; global $wgRTEParserEnabled, $wgHTTPProxy, $wgFogbugzAPIConfig, $wgCaptchaDirectory, $wgCaptchaDirectoryLevels, $wgStylePath; if (!isset($params['id'])) { return ''; } $output = Xml::openElement('span', array('class' => 'fogbugz_tck', 'data-id' => $parser->recursiveTagParse($params['id'], $frame))); //$output .= $input; $output .= Xml::openElement('img', array('src' => $wgStylePath . '/common/images/ajax.gif')); $output .= Xml::closeElement('span'); $data = array('wikitext' => RTEData::get('wikitext', self::$mWikitextIdx), 'placeholder' => 1); //$dataIdx = RTEData::put('data', $data); //$output = RTEData::addIdxToTag($dataIdx, $output); $output .= F::build('JSSnippets')->addToStack(array('/extensions/wikia/FogbugzTag/js/FogbugzTag.js')); return $output; }
/** * Return the text to be used for a given extension tag. * This is the ghost of strip(). * * @param $params Associative array of parameters: * name PPNode for the tag name * attr PPNode for unparsed text where tag attributes are thought to be * attributes Optional associative array of parsed attributes * inner Contents of extension element * noClose Original text did not have a close tag * @param $frame PPFrame * * @return string */ function extensionSubstitution($params, $frame) { $name = $frame->expand($params['name']); $attrText = !isset($params['attr']) ? null : $frame->expand($params['attr']); $content = !isset($params['inner']) ? null : $frame->expand($params['inner']); # RTE (Rich Text Editor) - begin # @author: Inez Korczyński global $wgRTEParserEnabled; if (!empty($wgRTEParserEnabled)) { $wikitextIdx = RTEMarker::getDataIdx(RTEMarker::EXT_WIKITEXT, $content); # Allow parser extensions to generate their own placeholders (instead of default one from RTE) # @author: Macbre if (wfRunHooks('RTEUseDefaultPlaceholder', array($name, $params, $frame, $wikitextIdx))) { if ($wikitextIdx !== null) { $dataIdx = RTEData::put('placeholder', array('type' => 'ext', 'wikitextIdx' => $wikitextIdx)); return RTEMarker::generate(RTEMarker::PLACEHOLDER, $dataIdx); } } else { RTE::log(__METHOD__, "skipped default placeholder for <{$name}>"); // restore value of $content $content = RTEData::get('wikitext', $wikitextIdx); // keep inner content of tag $content = preg_replace('#^<[^>]+>(.*)<[^>]+>$#s', '\\1', $content); } } # RTE - end $marker = "{$this->mUniqPrefix}-{$name}-" . sprintf('%08X', $this->mMarkerIndex++) . self::MARKER_SUFFIX; $isFunctionTag = isset($this->mFunctionTagHooks[strtolower($name)]) && ($this->ot['html'] || $this->ot['pre']); if ($isFunctionTag) { $markerType = 'none'; } else { $markerType = 'general'; } if ($this->ot['html'] || $isFunctionTag) { $name = strtolower($name); # PLB - begin # @author: Tomasz Odrobny $this->mCurrentTagName = $name; # PLB - end $attributes = Sanitizer::decodeTagAttributes($attrText); if (isset($params['attributes'])) { $attributes = $attributes + $params['attributes']; } if (isset($this->mTagHooks[$name])) { # Workaround for PHP bug 35229 and similar if (!is_callable($this->mTagHooks[$name])) { throw new MWException("Tag hook for {$name} is not callable\n"); } wfRunHooks('ParserTagHooksBeforeInvoke', [$name, $marker, $content, $attributes, $this, $frame]); $output = call_user_func_array($this->mTagHooks[$name], array($content, $attributes, $this, $frame)); } elseif (isset($this->mFunctionTagHooks[$name])) { list($callback, $flags) = $this->mFunctionTagHooks[$name]; if (!is_callable($callback)) { throw new MWException("Tag hook for {$name} is not callable\n"); } $output = call_user_func_array($callback, array(&$this, $frame, $content, $attributes)); } else { $output = '<span class="error">Invalid tag extension name: ' . htmlspecialchars($name) . '</span>'; } if (is_array($output)) { # Extract flags to local scope (to override $markerType) $flags = $output; $output = $flags[0]; unset($flags[0]); extract($flags); } } else { if (is_null($attrText)) { $attrText = ''; } if (isset($params['attributes'])) { foreach ($params['attributes'] as $attrName => $attrValue) { $attrText .= ' ' . htmlspecialchars($attrName) . '="' . htmlspecialchars($attrValue) . '"'; } } if ($content === null) { $output = "<{$name}{$attrText}/>"; } else { $close = is_null($params['close']) ? '' : $frame->expand($params['close']); $output = "<{$name}{$attrText}>{$content}{$close}"; } } if ($markerType === 'none') { return $output; } elseif ($markerType === 'nowiki') { $this->mStripState->addNoWiki($marker, $output); } elseif ($markerType === 'general') { $this->mStripState->addGeneral($marker, $output); } else { throw new MWException(__METHOD__ . ': invalid marker type'); } return $marker; }
/** * Render gallery placeholder for RTE * * @param $gallery WikiaPhotoGallery * @param $width Integer * $param $height Integer */ public static function renderGalleryPlaceholder($gallery, $width, $height) { wfProfileIn(__METHOD__); $data = $gallery->getData(); $class = 'media-placeholder image-gallery'; if ($data['type'] == WikiaPhotoGallery::WIKIA_PHOTO_SLIDESHOW) { $class .= ' image-slideshow'; } // support "position" attribute (gallery alignment) switch ($gallery->getParam('position')) { case 'left': $class .= ' alignLeft'; break; case 'center': $class .= ' alignCenter'; break; case 'right': $class .= ' alignRight'; break; } if ($data['type'] == WikiaPhotoGallery::WIKIA_PHOTO_SLIDER) { $class .= ' image-gallery-slider'; } global $wgBlankImgUrl; $attribs = array('src' => $wgBlankImgUrl, 'class' => $class, 'type' => 'image-gallery', 'height' => $height, 'width' => $width); // render image for media placeholder $ret = Xml::element('img', $attribs); // store wikitext $data['wikitext'] = RTEData::get('wikitext', self::$mWikitextIdx); // store data and mark HTML $dataIdx = RTEData::put('data', $data); $ret = RTEData::addIdxToTag($dataIdx, $ret); wfProfileOut(__METHOD__); return $ret; }
/** * Returns placeholder for broken image link * * This one is called directly by RTEParser::makeImage() * */ public static function makeBrokenImageLinkObj($title, $html = '', $query = '', $trail = '', $prefix = '', $time = false, $wikitextIdx = null) { wfProfileIn(__METHOD__); if (!$title instanceof Title) { throw new \Exception('$title is not Title class instance'); } // try to resolve internal links in broken image caption (RT #90616) $wikitext = RTEData::get('wikitext', $wikitextIdx); if (RTEData::resolveLinksInMediaCaption($wikitext)) { // update wikitext data $wikitextIdx = RTEData::put('wikitext', $wikitext); } $ret = RTEMarker::generate(RTEMarker::PLACEHOLDER, RTEData::put('placeholder', array('type' => 'broken-image', 'wikitextIdx' => $wikitextIdx, 'title' => $title->getDBkey()))); wfProfileOut(__METHOD__); return $ret; }
/** * Return XML object for parser when RTE enabled * called from Parser::replaceInternalLinks2 * * @param $poll WikiaPoll * @param $nt Title * @param $RTE_wikitextIdx */ public static function generateRTE($poll, $nt, $RTE_wikitextIdx) { global $wgBlankImgUrl; $data = array(); $data['type'] = 'poll'; $data['pollId'] = $nt->getArticleId(); $data['wikitext'] = RTEData::get('wikitext', $RTE_wikitextIdx); $pollData = $poll->getData(); if (isset($pollData['question'])) { $data['question'] = $pollData['question']; } if (isset($pollData['answers'])) { foreach ($pollData["answers"] as $answer) { $data['answers'][] = $answer['text']; } } // store data and mark HTML $dataIdx = RTEData::put('data', $data); // render poll placeholder $tag = Xml::element('img', array('_rte_dataidx' => sprintf('%04d', $dataIdx), 'class' => "media-placeholder placeholder-poll", 'src' => $wgBlankImgUrl, 'type' => 'poll')); return RTEData::addIdxToTag($dataIdx, $tag); }
/** * @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]; }
public static function doDoubleUnderscoreReplace($matches) { wfProfileIn(__METHOD__); $dataIdx = RTEData::put('placeholder', array('type' => 'double-underscore', 'wikitext' => $matches[0])); $ret = RTEMarker::generate(RTEMarker::PLACEHOLDER, $dataIdx); wfProfileOut(__METHOD__); return $ret; }
/** * Render HTML for given placeholder * * @author: Macbre */ public static function renderPlaceholder($label, $data) { // this is placeholder $data['placeholder'] = 1; // Special case for WikiaPoll placeholder // If we do more of these, refactor if (defined("NS_WIKIA_POLL")) { global $wgContLang; $pollNamespace = $wgContLang->getNsText(NS_WIKIA_POLL); // Check for both canonical Poll and localized version of Poll if (isset($data['title']) && (stripos($data['title'], 'Poll') === 0 || stripos($data['title'], $pollNamespace) === 0)) { $data['type'] = 'poll'; $cssClass = "media-placeholder placeholder-poll"; $title = Title::newFromText($data['title'], NS_WIKIA_POLL); if ($title->exists()) { $data['pollId'] = $title->getArticleId(); $poll = WikiaPoll::newFromTitle($title); $pollData = $poll->getData(); $data['question'] = $pollData['question']; if (isset($pollData['answers'])) { foreach ($pollData["answers"] as $answer) { $data['answers'][] = $answer['text']; } } } } } // store data $dataIdx = RTEData::put('data', $data); // render placeholder global $wgBlankImgUrl; return Xml::element('img', array('_rte_dataidx' => sprintf('%04d', $dataIdx), 'class' => "placeholder placeholder-{$data['type']}", 'src' => $wgBlankImgUrl, 'type' => $data['type'])); }