matomo / plugins /CoreAdminHome /Controller.php
Leon4gr45's picture
Upload source files chunk 15
4202035 verified
Raw
History Blame Contribute Delete
17.7 kB
<?php
/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\CoreAdminHome;
use Exception;
use Piwik\API\ResponseBuilder;
use Piwik\ArchiveProcessor\Rules;
use Piwik\Common;
use Piwik\Config;
use Piwik\DataTable\Renderer\Json;
use Piwik\Mail;
use Piwik\Menu\MenuTop;
use Piwik\Piwik;
use Piwik\Plugin;
use Piwik\Plugin\ControllerAdmin;
use Piwik\Changes\UserChanges;
use Piwik\Plugins\CorePluginsAdmin\CorePluginsAdmin;
use Piwik\Plugins\Marketplace\Marketplace;
use Piwik\Plugins\CustomVariables\CustomVariables;
use Piwik\Plugins\LanguagesManager\LanguagesManager;
use Piwik\Plugins\Login\PasswordVerifier;
use Piwik\Plugins\PrivacyManager\DoNotTrackHeaderChecker;
use Piwik\Plugins\SitesManager\API as APISitesManager;
use Piwik\Request;
use Piwik\Site;
use Piwik\Translation\Translator;
use Piwik\Url;
use Piwik\UrlHelper;
use Piwik\View;
use Piwik\Widget\WidgetsList;
use Piwik\SettingsPiwik;
use Piwik\Plugins\UsersManager\Model as UsersModel;
use Piwik\Plugins\UsersManager\UserPreferences;
class Controller extends ControllerAdmin
{
/**
* @var Translator
*/
private $translator;
/** @var OptOutManager */
private $optOutManager;
/** @var PasswordVerifier */
private $passwordVerify;
public function __construct(Translator $translator, OptOutManager $optOutManager, PasswordVerifier $passwordVerify)
{
$this->translator = $translator;
$this->optOutManager = $optOutManager;
$this->passwordVerify = $passwordVerify;
parent::__construct();
}
public function home()
{
$isInternetEnabled = SettingsPiwik::isInternetEnabled();
$isMarketplaceEnabled = Marketplace::isMarketplaceEnabled();
$isFeedbackEnabled = Plugin\Manager::getInstance()->isPluginLoaded('Feedback');
$widgetsList = WidgetsList::get();
if ($isInternetEnabled && $isMarketplaceEnabled) {
$this->securityPolicy->addPolicy('img-src', '*.matomo.org');
}
$hasDonateForm = $widgetsList->isDefined('CoreHome', 'getDonateForm');
$hasPiwikBlog = $widgetsList->isDefined('RssWidget', 'rssPiwik');
$hasPremiumFeatures = $widgetsList->isDefined('Marketplace', 'getPremiumFeatures');
$hasNewPlugins = $widgetsList->isDefined('Marketplace', 'getNewPlugins');
$hasDiagnostics = $widgetsList->isDefined('Installation', 'getSystemCheck');
$hasTrackingFailures = $widgetsList->isDefined('CoreAdminHome', 'getTrackingFailures');
$hasQuickLinks = $widgetsList->isDefined('CoreHome', 'quickLinks');
$hasSystemSummary = $widgetsList->isDefined('CoreHome', 'getSystemSummary');
return $this->renderTemplate('home', array(
'isInternetEnabled' => $isInternetEnabled,
'isMarketplaceEnabled' => $isMarketplaceEnabled,
'hasPremiumFeatures' => $hasPremiumFeatures,
'hasNewPlugins' => $hasNewPlugins,
'isFeedbackEnabled' => $isFeedbackEnabled,
'hasDonateForm' => $hasDonateForm,
'hasPiwikBlog' => $hasPiwikBlog,
'hasDiagnostics' => $hasDiagnostics,
'hasTrackingFailures' => $hasTrackingFailures,
'hasQuickLinks' => $hasQuickLinks,
'hasSystemSummary' => $hasSystemSummary,
));
}
public function index()
{
$this->redirectToIndex('UsersManager', 'userSettings');
return;
}
public function trackingFailures()
{
Piwik::checkUserHasSomeAdminAccess();
return $this->renderTemplate('trackingFailures');
}
public function generalSettings()
{
Piwik::checkUserHasSuperUserAccess();
$view = new View('@CoreAdminHome/generalSettings');
$this->handleGeneralSettingsAdmin($view);
$view->trustedHosts = array_values(Url::getTrustedHostsFromConfig());
$logo = new CustomLogo();
$view->branding = array('use_custom_logo' => $logo->isEnabled());
$view->fileUploadEnabled = $logo->isFileUploadEnabled();
$view->logosWriteable = $logo->isCustomLogoWritable();
$view->customLogoEnabled = $logo->isCustomLogoFeatureEnabled();
$view->hasUserLogo = CustomLogo::hasUserLogo();
$view->pathUserLogo = CustomLogo::getPathUserLogo();
$view->hasUserFavicon = CustomLogo::hasUserFavicon();
$view->pathUserFavicon = CustomLogo::getPathUserFavicon();
$view->pathUserLogoSmall = CustomLogo::getPathUserLogoSmall();
$view->pathUserLogoSVG = CustomLogo::getPathUserSvgLogo();
$view->pathUserLogoDirectory = realpath(dirname($view->pathUserLogo) . '/');
$view->mailTypes = array(
'' => '',
'Plain' => 'Plain',
'Login' => 'Login',
'Cram-md5' => 'Cram-md5',
);
$view->mailEncryptions = array(
'' => 'auto',
'ssl' => 'SSL',
'tls' => 'TLS',
'none' => 'none',
);
$mail = new Mail();
$view->mailHost = $mail->getMailHost();
$view->language = LanguagesManager::getLanguageCodeForCurrentUser();
$this->setBasicVariablesView($view);
return $view->render();
}
public function setMailSettings()
{
Piwik::checkUserHasSuperUserAccess();
if (!self::isGeneralSettingsAdminEnabled()) {
// General settings + Beta channel + SMTP settings is disabled
return '';
}
if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') {
throw new Exception('Invalid HTTP method.');
}
$response = new ResponseBuilder('json');
try {
$this->checkTokenInUrl();
$request = Request::fromPost();
// require password re-authentication before applying any changes
$login = Piwik::getCurrentUserLogin();
if (Piwik::doesUserRequirePasswordConfirmation($login)) {
$passwordConfirmation = $request->getStringParameter('passwordConfirmation', '');
if (
$passwordConfirmation === ''
|| !$this->passwordVerify->isPasswordCorrect($login, $passwordConfirmation)
) {
throw new Exception(Piwik::translate('UsersManager_CurrentPasswordNotCorrect'));
}
}
// Update email settings
$mail = [];
$mail['transport'] = $request->getBoolParameter('mailUseSmtp') ? 'smtp' : '';
$mail['port'] = $request->getStringParameter('mailPort', '');
$mail['host'] = $request->getStringParameter('mailHost', '');
$mail['type'] = $request->getStringParameter('mailType', '');
$mail['username'] = $request->getStringParameter('mailUsername', '');
$mail['password'] = $request->getStringParameter('mailPassword', '');
if (!array_key_exists('mailPassword', $request->getParameters()) && Config::getInstance()->mail['host'] === $mail['host']) {
// use old password if it wasn't set in request (and the host wasn't changed)
$mail['password'] = Config::getInstance()->mail['password'];
}
$mail['encryption'] = $request->getStringParameter('mailEncryption', '');
Config::getInstance()->mail = $mail;
$general = Config::getInstance()->General;
$general['noreply_email_name'] = $request->getStringParameter('mailFromName', '');
$mailFrom = $request->getStringParameter('mailFromAddress', '');
if (empty($mailFrom)) {
$mailFrom = 'noreply@{DOMAIN}';
}
if (!Piwik::isValidEmailString($mailFrom) && !Common::stringEndsWith($mailFrom, '@{DOMAIN}')) {
throw new Exception(Piwik::translate('CoreAdminHome_ErrorEmailFromAddressNotValid'));
}
$general['noreply_email_address'] = $mailFrom;
Config::getInstance()->General = $general;
Config::getInstance()->forceSave();
$toReturn = $response->getResponse();
} catch (Exception $e) {
$toReturn = $response->getResponseException($e);
}
return $toReturn;
}
/**
* Renders and returns an admin page that lets users generate custom JavaScript
* tracking code and custom image tracker links.
*/
public function trackingCodeGenerator()
{
Piwik::checkUserHasSomeViewAccess();
$view = new View('@CoreAdminHome/trackingCodeGenerator');
$this->setBasicVariablesView($view);
$view->topMenu = MenuTop::getInstance()->getMenu();
$viewableIdSites = APISitesManager::getInstance()->getSitesIdWithAtLeastViewAccess();
$defaultIdSite = reset($viewableIdSites);
$view->idSite = $this->idSite ?: $defaultIdSite;
if ($view->idSite) {
try {
$view->siteName = Site::getNameFor($view->idSite);
$view->siteNameDecoded = Common::unsanitizeInputValue($view->siteName);
} catch (Exception $e) {
// ignore if site no longer exists
}
}
$view->defaultReportSiteName = Site::getNameFor($view->idSite);
$view->defaultSiteRevenue = Site::getCurrencySymbolFor($view->idSite);
$view->maxCustomVariables = 0;
if (Plugin\Manager::getInstance()->isPluginActivated('CustomVariables')) {
$view->maxCustomVariables = CustomVariables::getNumUsableCustomVariables();
}
$view->defaultSite = array('id' => $view->idSite, 'name' => $view->defaultReportSiteName);
$view->defaultSiteDecoded = [
'id' => $view->idSite,
'name' => Common::unsanitizeInputValue($view->defaultReportSiteName),
];
$allUrls = APISitesManager::getInstance()->getSiteUrlsFromId($view->idSite);
if (isset($allUrls[1])) {
$aliasUrl = $allUrls[1];
} else {
$aliasUrl = 'x.domain.com';
}
$view->defaultReportSiteAlias = $aliasUrl;
$mainUrl = Site::getMainUrlFor($view->idSite);
$view->defaultReportSiteDomain = @parse_url($mainUrl, PHP_URL_HOST);
$dntChecker = new DoNotTrackHeaderChecker();
$view->serverSideDoNotTrackEnabled = $dntChecker->isActive();
return $view->render();
}
/**
* Shows the "Track Visits" checkbox - iFrame (deprecated)
*/
public function optOut()
{
return $this->optOutManager->getOptOutViewIframe()->render();
}
/**
* Shows the Javascript opt out
*
* @throws Exception
*/
public function optOutJS(): string
{
Common::sendHeader('Content-Type: application/javascript; charset=utf-8');
Common::sendHeader('Cache-Control: no-store');
return $this->optOutManager->getOptOutJS();
}
public function uploadCustomLogo()
{
Piwik::checkUserHasSuperUserAccess();
$this->checkTokenInUrl();
$logo = new CustomLogo();
if (! $logo->isCustomLogoFeatureEnabled()) {
return '0';
}
$successLogo = $logo->uploadLogoToTempFolder();
$successFavicon = $logo->uploadFaviconToTempFolder();
$response = [];
if ($successLogo) {
$response['logo'] = $logo->getTempUserLogoBase64();
}
if ($successFavicon) {
$response['favicon'] = $logo->getTempUserFaviconBase64();
}
Json::sendHeaderJSON();
return json_encode($response);
}
public static function isGeneralSettingsAdminEnabled()
{
return (bool) Config::getInstance()->General['enable_general_settings_admin'];
}
private function handleGeneralSettingsAdmin($view)
{
// Whether to display or not the general settings (cron, beta, smtp)
$view->isGeneralSettingsAdminEnabled = self::isGeneralSettingsAdminEnabled();
$view->isMultiServerEnvironment = SettingsPiwik::isMultiServerEnvironment();
$view->isPluginsAdminEnabled = CorePluginsAdmin::isPluginsAdminEnabled();
if ($view->isGeneralSettingsAdminEnabled) {
$this->displayWarningIfConfigFileNotWritable();
}
$enableBrowserTriggerArchiving = Rules::isBrowserTriggerEnabled();
$todayArchiveTimeToLive = Rules::getTodayArchiveTimeToLive();
$showWarningCron = false;
if (
!$enableBrowserTriggerArchiving
&& $todayArchiveTimeToLive < 3600
) {
$showWarningCron = true;
}
$view->showWarningCron = $showWarningCron;
$view->todayArchiveTimeToLive = $todayArchiveTimeToLive;
$view->todayArchiveTimeToLiveDefault = Rules::getTodayArchiveTimeToLiveDefault();
$view->enableBrowserTriggerArchiving = $enableBrowserTriggerArchiving;
$view->showSegmentArchiveTriggerInfo = Rules::isBrowserArchivingAvailableForSegments();
$mail = Config::getInstance()->mail;
$mail['noreply_email_address'] = Config::getInstance()->General['noreply_email_address'];
$mail['noreply_email_name'] = Config::getInstance()->General['noreply_email_name'];
$mail['password'] = !empty($mail['password']) ? '*****' : '';
$view->mail = $mail;
}
/**
* Show the what is new changes list
*/
public function whatIsNew()
{
Piwik::checkUserHasSomeViewAccess();
Piwik::checkUserIsNotAnonymous();
$model = new UsersModel();
$user = $model->getUser(Piwik::getCurrentUserLogin());
if (!empty($user)) {
$userChanges = new UserChanges($user);
$changes = $this->enrichChangesForWhatIsNew($userChanges->getChanges());
return $this->renderTemplate('whatIsNew', ['changes' => $changes]);
} else {
throw new \Exception('Unable to getUser() when attempting to show whatIsNew');
}
}
/**
* Adds metadata used for rendering entries in the What's New popup.
*/
private function enrichChangesForWhatIsNew(array $changes): array
{
$pluginManager = Plugin\Manager::getInstance();
foreach ($changes as &$change) {
$pluginName = $change['plugin_name'] ?? '';
$change['showPluginPrefix'] = !empty($pluginName)
&& !$pluginManager->isPluginBundledWithCore($pluginName);
if (!empty($change['link'])) {
$defaultIdSite = $this->getDefaultIdSiteForWhatIsNewLinks();
if (!empty($defaultIdSite)) {
$change['link'] = $this->normalizeWhatIsNewLink($change['link'], $defaultIdSite);
}
}
}
unset($change);
return $changes;
}
private function getDefaultIdSiteForWhatIsNewLinks(): ?int
{
$userPreferences = new UserPreferences();
$defaultReport = $userPreferences->getDefaultReport();
if (is_numeric($defaultReport)) {
return (int) $defaultReport;
}
$defaultWebsiteId = $userPreferences->getDefaultWebsiteId();
if (is_numeric($defaultWebsiteId)) {
return (int) $defaultWebsiteId;
}
return null;
}
private function normalizeWhatIsNewLink(string $link, int $defaultIdSite): string
{
if (!$this->isInternalWhatIsNewLink($link)) {
return $link;
}
// Use a path relative to the current Matomo install so subdirectory installs
// don't resolve internal "What's New" links against the web root.
if (strpos($link, '/index.php') === 0) {
$link = substr($link, 1);
}
$parsedLink = @parse_url($link);
if (!is_array($parsedLink)) {
return $link;
}
$query = $parsedLink['query'] ?? '';
$parsedLink['query'] = $this->replaceIdSiteInQueryString($query, $defaultIdSite);
$fragment = $parsedLink['fragment'] ?? '';
$parsedLink['fragment'] = $this->replaceIdSiteInFragment($fragment, $defaultIdSite);
if ($query === $parsedLink['query'] && $fragment === $parsedLink['fragment']) {
return $link;
}
return UrlHelper::getParseUrlReverse($parsedLink) ?: $link;
}
private function replaceIdSiteInFragment(string $fragment, int $idSite): string
{
$fragmentPrefix = '';
$fragmentQuery = '';
if (strpos($fragment, '/?') === 0) {
$fragmentPrefix = '/?';
$fragmentQuery = substr($fragment, 2);
} elseif (strpos($fragment, '?') === 0) {
$fragmentPrefix = '?';
$fragmentQuery = substr($fragment, 1);
}
if (empty($fragmentPrefix)) {
return $fragment;
}
return $fragmentPrefix . $this->replaceIdSiteInQueryString($fragmentQuery, $idSite);
}
private function replaceIdSiteInQueryString(string $queryStr, int $idSite): string
{
$queryParams = UrlHelper::getArrayFromQueryString($queryStr);
if (!array_key_exists('idSite', $queryParams)) {
return $queryStr;
}
$queryParams['idSite'] = $idSite;
return Url::getQueryStringFromParameters($queryParams);
}
private function isInternalWhatIsNewLink(string $link): bool
{
return strpos($link, 'index.php') === 0 || strpos($link, '/index.php') === 0;
}
}