public static function getBlackList()
 {
     $cache = \ObjectCache::getMainWANInstance();
     return $cache->getWithSetCallback(wfMemcKey('flowthread', 'spamblacklist'), 60, function () {
         return self::buildBlacklist();
     });
 }
Beispiel #2
0
 /**
  * Check whether feed's cache should be cleared; for changes feeds
  * If the feed should be purged; $timekey and $key will be removed from cache
  *
  * @param string $timekey Cache key of the timestamp of the last item
  * @param string $key Cache key of feed's content
  */
 public static function checkPurge($timekey, $key)
 {
     global $wgRequest, $wgUser;
     $purge = $wgRequest->getVal('action') === 'purge';
     if ($purge && $wgUser->isAllowed('purge')) {
         $cache = ObjectCache::getMainWANInstance();
         $cache->delete($timekey, 1);
         $cache->delete($key, 1);
     }
 }
Beispiel #3
0
 public function testGetLinkClasses()
 {
     $wanCache = ObjectCache::getMainWANInstance();
     $titleFormatter = MediaWikiServices::getInstance()->getTitleFormatter();
     $linkCache = new LinkCache($titleFormatter, $wanCache);
     $foobarTitle = new TitleValue(NS_MAIN, 'FooBar');
     $redirectTitle = new TitleValue(NS_MAIN, 'Redirect');
     $userTitle = new TitleValue(NS_USER, 'Someuser');
     $linkCache->addGoodLinkObj(1, $foobarTitle, 10, 0);
     $linkCache->addGoodLinkObj(2, $redirectTitle, 10, 1);
     $linkCache->addGoodLinkObj(3, $userTitle, 10, 0);
     $linkRenderer = new LinkRenderer($titleFormatter, $linkCache);
     $linkRenderer->setStubThreshold(0);
     $this->assertEquals('', $linkRenderer->getLinkClasses($foobarTitle));
     $linkRenderer->setStubThreshold(20);
     $this->assertEquals('stub', $linkRenderer->getLinkClasses($foobarTitle));
     $linkRenderer->setStubThreshold(0);
     $this->assertEquals('mw-redirect', $linkRenderer->getLinkClasses($redirectTitle));
     $linkRenderer->setStubThreshold(20);
     $this->assertEquals('', $linkRenderer->getLinkClasses($userTitle));
 }
Beispiel #4
0
 /**
  * Invalidates image redirect cache related to that image
  *
  * @param Title $title Title of page
  * @return void
  */
 function invalidateImageRedirect(Title $title)
 {
     $key = $this->getSharedCacheKey('image_redirect', md5($title->getDBkey()));
     if ($key) {
         $this->getMasterDB()->onTransactionPreCommitOrIdle(function () use($key) {
             ObjectCache::getMainWANInstance()->delete($key);
         });
     }
 }
 /**
  * Get the diff table body, without header
  *
  * @return mixed (string/false)
  */
 public function getDiffBody()
 {
     $this->mCacheHit = true;
     // Check if the diff should be hidden from this user
     if (!$this->loadRevisionData()) {
         return false;
     } elseif ($this->mOldRev && !$this->mOldRev->userCan(Revision::DELETED_TEXT, $this->getUser())) {
         return false;
     } elseif ($this->mNewRev && !$this->mNewRev->userCan(Revision::DELETED_TEXT, $this->getUser())) {
         return false;
     }
     // Short-circuit
     if ($this->mOldRev === false || $this->mOldRev && $this->mNewRev && $this->mOldRev->getID() == $this->mNewRev->getID()) {
         return '';
     }
     // Cacheable?
     $key = false;
     $cache = ObjectCache::getMainWANInstance();
     if ($this->mOldid && $this->mNewid) {
         $key = $this->getDiffBodyCacheKey();
         // Try cache
         if (!$this->mRefreshCache) {
             $difftext = $cache->get($key);
             if ($difftext) {
                 wfIncrStats('diff_cache.hit');
                 $difftext = $this->localiseLineNumbers($difftext);
                 $difftext .= "\n<!-- diff cache key {$key} -->\n";
                 return $difftext;
             }
         }
         // don't try to load but save the result
     }
     $this->mCacheHit = false;
     // Loadtext is permission safe, this just clears out the diff
     if (!$this->loadText()) {
         return false;
     }
     $difftext = $this->generateContentDiffBody($this->mOldContent, $this->mNewContent);
     // Save to cache for 7 days
     if (!Hooks::run('AbortDiffCache', array(&$this))) {
         wfIncrStats('diff_cache.uncacheable');
     } elseif ($key !== false && $difftext !== false) {
         wfIncrStats('diff_cache.miss');
         $cache->set($key, $difftext, 7 * 86400);
     } else {
         wfIncrStats('diff_cache.uncacheable');
     }
     // Replace line numbers with the text in the user's language
     if ($difftext !== false) {
         $difftext = $this->localiseLineNumbers($difftext);
     }
     return $difftext;
 }
 /**
  * @param BagOStuff $memCached A cache instance. If none, fall back to CACHE_NONE.
  * @param bool $useDB
  * @param int $expiry Lifetime for cache. @see $mExpiry.
  */
 function __construct($memCached, $useDB, $expiry)
 {
     global $wgUseLocalMessageCache;
     if (!$memCached) {
         $memCached = wfGetCache(CACHE_NONE);
     }
     $this->mMemc = $memCached;
     $this->mDisable = !$useDB;
     $this->mExpiry = $expiry;
     if ($wgUseLocalMessageCache) {
         $this->localCache = ObjectCache::newAccelerator(CACHE_NONE);
     } else {
         $this->localCache = wfGetCache(CACHE_NONE);
     }
     $this->wanCache = ObjectCache::getMainWANInstance();
 }
