PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.0.2
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.0.2
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 5 years ago ApiRenderer.php 5 years ago CORSHandler.php 5 years ago DataTableGenericFilter.php 5 years ago DataTableManipulator.php 5 years ago DataTablePostProcessor.php 5 years ago DocumentationGenerator.php 5 years ago Inconsistencies.php 5 years ago NoDefaultValue.php 5 years ago Proxy.php 5 years ago Request.php 5 years ago ResponseBuilder.php 5 years ago
DocumentationGenerator.php
402 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
300 $entityNames = StaticContainer::get('entities.idNames');
301 foreach ($entityNames as $entityName) {
302 if (isset($aParameters[$entityName])) {
303 continue;
304 }
305 $aParameters[$entityName] = false;
306 }
307
308 $moduleName = Proxy::getInstance()->getModuleNameFromClassName($class);
309 $aParameters = array_merge(array('module' => 'API', 'method' => $moduleName . '.' . $methodName), $aParameters);
310
311 foreach ($aParameters as $nameVariable => &$defaultValue) {
312 if (isset($knowExampleDefaultParametersValues[$nameVariable])) {
313 $defaultValue = $knowExampleDefaultParametersValues[$nameVariable];
314 } // if there isn't a default value for a given parameter,
315 // we need a 'know default value' or we can't generate the link
316 elseif ($defaultValue instanceof NoDefaultValue) {
317 return false;
318 }
319 }
320 return '?' . Url::getQueryStringFromParameters($aParameters);
321 }
322
323 /**
324 * Returns the methods $class.$name parameters (and default value if provided) as a string.
325 *
326 * @param string $class The class name
327 * @param string $name The method name
328 * @return string For example "(idSite, period, date = 'today')"
329 */
330 protected function getParametersString($class, $name)
331 {
332 $aParameters = Proxy::getInstance()->getParametersList($class, $name);
333 $asParameters = array();
334 foreach ($aParameters as $nameVariable => $defaultValue) {
335 // Do not show API parameters starting with _
336 // They are supposed to be used only in internal API calls
337 if (strpos($nameVariable, '_') === 0) {
338 continue;
339 }
340 $str = $nameVariable;
341 if (!($defaultValue instanceof NoDefaultValue)) {
342 if (is_array($defaultValue)) {
343 $str .= " = 'Array'";
344 } else {
345 $str .= " = '$defaultValue'";
346 }
347 }
348 $asParameters[] = $str;
349 }
350 $sParameters = implode(", ", $asParameters);
351 return "($sParameters)";
352 }
353
354 /**
355 * @param $outputExampleUrls
356 * @param $prefixUrls
357 * @param $displayTitlesAsAngularDirective
358 * @return array
359 */
360 protected function generateDocumentation($outputExampleUrls, $prefixUrls, $displayTitlesAsAngularDirective)
361 {
362 $str = $toc = '';
363
364 foreach (Proxy::getInstance()->getMetadata() as $class => $info) {
365 $moduleName = Proxy::getInstance()->getModuleNameFromClassName($class);
366 $rClass = new ReflectionClass($class);
367
368 if (!Piwik::hasUserSuperUserAccess() && $this->checkIfClassCommentContainsHideAnnotation($rClass)) {
369 continue;
370 }
371
372 if ($this->checkIfCommentContainsInternalAnnotation($rClass)) {
373 continue;
374 }
375
376 $toDisplay = $this->prepareModulesAndMethods($info, $moduleName);
377
378 foreach ($toDisplay as $moduleName => $methods) {
379 foreach ($methods as $index => $method) {
380 if (!method_exists($class, $method)) { // method is handled through API.Request.intercept event
381 continue;
382 }
383
384 $reflectionMethod = new \ReflectionMethod($class, $method);
385 if ($this->checkIfCommentContainsInternalAnnotation($reflectionMethod)) {
386 unset($toDisplay[$moduleName][$index]);
387 }
388 }
389 if (empty($toDisplay[$moduleName])) {
390 unset($toDisplay[$moduleName]);
391 }
392 }
393
394 foreach ($toDisplay as $moduleName => $methods) {
395 $toc .= $this->prepareModuleToDisplay($moduleName);
396 $str .= $this->prepareMethodToDisplay($moduleName, $info, $methods, $class, $outputExampleUrls, $prefixUrls, $displayTitlesAsAngularDirective);
397 }
398 }
399 return array($toc, $str);
400 }
401 }
402