Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
94.05% |
79 / 84 |
|
80.00% |
4 / 5 |
CRAP | |
0.00% |
0 / 1 |
| PFFormLinker | |
94.05% |
79 / 84 |
|
80.00% |
4 / 5 |
28.17 | |
0.00% |
0 / 1 |
| getDefaultForm | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
2 | |||
| createPageWithForm | |
100.00% |
21 / 21 |
|
100.00% |
1 / 1 |
3 | |||
| setBrokenLink | |
100.00% |
15 / 15 |
|
100.00% |
1 / 1 |
6 | |||
| getDefaultFormsForPage | |
82.76% |
24 / 29 |
|
0.00% |
0 / 1 |
13.87 | |||
| getDefaultFormForNamespace | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
4 | |||
| 1 | <?php |
| 2 | |
| 3 | use MediaWiki\Linker\LinkRenderer; |
| 4 | use MediaWiki\MediaWikiServices; |
| 5 | use MediaWiki\Title\Title; |
| 6 | |
| 7 | /** |
| 8 | * Gets the form(s) used to edit a page, both for existing pages and for |
| 9 | * not-yet-created, red-linked pages. |
| 10 | * |
| 11 | * @author Yaron Koren |
| 12 | * @file |
| 13 | * @ingroup PF |
| 14 | */ |
| 15 | |
| 16 | class PFFormLinker { |
| 17 | |
| 18 | private static $formPerNamespace = []; |
| 19 | |
| 20 | static function getDefaultForm( ?Title $title ): ?string { |
| 21 | // The title passed in can be null in at least one |
| 22 | // situation: if the "namespace page" is being checked, and |
| 23 | // the project namespace alias contains any non-ASCII |
| 24 | // characters. There may be other cases too. |
| 25 | // If that happens, just exit. |
| 26 | if ( $title === null ) { |
| 27 | return null; |
| 28 | } |
| 29 | |
| 30 | $props = MediaWikiServices::getInstance()->getPageProps() |
| 31 | ->getProperties( $title, [ 'PFDefaultForm', 'SFDefaultForm' ] ); |
| 32 | $pageID = $title->getArticleID(); |
| 33 | |
| 34 | // Keep backward compatibility with the page property name for Semantic Forms. |
| 35 | return $props[$pageID]['PFDefaultForm'] ?? $props[$pageID]['SFDefaultForm'] ?? null; |
| 36 | } |
| 37 | |
| 38 | public static function createPageWithForm( $title, $formName, $inQueryArr ) { |
| 39 | /** @var PFFormPrinter $wgPageFormsFormPrinter */ |
| 40 | global $wgPageFormsFormPrinter, $wgOut; |
| 41 | |
| 42 | $wgOut->enableOOUI(); |
| 43 | |
| 44 | $formTitle = Title::makeTitleSafe( PF_NS_FORM, $formName ); |
| 45 | $formDefinition = PFUtils::getPageText( $formTitle ); |
| 46 | $preloadContent = null; |
| 47 | |
| 48 | // Allow outside code to set/change the preloaded text. |
| 49 | MediaWikiServices::getInstance()->getHookContainer()->run( 'PageForms::EditFormPreloadText', [ &$preloadContent, $title, $formTitle ] ); |
| 50 | |
| 51 | [ $formText, $pageText, $formPageTitle, $generatedPageName ] = |
| 52 | $wgPageFormsFormPrinter->formHTML( |
| 53 | $formDefinition, false, false, null, $preloadContent, |
| 54 | 'Some very long page name that will hopefully never get created ABCDEF123', |
| 55 | null, PFFormPrinter::CONTEXT_AUTOCREATE, $inQueryArr |
| 56 | ); |
| 57 | $params = []; |
| 58 | |
| 59 | // Get user "responsible" for all auto-generated |
| 60 | // pages from red links. |
| 61 | $userID = 1; |
| 62 | global $wgPageFormsAutoCreateUser; |
| 63 | if ( $wgPageFormsAutoCreateUser !== null ) { |
| 64 | $user = MediaWikiServices::getInstance()->getUserFactory()->newFromName( $wgPageFormsAutoCreateUser ); |
| 65 | if ( $user !== null ) { |
| 66 | $userID = $user->getId(); |
| 67 | } |
| 68 | } |
| 69 | $params['user_id'] = $userID; |
| 70 | $params['page_text'] = $pageText; |
| 71 | $job = new PFCreatePageJob( $title, $params ); |
| 72 | MediaWikiServices::getInstance()->getJobQueueGroup()->push( $job ); |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * Called by the HtmlPageLinkRendererEnd hook. |
| 77 | * The $target argument is listed in the documentation as being of type |
| 78 | * LinkTarget, but in practice it seems to sometimes be of type Title |
| 79 | * and sometimes of type TitleValue. So we just leave out a type |
| 80 | * declaration for that argument in the header. |
| 81 | * |
| 82 | * @param LinkRenderer $linkRenderer |
| 83 | * @param Title $target |
| 84 | * @param bool $isKnown |
| 85 | * @param string &$text |
| 86 | * @param array &$attribs |
| 87 | * @param bool &$ret |
| 88 | * @return true |
| 89 | */ |
| 90 | static function setBrokenLink( LinkRenderer $linkRenderer, $target, $isKnown, &$text, &$attribs, &$ret ) { |
| 91 | global $wgContentNamespaces; |
| 92 | global $wgPageFormsLinkAllRedLinksToForms; |
| 93 | |
| 94 | // If it's not a broken (red) link, exit. |
| 95 | if ( $isKnown ) { |
| 96 | return true; |
| 97 | } |
| 98 | |
| 99 | $namespace = $target->getNamespace(); |
| 100 | |
| 101 | // Quick check. |
| 102 | if ( $namespace == NS_SPECIAL ) { |
| 103 | return true; |
| 104 | } |
| 105 | |
| 106 | if ( self::getDefaultFormForNamespace( $namespace ) !== null ) { |
| 107 | $title = Title::newFromLinkTarget( $target ); |
| 108 | $attribs['href'] = $title->getLinkURL( [ 'action' => 'formedit', 'redlink' => '1' ] ); |
| 109 | return true; |
| 110 | } |
| 111 | |
| 112 | // If there's no default form, keep going only if we're |
| 113 | // modifying "all" red links, and this is a link to a |
| 114 | // content namespace. |
| 115 | if ( !$wgPageFormsLinkAllRedLinksToForms || |
| 116 | !in_array( $namespace, $wgContentNamespaces ) ) { |
| 117 | return true; |
| 118 | } |
| 119 | |
| 120 | // We're still here - change the link. |
| 121 | // The class of $target can be either Title or |
| 122 | // TitleValue. |
| 123 | $title = Title::newFromLinkTarget( $target ); |
| 124 | $attribs['href'] = $title->getLinkURL( [ 'action' => 'formedit', 'redlink' => '1' ] ); |
| 125 | |
| 126 | return true; |
| 127 | } |
| 128 | |
| 129 | /** |
| 130 | * Get the form(s) used to edit this page - either: |
| 131 | * - the default form(s) for the page itself, if there are any; or |
| 132 | * - the default form(s) for a category that this article belongs to, |
| 133 | * if there are any; or |
| 134 | * - the default form(s) for the article's namespace, if there are any. |
| 135 | * @param Title $title |
| 136 | * @return array |
| 137 | */ |
| 138 | static function getDefaultFormsForPage( $title ) { |
| 139 | // See if the page itself has a default form (or forms), and |
| 140 | // return it/them if so. |
| 141 | // (Disregard category pages for this check.) |
| 142 | if ( $title->getNamespace() != NS_CATEGORY ) { |
| 143 | $default_form = self::getDefaultForm( $title ); |
| 144 | if ( $default_form === '' ) { |
| 145 | // A call to "{{#default_form:}}" (i.e., no form |
| 146 | // specified) should cancel any inherited forms. |
| 147 | return []; |
| 148 | } elseif ( $default_form !== null ) { |
| 149 | return [ $default_form ]; |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | // If this is not a category page, look for a default form |
| 154 | // for its parent category or categories. |
| 155 | $namespace = $title->getNamespace(); |
| 156 | if ( NS_CATEGORY !== $namespace ) { |
| 157 | $default_forms = []; |
| 158 | $categories = PFValuesUtils::getCategoriesForPage( $title ); |
| 159 | foreach ( $categories as $category ) { |
| 160 | if ( class_exists( 'PSSchema' ) ) { |
| 161 | // Check the Page Schema, if one exists. |
| 162 | $psSchema = new PSSchema( $category ); |
| 163 | if ( $psSchema->isPSDefined() ) { |
| 164 | $formName = PFPageSchemas::getFormName( $psSchema ); |
| 165 | if ( $formName !== null ) { |
| 166 | $default_forms[] = $formName; |
| 167 | } |
| 168 | } |
| 169 | } |
| 170 | $categoryPage = Title::makeTitleSafe( NS_CATEGORY, $category ); |
| 171 | $defaultFormForCategory = self::getDefaultForm( $categoryPage ); |
| 172 | if ( $defaultFormForCategory != '' ) { |
| 173 | $default_forms[] = $defaultFormForCategory; |
| 174 | } |
| 175 | } |
| 176 | if ( count( $default_forms ) > 0 ) { |
| 177 | // It is possible for two categories to have the same default form, so purge any |
| 178 | // duplicates from the array to avoid a "more than one default form" warning. |
| 179 | return array_unique( $default_forms ); |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | // All that's left is checking for the namespace. If this is |
| 184 | // a subpage, exit out - default forms for namespaces don't |
| 185 | // apply to subpages. |
| 186 | if ( $title->isSubpage() ) { |
| 187 | return []; |
| 188 | } |
| 189 | |
| 190 | $default_form = self::getDefaultFormForNamespace( $namespace ); |
| 191 | if ( $default_form != '' ) { |
| 192 | return [ $default_form ]; |
| 193 | } |
| 194 | |
| 195 | return []; |
| 196 | } |
| 197 | |
| 198 | public static function getDefaultFormForNamespace( $namespace ) { |
| 199 | if ( array_key_exists( $namespace, self::$formPerNamespace ) ) { |
| 200 | return self::$formPerNamespace[$namespace]; |
| 201 | } |
| 202 | |
| 203 | if ( NS_MAIN === $namespace ) { |
| 204 | // If it's in the main (blank) namespace, check for the |
| 205 | // file named with the word for "Main" in this language. |
| 206 | $namespace_label = wfMessage( 'pf_blank_namespace' )->inContentLanguage()->text(); |
| 207 | } else { |
| 208 | $namespace_labels = PFUtils::getContLang()->getNamespaces(); |
| 209 | if ( !array_key_exists( $namespace, $namespace_labels ) ) { |
| 210 | // This can happen if it's a custom namespace that |
| 211 | // was not entirely correctly declared. |
| 212 | self::$formPerNamespace[$namespace] = null; |
| 213 | return null; |
| 214 | } |
| 215 | $namespace_label = $namespace_labels[$namespace]; |
| 216 | } |
| 217 | |
| 218 | $namespacePage = Title::makeTitleSafe( NS_PROJECT, $namespace_label ); |
| 219 | $defaultForm = self::getDefaultForm( $namespacePage ); |
| 220 | self::$formPerNamespace[$namespace] = $defaultForm; |
| 221 | return $defaultForm; |
| 222 | } |
| 223 | } |