Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
MassMessageRequestParser
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 2
600
0.00% covered (danger)
0.00%
0 / 1
 parseRequest
0.00% covered (danger)
0.00%
0 / 53
0.00% covered (danger)
0.00%
0 / 1
182
 getSpamlist
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
132
1<?php
2declare( strict_types = 1 );
3
4namespace MediaWiki\MassMessage\RequestProcessing;
5
6use MediaWiki\MassMessage\Services;
7use MediaWiki\MassMessage\UrlHelper;
8use MediaWiki\MediaWikiServices;
9use MediaWiki\Revision\SlotRecord;
10use MediaWiki\Status\Status;
11use MediaWiki\Title\Title;
12use MediaWiki\User\UserIdentity;
13use MediaWiki\WikiMap\WikiMap;
14use function wfMessage;
15
16/**
17 * Parses request submitted by user for sending a mass message
18 * @author Abijeet Patro
19 * @since 2021.12
20 * @license GPL-2.0-or-later
21 */
22class MassMessageRequestParser {
23    /**
24     * @param array $data
25     * @param UserIdentity $user
26     * @return Status
27     */
28    public function parseRequest( array $data, UserIdentity $user ): Status {
29        // Trim all the things!
30        foreach ( $data as $k => $v ) {
31            if ( is_string( $v ) ) {
32                $data[$k] = trim( $v );
33            }
34        }
35
36        $status = new Status();
37        $currentWikiId = WikiMap::getCurrentWikiId();
38
39        $data['page-message'] ??= '';
40        $data['page-message-section'] ??= '';
41        $data['page-subject-section'] ??= '';
42        $data['message'] ??= '';
43        $data['subject'] ??= '';
44
45        if ( $data['subject'] === '' && $data['page-subject-section'] === '' ) {
46            $status->fatal( 'massmessage-empty-subject' );
47        }
48
49        $spamlist = self::getSpamlist( $data['spamlist'] );
50        if ( $spamlist instanceof Title ) {
51            // Prep the HTML comment message
52            if ( $spamlist->inNamespace( NS_CATEGORY ) ) {
53                $url = $spamlist->getFullURL();
54            } else {
55                $url = $spamlist->getFullURL(
56                    [ 'oldid' => $spamlist->getLatestRevID() ],
57                    false,
58                    PROTO_CANONICAL
59                );
60            }
61
62            $data['comment'] = [ $user->getName(), $currentWikiId, $url ];
63        } else {
64            // $spamlist contains a message key for an error message
65            $status->fatal( $spamlist );
66            // Set dummy values in order to continue validation
67            $spamlist = Title::newMainPage();
68            $data['comment'] = [];
69        }
70
71        $footer = wfMessage( 'massmessage-message-footer' )->inContentLanguage()->plain();
72        if ( trim( $footer ) ) {
73            // Only add the footer if it is not just whitespace
74            $data['message'] .= "\n" . $footer;
75        }
76
77        $request = new MassMessageRequest(
78            $spamlist,
79            $data['subject'],
80            $data['page-message'],
81            $data['page-message-section'],
82            $data['page-subject-section'],
83            $data['message'],
84            $data['comment']
85        );
86
87        $pageMessageBuilderResult = null;
88        if ( $request->hasPageMessage() ) {
89            $pageMessageBuilder = Services::getInstance()->getPageMessageBuilder();
90            $pageMessageBuilderResult = $pageMessageBuilder->getContent(
91                $request->getPageMessage(),
92                $request->getPageMessageSection(),
93                $request->getPageSubjectSection(),
94                $currentWikiId
95            );
96
97            if ( !$pageMessageBuilderResult->isOK() ) {
98                $status->merge( $pageMessageBuilderResult->getStatus() );
99            }
100        }
101
102        if ( !$request->hasMessage() && !$pageMessageBuilderResult ) {
103            $status->fatal( 'massmessage-empty-message' );
104        }
105
106        if ( $status->isOK() ) {
107            $status->setResult( true, $request );
108        }
109
110        return $status;
111    }
112
113    /**
114     * Parse and normalize the spamlist
115     * @param string $title
116     * @return Title|string string will be a error message key
117     */
118    private static function getSpamlist( string $title ) {
119        $spamlist = Title::newFromText( $title );
120
121        // Simply return the title if it is a category
122        if ( $spamlist !== null && $spamlist->inNamespace( NS_CATEGORY ) ) {
123            return $spamlist;
124        }
125
126        if ( $spamlist === null || !$spamlist->exists() ) {
127            return 'massmessage-spamlist-doesnotexist';
128        }
129
130        // Page exists, follow a redirect if possible
131        $target = UrlHelper::followRedirect( $spamlist );
132        if ( $target === null || !$target->exists() ) {
133            // Interwiki redirect or non-existent page.
134            return 'massmessage-spamlist-invalid';
135        }
136        $spamlist = $target;
137
138        $contentModel = $spamlist->getContentModel();
139
140        if ( ( $contentModel !== 'MassMessageListContent' && $contentModel !== CONTENT_MODEL_WIKITEXT )
141            || ( $contentModel === 'MassMessageListContent' && !MediaWikiServices::getInstance()
142                ->getRevisionLookup()
143                ->getRevisionByTitle( $spamlist )
144                ->getContent( SlotRecord::MAIN )
145                ->isValid()
146            )
147        ) {
148            return 'massmessage-spamlist-invalid';
149        }
150
151        return $spamlist;
152    }
153}