Beispiel #7
0
 /**
  * Returns a map of any tags used on the wiki to number of edits
  * tagged with them, ordered descending by the hitcount.
  * This does not include tags defined somewhere that have never been applied.
  *
  * Keeps a short-term cache in memory, so calling this multiple times in the
  * same request should be fine.
  *
  * @return array Array of string => int
  */
 public static function tagUsageStatistics()
 {
     static $cachedStats = null;
     // Process cache to avoid I/O and repeated regens during holdoff
     if ($cachedStats !== null) {
         return $cachedStats;
     }
     $fname = __METHOD__;
     $cachedStats = ObjectCache::getMainWANInstance()->getWithSetCallback(wfMemcKey('change-tag-statistics'), 300, function ($oldValue, &$ttl, array &$setOpts) use($fname) {
         $dbr = wfGetDB(DB_SLAVE, 'vslow');
         $setOpts += Database::getCacheSetOptions($dbr);
         $res = $dbr->select('change_tag', array('ct_tag', 'hitcount' => 'count(*)'), array(), $fname, array('GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC'));
         $out = array();
         foreach ($res as $row) {
             $out[$row->ct_tag] = $row->hitcount;
         }
         return $out;
     }, array('checkKeys' => array(wfMemcKey('change-tag-statistics')), 'lockTSE' => INF));
     return $cachedStats;
 }
Beispiel #8
0
 /**
  * Additional parameters include:
  *   - cluster : The name of an external cluster registered via LBFactory.
  *               If not specified, the primary DB cluster for the wiki will be used.
  *               This can be overridden with a custom cluster so that DB handles will
  *               be retrieved via LBFactory::getExternalLB() and getConnection().
  * @param array $params
  */
 protected function __construct(array $params)
 {
     parent::__construct($params);
     $this->cluster = isset($params['cluster']) ? $params['cluster'] : false;
     $this->cache = ObjectCache::getMainWANInstance();
 }
Beispiel #9
0
 /**
  * Load restrictions from the page_restrictions table
  *
  * @param string $oldFashionedRestrictions Comma-separated list of page
  *   restrictions from page table (pre 1.10)
  */
 public function loadRestrictions($oldFashionedRestrictions = null)
 {
     if ($this->mRestrictionsLoaded) {
         return;
     }
     $id = $this->getArticleID();
     if ($id) {
         $cache = ObjectCache::getMainWANInstance();
         $rows = $cache->getWithSetCallback($cache->makeKey('page-restrictions', $id, $this->getLatestRevID()), $cache::TTL_DAY, function ($curValue, &$ttl, array &$setOpts) {
             $dbr = wfGetDB(DB_REPLICA);
             $setOpts += Database::getCacheSetOptions($dbr);
             return iterator_to_array($dbr->select('page_restrictions', ['pr_type', 'pr_expiry', 'pr_level', 'pr_cascade'], ['pr_page' => $this->getArticleID()], __METHOD__));
         });
         $this->loadRestrictionsFromRows($rows, $oldFashionedRestrictions);
     } else {
         $title_protection = $this->getTitleProtection();
         if ($title_protection) {
             $now = wfTimestampNow();
             $expiry = wfGetDB(DB_REPLICA)->decodeExpiry($title_protection['expiry']);
             if (!$expiry || $expiry > $now) {
                 // Apply the restrictions
                 $this->mRestrictionsExpiry['create'] = $expiry;
                 $this->mRestrictions['create'] = explode(',', trim($title_protection['permission']));
             } else {
                 // Get rid of the old restrictions
                 $this->mTitleProtection = false;
             }
         } else {
             $this->mRestrictionsExpiry['create'] = 'infinity';
         }
         $this->mRestrictionsLoaded = true;
     }
 }
Beispiel #10
0
 private static function getBlackList()
 {
     $cache = \ObjectCache::getMainWANInstance();
     return $cache->getWithSetCallback(wfMemcKey('flowthread', 'spamblacklist'), 60, function () {
         $source = wfMessage('flowthread-blacklist')->inContentLanguage();
         if ($source->isDisabled()) {
             return array();
         }
         $lines = explode("\n", $source->text());
         return self::parseLines($lines);
     });
 }
Beispiel #11
0
 /**
  * Get an array of extended metadata. (See the imageinfo API for format.)
  *
  * @param File $file File to use
  * @return array [<property name> => ['value' => <value>]], or [] on error
  * @since 1.23
  */
 public function fetchExtendedMetadata(File $file)
 {
     $cache = ObjectCache::getMainWANInstance();
     // If revision deleted, exit immediately
     if ($file->isDeleted(File::DELETED_FILE)) {
         return [];
     }
     $cacheKey = wfMemcKey('getExtendedMetadata', $this->getLanguage()->getCode(), (int) $this->singleLang, $file->getSha1());
     $cachedValue = $cache->get($cacheKey);
     if ($cachedValue && Hooks::run('ValidateExtendedMetadataCache', [$cachedValue['timestamp'], $file])) {
         $extendedMetadata = $cachedValue['data'];
     } else {
         $maxCacheTime = $file instanceof ForeignAPIFile ? 60 * 60 * 12 : 60 * 60 * 24 * 30;
         $fileMetadata = $this->getExtendedMetadataFromFile($file);
         $extendedMetadata = $this->getExtendedMetadataFromHook($file, $fileMetadata, $maxCacheTime);
         if ($this->singleLang) {
             $this->resolveMultilangMetadata($extendedMetadata);
         }
         $this->discardMultipleValues($extendedMetadata);
         // Make sure the metadata won't break the API when an XML format is used.
         // This is an API-specific function so it would be cleaner to call it from
         // outside fetchExtendedMetadata, but this way we don't need to redo the
         // computation on a cache hit.
         $this->sanitizeArrayForAPI($extendedMetadata);
         $valueToCache = ['data' => $extendedMetadata, 'timestamp' => wfTimestampNow()];
         $cache->set($cacheKey, $valueToCache, $maxCacheTime);
     }
     return $extendedMetadata;
 }
