PluginProbe
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.13.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.13.0
5.13.0 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 All 83 releases
matomo / app / core / API / Request.php

Request.php in Matomo Analytics – Powerful, Privacy-First Insights for WordPress 5.13.0, at app/core/API/Request.php

676 lines 29.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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\Access;
13 use Piwik\Http\HttpCodeException;
14 use Piwik\Request\AuthenticationToken;
15 use Piwik\Cache;
16 use Piwik\Common;
17 use Piwik\Config\GeneralConfig;
18 use Piwik\Container\StaticContainer;
19 use Piwik\Context;
20 use Piwik\DataTable;
21 use Piwik\Exception\PluginDeactivatedException;
22 use Piwik\IP;
23 use Piwik\Piwik;
24 use Piwik\Plugin\Manager as PluginManager;
25 use Piwik\Plugins\CoreHome\LoginAllowlist;
26 use Piwik\SettingsServer;
27 use Piwik\Url;
28 use Piwik\UrlHelper;
29 use Piwik\Log\LoggerInterface;
30 /**
31 * Dispatches API requests to the appropriate API method.
32 *
33 * The Request class is used throughout Piwik to call API methods. The difference
34 * between using Request and calling API methods directly is that Request
35 * will do more after calling the API including: applying generic filters, applying queued filters,
36 * and handling the **flat** and **label** query parameters.
37 *
38 * Additionally, the Request class will **forward current query parameters** to the request
39 * which is more convenient than calling {@link Piwik\Common::getRequestVar()} many times over.
40 *
41 * In most cases, using a Request object to query the API is the correct approach.
42 *
43 * ### Post-processing
44 *
45 * The return value of API methods undergo some extra processing before being returned by Request.
46 *
47 * ### Output Formats
48 *
49 * The value returned by Request will be serialized to a certain format before being returned.
50 *
51 * ### Examples
52 *
53 * **Basic Usage**
54 *
55 * $request = new Request([
56 * 'method' => 'UserLanguage.getLanguage',
57 * 'idSite' => 1,
58 * 'date' => 'yesterday',
59 * 'period' => 'week',
60 * 'format' => 'xml',
61 * 'filter_limit' => 5,
62 * 'filter_offset' => 0,
63 * ])
64 * $result = $request->process();
65 * echo $result;
66 *
67 * **Getting a unrendered DataTable**
68 *
69 * // use the convenience method 'processRequest'
70 * $dataTable = Request::processRequest('UserLanguage.getLanguage', array(
71 * 'idSite' => 1,
72 * 'date' => 'yesterday',
73 * 'period' => 'week',
74 * 'filter_limit' => 5,
75 * 'filter_offset' => 0
76 *
77 * 'format' => 'original', // this is the important bit
78 * ));
79 * echo "This DataTable has " . $dataTable->getRowsCount() . " rows.";
80 *
81 * @see https://piwik.org/docs/analytics-api
82 * @api
83 */
84 class Request
85 {
86 /**
87 * The count of nested API request invocations. Used to determine if the currently executing request is the root or not.
88 *
89 * @var int
90 */
91 private static $nestedApiInvocationCount = 0;
92 private $request = null;
93 /**
94 * Converts the supplied request string into an array of query parameter name/value
95 * mappings. The current query parameters (everything in `$_GET` and `$_POST`) are
96 * forwarded to request array before it is returned.
97 *
98 * @param string|array|null $request The base request string or array, eg,
99 * `'module=UserLanguage&action=getLanguage'`.
100 * @param array $defaultRequest Default query parameters. If a query parameter is absent in `$request`, it will be loaded
101 * from this. Defaults to `$_GET + $_POST`.
102 * @return array
103 */
104 public static function getRequestArrayFromString($request, $defaultRequest = null)
105 {
106 if ($defaultRequest === null) {
107 $defaultRequest = self::getDefaultRequest();
108 $requestRaw = self::getRequestParametersGET();
109 if (!empty($requestRaw['segment'])) {
110 $defaultRequest['segment'] = $requestRaw['segment'];
111 }
112 // Only default to formatting metrics if the request doesn't already contain the format metrics parameter
113 if (!isset($defaultRequest['format_metrics']) && !isset($request['format_metrics'])) {
114 $defaultRequest['format_metrics'] = 'bc';
115 }
116 }
117 $requestArray = $defaultRequest;
118 if (!is_null($request)) {
119 if (is_array($request)) {
120 $requestParsed = $request;
121 } else {
122 $request = trim($request);
123 $request = str_replace(array("\n", "\t"), '', $request);
124 $requestParsed = UrlHelper::getArrayFromQueryString($request);
125 }
126 $requestArray = $requestParsed + $defaultRequest;
127 }
128 foreach ($requestArray as &$element) {
129 if (!is_array($element)) {
130 $element = trim((string) $element);
131 }
132 }
133 return $requestArray;
134 }
135 /**
136 * @param string|array $request Query string that defines the API call (must at least contain a **method** parameter),
137 * eg, `'method=UserLanguage.getLanguage&idSite=1&date=yesterday&period=week&format=xml'`
138 * If a request is not provided, then we use the values in the `$_GET` and `$_POST`
139 * superglobals.
140 * @param array $defaultRequest Default query parameters. If a query parameter is absent in `$request`, it will be loaded
141 * from this. Defaults to `$_GET + $_POST`.
142 */
143 public function __construct($request = null, $defaultRequest = null)
144 {
145 $this->request = self::getRequestArrayFromString($request, $defaultRequest);
146 $this->sanitizeRequest();
147 $this->renameModuleAndActionInRequest();
148 }
149 /**
150 * For backward compatibility: Piwik API still works if module=Referers,
151 * we rewrite to correct renamed plugin: Referrers
152 *
153 * @param $module
154 * @param $action
155 * @return array( $module, $action )
156 * @ignore
157 */
158 public static function getRenamedModuleAndAction($module, $action)
159 {
160 /**
161 * This event is posted in the Request dispatcher and can be used
162 * to overwrite the Module and Action to dispatch.
163 * This is useful when some Controller methods or API methods have been renamed or moved to another plugin.
164 *
165 * @param $module string
166 * @param $action string
167 */
168 Piwik::postEvent('Request.getRenamedModuleAndAction', array(&$module, &$action));
169 return array($module, $action);
170 }
171 /**
172 * Make sure that the request contains no logical errors
173 */
174 private function sanitizeRequest()
175 {
176 // The label filter does not work with expanded=1 because the data table IDs have a different meaning
177 // depending on whether the table has been loaded yet. expanded=1 causes all tables to be loaded, which
178 // is why the label filter can't descend when a recursive label has been requested.
179 // To fix this, we remove the expanded parameter if a label parameter is set.
180 if (isset($this->request['label']) && !empty($this->request['label']) && isset($this->request['expanded']) && $this->request['expanded']) {
181 unset($this->request['expanded']);
182 }
183 }
184 /**
185 * Dispatches the API request to the appropriate API method and returns the result
186 * after post-processing.
187 *
188 * Post-processing includes:
189 *
190 * - flattening if **flat** is 0
191 * - running generic filters unless **disable_generic_filters** is set to 1
192 * - URL decoding label column values
193 * - running queued filters unless **disable_queued_filters** is set to 1
194 * - removing columns based on the values of the **hideColumns** and **showColumns** query parameters
195 * - filtering rows if the **label** query parameter is set
196 * - converting the result to the appropriate format (ie, XML, JSON, etc.)
197 *
198 * If `'original'` is supplied for the output format, the result is returned as a PHP
199 * object.
200 *
201 * @return DataTable|DataTable\Map|scalar|array|object|resource|null The data resulting from the API call.
202 * @throws Exception if the requested API method cannot be called, if required parameters for the
203 * API method are missing or if the API method throws an exception and the **format**
204 * query parameter is **original**.
205 * @throws PluginDeactivatedException if the module plugin is not activated.
206 */
207 public function process()
208 {
209 $shouldReloadAuth = \false;
210 $hadSuperUserAccess = \false;
211 $tokenAuthToRestore = null;
212 try {
213 ++self::$nestedApiInvocationCount;
214 // read the format requested for the output data
215 $outputFormat = strtolower(Common::getRequestVar('format', 'xml', 'string', $this->request));
216 $disablePostProcessing = $this->shouldDisablePostProcessing();
217 // create the response
218 $response = new \Piwik\API\ResponseBuilder($outputFormat, $this->request);
219 // do not send any header when processing a nested API request,
220 // as the headers might remain for to the original response
221 if (!self::isCurrentApiRequestTheRootApiRequest()) {
222 $response->disableSendHeader();
223 }
224 if ($disablePostProcessing) {
225 $response->disableDataTablePostProcessor();
226 }
227 $corsHandler = new \Piwik\API\CORSHandler();
228 $corsHandler->handle();
229 $tokenAuth = StaticContainer::get(AuthenticationToken::class)->getAuthToken($this->request);
230 // IP check is needed here as we cannot listen to API.Request.authenticate as it would then not return proper API format response.
231 // We can also not do it by listening to API.Request.dispatch as by then the user is already authenticated and we want to make sure
232 // to not expose any information in case the IP is not allowed.
233 $list = new LoginAllowlist();
234 if ($list->shouldCheckAllowlist() && $list->shouldAllowlistApplyToAPI()) {
235 $ip = IP::getIpFromHeader();
236 $list->checkIsAllowed($ip);
237 }
238 // read parameters
239 $moduleMethod = Common::getRequestVar('method', null, 'string', $this->request);
240 [$module, $method] = $this->extractModuleAndMethod($moduleMethod);
241 [$module, $method] = self::getRenamedModuleAndAction($module, $method);
242 PluginManager::getInstance()->checkIsPluginActivated($module);
243 $apiClassName = self::getClassNameAPI($module);
244 if ($shouldReloadAuth = self::shouldReloadAuthUsingTokenAuth($this->request)) {
245 $access = Access::getInstance();
246 $tokenAuthToRestore = $access->getTokenAuth();
247 $hadSuperUserAccess = $access->hasSuperUserAccess();
248 self::forceReloadAuthUsingTokenAuth($tokenAuth);
249 }
250 // call the method
251 $returnedValue = \Piwik\API\Proxy::getInstance()->call($apiClassName, $method, $this->request);
252 // get the response with the request query parameters loaded, since DataTablePost processor will use the Report
253 // class instance, which may inspect the query parameters. (eg, it may look for the idCustomReport parameters
254 // which may only exist in $this->request, if the request was called programmatically)
255 $toReturn = Context::executeWithQueryParameters($this->request, function () use($response, $returnedValue, $module, $method) {
256 return $response->getResponse($returnedValue, $module, $method);
257 });
258 } catch (Exception $e) {
259 if ($e instanceof HttpCodeException && $e->getCode() >= 400 && $e->getCode() < 500) {
260 StaticContainer::get(LoggerInterface::class)->debug('Uncaught client error in API: {exception}', ['exception' => $e, 'ignoreInScreenWriter' => \true]);
261 } else {
262 StaticContainer::get(LoggerInterface::class)->error('Uncaught exception in API: {exception}', ['exception' => $e, 'ignoreInScreenWriter' => \true]);
263 }
264 if (empty($response)) {
265 $response = new \Piwik\API\ResponseBuilder('console', $this->request);
266 }
267 $toReturn = $response->getResponseException($e);
268 } finally {
269 --self::$nestedApiInvocationCount;
270 }
271 if ($shouldReloadAuth) {
272 $this->restoreAuthUsingTokenAuth($tokenAuthToRestore, $hadSuperUserAccess);
273 }
274 return $toReturn;
275 }
276 private function restoreAuthUsingTokenAuth(
277 #[\SensitiveParameter]
278 $tokenToRestore, $hadSuperUserAccess)
279 {
280 // if we would not make sure to unset super user access, the tokenAuth would be not authenticated and any
281 // token would just keep super user access (eg if the token that was reloaded before had super user access)
282 Access::getInstance()->setSuperUserAccess(\false);
283 // we need to restore by reloading the tokenAuth as some permissions could have been removed in the API
284 // request etc. Otherwise we could just store a clone of Access::getInstance() and restore here
285 self::forceReloadAuthUsingTokenAuth($tokenToRestore);
286 if ($hadSuperUserAccess && !Access::getInstance()->hasSuperUserAccess()) {
287 // we are in context of `doAsSuperUser()` and need to restore this behaviour
288 Access::getInstance()->setSuperUserAccess(\true);
289 }
290 }
291 /**
292 * Returns the name of a plugin's API class by plugin name.
293 *
294 * @param string $plugin The plugin name, eg, `'Referrers'`.
295 * @return string The fully qualified API class name, eg, `'\Piwik\Plugins\Referrers\API'`.
296 */
297 public static function getClassNameAPI($plugin)
298 {
299 return sprintf('\\Piwik\\Plugins\\%s\\API', $plugin);
300 }
301 /**
302 * @ignore
303 * @internal
304 * @param string $currentApiMethod
305 */
306 public static function setIsRootRequestApiRequest($currentApiMethod)
307 {
308 Cache::getTransientCache()->save('API.setIsRootRequestApiRequest', $currentApiMethod);
309 }
310 /**
311 * @ignore
312 * @internal
313 * @return string current Api Method if it is an api request
314 */
315 public static function getRootApiRequestMethod()
316 {
317 return Cache::getTransientCache()->fetch('API.setIsRootRequestApiRequest');
318 }
319 /**
320 * Detect if the root request (the actual request) is an API request or not. To detect whether an API is currently
321 * request within any request, have a look at {@link isApiRequest()}.
322 *
323 * @return bool
324 * @throws Exception
325 */
326 public static function isRootRequestApiRequest()
327 {
328 $apiMethod = Cache::getTransientCache()->fetch('API.setIsRootRequestApiRequest');
329 return !empty($apiMethod);
330 }
331 /**
332 * Checks if the currently executing API request is the root API request or not.
333 *
334 * Note: the "root" API request is the first request made. Within that request, further API methods
335 * can be called programmatically. These requests are considered "child" API requests.
336 *
337 * @return bool
338 * @throws Exception
339 */
340 public static function isCurrentApiRequestTheRootApiRequest()
341 {
342 return self::$nestedApiInvocationCount == 1;
343 }
344 /**
345 * Checks if the currently executing API request is running inside another API request.
346 *
347 * This is true only for "child" API requests, i.e. requests that were dispatched
348 * programmatically from within another API method (for example the sub-requests run by
349 * {@link \Piwik\Plugins\API\API::getBulkRequest()}). It is false for the root request and
350 * when no API request is currently being processed.
351 */
352 public static function isCurrentApiRequestNestedInAnotherApiRequest() : bool
353 {
354 return self::$nestedApiInvocationCount > 1;
355 }
356 /**
357 * Detect if request is an API request. Meaning the module is 'API' and an API method having a valid format was
358 * specified. Note that this method will return true even if the actual request is for example a regular UI
359 * reporting page request but within this request we are currently processing an API request (eg a
360 * controller calls Request::processRequest('API.getMatomoVersion')). To find out if the root request is an API
361 * request or not, call {@link isRootRequestApiRequest()}
362 *
363 * @param array $request eg array('module' => 'API', 'method' => 'Test.getMethod')
364 * @return bool
365 * @throws Exception
366 */
367 public static function isApiRequest($request)
368 {
369 $method = self::getMethodIfApiRequest($request);
370 return !empty($method);
371 }
372 /**
373 * Returns the current API method being executed, if the current request is an API request.
374 *
375 * @param array|null $request eg array('module' => 'API', 'method' => 'Test.getMethod')
376 * @return string|null
377 * @throws Exception
378 */
379 public static function getMethodIfApiRequest($request)
380 {
381 $module = Common::getRequestVar('module', '', 'string', $request);
382 $method = Common::getRequestVar('method', '', 'string', $request);
383 $isApi = $module === 'API' && !empty($method) && count(explode('.', $method)) === 2;
384 return $isApi ? $method : null;
385 }
386 /**
387 * If the token_auth is found in the $request parameter,
388 * the current session will be authenticated using this token_auth.
389 * It will overwrite the previous Auth object.
390 *
391 * @param array $request If null, uses the default request ($_GET)
392 * @return void
393 * @ignore
394 */
395 public static function reloadAuthUsingTokenAuth($request = null)
396 {
397 // if a token_auth is specified in the API request, we load the right permissions
398 $token_auth = StaticContainer::get(AuthenticationToken::class)->getAuthToken($request);
399 if (self::shouldReloadAuthUsingTokenAuth($request)) {
400 self::forceReloadAuthUsingTokenAuth($token_auth);
401 }
402 }
403 /**
404 * The current session will be authenticated using this token_auth.
405 * It will overwrite the previous Auth object.
406 *
407 * @param string $tokenAuth
408 * @return void
409 */
410 private static function forceReloadAuthUsingTokenAuth(
411 #[\SensitiveParameter]
412 $tokenAuth)
413 {
414 /**
415 * Triggered when authenticating an API request, but only if the **token_auth**
416 * query parameter is found in the request.
417 *
418 * Plugins that provide authentication capabilities should subscribe to this event
419 * and make sure the global authentication object (the object returned by `StaticContainer::get('Piwik\Auth')`)
420 * is setup to use `$token_auth` when its `authenticate()` method is executed.
421 *
422 * @param string $token_auth The value of the **token_auth** query parameter.
423 */
424 Piwik::postEvent('API.Request.authenticate', array($tokenAuth));
425 if (!Access::getInstance()->reloadAccess() && $tokenAuth && $tokenAuth !== 'anonymous') {
426 /**
427 * @ignore
428 * @internal
429 */
430 Piwik::postEvent('API.Request.authenticate.failed');
431 }
432 SettingsServer::raiseMemoryLimitIfNecessary();
433 }
434 /**
435 * Needs to be called AFTER the user has been authenticated using a token.
436 *
437 * @internal
438 * @ignore
439 * @param string $module
440 * @param string $action
441 * @throws Exception
442 */
443 public static function checkTokenAuthIsNotLimited($module, $action)
444 {
445 $isApi = $module === 'API' && (empty($action) || $action === 'index');
446 if ($isApi || Common::isPhpCliMode()) {
447 return;
448 }
449 if (Access::getInstance()->hasSuperUserAccess()) {
450 $ex = new \Piwik\Exception\Exception(Piwik::translate('Widgetize_TooHighAccessLevel', [Url::getExternalLinkTag('https://matomo.org/faq/troubleshooting/faq_147/'), '</a>']));
451 $ex->setIsHtmlMessage();
452 throw $ex;
453 }
454 $allowWriteAdminModuleActionConfig = StaticContainer::get('token_auth.write_admin_allowed_module_actions');
455 if (!is_array($allowWriteAdminModuleActionConfig)) {
456 $allowWriteAdminModuleActionConfig = [];
457 }
458 $allowWriteAdmin = GeneralConfig::getConfigValue('enable_framed_allow_write_admin_token_auth') == 1;
459 $allowWriteAdminModuleAction = in_array($module . '.' . $action, $allowWriteAdminModuleActionConfig, \true);
460 if (Piwik::isUserHasSomeWriteAccess() && !$allowWriteAdmin && !$allowWriteAdminModuleAction) {
461 // we allow UI authentication/ embedding widgets / reports etc only for users that have only view
462 // access. it's mostly there to get users to use auth tokens of view users when embedding reports
463 // token_auth is fine for API calls since they would be always authenticated later anyway
464 // token_auth is also fine in CLI mode as eg doAsSuperUser might be used etc
465 //
466 // NOTE: this does not apply if the [General] enable_framed_allow_write_admin_token_auth INI
467 // option is set, or if the current module/action is allowlisted in the
468 // token_auth.write_admin_allowed_module_actions DI entry.
469 $ex = new \Piwik\Exception\Exception(Piwik::translate('Widgetize_ViewAccessRequired', [Url::getExternalLinkTag('https://matomo.org/faq/troubleshooting/faq_147/') . 'https://matomo.org/faq/troubleshooting/faq_147/</a>']));
470 $ex->setIsHtmlMessage();
471 throw $ex;
472 }
473 }
474 /**
475 * @internal
476 * @ignore
477 * @param $request
478 * @return bool
479 * @throws Exception
480 */
481 public static function shouldReloadAuthUsingTokenAuth($request)
482 {
483 if (is_null($request)) {
484 return StaticContainer::get(AuthenticationToken::class)->getAuthToken() != Access::getInstance()->getTokenAuth();
485 }
486 if (!isset($request['token_auth'])) {
487 // no token is given so we just keep the current loaded user
488 return \false;
489 }
490 // a token is specified, we need to reload auth in case it is different than the current one, even if it is empty
491 $tokenAuth = Common::getRequestVar('token_auth', '', 'string', $request);
492 // not using !== is on purpose as getTokenAuth() might return null whereas $tokenAuth is '' . In this case
493 // we do not need to reload.
494 return $tokenAuth != Access::getInstance()->getTokenAuth();
495 }
496 /**
497 * Returns true if a token_auth parameter was supplied via a secure mechanism and is not present as a URL parameter
498 * At the moment POST requests are checked, but in future other mechanism such as Authorisation HTTP header
499 * and bearer tokens might be used as well.
500 *
501 * @return bool True if token was supplied in a secure way
502 * @deprecated will be removed in Matomo 6
503 */
504 public static function isTokenAuthProvidedSecurely() : bool
505 {
506 return StaticContainer::get(AuthenticationToken::class)->wasTokenAuthProvidedSecurely();
507 }
508 /**
509 * Returns array($class, $method) from the given string $class.$method
510 *
511 * @param string $parameter
512 * @throws Exception
513 * @return array
514 */
515 private function extractModuleAndMethod($parameter)
516 {
517 $a = explode('.', $parameter);
518 if (count($a) != 2) {
519 throw new Exception("The method name is invalid. Expected 'module.methodName'");
520 }
521 return $a;
522 }
523 /**
524 * Helper method that processes an API request in one line using the variables in `$_GET`
525 * and `$_POST`.
526 *
527 * @param string $method The API method to call, ie, `'Actions.getPageTitles'`.
528 * @param array $paramOverride The parameter name-value pairs to use instead of what's
529 * in `$_GET` & `$_POST`.
530 * @param array $defaultRequest Default query parameters. If a query parameter is absent in `$request`, it will be loaded
531 * from this. Defaults to `$_GET + $_POST`.
532 *
533 * To avoid using any parameters from $_GET or $_POST, set this to an empty `array()`.
534 * @return mixed The result of the API request. See {@link process()}.
535 */
536 public static function processRequest($method, $paramOverride = array(), $defaultRequest = null)
537 {
538 $params = array();
539 $params['format'] = 'original';
540 $params['serialize'] = '0';
541 $params['module'] = 'API';
542 $params['method'] = $method;
543 $params['compare'] = '0';
544 $params = $paramOverride + $params;
545 // process request
546 $request = new \Piwik\API\Request($params, $defaultRequest);
547 return $request->process();
548 }
549 /**
550 * Returns the original request parameters in the current query string as an array mapping
551 * query parameter names with values. The result of this function will not be affected
552 * by any modifications to `$_GET` and will not include parameters in `$_POST`.
553 *
554 * @return array
555 */
556 public static function getRequestParametersGET()
557 {
558 if (empty($_SERVER['QUERY_STRING'])) {
559 return array();
560 }
561 $GET = UrlHelper::getArrayFromQueryString($_SERVER['QUERY_STRING']);
562 return $GET;
563 }
564 /**
565 * Returns the URL for the current requested report w/o any filter parameters.
566 *
567 * @param string $module The API module.
568 * @param string $action The API action.
569 * @param array $queryParams Query parameter overrides.
570 * @return string
571 */
572 public static function getBaseReportUrl($module, $action, $queryParams = array())
573 {
574 $params = array_merge($queryParams, array('module' => $module, 'action' => $action));
575 return \Piwik\API\Request::getCurrentUrlWithoutGenericFilters($params);
576 }
577 /**
578 * Returns the current URL without generic filter query parameters.
579 *
580 * @param array $params Query parameter values to override in the new URL.
581 * @return string
582 */
583 public static function getCurrentUrlWithoutGenericFilters($params)
584 {
585 // unset all filter query params so the related report will show up in its default state,
586 // unless the filter param was in $queryParams
587 $genericFiltersInfo = \Piwik\API\DataTableGenericFilter::getGenericFiltersInformation();
588 foreach ($genericFiltersInfo as $filter) {
589 foreach ($filter[1] as $queryParamName => $queryParamInfo) {
590 if (!isset($params[$queryParamName])) {
591 $params[$queryParamName] = null;
592 }
593 }
594 }
595 $params['compareDates'] = null;
596 $params['comparePeriods'] = null;
597 $params['compareSegments'] = null;
598 return Url::getCurrentQueryStringWithParametersModified($params);
599 }
600 /**
601 * Returns whether the DataTable result will have to be expanded for the
602 * current request before rendering.
603 *
604 * @return bool
605 * @ignore
606 */
607 public static function shouldLoadExpanded()
608 {
609 // if filter_column_recursive & filter_pattern_recursive are supplied, and flat isn't supplied
610 // we have to load all the child subtables.
611 return Common::getRequestVar('filter_column_recursive', \false) !== \false && Common::getRequestVar('filter_pattern_recursive', \false) !== \false && !self::shouldLoadFlatten();
612 }
613 /**
614 * @return bool
615 */
616 public static function shouldLoadFlatten()
617 {
618 return Common::getRequestVar('flat', \false) == 1;
619 }
620 /**
621 * Returns the segment query parameter from the original request, without modifications.
622 *
623 * @return string|false
624 */
625 public static function getRawSegmentFromRequest()
626 {
627 // we need the URL encoded segment parameter, we fetch it from _SERVER['QUERY_STRING'] instead of default URL decoded _GET
628 $segmentRaw = \false;
629 $segment = Common::getRequestVar('segment', '', 'string');
630 if (!empty($segment)) {
631 $request = \Piwik\API\Request::getRequestParametersGET();
632 if (!empty($request['segment'])) {
633 $segmentRaw = $request['segment'];
634 }
635 }
636 return $segmentRaw;
637 }
638 private function renameModuleAndActionInRequest()
639 {
640 if (empty($this->request['apiModule'])) {
641 return;
642 }
643 if (empty($this->request['apiAction'])) {
644 $this->request['apiAction'] = null;
645 }
646 [$this->request['apiModule'], $this->request['apiAction']] = $this->getRenamedModuleAndAction($this->request['apiModule'], $this->request['apiAction']);
647 }
648 /**
649 * @return array
650 */
651 private static function getDefaultRequest()
652 {
653 return $_GET + $_POST;
654 }
655 private function shouldDisablePostProcessing()
656 {
657 $shouldDisable = \false;
658 /**
659 * After an API method returns a value, the value is post processed (eg, rows are sorted
660 * based on the `filter_sort_column` query parameter, rows are truncated based on the
661 * `filter_limit`/`filter_offset` parameters, amongst other things).
662 *
663 * If you're creating a plugin that needs to disable post processing entirely for
664 * certain requests, use this event.
665 *
666 * @param bool &$shouldDisable Set this to true to disable datatable post processing for a request.
667 * @param array $request The request parameters.
668 */
669 Piwik::postEvent('Request.shouldDisablePostProcessing', [&$shouldDisable, $this->request]);
670 if (!$shouldDisable) {
671 $shouldDisable = self::isCurrentApiRequestTheRootApiRequest() && Common::getRequestVar('disable_root_datatable_post_processor', 0, 'int', $this->request) == 1;
672 }
673 return $shouldDisable;
674 }
675 }
676