/** * Perform the actual upload. Returns a suitable result array on success; * dies on failure. * * @param array $warnings Array of Api upload warnings * @return array */ protected function performUpload($warnings) { // Use comment as initial page text by default if (is_null($this->mParams['text'])) { $this->mParams['text'] = $this->mParams['comment']; } /** @var $file LocalFile */ $file = $this->mUpload->getLocalFile(); // For preferences mode, we want to watch if 'watchdefault' is set, // or if the *file* doesn't exist, and either 'watchuploads' or // 'watchcreations' is set. But getWatchlistValue()'s automatic // handling checks if the *title* exists or not, so we need to check // all three preferences manually. $watch = $this->getWatchlistValue($this->mParams['watchlist'], $file->getTitle(), 'watchdefault'); if (!$watch && $this->mParams['watchlist'] == 'preferences' && !$file->exists()) { $watch = $this->getWatchlistValue('preferences', $file->getTitle(), 'watchuploads') || $this->getWatchlistValue('preferences', $file->getTitle(), 'watchcreations'); } // Deprecated parameters if ($this->mParams['watch']) { $watch = true; } if ($this->mParams['tags']) { $status = ChangeTags::canAddTagsAccompanyingChange($this->mParams['tags'], $this->getUser()); if (!$status->isOK()) { $this->dieStatus($status); } } // No errors, no warnings: do the upload if ($this->mParams['async']) { $progress = UploadBase::getSessionStatus($this->getUser(), $this->mParams['filekey']); if ($progress && $progress['result'] === 'Poll') { $this->dieUsage('Upload from stash already in progress.', 'publishfailed'); } UploadBase::setSessionStatus($this->getUser(), $this->mParams['filekey'], ['result' => 'Poll', 'stage' => 'queued', 'status' => Status::newGood()]); JobQueueGroup::singleton()->push(new PublishStashedFileJob(Title::makeTitle(NS_FILE, $this->mParams['filename']), ['filename' => $this->mParams['filename'], 'filekey' => $this->mParams['filekey'], 'comment' => $this->mParams['comment'], 'tags' => $this->mParams['tags'], 'text' => $this->mParams['text'], 'watch' => $watch, 'session' => $this->getContext()->exportSession()])); $result['result'] = 'Poll'; $result['stage'] = 'queued'; } else { /** @var $status Status */ $status = $this->mUpload->performUpload($this->mParams['comment'], $this->mParams['text'], $watch, $this->getUser(), $this->mParams['tags']); if (!$status->isGood()) { // Is there really no better way to do this? $errors = $status->getErrorsByType('error'); $msg = array_merge([$errors[0]['message']], $errors[0]['params']); $data = $status->getErrorsArray(); ApiResult::setIndexedTagName($data, 'error'); // For backwards-compatibility, we use the 'internal-error' fallback key and merge $data // into the root of the response (rather than something sane like [ 'details' => $data ]). $this->dieRecoverableError($msg, null, $data, 'internal-error'); } $result['result'] = 'Success'; } $result['filename'] = $file->getName(); if ($warnings && count($warnings) > 0) { $result['warnings'] = $warnings; } return $result; }
/** * Perform the actual upload. Returns a suitable result array on success; * dies on failure. * * @param array $warnings Array of Api upload warnings * @return array */ protected function performUpload($warnings) { // Use comment as initial page text by default if (is_null($this->mParams['text'])) { $this->mParams['text'] = $this->mParams['comment']; } /** @var $file File */ $file = $this->mUpload->getLocalFile(); // For preferences mode, we want to watch if 'watchdefault' is set or // if the *file* doesn't exist and 'watchcreations' is set. But // getWatchlistValue()'s automatic handling checks if the *title* // exists or not, so we need to check both prefs manually. $watch = $this->getWatchlistValue($this->mParams['watchlist'], $file->getTitle(), 'watchdefault'); if (!$watch && $this->mParams['watchlist'] == 'preferences' && !$file->exists()) { $watch = $this->getWatchlistValue($this->mParams['watchlist'], $file->getTitle(), 'watchcreations'); } // Deprecated parameters if ($this->mParams['watch']) { $this->logFeatureUsage('action=upload&watch'); $watch = true; } // No errors, no warnings: do the upload if ($this->mParams['async']) { $progress = UploadBase::getSessionStatus($this->getUser(), $this->mParams['filekey']); if ($progress && $progress['result'] === 'Poll') { $this->dieUsage("Upload from stash already in progress.", 'publishfailed'); } UploadBase::setSessionStatus($this->getUser(), $this->mParams['filekey'], array('result' => 'Poll', 'stage' => 'queued', 'status' => Status::newGood())); JobQueueGroup::singleton()->push(new PublishStashedFileJob(Title::makeTitle(NS_FILE, $this->mParams['filename']), array('filename' => $this->mParams['filename'], 'filekey' => $this->mParams['filekey'], 'comment' => $this->mParams['comment'], 'text' => $this->mParams['text'], 'watch' => $watch, 'session' => $this->getContext()->exportSession()))); $result['result'] = 'Poll'; $result['stage'] = 'queued'; } else { /** @var $status Status */ $status = $this->mUpload->performUpload($this->mParams['comment'], $this->mParams['text'], $watch, $this->getUser()); if (!$status->isGood()) { $error = $status->getErrorsArray(); if (count($error) == 1 && $error[0][0] == 'async') { // The upload can not be performed right now, because the user // requested so return array('result' => 'Queued', 'statuskey' => $error[0][1]); } ApiResult::setIndexedTagName($error, 'error'); $this->dieUsage('An internal error occurred', 'internal-error', 0, $error); } $result['result'] = 'Success'; } $result['filename'] = $file->getName(); if ($warnings && count($warnings) > 0) { $result['warnings'] = $warnings; } return $result; }
public function run() { $scope = RequestContext::importScopedSession($this->params['session']); $context = RequestContext::getMain(); try { $user = $context->getUser(); if (!$user->isLoggedIn()) { $this->setLastError("Could not load the author user from session."); return false; } if (count($_SESSION) === 0) { // Empty session probably indicates that we didn't associate // with the session correctly. Note that being able to load // the user does not necessarily mean the session was loaded. // Most likely cause by suhosin.session.encrypt = On. $this->setLastError("Error associating with user session. " . "Try setting suhosin.session.encrypt = Off"); return false; } UploadBase::setSessionStatus($this->params['filekey'], array('result' => 'Poll', 'stage' => 'assembling', 'status' => Status::newGood())); $upload = new UploadFromChunks($user); $upload->continueChunks($this->params['filename'], $this->params['filekey'], $context->getRequest()); // Combine all of the chunks into a local file and upload that to a new stash file $status = $upload->concatenateChunks(); if (!$status->isGood()) { UploadBase::setSessionStatus($this->params['filekey'], array('result' => 'Failure', 'stage' => 'assembling', 'status' => $status)); $this->setLastError($status->getWikiText()); return false; } // We have a new filekey for the fully concatenated file $newFileKey = $upload->getLocalFile()->getFileKey(); // Remove the old stash file row and first chunk file $upload->stash->removeFileNoAuth($this->params['filekey']); // Build the image info array while we have the local reference handy $apiMain = new ApiMain(); // dummy object (XXX) $imageInfo = $upload->getImageInfo($apiMain->getResult()); // Cleanup any temporary local file $upload->cleanupTempFile(); // Cache the info so the user doesn't have to wait forever to get the final info UploadBase::setSessionStatus($this->params['filekey'], array('result' => 'Success', 'stage' => 'assembling', 'filekey' => $newFileKey, 'imageinfo' => $imageInfo, 'status' => Status::newGood())); } catch (MWException $e) { UploadBase::setSessionStatus($this->params['filekey'], array('result' => 'Failure', 'stage' => 'assembling', 'status' => Status::newFatal('api-error-stashfailed'))); $this->setLastError(get_class($e) . ": " . $e->getText()); // To be extra robust. MWExceptionHandler::rollbackMasterChangesAndLog($e); return false; } return true; }
public function run() { $scope = RequestContext::importScopedSession($this->params['session']); $this->addTeardownCallback(function () use(&$scope) { ScopedCallback::consume($scope); // T126450 }); $context = RequestContext::getMain(); $user = $context->getUser(); try { if (!$user->isLoggedIn()) { $this->setLastError("Could not load the author user from session."); return false; } UploadBase::setSessionStatus($user, $this->params['filekey'], ['result' => 'Poll', 'stage' => 'assembling', 'status' => Status::newGood()]); $upload = new UploadFromChunks($user); $upload->continueChunks($this->params['filename'], $this->params['filekey'], new WebRequestUpload($context->getRequest(), 'null')); // Combine all of the chunks into a local file and upload that to a new stash file $status = $upload->concatenateChunks(); if (!$status->isGood()) { UploadBase::setSessionStatus($user, $this->params['filekey'], ['result' => 'Failure', 'stage' => 'assembling', 'status' => $status]); $this->setLastError($status->getWikiText(false, false, 'en')); return false; } // We have a new filekey for the fully concatenated file $newFileKey = $upload->getLocalFile()->getFileKey(); // Remove the old stash file row and first chunk file $upload->stash->removeFileNoAuth($this->params['filekey']); // Build the image info array while we have the local reference handy $apiMain = new ApiMain(); // dummy object (XXX) $imageInfo = $upload->getImageInfo($apiMain->getResult()); // Cleanup any temporary local file $upload->cleanupTempFile(); // Cache the info so the user doesn't have to wait forever to get the final info UploadBase::setSessionStatus($user, $this->params['filekey'], ['result' => 'Success', 'stage' => 'assembling', 'filekey' => $newFileKey, 'imageinfo' => $imageInfo, 'status' => Status::newGood()]); } catch (Exception $e) { UploadBase::setSessionStatus($user, $this->params['filekey'], ['result' => 'Failure', 'stage' => 'assembling', 'status' => Status::newFatal('api-error-stashfailed')]); $this->setLastError(get_class($e) . ": " . $e->getMessage()); // To be extra robust. MWExceptionHandler::rollbackMasterChangesAndLog($e); return false; } return true; }