Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 137 |
|
0.00% |
0 / 6 |
CRAP | |
0.00% |
0 / 1 |
Hooks | |
0.00% |
0 / 137 |
|
0.00% |
0 / 6 |
2162 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
onParserFirstCallInit | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
onEditPage__showEditForm_initial | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
12 | |||
renderParserFunction | |
0.00% |
0 / 83 |
|
0.00% |
0 / 1 |
756 | |||
saveProp | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
110 | |||
peopleList | |
0.00% |
0 / 20 |
|
0.00% |
0 / 1 |
20 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\Genealogy; |
4 | |
5 | use EditPage; |
6 | use Html; |
7 | use MediaWiki\Hook\EditPage__showEditForm_initialHook; |
8 | use MediaWiki\Hook\ParserFirstCallInitHook; |
9 | use MediaWiki\Linker\LinkRenderer; |
10 | use OutputPage; |
11 | use Parser; |
12 | use Title; |
13 | use Wikimedia\ParamValidator\TypeDef\BooleanDef; |
14 | use Wikimedia\Rdbms\ILoadBalancer; |
15 | |
16 | // phpcs:disable MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName |
17 | class Hooks implements ParserFirstCallInitHook, EditPage__showEditForm_initialHook { |
18 | |
19 | /** @var LinkRenderer */ |
20 | private LinkRenderer $linkRenderer; |
21 | |
22 | /** @var ILoadBalancer */ |
23 | private ILoadBalancer $loadBalancer; |
24 | |
25 | public function __construct( LinkRenderer $linkRenderer, ILoadBalancer $loadBalancer ) { |
26 | $this->linkRenderer = $linkRenderer; |
27 | $this->loadBalancer = $loadBalancer; |
28 | } |
29 | |
30 | /** |
31 | * @inheritDoc |
32 | */ |
33 | public function onParserFirstCallInit( $parser ) { |
34 | $parser->setFunctionHook( 'genealogy', [ $this, 'renderParserFunction' ] ); |
35 | return true; |
36 | } |
37 | |
38 | /** |
39 | * This method is called by the EditPage::showEditForm:initial hook and adds a list of the |
40 | * current page's Genealogy partners that *are not* a result of a {{#genealogy:partner|…}} call |
41 | * in the current page. |
42 | * @param EditPage $editPage The current page that's being edited. |
43 | * @param OutputPage $output The output. |
44 | * @return void |
45 | */ |
46 | public function onEditPage__showEditForm_initial( $editPage, $output ) { |
47 | $person = new Person( $this->loadBalancer, $editPage->getTitle() ); |
48 | $peopleList = []; |
49 | foreach ( $person->getPartners( true ) as $partner ) { |
50 | $peopleList[] = $this->linkRenderer->makeKnownLink( $partner->getTitle() ); |
51 | } |
52 | if ( count( $peopleList ) > 0 ) { |
53 | $msg = $output->msg( 'genealogy-existing-partners', count( $peopleList ) ); |
54 | $partnersMsg = $msg->escaped() . ' ' . implode( ', ', $peopleList ); |
55 | $output->addHTML( Html::rawElement( 'p', [], $partnersMsg ) ); |
56 | } |
57 | } |
58 | |
59 | /** |
60 | * Render the output of the parser function. |
61 | * The input parameters are wikitext with templates expanded. |
62 | * The output should be wikitext too. |
63 | * @param Parser $parser The parser. |
64 | * @return string|mixed[] The wikitext with which to replace the parser function call. |
65 | */ |
66 | public function renderParserFunction( Parser $parser ) { |
67 | $params = []; |
68 | $args = func_get_args(); |
69 | // Remove $parser from the args. |
70 | array_shift( $args ); |
71 | // Get param 1, the function type. |
72 | $type = array_shift( $args ); |
73 | // Everything that remains is required to be named (i.e. we discard other unnamed args). |
74 | foreach ( $args as $arg ) { |
75 | $pair = explode( '=', $arg, 2 ); |
76 | if ( count( $pair ) == 2 ) { |
77 | $name = trim( $pair[0] ); |
78 | $value = trim( $pair[1] ); |
79 | if ( in_array( $value, BooleanDef::$TRUEVALS, true ) ) { |
80 | $value = true; |
81 | } |
82 | if ( in_array( $value, BooleanDef::$FALSEVALS, true ) ) { |
83 | $value = false; |
84 | } |
85 | if ( $value !== '' ) { |
86 | $params[$name] = $value; |
87 | } |
88 | } else { |
89 | $params[] = $arg; |
90 | } |
91 | } |
92 | $out = ''; |
93 | $isHtml = false; |
94 | switch ( $type ) { |
95 | case 'person': |
96 | if ( isset( $params['birth date'] ) ) { |
97 | $out .= $params['birth date']; |
98 | $this->saveProp( $parser, 'birth date', $params['birth date'], false ); |
99 | } |
100 | if ( isset( $params['death date'] ) ) { |
101 | $out .= $params['death date']; |
102 | $this->saveProp( $parser, 'death date', $params['death date'], false ); |
103 | } |
104 | break; |
105 | case 'description': |
106 | if ( isset( $params[0] ) ) { |
107 | $out = $params[0]; |
108 | $this->saveProp( $parser, 'description', $out, false ); |
109 | } |
110 | break; |
111 | case 'parent': |
112 | $parentTitle = Title::newFromText( $params[0] ); |
113 | if ( !$parentTitle instanceof Title ) { |
114 | $invalidTitle = wfEscapeWikiText( $params[0] ); |
115 | $isHtml = true; |
116 | $msg = wfMessage( 'genealogy-invalid-parent-title', $invalidTitle )->escaped(); |
117 | $out .= Html::rawElement( 'span', [ 'class' => 'error' ], $msg ); |
118 | } else { |
119 | $parent = new Person( $this->loadBalancer, $parentTitle ); |
120 | // Even though it's a list of one, output a parent link according to the same |
121 | // system as the other relation types, so that it uses the same template. |
122 | $out .= $this->peopleList( $parser, [ $parent ] ); |
123 | $this->saveProp( $parser, 'parent', $parentTitle ); |
124 | } |
125 | break; |
126 | case 'siblings': |
127 | $person = new Person( $this->loadBalancer, $parser->getTitle() ); |
128 | $excludeSelf = isset( $params['exclude_self'] ) && $params['exclude_self']; |
129 | $out .= $this->peopleList( $parser, $person->getSiblings( $excludeSelf ) ); |
130 | break; |
131 | case 'partner': |
132 | $partnerTitle = Title::newFromText( $params[0] ); |
133 | if ( !$partnerTitle instanceof Title ) { |
134 | $invalidTitle = wfEscapeWikiText( $params[0] ); |
135 | $isHtml = true; |
136 | $msg = wfMessage( 'genealogy-invalid-partner-title', $invalidTitle )->escaped(); |
137 | $out .= Html::rawElement( 'span', [ 'class' => 'error' ], $msg ); |
138 | } else { |
139 | $this->saveProp( $parser, 'partner', $partnerTitle ); |
140 | } |
141 | break; |
142 | case 'partners': |
143 | $person = new Person( $this->loadBalancer, $parser->getTitle() ); |
144 | $out .= $this->peopleList( $parser, $person->getPartners() ); |
145 | break; |
146 | case 'children': |
147 | $person = new Person( $this->loadBalancer, $parser->getTitle() ); |
148 | $out .= $this->peopleList( $parser, $person->getChildren() ); |
149 | break; |
150 | case 'tree': |
151 | $tree = new Tree( $this->loadBalancer ); |
152 | $delimiter = $params['delimiter'] ?? "\n"; |
153 | if ( isset( $params['ancestors'] ) ) { |
154 | $tree->addAncestors( explode( $delimiter, $params['ancestors'] ) ); |
155 | } |
156 | if ( isset( $params['ancestor depth'] ) ) { |
157 | $tree->setAncestorDepth( $params['ancestor depth'] ); |
158 | } |
159 | if ( isset( $params['descendants'] ) ) { |
160 | $tree->addDescendants( explode( $delimiter, $params['descendants'] ) ); |
161 | } |
162 | if ( isset( $params['descendant depth'] ) ) { |
163 | $tree->setDescendantDepth( $params['descendant depth'] ); |
164 | } |
165 | if ( isset( $params['format'] ) ) { |
166 | $tree->setFormat( $params['format'] ); |
167 | } |
168 | $out = $tree->getWikitext( $parser ); |
169 | break; |
170 | default: |
171 | $msg = wfMessage( 'genealogy-parser-function-not-found', [ $type ] )->text(); |
172 | $out .= Html::element( 'span', [ 'class' => 'error' ], $msg ); |
173 | break; |
174 | } |
175 | // Return format is documented in Parser::setFunctionHook(). |
176 | return $isHtml ? [ 0 => $out, 'isHTML' => true ] : $out; |
177 | } |
178 | |
179 | /** |
180 | * Save a page property. |
181 | * @todo Remove ParserOutput::getProperty and ParserOutput::setProperty fallbacks after dropping support for MW 1.37 |
182 | * @param Parser $parser The parser object. |
183 | * @param string $prop The property name; it will be prefixed with 'genealogy '. |
184 | * @param string|Title $val The property value ('full text' will be used if this is a Title). |
185 | * @param bool $multi Whether this property can have multiple values (will be stored as |
186 | * multiple properties, with an integer appended to their name. |
187 | */ |
188 | public function saveProp( Parser $parser, $prop, $val, $multi = true ) { |
189 | $output = $parser->getOutput(); |
190 | $valString = ( $val instanceof Title ) ? $val->getFullText() : $val; |
191 | if ( $multi ) { |
192 | // Figure out what number we're up to for this property. |
193 | $propNum = 1; |
194 | $propVal = method_exists( $output, 'getPageProperty' ) |
195 | ? $output->getPageProperty( "genealogy $prop $propNum" ) |
196 | // @phan-suppress-next-line PhanUndeclaredMethod |
197 | : $output->getProperty( "genealogy $prop $propNum" ); |
198 | while ( $propVal && $propVal !== $valString ) { |
199 | $propNum++; |
200 | $propVal = method_exists( $output, 'getPageProperty' ) |
201 | ? $output->getPageProperty( "genealogy $prop $propNum" ) |
202 | // @phan-suppress-next-line PhanUndeclaredMethod |
203 | : $output->getProperty( "genealogy $prop $propNum" ); |
204 | } |
205 | // Save the property. |
206 | if ( method_exists( $output, 'setPageProperty' ) ) { |
207 | $output->setPageProperty( "genealogy $prop $propNum", $valString ); |
208 | } else { |
209 | // @phan-suppress-next-line PhanUndeclaredMethod |
210 | $output->setProperty( "genealogy $prop $propNum", $valString ); |
211 | } |
212 | |
213 | } else { |
214 | // A single-valued property. |
215 | if ( method_exists( $output, 'setPageProperty' ) ) { |
216 | $output->setPageProperty( "genealogy $prop", $valString ); |
217 | } else { |
218 | // @phan-suppress-next-line PhanUndeclaredMethod |
219 | $output->setProperty( "genealogy $prop", $valString ); |
220 | } |
221 | } |
222 | // For page-linking properties, add the referenced page as a dependency for this page. |
223 | // https://www.mediawiki.org/wiki/Manual:Tag_extensions#How_do_I_disable_caching_for_pages_using_my_extension.3F |
224 | if ( $val instanceof Title ) { |
225 | // Register the dependency in templatelinks table. |
226 | $output->addTemplate( $val, $val->getArticleID(), $val->getLatestRevID() ); |
227 | } |
228 | } |
229 | |
230 | /** |
231 | * Get a wikitext list of siblings, partners, or children. |
232 | * @param Parser $parser The parser to use to render each line. |
233 | * @param Person[] $people The people to list. |
234 | * @return string Wikitext list of people. |
235 | */ |
236 | public function peopleList( Parser $parser, $people ) { |
237 | $templateName = wfMessage( 'genealogy-person-list-item' )->text(); |
238 | $out = ''; |
239 | $index = 1; |
240 | $peopleCount = count( $people ); |
241 | $templateTitle = Title::newFromText( $templateName ); |
242 | $templateExists = $templateTitle instanceof Title && $templateTitle->exists(); |
243 | foreach ( $people as $person ) { |
244 | if ( $templateExists ) { |
245 | $template = '{{' . $templateName |
246 | . '|title=' . $person->getTitle()->getFullText() |
247 | . '|pagename=' . $person->getTitle()->getText() |
248 | . '|link=' . $person->getWikiLink() |
249 | . '|description=' . $person->getDescription() |
250 | . '|index=' . $index |
251 | . '|count=' . $peopleCount |
252 | . '}}'; |
253 | $out .= $parser->recursivePreprocess( $template ); |
254 | $index++; |
255 | } else { |
256 | $out .= "* " . $person->getWikiLink() . "\n"; |
257 | } |
258 | } |
259 | return $out; |
260 | } |
261 | |
262 | } |