Beispiel #12
0
 /**
  * Lazy-load the revision's text.
  * Currently hardcoded to the 'text' table storage engine.
  *
  * @return string|bool The revision's text, or false on failure
  */
 protected function loadText()
 {
     // Caching may be beneficial for massive use of external storage
     global $wgRevisionCacheExpiry;
     $cache = ObjectCache::getMainWANInstance();
     $textId = $this->getTextId();
     $key = wfMemcKey('revisiontext', 'textid', $textId);
     if ($wgRevisionCacheExpiry) {
         $text = $cache->get($key);
         if (is_string($text)) {
             wfDebug(__METHOD__ . ": got id {$textId} from cache\n");
             return $text;
         }
     }
     // If we kept data for lazy extraction, use it now...
     if ($this->mTextRow !== null) {
         $row = $this->mTextRow;
         $this->mTextRow = null;
     } else {
         $row = null;
     }
     if (!$row) {
         // Text data is immutable; check slaves first.
         $dbr = wfGetDB(DB_SLAVE);
         $row = $dbr->selectRow('text', array('old_text', 'old_flags'), array('old_id' => $textId), __METHOD__);
     }
     // Fallback to the master in case of slave lag. Also use FOR UPDATE if it was
     // used to fetch this revision to avoid missing the row due to REPEATABLE-READ.
     $forUpdate = $this->mQueryFlags & self::READ_LOCKING == self::READ_LOCKING;
     if (!$row && ($forUpdate || wfGetLB()->getServerCount() > 1)) {
         $dbw = wfGetDB(DB_MASTER);
         $row = $dbw->selectRow('text', array('old_text', 'old_flags'), array('old_id' => $textId), __METHOD__, $forUpdate ? array('FOR UPDATE') : array());
     }
     if (!$row) {
         wfDebugLog('Revision', "No text row with ID '{$textId}' (revision {$this->getId()}).");
     }
     $text = self::getRevisionText($row);
     if ($row && $text === false) {
         wfDebugLog('Revision', "No blob for text row '{$textId}' (revision {$this->getId()}).");
     }
     # No negative caching -- negative hits on text rows may be due to corrupted slave servers
     if ($wgRevisionCacheExpiry && $text !== false) {
         $cache->set($key, $text, $wgRevisionCacheExpiry);
     }
     return $text;
 }
Beispiel #13
0
 /**
  * Build an array that represents the sidebar(s), the navigation bar among them.
  *
  * BaseTemplate::getSidebar can be used to simplify the format and id generation in new skins.
  *
  * The format of the returned array is array( heading => content, ... ), where:
  * - heading is the heading of a navigation portlet. It is either:
  *   - magic string to be handled by the skins ('SEARCH' / 'LANGUAGES' / 'TOOLBOX' / ...)
  *   - a message name (e.g. 'navigation'), the message should be HTML-escaped by the skin
  *   - plain text, which should be HTML-escaped by the skin
  * - content is the contents of the portlet. It is either:
  *   - HTML text (<ul><li>...</li>...</ul>)
  *   - array of link data in a format accepted by BaseTemplate::makeListItem()
  *   - (for a magic string as a key, any value)
  *
  * Note that extensions can control the sidebar contents using the SkinBuildSidebar hook
  * and can technically insert anything in here; skin creators are expected to handle
  * values described above.
  *
  * @return array
  */
 function buildSidebar()
 {
     global $wgEnableSidebarCache, $wgSidebarCacheExpiry;
     $cache = ObjectCache::getMainWANInstance();
     $key = wfMemcKey('sidebar', $this->getLanguage()->getCode());
     if ($wgEnableSidebarCache) {
         $cachedsidebar = $cache->get($key);
         if ($cachedsidebar) {
             Hooks::run('SidebarBeforeOutput', array($this, &$cachedsidebar));
             return $cachedsidebar;
         }
     }
     $bar = array();
     $this->addToSidebar($bar, 'sidebar');
     Hooks::run('SkinBuildSidebar', array($this, &$bar));
     if ($wgEnableSidebarCache) {
         $cache->set($key, $bar, $wgSidebarCacheExpiry);
     }
     Hooks::run('SidebarBeforeOutput', array($this, &$bar));
     return $bar;
 }
Beispiel #14
0
 /**
  * Build an array that represents the sidebar(s), the navigation bar among them.
  *
  * BaseTemplate::getSidebar can be used to simplify the format and id generation in new skins.
  *
  * The format of the returned array is array( heading => content, ... ), where:
  * - heading is the heading of a navigation portlet. It is either:
  *   - magic string to be handled by the skins ('SEARCH' / 'LANGUAGES' / 'TOOLBOX' / ...)
  *   - a message name (e.g. 'navigation'), the message should be HTML-escaped by the skin
  *   - plain text, which should be HTML-escaped by the skin
  * - content is the contents of the portlet. It is either:
  *   - HTML text (<ul><li>...</li>...</ul>)
  *   - array of link data in a format accepted by BaseTemplate::makeListItem()
  *   - (for a magic string as a key, any value)
  *
  * Note that extensions can control the sidebar contents using the SkinBuildSidebar hook
  * and can technically insert anything in here; skin creators are expected to handle
  * values described above.
  *
  * @return array
  */
 function buildSidebar()
 {
     global $wgEnableSidebarCache, $wgSidebarCacheExpiry;
     $that = $this;
     $callback = function () use($that) {
         $bar = array();
         $that->addToSidebar($bar, 'sidebar');
         Hooks::run('SkinBuildSidebar', array($that, &$bar));
         return $bar;
     };
     if ($wgEnableSidebarCache) {
         $cache = ObjectCache::getMainWANInstance();
         $sidebar = $cache->getWithSetCallback($cache->makeKey('sidebar', $this->getLanguage()->getCode()), $wgSidebarCacheExpiry, $callback, array('lockTSE' => 30));
     } else {
         $sidebar = $callback();
     }
     // Apply post-processing to the cached value
     Hooks::run('SidebarBeforeOutput', array($this, &$sidebar));
     return $sidebar;
 }
Beispiel #15
0
 /**
  * @param BagOStuff $memCached A cache instance. If none, fall back to CACHE_NONE.
  * @param bool $useDB
  * @param int $expiry Lifetime for cache. @see $mExpiry.
  */
 function __construct($memCached, $useDB, $expiry)
 {
     global $wgUseLocalMessageCache;
     if (!$memCached) {
         $memCached = wfGetCache(CACHE_NONE);
     }
     $this->mMemc = $memCached;
     $this->mDisable = !$useDB;
     $this->mExpiry = $expiry;
     if ($wgUseLocalMessageCache) {
         $this->localCache = MediaWikiServices::getInstance()->getLocalServerObjectCache();
     } else {
         $this->localCache = new EmptyBagOStuff();
     }
     $this->wanCache = ObjectCache::getMainWANInstance();
 }
