Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 112
0.00% covered (danger)
0.00%
0 / 52
CRAP
0.00% covered (danger)
0.00%
0 / 1
WikiRevision
0.00% covered (danger)
0.00%
0 / 112
0.00% covered (danger)
0.00%
0 / 52
4032
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setID
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setTimestamp
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setUsername
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setUserObj
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
20
 setUserIP
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setModel
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setFormat
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setText
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 setContent
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 setComment
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setMinor
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setSrc
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setFileSrc
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setSha1Base36
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 setTags
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setFilename
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setArchiveName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setSize
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setAction
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setParams
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setNoUpdates
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTitle
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getID
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTimestamp
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getUser
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getUserObj
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getText
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getContentHandler
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getContent
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSlot
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSlotRoles
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getModel
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getFormat
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getComment
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getMinor
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSrc
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSha1
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 getSha1Base36
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getTags
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getFileSrc
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isTempSrc
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getFilename
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getArchiveName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSize
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getType
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getAction
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getParams
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 importOldRevision
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 importLogItem
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
30
1<?php
2/**
3 * MediaWiki page data importer.
4 *
5 * Copyright © 2003,2005 Brooke Vibber <bvibber@wikimedia.org>
6 * https://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
27use MediaWiki\MediaWikiServices;
28use MediaWiki\Revision\MutableRevisionSlots;
29use MediaWiki\Revision\SlotRecord;
30use MediaWiki\Title\Title;
31use MediaWiki\User\ExternalUserNames;
32use MediaWiki\User\User;
33use MediaWiki\User\UserIdentityValue;
34
35/**
36 * Represents a revision, log entry or upload during the import process.
37 * This class sticks closely to the structure of the XML dump.
38 *
39 * @since 1.2
40 *
41 * @ingroup SpecialPage
42 */
43class WikiRevision implements ImportableUploadRevision, ImportableOldRevision {
44
45    /**
46     * @since 1.2
47     * @var Title
48     */
49    public $title = null;
50
51    /**
52     * @since 1.6.4
53     * @var int
54     */
55    public $id = 0;
56
57    /**
58     * @since 1.2
59     * @var string TS_MW timestamp, a string with 14 digits
60     */
61    public $timestamp = "20010115000000";
62
63    /**
64     * @since 1.2
65     * @var string
66     */
67    public $user_text = "";
68
69    /**
70     * @deprecated since 1.39, use {@see $user_text} instead
71     * @since 1.27
72     * @var User|null
73     */
74    public $userObj = null;
75
76    /**
77     * @since 1.21
78     * @deprecated since 1.35, use getContent
79     * @var string
80     */
81    public $model = null;
82
83    /**
84     * @since 1.21
85     * @deprecated since 1.35, use getContent
86     * @var string
87     */
88    public $format = null;
89
90    /**
91     * @since 1.2
92     * @deprecated since 1.35, use getContent
93     * @var string
94     */
95    public $text = "";
96
97    /**
98     * @since 1.12.2
99     * @var int
100     */
101    protected $size;
102
103    /**
104     * @since 1.21
105     * @deprecated since 1.35, use getContent
106     * @var Content
107     */
108    public $content = null;
109
110    /**
111     * @since 1.24
112     * @var ContentHandler
113     */
114    protected $contentHandler = null;
115
116    /**
117     * @since 1.2.6
118     * @var string
119     */
120    public $comment = "";
121
122    private MutableRevisionSlots $slots;
123
124    /**
125     * @since 1.5.7
126     * @var bool
127     */
128    public $minor = false;
129
130    /**
131     * @since 1.12.2
132     * @var string
133     */
134    public $type = "";
135
136    /**
137     * @since 1.12.2
138     * @var string
139     */
140    public $action = "";
141
142    /**
143     * @since 1.12.2
144     * @var string
145     */
146    public $params = "";
147
148    /**
149     * @since 1.17
150     * @var string
151     */
152    public $fileSrc = '';
153
154    /**
155     * @var string|null
156     */
157    private $sha1base36;
158
159    /**
160     * @since 1.34
161     * @var string[]
162     */
163    protected $tags = [];
164
165    /**
166     * @since 1.17
167     * @var string
168     */
169    public $archiveName = '';
170
171    /**
172     * @since 1.12.2
173     * @var string|null
174     */
175    protected $filename;
176
177    /**
178     * @since 1.12.2
179     * @var string|null
180     */
181    protected $src = null;
182
183    /**
184     * @since 1.18
185     * @var bool
186     * @todo Unused?
187     */
188    public $isTemp = false;
189
190    /** @var bool */
191    private $mNoUpdates = false;
192
193    public function __construct() {
194        $this->slots = new MutableRevisionSlots();
195    }
196
197    /**
198     * @param Title $title
199     */
200    public function setTitle( Title $title ) {
201        $this->title = $title;
202    }
203
204    /**
205     * @since 1.6.4
206     * @param int $id
207     */
208    public function setID( $id ) {
209        $this->id = $id;
210    }
211
212    /**
213     * @since 1.2
214     * @param string $ts
215     */
216    public function setTimestamp( $ts ) {
217        # 2003-08-05T18:30:02Z
218        $this->timestamp = wfTimestamp( TS_MW, $ts );
219    }
220
221    /**
222     * @since 1.2
223     * @param string $user
224     */
225    public function setUsername( $user ) {
226        $this->user_text = $user;
227    }
228
229    /**
230     * @deprecated since 1.39, use {@see setUsername} instead
231     * @since 1.27
232     * @param User $user
233     */
234    public function setUserObj( $user ) {
235        // Not officially supported, but some callers pass false from e.g. User::newFromName()
236        $this->userObj = $user ?: null;
237        if ( $this->user_text === '' && $user ) {
238            $this->user_text = $user->getName();
239        }
240    }
241
242    /**
243     * @deprecated since 1.39, use {@see setUsername} instead, it does the same anyway
244     * @since 1.2
245     * @param string $ip
246     */
247    public function setUserIP( $ip ) {
248        $this->user_text = $ip;
249    }
250
251    /**
252     * @since 1.21
253     * @deprecated since 1.35, use setContent instead.
254     * @param string $model
255     */
256    public function setModel( $model ) {
257        $this->model = $model;
258    }
259
260    /**
261     * @since 1.21
262     * @deprecated since 1.35, use setContent instead.
263     * @param string $format
264     */
265    public function setFormat( $format ) {
266        $this->format = $format;
267    }
268
269    /**
270     * @since 1.2
271     * @deprecated since 1.35, use setContent instead.
272     * @param string $text
273     */
274    public function setText( $text ) {
275        $handler = ContentHandler::getForModelID( $this->getModel() );
276        $content = $handler->unserializeContent( $text );
277        $this->setContent( SlotRecord::MAIN, $content );
278    }
279
280    /**
281     * @since 1.35
282     * @param string $role
283     * @param Content $content
284     */
285    public function setContent( $role, $content ) {
286        $this->slots->setContent( $role, $content );
287
288        // backwards compat
289        if ( $role === SlotRecord::MAIN ) {
290            $this->content = $content;
291            $this->model = $content->getModel();
292            $this->format = $content->getDefaultFormat();
293            $this->text = $content->serialize();
294        }
295    }
296
297    /**
298     * @since 1.2.6
299     * @param string $text
300     */
301    public function setComment( string $text ) {
302        $this->comment = $text;
303    }
304
305    /**
306     * @since 1.5.7
307     * @param bool $minor
308     */
309    public function setMinor( $minor ) {
310        $this->minor = (bool)$minor;
311    }
312
313    /**
314     * @since 1.12.2
315     * @param string|null $src
316     */
317    public function setSrc( $src ) {
318        $this->src = $src;
319    }
320
321    /**
322     * @since 1.17
323     * @param string $src
324     * @param bool $isTemp
325     */
326    public function setFileSrc( $src, $isTemp ) {
327        $this->fileSrc = $src;
328        $this->isTemp = $isTemp;
329    }
330
331    /**
332     * @since 1.17
333     * @param string $sha1base36
334     */
335    public function setSha1Base36( $sha1base36 ) {
336        $this->sha1base36 = $sha1base36 ?: null;
337    }
338
339    /**
340     * @since 1.34
341     * @param string[] $tags
342     */
343    public function setTags( array $tags ) {
344        $this->tags = $tags;
345    }
346
347    /**
348     * @since 1.12.2
349     * @param string $filename
350     */
351    public function setFilename( $filename ) {
352        $this->filename = $filename;
353    }
354
355    /**
356     * @since 1.17
357     * @param string $archiveName
358     */
359    public function setArchiveName( $archiveName ) {
360        $this->archiveName = $archiveName;
361    }
362
363    /**
364     * @since 1.12.2
365     * @param int $size
366     */
367    public function setSize( $size ) {
368        $this->size = intval( $size );
369    }
370
371    /**
372     * @since 1.12.2
373     * @param string $type
374     */
375    public function setType( $type ) {
376        $this->type = $type;
377    }
378
379    /**
380     * @since 1.12.2
381     * @param string $action
382     */
383    public function setAction( $action ) {
384        $this->action = $action;
385    }
386
387    /**
388     * @since 1.12.2
389     * @param string $params
390     */
391    public function setParams( $params ) {
392        $this->params = $params;
393    }
394
395    /**
396     * @since 1.18
397     * @param bool $noupdates
398     */
399    public function setNoUpdates( $noupdates ) {
400        $this->mNoUpdates = $noupdates;
401    }
402
403    /**
404     * @since 1.2
405     * @return Title
406     */
407    public function getTitle() {
408        return $this->title;
409    }
410
411    /**
412     * @since 1.6.4
413     * @return int
414     */
415    public function getID() {
416        return $this->id;
417    }
418
419    /**
420     * @since 1.2
421     * @return string TS_MW timestamp, a string with 14 digits
422     */
423    public function getTimestamp() {
424        return $this->timestamp;
425    }
426
427    /**
428     * @since 1.2
429     * @return string
430     */
431    public function getUser() {
432        return $this->user_text;
433    }
434
435    /**
436     * @deprecated since 1.39, use {@see getUser} instead; this is almost always null anyway
437     * @since 1.27
438     * @return User|null Typically null, use {@see getUser} instead
439     */
440    public function getUserObj() {
441        return $this->userObj;
442    }
443
444    /**
445     * @since 1.2
446     * @return string
447     */
448    public function getText() {
449        return $this->text;
450    }
451
452    /**
453     * @since 1.24
454     * @deprecated since 1.35, use getContent
455     * @return ContentHandler
456     * @throws MWUnknownContentModelException
457     */
458    public function getContentHandler() {
459        $this->contentHandler ??= MediaWikiServices::getInstance()
460            ->getContentHandlerFactory()
461            ->getContentHandler( $this->getModel() );
462
463        return $this->contentHandler;
464    }
465
466    /**
467     * @since 1.21
468     * @param string $role added in 1.35
469     * @return Content
470     */
471    public function getContent( $role = SlotRecord::MAIN ) {
472        return $this->slots->getContent( $role );
473    }
474
475    /**
476     * @since 1.35
477     * @param string $role
478     * @return SlotRecord
479     */
480    public function getSlot( $role ) {
481        return $this->slots->getSlot( $role );
482    }
483
484    /**
485     * @since 1.35
486     * @return string[]
487     */
488    public function getSlotRoles() {
489        return $this->slots->getSlotRoles();
490    }
491
492    /**
493     * @since 1.21
494     * @deprecated since 1.35, use getContent
495     * @return string
496     */
497    public function getModel() {
498        $this->model ??= $this->getTitle()->getContentModel();
499
500        return $this->model;
501    }
502
503    /**
504     * @since 1.21
505     * @deprecated since 1.35, use getContent
506     * @return string
507     */
508    public function getFormat() {
509        $this->format ??= $this->getContentHandler()->getDefaultFormat();
510
511        return $this->format;
512    }
513
514    /**
515     * @since 1.2.6
516     * @return string
517     */
518    public function getComment(): string {
519        return $this->comment;
520    }
521
522    /**
523     * @since 1.5.7
524     * @return bool
525     */
526    public function getMinor() {
527        return $this->minor;
528    }
529
530    /**
531     * @since 1.12.2
532     * @return string|null
533     */
534    public function getSrc() {
535        return $this->src;
536    }
537
538    /**
539     * @since 1.17
540     * @return string|false
541     */
542    public function getSha1() {
543        if ( $this->sha1base36 ) {
544            return Wikimedia\base_convert( $this->sha1base36, 36, 16 );
545        }
546        return false;
547    }
548
549    /**
550     * @since 1.31
551     * @return string|false
552     */
553    public function getSha1Base36() {
554        return $this->sha1base36 ?? false;
555    }
556
557    /**
558     * @since 1.34
559     * @return string[]
560     */
561    public function getTags() {
562        return $this->tags;
563    }
564
565    /**
566     * @since 1.17
567     * @return string
568     */
569    public function getFileSrc() {
570        return $this->fileSrc;
571    }
572
573    /**
574     * @since 1.17
575     * @return bool
576     */
577    public function isTempSrc() {
578        return $this->isTemp;
579    }
580
581    /**
582     * @since 1.12.2
583     * @return mixed
584     */
585    public function getFilename() {
586        return $this->filename;
587    }
588
589    /**
590     * @since 1.17
591     * @return string
592     */
593    public function getArchiveName() {
594        return $this->archiveName;
595    }
596
597    /**
598     * @since 1.12.2
599     * @return mixed
600     */
601    public function getSize() {
602        return $this->size;
603    }
604
605    /**
606     * @since 1.12.2
607     * @return string
608     */
609    public function getType() {
610        return $this->type;
611    }
612
613    /**
614     * @since 1.12.2
615     * @return string
616     */
617    public function getAction() {
618        return $this->action;
619    }
620
621    /**
622     * @since 1.12.2
623     * @return string
624     */
625    public function getParams() {
626        return $this->params;
627    }
628
629    /**
630     * @since 1.4.1
631     * @deprecated since 1.31. Use OldRevisionImporter::import
632     * @return bool
633     */
634    public function importOldRevision() {
635        if ( $this->mNoUpdates ) {
636            $importer = MediaWikiServices::getInstance()->getWikiRevisionOldRevisionImporterNoUpdates();
637        } else {
638            $importer = MediaWikiServices::getInstance()->getWikiRevisionOldRevisionImporter();
639        }
640        return $importer->import( $this );
641    }
642
643    /**
644     * @since 1.12.2
645     * @return bool
646     */
647    public function importLogItem() {
648        $services = MediaWikiServices::getInstance();
649        $dbw = $services->getConnectionProvider()->getPrimaryDatabase();
650
651        $userName = $this->getUser();
652        if ( ExternalUserNames::isExternal( $userName ) ) {
653            // Use newAnonymous() since the user name is already prefixed.
654            $user = UserIdentityValue::newAnonymous( $userName );
655        } else {
656            $user = $this->getUserObj() ?: User::newFromName( $userName, false );
657        }
658
659        # @todo FIXME: This will not record autoblocks
660        if ( !$this->getTitle() ) {
661            wfDebug( __METHOD__ . ": skipping invalid {$this->type}/{$this->action} log time, timestamp " .
662                $this->timestamp );
663            return false;
664        }
665        # Check if it exists already
666        // @todo FIXME: Use original log ID (better for backups)
667        $prior = (bool)$dbw->newSelectQueryBuilder()
668            ->select( '1' )
669            ->from( 'logging' )
670            ->where( [
671                'log_type' => $this->getType(),
672                'log_action' => $this->getAction(),
673                'log_timestamp' => $dbw->timestamp( $this->timestamp ),
674                'log_namespace' => $this->getTitle()->getNamespace(),
675                'log_title' => $this->getTitle()->getDBkey(),
676                'log_params' => $this->params
677            ] )
678            ->caller( __METHOD__ )->fetchField();
679        // @todo FIXME: This could fail slightly for multiple matches :P
680        if ( $prior ) {
681            wfDebug( __METHOD__
682                . ": skipping existing item for Log:{$this->type}/{$this->action}, timestamp "
683                . $this->timestamp );
684            return false;
685        }
686        $actorId = $services->getActorNormalization()->acquireActorId( $user, $dbw );
687        $dbw->newInsertQueryBuilder()
688            ->insertInto( 'logging' )
689            ->row( [
690                'log_type' => $this->type,
691                'log_action' => $this->action,
692                'log_timestamp' => $dbw->timestamp( $this->timestamp ),
693                'log_actor' => $actorId,
694                'log_namespace' => $this->getTitle()->getNamespace(),
695                'log_title' => $this->getTitle()->getDBkey(),
696                'log_params' => $this->params
697                ] + $services->getCommentStore()->insert( $dbw, 'log_comment', $this->getComment() ) )
698            ->caller( __METHOD__ )->execute();
699        return true;
700    }
701
702}