Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.30% covered (success)
96.30%
130 / 135
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
GenericFormEditorCapability
96.30% covered (success)
96.30%
130 / 135
66.67% covered (warning)
66.67%
2 / 3
12
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 displayValidationError
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
3
 execute
95.19% covered (success)
95.19%
99 / 104
0.00% covered (danger)
0.00%
0 / 1
8
1<?php
2
3namespace MediaWiki\Extension\CommunityConfiguration\EditorCapabilities;
4
5use LogicException;
6use MediaWiki\Context\IContextSource;
7use MediaWiki\Extension\CommunityConfiguration\Hooks\HookRunner;
8use MediaWiki\Extension\CommunityConfiguration\Provider\ConfigurationProviderFactory;
9use MediaWiki\Extension\CommunityConfiguration\Provider\IConfigurationProvider;
10use MediaWiki\Html\Html;
11use MediaWiki\Language\FormatterFactory;
12use MediaWiki\Linker\LinkRenderer;
13use MediaWiki\Status\StatusFormatter;
14use MediaWiki\Title\Title;
15use StatusValue;
16
17class GenericFormEditorCapability extends AbstractEditorCapability {
18
19    private ConfigurationProviderFactory $providerFactory;
20    private LinkRenderer $linkRenderer;
21    private StatusFormatter $statusFormatter;
22    private IConfigurationProvider $provider;
23    private HookRunner $hookRunner;
24    private MessagesProcessor $messagesProcessor;
25
26    public function __construct(
27        IContextSource $ctx,
28        Title $parentTitle,
29        ConfigurationProviderFactory $providerFactory,
30        LinkRenderer $linkRenderer,
31        FormatterFactory $formatterFactory,
32        HookRunner $hookRunner,
33        MessagesProcessor $messagesProcessor
34    ) {
35        parent::__construct( $ctx, $parentTitle );
36
37        $this->providerFactory = $providerFactory;
38        $this->linkRenderer = $linkRenderer;
39        $this->statusFormatter = $formatterFactory->getStatusFormatter( $ctx );
40        $this->hookRunner = $hookRunner;
41        $this->messagesProcessor = $messagesProcessor;
42    }
43
44    /**
45     * @param StatusValue $validationError
46     * @return void
47     */
48    private function displayValidationError( StatusValue $validationError ): void {
49        $out = $this->getContext()->getOutput();
50        $infoPageLinkTarget = $this->provider->getStore()->getInfoPageLinkTarget();
51        $infoPageLink = $infoPageLinkTarget !== null ? $this->linkRenderer->makeLink(
52            $infoPageLinkTarget
53        ) : null;
54
55        $out->addHTML( implode( "\n", [
56            // Add a generic headline
57            Html::rawElement(
58                'p', [ 'class' => 'error' ],
59                $this->msg(
60                    $infoPageLink !== null ?
61                        'communityconfiguration-invalid-stored-config-error-with-link'
62                        : 'communityconfiguration-invalid-stored-config-error'
63                )
64                    ->params(
65                        $this->provider->getName( $this->getContext() )->text(),
66                        $this->provider->getId()
67                    )
68                    ->rawParams( $infoPageLink )
69                    ->parse()
70            ),
71
72            // Add detailed information about the error
73            Html::element( 'h2', [], $this->msg(
74                'communityconfiguration-invalid-stored-config-error-details-headline'
75            )->text() ),
76            $this->statusFormatter->getHTML( $validationError )
77        ] ) );
78    }
79
80    /**
81     * @inheritDoc
82     */
83    public function execute( ?string $subpage ): void {
84        if ( $subpage === null ) {
85            throw new LogicException(
86                __CLASS__ . ' does not support $subpage param being null'
87            );
88        }
89
90        $this->provider = $this->providerFactory->newProvider( $subpage );
91        $providerId = $this->provider->getId();
92
93        $out = $this->getContext()->getOutput();
94
95        $linkTarget = $this->provider->getStore()->getInfoPageLinkTarget();
96        if ( $linkTarget !== null ) {
97            $out->getSkin()->setRelevantTitle( Title::newFromLinkTarget( $linkTarget ) );
98        }
99
100        $out->setPageTitleMsg( $this->msg(
101            'communityconfigurationeditor',
102            $this->provider->getName( $this )
103        ) );
104        $out->addSubtitle( '&lt; ' . $this->linkRenderer->makeLink(
105            $this->getParentTitle()
106        ) );
107
108        $helpPage = $this->provider->getOptionValue( 'helpPage' );
109        $helpURL = $this->provider->getOptionValue( 'helpURL' );
110
111        if ( $helpPage ) {
112            $out->addHelpLink( $helpPage );
113        } elseif ( $helpURL ) {
114            $out->addHelpLink( $helpURL, true );
115        }
116
117        $config = $this->provider->loadValidConfigurationUncached();
118        if ( !$config->isOK() ) {
119            $this->displayValidationError( $config );
120            $this->logger->error(
121                'Failed to load valid config from ' . $providerId,
122                [
123                    'errors' => $config->getErrors()
124                ]
125            );
126            return;
127        }
128
129        $validationWarnings = $config->getMessages();
130        if ( $validationWarnings !== [] ) {
131            $this->logger->warning(
132                __METHOD__ . ': Loaded config with warnings for {provider}',
133                [
134                    'provider' => $providerId,
135                    'warnings' => $validationWarnings
136                ]
137            );
138        }
139        $rootSchema = $this->provider->getValidator()->getSchemaBuilder()->getRootSchema();
140        $this->hookRunner->onCommunityConfigurationSchemaBeforeEditor( $this->provider, $rootSchema );
141        $canEdit = $this->provider->getStore()->definitelyCanEdit( $this->getContext()->getAuthority() );
142        $out->addJsConfigVars( [
143            'communityConfigurationData' => [
144                'providerId' => $providerId,
145                'schema' => $rootSchema,
146                'data' => $config->getValue(),
147                'config' => [
148                    'i18nPrefix' => "communityconfiguration-" . strtolower( $subpage ),
149                    'i18nMessages' => $this->messagesProcessor->getMessages(
150                        $providerId,
151                        $this->provider->getValidator()->getSchemaIterator(),
152                        'communityconfiguration'
153                    ),
154                    'feedbackURL' => $this->getContext()->getConfig()
155                        ->get( 'CommunityConfigurationFeedbackURL' ),
156                    'canEdit' => $canEdit
157                ]
158            ]
159        ] );
160        $infoTextKey = 'communityconfiguration-' . strtolower( $providerId ) . '-info-text';
161        if ( !$this->msg( $infoTextKey )->isDisabled() ) {
162            $out->addHTML( Html::rawElement(
163                'div',
164                [ 'class' => 'communityconfiguration-info-section' ],
165                $this->msg( $infoTextKey )->parseAsBlock()
166            ) );
167        }
168        $out->addModuleStyles( [ 'ext.communityConfiguration.Editor.styles' ] );
169        $out->addModules( [ 'ext.communityConfiguration.Editor' ] );
170
171        $out->addHTML( Html::rawElement(
172            'div',
173            [ 'class' => 'ext-communityConfiguration-LoadingBar' ],
174            implode( "\n", [
175                Html::rawElement(
176                    'p',
177                    [],
178                    $this->msg( 'communityconfiguration-editor-loading-info-text' )
179                ),
180                Html::rawElement(
181                    'div',
182                    [],
183                    Html::rawElement(
184                        'div',
185                        [
186                            'class' => 'cdx-progress-bar',
187                            'role' => 'progressbar'
188                        ],
189                        Html::rawElement(
190                            'div',
191                            [ 'class' => 'cdx-progress-bar__bar' ]
192                        )
193                    )
194                )
195            ] )
196        ) );
197        $out->addHTML( Html::rawElement(
198            'p',
199            [ 'class' => 'ext-communityConfiguration-NoJSFallback' ],
200            $this->msg( 'communityconfiguration-editor-nojs-fallback-text' )
201        ) );
202        $out->addHTML( Html::element( 'div', [ 'id' => 'ext-communityConfiguration-app-root' ] ) );
203    }
204}