Beispiel #16
0
 /**
  * HTTP GET request to a mediawiki API (with caching)
  * @param string $target Used in cache key creation, mostly
  * @param array $query The query parameters for the API request
  * @param int $cacheTTL Time to live for the memcached caching
  * @return string|null
  */
 public function httpGetCached($target, $query, $cacheTTL = 3600)
 {
     if ($this->mApiBase) {
         $url = wfAppendQuery($this->mApiBase, $query);
     } else {
         $url = $this->makeUrl($query, 'api');
     }
     $cache = ObjectCache::getMainWANInstance();
     return $cache->getWithSetCallback($this->getLocalCacheKey(get_class($this), $target, md5($url)), $cacheTTL, function ($curValue, &$ttl) use($url, $cache) {
         $html = self::httpGet($url, 'default', [], $mtime);
         if ($html !== false) {
             $ttl = $mtime ? $cache->adaptiveTTL($mtime, $ttl) : $ttl;
         } else {
             $ttl = $cache->adaptiveTTL($mtime, $ttl);
             $html = null;
             // caches negatives
         }
         return $html;
     }, ['pcTTL' => $cache::TTL_PROC_LONG]);
 }
 /**
  * @param ResourceLoader $rl
  * @param LoggerInterface $logger
  */
 public function __construct(ResourceLoader $rl = null, LoggerInterface $logger = null)
 {
     $this->resourceloader = $rl;
     $this->logger = $logger ?: new NullLogger();
     $this->wanCache = ObjectCache::getMainWANInstance();
 }
 /**
  * @param string $name
  * @return mixed
  */
 private function getCachedConfigVar($name)
 {
     // @TODO: cleanup this whole method with a proper config system
     if ($this->wiki === wfWikiID()) {
         return $GLOBALS[$name];
         // common case
     } else {
         $wiki = $this->wiki;
         $cache = ObjectCache::getMainWANInstance();
         $value = $cache->getWithSetCallback($cache->makeGlobalKey('jobqueue', 'configvalue', $wiki, $name), $cache::TTL_DAY + mt_rand(0, $cache::TTL_DAY), function () use($wiki, $name) {
             global $wgConf;
             return array('v' => $wgConf->getConfig($wiki, $name));
         }, array('pcTTL' => 30));
         return $value['v'];
     }
 }
Beispiel #19
0
 /**
  * @param array $options
  */
 function purgeThumbnails($options = array())
 {
     $key = $this->repo->getLocalCacheKey('ForeignAPIRepo', 'ThumbUrl', $this->getName());
     ObjectCache::getMainWANInstance()->delete($key);
     $files = $this->getThumbnails();
     // Give media handler a chance to filter the purge list
     $handler = $this->getHandler();
     if ($handler) {
         $handler->filterThumbnailPurgeList($files, $options);
     }
     $dir = $this->getThumbPath($this->getName());
     $purgeList = array();
     foreach ($files as $file) {
         $purgeList[] = "{$dir}{$file}";
     }
     # Delete the thumbnails
     $this->repo->quickPurgeBatch($purgeList);
     # Clear out the thumbnail directory if empty
     $this->repo->quickCleanDir($dir);
 }
