MediaWiki  master
importTextFiles.php
Go to the documentation of this file.
1 <?php
27 
28 require_once __DIR__ . '/Maintenance.php';
29 
37  public function __construct() {
38  parent::__construct();
39  $this->addDescription( 'Reads in text files and imports their content to pages of the wiki' );
40  $this->addOption( 'user', 'Username to which edits should be attributed. ' .
41  'Default: "Maintenance script"', false, true, 'u' );
42  $this->addOption( 'summary', 'Specify edit summary for the edits', false, true, 's' );
43  $this->addOption( 'use-timestamp', 'Use the modification date of the text file ' .
44  'as the timestamp for the edit' );
45  $this->addOption( 'overwrite', 'Overwrite existing pages. If --use-timestamp is passed, this ' .
46  'will only overwrite pages if the file has been modified since the page was last modified.' );
47  $this->addOption( 'prefix', 'A string to place in front of the file name', false, true, 'p' );
48  $this->addOption( 'bot', 'Mark edits as bot edits in the recent changes list.' );
49  $this->addOption( 'rc', 'Place revisions in RecentChanges.' );
50  $this->addArg( 'files', 'Files to import' );
51  }
52 
53  public function execute() {
54  $userName = $this->getOption( 'user', false );
55  $summary = $this->getOption( 'summary', 'Imported from text file' );
56  $useTimestamp = $this->hasOption( 'use-timestamp' );
57  $rc = $this->hasOption( 'rc' );
58  $bot = $this->hasOption( 'bot' );
59  $overwrite = $this->hasOption( 'overwrite' );
60  $prefix = $this->getOption( 'prefix', '' );
61 
62  // Get all the arguments. A loop is required since Maintenance doesn't
63  // support an arbitrary number of arguments.
64  $files = [];
65  $i = 0;
66  while ( $arg = $this->getArg( $i++ ) ) {
67  if ( file_exists( $arg ) ) {
68  $files[$arg] = file_get_contents( $arg );
69  } else {
70  // use glob to support the Windows shell, which doesn't automatically
71  // expand wildcards
72  $found = false;
73  foreach ( glob( $arg ) as $filename ) {
74  $found = true;
75  $files[$filename] = file_get_contents( $filename );
76  }
77  if ( !$found ) {
78  $this->fatalError( "Fatal error: The file '$arg' does not exist!" );
79  }
80  }
81  }
82 
83  $count = count( $files );
84  $this->output( "Importing $count pages...\n" );
85 
86  if ( $userName === false ) {
87  $user = User::newSystemUser( User::MAINTENANCE_SCRIPT_USER, [ 'steal' => true ] );
88  } else {
89  $user = User::newFromName( $userName );
90  }
91 
92  if ( !$user ) {
93  $this->fatalError( "Invalid username\n" );
94  }
95  if ( $user->isAnon() ) {
96  $user->addToDatabase();
97  }
98 
99  $exit = 0;
100 
101  $successCount = 0;
102  $failCount = 0;
103  $skipCount = 0;
104 
105  $revLookup = $this->getServiceContainer()->getRevisionLookup();
106  foreach ( $files as $file => $text ) {
107  $pageName = $prefix . pathinfo( $file, PATHINFO_FILENAME );
108  $timestamp = $useTimestamp ? wfTimestamp( TS_UNIX, filemtime( $file ) ) : wfTimestampNow();
109 
110  $title = Title::newFromText( $pageName );
111  // Have to check for # manually, since it gets interpreted as a fragment
112  if ( !$title || $title->hasFragment() ) {
113  $this->error( "Invalid title $pageName. Skipping.\n" );
114  $skipCount++;
115  continue;
116  }
117 
118  $exists = $title->exists();
119  $oldRevID = $title->getLatestRevID();
120  $oldRevRecord = $oldRevID ? $revLookup->getRevisionById( $oldRevID ) : null;
121  $actualTitle = $title->getPrefixedText();
122 
123  if ( $exists ) {
124  $touched = wfTimestamp( TS_UNIX, $title->getTouched() );
125  if ( !$overwrite ) {
126  $this->output( "Title $actualTitle already exists. Skipping.\n" );
127  $skipCount++;
128  continue;
129  } elseif ( $useTimestamp && intval( $touched ) >= intval( $timestamp ) ) {
130  $this->output( "File for title $actualTitle has not been modified since the " .
131  "destination page was touched. Skipping.\n" );
132  $skipCount++;
133  continue;
134  }
135  }
136 
137  $content = ContentHandler::makeContent( rtrim( $text ), $title );
138  $rev = new WikiRevision();
139  $rev->setContent( SlotRecord::MAIN, $content );
140  $rev->setTitle( $title );
141  $rev->setUserObj( $user );
142  $rev->setComment( $summary );
143  $rev->setTimestamp( $timestamp );
144 
145  if ( $exists &&
146  $overwrite &&
147  $rev->getContent()->equals( $oldRevRecord->getContent( SlotRecord::MAIN ) )
148  ) {
149  $this->output( "File for title $actualTitle contains no changes from the current " .
150  "revision. Skipping.\n" );
151  $skipCount++;
152  continue;
153  }
154 
155  $status = $rev->importOldRevision();
156  $newId = $title->getLatestRevID();
157 
158  if ( $status ) {
159  $action = $exists ? 'updated' : 'created';
160  $this->output( "Successfully $action $actualTitle\n" );
161  $successCount++;
162  } else {
163  $action = $exists ? 'update' : 'create';
164  $this->output( "Failed to $action $actualTitle\n" );
165  $failCount++;
166  $exit = 1;
167  }
168 
169  // Create the RecentChanges entry if necessary
170  if ( $rc && $status ) {
171  if ( $exists ) {
172  if ( is_object( $oldRevRecord ) ) {
174  $timestamp,
175  $title,
176  $rev->getMinor(),
177  $user,
178  $summary,
179  $oldRevID,
180  $oldRevRecord->getTimestamp(),
181  $bot,
182  '',
183  $oldRevRecord->getSize(),
184  $rev->getSize(),
185  $newId,
186  // the pages don't need to be patrolled
187  1
188  );
189  }
190  } else {
192  $timestamp,
193  $title,
194  $rev->getMinor(),
195  $user,
196  $summary,
197  $bot,
198  '',
199  $rev->getSize(),
200  $newId,
201  1
202  );
203  }
204  }
205  }
206 
207  $this->output( "Done! $successCount succeeded, $skipCount skipped.\n" );
208  if ( $exit ) {
209  $this->fatalError( "Import failed with $failCount failed pages.\n", $exit );
210  }
211  }
212 }
213 
214 $maintClass = ImportTextFiles::class;
215 require_once RUN_MAINTENANCE_IF_MAIN;
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
Maintenance script which reads in text files and imports their content to a page of the wiki.
execute()
Do the actual work.
__construct()
Default constructor.
Abstract maintenance class for quickly writing and churning out maintenance scripts with minimal effo...
Definition: Maintenance.php:66
error( $err, $die=0)
Throw an error to the user.
addArg( $arg, $description, $required=true, $multi=false)
Add some args that are needed.
output( $out, $channel=null)
Throw some output to the user.
hasOption( $name)
Checks to see if a particular option was set.
getServiceContainer()
Returns the main service container.
getArg( $argId=0, $default=null)
Get an argument.
addDescription( $text)
Set the description text.
addOption( $name, $description, $required=false, $withArg=false, $shortName=false, $multiOccurrence=false)
Add a parameter to the script.
getOption( $name, $default=null)
Get an option, or return the default.
fatalError( $msg, $exitCode=1)
Output a message and terminate the current script.
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:40
Represents a title within MediaWiki.
Definition: Title.php:76
internal since 1.36
Definition: User.php:98
static notifyEdit( $timestamp, $page, $minor, $user, $comment, $oldId, $lastTimestamp, $bot, $ip='', $oldSize=0, $newSize=0, $newId=0, $patrol=0, $tags=[], EditResult $editResult=null)
Makes an entry in the database corresponding to an edit.
static notifyNew( $timestamp, $page, $minor, $user, $comment, $bot, $ip='', $size=0, $newId=0, $patrol=0, $tags=[])
Makes an entry in the database corresponding to page creation.
Represents a revision, log entry or upload during the import process.
$maintClass
$content
Definition: router.php:76
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42