PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 1.3.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v1.3.1
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 6 years ago ApiRenderer.php 6 years ago CORSHandler.php 6 years ago DataTableGenericFilter.php 6 years ago DataTableManipulator.php 6 years ago DataTablePostProcessor.php 6 years ago DocumentationGenerator.php 6 years ago Inconsistencies.php 6 years ago Proxy.php 6 years ago Request.php 6 years ago ResponseBuilder.php 6 years ago
DocumentationGenerator.php
398 lines
1 <?php
2 /**
3 * Piwik - 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_auth = "&token_auth=" . Piwik::getCurrentUserTokenAuth();
132 $parametersToSet = array(
133 'idSite' => Common::getRequestVar('idSite', 1, 'int'),
134 'period' => Common::getRequestVar('period', 'day', 'string'),
135 'date' => Common::getRequestVar('date', 'today', 'string')
136 );
137 $str = '';
138 $str .= "<span class=\"example\">";
139 $exampleUrl = $this->getExampleUrl($class, $methodName, $parametersToSet);
140 if ($exampleUrl !== false) {
141 $lastNUrls = '';
142 if (preg_match('/(&period)|(&date)/', $exampleUrl)) {
143 $exampleUrlRss = $prefixUrls . $this->getExampleUrl($class, $methodName, array('date' => 'last10', 'period' => 'day') + $parametersToSet);
144 $lastNUrls = ", RSS of the last <a target='_blank' href='$exampleUrlRss&format=rss$token_auth&translateColumnNames=1'>10 days</a>";
145 }
146 $exampleUrl = $prefixUrls . $exampleUrl;
147 $str .= " [ Example in
148 <a target='_blank' href='$exampleUrl&format=xml$token_auth'>XML</a>,
149 <a target='_blank' href='$exampleUrl&format=JSON$token_auth'>Json</a>,
150 <a target='_blank' href='$exampleUrl&format=Tsv$token_auth&translateColumnNames=1'>Tsv (Excel)</a>
151 $lastNUrls
152 ]";
153 } else {
154 $str .= " [ No example available ]";
155 }
156 $str .= "</span>";
157 return $str;
158 }
159
160 /**
161 * Check if Class contains @hide
162 *
163 * @param ReflectionClass $rClass instance of ReflectionMethod
164 * @return bool
165 */
166 public function checkIfClassCommentContainsHideAnnotation(ReflectionClass $rClass)
167 {
168 return false !== strstr($rClass->getDocComment(), '@hide');
169 }
170
171 /**
172 * Check if Class contains @internal
173 *
174 * @param ReflectionClass|\ReflectionMethod $rClass instance of ReflectionMethod
175 * @return bool
176 */
177 private function checkIfCommentContainsInternalAnnotation($rClass)
178 {
179 return false !== strstr($rClass->getDocComment(), '@internal');
180 }
181
182 /**
183 * Check if documentation contains @hide annotation and deletes it
184 *
185 * @param $moduleToCheck
186 * @return mixed
187 */
188 public function checkDocumentation($moduleToCheck)
189 {
190 if (strpos($moduleToCheck, '@hide') == true) {
191 $moduleToCheck = str_replace(strtok(strstr($moduleToCheck, '@hide'), "\n"), "", $moduleToCheck);
192 }
193 return $moduleToCheck;
194 }
195
196 /**
197 * Returns a string containing links to examples on how to call a given method on a given API
198 * It will export links to XML, CSV, HTML, JSON, PHP, etc.
199 * It will not export links for methods such as deleteSite or deleteUser
200 *
201 * @param string $class the class
202 * @param string $methodName the method
203 * @param array $parametersToSet parameters to set
204 * @return string|bool when not possible
205 */
206 public function getExampleUrl($class, $methodName, $parametersToSet = array())
207 {
208 $knowExampleDefaultParametersValues = array(
209 'access' => 'view',
210 'userLogin' => 'test',
211 'passwordMd5ied' => 'passwordExample',
212 'email' => 'test@example.org',
213
214 'languageCode' => 'fr',
215 'url' => 'https://divezone.net/',
216 'pageUrl' => 'https://divezone.net/',
217 'apiModule' => 'UserCountry',
218 'apiAction' => 'getCountry',
219 'lastMinutes' => '30',
220 'abandonedCarts' => '0',
221 'segmentName' => 'pageTitle',
222 'ip' => '194.57.91.215',
223 'idSites' => '1,2',
224 'idAlert' => '1',
225 'seconds' => '3600',
226 // 'segmentName' => 'browserCode',
227 );
228
229 foreach ($parametersToSet as $name => $value) {
230 $knowExampleDefaultParametersValues[$name] = $value;
231 }
232
233 // no links for these method names
234 $doNotPrintExampleForTheseMethods = array(
235 //Sites
236 'deleteSite',
237 'addSite',
238 'updateSite',
239 'addSiteAliasUrls',
240 //Users
241 'deleteUser',
242 'addUser',
243 'updateUser',
244 'setUserAccess',
245 //Goals
246 'addGoal',
247 'updateGoal',
248 'deleteGoal',
249 //Marketplace
250 'deleteLicenseKey'
251 );
252
253 if (in_array($methodName, $doNotPrintExampleForTheseMethods)) {
254 return false;
255 }
256
257 // we try to give an URL example to call the API
258 $aParameters = Proxy::getInstance()->getParametersList($class, $methodName);
259 $aParameters['format'] = false;
260 $aParameters['hideIdSubDatable'] = false;
261 $aParameters['serialize'] = false;
262 $aParameters['language'] = false;
263 $aParameters['translateColumnNames'] = false;
264 $aParameters['label'] = false;
265 $aParameters['labelSeries'] = false;
266 $aParameters['flat'] = false;
267 $aParameters['include_aggregate_rows'] = false;
268 $aParameters['filter_offset'] = false;
269 $aParameters['filter_limit'] = false;
270 $aParameters['filter_sort_column'] = false;
271 $aParameters['filter_sort_order'] = false;
272 $aParameters['filter_excludelowpop'] = false;
273 $aParameters['filter_excludelowpop_value'] = false;
274 $aParameters['filter_column_recursive'] = false;
275 $aParameters['filter_pattern'] = false;
276 $aParameters['filter_pattern_recursive'] = false;
277 $aParameters['filter_truncate'] = false;
278 $aParameters['hideColumns'] = false;
279 $aParameters['showColumns'] = false;
280 $aParameters['filter_pattern_recursive'] = false;
281 $aParameters['pivotBy'] = false;
282 $aParameters['pivotByColumn'] = false;
283 $aParameters['pivotByColumnLimit'] = false;
284 $aParameters['disable_queued_filters'] = false;
285 $aParameters['disable_generic_filters'] = false;
286 $aParameters['expanded'] = false;
287 $aParameters['idDimenson'] = false;
288 $aParameters['format_metrics'] = false;
289 $aParameters['compare'] = false;
290 $aParameters['compareDates'] = false;
291 $aParameters['comparePeriods'] = false;
292 $aParameters['compareSegments'] = false;
293 $aParameters['comparisonIdSubtables'] = false;
294 $aParameters['invert_compare_change_compute'] = false;
295
296 $entityNames = StaticContainer::get('entities.idNames');
297 foreach ($entityNames as $entityName) {
298 if (isset($aParameters[$entityName])) {
299 continue;
300 }
301 $aParameters[$entityName] = false;
302 }
303
304 $moduleName = Proxy::getInstance()->getModuleNameFromClassName($class);
305 $aParameters = array_merge(array('module' => 'API', 'method' => $moduleName . '.' . $methodName), $aParameters);
306
307 foreach ($aParameters as $nameVariable => &$defaultValue) {
308 if (isset($knowExampleDefaultParametersValues[$nameVariable])) {
309 $defaultValue = $knowExampleDefaultParametersValues[$nameVariable];
310 } // if there isn't a default value for a given parameter,
311 // we need a 'know default value' or we can't generate the link
312 elseif ($defaultValue instanceof NoDefaultValue) {
313 return false;
314 }
315 }
316 return '?' . Url::getQueryStringFromParameters($aParameters);
317 }
318
319 /**
320 * Returns the methods $class.$name parameters (and default value if provided) as a string.
321 *
322 * @param string $class The class name
323 * @param string $name The method name
324 * @return string For example "(idSite, period, date = 'today')"
325 */
326 protected function getParametersString($class, $name)
327 {
328 $aParameters = Proxy::getInstance()->getParametersList($class, $name);
329 $asParameters = array();
330 foreach ($aParameters as $nameVariable => $defaultValue) {
331 // Do not show API parameters starting with _
332 // They are supposed to be used only in internal API calls
333 if (strpos($nameVariable, '_') === 0) {
334 continue;
335 }
336 $str = $nameVariable;
337 if (!($defaultValue instanceof NoDefaultValue)) {
338 if (is_array($defaultValue)) {
339 $str .= " = 'Array'";
340 } else {
341 $str .= " = '$defaultValue'";
342 }
343 }
344 $asParameters[] = $str;
345 }
346 $sParameters = implode(", ", $asParameters);
347 return "($sParameters)";
348 }
349
350 /**
351 * @param $outputExampleUrls
352 * @param $prefixUrls
353 * @param $displayTitlesAsAngularDirective
354 * @return array
355 */
356 protected function generateDocumentation($outputExampleUrls, $prefixUrls, $displayTitlesAsAngularDirective)
357 {
358 $str = $toc = '';
359
360 foreach (Proxy::getInstance()->getMetadata() as $class => $info) {
361 $moduleName = Proxy::getInstance()->getModuleNameFromClassName($class);
362 $rClass = new ReflectionClass($class);
363
364 if (!Piwik::hasUserSuperUserAccess() && $this->checkIfClassCommentContainsHideAnnotation($rClass)) {
365 continue;
366 }
367
368 if ($this->checkIfCommentContainsInternalAnnotation($rClass)) {
369 continue;
370 }
371
372 $toDisplay = $this->prepareModulesAndMethods($info, $moduleName);
373
374 foreach ($toDisplay as $moduleName => $methods) {
375 foreach ($methods as $index => $method) {
376 if (!method_exists($class, $method)) { // method is handled through API.Request.intercept event
377 continue;
378 }
379
380 $reflectionMethod = new \ReflectionMethod($class, $method);
381 if ($this->checkIfCommentContainsInternalAnnotation($reflectionMethod)) {
382 unset($toDisplay[$moduleName][$index]);
383 }
384 }
385 if (empty($toDisplay[$moduleName])) {
386 unset($toDisplay[$moduleName]);
387 }
388 }
389
390 foreach ($toDisplay as $moduleName => $methods) {
391 $toc .= $this->prepareModuleToDisplay($moduleName);
392 $str .= $this->prepareMethodToDisplay($moduleName, $info, $methods, $class, $outputExampleUrls, $prefixUrls, $displayTitlesAsAngularDirective);
393 }
394 }
395 return array($toc, $str);
396 }
397 }
398