Beispiel #20
0
 /**
  * Returns page information in an easily-manipulated format. Array keys are used so extensions
  * may add additional information in arbitrary positions. Array values are arrays with one
  * element to be rendered as a header, arrays with two elements to be rendered as a table row.
  *
  * @return array
  */
 protected function pageInfo()
 {
     global $wgContLang;
     $user = $this->getUser();
     $lang = $this->getLanguage();
     $title = $this->getTitle();
     $id = $title->getArticleID();
     $config = $this->context->getConfig();
     $cache = ObjectCache::getMainWANInstance();
     $memcKey = wfMemcKey('infoaction', sha1($title->getPrefixedText()), $this->page->getLatest());
     $pageCounts = $cache->get($memcKey);
     $version = isset($pageCounts['cacheversion']) ? $pageCounts['cacheversion'] : false;
     if ($pageCounts === false || $version !== self::CACHE_VERSION) {
         // Get page information that would be too "expensive" to retrieve by normal means
         $pageCounts = $this->pageCounts($title);
         $pageCounts['cacheversion'] = self::CACHE_VERSION;
         $cache->set($memcKey, $pageCounts);
     }
     // Get page properties
     $dbr = wfGetDB(DB_SLAVE);
     $result = $dbr->select('page_props', array('pp_propname', 'pp_value'), array('pp_page' => $id), __METHOD__);
     $pageProperties = array();
     foreach ($result as $row) {
         $pageProperties[$row->pp_propname] = $row->pp_value;
     }
     // Basic information
     $pageInfo = array();
     $pageInfo['header-basic'] = array();
     // Display title
     $displayTitle = $title->getPrefixedText();
     if (isset($pageProperties['displaytitle'])) {
         $displayTitle = $pageProperties['displaytitle'];
     }
     $pageInfo['header-basic'][] = array($this->msg('pageinfo-display-title'), $displayTitle);
     // Is it a redirect? If so, where to?
     if ($title->isRedirect()) {
         $pageInfo['header-basic'][] = array($this->msg('pageinfo-redirectsto'), Linker::link($this->page->getRedirectTarget()) . $this->msg('word-separator')->escaped() . $this->msg('parentheses')->rawParams(Linker::link($this->page->getRedirectTarget(), $this->msg('pageinfo-redirectsto-info')->escaped(), array(), array('action' => 'info')))->escaped());
     }
     // Default sort key
     $sortKey = $title->getCategorySortkey();
     if (isset($pageProperties['defaultsort'])) {
         $sortKey = $pageProperties['defaultsort'];
     }
     $sortKey = htmlspecialchars($sortKey);
     $pageInfo['header-basic'][] = array($this->msg('pageinfo-default-sort'), $sortKey);
     // Page length (in bytes)
     $pageInfo['header-basic'][] = array($this->msg('pageinfo-length'), $lang->formatNum($title->getLength()));
     // Page ID (number not localised, as it's a database ID)
     $pageInfo['header-basic'][] = array($this->msg('pageinfo-article-id'), $id);
     // Language in which the page content is (supposed to be) written
     $pageLang = $title->getPageLanguage()->getCode();
     if ($config->get('PageLanguageUseDB') && $this->getTitle()->userCan('pagelang', $this->getUser())) {
         // Link to Special:PageLanguage with pre-filled page title if user has permissions
         $titleObj = SpecialPage::getTitleFor('PageLanguage', $title->getPrefixedText());
         $langDisp = Linker::link($titleObj, $this->msg('pageinfo-language')->escaped());
     } else {
         // Display just the message
         $langDisp = $this->msg('pageinfo-language')->escaped();
     }
     $pageInfo['header-basic'][] = array($langDisp, Language::fetchLanguageName($pageLang, $lang->getCode()) . ' ' . $this->msg('parentheses', $pageLang)->escaped());
     // Content model of the page
     $pageInfo['header-basic'][] = array($this->msg('pageinfo-content-model'), htmlspecialchars(ContentHandler::getLocalizedName($title->getContentModel())));
     // Search engine status
     $pOutput = new ParserOutput();
     if (isset($pageProperties['noindex'])) {
         $pOutput->setIndexPolicy('noindex');
     }
     if (isset($pageProperties['index'])) {
         $pOutput->setIndexPolicy('index');
     }
     // Use robot policy logic
     $policy = $this->page->getRobotPolicy('view', $pOutput);
     $pageInfo['header-basic'][] = array($this->msg('pageinfo-robot-policy'), $this->msg("pageinfo-robot-{$policy['index']}"));
     $unwatchedPageThreshold = $config->get('UnwatchedPageThreshold');
     if ($user->isAllowed('unwatchedpages') || $unwatchedPageThreshold !== false && $pageCounts['watchers'] >= $unwatchedPageThreshold) {
         // Number of page watchers
         $pageInfo['header-basic'][] = array($this->msg('pageinfo-watchers'), $lang->formatNum($pageCounts['watchers']));
         if ($config->get('ShowUpdatedMarker') && isset($pageCounts['visitingWatchers'])) {
             $minToDisclose = $config->get('UnwatchedPageSecret');
             if ($pageCounts['visitingWatchers'] > $minToDisclose || $user->isAllowed('unwatchedpages')) {
                 $pageInfo['header-basic'][] = array($this->msg('pageinfo-visiting-watchers'), $lang->formatNum($pageCounts['visitingWatchers']));
             } else {
                 $pageInfo['header-basic'][] = array($this->msg('pageinfo-visiting-watchers'), $this->msg('pageinfo-few-visiting-watchers'));
             }
         }
     } elseif ($unwatchedPageThreshold !== false) {
         $pageInfo['header-basic'][] = array($this->msg('pageinfo-watchers'), $this->msg('pageinfo-few-watchers')->numParams($unwatchedPageThreshold));
     }
     // Redirects to this page
     $whatLinksHere = SpecialPage::getTitleFor('Whatlinkshere', $title->getPrefixedText());
     $pageInfo['header-basic'][] = array(Linker::link($whatLinksHere, $this->msg('pageinfo-redirects-name')->escaped(), array(), array('hidelinks' => 1, 'hidetrans' => 1, 'hideimages' => $title->getNamespace() == NS_FILE)), $this->msg('pageinfo-redirects-value')->numParams(count($title->getRedirectsHere())));
     // Is it counted as a content page?
     if ($this->page->isCountable()) {
         $pageInfo['header-basic'][] = array($this->msg('pageinfo-contentpage'), $this->msg('pageinfo-contentpage-yes'));
     }
     // Subpages of this page, if subpages are enabled for the current NS
     if (MWNamespace::hasSubpages($title->getNamespace())) {
         $prefixIndex = SpecialPage::getTitleFor('Prefixindex', $title->getPrefixedText() . '/');
         $pageInfo['header-basic'][] = array(Linker::link($prefixIndex, $this->msg('pageinfo-subpages-name')->escaped()), $this->msg('pageinfo-subpages-value')->numParams($pageCounts['subpages']['total'], $pageCounts['subpages']['redirects'], $pageCounts['subpages']['nonredirects']));
     }
     if ($title->inNamespace(NS_CATEGORY)) {
         $category = Category::newFromTitle($title);
         // $allCount is the total number of cat members,
         // not the count of how many members are normal pages.
         $allCount = (int) $category->getPageCount();
         $subcatCount = (int) $category->getSubcatCount();
         $fileCount = (int) $category->getFileCount();
         $pagesCount = $allCount - $subcatCount - $fileCount;
         $pageInfo['category-info'] = array(array($this->msg('pageinfo-category-total'), $lang->formatNum($allCount)), array($this->msg('pageinfo-category-pages'), $lang->formatNum($pagesCount)), array($this->msg('pageinfo-category-subcats'), $lang->formatNum($subcatCount)), array($this->msg('pageinfo-category-files'), $lang->formatNum($fileCount)));
     }
     // Page protection
     $pageInfo['header-restrictions'] = array();
     // Is this page affected by the cascading protection of something which includes it?
     if ($title->isCascadeProtected()) {
         $cascadingFrom = '';
         $sources = $title->getCascadeProtectionSources();
         // Array deferencing is in PHP 5.4 :(
         foreach ($sources[0] as $sourceTitle) {
             $cascadingFrom .= Html::rawElement('li', array(), Linker::linkKnown($sourceTitle));
         }
         $cascadingFrom = Html::rawElement('ul', array(), $cascadingFrom);
         $pageInfo['header-restrictions'][] = array($this->msg('pageinfo-protect-cascading-from'), $cascadingFrom);
     }
     // Is out protection set to cascade to other pages?
     if ($title->areRestrictionsCascading()) {
         $pageInfo['header-restrictions'][] = array($this->msg('pageinfo-protect-cascading'), $this->msg('pageinfo-protect-cascading-yes'));
     }
     // Page protection
     foreach ($title->getRestrictionTypes() as $restrictionType) {
         $protectionLevel = implode(', ', $title->getRestrictions($restrictionType));
         if ($protectionLevel == '') {
             // Allow all users
             $message = $this->msg('protect-default')->escaped();
         } else {
             // Administrators only
             // Messages: protect-level-autoconfirmed, protect-level-sysop
             $message = $this->msg("protect-level-{$protectionLevel}");
             if ($message->isDisabled()) {
                 // Require "$1" permission
                 $message = $this->msg("protect-fallback", $protectionLevel)->parse();
             } else {
                 $message = $message->escaped();
             }
         }
         $expiry = $title->getRestrictionExpiry($restrictionType);
         $formattedexpiry = $this->msg('parentheses', $this->getLanguage()->formatExpiry($expiry))->escaped();
         $message .= $this->msg('word-separator')->escaped() . $formattedexpiry;
         // Messages: restriction-edit, restriction-move, restriction-create,
         // restriction-upload
         $pageInfo['header-restrictions'][] = array($this->msg("restriction-{$restrictionType}"), $message);
     }
     if (!$this->page->exists()) {
         return $pageInfo;
     }
     // Edit history
     $pageInfo['header-edits'] = array();
     $firstRev = $this->page->getOldestRevision();
     $lastRev = $this->page->getRevision();
     $batch = new LinkBatch();
     if ($firstRev) {
         $firstRevUser = $firstRev->getUserText(Revision::FOR_THIS_USER);
         if ($firstRevUser !== '') {
             $batch->add(NS_USER, $firstRevUser);
             $batch->add(NS_USER_TALK, $firstRevUser);
         }
     }
     if ($lastRev) {
         $lastRevUser = $lastRev->getUserText(Revision::FOR_THIS_USER);
         if ($lastRevUser !== '') {
             $batch->add(NS_USER, $lastRevUser);
             $batch->add(NS_USER_TALK, $lastRevUser);
         }
     }
     $batch->execute();
     if ($firstRev) {
         // Page creator
         $pageInfo['header-edits'][] = array($this->msg('pageinfo-firstuser'), Linker::revUserTools($firstRev));
         // Date of page creation
         $pageInfo['header-edits'][] = array($this->msg('pageinfo-firsttime'), Linker::linkKnown($title, htmlspecialchars($lang->userTimeAndDate($firstRev->getTimestamp(), $user)), array(), array('oldid' => $firstRev->getId())));
     }
     if ($lastRev) {
         // Latest editor
         $pageInfo['header-edits'][] = array($this->msg('pageinfo-lastuser'), Linker::revUserTools($lastRev));
         // Date of latest edit
         $pageInfo['header-edits'][] = array($this->msg('pageinfo-lasttime'), Linker::linkKnown($title, htmlspecialchars($lang->userTimeAndDate($this->page->getTimestamp(), $user)), array(), array('oldid' => $this->page->getLatest())));
     }
     // Total number of edits
     $pageInfo['header-edits'][] = array($this->msg('pageinfo-edits'), $lang->formatNum($pageCounts['edits']));
     // Total number of distinct authors
     if ($pageCounts['authors'] > 0) {
         $pageInfo['header-edits'][] = array($this->msg('pageinfo-authors'), $lang->formatNum($pageCounts['authors']));
     }
     // Recent number of edits (within past 30 days)
     $pageInfo['header-edits'][] = array($this->msg('pageinfo-recent-edits', $lang->formatDuration($config->get('RCMaxAge'))), $lang->formatNum($pageCounts['recent_edits']));
     // Recent number of distinct authors
     $pageInfo['header-edits'][] = array($this->msg('pageinfo-recent-authors'), $lang->formatNum($pageCounts['recent_authors']));
     // Array of MagicWord objects
     $magicWords = MagicWord::getDoubleUnderscoreArray();
     // Array of magic word IDs
     $wordIDs = $magicWords->names;
     // Array of IDs => localized magic words
     $localizedWords = $wgContLang->getMagicWords();
     $listItems = array();
     foreach ($pageProperties as $property => $value) {
         if (in_array($property, $wordIDs)) {
             $listItems[] = Html::element('li', array(), $localizedWords[$property][1]);
         }
     }
     $localizedList = Html::rawElement('ul', array(), implode('', $listItems));
     $hiddenCategories = $this->page->getHiddenCategories();
     if (count($listItems) > 0 || count($hiddenCategories) > 0 || $pageCounts['transclusion']['from'] > 0 || $pageCounts['transclusion']['to'] > 0) {
         $options = array('LIMIT' => $config->get('PageInfoTransclusionLimit'));
         $transcludedTemplates = $title->getTemplateLinksFrom($options);
         if ($config->get('MiserMode')) {
             $transcludedTargets = array();
         } else {
             $transcludedTargets = $title->getTemplateLinksTo($options);
         }
         // Page properties
         $pageInfo['header-properties'] = array();
         // Magic words
         if (count($listItems) > 0) {
             $pageInfo['header-properties'][] = array($this->msg('pageinfo-magic-words')->numParams(count($listItems)), $localizedList);
         }
         // Hidden categories
         if (count($hiddenCategories) > 0) {
             $pageInfo['header-properties'][] = array($this->msg('pageinfo-hidden-categories')->numParams(count($hiddenCategories)), Linker::formatHiddenCategories($hiddenCategories));
         }
         // Transcluded templates
         if ($pageCounts['transclusion']['from'] > 0) {
             if ($pageCounts['transclusion']['from'] > count($transcludedTemplates)) {
                 $more = $this->msg('morenotlisted')->escaped();
             } else {
                 $more = null;
             }
             $pageInfo['header-properties'][] = array($this->msg('pageinfo-templates')->numParams($pageCounts['transclusion']['from']), Linker::formatTemplates($transcludedTemplates, false, false, $more));
         }
         if (!$config->get('MiserMode') && $pageCounts['transclusion']['to'] > 0) {
             if ($pageCounts['transclusion']['to'] > count($transcludedTargets)) {
                 $more = Linker::link($whatLinksHere, $this->msg('moredotdotdot')->escaped(), array(), array('hidelinks' => 1, 'hideredirs' => 1));
             } else {
                 $more = null;
             }
             $pageInfo['header-properties'][] = array($this->msg('pageinfo-transclusions')->numParams($pageCounts['transclusion']['to']), Linker::formatTemplates($transcludedTargets, false, false, $more));
         }
     }
     return $pageInfo;
 }
