MediaWiki master
PublishStashedFileJob.php
Go to the documentation of this file.
1<?php
24use Wikimedia\ScopedCallback;
25
33
34 public function __construct( array $params ) {
35 parent::__construct( 'PublishStashedFile', $params );
36 $this->removeDuplicates = true;
37 }
38
39 public function run() {
40 $scope = RequestContext::importScopedSession( $this->params['session'] );
41 $this->addTeardownCallback( static function () use ( &$scope ) {
42 ScopedCallback::consume( $scope ); // T126450
43 } );
44
45 $context = RequestContext::getMain();
46 $user = $context->getUser();
47 try {
48 if ( !$user->isRegistered() ) {
49 $this->setLastError( "Could not load the author user from session." );
50
51 return false;
52 }
53
54 $startingStatus = UploadBase::getSessionStatus( $user, $this->params['filekey'] );
55 // Warn if in wrong stage, but still continue. User may be able to trigger
56 // this by retrying after failure.
57 if (
58 !$startingStatus ||
59 ( $startingStatus['result'] ?? '' ) !== 'Poll' ||
60 ( $startingStatus['stage'] ?? '' ) !== 'queued'
61 ) {
62 $logger = LoggerFactory::getInstance( 'upload' );
63 $logger->warning( "Tried to publish upload that is in stage {stage}/{result}",
64 [
65 'stage' => $startingStatus['stage'] ?? '-',
66 'result' => $startingStatus['result'] ?? '-',
67 'status' => (string)( $startingStatus['status'] ?? '-' ),
68 'filekey' => $this->params['filekey'],
69 'filename' => $this->params['filename'],
70 'user' => $user->getName(),
71 ]
72 );
73 }
74
75 UploadBase::setSessionStatus(
76 $user,
77 $this->params['filekey'],
78 [ 'result' => 'Poll', 'stage' => 'publish', 'status' => Status::newGood() ]
79 );
80
81 $upload = new UploadFromStash( $user );
82 // @todo initialize() causes a GET, ideally we could frontload the antivirus
83 // checks and anything else to the stash stage (which includes concatenation and
84 // the local file is thus already there). That way, instead of GET+PUT, there could
85 // just be a COPY operation from the stash to the public zone.
86 $upload->initialize( $this->params['filekey'], $this->params['filename'] );
87
88 // Check if the local file checks out (this is generally a no-op)
89 $verification = $upload->verifyUpload();
90 if ( $verification['status'] !== UploadBase::OK ) {
91 $status = Status::newFatal( 'verification-error' );
92 $status->value = [ 'verification' => $verification ];
93 UploadBase::setSessionStatus(
94 $user,
95 $this->params['filekey'],
96 [ 'result' => 'Failure', 'stage' => 'publish', 'status' => $status ]
97 );
98 $this->setLastError( "Could not verify upload." );
99
100 return false;
101 }
102
103 // Upload the stashed file to a permanent location
104 $status = $upload->performUpload(
105 $this->params['comment'],
106 $this->params['text'],
107 $this->params['watch'],
108 $user,
109 $this->params['tags'] ?? [],
110 $this->params['watchlistexpiry'] ?? null
111 );
112 if ( !$status->isGood() ) {
113 UploadBase::setSessionStatus(
114 $user,
115 $this->params['filekey'],
116 [ 'result' => 'Failure', 'stage' => 'publish', 'status' => $status ]
117 );
118 $this->setLastError( $status->getWikiText( false, false, 'en' ) );
119
120 return false;
121 }
122
123 // Build the image info array while we have the local reference handy
124 $apiUpload = ApiUpload::getDummyInstance();
125 $imageInfo = $apiUpload->getUploadImageInfo( $upload );
126
127 // Cleanup any temporary local file
128 $upload->cleanupTempFile();
129
130 // Cache the info so the user doesn't have to wait forever to get the final info
131 UploadBase::setSessionStatus(
132 $user,
133 $this->params['filekey'],
134 [
135 'result' => 'Success',
136 'stage' => 'publish',
137 'filename' => $upload->getLocalFile()->getName(),
138 'imageinfo' => $imageInfo,
139 'status' => Status::newGood()
140 ]
141 );
142 } catch ( Exception $e ) {
143 UploadBase::setSessionStatus(
144 $user,
145 $this->params['filekey'],
146 [
147 'result' => 'Failure',
148 'stage' => 'publish',
149 'status' => Status::newFatal( 'api-error-publishfailed' )
150 ]
151 );
152 $this->setLastError( get_class( $e ) . ": " . $e->getMessage() );
153 // To prevent potential database referential integrity issues.
154 // See T34551.
155 MWExceptionHandler::rollbackPrimaryChangesAndLog( $e );
156
157 return false;
158 }
159
160 return true;
161 }
162
163 public function getDeduplicationInfo() {
164 $info = parent::getDeduplicationInfo();
165 if ( is_array( $info['params'] ) ) {
166 $info['params'] = [ 'filekey' => $info['params']['filekey'] ];
167 }
168
169 return $info;
170 }
171
172 public function allowRetries() {
173 return false;
174 }
175}
static getDummyInstance()
Class to both describe a background job and handle jobs.
Definition Job.php:40
setLastError( $error)
Definition Job.php:434
addTeardownCallback( $callback)
Definition Job.php:356
Group all the pieces relevant to the context of a request into one instance.
Create PSR-3 logger objects.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:54
Upload a file from the upload stash into the local file repo.
allowRetries()
bool Whether this job can be retried on failure by job runners 1.21to override
getDeduplicationInfo()
Subclasses may need to override this to make duplication detection work.
Implements uploading from previously stored file.
Interface for generic jobs only uses the parameters field and are JSON serializable.