PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.2.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.2.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 1 year ago ApiRenderer.php 1 year ago CORSHandler.php 1 year ago DataTableGenericFilter.php 1 year ago DataTableManipulator.php 1 year ago DataTablePostProcessor.php 1 year ago DocumentationGenerator.php 1 year ago Inconsistencies.php 2 years ago NoDefaultValue.php 2 years ago Proxy.php 1 year ago Request.php 1 year ago ResponseBuilder.php 1 year ago
DocumentationGenerator.php
351 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.piwik.org
55 *
56 * @param bool|true $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 * @param ReflectionClass $rClass instance of ReflectionMethod
138 * @return bool
139 */
140 public function checkIfClassCommentContainsHideAnnotation(ReflectionClass $rClass)
141 {
142 return \false !== strstr($rClass->getDocComment(), '@hide');
143 }
144 /**
145 * Check if Class contains @internal
146 *
147 * @param ReflectionClass|\ReflectionMethod $rClass instance of ReflectionMethod
148 * @return bool
149 */
150 private function checkIfCommentContainsInternalAnnotation($rClass)
151 {
152 return \false !== strstr($rClass->getDocComment(), '@internal');
153 }
154 /**
155 * Check if documentation contains @hide annotation and deletes it
156 *
157 * @param $moduleToCheck
158 * @return mixed
159 */
160 public function checkDocumentation($moduleToCheck)
161 {
162 if (strpos($moduleToCheck, '@hide') == \true) {
163 $moduleToCheck = str_replace(strtok(strstr($moduleToCheck, '@hide'), "\n"), "", $moduleToCheck);
164 }
165 return $moduleToCheck;
166 }
167 /**
168 * Returns a string containing links to examples on how to call a given method on a given API
169 * It will export links to XML, CSV, HTML, JSON, PHP, etc.
170 * It will not export links for methods such as deleteSite or deleteUser
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['include_aggregate_rows'] = \false;
216 $aParameters['filter_offset'] = \false;
217 $aParameters['filter_limit'] = \false;
218 $aParameters['filter_sort_column'] = \false;
219 $aParameters['filter_sort_order'] = \false;
220 $aParameters['filter_excludelowpop'] = \false;
221 $aParameters['filter_excludelowpop_value'] = \false;
222 $aParameters['filter_column_recursive'] = \false;
223 $aParameters['filter_pattern'] = \false;
224 $aParameters['filter_pattern_recursive'] = \false;
225 $aParameters['filter_truncate'] = \false;
226 $aParameters['hideColumns'] = \false;
227 $aParameters['hideColumnsRecursively'] = \false;
228 $aParameters['showColumns'] = \false;
229 $aParameters['pivotBy'] = \false;
230 $aParameters['pivotByColumn'] = \false;
231 $aParameters['pivotByColumnLimit'] = \false;
232 $aParameters['disable_queued_filters'] = \false;
233 $aParameters['disable_generic_filters'] = \false;
234 $aParameters['expanded'] = \false;
235 $aParameters['idDimenson'] = \false;
236 $aParameters['format_metrics'] = \false;
237 $aParameters['compare'] = \false;
238 $aParameters['compareDates'] = \false;
239 $aParameters['comparePeriods'] = \false;
240 $aParameters['compareSegments'] = \false;
241 $aParameters['comparisonIdSubtables'] = \false;
242 $aParameters['invert_compare_change_compute'] = \false;
243 $aParameters['filter_update_columns_when_show_all_goals'] = \false;
244 $aParameters['filter_show_goal_columns_process_goals'] = \false;
245 $extraParameters = StaticContainer::get('entities.idNames');
246 $extraParameters = array_merge($extraParameters, StaticContainer::get('DocumentationGenerator.customParameters'));
247 foreach ($extraParameters as $paramName) {
248 if (isset($aParameters[$paramName])) {
249 continue;
250 }
251 $aParameters[$paramName] = \false;
252 }
253 $moduleName = \Piwik\API\Proxy::getInstance()->getModuleNameFromClassName($class);
254 $aParameters = array_merge(array('module' => 'API', 'method' => $moduleName . '.' . $methodName), $aParameters);
255 foreach ($aParameters as $nameVariable => &$defaultValue) {
256 if (isset($knowExampleDefaultParametersValues[$nameVariable])) {
257 $defaultValue = $knowExampleDefaultParametersValues[$nameVariable];
258 } elseif ($defaultValue instanceof \Piwik\API\NoDefaultValue) {
259 // if there isn't a default value for a given parameter,
260 // we need a 'know default value' or we can't generate the link
261 return \false;
262 }
263 }
264 return '?' . Url::getQueryStringFromParameters($aParameters);
265 }
266 /**
267 * Returns the methods $class.$name parameters (and default value if provided) as a string.
268 *
269 * @param string $class The class name
270 * @param string $name The method name
271 * @return string For example "(idSite, period, date = 'today')"
272 */
273 protected function getParametersString($class, $name)
274 {
275 $aParameters = \Piwik\API\Proxy::getInstance()->getParametersListWithTypes($class, $name);
276 $asParameters = array();
277 foreach ($aParameters as $nameVariable => $parameter) {
278 // Do not show API parameters starting with _
279 // They are supposed to be used only in internal API calls
280 if (strpos($nameVariable, '_') === 0) {
281 continue;
282 }
283 $str = '';
284 if (!empty($parameter['type'])) {
285 $prefix = $parameter['allowsNull'] ? '?' : '';
286 $str = '<i>' . $prefix . $parameter['type'] . '</i> ';
287 }
288 $str .= $nameVariable;
289 $defaultValue = $parameter['default'];
290 if (!$defaultValue instanceof \Piwik\API\NoDefaultValue) {
291 if (is_array($defaultValue)) {
292 $str .= " = 'Array'";
293 } elseif (!empty($parameter['type']) && $parameter['allowsNull']) {
294 $str .= "";
295 // don't display default value, as the ? before the type hint indicates it's optional
296 } elseif ($parameter['type'] === 'bool' && $defaultValue === \true) {
297 $str .= " = true";
298 } elseif ($parameter['type'] === 'bool' && $defaultValue === \false) {
299 $str .= " = false";
300 } else {
301 $str .= " = '{$defaultValue}'";
302 }
303 }
304 $asParameters[] = $str;
305 }
306 $sParameters = implode(", ", $asParameters);
307 return "({$sParameters})";
308 }
309 /**
310 * @param $outputExampleUrls
311 * @param $prefixUrls
312 * @param $displayTitlesAsEnrichedHeadline
313 * @return array
314 */
315 protected function generateDocumentation($outputExampleUrls, $prefixUrls, $displayTitlesAsEnrichedHeadline)
316 {
317 $str = $toc = '';
318 foreach (\Piwik\API\Proxy::getInstance()->getMetadata() as $class => $info) {
319 $moduleName = \Piwik\API\Proxy::getInstance()->getModuleNameFromClassName($class);
320 $rClass = new ReflectionClass($class);
321 if (!Piwik::hasUserSuperUserAccess() && $this->checkIfClassCommentContainsHideAnnotation($rClass)) {
322 continue;
323 }
324 if ($this->checkIfCommentContainsInternalAnnotation($rClass)) {
325 continue;
326 }
327 $toDisplay = $this->prepareModulesAndMethods($info, $moduleName);
328 foreach ($toDisplay as $moduleName => $methods) {
329 foreach ($methods as $index => $method) {
330 if (!method_exists($class, $method)) {
331 // method is handled through API.Request.intercept event
332 continue;
333 }
334 $reflectionMethod = new \ReflectionMethod($class, $method);
335 if ($this->checkIfCommentContainsInternalAnnotation($reflectionMethod)) {
336 unset($toDisplay[$moduleName][$index]);
337 }
338 }
339 if (empty($toDisplay[$moduleName])) {
340 unset($toDisplay[$moduleName]);
341 }
342 }
343 foreach ($toDisplay as $moduleName => $methods) {
344 $toc .= $this->prepareModuleToDisplay($moduleName);
345 $str .= $this->prepareMethodToDisplay($moduleName, $info, $methods, $class, $outputExampleUrls, $prefixUrls, $displayTitlesAsEnrichedHeadline);
346 }
347 }
348 return array($toc, $str);
349 }
350 }
351