34use Wikimedia\Timestamp\TimestampFormat as TS;
47 private readonly
Parser $parser,
53 parent::__construct( $article, $context );
77 ContentSecurityPolicy::sendRestrictiveHeader();
80 $config = $this->context->getConfig();
82 if ( $this->
getOutput()->checkLastModified(
92 $smaxage =
$request->getIntOrNull(
'smaxage' );
93 if ( $smaxage ===
null ) {
95 $contentType ===
'text/css' ||
96 $contentType ===
'application/json' ||
97 $contentType ===
'text/javascript'
110 $response->header( $this->
getOutput()->getVaryHeader() );
114 $privateCache = !$this->permissionManager->isEveryoneAllowed(
'read' ) &&
115 ( $smaxage === 0 ||
$request->getSession()->isPersistent() );
117 $privateCache = $privateCache || $this->
getUser()->isRegistered();
118 $mode = $privateCache ?
'private' :
'public';
120 'Cache-Control: ' . $mode .
', s-maxage=' . $smaxage .
', max-age=' . $maxage
128 if ( $title->isUserConfigPage() && $contentType !==
'text/x-wiki' ) {
131 $rootPage = strtok( $title->getText(),
'/' );
132 $userFromTitle = $this->userFactory->newFromName( $rootPage, UserRigorOptions::RIGOR_USABLE );
133 if ( !$userFromTitle || !$userFromTitle->isRegistered() ) {
134 $elevated = $this->
getAuthority()->isAllowed(
'editinterface' );
135 $elevatedText = $elevated ?
'by elevated ' :
'';
136 $log = LoggerFactory::getInstance(
"security" );
138 "Unsafe JS/CSS/Json {$elevatedText}load - {user} loaded {title} with {ctype}",
141 'title' => $title->getPrefixedDBkey(),
142 'ctype' => $contentType,
143 'elevated' => $elevated
157 $contentType ===
'text/javascript' &&
158 !$title->isUserJsConfigPage() &&
161 $pageRestrictions = $this->restrictionStore->getRestrictions( $title,
'edit' );
162 if ( !in_array(
'sysop', $pageRestrictions ) &&
163 !in_array(
'editprotected', $pageRestrictions )
165 $log = LoggerFactory::getInstance(
"security" );
166 $log->info(
"Blocked loading unprotected JS {title} for {user}",
169 'title' => $title->getPrefixedDBkey(),
182 if ( $contentType ===
'text/javascript' && $title->exists() ) {
183 if ( !( $title->isSiteJsConfigPage() || $title->isUserJsConfigPage() ) ) {
185 ->getRedirectLookup()
186 ->getRedirectTarget( $title );
187 $redirectTitle = $redirectTarget ? Title::newFromLinkTarget( $redirectTarget ) :
null;
188 $isRedirectToJsConfigPage = $redirectTitle &&
189 ( $redirectTitle->isSiteJsConfigPage() || $redirectTitle->isUserJsConfigPage() );
191 $log = LoggerFactory::getInstance(
"security" );
192 if ( $isRedirectToJsConfigPage ) {
194 "Did not block loading JS redirect {title} to {redirectTarget} "
195 .
"for {user} with more restrictions",
198 'title' => $title->getPrefixedDBkey(),
199 'redirectTarget' => $redirectTitle->getPrefixedDBkey(),
203 $log->info(
"Did not block loading unprotected JS {title} for {user} with more restrictions",
206 'title' => $title->getPrefixedDBkey(),
213 $response->header(
'Content-type: ' . $contentType .
'; charset=UTF-8' );
221 if ( $text ===
false && $contentType ===
'text/x-wiki' ) {
222 $response->statusHeader( 404 );
225 if ( !$this->
getHookRunner()->onRawPageViewBeforeOutput( $this, $text ) ) {
226 wfDebug( __METHOD__ .
": RawPageViewBeforeOutput hook broke raw page output." );
246 $rev = $this->revisionLookup->getRevisionByTitle( $title, $this->
getOldId() );
248 $lastMod =
wfTimestamp( TS::RFC2822, $rev->getTimestamp() );
249 $request->response()->header(
"Last-modified: $lastMod" );
253 $slot = $this->
getRequest()->getText(
'slot' );
255 if ( $rev->hasSlot( $slot ) ) {
256 $content = $rev->getContent( $slot );
261 $content = $rev->getContent( SlotRecord::MAIN );
264 if ( $content ===
null ) {
266 } elseif ( !$content instanceof
TextContent && !method_exists( $content,
'getText' ) ) {
270 "Unsupported Media Type",
"The requested page uses the content model `"
271 . $content->getModel() .
"` which is not supported via this interface."
276 $section =
$request->getIntOrNull(
'section' );
277 if ( $section !==
null ) {
278 $content = $content->getSection( $section );
281 if ( $content !==
null && $content !==
false ) {
283 $text = $content->getText();
288 if ( $text !==
false && $text !==
'' &&
$request->getRawVal(
'templates' ) ===
'expand' ) {
289 $text = $this->parser->preprocess(
305 $oldId = $this->
getRequest()->getInt(
'oldid' );
306 $rl = $this->revisionLookup;
307 switch ( $this->
getRequest()->getText(
'direction' ) ) {
309 # output next revision, or nothing if there isn't one
312 $oldRev = $rl->getRevisionById( $oldId );
314 $nextRev = $rl->getNextRevision( $oldRev );
317 $oldId = $nextRev ? $nextRev->getId() : -1;
320 # output previous revision, or nothing if there isn't one
323 # get the latest revision so we can get the penultimate one
326 $oldRev = $rl->getRevisionById( $oldId );
328 $prevRev = $rl->getPreviousRevision( $oldRev );
330 $oldId = $prevRev ? $prevRev->getId() : -1;
352 $gen = $this->
getRequest()->getRawVal(
'gen' );
353 if ( $gen ==
'js' ) {
354 $ctype =
'text/javascript';
355 } elseif ( $gen ==
'css' ) {
360 static $allowedCTypes = [
365 'application/x-zope-edit',
368 if (
$ctype ==
'' || !in_array(
$ctype, $allowedCTypes ) ) {
377class_alias( RawAction::class,
'RawAction' );
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
wfHttpError( $code, $label, $desc)
Provide a simple HTTP error.
wfTimestamp( $outputtype=TS::UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Content object implementation for representing flat text.
A class containing constants representing the names of configuration variables.
const CdnMaxAge
Name constant for the CdnMaxAge setting, for use with Config::get()
const ForcedRawSMaxage
Name constant for the ForcedRawSMaxage setting, for use with Config::get()
Legacy class representing an editable page and handling UI for some page actions.
Handle sending Content-Security-Policy headers.
Interface for objects which can provide a MediaWiki context on request.