Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
91.11% covered (success)
91.11%
41 / 45
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
BabelAutoCreate
91.11% covered (success)
91.11%
41 / 45
66.67% covered (warning)
66.67%
2 / 3
13.12
0.00% covered (danger)
0.00%
0 / 1
 create
87.50% covered (warning)
87.50%
28 / 32
0.00% covered (danger)
0.00%
0 / 1
9.16
 user
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getCategoryText
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2/**
3 * Code for automatic creation of categories.
4 *
5 * @file
6 * @author Robert Leverington
7 * @author Robin Pepermans
8 * @author Niklas Laxström
9 * @author Brian Wolff
10 * @author Purodha Blissenbach
11 * @author Sam Reed
12 * @author Siebrand Mazeland
13 * @author Winston Sung
14 * @license GPL-2.0-or-later
15 */
16
17declare( strict_types = 1 );
18
19namespace MediaWiki\Babel;
20
21use MediaWiki\CommentStore\CommentStoreComment;
22use MediaWiki\Content\ContentHandler;
23use MediaWiki\Deferred\DeferredUpdates;
24use MediaWiki\MainConfigNames;
25use MediaWiki\MediaWikiServices;
26use MediaWiki\Revision\SlotRecord;
27use MediaWiki\Title\Title;
28use MediaWiki\User\User;
29use RecentChange;
30
31/**
32 * Class for automatic creation of Babel category pages.
33 */
34class BabelAutoCreate {
35    public const MSG_USERNAME = 'babel-autocreate-user';
36
37    /**
38     * Create category.
39     *
40     * @param string $category Name of category to create.
41     * @param string $text Text to use when creating the category.
42     */
43    public static function create( string $category, string $text ): void {
44        $category = strip_tags( $category );
45        $title = Title::makeTitleSafe( NS_CATEGORY, $category );
46        # T170654: We need to check whether non-existing category page in one language variant actually
47        #  exists in another language variant when a language supports multiple language variants.
48        MediaWikiServices::getInstance()->getLanguageConverterFactory()->getLanguageConverter()
49            ->findVariantLink( $category, $title, true );
50        DeferredUpdates::addCallableUpdate( function () use ( $title, $text ) {
51            $mwServices = MediaWikiServices::getInstance();
52            // Extra exists check here in case the category was created while this code was running
53            if ( $title === null || $title->exists() ) {
54                return;
55            }
56
57            $user = self::user();
58            # Do not add a message if the username is invalid or if the account that adds it, is blocked
59            if ( !$user || $user->getBlock() ) {
60                return;
61            }
62
63            if ( !$mwServices->getPermissionManager()
64                ->quickUserCan( 'create', $user, $title )
65            ) {
66                # The Babel AutoCreate account is not allowed to create the page
67                return;
68            }
69
70            $pageUpdater = $mwServices
71                ->getWikiPageFactory()
72                ->newFromTitle( $title )
73                ->newPageUpdater( $user )
74                ->setContent( SlotRecord::MAIN, ContentHandler::makeContent( $text, $title ) )
75                ->setFlags( EDIT_FORCE_BOT );
76
77            $config = $mwServices->getMainConfig();
78
79            $useNPPatrol = $config->get( MainConfigNames::UseNPPatrol );
80            $useRCPatrol = $config->get( MainConfigNames::UseRCPatrol );
81            $needsPatrol = $useRCPatrol || $useNPPatrol;
82
83            if ( $needsPatrol && $user->authorizeWrite( 'autopatrol', $title ) ) {
84                $pageUpdater->setRcPatrolStatus( RecentChange::PRC_AUTOPATROLLED );
85            }
86
87            $url = wfMessage( 'babel-url' )->inContentLanguage()->plain();
88            $commentStoreComment = CommentStoreComment::newUnsavedComment(
89                wfMessage( 'babel-autocreate-reason', $url )
90            );
91            $pageUpdater->saveRevision( $commentStoreComment );
92        } );
93    }
94
95    /**
96     * Get user object.
97     *
98     * @return User|null User object for autocreate user, null if invalid.
99     */
100    public static function user(): ?User {
101        $userName = wfMessage( self::MSG_USERNAME )->inContentLanguage()->plain();
102        return User::newSystemUser( $userName, [ 'steal' => true ] );
103    }
104
105    /**
106     * Returns the text to use when creating a babel category with the given code and level
107     * @param string $code Code of language that the category is for.
108     * @param string|null $level Level that the category is for.
109     * @param string|null $parent An eventual parent category to add to the newly-created category if one is created.
110     * @return string The text to use to create the category.
111     */
112    public static function getCategoryText( string $code, ?string $level, ?string $parent ): string {
113        global $wgLanguageCode;
114        $language = BabelLanguageCodes::getName( $code, $wgLanguageCode );
115        if ( !$parent ) {
116            $parent = "";
117        } else {
118            $sortkey = $level ?? $code;
119            $parent = "[[Category:$parent|{$sortkey}]]";
120        }
121        $params = [ $language, $code, $parent ];
122        if ( $level === null ) {
123            $text = wfMessage( 'babel-autocreate-text-main', $params )->inContentLanguage()->plain();
124        } else {
125            array_unshift( $params, $level );
126            $text = wfMessage( 'babel-autocreate-text-levels', $params )->inContentLanguage()->plain();
127        }
128        return $text;
129    }
130}