/** * Make a nested call to the API to request watchlist items in the last $hours. * Wrap the result as an RSS/Atom feed. */ public function execute() { $config = $this->getConfig(); $feedClasses = $config->get('FeedClasses'); try { $params = $this->extractRequestParams(); if (!$config->get('Feed')) { $this->dieUsage('Syndication feeds are not available', 'feed-unavailable'); } if (!isset($feedClasses[$params['feedformat']])) { $this->dieUsage('Invalid subscription feed type', 'feed-invalid'); } // limit to the number of hours going from now back $endTime = wfTimestamp(TS_MW, time() - intval($params['hours'] * 60 * 60)); // Prepare parameters for nested request $fauxReqArr = array('action' => 'query', 'meta' => 'siteinfo', 'siprop' => 'general', 'list' => 'watchlist', 'wlprop' => 'title|user|comment|timestamp|ids', 'wldir' => 'older', 'wlend' => $endTime, 'wllimit' => min(50, $this->getConfig()->get('FeedLimit'))); if ($params['wlowner'] !== null) { $fauxReqArr['wlowner'] = $params['wlowner']; } if ($params['wltoken'] !== null) { $fauxReqArr['wltoken'] = $params['wltoken']; } if ($params['wlexcludeuser'] !== null) { $fauxReqArr['wlexcludeuser'] = $params['wlexcludeuser']; } if ($params['wlshow'] !== null) { $fauxReqArr['wlshow'] = $params['wlshow']; } if ($params['wltype'] !== null) { $fauxReqArr['wltype'] = $params['wltype']; } // Support linking directly to sections when possible // (possible only if section name is present in comment) if ($params['linktosections']) { $this->linkToSections = true; } // Check for 'allrev' parameter, and if found, show all revisions to each page on wl. if ($params['allrev']) { $fauxReqArr['wlallrev'] = ''; } // Create the request $fauxReq = new FauxRequest($fauxReqArr); // Execute $module = new ApiMain($fauxReq); $module->execute(); $data = $module->getResult()->getResultData(array('query', 'watchlist')); $feedItems = array(); foreach ((array) $data as $key => $info) { if (ApiResult::isMetadataKey($key)) { continue; } $feedItem = $this->createFeedItem($info); if ($feedItem) { $feedItems[] = $feedItem; } } $msg = wfMessage('watchlist')->inContentLanguage()->text(); $feedTitle = $this->getConfig()->get('Sitename') . ' - ' . $msg . ' [' . $this->getConfig()->get('LanguageCode') . ']'; $feedUrl = SpecialPage::getTitleFor('Watchlist')->getFullURL(); $feed = new $feedClasses[$params['feedformat']]($feedTitle, htmlspecialchars($msg), $feedUrl); ApiFormatFeedWrapper::setResult($this->getResult(), $feed, $feedItems); } catch (Exception $e) { // Error results should not be cached $this->getMain()->setCacheMaxAge(0); // @todo FIXME: Localise brackets $feedTitle = $this->getConfig()->get('Sitename') . ' - Error - ' . wfMessage('watchlist')->inContentLanguage()->text() . ' [' . $this->getConfig()->get('LanguageCode') . ']'; $feedUrl = SpecialPage::getTitleFor('Watchlist')->getFullURL(); $feedFormat = isset($params['feedformat']) ? $params['feedformat'] : 'rss'; $msg = wfMessage('watchlist')->inContentLanguage()->escaped(); $feed = new $feedClasses[$feedFormat]($feedTitle, $msg, $feedUrl); if ($e instanceof UsageException) { $errorCode = $e->getCodeString(); } else { // Something is seriously wrong $errorCode = 'internal_api_error'; } $errorText = $e->getMessage(); $feedItems[] = new FeedItem("Error ({$errorCode})", $errorText, '', '', ''); ApiFormatFeedWrapper::setResult($this->getResult(), $feed, $feedItems); } }
/** * This method takes an array and converts it to XML. * * @param string|null $name Tag name * @param mixed $value Tag value (attributes/content/subelements) * @param int|null $indent Indentation * @param array $attributes Additional attributes * @return string */ public static function recXmlPrint($name, $value, $indent, $attributes = []) { $retval = ''; if ($indent !== null) { if ($name !== null) { $indent += 2; } $indstr = "\n" . str_repeat(' ', $indent); } else { $indstr = ''; } if (is_object($value)) { $value = (array) $value; } if (is_array($value)) { $contentKey = isset($value[ApiResult::META_CONTENT]) ? $value[ApiResult::META_CONTENT] : '*'; $subelementKeys = isset($value[ApiResult::META_SUBELEMENTS]) ? $value[ApiResult::META_SUBELEMENTS] : []; if (isset($value[ApiResult::META_BC_SUBELEMENTS])) { $subelementKeys = array_merge($subelementKeys, $value[ApiResult::META_BC_SUBELEMENTS]); } $preserveKeys = isset($value[ApiResult::META_PRESERVE_KEYS]) ? $value[ApiResult::META_PRESERVE_KEYS] : []; $indexedTagName = isset($value[ApiResult::META_INDEXED_TAG_NAME]) ? self::mangleName($value[ApiResult::META_INDEXED_TAG_NAME], $preserveKeys) : '_v'; $bcBools = isset($value[ApiResult::META_BC_BOOLS]) ? $value[ApiResult::META_BC_BOOLS] : []; $indexSubelements = isset($value[ApiResult::META_TYPE]) ? $value[ApiResult::META_TYPE] !== 'array' : false; $content = null; $subelements = []; $indexedSubelements = []; foreach ($value as $k => $v) { if (ApiResult::isMetadataKey($k) && !in_array($k, $preserveKeys, true)) { continue; } $oldv = $v; if (is_bool($v) && !in_array($k, $bcBools, true)) { $v = $v ? 'true' : 'false'; } if ($name !== null && $k === $contentKey) { $content = $v; } elseif (is_int($k)) { $indexedSubelements[$k] = $v; } elseif (is_array($v) || is_object($v)) { $subelements[self::mangleName($k, $preserveKeys)] = $v; } elseif (in_array($k, $subelementKeys, true) || $name === null) { $subelements[self::mangleName($k, $preserveKeys)] = ['content' => $v, ApiResult::META_CONTENT => 'content', ApiResult::META_TYPE => 'assoc']; } elseif (is_bool($oldv)) { if ($oldv) { $attributes[self::mangleName($k, $preserveKeys)] = ''; } } elseif ($v !== null) { $attributes[self::mangleName($k, $preserveKeys)] = $v; } } if ($content !== null) { if ($subelements || $indexedSubelements) { $subelements[self::mangleName($contentKey, $preserveKeys)] = ['content' => $content, ApiResult::META_CONTENT => 'content', ApiResult::META_TYPE => 'assoc']; $content = null; } elseif (is_scalar($content)) { // Add xml:space="preserve" to the element so XML parsers // will leave whitespace in the content alone $attributes += ['xml:space' => 'preserve']; } } if ($content !== null) { if (is_scalar($content)) { $retval .= $indstr . Xml::element($name, $attributes, $content); } else { if ($name !== null) { $retval .= $indstr . Xml::element($name, $attributes, null); } $retval .= static::recXmlPrint(null, $content, $indent); if ($name !== null) { $retval .= $indstr . Xml::closeElement($name); } } } elseif (!$indexedSubelements && !$subelements) { if ($name !== null) { $retval .= $indstr . Xml::element($name, $attributes); } } else { if ($name !== null) { $retval .= $indstr . Xml::element($name, $attributes, null); } foreach ($subelements as $k => $v) { $retval .= static::recXmlPrint($k, $v, $indent); } foreach ($indexedSubelements as $k => $v) { $retval .= static::recXmlPrint($indexedTagName, $v, $indent, $indexSubelements ? ['_idx' => $k] : []); } if ($name !== null) { $retval .= $indstr . Xml::closeElement($name); } } } else { // to make sure null value doesn't produce unclosed element, // which is what Xml::element( $name, null, null ) returns if ($value === null) { $retval .= $indstr . Xml::element($name, $attributes); } else { $retval .= $indstr . Xml::element($name, $attributes, $value); } } return $retval; }