Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
96.30% |
130 / 135 |
|
66.67% |
2 / 3 |
CRAP | |
0.00% |
0 / 1 |
GenericFormEditorCapability | |
96.30% |
130 / 135 |
|
66.67% |
2 / 3 |
12 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
displayValidationError | |
100.00% |
25 / 25 |
|
100.00% |
1 / 1 |
3 | |||
execute | |
95.19% |
99 / 104 |
|
0.00% |
0 / 1 |
8 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\CommunityConfiguration\EditorCapabilities; |
4 | |
5 | use LogicException; |
6 | use MediaWiki\Context\IContextSource; |
7 | use MediaWiki\Extension\CommunityConfiguration\Hooks\HookRunner; |
8 | use MediaWiki\Extension\CommunityConfiguration\Provider\ConfigurationProviderFactory; |
9 | use MediaWiki\Extension\CommunityConfiguration\Provider\IConfigurationProvider; |
10 | use MediaWiki\Html\Html; |
11 | use MediaWiki\Language\FormatterFactory; |
12 | use MediaWiki\Linker\LinkRenderer; |
13 | use MediaWiki\Status\StatusFormatter; |
14 | use MediaWiki\Title\Title; |
15 | use StatusValue; |
16 | |
17 | class 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( '< ' . $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 | } |