PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.12.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.12.1
5.12.1 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 3 weeks ago ApiRenderer.php 3 weeks ago CORSHandler.php 1 year ago DataTableGenericFilter.php 3 weeks ago DataTableManipulator.php 3 weeks ago DataTablePostProcessor.php 3 weeks ago DocumentationGenerator.php 3 weeks ago Inconsistencies.php 3 weeks ago NoDefaultValue.php 2 years ago Proxy.php 3 weeks ago Request.php 2 months ago ResponseBuilder.php 3 weeks ago
DocumentationGenerator.php
353 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
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 * Possible tags to use in APIs
19 *
20 * @hide -> Won't be shown in list of all APIs but is also not possible to be called via HTTP API
21 * @hideForAll Same as @hide
22 * @hideExceptForSuperUser Same as @hide but still shown and possible to be called by a user with super user access
23 * @internal -> Won't be shown in list of all APIs but is possible to be called via HTTP API
24 */
25 class DocumentationGenerator
26 {
27 protected $countPluginsLoaded = 0;
28 /**
29 * trigger loading all plugins with an API.php file in the Proxy
30 */
31 public function __construct()
32 {
33 $plugins = \Piwik\Plugin\Manager::getInstance()->getLoadedPluginsName();
34 foreach ($plugins as $plugin) {
35 try {
36 $className = \Piwik\API\Request::getClassNameAPI($plugin);
37 \Piwik\API\Proxy::getInstance()->registerClass($className);
38 } catch (Exception $e) {
39 }
40 }
41 }
42 /**
43 * Returns a HTML page containing help for all the successfully loaded APIs.
44 *
45 * @param bool $outputExampleUrls
46 * @return string
47 */
48 public function getApiDocumentationAsString($outputExampleUrls = \true)
49 {
50 list($toc, $str) = $this->generateDocumentation($outputExampleUrls, $prefixUrls = '', $displayTitlesAsEnrichedHeadline = \true);
51 return "<div vue-entry=\"CoreHome.ContentBlock\" content-title='Quick access to APIs' id='topApiRef' name='topApiRef'>\n\t\t\t\t{$toc}</div>\n\t\t\t\t{$str}";
52 }
53 /**
54 * Used on developer.matomo.org
55 *
56 * @param bool $outputExampleUrls
57 * @param string $prefixUrls
58 * @return string
59 */
60 public function getApiDocumentationAsStringForDeveloperReference($outputExampleUrls = \true, $prefixUrls = '')
61 {
62 list($toc, $str) = $this->generateDocumentation($outputExampleUrls, $prefixUrls, $displayTitlesAsEnrichedHeadline = \false);
63 return "<h2 id='topApiRef' name='topApiRef'>Quick access to APIs</h2>\n\t\t\t\t{$toc}\n\t\t\t\t{$str}";
64 }
65 protected function prepareModuleToDisplay($moduleName)
66 {
67 return "<a href='#{$moduleName}'>{$moduleName}</a><br/>";
68 }
69 protected function prepareMethodToDisplay($moduleName, $info, $methods, $class, $outputExampleUrls, $prefixUrls, $displayTitlesAsEnrichedHeadline)
70 {
71 $str = '';
72 $str .= "\n<a name='{$moduleName}' id='{$moduleName}'></a>";
73 if ($displayTitlesAsEnrichedHeadline) {
74 $str .= "<div vue-entry=\"CoreHome.ContentBlock\" content-title='Module " . $moduleName . "'>";
75 } else {
76 $str .= "<h2>Module " . $moduleName . "</h2>";
77 }
78 $info['__documentation'] = $this->checkDocumentation($info['__documentation']);
79 $str .= "<div class='apiDescription'> " . $info['__documentation'] . " </div>";
80 foreach ($methods as $methodName) {
81 if (\Piwik\API\Proxy::getInstance()->isDeprecatedMethod($class, $methodName)) {
82 continue;
83 }
84 $params = $this->getParametersString($class, $methodName);
85 $str .= "\n <div class='apiMethod'>- <b>{$moduleName}.{$methodName} </b>" . $params . "";
86 $str .= '<small>';
87 if ($outputExampleUrls) {
88 $str .= $this->addExamples($class, $methodName, $prefixUrls);
89 }
90 $str .= '</small>';
91 $str .= "</div>\n";
92 }
93 if ($displayTitlesAsEnrichedHeadline) {
94 $str .= "</div>";
95 }
96 return $str;
97 }
98 protected function prepareModulesAndMethods($info, $moduleName)
99 {
100 $toDisplay = array();
101 foreach ($info as $methodName => $infoMethod) {
102 if ($methodName == '__documentation') {
103 continue;
104 }
105 $toDisplay[$moduleName][] = $methodName;
106 }
107 return $toDisplay;
108 }
109 protected function addExamples($class, $methodName, $prefixUrls)
110 {
111 $token = Piwik::getCurrentUserTokenAuth();
112 $token_auth_url = "&token_auth=" . $token;
113 if ($token !== 'anonymous') {
114 $token_auth_url .= "&force_api_session=1";
115 }
116 $parametersToSet = array('idSite' => Common::getRequestVar('idSite', 1, 'int'), 'period' => Common::getRequestVar('period', 'day', 'string'), 'date' => Common::getRequestVar('date', 'today', 'string'));
117 $str = '';
118 $str .= "<span class=\"example\">";
119 $exampleUrl = $this->getExampleUrl($class, $methodName, $parametersToSet);
120 if ($exampleUrl !== \false) {
121 $lastNUrls = '';
122 if (preg_match('/(&period)|(&date)/', $exampleUrl)) {
123 $exampleUrlRss = $prefixUrls . $this->getExampleUrl($class, $methodName, array('date' => 'last10', 'period' => 'day') + $parametersToSet);
124 $lastNUrls = ", RSS of the last <a target='_blank' href='{$exampleUrlRss}&format=rss{$token_auth_url}&translateColumnNames=1'>10 days</a>";
125 }
126 $exampleUrl = $prefixUrls . $exampleUrl;
127 $str .= " [ Example in\n <a target='_blank' href='{$exampleUrl}&format=xml{$token_auth_url}'>XML</a>,\n <a target='_blank' href='{$exampleUrl}&format=JSON{$token_auth_url}'>Json</a>,\n <a target='_blank' href='{$exampleUrl}&format=Tsv{$token_auth_url}&translateColumnNames=1'>Tsv (Excel)</a>\n {$lastNUrls}\n ]";
128 } else {
129 $str .= " [ No example available ]";
130 }
131 $str .= "</span>";
132 return $str;
133 }
134 /**
135 * Check if Class contains @hide
136 *
137 * @return bool
138 */
139 public function checkIfClassCommentContainsHideAnnotation(ReflectionClass $rClass)
140 {
141 return \false !== strstr($rClass->getDocComment(), '@hide');
142 }
143 /**
144 * Check if a class or method doc comment contains @internal
145 *
146 * @param ReflectionClass|\ReflectionMethod $rClass
147 * @return bool
148 */
149 private function checkIfCommentContainsInternalAnnotation($rClass)
150 {
151 return \false !== strstr($rClass->getDocComment(), '@internal');
152 }
153 /**
154 * Check if documentation contains @hide annotation and deletes it
155 *
156 * @param string $moduleToCheck
157 * @return string
158 */
159 public function checkDocumentation($moduleToCheck)
160 {
161 if (strpos($moduleToCheck, '@hide') == \true) {
162 $moduleToCheck = str_replace(strtok(strstr($moduleToCheck, '@hide'), "\n"), "", $moduleToCheck);
163 }
164 return $moduleToCheck;
165 }
166 /**
167 * Builds the example API request query string (starting with '?') for a given method,
168 * used by self::addExamples() to render example links.
169 * Returns false for methods such as deleteSite or deleteUser, or when a required
170 * parameter has no known default value.
171 *
172 * @param string $class the class
173 * @param string $methodName the method
174 * @param array $parametersToSet parameters to set
175 * @return string|bool when not possible
176 */
177 public function getExampleUrl($class, $methodName, $parametersToSet = array())
178 {
179 $knowExampleDefaultParametersValues = array('access' => 'view', 'userLogin' => 'test', 'passwordMd5ied' => 'passwordExample', 'email' => 'test@example.org', 'languageCode' => 'fr', 'url' => 'https://divezone.net/', 'pageUrl' => 'https://divezone.net/', 'apiModule' => 'UserCountry', 'apiAction' => 'getCountry', 'lastMinutes' => '30', 'abandonedCarts' => '0', 'segmentName' => 'pageTitle', 'ip' => '194.57.91.215', 'idSites' => '1,2', 'idAlert' => '1', 'seconds' => '3600');
180 foreach ($parametersToSet as $name => $value) {
181 $knowExampleDefaultParametersValues[$name] = $value;
182 }
183 // no links for these method names
184 $doNotPrintExampleForTheseMethods = array(
185 //Sites
186 'deleteSite',
187 'addSite',
188 'updateSite',
189 'addSiteAliasUrls',
190 //Users
191 'deleteUser',
192 'addUser',
193 'updateUser',
194 'setUserAccess',
195 //Goals
196 'addGoal',
197 'updateGoal',
198 'deleteGoal',
199 //Marketplace
200 'deleteLicenseKey',
201 );
202 if (in_array($methodName, $doNotPrintExampleForTheseMethods)) {
203 return \false;
204 }
205 // we try to give an URL example to call the API
206 $aParameters = \Piwik\API\Proxy::getInstance()->getParametersList($class, $methodName);
207 $aParameters['format'] = \false;
208 $aParameters['hideIdSubDatable'] = \false;
209 $aParameters['serialize'] = \false;
210 $aParameters['language'] = \false;
211 $aParameters['translateColumnNames'] = \false;
212 $aParameters['label'] = \false;
213 $aParameters['labelSeries'] = \false;
214 $aParameters['flat'] = \false;
215 $aParameters['show_dimensions'] = \false;
216 $aParameters['include_aggregate_rows'] = \false;
217 $aParameters['filter_offset'] = \false;
218 $aParameters['filter_limit'] = \false;
219 $aParameters['filter_sort_column'] = \false;
220 $aParameters['filter_sort_order'] = \false;
221 $aParameters['filter_excludelowpop'] = \false;
222 $aParameters['filter_excludelowpop_value'] = \false;
223 $aParameters['filter_column_recursive'] = \false;
224 $aParameters['filter_pattern'] = \false;
225 $aParameters['filter_pattern_recursive'] = \false;
226 $aParameters['filter_truncate'] = \false;
227 $aParameters['hideColumns'] = \false;
228 $aParameters['hideColumnsRecursively'] = \false;
229 $aParameters['showColumns'] = \false;
230 $aParameters['pivotBy'] = \false;
231 $aParameters['pivotByColumn'] = \false;
232 $aParameters['pivotByColumnLimit'] = \false;
233 $aParameters['disable_queued_filters'] = \false;
234 $aParameters['disable_generic_filters'] = \false;
235 $aParameters['expanded'] = \false;
236 $aParameters['idDimenson'] = \false;
237 $aParameters['format_metrics'] = \false;
238 $aParameters['compare'] = \false;
239 $aParameters['compareDates'] = \false;
240 $aParameters['comparePeriods'] = \false;
241 $aParameters['compareSegments'] = \false;
242 $aParameters['comparisonIdSubtables'] = \false;
243 $aParameters['invert_compare_change_compute'] = \false;
244 $aParameters['filter_update_columns_when_show_all_goals'] = \false;
245 $aParameters['filter_show_goal_columns_process_goals'] = \false;
246 $aParameters['showMetadata'] = \false;
247 $extraParameters = StaticContainer::get('entities.idNames');
248 $extraParameters = array_merge($extraParameters, StaticContainer::get('DocumentationGenerator.customParameters'));
249 foreach ($extraParameters as $paramName) {
250 if (isset($aParameters[$paramName])) {
251 continue;
252 }
253 $aParameters[$paramName] = \false;
254 }
255 $moduleName = \Piwik\API\Proxy::getInstance()->getModuleNameFromClassName($class);
256 $aParameters = array_merge(array('module' => 'API', 'method' => $moduleName . '.' . $methodName), $aParameters);
257 foreach ($aParameters as $nameVariable => &$defaultValue) {
258 if (isset($knowExampleDefaultParametersValues[$nameVariable])) {
259 $defaultValue = $knowExampleDefaultParametersValues[$nameVariable];
260 } elseif ($defaultValue instanceof \Piwik\API\NoDefaultValue) {
261 // if there isn't a default value for a given parameter,
262 // we need a 'know default value' or we can't generate the link
263 return \false;
264 }
265 }
266 return '?' . Url::getQueryStringFromParameters($aParameters);
267 }
268 /**
269 * Returns the methods $class.$name parameters (and default value if provided) as a string.
270 *
271 * @param string $class The class name
272 * @param string $name The method name
273 * @return string For example "(idSite, period, date = 'today')"
274 */
275 protected function getParametersString($class, $name)
276 {
277 $aParameters = \Piwik\API\Proxy::getInstance()->getParametersListWithTypes($class, $name);
278 $asParameters = array();
279 foreach ($aParameters as $nameVariable => $parameter) {
280 // Do not show API parameters starting with _
281 // They are supposed to be used only in internal API calls
282 if (strpos($nameVariable, '_') === 0) {
283 continue;
284 }
285 $str = '';
286 if (!empty($parameter['type'])) {
287 $prefix = $parameter['allowsNull'] ? '?' : '';
288 $str = '<i>' . $prefix . $parameter['type'] . '</i> ';
289 }
290 $str .= $nameVariable;
291 $defaultValue = $parameter['default'];
292 if (!$defaultValue instanceof \Piwik\API\NoDefaultValue) {
293 if (is_array($defaultValue)) {
294 $str .= " = 'Array'";
295 } elseif (!empty($parameter['type']) && $parameter['allowsNull']) {
296 $str .= "";
297 // don't display default value, as the ? before the type hint indicates it's optional
298 } elseif ($parameter['type'] === 'bool' && $defaultValue === \true) {
299 $str .= " = true";
300 } elseif ($parameter['type'] === 'bool' && $defaultValue === \false) {
301 $str .= " = false";
302 } else {
303 $str .= " = '{$defaultValue}'";
304 }
305 }
306 $asParameters[] = $str;
307 }
308 $sParameters = implode(", ", $asParameters);
309 return "({$sParameters})";
310 }
311 /**
312 * @param bool $outputExampleUrls
313 * @param string $prefixUrls
314 * @param bool $displayTitlesAsEnrichedHeadline
315 * @return array
316 */
317 protected function generateDocumentation($outputExampleUrls, $prefixUrls, $displayTitlesAsEnrichedHeadline)
318 {
319 $str = $toc = '';
320 foreach (\Piwik\API\Proxy::getInstance()->getMetadata() as $class => $info) {
321 $moduleName = \Piwik\API\Proxy::getInstance()->getModuleNameFromClassName($class);
322 $rClass = new ReflectionClass($class);
323 if (!Piwik::hasUserSuperUserAccess() && $this->checkIfClassCommentContainsHideAnnotation($rClass)) {
324 continue;
325 }
326 if ($this->checkIfCommentContainsInternalAnnotation($rClass)) {
327 continue;
328 }
329 $toDisplay = $this->prepareModulesAndMethods($info, $moduleName);
330 foreach ($toDisplay as $moduleName => $methods) {
331 foreach ($methods as $index => $method) {
332 if (!method_exists($class, $method)) {
333 // method is handled through API.Request.intercept event
334 continue;
335 }
336 $reflectionMethod = new \ReflectionMethod($class, $method);
337 if ($this->checkIfCommentContainsInternalAnnotation($reflectionMethod)) {
338 unset($toDisplay[$moduleName][$index]);
339 }
340 }
341 if (empty($toDisplay[$moduleName])) {
342 unset($toDisplay[$moduleName]);
343 }
344 }
345 foreach ($toDisplay as $moduleName => $methods) {
346 $toc .= $this->prepareModuleToDisplay($moduleName);
347 $str .= $this->prepareMethodToDisplay($moduleName, $info, $methods, $class, $outputExampleUrls, $prefixUrls, $displayTitlesAsEnrichedHeadline);
348 }
349 }
350 return array($toc, $str);
351 }
352 }
353