Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 54
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
WikiRevisionFactory
0.00% covered (danger)
0.00%
0 / 54
0.00% covered (danger)
0.00%
0 / 7
132
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
 newWikiRevision
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 setInterWikiPrefix
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 newFromFileRevision
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 newFromTextRevision
0.00% covered (danger)
0.00%
0 / 19
0.00% covered (danger)
0.00%
0 / 1
2
 createCentralAuthUser
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 prefixCommentLinks
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace FileImporter\Services;
4
5use FileImporter\Data\FileRevision;
6use FileImporter\Data\TextRevision;
7use MediaWiki\Content\IContentHandlerFactory;
8use MediaWiki\Revision\SlotRecord;
9use MediaWiki\Title\Title;
10use MediaWiki\User\ExternalUserNames;
11use WikiRevision;
12
13/**
14 * @license GPL-2.0-or-later
15 * @author Addshore
16 */
17class WikiRevisionFactory {
18
19    /** @var string */
20    private $interwikiPrefix;
21    private ExternalUserNames $externalUserNames;
22
23    // TODO: should be changed back to lowercase when T221235 is fixed.
24    public const DEFAULT_USERNAME_PREFIX = 'Imported';
25
26    public function __construct(
27        private readonly IContentHandlerFactory $contentHandlerFactory,
28    ) {
29        $this->externalUserNames = new ExternalUserNames( self::DEFAULT_USERNAME_PREFIX, true );
30    }
31
32    private function newWikiRevision(
33        string $title,
34        string $timestamp,
35        ?string $sha1
36    ): WikiRevision {
37        $filename = array_last( explode( ':', $title, 2 ) );
38
39        // The 3 fields rev_page, rev_timestamp, and rev_sha1 make a revision unique, see
40        // ImportableOldRevisionImporter::import()
41        $revision = new WikiRevision();
42        $revision->setTitle( Title::makeTitleSafe( NS_FILE, $filename ) );
43        $revision->setTimestamp( $timestamp );
44        // File revisions older than 2012 might not have a hash yet. Import as is.
45        if ( $sha1 ) {
46            $revision->setSha1Base36( $sha1 );
47        }
48
49        return $revision;
50    }
51
52    public function setInterWikiPrefix( string $prefix ): void {
53        $this->interwikiPrefix = $prefix;
54        $this->externalUserNames = new ExternalUserNames(
55            $prefix ?: self::DEFAULT_USERNAME_PREFIX,
56            true
57        );
58    }
59
60    public function newFromFileRevision( FileRevision $fileRevision, string $src ): WikiRevision {
61        $revision = $this->newWikiRevision(
62            $fileRevision->getField( 'name' ),
63            $fileRevision->getField( 'timestamp' ),
64            $fileRevision->getField( 'sha1' )
65        );
66        $revision->setFileSrc( $src, true );
67        $revision->setComment( $fileRevision->getField( 'description' ) );
68
69        $importedUser = $this->createCentralAuthUser( $fileRevision->getField( 'user' ) );
70        $revision->setUsername( $importedUser );
71
72        // Mark old file revisions as such
73        $archiveName = $fileRevision->getField( 'archivename' );
74        if ( $archiveName ) {
75            $revision->setArchiveName( $archiveName );
76        }
77
78        return $revision;
79    }
80
81    /**
82     * @return WikiRevision
83     */
84    public function newFromTextRevision( TextRevision $textRevision ) {
85        $content = $this->contentHandlerFactory
86            ->getContentHandler( $textRevision->getContentModel() )
87            ->unserializeContent(
88                $textRevision->getContent(),
89                $textRevision->getContentFormat()
90            );
91
92        $revision = $this->newWikiRevision(
93            $textRevision->getField( 'title' ),
94            $textRevision->getField( 'timestamp' ),
95            $textRevision->getField( 'sha1' )
96        );
97        $revision->setUsername( $this->createCentralAuthUser( $textRevision->getField( 'user' ) ) );
98        $revision->setComment( $this->prefixCommentLinks( $textRevision->getField( 'comment' ) ) );
99        $revision->setMinor( $textRevision->getField( 'minor' ) );
100        $revision->setContent( SlotRecord::MAIN, $content );
101        $revision->setTags(
102            array_merge( $textRevision->getField( 'tags' ), [ 'fileimporter-imported' ] )
103        );
104
105        return $revision;
106    }
107
108    /**
109     * @return string Either the unchanged username if it's a known local or valid CentralAuth/SUL
110     *  user, otherwise the name with the DEFAULT_USERNAME_PREFIX prefix prepended.
111     */
112    private function createCentralAuthUser( string $username ): string {
113        // This uses the prefix only as fallback
114        return $this->externalUserNames->applyPrefix( $username );
115    }
116
117    /**
118     * TODO: We can almost certainly replace this with WikiLinkCleaners.
119     */
120    private function prefixCommentLinks( string $summaryText ): string {
121        if ( !$this->interwikiPrefix ) {
122            return $summaryText;
123        }
124
125        /** Mostly taken from {@see CommentParser::doWikiLinks} */
126        return preg_replace(
127            '/
128                \[\[
129                \s*+ # ignore leading whitespace, the *+ quantifier disallows backtracking
130                :?
131                (?=
132                    [^\[\]|]+
133                    (?:\|
134                        # The "possessive" *+ quantifier disallows backtracking
135                        (?:]?[^\]])*+
136                    )?
137                    \]\]
138                )
139            /x',
140            '[[' . $this->interwikiPrefix . ':',
141            $summaryText
142        );
143    }
144
145}