]> scripts.mit.edu Git - autoinstalls/mediawiki.git/blob - includes/specials/SpecialImport.php
MediaWiki 1.17.0
[autoinstalls/mediawiki.git] / includes / specials / SpecialImport.php
1 <?php
2 /**
3  * Implements Special:Import
4  *
5  * Copyright © 2003,2005 Brion Vibber <brion@pobox.com>
6  * http://www.mediawiki.org/
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  * http://www.gnu.org/copyleft/gpl.html
22  *
23  * @file
24  * @ingroup SpecialPage
25  */
26
27 /**
28  * MediaWiki page data importer
29  *
30  * @ingroup SpecialPage
31  */
32 class SpecialImport extends SpecialPage {
33         
34         private $interwiki = false;
35         private $namespace;
36         private $frompage = '';
37         private $logcomment= false;
38         private $history = true;
39         private $includeTemplates = false;
40         
41         /**
42          * Constructor
43          */
44         public function __construct() {
45                 parent::__construct( 'Import', 'import' );
46                 global $wgImportTargetNamespace;
47                 $this->namespace = $wgImportTargetNamespace;
48         }
49         
50         /**
51          * Execute
52          */
53         function execute( $par ) {
54                 global $wgRequest, $wgUser, $wgOut;
55                 
56                 $this->setHeaders();
57                 $this->outputHeader();
58                 
59                 if ( wfReadOnly() ) {
60                         $wgOut->readOnlyPage();
61                         return;
62                 }
63                 
64                 if( !$wgUser->isAllowed( 'import' ) && !$wgUser->isAllowed( 'importupload' ) )
65                         return $wgOut->permissionRequired( 'import' );
66
67                 # TODO: allow Title::getUserPermissionsErrors() to take an array
68                 # FIXME: Title::checkSpecialsAndNSPermissions() has a very wierd expectation of what
69                 # getUserPermissionsErrors() might actually be used for, hence the 'ns-specialprotected'
70                 $errors = wfMergeErrorArrays(
71                         $this->getTitle()->getUserPermissionsErrors(
72                                 'import', $wgUser, true,
73                                 array( 'ns-specialprotected', 'badaccess-group0', 'badaccess-groups' )
74                         ),
75                         $this->getTitle()->getUserPermissionsErrors(
76                                 'importupload', $wgUser, true,
77                                 array( 'ns-specialprotected', 'badaccess-group0', 'badaccess-groups' )
78                         )
79                 );
80
81                 if( $errors ){
82                         $wgOut->showPermissionsErrorPage( $errors );
83                         return;
84                 }
85
86                 if ( $wgRequest->wasPosted() && $wgRequest->getVal( 'action' ) == 'submit' ) {
87                         $this->doImport();
88                 }
89                 $this->showForm();
90         }
91         
92         /**
93          * Do the actual import
94          */
95         private function doImport() {
96                 global $wgOut, $wgRequest, $wgUser, $wgImportSources, $wgExportMaxLinkDepth;
97                 $isUpload = false;
98                 $this->namespace = $wgRequest->getIntOrNull( 'namespace' );
99                 $sourceName = $wgRequest->getVal( "source" );
100
101                 $this->logcomment = $wgRequest->getText( 'log-comment' );
102                 $this->pageLinkDepth = $wgExportMaxLinkDepth == 0 ? 0 : $wgRequest->getIntOrNull( 'pagelink-depth' );
103
104                 if ( !$wgUser->matchEditToken( $wgRequest->getVal( 'editToken' ) ) ) {
105                         $source = Status::newFatal( 'import-token-mismatch' );
106                 } elseif ( $sourceName == 'upload' ) {
107                         $isUpload = true;
108                         if( $wgUser->isAllowed( 'importupload' ) ) {
109                                 $source = ImportStreamSource::newFromUpload( "xmlimport" );
110                         } else {
111                                 return $wgOut->permissionRequired( 'importupload' );
112                         }
113                 } elseif ( $sourceName == "interwiki" ) {
114                         if( !$wgUser->isAllowed( 'import' ) ){
115                                 return $wgOut->permissionRequired( 'import' );
116                         }
117                         $this->interwiki = $wgRequest->getVal( 'interwiki' );
118                         if ( !in_array( $this->interwiki, $wgImportSources ) ) {
119                                 $source = Status::newFatal( "import-invalid-interwiki" );
120                         } else {
121                                 $this->history = $wgRequest->getCheck( 'interwikiHistory' );
122                                 $this->frompage = $wgRequest->getText( "frompage" );
123                                 $this->includeTemplates = $wgRequest->getCheck( 'interwikiTemplates' );
124                                 $source = ImportStreamSource::newFromInterwiki(
125                                         $this->interwiki,
126                                         $this->frompage,
127                                         $this->history,
128                                         $this->includeTemplates,
129                                         $this->pageLinkDepth );
130                         }
131                 } else {
132                         $source = Status::newFatal( "importunknownsource" );
133                 }
134
135                 if( !$source->isGood() ) {
136                         $wgOut->wrapWikiMsg( "<p class=\"error\">\n$1\n</p>", array( 'importfailed', $source->getWikiText() ) );
137                 } else {
138                         $wgOut->addWikiMsg( "importstart" );
139
140                         $importer = new WikiImporter( $source->value );
141                         if( !is_null( $this->namespace ) ) {
142                                 $importer->setTargetNamespace( $this->namespace );
143                         }
144                         $reporter = new ImportReporter( $importer, $isUpload, $this->interwiki , $this->logcomment);
145                         $exception = false;
146
147                         $reporter->open();
148                         try {
149                                 $importer->doImport();
150                         } catch ( MWException $e ) {
151                                 $exception = $e;
152                         }
153                         $result = $reporter->close();
154
155                         if ( $exception ) {
156                                 # No source or XML parse error
157                                 $wgOut->wrapWikiMsg( "<p class=\"error\">\n$1\n</p>", array( 'importfailed', $exception->getMessage() ) );
158                         } elseif( !$result->isGood() ) {
159                                 # Zero revisions
160                                 $wgOut->wrapWikiMsg( "<p class=\"error\">\n$1\n</p>", array( 'importfailed', $result->getWikiText() ) );
161                         } else {
162                                 # Success!
163                                 $wgOut->addWikiMsg( 'importsuccess' );
164                         }
165                         $wgOut->addWikiText( '<hr />' );
166                 }
167         }
168
169         private function showForm() {
170                 global $wgUser, $wgOut, $wgImportSources, $wgExportMaxLinkDepth;
171
172                 $action = $this->getTitle()->getLocalUrl( array( 'action' => 'submit' ) );
173
174                 if( $wgUser->isAllowed( 'importupload' ) ) {
175                         $wgOut->addWikiMsg( "importtext" );
176                         $wgOut->addHTML(
177                                 Xml::fieldset( wfMsg( 'import-upload' ) ).
178                                 Xml::openElement( 'form', array( 'enctype' => 'multipart/form-data', 'method' => 'post',
179                                         'action' => $action, 'id' => 'mw-import-upload-form' ) ) .
180                                 Html::hidden( 'action', 'submit' ) .
181                                 Html::hidden( 'source', 'upload' ) .
182                                 Xml::openElement( 'table', array( 'id' => 'mw-import-table' ) ) .
183
184                                 "<tr>
185                                         <td class='mw-label'>" .
186                                                 Xml::label( wfMsg( 'import-upload-filename' ), 'xmlimport' ) .
187                                         "</td>
188                                         <td class='mw-input'>" .
189                                                 Xml::input( 'xmlimport', 50, '', array( 'type' => 'file' ) ) . ' ' .
190                                         "</td>
191                                 </tr>
192                                 <tr>
193                                         <td class='mw-label'>" .
194                                                 Xml::label( wfMsg( 'import-comment' ), 'mw-import-comment' ) .
195                                         "</td>
196                                         <td class='mw-input'>" .
197                                                 Xml::input( 'log-comment', 50, '',
198                                                         array( 'id' => 'mw-import-comment', 'type' => 'text' ) ) . ' ' .
199                                         "</td>
200                                 </tr>
201                                 <tr>
202                                         <td></td>
203                                         <td class='mw-submit'>" .
204                                                 Xml::submitButton( wfMsg( 'uploadbtn' ) ) .
205                                         "</td>
206                                 </tr>" .
207                                 Xml::closeElement( 'table' ).
208                                 Html::hidden( 'editToken', $wgUser->editToken() ) .
209                                 Xml::closeElement( 'form' ) .
210                                 Xml::closeElement( 'fieldset' )
211                         );
212                 } else {
213                         if( empty( $wgImportSources ) ) {
214                                 $wgOut->addWikiMsg( 'importnosources' );
215                         }
216                 }
217
218                 if( $wgUser->isAllowed( 'import' ) && !empty( $wgImportSources ) ) {
219                         # Show input field for import depth only if $wgExportMaxLinkDepth > 0
220                         $importDepth = '';
221                         if( $wgExportMaxLinkDepth > 0 ) {
222                                 $importDepth = "<tr>
223                                                         <td class='mw-label'>" .
224                                                                 wfMsgExt( 'export-pagelinks', 'parseinline' ) .
225                                                         "</td>
226                                                         <td class='mw-input'>" .
227                                                                 Xml::input( 'pagelink-depth', 3, 0 ) .
228                                                         "</td>
229                                                 </tr>";
230                         }
231
232                         $wgOut->addHTML(
233                                 Xml::fieldset(  wfMsg( 'importinterwiki' ) ) .
234                                 Xml::openElement( 'form', array( 'method' => 'post', 'action' => $action, 'id' => 'mw-import-interwiki-form' ) ) .
235                                 wfMsgExt( 'import-interwiki-text', array( 'parse' ) ) .
236                                 Html::hidden( 'action', 'submit' ) .
237                                 Html::hidden( 'source', 'interwiki' ) .
238                                 Html::hidden( 'editToken', $wgUser->editToken() ) .
239                                 Xml::openElement( 'table', array( 'id' => 'mw-import-table' ) ) .
240                                 "<tr>
241                                         <td class='mw-label'>" .
242                                                 Xml::label( wfMsg( 'import-interwiki-source' ), 'interwiki' ) .
243                                         "</td>
244                                         <td class='mw-input'>" .
245                                                 Xml::openElement( 'select', array( 'name' => 'interwiki' ) )
246                         );
247                         foreach( $wgImportSources as $prefix ) {
248                                 $selected = ( $this->interwiki === $prefix ) ? ' selected="selected"' : '';
249                                 $wgOut->addHTML( Xml::option( $prefix, $prefix, $selected ) );
250                         }
251
252                         $wgOut->addHTML(
253                                                 Xml::closeElement( 'select' ) .
254                                                 Xml::input( 'frompage', 50, $this->frompage ) .
255                                         "</td>
256                                 </tr>
257                                 <tr>
258                                         <td>
259                                         </td>
260                                         <td class='mw-input'>" .
261                                                 Xml::checkLabel( wfMsg( 'import-interwiki-history' ), 'interwikiHistory', 'interwikiHistory', $this->history ) .
262                                         "</td>
263                                 </tr>
264                                 <tr>
265                                         <td>
266                                         </td>
267                                         <td class='mw-input'>" .
268                                                 Xml::checkLabel( wfMsg( 'import-interwiki-templates' ), 'interwikiTemplates', 'interwikiTemplates', $this->includeTemplates ) .
269                                         "</td>
270                                 </tr>
271                                 $importDepth
272                                 <tr>
273                                         <td class='mw-label'>" .
274                                                 Xml::label( wfMsg( 'import-interwiki-namespace' ), 'namespace' ) .
275                                         "</td>
276                                         <td class='mw-input'>" .
277                                                 Xml::namespaceSelector( $this->namespace, '' ) .
278                                         "</td>
279                                 </tr>
280                                 <tr>
281                                         <td class='mw-label'>" .
282                                                 Xml::label( wfMsg( 'import-comment' ), 'mw-interwiki-comment' ) .
283                                         "</td>
284                                         <td class='mw-input'>" .
285                                                 Xml::input( 'log-comment', 50, '',
286                                                         array( 'id' => 'mw-interwiki-comment', 'type' => 'text' ) ) . ' ' .
287                                         "</td>
288                                 </tr>
289                                 <tr>
290                                         <td>
291                                         </td>
292                                         <td class='mw-submit'>" .
293                                                 Xml::submitButton( wfMsg( 'import-interwiki-submit' ), $wgUser->getSkin()->tooltipAndAccessKeyAttribs( 'import' ) ) .
294                                         "</td>
295                                 </tr>" .
296                                 Xml::closeElement( 'table' ).
297                                 Xml::closeElement( 'form' ) .
298                                 Xml::closeElement( 'fieldset' )
299                         );
300                 }
301         }
302 }
303
304 /**
305  * Reporting callback
306  * @ingroup SpecialPage
307  */
308 class ImportReporter {
309         private $reason=false;
310         private $mOriginalLogCallback = null;
311         private $mOriginalPageOutCallback = null;
312         private $mLogItemCount = 0;
313
314         function __construct( $importer, $upload, $interwiki , $reason=false ) {
315                 $this->mOriginalPageOutCallback = 
316                         $importer->setPageOutCallback( array( $this, 'reportPage' ) );
317                 $this->mOriginalLogCallback =
318                         $importer->setLogItemCallback( array( $this, 'reportLogItem' ) );
319                 $this->mPageCount = 0;
320                 $this->mIsUpload = $upload;
321                 $this->mInterwiki = $interwiki;
322                 $this->reason = $reason;
323         }
324
325         function open() {
326                 global $wgOut;
327                 $wgOut->addHTML( "<ul>\n" );
328         }
329         
330         function reportLogItem( /* ... */ ) {
331                 $this->mLogItemCount++;
332                 if ( is_callable( $this->mOriginalLogCallback ) ) {
333                         call_user_func_array( $this->mOriginalLogCallback, func_get_args() );
334                 }
335         }
336
337         function reportPage( $title, $origTitle, $revisionCount, $successCount, $pageInfo ) {
338                 global $wgOut, $wgUser, $wgLang, $wgContLang;
339                 
340                 $args = func_get_args();
341                 call_user_func_array( $this->mOriginalPageOutCallback, $args );
342
343                 $skin = $wgUser->getSkin();
344
345                 $this->mPageCount++;
346
347                 $localCount = $wgLang->formatNum( $successCount );
348                 $contentCount = $wgContLang->formatNum( $successCount );
349
350                 if( $successCount > 0 ) {
351                         $wgOut->addHTML( "<li>" . $skin->linkKnown( $title ) . " " .
352                                 wfMsgExt( 'import-revision-count', array( 'parsemag', 'escape' ), $localCount ) .
353                                 "</li>\n"
354                         );
355
356                         $log = new LogPage( 'import' );
357                         if( $this->mIsUpload ) {
358                                 $detail = wfMsgExt( 'import-logentry-upload-detail', array( 'content', 'parsemag' ),
359                                         $contentCount );
360                                 if ( $this->reason ) {
361                                         $detail .=  wfMsgForContent( 'colon-separator' ) . $this->reason;
362                                 }
363                                 $log->addEntry( 'upload', $title, $detail );
364                         } else {
365                                 $interwiki = '[[:' . $this->mInterwiki . ':' .
366                                         $origTitle->getPrefixedText() . ']]';
367                                 $detail = wfMsgExt( 'import-logentry-interwiki-detail', array( 'content', 'parsemag' ),
368                                         $contentCount, $interwiki );
369                                 if ( $this->reason ) {
370                                         $detail .=  wfMsgForContent( 'colon-separator' ) . $this->reason;
371                                 }
372                                 $log->addEntry( 'interwiki', $title, $detail );
373                         }
374
375                         $comment = $detail; // quick
376                         $dbw = wfGetDB( DB_MASTER );
377                         $latest = $title->getLatestRevID();
378                         $nullRevision = Revision::newNullRevision( $dbw, $title->getArticleId(), $comment, true );
379                         $nullRevision->insertOn( $dbw );
380                         $article = new Article( $title );
381                         # Update page record
382                         $article->updateRevisionOn( $dbw, $nullRevision );
383                         wfRunHooks( 'NewRevisionFromEditComplete', array($article, $nullRevision, $latest, $wgUser) );
384                 } else {
385                         $wgOut->addHTML( "<li>" . $skin->linkKnown( $title ) . " " .
386                                 wfMsgHtml( 'import-nonewrevisions' ) . "</li>\n" );
387                 }
388         }
389
390         function close() {
391                 global $wgOut, $wgLang;
392                 
393                 if ( $this->mLogItemCount > 0 ) {
394                         $msg = wfMsgExt( 'imported-log-entries', 'parseinline',
395                                                 $wgLang->formatNum( $this->mLogItemCount ) );
396                         $wgOut->addHTML( Xml::tags( 'li', null, $msg ) );
397                 } elseif( $this->mPageCount == 0 && $this->mLogItemCount == 0 ) {
398                         $wgOut->addHTML( "</ul>\n" );
399                         return Status::newFatal( 'importnopages' );
400                 }
401                 $wgOut->addHTML( "</ul>\n" );
402
403                 return Status::newGood( $this->mPageCount );
404         }
405 }