Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
61.70% |
29 / 47 |
|
90.00% |
9 / 10 |
CRAP | |
0.00% |
0 / 1 |
Tree | |
61.70% |
29 / 47 |
|
90.00% |
9 / 10 |
52.72 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
setAncestorDepth | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setDescendantDepth | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addAncestors | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addDescendants | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
addAncestorsOrDescendants | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
3 | |||
hasAncestorsOrDescendants | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setFormat | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getWikitext | |
0.00% |
0 / 18 |
|
0.00% |
0 / 1 |
90 | |||
getTreeSource | |
100.00% |
16 / 16 |
|
100.00% |
1 / 1 |
4 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\Genealogy; |
4 | |
5 | use ExtensionRegistry; |
6 | use Html; |
7 | use MediaWiki\Page\WikiPageFactory; |
8 | use Parser; |
9 | use Title; |
10 | use Wikimedia\Rdbms\ILoadBalancer; |
11 | |
12 | class Tree { |
13 | |
14 | /** @var string The tree's format, either 'graphviz' or 'mermaid'. */ |
15 | protected $format = 'graphviz'; |
16 | |
17 | /** @var Person[] */ |
18 | protected $ancestors = []; |
19 | |
20 | /** @var Person[] */ |
21 | protected $descendants = []; |
22 | |
23 | /** @var int */ |
24 | protected $ancestorDepth; |
25 | |
26 | /** @var int */ |
27 | protected $descendantDepth; |
28 | |
29 | /** @var ILoadBalancer */ |
30 | private ILoadBalancer $loadBalancer; |
31 | |
32 | /** @var WikiPageFactory */ |
33 | private WikiPageFactory $wikiPageFactory; |
34 | |
35 | public function __construct( |
36 | ILoadBalancer $loadBalancer, |
37 | WikiPageFactory $wikiPageFactory |
38 | ) { |
39 | $this->loadBalancer = $loadBalancer; |
40 | $this->wikiPageFactory = $wikiPageFactory; |
41 | } |
42 | |
43 | /** |
44 | * Set the number of levels the tree will go up to from the ancestors' starting points. |
45 | * @param int $ancestorDepth The new ancestor depth. |
46 | */ |
47 | public function setAncestorDepth( $ancestorDepth ) { |
48 | $this->ancestorDepth = (int)$ancestorDepth; |
49 | } |
50 | |
51 | /** |
52 | * Set the number of levels the tree will go down to from the descendants' starting points. |
53 | * @param int $descendantDepth The new descendant depth. |
54 | */ |
55 | public function setDescendantDepth( $descendantDepth ) { |
56 | $this->descendantDepth = (int)$descendantDepth; |
57 | } |
58 | |
59 | /** |
60 | * Add ancestor starting points to this tree, from which to traverse upwards. |
61 | * @param string[] $ancestors Array of page titles. |
62 | */ |
63 | public function addAncestors( $ancestors ) { |
64 | $this->addAncestorsOrDescendants( 'ancestors', $ancestors ); |
65 | } |
66 | |
67 | /** |
68 | * Add descendant starting points to this tree, from which to traverse downwards. |
69 | * @param string[] $descendants Array of page titles. |
70 | */ |
71 | public function addDescendants( $descendants ) { |
72 | $this->addAncestorsOrDescendants( 'descendants', $descendants ); |
73 | } |
74 | |
75 | /** |
76 | * Add ancestor or descendant starting points to this tree. |
77 | * @param string $type Either 'ancestors' or 'descendants'. |
78 | * @param string[] $list Array of page titles. |
79 | */ |
80 | protected function addAncestorsOrDescendants( $type, $list ) { |
81 | foreach ( $list as $a ) { |
82 | $title = Title::newFromText( $a ); |
83 | if ( $title ) { |
84 | $person = new Person( $this->loadBalancer, $this->wikiPageFactory, $title ); |
85 | $this->{$type}[] = $person; |
86 | } |
87 | } |
88 | } |
89 | |
90 | /** |
91 | * Whether any ancestors or descendants have been added to this tree. |
92 | * @return bool |
93 | */ |
94 | public function hasAncestorsOrDescendants() { |
95 | return ( count( $this->ancestors ) + count( $this->descendants ) ) > 0; |
96 | } |
97 | |
98 | /** |
99 | * Set the output format for the tree. |
100 | * |
101 | * @param string $format Either 'graphviz' or 'mermaid' (case insensitive). |
102 | * @return void |
103 | */ |
104 | public function setFormat( $format ) { |
105 | $this->format = strtolower( $format ); |
106 | } |
107 | |
108 | /** |
109 | * Get the wikitext output for the tree. |
110 | * |
111 | * @param Parser $parser The parser. |
112 | * @return string Unsafe half-parsed HTML, as returned by Parser::recursiveTagParse(). |
113 | */ |
114 | public function getWikitext( Parser $parser ) { |
115 | // If there's nothing to render, give up. |
116 | if ( !$this->hasAncestorsOrDescendants() ) { |
117 | return ''; |
118 | } |
119 | |
120 | $extenstionRegistry = ExtensionRegistry::getInstance(); |
121 | $diagramsInstalled = $extenstionRegistry->isLoaded( 'Diagrams' ); |
122 | $graphvizInstalled = $extenstionRegistry->isLoaded( 'GraphViz' ) |
123 | || $diagramsInstalled; |
124 | $mermaidInstalled = $extenstionRegistry->isLoaded( 'Mermaid' ); |
125 | $treeSource = $this->getTreeSource(); |
126 | if ( $this->format === 'mermaid' && $mermaidInstalled ) { |
127 | $wikitext = "{{#mermaid:$treeSource|config.flowchart.useMaxWidth=0|config.theme=neutral}}"; |
128 | $out = $parser->recursiveTagParse( $wikitext ); |
129 | } elseif ( $this->format === 'mermaid' && $diagramsInstalled ) { |
130 | $out = $parser->recursiveTagParse( "<mermaid>$treeSource</mermaid>" ); |
131 | } elseif ( $this->format === 'graphviz' && $graphvizInstalled ) { |
132 | $out = $parser->recursiveTagParse( "<graphviz>$treeSource</graphviz>" ); |
133 | } else { |
134 | $err = wfMessage( 'genealogy-invalid-tree-format', $this->format )->text(); |
135 | return Html::element( 'p', [ 'class' => 'error' ], $err ); |
136 | } |
137 | |
138 | // Debugging. |
139 | // $out .= $parser->recursiveTagParse( "<pre>$treeSource</pre>" ); |
140 | |
141 | return $out; |
142 | } |
143 | |
144 | /** |
145 | * @return string |
146 | */ |
147 | public function getTreeSource() { |
148 | $traverser = new Traverser(); |
149 | $formatter = $this->format === 'mermaid' |
150 | ? new MermaidTreeFormatter( $this->ancestors, $this->descendants ) |
151 | : new GraphVizTreeFormatter( $this->ancestors, $this->descendants ); |
152 | $formatter->setName( md5( |
153 | implode( '', $this->ancestors ) |
154 | . implode( '', $this->descendants ) |
155 | . $this->ancestorDepth |
156 | . $this->descendantDepth |
157 | ) ); |
158 | $traverser->register( [ $formatter, 'visit' ] ); |
159 | |
160 | foreach ( $this->ancestors as $ancestor ) { |
161 | $traverser->ancestors( $ancestor, $this->ancestorDepth ); |
162 | } |
163 | |
164 | foreach ( $this->descendants as $descendant ) { |
165 | $traverser->descendants( $descendant, $this->descendantDepth ); |
166 | } |
167 | |
168 | return $formatter->getOutput(); |
169 | } |
170 | } |