Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
74.68% covered (warning)
74.68%
59 / 79
80.00% covered (warning)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
SourceWikiCleanupSnippet
74.68% covered (warning)
74.68%
59 / 79
80.00% covered (warning)
80.00%
4 / 5
15.74
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 getHtml
67.74% covered (warning)
67.74%
42 / 62
0.00% covered (danger)
0.00%
0 / 1
7.21
 isFreshImport
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isSourceEditAllowed
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 isSourceDeleteAllowed
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3namespace FileImporter\Html;
4
5use FileImporter\Data\ImportPlan;
6use FileImporter\Data\ImportRequest;
7use FileImporter\Data\SourceUrl;
8use FileImporter\Remote\MediaWiki\RemoteApiActionExecutor;
9use FileImporter\Services\WikidataTemplateLookup;
10use MediaWiki\Context\IContextSource;
11use MediaWiki\Context\RequestContext;
12use MediaWiki\Html\Html;
13use MediaWiki\MediaWikiServices;
14use MediaWiki\User\User;
15use OOUI\CheckboxInputWidget;
16use OOUI\FieldLayout;
17use Psr\Log\LoggerInterface;
18use Psr\Log\NullLogger;
19
20/**
21 * @license GPL-2.0-or-later
22 */
23class SourceWikiCleanupSnippet {
24
25    public const ACTION_OFFERED_SOURCE_DELETE = 'offeredSourceDelete';
26    public const ACTION_OFFERED_SOURCE_EDIT = 'offeredSourceEdit';
27
28    /** @var bool */
29    private $sourceEditingEnabled;
30    /** @var bool */
31    private $sourceDeletionEnabled;
32    private WikidataTemplateLookup $lookup;
33    private RemoteApiActionExecutor $remoteActionApi;
34    private LoggerInterface $logger;
35
36    /**
37     * @param bool $sourceEditingEnabled
38     * @param bool $sourceDeletionEnabled
39     * @param LoggerInterface|null $logger
40     */
41    public function __construct(
42        $sourceEditingEnabled = true,
43        $sourceDeletionEnabled = true,
44        LoggerInterface $logger = null
45    ) {
46        $this->sourceEditingEnabled = $sourceEditingEnabled;
47        $this->sourceDeletionEnabled = $sourceDeletionEnabled;
48        $this->logger = $logger ?? new NullLogger();
49
50        // TODO: Inject
51        $this->lookup = MediaWikiServices::getInstance()->getService(
52            'FileImporterTemplateLookup' );
53        $this->remoteActionApi = MediaWikiServices::getInstance()->getService(
54            'FileImporterMediaWikiRemoteApiActionExecutor' );
55    }
56
57    /**
58     * @return string
59     */
60    public function getHtml( ImportPlan $importPlan, User $user ) {
61        /** @var IContextSource $context */
62        $context = RequestContext::getMain();
63        $sourceUrl = $importPlan->getRequest()->getUrl();
64
65        $canAutomateEdit = $this->isSourceEditAllowed(
66            $sourceUrl,
67            $user,
68            $importPlan->getOriginalTitle()->getPrefixedText()
69        );
70        $canAutomateDelete = $this->isSourceDeleteAllowed( $sourceUrl, $user );
71
72        if ( !$canAutomateEdit && !$canAutomateDelete ) {
73            return '';
74        }
75
76        $html = Html::element(
77            'h2',
78            [],
79            $context->msg( 'fileimporter-heading-cleanup' )->plain()
80        );
81
82        if ( $canAutomateDelete ) {
83            $automateDeleteSelected = $importPlan->getAutomateSourceWikiDelete();
84            $importPlan->setActionIsPerformed( self::ACTION_OFFERED_SOURCE_DELETE );
85
86            $html .= Html::rawElement(
87                    'p',
88                    [],
89                    $context->msg( 'fileimporter-delete-text' )->parse()
90                ) .
91                new FieldLayout(
92                    new CheckboxInputWidget(
93                        [
94                            'name' => 'automateSourceWikiDelete',
95                            'selected' => $automateDeleteSelected,
96                            'value' => true
97                        ]
98                    ),
99                    [
100                        'label' => $context->msg( 'fileimporter-delete-checkboxlabel' )->parse(),
101                        'align' => 'inline'
102                    ]
103                );
104        } elseif ( $canAutomateEdit ) {
105            $automateEditSelected = $importPlan->getAutomateSourceWikiCleanUp() ||
106                $this->isFreshImport( $importPlan->getRequest() );
107            $importPlan->setActionIsPerformed( self::ACTION_OFFERED_SOURCE_EDIT );
108
109            $html .= Html::rawElement(
110                    'p',
111                    [],
112                    $context->msg(
113                        'fileimporter-cleanup-text',
114                        $this->lookup->fetchNowCommonsLocalTitle( $sourceUrl )
115                    )->parse()
116                ) .
117                new FieldLayout(
118                    new CheckboxInputWidget(
119                        [
120                            'name' => 'automateSourceWikiCleanup',
121                            'selected' => $automateEditSelected,
122                            'value' => true
123                        ]
124                    ),
125                    [
126                        'label' => $context->msg( 'fileimporter-cleanup-checkboxlabel' )->parse(),
127                        'align' => 'inline'
128                    ]
129                );
130        }
131
132        return $html;
133    }
134
135    /**
136     * @return bool
137     */
138    private function isFreshImport( ImportRequest $importRequest ) {
139        return $importRequest->getImportDetailsHash() === '';
140    }
141
142    /**
143     * Warning, contrary to the method name this currently doesn't check if the user is allowed to
144     * edit the page!
145     *
146     * @return bool True if source wiki editing is enabled and a localized {{Now Commons}} template
147     *  can be found.
148     */
149    private function isSourceEditAllowed( SourceUrl $sourceUrl, User $user, string $title ) {
150        if ( !$this->sourceEditingEnabled ||
151            // Note: This intentionally doesn't allow a template with the name "0".
152            !$this->lookup->fetchNowCommonsLocalTitle( $sourceUrl )
153        ) {
154            return false;
155        }
156
157        return $this->remoteActionApi->executeTestEditActionQuery( $sourceUrl, $user, $title )
158            ->isGood();
159    }
160
161    /**
162     * @return bool True if source wiki deletions are enabled and the user does have the right to
163     *  delete pages. Also returns false if querying the user rights failed.
164     */
165    private function isSourceDeleteAllowed( SourceUrl $sourceUrl, User $user ) {
166        if ( !$this->sourceDeletionEnabled ) {
167            return false;
168        }
169
170        return $this->remoteActionApi->executeUserRightsQuery( $sourceUrl, $user )
171            ->isGood();
172    }
173
174}