PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.13.2
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.13.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 / View.php
matomo / app / core Last commit date
API 3 years ago Access 3 years ago Application 4 years ago Archive 3 years ago ArchiveProcessor 3 years ago Archiver 5 years ago AssetManager 3 years ago Auth 3 years ago Category 5 years ago Changes 4 years ago CliMulti 4 years ago Columns 3 years ago Concurrency 3 years ago Config 4 years ago Container 4 years ago CronArchive 3 years ago DataAccess 3 years ago DataFiles 5 years ago DataTable 3 years ago Db 3 years ago DeviceDetector 3 years ago Email 5 years ago Exception 3 years ago Http 4 years ago Intl 4 years ago Mail 3 years ago Measurable 5 years ago Menu 3 years ago Metrics 4 years ago Notification 4 years ago Period 4 years ago Plugin 3 years ago ProfessionalServices 4 years ago Report 5 years ago ReportRenderer 3 years ago Scheduler 4 years ago Segment 3 years ago Session 4 years ago Settings 3 years ago Tracker 3 years ago Translation 4 years ago UpdateCheck 5 years ago Updater 3 years ago Updates 3 years ago Validators 4 years ago View 4 years ago ViewDataTable 3 years ago Visualization 4 years ago Widget 5 years ago .htaccess 6 years ago Access.php 4 years ago Archive.php 4 years ago ArchiveProcessor.php 4 years ago AssetManager.php 4 years ago Auth.php 5 years ago AuthResult.php 5 years ago BaseFactory.php 5 years ago Cache.php 5 years ago CacheId.php 5 years ago CliMulti.php 4 years ago Common.php 3 years ago Config.php 3 years ago Console.php 3 years ago Context.php 5 years ago Cookie.php 3 years ago CronArchive.php 3 years ago DataArray.php 4 years ago DataTable.php 3 years ago Date.php 4 years ago Db.php 4 years ago DbHelper.php 3 years ago Development.php 5 years ago ErrorHandler.php 5 years ago EventDispatcher.php 3 years ago ExceptionHandler.php 3 years ago FileIntegrity.php 5 years ago Filechecks.php 4 years ago Filesystem.php 3 years ago FrontController.php 3 years ago Http.php 3 years ago IP.php 4 years ago Log.php 4 years ago LogDeleter.php 4 years ago Mail.php 4 years ago Metrics.php 3 years ago NoAccessException.php 5 years ago Nonce.php 3 years ago Notification.php 5 years ago NumberFormatter.php 4 years ago Option.php 4 years ago Period.php 5 years ago Piwik.php 3 years ago Plugin.php 4 years ago Profiler.php 4 years ago ProxyHeaders.php 5 years ago ProxyHttp.php 3 years ago QuickForm2.php 5 years ago RankingQuery.php 3 years ago ReportRenderer.php 4 years ago Segment.php 3 years ago Sequence.php 5 years ago Session.php 3 years ago SettingsPiwik.php 3 years ago SettingsServer.php 5 years ago Singleton.php 5 years ago Site.php 3 years ago SiteContentDetector.php 3 years ago SupportedBrowser.php 3 years ago TCPDF.php 5 years ago Theme.php 5 years ago Timer.php 5 years ago Tracker.php 4 years ago Twig.php 4 years ago Unzip.php 5 years ago UpdateCheck.php 5 years ago Updater.php 4 years ago UpdaterErrorException.php 5 years ago Updates.php 5 years ago Url.php 3 years ago UrlHelper.php 3 years ago Version.php 3 years ago View.php 3 years ago bootstrap.php 3 years ago dispatch.php 5 years ago testMinimumPhpVersion.php 3 years ago
View.php
518 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;
10
11 use Exception;
12 use Piwik\AssetManager\UIAssetCacheBuster;
13 use Piwik\Container\StaticContainer;
14 use Piwik\Plugins\CoreAdminHome\Controller;
15 use Piwik\Plugins\CorePluginsAdmin\CorePluginsAdmin;
16 use Piwik\View\ViewInterface;
17 use Piwik\View\SecurityPolicy;
18 use Twig\Environment;
19
20 /**
21 * Transition for pre-Piwik 0.4.4
22 */
23 if (!defined('PIWIK_USER_PATH')) {
24 define('PIWIK_USER_PATH', PIWIK_INCLUDE_PATH);
25 }
26
27 /**
28 * Encapsulates and manages a [Twig](http://twig.sensiolabs.org/) template.
29 *
30 * View lets you set properties that will be passed on to a Twig template.
31 * View will also set several properties that will be available in all Twig
32 * templates, including:
33 *
34 * - **currentModule**: The value of the **module** query parameter.
35 * - **currentAction**: The value of the **action** query parameter.
36 * - **userLogin**: The current user login name.
37 * - **sites**: List of site data for every site the current user has at least
38 * view access for.
39 * - **url**: The current URL (sanitized).
40 * - **token_auth**: The current user's token auth.
41 * - **userHasSomeAdminAccess**: True if the user has admin access to at least
42 * one site, false if otherwise.
43 * - **userIsSuperUser**: True if the user is the superuser, false if otherwise.
44 * - **latest_version_available**: The latest version of Piwik available.
45 * - **isWidget**: The value of the 'widget' query parameter.
46 * - **show_autocompleter**: Whether the site selector should be shown or not.
47 * - **loginModule**: The name of the currently used authentication module.
48 * - **isInternetEnabled**: Whether the matomo server is allowed to connect to
49 * external networks.
50 *
51 * ### Template Naming Convention
52 *
53 * Template files should be named after the controller method they are used in.
54 * If they are used in more than one controller method or are included by another
55 * template, they should describe the output they generate and be prefixed with
56 * an underscore, eg, **_dataTable.twig**.
57 *
58 * ### Twig
59 *
60 * Twig templates must exist in the **templates** folder in a plugin's root
61 * folder.
62 *
63 * The following filters are available to twig templates:
64 *
65 * - **translate**: Outputs internationalized text using a translation token, eg,
66 * `{{ 'General_Date'|translate }}`. sprintf parameters can be passed
67 * to the filter.
68 * - **urlRewriteWithParameters**: Modifies the current query string with the given
69 * set of parameters, eg,
70 *
71 * {{ {'module':'MyPlugin', 'action':'index'} | urlRewriteWithParameters }}
72 *
73 * - **sumTime**: Pretty formats an number of seconds.
74 * - **money**: Formats a numerical value as a monetary value using the currency
75 * of the supplied site (second arg is site ID).
76 * eg, `{{ 23|money(site.idsite)|raw }}
77 * - **truncate**: Truncates the text to certain length (determined by first arg.)
78 * eg, `{{ myReallyLongText|truncate(80) }}`
79 * - **implode**: Calls `implode`.
80 * - **ucwords**: Calls `ucwords`.
81 *
82 * The following functions are available to twig templates:
83 *
84 * - **linkTo**: Modifies the current query string with the given set of parameters,
85 * eg `{{ linkTo({'module':'MyPlugin', 'action':'index'}) }}`.
86 * - **sparkline**: Outputs a sparkline image HTML element using the sparkline image
87 * src link. eg, `{{ sparkline(sparklineUrl) }}`.
88 * - **postEvent**: Posts an event that allows event observers to add text to a string
89 * which is outputted in the template, eg, `{{ postEvent('MyPlugin.event') }}`
90 * - **isPluginLoaded**: Returns true if the supplied plugin is loaded, false if otherwise.
91 * `{% if isPluginLoaded('Goals') %}...{% endif %}`
92 * - **areAdsForProfessionalServicesEnabled**: Returns true if it is ok to show some advertising in the UI for providers of Professional Support for Piwik (from Piwik 2.16.0)
93 * - **isMultiServerEnvironment**: Returns true if Piwik is used on more than one server (since Piwik 2.16.1)
94 *
95 * ### Examples
96 *
97 * **Basic usage**
98 *
99 * // a controller method
100 * public function myView()
101 * {
102 * $view = new View("@MyPlugin/myView");
103 * $view->property1 = "a view property";
104 * $view->property2 = "another view property";
105 * return $view->render();
106 * }
107 *
108 *
109 * @api
110 */
111 class View implements ViewInterface
112 {
113 private $template = '';
114
115 /**
116 * Instance
117 * @var Environment
118 */
119 private $twig;
120 protected $templateVars = array();
121 private $contentType = 'text/html; charset=utf-8';
122 private $xFrameOptions = null;
123 private $enableCacheBuster = true;
124
125 private $useStrictReferrerPolicy = true;
126
127 /**
128 * Can be disabled to not send headers when rendering a view. This can be useful if heaps of views are being
129 * rendered during one request to possibly prevent a segmentation fault see eg #15307 . It should not be disabled
130 * for a main view, but could be disabled for views that are being rendered eg during a twig event as a "subview" which
131 * is part of the "main view".
132 * @var bool
133 */
134 public $sendHeadersWhenRendering = true;
135
136 /**
137 * Constructor.
138 *
139 * @param string $templateFile The template file to load. Must be in the following format:
140 * `"@MyPlugin/templateFileName"`. Note the absence of .twig
141 * from the end of the name.
142 */
143 public function __construct($templateFile)
144 {
145 $templateExt = '.twig';
146 if (substr($templateFile, -strlen($templateExt)) !== $templateExt) {
147 $templateFile .= $templateExt;
148 }
149 $this->template = $templateFile;
150
151 $this->initializeTwig();
152
153 $this->piwik_version = Version::VERSION;
154 $this->userLogin = Piwik::getCurrentUserLogin();
155 $this->isSuperUser = Access::getInstance()->hasSuperUserAccess();
156 // following is used in ajaxMacros called macro (showMoreHelp as passed in other templates) - requestErrorDiv
157 $isGeneralSettingsAdminEnabled = Controller::isGeneralSettingsAdminEnabled();
158 $isPluginsAdminEnabled = CorePluginsAdmin::isPluginsAdminEnabled();
159 // simplify template usage
160 $this->showMoreFaqInfo = $this->isSuperUser && ($isGeneralSettingsAdminEnabled || $isPluginsAdminEnabled);
161
162 try {
163 $this->piwikUrl = SettingsPiwik::getPiwikUrl();
164 } catch (Exception $ex) {
165 // pass (occurs when DB cannot be connected to, perhaps piwik URL cache should be stored in config file...)
166 }
167
168 $this->userRequiresPasswordConfirmation = Piwik::doesUserRequirePasswordConfirmation(Piwik::getCurrentUserLogin());
169 }
170
171 /**
172 * Disables the cache buster (adding of ?cb=...) to JavaScript and stylesheet files
173 */
174 public function disableCacheBuster()
175 {
176 $this->enableCacheBuster = false;
177 }
178
179 /**
180 * Returns the template filename.
181 *
182 * @return string
183 */
184 public function getTemplateFile()
185 {
186 return $this->template;
187 }
188
189 /**
190 * Returns the variables to bind to the template when rendering.
191 *
192 * @param array $override Template variable override values. Mainly useful
193 * when including View templates in other templates.
194 * @return array
195 */
196 public function getTemplateVars($override = array())
197 {
198 return $override + $this->templateVars;
199 }
200
201 /**
202 * Directly assigns a variable to the view script.
203 * Variable names may not be prefixed with '_'.
204 *
205 * @param string $key The variable name.
206 * @param mixed $val The variable value.
207 */
208 public function __set($key, $val)
209 {
210 $this->templateVars[$key] = $val;
211 }
212
213 /**
214 * Retrieves an assigned variable.
215 * Variable names may not be prefixed with '_'.
216 *
217 * @param string $key The variable name.
218 * @return mixed The variable value.
219 */
220 public function &__get($key)
221 {
222 return $this->templateVars[$key];
223 }
224
225 /**
226 * Returns true if a template variable has been set or not.
227 *
228 * @param string $name The name of the template variable.
229 * @return bool
230 */
231 public function __isset($name)
232 {
233 return isset($this->templateVars[$name]);
234 }
235
236 /**
237 * Unsets a template variable.
238 *
239 * @param string $name The name of the template variable.
240 */
241 public function __unset($name)
242 {
243 unset($this->templateVars[$name]);
244 }
245
246 private function initializeTwig()
247 {
248 $this->twig = StaticContainer::get(Twig::class)->getTwigEnvironment();
249 }
250
251 /**
252 * Renders the current view. Also sends the stored 'Content-Type' HTML header.
253 * See {@link setContentType()}.
254 *
255 * @return string Generated template.
256 */
257 public function render()
258 {
259 try {
260 $this->currentModule = Piwik::getModule();
261 $this->currentAction = Piwik::getAction();
262
263 $this->url = Common::sanitizeInputValue(Url::getCurrentUrl());
264 $this->token_auth = Piwik::getCurrentUserTokenAuth();
265 $this->userHasSomeAdminAccess = Piwik::isUserHasSomeAdminAccess();
266 $this->userIsAnonymous = Piwik::isUserIsAnonymous();
267 $this->userIsSuperUser = Piwik::hasUserSuperUserAccess();
268 $this->latest_version_available = UpdateCheck::isNewestVersionAvailable();
269 $this->showUpdateNotificationToUser = !SettingsPiwik::isShowUpdateNotificationToSuperUsersOnlyEnabled() || Piwik::hasUserSuperUserAccess();
270 $this->disableLink = Common::getRequestVar('disableLink', 0, 'int');
271 $this->isWidget = Common::getRequestVar('widget', 0, 'int');
272 $this->isMultiServerEnvironment = SettingsPiwik::isMultiServerEnvironment();
273 $this->isInternetEnabled = SettingsPiwik::isInternetEnabled();
274 $this->shouldPropagateTokenAuth = $this->shouldPropagateTokenAuthInAjaxRequests();
275
276 $piwikAds = StaticContainer::get('Piwik\ProfessionalServices\Advertising');
277 $this->areAdsForProfessionalServicesEnabled = $piwikAds->areAdsForProfessionalServicesEnabled();
278
279 if (Development::isEnabled()) {
280 $cacheBuster = rand(0, 10000);
281 } else {
282 $cacheBuster = UIAssetCacheBuster::getInstance()->piwikVersionBasedCacheBuster();
283 }
284 $this->cacheBuster = $cacheBuster;
285
286 $this->loginModule = Piwik::getLoginPluginName();
287 } catch (Exception $e) {
288 Log::debug($e);
289
290 // can fail, for example at installation (no plugin loaded yet)
291 }
292
293 if ($this->sendHeadersWhenRendering) {
294 ProxyHttp::overrideCacheControlHeaders('no-store');
295
296 Common::sendHeader('Content-Type: ' . $this->contentType);
297 // always sending this header, sometimes empty, to ensure that Dashboard embed loads
298 // - when calling sendHeader() multiple times, the last one prevails
299 if(!empty($this->xFrameOptions)) {
300 Common::sendHeader('X-Frame-Options: ' . (string)$this->xFrameOptions);
301 }
302
303 // don't send Referer-Header for outgoing links
304 if (!empty($this->useStrictReferrerPolicy)) {
305 Common::sendHeader('Referrer-Policy: same-origin');
306 } else {
307 // always send explicit default header
308 Common::sendHeader('Referrer-Policy: no-referrer-when-downgrade');
309 }
310
311 // this will be an empty string if CSP is disabled
312 $cspHeader = StaticContainer::get(SecurityPolicy::class)->createHeaderString();
313 if ('' !== $cspHeader) {
314 Common::sendHeader($cspHeader);
315 }
316 }
317
318 return $this->renderTwigTemplate();
319 }
320
321 /**
322 * @internal
323 * @ignore
324 * @return Environment
325 */
326 public function getTwig()
327 {
328 return $this->twig;
329 }
330
331 protected function renderTwigTemplate()
332 {
333 $output = $this->twig->render($this->getTemplateFile(), $this->getTemplateVars());
334
335 if ($this->enableCacheBuster) {
336 $output = $this->applyFilter_cacheBuster($output);
337 }
338
339 $helper = new Theme;
340 $output = $helper->rewriteAssetsPathToTheme($output);
341 return $output;
342 }
343
344 protected function applyFilter_cacheBuster($output)
345 {
346 $cacheBuster = UIAssetCacheBuster::getInstance();
347 $cache = Cache::getTransientCache();
348
349 $cssCacheBusterId = $cache->fetch('cssCacheBusterId');
350 if (empty($cssCacheBusterId)) {
351 $assetManager = AssetManager::getInstance();
352 $stylesheet = $assetManager->getMergedStylesheetAsset();
353 if ($stylesheet->exists()) {
354 $content = $stylesheet->getContent();
355 } else {
356 $content = $assetManager->getMergedStylesheet()->getContent();
357 }
358 $cssCacheBusterId = $cacheBuster->md5BasedCacheBuster($content);
359 $cache->save('cssCacheBusterId', $cssCacheBusterId);
360 }
361
362 $tagJs = 'cb=' . ($this->cacheBuster ?? $cacheBuster->piwikVersionBasedCacheBuster());
363 $tagCss = 'cb=' . $cssCacheBusterId;
364
365 $pattern = array(
366 '~<script type=[\'"]text/javascript[\'"] src=[\'"]([^\'"]+)[\'"]>~',
367 '~<script src=[\'"]([^\'"]+)[\'"] type=[\'"]text/javascript[\'"]>~',
368 '~<script type=[\'"]text/javascript[\'"] src=[\'"]([^\'"]+?chunk=[^\'"]+)[\'"] defer>~',
369 '~<link rel=[\'"]stylesheet[\'"] type=[\'"]text/css[\'"] href=[\'"]([^\'"]+)[\'"] ?/?>~',
370 // removes the double ?cb= tag
371 '~(src|href)=\"index.php\?module=([A-Za-z0-9_]+)&action=([A-Za-z0-9_]+)\?cb=~',
372 );
373
374 $replace = array(
375 '<script type="text/javascript" src="$1?' . $tagJs . '">',
376 '<script type="text/javascript" src="$1?' . $tagJs . '">',
377 '<script type="text/javascript" src="$1&' . $tagJs . '" defer>',
378 '<link rel="stylesheet" type="text/css" href="$1?' . $tagCss . '" />',
379 '$1="index.php?module=$2&amp;action=$3&amp;cb=',
380 );
381
382 return preg_replace($pattern, $replace, $output);
383 }
384
385 /**
386 * Set stored value used in the Content-Type HTTP header field. The header is
387 * set just before rendering.
388 *
389 * @param string $contentType
390 */
391 public function setContentType($contentType)
392 {
393 $this->contentType = $contentType;
394 }
395
396 /**
397 * Set X-Frame-Options field in the HTTP response. The header is set just
398 * before rendering.
399 *
400 * _Note: setting this allows you to make sure the View **cannot** be
401 * embedded in iframes. Learn more [here](https://developer.mozilla.org/en-US/docs/HTTP/X-Frame-Options)._
402 *
403 * @param string $option ('deny' or 'sameorigin')
404 */
405 public function setXFrameOptions($option = 'deny')
406 {
407
408 if ($option === 'deny' || $option === 'sameorigin') {
409 $this->xFrameOptions = $option;
410 }
411 if ($option == 'allow') {
412 $this->xFrameOptions = null;
413 }
414 }
415
416 /**
417 * Add form to view
418 *
419 * @param QuickForm2 $form
420 * @ignore
421 */
422 public function addForm(QuickForm2 $form)
423 {
424
425 // assign array with form data
426 $this->assign('form_data', $form->getFormData());
427 $this->assign('element_list', $form->getElementList());
428 }
429
430 /**
431 * Assign value to a variable for use in a template
432 * @param string|array $var
433 * @param mixed $value
434 * @ignore
435 */
436 public function assign($var, $value = null)
437 {
438 if (is_string($var)) {
439 $this->$var = $value;
440 } elseif (is_array($var)) {
441 foreach ($var as $key => $value) {
442 $this->$key = $value;
443 }
444 }
445 }
446
447 /**
448 * Clear compiled Twig templates
449 * @ignore
450 */
451 public static function clearCompiledTemplates()
452 {
453 $enable = StaticContainer::get('view.clearcompiledtemplates.enable');
454 if ($enable) {
455 // some high performance systems that run many Matomo instances may never want to clear this template cache
456 // if they use eg a blue/green deployment
457 $templatesCompiledPath = StaticContainer::get('path.tmp.templates');
458 Filesystem::unlinkRecursive($templatesCompiledPath, false);
459 }
460 }
461
462 /**
463 * Creates a View for and then renders the single report template.
464 *
465 * Can be used for pages that display only one report to avoid having to create
466 * a new template.
467 *
468 * @param string $title The report title.
469 * @param string $reportHtml The report body HTML.
470 * @return string|void The report contents if `$fetch` is true.
471 */
472 public static function singleReport($title, $reportHtml)
473 {
474 $view = new View('@CoreHome/_singleReport');
475 $view->title = $title;
476 $view->report = $reportHtml;
477 return $view->render();
478 }
479
480 private function shouldPropagateTokenAuthInAjaxRequests()
481 {
482 $generalConfig = Config::getInstance()->General;
483 return Common::getRequestVar('module', false) == 'Widgetize' ||
484 $generalConfig['enable_framed_pages'] == '1' ||
485 $this->validTokenAuthInUrl();
486 }
487
488 /**
489 * @return bool
490 * @throws Exception
491 */
492 private function validTokenAuthInUrl()
493 {
494 $tokenAuth = Common::getRequestVar('token_auth', '', 'string', $_GET);
495 return ($tokenAuth && $tokenAuth === Piwik::getCurrentUserTokenAuth());
496 }
497
498 /**
499 * Returns whether a strict Referrer-Policy header will be sent. Generally this should be set to 'true'.
500 *
501 * @return bool
502 */
503 public function getUseStrictReferrerPolicy()
504 {
505 return $this->useStrictReferrerPolicy;
506 }
507
508 /**
509 * Sets whether a strict Referrer-Policy header will be sent (if not, nothing is sent).
510 *
511 * @param bool $useStrictReferrerPolicy
512 */
513 public function setUseStrictReferrerPolicy($useStrictReferrerPolicy)
514 {
515 $this->useStrictReferrerPolicy = $useStrictReferrerPolicy;
516 }
517 }
518