Page MenuHomeWickedGov Phorge

SpecialBannerLoader.php
No OneTemporary

Size
7 KB
Referenced Files
None
Subscribers
None

SpecialBannerLoader.php

<?php
use MediaWiki\Html\Html;
use MediaWiki\Json\FormatJson;
use MediaWiki\SpecialPage\UnlistedSpecialPage;
/**
* Renders banner contents as jsonp.
*/
class SpecialBannerLoader extends UnlistedSpecialPage {
/**
* Seconds leeway for checking stale choice data. Should be the same
* as CAMPAIGN_STALENESS_LEEWAY in ext.centralNotice.display.chooser.js.
*/
private const CAMPAIGN_STALENESS_LEEWAY = 900;
/* Possible values for $this->cacheResponse */
private const MAX_CACHE_NORMAL = 0;
private const MAX_CACHE_REDUCED = 1;
/* Possible values for $this->requestType */
private const USER_DISPLAY_REQUEST = 0;
private const TESTING_SAVED_REQUEST = 1;
private const PREVIEW_UNSAVED_REQUEST = 2;
/** @var string Name of the chosen banner */
private $bannerName;
/** @var string|null Name of the campaign that the banner belongs to. */
private $campaignName;
/** @var string|null Content of the banner to be previewed */
private $previewContent;
/** @var string[]|null Unsaved messages for substitution in preview banner content */
private $previewMessages;
/** @var string[]|null */
private $editToken;
/** @var bool */
private $debug;
/** @var int Type of caching to set (see constants, above) */
private $cacheResponse;
/** @var int Request type (see constants, above) */
private $requestType;
public function __construct() {
// Register special page
parent::__construct( "BannerLoader" );
}
public function execute( $par ) {
$this->getOutput()->disable();
try {
$this->getParamsAndSetState();
$out = $this->getJsNotice();
} catch ( EmptyBannerException $e ) {
$out = "mw.centralNotice.handleBannerLoaderError( 'Empty banner' );";
// Force reduced cache time
$this->cacheResponse = self::MAX_CACHE_REDUCED;
} catch ( Exception $e ) {
if ( $e instanceof BannerPreviewPermissionsException ) {
$msg = $this->msg( 'centralnotice-preview-loader-permissions-error' )->escaped();
} else {
$msg = $e->getMessage();
}
// @phan-suppress-next-line SecurityCheck-DoubleEscaped
$msgParamStr = $msg ? Html::encodeJsVar( $msg ) : '';
// For preview requests, a different error callback is needed.
if ( $this->requestType === self::PREVIEW_UNSAVED_REQUEST ) {
$callback = 'mw.centralNotice.adminUi.bannerEditor.handleBannerLoaderError';
} else {
$callback = 'mw.centralNotice.handleBannerLoaderError';
}
$out = "$callback({$msgParamStr});";
// Force reduced cache time
$this->cacheResponse = self::MAX_CACHE_REDUCED;
wfDebugLog( 'CentralNotice', $msg );
}
// We have to call this since we've disabled output.
// TODO See if there's a better way to do this, maybe OutputPage::setCdnMaxage()?
$this->sendHeaders();
echo $out;
}
public function getParamsAndSetState() {
$request = $this->getRequest();
$this->campaignName = $request->getText( 'campaign' );
$this->bannerName = $request->getText( 'banner' );
$this->debug = $request->getFuzzyBool( 'debug' );
$this->previewContent = $request->getText( 'previewcontent' );
$this->previewMessages = $request->getArray( 'previewmessages' );
$this->editToken = $request->getVal( 'token' );
// All request types should have at least a non-empty banner name
if ( !$this->bannerName ) {
throw new MissingRequiredParamsException();
}
// Only render preview content and messages for users with CN admin rights, on
// requests that were POSTed, with the correct edit token. This is to prevent
// malicious use of the reflection of unsanitized parameters.
if ( $this->getRequest()->wasPosted() && $this->previewContent ) {
$this->requestType = self::PREVIEW_UNSAVED_REQUEST;
// Check credentials
if (
!$this->getUser()->isAllowed( 'centralnotice-admin' ) ||
!$this->editToken ||
!$this->getUser()->matchEditToken( $this->editToken )
) {
throw new BannerPreviewPermissionsException( $this->bannerName );
}
// Note: We don't set $this->cacheResponse since there's no caching for
// logged-in users anyway.
// Distinguish a testing request for a saved banner by the absence of a campaign
} elseif ( !$this->campaignName ) {
$this->requestType = self::TESTING_SAVED_REQUEST;
$this->cacheResponse = self::MAX_CACHE_REDUCED;
// We have at least a campaign name and a banner name, which means this is a
// normal request for a banner to display to a user.
} else {
$this->requestType = self::USER_DISPLAY_REQUEST;
$this->cacheResponse = self::MAX_CACHE_NORMAL;
}
}
/**
* Generate the HTTP response headers for the banner file, setting maxage cache time
* for front-send cache as appropriate.
*
* For anonymous users, set cache as per $this->cacheResponse, $wgNoticeBannerMaxAge
* and $wgNoticeBannerReducedMaxAge. Never cache for logged-in users.
*
* TODO Couldn't we cache for logged-in users? See T149873
*/
private function sendHeaders() {
header( "Content-type: text/javascript; charset=utf-8" );
if ( !$this->getUser()->isRegistered() ) {
// Header tells front-end caches to retain the content for $sMaxAge seconds.
$sMaxAge = ( $this->cacheResponse === self::MAX_CACHE_NORMAL )
? $this->getConfig()->get( 'NoticeBannerMaxAge' )
: $this->getConfig()->get( 'NoticeBannerReducedMaxAge' );
} else {
$sMaxAge = 0;
}
header( "Cache-Control: public, s-maxage={$sMaxAge}, max-age=0" );
}
/**
* Generate the JS for the requested banner
* @return string of JavaScript containing a call to insertBanner()
* with JSON containing the banner content as the parameter
* @throws EmptyBannerException
* @throws StaleCampaignException
*/
public function getJsNotice() {
$banner = Banner::fromName( $this->bannerName );
if ( $this->requestType === self::USER_DISPLAY_REQUEST ) {
// The following will throw a CampaignExistenceException if there's
// no such campaign.
$campaign = new Campaign( $this->campaignName );
// Check that this is from a campaign that hasn't ended. We might get old
// campaigns due to forever-cached JS somewhere. Note that we include some
// leeway and don't consider archived or enabled status because the
// campaign might just have been updated and there is a normal caching lag for
// the data about campaigns sent to browsers.
$endTimePlusLeeway = wfTimestamp(
TS_UNIX,
(int)$campaign->getEndTime()->getTimestamp() + self::CAMPAIGN_STALENESS_LEEWAY
);
$now = wfTimestamp();
if ( $endTimePlusLeeway < $now ) {
throw new StaleCampaignException(
$this->bannerName, "Campaign: {$this->campaignName}" );
}
}
if ( $this->requestType === self::PREVIEW_UNSAVED_REQUEST ) {
$bannerRenderer = new BannerRenderer(
$this->getContext(),
$banner,
$this->campaignName,
$this->previewContent,
$this->previewMessages,
$this->debug
);
$jsCallbackFn = 'mw.centralNotice.adminUi.bannerEditor.updateBannerPreview';
} else {
if ( !$banner->exists() ) {
throw new EmptyBannerException( $this->bannerName );
}
$bannerRenderer = new BannerRenderer(
$this->getContext(),
$banner,
$this->campaignName,
null,
null,
$this->debug
);
$jsCallbackFn = 'mw.centralNotice.insertBanner';
}
$bannerHtml = $bannerRenderer->toHtml();
$bannerArray = [ 'bannerHtml' => $bannerHtml ];
$bannerJson = FormatJson::encode( $bannerArray );
$preload = $bannerRenderer->getPreloadJs();
return "{$preload}\n{$jsCallbackFn}( {$bannerJson} );";
}
}

File Metadata

Mime Type
text/x-php
Expires
Sat, Oct 3, 03:11 (3 w, 6 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
8d/a9/382873265f9ae9de5e5c1bd5fc81
Default Alt Text
SpecialBannerLoader.php (7 KB)

Event Timeline