ImportableUploadRevisionImporter.php 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <?php
  2. use MediaWiki\MediaWikiServices;
  3. use Psr\Log\LoggerInterface;
  4. /**
  5. * @since 1.31
  6. */
  7. class ImportableUploadRevisionImporter implements UploadRevisionImporter {
  8. /**
  9. * @var LoggerInterface
  10. */
  11. private $logger;
  12. /**
  13. * @var bool
  14. */
  15. private $enableUploads;
  16. /**
  17. * @var bool
  18. */
  19. private $shouldCreateNullRevision = true;
  20. /**
  21. * @param bool $enableUploads
  22. * @param LoggerInterface $logger
  23. */
  24. public function __construct(
  25. $enableUploads,
  26. LoggerInterface $logger
  27. ) {
  28. $this->enableUploads = $enableUploads;
  29. $this->logger = $logger;
  30. }
  31. /**
  32. * Setting this to false will deactivate the creation of a null revision as part of the upload
  33. * process logging in LocalFile::recordUpload2, see T193621
  34. *
  35. * @param bool $shouldCreateNullRevision
  36. */
  37. public function setNullRevisionCreation( $shouldCreateNullRevision ) {
  38. $this->shouldCreateNullRevision = $shouldCreateNullRevision;
  39. }
  40. /**
  41. * @return StatusValue
  42. */
  43. private function newNotOkStatus() {
  44. $statusValue = new StatusValue();
  45. $statusValue->setOK( false );
  46. return $statusValue;
  47. }
  48. public function import( ImportableUploadRevision $importableRevision ) {
  49. # Construct a file
  50. $archiveName = $importableRevision->getArchiveName();
  51. if ( $archiveName ) {
  52. $this->logger->debug( __METHOD__ . "Importing archived file as $archiveName\n" );
  53. $file = OldLocalFile::newFromArchiveName( $importableRevision->getTitle(),
  54. RepoGroup::singleton()->getLocalRepo(), $archiveName );
  55. } else {
  56. $file = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()
  57. ->newFile( $importableRevision->getTitle() );
  58. $file->load( File::READ_LATEST );
  59. $this->logger->debug( __METHOD__ . 'Importing new file as ' . $file->getName() . "\n" );
  60. if ( $file->exists() && $file->getTimestamp() > $importableRevision->getTimestamp() ) {
  61. $archiveName = $importableRevision->getTimestamp() . '!' . $file->getName();
  62. $file = OldLocalFile::newFromArchiveName( $importableRevision->getTitle(),
  63. RepoGroup::singleton()->getLocalRepo(), $archiveName );
  64. $this->logger->debug( __METHOD__ . "File already exists; importing as $archiveName\n" );
  65. }
  66. }
  67. if ( !$file ) {
  68. $this->logger->debug( __METHOD__ . ': Bad file for ' . $importableRevision->getTitle() . "\n" );
  69. return $this->newNotOkStatus();
  70. }
  71. # Get the file source or download if necessary
  72. $source = $importableRevision->getFileSrc();
  73. $autoDeleteSource = $importableRevision->isTempSrc();
  74. if ( !strlen( $source ) ) {
  75. $source = $this->downloadSource( $importableRevision );
  76. $autoDeleteSource = true;
  77. }
  78. if ( !strlen( $source ) ) {
  79. $this->logger->debug( __METHOD__ . ": Could not fetch remote file.\n" );
  80. return $this->newNotOkStatus();
  81. }
  82. $tmpFile = new TempFSFile( $source );
  83. if ( $autoDeleteSource ) {
  84. $tmpFile->autocollect();
  85. }
  86. $sha1File = ltrim( sha1_file( $source ), '0' );
  87. $sha1 = $importableRevision->getSha1();
  88. if ( $sha1 && ( $sha1 !== $sha1File ) ) {
  89. $this->logger->debug( __METHOD__ . ": Corrupt file $source.\n" );
  90. return $this->newNotOkStatus();
  91. }
  92. $user = $importableRevision->getUserObj()
  93. ?: User::newFromName( $importableRevision->getUser(), false );
  94. # Do the actual upload
  95. if ( $file instanceof OldLocalFile ) {
  96. $status = $file->uploadOld(
  97. $source,
  98. $importableRevision->getTimestamp(),
  99. $importableRevision->getComment(),
  100. $user
  101. );
  102. } else {
  103. $flags = 0;
  104. $status = $file->upload(
  105. $source,
  106. $importableRevision->getComment(),
  107. $importableRevision->getComment(),
  108. $flags,
  109. false,
  110. $importableRevision->getTimestamp(),
  111. $user,
  112. [],
  113. $this->shouldCreateNullRevision
  114. );
  115. }
  116. if ( $status->isGood() ) {
  117. $this->logger->debug( __METHOD__ . ": Successful\n" );
  118. } else {
  119. $this->logger->debug( __METHOD__ . ': failed: ' . $status->getHTML() . "\n" );
  120. }
  121. return $status;
  122. }
  123. /**
  124. * @deprecated DO NOT CALL ME.
  125. * This method was introduced when factoring (Importable)UploadRevisionImporter out of
  126. * WikiRevision. It only has 1 use by the deprecated downloadSource method in WikiRevision.
  127. * Do not use this in new code, it will be made private soon.
  128. *
  129. * @param ImportableUploadRevision $wikiRevision
  130. *
  131. * @return bool|string
  132. */
  133. public function downloadSource( ImportableUploadRevision $wikiRevision ) {
  134. if ( !$this->enableUploads ) {
  135. return false;
  136. }
  137. $tempo = tempnam( wfTempDir(), 'download' );
  138. $f = fopen( $tempo, 'wb' );
  139. if ( !$f ) {
  140. $this->logger->debug( "IMPORT: couldn't write to temp file $tempo\n" );
  141. return false;
  142. }
  143. // @todo FIXME!
  144. $src = $wikiRevision->getSrc();
  145. $data = MediaWikiServices::getInstance()->getHttpRequestFactory()->
  146. get( $src, [], __METHOD__ );
  147. if ( !$data ) {
  148. $this->logger->debug( "IMPORT: couldn't fetch source $src\n" );
  149. fclose( $f );
  150. unlink( $tempo );
  151. return false;
  152. }
  153. fwrite( $f, $data );
  154. fclose( $f );
  155. return $tempo;
  156. }
  157. }