Beispiel #21
0
 /**
  * Get the user touched timestamp
  *
  * Use this value only to validate caches via inequalities
  * such as in the case of HTTP If-Modified-Since response logic
  *
  * @return string TS_MW Timestamp
  */
 public function getTouched()
 {
     $this->load();
     if ($this->mId) {
         if ($this->mQuickTouched === null) {
             $key = wfMemcKey('user-quicktouched', 'id', $this->mId);
             $cache = ObjectCache::getMainWANInstance();
             $this->mQuickTouched = wfTimestamp(TS_MW, $cache->getCheckKeyTime($key));
         }
         return max($this->mTouched, $this->mQuickTouched);
     }
     return $this->mTouched;
 }
Beispiel #22
0
 /**
  * HTTP GET request to a mediawiki API (with caching)
  * @param string $target Used in cache key creation, mostly
  * @param array $query The query parameters for the API request
  * @param int $cacheTTL Time to live for the memcached caching
  * @return null
  */
 public function httpGetCached($target, $query, $cacheTTL = 3600)
 {
     if ($this->mApiBase) {
         $url = wfAppendQuery($this->mApiBase, $query);
     } else {
         $url = $this->makeUrl($query, 'api');
     }
     if (!isset($this->mQueryCache[$url])) {
         $data = ObjectCache::getMainWANInstance()->getWithSetCallback($this->getLocalCacheKey(get_class($this), $target, md5($url)), $cacheTTL, function () use($url) {
             return ForeignAPIRepo::httpGet($url);
         });
         if (!$data) {
             return null;
         }
         if (count($this->mQueryCache) > 100) {
             // Keep the cache from growing infinitely
             $this->mQueryCache = [];
         }
         $this->mQueryCache[$url] = $data;
     }
     return $this->mQueryCache[$url];
 }
