Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 75 |
|
0.00% |
0 / 7 |
CRAP | |
0.00% |
0 / 1 |
ApiCategoryTree | |
0.00% |
0 / 75 |
|
0.00% |
0 / 7 |
380 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
20 | |||
extractOptions | |
0.00% |
0 / 14 |
|
0.00% |
0 / 1 |
72 | |||
getConditionalRequestData | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
6 | |||
getHTML | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
6 | |||
getAllowedParams | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
isInternal | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\CategoryTree; |
4 | |
5 | use ApiBase; |
6 | use ApiMain; |
7 | use FormatJson; |
8 | use MediaWiki\Config\Config; |
9 | use MediaWiki\Config\ConfigFactory; |
10 | use MediaWiki\Languages\LanguageConverterFactory; |
11 | use MediaWiki\Linker\LinkRenderer; |
12 | use MediaWiki\Title\Title; |
13 | use WANObjectCache; |
14 | use Wikimedia\ParamValidator\ParamValidator; |
15 | use Wikimedia\Rdbms\IConnectionProvider; |
16 | |
17 | /** |
18 | * This program is free software; you can redistribute it and/or modify |
19 | * it under the terms of the GNU General Public License as published by |
20 | * the Free Software Foundation; either version 2 of the License, or |
21 | * (at your option) any later version. |
22 | * |
23 | * This program is distributed in the hope that it will be useful, |
24 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
25 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
26 | * GNU General Public License for more details. |
27 | * |
28 | * You should have received a copy of the GNU General Public License along |
29 | * with this program; if not, write to the Free Software Foundation, Inc., |
30 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
31 | * http://www.gnu.org/copyleft/gpl.html |
32 | */ |
33 | |
34 | class ApiCategoryTree extends ApiBase { |
35 | /** @var ConfigFactory */ |
36 | private $configFactory; |
37 | |
38 | /** @var LanguageConverterFactory */ |
39 | private $languageConverterFactory; |
40 | |
41 | /** @var LinkRenderer */ |
42 | private $linkRenderer; |
43 | |
44 | /** @var IConnectionProvider */ |
45 | private $dbProvider; |
46 | |
47 | /** @var WANObjectCache */ |
48 | private $wanCache; |
49 | |
50 | /** |
51 | * @param ApiMain $main |
52 | * @param string $action |
53 | * @param ConfigFactory $configFactory |
54 | * @param IConnectionProvider $dbProvider |
55 | * @param LanguageConverterFactory $languageConverterFactory |
56 | * @param LinkRenderer $linkRenderer |
57 | * @param WANObjectCache $wanCache |
58 | */ |
59 | public function __construct( |
60 | ApiMain $main, |
61 | $action, |
62 | ConfigFactory $configFactory, |
63 | IConnectionProvider $dbProvider, |
64 | LanguageConverterFactory $languageConverterFactory, |
65 | LinkRenderer $linkRenderer, |
66 | WANObjectCache $wanCache |
67 | ) { |
68 | parent::__construct( $main, $action ); |
69 | $this->configFactory = $configFactory; |
70 | $this->languageConverterFactory = $languageConverterFactory; |
71 | $this->linkRenderer = $linkRenderer; |
72 | $this->dbProvider = $dbProvider; |
73 | $this->wanCache = $wanCache; |
74 | } |
75 | |
76 | /** |
77 | * @inheritDoc |
78 | */ |
79 | public function execute() { |
80 | $params = $this->extractRequestParams(); |
81 | |
82 | $options = $this->extractOptions( $params ); |
83 | |
84 | $title = CategoryTree::makeTitle( $params['category'] ); |
85 | if ( !$title || $title->isExternal() ) { |
86 | $this->dieWithError( [ 'apierror-invalidtitle', wfEscapeWikiText( $params['category'] ) ] ); |
87 | } |
88 | |
89 | $depth = isset( $options['depth'] ) ? (int)$options['depth'] : 1; |
90 | |
91 | $ct = new CategoryTree( $options, $this->getConfig(), $this->dbProvider, $this->linkRenderer ); |
92 | $depth = $ct->optionManager->capDepth( $depth ); |
93 | $ctConfig = $this->configFactory->makeConfig( 'categorytree' ); |
94 | $html = $this->getHTML( $ct, $title, $depth, $ctConfig ); |
95 | |
96 | $this->getMain()->setCacheMode( 'public' ); |
97 | |
98 | $this->getResult()->addContentValue( $this->getModuleName(), 'html', $html ); |
99 | } |
100 | |
101 | /** |
102 | * @param array $params |
103 | * @return string[] |
104 | */ |
105 | private function extractOptions( $params ): array { |
106 | if ( !isset( $params['options'] ) ) { |
107 | return []; |
108 | } |
109 | |
110 | $options = FormatJson::decode( $params['options'] ); |
111 | if ( !is_object( $options ) ) { |
112 | $this->dieWithError( 'apierror-categorytree-invalidjson', 'invalidjson' ); |
113 | } |
114 | |
115 | foreach ( $options as $option => $value ) { |
116 | if ( is_scalar( $value ) || $value === null ) { |
117 | continue; |
118 | } |
119 | if ( $option === 'namespaces' && is_array( $value ) ) { |
120 | continue; |
121 | } |
122 | $this->dieWithError( |
123 | [ 'apierror-categorytree-invalidjson-option', $option ], 'invalidjson-option' |
124 | ); |
125 | } |
126 | |
127 | return get_object_vars( $options ); |
128 | } |
129 | |
130 | /** |
131 | * @param string $condition |
132 | * |
133 | * @return bool|null|string |
134 | */ |
135 | public function getConditionalRequestData( $condition ) { |
136 | if ( $condition === 'last-modified' ) { |
137 | $params = $this->extractRequestParams(); |
138 | $title = CategoryTree::makeTitle( $params['category'] ); |
139 | return $this->dbProvider->getReplicaDatabase()->newSelectQueryBuilder() |
140 | ->select( 'page_touched' ) |
141 | ->from( 'page' ) |
142 | ->where( [ |
143 | 'page_namespace' => NS_CATEGORY, |
144 | 'page_title' => $title->getDBkey(), |
145 | ] ) |
146 | ->caller( __METHOD__ ) |
147 | ->fetchField(); |
148 | } |
149 | } |
150 | |
151 | /** |
152 | * Get category tree HTML for the given tree, title, depth and config |
153 | * |
154 | * @param CategoryTree $ct |
155 | * @param Title $title |
156 | * @param int $depth |
157 | * @param Config $ctConfig Config for CategoryTree |
158 | * @return string HTML |
159 | */ |
160 | private function getHTML( CategoryTree $ct, Title $title, $depth, Config $ctConfig ) { |
161 | $langConv = $this->languageConverterFactory->getLanguageConverter(); |
162 | |
163 | return $this->wanCache->getWithSetCallback( |
164 | $this->wanCache->makeKey( |
165 | 'categorytree-html-ajax', |
166 | md5( $title->getDBkey() ), |
167 | md5( $ct->optionManager->getOptionsAsCacheKey( $depth ) ), |
168 | $this->getLanguage()->getCode(), |
169 | $langConv->getExtraHashOptions(), |
170 | $ctConfig->get( 'RenderHashAppend' ) |
171 | ), |
172 | $this->wanCache::TTL_DAY, |
173 | static function () use ( $ct, $title, $depth ) { |
174 | return trim( $ct->renderChildren( $title, $depth ) ); |
175 | }, |
176 | [ |
177 | 'touchedCallback' => function () { |
178 | $timestamp = $this->getConditionalRequestData( 'last-modified' ); |
179 | |
180 | return $timestamp ? wfTimestamp( TS_UNIX, $timestamp ) : null; |
181 | } |
182 | ] |
183 | ); |
184 | } |
185 | |
186 | /** |
187 | * @inheritDoc |
188 | */ |
189 | public function getAllowedParams() { |
190 | return [ |
191 | 'category' => [ |
192 | ParamValidator::PARAM_TYPE => 'string', |
193 | ParamValidator::PARAM_REQUIRED => true, |
194 | ], |
195 | 'options' => [ |
196 | ParamValidator::PARAM_TYPE => 'string', |
197 | ], |
198 | ]; |
199 | } |
200 | |
201 | /** |
202 | * @inheritDoc |
203 | */ |
204 | public function isInternal() { |
205 | return true; |
206 | } |
207 | } |