Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 154 |
|
0.00% |
0 / 14 |
CRAP | |
0.00% |
0 / 1 |
PFHooks | |
0.00% |
0 / 154 |
|
0.00% |
0 / 14 |
1640 | |
0.00% |
0 / 1 |
registerExtension | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 | |||
initialize | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
registerModules | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
6 | |||
registerNamespaces | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
registerFunctions | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
2 | |||
setGlobalJSVariables | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
6 | |||
registerPageSchemasClass | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
addToAdminLinks | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
12 | |||
addToCargoTablesColumns | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
addToCargoTablesLinks | |
0.00% |
0 / 29 |
|
0.00% |
0 / 1 |
110 | |||
addToCargoTablesRow | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
disableTinyMCE | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
12 | |||
showFormPreview | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
20 | |||
setPostEditCookie | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | /** |
3 | * Static functions called by various outside hooks, as well as by |
4 | * extension.json. |
5 | * |
6 | * @author Yaron Koren |
7 | * @file |
8 | * @ingroup PF |
9 | */ |
10 | |
11 | use MediaWiki\MediaWikiServices; |
12 | use MediaWiki\ResourceLoader\ResourceLoader; |
13 | |
14 | class PFHooks { |
15 | |
16 | /** |
17 | * Used for caching by addToCargoTablesLinks(). |
18 | */ |
19 | private static $mMultiPageEditPage = null; |
20 | |
21 | public static function registerExtension() { |
22 | if ( defined( 'PF_VERSION' ) ) { |
23 | // Do not load Page Forms more than once. |
24 | return 1; |
25 | } |
26 | |
27 | define( 'PF_VERSION', '5.7.2' ); |
28 | |
29 | $GLOBALS['wgPageFormsIP'] = dirname( __DIR__ ) . '/../'; |
30 | |
31 | /** |
32 | * This is a delayed init that makes sure that MediaWiki is set |
33 | * up properly before we add our stuff. |
34 | */ |
35 | |
36 | if ( defined( 'SMW_VERSION' ) || ExtensionRegistry::getInstance()->isLoaded( 'SemanticMediaWiki' ) ) { |
37 | $GLOBALS['wgSpecialPages']['CreateProperty'] = 'PFCreateProperty'; |
38 | $GLOBALS['wgAutoloadClasses']['PFCreateProperty'] = __DIR__ . '/../specials/PF_CreateProperty.php'; |
39 | $GLOBALS['smwgEnabledSpecialPage'][] = 'RunQuery'; |
40 | } |
41 | |
42 | // Allow for popup windows for file upload |
43 | $GLOBALS['wgEditPageFrameOptions'] = 'SAMEORIGIN'; |
44 | } |
45 | |
46 | public static function initialize() { |
47 | $GLOBALS['wgPageFormsScriptPath'] = $GLOBALS['wgExtensionAssetsPath'] . '/PageForms'; |
48 | |
49 | // This global variable is needed so that other |
50 | // extensions can hook into it to add their own |
51 | // input types. |
52 | // @phan-suppress-next-line PhanUndeclaredFunctionInCallable |
53 | $GLOBALS['wgPageFormsFormPrinter'] = new StubObject( 'wgPageFormsFormPrinter', 'PFFormPrinter' ); |
54 | } |
55 | |
56 | /** |
57 | * Called by ResourceLoaderRegisterModules hook. |
58 | * |
59 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/ResourceLoaderRegisterModules |
60 | * |
61 | * @param ResourceLoader $resourceLoader The ResourceLoader object |
62 | */ |
63 | public static function registerModules( ResourceLoader $resourceLoader ) { |
64 | // These used to use a value of __DIR__ for 'localBasePath', |
65 | // but apparently in some installations that had a value of |
66 | // /PageForms/libs and in others just /PageForms, so we'll set |
67 | // the value here instead. |
68 | $pageFormsDir = __DIR__ . '/..'; |
69 | |
70 | $mapsModuleAttrs = [ |
71 | 'localBasePath' => $pageFormsDir, |
72 | 'remoteExtPath' => 'PageForms', |
73 | 'dependencies' => [ 'oojs-ui.styles.icons-location' ] |
74 | ]; |
75 | |
76 | if ( ExtensionRegistry::getInstance()->isLoaded( 'OpenLayers' ) ) { |
77 | $mapsModuleAttrs['scripts'] = '/libs/PF_maps.offline.js'; |
78 | $mapsModuleAttrs['dependencies'][] = 'ext.openlayers.main'; |
79 | } else { |
80 | $mapsModuleAttrs['scripts'] = '/libs/PF_maps.js'; |
81 | } |
82 | |
83 | $resourceLoader->register( [ 'ext.pageforms.maps' => $mapsModuleAttrs ] ); |
84 | } |
85 | |
86 | /** |
87 | * Register the namespaces for Page Forms. |
88 | * @see https://www.mediawiki.org/wiki/Manual:Hooks/CanonicalNamespaces |
89 | * |
90 | * @since 2.4.1 |
91 | * |
92 | * @param array &$list |
93 | */ |
94 | public static function registerNamespaces( array &$list ) { |
95 | global $wgNamespacesWithSubpages; |
96 | |
97 | if ( !defined( 'PF_NS_FORM' ) ) { |
98 | define( 'PF_NS_FORM', 106 ); |
99 | define( 'PF_NS_FORM_TALK', 107 ); |
100 | } |
101 | |
102 | $list[PF_NS_FORM] = 'Form'; |
103 | $list[PF_NS_FORM_TALK] = 'Form_talk'; |
104 | |
105 | // Support subpages only for talk pages by default |
106 | $wgNamespacesWithSubpages[PF_NS_FORM_TALK] = true; |
107 | } |
108 | |
109 | static function registerFunctions( Parser $parser ) { |
110 | $parser->setFunctionHook( 'default_form', [ 'PFDefaultForm', 'run' ] ); |
111 | $parser->setFunctionHook( 'forminput', [ 'PFFormInputParserFunction', 'run' ] ); |
112 | $parser->setFunctionHook( 'formlink', [ 'PFFormLink', 'run' ] ); |
113 | $parser->setFunctionHook( 'formredlink', [ 'PFFormRedLink', 'run' ] ); |
114 | $parser->setFunctionHook( 'queryformlink', [ 'PFQueryFormLink', 'run' ] ); |
115 | $parser->setFunctionHook( 'arraymap', [ 'PFArrayMap', 'run' ], Parser::SFH_OBJECT_ARGS ); |
116 | $parser->setFunctionHook( 'arraymaptemplate', [ 'PFArrayMapTemplate', 'run' ], Parser::SFH_OBJECT_ARGS ); |
117 | |
118 | $parser->setFunctionHook( 'autoedit', [ 'PFAutoEdit', 'run' ] ); |
119 | $parser->setFunctionHook( 'autoedit_rating', [ 'PFAutoEditRating', 'run' ] ); |
120 | $parser->setFunctionHook( 'template_params', [ 'PFTemplateParams', 'run' ] ); |
121 | $parser->setFunctionHook( 'template_display', [ 'PFTemplateDisplay', 'run' ], Parser::SFH_OBJECT_ARGS ); |
122 | } |
123 | |
124 | static function setGlobalJSVariables( &$vars ) { |
125 | global $wgPageFormsTargetName; |
126 | global $wgPageFormsAutocompleteValues, $wgPageFormsAutocompleteOnAllChars; |
127 | global $wgPageFormsFieldProperties, $wgPageFormsCargoFields, $wgPageFormsDependentFields; |
128 | global $wgPageFormsGridValues, $wgPageFormsGridParams; |
129 | global $wgPageFormsCalendarValues, $wgPageFormsCalendarParams, $wgPageFormsCalendarHTML; |
130 | global $wgPageFormsContLangYes, $wgPageFormsContLangNo, $wgPageFormsContLangMonths; |
131 | global $wgPageFormsHeightForMinimizingInstances; |
132 | global $wgPageFormsShowOnSelect, $wgPageFormsScriptPath; |
133 | global $edgValues, $wgPageFormsEDSettings; |
134 | global $wgAmericanDates; |
135 | |
136 | $vars['wgPageFormsTargetName'] = $wgPageFormsTargetName; |
137 | $vars['wgPageFormsAutocompleteValues'] = $wgPageFormsAutocompleteValues; |
138 | $vars['wgPageFormsAutocompleteOnAllChars'] = $wgPageFormsAutocompleteOnAllChars; |
139 | $vars['wgPageFormsFieldProperties'] = $wgPageFormsFieldProperties; |
140 | $vars['wgPageFormsCargoFields'] = $wgPageFormsCargoFields; |
141 | $vars['wgPageFormsDependentFields'] = $wgPageFormsDependentFields; |
142 | $vars['wgPageFormsCalendarValues'] = $wgPageFormsCalendarValues; |
143 | $vars['wgPageFormsCalendarParams'] = $wgPageFormsCalendarParams; |
144 | $vars['wgPageFormsCalendarHTML'] = $wgPageFormsCalendarHTML; |
145 | $vars['wgPageFormsGridValues'] = $wgPageFormsGridValues; |
146 | $vars['wgPageFormsGridParams'] = $wgPageFormsGridParams; |
147 | $vars['wgPageFormsContLangYes'] = $wgPageFormsContLangYes; |
148 | $vars['wgPageFormsContLangNo'] = $wgPageFormsContLangNo; |
149 | $vars['wgPageFormsContLangMonths'] = $wgPageFormsContLangMonths; |
150 | $vars['wgPageFormsHeightForMinimizingInstances'] = $wgPageFormsHeightForMinimizingInstances; |
151 | $vars['wgPageFormsShowOnSelect'] = $wgPageFormsShowOnSelect; |
152 | $vars['wgPageFormsScriptPath'] = $wgPageFormsScriptPath; |
153 | if ( method_exists( 'EDParserFunctions', 'getAllValues' ) ) { |
154 | // External Data 2.3+ |
155 | $vars['edgValues'] = EDParserFunctions::getAllValues(); |
156 | } else { |
157 | $vars['edgValues'] = $edgValues; |
158 | } |
159 | $vars['wgPageFormsEDSettings'] = $wgPageFormsEDSettings; |
160 | $vars['wgAmericanDates'] = $wgAmericanDates; |
161 | } |
162 | |
163 | public static function registerPageSchemasClass() { |
164 | global $wgPageSchemasHandlerClasses; |
165 | $wgPageSchemasHandlerClasses[] = 'PFPageSchemas'; |
166 | return true; |
167 | } |
168 | |
169 | public static function addToAdminLinks( &$admin_links_tree ) { |
170 | $data_structure_label = wfMessage( 'pf-adminlinks-datastructure' )->escaped(); |
171 | $data_structure_section = $admin_links_tree->getSection( $data_structure_label ); |
172 | if ( $data_structure_section === null ) { |
173 | $data_structure_section = new ALSection( wfMessage( 'pf-adminlinks-datastructure' )->escaped() ); |
174 | } |
175 | |
176 | $pf_row = new ALRow( 'pageforms' ); |
177 | $pf_row->addItem( ALItem::newFromSpecialPage( 'Categories' ) ); |
178 | $data_structure_section->addRow( $pf_row ); |
179 | $pf_admin_row = new ALRow( 'pageforms_admin' ); |
180 | $data_structure_section->addRow( $pf_admin_row ); |
181 | |
182 | $admin_links_tree->addSection( $data_structure_section, wfMessage( 'adminlinks_browsesearch' )->escaped() ); |
183 | |
184 | $pf_row->addItem( ALItem::newFromSpecialPage( 'Templates' ), 'Properties' ); |
185 | $pf_row->addItem( ALItem::newFromSpecialPage( 'Forms' ), 'SemanticStatistics' ); |
186 | $pf_row->addItem( ALItem::newFromSpecialPage( 'MultiPageEdit' ) ); |
187 | $pf_admin_row->addItem( ALItem::newFromSpecialPage( 'CreateClass' ), 'SMWAdmin' ); |
188 | if ( class_exists( 'PFCreateProperty' ) ) { |
189 | $pf_admin_row->addItem( ALItem::newFromSpecialPage( 'CreateProperty' ), 'SMWAdmin' ); |
190 | } |
191 | $pf_admin_row->addItem( ALItem::newFromSpecialPage( 'CreateTemplate' ), 'SMWAdmin' ); |
192 | $pf_admin_row->addItem( ALItem::newFromSpecialPage( 'CreateForm' ), 'SMWAdmin' ); |
193 | $pf_admin_row->addItem( ALItem::newFromSpecialPage( 'CreateCategory' ), 'SMWAdmin' ); |
194 | } |
195 | |
196 | public static function addToCargoTablesColumns( $cargoTablesPage, &$allowedActions ) { |
197 | if ( !$cargoTablesPage->getUser()->isAllowed( 'multipageedit' ) ) { |
198 | return; |
199 | } |
200 | |
201 | $cargoTablesPage->getOutput()->addModuleStyles( [ 'oojs-ui.styles.icons-editing-core' ] ); |
202 | |
203 | $editColumn = [ 'edit' => [ 'ooui-icon' => 'edit', 'ooui-title' => 'edit' ] ]; |
204 | $indexOfDrilldown = array_search( 'drilldown', array_keys( $allowedActions ) ); |
205 | $pos = $indexOfDrilldown === false ? count( $allowedActions ) : $indexOfDrilldown + 1; |
206 | $allowedActions = array_merge( array_slice( $allowedActions, 0, $pos ), $editColumn, array_slice( $allowedActions, $pos ) ); |
207 | } |
208 | |
209 | /** |
210 | * Called by the CargoTablesActionLinks hook. |
211 | * |
212 | * Adds an "Edit" link to Special:CargoTables, pointing to Special:MultiPageEdit. |
213 | * |
214 | * @param array &$actionLinks Action links |
215 | * @param string $tableName Cargo table name |
216 | * @param bool $isReplacementTable Whether this table is a replacement table |
217 | * @param bool $hasReplacementTable Whether this table has a replacement table |
218 | * @param int[][] $templatesThatDeclareTables |
219 | * @param string[] $templatesThatAttachToTables |
220 | * @param User|null $user The current user |
221 | * |
222 | * @since 4.4 |
223 | */ |
224 | public static function addToCargoTablesLinks( &$actionLinks, $tableName, $isReplacementTable, $hasReplacementTable, $templatesThatDeclareTables, $templatesThatAttachToTables, $user = null ) { |
225 | // If it has a "replacement table", it's read-only and can't |
226 | // be edited (though the replacement table can). |
227 | if ( $hasReplacementTable ) { |
228 | return; |
229 | } |
230 | |
231 | // Check permissions. |
232 | if ( $user == null ) { |
233 | // For Cargo versions < 3.1. |
234 | $user = RequestContext::getMain()->getUser(); |
235 | } |
236 | |
237 | if ( !$user->isAllowed( 'multipageedit' ) ) { |
238 | return; |
239 | } |
240 | // Only put in an "Edit" link if there's exactly one template |
241 | // for this Cargo table, and one form for that template. |
242 | if ( !array_key_exists( $tableName, $templatesThatDeclareTables ) ) { |
243 | return; |
244 | } |
245 | if ( array_key_exists( $tableName, $templatesThatAttachToTables ) ) { |
246 | return; |
247 | } |
248 | $templateIDs = $templatesThatDeclareTables[$tableName]; |
249 | if ( count( $templateIDs ) > 1 ) { |
250 | return; |
251 | } |
252 | |
253 | $templateTitle = Title::newFromID( $templateIDs[0] ); |
254 | $templateName = $templateTitle->getText(); |
255 | if ( self::$mMultiPageEditPage == null ) { |
256 | self::$mMultiPageEditPage = new PFMultiPageEdit(); |
257 | self::$mMultiPageEditPage->setTemplateList(); |
258 | } |
259 | $formName = self::$mMultiPageEditPage->getFormForTemplate( $templateName ); |
260 | if ( $formName == null ) { |
261 | return; |
262 | } |
263 | |
264 | $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer(); |
265 | $sp = PFUtils::getSpecialPage( 'MultiPageEdit' ); |
266 | $editMsg = wfMessage( 'edit' )->text(); |
267 | $linkParams = [ 'template' => $templateName, 'form' => $formName ]; |
268 | $text = $linkRenderer->makeKnownLink( $sp->getPageTitle(), $editMsg, [], $linkParams ); |
269 | |
270 | $indexOfDrilldown = array_search( 'drilldown', array_keys( $actionLinks ) ); |
271 | $pos = $indexOfDrilldown === false ? count( $actionLinks ) : $indexOfDrilldown + 1; |
272 | $actionLinks = array_merge( array_slice( $actionLinks, 0, $pos ), [ 'edit' => $text ], array_slice( $actionLinks, $pos ) ); |
273 | } |
274 | |
275 | /** |
276 | * Called by the CargoTablesSetActionLinks hook. |
277 | * |
278 | * Adds an "Edit" link to Special:CargoTables, pointing to Special:MultiPageEdit. |
279 | * |
280 | * @param SpecialPage $cargoTablesPage |
281 | * @param array &$actionLinks Action links |
282 | * @param string $tableName Cargo table name |
283 | * @param bool $isReplacementTable Whether this table iss a replacement table |
284 | * @param bool $hasReplacementTable Whether this table has a replacement table |
285 | * @param int[][] $templatesThatDeclareTables |
286 | * @param string[] $templatesThatAttachToTables |
287 | * @param string[] $actionList |
288 | * @param User|null $user The current user |
289 | * |
290 | * @since 4.8.1 |
291 | */ |
292 | public static function addToCargoTablesRow( $cargoTablesPage, &$actionLinks, $tableName, $isReplacementTable, $hasReplacementTable, $templatesThatDeclareTables, $templatesThatAttachToTables, $actionList, $user = null ) { |
293 | $cargoTablesPage->getOutput()->addModuleStyles( [ 'oojs-ui.styles.icons-editing-core' ] ); |
294 | |
295 | // For the sake of simplicity, this function basically just |
296 | // wraps around the previous hook function, for Cargo <= 2.4. |
297 | // That's why there's this awkward behavior of parsing links |
298 | // to get their URL. Hopefully this won't cause problems. |
299 | self::addToCargoTablesLinks( $actionLinks, $tableName, $isReplacementTable, $hasReplacementTable, $templatesThatDeclareTables, $templatesThatAttachToTables, $user ); |
300 | |
301 | if ( array_key_exists( 'edit', $actionLinks ) ) { |
302 | preg_match( '/href="(.*?)"/', $actionLinks['edit'], $matches ); |
303 | $mpeURL = html_entity_decode( $matches[1] ); |
304 | $actionLinks['edit'] = $cargoTablesPage->getActionButton( 'edit', $mpeURL ); |
305 | } |
306 | } |
307 | |
308 | /** |
309 | * Disable TinyMCE if this is a form definition page, or a form-editable page. |
310 | * |
311 | * @param Title $title The page Title object |
312 | * @return bool Whether or not to disable TinyMCE |
313 | */ |
314 | public static function disableTinyMCE( $title ) { |
315 | if ( $title->getNamespace() == PF_NS_FORM ) { |
316 | return false; |
317 | } |
318 | |
319 | $defaultForms = PFFormLinker::getDefaultFormsForPage( $title ); |
320 | if ( count( $defaultForms ) > 0 ) { |
321 | return false; |
322 | } |
323 | |
324 | return true; |
325 | } |
326 | |
327 | public static function showFormPreview( EditPage $editpage, WebRequest $request ) { |
328 | global $wgOut, $wgPageFormsFormPrinter; |
329 | |
330 | wfDebug( __METHOD__ . ": enter.\n" ); |
331 | |
332 | // Exit if we're not in preview mode. |
333 | if ( !$editpage->preview ) { |
334 | return; |
335 | } |
336 | // Exit if we aren't in the "Form" namespace. |
337 | if ( $editpage->getArticle()->getTitle()->getNamespace() != PF_NS_FORM ) { |
338 | return; |
339 | } |
340 | |
341 | // Needed in case there are any OOUI-based input types in the form. |
342 | $wgOut->enableOOUI(); |
343 | |
344 | $previewNote = $wgOut->parseAsInterface( wfMessage( 'pf-preview-note' )->text() ); |
345 | // The "pfForm" ID is there so the form JS will be activated. |
346 | $editpage->previewTextAfterContent .= Html::element( 'h2', null, wfMessage( 'pf-preview-header' )->text() ) . "\n" . |
347 | '<div id="pfForm" class="previewnote" style="font-weight: bold">' . $previewNote . "</div>\n<hr />\n"; |
348 | |
349 | $form_definition = StringUtils::delimiterReplace( '<noinclude>', '</noinclude>', '', $editpage->textbox1 ); |
350 | list( $form_text, $data_text, $form_page_title, $generated_page_name ) = |
351 | $wgPageFormsFormPrinter->formHTML( $form_definition, null, false, null, null, "Page Forms form preview dummy title", null ); |
352 | |
353 | $parserOutput = PFUtils::getParser()->getOutput(); |
354 | if ( method_exists( $wgOut, 'addParserOutputMetadata' ) ) { |
355 | $wgOut->addParserOutputMetadata( $parserOutput ); |
356 | } else { |
357 | $wgOut->addParserOutputNoText( $parserOutput ); |
358 | } |
359 | |
360 | PFUtils::addFormRLModules(); |
361 | $editpage->previewTextAfterContent .= |
362 | '<div style="margin-top: 15px">' . $form_text . "</div>"; |
363 | } |
364 | |
365 | /** |
366 | * Called by the PageSaveComplete hook. |
367 | * |
368 | * Set a cookie after the page save so that a "Your edit was saved" |
369 | * popup will appear after form-based saves, just as it does after |
370 | * standard saves. This code will be called after all saves, which |
371 | * means that it will lead to redundant cookie-setting after normal |
372 | * saves. However, there doesn't appear to be a way to to set the |
373 | * cookie correctly only after form-based saves, unfortunately. |
374 | * |
375 | * @param WikiPage $wikiPage |
376 | * @param MediaWiki\User\UserIdentity $user |
377 | * @param string $summary |
378 | * @param int $flags |
379 | * @param MediaWiki\Revision\RevisionRecord $revisionRecord |
380 | * @param MediaWiki\Storage\EditResult $editResult |
381 | */ |
382 | public static function setPostEditCookie( WikiPage $wikiPage, MediaWiki\User\UserIdentity $user, string $summary, int $flags, |
383 | MediaWiki\Revision\RevisionRecord $revisionRecord, MediaWiki\Storage\EditResult $editResult |
384 | ) { |
385 | // Have this take effect only if the save came from a form - |
386 | // we need to use a global variable to determine that. |
387 | global $wgPageFormsFormPrinter; |
388 | if ( !property_exists( $wgPageFormsFormPrinter, 'mInputTypeHooks' ) ) { |
389 | return; |
390 | } |
391 | |
392 | // Code based loosely on EditPage::setPostEditCookie(). |
393 | $postEditKey = EditPage::POST_EDIT_COOKIE_KEY_PREFIX . $revisionRecord->getID(); |
394 | $response = RequestContext::getMain()->getRequest()->response(); |
395 | $response->setCookie( $postEditKey, 'saved', time() + EditPage::POST_EDIT_COOKIE_DURATION ); |
396 | } |
397 | |
398 | } |