Beispiel #23
0
 /**
  * Returns the page's content model id (see the CONTENT_MODEL_XXX constants).
  *
  * Will use the revisions actual content model if the page exists,
  * and the page's default if the page doesn't exist yet.
  *
  * @return string
  *
  * @since 1.21
  */
 public function getContentModel()
 {
     if ($this->exists()) {
         $cache = ObjectCache::getMainWANInstance();
         return $cache->getWithSetCallback($cache->makeKey('page', 'content-model', $this->getLatest()), $cache::TTL_MONTH, function () {
             $rev = $this->getRevision();
             if ($rev) {
                 // Look at the revision's actual content model
                 return $rev->getContentModel();
             } else {
                 $title = $this->mTitle->getPrefixedDBkey();
                 wfWarn("Page {$title} exists but has no (visible) revisions!");
                 return $this->mTitle->getContentModel();
             }
         });
     }
     // use the default model for this page
     return $this->mTitle->getContentModel();
 }
Beispiel #24
0
 /**
  * Purge the file object/metadata cache
  */
 function invalidateCache()
 {
     $key = $this->getCacheKey();
     if (!$key) {
         return;
     }
     ObjectCache::getMainWANInstance()->delete($key);
 }
Beispiel #25
0
 /**
  * Get the HTML text of the description page, if available
  *
  * @param bool|Language $lang Optional language to fetch description in
  * @return string
  */
 function getDescriptionText($lang = false)
 {
     global $wgLang;
     if (!$this->repo || !$this->repo->fetchDescription) {
         return false;
     }
     $lang = $lang ?: $wgLang;
     $renderUrl = $this->repo->getDescriptionRenderUrl($this->getName(), $lang->getCode());
     if ($renderUrl) {
         $cache = ObjectCache::getMainWANInstance();
         $key = null;
         if ($this->repo->descriptionCacheExpiry > 0) {
             wfDebug("Attempting to get the description from cache...");
             $key = $this->repo->getLocalCacheKey('RemoteFileDescription', 'url', $lang->getCode(), $this->getName());
             $obj = $cache->get($key);
             if ($obj) {
                 wfDebug("success!\n");
                 return $obj;
             }
             wfDebug("miss\n");
         }
         wfDebug("Fetching shared description from {$renderUrl}\n");
         $res = Http::get($renderUrl, array(), __METHOD__);
         if ($res && $key) {
             $cache->set($key, $res, $this->repo->descriptionCacheExpiry);
         }
         return $res;
     } else {
         return false;
     }
 }
Beispiel #26
0
 /**
  * Returns a map of any tags used on the wiki to number of edits
  * tagged with them, ordered descending by the hitcount.
  * This does not include tags defined somewhere that have never been applied.
  *
  * Keeps a short-term cache in memory, so calling this multiple times in the
  * same request should be fine.
  *
  * @return array Array of string => int
  */
 public static function tagUsageStatistics()
 {
     $fname = __METHOD__;
     return ObjectCache::getMainWANInstance()->getWithSetCallback(wfMemcKey('change-tag-statistics'), 300, function ($oldValue, &$ttl, array &$setOpts) use($fname) {
         $dbr = wfGetDB(DB_SLAVE, 'vslow');
         $setOpts += Database::getCacheSetOptions($dbr);
         $res = $dbr->select('change_tag', array('ct_tag', 'hitcount' => 'count(*)'), array(), $fname, array('GROUP BY' => 'ct_tag', 'ORDER BY' => 'hitcount DESC'));
         $out = array();
         foreach ($res as $row) {
             $out[$row->ct_tag] = $row->hitcount;
         }
         return $out;
     }, array('checkKeys' => array(wfMemcKey('change-tag-statistics')), 'lockTSE' => INF, 'pcTTL' => 30));
 }
