PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.15.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.15.0
5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / API / DocumentationGenerator.php
matomo / app / core / API Last commit date
DataTableManipulator 2 years ago ApiRenderer.php 4 years ago CORSHandler.php 5 years ago DataTableGenericFilter.php 5 years ago DataTableManipulator.php 2 years ago DataTablePostProcessor.php 2 years ago DocumentationGenerator.php 2 years ago Inconsistencies.php 2 years ago NoDefaultValue.php 5 years ago Proxy.php 5 years ago Request.php 3 years ago ResponseBuilder.php 5 years ago
DocumentationGenerator.php
405 lines
1 <?php
2 /**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 *
8 */
9 namespace Piwik\API;
10
11 use Exception;
12 use Piwik\Common;
13 use Piwik\Container\StaticContainer;
14 use Piwik\Piwik;
15 use Piwik\Url;
16 use ReflectionClass;
17
18 /**
19 * Possible tags to use in APIs
20 *
21 * @hide -> Won't be shown in list of all APIs but is also not possible to be called via HTTP API
22 * @hideForAll Same as @hide
23 * @hideExceptForSuperUser Same as @hide but still shown and possible to be called by a user with super user access
24 * @internal -> Won't be shown in list of all APIs but is possible to be called via HTTP API
25 */
26 class DocumentationGenerator
27 {
28 protected $countPluginsLoaded = 0;
29
30 /**
31 * trigger loading all plugins with an API.php file in the Proxy
32 */
33 public function __construct()
34 {
35 $plugins = \Piwik\Plugin\Manager::getInstance()->getLoadedPluginsName();
36 foreach ($plugins as $plugin) {
37 try {
38 $className = Request::getClassNameAPI($plugin);
39 Proxy::getInstance()->registerClass($className);
40 } catch (Exception $e) {
41 }
42 }
43 }
44
45 /**
46 * Returns a HTML page containing help for all the successfully loaded APIs.
47 *
48 * @param bool $outputExampleUrls
49 * @return string
50 */
51 public function getApiDocumentationAsString($outputExampleUrls = true)
52 {
53 list($toc, $str) = $this->generateDocumentation($outputExampleUrls, $prefixUrls = '', $displayTitlesAsAngularDirective = true);
54
55 return "<div piwik-content-block content-title='Quick access to APIs' id='topApiRef' name='topApiRef'>
56 $toc</div>
57 $str";
58 }
59
60 /**
61 * Used on developer.piwik.org
62 *
63 * @param bool|true $outputExampleUrls
64 * @param string $prefixUrls
65 * @return string
66 */
67 public function getApiDocumentationAsStringForDeveloperReference($outputExampleUrls = true, $prefixUrls = '')
68 {
69 list($toc, $str) = $this->generateDocumentation($outputExampleUrls, $prefixUrls, $displayTitlesAsAngularDirective = false);
70
71 return "<h2 id='topApiRef' name='topApiRef'>Quick access to APIs</h2>
72 $toc
73 $str";
74 }
75
76 protected function prepareModuleToDisplay($moduleName)
77 {
78 return "<a href='#$moduleName'>$moduleName</a><br/>";
79 }
80
81 protected function prepareMethodToDisplay($moduleName, $info, $methods, $class, $outputExampleUrls, $prefixUrls, $displayTitlesAsAngularDirective)
82 {
83 $str = '';
84 $str .= "\n<a name='$moduleName' id='$moduleName'></a>";
85 if($displayTitlesAsAngularDirective) {
86 $str .= "<div piwik-content-block content-title='Module " . $moduleName . "'>";
87 } else {
88 $str .= "<h2>Module " . $moduleName . "</h2>";
89 }
90 $info['__documentation'] = $this->checkDocumentation($info['__documentation']);
91 $str .= "<div class='apiDescription'> " . $info['__documentation'] . " </div>";
92 foreach ($methods as $methodName) {
93 if (Proxy::getInstance()->isDeprecatedMethod($class, $methodName)) {
94 continue;
95 }
96
97 $params = $this->getParametersString($class, $methodName);
98
99 $str .= "\n <div class='apiMethod'>- <b>$moduleName.$methodName </b>" . $params . "";
100 $str .= '<small>';
101 if ($outputExampleUrls) {
102 $str .= $this->addExamples($class, $methodName, $prefixUrls);
103 }
104 $str .= '</small>';
105 $str .= "</div>\n";
106 }
107
108 if($displayTitlesAsAngularDirective) {
109 $str .= "</div>";
110 }
111
112 return $str;
113 }
114
115 protected function prepareModulesAndMethods($info, $moduleName)
116 {
117 $toDisplay = array();
118
119 foreach ($info as $methodName => $infoMethod) {
120 if ($methodName == '__documentation') {
121 continue;
122 }
123 $toDisplay[$moduleName][] = $methodName;
124 }
125
126 return $toDisplay;
127 }
128
129 protected function addExamples($class, $methodName, $prefixUrls)
130 {
131 $token = Piwik::getCurrentUserTokenAuth();
132 $token_auth_url = "&token_auth=" . $token;
133 if ($token !== 'anonymous') {
134 $token_auth_url .= "&force_api_session=1";
135 }
136 $parametersToSet = array(
137 'idSite' => Common::getRequestVar('idSite', 1, 'int'),
138 'period' => Common::getRequestVar('period', 'day', 'string'),
139 'date' => Common::getRequestVar('date', 'today', 'string')
140 );
141 $str = '';
142 $str .= "<span class=\"example\">";
143 $exampleUrl = $this->getExampleUrl($class, $methodName, $parametersToSet);
144 if ($exampleUrl !== false) {
145 $lastNUrls = '';
146 if (preg_match('/(&period)|(&date)/', $exampleUrl)) {
147 $exampleUrlRss = $prefixUrls . $this->getExampleUrl($class, $methodName, array('date' => 'last10', 'period' => 'day') + $parametersToSet);
148 $lastNUrls = ", RSS of the last <a target='_blank' href='$exampleUrlRss&format=rss$token_auth_url&translateColumnNames=1'>10 days</a>";
149 }
150 $exampleUrl = $prefixUrls . $exampleUrl;
151 $str .= " [ Example in
152 <a target='_blank' href='$exampleUrl&format=xml$token_auth_url'>XML</a>,
153 <a target='_blank' href='$exampleUrl&format=JSON$token_auth_url'>Json</a>,
154 <a target='_blank' href='$exampleUrl&format=Tsv$token_auth_url&translateColumnNames=1'>Tsv (Excel)</a>
155 $lastNUrls
156 ]";
157 } else {
158 $str .= " [ No example available ]";
159 }
160 $str .= "</span>";
161 return $str;
162 }
163
164 /**
165 * Check if Class contains @hide
166 *
167 * @param ReflectionClass $rClass instance of ReflectionMethod
168 * @return bool
169 */
170 public function checkIfClassCommentContainsHideAnnotation(ReflectionClass $rClass)
171 {
172 return false !== strstr($rClass->getDocComment(), '@hide');
173 }
174
175 /**
176 * Check if Class contains @internal
177 *
178 * @param ReflectionClass|\ReflectionMethod $rClass instance of ReflectionMethod
179 * @return bool
180 */
181 private function checkIfCommentContainsInternalAnnotation($rClass)
182 {
183 return false !== strstr($rClass->getDocComment(), '@internal');
184 }
185
186 /**
187 * Check if documentation contains @hide annotation and deletes it
188 *
189 * @param $moduleToCheck
190 * @return mixed
191 */
192 public function checkDocumentation($moduleToCheck)
193 {
194 if (strpos($moduleToCheck, '@hide') == true) {
195 $moduleToCheck = str_replace(strtok(strstr($moduleToCheck, '@hide'), "\n"), "", $moduleToCheck);
196 }
197 return $moduleToCheck;
198 }
199
200 /**
201 * Returns a string containing links to examples on how to call a given method on a given API
202 * It will export links to XML, CSV, HTML, JSON, PHP, etc.
203 * It will not export links for methods such as deleteSite or deleteUser
204 *
205 * @param string $class the class
206 * @param string $methodName the method
207 * @param array $parametersToSet parameters to set
208 * @return string|bool when not possible
209 */
210 public function getExampleUrl($class, $methodName, $parametersToSet = array())
211 {
212 $knowExampleDefaultParametersValues = array(
213 'access' => 'view',
214 'userLogin' => 'test',
215 'passwordMd5ied' => 'passwordExample',
216 'email' => 'test@example.org',
217
218 'languageCode' => 'fr',
219 'url' => 'https://divezone.net/',
220 'pageUrl' => 'https://divezone.net/',
221 'apiModule' => 'UserCountry',
222 'apiAction' => 'getCountry',
223 'lastMinutes' => '30',
224 'abandonedCarts' => '0',
225 'segmentName' => 'pageTitle',
226 'ip' => '194.57.91.215',
227 'idSites' => '1,2',
228 'idAlert' => '1',
229 'seconds' => '3600',
230 // 'segmentName' => 'browserCode',
231 );
232
233 foreach ($parametersToSet as $name => $value) {
234 $knowExampleDefaultParametersValues[$name] = $value;
235 }
236
237 // no links for these method names
238 $doNotPrintExampleForTheseMethods = array(
239 //Sites
240 'deleteSite',
241 'addSite',
242 'updateSite',
243 'addSiteAliasUrls',
244 //Users
245 'deleteUser',
246 'addUser',
247 'updateUser',
248 'setUserAccess',
249 //Goals
250 'addGoal',
251 'updateGoal',
252 'deleteGoal',
253 //Marketplace
254 'deleteLicenseKey'
255 );
256
257 if (in_array($methodName, $doNotPrintExampleForTheseMethods)) {
258 return false;
259 }
260
261 // we try to give an URL example to call the API
262 $aParameters = Proxy::getInstance()->getParametersList($class, $methodName);
263 $aParameters['format'] = false;
264 $aParameters['hideIdSubDatable'] = false;
265 $aParameters['serialize'] = false;
266 $aParameters['language'] = false;
267 $aParameters['translateColumnNames'] = false;
268 $aParameters['label'] = false;
269 $aParameters['labelSeries'] = false;
270 $aParameters['flat'] = false;
271 $aParameters['include_aggregate_rows'] = false;
272 $aParameters['filter_offset'] = false;
273 $aParameters['filter_limit'] = false;
274 $aParameters['filter_sort_column'] = false;
275 $aParameters['filter_sort_order'] = false;
276 $aParameters['filter_excludelowpop'] = false;
277 $aParameters['filter_excludelowpop_value'] = false;
278 $aParameters['filter_column_recursive'] = false;
279 $aParameters['filter_pattern'] = false;
280 $aParameters['filter_pattern_recursive'] = false;
281 $aParameters['filter_truncate'] = false;
282 $aParameters['hideColumns'] = false;
283 $aParameters['hideColumnsRecursively'] = false;
284 $aParameters['showColumns'] = false;
285 $aParameters['pivotBy'] = false;
286 $aParameters['pivotByColumn'] = false;
287 $aParameters['pivotByColumnLimit'] = false;
288 $aParameters['disable_queued_filters'] = false;
289 $aParameters['disable_generic_filters'] = false;
290 $aParameters['expanded'] = false;
291 $aParameters['idDimenson'] = false;
292 $aParameters['format_metrics'] = false;
293 $aParameters['compare'] = false;
294 $aParameters['compareDates'] = false;
295 $aParameters['comparePeriods'] = false;
296 $aParameters['compareSegments'] = false;
297 $aParameters['comparisonIdSubtables'] = false;
298 $aParameters['invert_compare_change_compute'] = false;
299 $aParameters['filter_update_columns_when_show_all_goals'] = false;
300 $aParameters['filter_show_goal_columns_process_goals'] = false;
301
302 $extraParameters = StaticContainer::get('entities.idNames');
303 $extraParameters = array_merge($extraParameters, StaticContainer::get('DocumentationGenerator.customParameters'));
304 foreach ($extraParameters as $paramName) {
305 if (isset($aParameters[$paramName])) {
306 continue;
307 }
308 $aParameters[$paramName] = false;
309 }
310
311 $moduleName = Proxy::getInstance()->getModuleNameFromClassName($class);
312 $aParameters = array_merge(array('module' => 'API', 'method' => $moduleName . '.' . $methodName), $aParameters);
313
314 foreach ($aParameters as $nameVariable => &$defaultValue) {
315 if (isset($knowExampleDefaultParametersValues[$nameVariable])) {
316 $defaultValue = $knowExampleDefaultParametersValues[$nameVariable];
317 } // if there isn't a default value for a given parameter,
318 // we need a 'know default value' or we can't generate the link
319 elseif ($defaultValue instanceof NoDefaultValue) {
320 return false;
321 }
322 }
323 return '?' . Url::getQueryStringFromParameters($aParameters);
324 }
325
326 /**
327 * Returns the methods $class.$name parameters (and default value if provided) as a string.
328 *
329 * @param string $class The class name
330 * @param string $name The method name
331 * @return string For example "(idSite, period, date = 'today')"
332 */
333 protected function getParametersString($class, $name)
334 {
335 $aParameters = Proxy::getInstance()->getParametersList($class, $name);
336 $asParameters = array();
337 foreach ($aParameters as $nameVariable => $defaultValue) {
338 // Do not show API parameters starting with _
339 // They are supposed to be used only in internal API calls
340 if (strpos($nameVariable, '_') === 0) {
341 continue;
342 }
343 $str = $nameVariable;
344 if (!($defaultValue instanceof NoDefaultValue)) {
345 if (is_array($defaultValue)) {
346 $str .= " = 'Array'";
347 } else {
348 $str .= " = '$defaultValue'";
349 }
350 }
351 $asParameters[] = $str;
352 }
353 $sParameters = implode(", ", $asParameters);
354 return "($sParameters)";
355 }
356
357 /**
358 * @param $outputExampleUrls
359 * @param $prefixUrls
360 * @param $displayTitlesAsAngularDirective
361 * @return array
362 */
363 protected function generateDocumentation($outputExampleUrls, $prefixUrls, $displayTitlesAsAngularDirective)
364 {
365 $str = $toc = '';
366
367 foreach (Proxy::getInstance()->getMetadata() as $class => $info) {
368 $moduleName = Proxy::getInstance()->getModuleNameFromClassName($class);
369 $rClass = new ReflectionClass($class);
370
371 if (!Piwik::hasUserSuperUserAccess() && $this->checkIfClassCommentContainsHideAnnotation($rClass)) {
372 continue;
373 }
374
375 if ($this->checkIfCommentContainsInternalAnnotation($rClass)) {
376 continue;
377 }
378
379 $toDisplay = $this->prepareModulesAndMethods($info, $moduleName);
380
381 foreach ($toDisplay as $moduleName => $methods) {
382 foreach ($methods as $index => $method) {
383 if (!method_exists($class, $method)) { // method is handled through API.Request.intercept event
384 continue;
385 }
386
387 $reflectionMethod = new \ReflectionMethod($class, $method);
388 if ($this->checkIfCommentContainsInternalAnnotation($reflectionMethod)) {
389 unset($toDisplay[$moduleName][$index]);
390 }
391 }
392 if (empty($toDisplay[$moduleName])) {
393 unset($toDisplay[$moduleName]);
394 }
395 }
396
397 foreach ($toDisplay as $moduleName => $methods) {
398 $toc .= $this->prepareModuleToDisplay($moduleName);
399 $str .= $this->prepareMethodToDisplay($moduleName, $info, $methods, $class, $outputExampleUrls, $prefixUrls, $displayTitlesAsAngularDirective);
400 }
401 }
402 return array($toc, $str);
403 }
404 }
405