Beispiel #27
0
 /**
  * Generate help for the specified modules
  *
  * Help is placed into the OutputPage object returned by
  * $context->getOutput().
  *
  * Recognized options include:
  *  - headerlevel: (int) Header tag level
  *  - nolead: (bool) Skip the inclusion of api-help-lead
  *  - noheader: (bool) Skip the inclusion of the top-level section headers
  *  - submodules: (bool) Include help for submodules of the current module
  *  - recursivesubmodules: (bool) Include help for submodules recursively
  *  - helptitle: (string) Title to link for additional modules' help. Should contain $1.
  *  - toc: (bool) Include a table of contents
  *
  * @param IContextSource $context
  * @param ApiBase[]|ApiBase $modules
  * @param array $options Formatting options (described above)
  * @return string
  */
 public static function getHelp(IContextSource $context, $modules, array $options)
 {
     global $wgContLang;
     if (!is_array($modules)) {
         $modules = array($modules);
     }
     $out = $context->getOutput();
     $out->addModuleStyles('mediawiki.hlist');
     $out->addModuleStyles('mediawiki.apihelp');
     if (!empty($options['toc'])) {
         $out->addModules('mediawiki.toc');
     }
     $out->setPageTitle($context->msg('api-help-title'));
     $cache = ObjectCache::getMainWANInstance();
     $cacheKey = null;
     if (count($modules) == 1 && $modules[0] instanceof ApiMain && $options['recursivesubmodules'] && $context->getLanguage() === $wgContLang) {
         $cacheHelpTimeout = $context->getConfig()->get('APICacheHelpTimeout');
         if ($cacheHelpTimeout > 0) {
             // Get help text from cache if present
             $cacheKey = wfMemcKey('apihelp', $modules[0]->getModulePath(), (int) (!empty($options['toc'])), str_replace(' ', '_', SpecialVersion::getVersion('nodb')));
             $cached = $cache->get($cacheKey);
             if ($cached) {
                 $out->addHTML($cached);
                 return;
             }
         }
     }
     if ($out->getHTML() !== '') {
         // Don't save to cache, there's someone else's content in the page
         // already
         $cacheKey = null;
     }
     $options['recursivesubmodules'] = !empty($options['recursivesubmodules']);
     $options['submodules'] = $options['recursivesubmodules'] || !empty($options['submodules']);
     // Prepend lead
     if (empty($options['nolead'])) {
         $msg = $context->msg('api-help-lead');
         if (!$msg->isDisabled()) {
             $out->addHTML($msg->parseAsBlock());
         }
     }
     $haveModules = array();
     $html = self::getHelpInternal($context, $modules, $options, $haveModules);
     if (!empty($options['toc']) && $haveModules) {
         $out->addHTML(Linker::generateTOC($haveModules, $context->getLanguage()));
     }
     $out->addHTML($html);
     $helptitle = isset($options['helptitle']) ? $options['helptitle'] : null;
     $html = self::fixHelpLinks($out->getHTML(), $helptitle, $haveModules);
     $out->clearHTML();
     $out->addHTML($html);
     if ($cacheKey !== null) {
         $cache->set($cacheKey, $out->getHTML(), $cacheHelpTimeout);
     }
 }
Beispiel #28
0
 /**
  * Load the interwiki, trying first memcached then the DB
  *
  * @param string $prefix The interwiki prefix
  * @return Interwiki|bool Interwiki if $prefix is valid, otherwise false
  */
 protected static function load($prefix)
 {
     global $wgInterwikiExpiry;
     $iwData = array();
     if (!Hooks::run('InterwikiLoadPrefix', array($prefix, &$iwData))) {
         return Interwiki::loadFromArray($iwData);
     }
     $cache = ObjectCache::getMainWANInstance();
     if (!$iwData) {
         $key = wfMemcKey('interwiki', $prefix);
         $iwData = $cache->get($key);
         if ($iwData === '!NONEXISTENT') {
             // negative cache hit
             return false;
         }
     }
     // is_array is hack for old keys
     if ($iwData && is_array($iwData)) {
         $iw = Interwiki::loadFromArray($iwData);
         if ($iw) {
             return $iw;
         }
     }
     $db = wfGetDB(DB_SLAVE);
     $row = $db->fetchRow($db->select('interwiki', self::selectFields(), array('iw_prefix' => $prefix), __METHOD__));
     $iw = Interwiki::loadFromArray($row);
     if ($iw) {
         $mc = array('iw_url' => $iw->mURL, 'iw_api' => $iw->mAPI, 'iw_local' => $iw->mLocal, 'iw_trans' => $iw->mTrans);
         $cache->set($key, $mc, $wgInterwikiExpiry);
         return $iw;
     }
     // negative cache hit
     $cache->set($key, '!NONEXISTENT', $wgInterwikiExpiry);
     return false;
 }
 /**
  * Get the backend object with a given name
  *
  * @param string $name
  * @return FileBackend
  * @throws FileBackendException
  */
 public function get($name)
 {
     if (!isset($this->backends[$name])) {
         throw new FileBackendException("No backend defined with the name `{$name}`.");
     }
     // Lazy-load the actual backend instance
     if (!isset($this->backends[$name]['instance'])) {
         $class = $this->backends[$name]['class'];
         $config = $this->backends[$name]['config'];
         $config['wikiId'] = isset($config['wikiId']) ? $config['wikiId'] : wfWikiID();
         // e.g. "my_wiki-en_"
         $config['lockManager'] = LockManagerGroup::singleton($config['wikiId'])->get($config['lockManager']);
         $config['fileJournal'] = isset($config['fileJournal']) ? FileJournal::factory($config['fileJournal'], $name) : FileJournal::factory(array('class' => 'NullFileJournal'), $name);
         $config['wanCache'] = ObjectCache::getMainWANInstance();
         $this->backends[$name]['instance'] = new $class($config);
     }
     return $this->backends[$name]['instance'];
 }
Beispiel #30
0
 /**
  * Purge the file object/metadata cache
  */
 public function invalidateCache()
 {
     $key = $this->getCacheKey();
     if (!$key) {
         return;
     }
     $this->repo->getMasterDB()->onTransactionPreCommitOrIdle(function () use($key) {
         ObjectCache::getMainWANInstance()->delete($key);
     });
 }