diff --git a/CHANGELOG.md b/CHANGELOG.md old mode 100644 new mode 100755 index 720156637..0fc9b07ae --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ == Changelog == += 4.10.0 = +* Update Matomo core to 4.10.0 +* Fix a white screen issue in multisite mode +* Add wpstatistics import feature + = 4.6.0 = * Update Matomo core to 4.6.0 * WooCommerce: Make excluded order status configurable using a constant diff --git a/app/LEGALNOTICE b/app/LEGALNOTICE index ae764aad3..fbbd91319 100644 --- a/app/LEGALNOTICE +++ b/app/LEGALNOTICE @@ -283,7 +283,7 @@ THIRD-PARTY CONTENT Link: https://design.google.com/icons/ License: Apache License Version 2.0 - Name: IcoMoon - Free icons ("icon-funnel", "icon-lab", "icon-archive", "icon-rocket", "icon-embed", "icon-page-performance") in plugins/Morpheus/fonts + Name: IcoMoon - Free icons ("icon-funnel", "icon-lab", "icon-archive", "icon-rocket", "icon-embed", "icon-page-performance", "icon-github") in plugins/Morpheus/fonts Link: https://icomoon.io/#icons-icomoon License: GPL diff --git a/app/README.md b/app/README.md index 726dc87d4..ca1ce8baa 100644 --- a/app/README.md +++ b/app/README.md @@ -6,7 +6,7 @@ ## Code Status -[![Build Status](https://travis-ci.com/matomo-org/matomo.svg?branch=4.x-dev)](https://travis-ci.com/matomo-org/matomo/branches) +[![Build Status](https://travis-ci.com/matomo-org/matomo.svg?branch=4.x-dev)](https://app.travis-ci.com/matomo-org/matomo/branches) [![Percentage of issues still open](http://isitmaintained.com/badge/open/matomo-org/matomo.svg)](http://isitmaintained.com/project/matomo-org/matomo "Percentage of issues still open") ## Description diff --git a/app/bootstrap.php b/app/bootstrap.php index 4d5fb61b1..41b493ff0 100644 --- a/app/bootstrap.php +++ b/app/bootstrap.php @@ -156,7 +156,7 @@ function matomo_log_message_no_display($message) if ( !is_plugin_active('matomo/matomo.php') && (!defined( 'MATOMO_PHPUNIT_TEST' ) || !MATOMO_PHPUNIT_TEST) ) { // during tests the plugin may temporarily not be active - exit; + exit; } if ( $GLOBALS['MATOMO_LOADED_DIRECTLY'] ) { diff --git a/app/config/global.ini.php b/app/config/global.ini.php old mode 100755 new mode 100644 index 8443a5d2a..4f79340bb --- a/app/config/global.ini.php +++ b/app/config/global.ini.php @@ -385,6 +385,11 @@ ; This feature will not work with the MYSQLI extension. archiving_query_max_execution_time = 7200 + +; Allows you to disable archiving segments for selected plugins. For more details please see https://matomo.org/faq/how-to-disable-archiving-the-segment-reports-for-specific-plugins +; Here you can specify the comma separated list eg: "plugin1,plugin2" +disable_archiving_segment_for_plugins = "" + ; By default Matomo runs OPTIMIZE TABLE SQL queries to free spaces after deleting some data. ; If your Matomo tracks millions of pages, the OPTIMIZE TABLE queries might run for hours (seen in "SHOW FULL PROCESSLIST \g") ; so you can disable these special queries here: @@ -634,6 +639,7 @@ ; By enabling this flag we will for example not allow the installation of a plugin via the UI as a plugin would be only ; installed on one server or a config one change would be only made on one server instead of all servers. ; This flag doesn't need to be enabled when the config file is on a shared filesystem such as NFS or EFS. +; When enabled, Matomo will return the response code 200 instead of 503 in maintenance mode. multi_server_environment = 0 ; List of proxy headers for client IP addresses @@ -676,7 +682,8 @@ enable_trusted_host_check = 1 ; List of trusted hosts (eg domain or subdomain names) when generating absolute URLs. -; +; This only needs to be set for any hostnames that the Matomo UI will be accessed from. It is not necessary to set this +; for other additional hostnames (For example tracking, API, etc.) ; Examples: ;trusted_hosts[] = example.com ;trusted_hosts[] = stats.example.com @@ -922,6 +929,11 @@ ; By default, Matomo will remove the most common parameters which are known to change often (eg. session ID parameters) url_query_parameter_to_exclude_from_url = "gclid,fbclid,fb_xd_fragment,fb_comment_id,phpsessid,jsessionid,sessionid,aspsessionid,doing_wp_cron,sid,pk_vid" +; If set to 1, Matomo will use the default provider if no other provider is configured. +; In addition the default provider will be used as a fallback when the configure provider does not return any results. +; If set to 0, the default provider will be unavailable. Instead the "disabled" provider will be used as default and fallback instead. +enable_default_location_provider = 1 + ; if set to 1, Matomo attempts a "best guess" at the visitor's country of ; origin when the preferred language tag omits region information. ; The mapping is defined in core/DataFiles/LanguageToCountry.php, @@ -1229,4 +1241,4 @@ SDK_batch_size = 10 SDK_interval_value = 30 -; NOTE: do not directly edit this file! See notice at the top \ No newline at end of file +; NOTE: do not directly edit this file! See notice at the top diff --git a/app/config/global.php b/app/config/global.php index 1e43a2e47..f6fbe51e0 100644 --- a/app/config/global.php +++ b/app/config/global.php @@ -4,7 +4,7 @@ use Matomo\Cache\Eager; use Piwik\SettingsServer; -return array( +return [ 'path.root' => PIWIK_DOCUMENT_ROOT, @@ -35,6 +35,8 @@ 'view.clearcompiledtemplates.enable' => true, + 'twig.cache' => DI\string('{path.tmp.templates}'), + 'Matomo\Cache\Eager' => function (ContainerInterface $c) { $backend = $c->get('Matomo\Cache\Backend'); $cacheId = $c->get('cache.eager.cache_id'); @@ -58,7 +60,7 @@ // If Piwik is not installed yet, it's possible the tmp/ folder is not writable // we prevent failing with an unclear message eg. coming from doctrine-cache // by forcing to use a cache backend which always works ie. array - if(!\Piwik\SettingsPiwik::isMatomoInstalled()) { + if (!\Piwik\SettingsPiwik::isMatomoInstalled()) { $backend = 'array'; } else { try { @@ -71,10 +73,10 @@ return \Piwik\Cache::buildBackend($backend); }, 'cache.eager.cache_id' => function () { - return 'eagercache-' . str_replace(array('.', '-'), '', \Piwik\Version::VERSION) . '-'; + return 'eagercache-' . str_replace(['.', '-'], '', \Piwik\Version::VERSION) . '-'; }, - 'entities.idNames' => DI\add(array('idGoal', 'idDimension')), + 'entities.idNames' => DI\add(['idGoal', 'idDimension']), 'Psr\Log\LoggerInterface' => DI\create('Psr\Log\NullLogger'), @@ -83,19 +85,20 @@ 'DeviceDetector\Cache\Cache' => DI\autowire('Piwik\DeviceDetector\DeviceDetectorCache')->constructor(86400), - 'observers.global' => array(), + 'observers.global' => [], /** - * By setting this option to false, the check that the DB schema version matches the version of the source code will be no longer performed. - * Thus it allows you to execute for example a newer version of Matomo with an older Matomo database version. Please note - * disabling this setting is not recommended because often an older DB version is not compatible with newer source code. - * If you disable this setting, make sure to execute the updates after updating the source code. The setting can be useful if - * you want to update Matomo without any outage when you know the current source code update will still run fine for a short time - * while in the background the database updates are running. + * By setting this option to false, the check that the DB schema version matches the version of the source code will + * be no longer performed. Thus it allows you to execute for example a newer version of Matomo with an older Matomo + * database version. Please note disabling this setting is not recommended because often an older DB version is not + * compatible with newer source code. + * If you disable this setting, make sure to execute the updates after updating the source code. The setting can be + * useful if you want to update Matomo without any outage when you know the current source code update will still + * run fine for a short time while in the background the database updates are running. */ 'EnableDbVersionCheck' => true, - 'fileintegrity.ignore' => DI\add(array( + 'fileintegrity.ignore' => DI\add([ '*.htaccess', '*web.config', 'bootstrap.php', @@ -146,7 +149,7 @@ '*.gitattributes', '*.bower.json', '*.travis.yml', - )), + ]), 'Piwik\EventDispatcher' => DI\autowire()->constructorParameter('observers', DI\get('observers.global')), @@ -155,7 +158,7 @@ $config = $c->get('Piwik\Config'); $general = $config->General; - $ips = array(); + $ips = []; if (!empty($general['login_allowlist_ip']) && is_array($general['login_allowlist_ip'])) { $ips = $general['login_allowlist_ip']; } elseif (!empty($general['login_whitelist_ip']) && is_array($general['login_whitelist_ip'])) { @@ -163,7 +166,7 @@ $ips = $general['login_whitelist_ip']; } - $ipsResolved = array(); + $ipsResolved = []; foreach ($ips as $ip) { $ip = trim($ip); @@ -186,8 +189,10 @@ if (function_exists('dns_get_record')) { $entry = @dns_get_record($ip, DNS_AAAA); - if (!empty($entry['0']['ipv6']) - && filter_var($entry['0']['ipv6'], FILTER_VALIDATE_IP)) { + if ( + !empty($entry['0']['ipv6']) + && filter_var($entry['0']['ipv6'], FILTER_VALIDATE_IP) + ) { $resolvedIps[] = $entry['0']['ipv6']; } } @@ -216,15 +221,19 @@ ->constructorParameter('lookbackNSecondsCustom', DI\get('ini.Tracker.window_look_back_for_visitor')), 'Piwik\Tracker\Settings' => DI\autowire() - ->constructorParameter('isSameFingerprintsAcrossWebsites', DI\get('ini.Tracker.enable_fingerprinting_across_websites')), + ->constructorParameter( + 'isSameFingerprintsAcrossWebsites', + DI\get('ini.Tracker.enable_fingerprinting_across_websites') + ), 'archiving.performance.logger' => null, - \Piwik\CronArchive\Performance\Logger::class => DI\autowire()->constructorParameter('logger', DI\get('archiving.performance.logger')), + \Piwik\CronArchive\Performance\Logger::class => DI\autowire() + ->constructorParameter('logger', DI\get('archiving.performance.logger')), \Piwik\Concurrency\LockBackend::class => \DI\get(\Piwik\Concurrency\LockBackend\MySqlLockBackend::class), - \Piwik\Segment\SegmentsList::class => function(){ + \Piwik\Segment\SegmentsList::class => function () { return \Piwik\Segment\SegmentsList::get(); } -); +]; diff --git a/app/core/Access.php b/app/core/Access.php index b95e7f304..3a5b30626 100644 --- a/app/core/Access.php +++ b/app/core/Access.php @@ -616,7 +616,7 @@ public function checkUserHasCapability($idSites, $capability) foreach ($idSites as $idsite) { if (!in_array($idsite, $idSitesAccessible)) { - $this->throwNoAccessException(Piwik::translate('ExceptionCapabilityAccessWebsite', array("'" . $capability ."'", $idsite))); + $this->throwNoAccessException(Piwik::translate('General_ExceptionCapabilityAccessWebsite', array("'" . $capability ."'", $idsite))); } } diff --git a/app/core/Access/RolesProvider.php b/app/core/Access/RolesProvider.php index 4295e9b71..50df3fa1e 100644 --- a/app/core/Access/RolesProvider.php +++ b/app/core/Access/RolesProvider.php @@ -59,7 +59,7 @@ public function checkValidRole(string $roleId): void { if (!$this->isValidRole($roleId)) { $roles = $this->getAllRoleIds(); - throw new Exception(Piwik::translate("UsersManager_ExceptionAccessValues", implode(", ", $roles))); + throw new Exception(Piwik::translate("UsersManager_ExceptionAccessValues", [implode(", ", $roles), $roleId])); } } diff --git a/app/core/Archive.php b/app/core/Archive.php index 007ca09e5..09e6c3bb3 100644 --- a/app/core/Archive.php +++ b/app/core/Archive.php @@ -1,4 +1,5 @@ params = $params; $this->forceIndexedBySite = $forceIndexedBySite; $this->forceIndexedByDate = $forceIndexedByDate; @@ -210,8 +214,13 @@ public function __construct(Parameters $params, $forceIndexedBySite = false, */ public static function build($idSites, $period, $strDate, $segment = false, $_restrictSitesToLogin = false) { - return StaticContainer::get(ArchiveQueryFactory::class)->build($idSites, $period, $strDate, $segment, - $_restrictSitesToLogin); + return StaticContainer::get(ArchiveQueryFactory::class)->build( + $idSites, + $period, + $strDate, + $segment, + $_restrictSitesToLogin + ); } /** @@ -236,11 +245,20 @@ public static function build($idSites, $period, $strDate, $segment = false, $_re * * @return ArchiveQuery */ - public static function factory(Segment $segment, array $periods, array $idSites, $idSiteIsAll = false, - $isMultipleDate = false) - { - return StaticContainer::get(ArchiveQueryFactory::class)->factory($segment, $periods, $idSites, $idSiteIsAll, - $isMultipleDate); + public static function factory( + Segment $segment, + array $periods, + array $idSites, + $idSiteIsAll = false, + $isMultipleDate = false + ) { + return StaticContainer::get(ArchiveQueryFactory::class)->factory( + $segment, + $periods, + $idSites, + $idSiteIsAll, + $isMultipleDate + ); } public static function shouldSkipArchiveIfSkippingSegmentArchiveForToday(Site $site, Period $period, Segment $segment) @@ -277,7 +295,8 @@ public function getNumeric($names) $result = $data->getIndexedArray($resultIndices); // if only one metric is returned, just return it as a numeric value - if (empty($resultIndices) + if ( + empty($resultIndices) && count($result) <= 1 && (!is_array($names) || count($names) === 1) ) { @@ -410,7 +429,7 @@ public function getDataTableExpanded($name, $idSubtable = null, $depth = null, $ */ private function getRequestedPlugins($archiveNames) { - $result = array(); + $result = []; foreach ($archiveNames as $name) { $result[] = self::getPluginForReport($name); @@ -441,7 +460,7 @@ public function getParams() * @param string $segment @see {@link build()} * @param bool $expanded If true, loads all subtables. See {@link getDataTableExpanded()} * @param bool $flat If true, loads all subtables and disabled all recursive filters. - * @param int|null $idSubtable See {@link getDataTableExpanded()} + * @param int|null|string $idSubtable See {@link getDataTableExpanded()} * @param int|null $depth See {@link getDataTableExpanded()} * @return DataTable|DataTable\Map */ @@ -449,14 +468,19 @@ public static function createDataTableFromArchive($recordName, $idSite, $period, { Piwik::checkUserHasViewAccess($idSite); + if ($idSubtable === false || $idSubtable === '') { + $idSubtable = null; + } + + if (!empty($idSubtable) && (strtolower($idSubtable) !== self::ID_SUBTABLE_LOAD_ALL_SUBTABLES && !is_numeric($idSubtable))) { + throw new \Exception("idSubtable needs to be a number or '" . self::ID_SUBTABLE_LOAD_ALL_SUBTABLES . "', '$idSubtable' given."); + } + if ($flat && !$idSubtable) { $expanded = true; } $archive = Archive::build($idSite, $period, $date, $segment, $_restrictSitesToLogin = false); - if ($idSubtable === false) { - $idSubtable = null; - } if ($expanded) { $dataTable = $archive->getDataTableExpanded($recordName, $idSubtable, $depth); @@ -489,19 +513,20 @@ public static function createDataTableFromArchive($recordName, $idSite, $period, protected function get($archiveNames, $archiveDataType, $idSubtable = null) { if (!is_array($archiveNames)) { - $archiveNames = array($archiveNames); + $archiveNames = [$archiveNames]; } $archiveNames = array_filter($archiveNames); // apply idSubtable - if ($idSubtable !== null + if ( + $idSubtable !== null && $idSubtable !== self::ID_SUBTABLE_LOAD_ALL_SUBTABLES ) { // this is also done in ArchiveSelector. It should be actually only done in ArchiveSelector but DataCollection // does require to have the subtableId appended. Needs to be changed in refactoring to have it only in one // place. - $dataNames = array(); + $dataNames = []; foreach ($archiveNames as $name) { $dataNames[] = ArchiveSelector::appendIdsubtable($name, $idSubtable); } @@ -510,9 +535,15 @@ protected function get($archiveNames, $archiveDataType, $idSubtable = null) } $result = new Archive\DataCollection( - $dataNames, $archiveDataType, $this->params->getIdSites(), $this->params->getPeriods(), $this->params->getSegment(), $defaultRow = null); + $dataNames, + $archiveDataType, + $this->params->getIdSites(), + $this->params->getPeriods(), + $this->params->getSegment(), + $defaultRow = null + ); if (empty($dataNames)) { - return $result; // NOTE: note posting Archive.noArchivedData here, because there might be archive data, someone just requested nothing + return $result; // NOTE: not posting Archive.noArchivedData here, because there might be archive data, someone just requested nothing } $archiveIds = $this->getArchiveIds($archiveNames); @@ -560,8 +591,8 @@ private function getArchiveIds($archiveNames) // figure out which archives haven't been processed (if an archive has been processed, // then we have the archive IDs in $this->idarchives) - $doneFlags = array(); - $archiveGroups = array(); + $doneFlags = []; + $archiveGroups = []; foreach (array_merge($plugins, ['all']) as $plugin) { $doneFlag = $this->getDoneStringForPlugin($plugin, $this->params->getIdSites()); @@ -586,7 +617,8 @@ private function getArchiveIds($archiveNames) // cache id archives for plugins we haven't processed yet if (!empty($archiveGroups)) { - if (Rules::isArchivingEnabledFor($this->params->getIdSites(), $this->params->getSegment(), $this->getPeriodLabel()) + if ( + Rules::isArchivingEnabledFor($this->params->getIdSites(), $this->params->getSegment(), $this->getPeriodLabel()) && !$this->forceFetchingWithoutLaunchingArchiving ) { $this->cacheArchiveIdsAfterLaunching($archiveGroups, $plugins); @@ -610,16 +642,14 @@ private function getArchiveIds($archiveNames) */ private function cacheArchiveIdsAfterLaunching($archiveGroups, $plugins) { - $today = Date::today(); - foreach ($this->params->getPeriods() as $period) { - $twoDaysBeforePeriod = $period->getDateStart()->subDay(2); $twoDaysAfterPeriod = $period->getDateEnd()->addDay(2); foreach ($this->params->getIdSites() as $idSite) { $site = new Site($idSite); - if (Common::getRequestVar('skipArchiveSegmentToday', 0, 'int') + if ( + Common::getRequestVar('skipArchiveSegmentToday', 0, 'int') && self::shouldSkipArchiveIfSkippingSegmentArchiveForToday($site, $period, $this->params->getSegment()) ) { Log::debug("Skipping archive %s for %s as segment today is disabled", $period->getLabel(), $period->getPrettyString()); @@ -630,15 +660,26 @@ private function cacheArchiveIdsAfterLaunching($archiveGroups, $plugins) // we already know there are no stats for this period // we add one day to make sure we don't miss the day of the website creation if ($twoDaysAfterPeriod->isEarlier($site->getCreationDate())) { - Log::debug("Archive site %s, %s (%s) skipped, archive is before the website was created.", - $idSite, $period->getLabel(), $period->getPrettyString()); + Log::debug( + "Archive site %s, %s (%s) skipped, archive is before the website was created.", + $idSite, + $period->getLabel(), + $period->getPrettyString() + ); continue; } - // if the starting date is in the future we know there is no visiidsite = ?t - if ($twoDaysBeforePeriod->isLater($today)) { - Log::debug("Archive site %s, %s (%s) skipped, archive is after today.", - $idSite, $period->getLabel(), $period->getPrettyString()); + // Allow for site timezone, local time may have started a new day ahead of UTC + $today = \Piwik\Date::factory('now', $site->getTimezone()); + + // if the starting date is in the future we know there are no visits + if ($period->getDateStart()->isLater($today)) { + Log::debug( + "Archive site %s, %s (%s) skipped, archive is after today.", + $idSite, + $period->getLabel(), + $period->getPrettyString() + ); continue; } @@ -657,7 +698,11 @@ private function cacheArchiveIdsAfterLaunching($archiveGroups, $plugins) private function cacheArchiveIdsWithoutLaunching($plugins) { $idarchivesByReport = ArchiveSelector::getArchiveIds( - $this->params->getIdSites(), $this->params->getPeriods(), $this->params->getSegment(), $plugins); + $this->params->getIdSites(), + $this->params->getPeriods(), + $this->params->getSegment(), + $plugins + ); // initialize archive ID cache for each report foreach ($plugins as $plugin) { @@ -686,10 +731,10 @@ private function cacheArchiveIdsWithoutLaunching($plugins) private function getDoneStringForPlugin($plugin, $idSites) { return Rules::getDoneStringFlagFor( - $idSites, - $this->params->getSegment(), - $this->getPeriodLabel(), - $plugin + $idSites, + $this->params->getSegment(), + $this->getPeriodLabel(), + $plugin ); } @@ -707,15 +752,17 @@ private function getPeriodLabel() */ private function getResultIndices() { - $indices = array(); + $indices = []; - if (count($this->params->getIdSites()) > 1 + if ( + count($this->params->getIdSites()) > 1 || $this->forceIndexedBySite ) { $indices['site'] = 'idSite'; } - if (count($this->params->getPeriods()) > 1 + if ( + count($this->params->getPeriods()) > 1 || $this->forceIndexedByDate ) { $indices['period'] = 'date'; @@ -757,7 +804,7 @@ private function formatNumericValue($value) private function initializeArchiveIdCache($doneFlag) { if (!isset($this->idarchives[$doneFlag])) { - $this->idarchives[$doneFlag] = array(); + $this->idarchives[$doneFlag] = []; } } @@ -795,11 +842,11 @@ private function getArchiveGroupOfPlugin($plugin) */ public static function getPluginForReport($report) { - // Core metrics are always processed in Core, for the requested date/period/segment if (in_array($report, Metrics::getVisitsMetricNames())) { + // Core metrics are always processed in Core, for the requested date/period/segment $report = 'VisitsSummary_CoreMetrics'; - } // Goal_* metrics are processed by the Goals plugin (HACK) - elseif (strpos($report, 'Goal_') === 0) { + } elseif (strpos($report, 'Goal_') === 0) { + // Goal_* metrics are processed by the Goals plugin (HACK) $report = 'Goals_Metrics'; } elseif ( strrpos($report, '_returning') === strlen($report) - strlen('_returning') || @@ -809,7 +856,8 @@ public static function getPluginForReport($report) } $plugin = substr($report, 0, strpos($report, '_')); - if (empty($plugin) + if ( + empty($plugin) || !\Piwik\Plugin\Manager::getInstance()->isPluginActivated($plugin) ) { throw new \Exception("Error: The report '$report' was requested but it is not available at this stage." @@ -835,7 +883,7 @@ private function prepareArchive(array $archiveGroups, Site $site, Period $period $periodString = $period->getRangeString(); $periodDateStr = $period->getLabel() == 'range' ? $periodString : $period->getDateStart()->toString(); - $idSites = array($site->getId()); + $idSites = [$site->getId()]; // process for each plugin as well foreach ($archiveGroups as $plugin) { @@ -843,10 +891,16 @@ private function prepareArchive(array $archiveGroups, Site $site, Period $period $this->initializeArchiveIdCache($doneFlag); $prepareResult = $coreAdminHomeApi->archiveReports( - $site->getId(), $period->getLabel(), $periodDateStr, $this->params->getSegment()->getOriginalString(), - $plugin, $requestedReport); - - if (!empty($prepareResult) + $site->getId(), + $period->getLabel(), + $periodDateStr, + $this->params->getSegment()->getOriginalString(), + $plugin, + $requestedReport + ); + + if ( + !empty($prepareResult) && !empty($prepareResult['idarchives']) ) { foreach ($prepareResult['idarchives'] as $idArchive) { @@ -859,7 +913,7 @@ private function prepareArchive(array $archiveGroups, Site $site, Period $period private function getIdArchivesByMonth($doneFlags) { // order idarchives by the table month they belong to - $idArchivesByMonth = array(); + $idArchivesByMonth = []; foreach (array_keys($doneFlags) as $doneFlag) { if (empty($this->idarchives[$doneFlag])) { diff --git a/app/core/Archive/ArchiveInvalidator.php b/app/core/Archive/ArchiveInvalidator.php index e7c83cf7d..585c22dd8 100644 --- a/app/core/Archive/ArchiveInvalidator.php +++ b/app/core/Archive/ArchiveInvalidator.php @@ -30,7 +30,6 @@ use Piwik\SettingsServer; use Piwik\Site; use Piwik\Tracker\Cache; -use Piwik\Tracker\Model as TrackerModel; use Psr\Log\LoggerInterface; /** @@ -97,7 +96,7 @@ public function getAllRememberToInvalidateArchivedReportsLater() // we do not really have to get the value first. we could simply always try to call set() and it would update or // insert the record if needed but we do not want to lock the table (especially since there are still some // MyISAM installations) - $values = Option::getLike('%' . $this->rememberArchivedReportIdStart . '%'); + $values = Option::getLike('%' . str_replace('_', '\_', $this->rememberArchivedReportIdStart) . '%'); $all = []; foreach ($values as $name => $value) { @@ -122,7 +121,7 @@ public function rememberToInvalidateArchivedReportsLater($idSite, Date $date) // we do not really have to get the value first. we could simply always try to call set() and it would update or // insert the record if needed but we do not want to lock the table (especially since there are still some // MyISAM installations) - $value = Option::getLike('%' . $key . '%'); + $value = Option::getLike('%' . str_replace('_', '\_', $key) . '%'); } // getLike() returns an empty array rather than 'false' @@ -155,7 +154,7 @@ private function getRememberedArchivedReportsOptionFromTracker($idSite, $dateStr public function getRememberedArchivedReportsThatShouldBeInvalidated() { - $reports = Option::getLike('%' . $this->rememberArchivedReportIdStart . '%_%'); + $reports = Option::getLike('%' . str_replace('_', '\_', $this->rememberArchivedReportIdStart) . '%\_%'); $sitesPerDay = array(); @@ -205,9 +204,11 @@ private function buildRememberArchivedReportIdProcessSafe($idSite, $date) public function forgetRememberedArchivedReportsToInvalidateForSite($idSite) { - $id = $this->buildRememberArchivedReportIdForSite($idSite) . '\_'; - $this->deleteOptionLike($id); - Cache::clearCacheGeneral(); + $id = $this->buildRememberArchivedReportIdForSite($idSite) . '_'; + $hasDeletedSomething = $this->deleteOptionLike($id); + if ($hasDeletedSomething) { + Cache::clearCacheGeneral(); + } } /** @@ -221,17 +222,22 @@ public function forgetRememberedArchivedReportsToInvalidate($idSite, Date $date) // The process pid is added to the end of the entry in order to support multiple concurrent transactions. // So this must be a deleteLike call to get all the entries, where there used to only be one. - $this->deleteOptionLike($id); + return $this->deleteOptionLike($id); } + /** + * @param $id + * @return bool true if a record was deleted, false otherwise. + * @throws \Zend_Db_Statement_Exception + */ private function deleteOptionLike($id) { // we're not using deleteLike since it maybe could cause deadlocks see https://github.com/matomo-org/matomo/issues/15545 // we want to reduce number of rows scanned and only delete specific primary key - $keys = Option::getLike('%' . $id . '%'); + $keys = Option::getLike('%' . str_replace('_', '\_', $id) . '%'); if (empty($keys)) { - return; + return false; } $keys = array_keys($keys); @@ -239,7 +245,8 @@ private function deleteOptionLike($id) $placeholders = Common::getSqlStringFieldsArray($keys); $table = Common::prefixTable('option'); - Db::query('DELETE FROM `' . $table . '` WHERE `option_name` IN (' . $placeholders . ')', $keys); + $db = Db::query('DELETE FROM `' . $table . '` WHERE `option_name` IN (' . $placeholders . ')', $keys); + return (bool) $db->rowCount(); } /** @@ -251,6 +258,7 @@ private function deleteOptionLike($id) * @param bool $forceInvalidateNonexistantRanges set true to force inserting rows for ranges in archive_invalidations * @param string $name null to make sure every plugin is archived when this invalidation is processed by core:archive, * or a plugin name to only archive the specific plugin. + * @param bool $ignorePurgeLogDataDate * @return InvalidationResult * @throws \Exception */ @@ -281,6 +289,7 @@ public function markArchivesAsInvalidated(array $idSites, array $dates, $period, && count($dates) == 1 && ((string)$dates[0]) == ((string)Date::factoryInTimezone('today', $tz)) ) { + // date is for today $hasMoreThanJustToday[$idSite] = false; } } @@ -316,20 +325,27 @@ public function markArchivesAsInvalidated(array $idSites, array $dates, $period, $isInvalidatingDays = $period == 'day' || $cascadeDown || empty($period); $isNotInvalidatingSegment = empty($segment) || empty($segment->getString()); + if ($isInvalidatingDays && $isNotInvalidatingSegment ) { + + $hasDeletedAny = false; + foreach ($idSites as $idSite) { foreach ($dates as $date) { if (is_string($date)) { $date = Date::factory($date); } - $this->forgetRememberedArchivedReportsToInvalidate($idSite, $date); + $hasDeletedAny = $this->forgetRememberedArchivedReportsToInvalidate($idSite, $date) || $hasDeletedAny; } } + + if ($hasDeletedAny) { + Cache::clearCacheGeneral(); + } } - Cache::clearCacheGeneral(); return $invalidationInfo; } @@ -457,7 +473,7 @@ public function markArchivesOverlappingRangeAsInvalidated(array $idSites, array */ public function reArchiveReport($idSites, string $plugin = null, string $report = null, Date $startDate = null, Segment $segment = null) { - $date2 = Date::yesterday(); + $date2 = Date::today(); $earliestDateToRearchive = $this->getEarliestDateToRearchive(); if (empty($startDate)) { diff --git a/app/core/Archive/DataTableFactory.php b/app/core/Archive/DataTableFactory.php index d2c247944..0725fd1b5 100644 --- a/app/core/Archive/DataTableFactory.php +++ b/app/core/Archive/DataTableFactory.php @@ -11,7 +11,6 @@ use Piwik\DataTable; use Piwik\DataTable\Row; -use Piwik\Period; use Piwik\Segment; use Piwik\Site; diff --git a/app/core/ArchiveProcessor.php b/app/core/ArchiveProcessor.php index 09bfeb242..2fa72d2db 100644 --- a/app/core/ArchiveProcessor.php +++ b/app/core/ArchiveProcessor.php @@ -679,8 +679,9 @@ public function processDependentArchive($plugin, $segment) // vs here we would use // userId!@%40matomo.org;userId!=hello%40matomo.org;visitorType==new // thus these would result in different segment hashes and therefore the reports would either show 0 or archive the data twice - $newSegment = Segment::combine($params->getSegment()->getOriginalString(), SegmentExpression::AND_DELIMITER, $segment); - if ($newSegment === $segment && $params->getRequestedPlugin() === $plugin) { // being processed now + $originSegmentString = $params->getSegment()->getOriginalString(); + $newSegment = Segment::combine($originSegmentString, SegmentExpression::AND_DELIMITER, $segment); + if (!empty($originSegmentString) && $newSegment === $segment && $params->getRequestedPlugin() === $plugin) { // being processed now return; } diff --git a/app/core/ArchiveProcessor/ArchivingStatus.php b/app/core/ArchiveProcessor/ArchivingStatus.php index 54dfae9da..5f0bd763b 100644 --- a/app/core/ArchiveProcessor/ArchivingStatus.php +++ b/app/core/ArchiveProcessor/ArchivingStatus.php @@ -9,11 +9,9 @@ namespace Piwik\ArchiveProcessor; -use Piwik\Common; use Piwik\Concurrency\Lock; use Piwik\Concurrency\LockBackend; use Piwik\Container\StaticContainer; -use Piwik\SettingsPiwik; class ArchivingStatus { diff --git a/app/core/ArchiveProcessor/Loader.php b/app/core/ArchiveProcessor/Loader.php index d99a5ea0f..00d834a8e 100644 --- a/app/core/ArchiveProcessor/Loader.php +++ b/app/core/ArchiveProcessor/Loader.php @@ -468,6 +468,10 @@ public function canSkipArchiveForSegment() return false; } + if (!empty($params->getRequestedPlugin()) && Rules::isSegmentPluginArchivingDisabled($params->getRequestedPlugin(), $params->getSite()->getId())) { + return true; + } + /** @var SegmentArchiving */ $segmentArchiving = StaticContainer::get(SegmentArchiving::class); $segmentInfo = $segmentArchiving->findSegmentForHash($params->getSegment()->getHash(), $params->getSite()->getId()); diff --git a/app/core/ArchiveProcessor/LoaderLock.php b/app/core/ArchiveProcessor/LoaderLock.php index 3ab8d3f00..fca94a554 100644 --- a/app/core/ArchiveProcessor/LoaderLock.php +++ b/app/core/ArchiveProcessor/LoaderLock.php @@ -18,6 +18,10 @@ class LoaderLock const MAX_LOCK_TIME = 60; //in seconds protected $id; + /** + * @param string $id + * @throws \Exception + */ public function __construct($id) { // instanceId is needed for multi tenant database solution diff --git a/app/core/ArchiveProcessor/Parameters.php b/app/core/ArchiveProcessor/Parameters.php index c32817063..4d9975ca9 100644 --- a/app/core/ArchiveProcessor/Parameters.php +++ b/app/core/ArchiveProcessor/Parameters.php @@ -9,9 +9,6 @@ namespace Piwik\ArchiveProcessor; -use Piwik\Cache; -use Piwik\DataAccess\Model; -use Piwik\DataAccess\RawLogDao; use Piwik\Date; use Piwik\Log; use Piwik\Period; diff --git a/app/core/ArchiveProcessor/Rules.php b/app/core/ArchiveProcessor/Rules.php index c31c3bf4f..d1ff2e088 100644 --- a/app/core/ArchiveProcessor/Rules.php +++ b/app/core/ArchiveProcessor/Rules.php @@ -9,17 +9,15 @@ namespace Piwik\ArchiveProcessor; use Exception; -use Piwik\Common; use Piwik\Config; +use Piwik\Config\GeneralConfig; use Piwik\DataAccess\ArchiveWriter; -use Piwik\DataAccess\Model; use Piwik\Date; use Piwik\Log; use Piwik\Option; use Piwik\Piwik; use Piwik\Plugin\Manager; use Piwik\Plugins\CoreAdminHome\Controller; -use Piwik\Scheduler\Task; use Piwik\Segment; use Piwik\SettingsPiwik; use Piwik\SettingsServer; @@ -58,7 +56,7 @@ class Rules public static function getDoneStringFlagFor(array $idSites, $segment, $periodLabel, $plugin) { if (!empty($plugin) - && !self::shouldProcessReportsAllPlugins($idSites, $segment, $periodLabel) + && !self::shouldProcessReportsAllPlugins($idSites, $segment, $periodLabel) ) { return self::getDoneFlagArchiveContainsOnePlugin($segment, $plugin); } @@ -101,7 +99,7 @@ public static function getSegmentsToProcess($idSites) public static function getDoneFlagArchiveContainsOnePlugin(Segment $segment, $plugin) { - return 'done' . $segment->getHash() . '.' . $plugin ; + return 'done' . $segment->getHash() . '.' . $plugin; } public static function getDoneFlagArchiveContainsAllPlugins(Segment $segment) @@ -131,8 +129,11 @@ public static function getDoneFlags(array $plugins, Segment $segment) } public static function getMinTimeProcessedForInProgressArchive( - Date $dateStart, \Piwik\Period $period, Segment $segment, Site $site) - { + Date $dateStart, + \Piwik\Period $period, + Segment $segment, + Site $site + ) { $todayArchiveTimeToLive = self::getPeriodArchiveTimeToLiveDefault($period->getLabel()); $now = time(); @@ -142,7 +143,7 @@ public static function getMinTimeProcessedForInProgressArchive( $isArchivingDisabled = Rules::isArchivingDisabledFor($idSites, $segment, $period->getLabel()); if ($isArchivingDisabled) { if ($period->getNumberOfSubperiods() == 0 - && $dateStart->getTimestamp() <= $now + && $dateStart->getTimestamp() <= $now ) { // Today: accept any recent enough archive $minimumArchiveTime = false; @@ -150,7 +151,8 @@ public static function getMinTimeProcessedForInProgressArchive( // This week, this month, this year: // accept any archive that was processed today after 00:00:01 this morning $timezone = $site->getTimezone(); - $minimumArchiveTime = Date::factory(Date::factory('now', $timezone)->getDateStartUTC())->setTimezone($timezone)->getTimestamp(); + $minimumArchiveTime = Date::factory(Date::factory('now', + $timezone)->getDateStartUTC())->setTimezone($timezone)->getTimestamp(); } } return $minimumArchiveTime; @@ -214,7 +216,7 @@ public static function isArchivingEnabledFor(array $idSites, Segment $segment, $ if ($periodLabel === 'range') { if (isset($generalConfig['archiving_range_force_on_browser_request']) - && $generalConfig['archiving_range_force_on_browser_request'] == false + && $generalConfig['archiving_range_force_on_browser_request'] == false ) { Log::debug("Not forcing archiving for range period."); return $isArchivingEnabled; @@ -229,8 +231,8 @@ public static function isArchivingEnabledFor(array $idSites, Segment $segment, $ } if (!$isArchivingEnabled - && (!self::isBrowserArchivingAvailableForSegments() || self::isSegmentPreProcessed($idSites, $segment)) - && !SettingsServer::isArchivePhpTriggered() // Only applies when we are not running core:archive command + && (!self::isBrowserArchivingAvailableForSegments() || self::isSegmentPreProcessed($idSites, $segment)) + && !SettingsServer::isArchivePhpTriggered() // Only applies when we are not running core:archive command ) { Log::debug("Archiving is disabled because of config setting browser_archiving_disabled_enforce=1 or because the segment is selected to be pre-processed."); return false; @@ -315,7 +317,7 @@ public static function isSegmentPreProcessed(array $idSites, Segment $segment) $segmentsToProcessUrlDecoded = array_map('urldecode', $segmentsToProcess); return in_array($segment, $segmentsToProcess) - || in_array($segment, $segmentsToProcessUrlDecoded); + || in_array($segment, $segmentsToProcessUrlDecoded); } /** @@ -323,8 +325,11 @@ public static function isSegmentPreProcessed(array $idSites, Segment $segment) * * @return string[] */ - public static function getSelectableDoneFlagValues($includeInvalidated = true, Parameters $params = null, $checkAuthorizedToArchive = true) - { + public static function getSelectableDoneFlagValues( + $includeInvalidated = true, + Parameters $params = null, + $checkAuthorizedToArchive = true + ) { $possibleValues = array(ArchiveWriter::DONE_OK, ArchiveWriter::DONE_OK_TEMPORARY); if ($includeInvalidated) { @@ -356,4 +361,24 @@ public static function shouldProcessSegmentsWhenReArchivingReports() { return Config::getInstance()->General['rearchive_reports_in_past_exclude_segments'] != 1; } + + public static function isSegmentPluginArchivingDisabled($pluginName, $siteId = null) + { + $pluginArchivingSetting = GeneralConfig::getConfigValue('disable_archiving_segment_for_plugins', $siteId); + + if (empty($pluginArchivingSetting)) { + return false; + } + + if (is_string($pluginArchivingSetting)) { + $pluginArchivingSetting = explode(",", $pluginArchivingSetting); + $pluginArchivingSetting = array_filter($pluginArchivingSetting, function($plugin){ + return Manager::getInstance()->isValidPluginName($plugin); + }); + } + + $pluginArchivingSetting = array_map('strtolower', $pluginArchivingSetting); + + return in_array(strtolower($pluginName), $pluginArchivingSetting); + } } diff --git a/app/core/AssetManager.php b/app/core/AssetManager.php index 3e174718e..1eb8820f0 100644 --- a/app/core/AssetManager.php +++ b/app/core/AssetManager.php @@ -16,6 +16,7 @@ use Piwik\AssetManager\UIAssetFetcher\JScriptUIAssetFetcher; use Piwik\AssetManager\UIAssetFetcher\StaticUIAssetFetcher; use Piwik\AssetManager\UIAssetFetcher\StylesheetUIAssetFetcher; +use Piwik\AssetManager\UIAssetFetcher\PluginUmdAssetFetcher; use Piwik\AssetManager\UIAssetFetcher; use Piwik\AssetManager\UIAssetMerger\JScriptUIAssetMerger; use Piwik\AssetManager\UIAssetMerger\StylesheetUIAssetMerger; @@ -44,9 +45,11 @@ class AssetManager extends Singleton const CSS_IMPORT_DIRECTIVE = "\n"; const JS_IMPORT_DIRECTIVE = "\n"; + const JS_DEFER_IMPORT_DIRECTIVE = "\n"; const GET_CSS_MODULE_ACTION = "index.php?module=Proxy&action=getCss"; const GET_CORE_JS_MODULE_ACTION = "index.php?module=Proxy&action=getCoreJs"; const GET_NON_CORE_JS_MODULE_ACTION = "index.php?module=Proxy&action=getNonCoreJs"; + const GET_JS_UMD_MODULE_ACTION = "index.php?module=Proxy&action=getUmdJs&chunk="; /** * @var UIAssetCacheBuster @@ -149,11 +152,27 @@ public function getJsInclusionDirective() } else { $result .= sprintf(self::JS_IMPORT_DIRECTIVE, self::GET_CORE_JS_MODULE_ACTION); $result .= sprintf(self::JS_IMPORT_DIRECTIVE, self::GET_NON_CORE_JS_MODULE_ACTION); + + $result .= $this->getPluginUmdChunks(); } return $result; } + protected function getPluginUmdChunks() + { + $fetcher = $this->getPluginUmdJScriptFetcher(); + + $chunks = $fetcher->getChunkFiles(); + + $result = ''; + foreach ($chunks as $chunk) { + $src = self::GET_JS_UMD_MODULE_ACTION . urlencode($chunk->getChunkName()); + $result .= sprintf(self::JS_DEFER_IMPORT_DIRECTIVE, $src); + } + return $result; + } + /** * Return the base.less compiled to css * @@ -212,7 +231,22 @@ public function getMergedNonCoreJavaScript() } /** - * @param boolean $core + * Return a chunk JS merged file absolute location. + * If there is none, the generation process will be triggered. + * + * @param string $chunk The name of the chunk. Will either be a plugin name or an integer. + * @return UIAsset + */ + public function getMergedJavaScriptChunk($chunk) + { + $assetFetcher = $this->getPluginUmdJScriptFetcher($chunk); + $outputFile = $assetFetcher->getRequestedChunkOutputFile(); + + return $this->getMergedJavascript($assetFetcher, $this->getMergedUIAsset($outputFile)); + } + + /** + * @param boolean|"all" $core * @return string[] */ public function getLoadedPlugins($core) @@ -223,7 +257,7 @@ public function getLoadedPlugins($core) $pluginName = $plugin->getPluginName(); $pluginIsCore = Manager::getInstance()->isPluginBundledWithCore($pluginName); - if (($pluginIsCore && $core) || (!$pluginIsCore && !$core)) { + if ($core === 'all' || ($pluginIsCore && $core) || (!$pluginIsCore && !$core)) { $loadedPlugins[] = $pluginName; } } @@ -245,10 +279,40 @@ public function removeMergedAssets($pluginName = false) } else { $assetsToRemove[] = $this->getMergedNonCoreJSAsset(); } + + $assetFetcher = $this->getPluginUmdJScriptFetcher(); + foreach ($assetFetcher->getChunkFiles() as $chunk) { + $files = $chunk->getFiles(); + + $foundInChunk = false; + foreach ($files as $file) { + if (strpos($file, "/$pluginName.umd.") !== false) { + $foundInChunk = true; + } + } + + if ($foundInChunk) { + $outputFile = $chunk->getOutputFile(); + $asset = $this->getMergedUIAsset($outputFile); + if ($asset->exists()) { + $assetsToRemove[] = $asset; + } + break; + } + } } } else { $assetsToRemove[] = $this->getMergedCoreJSAsset(); $assetsToRemove[] = $this->getMergedNonCoreJSAsset(); + + $assetFetcher = $this->getPluginUmdJScriptFetcher(); + foreach ($assetFetcher->getChunkFiles() as $chunk) { + $outputFile = $chunk->getOutputFile(); + $asset = $this->getMergedUIAsset($outputFile); + if ($asset->exists()) { + $assetsToRemove[] = $asset; + } + } } $this->removeAssets($assetsToRemove); @@ -285,11 +349,11 @@ public function isMergedAssetsDisabled() if (Config::getInstance()->Development['disable_merged_assets'] == 1) { return true; } - + if (isset($_GET['disable_merged_assets']) && $_GET['disable_merged_assets'] == 1) { return true; } - + return false; } @@ -316,7 +380,8 @@ protected function getIndividualCoreAndNonCoreJsIncludes() { return $this->getIndividualJsIncludesFromAssetFetcher($this->getCoreJScriptFetcher()) . - $this->getIndividualJsIncludesFromAssetFetcher($this->getNonCoreJScriptFetcher()); + $this->getIndividualJsIncludesFromAssetFetcher($this->getNonCoreJScriptFetcher()) . + $this->getIndividualJsIncludesFromAssetFetcher($this->getPluginUmdJScriptFetcher()); } /** @@ -347,6 +412,11 @@ protected function getNonCoreJScriptFetcher() return new JScriptUIAssetFetcher($this->getLoadedPlugins(false), $this->theme); } + protected function getPluginUmdJScriptFetcher($chunk = null) + { + return new PluginUmdAssetFetcher($this->getLoadedPlugins('all'), $this->theme, $chunk); + } + /** * @param string $pluginName * @return boolean @@ -440,12 +510,12 @@ public static function compileCustomJs($files) { $mergedAsset = new InMemoryUIAsset(); $fetcher = new StaticUIAssetFetcher($files, $priorityOrder = array(), $theme = null); - + $cacheBuster = UIAssetCacheBuster::getInstance(); $assetMerger = new JScriptUIAssetMerger($mergedAsset, $fetcher, $cacheBuster); $assetMerger->generateFile(); - + return $mergedAsset->getContent(); } } diff --git a/app/core/AssetManager/UIAsset.php b/app/core/AssetManager/UIAsset.php index cc26ba956..012d5ab1e 100644 --- a/app/core/AssetManager/UIAsset.php +++ b/app/core/AssetManager/UIAsset.php @@ -8,7 +8,6 @@ */ namespace Piwik\AssetManager; -use Exception; abstract class UIAsset { diff --git a/app/core/AssetManager/UIAssetFetcher/Chunk.php b/app/core/AssetManager/UIAssetFetcher/Chunk.php new file mode 100644 index 000000000..db42428ba --- /dev/null +++ b/app/core/AssetManager/UIAssetFetcher/Chunk.php @@ -0,0 +1,69 @@ +chunkName = $chunkName; + $this->files = $files; + } + + /** + * @return string + */ + public function getOutputFile(): string + { + return "asset_manager_chunk.{$this->chunkName}.js"; + } + + /** + * @return string[] + */ + public function getFiles(): array + { + return $this->files; + } + + /** + * @param string[] $files + */ + public function setFiles(array $files): void + { + $this->files = $files; + } + + /** + * @return string + */ + public function getChunkName(): string + { + return $this->chunkName; + } + + /** + * @param string $chunkName + */ + public function setChunkName(string $chunkName): void + { + $this->chunkName = $chunkName; + } +} \ No newline at end of file diff --git a/app/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php b/app/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php index 84484ab0d..4248db939 100644 --- a/app/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php +++ b/app/core/AssetManager/UIAssetFetcher/JScriptUIAssetFetcher.php @@ -9,7 +9,6 @@ namespace Piwik\AssetManager\UIAssetFetcher; use Piwik\AssetManager\UIAssetFetcher; -use Piwik\Development; use Piwik\Piwik; class JScriptUIAssetFetcher extends UIAssetFetcher @@ -18,7 +17,6 @@ class JScriptUIAssetFetcher extends UIAssetFetcher protected function retrieveFileLocations() { if (!empty($this->plugins)) { - /** * Triggered when gathering the list of all JavaScript files needed by Piwik * and its plugins. @@ -44,8 +42,6 @@ protected function retrieveFileLocations() * @param string[] $jsFiles The JavaScript files to load. */ Piwik::postEvent('AssetManager.getJavaScriptFiles', array(&$this->fileLocations), null, $this->plugins); - - $this->addUmdFilesIfDetected($this->plugins); } $this->addThemeFiles(); @@ -74,8 +70,10 @@ protected function getPriorityOrder() { return array( 'node_modules/jquery/dist/jquery.min.js', + 'node_modules/jquery/dist/jquery.js', 'node_modules/materialize-css/dist/js/materialize.min.js', // so jquery ui datepicker overrides materializecss 'node_modules/jquery-ui-dist/jquery-ui.min.js', + 'node_modules/jquery-ui-dist/jquery-ui.js', "plugins/CoreHome/javascripts/materialize-bc.js", "node_modules/jquery.browser/dist/jquery.browser.min.js", 'node_modules/', @@ -94,18 +92,4 @@ protected function getPriorityOrder() 'tests/', ); } - - private function addUmdFilesIfDetected($plugins) - { - foreach ($plugins as $plugin) { - $devUmd = "plugins/$plugin/vue/dist/$plugin.development.umd.js"; - $minifiedUmd = "plugins/$plugin/vue/dist/$plugin.umd.min.js"; - - if (Development::isEnabled() && is_file(PIWIK_INCLUDE_PATH . '/' . $devUmd)) { - $this->fileLocations[] = $devUmd; - } else if (is_file(PIWIK_INCLUDE_PATH . '/' . $minifiedUmd)) { - $this->fileLocations[] = $minifiedUmd; - } - } - } } diff --git a/app/core/AssetManager/UIAssetFetcher/PluginUmdAssetFetcher.php b/app/core/AssetManager/UIAssetFetcher/PluginUmdAssetFetcher.php new file mode 100644 index 000000000..9ac1edaec --- /dev/null +++ b/app/core/AssetManager/UIAssetFetcher/PluginUmdAssetFetcher.php @@ -0,0 +1,297 @@ +requestedChunk = $chunk; + $this->loadIndividually = $loadIndividually; + $this->chunkCount = $chunkCount; + + if (!$this->loadIndividually && (!is_int($chunkCount) || $chunkCount <= 0)) { + throw new \Exception("Invalid chunk count: $chunkCount"); + } + } + + public function getRequestedChunkOutputFile() + { + return "asset_manager_chunk.{$this->requestedChunk}.js"; + } + + /** + * @return Chunk[] + */ + public function getChunkFiles() + { + $allPluginUmds = $this->getAllPluginUmds(); + + if ($this->loadIndividually) { + return $allPluginUmds; + } + + $totalSize = $this->getTotalChunkSize($allPluginUmds); + + $chunkFiles = $this->dividePluginUmdsByChunkCount($allPluginUmds, $totalSize); + + $chunks = []; + foreach ($chunkFiles as $index => $jsFiles) { + $chunks[] = new Chunk($index, $jsFiles); + } + return $chunks; + } + + private function getTotalChunkSize($allPluginUmds) + { + $totalSize = 0; + foreach ($allPluginUmds as $chunk) { + $path = PIWIK_INCLUDE_PATH . '/' . $chunk->getFiles()[0]; + if (is_file($path)) { + $totalSize += filesize($path); + } + } + return $totalSize; + } + + private function getAllPluginUmds() + { + $plugins = self::orderPluginsByPluginDependencies($this->plugins, false); + + $allPluginUmds = []; + foreach ($plugins as $plugin) { + $pluginDir = self::getRelativePluginDirectory($plugin); + $minifiedUmd = "$pluginDir/vue/dist/$plugin.umd.min.js"; + if (!is_file(PIWIK_INCLUDE_PATH . '/' . $minifiedUmd)) { + continue; + } + + $allPluginUmds[] = new Chunk($plugin, [$minifiedUmd]); + } + return $allPluginUmds; + } + + private function dividePluginUmdsByChunkCount($allPluginUmds, $totalSize) + { + $chunkSizeLimit = floor($totalSize / $this->chunkCount); + + $chunkFiles = []; + + $currentChunkIndex = 0; + $currentChunkSize = 0; + foreach ($allPluginUmds as $pluginChunk) { + $path = PIWIK_INCLUDE_PATH . '/' . $pluginChunk->getFiles()[0]; + if (!is_file($path)) { + continue; + } + + $size = filesize($path); + $currentChunkSize += $size; + + if ($currentChunkSize > $chunkSizeLimit + && !empty($chunkFiles[$currentChunkIndex]) + && $currentChunkIndex < $this->chunkCount - 1 + ) { + ++$currentChunkIndex; + $currentChunkSize = $size; + } + + $chunkFiles[$currentChunkIndex][] = $pluginChunk->getFiles()[0]; + } + + return $chunkFiles; + } + + protected function retrieveFileLocations() + { + if (empty($this->plugins)) { + return; + } + + if ($this->requestedChunk !== null && $this->requestedChunk !== '') { + $chunkFiles = $this->getChunkFiles(); + + $foundChunk = null; + foreach ($chunkFiles as $chunk) { + if ($chunk->getChunkName() == $this->requestedChunk) { + $foundChunk = $chunk; + break; + } + } + + if (!$foundChunk) { + throw new \Exception("Could not find chunk {$this->requestedChunk}"); + } + + foreach ($foundChunk->getFiles() as $file) { + $this->fileLocations[] = $file; + } + + return; + } + + // either loadFilesIndividually = true, or being called w/ disable_merged_assets=1 + $this->addUmdFilesIfDetected($this->plugins); + } + + private function addUmdFilesIfDetected($plugins) + { + $plugins = self::orderPluginsByPluginDependencies($plugins, false); + + foreach ($plugins as $plugin) { + $fileLocation = self::getUmdFileToUseForPlugin($plugin); + if ($fileLocation) { + $this->fileLocations[] = $fileLocation; + } + } + } + + public static function getUmdFileToUseForPlugin($plugin) + { + $pluginDir = self::getRelativePluginDirectory($plugin); + + $devUmd = "$pluginDir/vue/dist/$plugin.development.umd.js"; + $minifiedUmd = "$pluginDir/vue/dist/$plugin.umd.min.js"; + $umdSrcFolder = "$pluginDir/vue/src"; + + // in case there are dist files but no src files, which can happen during development + if (is_dir(PIWIK_INCLUDE_PATH . '/' . $umdSrcFolder)) { + if (Development::isEnabled() && is_file(PIWIK_INCLUDE_PATH . '/' . $devUmd)) { + return $devUmd; + } else if (is_file(PIWIK_INCLUDE_PATH . '/' . $minifiedUmd)) { + return $minifiedUmd; + } + } + + return null; + } + + public static function orderPluginsByPluginDependencies($plugins, $keepUnresolved = true) + { + $result = []; + + while (!empty($plugins)) { + self::visitPlugin(reset($plugins), $keepUnresolved, $plugins, $result); + } + + return $result; + } + + public static function getPluginDependencies($plugin) + { + $pluginDir = self::getPluginDirectory($plugin); + $umdMetadata = "$pluginDir/vue/dist/umd.metadata.json"; + + $cache = Cache::getTransientCache(); + $cacheKey = 'PluginUmdAssetFetcher.pluginDependencies.' . $plugin; + + $pluginDependencies = $cache->fetch($cacheKey); + if (!is_array($pluginDependencies)) { + $pluginDependencies = []; + if (is_file($umdMetadata)) { + $pluginDependencies = json_decode(file_get_contents($umdMetadata), true); + $pluginDependencies = $pluginDependencies['dependsOn'] ?? []; + } + $cache->save($cacheKey, $pluginDependencies); + } + return $cache->fetch($cacheKey); + } + + private static function visitPlugin($plugin, $keepUnresolved, &$plugins, &$result) + { + // remove the plugin from the array of plugins to visit + $index = array_search($plugin, $plugins); + if ($index !== false) { + unset($plugins[$index]); + } else { + return; // already visited + } + + // read the plugin dependencies, if any + $pluginDependencies = self::getPluginDependencies($plugin); + + if (!empty($pluginDependencies)) { + // visit each plugin this one depends on first, so it is loaded first + foreach ($pluginDependencies as $pluginDependency) { + // check if dependency is not activated + if (!in_array($pluginDependency, $plugins) + && !in_array($pluginDependency, $result) + && !$keepUnresolved + ) { + return; + } + + self::visitPlugin($pluginDependency, $keepUnresolved, $plugins, $result); + } + } + + // add the plugin to the load order after visiting its dependencies + $result[] = $plugin; + } + + protected function getPriorityOrder() + { + // the JS files are already ordered properly so this result doesn't matter + return []; + } + + private static function getRelativePluginDirectory($plugin) + { + $result = self::getPluginDirectory($plugin); + $result = str_replace(PIWIK_INCLUDE_PATH . '/', '', $result); + return $result; + } + + private static function getPluginDirectory($plugin) + { + return Manager::getInstance()->getPluginDirectory($plugin); + } + + public static function getDefaultLoadIndividually() + { + return (Config::getInstance()->General['assets_umd_load_individually'] ?? 0) == 1; + } + + public static function getDefaultChunkCount() + { + return (int)(Config::getInstance()->General['assets_umd_chunk_count'] ?? 3); + } +} \ No newline at end of file diff --git a/app/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php b/app/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php index cca95ef38..68004330e 100644 --- a/app/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php +++ b/app/core/AssetManager/UIAssetFetcher/StylesheetUIAssetFetcher.php @@ -9,7 +9,6 @@ namespace Piwik\AssetManager\UIAssetFetcher; use Piwik\AssetManager\UIAssetFetcher; -use Piwik\Development; use Piwik\Piwik; class StylesheetUIAssetFetcher extends UIAssetFetcher diff --git a/app/core/Changes/Model.php b/app/core/Changes/Model.php new file mode 100644 index 000000000..0988ca96d --- /dev/null +++ b/app/core/Changes/Model.php @@ -0,0 +1,218 @@ +db = ($db ?? Db::get()); + $this->pluginManager = ($pluginManager ?? PluginManager::getInstance()); + } + + /** + * Add any new changes for a plugin to the changes table + * + * @param string $pluginName + * + * @throws \Exception + */ + public function addChanges(string $pluginName): void + { + if ($this->pluginManager->isValidPluginName($pluginName) && $this->pluginManager->isPluginInFilesystem($pluginName)) { + + $plugin = $this->pluginManager->loadPlugin($pluginName); + if (!$plugin) { + return; + } + + $changes = $plugin->getChanges(); + foreach ($changes as $change) { + $this->addChange($pluginName, $change); + } + } + } + + /** + * Remove all changes for a plugin + * + * @param string $pluginName + */ + public function removeChanges(string $pluginName): void + { + $table = Common::prefixTable('changes'); + + try { + $this->db->query("DELETE FROM " . $table . " WHERE plugin_name = ?", [$pluginName]); + } catch (\Exception $e) { + if (Db::get()->isErrNo($e, Migration\Db::ERROR_CODE_TABLE_NOT_EXISTS)) { + return; + } + throw $e; + } + } + + /** + * Add a change item to the database table + * + * @param string $pluginName + * @param array $change + */ + public function addChange(string $pluginName, array $change): void + { + if(!isset($change['version']) || !isset($change['title']) || !isset($change['description'])) { + StaticContainer::get('Psr\Log\LoggerInterface')->warning( + "Change item for plugin {plugin} missing version, title or description fields - ignored", + ['plugin' => $pluginName]); + return; + } + + $table = Common::prefixTable('changes'); + + $fields = ['created_time', 'plugin_name', 'version', 'title', 'description']; + $params = [Date::now()->getDatetime(), $pluginName, $change['version'], $change['title'], $change['description']]; + + if (isset($change['link_name']) && isset($change['link'])) { + $fields[] = 'link_name'; + $fields[] = 'link'; + $params[] = $change['link_name']; + $params[] = $change['link']; + } + + $insertSql = 'INSERT IGNORE INTO ' . $table . ' ('.implode(',', $fields).') + VALUES ('.Common::getSqlStringFieldsArray($params).')'; + + try { + $this->db->query($insertSql, $params); + } catch (\Exception $e) { + if (Db::get()->isErrNo($e, Migration\Db::ERROR_CODE_TABLE_NOT_EXISTS)) { + return; + } + throw $e; + } + } + + /** + * Check if any changes items exist + * + * @param int|null $newerThanId Only count new changes as having a key > than this sequential key + * + * @return int + */ + public function doChangesExist(?int $newerThanId = null): int + { + $changes = $this->getChangeItems(); + + $all = 0; + $new = 0; + foreach ($changes as $c) { + $all++; + if ($newerThanId !== null && isset($c['idchange']) && $c['idchange'] > $newerThanId) { + $new++; + } + } + + if ($all === 0) { + return self::NO_CHANGES_EXIST; + } else if ($all > 0 && $new === 0) { + return self::CHANGES_EXIST; + } else { + return self::NEW_CHANGES_EXIST; + } + } + + /** + * Return an array of change items from the changes table + * + * @return array + * @throws DbException + */ + public function getChangeItems(): array + { + $showAtLeast = 10; // Always show at least this number of changes + $expireOlderThanDays = 90; // Don't show changes that were added to the table more than x days ago + + $table = Common::prefixTable('changes'); + $selectSql = "SELECT * FROM " . $table . " WHERE title IS NOT NULL ORDER BY idchange DESC"; + + try { + $changes = $this->db->fetchAll($selectSql); + } catch (\Exception $e) { + if (Db::get()->isErrNo($e, Migration\Db::ERROR_CODE_TABLE_NOT_EXISTS)) { + return []; + } + throw $e; + } + + // Remove expired changes, only if there are at more than the minimum changes + $cutOffDate = Date::now()->subDay($expireOlderThanDays); + foreach ($changes as $k => $change) { + if (isset($change['idchange'])) { + $changes[$k]['idchange'] = (int)$change['idchange']; + } + if (count($changes) > $showAtLeast && $change['created_time'] < $cutOffDate) { + unset($changes[$k]); + } + } + + /** + * Event triggered before changes are displayed + * + * Can be used to filter out unwanted changes + * + * **Example** + * + * Piwik::addAction('Changes.filterChanges', function ($changes) { + * foreach ($changes as $k => $c) { + * // Hide changes for the CoreHome plugin + * if (isset($c['plugin_name']) && $c['plugin_name'] == 'CoreHome') { + * unset($changes[$k]); + * } + * } + * }); + * + * @param array &$changes + */ + Piwik::postEvent('Changes.filterChanges', array(&$changes)); + + return $changes; + } + +} diff --git a/app/core/Changes/UserChanges.php b/app/core/Changes/UserChanges.php new file mode 100644 index 000000000..5e971c776 --- /dev/null +++ b/app/core/Changes/UserChanges.php @@ -0,0 +1,77 @@ +db = ($db ?? Db::get()); + $this->user = $user; + } + + /** + * Return a value indicating if there are any changes available to show the user + * + * @return int Changes\Model::NO_CHANGES_EXIST, Changes\Model::CHANGES_EXIST or Changes\Model::NEW_CHANGES_EXIST + * @throws \Exception + */ + public function getNewChangesStatus(): int + { + $idchangeLastViewed = (isset($this->user['idchange_last_viewed']) ? $this->user['idchange_last_viewed'] : null); + + $changesModel = new ChangesModel($this->db); + return $changesModel->doChangesExist($idchangeLastViewed); + } + + /** + * Return an array of changes and update the user's changes last viewed value + * + * @return array + */ + public function getChanges(): array + { + $changesModel = new ChangesModel(Db::get()); + $changes = $changesModel->getChangeItems(); + + // Record the time that changes were viewed for the current user + $maxId = null; + foreach ($changes as $k => $change) { + if ($maxId < $change['idchange']) { + $maxId = $change['idchange']; + } + } + + if ($maxId) { + $usersModel = new UsersModel(); + $usersModel->updateUserFields($this->user['login'], ['idchange_last_viewed' => $maxId]); + } + + return $changes; + } + +} diff --git a/app/core/CliMulti/Output.php b/app/core/CliMulti/Output.php index eced3854a..84fbaefb9 100644 --- a/app/core/CliMulti/Output.php +++ b/app/core/CliMulti/Output.php @@ -8,7 +8,6 @@ namespace Piwik\CliMulti; use Piwik\CliMulti; -use Piwik\Common; use Piwik\Filesystem; class Output implements OutputInterface diff --git a/app/core/CliMulti/OutputInterface.php b/app/core/CliMulti/OutputInterface.php index 33d197879..8eead52bd 100644 --- a/app/core/CliMulti/OutputInterface.php +++ b/app/core/CliMulti/OutputInterface.php @@ -7,9 +7,6 @@ */ namespace Piwik\CliMulti; -use Piwik\CliMulti; -use Piwik\Common; -use Piwik\Filesystem; interface OutputInterface { diff --git a/app/core/CliMulti/Process.php b/app/core/CliMulti/Process.php index e3a6bd968..a2e6f86e6 100644 --- a/app/core/CliMulti/Process.php +++ b/app/core/CliMulti/Process.php @@ -215,6 +215,7 @@ public static function isSupportedWithReason() if (SettingsServer::isWindows()) { $reasons[] = 'not supported on windows'; + return $reasons; } if (self::isMethodDisabled('shell_exec')) { @@ -287,6 +288,11 @@ private static function returnsSuccessCode($command) { $exec = $command . ' > /dev/null 2>&1; echo $?'; $returnCode = @shell_exec($exec); + + if (false === $returnCode || null === $returnCode) { + return false; + } + $returnCode = trim($returnCode); return 0 == (int) $returnCode; } diff --git a/app/core/CliMulti/StaticOutput.php b/app/core/CliMulti/StaticOutput.php index d298cc7bc..8c2291db6 100644 --- a/app/core/CliMulti/StaticOutput.php +++ b/app/core/CliMulti/StaticOutput.php @@ -7,9 +7,7 @@ */ namespace Piwik\CliMulti; -use Piwik\CliMulti; use Piwik\Common; -use Piwik\Filesystem; class StaticOutput implements OutputInterface { diff --git a/app/core/Columns/Dimension.php b/app/core/Columns/Dimension.php index fd5c50860..0875e1e75 100644 --- a/app/core/Columns/Dimension.php +++ b/app/core/Columns/Dimension.php @@ -12,7 +12,6 @@ use Piwik\Piwik; use Piwik\Plugin; use Piwik\Plugin\ArchivedMetric; -use Piwik\Plugin\ComponentFactory; use Piwik\Plugin\Segment; use Exception; use Piwik\CacheId; @@ -456,10 +455,7 @@ public function formatValue($value, $idSite, Formatter $formatter) case Dimension::TYPE_DURATION_S: return $formatter->getPrettyTimeFromSeconds($value, $displayAsSentence = false); case Dimension::TYPE_DURATION_MS: - $val = number_format($value / 1000, 2); - if ($val > 60) { - $val = round($val); - } + $val = round(($value / 1000), ($value / 1000) > 60 ? 0 : 2); return $formatter->getPrettyTimeFromSeconds($val, $displayAsSentence = true); case Dimension::TYPE_PERCENT: return $formatter->getPrettyPercentFromQuotient($value); diff --git a/app/core/Columns/Discriminator.php b/app/core/Columns/Discriminator.php index 7f8568498..98a85d20a 100644 --- a/app/core/Columns/Discriminator.php +++ b/app/core/Columns/Discriminator.php @@ -9,7 +9,6 @@ namespace Piwik\Columns; use Exception; -use Piwik\Plugins\Actions\Actions\ActionSiteSearch; /** * @api diff --git a/app/core/Common.php b/app/core/Common.php index 90ceda1d4..44d9e2cfd 100644 --- a/app/core/Common.php +++ b/app/core/Common.php @@ -13,7 +13,6 @@ use Piwik\Container\StaticContainer; use Piwik\Intl\Data\Provider\LanguageDataProvider; use Piwik\Intl\Data\Provider\RegionDataProvider; -use Piwik\Plugins\UserCountry\LocationProvider\DefaultProvider; use Piwik\Tracker\Cache as TrackerCache; /** @@ -577,6 +576,38 @@ public static function getRequestVar($varName, $varDefault = null, $varType = nu return $value; } + /** + * Replaces lbrace with an encoded entity to prevent angular from parsing the content + * + * @deprecated Will be removed, once the vue js migration is done + * + * @param $string + * @return array|string|string[]|null + */ + public static function fixLbrace($string) + { + $chars = array('{', '{', '{', '{', '{', '{'); + + static $search; + static $replace; + + if (!isset($search)) { + $search = array_map(function ($val) { return $val . $val; }, $chars); + } + if (!isset($replace)) { + $replace = array_map(function ($val) { return $val . '⁣' . $val; }, $chars); + } + + $replacedString = is_null($string) ? $string : str_replace($search, $replace, $string); + + // try to replace characters until there are no changes + if ($string !== $replacedString) { + return self::fixLbrace($replacedString); + } + + return $string; + } + /* * Generating unique strings */ @@ -1139,7 +1170,7 @@ public static function getCurrentLocationProviderId() { $cache = TrackerCache::getCacheGeneral(); return empty($cache['currentLocationProviderId']) - ? DefaultProvider::ID + ? Plugins\UserCountry\LocationProvider::getDefaultProviderId() : $cache['currentLocationProviderId']; } diff --git a/app/core/Config/Cache.php b/app/core/Config/Cache.php index 5548c1486..d0b781dea 100644 --- a/app/core/Config/Cache.php +++ b/app/core/Config/Cache.php @@ -1,4 +1,5 @@ directory; - foreach ($hosts as $host) - { + foreach ($hosts as $host) { $dir = $this->makeCacheDir($host); if (@is_dir($dir)) { $this->directory = $dir; $success = parent::doDelete($id); if ($success) { - Piwik::postEvent('Core.configFileDeleted', array($this->getFilename($id))); + Piwik::postEvent('Core.configFileDeleted', [$this->getFilename($id)]); } } } $this->directory = $initialDir; } - } diff --git a/app/core/Config/GeneralConfig.php b/app/core/Config/GeneralConfig.php new file mode 100644 index 000000000..89fe39ddb --- /dev/null +++ b/app/core/Config/GeneralConfig.php @@ -0,0 +1,48 @@ +General = $section; + } + + public static function getConfigValue($name, $idSite = null) + { + $config = self::getConfig(); + if (!empty($idSite)) { + $siteSpecificConfig = self::getSiteSpecificConfig($idSite); + $config = array_merge($config, $siteSpecificConfig); + } + return $config[$name] ?? null; + } + + private static function getConfig() + { + return Config::getInstance()->General; + } + + private static function getSiteSpecificConfig($idSite) + { + $key = 'General_' . $idSite; + return Config::getInstance()->$key; + } +} diff --git a/app/core/Config/IniFileChain.php b/app/core/Config/IniFileChain.php index 2801cbeb2..83d0f7344 100644 --- a/app/core/Config/IniFileChain.php +++ b/app/core/Config/IniFileChain.php @@ -1,10 +1,12 @@ reload($defaultSettingsFiles, $userSettingsFile); } @@ -71,7 +73,7 @@ public function __construct(array $defaultSettingsFiles = array(), $userSettings public function &get($name) { if (!isset($this->mergedSettings[$name])) { - $this->mergedSettings[$name] = array(); + $this->mergedSettings[$name] = []; } $result =& $this->mergedSettings[$name]; @@ -146,12 +148,12 @@ public function dumpChanges($header = '') $dirty = false; - $configToWrite = array(); + $configToWrite = []; foreach ($this->mergedSettings as $sectionName => $changedSection) { - if(isset($existingMutableSettings[$sectionName])){ + if (isset($existingMutableSettings[$sectionName])) { $existingMutableSection = $existingMutableSettings[$sectionName]; - } else{ - $existingMutableSection = array(); + } else { + $existingMutableSection = []; } // remove default values from both (they should not get written to local) @@ -162,7 +164,8 @@ public function dumpChanges($header = '') // if either local/config have non-default values and the other doesn't, // OR both have values, but different values, we must write to config.ini.php - if (empty($changedSection) xor empty($existingMutableSection) + if ( + empty($changedSection) xor empty($existingMutableSection) || (!empty($changedSection) && !empty($existingMutableSection) && self::compareElements($changedSection, $existingMutableSection)) @@ -207,9 +210,10 @@ public function dumpChanges($header = '') /** * Reloads settings from disk. */ - public function reload($defaultSettingsFiles = array(), $userSettingsFile = null) + public function reload($defaultSettingsFiles = [], $userSettingsFile = null) { - if (!empty($defaultSettingsFiles) + if ( + !empty($defaultSettingsFiles) || !empty($userSettingsFile) ) { $this->resetSettingsChain($defaultSettingsFiles, $userSettingsFile); @@ -218,13 +222,15 @@ public function reload($defaultSettingsFiles = array(), $userSettingsFile = null $hasAbsoluteConfigFile = !empty($userSettingsFile) && strpos($userSettingsFile, DIRECTORY_SEPARATOR) === 0; $useConfigCache = !empty($GLOBALS['ENABLE_CONFIG_PHP_CACHE']) && $hasAbsoluteConfigFile; - if ($useConfigCache) { + if ($useConfigCache && is_file($userSettingsFile)) { $cache = new Cache(); $values = $cache->doFetch(self::CONFIG_CACHE_KEY); - - if (!empty($values) + + if ( + !empty($values) && isset($values['mergedSettings']) - && isset($values['settingsChain'][$userSettingsFile])) { + && isset($values['settingsChain'][$userSettingsFile]) + ) { $this->mergedSettings = $values['mergedSettings']; $this->settingsChain = $values['settingsChain']; return; @@ -253,16 +259,18 @@ public function reload($defaultSettingsFiles = array(), $userSettingsFile = null if (!empty($GLOBALS['MATOMO_MODIFY_CONFIG_SETTINGS']) && !empty($this->mergedSettings)) { $this->mergedSettings = call_user_func($GLOBALS['MATOMO_MODIFY_CONFIG_SETTINGS'], $this->mergedSettings); } - - if ($useConfigCache - && !empty($this->mergedSettings) - && !empty($this->settingsChain)) { + if ( + $useConfigCache + && !empty($this->mergedSettings) + && !empty($this->settingsChain) + && Cache::hasHostConfig($this->mergedSettings) + ) { $ttlOneHour = 3600; $cache = new Cache(); if ($cache->isValidHost($this->mergedSettings)) { // we make sure to save the config only if the host is valid... - $data = array('mergedSettings' => $this->mergedSettings, 'settingsChain' => $this->settingsChain); + $data = ['mergedSettings' => $this->mergedSettings, 'settingsChain' => $this->settingsChain]; $cache->doSave(self::CONFIG_CACHE_KEY, $data, $ttlOneHour); } } @@ -278,7 +286,7 @@ public function deleteConfigCache() private function copy($merged) { - $copy = array(); + $copy = []; foreach ($merged as $index => $value) { if (is_array($value)) { $copy[$index] = $this->copy($value); @@ -291,7 +299,7 @@ private function copy($merged) private function resetSettingsChain($defaultSettingsFiles, $userSettingsFile) { - $this->settingsChain = array(); + $this->settingsChain = []; if (!empty($defaultSettingsFiles)) { foreach ($defaultSettingsFiles as $file) { @@ -308,7 +316,7 @@ protected function mergeFileSettings() { $mergedSettings = $this->getMergedDefaultSettings(); - $userSettings = end($this->settingsChain) ?: array(); + $userSettings = end($this->settingsChain) ?: []; foreach ($userSettings as $sectionName => $section) { if (!isset($mergedSettings[$sectionName])) { $mergedSettings[$sectionName] = $section; @@ -326,9 +334,10 @@ protected function getMergedDefaultSettings() { $userSettingsFile = $this->getUserSettingsFile(); - $mergedSettings = array(); + $mergedSettings = []; foreach ($this->settingsChain as $file => $settings) { - if ($file == $userSettingsFile + if ( + $file == $userSettingsFile || empty($settings) ) { continue; @@ -396,14 +405,14 @@ public function arrayUnmerge($original, $modified) // ignore keys that are in $original but not in $modified if (empty($original) || !is_array($original)) { - $original = array(); + $original = []; } if (empty($modified) || !is_array($modified)) { - $modified = array(); + $modified = []; } - return array_udiff_assoc($modified, $original, array(__CLASS__, 'compareElements')); + return array_udiff_assoc($modified, $original, [__CLASS__, 'compareElements']); } /** @@ -537,7 +546,7 @@ private function dumpSettings($values, $header) * * @param array &$values Config values that will be saved */ - Piwik::postEvent('Config.beforeSave', array(&$values)); + Piwik::postEvent('Config.beforeSave', [&$values]); $values = $this->encodeValues($values); $writer = new IniWriter(); diff --git a/app/core/Console.php b/app/core/Console.php index 945c2dc9a..0d1b99505 100644 --- a/app/core/Console.php +++ b/app/core/Console.php @@ -53,6 +53,11 @@ public function __construct(Environment $environment = null) ); $this->getDefinition()->addOption($option); + + $option = new InputOption('ignore-warn', null, InputOption::VALUE_NONE, + 'Return 0 exit code even if there are warning logs or error logs detected in the command output.'); + + $this->getDefinition()->addOption($option); } public function renderException($e, $output) @@ -132,7 +137,10 @@ private function doRunImpl(InputInterface $input, OutputInterface $output) } $importantLogDetector = StaticContainer::get(FailureLogMessageDetector::class); - if ($exitCode === 0 && $importantLogDetector->hasEncounteredImportantLog()) { + if (!$input->hasParameterOption('--ignore-warn') + && $exitCode === 0 + && $importantLogDetector->hasEncounteredImportantLog() + ) { $output->writeln("Error: error or warning logs detected, exit 1"); $exitCode = 1; } diff --git a/app/core/Cookie.php b/app/core/Cookie.php index ed7d1351a..6ce81416e 100644 --- a/app/core/Cookie.php +++ b/app/core/Cookie.php @@ -146,7 +146,10 @@ protected function setCookie($Name, $Value, $Expires, $Path = '', $Domain = '', } } - $Expires = $this->formatExpireTime($Expires); + // Format expire time only for non session cookies + if (0 !== $Expires) { + $Expires = $this->formatExpireTime($Expires); + } $header = 'Set-Cookie: ' . rawurlencode($Name) . '=' . rawurlencode($Value) . (empty($Expires) ? '' : '; expires=' . $Expires) diff --git a/app/core/CronArchive.php b/app/core/CronArchive.php index bbdd8a691..de55cddcc 100644 --- a/app/core/CronArchive.php +++ b/app/core/CronArchive.php @@ -23,8 +23,6 @@ use Piwik\CronArchive\SharedSiteIds; use Piwik\CronArchive\StopArchiverException; use Piwik\DataAccess\ArchiveSelector; -use Piwik\DataAccess\ArchiveTableCreator; -use Piwik\DataAccess\ArchiveWriter; use Piwik\DataAccess\Model; use Piwik\DataAccess\RawLogDao; use Piwik\Exception\UnexpectedWebsiteFoundException; @@ -162,6 +160,23 @@ class CronArchive */ public $maxConcurrentArchivers = false; + /** + * Maximum number of sites to process during a single execution of the archiver. + * + * @var int|null + */ + public $maxSitesToProcess = null; + + /** + * Maximum number of archives to process during a single execution of the archiver. + * + * Note that this is not a hard limit as the limit is only checked after all + * archives for a site have been processed. + * + * @var int|null + */ + public $maxArchivesToProcess = null; + private $archivingStartingTime; private $formatter; @@ -360,6 +375,8 @@ public function run() $queueConsumer = new QueueConsumer($this->logger, $this->websiteIdArchiveList, $countOfProcesses, $pid, $this->model, $this->segmentArchiving, $this, $this->cliMultiRequestParser, $this->archiveFilter); + $queueConsumer->setMaxSitesToProcess($this->maxSitesToProcess); + while (true) { if ($this->isMaintenanceModeEnabled()) { $this->logger->info("Archiving will stop now because maintenance mode is enabled"); @@ -389,6 +406,10 @@ public function run() $successCount = $this->launchArchivingFor($archivesToProcess, $queueConsumer); $numArchivesFinished += $successCount; + if ($this->maxArchivesToProcess && $numArchivesFinished >= $this->maxArchivesToProcess) { + $this->logger->info("Maximum number of archives to process per execution has been reached."); + break; + } } $this->disconnectDb(); @@ -474,7 +495,7 @@ private function launchArchivingFor($archives, QueueConsumer $queueConsumer) foreach ($urls as $index => $url) { $content = array_key_exists($index, $responses) ? $responses[$index] : null; - $this->checkResponse($content, $url); + $checkInvalid = $this->checkResponse($content, $url); $stats = json_decode($content, $assoc = true); if (!is_array($stats)) { @@ -491,7 +512,10 @@ private function launchArchivingFor($archives, QueueConsumer $queueConsumer) $visitsForPeriod = $this->getVisitsFromApiResponse($stats); - $this->logArchiveJobFinished($url, $timers[$index], $visitsForPeriod, $archivesBeingQueried[$index]['plugin'], $archivesBeingQueried[$index]['report']); + + $this->logArchiveJobFinished($url, $timers[$index], $visitsForPeriod, + $archivesBeingQueried[$index]['plugin'], $archivesBeingQueried[$index]['report'], !$checkInvalid); + $this->deleteInvalidatedArchives($archivesBeingQueried[$index]); @@ -549,12 +573,14 @@ private function generateUrlToArchiveFromArchiveInfo($archive) return [$url, $segment, $plugin]; } - private function logArchiveJobFinished($url, $timer, $visits, $plugin = null, $report = null) + private function logArchiveJobFinished($url, $timer, $visits, $plugin = null, $report = null, $wasSkipped = null) { $params = UrlHelper::getArrayFromQueryString($url); $visits = (int) $visits; - $this->logger->info("Archived website id {$params['idSite']}, period = {$params['period']}, date = " + $message = $wasSkipped ? "Skipped Archiving website" : "Archived website"; + + $this->logger->info($message." id {$params['idSite']}, period = {$params['period']}, date = " . "{$params['date']}, segment = '" . (isset($params['segment']) ? urldecode(urldecode($params['segment'])) : '') . "', " . ($plugin ? "plugin = $plugin, " : "") . ($report ? "report = $report, " : "") . "$visits visits found. $timer"); } @@ -692,6 +718,12 @@ public function logError($m) private function logNetworkError($url, $response) { + + if (preg_match("/Segment (.*?) is not a supported segment/i", $response, $match)) { + $this->logger->info($match[0]); + return false; + } + $message = "Got invalid response from API request: $url. "; if (empty($response)) { $message .= "The response was empty. This usually means a server error. A solution to this error is generally to increase the value of 'memory_limit' in your php.ini file. "; @@ -896,6 +928,11 @@ private function invalidateWithSegments($idSites, $date, $period, $_forceInvalid } foreach ($this->segmentArchiving->getAllSegmentsToArchive($idSite) as $segmentDefinition) { + + // check if the segment is available + if (!$this->isSegmentAvailable($segmentDefinition, [$idSite])) { + continue; + } $params = new Parameters(new Site($idSite), $periodObj, new Segment($segmentDefinition, [$idSite], $periodObj->getDateStart(), $periodObj->getDateEnd())); if ($this->canWeSkipInvalidatingBecauseThereIsAUsablePeriod($params, $doNotIncludeTtlInExistingArchiveCheck)) { $this->logger->debug(' Found usable archive for {archive}, skipping invalidation.', ['archive' => $params]); @@ -924,6 +961,24 @@ private function invalidateWithSegments($idSites, $date, $period, $_forceInvalid } } + + /** + * check if segments that contain dimensions that don't exist anymore + * @param $segmentDefinition + * @param $idSites + * @return bool + */ + protected function isSegmentAvailable($segmentDefinition, $idSites) + { + try { + new Segment($segmentDefinition, $idSites); + } catch (\Exception $e) { + $this->logger->info("Segment '".$segmentDefinition."' is not a supported segment"); + return false; + } + return true; + } + /** * Returns true if there is an existing valid period we can use, or false if there isn't and the invalidation should go through. * @@ -1115,11 +1170,18 @@ private function logArchiveTimeoutInfo() } } + if ($this->maxSitesToProcess) { + $this->logger->info("- Maximum {$this->maxSitesToProcess} websites will be processed."); + } + if ($this->maxArchivesToProcess) { + $this->logger->info("- Maximum {$this->maxArchivesToProcess} archives will be processed (soft limit)."); + } + // Try and not request older data we know is already archived if ($this->lastSuccessRunTimestamp !== false) { $dateLast = time() - $this->lastSuccessRunTimestamp; $this->logger->info("- Archiving was last executed without error " - . $this->formatter->getPrettyTimeFromSeconds($dateLast, true) . " ago"); + . $this->formatter->getPrettyTimeFromSeconds($dateLast, true) . " ago."); } } diff --git a/app/core/CronArchive/QueueConsumer.php b/app/core/CronArchive/QueueConsumer.php index e527ac0e7..7e920fa6b 100644 --- a/app/core/CronArchive/QueueConsumer.php +++ b/app/core/CronArchive/QueueConsumer.php @@ -14,18 +14,14 @@ use Piwik\ArchiveProcessor\Parameters; use Piwik\ArchiveProcessor\Rules; use Piwik\CliMulti\RequestParser; -use Piwik\Common; use Piwik\CronArchive; use Piwik\DataAccess\ArchiveSelector; use Piwik\DataAccess\Model; use Piwik\Date; -use Piwik\Db; -use Piwik\Exception\UnexpectedWebsiteFoundException; use Piwik\Period; use Piwik\Period\Factory as PeriodFactory; use Piwik\Piwik; use Piwik\Plugin\Manager; -use Piwik\Plugins\SitesManager\API; use Piwik\Segment; use Piwik\Site; use Piwik\Timer; @@ -108,6 +104,11 @@ class QueueConsumer */ private $currentSiteArchivingStartTime; + /** + * @var int|null + */ + private $maxSitesToProcess = null; + private $processedSiteCount = 0; public function __construct(LoggerInterface $logger, $websiteIdArchiveList, $countOfProcesses, $pid, Model $model, @@ -131,9 +132,22 @@ public function __construct(LoggerInterface $logger, $websiteIdArchiveList, $cou $this->periodIdsToLabels = array_flip(Piwik::$idPeriods); } + /** + * Get next archives to process. + * + * Returns either an array of archives to process for the current site (may be + * empty if there are no more archives to process for it) or null when there are + * no more sites to process. + * + * @return null|array + */ public function getNextArchivesToProcess() { if (empty($this->idSite)) { + if ($this->maxSitesToProcess && $this->processedSiteCount >= $this->maxSitesToProcess) { + $this->logger->info("Maximum number of sites to process per execution has been reached."); + return null; + } $this->idSite = $this->getNextIdSiteToArchive(); if (empty($this->idSite)) { // no sites left to archive, stop $this->logger->debug("No more sites left to archive, stopping."); @@ -617,4 +631,19 @@ public function getIdSite() { return $this->idSite; } + + /** + * Set or get the maximum number of sites to process + * + * @param int|null $newValue New value or null to just return current value + * + * @return int|null New or existing value + */ + public function setMaxSitesToProcess($newValue = null) + { + if (null !== $newValue) { + $this->maxSitesToProcess = $newValue; + } + return $this->maxSitesToProcess; + } } diff --git a/app/core/CronArchive/SegmentArchiving.php b/app/core/CronArchive/SegmentArchiving.php index 1464eeb29..f7dc4636e 100644 --- a/app/core/CronArchive/SegmentArchiving.php +++ b/app/core/CronArchive/SegmentArchiving.php @@ -14,7 +14,6 @@ use Piwik\ArchiveProcessor\Rules; use Piwik\Common; use Piwik\Container\StaticContainer; -use Piwik\CronArchive; use Piwik\Date; use Piwik\Db; use Piwik\Period\Range; diff --git a/app/core/DataAccess/ArchiveSelector.php b/app/core/DataAccess/ArchiveSelector.php index c0a19a603..721163804 100644 --- a/app/core/DataAccess/ArchiveSelector.php +++ b/app/core/DataAccess/ArchiveSelector.php @@ -20,7 +20,6 @@ use Piwik\Period; use Piwik\Period\Range; use Piwik\Segment; -use Piwik\SettingsServer; use Psr\Log\LoggerInterface; /** diff --git a/app/core/DataAccess/ArchivingDbAdapter.php b/app/core/DataAccess/ArchivingDbAdapter.php index 660268c27..6483869a0 100644 --- a/app/core/DataAccess/ArchivingDbAdapter.php +++ b/app/core/DataAccess/ArchivingDbAdapter.php @@ -13,6 +13,7 @@ use Piwik\Db\AdapterInterface; use Piwik\DbHelper; use Psr\Log\LoggerInterface; +use Exception; class ArchivingDbAdapter { @@ -48,7 +49,7 @@ public function exec($sql) $sql = DbHelper::addMaxExecutionTimeHintToQuery($sql, $this->maxExecutionTime); $this->logSql($sql); - return call_user_func_array([$this->wrapped, __FUNCTION__], func_get_args()); + return call_user_func_array([$this, "callFunction"], array_merge([__FUNCTION__], func_get_args())); } public function query($sql) @@ -56,7 +57,7 @@ public function query($sql) $sql = DbHelper::addMaxExecutionTimeHintToQuery($sql, $this->maxExecutionTime); $this->logSql($sql); - return call_user_func_array([$this->wrapped, __FUNCTION__], func_get_args()); + return call_user_func_array([$this, "callFunction"], array_merge([__FUNCTION__], func_get_args())); } public function fetchAll($sql) @@ -64,7 +65,7 @@ public function fetchAll($sql) $sql = DbHelper::addMaxExecutionTimeHintToQuery($sql, $this->maxExecutionTime); $this->logSql($sql); - return call_user_func_array([$this->wrapped, __FUNCTION__], func_get_args()); + return call_user_func_array([$this, "callFunction"], array_merge([__FUNCTION__], func_get_args())); } public function fetchRow($sql) @@ -72,7 +73,7 @@ public function fetchRow($sql) $sql = DbHelper::addMaxExecutionTimeHintToQuery($sql, $this->maxExecutionTime); $this->logSql($sql); - return call_user_func_array([$this->wrapped, __FUNCTION__], func_get_args()); + return call_user_func_array([$this, "callFunction"], array_merge([__FUNCTION__], func_get_args())); } public function fetchOne($sql) @@ -80,7 +81,7 @@ public function fetchOne($sql) $sql = DbHelper::addMaxExecutionTimeHintToQuery($sql, $this->maxExecutionTime); $this->logSql($sql); - return call_user_func_array([$this->wrapped, __FUNCTION__], func_get_args()); + return call_user_func_array([$this, "callFunction"], array_merge([__FUNCTION__], func_get_args())); } public function fetchAssoc($sql) @@ -88,7 +89,19 @@ public function fetchAssoc($sql) $sql = DbHelper::addMaxExecutionTimeHintToQuery($sql, $this->maxExecutionTime); $this->logSql($sql); - return call_user_func_array([$this->wrapped, __FUNCTION__], func_get_args()); + return call_user_func_array([$this, "callFunction"], array_merge([__FUNCTION__], func_get_args())); + } + + /** + * Test error number + * + * @param Exception $e + * @param string $errno + * @return bool + */ + public function isErrNo($e, $errno) + { + return $this->wrapped->isErrNo($e, $errno); } private function logSql($sql) @@ -98,4 +111,25 @@ private function logSql($sql) $this->logger->debug($sql); } } + + private function callFunction($function) { + + $args = func_get_args(); + unset($args[0]); + + try { + return call_user_func_array([$this->wrapped, $function], $args); + } catch (\Exception $e) { + if ($this->isErrNo($e, \Piwik\Updater\Migration\Db::ERROR_CODE_MAX_EXECUTION_TIME_EXCEEDED_QUERY_INTERRUPTED) || + $this->isErrNo($e, \Piwik\Updater\Migration\Db::ERROR_CODE_MAX_EXECUTION_TIME_EXCEEDED_SORT_ABORTED) + ) + { + $this->logger->warning('Archiver query exceeded maximum execution time: {details}', + ['details' => json_encode($args, true)]); + + } + throw $e; + } + } + } \ No newline at end of file diff --git a/app/core/DataAccess/LogAggregator.php b/app/core/DataAccess/LogAggregator.php index e283af269..cada4b157 100644 --- a/app/core/DataAccess/LogAggregator.php +++ b/app/core/DataAccess/LogAggregator.php @@ -282,8 +282,7 @@ private function createTemporaryTable($unprefixedSegmentTableName, $segmentSelec if (defined('PIWIK_TEST_MODE') && PIWIK_TEST_MODE) { $engine = 'ENGINE=MEMORY'; } - $tempTableIdVisitColumn = 'idvisit BIGINT(10) UNSIGNED NOT NULL'; - $createTableSql = 'CREATE TEMPORARY TABLE ' . $table . ' (' . $tempTableIdVisitColumn . ') ' . $engine; + $createTableSql = 'CREATE TEMPORARY TABLE ' . $table . ' (idvisit BIGINT(10) UNSIGNED NOT NULL, PRIMARY KEY (`idvisit`)) ' . $engine; // we do not insert the data right away using create temporary table ... select ... // to avoid metadata lock see eg https://www.percona.com/blog/2018/01/10/why-avoid-create-table-as-select-statement/ @@ -293,23 +292,7 @@ private function createTemporaryTable($unprefixedSegmentTableName, $segmentSelec } catch (\Exception $e) { if ($readerDb->isErrNo($e, \Piwik\Updater\Migration\Db::ERROR_CODE_TABLE_EXISTS)) { return; - } elseif ($readerDb->isErrNo($e, \Piwik\Updater\Migration\Db::ERROR_CODE_REQUIRES_PRIMARY_KEY) - || $readerDb->isErrNo($e, \Piwik\Updater\Migration\Db::ERROR_CODE_UNABLE_CREATE_TABLE_WITHOUT_PRIMARY_KEY - || stripos($e->getMessage(), 'requires a primary key') !== false - || stripos($e->getMessage(), 'table without a primary key') !== false) - ) { - $createTableSql = str_replace($tempTableIdVisitColumn, $tempTableIdVisitColumn . ', PRIMARY KEY (`idvisit`)', $createTableSql); - - try { - $readerDb->query($createTableSql); - } catch (\Exception $e) { - if ($readerDb->isErrNo($e, \Piwik\Updater\Migration\Db::ERROR_CODE_TABLE_EXISTS)) { - return; - } else { - throw $e; - } - } - } else { + } else { throw $e; } } diff --git a/app/core/DataAccess/Model.php b/app/core/DataAccess/Model.php index e6e821e1a..4d2199ce2 100644 --- a/app/core/DataAccess/Model.php +++ b/app/core/DataAccess/Model.php @@ -82,7 +82,7 @@ public function getInvalidatedArchiveIdsSafeToDelete($archiveTable, $setGroupCon break; } - list($idarchive, $value) = explode('.', $pair); + [$idarchive, $value] = explode('.', $pair); array_shift($duplicateArchives); @@ -99,7 +99,7 @@ public function getInvalidatedArchiveIdsSafeToDelete($archiveTable, $setGroupCon break; } - list($idarchive, $value) = explode('.', $pair); + [$idarchive, $value] = explode('.', $pair); $archiveIds[] = $idarchive; // does not matter what the value is, the latest is usable so older archives can be purged } } @@ -157,7 +157,7 @@ public function updateArchiveAsInvalidated($archiveTable, $idSites, $allPeriodsT if (!empty($name)) { if (strpos($name, '.') !== false) { - list($plugin, $name) = explode('.', $name, 2); + [$plugin, $name] = explode('.', $name, 2); } else { $plugin = $name; $name = null; @@ -512,7 +512,14 @@ public function allocateNewArchiveId($numericTable) $idarchive = $sequence->getNextId(); } catch (Exception $e) { // edge case: sequence was not found, create it now - $sequence->create(); + try { + $sequence->create(); + } catch (Exception $ex) { + // Ignore duplicate entry error, as that means another request might have already created the sequence + if (!Db::get()->isErrNo($ex, \Piwik\Updater\Migration\Db::ERROR_CODE_DUPLICATE_ENTRY)) { + throw $ex; + } + } $idarchive = $sequence->getNextId(); } diff --git a/app/core/DataTable.php b/app/core/DataTable.php index 8b9291a78..62ebc6db1 100644 --- a/app/core/DataTable.php +++ b/app/core/DataTable.php @@ -11,7 +11,6 @@ use Closure; use Exception; -use Piwik\Archive\DataTableFactory; use Piwik\DataTable\DataTableInterface; use Piwik\DataTable\Manager; use Piwik\DataTable\Renderer\Html; diff --git a/app/core/DataTable/Map.php b/app/core/DataTable/Map.php index 8700d8822..351cb4d05 100644 --- a/app/core/DataTable/Map.php +++ b/app/core/DataTable/Map.php @@ -11,7 +11,6 @@ use Closure; use Piwik\Common; use Piwik\DataTable; -use Piwik\DataTable\Renderer\Console; use Piwik\DataTable\Renderer\Html; /** diff --git a/app/core/DataTable/Renderer/Xml.php b/app/core/DataTable/Renderer/Xml.php index 879c814d0..daac72865 100644 --- a/app/core/DataTable/Renderer/Xml.php +++ b/app/core/DataTable/Renderer/Xml.php @@ -179,7 +179,7 @@ private function renderArray($array, $prefixLines) } else { $xmlValue = self::formatValueXml($value); - if (strlen($xmlValue) != 0) { + if (strlen(strval($xmlValue)) !== 0) { $result .= $prefixLines . $prefix . $xmlValue . $suffix . "\n"; } else { $result .= $prefixLines . $emptyNode . "\n"; diff --git a/app/core/DataTable/Row.php b/app/core/DataTable/Row.php index bfa1a4966..878c1a61c 100644 --- a/app/core/DataTable/Row.php +++ b/app/core/DataTable/Row.php @@ -12,7 +12,6 @@ use Piwik\Container\StaticContainer; use Piwik\DataTable; use Piwik\Date; -use Piwik\Log; use Piwik\Metrics; use Piwik\Period; use Psr\Log\LoggerInterface; @@ -36,7 +35,8 @@ class Row extends \ArrayObject */ private static $unsummableColumns = array( 'label' => true, - 'full_url' => true // column used w/ old Piwik versions, + 'full_url' => true, // column used w/ old Piwik versions, + 'ts_archived' => true // date column used in metadata for proportional tooltips ); // @see sumRow - implementation detail @@ -677,6 +677,9 @@ protected function sumRowArray($thisColumnValue, $columnToSumValue, $columnName if (is_array($columnToSumValue)) { $newValue = $thisColumnValue; foreach ($columnToSumValue as $arrayIndex => $arrayValue) { + if (!is_numeric($arrayIndex) && !$this->isSummableColumn($arrayIndex)) { + continue; + } if (!isset($newValue[$arrayIndex])) { $newValue[$arrayIndex] = false; } diff --git a/app/core/Date.php b/app/core/Date.php index a214751b8..6c5b4d785 100644 --- a/app/core/Date.php +++ b/app/core/Date.php @@ -889,6 +889,10 @@ protected function formatToken($token) case "ss": case "s": return $this->toString('s'); + // would normally also include AM, PM, Noon and Midnight + case "b": + // would normally be a textual presentation like "in the afternoon" + case "B": // am / pm case "a": return $this->toString('a') == 'am' ? $translator->translate('Intl_Time_AM') : $translator->translate('Intl_Time_PM'); @@ -911,7 +915,7 @@ protected function formatToken($token) } protected static $tokens = array( - 'G', 'y', 'M', 'L', 'd', 'h', 'H', 'k', 'K', 'm', 's', 'E', 'c', 'e', 'D', 'F', 'w', 'W', 'a', 'z', 'Z', 'v', + 'G', 'y', 'M', 'L', 'd', 'h', 'H', 'k', 'K', 'm', 's', 'E', 'c', 'e', 'D', 'F', 'w', 'W', 'a', 'b', 'B', 'z', 'Z', 'v', ); /** diff --git a/app/core/Db.php b/app/core/Db.php index 23d0a493b..b047d1d8f 100644 --- a/app/core/Db.php +++ b/app/core/Db.php @@ -9,7 +9,6 @@ namespace Piwik; use Exception; -use Piwik\DataAccess\TableMetadata; use Piwik\Db\Adapter; /** @@ -40,6 +39,8 @@ class Db private static $logQueries = true; + // this is used for indicate TransactionLevel Cache + public $supportsUncommitted; /** * Returns the database connection and creates it if it hasn't been already. * diff --git a/app/core/Db/Adapter/Mysqli.php b/app/core/Db/Adapter/Mysqli.php index 87f91ac32..a46236c57 100644 --- a/app/core/Db/Adapter/Mysqli.php +++ b/app/core/Db/Adapter/Mysqli.php @@ -25,6 +25,10 @@ class Mysqli extends Zend_Db_Adapter_Mysqli implements AdapterInterface * * @param array|Zend_Config $config database configuration */ + + // this is used for indicate TransactionLevel Cache + public $supportsUncommitted; + public function __construct($config) { // Enable LOAD DATA INFILE diff --git a/app/core/Db/Adapter/Pdo/Mysql.php b/app/core/Db/Adapter/Pdo/Mysql.php index 1170afe3f..adbdaa690 100644 --- a/app/core/Db/Adapter/Pdo/Mysql.php +++ b/app/core/Db/Adapter/Pdo/Mysql.php @@ -29,6 +29,10 @@ class Mysql extends Zend_Db_Adapter_Pdo_Mysql implements AdapterInterface * * @param array|Zend_Config $config database configuration */ + + // this is used for indicate TransactionLevel Cache + public $supportsUncommitted; + public function __construct($config) { // Enable LOAD DATA INFILE diff --git a/app/core/Db/Schema/Mysql.php b/app/core/Db/Schema/Mysql.php index 92d2b882f..fcf943bbd 100644 --- a/app/core/Db/Schema/Mysql.php +++ b/app/core/Db/Schema/Mysql.php @@ -52,6 +52,7 @@ public function getTablesCreateSql() superuser_access TINYINT(2) unsigned NOT NULL DEFAULT '0', date_registered TIMESTAMP NULL, ts_password_modified TIMESTAMP NULL, + idchange_last_viewed TIMESTAMP NULL, PRIMARY KEY(login) ) ENGINE=$engine DEFAULT CHARSET=$charset ", @@ -190,7 +191,7 @@ public function getTablesCreateSql() PRIMARY KEY(idvisit), INDEX index_idsite_config_datetime (idsite, config_id, visit_last_action_time), INDEX index_idsite_datetime (idsite, visit_last_action_time), - INDEX index_idsite_idvisitor (idsite, idvisitor) + INDEX index_idsite_idvisitor (idsite, idvisitor, visit_last_action_time DESC) ) ENGINE=$engine DEFAULT CHARSET=$charset ", @@ -358,6 +359,19 @@ public function getTablesCreateSql() PRIMARY KEY (`key`) ) ENGINE=$engine DEFAULT CHARSET=$charset ", + 'changes' => "CREATE TABLE `{$prefixTables}changes` ( + `idchange` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `created_time` DATETIME NOT NULL, + `plugin_name` VARCHAR(60) NOT NULL, + `version` VARCHAR(20) NOT NULL, + `title` VARCHAR(255) NOT NULL, + `description` TEXT NULL, + `link_name` VARCHAR(255) NULL, + `link` VARCHAR(255) NULL, + PRIMARY KEY(`idchange`), + UNIQUE KEY unique_plugin_version_title (`plugin_name`, `version`, `title`(100)) + ) ENGINE=$engine DEFAULT CHARSET=$charset + ", ); return $tables; @@ -574,8 +588,9 @@ public function createAnonymousUser() // note that the token_auth value is anonymous, which is assigned by default as well in the Login plugin $db = $this->getDb(); $db->query("INSERT IGNORE INTO " . Common::prefixTable("user") . " - (`login`, `password`, `email`, `twofactor_secret`, `superuser_access`, `date_registered`, `ts_password_modified`) - VALUES ( 'anonymous', '', 'anonymous@example.org', '', 0, '$now', '$now' );"); + (`login`, `password`, `email`, `twofactor_secret`, `superuser_access`, `date_registered`, `ts_password_modified`, + `idchange_last_viewed`) + VALUES ( 'anonymous', '', 'anonymous@example.org', '', 0, '$now', '$now' , NULL);"); $model = new Model(); $model->addTokenAuth('anonymous', 'anonymous', 'anonymous default token', $now); diff --git a/app/core/Db/TransactionLevel.php b/app/core/Db/TransactionLevel.php index c19699084..f087f4adc 100644 --- a/app/core/Db/TransactionLevel.php +++ b/app/core/Db/TransactionLevel.php @@ -40,12 +40,18 @@ public function canLikelySetTransactionLevel() public function setUncommitted() { + if ($this->db->supportsUncommitted === false) { + // we know "Uncommitted" transaction level is not supported, we don't need to do anything as it won't work to set the status + return false; + } + try { $backup = $this->db->fetchOne('SELECT @@TX_ISOLATION'); } catch (\Exception $e) { try { $backup = $this->db->fetchOne('SELECT @@transaction_isolation'); } catch (\Exception $e) { + $this->db->supportsUncommitted = false; return false; } } @@ -54,9 +60,17 @@ public function setUncommitted() $this->db->query('SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED'); $this->statusBackup = $backup; - Option::set(self::TEST_OPTION_NAME, '1'); // try setting something w/ the new transaction isolation level + if ($this->db->supportsUncommitted === null) { + // the first time we need to check if the transaction level actually works by + // trying to set something w/ the new transaction isolation level + Option::set(self::TEST_OPTION_NAME, '1'); + } + + $this->db->supportsUncommitted = true; } catch (\Exception $e) { + // setting the transaction level status did not work // catch eg 1665 Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging. InnoDB is limited to row-logging when transaction isolation level is READ COMMITTED or READ UNCOMMITTED + $this->db->supportsUncommitted = false; $this->restorePreviousStatus(); return false; } @@ -77,7 +91,5 @@ public function restorePreviousStatus() $this->db->query('SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ'); } } - } - } diff --git a/app/core/DeviceDetector/DeviceDetectorFactory.php b/app/core/DeviceDetector/DeviceDetectorFactory.php index 83931aaa1..bc81a74a5 100644 --- a/app/core/DeviceDetector/DeviceDetectorFactory.php +++ b/app/core/DeviceDetector/DeviceDetectorFactory.php @@ -9,7 +9,6 @@ namespace Piwik\DeviceDetector; use DeviceDetector\DeviceDetector; -use Piwik\Common; use Piwik\Container\StaticContainer; class DeviceDetectorFactory diff --git a/app/core/Exception/NotYetInstalledException.php b/app/core/Exception/NotYetInstalledException.php index c1635f505..a8ebbb7d8 100644 --- a/app/core/Exception/NotYetInstalledException.php +++ b/app/core/Exception/NotYetInstalledException.php @@ -8,8 +8,6 @@ */ namespace Piwik\Exception; -use Piwik\Common; -use Piwik\Url; use Throwable; class NotYetInstalledException extends InvalidRequestParameterException diff --git a/app/core/ExceptionHandler.php b/app/core/ExceptionHandler.php index a6af86354..ca549622b 100644 --- a/app/core/ExceptionHandler.php +++ b/app/core/ExceptionHandler.php @@ -1,4 +1,5 @@ getMessage(); @@ -118,11 +120,9 @@ private static function getErrorResponse($ex) $isHtmlMessage = method_exists($ex, 'isHtmlMessage') && $ex->isHtmlMessage(); if (!$isHtmlMessage && Request::isApiRequest($_GET)) { - $outputFormat = strtolower(Common::getRequestVar('format', 'xml', 'string', $_GET + $_POST)); $response = new ResponseBuilder($outputFormat); return $response->getResponseException($ex); - } elseif (!$isHtmlMessage) { $message = Common::sanitizeInputValue($message); } @@ -146,7 +146,24 @@ private static function getErrorResponse($ex) } } - $result = Piwik_GetErrorMessagePage($message, $debugTrace, true, true, $logoHeaderUrl, $logoFaviconUrl); + // Unsupported browser errors shouldn't be written to the web server log. At DEBUG logging level this error will + // be written to the application log instead + $writeErrorLog = !($ex instanceof \Piwik\Exception\NotSupportedBrowserException); + + $hostname = Url::getRFCValidHostname(); + $hostStr = $hostname ? "[$hostname] " : '- '; + + $result = Piwik_GetErrorMessagePage( + $message, + $debugTrace, + true, + true, + $logoHeaderUrl, + $logoFaviconUrl, + null, + $hostStr, + $writeErrorLog + ); try { /** @@ -158,7 +175,7 @@ private static function getErrorResponse($ex) * @param string &$result The HTML of the error page. * @param Exception $ex The Exception displayed in the error page. */ - Piwik::postEvent('FrontController.modifyErrorPage', array(&$result, $ex)); + Piwik::postEvent('FrontController.modifyErrorPage', [&$result, $ex]); } catch (ContainerDoesNotExistException $ex) { // this can happen when an error occurs before the Piwik environment is created } @@ -166,17 +183,17 @@ private static function getErrorResponse($ex) return $result; } - private static function logException($exception, $loglevel=Log::ERROR) + private static function logException($exception, $loglevel = Log::ERROR) { try { switch ($loglevel) { - case(Log::DEBUG): + case (Log::DEBUG): StaticContainer::get(LoggerInterface::class)->debug('Uncaught exception: {exception}', [ 'exception' => $exception, 'ignoreInScreenWriter' => true, ]); break; - case(Log::ERROR): + case (Log::ERROR): default: StaticContainer::get(LoggerInterface::class)->error('Uncaught exception: {exception}', [ 'exception' => $exception, diff --git a/app/core/Filechecks.php b/app/core/Filechecks.php index 1c0fc049e..b37b52a58 100644 --- a/app/core/Filechecks.php +++ b/app/core/Filechecks.php @@ -107,9 +107,13 @@ public static function getAutoUpdateMakeWritableMessage() { $realpath = Filesystem::realpath(PIWIK_INCLUDE_PATH . '/'); $message = ''; - $message .= "
" . self::getCommandToChangeOwnerOfPiwikFiles() . "
"; + if (!SettingsServer::isWindows()) { + $message .= "
" . self::getCommandToChangeOwnerOfPiwikFiles() . "
"; + } $message .= self::getMakeWritableCommand($realpath); - $message .= 'chmod 755 '.$realpath.'/console
'; + if (!SettingsServer::isWindows()) { + $message .= 'chmod 755 ' . $realpath . '/console
'; + } $message .= 'After you execute these commands (or change permissions via your FTP software), refresh the page and you should be able to use the "Automatic Update" feature.'; return $message; } diff --git a/app/core/Filesystem.php b/app/core/Filesystem.php index ff7942190..5b8101291 100644 --- a/app/core/Filesystem.php +++ b/app/core/Filesystem.php @@ -294,8 +294,17 @@ public static function sortFilesDescByPathLength($files) */ public static function directoryDiff($source, $target) { - $sourceFiles = self::globr($source, '*'); - $targetFiles = self::globr($target, '*'); + $flags = 0; + $pattern = '*'; + + if (defined('GLOB_BRACE')) { + // The GLOB_BRACE flag is not available on some non GNU systems, like Solaris or Alpine Linux. + $flags = GLOB_BRACE; + $pattern = '{,.}*[!.]*'; // matches all files and folders, including those starting with ".", but excludes "." and ".." + } + + $sourceFiles = self::globr($source, $pattern, $flags); + $targetFiles = self::globr($target, $pattern, $flags); $sourceFiles = array_map(function ($file) use ($source) { return str_replace($source, '', $file); @@ -305,7 +314,11 @@ public static function directoryDiff($source, $target) return str_replace($target, '', $file); }, $targetFiles); - $diff = array_diff($targetFiles, $sourceFiles); + if (FileSystem::isFileSystemCaseInsensitive()) { + $diff = array_udiff($targetFiles, $sourceFiles, 'strcasecmp'); + } else { + $diff = array_diff($targetFiles, $sourceFiles); + } return array_values($diff); } @@ -548,6 +561,23 @@ private static function isPathWithinTmpFolder($path) return $isPathWithinTmpFolder; } + /** + * Check if the filesystem is case sensitive by writing a temporary file + * + * @return bool + */ + public static function isFileSystemCaseInsensitive() : bool + { + $testFileName = 'caseSensitivityTest.txt'; + $pathTmp = StaticContainer::get('path.tmp'); + @file_put_contents($pathTmp.'/'.$testFileName, 'Nothing to see here.'); + if (\file_exists($pathTmp.'/'.strtolower($testFileName))) { + // Wrote caseSensitivityTest.txt but casesensitivitytest.txt exists, so case insensitive + return true; + } + return false; + } + /** * in tmp/ (sub-)folder(s) we create empty index.htm|php files * diff --git a/app/core/FrontController.php b/app/core/FrontController.php index 448abb5b7..3227ffa33 100644 --- a/app/core/FrontController.php +++ b/app/core/FrontController.php @@ -11,6 +11,7 @@ use Exception; use Piwik\API\Request; +use Piwik\Config\GeneralConfig; use Piwik\Container\StaticContainer; use Piwik\DataTable\Manager; use Piwik\Exception\AuthenticationFailedException; @@ -396,6 +397,11 @@ public function init() $loggedIn = false; + //move this up unsupported Browser do not create session + if ($this->isSupportedBrowserCheckNeeded()) { + SupportedBrowser::checkIfBrowserSupported(); + } + // don't use sessionauth in cli mode // try authenticating w/ session first... $sessionAuth = $this->makeSessionAuthenticator(); @@ -423,9 +429,7 @@ public function init() $this->makeAuthenticator($sessionAuth); // Piwik\Auth must be set to the correct Login plugin } - if ($this->isSupportedBrowserCheckNeeded()) { - SupportedBrowser::checkIfBrowserSupported(); - } + // Force the auth to use the token_auth if specified, so that embed dashboard // and all other non widgetized controller methods works fine @@ -457,6 +461,10 @@ protected function prepareDispatch($module, $action, $parameters) if (is_null($action)) { $action = Common::getRequestVar('action', false); + if ($action !== false) { + // If a value was provided, check it has the correct type. + $action = Common::getRequestVar('action', null, 'string'); + } } if (Session::isSessionStarted()) { @@ -486,10 +494,14 @@ protected function prepareDispatch($module, $action, $parameters) protected function handleMaintenanceMode() { - if ((Config::getInstance()->General['maintenance_mode'] != 1) || Common::isPhpCliMode()) { + if ((GeneralConfig::getConfigValue('maintenance_mode') != 1) || Common::isPhpCliMode() ) { return; } - Common::sendResponseCode(503); + + // as request matomo behind load balancer should not return 503. https://github.com/matomo-org/matomo/issues/18054 + if (GeneralConfig::getConfigValue('multi_server_environment') != 1) { + Common::sendResponseCode(503); + } $logoUrl = 'plugins/Morpheus/images/logo.svg'; $faviconUrl = 'plugins/CoreHome/images/favicon.png'; diff --git a/app/core/Http.php b/app/core/Http.php index 500cfe8e4..ae9b0b411 100644 --- a/app/core/Http.php +++ b/app/core/Http.php @@ -226,7 +226,7 @@ public static function sendHttpRequestBy( $isBlocked = false; foreach ($disallowedHosts as $host) { - if (preg_match(self::convertWildcardToPattern($host), $parsedUrl['host']) === 1) { + if (!empty($parsedUrl['host']) && preg_match(self::convertWildcardToPattern($host), $parsedUrl['host']) === 1) { $isBlocked = true; break; } diff --git a/app/core/Http/ControllerResolver.php b/app/core/Http/ControllerResolver.php index 82b289132..b518b38a0 100644 --- a/app/core/Http/ControllerResolver.php +++ b/app/core/Http/ControllerResolver.php @@ -10,7 +10,6 @@ use DI\FactoryInterface; use Exception; -use Piwik\Plugin\Controller; use Piwik\Plugin\ReportsProvider; use Piwik\Plugin\WidgetsProvider; diff --git a/app/core/Mail/Transport.php b/app/core/Mail/Transport.php index edf6b1375..e88440b0a 100644 --- a/app/core/Mail/Transport.php +++ b/app/core/Mail/Transport.php @@ -15,7 +15,6 @@ use Piwik\Container\StaticContainer; use Piwik\Mail; use Piwik\Piwik; -use Piwik\Version; class Transport { diff --git a/app/core/Menu/MenuAbstract.php b/app/core/Menu/MenuAbstract.php index 2783d7f48..5a276dce2 100644 --- a/app/core/Menu/MenuAbstract.php +++ b/app/core/Menu/MenuAbstract.php @@ -98,10 +98,11 @@ protected function getAllMenus() * @param bool|string $tooltip An optional tooltip to display or false to display the tooltip. * @param bool|string $icon An icon classname, such as "icon-add". Only supported by admin menu * @param bool|string $onclick Will execute the on click handler instead of executing the link. Only supported by admin menu. + * @param string $attribute Will add this string as a link attribute. * @since 2.7.0 * @api */ - public function addItem($menuName, $subMenuName, $url, $order = 50, $tooltip = false, $icon = false, $onclick = false) + public function addItem($menuName, $subMenuName, $url, $order = 50, $tooltip = false, $icon = false, $onclick = false, $attribute = false) { // make sure the idSite value used is numeric (hack-y fix for #3426) if (isset($url['idSite']) && !is_numeric($url['idSite'])) { @@ -116,7 +117,8 @@ public function addItem($menuName, $subMenuName, $url, $order = 50, $tooltip = f $order, $tooltip, $icon, - $onclick + $onclick, + $attribute ); } @@ -144,7 +146,7 @@ public function remove($menuName, $subMenuName = false) * @param int $order * @param bool|string $tooltip Tooltip to display. */ - private function buildMenuItem($menuName, $subMenuName, $url, $order = 50, $tooltip = false, $icon = false, $onclick = false) + private function buildMenuItem($menuName, $subMenuName, $url, $order = 50, $tooltip = false, $icon = false, $onclick = false, $attribute = false) { if (!isset($this->menu[$menuName])) { $this->menu[$menuName] = array( @@ -158,17 +160,22 @@ private function buildMenuItem($menuName, $subMenuName, $url, $order = 50, $tool $this->menu[$menuName]['_order'] = $order; $this->menu[$menuName]['_name'] = $menuName; $this->menu[$menuName]['_tooltip'] = $tooltip; + $this->menu[$menuName]['_attribute'] = $attribute; if (!empty($this->menuIcons[$menuName])) { $this->menu[$menuName]['_icon'] = $this->menuIcons[$menuName]; } else { $this->menu[$menuName]['_icon'] = ''; } + if (!empty($onclick)) { + $this->menu[$menuName]['_onclick'] = $onclick; + } } if (!empty($subMenuName)) { $this->menu[$menuName][$subMenuName]['_url'] = $url; $this->menu[$menuName][$subMenuName]['_order'] = $order; $this->menu[$menuName][$subMenuName]['_name'] = $subMenuName; $this->menu[$menuName][$subMenuName]['_tooltip'] = $tooltip; + $this->menu[$menuName][$subMenuName]['_attribute'] = $attribute; $this->menu[$menuName][$subMenuName]['_icon'] = $icon; $this->menu[$menuName][$subMenuName]['_onclick'] = $onclick; $this->menu[$menuName]['_hasSubmenu'] = true; @@ -185,7 +192,7 @@ private function buildMenuItem($menuName, $subMenuName, $url, $order = 50, $tool private function buildMenu() { foreach ($this->menuEntries as $menuEntry) { - $this->buildMenuItem($menuEntry[0], $menuEntry[1], $menuEntry[2], $menuEntry[3], $menuEntry[4], $menuEntry[5], $menuEntry[6]); + $this->buildMenuItem($menuEntry[0], $menuEntry[1], $menuEntry[2], $menuEntry[3], $menuEntry[4], $menuEntry[5], $menuEntry[6], $menuEntry[7]); } } diff --git a/app/core/Metrics.php b/app/core/Metrics.php index 7cb3f26b8..cde226b60 100644 --- a/app/core/Metrics.php +++ b/app/core/Metrics.php @@ -9,7 +9,6 @@ namespace Piwik; use Piwik\Cache as PiwikCache; -use Piwik\Container\StaticContainer; require_once PIWIK_INCLUDE_PATH . "/core/Piwik.php"; diff --git a/app/core/NumberFormatter.php b/app/core/NumberFormatter.php index 694b94735..d0a3ebe7e 100644 --- a/app/core/NumberFormatter.php +++ b/app/core/NumberFormatter.php @@ -142,7 +142,7 @@ public function formatPercentEvolution($value) */ public function formatCurrency($value, $currency, $precision=2) { - $newValue = trim($value, " \0\x0B$currency"); + $newValue = trim(strval($value), " \0\x0B$currency"); if (!is_numeric($newValue)) { return $value; } diff --git a/app/core/Period/Range.php b/app/core/Period/Range.php index 1a4e381dc..cdebc2711 100644 --- a/app/core/Period/Range.php +++ b/app/core/Period/Range.php @@ -263,7 +263,7 @@ protected function generate() if (strpos($strDateEnd, '-') === false) { $timezone = $this->timezone; } - $endDate = Date::factory($strDateEnd, $timezone); + $endDate = Date::factory($strDateEnd, $timezone)->setTime("00:00:00"); } else { throw new Exception($this->translator->translate('General_ExceptionInvalidDateRange', array($this->strDate, ' \'lastN\', \'previousN\', \'YYYY-MM-DD,YYYY-MM-DD\''))); } @@ -498,6 +498,16 @@ public static function getDateXPeriodsAgo($subXPeriods, $date = false, $period = return array($strLastDate, $lastPeriod); } + /** + * Return the number of days contained in this range + * + * @return int + * @throws Exception + */ + public function getDayCount() + { + return (self::getNumDaysDifference($this->getDateStart(), $this->getDateEnd()) + 1); + } private static function getNumDaysDifference(Date $date1, Date $date2) { diff --git a/app/core/Piwik.php b/app/core/Piwik.php index e0c25d81d..0c9c1ff66 100644 --- a/app/core/Piwik.php +++ b/app/core/Piwik.php @@ -185,7 +185,7 @@ public static function getCurrentUserCreationDate() $user = APIUsersManager::getInstance()->getUser(Piwik::getCurrentUserLogin()); return $user['date_registered'] ?? ''; } - + /** * Returns the current user's Last Seen. * @@ -889,4 +889,32 @@ public static function translate($translationId, $args = array(), $language = nu return $translator->translate($translationId, $args, $language); } + + /** + * Returns the period provided in the current request. + * If no $default is provided, this method will throw an Exception if `period` can't be found in the request + * + * @param string|null $default default value to use + * @throws Exception + * @return string + * @api + */ + public static function getPeriod($default = null) + { + return Common::getRequestVar('period', $default, 'string'); + } + + /** + * Returns the date provided in the current request. + * If no $default is provided, this method will throw an Exception if `date` can't be found in the request + * + * @param string|null $default default value to use + * @throws Exception + * @return string + * @api + */ + public static function getDate($default = null) + { + return Common::getRequestVar('date', $default, 'string'); + } } diff --git a/app/core/Plugin.php b/app/core/Plugin.php index 109592635..4e1df7eba 100644 --- a/app/core/Plugin.php +++ b/app/core/Plugin.php @@ -648,6 +648,28 @@ private function makeDependency($piwikVersion) } return $dependency; } + + /** + * Get all changes for this plugin + * + * @return array Array of changes + * [{"title":"abc","description":"xyz","linkName":"def","link":"https://link","version":"1.2.3"}] + */ + public function getChanges() + { + $file = Manager::getPluginDirectory($this->pluginName).'/changes.json'; + if (file_exists($file)) { + $json = file_get_contents($file); + if ($json) { + $changes = json_decode($json, true); + if ($changes && is_array($changes)) { + return array_reverse($changes); + } + } + } + return []; + } + } } diff --git a/app/core/Plugin/ArchivedMetric.php b/app/core/Plugin/ArchivedMetric.php index c55d2fde5..20dff6953 100644 --- a/app/core/Plugin/ArchivedMetric.php +++ b/app/core/Plugin/ArchivedMetric.php @@ -147,10 +147,7 @@ public function format($value, Formatter $formatter) case Dimension::TYPE_DURATION_S: return $formatter->getPrettyTimeFromSeconds($value, $displayAsSentence = true); case Dimension::TYPE_DURATION_MS: - $val = number_format($value / 1000, 2); - if ($val > 60) { - $val = round($val); - } + $val = round(($value / 1000), ($value / 1000) > 60 ? 0 : 2); return $formatter->getPrettyTimeFromSeconds($val, $displayAsSentence = true); case Dimension::TYPE_PERCENT: return $formatter->getPrettyPercentFromQuotient($value); diff --git a/app/core/Plugin/ComputedMetric.php b/app/core/Plugin/ComputedMetric.php index e0daa970f..2039555d6 100644 --- a/app/core/Plugin/ComputedMetric.php +++ b/app/core/Plugin/ComputedMetric.php @@ -167,10 +167,7 @@ public function format($value, Formatter $formatter) case Dimension::TYPE_DURATION_S: return $formatter->getPrettyTimeFromSeconds(round($value), $displayAsSentence = true); case Dimension::TYPE_DURATION_MS: - $val = number_format($value / 1000, 2); - if ($val > 60) { - $val = round($val); - } + $val = round(($value / 1000), ($value / 1000) > 60 ? 0 : 2); return $formatter->getPrettyTimeFromSeconds($val, $displayAsSentence = true); case Dimension::TYPE_PERCENT: return $formatter->getPrettyPercentFromQuotient($value); diff --git a/app/core/Plugin/Controller.php b/app/core/Plugin/Controller.php index 241ef0632..2d0093912 100644 --- a/app/core/Plugin/Controller.php +++ b/app/core/Plugin/Controller.php @@ -476,25 +476,14 @@ protected function getLastUnitGraphAcrossPlugins($currentModuleName, $currentCon */ protected function getGraphParamsModified($paramsToSet = array()) { - if (!isset($paramsToSet['period'])) { - $period = Common::getRequestVar('period'); - } else { - $period = $paramsToSet['period']; - } + $period = $paramsToSet['period'] ?? Piwik::getPeriod(); + if ($period === 'range') { return $paramsToSet; } - if (!isset($paramsToSet['range'])) { - $range = 'last30'; - } else { - $range = $paramsToSet['range']; - } - if (!isset($paramsToSet['date'])) { - $endDate = $this->strDate; - } else { - $endDate = $paramsToSet['date']; - } + $range = isset($paramsToSet['range']) ? $paramsToSet['range'] : 'last30'; + $endDate = isset($paramsToSet['date']) ? $paramsToSet['date'] : $this->strDate; if (is_null($this->site)) { throw new NoAccessException("Website not initialized, check that you are logged in and/or using the correct token_auth."); @@ -640,10 +629,10 @@ protected function setGeneralVariablesViewAs($view, $viewType) $maxDate = Date::factory('now', $siteTimezone); $this->setMaxDateView($maxDate, $view); - $rawDate = Common::getRequestVar('date'); + $rawDate = Piwik::getDate(); Period::checkDateFormat($rawDate); - $periodStr = Common::getRequestVar('period'); + $periodStr = Piwik::getPeriod(); if ($periodStr !== 'range') { $date = Date::factory($this->strDate); @@ -923,14 +912,15 @@ public static function setPeriodVariablesView($view) $periodValidator = new PeriodValidator(); - $currentPeriod = Common::getRequestVar('period'); - $view->displayUniqueVisitors = SettingsPiwik::isUniqueVisitorsEnabled($currentPeriod); + $currentPeriod = Piwik::getPeriod(); $availablePeriods = $periodValidator->getPeriodsAllowedForUI(); if (! $periodValidator->isPeriodAllowedForUI($currentPeriod)) { throw new Exception("Period must be one of: " . implode(", ", $availablePeriods)); } + $view->displayUniqueVisitors = SettingsPiwik::isUniqueVisitorsEnabled($currentPeriod); + $found = array_search($currentPeriod, $availablePeriods); unset($availablePeriods[$found]); diff --git a/app/core/Plugin/Dimension/VisitDimension.php b/app/core/Plugin/Dimension/VisitDimension.php index 9ba020611..030ec18f9 100644 --- a/app/core/Plugin/Dimension/VisitDimension.php +++ b/app/core/Plugin/Dimension/VisitDimension.php @@ -12,6 +12,7 @@ use Piwik\Cache as PiwikCache; use Piwik\Columns\Dimension; use Piwik\Common; +use Piwik\DataTable; use Piwik\Db; use Piwik\DbHelper; use Piwik\Plugin\Manager as PluginManager; @@ -346,4 +347,51 @@ public static function getDimensions(Plugin $plugin) return $instances; } + + /** + * Sort a key => value array descending by the number of occurances of the key in the supplied table and column + * + * @param array $array Key value array + * @param DataTable $table Datatable from which to count occurances + * @param string $keyColumn Column in the datatable to match against the array key + * @param int $maxValuesToReturn Limit the return array to this number of elements + * + * @return array An array of values from the source array sorted by most occurances, descending + */ + public function sortStaticListByUsage(array $array, DataTable $table, string $keyColumn, int $maxValuesToReturn) : array + { + // Convert to multi-dimensional array and count the number of visits for each browser name + foreach ($array as $k => $v) { + $array[$k] = ['count' => 0, 'name' => $v]; + } + $array['xx'] = ['count' => 0, 'name' => 'Unknown']; + + foreach ($table->getRows() as $row) { + if (isset($row[$keyColumn])) { + if (isset($array[$row[$keyColumn]])) { + $array[$row[$keyColumn]]['count']++; + } else { + $array['xx']['count']++; + } + } + } + // Sort by most visits descending + uasort($array, function($a, $b) { + return $a <=> $b; + }); + $array = array_reverse($array, true); + + // Flatten and limit the return array + $flat = []; + $i = 0; + foreach ($array as $k => $v) { + $flat[$k] = $v['name']; + $i++; + if ($i == ($maxValuesToReturn)) { + break; + } + } + + return array_values($flat); + } } diff --git a/app/core/Plugin/Manager.php b/app/core/Plugin/Manager.php index a3339f027..61538b596 100644 --- a/app/core/Plugin/Manager.php +++ b/app/core/Plugin/Manager.php @@ -16,7 +16,6 @@ use Piwik\Config; use Piwik\Config as PiwikConfig; use Piwik\Container\StaticContainer; -use Piwik\Date; use Piwik\Development; use Piwik\EventDispatcher; use Piwik\Exception\PluginDeactivatedException; diff --git a/app/core/Plugin/MetadataLoader.php b/app/core/Plugin/MetadataLoader.php index a6fb42025..df478d543 100644 --- a/app/core/Plugin/MetadataLoader.php +++ b/app/core/Plugin/MetadataLoader.php @@ -11,7 +11,6 @@ use Exception; use Piwik\Piwik; use Piwik\Version; -use Piwik\Plugin; /** * @see core/Version.php diff --git a/app/core/Plugin/Segment.php b/app/core/Plugin/Segment.php index 505935755..8fd27241b 100644 --- a/app/core/Plugin/Segment.php +++ b/app/core/Plugin/Segment.php @@ -56,6 +56,7 @@ class Segment private $unionOfSegments; private $isInternalSegment = false; private $suggestedValuesApi = ''; + private $needsMostFrequentValues = true; /** * If true, this segment will only be visible to a registered user (see API.getSegmentsMetadata). @@ -323,6 +324,14 @@ public function setSuggestedValuesApi($suggestedValuesApi) $this->suggestedValuesApi = $suggestedValuesApi; } + /** + * @param bool $value + */ + public function setNeedsMostFrequentValues(bool $value) + { + $this->needsMostFrequentValues = $value; + } + /** * You can restrict the access to this segment by passing a boolean `false`. For instance if you want to make * a certain segment only available to users having super user access you could do the following: @@ -342,11 +351,12 @@ public function setPermission($permission) public function toArray() { $segment = array( - 'type' => $this->type, - 'category' => $this->category, - 'name' => $this->name, - 'segment' => $this->segment, - 'sqlSegment' => $this->sqlSegment, + 'type' => $this->type, + 'category' => $this->category, + 'name' => $this->name, + 'segment' => $this->segment, + 'sqlSegment' => $this->sqlSegment, + 'needsMostFrequentValues' => $this->needsMostFrequentValues, ); if (!empty($this->unionOfSegments)) { diff --git a/app/core/Plugin/Visualization.php b/app/core/Plugin/Visualization.php index 246a0a9cd..b2ff753f3 100644 --- a/app/core/Plugin/Visualization.php +++ b/app/core/Plugin/Visualization.php @@ -14,6 +14,7 @@ use Piwik\API\Request; use Piwik\API\Request as ApiRequest; use Piwik\API\ResponseBuilder; +use Piwik\ArchiveProcessor\Rules; use Piwik\Common; use Piwik\Container\StaticContainer; use Piwik\DataTable; @@ -156,7 +157,6 @@ class Visualization extends ViewDataTable private $templateVars = array(); private $reportLastUpdatedMessage = null; - private $metadata = null; protected $metricsFormatter = null; /** @@ -232,6 +232,7 @@ public function render() // if it's likely that the report data for this data table has been purged, // set whether we should display a message to that effect. $view->showReportDataWasPurgedMessage = $this->hasReportBeenPurged(); + $view->showPluginArchiveDisabled = $this->hasReportSegmentDisabled(); $view->deleteReportsOlderThan = Option::get('delete_reports_older_than'); } @@ -456,14 +457,35 @@ private function postDataTableLoadedFromAPI() } // deal w/ table metadata + $metadata = null; if ($this->dataTable instanceof DataTable) { - $this->metadata = $this->dataTable->getAllTableMetadata(); - - if (isset($this->metadata[DataTable::ARCHIVED_DATE_METADATA_NAME])) { - $this->reportLastUpdatedMessage = $this->makePrettyArchivedOnText(); + $metadata = $this->dataTable->getAllTableMetadata(); + } else { + // if the dataTable is Map + if ($this->dataTable instanceof DataTable\Map) { + // load all the data + $dataTable = $this->dataTable->getDataTables(); + // find the latest key + foreach ($dataTable as $item) { + $itemMetaData = $item->getAllTableMetadata(); + // initial metadata and update metadata if current is more recent + if (!empty($itemMetaData[DataTable::ARCHIVED_DATE_METADATA_NAME]) + && ( + empty($metadata[DataTable::ARCHIVED_DATE_METADATA_NAME]) + || strtotime($itemMetaData[DataTable::ARCHIVED_DATE_METADATA_NAME]) > strtotime($metadata[DataTable::ARCHIVED_DATE_METADATA_NAME]) + ) + ) { + $metadata = $itemMetaData; + } + } } } + // if metadata set display report date + if (!empty($metadata[DataTable::ARCHIVED_DATE_METADATA_NAME])) { + $this->reportLastUpdatedMessage = $this->makePrettyArchivedOnText($metadata[DataTable::ARCHIVED_DATE_METADATA_NAME]); + } + $pivotBy = Common::getRequestVar('pivotBy', false) ?: $this->requestConfig->pivotBy; if (empty($pivotBy) && $this->dataTable instanceof DataTable @@ -556,14 +578,16 @@ private function removeEmptyColumnsFromDisplay() } } + /** * Returns prettified and translated text that describes when a report was last updated. * + * @param $dateText * @return string + * @throws \Exception */ - private function makePrettyArchivedOnText() + private function makePrettyArchivedOnText($dateText) { - $dateText = $this->metadata[DataTable::ARCHIVED_DATE_METADATA_NAME]; $date = Date::factory($dateText); $today = mktime(0, 0, 0); $metricsFormatter = new HtmlFormatter(); @@ -600,6 +624,22 @@ private function hasReportBeenPurged() return PrivacyManager::hasReportBeenPurged($this->dataTable); } + /** + * Return true if the config for the plug is disabled + * @return bool + */ + + private function hasReportSegmentDisabled() + { + $module = $this->requestConfig->getApiModuleToRequest(); + $rawSegment = \Piwik\API\Request::getRawSegmentFromRequest(); + + if (!empty($rawSegment) && Rules::isSegmentPluginArchivingDisabled($module)) { + return true; + } + return false; + } + /** * Returns array of properties that should be visible to client side JavaScript. The data * will be available in the data-props HTML attribute of the .dataTable div. diff --git a/app/core/ProfessionalServices/Advertising.php b/app/core/ProfessionalServices/Advertising.php index 852a992d8..163b366e7 100644 --- a/app/core/ProfessionalServices/Advertising.php +++ b/app/core/ProfessionalServices/Advertising.php @@ -93,6 +93,7 @@ public function addPromoCampaignParametersToUrl($url, $campaignName, $campaignMe } /** + * @deprecated * Generates campaign URL parameters that can be used with promoting Professional Support service. * * @param string $campaignName diff --git a/app/core/ReportRenderer.php b/app/core/ReportRenderer.php index ce95e7e32..ee1cd6272 100644 --- a/app/core/ReportRenderer.php +++ b/app/core/ReportRenderer.php @@ -1,4 +1,5 @@ Piwik::translate('General_Name'), 'value' => Piwik::translate('General_Value'), - ); + ]; } - return array( + return [ $finalReport, $reportColumns, - ); + ]; } public static function getStaticGraph($reportMetadata, $width, $height, $evolution, $segment) diff --git a/app/core/ReportRenderer/Html.php b/app/core/ReportRenderer/Html.php index ef3029cf6..71b77c00d 100644 --- a/app/core/ReportRenderer/Html.php +++ b/app/core/ReportRenderer/Html.php @@ -8,7 +8,6 @@ */ namespace Piwik\ReportRenderer; -use Piwik\Mail; use Piwik\Piwik; use Piwik\ReportRenderer; use Piwik\View; diff --git a/app/core/ReportRenderer/Pdf.php b/app/core/ReportRenderer/Pdf.php index 3875f1e48..2ec74f473 100644 --- a/app/core/ReportRenderer/Pdf.php +++ b/app/core/ReportRenderer/Pdf.php @@ -12,10 +12,10 @@ use Piwik\Filesystem; use Piwik\NumberFormatter; use Piwik\Piwik; -use Piwik\Plugins\API\API; use Piwik\Plugins\CoreAdminHome\CustomLogo; use Piwik\ReportRenderer; use Piwik\TCPDF; +use TCPDF_FONTS; /** * @see libs/tcpdf @@ -40,6 +40,8 @@ class Pdf extends ReportRenderer const MAX_GRAPH_REPORTS = 3; const MAX_2COL_TABLE_REPORTS = 2; + const IMPORT_FONT_PATH = 'plugins/ImageGraph/fonts/unifont.ttf'; + const PDF_CONTENT_TYPE = 'pdf'; private $reportFontStyle = ''; @@ -136,6 +138,10 @@ public function setLocale($locale) } // WARNING: Did you read the warning above? + // When user follow the FAQ https://matomo.org/faq/how-to-install/faq_142/, imported unifont font, it will apply across the entire report + if (is_file(self::IMPORT_FONT_PATH)) { + $reportFont = TCPDF_FONTS::addTTFfont(self::IMPORT_FONT_PATH, 'TrueTypeUnicode'); + } $this->reportFont = $reportFont; } @@ -370,7 +376,7 @@ private function paintReportTable() $posX = $this->TCPDF->GetX(); $posY = $this->TCPDF->GetY(); if (isset($rowMetrics[$columnId])) { - $text = substr($rowMetrics[$columnId], 0, $this->truncateAfter); + $text = mb_substr($rowMetrics[$columnId], 0, $this->truncateAfter); if ($isLogoDisplayable) { $text = $leftSpacesBeforeLogo . $text; } diff --git a/app/core/Scheduler/RetryableException.php b/app/core/Scheduler/RetryableException.php new file mode 100644 index 000000000..70650089a --- /dev/null +++ b/app/core/Scheduler/RetryableException.php @@ -0,0 +1,9 @@ +scheduledTime = $scheduledTime; + } + } \ No newline at end of file diff --git a/app/core/Scheduler/Scheduler.php b/app/core/Scheduler/Scheduler.php index b126ccf7b..0d7b8296b 100644 --- a/app/core/Scheduler/Scheduler.php +++ b/app/core/Scheduler/Scheduler.php @@ -8,7 +8,6 @@ namespace Piwik\Scheduler; -use Exception; use Piwik\Piwik; use Piwik\Timer; use Psr\Log\LoggerInterface; @@ -54,6 +53,12 @@ class Scheduler */ private $isRunningTask = false; + /** + * Should the last run task be scheduled for a retry + * @var bool + */ + private $scheduleRetry = false; + /** * @var Timetable */ @@ -145,8 +150,36 @@ public function run() if ($shouldExecuteTask) { $readFromOption = true; + $this->scheduleRetry = false; $message = $this->executeTask($task); + // Task has thrown an exception and should be scheduled for a retry + if ($this->scheduleRetry) { + + if($this->timetable->getRetryCount($task->getName()) == 3) { + + // Task has already been retried three times, give up + $this->timetable->clearRetryCount($task->getName()); + + $this->logger->warning("Scheduler: '{task}' has already been retried three times, giving up", + ['task' => $task->getName()]); + + } else { + + $readFromOption = true; + $rescheduledDate = $this->timetable->rescheduleTaskAndRunInOneHour($task); + $this->timetable->incrementRetryCount($task->getName()); + + $this->logger->info("Scheduler: '{task}' retry scheduled for {date}", + ['task' => $task->getName(), 'date' => $rescheduledDate]); + } + $this->scheduleRetry = false; + } else { + if ($this->timetable->getRetryCount($task->getName()) > 0) { + $this->timetable->clearRetryCount($task->getName()); + } + } + $executionResults[] = array('task' => $taskName, 'output' => $message); } } @@ -275,8 +308,15 @@ private function executeTask($task) $callable = array($task->getObjectInstance(), $task->getMethodName()); call_user_func($callable, $task->getMethodParameter()); $message = $timer->__toString(); - } catch (Exception $e) { + } catch (\Exception $e) { + $this->logger->error("Scheduler: Error {errorMessage} for task '{task}'", + ['errorMessage' => $e->getMessage(), 'task' => $task->getName()]); $message = 'ERROR: ' . $e->getMessage(); + + // If the task has indicated that retrying on exception is safe then flag for rescheduling + if ($e instanceof RetryableException) { + $this->scheduleRetry = true; + } } $this->isRunningTask = false; diff --git a/app/core/Scheduler/Timetable.php b/app/core/Scheduler/Timetable.php index 47e6b094d..9073b73dc 100644 --- a/app/core/Scheduler/Timetable.php +++ b/app/core/Scheduler/Timetable.php @@ -19,8 +19,10 @@ class Timetable { const TIMETABLE_OPTION_STRING = "TaskScheduler.timetable"; + const RETRY_OPTION_STRING = "TaskScheduler.retryList"; private $timetable; + private $retryList; public function __construct() { @@ -37,6 +39,11 @@ public function setTimetable($timetable) $this->timetable = $timetable; } + public function setRetryList($retryList) + { + $this->retryList = $retryList; + } + /** * @param Task[] $activeTasks */ @@ -124,6 +131,17 @@ public function rescheduleTaskAndRunTomorrow(Task $task) return $tomorrow; } + public function rescheduleTaskAndRunInOneHour(Task $task) + { + $oneHourFromNow = Date::factory('now')->addHour(1); + + // update the scheduled time + $this->timetable[$task->getName()] = $oneHourFromNow->getTimestamp(); + $this->save(); + + return $oneHourFromNow; + } + public function save() { Option::set(self::TIMETABLE_OPTION_STRING, serialize($this->timetable)); @@ -149,4 +167,74 @@ public function readFromOption() $this->timetable = $unserializedTimetable === false ? array() : $unserializedTimetable; } + + /** + * Read the retry list option from the database + * + * @throws \Throwable + */ + private function readRetryList() + { + Option::clearCachedOption(self::RETRY_OPTION_STRING); + $retryData = Option::get(self::RETRY_OPTION_STRING); + $unserializedRetryList = Common::safe_unserialize($retryData); + + $this->retryList = $unserializedRetryList === false ? array() : $unserializedRetryList; + } + + /** + * Save the retry list option to the database + */ + public function saveRetryList() + { + Option::set(self::RETRY_OPTION_STRING, serialize($this->retryList)); + } + + /** + * Remove a task from the retry list + * + * @param string $taskName + */ + public function clearRetryCount(string $taskName) + { + if (isset($this->retryList[$taskName])) { + unset($this->retryList[$taskName]); + $this->saveRetryList(); + } + } + + /** + * Increment the retry counter for a task + * + * @param string $taskName + */ + public function incrementRetryCount(string $taskName) + { + $this->readRetryList(); + if (!isset($this->retryList[$taskName])) { + $this->retryList[$taskName] = 0; + } + $this->retryList[$taskName]++; + $this->saveRetryList(); + } + + /** + * Return the current number of retries for a task + * + * @param string $taskName + * + * @return int + */ + public function getRetryCount(string $taskName) : int + { + $this->readRetryList(); + + // Ignore excessive retry counts, workaround for SchedulerTest mock + if (!isset($this->retryList[$taskName]) || $this->retryList[$taskName] > 10000) { + return 0; + } + + return $this->retryList[$taskName]; + } + } diff --git a/app/core/Segment.php b/app/core/Segment.php index 4149f9b13..b39d9072a 100644 --- a/app/core/Segment.php +++ b/app/core/Segment.php @@ -11,12 +11,12 @@ use Exception; use Piwik\API\Request; use Piwik\ArchiveProcessor\Rules; +use Piwik\Cache as PiwikCache; use Piwik\Container\StaticContainer; use Piwik\DataAccess\LogQueryBuilder; use Piwik\Plugins\SegmentEditor\SegmentEditor; use Piwik\Segment\SegmentExpression; use Piwik\Plugins\SegmentEditor\Model as SegmentEditorModel; -use Piwik\Cache; /** * Limits the set of visits Piwik uses when aggregating analytics data. @@ -180,17 +180,30 @@ public function getSegmentExpression() return $this->segmentExpression; } + /** + * @throws Exception + */ private function getAvailableSegments() { + // start cache + $cache = PiwikCache::getTransientCache(); + + //covert cache id + $cacheId = 'API.getSegmentsMetadata.'.SettingsPiwik::getPiwikInstanceId() . '.' . implode(",", $this->idSites); + + //fetch cache lockId + $this->availableSegments = $cache->fetch($cacheId); // segment metadata if (empty($this->availableSegments)) { + $this->availableSegments = Request::processRequest('API.getSegmentsMetadata', array( - 'idSites' => $this->idSites, - '_hideImplementationData' => 0, - 'filter_limit' => -1, - 'filter_offset' => 0, - '_showAllSegments' => 1, + 'idSites' => $this->idSites, + '_hideImplementationData' => 0, + 'filter_limit' => -1, + 'filter_offset' => 0, + '_showAllSegments' => 1, ), []); + $cache->save($cacheId, $this->availableSegments); } return $this->availableSegments; diff --git a/app/core/Segment/SegmentsList.php b/app/core/Segment/SegmentsList.php index 82ea86065..448a4f159 100644 --- a/app/core/Segment/SegmentsList.php +++ b/app/core/Segment/SegmentsList.php @@ -132,6 +132,20 @@ public static function get() $dimension->configureSegments($list, new DimensionSegmentFactory($dimension)); } + /** + * Triggered to filter segment definitions. + * + * **Example** + * + * public function filterSegments(&$segmentList) + * { + * $segmentList->remove('Category'); + * } + * + * @param SegmentsList $list An instance of the SegmentsList. + */ + Piwik::postEvent('Segment.filterSegments', array($list)); + $cache->save($cacheKey, $list); return $list; diff --git a/app/core/Session.php b/app/core/Session.php index 641334676..096914e6e 100644 --- a/app/core/Session.php +++ b/app/core/Session.php @@ -11,6 +11,7 @@ use Exception; use Piwik\Container\StaticContainer; use Piwik\Exception\MissingFilePermissionException; +use Piwik\Plugins\Overlay\Overlay; use Piwik\Session\SaveHandler\DbTable; use Psr\Log\LoggerInterface; use Zend_Session; @@ -170,10 +171,11 @@ public static function getSameSiteCookieValue() $module = Piwik::getModule(); $action = Piwik::getAction(); + $method = Common::getRequestVar('method', '', 'string'); + $referer = Url::getReferrer(); $isOptOutRequest = $module == 'CoreAdminHome' && $action == 'optOut'; - $isOverlay = $module == 'Overlay'; - $shouldUseNone = !empty($general['enable_framed_pages']) || $isOptOutRequest || $isOverlay; + $shouldUseNone = !empty($general['enable_framed_pages']) || $isOptOutRequest || Overlay::isOverlayRequest($module, $action, $method, $referer); if ($shouldUseNone && ProxyHttp::isHttps()) { return 'None'; diff --git a/app/core/Session/SessionAuth.php b/app/core/Session/SessionAuth.php index bff91c113..0f2d1cada 100644 --- a/app/core/Session/SessionAuth.php +++ b/app/core/Session/SessionAuth.php @@ -11,11 +11,9 @@ use Piwik\Auth; use Piwik\AuthResult; -use Piwik\Common; use Piwik\Config; use Piwik\Container\StaticContainer; use Piwik\Date; -use Piwik\Plugins\UsersManager\Model; use Piwik\Plugins\UsersManager\Model as UsersModel; use Piwik\Session; use Psr\Log\LoggerInterface; diff --git a/app/core/Session/SessionFingerprint.php b/app/core/Session/SessionFingerprint.php index 231ba17ea..fa6a4fd22 100644 --- a/app/core/Session/SessionFingerprint.php +++ b/app/core/Session/SessionFingerprint.php @@ -9,7 +9,6 @@ namespace Piwik\Session; -use Piwik\Common; use Piwik\Config; use Piwik\Date; diff --git a/app/core/Settings/FieldConfig.php b/app/core/Settings/FieldConfig.php index bf9f9f46d..a6480216b 100644 --- a/app/core/Settings/FieldConfig.php +++ b/app/core/Settings/FieldConfig.php @@ -123,9 +123,21 @@ class FieldConfig * "formField.value" angular model. For an example see "plugins/CorePluginsAdmin/angularjs/form-field/field-text.html" * * @var string + * @deprecated set $customFieldComponent to ['plugin' => 'MyPlugin', 'component' => 'MyComponentAsItIsExported'] */ public $customUiControlTemplateFile = ''; + /** + * Defines a custom Vue component to use for the internal field UI control. This should be an array with two + * keys: + * + * - plugin: the name of the plugin that the UI control exists in. + * - name: the name of the export for the component in the plugin's Vue UMD module. + * + * @var string[] + */ + public $customFieldComponent; + /** * Name-value mapping of HTML attributes that will be added HTML form control, eg, * `array('size' => 3)`. Attributes will be escaped before outputting. diff --git a/app/core/Settings/FieldConfig/ArrayField.php b/app/core/Settings/FieldConfig/ArrayField.php index 8410cf4c0..9628e2925 100644 --- a/app/core/Settings/FieldConfig/ArrayField.php +++ b/app/core/Settings/FieldConfig/ArrayField.php @@ -39,6 +39,14 @@ class ArrayField */ public $customUiControlTemplateFile = ''; + /** + * Array like ['plugin' => 'MyPlugin', 'component' => 'MyExportedCustomFieldComponent']. For an example see + * "plugins/CorePluginsAdmin/vue/src/FormField/FieldText.vue" + * + * @var string[] + */ + public $customFieldComponent = null; + /** * This setting's display name, for example, `'Refresh Interval'`. * @@ -72,6 +80,7 @@ public function toArray() 'title' => $this->title, 'uiControl' => $this->uiControl, 'templateFile' => $this->customUiControlTemplateFile, + 'component' => $this->customFieldComponent, 'availableValues' => $this->availableValues, ); } diff --git a/app/core/Settings/FieldConfig/MultiPair.php b/app/core/Settings/FieldConfig/MultiPair.php index 40b27c97b..22788e76b 100644 --- a/app/core/Settings/FieldConfig/MultiPair.php +++ b/app/core/Settings/FieldConfig/MultiPair.php @@ -44,9 +44,18 @@ class MultiPair * "formField.value" angular model. For an example see "plugins/CorePluginsAdmin/angularjs/form-field/field-text.html" * * @var string + * @deprecated use customFieldComponent instead */ public $customUiControlTemplateFile = ''; + /** + * Array like ['plugin' => 'MyPlugin', 'component' => 'MyExportedCustomFieldComponent']. For an example see + * "plugins/CorePluginsAdmin/vue/src/FormField/FieldText.vue" + * + * @var string[] + */ + public $customFieldComponent = null; + /** * This setting's display name, for example, `'Refresh Interval'`. * @@ -82,6 +91,7 @@ public function toArray() 'title' => $this->title, 'uiControl' => $this->uiControl, 'templateFile' => $this->customUiControlTemplateFile, + 'component' => $this->customFieldComponent, 'availableValues' => $this->availableValues, ); } diff --git a/app/core/SettingsPiwik.php b/app/core/SettingsPiwik.php index 14f4547d2..a78f0a84c 100644 --- a/app/core/SettingsPiwik.php +++ b/app/core/SettingsPiwik.php @@ -31,7 +31,7 @@ public static function getSalt(): ?string { static $salt = null; if (is_null($salt)) { - $salt = @Config::getInstance()->General['salt']; + $salt = Config::getInstance()->General['salt'] ?? ''; } return $salt; } diff --git a/app/core/SupportedBrowser.php b/app/core/SupportedBrowser.php index 6f9abe631..3f9f195df 100644 --- a/app/core/SupportedBrowser.php +++ b/app/core/SupportedBrowser.php @@ -9,7 +9,6 @@ namespace Piwik; -use Piwik\Http; use Piwik\Piwik; use Piwik\Container\StaticContainer; use Piwik\DeviceDetector\DeviceDetectorFactory; diff --git a/app/core/Tracker.php b/app/core/Tracker.php index 9d813c079..90485e1a5 100644 --- a/app/core/Tracker.php +++ b/app/core/Tracker.php @@ -1,4 +1,5 @@ init(); + + if ($this->isPreFlightCorsRequest()) { + Common::sendHeader('Access-Control-Allow-Methods: GET, POST'); + Common::sendHeader('Access-Control-Allow-Headers: *'); + Common::sendHeader('Access-Control-Allow-Origin: *'); + Common::sendResponseCode(204); + $this->logger->debug("Tracker detected preflight CORS request. Skipping..."); + return null; + } + $handler->init($this, $requestSet); $this->track($handler, $requestSet); } catch (Exception $e) { - StaticContainer::get(LoggerInterface::class)->debug("Tracker encountered an exception: {ex}", [$e]); + $this->logger->debug("Tracker encountered an exception: {ex}", [$e]); $handler->onException($this, $requestSet, $e); } @@ -171,7 +183,8 @@ public function trackRequest(Request $request) */ public static function initCorePiwikInTrackerMode() { - if (SettingsServer::isTrackerApiRequest() + if ( + SettingsServer::isTrackerApiRequest() && self::$initTrackerMode === false ) { self::$initTrackerMode = true; @@ -291,7 +304,8 @@ public static function setTestEnvironment($args = null, $requestMethod = null) } // Tests using window_look_back_for_visitor - if (Common::getRequestVar('forceLargeWindowLookBackForVisitor', false, null, $args) == 1 + if ( + Common::getRequestVar('forceLargeWindowLookBackForVisitor', false, null, $args) == 1 // also look for this in bulk requests (see fake_logs_replay.log) || strpos(json_encode($args, true), '"forceLargeWindowLookBackForVisitor":"1"') !== false ) { @@ -330,7 +344,8 @@ protected function loadTrackerPlugins() private function handleFatalErrors() { - register_shutdown_function(function () { // TODO: add a log here + register_shutdown_function(function () { + // TODO: add a log here $lastError = error_get_last(); if (!empty($lastError) && $lastError['type'] == E_ERROR) { Common::sendResponseCode(500); @@ -355,4 +370,12 @@ private static function isDebugEnabled() return false; } + + public function isPreFlightCorsRequest(): bool + { + if (isset($_SERVER['REQUEST_METHOD']) && strtoupper($_SERVER['REQUEST_METHOD']) === 'OPTIONS') { + return !empty($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']) || !empty($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']); + } + return false; + } } diff --git a/app/core/Tracker/Action.php b/app/core/Tracker/Action.php index bd0cc078c..13baf6c0d 100644 --- a/app/core/Tracker/Action.php +++ b/app/core/Tracker/Action.php @@ -381,6 +381,8 @@ public function loadIdsFromLogActionTable() */ public function record(Visitor $visitor, $idReferrerActionUrl, $idReferrerActionName) { + + $this->loadIdsFromLogActionTable(); $visitAction = array( diff --git a/app/core/Tracker/Cache.php b/app/core/Tracker/Cache.php index 4cce6e413..67ab93b71 100644 --- a/app/core/Tracker/Cache.php +++ b/app/core/Tracker/Cache.php @@ -17,7 +17,6 @@ use Piwik\Piwik; use Piwik\Tracker; use Psr\Log\LoggerInterface; -use function DI\object; /** * Simple cache mechanism used in Tracker to avoid requesting settings from mysql on every request diff --git a/app/core/Tracker/Db.php b/app/core/Tracker/Db.php index 22a9173a8..a6bce56a7 100644 --- a/app/core/Tracker/Db.php +++ b/app/core/Tracker/Db.php @@ -30,6 +30,9 @@ abstract class Db protected $connection = null; + // this is used for indicate TransactionLevel Cache + public $supportsUncommitted = null; + /** * Enables the SQL profiling. * For each query, saves in the DB the time spent on this query. diff --git a/app/core/Tracker/Db/Pdo/Mysql.php b/app/core/Tracker/Db/Pdo/Mysql.php index 1e83b7135..8b1a53670 100644 --- a/app/core/Tracker/Db/Pdo/Mysql.php +++ b/app/core/Tracker/Db/Pdo/Mysql.php @@ -12,7 +12,6 @@ use PDO; use PDOException; use PDOStatement; -use Piwik\Config; use Piwik\Tracker\Db; use Piwik\Tracker\Db\DbException; diff --git a/app/core/Tracker/FingerprintSalt.php b/app/core/Tracker/FingerprintSalt.php index 9896c074c..c3d056ba7 100644 --- a/app/core/Tracker/FingerprintSalt.php +++ b/app/core/Tracker/FingerprintSalt.php @@ -11,13 +11,7 @@ use Piwik\Common; use Piwik\Date; -use Piwik\Exception\InvalidRequestParameterException; -use Piwik\Exception\UnexpectedWebsiteFoundException; use Piwik\Option; -use Piwik\Piwik; -use Piwik\SettingsServer; -use Piwik\Site; -use Piwik\Db as PiwikDb; class FingerprintSalt { diff --git a/app/core/Tracker/GoalManager.php b/app/core/Tracker/GoalManager.php index 1d1e3314d..ea37d447b 100644 --- a/app/core/Tracker/GoalManager.php +++ b/app/core/Tracker/GoalManager.php @@ -19,7 +19,6 @@ use Piwik\Plugin\Manager; use Piwik\Plugins\CustomVariables\CustomVariables; use Piwik\Plugins\Events\Actions\ActionEvent; -use Piwik\Tracker; use Piwik\Tracker\Visit\VisitProperties; /** @@ -561,8 +560,10 @@ private function getCleanedEcommerceItems($items) foreach ($cleanedItems as $item) { $actionsToLookup = array(); - list($sku, $name, $category, $price, $quantity) = $item; + list($sku_check, $name_check, $category, $price, $quantity) = $item; + $sku = is_array($sku_check) ? join(',', $sku_check) : $sku_check; $actionsToLookup[] = array(trim($sku), Action::TYPE_ECOMMERCE_ITEM_SKU); + $name = is_array($name_check) ? join(',', $name_check) : $name_check; $actionsToLookup[] = array(trim($name), Action::TYPE_ECOMMERCE_ITEM_NAME); // Only one category diff --git a/app/core/Tracker/PageUrl.php b/app/core/Tracker/PageUrl.php index 0d875145a..a500877c9 100644 --- a/app/core/Tracker/PageUrl.php +++ b/app/core/Tracker/PageUrl.php @@ -287,8 +287,8 @@ public static function reencodeParameters(&$queryParameters, $encoding = false) if (function_exists('mb_check_encoding')) { // if query params are encoded w/ non-utf8 characters (due to browser bug or whatever), // encode to UTF-8. - if (strtolower($encoding) != 'utf-8' - && $encoding != false + if (is_string($encoding) && + strtolower($encoding) !== 'utf-8' ) { Common::printDebug("Encoding page URL query parameters to $encoding."); diff --git a/app/core/Tracker/Request.php b/app/core/Tracker/Request.php index 2dd8be557..2b1496f45 100644 --- a/app/core/Tracker/Request.php +++ b/app/core/Tracker/Request.php @@ -10,7 +10,6 @@ use Exception; use Piwik\Common; -use Piwik\Config; use Piwik\Container\StaticContainer; use Piwik\Cookie; use Piwik\Exception\InvalidRequestParameterException; @@ -18,7 +17,6 @@ use Piwik\IP; use Matomo\Network\IPUtils; use Piwik\Piwik; -use Piwik\Plugins\PrivacyManager\PrivacyManager; use Piwik\Plugins\UsersManager\UsersManager; use Piwik\ProxyHttp; use Piwik\Segment\SegmentExpression; @@ -46,6 +44,8 @@ class Request protected $tokenAuth; + + /** * Stores plugin specific tracking request metadata. RequestProcessors can store * whatever they want in this array, and other RequestProcessors can modify these @@ -74,6 +74,7 @@ public function __construct($params, $tokenAuth = false) $this->timestamp = time(); $this->isEmptyRequest = empty($params); + // When the 'url' and referrer url parameter are not given, we might be in the 'Simple Image Tracker' mode. // The URL can default to the Referrer, which will be in this case // the URL of the page containing the Simple Image beacon @@ -923,4 +924,5 @@ private function getVisitorIdAsBinary($idVisitor) } return false; } + } diff --git a/app/core/Tracker/RequestSet.php b/app/core/Tracker/RequestSet.php index d243f4550..7249ea226 100644 --- a/app/core/Tracker/RequestSet.php +++ b/app/core/Tracker/RequestSet.php @@ -36,7 +36,7 @@ public function setRequests($requests) if (empty($requests)|| !is_array($requests)) { return; } - + foreach ($requests as $request) { if (empty($request) && !is_array($request)) { continue; @@ -45,7 +45,6 @@ public function setRequests($requests) if (!$request instanceof Request) { $request = new Request($request, $this->getTokenAuth()); } - $this->requests[] = $request; } } diff --git a/app/core/Tracker/Response.php b/app/core/Tracker/Response.php old mode 100755 new mode 100644 index 9e1a63c84..e99feb8b3 --- a/app/core/Tracker/Response.php +++ b/app/core/Tracker/Response.php @@ -1,4 +1,5 @@ isDebugModeEnabled() + if ( + $tracker->isDebugModeEnabled() && $tracker->isDatabaseConnected() && TrackerDb::isProfilingEnabled() ) { @@ -190,7 +194,7 @@ private function outputCustomImage(string $customImage): bool // Base64 image string $img = base64_decode($customImage); $size = getimagesizefromstring($img); - } else if (is_file($customImage) && is_readable($customImage)) { + } elseif (is_file($customImage) && is_readable($customImage)) { // Image file $img = file_get_contents($customImage); $size = getimagesize($customImage); // imagesize is used to get the mime type @@ -198,7 +202,7 @@ private function outputCustomImage(string $customImage): bool // Must have valid image data and a valid mime type to proceed if ($img && $size && isset($size['mime']) && in_array($size['mime'], $supportedMimeTypes)) { - Common::sendHeader('Content-Type: '.$size['mime']); + Common::sendHeader('Content-Type: ' . $size['mime']); echo $img; return true; } @@ -229,6 +233,8 @@ protected function getMessageFromException($e) protected function logExceptionToErrorLog($e) { - error_log(sprintf("Error in Matomo (tracker): %s", str_replace("\n", " ", $this->getMessageFromException($e)))); + $hostname = Url::getRFCValidHostname(); + $hostStr = $hostname ? "[$hostname]" : '-'; + error_log(sprintf("$hostStr Error in Matomo (tracker): %s", str_replace("\n", " ", $this->getMessageFromException($e)))); } } diff --git a/app/core/Twig.php b/app/core/Twig.php index 4a0960a4b..4aa9e6b4e 100644 --- a/app/core/Twig.php +++ b/app/core/Twig.php @@ -42,26 +42,7 @@ function piwik_format_number($string, $minFractionDigits, $maxFractionDigits) function piwik_fix_lbrace($string) { - $chars = array('{', '{', '{', '{', '{', '{'); - - static $search; - static $replace; - - if (!isset($search)) { - $search = array_map(function ($val) { return $val . $val; }, $chars); - } - if (!isset($replace)) { - $replace = array_map(function ($val) { return $val . '⁣' . $val; }, $chars); - } - - $replacedString = is_null($string) ? $string : str_replace($search, $replace, $string); - - // try to replace characters until there are no changes - if ($string !== $replacedString) { - return piwik_fix_lbrace($replacedString); - } - - return $string; + return Common::fixLbrace($string); } function piwik_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) { @@ -153,13 +134,13 @@ public function __construct() $chainLoader = new ChainLoader($loaders); // Create new Twig Environment and set cache dir - $templatesCompiledPath = StaticContainer::get('path.tmp.templates'); + $cache = StaticContainer::get('twig.cache'); $this->twig = new Environment($chainLoader, array( 'debug' => true, // to use {{ dump(var) }} in twig templates 'strict_variables' => true, // throw an exception if variables are invalid - 'cache' => $templatesCompiledPath, + 'cache' => $cache, ) ); $this->twig->addExtension(new DebugExtension()); @@ -185,6 +166,9 @@ public function __construct() $this->twig->addFilter(new TwigFilter('ucwords', 'ucwords')); $this->twig->addFilter(new TwigFilter('lcfirst', 'lcfirst')); $this->twig->addFilter(new TwigFilter('ucfirst', 'ucfirst')); + $this->twig->addFilter(new TwigFilter('preg_replace', function ($subject, $pattern, $replacement) { + return preg_replace($pattern, $replacement, $subject); + })); $this->addFunction_includeAssets(); $this->addFunction_linkTo(); @@ -376,6 +360,11 @@ protected function addFilter_notification() protected function addFilter_safeDecodeRaw() { $rawSafeDecoded = new TwigFilter('rawSafeDecoded', function ($string) { + + if ($string === null) { + return ''; + } + $string = str_replace('+', '%2B', $string); $string = str_replace(' ', html_entity_decode(' ', ENT_COMPAT | ENT_HTML401, 'UTF-8'), $string); diff --git a/app/core/Updater.php b/app/core/Updater.php index 407a0b72d..5c01f70f4 100644 --- a/app/core/Updater.php +++ b/app/core/Updater.php @@ -233,8 +233,8 @@ public function hasMajorDbUpdate() */ public function getSqlQueriesToExecute() { - $queries = array(); - $classNames = array(); + $queries = []; + $classNames = []; foreach ($this->componentsWithUpdateFile as $componentName => $componentUpdateInfo) { foreach ($componentUpdateInfo as $file => $fileVersion) { @@ -253,14 +253,16 @@ public function getSqlQueriesToExecute() $classNames[] = $className; - /** @var Updates $update */ - $update = StaticContainer::getContainer()->make($className); - $migrationsForComponent = $update->getMigrations($this); + $migrationsForComponent = Access::doAsSuperUser(function() use ($className) { + /** @var Updates $update */ + $update = StaticContainer::getContainer()->make($className); + return $update->getMigrations($this); + }); foreach ($migrationsForComponent as $index => $migration) { $migration = $this->keepBcForOldMigrationQueryFormat($index, $migration); $queries[] = $migration; } - $this->hasMajorDbUpdate = $this->hasMajorDbUpdate || call_user_func(array($className, 'isMajorUpdate')); + $this->hasMajorDbUpdate = $this->hasMajorDbUpdate || call_user_func([$className, 'isMajorUpdate']); } } return $queries; @@ -477,40 +479,34 @@ public function updateComponents($componentsWithUpdateFile) } if (!empty($componentsWithUpdateFile)) { - $currentAccess = Access::getInstance(); - $hasSuperUserAccess = $currentAccess->hasSuperUserAccess(); - if (!$hasSuperUserAccess) { - $currentAccess->setSuperUserAccess(true); - } - - $pluginManager = \Piwik\Plugin\Manager::getInstance(); - - // if error in any core update, show message + help message + EXIT - // if errors in any plugins updates, show them on screen, disable plugins that errored + CONTINUE - // if warning in any core update or in any plugins update, show message + CONTINUE - // if no error or warning, success message + CONTINUE - foreach ($componentsWithUpdateFile as $name => $filenames) { - try { - $warnings = array_merge($warnings, $this->update($name)); - } catch (UpdaterErrorException $e) { - $errors[] = $e->getMessage(); - if ($name == 'core') { - $coreError = true; - break; - } elseif ($pluginManager->isPluginActivated($name) && $pluginManager->isPluginBundledWithCore($name)) { - $coreError = true; - break; - } elseif ($pluginManager->isPluginActivated($name)) { - $pluginManager->deactivatePlugin($name); - $deactivatedPlugins[] = $name; + Access::doAsSuperUser(function() use ($componentsWithUpdateFile, &$coreError, &$deactivatedPlugins, &$errors, &$warnings) { + + $pluginManager = \Piwik\Plugin\Manager::getInstance(); + + // if error in any core update, show message + help message + EXIT + // if errors in any plugins updates, show them on screen, disable plugins that errored + CONTINUE + // if warning in any core update or in any plugins update, show message + CONTINUE + // if no error or warning, success message + CONTINUE + foreach ($componentsWithUpdateFile as $name => $filenames) { + try { + $warnings = array_merge($warnings, $this->update($name)); + } catch (UpdaterErrorException $e) { + $errors[] = $e->getMessage(); + if ($name == 'core') { + $coreError = true; + break; + } elseif ($pluginManager->isPluginActivated($name) && $pluginManager->isPluginBundledWithCore($name)) { + $coreError = true; + break; + } elseif ($pluginManager->isPluginActivated($name)) { + $pluginManager->deactivatePlugin($name); + $deactivatedPlugins[] = $name; + } } } - } - if (!$hasSuperUserAccess) { - $currentAccess->setSuperUserAccess(false); - } + }); } Filesystem::deleteAllCacheOnUpdate(); diff --git a/app/core/Updater/Migration/Custom.php b/app/core/Updater/Migration/Custom.php index c69dca9fd..3e4e92bea 100644 --- a/app/core/Updater/Migration/Custom.php +++ b/app/core/Updater/Migration/Custom.php @@ -7,8 +7,6 @@ */ namespace Piwik\Updater\Migration; -use Piwik\Config; -use Piwik\Container\StaticContainer; use Piwik\Updater\Migration; /** diff --git a/app/core/Updater/Migration/Db/DropColumns.php b/app/core/Updater/Migration/Db/DropColumns.php index 5df61ddf0..f71c17969 100644 --- a/app/core/Updater/Migration/Db/DropColumns.php +++ b/app/core/Updater/Migration/Db/DropColumns.php @@ -7,7 +7,6 @@ */ namespace Piwik\Updater\Migration\Db; -use Piwik\Db; use Piwik\DbHelper; /** diff --git a/app/core/Updates/4.0.0-b1.php b/app/core/Updates/4.0.0-b1.php index 13c7bdd86..1aaa6cfee 100644 --- a/app/core/Updates/4.0.0-b1.php +++ b/app/core/Updates/4.0.0-b1.php @@ -25,7 +25,6 @@ use Piwik\Plugins\PagePerformance\Columns\TimeOnLoad; use Piwik\Plugins\PagePerformance\Columns\TimeServer; use Piwik\Plugins\PagePerformance\Columns\TimeTransfer; -use Piwik\Plugins\UsersManager\Model; use Piwik\Common; use Piwik\Config; use Piwik\Plugins\UserCountry\LocationProvider; diff --git a/app/core/Updates/4.0.0-rc3.php b/app/core/Updates/4.0.0-rc3.php index 86c9ca018..cda763e66 100644 --- a/app/core/Updates/4.0.0-rc3.php +++ b/app/core/Updates/4.0.0-rc3.php @@ -9,7 +9,6 @@ namespace Piwik\Updates; -use Piwik\Config; use Piwik\Updater; use Piwik\Updates as PiwikUpdates; use Piwik\Updater\Migration\Factory as MigrationFactory; diff --git a/app/core/Updates/4.0.0-rc4.php b/app/core/Updates/4.0.0-rc4.php index ad408f1ed..2315f491c 100644 --- a/app/core/Updates/4.0.0-rc4.php +++ b/app/core/Updates/4.0.0-rc4.php @@ -9,7 +9,6 @@ namespace Piwik\Updates; -use Piwik\Config; use Piwik\Container\StaticContainer; use Piwik\DataAccess\ArchiveTableCreator; use Piwik\Date; diff --git a/app/core/Updates/4.0.1-b1.php b/app/core/Updates/4.0.1-b1.php index 852b3b934..c3b4d503d 100644 --- a/app/core/Updates/4.0.1-b1.php +++ b/app/core/Updates/4.0.1-b1.php @@ -10,12 +10,6 @@ namespace Piwik\Updates; use Piwik\Common; -use Piwik\Config; -use Piwik\Container\StaticContainer; -use Piwik\DataAccess\ArchiveTableCreator; -use Piwik\Date; -use Piwik\DbHelper; -use Piwik\Plugin\ReleaseChannels; use Piwik\SettingsPiwik; use Piwik\Updater; use Piwik\Updates as PiwikUpdates; diff --git a/app/core/Updates/4.1.2-b2.php b/app/core/Updates/4.1.2-b2.php index 0c38c3848..bbabf2ca9 100644 --- a/app/core/Updates/4.1.2-b2.php +++ b/app/core/Updates/4.1.2-b2.php @@ -9,13 +9,7 @@ namespace Piwik\Updates; -use Piwik\Container\StaticContainer; -use Piwik\CronArchive; use Piwik\DataAccess\ArchiveTableCreator; -use Piwik\Date; -use Piwik\Plugins\SegmentEditor\API; -use Piwik\Archive\ArchiveInvalidator; -use Piwik\ArchiveProcessor\Rules; use Piwik\Updater; use Piwik\Updates as PiwikUpdates; use Piwik\Updater\Migration\Factory as MigrationFactory; diff --git a/app/core/Updates/4.10.0-b1.php b/app/core/Updates/4.10.0-b1.php new file mode 100644 index 000000000..df8d40804 --- /dev/null +++ b/app/core/Updates/4.10.0-b1.php @@ -0,0 +1,61 @@ +migration = $factory; + } + + /** + * @param Updater $updater + * + * @return Migration[] + */ + public function getMigrations(Updater $updater) + { + $table = Common::prefixTable('report'); + $invalidCount = Db::fetchOne( + "SELECT COUNT(*) FROM $table WHERE reports = ? OR parameters = ?", + ['Array', 'Array'] + ); + + if (0 === (int) $invalidCount) { + return []; + } + + return [ + $this->migration->db->sql("DELETE FROM " . $table . " WHERE reports = 'Array' OR parameters = 'Array'") + ]; + } + + public function doUpdate(Updater $updater) + { + $updater->executeMigrations(__FILE__, $this->getMigrations($updater)); + } +} diff --git a/app/core/Updates/4.3.0-b3.php b/app/core/Updates/4.3.0-b3.php index d9588f1d3..14a79f575 100644 --- a/app/core/Updates/4.3.0-b3.php +++ b/app/core/Updates/4.3.0-b3.php @@ -11,10 +11,7 @@ use Piwik\Updater; use Piwik\Updates as PiwikUpdates; -use Piwik\Updater\Migration; use Piwik\Updater\Migration\Factory as MigrationFactory; -use Piwik\Db; -use Piwik\Common; /** * Update for version 4.3.0-b3. diff --git a/app/core/Updates/4.3.0-b4.php b/app/core/Updates/4.3.0-b4.php index de579b9c1..f0ced8b9e 100644 --- a/app/core/Updates/4.3.0-b4.php +++ b/app/core/Updates/4.3.0-b4.php @@ -11,7 +11,6 @@ use Piwik\Updater; use Piwik\Updates as PiwikUpdates; -use Piwik\Updater\Migration; use Piwik\Updater\Migration\Factory as MigrationFactory; use Piwik\Db; use Piwik\Common; diff --git a/app/core/Updates/4.3.0-rc2.php b/app/core/Updates/4.3.0-rc2.php index 924699772..09ad6f2f2 100644 --- a/app/core/Updates/4.3.0-rc2.php +++ b/app/core/Updates/4.3.0-rc2.php @@ -11,10 +11,7 @@ use Piwik\Updater; use Piwik\Updates as PiwikUpdates; -use Piwik\Updater\Migration; use Piwik\Updater\Migration\Factory as MigrationFactory; -use Piwik\Db; -use Piwik\Common; /** * Update for version 4.3.0-rc2. diff --git a/app/core/Updates/4.5.0-b1.php b/app/core/Updates/4.5.0-b1.php index 1ac0cae44..3cd30ac85 100644 --- a/app/core/Updates/4.5.0-b1.php +++ b/app/core/Updates/4.5.0-b1.php @@ -9,7 +9,6 @@ namespace Piwik\Updates; -use Piwik\DbHelper; use Piwik\Updater; use Piwik\Updates as PiwikUpdates; use Piwik\Updater\Migration\Factory as MigrationFactory; diff --git a/app/core/Updates/4.6.0-b1.php b/app/core/Updates/4.6.0-b1.php index 557ed9b95..d48a72ea4 100644 --- a/app/core/Updates/4.6.0-b1.php +++ b/app/core/Updates/4.6.0-b1.php @@ -11,7 +11,6 @@ use Piwik\Updater; use Piwik\Updates as PiwikUpdates; -use Piwik\Updater\Migration; use Piwik\Updater\Migration\Factory as MigrationFactory; class Updates_4_6_0_b1 extends PiwikUpdates diff --git a/app/core/Updates/4.7.0-b2.php b/app/core/Updates/4.7.0-b2.php new file mode 100644 index 000000000..7d57ee147 --- /dev/null +++ b/app/core/Updates/4.7.0-b2.php @@ -0,0 +1,67 @@ +migration = $factory; + } + + /** + * Here you can define one or multiple SQL statements that should be executed during the update. + * + * @param Updater $updater + * + * @return Migration[] + */ + public function getMigrations(Updater $updater) + { + $migrations = []; + + // add column to track the last change a user viewed the changes list + $migrations[] = $this->migration->db->addColumn('user', 'idchange_last_viewed', + 'INTEGER UNSIGNED NULL'); + + $migrations[] = $this->migration->db->createTable('changes', array( + 'idchange' => 'INT(11) NOT NULL AUTO_INCREMENT', + 'created_time' => 'DATETIME NOT NULL', + 'plugin_name' => 'VARCHAR(255) NOT NULL', + 'version' => 'VARCHAR(20) NOT NULL', + 'title' => 'VARCHAR(255) NOT NULL', + 'description' => 'TEXT NOT NULL', + 'link_name' => 'VARCHAR(255) NULL', + 'link' => 'VARCHAR(255) NULL', + ), $primaryKey = 'idchange'); + + return $migrations; + } + + public function doUpdate(Updater $updater) + { + $updater->executeMigrations(__FILE__, $this->getMigrations($updater)); + } + +} diff --git a/app/core/Updates/4.7.1-b1.php b/app/core/Updates/4.7.1-b1.php new file mode 100644 index 000000000..b543b812b --- /dev/null +++ b/app/core/Updates/4.7.1-b1.php @@ -0,0 +1,57 @@ +migration = $factory; + } + + /** + * Here you can define one or multiple SQL statements that should be executed during the update. + * + * @param Updater $updater + * + * @return Migration[] + */ + public function getMigrations(Updater $updater) + { + $migrations = []; + + $migrations[] = $this->migration->db->changeColumn('changes', 'plugin_name', 'plugin_name', 'VARCHAR(60) NOT NULL'); + + $migrations[] = $this->migration->db->dropIndex('changes', 'unique_plugin_version_title'); + $migrations[] = $this->migration->db->addUniqueKey('changes', ['plugin_name', 'version', 'title(100)'], 'unique_plugin_version_title'); + + return $migrations; + } + + public function doUpdate(Updater $updater) + { + $updater->executeMigrations(__FILE__, $this->getMigrations($updater)); + } + +} diff --git a/app/core/Updates/5.0.0-b1.php b/app/core/Updates/5.0.0-b1.php new file mode 100644 index 000000000..883788132 --- /dev/null +++ b/app/core/Updates/5.0.0-b1.php @@ -0,0 +1,91 @@ +migration = $factory; + + $this->tableName = Common::prefixTable('log_visit'); + $this->indexName = 'index_idsite_idvisitor'; + } + + public function doUpdate(Updater $updater) + { + $updater->executeMigrations(__FILE__, $this->getMigrations($updater)); + } + + public function getMigrations(Updater $updater) + { + if ($this->requiresUpdatedLogVisitTableIndex()) { + return $this->getLogVisitTableMigrations(); + } + + return []; + } + + private function getLogVisitTableMigrations() + { + $migrations = []; + + $migrations[] = $this->migration->db->dropIndex('log_visit', $this->indexName); + + // Using the custom `sql` method instead of the `addIndex` method as it doesn't support DESC collation + $migrations[] = $this->migration->db->sql( + "ALTER TABLE `{$this->tableName}` ADD INDEX `{$this->indexName}` (`idsite`, `idvisitor`, `visit_last_action_time` DESC)", + [DbAlias::ERROR_CODE_DUPLICATE_KEY, DbAlias::ERROR_CODE_KEY_COLUMN_NOT_EXISTS] + ); + + return $migrations; + } + + private function requiresUpdatedLogVisitTableIndex() + { + $sql = "SHOW INDEX FROM `{$this->tableName}` WHERE Key_name = '{$this->indexName}'"; + + $result = Db::fetchAll($sql); + + if (empty($result)) { + // No index present - should be added + return true; + } + + // Check that the $result contains all the required column names. This is required as there was a previous index + // with the same name that only consisted of two columns. We want to check this index is built with all three. + // $diff will be empty if all three columns are found, meaning that the index already exists. + $diff = array_diff(['idsite', 'idvisitor', 'visit_last_action_time'], array_column($result, 'Column_name')); + + if (!$diff) { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/app/core/Url.php b/app/core/Url.php index a79fdecb5..cc572aed0 100644 --- a/app/core/Url.php +++ b/app/core/Url.php @@ -1,4 +1,5 @@ General['proxy_uri_header']) + if ( + isset(Config::getInstance()->General['proxy_uri_header']) && Config::getInstance()->General['proxy_uri_header'] == 1 && !empty($_SERVER['HTTP_X_FORWARDED_URI']) ) { @@ -206,16 +209,17 @@ public static function getCurrentScheme() * value from the request. * @return bool `true` if valid; `false` otherwise. */ - public static function isValidHost($host = false) + public static function isValidHost($host = false): bool { // only do trusted host check if it's enabled - if (isset(Config::getInstance()->General['enable_trusted_host_check']) + if ( + isset(Config::getInstance()->General['enable_trusted_host_check']) && Config::getInstance()->General['enable_trusted_host_check'] == 0 ) { return true; } - if ($host === false) { + if (false === $host || null === $host) { $host = self::getHostFromServerVariable(); if (empty($host)) { // if no current host, assume valid @@ -277,12 +281,13 @@ public static function saveCORSHostnameInConfig($host) protected static function saveHostsnameInConfig($host, $domain, $key) { - if (Piwik::hasUserSuperUserAccess() + if ( + Piwik::hasUserSuperUserAccess() && file_exists(Config::getLocalConfigPath()) ) { $config = Config::getInstance()->$domain; if (!is_array($host)) { - $host = array($host); + $host = [$host]; } $host = array_filter($host); if (empty($host)) { @@ -339,6 +344,21 @@ protected static function getHostFromServerVariable() return false; } + /** + * Returns the valid hostname (according to RFC standards) as a string; else it will return false if it isn't valid. + * If the hostname isn't supplied it will default to using Url::getHost + * Note: this will not verify if the hostname is trusted. + * @param $hostname + * @return false|string + */ + public static function getRFCValidHostname($hostname = null) + { + if (empty($hostname)) { + $hostname = self::getHost(false); + } + return filter_var($hostname, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME); + } + /** * Sets the host. Useful for CLI scripts, eg. core:archive command * @@ -363,7 +383,7 @@ public static function setHost($host) */ public static function getCurrentHost($default = 'unknown', $checkTrustedHost = true) { - $hostHeaders = array(); + $hostHeaders = []; $config = Config::getInstance()->General; if (isset($config['proxy_host_headers'])) { @@ -371,7 +391,7 @@ public static function getCurrentHost($default = 'unknown', $checkTrustedHost = } if (!is_array($hostHeaders)) { - $hostHeaders = array(); + $hostHeaders = []; } $host = self::getHost($checkTrustedHost); @@ -390,7 +410,8 @@ public static function getCurrentHost($default = 'unknown', $checkTrustedHost = public static function getCurrentQueryString() { $url = ''; - if (isset($_SERVER['QUERY_STRING']) + if ( + isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING']) ) { $url .= "?" . $_SERVER['QUERY_STRING']; @@ -491,7 +512,8 @@ public static function redirectToReferrer() private static function redirectToUrlNoExit($url) { - if (UrlHelper::isLookLikeUrl($url) + if ( + UrlHelper::isLookLikeUrl($url) || strpos($url, 'index.php') === 0 ) { Common::sendResponseCode(302); @@ -568,13 +590,13 @@ public static function isLocalUrl($url) // handle host name mangling $requestUri = isset($_SERVER['SCRIPT_URI']) ? $_SERVER['SCRIPT_URI'] : ''; $parseRequest = @parse_url($requestUri); - $hosts = array(self::getHost(), self::getCurrentHost()); + $hosts = [self::getHost(), self::getCurrentHost()]; if (!empty($parseRequest['host'])) { $hosts[] = $parseRequest['host']; } // drop port numbers from hostnames and IP addresses - $hosts = array_map(array('self', 'getHostSanitized'), $hosts); + $hosts = array_map(['self', 'getHostSanitized'], $hosts); $disableHostCheck = Config::getInstance()->General['enable_trusted_host_check'] == 0; // compare scheme and host @@ -583,7 +605,7 @@ public static function isLocalUrl($url) return !empty($host) && ($disableHostCheck || in_array($host, $hosts)) && !empty($parsedUrl['scheme']) - && in_array($parsedUrl['scheme'], array('http', 'https')); + && in_array($parsedUrl['scheme'], ['http', 'https']); } /** @@ -635,7 +657,7 @@ public static function getCorsHostsFromConfig() * Returns hostname, without port numbers * * @param $host - * @return array + * @return string */ public static function getHostSanitized($host) { @@ -650,12 +672,12 @@ protected static function getHostsFromConfig($domain, $key) $config = @Config::getInstance()->$domain; if (!isset($config[$key])) { - return array(); + return []; } $hosts = $config[$key]; if (!is_array($hosts)) { - return array(); + return []; } return $hosts; } @@ -735,7 +757,7 @@ private static function getAlwaysTrustedHosts() */ public static function getLocalHostnames() { - return array('localhost', '127.0.0.1', '::1', '[::1]', '[::]', '0000::1', '0177.0.0.1', '2130706433', '[0:0:0:0:0:ffff:127.0.0.1]'); + return ['localhost', '127.0.0.1', '::1', '[::1]', '[::]', '0000::1', '0177.0.0.1', '2130706433', '[0:0:0:0:0:ffff:127.0.0.1]']; } /** @@ -769,10 +791,10 @@ protected static function getCurrentSchemeFromRequestHeader() return 'http'; } - if ((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] === true)) + if ( + (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] === true)) || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') ) { - return 'https'; } return 'http'; @@ -796,7 +818,8 @@ public static function getHostFromServerNameVar() { $host = @$_SERVER['SERVER_NAME']; if (!empty($host)) { - if (strpos($host, ':') === false + if ( + strpos($host, ':') === false && !empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443 diff --git a/app/core/UrlHelper.php b/app/core/UrlHelper.php index 2a0fc4cb1..94c8ff9d5 100644 --- a/app/core/UrlHelper.php +++ b/app/core/UrlHelper.php @@ -202,7 +202,7 @@ public static function getParseUrlReverse($parsed) */ public static function getArrayFromQueryString($urlQuery) { - if (strlen($urlQuery) == 0) { + if (empty($urlQuery)) { return array(); } diff --git a/app/core/Validators/CharacterLength.php b/app/core/Validators/CharacterLength.php index d40b11bfc..6e235bf08 100644 --- a/app/core/Validators/CharacterLength.php +++ b/app/core/Validators/CharacterLength.php @@ -9,7 +9,6 @@ namespace Piwik\Validators; -use Piwik\Common; use Piwik\Piwik; class CharacterLength extends BaseValidator diff --git a/app/core/Version.php b/app/core/Version.php index 130102258..0b44b74cd 100644 --- a/app/core/Version.php +++ b/app/core/Version.php @@ -1,4 +1,5 @@ save('cssCacheBusterId', $cssCacheBusterId); } - $tagJs = 'cb=' . $cacheBuster->piwikVersionBasedCacheBuster(); + $tagJs = 'cb=' . ($this->cacheBuster ?? $cacheBuster->piwikVersionBasedCacheBuster()); $tagCss = 'cb=' . $cssCacheBusterId; $pattern = array( '~ diff --git a/app/plugins/CoreAdminHome/vue/src/BrandingSettings/BrandingSettings.adapter.ts b/app/plugins/CoreAdminHome/vue/src/BrandingSettings/BrandingSettings.adapter.ts new file mode 100644 index 000000000..67a669560 --- /dev/null +++ b/app/plugins/CoreAdminHome/vue/src/BrandingSettings/BrandingSettings.adapter.ts @@ -0,0 +1,49 @@ +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +import { createAngularJsAdapter } from 'CoreHome'; +import BrandingSettings from './BrandingSettings.vue'; + +export default createAngularJsAdapter({ + component: BrandingSettings, + scope: { + fileUploadEnabled: { + angularJsBind: '<', + }, + logosWriteable: { + angularJsBind: '<', + }, + useCustomLogo: { + angularJsBind: '<', + }, + pathUserLogoDirectory: { + angularJsBind: '<', + }, + pathUserLogo: { + angularJsBind: '<', + }, + pathUserLogoSmall: { + angularJsBind: '<', + }, + pathUserLogoSvg: { + angularJsBind: '<', + }, + hasUserLogo: { + angularJsBind: '<', + }, + pathUserFavicon: { + angularJsBind: '<', + }, + hasUserFavicon: { + angularJsBind: '<', + }, + isPluginsAdminEnabled: { + angularJsBind: '<', + }, + }, + directiveName: 'matomoBrandingSettings', +}); diff --git a/app/plugins/CoreAdminHome/vue/src/BrandingSettings/BrandingSettings.vue b/app/plugins/CoreAdminHome/vue/src/BrandingSettings/BrandingSettings.vue new file mode 100644 index 000000000..631250639 --- /dev/null +++ b/app/plugins/CoreAdminHome/vue/src/BrandingSettings/BrandingSettings.vue @@ -0,0 +1,321 @@ + + + + + diff --git a/app/plugins/CoreAdminHome/vue/src/ImageTrackingCodeGenerator/ImageTrackingCodeGenerator.adapter.ts b/app/plugins/CoreAdminHome/vue/src/ImageTrackingCodeGenerator/ImageTrackingCodeGenerator.adapter.ts new file mode 100644 index 000000000..84e06eef0 --- /dev/null +++ b/app/plugins/CoreAdminHome/vue/src/ImageTrackingCodeGenerator/ImageTrackingCodeGenerator.adapter.ts @@ -0,0 +1,19 @@ +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +import { createAngularJsAdapter } from 'CoreHome'; +import ImageTrackingCodeGenerator from './ImageTrackingCodeGenerator.vue'; + +export default createAngularJsAdapter({ + component: ImageTrackingCodeGenerator, + scope: { + defaultSite: { + angularJsBind: '<', + }, + }, + directiveName: 'matomoImageTrackingCodeGenerator', +}); diff --git a/app/plugins/CoreAdminHome/vue/src/ImageTrackingCodeGenerator/ImageTrackingCodeGenerator.vue b/app/plugins/CoreAdminHome/vue/src/ImageTrackingCodeGenerator/ImageTrackingCodeGenerator.vue new file mode 100644 index 000000000..0e8f02e1d --- /dev/null +++ b/app/plugins/CoreAdminHome/vue/src/ImageTrackingCodeGenerator/ImageTrackingCodeGenerator.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/app/plugins/CoreAdminHome/vue/src/JsTrackingCodeGenerator/JsTrackingCodeGenerator.adapter.ts b/app/plugins/CoreAdminHome/vue/src/JsTrackingCodeGenerator/JsTrackingCodeGenerator.adapter.ts new file mode 100644 index 000000000..daca15cb4 --- /dev/null +++ b/app/plugins/CoreAdminHome/vue/src/JsTrackingCodeGenerator/JsTrackingCodeGenerator.adapter.ts @@ -0,0 +1,25 @@ +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +import { createAngularJsAdapter } from 'CoreHome'; +import JsTrackingCodeGenerator from './JsTrackingCodeGenerator.vue'; + +export default createAngularJsAdapter({ + component: JsTrackingCodeGenerator, + scope: { + defaultSite: { + angularJsBind: '<', + }, + maxCustomVariables: { + angularJsBind: '<', + }, + serverSideDoNotTrackEnabled: { + angularJsBind: '<', + }, + }, + directiveName: 'matomoJsTrackingCodeGenerator', +}); diff --git a/app/plugins/CoreAdminHome/vue/src/JsTrackingCodeGenerator/JsTrackingCodeGenerator.vue b/app/plugins/CoreAdminHome/vue/src/JsTrackingCodeGenerator/JsTrackingCodeGenerator.vue new file mode 100644 index 000000000..de0dfb784 --- /dev/null +++ b/app/plugins/CoreAdminHome/vue/src/JsTrackingCodeGenerator/JsTrackingCodeGenerator.vue @@ -0,0 +1,622 @@ + + + + + diff --git a/app/plugins/CoreAdminHome/vue/src/SmtpSettings/SmtpSettings.adapter.ts b/app/plugins/CoreAdminHome/vue/src/SmtpSettings/SmtpSettings.adapter.ts new file mode 100644 index 000000000..21e752b3a --- /dev/null +++ b/app/plugins/CoreAdminHome/vue/src/SmtpSettings/SmtpSettings.adapter.ts @@ -0,0 +1,25 @@ +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +import { createAngularJsAdapter } from 'CoreHome'; +import SmtpSettings from './SmtpSettings.vue'; + +export default createAngularJsAdapter({ + component: SmtpSettings, + scope: { + mail: { + angularJsBind: '<', + }, + mailTypes: { + angularJsBind: '<', + }, + mailEncryptions: { + angularJsBind: '<', + }, + }, + directiveName: 'matomoSmtpSettings', +}); diff --git a/app/plugins/CoreAdminHome/vue/src/SmtpSettings/SmtpSettings.vue b/app/plugins/CoreAdminHome/vue/src/SmtpSettings/SmtpSettings.vue new file mode 100644 index 000000000..64214e076 --- /dev/null +++ b/app/plugins/CoreAdminHome/vue/src/SmtpSettings/SmtpSettings.vue @@ -0,0 +1,241 @@ + + + + + diff --git a/app/plugins/CoreAdminHome/vue/src/TrackingFailures/FailureRow.vue b/app/plugins/CoreAdminHome/vue/src/TrackingFailures/FailureRow.vue new file mode 100644 index 000000000..a1b48fb3c --- /dev/null +++ b/app/plugins/CoreAdminHome/vue/src/TrackingFailures/FailureRow.vue @@ -0,0 +1,67 @@ + + + diff --git a/app/plugins/CoreAdminHome/vue/src/TrackingFailures/TrackingFailures.adapter.ts b/app/plugins/CoreAdminHome/vue/src/TrackingFailures/TrackingFailures.adapter.ts new file mode 100644 index 000000000..2b7b6ac04 --- /dev/null +++ b/app/plugins/CoreAdminHome/vue/src/TrackingFailures/TrackingFailures.adapter.ts @@ -0,0 +1,14 @@ +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +import { createAngularJsAdapter } from 'CoreHome'; +import TrackingFailures from './TrackingFailures.vue'; + +export default createAngularJsAdapter({ + component: TrackingFailures, + directiveName: 'matomoTrackingFailures', +}); diff --git a/app/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.less b/app/plugins/CoreAdminHome/vue/src/TrackingFailures/TrackingFailures.less similarity index 100% rename from app/plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.less rename to app/plugins/CoreAdminHome/vue/src/TrackingFailures/TrackingFailures.less diff --git a/app/plugins/CoreAdminHome/vue/src/TrackingFailures/TrackingFailures.vue b/app/plugins/CoreAdminHome/vue/src/TrackingFailures/TrackingFailures.vue new file mode 100644 index 000000000..d7cd39358 --- /dev/null +++ b/app/plugins/CoreAdminHome/vue/src/TrackingFailures/TrackingFailures.vue @@ -0,0 +1,220 @@ + + + + + diff --git a/app/plugins/CoreAdminHome/vue/src/index.ts b/app/plugins/CoreAdminHome/vue/src/index.ts new file mode 100644 index 000000000..ebe1b72c7 --- /dev/null +++ b/app/plugins/CoreAdminHome/vue/src/index.ts @@ -0,0 +1,24 @@ +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later +*/ + +import './ArchivingSettings/ArchivingSettings.adapter'; +import './BrandingSettings/BrandingSettings.adapter'; +import './SmtpSettings/SmtpSettings.adapter'; +import './JsTrackingCodeGenerator/JsTrackingCodeGenerator.adapter'; +import './ImageTrackingCodeGenerator/ImageTrackingCodeGenerator.adapter'; +import './TrackingFailures/TrackingFailures.adapter'; + +export { default as ArchivingSettings } from './ArchivingSettings/ArchivingSettings.vue'; +export { default as BrandingSettings } from './BrandingSettings/BrandingSettings.vue'; +export { default as SmtpSettings } from './SmtpSettings/SmtpSettings.vue'; +export { + default as JsTrackingCodeGenerator, +} from './JsTrackingCodeGenerator/JsTrackingCodeGenerator.vue'; +export { + default as ImageTrackingCodeGenerator, +} from './ImageTrackingCodeGenerator/ImageTrackingCodeGenerator.vue'; +export { default as TrackingFailures } from './TrackingFailures/TrackingFailures.vue'; diff --git a/app/plugins/CoreConsole/Commands/ComputeJsAssetSize.php b/app/plugins/CoreConsole/Commands/ComputeJsAssetSize.php index 9b0bd310e..0d6abab12 100644 --- a/app/plugins/CoreConsole/Commands/ComputeJsAssetSize.php +++ b/app/plugins/CoreConsole/Commands/ComputeJsAssetSize.php @@ -9,7 +9,6 @@ namespace Piwik\Plugins\CoreConsole\Commands; use Piwik\AssetManager; -use Piwik\Common; use Piwik\Development; use Piwik\Metrics\Formatter; use Piwik\Piwik; @@ -18,6 +17,7 @@ use Piwik\Plugin\Manager; use Piwik\ProxyHttp; use Piwik\SettingsPiwik; +use Piwik\Theme; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -25,12 +25,15 @@ class ComputeJsAssetSize extends ConsoleCommand { + private $totals = []; + protected function configure() { $this->setName('development:compute-js-asset-size'); $this->setDescription('Generates production assets and computes the size of the resulting code.'); $this->addOption('exclude-angular', null, InputOption::VALUE_NONE); $this->addOption('no-delete', null, InputOption::VALUE_NONE, 'Do not delete files after creating them.'); + $this->addOption('plugin', null, InputOption::VALUE_REQUIRED, 'For submodule plugins and 3rd party plugins.'); } public function isEnabled() @@ -42,31 +45,36 @@ protected function execute(InputInterface $input, OutputInterface $output) { $excludeAngular = $input->getOption('exclude-angular'); $noDelete = $input->getOption('no-delete'); + $plugin = $input->getOption('plugin'); $this->checkDevelopmentModeDisabled(); - $this->ensureThirdPartyPluginsActivated(); + + $this->ensureThirdPartyPluginsActivated($plugin); $output->writeln("Building and printing sizes of built JS assets..."); + $fetcher = $this->makeUmdFetcher(); + if ($excludeAngular) { $this->excludeAngular($output); } $this->deleteMergedAssets(); - $this->buildAssets(); + $this->buildAssets($fetcher); $output->writeln(""); - $this->printCurrentGitHashAndBranch($output, $excludeAngular); + + $this->printCurrentGitHashAndBranch($output, $excludeAngular, $plugin); $output->writeln(""); - $this->printFilesizes($output); + $this->printFilesizes($fetcher, $output); if (!$noDelete) { $this->deleteMergedAssets(); } } - private function ensureThirdPartyPluginsActivated() + private function ensureThirdPartyPluginsActivated($plugin = null) { $expectedPluginsLoadedAndActivated = [ "CorePluginsAdmin", @@ -154,12 +162,19 @@ private function ensureThirdPartyPluginsActivated() "WhiteLabel", "WooCommerceAnalytics", "AdvertisingConversionExport", + "AnonymousPiwikUsageMeasurement", ]; + if ($plugin) { + $expectedPluginsLoadedAndActivated[] = $plugin; + } + if (is_file(PIWIK_INCLUDE_PATH . '/plugins/CoreVue/plugin.json')) { $expectedPluginsLoadedAndActivated[] = "CoreVue"; } + $expectedPluginsLoadedAndActivated = array_unique($expectedPluginsLoadedAndActivated); + $pluginsLoadedAndActivated = Manager::getInstance()->getPluginsLoadedAndActivated(); $pluginsLoadedAndActivated = array_map(function (Plugin $p) { return $p->getPluginName(); }, $pluginsLoadedAndActivated); @@ -187,10 +202,15 @@ private function excludeAngular(OutputInterface $output) }); } - private function buildAssets() + private function buildAssets(AssetManager\UIAssetFetcher\PluginUmdAssetFetcher $fetcher) { AssetManager::getInstance()->getMergedCoreJavaScript(); AssetManager::getInstance()->getMergedNonCoreJavaScript(); + + $chunks = $fetcher->getChunkFiles(); + foreach ($chunks as $chunk) { + AssetManager::getInstance()->getMergedJavaScriptChunk($chunk->getChunkName()); + } } private function deleteMergedAssets() @@ -198,26 +218,40 @@ private function deleteMergedAssets() AssetManager::getInstance()->removeMergedAssets(); } - private function printFilesizes(OutputInterface $output) + private function printFilesizes(AssetManager\UIAssetFetcher\PluginUmdAssetFetcher $fetcher, OutputInterface $output) { $fileSizes = []; $mergedCore = AssetManager::getInstance()->getMergedCoreJavaScript(); - $fileSizes[] = [$mergedCore->getRelativeLocation(), $this->getFileSize($mergedCore->getAbsoluteLocation()), $this->getGzippedFileSize($mergedCore->getAbsoluteLocation())]; + $fileSizes[] = $this->getFileSizeRow($mergedCore); $mergedNonCore = AssetManager::getInstance()->getMergedNonCoreJavaScript(); - $fileSizes[] = [$mergedNonCore->getRelativeLocation(), $this->getFileSize($mergedNonCore->getAbsoluteLocation()), $this->getGzippedFileSize($mergedNonCore->getAbsoluteLocation())]; + $fileSizes[] = $this->getFileSizeRow($mergedNonCore); + + $chunks = $fetcher->getChunkFiles(); + foreach ($chunks as $chunk) { + $chunkAsset = AssetManager::getInstance()->getMergedJavaScriptChunk($chunk->getChunkName()); + $fileSizes[] = $this->getFileSizeRow($chunkAsset); + } + + $fileSizes[] = []; + $fileSizes[] = ['Total', $this->getFormattedSize($this->totals['merged']), $this->getFormattedSize($this->totals['gzip'])]; $table = new Table($output); $table->setHeaders(['File', 'Size', 'Size (gzipped)'])->setRows($fileSizes); $table->render(); } - private function getFileSize($fileLocation) + private function getFileSize($fileLocation, $type) { - $formatter = new Formatter(); - $size = filesize($fileLocation); + $this->totals[$type] = ($this->totals[$type] ?? 0) + $size; + return $this->getFormattedSize($size); + } + + private function getFormattedSize($size) + { + $formatter = new Formatter(); $size = $formatter->getPrettySizeFromBytes($size, 'K', 2); return $size; } @@ -240,14 +274,44 @@ private function getGzippedFileSize($path) $compressedPath = dirname($path) . '/' . basename($path) . '.gz'; file_put_contents($compressedPath, $data); - return $this->getFileSize($compressedPath); + return $this->getFileSize($compressedPath, 'gzip'); } - private function printCurrentGitHashAndBranch(OutputInterface $output, $excludeAngular) + private function printCurrentGitHashAndBranch(OutputInterface $output, $excludeAngular, $plugin = null) { $branchName = trim(`git rev-parse --abbrev-ref HEAD`); $lastCommit = trim(`git log --pretty=format:'%h' -n 1`); - $output->writeln("$branchName ($lastCommit) " . ($excludeAngular ? '(without angularjs)' : '') . ""); + $pluginSuffix = ''; + if ($plugin) { + $prefix = 'cd "' . addslashes(PIWIK_INCLUDE_PATH . '/plugins/' . $plugin) . '"; '; + + $pluginBranchName = trim(`$prefix git rev-parse --abbrev-ref HEAD`); + $pluginLastCommit = trim(`$prefix git log --pretty=format:'%h' -n 1`); + + $pluginSuffix = " [$plugin: $pluginBranchName ($pluginLastCommit)]"; + } + + $output->writeln("$branchName ($lastCommit)$pluginSuffix " + . ($excludeAngular ? '(without angularjs)' : '') . ""); + } + + private function makeUmdFetcher() + { + $plugins = Manager::getInstance()->getPluginsLoadedAndActivated(); + $pluginNames = array_map(function ($p) { return $p->getPluginName(); }, $plugins); + + $theme = Manager::getInstance()->getThemeEnabled(); + if (!empty($theme)) { + $theme = new Theme(); + } + + $fetcher = new AssetManager\UIAssetFetcher\PluginUmdAssetFetcher($pluginNames, $theme, null); + return $fetcher; + } + + private function getFileSizeRow(AssetManager\UIAsset $asset) + { + return [$asset->getRelativeLocation(), $this->getFileSize($asset->getAbsoluteLocation(), 'merged'), $this->getGzippedFileSize($asset->getAbsoluteLocation())]; } } diff --git a/app/plugins/CoreConsole/Commands/CoreArchiver.php b/app/plugins/CoreConsole/Commands/CoreArchiver.php index 9f4f3cd87..25cef83c8 100644 --- a/app/plugins/CoreConsole/Commands/CoreArchiver.php +++ b/app/plugins/CoreConsole/Commands/CoreArchiver.php @@ -46,6 +46,8 @@ public static function makeArchiver($url, InputInterface $input) $archiver->concurrentRequestsPerWebsite = $input->getOption('concurrent-requests-per-website'); $archiver->maxConcurrentArchivers = $input->getOption('concurrent-archivers'); $archiver->shouldArchiveAllSites = $input->getOption('force-all-websites'); + $archiver->maxSitesToProcess = $input->getOption('max-websites-to-process'); + $archiver->maxArchivesToProcess = $input->getOption('max-archives-to-process'); $archiver->setUrlToPiwik($url); $archiveFilter = new CronArchive\ArchiveFilter(); @@ -111,6 +113,10 @@ public static function configureArchiveCommand(ConsoleCommand $command) "When processing a website and its segments, number of requests to process in parallel", CronArchive::MAX_CONCURRENT_API_REQUESTS); $command->addOption('concurrent-archivers', null, InputOption::VALUE_OPTIONAL, "The number of max archivers to run in parallel. Depending on how you start the archiver as a cronjob, you may need to double the amount of archivers allowed if the same process appears twice in the `ps ex` output.", false); + $command->addOption('max-websites-to-process', null, InputOption::VALUE_REQUIRED, + "Maximum number of websites to process during a single execution of the archiver. Can be used to limit the process lifetime e.g. to avoid increasing memory usage."); + $command->addOption('max-archives-to-process', null, InputOption::VALUE_REQUIRED, + "Maximum number of archives to process during a single execution of the archiver. Can be used to limit the process lifetime e.g. to avoid increasing memory usage."); $command->addOption('disable-scheduled-tasks', null, InputOption::VALUE_NONE, "Skips executing Scheduled tasks (sending scheduled reports, db optimization, etc.)."); $command->addOption('accept-invalid-ssl-certificate', null, InputOption::VALUE_NONE, diff --git a/app/plugins/CoreConsole/Commands/DevelopmentEnable.php b/app/plugins/CoreConsole/Commands/DevelopmentEnable.php index ec1df4735..c13376119 100644 --- a/app/plugins/CoreConsole/Commands/DevelopmentEnable.php +++ b/app/plugins/CoreConsole/Commands/DevelopmentEnable.php @@ -14,7 +14,6 @@ use Piwik\SettingsPiwik; use Piwik\Plugin\ConsoleCommand; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; /** diff --git a/app/plugins/CoreConsole/Commands/GenerateDimension.php b/app/plugins/CoreConsole/Commands/GenerateDimension.php index 9168e0bcc..f8efe64e4 100644 --- a/app/plugins/CoreConsole/Commands/GenerateDimension.php +++ b/app/plugins/CoreConsole/Commands/GenerateDimension.php @@ -12,7 +12,6 @@ use Piwik\Common; use Piwik\DbHelper; use Piwik\Plugin\Manager; -use Piwik\Plugin\Report; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; diff --git a/app/plugins/CoreHome/Categories/VisitorsCategory.php b/app/plugins/CoreHome/Categories/VisitorsCategory.php index 8cbbbb903..ad75ace57 100644 --- a/app/plugins/CoreHome/Categories/VisitorsCategory.php +++ b/app/plugins/CoreHome/Categories/VisitorsCategory.php @@ -10,7 +10,6 @@ use Piwik\Category\Category; use Piwik\Piwik; -use Piwik\Url; class VisitorsCategory extends Category { diff --git a/app/plugins/CoreHome/Columns/Metrics/ConversionRate.php b/app/plugins/CoreHome/Columns/Metrics/ConversionRate.php index 16dd7bba1..31447dd72 100644 --- a/app/plugins/CoreHome/Columns/Metrics/ConversionRate.php +++ b/app/plugins/CoreHome/Columns/Metrics/ConversionRate.php @@ -8,7 +8,6 @@ namespace Piwik\Plugins\CoreHome\Columns\Metrics; use Piwik\DataTable\Row; -use Piwik\Metrics; use Piwik\Metrics\Formatter; use Piwik\Piwik; use Piwik\Plugin\ProcessedMetric; diff --git a/app/plugins/CoreHome/Columns/Metrics/VisitsPercent.php b/app/plugins/CoreHome/Columns/Metrics/VisitsPercent.php index 325e1e3e4..49f18a8aa 100644 --- a/app/plugins/CoreHome/Columns/Metrics/VisitsPercent.php +++ b/app/plugins/CoreHome/Columns/Metrics/VisitsPercent.php @@ -13,7 +13,6 @@ use Piwik\Metrics\Formatter; use Piwik\Piwik; use Piwik\Plugin\ProcessedMetric; -use Piwik\Plugin\Report; /** * Percent of visits in the whole table. Calculated as: diff --git a/app/plugins/CoreHome/Columns/ServerMinute.php b/app/plugins/CoreHome/Columns/ServerMinute.php index 8f2c761d3..ec1128351 100644 --- a/app/plugins/CoreHome/Columns/ServerMinute.php +++ b/app/plugins/CoreHome/Columns/ServerMinute.php @@ -16,7 +16,7 @@ class ServerMinute extends ActionDimension protected $columnName = 'server_time'; protected $segmentName = 'actionServerMinute'; protected $sqlSegment = 'MINUTE(log_link_visit_action.server_time)'; - protected $nameSingular = 'VisitTime_ColumnServerMinute'; + protected $nameSingular = 'VisitTime_ColumnUTCMinute'; protected $type = self::TYPE_DATETIME; protected $acceptValues = '0, 1, 2, 3, ..., 56, 57, 58, 59'; diff --git a/app/plugins/CoreHome/Columns/ServerTime.php b/app/plugins/CoreHome/Columns/ServerTime.php index d2005f11f..9c41d89db 100644 --- a/app/plugins/CoreHome/Columns/ServerTime.php +++ b/app/plugins/CoreHome/Columns/ServerTime.php @@ -23,7 +23,7 @@ class ServerTime extends ActionDimension protected $columnType = 'DATETIME NOT NULL'; protected $segmentName = 'actionServerHour'; protected $sqlSegment = 'HOUR(log_link_visit_action.server_time)'; - protected $nameSingular = 'VisitTime_ColumnServerHour'; + protected $nameSingular = 'VisitTime_ColumnSiteHour'; protected $type = self::TYPE_DATETIME; public function __construct() diff --git a/app/plugins/CoreHome/Columns/UserId.php b/app/plugins/CoreHome/Columns/UserId.php index db23f9b7f..bc0c77ab7 100644 --- a/app/plugins/CoreHome/Columns/UserId.php +++ b/app/plugins/CoreHome/Columns/UserId.php @@ -9,13 +9,11 @@ namespace Piwik\Plugins\CoreHome\Columns; use Piwik\Cache; -use Piwik\Common; use Piwik\DataTable; use Piwik\DataTable\Map; use Piwik\Metrics; use Piwik\Plugin; use Piwik\Plugin\Dimension\VisitDimension; -use Piwik\Plugins\VisitsSummary\API as VisitsSummaryApi; use Piwik\Tracker\Request; use Piwik\Tracker\Visitor; use Piwik\Tracker\Action; diff --git a/app/plugins/CoreHome/Columns/VisitFirstActionMinute.php b/app/plugins/CoreHome/Columns/VisitFirstActionMinute.php index 1db6e914f..47e2d0ff6 100644 --- a/app/plugins/CoreHome/Columns/VisitFirstActionMinute.php +++ b/app/plugins/CoreHome/Columns/VisitFirstActionMinute.php @@ -23,7 +23,7 @@ class VisitFirstActionMinute extends VisitDimension protected $sqlSegment = 'MINUTE(log_visit.visit_first_action_time)'; protected $segmentName = 'visitStartServerMinute'; protected $acceptValues = '0, 1, 2, 3, ..., 56, 57, 58, 59'; - protected $nameSingular = 'VisitTime_ColumnVisitStartServerMinute'; + protected $nameSingular = 'VisitTime_ColumnVisitStartUTCMinute'; public function __construct() { diff --git a/app/plugins/CoreHome/Columns/VisitFirstActionTime.php b/app/plugins/CoreHome/Columns/VisitFirstActionTime.php index 5f76c5076..56ee0231e 100644 --- a/app/plugins/CoreHome/Columns/VisitFirstActionTime.php +++ b/app/plugins/CoreHome/Columns/VisitFirstActionTime.php @@ -26,7 +26,7 @@ class VisitFirstActionTime extends VisitDimension protected $sqlSegment = 'HOUR(log_visit.visit_first_action_time)'; protected $segmentName = 'visitStartServerHour'; protected $acceptValues = '0, 1, 2, 3, ..., 20, 21, 22, 23'; - protected $nameSingular = 'VisitTime_ColumnVisitStartServerHour'; + protected $nameSingular = 'VisitTime_ColumnVisitStartSiteHour'; public function __construct() { diff --git a/app/plugins/CoreHome/Columns/VisitGoalBuyer.php b/app/plugins/CoreHome/Columns/VisitGoalBuyer.php index 904519494..711380cf9 100644 --- a/app/plugins/CoreHome/Columns/VisitGoalBuyer.php +++ b/app/plugins/CoreHome/Columns/VisitGoalBuyer.php @@ -11,7 +11,6 @@ use Piwik\Metrics\Formatter; use Piwik\Piwik; use Piwik\Plugin\Dimension\VisitDimension; -use Piwik\Plugins\CoreHome\Segment; use Piwik\Tracker\Action; use Piwik\Tracker\GoalManager; use Piwik\Tracker\Request; diff --git a/app/plugins/CoreHome/Columns/VisitLastActionDate.php b/app/plugins/CoreHome/Columns/VisitLastActionDate.php index 6b2fd6756..9f0d1cdaf 100644 --- a/app/plugins/CoreHome/Columns/VisitLastActionDate.php +++ b/app/plugins/CoreHome/Columns/VisitLastActionDate.php @@ -19,7 +19,7 @@ class VisitLastActionDate extends VisitDimension protected $columnName = 'visit_last_action_time'; protected $type = self::TYPE_DATETIME; protected $segmentName = 'visitEndServerDate'; - protected $nameSingular = 'VisitTime_ColumnVisitEndServerDate'; + protected $nameSingular = 'VisitTime_ColumnVisitEndUTCDate'; protected $sqlSegment = 'DATE(log_visit.visit_last_action_time)'; protected $acceptValues = '2018-12-31, 2018-03-20, ...'; diff --git a/app/plugins/CoreHome/Columns/VisitLastActionDayOfMonth.php b/app/plugins/CoreHome/Columns/VisitLastActionDayOfMonth.php index e3ef6bd8a..c263c17cf 100644 --- a/app/plugins/CoreHome/Columns/VisitLastActionDayOfMonth.php +++ b/app/plugins/CoreHome/Columns/VisitLastActionDayOfMonth.php @@ -18,7 +18,7 @@ class VisitLastActionDayOfMonth extends VisitDimension protected $columnName = 'visit_last_action_time'; protected $type = self::TYPE_DATETIME; protected $segmentName = 'visitEndServerDayOfMonth'; - protected $nameSingular = 'VisitTime_ColumnVisitEndServerDayOfMonth'; + protected $nameSingular = 'VisitTime_ColumnVisitEndUTCDayOfMonth'; protected $sqlSegment = 'DAYOFMONTH(log_visit.visit_last_action_time)'; protected $acceptValues = '0, 1, 2, 3, ..., 29, 30, 31'; diff --git a/app/plugins/CoreHome/Columns/VisitLastActionDayOfWeek.php b/app/plugins/CoreHome/Columns/VisitLastActionDayOfWeek.php index 81c5df338..9bd42bdc0 100644 --- a/app/plugins/CoreHome/Columns/VisitLastActionDayOfWeek.php +++ b/app/plugins/CoreHome/Columns/VisitLastActionDayOfWeek.php @@ -20,7 +20,7 @@ class VisitLastActionDayOfWeek extends VisitDimension protected $columnName = 'visit_last_action_time'; protected $type = self::TYPE_DATETIME; protected $segmentName = 'visitEndServerDayOfWeek'; - protected $nameSingular = 'VisitTime_ColumnVisitEndServerDayOfWeek'; + protected $nameSingular = 'VisitTime_ColumnVisitEndUTCDayOfWeek'; protected $sqlSegment = 'DAYOFWEEK(log_visit.visit_last_action_time)'; protected $acceptValues = '1, 2, 3, 4, 5, 6, 7'; diff --git a/app/plugins/CoreHome/Columns/VisitLastActionDayOfYear.php b/app/plugins/CoreHome/Columns/VisitLastActionDayOfYear.php index b00cea3c0..5e3449851 100644 --- a/app/plugins/CoreHome/Columns/VisitLastActionDayOfYear.php +++ b/app/plugins/CoreHome/Columns/VisitLastActionDayOfYear.php @@ -18,7 +18,7 @@ class VisitLastActionDayOfYear extends VisitDimension protected $columnName = 'visit_last_action_time'; protected $type = self::TYPE_DATETIME; protected $segmentName = 'visitEndServerDayOfYear'; - protected $nameSingular = 'VisitTime_ColumnVisitEndServerDayOfYear'; + protected $nameSingular = 'VisitTime_ColumnVisitEndUTCDayOfYear'; protected $sqlSegment = 'DAYOFYEAR(log_visit.visit_last_action_time)'; protected $acceptValues = '1, 2, 3, 4, ..., 365, 366'; diff --git a/app/plugins/CoreHome/Columns/VisitLastActionMinute.php b/app/plugins/CoreHome/Columns/VisitLastActionMinute.php index b3b50fbd5..a465916cb 100644 --- a/app/plugins/CoreHome/Columns/VisitLastActionMinute.php +++ b/app/plugins/CoreHome/Columns/VisitLastActionMinute.php @@ -25,7 +25,7 @@ class VisitLastActionMinute extends VisitDimension protected $columnName = 'visit_last_action_time'; protected $type = self::TYPE_DATETIME; protected $segmentName = 'visitEndServerMinute'; - protected $nameSingular = 'VisitTime_ColumnVisitEndServerMinute'; + protected $nameSingular = 'VisitTime_ColumnVisitEndUTCMinute'; protected $sqlSegment = 'MINUTE(log_visit.visit_last_action_time)'; protected $acceptValues = '0, 1, 2, 3, ..., 56, 57, 58, 59'; diff --git a/app/plugins/CoreHome/Columns/VisitLastActionMonth.php b/app/plugins/CoreHome/Columns/VisitLastActionMonth.php index 92094685e..369d8bf77 100644 --- a/app/plugins/CoreHome/Columns/VisitLastActionMonth.php +++ b/app/plugins/CoreHome/Columns/VisitLastActionMonth.php @@ -20,7 +20,7 @@ class VisitLastActionMonth extends VisitDimension protected $columnName = 'visit_last_action_time'; protected $type = self::TYPE_DATETIME; protected $segmentName = 'visitEndServerMonth'; - protected $nameSingular = 'VisitTime_ColumnVisitEndServerMonth'; + protected $nameSingular = 'VisitTime_ColumnVisitEndUTCMonth'; protected $sqlSegment = 'MONTH(log_visit.visit_last_action_time)'; protected $acceptValues = '1, 2, 3, ..., 11, 12'; diff --git a/app/plugins/CoreHome/Columns/VisitLastActionQuarter.php b/app/plugins/CoreHome/Columns/VisitLastActionQuarter.php index f55daec3e..196616945 100644 --- a/app/plugins/CoreHome/Columns/VisitLastActionQuarter.php +++ b/app/plugins/CoreHome/Columns/VisitLastActionQuarter.php @@ -18,7 +18,7 @@ class VisitLastActionQuarter extends VisitDimension protected $columnName = 'visit_last_action_time'; protected $type = self::TYPE_DATETIME; protected $segmentName = 'visitEndServerQuarter'; - protected $nameSingular = 'VisitTime_ColumnVisitEndServerQuarter'; + protected $nameSingular = 'VisitTime_ColumnVisitEndUTCQuarter'; protected $sqlSegment = 'QUARTER(log_visit.visit_last_action_time)'; protected $acceptValues = '1, 2, 3, 4'; diff --git a/app/plugins/CoreHome/Columns/VisitLastActionSecond.php b/app/plugins/CoreHome/Columns/VisitLastActionSecond.php index 34dd1552b..291589883 100644 --- a/app/plugins/CoreHome/Columns/VisitLastActionSecond.php +++ b/app/plugins/CoreHome/Columns/VisitLastActionSecond.php @@ -18,7 +18,7 @@ class VisitLastActionSecond extends VisitDimension protected $columnName = 'visit_last_action_time'; protected $type = self::TYPE_DATETIME; protected $segmentName = 'visitEndServerSecond'; - protected $nameSingular = 'VisitTime_ColumnVisitEndServerSecond'; + protected $nameSingular = 'VisitTime_ColumnVisitEndUTCSecond'; protected $sqlSegment = 'SECOND(log_visit.visit_last_action_time)'; protected $acceptValues = '0, 1, 2, 3, ..., 58, 59'; diff --git a/app/plugins/CoreHome/Columns/VisitLastActionTime.php b/app/plugins/CoreHome/Columns/VisitLastActionTime.php index 2ec021f68..f40643f99 100644 --- a/app/plugins/CoreHome/Columns/VisitLastActionTime.php +++ b/app/plugins/CoreHome/Columns/VisitLastActionTime.php @@ -32,7 +32,7 @@ class VisitLastActionTime extends VisitDimension { protected $columnName = 'visit_last_action_time'; protected $type = self::TYPE_DATETIME; - protected $nameSingular = 'VisitTime_ColumnVisitEndServerHour'; + protected $nameSingular = 'VisitTime_ColumnVisitEndSiteHour'; protected $sqlSegment = 'HOUR(log_visit.visit_last_action_time)'; protected $segmentName = 'visitServerHour'; protected $acceptValues = '0, 1, 2, 3, ..., 20, 21, 22, 23'; diff --git a/app/plugins/CoreHome/Columns/VisitLastActionWeekOfYear.php b/app/plugins/CoreHome/Columns/VisitLastActionWeekOfYear.php index 2f3db58d9..44c21a067 100644 --- a/app/plugins/CoreHome/Columns/VisitLastActionWeekOfYear.php +++ b/app/plugins/CoreHome/Columns/VisitLastActionWeekOfYear.php @@ -18,7 +18,7 @@ class VisitLastActionWeekOfYear extends VisitDimension protected $columnName = 'visit_last_action_time'; protected $type = self::TYPE_DATETIME; protected $segmentName = 'visitEndServerWeekOfYear'; - protected $nameSingular = 'VisitTime_ColumnVisitEndServerWeekOfYear'; + protected $nameSingular = 'VisitTime_ColumnVisitEndUTCWeekOfYear'; protected $sqlSegment = 'WEEKOFYEAR(log_visit.visit_last_action_time)'; protected $acceptValues = '1, 2, 3, 4, ..., 51, 52, 53'; diff --git a/app/plugins/CoreHome/Columns/VisitLastActionYear.php b/app/plugins/CoreHome/Columns/VisitLastActionYear.php index ffc3d2929..18d05db6e 100644 --- a/app/plugins/CoreHome/Columns/VisitLastActionYear.php +++ b/app/plugins/CoreHome/Columns/VisitLastActionYear.php @@ -18,7 +18,7 @@ class VisitLastActionYear extends VisitDimension protected $columnName = 'visit_last_action_time'; protected $type = self::TYPE_DATETIME; protected $segmentName = 'visitEndServerYear'; - protected $nameSingular = 'VisitTime_ColumnVisitEndServerYear'; + protected $nameSingular = 'VisitTime_ColumnVisitEndUTCYear'; protected $sqlSegment = 'YEAR(log_visit.visit_last_action_time)'; protected $acceptValues = '2016, 2017, 2018, ..., 9998, 9999'; diff --git a/app/plugins/CoreHome/Columns/VisitTotalTime.php b/app/plugins/CoreHome/Columns/VisitTotalTime.php index 858028f59..0401a54e0 100644 --- a/app/plugins/CoreHome/Columns/VisitTotalTime.php +++ b/app/plugins/CoreHome/Columns/VisitTotalTime.php @@ -10,7 +10,6 @@ use Piwik\Config; use Piwik\Plugin\Dimension\VisitDimension; -use Piwik\Plugins\CoreHome\Segment; use Piwik\Tracker\Action; use Piwik\Tracker\Request; use Piwik\Tracker\Visitor; diff --git a/app/plugins/CoreHome/Columns/VisitorFingerprint.php b/app/plugins/CoreHome/Columns/VisitorFingerprint.php index cc216a042..e88b3b5b6 100644 --- a/app/plugins/CoreHome/Columns/VisitorFingerprint.php +++ b/app/plugins/CoreHome/Columns/VisitorFingerprint.php @@ -32,7 +32,7 @@ class VisitorFingerprint extends VisitDimension public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory) { $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_UNIQUE); - $metric->setTranslatedName(Piwik::translate('Visitor_Fingerprint')); + $metric->setTranslatedName(Piwik::translate('General_VisitorFingerprint')); $metricsList->addMetric($metric); } } \ No newline at end of file diff --git a/app/plugins/CoreHome/Columns/VisitorId.php b/app/plugins/CoreHome/Columns/VisitorId.php index f6b29699b..ff1596837 100644 --- a/app/plugins/CoreHome/Columns/VisitorId.php +++ b/app/plugins/CoreHome/Columns/VisitorId.php @@ -1,4 +1,5 @@ disableVisitorProfile->getValue() === false - && $systemSettings->disableVisitorLog->getValue() === false; + $visitorProfileEnabled = Live::isVisitorProfileEnabled(); } catch (\Zend_Db_Exception $e) { // when running tests the db might not yet be set up when fetching available segments if (!defined('PIWIK_TEST_MODE') || !PIWIK_TEST_MODE) { diff --git a/app/plugins/CoreHome/Columns/VisitorSecondsSinceFirst.php b/app/plugins/CoreHome/Columns/VisitorSecondsSinceFirst.php index bed71d332..64e2a3403 100644 --- a/app/plugins/CoreHome/Columns/VisitorSecondsSinceFirst.php +++ b/app/plugins/CoreHome/Columns/VisitorSecondsSinceFirst.php @@ -11,7 +11,6 @@ use Piwik\Common; use Piwik\Date; use Piwik\Plugin\Dimension\VisitDimension; -use Piwik\Plugin\Segment; use Piwik\Tracker\Action; use Piwik\Tracker\Request; use Piwik\Tracker\Visitor; diff --git a/app/plugins/CoreHome/Columns/VisitorSecondsSinceOrder.php b/app/plugins/CoreHome/Columns/VisitorSecondsSinceOrder.php index 8837bfdd7..0e67fe418 100644 --- a/app/plugins/CoreHome/Columns/VisitorSecondsSinceOrder.php +++ b/app/plugins/CoreHome/Columns/VisitorSecondsSinceOrder.php @@ -10,7 +10,6 @@ use Piwik\Date; use Piwik\Plugin\Dimension\VisitDimension; -use Piwik\Plugin\Segment; use Piwik\Tracker\Action; use Piwik\Tracker\Request; use Piwik\Tracker\Visitor; diff --git a/app/plugins/CoreHome/Controller.php b/app/plugins/CoreHome/Controller.php index a8f6fa09b..6982562ea 100644 --- a/app/plugins/CoreHome/Controller.php +++ b/app/plugins/CoreHome/Controller.php @@ -24,7 +24,6 @@ use Piwik\Plugins\CoreHome\DataTableRowAction\RowEvolution; use Piwik\Plugins\Dashboard\DashboardManagerControl; use Piwik\Plugins\UsersManager\API; -use Piwik\Site; use Piwik\Translation\Translator; use Piwik\UpdateCheck; use Piwik\Url; @@ -326,4 +325,5 @@ public function saveViewDataTableParameters() ViewDataTableManager::saveViewDataTableParameters($login, $reportId, $parameters, $containerId); } + } diff --git a/app/plugins/CoreHome/CoreHome.php b/app/plugins/CoreHome/CoreHome.php index 4464aa1cd..3c9aff226 100644 --- a/app/plugins/CoreHome/CoreHome.php +++ b/app/plugins/CoreHome/CoreHome.php @@ -14,6 +14,7 @@ use Piwik\Common; use Piwik\Container\StaticContainer; use Piwik\DbHelper; +use Piwik\Development; use Piwik\IP; use Piwik\Piwik; use Piwik\Plugin\ArchivedMetric; @@ -138,18 +139,18 @@ public function getStylesheetFiles(&$stylesheets) $stylesheets[] = "plugins/CoreHome/stylesheets/layout.less"; $stylesheets[] = "plugins/CoreHome/vue/src/EnrichedHeadline/EnrichedHeadline.less"; $stylesheets[] = "plugins/CoreHome/vue/src/Notification/Notification.less"; - $stylesheets[] = "plugins/CoreHome/angularjs/quick-access/quick-access.directive.less"; + $stylesheets[] = "plugins/CoreHome/vue/src/QuickAccess/QuickAccess.less"; $stylesheets[] = "plugins/CoreHome/stylesheets/selector.less"; - $stylesheets[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.less"; - $stylesheets[] = "plugins/CoreHome/angularjs/report-export/reportexport.popover.less"; - $stylesheets[] = "plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.less"; - $stylesheets[] = "plugins/CoreHome/angularjs/progressbar/progressbar.directive.less"; + $stylesheets[] = "plugins/CoreHome/vue/src/ReportingPage/ReportingPage.less"; + $stylesheets[] = "plugins/CoreHome/vue/src/ReportExport/ReportExport.less"; + $stylesheets[] = "plugins/CoreHome/vue/src/WidgetByDimensionContainer/WidgetByDimensionContainer.less"; + $stylesheets[] = "plugins/CoreHome/vue/src/Progressbar/Progressbar.less"; $stylesheets[] = "plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.less"; - $stylesheets[] = "plugins/CoreHome/angularjs/period-selector/period-selector.directive.less"; - $stylesheets[] = "plugins/CoreHome/angularjs/multipairfield/multipairfield.directive.less"; + $stylesheets[] = "plugins/CoreHome/vue/src/PeriodSelector/PeriodSelector.less"; + $stylesheets[] = "plugins/CoreHome/vue/src/MultiPairField/MultiPairField.less"; $stylesheets[] = "plugins/CoreHome/vue/src/DropdownMenu/DropdownMenu.less"; - $stylesheets[] = "plugins/CoreHome/angularjs/sparkline/sparkline.component.less"; - $stylesheets[] = "plugins/CoreHome/angularjs/field-array/field-array.directive.less"; + $stylesheets[] = "plugins/CoreHome/vue/src/Sparkline/Sparkline.less"; + $stylesheets[] = "plugins/CoreHome/vue/src/FieldArray/FieldArray.less"; $stylesheets[] = "plugins/CoreHome/vue/src/Comparisons/Comparisons.less"; $stylesheets[] = "plugins/CoreHome/stylesheets/vue-transitions.less"; } @@ -164,7 +165,11 @@ public function getJsFiles(&$jsFiles) $jsFiles[] = "node_modules/jquery.scrollto/jquery.scrollTo.min.js"; $jsFiles[] = "node_modules/sprintf-js/dist/sprintf.min.js"; $jsFiles[] = "node_modules/mousetrap/mousetrap.min.js"; - $jsFiles[] = 'node_modules/angular/angular.min.js'; + + $devAngularJs = 'node_modules/angular/angular.js'; + $jsFiles[] = Development::isEnabled() && is_file(PIWIK_INCLUDE_PATH . '/' . $devAngularJs) + ? $devAngularJs : 'node_modules/angular/angular.min.js'; + $jsFiles[] = "node_modules/angular-sanitize/angular-sanitize.min.js"; $jsFiles[] = "node_modules/angular-animate/angular-animate.min.js"; $jsFiles[] = "node_modules/angular-cookies/angular-cookies.min.js"; @@ -192,8 +197,6 @@ public function getJsFiles(&$jsFiles) $jsFiles[] = "plugins/CoreHome/angularjs/common/services/service.module.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/services/piwik-api.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/common/services/report-metadata-model.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/common/services/reporting-pages-model.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/filter.module.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/translate.js"; @@ -210,14 +213,10 @@ public function getJsFiles(&$jsFiles) $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/directive.module.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/attributes.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/field-condition.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/show-sensitive-data.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/autocomplete-matched.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/ignore-click.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/onenter.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/translate.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/dropdown-button.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/select-on-focus.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/side-nav.js"; $jsFiles[] = "plugins/CoreHome/angularjs/common/directives/string-to-number.js"; $jsFiles[] = "plugins/CoreHome/angularjs/piwikApp.js"; @@ -226,76 +225,9 @@ public function getJsFiles(&$jsFiles) $jsFiles[] = "plugins/CoreHome/angularjs/history/history.service.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/progressbar/progressbar.directive.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/sparkline/sparkline.component.js"; - - $jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector-model.service.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector.controller.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector.directive.js"; - - - $jsFiles[] = "plugins/CoreHome/angularjs/content-intro/content-intro.directive.js"; - - - $jsFiles[] = "plugins/CoreHome/angularjs/ajax-form/ajax-form.controller.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js"; - - $jsFiles[] = "plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/widget/widget.directive.js"; - - $jsFiles[] = "plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js"; - - $jsFiles[] = "plugins/CoreHome/angularjs/report-export/reportexport.directive.js"; - - $jsFiles[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js"; - - $jsFiles[] = "plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js"; - - $jsFiles[] = "plugins/CoreHome/angularjs/quick-access/quick-access.controller.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/quick-access/quick-access.directive.js"; - - $jsFiles[] = "plugins/CoreHome/angularjs/content-table/content-table.directive.js"; - - - $jsFiles[] = "plugins/CoreHome/angularjs/period-selector/period-selector.directive.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/period-selector/period-selector.controller.js"; - - $jsFiles[] = "plugins/CoreHome/angularjs/multipairfield/multipairfield.directive.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/multipairfield/multipairfield.controller.js"; - - $jsFiles[] = "plugins/CoreHome/angularjs/field-array/field-array.directive.js"; - $jsFiles[] = "plugins/CoreHome/angularjs/field-array/field-array.controller.js"; - - - // we have to load these CoreAdminHome files here. If we loaded them in CoreAdminHome, - // there would be JS errors as CoreAdminHome is loaded first. Meaning it is loaded before - // any angular JS file is loaded etc. - $jsFiles[] = "plugins/CoreAdminHome/angularjs/smtp/mail-smtp.controller.js"; - $jsFiles[] = "plugins/CoreAdminHome/angularjs/branding/branding.controller.js"; - $jsFiles[] = "plugins/CoreAdminHome/angularjs/trackingcode/jstrackingcode.controller.js"; - $jsFiles[] = "plugins/CoreAdminHome/angularjs/trackingcode/imagetrackingcode.controller.js"; - $jsFiles[] = "plugins/CoreAdminHome/angularjs/archiving/archiving.controller.js"; - $jsFiles[] = "plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.controller.js"; - $jsFiles[] = "plugins/CoreAdminHome/angularjs/trackingfailures/trackingfailures.directive.js"; - // we have to load these CorePluginsAdmin files here. If we loaded them in CorePluginsAdmin, // there would be JS errors as CorePluginsAdmin is loaded first. Meaning it is loaded before // any angular JS file is loaded etc. - $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.controller.js"; - $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.js"; - $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/form/form.directive.js"; - $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js"; - $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/field/field.directive.js"; - $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/save-button/save-button.directive.js"; - $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/plugins/plugin-filter.directive.js"; - $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/plugins/plugin-management.directive.js"; - $jsFiles[] = "plugins/CorePluginsAdmin/angularjs/plugins/plugin-upload.directive.js"; $jsFiles[] = "node_modules/iframe-resizer/js/iframeResizer.min.js"; $jsFiles[] = "node_modules/iframe-resizer/js/iframeResizer.contentWindow.min.js"; } @@ -475,5 +407,8 @@ public function getClientSideTranslationKeys(&$translationKeys) $translationKeys[] = 'General_PreviousYear'; $translationKeys[] = 'CoreHome_ReportingCategoryHelpPrefix'; $translationKeys[] = 'CoreHome_TechDeprecationWarning'; + $translationKeys[] = 'CoreHome_StartDate'; + $translationKeys[] = 'CoreHome_EndDate'; + $translationKeys[] = 'CoreHome_DataForThisReportHasBeenDisabled'; } } diff --git a/app/plugins/CoreHome/Menu.php b/app/plugins/CoreHome/Menu.php index 3b3051f60..6cade0124 100644 --- a/app/plugins/CoreHome/Menu.php +++ b/app/plugins/CoreHome/Menu.php @@ -8,10 +8,8 @@ */ namespace Piwik\Plugins\CoreHome; -use Piwik\Db; use Piwik\Menu\MenuTop; use Piwik\Piwik; -use Piwik\Plugin; class Menu extends \Piwik\Plugin\Menu { diff --git a/app/plugins/CoreHome/Tracker/VisitRequestProcessor.php b/app/plugins/CoreHome/Tracker/VisitRequestProcessor.php index 65906cd45..74abe8ffd 100644 --- a/app/plugins/CoreHome/Tracker/VisitRequestProcessor.php +++ b/app/plugins/CoreHome/Tracker/VisitRequestProcessor.php @@ -10,7 +10,6 @@ use Piwik\Common; use Piwik\Date; -use Piwik\Config; use Piwik\EventDispatcher; use Piwik\Exception\UnexpectedWebsiteFoundException; use Piwik\Tracker\Cache; diff --git a/app/plugins/CoreHome/Widgets/GetDonateForm.php b/app/plugins/CoreHome/Widgets/GetDonateForm.php index 72431e2d7..c5ea5cf46 100644 --- a/app/plugins/CoreHome/Widgets/GetDonateForm.php +++ b/app/plugins/CoreHome/Widgets/GetDonateForm.php @@ -13,7 +13,6 @@ use Piwik\Widget\Widget; use Piwik\Widget\WidgetConfig; use Piwik\Translation\Translator; -use Piwik\View; class GetDonateForm extends Widget { diff --git a/app/plugins/CoreHome/Widgets/GetSystemSummary.php b/app/plugins/CoreHome/Widgets/GetSystemSummary.php index 3d56a9979..e09fe22d4 100644 --- a/app/plugins/CoreHome/Widgets/GetSystemSummary.php +++ b/app/plugins/CoreHome/Widgets/GetSystemSummary.php @@ -8,7 +8,6 @@ */ namespace Piwik\Plugins\CoreHome\Widgets; -use Piwik\API\Request; use Piwik\Db; use Piwik\Piwik; use Piwik\Plugin; @@ -20,6 +19,7 @@ class GetSystemSummary extends Widget { + const TEST_MYSQL_VERSION = 'mysql-version-redacted'; /** * @var StoredSegmentService */ @@ -108,6 +108,10 @@ public function render() private function getMySqlVersion() { + if (defined('PIWIK_TEST_MODE')) { + return self::TEST_MYSQL_VERSION; + } + $db = Db::get(); return $db->getServerVersion(); } diff --git a/app/plugins/CoreHome/angularjs/field-array/field-array.directive.html b/app/plugins/CoreHome/angularjs/field-array/field-array.directive.html deleted file mode 100644 index 1ce3fcaec..000000000 --- a/app/plugins/CoreHome/angularjs/field-array/field-array.directive.html +++ /dev/null @@ -1,23 +0,0 @@ -
-
- -
-
- - -
-
\ No newline at end of file diff --git a/app/plugins/CoreHome/angularjs/multipairfield/multipairfield.directive.html b/app/plugins/CoreHome/angularjs/multipairfield/multipairfield.directive.html deleted file mode 100644 index a14cc99c3..000000000 --- a/app/plugins/CoreHome/angularjs/multipairfield/multipairfield.directive.html +++ /dev/null @@ -1,61 +0,0 @@ -
-
- -
-
- -
-
- -
-
- -
-
- - -
-
\ No newline at end of file diff --git a/app/plugins/CoreHome/angularjs/period-selector/period-selector.directive.html b/app/plugins/CoreHome/angularjs/period-selector/period-selector.directive.html deleted file mode 100644 index a93c638b5..000000000 --- a/app/plugins/CoreHome/angularjs/period-selector/period-selector.directive.html +++ /dev/null @@ -1,134 +0,0 @@ -
- - - {{ periodSelector.getCurrentlyViewingText() }} - - -
diff --git a/app/plugins/CoreHome/angularjs/progressbar/progressbar.directive.html b/app/plugins/CoreHome/angularjs/progressbar/progressbar.directive.html deleted file mode 100644 index 5bbb4e440..000000000 --- a/app/plugins/CoreHome/angularjs/progressbar/progressbar.directive.html +++ /dev/null @@ -1,7 +0,0 @@ -
-
-
-
- -
\ No newline at end of file diff --git a/app/plugins/CoreHome/angularjs/quick-access/quick-access.directive.html b/app/plugins/CoreHome/angularjs/quick-access/quick-access.directive.html deleted file mode 100644 index c28c8f1d1..000000000 --- a/app/plugins/CoreHome/angularjs/quick-access/quick-access.directive.html +++ /dev/null @@ -1,48 +0,0 @@ -
- - - -
diff --git a/app/plugins/CoreHome/angularjs/report-export/reportexport.popover.html b/app/plugins/CoreHome/angularjs/report-export/reportexport.popover.html deleted file mode 100644 index f20642460..000000000 --- a/app/plugins/CoreHome/angularjs/report-export/reportexport.popover.html +++ /dev/null @@ -1,76 +0,0 @@ -
- -
-
-
- -
-
-
-
-
-
-
- -
-
-
- -
-
-
-
-
- -
-
-
-
- -
- -
-
- -
- {{ 'General_Export'|translate }} - - {{ 'CoreHome_ShowExportUrl'|translate }} - {{ 'CoreHome_HideExportUrl'|translate }} - -
- -
\ No newline at end of file diff --git a/app/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html b/app/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html deleted file mode 100644 index ff3a54906..000000000 --- a/app/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html +++ /dev/null @@ -1,72 +0,0 @@ -
- - -
diff --git a/app/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html b/app/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html deleted file mode 100644 index 4abf3a27e..000000000 --- a/app/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html +++ /dev/null @@ -1,17 +0,0 @@ -
- -
- -
{{ 'CoreHome_NoSuchPage'|translate }}
- -
-
- -
-
-
-
-
-
-
-
diff --git a/app/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.less b/app/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.less deleted file mode 100644 index 1d9fde17b..000000000 --- a/app/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.less +++ /dev/null @@ -1,24 +0,0 @@ -.reporting-page { - > .row { - margin-bottom: 0; - } - - .fullWidgetColumn { - padding-left: 0; - padding-right: 0; - } - - .leftWidgetColumn { - padding-left: 0; - } - - .rightWidgetColumn { - padding-right: 0; - } - - .isFirstWidgetInPage { - .card { - margin-top: 0; - } - } -} \ No newline at end of file diff --git a/app/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html b/app/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html deleted file mode 100644 index 8954b2042..000000000 --- a/app/plugins/CoreHome/angularjs/siteselector/siteselector.directive.html +++ /dev/null @@ -1,73 +0,0 @@ -
- - - - - - - - - ? - {{ placeholder }} - - - - -
diff --git a/app/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html b/app/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html deleted file mode 100644 index 502cdaf72..000000000 --- a/app/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html +++ /dev/null @@ -1,23 +0,0 @@ -
- -
-
- {{ category.name }} -
    -
  • - {{widget.name}} -
  • -
-
-
- -
-
-
-
- -
\ No newline at end of file diff --git a/app/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html b/app/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html deleted file mode 100644 index 21ebd3d0c..000000000 --- a/app/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html +++ /dev/null @@ -1,10 +0,0 @@ -
- - - -
-
-
-
\ No newline at end of file diff --git a/app/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html b/app/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html deleted file mode 100644 index c29f0bbbd..000000000 --- a/app/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html +++ /dev/null @@ -1,15 +0,0 @@ -
-
- -
-

{{widgetName}}

- -
- {{ 'General_ErrorRequest'|translate:(''):('') }} - -
-
- -
- -
\ No newline at end of file diff --git a/app/plugins/CoreHome/angularjs/widget/widget.directive.html b/app/plugins/CoreHome/angularjs/widget/widget.directive.html deleted file mode 100644 index f0e9c4862..000000000 --- a/app/plugins/CoreHome/angularjs/widget/widget.directive.html +++ /dev/null @@ -1,15 +0,0 @@ -
- -
- -
-
-
- -
-
-
-
\ No newline at end of file diff --git a/app/plugins/CoreHome/config/test.php b/app/plugins/CoreHome/config/test.php deleted file mode 100644 index 9e625c422..000000000 --- a/app/plugins/CoreHome/config/test.php +++ /dev/null @@ -1,38 +0,0 @@ - DI\add([ - [ - 'Request.dispatch', - DI\value( - function () { - if (!empty($_GET['setNotifications']) && $_GET['setNotifications'] == 1) { - // trigger some notification - $notification = new Notification('This is a persistent test notification'); - $notification->title = 'Warning:'; - $notification->context = Notification::CONTEXT_WARNING; - $notification->type = Notification::TYPE_PERSISTENT; - $notification->flags = Notification::FLAG_CLEAR; - Notification\Manager::notify('NotificationFixture_persistent_warning', $notification); - - $notification = new Notification('This is another persistent test notification'); - $notification->title = 'Error:'; - $notification->context = Notification::CONTEXT_ERROR; - $notification->type = Notification::TYPE_PERSISTENT; - $notification->flags = Notification::FLAG_CLEAR; - Notification\Manager::notify('NotificationFixture_persistent_error', $notification); - - $notification = new Notification('This is transient test notification'); - $notification->title = 'Error:'; - $notification->context = Notification::CONTEXT_ERROR; - $notification->type = Notification::TYPE_TRANSIENT; - $notification->flags = Notification::FLAG_CLEAR; - Notification\Manager::notify('NotificationFixture_transient_error', $notification); - } - } - ), - ], - ]), -]; \ No newline at end of file diff --git a/app/plugins/CoreHome/lang/ga.json b/app/plugins/CoreHome/lang/ga.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/app/plugins/CoreHome/lang/ga.json @@ -0,0 +1 @@ +{} diff --git a/app/plugins/CoreHome/stylesheets/layout.less b/app/plugins/CoreHome/stylesheets/layout.less index dd1a8b6fd..07da8e266 100644 --- a/app/plugins/CoreHome/stylesheets/layout.less +++ b/app/plugins/CoreHome/stylesheets/layout.less @@ -365,6 +365,9 @@ nav { .item .icon-arrow-right:before { content: "\e63b"; } + .menu-icon { + padding-right: 13px; + } } .collapsible { @@ -419,13 +422,17 @@ nav { input { height: 33px; font-size: 11px; - padding: 10px 12px 10px 10px; + padding: 10px 12px 10px 30px; border: 0; margin: 0; box-sizing: border-box; border-radius: 2px !important; box-shadow: none!important; } + + input::placeholder { + color: #a9a9a9; + } } .piwikTopControl { diff --git a/app/plugins/CoreHome/stylesheets/vue-transitions.less b/app/plugins/CoreHome/stylesheets/vue-transitions.less index cdfef3e1e..d9bd4cf49 100644 --- a/app/plugins/CoreHome/stylesheets/vue-transitions.less +++ b/app/plugins/CoreHome/stylesheets/vue-transitions.less @@ -13,4 +13,12 @@ .slow-fade-out-leave-to { opacity: 0; -} \ No newline at end of file +} + +.fade-out-enter-active { + transition: opacity 0.4s ease; +} + +.fade-out-enter-from { + opacity: 1; +} diff --git a/app/plugins/CoreHome/templates/_adblockDetect.twig b/app/plugins/CoreHome/templates/_adblockDetect.twig index f60d2ac0e..22d02e8c2 100644 --- a/app/plugins/CoreHome/templates/_adblockDetect.twig +++ b/app/plugins/CoreHome/templates/_adblockDetect.twig @@ -1,42 +1,44 @@
 
\ No newline at end of file diff --git a/app/plugins/CoreHome/templates/_dataTable.twig b/app/plugins/CoreHome/templates/_dataTable.twig index f9abda1fc..4ba518a51 100644 --- a/app/plugins/CoreHome/templates/_dataTable.twig +++ b/app/plugins/CoreHome/templates/_dataTable.twig @@ -13,7 +13,7 @@ {% if properties.title %}

{{ properties.title }}

{% endif %} @@ -59,6 +59,8 @@
{% if showReportDataWasPurgedMessage is defined and showReportDataWasPurgedMessage %} {{ 'CoreHome_DataForThisReportHasBeenPurged'|translate(deleteReportsOlderThan) }} + {% elseif showPluginArchiveDisabled is defined and showPluginArchiveDisabled%} + {{ 'CoreHome_DataForThisReportHasBeenDisabled'|translate('', '')|raw }} {% elseif properties.no_data_message %} {{ properties.no_data_message|raw }} {% else %} @@ -73,7 +75,7 @@ {% include "@CoreHome/_dataTableFooter.twig" %} {% endif %} - {% include "@CoreHome/_dataTableJS.twig" %} + {% include "@CoreHome/_dataTableJS.twig" with { reportId: properties.report_id } %} {% endif %}
diff --git a/app/plugins/CoreHome/templates/_dataTableJS.twig b/app/plugins/CoreHome/templates/_dataTableJS.twig index 975dca4b3..7d671c398 100644 --- a/app/plugins/CoreHome/templates/_dataTableJS.twig +++ b/app/plugins/CoreHome/templates/_dataTableJS.twig @@ -1,5 +1,5 @@ diff --git a/app/plugins/CoreHome/templates/_topBar.twig b/app/plugins/CoreHome/templates/_topBar.twig index febe66468..961c6d6b8 100644 --- a/app/plugins/CoreHome/templates/_topBar.twig +++ b/app/plugins/CoreHome/templates/_topBar.twig @@ -13,6 +13,8 @@ {{ menu._html|raw }} {% else %} 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { values[_key - 1] = arguments[_key]; } var pkArgs = values; // handle variadic args AND single array of values (to match _pk_translate signature) - if (values.length === 1 && values[0] && values[0] instanceof Array) { + if (values.length === 1 && values[0] && Array.isArray(values[0])) { pkArgs = values[0]; } return window._pk_translate(translationStringId, pkArgs); // eslint-disable-line } +function translateOrDefault(translationStringIdOrText) { + if (!translationStringIdOrText || !window.piwik_translations[translationStringIdOrText]) { + return translationStringIdOrText; + } + + for (var _len2 = arguments.length, values = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + values[_key2 - 1] = arguments[_key2]; + } + + var pkArgs = values; // handle variadic args AND single array of values (to match _pk_translate signature) + + if (values.length === 1 && values[0] && Array.isArray(values[0])) { + pkArgs = values[0]; + } + + return window._pk_translate(translationStringIdOrText, pkArgs); // eslint-disable-line +} // CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Periods/utilities.ts /*! * Matomo - free/libre analytics platform @@ -543,6 +602,11 @@ var Range_RangePeriod = /*#__PURE__*/function () { value: function containsToday() { return todayIsInRange(this.getDateRange()); } + }, { + key: "getDayCount", + value: function getDayCount() { + return Math.ceil((this.endDate.getTime() - this.startDate.getTime()) / (1000 * 3600 * 24)) + 1; + } }], [{ key: "getLastNRange", value: function getLastNRange(childPeriodType, strAmount, strEndDate) { @@ -1018,9 +1082,17 @@ Periods_Periods.addCustomPeriod('year', Year_YearPeriod); // CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MatomoUrl/MatomoUrl.ts -function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } +function MatomoUrl_slicedToArray(arr, i) { return MatomoUrl_arrayWithHoles(arr) || MatomoUrl_iterableToArrayLimit(arr, i) || MatomoUrl_unsupportedIterableToArray(arr, i) || MatomoUrl_nonIterableRest(); } + +function MatomoUrl_nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } + +function MatomoUrl_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return MatomoUrl_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return MatomoUrl_arrayLikeToArray(o, minLen); } -function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { MatomoUrl_defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } +function MatomoUrl_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } + +function MatomoUrl_iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } + +function MatomoUrl_arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function MatomoUrl_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } @@ -1068,15 +1140,15 @@ var MatomoUrl_MatomoUrl = /*#__PURE__*/function () { MatomoUrl_defineProperty(this, "hashQuery", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])('')); MatomoUrl_defineProperty(this, "urlParsed", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { - return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(MatomoUrl_broadcast.getValuesFromUrl("?".concat(_this.urlQuery.value), true)); + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(_this.parse(_this.urlQuery.value)); })); MatomoUrl_defineProperty(this, "hashParsed", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { - return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(MatomoUrl_broadcast.getValuesFromUrl("?".concat(_this.hashQuery.value), true)); + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(_this.parse(_this.hashQuery.value)); })); MatomoUrl_defineProperty(this, "parsed", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { - return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(_objectSpread(_objectSpread({}, _this.urlParsed.value), _this.hashParsed.value)); + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(Object.assign(Object.assign({}, _this.urlParsed.value), _this.hashParsed.value)); })); this.setUrlQuery(window.location.search); @@ -1095,11 +1167,59 @@ var MatomoUrl_MatomoUrl = /*#__PURE__*/function () { } MatomoUrl_createClass(MatomoUrl, [{ + key: "updateHashToUrl", + value: function updateHashToUrl(url) { + var $location = Matomo_Matomo.helper.getAngularDependency('$location'); + $location.url(url); + } + }, { key: "updateHash", value: function updateHash(params) { - var serializedParams = typeof params !== 'string' ? this.stringify(params) : params; + var modifiedParams = this.getFinalHashParams(params); + var serializedParams = this.stringify(modifiedParams); var $location = Matomo_Matomo.helper.getAngularDependency('$location'); $location.search(serializedParams); + var $timeout = Matomo_Matomo.helper.getAngularDependency('$timeout'); + $timeout(); + } + }, { + key: "updateUrl", + value: function updateUrl(params) { + var hashParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var serializedParams = typeof params !== 'string' ? this.stringify(params) : params; + var modifiedHashParams = Object.keys(hashParams).length ? this.getFinalHashParams(hashParams, params) : {}; + var serializedHashParams = this.stringify(modifiedHashParams); + var url = "?".concat(serializedParams); + + if (serializedHashParams.length) { + url = "".concat(url, "#?").concat(serializedHashParams); + } + + window.broadcast.propagateNewPage('', undefined, undefined, undefined, url); + } + }, { + key: "getFinalHashParams", + value: function getFinalHashParams(params) { + var urlParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var paramsObj = typeof params !== 'string' ? params : this.parse(params); + var urlParamsObj = typeof params !== 'string' ? urlParams : this.parse(urlParams); + return Object.assign({ + // these params must always be present in the hash + period: urlParamsObj.period || this.parsed.value.period, + date: urlParamsObj.date || this.parsed.value.date, + segment: urlParamsObj.segment || this.parsed.value.segment + }, paramsObj); + } // if we're in an embedded context, loads an entire new URL, otherwise updates the hash + + }, { + key: "updateLocation", + value: function updateLocation(params) { + if (Matomo_Matomo.helper.isAngularRenderingThePage()) { + this.updateHash(params); + return; + } + + this.updateUrl(params); } }, { key: "getSearchParam", @@ -1117,11 +1237,25 @@ var MatomoUrl_MatomoUrl = /*#__PURE__*/function () { return window.broadcast.getValueFromUrl(paramName, window.location.search); } + }, { + key: "parse", + value: function parse(query) { + return MatomoUrl_broadcast.getValuesFromUrl("?".concat(query), true); + } }, { key: "stringify", value: function stringify(search) { - // TODO: using $ since URLSearchParams does not handle array params the way Matomo uses them - return $.param(search).replace(/%5B%5D/g, '[]'); + var searchWithoutEmpty = Object.fromEntries(Object.entries(search).filter(function (_ref) { + var _ref2 = MatomoUrl_slicedToArray(_ref, 2), + value = _ref2[1]; + + return value !== '' && value !== null && value !== undefined; + })); // TODO: using $ since URLSearchParams does not handle array params the way Matomo uses them + + return $.param(searchWithoutEmpty).replace(/%5B%5D/g, '[]') // some browsers treat URLs w/ date=a,b differently from date=a%2Cb, causing multiple + // entries to show up in the browser history. this has a compounding effect w/ angular.js, + // which when the back button is pressed to effectively abort the back navigation. + .replace(/%2C/g, ','); } }, { key: "updatePeriodParamsFromUrl", @@ -1186,8 +1320,11 @@ function piwikUrl() { return model; } -piwikUrl.$inject = []; -angular.module('piwikApp.service').service('piwikUrl', piwikUrl); +window.angular.module('piwikApp.service').service('piwikUrl', piwikUrl); // make sure $location is initialized early + +window.angular.module('piwikApp.service').run(['$location', function () { + return null; +}]); // CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Matomo/Matomo.adapter.ts /*! * Matomo - free/libre analytics platform @@ -1213,7 +1350,7 @@ function initPiwikService(piwik, $rootScope) { } Matomo_Matomo.postEventNoEmit.apply(Matomo_Matomo, [name].concat(args)); - return this.$oldEmit.apply(this, [name].concat(args)); + return this.$oldEmit.apply(this, [name].concat(args)); // eslint-disable-line }; $rootScope.$oldBroadcast = $rootScope.$broadcast; // eslint-disable-line @@ -1233,11 +1370,19 @@ function initPiwikService(piwik, $rootScope) { initPiwikService.$inject = ['piwik', '$rootScope']; window.angular.module('piwikApp.service').run(initPiwikService); // CONCATENATED MODULE: ./plugins/CoreHome/vue/src/AjaxHelper/AjaxHelper.ts -function AjaxHelper_ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } +function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } -function AjaxHelper_objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { AjaxHelper_ownKeys(Object(source), true).forEach(function (key) { AjaxHelper_defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { AjaxHelper_ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } +function AjaxHelper_toConsumableArray(arr) { return AjaxHelper_arrayWithoutHoles(arr) || AjaxHelper_iterableToArray(arr) || AjaxHelper_unsupportedIterableToArray(arr) || AjaxHelper_nonIterableSpread(); } -function AjaxHelper_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +function AjaxHelper_nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } + +function AjaxHelper_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return AjaxHelper_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return AjaxHelper_arrayLikeToArray(o, minLen); } + +function AjaxHelper_iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } + +function AjaxHelper_arrayWithoutHoles(arr) { if (Array.isArray(arr)) return AjaxHelper_arrayLikeToArray(arr); } + +function AjaxHelper_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function AjaxHelper_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } @@ -1245,6 +1390,28 @@ function AjaxHelper_createClass(Constructor, protoProps, staticProps) { if (prot function AjaxHelper_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function AjaxHelper_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); } + +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + +function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); } + +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } + +function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); } + +function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); } + +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } + +function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } + /*! * Matomo - free/libre analytics platform * @@ -1314,6 +1481,20 @@ function defaultErrorCallback(deferred, status) { loadingError.show(); } } + +var ApiResponseError = /*#__PURE__*/function (_Error) { + _inherits(ApiResponseError, _Error); + + var _super = _createSuper(ApiResponseError); + + function ApiResponseError() { + AjaxHelper_classCallCheck(this, ApiResponseError); + + return _super.apply(this, arguments); + } + + return ApiResponseError; +}( /*#__PURE__*/_wrapNativeSuper(Error)); /** * Global ajax helper to handle requests within Matomo */ @@ -1347,10 +1528,16 @@ var AjaxHelper_AjaxHelper = /*#__PURE__*/function () { AjaxHelper_defineProperty(this, "errorElement", '#ajaxError'); + AjaxHelper_defineProperty(this, "headers", void 0); + AjaxHelper_defineProperty(this, "requestHandle", null); + AjaxHelper_defineProperty(this, "abortController", null); + AjaxHelper_defineProperty(this, "defaultParams", ['idSite', 'period', 'date', 'segment']); + AjaxHelper_defineProperty(this, "resolveWithHelper", false); + this.errorCallback = defaultErrorCallback; } /** @@ -1377,6 +1564,10 @@ var AjaxHelper_AjaxHelper = /*#__PURE__*/function () { return; } + if (typeof value === 'boolean') { + value = value ? 1 : 0; + } + if (type.toLowerCase() === 'get') { _this.getParams[key] = value; } else if (type.toLowerCase() === 'post') { @@ -1580,14 +1771,44 @@ var AjaxHelper_AjaxHelper = /*#__PURE__*/function () { this.requestHandle = this.buildAjaxCall(); window.globalAjaxQueue.push(this.requestHandle); - return new Promise(function (resolve, reject) { - _this2.requestHandle.then(resolve).fail(function (xhr) { - if (xhr.statusText !== 'abort') { - console.log("Warning: the ".concat($.param(_this2.getParams), " request failed!")); - reject(xhr); + var $timeout = null; + + try { + $timeout = Matomo_Matomo.helper.getAngularDependency('$timeout'); + } catch (e) {// ignore + } + + if (this.abortController) { + this.abortController.signal.addEventListener('abort', function () { + if (_this2.requestHandle) { + _this2.requestHandle.abort(); + } + }); + } + + var result = new Promise(function (resolve, reject) { + _this2.requestHandle.then(function (data) { + if (_this2.resolveWithHelper) { + // NOTE: we can't resolve w/ the jquery xhr, because it's a promise, and will + // just result in following the promise chain back to 'data' + resolve(_this2); // casting hack here + } else { + resolve(data); // ignoring textStatus/jqXHR + } + }).fail(function (xhr) { + if (xhr.statusText === 'abort') { + return; + } + + console.log("Warning: the ".concat($.param(_this2.getParams), " request failed!")); + reject(xhr); + }).done(function () { + if ($timeout) { + $timeout(); // trigger digest } }); }); + return result; } /** * Aborts the current request if it is (still) running @@ -1637,6 +1858,7 @@ var AjaxHelper_AjaxHelper = /*#__PURE__*/function () { url: url, dataType: this.format || 'json', complete: this.completeCallback, + headers: this.headers ? this.headers : undefined, error: function errorCallback() { window.globalAjaxQueue.active -= 1; @@ -1663,7 +1885,9 @@ var AjaxHelper_AjaxHelper = /*#__PURE__*/function () { type = null; } - if (response.message) { + var isLoggedIn = !document.querySelector('#login_form'); + + if (response.message && isLoggedIn) { var UI = window['require']('piwik/UI'); // eslint-disable-line var notification = new UI.Notification(); @@ -1724,9 +1948,7 @@ var AjaxHelper_AjaxHelper = /*#__PURE__*/function () { key: "mixinDefaultPostParams", value: function mixinDefaultPostParams(params) { var defaultParams = this.getDefaultPostParams(); - - var mergedParams = AjaxHelper_objectSpread(AjaxHelper_objectSpread({}, defaultParams), params); - + var mergedParams = Object.assign(Object.assign({}, defaultParams), params); return mergedParams; } /** @@ -1765,6 +1987,11 @@ var AjaxHelper_AjaxHelper = /*#__PURE__*/function () { return params; } + }, { + key: "getRequestHandle", + value: function getRequestHandle() { + return this.requestHandle; + } }], [{ key: "fetch", value: @@ -1825,11 +2052,16 @@ var AjaxHelper_AjaxHelper = /*#__PURE__*/function () { * Element to be displayed on error */ + /** + * Extra headers to add to the request. + */ + /** * Handle for current request */ // helper method entry point - function fetch(params) { + function fetch( // eslint-disable-line + params) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var helper = new AjaxHelper(); @@ -1837,17 +2069,89 @@ var AjaxHelper_AjaxHelper = /*#__PURE__*/function () { helper.withTokenInUrl(); } - helper.setFormat('json'); - helper.addParams(AjaxHelper_objectSpread({ - module: 'API', - format: 'json' - }, params), 'get'); + if (options.errorElement) { + helper.setErrorElement(options.errorElement); + } + + if (options.redirectOnSuccess) { + helper.redirectOnSuccess(options.redirectOnSuccess !== true ? options.redirectOnSuccess : undefined); + } + + helper.setFormat(options.format || 'json'); + + if (Array.isArray(params)) { + helper.setBulkRequests.apply(helper, AjaxHelper_toConsumableArray(params)); + } else { + helper.addParams(Object.assign(Object.assign({ + module: 'API', + format: options.format || 'json' + }, params), {}, { + // ajax helper does not encode the segment parameter assuming it is already encoded. this is + // probably for pre-angularjs code, so we don't want to do this now, but just treat segment + // as a normal query parameter input (so it will have double encoded values in input params + // object, then naturally triple encoded in the URL after a $.param call), however we need + // to support any existing uses of the old code, so instead we do a manual encode here. new + // code that uses .fetch() will not need to pre-encode the parameter, while old code + // can pre-encode it. + segment: params.segment ? encodeURIComponent(params.segment) : undefined + }), 'get'); + } if (options.postParams) { helper.addParams(options.postParams, 'post'); } - return helper.send(); + if (options.headers) { + helper.headers = options.headers; + } + + var createErrorNotification = true; + + if (typeof options.createErrorNotification !== 'undefined' && !options.createErrorNotification) { + helper.useCallbackInCaseOfError(); + helper.setErrorCallback(null); + createErrorNotification = false; + } + + if (options.abortController) { + helper.abortController = options.abortController; + } + + if (options.returnResponseObject) { + helper.resolveWithHelper = true; + } + + return helper.send().then(function (result) { + var data = result instanceof AjaxHelper ? result.requestHandle.responseJSON : result; // check for error if not using default notification behavior + + if (data.result === 'error') { + throw new ApiResponseError(data.message); + } + + return result; + }).catch(function (xhr) { + if (createErrorNotification) { + throw xhr; + } + + var message = 'Something went wrong'; + + if (xhr.status === 504) { + message = 'Request was prossibly aborted'; + } + + throw new Error(message); + }); + } // eslint-disable-next-line @typescript-eslint/no-explicit-any + + }, { + key: "post", + value: function post(params) { + var postParams = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; + var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + return this.fetch(params, Object.assign(Object.assign({}, options), {}, { + postParams: postParams + })); } }]); @@ -1860,11 +2164,17 @@ var AjaxHelper_AjaxHelper = /*#__PURE__*/function () { window.ajaxHelper = AjaxHelper_AjaxHelper; function ajaxQueue() { - return globalAjaxQueue; + return window.globalAjaxQueue; } -angular.module('piwikApp.service').service('globalAjaxQueue', ajaxQueue); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DropdownMenu/DropdownMenu.ts +window.angular.module('piwikApp.service').service('globalAjaxQueue', ajaxQueue); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/PopoverHandler/PopoverHandler.ts +function PopoverHandler_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function PopoverHandler_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function PopoverHandler_createClass(Constructor, protoProps, staticProps) { if (protoProps) PopoverHandler_defineProperties(Constructor.prototype, protoProps); if (staticProps) PopoverHandler_defineProperties(Constructor, staticProps); return Constructor; } + /*! * Matomo - free/libre analytics platform * @@ -1872,47 +2182,129 @@ angular.module('piwikApp.service').service('globalAjaxQueue', ajaxQueue); * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ -/** - * A materializecss dropdown menu that supports submenus. - * - * To use a submenu, just use this directive within another dropdown. - * - * Note: if submenus are used, then dropdowns will never scroll. - * - * Usage: - * Menu - * - */ -/* harmony default export */ var DropdownMenu = ({ - mounted: function mounted(element, binding) { - var options = {}; - $(element).addClass('matomo-dropdown-menu'); - var isSubmenu = !!$(element).parent().closest('.dropdown-content').length; - if (isSubmenu) { - options = { - hover: true - }; - $(element).addClass('submenu'); - $(binding.value.activates).addClass('submenu-dropdown-content'); // if a submenu is used, the dropdown will never scroll +var PopoverHandler_window = window, + PopoverHandler_$ = PopoverHandler_window.$; - $(element).parents('.dropdown-content').addClass('submenu-container'); +var PopoverHandler_PopoverHandler = /*#__PURE__*/function () { + function PopoverHandler() { + PopoverHandler_classCallCheck(this, PopoverHandler); + + this.setup(); + } + + PopoverHandler_createClass(PopoverHandler, [{ + key: "setup", + value: function setup() { + var _this = this; + + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["watch"])(function () { + return src_MatomoUrl_MatomoUrl.parsed.value.popover; + }, function () { + return _this.onPopoverParamChanged(); + }); + + if (src_MatomoUrl_MatomoUrl.parsed.value.popover) { + this.onPopoverParamChangedInitial(); + } + } // don't initiate the handler until the page had a chance to render, + // since some rowactions depend on what's been loaded. + + }, { + key: "onPopoverParamChangedInitial", + value: function onPopoverParamChangedInitial() { + var _this2 = this; + + PopoverHandler_$(function () { + setTimeout(function () { + _this2.openOrClose(); + }); + }); } + }, { + key: "onPopoverParamChanged", + value: function onPopoverParamChanged() { + var _this3 = this; - $(element).dropdown(options); + // make sure all popover handles were registered + PopoverHandler_$(function () { + _this3.openOrClose(); + }); + } + }, { + key: "openOrClose", + value: function openOrClose() { + this.close(); // should be rather done by routing + + var popoverParam = src_MatomoUrl_MatomoUrl.parsed.value.popover; + + if (popoverParam) { + this.open(popoverParam); + } else { + // the URL should only be set to an empty popover if there are no popovers in the stack. + // to avoid avoid any strange inconsistent states, we reset the popover stack here. + window.broadcast.resetPopoverStack(); + } + } + }, { + key: "close", + value: function close() { + window.Piwik_Popover.close(); + } + }, { + key: "open", + value: function open(thePopoverParam) { + // in case the $ was encoded (e.g. when using copy&paste on urls in some browsers) + var popoverParam = decodeURIComponent(thePopoverParam); // revert special encoding from broadcast.propagateNewPopoverParameter() + + popoverParam = popoverParam.replace(/\$/g, '%'); + popoverParam = decodeURIComponent(popoverParam); + var popoverParamParts = popoverParam.split(':'); + var handlerName = popoverParamParts[0]; + popoverParamParts.shift(); + var param = popoverParamParts.join(':'); + + if (typeof window.broadcast.popoverHandlers[handlerName] !== 'undefined' && !window.broadcast.isLoginPage()) { + window.broadcast.popoverHandlers[handlerName](param); + } + } + }]); + + return PopoverHandler; +}(); + +/* harmony default export */ var src_PopoverHandler_PopoverHandler = (new PopoverHandler_PopoverHandler()); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Alert/Alert.vue?vue&type=template&id=c3863ae2 +function Alertvue_type_template_id_c3863ae2_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + + +function Alertvue_type_template_id_c3863ae2_render(_ctx, _cache, $props, $setup, $data, $options) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["alert", Alertvue_type_template_id_c3863ae2_defineProperty({}, "alert-".concat(_ctx.severity), true)]) + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderSlot"])(_ctx.$slots, "default")], 2); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Alert/Alert.vue?vue&type=template&id=c3863ae2 + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Alert/Alert.vue?vue&type=script&lang=ts + +/* harmony default export */ var Alertvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + severity: { + type: String, + required: true + } } -}); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DropdownMenu/DropdownMenu.adapter.ts +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Alert/Alert.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Alert/Alert.vue + + + +Alertvue_type_script_lang_ts.render = Alertvue_type_template_id_c3863ae2_render + +/* harmony default export */ var Alert = (Alertvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/createVueApp.ts /*! * Matomo - free/libre analytics platform * @@ -1921,106 +2313,312 @@ angular.module('piwikApp.service').service('globalAjaxQueue', ajaxQueue); */ -function piwikDropdownMenu($timeout) { - return { - restrict: 'A', - link: function piwikDropdownMenuLink(scope, element, attrs) { - var binding = { - instance: null, - value: { - activates: $("#".concat(attrs.activates))[0] - }, - oldValue: null, - modifiers: {}, - dir: {} - }; - $timeout(function () { - DropdownMenu.mounted(element[0], binding); - }); - } - }; +function createVueApp() { + var app = external_commonjs_vue_commonjs2_vue_root_Vue_["createApp"].apply(void 0, arguments); + app.config.globalProperties.$sanitize = window.vueSanitize; + app.config.globalProperties.translate = translate; + app.config.globalProperties.translateOrDefault = translateOrDefault; + return app; } +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/createAngularJsAdapter.ts +function createAngularJsAdapter_slicedToArray(arr, i) { return createAngularJsAdapter_arrayWithHoles(arr) || createAngularJsAdapter_iterableToArrayLimit(arr, i) || createAngularJsAdapter_unsupportedIterableToArray(arr, i) || createAngularJsAdapter_nonIterableRest(); } + +function createAngularJsAdapter_nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } + +function createAngularJsAdapter_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return createAngularJsAdapter_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return createAngularJsAdapter_arrayLikeToArray(o, minLen); } + +function createAngularJsAdapter_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } + +function createAngularJsAdapter_iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } + +function createAngularJsAdapter_arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } + +function createAngularJsAdapter_typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { createAngularJsAdapter_typeof = function _typeof(obj) { return typeof obj; }; } else { createAngularJsAdapter_typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return createAngularJsAdapter_typeof(obj); } -piwikDropdownMenu.$inject = ['$timeout']; -angular.module('piwikApp').directive('piwikDropdownMenu', piwikDropdownMenu); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/FocusAnywhereButHere/FocusAnywhereButHere.ts /*! * Matomo - free/libre analytics platform * * @link https://matomo.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ -function onClickOutsideElement(element, binding, event) { - var hadUsedScrollbar = binding.value.isMouseDown && binding.value.hasScrolled; - binding.value.isMouseDown = false; - binding.value.hasScrolled = false; - if (hadUsedScrollbar) { - return; - } +/* eslint-disable @typescript-eslint/no-explicit-any */ - if (!element.contains(event.target)) { - if (binding.value) { - binding.value.blur(); - } - } + + +var transcludeCounter = 0; + +function toKebabCase(arg) { + return arg.substring(0, 1).toLowerCase() + arg.substring(1).replace(/[A-Z]/g, function (s) { + return "-".concat(s.toLowerCase()); + }); } -function onScroll(element, binding) { - binding.value.hasScrolled = true; +function toAngularJsCamelCase(arg) { + return arg.substring(0, 1).toLowerCase() + arg.substring(1).replace(/-([a-z])/g, function (s, p) { + return p.toUpperCase(); + }); } -function onMouseDown(element, binding) { - binding.value.isMouseDown = true; - binding.value.hasScrolled = false; +function removeAngularJsSpecificProperties(newValue) { + if (createAngularJsAdapter_typeof(newValue) === 'object' && newValue !== null && Object.getPrototypeOf(newValue) === Object.prototype) { + return Object.fromEntries(Object.entries(newValue).filter(function (pair) { + return !/^\$/.test(pair[0]); + })); + } + + return newValue; } +function createAngularJsAdapter(options) { + var component = options.component, + require = options.require, + _options$scope = options.scope, + scope = _options$scope === void 0 ? {} : _options$scope, + _options$events = options.events, + events = _options$events === void 0 ? {} : _options$events, + $inject = options.$inject, + directiveName = options.directiveName, + transclude = options.transclude, + mountPointFactory = options.mountPointFactory, + postCreate = options.postCreate, + noScope = options.noScope, + _options$restrict = options.restrict, + restrict = _options$restrict === void 0 ? 'A' : _options$restrict, + priority = options.priority, + replace = options.replace; + var currentTranscludeCounter = transcludeCounter; -function onEscapeHandler(element, binding, event) { - if (event.which === 27) { - setTimeout(function () { - binding.value.isMouseDown = false; - binding.value.hasScrolled = false; + if (transclude) { + transcludeCounter += 1; + } - if (binding.value.blur) { - binding.value.blur(); + var vueToAngular = {}; + var angularJsScope = {}; + Object.entries(scope).forEach(function (_ref) { + var _ref2 = createAngularJsAdapter_slicedToArray(_ref, 2), + scopeVarName = _ref2[0], + info = _ref2[1]; + + if (!info.vue) { + info.vue = scopeVarName; + } + + if (info.angularJsBind) { + angularJsScope[scopeVarName] = info.angularJsBind; + } + + vueToAngular[info.vue] = scopeVarName; + }); + + function angularJsAdapter() { + for (var _len = arguments.length, injectedServices = new Array(_len), _key = 0; _key < _len; _key++) { + injectedServices[_key] = arguments[_key]; + } + + var adapter = { + restrict: restrict, + require: require, + priority: priority, + scope: noScope ? undefined : angularJsScope, + compile: function angularJsAdapterCompile() { + return { + post: function angularJsAdapterLink(ngScope, ngElement, ngAttrs, ngController) { + var transcludeClone = transclude ? ngElement.find("[ng-transclude][counter=".concat(currentTranscludeCounter, "]")) : null; // build the root vue template + + var rootVueTemplate = '"); + } + + return adapter; } + + angularJsAdapter.$inject = $inject || []; + window.angular.module('piwikApp').directive(directiveName, angularJsAdapter); + return angularJsAdapter; } +function transformAngularJsBoolAttr(v) { + if (typeof v === 'undefined') { + return undefined; + } -var doc = document.documentElement; -/** - * Usage (in a component): - * - * directives: { - * // function call is important since we store state in this directive - * FocusAnywhereButHere: FocusAnywhereButHere(), - * } - * - * Note: the binding data needs to be static, changes will not be handled. - */ + if (v === 'true') { + return true; + } -/* harmony default export */ var FocusAnywhereButHere = ({ - mounted: function mounted(el, binding) { - binding.value.isMouseDown = false; - binding.value.hasScrolled = false; - binding.value.onEscapeHandler = onEscapeHandler.bind(null, el, binding); - binding.value.onMouseDown = onMouseDown.bind(null, el, binding); - binding.value.onClickOutsideElement = onClickOutsideElement.bind(null, el, binding); - binding.value.onScroll = onScroll.bind(null, el, binding); - doc.addEventListener('keyup', binding.value.onEscapeHandler); - doc.addEventListener('mousedown', binding.value.onMouseDown); - doc.addEventListener('mouseup', binding.value.onClickOutsideElement); - doc.addEventListener('scroll', binding.value.onScroll); - }, - unmounted: function unmounted(el, binding) { - doc.removeEventListener('keyup', binding.value.onEscapeHandler); - doc.removeEventListener('mousedown', binding.value.onMouseDown); - doc.removeEventListener('mouseup', binding.value.onClickOutsideElement); - doc.removeEventListener('scroll', binding.value.onScroll); + return !!v && v > 0 && v !== '0'; +} +function transformAngularJsIntAttr(v) { + if (typeof v === 'undefined') { + return undefined; } -}); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/FocusAnywhereButHere/FocusAnywhereButHere.adapter.ts + + if (v === null) { + return null; + } + + return parseInt(v, 10); +} // utility function for service adapters + +function clone(p) { + if (typeof p === 'undefined') { + return p; + } + + return JSON.parse(JSON.stringify(p)); +} +function cloneThenApply(p) { + var result = clone(p); + Matomo_Matomo.helper.getAngularDependency('$rootScope').$applyAsync(); + return result; +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Alert/Alert.adapter.ts /*! * Matomo - free/libre analytics platform * @@ -2028,15 +2626,202 @@ var doc = document.documentElement; * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ -/** - * The given expression will be executed when the user presses either escape or presses something - * outside of this element - * - * Example: - *
my dialog
- */ -function piwikFocusAnywhereButHere() { +/* harmony default export */ var Alert_adapter = (createAngularJsAdapter({ + component: Alert, + scope: { + severity: { + vue: 'severity', + angularJsBind: '@piwikAlert' + } + }, + directiveName: 'piwikAlert', + transclude: true +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DropdownMenu/DropdownMenu.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * A materializecss dropdown menu that supports submenus. + * + * To use a submenu, just use this directive within another dropdown. + * + * Note: if submenus are used, then dropdowns will never scroll. + * + * Usage: + * Menu + * + */ + +/* harmony default export */ var DropdownMenu = ({ + mounted: function mounted(element, binding) { + var options = {}; + $(element).addClass('matomo-dropdown-menu'); + var isSubmenu = !!$(element).parent().closest('.dropdown-content').length; + + if (isSubmenu) { + var _binding$value; + + options = { + hover: true + }; + $(element).addClass('submenu'); + $(((_binding$value = binding.value) === null || _binding$value === void 0 ? void 0 : _binding$value.activates) || $(element).data('target')).addClass('submenu-dropdown-content'); // if a submenu is used, the dropdown will never scroll + + $(element).parents('.dropdown-content').addClass('submenu-container'); + } + + $(element).dropdown(options); + }, + updated: function updated(element) { + // classes can be overwritten when elements bind to :class, nextTick + using + // updated avoids this problem (and doing in both mounted and updated avoids a temporary + // state where the classes aren't added) + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["nextTick"])(function () { + $(element).addClass('matomo-dropdown-menu'); + }); + } +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DropdownMenu/DropdownMenu.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +function piwikDropdownMenu($timeout) { + return { + restrict: 'A', + link: function piwikDropdownMenuLink(scope, element, attrs) { + var binding = { + instance: null, + value: { + activates: $("#".concat(attrs.activates))[0] + }, + oldValue: null, + modifiers: {}, + dir: {} + }; + $timeout(function () { + DropdownMenu.mounted(element[0], binding); + }); + } + }; +} + +piwikDropdownMenu.$inject = ['$timeout']; +window.angular.module('piwikApp').directive('piwikDropdownMenu', piwikDropdownMenu); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/FocusAnywhereButHere/FocusAnywhereButHere.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +function onClickOutsideElement(element, binding, event) { + var hadUsedScrollbar = binding.value.isMouseDown && binding.value.hasScrolled; + binding.value.isMouseDown = false; + binding.value.hasScrolled = false; + + if (hadUsedScrollbar) { + return; + } + + if (!element.contains(event.target)) { + if (binding.value) { + binding.value.blur(); + } + } +} + +function onScroll(element, binding) { + binding.value.hasScrolled = true; +} + +function onMouseDown(element, binding) { + binding.value.isMouseDown = true; + binding.value.hasScrolled = false; +} + +function onEscapeHandler(element, binding, event) { + if (event.which === 27) { + setTimeout(function () { + binding.value.isMouseDown = false; + binding.value.hasScrolled = false; + + if (binding.value.blur) { + binding.value.blur(); + } + }, 0); + } +} + +var doc = document.documentElement; +/** + * Usage (in a component): + * + * directives: { + * // function call is important since we store state in this directive + * FocusAnywhereButHere: FocusAnywhereButHere(), + * } + * + * Note: the binding data needs to be static, changes will not be handled. + */ + +/* harmony default export */ var FocusAnywhereButHere = ({ + mounted: function mounted(el, binding) { + binding.value.isMouseDown = false; + binding.value.hasScrolled = false; + binding.value.onEscapeHandler = onEscapeHandler.bind(null, el, binding); + binding.value.onMouseDown = onMouseDown.bind(null, el, binding); + binding.value.onClickOutsideElement = onClickOutsideElement.bind(null, el, binding); + binding.value.onScroll = onScroll.bind(null, el, binding); + doc.addEventListener('keyup', binding.value.onEscapeHandler); + doc.addEventListener('mousedown', binding.value.onMouseDown); + doc.addEventListener('mouseup', binding.value.onClickOutsideElement); + doc.addEventListener('scroll', binding.value.onScroll); + }, + unmounted: function unmounted(el, binding) { + doc.removeEventListener('keyup', binding.value.onEscapeHandler); + doc.removeEventListener('mousedown', binding.value.onMouseDown); + doc.removeEventListener('mouseup', binding.value.onClickOutsideElement); + doc.removeEventListener('scroll', binding.value.onScroll); + } +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/FocusAnywhereButHere/FocusAnywhereButHere.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * The given expression will be executed when the user presses either escape or presses something + * outside of this element + * + * Example: + *
my dialog
+ */ + +function piwikFocusAnywhereButHere() { return { restrict: 'A', link: function focusAnywhereButHereLink(scope, element, attr) { @@ -2061,36 +2846,5953 @@ function piwikFocusAnywhereButHere() { }; } -piwikFocusAnywhereButHere.$inject = []; -angular.module('piwikApp.directive').directive('piwikFocusAnywhereButHere', piwikFocusAnywhereButHere); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/FocusIf/FocusIf.ts -/*! - * Matomo - free/libre analytics platform - * - * @link https://matomo.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ -function doFocusIf(el, binding) { - if (binding.arg) { - setTimeout(function () { - el.focus(); +window.angular.module('piwikApp.directive').directive('piwikFocusAnywhereButHere', piwikFocusAnywhereButHere); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/FocusIf/FocusIf.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +function doFocusIf(el, binding) { + if (binding.arg) { + setTimeout(function () { + el.focus(); + + if (binding.value.afterFocus) { + binding.value.afterFocus(); + } + }, 5); + } +} + +/* harmony default export */ var FocusIf = ({ + mounted: function mounted(el, binding) { + doFocusIf(el, binding); + }, + updated: function updated(el, binding) { + doFocusIf(el, binding); + } +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/FocusIf/FocusIf.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/** + * If the given expression evaluates to true the element will be focused + * + * Example: + * + */ + +function piwikFocusIf() { + return { + restrict: 'A', + link: function focusIfLink(scope, element, attrs) { + scope.$watch(attrs.piwikFocusIf, function (newValue) { + var binding = { + instance: null, + arg: newValue ? '1' : undefined, + value: { + afterFocus: function afterFocus() { + return scope.$apply(); + } + }, + oldValue: null, + modifiers: {}, + dir: {} + }; + FocusIf.updated(element[0], binding); + }); + } + }; +} + +window.angular.module('piwikApp.directive').directive('piwikFocusIf', piwikFocusIf); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/directiveUtilities.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +function getRef(expander, binding) { + var _binding$instance; + + return expander instanceof HTMLElement ? expander : (_binding$instance = binding.instance) === null || _binding$instance === void 0 ? void 0 : _binding$instance.$refs[expander]; +} + +/* harmony default export */ var directiveUtilities = ({ + getRef: getRef +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ExpandOnClick/ExpandOnClick.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + + +function onExpand(element) { + element.classList.toggle('expanded'); + var positionElement = element.querySelector('.dropdown.positionInViewport'); + + if (positionElement) { + Matomo_Matomo.helper.setMarginLeftToBeInViewport(positionElement); + } +} + +function ExpandOnClick_onClickOutsideElement(element, binding, event) { + var hadUsedScrollbar = binding.value.isMouseDown && binding.value.hasScrolled; + binding.value.isMouseDown = false; + binding.value.hasScrolled = false; + + if (hadUsedScrollbar) { + return; + } + + if (!element.contains(event.target)) { + element.classList.remove('expanded'); + } +} + +function ExpandOnClick_onScroll(binding) { + binding.value.hasScrolled = true; +} + +function ExpandOnClick_onMouseDown(binding) { + binding.value.isMouseDown = true; + binding.value.hasScrolled = false; +} + +function ExpandOnClick_onEscapeHandler(element, binding, event) { + if (event.which === 27) { + binding.value.isMouseDown = false; + binding.value.hasScrolled = false; + element.classList.remove('expanded'); + } +} + +var ExpandOnClick_doc = document.documentElement; +var ExpandOnClick_window = window, + ExpandOnClick_$ = ExpandOnClick_window.$; +/** + * Usage (in a component): + * + * directives: { + * ExpandOnClick: ExpandOnClick(), // function call is important since we store state + * // in this directive + * } + */ + +/* harmony default export */ var ExpandOnClick = ({ + mounted: function mounted(el, binding) { + binding.value.isMouseDown = false; + binding.value.hasScrolled = false; + binding.value.onExpand = onExpand.bind(null, el); + binding.value.onEscapeHandler = ExpandOnClick_onEscapeHandler.bind(null, el, binding); + binding.value.onMouseDown = ExpandOnClick_onMouseDown.bind(null, binding); + binding.value.onClickOutsideElement = ExpandOnClick_onClickOutsideElement.bind(null, el, binding); + binding.value.onScroll = ExpandOnClick_onScroll.bind(null, binding); + setTimeout(function () { + var expander = directiveUtilities.getRef(binding.value.expander, binding); + + if (expander) { + ExpandOnClick_$(expander).on('click', binding.value.onExpand); + } + }); + ExpandOnClick_doc.addEventListener('keyup', binding.value.onEscapeHandler); + ExpandOnClick_doc.addEventListener('mousedown', binding.value.onMouseDown); + ExpandOnClick_doc.addEventListener('mouseup', binding.value.onClickOutsideElement); + ExpandOnClick_doc.addEventListener('scroll', binding.value.onScroll); + }, + unmounted: function unmounted(el, binding) { + var expander = directiveUtilities.getRef(binding.value.expander, binding); + + if (expander) { + ExpandOnClick_$(expander).off('click', binding.value.onExpand); + } + + ExpandOnClick_doc.removeEventListener('keyup', binding.value.onEscapeHandler); + ExpandOnClick_doc.removeEventListener('mousedown', binding.value.onMouseDown); + ExpandOnClick_doc.removeEventListener('mouseup', binding.value.onClickOutsideElement); + ExpandOnClick_doc.removeEventListener('scroll', binding.value.onScroll); + } +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ExpandOnClick/ExpandOnClick.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +function piwikExpandOnClick() { + return { + restrict: 'A', + link: function expandOnClickLink(scope, element) { + var binding = { + instance: null, + value: { + expander: element.find('.title').first()[0] + }, + oldValue: null, + modifiers: {}, + dir: {} + }; + ExpandOnClick.mounted(element[0], binding); + element.on('$destroy', function () { + return ExpandOnClick.unmounted(element[0], binding); + }); + } + }; +} +window.angular.module('piwikApp').directive('piwikExpandOnClick', piwikExpandOnClick); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ExpandOnHover/ExpandOnHover.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + + +function onMouseEnter(element) { + element.classList.add('expanded'); + var positionElement = element.querySelector('.dropdown.positionInViewport'); + + if (positionElement) { + Matomo_Matomo.helper.setMarginLeftToBeInViewport(positionElement); + } +} + +function onMouseLeave(element) { + element.classList.remove('expanded'); +} + +function ExpandOnHover_onClickOutsideElement(element, event) { + if (!element.contains(event.target)) { + element.classList.remove('expanded'); + } +} + +function ExpandOnHover_onEscapeHandler(element, event) { + if (event.which === 27) { + element.classList.remove('expanded'); + } +} + +var ExpandOnHover_doc = document.documentElement; +/** + * Usage (in a component): + * + * directives: { + * ExpandOnHover: ExpandOnHover(), // function call is important since we store state + * // in this directive + * } + */ + +/* harmony default export */ var ExpandOnHover = ({ + mounted: function mounted(el, binding) { + binding.value.onMouseEnter = onMouseEnter.bind(null, el); + binding.value.onMouseLeave = onMouseLeave.bind(null, el); + binding.value.onClickOutsideElement = ExpandOnHover_onClickOutsideElement.bind(null, el); + binding.value.onEscapeHandler = ExpandOnHover_onEscapeHandler.bind(null, el); + setTimeout(function () { + var expander = directiveUtilities.getRef(binding.value.expander, binding); + + if (expander) { + expander.addEventListener('mouseenter', binding.value.onMouseEnter); + } + }); + el.addEventListener('mouseleave', binding.value.onMouseLeave); + ExpandOnHover_doc.addEventListener('keyup', binding.value.onEscapeHandler); + ExpandOnHover_doc.addEventListener('mouseup', binding.value.onClickOutsideElement); + }, + unmounted: function unmounted(el, binding) { + var expander = directiveUtilities.getRef(binding.value.expander, binding); + + if (expander) { + expander.removeEventListener('mouseenter', binding.value.onMouseEnter); + } + + el.removeEventListener('mouseleave', binding.value.onMouseLeave); + document.removeEventListener('keyup', binding.value.onEscapeHandler); + document.removeEventListener('mouseup', binding.value.onClickOutsideElement); + } +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ExpandOnHover/ExpandOnHover.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +function piwikExpandOnHover() { + return { + restrict: 'A', + link: function expandOnHoverLink(scope, element) { + var binding = { + instance: null, + value: { + expander: element.find('.title').first()[0] + }, + oldValue: null, + modifiers: {}, + dir: {} + }; + ExpandOnHover.mounted(element[0], binding); + element.on('$destroy', function () { + return ExpandOnHover.unmounted(element[0], binding); + }); + } + }; +} + +window.angular.module('piwikApp').directive('piwikExpandOnHover', piwikExpandOnHover); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ShowSensitiveData/ShowSensitiveData.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +var ShowSensitiveData_window = window, + ShowSensitiveData_$ = ShowSensitiveData_window.$; +/** + * Handles visibility of sensitive data. By default data will be shown replaced with stars (*) + * On click on the element the full data will be shown + * + * Configuration attributes: + * data-show-characters number of characters to show in clear text (defaults to 6) + * data-click-element-selector selector for element that will show the full data on click + * (defaults to element) + * + * Example: + *
+ */ + +/* harmony default export */ var ShowSensitiveData = ({ + mounted: function mounted(el, binding) { + var element = ShowSensitiveData_$(el); + var sensitiveData = binding.value.sensitiveData; + var showCharacters = binding.value.showCharacters || 6; + var clickElement = binding.value.clickElementSelector || element; + var protectedData = ''; + + if (showCharacters > 0) { + protectedData += sensitiveData.substr(0, showCharacters); + } + + protectedData += sensitiveData.substr(showCharacters).replace(/./g, '*'); + element.html(protectedData); + + function onClickHandler() { + element.html(sensitiveData); + ShowSensitiveData_$(clickElement).css({ + cursor: '' + }); + ShowSensitiveData_$(clickElement).tooltip('destroy'); + } + + ShowSensitiveData_$(clickElement).tooltip({ + content: translate('CoreHome_ClickToSeeFullInformation'), + items: '*', + track: true + }); + ShowSensitiveData_$(clickElement).one('click', onClickHandler); + ShowSensitiveData_$(clickElement).css({ + cursor: 'pointer' + }); + } +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ShowSensitiveData/ShowSensitiveData.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +function piwikShowSensitiveData() { + return { + restrict: 'A', + link: function piwikShowSensitiveDataLink(scope, element, attr) { + var binding = { + instance: null, + value: { + sensitiveData: attr.piwikShowSensitiveData || (attr.text ? attr.text() : ''), + showCharacters: attr.showCharacters ? parseInt(attr.showCharacters, 10) : undefined, + clickElementSelector: attr.clickElementSelector + }, + oldValue: null, + modifiers: {}, + dir: {} + }; + ShowSensitiveData.mounted(element[0], binding); + } + }; +} +window.angular.module('piwikApp').directive('piwikShowSensitiveData', piwikShowSensitiveData); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DropdownButton/DropdownButton.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +var DropdownButton_window = window, + DropdownButton_$ = DropdownButton_window.$; +/* harmony default export */ var DropdownButton = ({ + mounted: function mounted(el) { + var element = DropdownButton_$(el); // BC for materializecss 0.97 => 1.0 + + if (!element.attr('data-target') && element.attr('data-activates')) { + element.attr('data-target', element.attr('data-activates')); + } + + var target = element.attr('data-target'); + + if (target && DropdownButton_$("#".concat(target)).length) { + element.dropdown({ + inDuration: 300, + outDuration: 225, + constrainWidth: false, + // hover: true, // Activate on hover + belowOrigin: true // Displays dropdown below the button + + }); + } + } +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DropdownButton/DropdownButton.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +function piwikDropdownButton() { + return { + restrict: 'C', + link: function piwikDropdownButtonLink(scope, element) { + DropdownButton.mounted(element[0]); + } + }; +} +window.angular.module('piwikApp').directive('dropdownButton', piwikDropdownButton); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/SelectOnFocus/SelectOnFocus.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +function onFocusHandler(binding, event) { + if (binding.value.focusedElement !== event.target) { + binding.value.focusedElement = event.target; + window.angular.element(event.target).select(); + } +} + +function SelectOnFocus_onClickHandler(event) { + // .select() + focus and blur seems to not work on pre elements + var range = document.createRange(); + range.selectNode(event.target); + var selection = window.getSelection(); + + if (selection && selection.rangeCount > 0) { + selection.removeAllRanges(); + } + + if (selection) { + selection.addRange(range); + } +} + +function onBlurHandler(binding) { + delete binding.value.focusedElement; +} + +/* harmony default export */ var SelectOnFocus = ({ + mounted: function mounted(el, binding) { + var tagName = el.tagName.toLowerCase(); + binding.value.elementSupportsSelect = tagName === 'textarea'; + + if (binding.value.elementSupportsSelect) { + binding.value.onFocusHandler = onFocusHandler.bind(null, binding); + binding.value.onBlurHandler = onBlurHandler.bind(null, binding); + el.addEventListener('focus', binding.value.onFocusHandler); + el.addEventListener('blur', binding.value.onBlurHandler); + } else { + binding.value.onClickHandler = SelectOnFocus_onClickHandler; + el.addEventListener('click', binding.value.onClickHandler); + } + }, + unmounted: function unmounted(el, binding) { + if (binding.value.elementSupportsSelect) { + el.removeEventListener('focus', binding.value.onFocusHandler); + el.removeEventListener('blur', binding.value.onBlurHandler); + } else { + el.removeEventListener('click', binding.value.onClickHandler); + } + } +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/SelectOnFocus/SelectOnFocus.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +function piwikSelectOnFocus() { + return { + restrict: 'A', + link: function piwikSelectOnFocusLink(scope, element) { + var binding = { + instance: null, + value: {}, + oldValue: null, + modifiers: {}, + dir: {} + }; + SelectOnFocus.mounted(element[0], binding); + element.on('$destroy', function () { + return SelectOnFocus.unmounted(element[0], binding); + }); + } + }; +} +window.angular.module('piwikApp').directive('piwikSelectOnFocus', piwikSelectOnFocus); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/SideNav/SideNav.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +var initialized = false; +/** + * Will activate the materialize side nav feature once rendered. We use this directive as + * it makes sure the actual left menu is rendered at the time we init the side nav. + * + * Has to be set on a collaapsible element + * + * Example: + *
...
+ */ + +/* harmony default export */ var SideNav = ({ + mounted: function mounted(el, binding) { + if (!binding.value.activator) { + return; + } + + setTimeout(function () { + if (!initialized) { + initialized = true; + var sideNavActivator = directiveUtilities.getRef(binding.value.activator, binding); + + if (sideNavActivator) { + window.$(sideNavActivator).show(); + var targetSelector = sideNavActivator.getAttribute('data-target'); // @ts-ignore + + window.$("#".concat(targetSelector)).sidenav({ + closeOnClick: true + }); + } + } + + if (el.classList.contains('collapsible')) { + window.$(el).collapsible(); + } + }); + } +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/SideNav/SideNav.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +function piwikSideNav($timeout) { + return { + restrict: 'A', + priority: 10, + link: function linkPiwikSideNav(scope, element, attr) { + var binding = { + instance: null, + value: { + activator: $(attr.piwikSideNav)[0] + }, + oldValue: null, + modifiers: {}, + dir: {} + }; + $timeout(function () { + SideNav.mounted(element[0], binding); + }); + } + }; +} +piwikSideNav.$inject = ['$timeout']; +window.angular.module('piwikApp.directive').directive('piwikSideNav', piwikSideNav); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/MatomoDialog/MatomoDialog.vue?vue&type=template&id=00aba563 + +var _hoisted_1 = { + ref: "root" +}; +function MatomoDialogvue_type_template_id_00aba563_render(_ctx, _cache, $props, $setup, $data, $options) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])((Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", _hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderSlot"])(_ctx.$slots, "default")], 512)), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.modelValue]]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MatomoDialog/MatomoDialog.vue?vue&type=template&id=00aba563 + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/MatomoDialog/MatomoDialog.vue?vue&type=script&lang=ts + + +/* harmony default export */ var MatomoDialogvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + /** + * Whether the modal is displayed or not; + */ + modelValue: { + type: Boolean, + required: true + }, + + /** + * Only here for backwards compatibility w/ AngularJS. If supplied, we use this + * element to launch the modal instead of the element in the slot. This should not + * be used for new Vue code. + * + * @deprecated + */ + element: { + type: HTMLElement, + required: false + } + }, + emits: ['yes', 'no', 'closeEnd', 'close', 'validation', 'update:modelValue'], + activated: function activated() { + this.$emit('update:modelValue', false); + }, + watch: { + modelValue: function modelValue(newValue, oldValue) { + var _this = this; + + if (newValue) { + var slotElement = this.element || this.$refs.root.firstElementChild; + Matomo_Matomo.helper.modalConfirm(slotElement, { + yes: function yes() { + _this.$emit('yes'); + }, + no: function no() { + _this.$emit('no'); + }, + validation: function validation() { + _this.$emit('validation'); + } + }, { + onCloseEnd: function onCloseEnd() { + // materialize removes the child element, so we move it back to the slot + if (!_this.element) { + _this.$refs.root.appendChild(slotElement); + } + + _this.$emit('update:modelValue', false); + + _this.$emit('closeEnd'); + } + }); + } else if (newValue === false && oldValue === true) { + // the user closed the dialog, e.g. by pressing Esc or clicking away from it + this.$emit('close'); + } + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MatomoDialog/MatomoDialog.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MatomoDialog/MatomoDialog.vue + + + +MatomoDialogvue_type_script_lang_ts.render = MatomoDialogvue_type_template_id_00aba563_render + +/* harmony default export */ var MatomoDialog = (MatomoDialogvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MatomoDialog/MatomoDialog.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var MatomoDialog_adapter = (createAngularJsAdapter({ + component: MatomoDialog, + scope: { + show: { + vue: 'modelValue', + default: false + }, + element: { + default: function _default(scope, element) { + return element[0]; + } + } + }, + events: { + yes: function yes($event, vm, scope, element, attrs) { + if (attrs.yes) { + scope.$eval(attrs.yes); + setTimeout(function () { + scope.$apply(); + }, 0); + } + }, + no: function no($event, vm, scope, element, attrs) { + if (attrs.no) { + scope.$eval(attrs.no); + setTimeout(function () { + scope.$apply(); + }, 0); + } + }, + validation: function validation($event, vm, scope, element, attrs) { + if (attrs.no) { + scope.$eval(attrs.no); + setTimeout(function () { + scope.$apply(); + }, 0); + } + }, + close: function close($event, vm, scope, element, attrs) { + if (attrs.close) { + scope.$eval(attrs.close); + setTimeout(function () { + scope.$apply(); + }, 0); + } + }, + 'update:modelValue': function updateModelValue(newValue, vm, scope, element, attrs, controller, $parse) { + setTimeout(function () { + scope.$apply($parse(attrs.piwikDialog).assign(scope, newValue)); + }, 0); + } + }, + $inject: ['$parse'], + directiveName: 'piwikDialog', + transclude: true, + mountPointFactory: function mountPointFactory(scope, element) { + var vueRootPlaceholder = $('
'); + vueRootPlaceholder.appendTo(element); + return vueRootPlaceholder[0]; + }, + postCreate: function postCreate(vm, scope, element, attrs) { + scope.$watch(attrs.piwikDialog, function (newValue, oldValue) { + if (oldValue !== newValue) { + vm.modelValue = newValue || false; + } + }); + }, + noScope: true +})); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/EnrichedHeadline/EnrichedHeadline.vue?vue&type=template&id=7b85675d + +var EnrichedHeadlinevue_type_template_id_7b85675d_hoisted_1 = { + key: 0, + class: "title", + tabindex: "6" +}; +var _hoisted_2 = ["href", "title"]; +var _hoisted_3 = { + class: "iconsBar" +}; +var _hoisted_4 = ["href", "title"]; + +var _hoisted_5 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + class: "icon-help" +}, null, -1); + +var _hoisted_6 = [_hoisted_5]; +var _hoisted_7 = ["title"]; + +var _hoisted_8 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + class: "icon-info" +}, null, -1); + +var _hoisted_9 = [_hoisted_8]; +var _hoisted_10 = { + class: "ratingIcons" +}; +var _hoisted_11 = { + class: "inlineHelp" +}; +var _hoisted_12 = ["innerHTML"]; +var _hoisted_13 = ["innerHTML"]; +var _hoisted_14 = ["href"]; +function EnrichedHeadlinevue_type_template_id_7b85675d_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_RateFeature = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("RateFeature"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + class: "enrichedHeadline", + onMouseenter: _cache[1] || (_cache[1] = function ($event) { + return _ctx.showIcons = true; + }), + onMouseleave: _cache[2] || (_cache[2] = function ($event) { + return _ctx.showIcons = false; + }), + ref: "root" + }, [!_ctx.editUrl ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", EnrichedHeadlinevue_type_template_id_7b85675d_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderSlot"])(_ctx.$slots, "default")])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.editUrl ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { + key: 1, + class: "title", + href: _ctx.editUrl, + title: _ctx.translate('CoreHome_ClickToEditX', _ctx.$sanitize(_ctx.actualFeatureName)) + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderSlot"])(_ctx.$slots, "default")], 8, _hoisted_2)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", _hoisted_3, [_ctx.helpUrl && !_ctx.actualInlineHelp ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { + key: 0, + rel: "noreferrer noopener", + target: "_blank", + class: "helpIcon", + href: _ctx.helpUrl, + title: _ctx.translate('CoreHome_ExternalHelp') + }, _hoisted_6, 8, _hoisted_4)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.actualInlineHelp ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { + key: 1, + onClick: _cache[0] || (_cache[0] = function ($event) { + return _ctx.showInlineHelp = !_ctx.showInlineHelp; + }), + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["helpIcon", { + 'active': _ctx.showInlineHelp + }]), + title: _ctx.translate(_ctx.reportGenerated ? 'General_HelpReport' : 'General_Help') + }, _hoisted_9, 10, _hoisted_7)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", _hoisted_10, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_RateFeature, { + title: _ctx.actualFeatureName + }, null, 8, ["title"])])], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.showIcons || _ctx.showInlineHelp]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", _hoisted_11, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", { + innerHTML: _ctx.$sanitize(_ctx.actualInlineHelp) + }, null, 8, _hoisted_12), _ctx.reportGenerated != '' ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("span", { + key: 0, + class: "helpDate", + innerHTML: _ctx.$sanitize(_ctx.reportGenerated) + }, null, 8, _hoisted_13)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.helpUrl ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { + key: 1, + rel: "noreferrer noopener", + target: "_blank", + class: "readMore", + href: _ctx.helpUrl + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_MoreDetails')), 9, _hoisted_14)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.showInlineHelp]])], 544); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/EnrichedHeadline/EnrichedHeadline.vue?vue&type=template&id=7b85675d + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/useExternalPluginComponent.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +/* eslint-disable @typescript-eslint/ban-ts-comment */ + +function useExternalPluginComponent(plugin, component) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineAsyncComponent"])(function () { + return new Promise(function (resolve) { + window.$(document).ready(function () { + if (window[plugin]) { + resolve(window[plugin][component]); + } else { + // @ts-ignore + resolve(null); // plugin not loaded + } + }); + }); + }); +} +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/EnrichedHeadline/EnrichedHeadline.vue?vue&type=script&lang=ts + + + + // working around a cycle in dependencies (CoreHome depends on Feedback, Feedback depends on +// CoreHome) + +var RateFeature = useExternalPluginComponent('Feedback', 'RateFeature'); +/** + * Usage: + * + *

All Websites Dashboard

+ * -> uses "All Websites Dashboard" as featurename + * + *

All Websites Dashboard (Total: + * 309 Visits)

+ * -> custom featurename + * + *

All Websites Dashboard

+ * -> shows help icon and links to external url + * + *

All Websites + * Dashboard

+ * -> makes the headline clickable linking to the specified url + * + *

Pages report

+ * -> inlineHelp specified via a attribute shows help icon on headline hover + * + *

All Websites Dashboard + *
My inline help
+ *

+ * -> alternative definition for inline help + * -> shows help icon to display inline help on click. Note: You can combine inlinehelp and help-url + * + * *

Pages report

+ * -> reportGenerated specified via this attribute shows a clock icon with a tooltip which + * activated by hover + * -> the tooltip shows the value of the attribute + */ + +/* harmony default export */ var EnrichedHeadlinevue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + helpUrl: { + type: String, + default: '' + }, + editUrl: { + type: String, + default: '' + }, + reportGenerated: String, + featureName: String, + inlineHelp: String + }, + components: { + RateFeature: RateFeature + }, + data: function data() { + return { + showIcons: false, + showInlineHelp: false, + actualFeatureName: this.featureName, + actualInlineHelp: this.inlineHelp + }; + }, + watch: { + inlineHelp: function inlineHelp(newValue) { + this.actualInlineHelp = newValue; + }, + featureName: function featureName(newValue) { + this.actualFeatureName = newValue; + } + }, + mounted: function mounted() { + var _this = this; + + var root = this.$refs.root; // timeout used since angularjs does not fill out the transclude at this point + + setTimeout(function () { + if (!_this.actualInlineHelp) { + var _root$parentElement; + + var helpNode = root.querySelector('.title .inlineHelp'); + + if (!helpNode && (_root$parentElement = root.parentElement) !== null && _root$parentElement !== void 0 && _root$parentElement.nextElementSibling) { + // hack for reports :( + helpNode = root.parentElement.nextElementSibling.querySelector('.reportDocumentation'); + } + + if (helpNode) { + var _helpNode$getAttribut; + + // hackish solution to get binded html of p tag within the help node + // at this point the ng-bind-html is not yet converted into html when report is not + // initially loaded. Using $compile doesn't work. So get and set it manually + var helpDocs = (_helpNode$getAttribut = helpNode.getAttribute('data-content')) === null || _helpNode$getAttribut === void 0 ? void 0 : _helpNode$getAttribut.trim(); + + if (helpDocs && helpDocs.length) { + _this.actualInlineHelp = "

".concat(helpDocs, "

"); + setTimeout(function () { + return helpNode.remove(); + }, 0); + } + } + } + + if (!_this.actualFeatureName) { + var _root$querySelector; + + _this.actualFeatureName = (_root$querySelector = root.querySelector('.title')) === null || _root$querySelector === void 0 ? void 0 : _root$querySelector.textContent; + } + + if (Matomo_Matomo.period && Matomo_Matomo.currentDateString) { + var currentPeriod = Periods_Periods.parse(Matomo_Matomo.period, Matomo_Matomo.currentDateString); + + if (_this.reportGenerated && currentPeriod.containsToday()) { + window.$(root.querySelector('.report-generated')).tooltip({ + track: true, + content: _this.reportGenerated, + items: 'div', + show: false, + hide: false + }); + } + } + }); + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/EnrichedHeadline/EnrichedHeadline.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/EnrichedHeadline/EnrichedHeadline.vue + + + +EnrichedHeadlinevue_type_script_lang_ts.render = EnrichedHeadlinevue_type_template_id_7b85675d_render + +/* harmony default export */ var EnrichedHeadline = (EnrichedHeadlinevue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/EnrichedHeadline/EnrichedHeadline.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var EnrichedHeadline_adapter = (createAngularJsAdapter({ + component: EnrichedHeadline, + scope: { + helpUrl: { + angularJsBind: '@' + }, + editUrl: { + angularJsBind: '@' + }, + reportGenerated: { + angularJsBind: '@?' + }, + featureName: { + angularJsBind: '@' + }, + inlineHelp: { + angularJsBind: '@?' + } + }, + directiveName: 'piwikEnrichedHeadline', + transclude: true +})); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/ContentBlock/ContentBlock.vue?vue&type=template&id=3f4d113e + +var ContentBlockvue_type_template_id_3f4d113e_hoisted_1 = { + class: "card", + ref: "root" +}; +var ContentBlockvue_type_template_id_3f4d113e_hoisted_2 = { + class: "card-content" +}; +var ContentBlockvue_type_template_id_3f4d113e_hoisted_3 = { + key: 0, + class: "card-title" +}; +var ContentBlockvue_type_template_id_3f4d113e_hoisted_4 = { + key: 1, + class: "card-title" +}; +var ContentBlockvue_type_template_id_3f4d113e_hoisted_5 = { + ref: "content" +}; +function ContentBlockvue_type_template_id_3f4d113e_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_EnrichedHeadline = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("EnrichedHeadline"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", ContentBlockvue_type_template_id_3f4d113e_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ContentBlockvue_type_template_id_3f4d113e_hoisted_2, [_ctx.contentTitle && !_ctx.actualFeature && !_ctx.helpUrl && !_ctx.actualHelpText ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("h2", ContentBlockvue_type_template_id_3f4d113e_hoisted_3, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.contentTitle), 1)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.contentTitle && (_ctx.actualFeature || _ctx.helpUrl || _ctx.actualHelpText) ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("h2", ContentBlockvue_type_template_id_3f4d113e_hoisted_4, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_EnrichedHeadline, { + "feature-name": _ctx.actualFeature, + "help-url": _ctx.helpUrl, + "inline-help": _ctx.actualHelpText + }, { + default: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withCtx"])(function () { + return [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.contentTitle), 1)]; + }), + _: 1 + }, 8, ["feature-name", "help-url", "inline-help"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ContentBlockvue_type_template_id_3f4d113e_hoisted_5, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderSlot"])(_ctx.$slots, "default")], 512)])], 512); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ContentBlock/ContentBlock.vue?vue&type=template&id=3f4d113e + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/ContentBlock/ContentBlock.vue?vue&type=script&lang=ts + + +var adminContent = null; +var ContentBlockvue_type_script_lang_ts_window = window, + ContentBlockvue_type_script_lang_ts_$ = ContentBlockvue_type_script_lang_ts_window.$; +/* harmony default export */ var ContentBlockvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + contentTitle: String, + feature: String, + helpUrl: String, + helpText: String, + anchor: String + }, + components: { + EnrichedHeadline: EnrichedHeadline + }, + data: function data() { + return { + actualFeature: this.feature, + actualHelpText: this.helpText + }; + }, + watch: { + feature: function feature(newValue) { + this.actualFeature = newValue; + }, + helpText: function helpText(newValue) { + this.actualHelpText = newValue; + } + }, + mounted: function mounted() { + var _this = this; + + var root = this.$refs.root; + var content = this.$refs.content; + + if (this.anchor && root && root.parentElement) { + var anchorElement = document.createElement('a'); + anchorElement.id = this.anchor; + ContentBlockvue_type_script_lang_ts_$(root.parentElement).prepend(anchorElement); + } + + setTimeout(function () { + var inlineHelp = content.querySelector('.contentHelp'); + + if (inlineHelp) { + _this.actualHelpText = inlineHelp.innerHTML; + inlineHelp.remove(); + } + }, 0); + + if (this.actualFeature && this.actualFeature === 'true') { + this.actualFeature = this.contentTitle; + } + + if (adminContent === null) { + // cache admin node for further content blocks + adminContent = document.querySelector('#content.admin'); + } + + var contentTopPosition = null; + + if (adminContent) { + contentTopPosition = adminContent.offsetTop; + } + + if (contentTopPosition || contentTopPosition === 0) { + var parents = root.closest('[piwik-widget-loader]'); // when shown within the widget loader, we need to get the offset of that element + // as the widget loader might be still shown. Would otherwise not position correctly + // the widgets on the admin home page + + var topThis = parents ? parents.offsetTop : root.offsetTop; + + if (topThis - contentTopPosition < 17) { + // we make sure to display the first card with no margin-top to have it on same as line as + // navigation + root.style.marginTop = '0'; + } + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ContentBlock/ContentBlock.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ContentBlock/ContentBlock.vue + + + +ContentBlockvue_type_script_lang_ts.render = ContentBlockvue_type_template_id_3f4d113e_render + +/* harmony default export */ var ContentBlock = (ContentBlockvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ContentBlock/ContentBlock.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var ContentBlock_adapter = (createAngularJsAdapter({ + component: ContentBlock, + scope: { + contentTitle: { + angularJsBind: '@' + }, + feature: { + angularJsBind: '@' + }, + helpUrl: { + angularJsBind: '@' + }, + helpText: { + angularJsBind: '@' + }, + anchor: { + angularJsBind: '@?' + } + }, + directiveName: 'piwikContentBlock', + transclude: true +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Segmentation/Segments.store.ts +function Segments_store_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function Segments_store_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function Segments_store_createClass(Constructor, protoProps, staticProps) { if (protoProps) Segments_store_defineProperties(Constructor.prototype, protoProps); if (staticProps) Segments_store_defineProperties(Constructor, staticProps); return Constructor; } + +function Segments_store_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + + +var Segments_store_SegmentsStore = /*#__PURE__*/function () { + function SegmentsStore() { + var _this = this; + + Segments_store_classCallCheck(this, SegmentsStore); + + Segments_store_defineProperty(this, "segmentState", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["reactive"])({ + availableSegments: [] + })); + + Matomo_Matomo.on('piwikSegmentationInited', function () { + return _this.setSegmentState(); + }); + } + + Segments_store_createClass(SegmentsStore, [{ + key: "state", + get: function get() { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(this.segmentState); + } + }, { + key: "setSegmentState", + value: function setSegmentState() { + try { + var uiControlObject = $('.segmentEditorPanel').data('uiControlObject'); + this.segmentState.availableSegments = uiControlObject.impl.availableSegments || []; + } catch (e) {// segment editor is not initialized yet + } + } + }]); + + return SegmentsStore; +}(); + +/* harmony default export */ var Segments_store = (new Segments_store_SegmentsStore()); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Comparisons/Comparisons.store.ts +function Comparisons_store_toConsumableArray(arr) { return Comparisons_store_arrayWithoutHoles(arr) || Comparisons_store_iterableToArray(arr) || Comparisons_store_unsupportedIterableToArray(arr) || Comparisons_store_nonIterableSpread(); } + +function Comparisons_store_nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } + +function Comparisons_store_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return Comparisons_store_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return Comparisons_store_arrayLikeToArray(o, minLen); } + +function Comparisons_store_iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } + +function Comparisons_store_arrayWithoutHoles(arr) { if (Array.isArray(arr)) return Comparisons_store_arrayLikeToArray(arr); } + +function Comparisons_store_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } + +function Comparisons_store_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function Comparisons_store_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function Comparisons_store_createClass(Constructor, protoProps, staticProps) { if (protoProps) Comparisons_store_defineProperties(Constructor.prototype, protoProps); if (staticProps) Comparisons_store_defineProperties(Constructor, staticProps); return Constructor; } + +function Comparisons_store_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + + + + + + +var SERIES_COLOR_COUNT = 8; +var SERIES_SHADE_COUNT = 3; + +function wrapArray(values) { + if (!values) { + return []; + } + + return Array.isArray(values) ? values : [values]; +} + +var Comparisons_store_ComparisonsStore = /*#__PURE__*/function () { + // for tests + function ComparisonsStore() { + var _this = this; + + Comparisons_store_classCallCheck(this, ComparisonsStore); + + Comparisons_store_defineProperty(this, "privateState", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["reactive"])({ + comparisonsDisabledFor: [] + })); + + Comparisons_store_defineProperty(this, "state", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(this.privateState)); + + Comparisons_store_defineProperty(this, "colors", {}); + + Comparisons_store_defineProperty(this, "segmentComparisons", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return _this.parseSegmentComparisons(); + })); + + Comparisons_store_defineProperty(this, "periodComparisons", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return _this.parsePeriodComparisons(); + })); + + Comparisons_store_defineProperty(this, "isEnabled", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return _this.checkEnabledForCurrentPage(); + })); + + this.loadComparisonsDisabledFor(); + $(function () { + _this.colors = _this.getAllSeriesColors(); + }); + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["watch"])(function () { + return _this.getComparisons(); + }, function () { + return Matomo_Matomo.postEvent('piwikComparisonsChanged'); + }, { + deep: true + }); + } + + Comparisons_store_createClass(ComparisonsStore, [{ + key: "getComparisons", + value: function getComparisons() { + return this.getSegmentComparisons().concat(this.getPeriodComparisons()); + } + }, { + key: "isComparing", + value: function isComparing() { + return this.isComparisonEnabled() // first two in each array are for the currently selected segment/period + && (this.segmentComparisons.value.length > 1 || this.periodComparisons.value.length > 1); + } + }, { + key: "isComparingPeriods", + value: function isComparingPeriods() { + return this.getPeriodComparisons().length > 1; // first is currently selected period + } + }, { + key: "getSegmentComparisons", + value: function getSegmentComparisons() { + if (!this.isComparisonEnabled()) { + return []; + } + + return this.segmentComparisons.value; + } + }, { + key: "getPeriodComparisons", + value: function getPeriodComparisons() { + if (!this.isComparisonEnabled()) { + return []; + } + + return this.periodComparisons.value; + } + }, { + key: "getSeriesColor", + value: function getSeriesColor(segmentComparison, periodComparison) { + var metricIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; + var seriesIndex = this.getComparisonSeriesIndex(periodComparison.index, segmentComparison.index) % SERIES_COLOR_COUNT; + + if (metricIndex === 0) { + return this.colors["series".concat(seriesIndex)]; + } + + var shadeIndex = metricIndex % SERIES_SHADE_COUNT; + return this.colors["series".concat(seriesIndex, "-shade").concat(shadeIndex)]; + } + }, { + key: "getSeriesColorName", + value: function getSeriesColorName(seriesIndex, metricIndex) { + var colorName = "series".concat(seriesIndex % SERIES_COLOR_COUNT); + + if (metricIndex > 0) { + colorName += "-shade".concat(metricIndex % SERIES_SHADE_COUNT); + } + + return colorName; + } + }, { + key: "isComparisonEnabled", + value: function isComparisonEnabled() { + return this.isEnabled.value; + } + }, { + key: "getIndividualComparisonRowIndices", + value: function getIndividualComparisonRowIndices(seriesIndex) { + var segmentCount = this.getSegmentComparisons().length; + var segmentIndex = seriesIndex % segmentCount; + var periodIndex = Math.floor(seriesIndex / segmentCount); + return { + segmentIndex: segmentIndex, + periodIndex: periodIndex + }; + } + }, { + key: "getComparisonSeriesIndex", + value: function getComparisonSeriesIndex(periodIndex, segmentIndex) { + var segmentCount = this.getSegmentComparisons().length; + return periodIndex * segmentCount + segmentIndex; + } + }, { + key: "getAllComparisonSeries", + value: function getAllComparisonSeries() { + var _this2 = this; + + var seriesInfo = []; + var seriesIndex = 0; + this.getPeriodComparisons().forEach(function (periodComp) { + _this2.getSegmentComparisons().forEach(function (segmentComp) { + seriesInfo.push({ + index: seriesIndex, + params: Object.assign(Object.assign({}, segmentComp.params), periodComp.params), + color: _this2.colors["series".concat(seriesIndex)] + }); + seriesIndex += 1; + }); + }); + return seriesInfo; + } + }, { + key: "removeSegmentComparison", + value: function removeSegmentComparison(index) { + if (!this.isComparisonEnabled()) { + throw new Error('Comparison disabled.'); + } + + var newComparisons = Comparisons_store_toConsumableArray(this.segmentComparisons.value); + + newComparisons.splice(index, 1); + var extraParams = {}; + + if (index === 0) { + extraParams.segment = newComparisons[0].params.segment; + } + + this.updateQueryParamsFromComparisons(newComparisons, this.periodComparisons.value, extraParams); + } + }, { + key: "addSegmentComparison", + value: function addSegmentComparison(params) { + if (!this.isComparisonEnabled()) { + throw new Error('Comparison disabled.'); + } + + var newComparisons = this.segmentComparisons.value.concat([{ + params: params, + index: -1, + title: '' + }]); + this.updateQueryParamsFromComparisons(newComparisons, this.periodComparisons.value); + } + }, { + key: "updateQueryParamsFromComparisons", + value: function updateQueryParamsFromComparisons(segmentComparisons, periodComparisons) { + var extraParams = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + // get unique segments/periods/dates from new Comparisons + var compareSegments = {}; + var comparePeriodDatePairs = {}; + var firstSegment = false; + var firstPeriod = false; + segmentComparisons.forEach(function (comparison) { + if (firstSegment) { + compareSegments[comparison.params.segment] = true; + } else { + firstSegment = true; + } + }); + periodComparisons.forEach(function (comparison) { + if (firstPeriod) { + comparePeriodDatePairs["".concat(comparison.params.period, "|").concat(comparison.params.date)] = true; + } else { + firstPeriod = true; + } + }); + var comparePeriods = []; + var compareDates = []; + Object.keys(comparePeriodDatePairs).forEach(function (pair) { + var parts = pair.split('|'); + comparePeriods.push(parts[0]); + compareDates.push(parts[1]); + }); + var compareParams = { + compareSegments: Object.keys(compareSegments), + comparePeriods: comparePeriods, + compareDates: compareDates + }; // change the page w/ these new param values + + var baseParams = Matomo_Matomo.helper.isAngularRenderingThePage() ? src_MatomoUrl_MatomoUrl.hashParsed.value : src_MatomoUrl_MatomoUrl.urlParsed.value; + src_MatomoUrl_MatomoUrl.updateLocation(Object.assign(Object.assign(Object.assign({}, baseParams), compareParams), extraParams)); + } + }, { + key: "getAllSeriesColors", + value: function getAllSeriesColors() { + var ColorManager = Matomo_Matomo.ColorManager; + + if (!ColorManager) { + return []; + } + + var seriesColorNames = []; + + for (var i = 0; i < SERIES_COLOR_COUNT; i += 1) { + seriesColorNames.push("series".concat(i)); + + for (var j = 0; j < SERIES_SHADE_COUNT; j += 1) { + seriesColorNames.push("series".concat(i, "-shade").concat(j)); + } + } + + return ColorManager.getColors('comparison-series-color', seriesColorNames); + } + }, { + key: "loadComparisonsDisabledFor", + value: function loadComparisonsDisabledFor() { + var _this3 = this; + + var matomoModule = src_MatomoUrl_MatomoUrl.parsed.value.module; // check if body id #installation exist + + if (window.piwik.installation) { + this.privateState.comparisonsDisabledFor = []; + return; + } + + if (matomoModule === 'CoreUpdater' || matomoModule === 'Installation' || matomoModule === 'Overlay') { + this.privateState.comparisonsDisabledFor = []; + return; + } + + AjaxHelper_AjaxHelper.fetch({ + module: 'API', + method: 'API.getPagesComparisonsDisabledFor' + }).then(function (result) { + _this3.privateState.comparisonsDisabledFor = result; + }); + } + }, { + key: "parseSegmentComparisons", + value: function parseSegmentComparisons() { + var availableSegments = Segments_store.state.availableSegments; + + var compareSegments = Comparisons_store_toConsumableArray(wrapArray(src_MatomoUrl_MatomoUrl.parsed.value.compareSegments)); // add base comparisons + + + compareSegments.unshift(src_MatomoUrl_MatomoUrl.parsed.value.segment || ''); + var newSegmentComparisons = []; + compareSegments.forEach(function (segment, idx) { + var storedSegment; + availableSegments.forEach(function (s) { + if (s.definition === segment || s.definition === decodeURIComponent(segment) || decodeURIComponent(s.definition) === segment) { + storedSegment = s; + } + }); + var segmentTitle = storedSegment ? storedSegment.name : translate('General_Unknown'); + + if (segment.trim() === '') { + segmentTitle = translate('SegmentEditor_DefaultAllVisits'); + } + + newSegmentComparisons.push({ + params: { + segment: segment + }, + title: Matomo_Matomo.helper.htmlDecode(segmentTitle), + index: idx + }); + }); + return newSegmentComparisons; + } + }, { + key: "parsePeriodComparisons", + value: function parsePeriodComparisons() { + var comparePeriods = Comparisons_store_toConsumableArray(wrapArray(src_MatomoUrl_MatomoUrl.parsed.value.comparePeriods)); + + var compareDates = Comparisons_store_toConsumableArray(wrapArray(src_MatomoUrl_MatomoUrl.parsed.value.compareDates)); + + comparePeriods.unshift(src_MatomoUrl_MatomoUrl.parsed.value.period); + compareDates.unshift(src_MatomoUrl_MatomoUrl.parsed.value.date); + var newPeriodComparisons = []; + + for (var i = 0; i < Math.min(compareDates.length, comparePeriods.length); i += 1) { + var title = void 0; + + try { + title = Periods_Periods.parse(comparePeriods[i], compareDates[i]).getPrettyString(); + } catch (e) { + title = translate('General_Error'); + } + + newPeriodComparisons.push({ + params: { + date: compareDates[i], + period: comparePeriods[i] + }, + title: title, + index: i + }); + } + + return newPeriodComparisons; + } + }, { + key: "checkEnabledForCurrentPage", + value: function checkEnabledForCurrentPage() { + // category/subcategory is not included on top bar pages, so in that case we use module/action + var category = src_MatomoUrl_MatomoUrl.parsed.value.category || src_MatomoUrl_MatomoUrl.parsed.value.module; + var subcategory = src_MatomoUrl_MatomoUrl.parsed.value.subcategory || src_MatomoUrl_MatomoUrl.parsed.value.action; + var id = "".concat(category, ".").concat(subcategory); + var isEnabled = this.privateState.comparisonsDisabledFor.indexOf(id) === -1 && this.privateState.comparisonsDisabledFor.indexOf("".concat(category, ".*")) === -1; + document.documentElement.classList.toggle('comparisonsDisabled', !isEnabled); + return isEnabled; + } + }]); + + return ComparisonsStore; +}(); + + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Comparisons/Comparisons.store.instance.ts + +/* harmony default export */ var Comparisons_store_instance = (new Comparisons_store_ComparisonsStore()); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Comparisons/Comparisons.vue?vue&type=template&id=d1a096d8 + +var Comparisonsvue_type_template_id_d1a096d8_hoisted_1 = { + key: 0, + ref: "root", + class: "matomo-comparisons" +}; +var Comparisonsvue_type_template_id_d1a096d8_hoisted_2 = { + class: "comparison-type" +}; +var Comparisonsvue_type_template_id_d1a096d8_hoisted_3 = ["title"]; +var Comparisonsvue_type_template_id_d1a096d8_hoisted_4 = ["href"]; +var Comparisonsvue_type_template_id_d1a096d8_hoisted_5 = ["title"]; +var Comparisonsvue_type_template_id_d1a096d8_hoisted_6 = { + class: "comparison-period-label" +}; +var Comparisonsvue_type_template_id_d1a096d8_hoisted_7 = ["onClick"]; +var Comparisonsvue_type_template_id_d1a096d8_hoisted_8 = ["title"]; +var Comparisonsvue_type_template_id_d1a096d8_hoisted_9 = { + class: "loadingPiwik", + style: { + "display": "none" + } +}; +var Comparisonsvue_type_template_id_d1a096d8_hoisted_10 = ["alt"]; +function Comparisonsvue_type_template_id_d1a096d8_render(_ctx, _cache, $props, $setup, $data, $options) { + var _directive_tooltips = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("tooltips"); + + return _ctx.isComparing ? Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])((Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", Comparisonsvue_type_template_id_d1a096d8_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h3", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_Comparisons')), 1), (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.segmentComparisons, function (comparison, $index) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + class: "comparison card", + key: comparison.index + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", Comparisonsvue_type_template_id_d1a096d8_hoisted_2, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_Segment')), 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", { + class: "title", + title: comparison.title + '
' + decodeURIComponent(comparison.params.segment) + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + target: "_blank", + href: _ctx.getUrlToSegment(comparison.params.segment) + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(comparison.title), 9, Comparisonsvue_type_template_id_d1a096d8_hoisted_4)], 8, Comparisonsvue_type_template_id_d1a096d8_hoisted_3), (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.periodComparisons, function (periodComparison) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + class: "comparison-period", + key: periodComparison.index, + title: _ctx.getComparisonTooltip(comparison, periodComparison) + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + class: "comparison-dot", + style: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeStyle"])({ + 'background-color': _ctx.getSeriesColor(comparison, periodComparison) + }) + }, null, 4), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", Comparisonsvue_type_template_id_d1a096d8_hoisted_6, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(periodComparison.title) + " (" + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.getComparisonPeriodType(periodComparison)) + ") ", 1)], 8, Comparisonsvue_type_template_id_d1a096d8_hoisted_5); + }), 128)), _ctx.segmentComparisons.length > 1 ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { + key: 0, + class: "remove-button", + onClick: function onClick($event) { + return _ctx.removeSegmentComparison($index); + } + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + class: "icon icon-close", + title: _ctx.translate('General_ClickToRemoveComp') + }, null, 8, Comparisonsvue_type_template_id_d1a096d8_hoisted_8)], 8, Comparisonsvue_type_template_id_d1a096d8_hoisted_7)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)]); + }), 128)), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", Comparisonsvue_type_template_id_d1a096d8_hoisted_9, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("img", { + src: "plugins/Morpheus/images/loading-blue.gif", + alt: _ctx.translate('General_LoadingData') + }, null, 8, Comparisonsvue_type_template_id_d1a096d8_hoisted_10), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(" " + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_LoadingData')), 1)])], 512)), [[_directive_tooltips, { + duration: 200, + delay: 200, + content: _ctx.transformTooltipContent + }]]) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Comparisons/Comparisons.vue?vue&type=template&id=d1a096d8 + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Tooltips/Tooltips.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +var Tooltips_window = window, + Tooltips_$ = Tooltips_window.$; + +function defaultContentTransform() { + var title = Tooltips_$(this).attr('title') || ''; + return window.vueSanitize(title.replace(/\n/g, '
')); +} + +function setupTooltips(el, binding) { + var _binding$value, _binding$value2, _binding$value3; + + Tooltips_$(el).tooltip({ + track: true, + content: ((_binding$value = binding.value) === null || _binding$value === void 0 ? void 0 : _binding$value.content) || defaultContentTransform, + show: { + delay: ((_binding$value2 = binding.value) === null || _binding$value2 === void 0 ? void 0 : _binding$value2.delay) || 700, + duration: ((_binding$value3 = binding.value) === null || _binding$value3 === void 0 ? void 0 : _binding$value3.duration) || 200 + }, + hide: false + }); +} + +/* harmony default export */ var Tooltips = ({ + mounted: function mounted(el, binding) { + setTimeout(function () { + return setupTooltips(el, binding); + }); + }, + updated: function updated(el, binding) { + setTimeout(function () { + return setupTooltips(el, binding); + }); + }, + beforeUnmount: function beforeUnmount(el) { + try { + window.$(el).tooltip('destroy'); + } catch (e) {// ignore + } + } +}); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Comparisons/Comparisons.vue?vue&type=script&lang=ts + + + + + + + +/* harmony default export */ var Comparisonsvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: {}, + directives: { + Tooltips: Tooltips + }, + data: function data() { + return { + comparisonTooltips: null + }; + }, + setup: function setup() { + // accessing has to be done through a computed property so we can use the computed + // instance directly in the template. unfortunately, vue won't register to changes. + var isComparing = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return Comparisons_store_instance.isComparing(); + }); + var segmentComparisons = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return Comparisons_store_instance.getSegmentComparisons(); + }); + var periodComparisons = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return Comparisons_store_instance.getPeriodComparisons(); + }); + var getSeriesColor = Comparisons_store_instance.getSeriesColor.bind(Comparisons_store_instance); + + function transformTooltipContent() { + var title = window.$(this).attr('title'); + + if (!title) { + return title; + } + + return window.vueSanitize(title.replace(/\n/g, '
')); + } + + return { + isComparing: isComparing, + segmentComparisons: segmentComparisons, + periodComparisons: periodComparisons, + getSeriesColor: getSeriesColor, + transformTooltipContent: transformTooltipContent + }; + }, + methods: { + comparisonHasSegment: function comparisonHasSegment(comparison) { + return typeof comparison.params.segment !== 'undefined'; + }, + removeSegmentComparison: function removeSegmentComparison(index) { + // otherwise the tooltip will be stuck on the screen + window.$(this.$refs.root).tooltip('destroy'); + Comparisons_store_instance.removeSegmentComparison(index); + }, + getComparisonPeriodType: function getComparisonPeriodType(comparison) { + var period = comparison.params.period; + + if (period === 'range') { + return translate('CoreHome_PeriodRange'); + } + + var periodStr = translate("Intl_Period".concat(period.substring(0, 1).toUpperCase()).concat(period.substring(1))); + return periodStr.substring(0, 1).toUpperCase() + periodStr.substring(1); + }, + getComparisonTooltip: function getComparisonTooltip(segmentComparison, periodComparison) { + if (!this.comparisonTooltips || !Object.keys(this.comparisonTooltips).length) { + return undefined; + } + + return (this.comparisonTooltips[periodComparison.index] || {})[segmentComparison.index]; + }, + getUrlToSegment: function getUrlToSegment(segment) { + var hash = Object.assign({}, src_MatomoUrl_MatomoUrl.hashParsed.value); + delete hash.comparePeriods; + delete hash.compareDates; + delete hash.compareSegments; + hash.segment = segment; + return "".concat(window.location.search, "#?").concat(src_MatomoUrl_MatomoUrl.stringify(hash)); + }, + onComparisonsChanged: function onComparisonsChanged() { + var _this = this; + + this.comparisonTooltips = null; + + if (!Comparisons_store_instance.isComparing()) { + return; + } + + var periodComparisons = Comparisons_store_instance.getPeriodComparisons(); + var segmentComparisons = Comparisons_store_instance.getSegmentComparisons(); + AjaxHelper_AjaxHelper.fetch({ + method: 'API.getProcessedReport', + apiModule: 'VisitsSummary', + apiAction: 'get', + compare: '1', + compareSegments: src_MatomoUrl_MatomoUrl.getSearchParam('compareSegments'), + comparePeriods: src_MatomoUrl_MatomoUrl.getSearchParam('comparePeriods'), + compareDates: src_MatomoUrl_MatomoUrl.getSearchParam('compareDates'), + format_metrics: '1' + }).then(function (report) { + _this.comparisonTooltips = {}; + periodComparisons.forEach(function (periodComp) { + _this.comparisonTooltips[periodComp.index] = {}; + segmentComparisons.forEach(function (segmentComp) { + var tooltip = _this.generateComparisonTooltip(report, periodComp, segmentComp); + + _this.comparisonTooltips[periodComp.index][segmentComp.index] = tooltip; + }); + }); + }); + }, + generateComparisonTooltip: function generateComparisonTooltip(visitsSummary, periodComp, segmentComp) { + if (!visitsSummary.reportData.comparisons) { + // sanity check + return ''; + } + + var firstRowIndex = Comparisons_store_instance.getComparisonSeriesIndex(periodComp.index, 0); + var firstRow = visitsSummary.reportData.comparisons[firstRowIndex]; + var comparisonRowIndex = Comparisons_store_instance.getComparisonSeriesIndex(periodComp.index, segmentComp.index); + var comparisonRow = visitsSummary.reportData.comparisons[comparisonRowIndex]; + var firstPeriodRow = visitsSummary.reportData.comparisons[segmentComp.index]; + var tooltip = '
'; + var visitsPercent = (comparisonRow.nb_visits / firstRow.nb_visits * 100).toFixed(2); + visitsPercent = "".concat(visitsPercent, "%"); + tooltip += translate('General_ComparisonCardTooltip1', ["'".concat(comparisonRow.compareSegmentPretty, "'"), comparisonRow.comparePeriodPretty, visitsPercent, comparisonRow.nb_visits.toString(), firstRow.nb_visits.toString()]); + + if (periodComp.index > 0) { + tooltip += '

'; + tooltip += translate('General_ComparisonCardTooltip2', [comparisonRow.nb_visits_change.toString(), firstPeriodRow.compareSegmentPretty, firstPeriodRow.comparePeriodPretty]); + } + + tooltip += '
'; + return tooltip; + } + }, + mounted: function mounted() { + var _this2 = this; + + Matomo_Matomo.on('piwikComparisonsChanged', function () { + _this2.onComparisonsChanged(); + }); + this.onComparisonsChanged(); + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Comparisons/Comparisons.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Comparisons/Comparisons.vue + + + +Comparisonsvue_type_script_lang_ts.render = Comparisonsvue_type_template_id_d1a096d8_render + +/* harmony default export */ var Comparisons = (Comparisonsvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Comparisons/Comparisons.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + + + +function ComparisonFactory() { + return Comparisons_store_instance; +} + +window.angular.module('piwikApp.service').factory('piwikComparisonsService', ComparisonFactory); +/* harmony default export */ var Comparisons_adapter = (createAngularJsAdapter({ + component: Comparisons, + directiveName: 'piwikComparisons', + restrict: 'E' +})); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/MenuItemsDropdown/MenuItemsDropdown.vue?vue&type=template&id=ee5932cc + +var MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_1 = { + ref: "root", + class: "menuDropdown" +}; +var MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_2 = ["title"]; +var MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_3 = ["innerHTML"]; + +var MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_4 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + class: "icon-arrow-bottom" +}, null, -1); + +var MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_5 = { + class: "items" +}; +var MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_6 = { + key: 0, + class: "search" +}; +var MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_7 = ["placeholder"]; +var MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_8 = ["title"]; +var MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_9 = ["title"]; +function MenuItemsDropdownvue_type_template_id_ee5932cc_render(_ctx, _cache, $props, $setup, $data, $options) { + var _directive_focus_if = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("focus-if"); + + var _directive_focus_anywhere_but_here = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("focus-anywhere-but-here"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])((Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + class: "title", + onClick: _cache[0] || (_cache[0] = function ($event) { + return _ctx.showItems = !_ctx.showItems; + }), + title: _ctx.tooltip + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + innerHTML: _ctx.$sanitize(this.actualMenuTitle) + }, null, 8, MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_3), MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_4], 8, MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_2), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_5, [_ctx.showSearch && _ctx.showItems ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_6, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { + type: "text", + "onUpdate:modelValue": _cache[1] || (_cache[1] = function ($event) { + return _ctx.searchTerm = $event; + }), + onKeydown: _cache[2] || (_cache[2] = function ($event) { + return _ctx.onSearchTermKeydown($event); + }), + placeholder: _ctx.translate('General_Search') + }, null, 40, MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_7), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vModelText"], _ctx.searchTerm], [_directive_focus_if, {}, _ctx.showItems]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("img", { + class: "search_ico", + src: "plugins/Morpheus/images/search_ico.png", + title: _ctx.translate('General_Search') + }, null, 8, MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_8), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], !_ctx.searchTerm]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("img", { + onClick: _cache[3] || (_cache[3] = function ($event) { + _ctx.searchTerm = ''; + + _ctx.searchItems(''); + }), + class: "reset", + src: "plugins/CoreHome/images/reset_search.png", + title: _ctx.translate('General_Clear') + }, null, 8, MenuItemsDropdownvue_type_template_id_ee5932cc_hoisted_9), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.searchTerm]])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", { + onClick: _cache[4] || (_cache[4] = function ($event) { + return _ctx.selectItem($event); + }) + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderSlot"])(_ctx.$slots, "default")])], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.showItems]])], 512)), [[_directive_focus_anywhere_but_here, { + blur: _ctx.lostFocus + }]]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MenuItemsDropdown/MenuItemsDropdown.vue?vue&type=template&id=ee5932cc + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/MenuItemsDropdown/MenuItemsDropdown.vue?vue&type=script&lang=ts + + + +var MenuItemsDropdownvue_type_script_lang_ts_window = window, + MenuItemsDropdownvue_type_script_lang_ts_$ = MenuItemsDropdownvue_type_script_lang_ts_window.$; +/* harmony default export */ var MenuItemsDropdownvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + menuTitle: String, + tooltip: String, + showSearch: Boolean, + menuTitleChangeOnClick: Boolean + }, + directives: { + FocusAnywhereButHere: FocusAnywhereButHere, + FocusIf: FocusIf + }, + emits: ['afterSelect'], + watch: { + menuTitle: function menuTitle() { + this.actualMenuTitle = this.menuTitle; + } + }, + data: function data() { + return { + showItems: false, + searchTerm: '', + actualMenuTitle: this.menuTitle + }; + }, + methods: { + lostFocus: function lostFocus() { + this.showItems = false; + }, + selectItem: function selectItem(event) { + var targetClasses = event.target.classList; + + if (!targetClasses.contains('item') || targetClasses.contains('disabled') || targetClasses.contains('separator')) { + return; + } + + if (this.menuTitleChangeOnClick) { + this.actualMenuTitle = (event.target.textContent || '').replace(/[\u0000-\u2666]/g, function (c) { + return "&#".concat(c.charCodeAt(0), ";"); + }); // eslint-disable-line + } + + this.showItems = false; + MenuItemsDropdownvue_type_script_lang_ts_$(this.$slots.default()[0].el).find('.item').removeClass('active'); + targetClasses.add('active'); + this.$emit('afterSelect'); + }, + onSearchTermKeydown: function onSearchTermKeydown() { + var _this = this; + + setTimeout(function () { + _this.searchItems(_this.searchTerm); + }); + }, + searchItems: function searchItems(unprocessedSearchTerm) { + var searchTerm = unprocessedSearchTerm.toLowerCase(); + MenuItemsDropdownvue_type_script_lang_ts_$(this.$refs.root).find('.item').each(function (index, node) { + var $node = MenuItemsDropdownvue_type_script_lang_ts_$(node); + + if ($node.text().toLowerCase().indexOf(searchTerm) === -1) { + $node.hide(); + } else { + $node.show(); + } + }); + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MenuItemsDropdown/MenuItemsDropdown.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MenuItemsDropdown/MenuItemsDropdown.vue + + + +MenuItemsDropdownvue_type_script_lang_ts.render = MenuItemsDropdownvue_type_template_id_ee5932cc_render + +/* harmony default export */ var MenuItemsDropdown = (MenuItemsDropdownvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MenuItemsDropdown/MenuItemsDropdown.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var MenuItemsDropdown_adapter = (createAngularJsAdapter({ + component: MenuItemsDropdown, + scope: { + menuTitle: { + angularJsBind: '@' + }, + tooltip: { + angularJsBind: '@' + }, + showSearch: { + angularJsBind: '=' + }, + menuTitleChangeOnClick: { + angularJsBind: '=' + } + }, + directiveName: 'piwikMenudropdown', + transclude: true, + events: { + 'after-select': function afterSelect($event, vm, scope) { + setTimeout(function () { + scope.$apply(); + }, 0); + } + } +})); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/DatePicker/DatePicker.vue?vue&type=template&id=589729fc + +var DatePickervue_type_template_id_589729fc_hoisted_1 = { + ref: "root" +}; +function DatePickervue_type_template_id_589729fc_render(_ctx, _cache, $props, $setup, $data, $options) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", DatePickervue_type_template_id_589729fc_hoisted_1, null, 512); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DatePicker/DatePicker.vue?vue&type=template&id=589729fc + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/DatePicker/DatePicker.vue?vue&type=script&lang=ts + + + +var DEFAULT_STEP_MONTHS = 1; +var DatePickervue_type_script_lang_ts_window = window, + DatePickervue_type_script_lang_ts_$ = DatePickervue_type_script_lang_ts_window.$; +/* harmony default export */ var DatePickervue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + selectedDateStart: Date, + selectedDateEnd: Date, + highlightedDateStart: Date, + highlightedDateEnd: Date, + viewDate: [String, Date], + stepMonths: Number, + disableMonthDropdown: Boolean, + options: Object + }, + emits: ['cellHover', 'cellHoverLeave', 'dateSelect'], + setup: function setup(props, context) { + var root = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])(null); + + function setDateCellColor($dateCell, dateValue) { + var $dateCellLink = $dateCell.children('a'); + + if (props.selectedDateStart && props.selectedDateEnd && dateValue >= props.selectedDateStart && dateValue <= props.selectedDateEnd) { + $dateCell.addClass('ui-datepicker-current-period'); + } else { + $dateCell.removeClass('ui-datepicker-current-period'); + } + + if (props.highlightedDateStart && props.highlightedDateEnd && dateValue >= props.highlightedDateStart && dateValue <= props.highlightedDateEnd) { + // other-month cells don't have links, so the must have the ui-state-hover class + var elementToAddClassTo = $dateCellLink.length ? $dateCellLink : $dateCell; + elementToAddClassTo.addClass('ui-state-hover'); + } else { + $dateCell.removeClass('ui-state-hover'); + $dateCellLink.removeClass('ui-state-hover'); + } + } + + function getCellDate($dateCell, month, year) { + if ($dateCell.hasClass('ui-datepicker-other-month')) { + return getOtherMonthDate($dateCell, month, year); // eslint-disable-line + } + + var day = parseInt($dateCell.children('a,span').text(), 10); + return new Date(year, month, day); + } + + function getOtherMonthDate($dateCell, month, year) { + var date; + var $row = $dateCell.parent(); + var $rowCells = $row.children('td'); // if in the first row, the date cell is before the current month + + if ($row.is(':first-child')) { + var $firstDateInMonth = $row.children('td:not(.ui-datepicker-other-month)').first(); + date = getCellDate($firstDateInMonth, month, year); + date.setDate($rowCells.index($dateCell) - $rowCells.index($firstDateInMonth) + 1); + return date; + } // the date cell is after the current month + + + var $lastDateInMonth = $row.children('td:not(.ui-datepicker-other-month)').last(); + date = getCellDate($lastDateInMonth, month, year); + date.setDate(date.getDate() + $rowCells.index($dateCell) - $rowCells.index($lastDateInMonth)); + return date; + } + + function getMonthYearDisplayed() { + var element = DatePickervue_type_script_lang_ts_$(root.value); + var $firstCellWithMonth = element.find('td[data-month]'); + var month = parseInt($firstCellWithMonth.attr('data-month'), 10); + var year = parseInt($firstCellWithMonth.attr('data-year'), 10); + return [month, year]; + } + + function setDatePickerCellColors() { + var element = DatePickervue_type_script_lang_ts_$(root.value); + var $calendarTable = element.find('.ui-datepicker-calendar'); + var monthYear = getMonthYearDisplayed(); // highlight the rest of the cells by first getting the date for the first cell + // in the calendar, then just incrementing by one for the rest of the cells. + + var $cells = $calendarTable.find('td'); + var $firstDateCell = $cells.first(); + var currentDate = getCellDate($firstDateCell, monthYear[0], monthYear[1]); + $cells.each(function setCellColor() { + setDateCellColor(DatePickervue_type_script_lang_ts_$(this), currentDate); + currentDate.setDate(currentDate.getDate() + 1); + }); + } + + function viewDateChanged() { + if (!props.viewDate) { + return false; + } + + var date; + + if (typeof props.viewDate === 'string') { + try { + date = parseDate(props.viewDate); + } catch (e) { + return false; + } + } else { + date = props.viewDate; + } + + var element = DatePickervue_type_script_lang_ts_$(root.value); // only change the datepicker date if the date is outside of the current month/year. + // this avoids a re-render in other cases. + + var monthYear = getMonthYearDisplayed(); + + if (monthYear[0] !== date.getMonth() || monthYear[1] !== date.getFullYear()) { + element.datepicker('setDate', date); + return true; + } + + return false; + } // remove the ui-state-active class & click handlers for every cell. we bypass + // the datepicker's date selection logic for smoother browser rendering. + + + function onJqueryUiRenderedPicker() { + var element = DatePickervue_type_script_lang_ts_$(root.value); + element.find('td[data-event]').off('click'); + element.find('.ui-state-active').removeClass('ui-state-active'); + element.find('.ui-datepicker-current-day').removeClass('ui-datepicker-current-day'); // add href to left/right nav in calendar so they can be accessed via keyboard + + element.find('.ui-datepicker-prev,.ui-datepicker-next').attr('href', ''); + } + + function stepMonthsChanged() { + var element = DatePickervue_type_script_lang_ts_$(root.value); + var stepMonths = props.stepMonths || DEFAULT_STEP_MONTHS; + + if (element.datepicker('option', 'stepMonths') === stepMonths) { + return false; + } // setting stepMonths will change the month in view back to the selected date. to avoid + // we set the selected date to the month in view. + + + var currentMonth = DatePickervue_type_script_lang_ts_$('.ui-datepicker-month', element).val(); + var currentYear = DatePickervue_type_script_lang_ts_$('.ui-datepicker-year', element).val(); + element.datepicker('option', 'stepMonths', stepMonths).datepicker('setDate', new Date(currentYear, currentMonth)); + onJqueryUiRenderedPicker(); + return true; + } + + function enableDisableMonthDropdown() { + var element = DatePickervue_type_script_lang_ts_$(root.value); + var monthPicker = element.find('.ui-datepicker-month')[0]; + + if (monthPicker) { + monthPicker.disabled = props.disableMonthDropdown; + } + } + + function handleOtherMonthClick() { + if (!DatePickervue_type_script_lang_ts_$(this).hasClass('ui-state-hover')) { + return; + } + + var $row = DatePickervue_type_script_lang_ts_$(this).parent(); + var $tbody = $row.parent(); + + if ($row.is(':first-child')) { + // click on first of the month + $tbody.find('a').first().click(); + } else { + // click on last of month + $tbody.find('a').last().click(); + } + } + + function onCalendarViewChange() { + // clicking left/right re-enables the month dropdown, so we disable it again + enableDisableMonthDropdown(); + setDatePickerCellColors(); + } // on a prop change (NOTE: we can't watch just `props`, since then newProps and oldProps will + // have the same values (since it is a proxy object). Using a copy doesn't quite work, the + // object it returns will always be different, BUT, since we check what changes it works + // for our purposes. The only downside is that it runs on every tick basically, but since + // that is within the context of the date picker component, it's bearable. + + + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["watch"])(function () { + return Object.assign({}, props); + }, function (newProps, oldProps) { + var redraw = false; + [function (x) { + return x.selectedDateStart; + }, function (x) { + return x.selectedDateEnd; + }, function (x) { + return x.highlightedDateStart; + }, function (x) { + return x.highlightedDateEnd; + }].forEach(function (selector) { + if (redraw) { + return; + } + + var newProp = selector(newProps); + var oldProp = selector(oldProps); + + if (!newProp && oldProp) { + redraw = true; + } + + if (newProp && !oldProp) { + redraw = true; + } + + if (newProp && oldProp && newProp.getTime() !== oldProp.getTime()) { + redraw = true; + } + }); + + if (newProps.viewDate !== oldProps.viewDate && viewDateChanged()) { + redraw = true; + } + + if (newProps.stepMonths !== oldProps.stepMonths) { + stepMonthsChanged(); + } + + if (newProps.disableMonthDropdown !== oldProps.disableMonthDropdown) { + enableDisableMonthDropdown(); + } // redraw when selected/highlighted dates change + + + if (redraw) { + setDatePickerCellColors(); + } + }); + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["onMounted"])(function () { + var element = DatePickervue_type_script_lang_ts_$(root.value); + var customOptions = props.options || {}; + var datePickerOptions = Object.assign(Object.assign(Object.assign({}, Matomo_Matomo.getBaseDatePickerOptions()), customOptions), {}, { + onChangeMonthYear: function onChangeMonthYear() { + // datepicker renders the HTML after this hook is called, so we use setTimeout + // to run some code after the render. + setTimeout(function () { + onJqueryUiRenderedPicker(); + }); + } + }); + element.datepicker(datePickerOptions); + element.on('mouseover', 'tbody td a', function (event) { + // this event is triggered when a user clicks a date as well. in that case, + // the originalEvent is null. we don't need to redraw again for that, so + // we ignore events like that. + if (event.originalEvent) { + setDatePickerCellColors(); + } + }); // on hover cell, execute scope.cellHover() + + element.on('mouseenter', 'tbody td', function onMouseEnter() { + var monthYear = getMonthYearDisplayed(); + var $dateCell = DatePickervue_type_script_lang_ts_$(this); + var dateValue = getCellDate($dateCell, monthYear[0], monthYear[1]); + context.emit('cellHover', { + date: dateValue, + $cell: $dateCell + }); + }); // overrides jquery UI handler that unhighlights a cell when the mouse leaves it + + element.on('mouseout', 'tbody td a', function () { + setDatePickerCellColors(); + }); // call scope.cellHoverLeave() when mouse leaves table body (can't do event on tbody, for + // some reason that fails, so we do two events, one on the table & one on thead) + + element.on('mouseleave', 'table', function () { + return context.emit('cellHoverLeave'); + }).on('mouseenter', 'thead', function () { + return context.emit('cellHoverLeave'); + }); // make sure whitespace is clickable when the period makes it appropriate + + element.on('click', 'tbody td.ui-datepicker-other-month', handleOtherMonthClick); // NOTE: using a selector w/ .on() doesn't seem to work for some reason... + + element.on('click', function (e) { + e.preventDefault(); + var $target = DatePickervue_type_script_lang_ts_$(e.target).closest('a'); + + if (!$target.is('.ui-datepicker-next') && !$target.is('.ui-datepicker-prev')) { + return; + } + + onCalendarViewChange(); + }); // when a cell is clicked, invoke the onDateSelected function. this, in conjunction + // with onJqueryUiRenderedPicker(), overrides the date picker's click behavior. + + element.on('click', 'td[data-month]', function (event) { + var $cell = DatePickervue_type_script_lang_ts_$(event.target).closest('td'); + var month = parseInt($cell.attr('data-month'), 10); + var year = parseInt($cell.attr('data-year'), 10); + var day = parseInt($cell.children('a,span').text(), 10); + context.emit('dateSelect', { + date: new Date(year, month, day) + }); + }); + var renderPostProcessed = stepMonthsChanged(); + viewDateChanged(); + enableDisableMonthDropdown(); + + if (!renderPostProcessed) { + onJqueryUiRenderedPicker(); + } + + setDatePickerCellColors(); + }); + return { + root: root + }; + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DatePicker/DatePicker.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DatePicker/DatePicker.vue + + + +DatePickervue_type_script_lang_ts.render = DatePickervue_type_template_id_589729fc_render + +/* harmony default export */ var DatePicker = (DatePickervue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DatePicker/DatePicker.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var DatePicker_adapter = (createAngularJsAdapter({ + component: DatePicker, + scope: { + selectedDateStart: { + angularJsBind: '<' + }, + selectedDateEnd: { + angularJsBind: '<' + }, + highlightedDateStart: { + angularJsBind: '<' + }, + highlightedDateEnd: { + angularJsBind: '<' + }, + viewDate: { + angularJsBind: '<' + }, + stepMonths: { + angularJsBind: '<' + }, + disableMonthDropdown: { + angularJsBind: '<' + }, + options: { + angularJsBind: '<' + }, + cellHover: { + angularJsBind: '&' + }, + cellHoverLeave: { + angularJsBind: '&' + }, + dateSelect: { + angularJsBind: '&' + } + }, + directiveName: 'piwikDatePicker', + events: { + 'cell-hover': function cellHover(event, vm, scope, element, attrs, controller, $timeout) { + $timeout(); // trigger new digest + }, + 'cell-hover-leave': function cellHoverLeave(event, vm, scope, element, attrs, controller, $timeout) { + $timeout(); // trigger new digest + }, + 'date-select': function dateSelect(event, vm, scope, element, attrs, controller, $timeout) { + $timeout(); // trigger new digest + } + }, + $inject: ['$timeout'] +})); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.vue?vue&type=template&id=7bf842c2 + +var DateRangePickervue_type_template_id_7bf842c2_hoisted_1 = { + id: "calendarRangeFrom" +}; +var DateRangePickervue_type_template_id_7bf842c2_hoisted_2 = { + id: "calendarRangeTo" +}; +function DateRangePickervue_type_template_id_7bf842c2_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_DatePicker = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("DatePicker"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", DateRangePickervue_type_template_id_7bf842c2_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h6", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_DateRangeFrom')) + " ", 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { + type: "text", + id: "inputCalendarFrom", + name: "inputCalendarFrom", + class: "browser-default", + "onUpdate:modelValue": _cache[0] || (_cache[0] = function ($event) { + return _ctx.startDateText = $event; + }), + onKeydown: _cache[1] || (_cache[1] = function ($event) { + return _ctx.onRangeInputChanged('from', $event); + }), + onKeyup: _cache[2] || (_cache[2] = function ($event) { + return _ctx.handleEnterPress($event); + }) + }, null, 544), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vModelText"], _ctx.startDateText]])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_DatePicker, { + id: "calendarFrom", + "view-date": _ctx.startDate, + "selected-date-start": _ctx.fromPickerSelectedDates[0], + "selected-date-end": _ctx.fromPickerSelectedDates[1], + "highlighted-date-start": _ctx.fromPickerHighlightedDates[0], + "highlighted-date-end": _ctx.fromPickerHighlightedDates[1], + onDateSelect: _cache[3] || (_cache[3] = function ($event) { + return _ctx.setStartRangeDate($event.date); + }), + onCellHover: _cache[4] || (_cache[4] = function ($event) { + return _ctx.fromPickerHighlightedDates = _ctx.getNewHighlightedDates($event.date, $event.$cell); + }), + onCellHoverLeave: _cache[5] || (_cache[5] = function ($event) { + return _ctx.fromPickerHighlightedDates = [null, null]; + }) + }, null, 8, ["view-date", "selected-date-start", "selected-date-end", "highlighted-date-start", "highlighted-date-end"])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", DateRangePickervue_type_template_id_7bf842c2_hoisted_2, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h6", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_DateRangeTo')) + " ", 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { + type: "text", + id: "inputCalendarTo", + name: "inputCalendarTo", + class: "browser-default", + "onUpdate:modelValue": _cache[6] || (_cache[6] = function ($event) { + return _ctx.endDateText = $event; + }), + onKeydown: _cache[7] || (_cache[7] = function ($event) { + return _ctx.onRangeInputChanged('to', $event); + }), + onKeyup: _cache[8] || (_cache[8] = function ($event) { + return _ctx.handleEnterPress($event); + }) + }, null, 544), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vModelText"], _ctx.endDateText]])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_DatePicker, { + id: "calendarTo", + "view-date": _ctx.endDate, + "selected-date-start": _ctx.toPickerSelectedDates[0], + "selected-date-end": _ctx.toPickerSelectedDates[1], + "highlighted-date-start": _ctx.toPickerHighlightedDates[0], + "highlighted-date-end": _ctx.toPickerHighlightedDates[1], + onDateSelect: _cache[9] || (_cache[9] = function ($event) { + return _ctx.setEndRangeDate($event.date); + }), + onCellHover: _cache[10] || (_cache[10] = function ($event) { + return _ctx.toPickerHighlightedDates = _ctx.getNewHighlightedDates($event.date, $event.$cell); + }), + onCellHoverLeave: _cache[11] || (_cache[11] = function ($event) { + return _ctx.toPickerHighlightedDates = [null, null]; + }) + }, null, 8, ["view-date", "selected-date-start", "selected-date-end", "highlighted-date-start", "highlighted-date-end"])])]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.vue?vue&type=template&id=7bf842c2 + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.vue?vue&type=script&lang=ts + + + +var DATE_FORMAT = 'YYYY-MM-DD'; +/* harmony default export */ var DateRangePickervue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + startDate: String, + endDate: String + }, + components: { + DatePicker: DatePicker + }, + data: function data() { + var startDate = null; + + try { + if (this.startDate) { + startDate = parseDate(this.startDate); + } + } catch (e) {// ignore + } + + var endDate = null; + + try { + if (this.endDate) { + endDate = parseDate(this.endDate); + } + } catch (e) {// ignore + } + + return { + fromPickerSelectedDates: [startDate, startDate], + toPickerSelectedDates: [endDate, endDate], + fromPickerHighlightedDates: [null, null], + toPickerHighlightedDates: [null, null], + startDateText: this.startDate, + endDateText: this.endDate, + startDateInvalid: false, + endDateInvalid: false + }; + }, + emits: ['rangeChange', 'submit'], + watch: { + startDate: function startDate() { + this.startDateText = this.startDate; + this.setStartRangeDateFromStr(this.startDate); + }, + endDate: function endDate() { + this.endDateText = this.endDate; + this.setEndRangeDateFromStr(this.endDate); + } + }, + mounted: function mounted() { + this.rangeChanged(); // emit with initial range pair + }, + methods: { + setStartRangeDate: function setStartRangeDate(date) { + this.fromPickerSelectedDates = [date, date]; + this.rangeChanged(); + }, + setEndRangeDate: function setEndRangeDate(date) { + this.toPickerSelectedDates = [date, date]; + this.rangeChanged(); + }, + onRangeInputChanged: function onRangeInputChanged(source, event) { + var _this = this; + + setTimeout(function () { + if (source === 'from') { + _this.setStartRangeDateFromStr(event.target.value); + } else { + _this.setEndRangeDateFromStr(event.target.value); + } + }); + }, + getNewHighlightedDates: function getNewHighlightedDates(date, $cell) { + if ($cell.hasClass('ui-datepicker-unselectable')) { + return null; + } + + return [date, date]; + }, + handleEnterPress: function handleEnterPress($event) { + if ($event.keyCode !== 13) { + return; + } + + this.$emit('submit', { + start: this.startDate, + end: this.endDate + }); + }, + setStartRangeDateFromStr: function setStartRangeDateFromStr(dateStr) { + this.startDateInvalid = true; + var startDateParsed = null; + + try { + if (dateStr && dateStr.length === DATE_FORMAT.length) { + startDateParsed = parseDate(dateStr); + } + } catch (e) {// ignore + } + + if (startDateParsed) { + this.fromPickerSelectedDates = [startDateParsed, startDateParsed]; + this.startDateInvalid = false; + this.rangeChanged(); + } + }, + setEndRangeDateFromStr: function setEndRangeDateFromStr(dateStr) { + this.endDateInvalid = true; + var endDateParsed = null; + + try { + if (dateStr && dateStr.length === DATE_FORMAT.length) { + endDateParsed = parseDate(dateStr); + } + } catch (e) {// ignore + } + + if (endDateParsed) { + this.toPickerSelectedDates = [endDateParsed, endDateParsed]; + this.endDateInvalid = false; + this.rangeChanged(); + } + }, + rangeChanged: function rangeChanged() { + this.$emit('rangeChange', { + start: this.fromPickerSelectedDates[0] ? format(this.fromPickerSelectedDates[0]) : null, + end: this.toPickerSelectedDates[0] ? format(this.toPickerSelectedDates[0]) : null + }); + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.vue + + + +DateRangePickervue_type_script_lang_ts.render = DateRangePickervue_type_template_id_7bf842c2_render + +/* harmony default export */ var DateRangePicker = (DateRangePickervue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var DateRangePicker_adapter = (createAngularJsAdapter({ + component: DateRangePicker, + scope: { + startDate: { + angularJsBind: '<' + }, + endDate: { + angularJsBind: '<' + }, + rangeChange: { + angularJsBind: '&' + }, + submit: { + angularJsBind: '&' + } + }, + directiveName: 'piwikDateRangePicker', + restrict: 'E' +})); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/PeriodDatePicker/PeriodDatePicker.vue?vue&type=template&id=6d1fa14c + +function PeriodDatePickervue_type_template_id_6d1fa14c_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_DatePicker = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("DatePicker"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createBlock"])(_component_DatePicker, { + "selected-date-start": _ctx.selectedDates[0], + "selected-date-end": _ctx.selectedDates[1], + "highlighted-date-start": _ctx.highlightedDates[0], + "highlighted-date-end": _ctx.highlightedDates[1], + "view-date": _ctx.viewDate, + "step-months": _ctx.period === 'year' ? 12 : 1, + "disable-month-dropdown": _ctx.period === 'year', + onCellHover: _cache[0] || (_cache[0] = function ($event) { + return _ctx.onHoverNormalCell($event.date, $event.$cell); + }), + onCellHoverLeave: _cache[1] || (_cache[1] = function ($event) { + return _ctx.onHoverLeaveNormalCells(); + }), + onDateSelect: _cache[2] || (_cache[2] = function ($event) { + return _ctx.onDateSelected($event.date); + }) + }, null, 8, ["selected-date-start", "selected-date-end", "highlighted-date-start", "highlighted-date-end", "view-date", "step-months", "disable-month-dropdown"]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/PeriodDatePicker/PeriodDatePicker.vue?vue&type=template&id=6d1fa14c + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/PeriodDatePicker/PeriodDatePicker.vue?vue&type=script&lang=ts + + + + +var PeriodDatePickervue_type_script_lang_ts_piwikMinDate = new Date(Matomo_Matomo.minDateYear, Matomo_Matomo.minDateMonth - 1, Matomo_Matomo.minDateDay); +var piwikMaxDate = new Date(Matomo_Matomo.maxDateYear, Matomo_Matomo.maxDateMonth - 1, Matomo_Matomo.maxDateDay); +/* harmony default export */ var PeriodDatePickervue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + period: { + type: String, + required: true + }, + date: [String, Date] + }, + components: { + DatePicker: DatePicker + }, + emits: ['select'], + setup: function setup(props, context) { + var viewDate = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])(props.date); + var selectedDates = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])([null, null]); + var highlightedDates = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])([null, null]); + + function getBoundedDateRange(date) { + var dates = Periods_Periods.get(props.period).parse(date).getDateRange(); // make sure highlighted date range is within min/max date range + + dates[0] = PeriodDatePickervue_type_script_lang_ts_piwikMinDate < dates[0] ? dates[0] : PeriodDatePickervue_type_script_lang_ts_piwikMinDate; + dates[1] = piwikMaxDate > dates[1] ? dates[1] : piwikMaxDate; + return dates; + } + + function onHoverNormalCell(cellDate, $cell) { + var isOutOfMinMaxDateRange = cellDate < PeriodDatePickervue_type_script_lang_ts_piwikMinDate || cellDate > piwikMaxDate; // don't highlight anything if the period is month or day, and we're hovering over calendar + // whitespace. since there are no dates, it's doesn't make sense what you're selecting. + + var shouldNotHighlightFromWhitespace = $cell.hasClass('ui-datepicker-other-month') && (props.period === 'month' || props.period === 'day'); + + if (isOutOfMinMaxDateRange || shouldNotHighlightFromWhitespace) { + highlightedDates.value = [null, null]; + return; + } + + highlightedDates.value = getBoundedDateRange(cellDate); + } + + function onHoverLeaveNormalCells() { + highlightedDates.value = [null, null]; + } + + function onDateSelected(date) { + context.emit('select', { + date: date + }); + } + + function onChanges() { + if (!props.period || !props.date) { + selectedDates.value = [null, null]; + viewDate.value = null; + return; + } + + selectedDates.value = getBoundedDateRange(props.date); + viewDate.value = parseDate(props.date); + } + + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["watch"])(props, onChanges); + onChanges(); + return { + selectedDates: selectedDates, + highlightedDates: highlightedDates, + viewDate: viewDate, + onHoverNormalCell: onHoverNormalCell, + onHoverLeaveNormalCells: onHoverLeaveNormalCells, + onDateSelected: onDateSelected + }; + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/PeriodDatePicker/PeriodDatePicker.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/PeriodDatePicker/PeriodDatePicker.vue + + + +PeriodDatePickervue_type_script_lang_ts.render = PeriodDatePickervue_type_template_id_6d1fa14c_render + +/* harmony default export */ var PeriodDatePicker = (PeriodDatePickervue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/PeriodDatePicker/PeriodDatePicker.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var PeriodDatePicker_adapter = (createAngularJsAdapter({ + component: PeriodDatePicker, + scope: { + period: { + angularJsBind: '<' + }, + date: { + angularJsBind: '<' + }, + select: { + angularJsBind: '&' + } + }, + directiveName: 'piwikPeriodDatePicker', + restrict: 'E' +})); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/SiteSelector/SiteSelector.vue?vue&type=template&id=48e19035 + +var SiteSelectorvue_type_template_id_48e19035_hoisted_1 = ["value", "name"]; +var SiteSelectorvue_type_template_id_48e19035_hoisted_2 = ["title"]; +var SiteSelectorvue_type_template_id_48e19035_hoisted_3 = ["textContent"]; +var SiteSelectorvue_type_template_id_48e19035_hoisted_4 = { + key: 1, + class: "placeholder" +}; +var SiteSelectorvue_type_template_id_48e19035_hoisted_5 = { + class: "dropdown" +}; +var SiteSelectorvue_type_template_id_48e19035_hoisted_6 = { + class: "custom_select_search" +}; +var SiteSelectorvue_type_template_id_48e19035_hoisted_7 = ["placeholder"]; +var SiteSelectorvue_type_template_id_48e19035_hoisted_8 = { + key: 0 +}; +var SiteSelectorvue_type_template_id_48e19035_hoisted_9 = { + class: "custom_select_container" +}; +var SiteSelectorvue_type_template_id_48e19035_hoisted_10 = ["onClick"]; +var SiteSelectorvue_type_template_id_48e19035_hoisted_11 = ["innerHTML", "href", "title"]; +var SiteSelectorvue_type_template_id_48e19035_hoisted_12 = { + class: "ui-autocomplete ui-front ui-menu ui-widget ui-widget-content ui-corner-all\n siteSelect" +}; +var SiteSelectorvue_type_template_id_48e19035_hoisted_13 = { + class: "ui-menu-item" +}; +var SiteSelectorvue_type_template_id_48e19035_hoisted_14 = { + class: "ui-corner-all", + tabindex: "-1" +}; +var _hoisted_15 = { + key: 1 +}; +function SiteSelectorvue_type_template_id_48e19035_render(_ctx, _cache, $props, $setup, $data, $options) { + var _ctx$modelValue, _ctx$modelValue2, _ctx$modelValue3, _ctx$modelValue4; + + var _component_AllSitesLink = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("AllSitesLink"); + + var _directive_focus_if = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("focus-if"); + + var _directive_focus_anywhere_but_here = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("focus-anywhere-but-here"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])((Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["siteSelector piwikSelector borderedControl", { + 'expanded': _ctx.showSitesList, + 'disabled': !_ctx.hasMultipleSites + }]) + }, [_ctx.name ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("input", { + key: 0, + type: "hidden", + value: (_ctx$modelValue = _ctx.modelValue) === null || _ctx$modelValue === void 0 ? void 0 : _ctx$modelValue.id, + name: _ctx.name + }, null, 8, SiteSelectorvue_type_template_id_48e19035_hoisted_1)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + ref: "selectorLink", + onClick: _cache[0] || (_cache[0] = function () { + return _ctx.onClickSelector && _ctx.onClickSelector.apply(_ctx, arguments); + }), + onKeydown: _cache[1] || (_cache[1] = function ($event) { + return _ctx.onPressEnter($event); + }), + href: "javascript:void(0)", + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])([{ + 'loading': _ctx.isLoading + }, "title"]), + tabindex: "4", + title: _ctx.selectorLinkTitle + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["icon icon-arrow-bottom", { + 'iconHidden': _ctx.isLoading, + 'collapsed': !_ctx.showSitesList + }]) + }, null, 2), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", null, [(_ctx$modelValue2 = _ctx.modelValue) !== null && _ctx$modelValue2 !== void 0 && _ctx$modelValue2.name || !_ctx.placeholder ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("span", { + key: 0, + textContent: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(((_ctx$modelValue3 = _ctx.modelValue) === null || _ctx$modelValue3 === void 0 ? void 0 : _ctx$modelValue3.name) || _ctx.firstSiteName) + }, null, 8, SiteSelectorvue_type_template_id_48e19035_hoisted_3)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), !((_ctx$modelValue4 = _ctx.modelValue) !== null && _ctx$modelValue4 !== void 0 && _ctx$modelValue4.name) && _ctx.placeholder ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("span", SiteSelectorvue_type_template_id_48e19035_hoisted_4, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.placeholder), 1)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)])], 42, SiteSelectorvue_type_template_id_48e19035_hoisted_2), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", SiteSelectorvue_type_template_id_48e19035_hoisted_5, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", SiteSelectorvue_type_template_id_48e19035_hoisted_6, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { + type: "text", + onClick: _cache[2] || (_cache[2] = function ($event) { + _ctx.searchTerm = ''; + + _ctx.loadInitialSites(); + }), + "onUpdate:modelValue": _cache[3] || (_cache[3] = function ($event) { + return _ctx.searchTerm = $event; + }), + tabindex: "4", + class: "websiteSearch inp browser-default", + placeholder: _ctx.translate('General_Search') + }, null, 8, SiteSelectorvue_type_template_id_48e19035_hoisted_7), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vModelText"], _ctx.searchTerm], [_directive_focus_if, {}, _ctx.shouldFocusOnSearch]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("img", { + title: "Clear", + onClick: _cache[4] || (_cache[4] = function ($event) { + _ctx.searchTerm = ''; + + _ctx.loadInitialSites(); + }), + class: "reset", + src: "plugins/CoreHome/images/reset_search.png" + }, null, 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.searchTerm]])], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.autocompleteMinSites <= _ctx.sites.length || _ctx.searchTerm]]), _ctx.allSitesLocation === 'top' && _ctx.showAllSitesItem ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", SiteSelectorvue_type_template_id_48e19035_hoisted_8, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_AllSitesLink, { + href: _ctx.urlAllSites, + "all-sites-text": _ctx.allSitesText, + onClick: _cache[5] || (_cache[5] = function ($event) { + return _ctx.onAllSitesClick($event); + }) + }, null, 8, ["href", "all-sites-text"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", SiteSelectorvue_type_template_id_48e19035_hoisted_9, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("ul", { + class: "custom_select_ul_list", + onClick: _cache[7] || (_cache[7] = function ($event) { + return _ctx.showSitesList = false; + }) + }, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.sites, function (site, index) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])((Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("li", { + onClick: function onClick($event) { + return _ctx.switchSite(Object.assign(Object.assign({}, site), {}, { + id: site.idsite + }), $event); + }, + key: index + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + onClick: _cache[6] || (_cache[6] = function ($event) { + return $event.preventDefault(); + }), + innerHTML: _ctx.$sanitize(_ctx.getMatchedSiteName(site.name)), + tabindex: "4", + href: _ctx.getUrlForSiteId(site.idsite), + title: site.name + }, null, 8, SiteSelectorvue_type_template_id_48e19035_hoisted_11)], 8, SiteSelectorvue_type_template_id_48e19035_hoisted_10)), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], !(!_ctx.showSelectedSite && _ctx.activeSiteId === site.idsite)]]); + }), 128))]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("ul", SiteSelectorvue_type_template_id_48e19035_hoisted_12, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("li", SiteSelectorvue_type_template_id_48e19035_hoisted_13, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", SiteSelectorvue_type_template_id_48e19035_hoisted_14, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('SitesManager_NotFound') + ' ' + _ctx.searchTerm), 1)])], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], !_ctx.sites.length && _ctx.searchTerm]])]), _ctx.allSitesLocation === 'bottom' && _ctx.showAllSitesItem ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", _hoisted_15, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_AllSitesLink, { + href: _ctx.urlAllSites, + "all-sites-text": _ctx.allSitesText, + onClick: _cache[8] || (_cache[8] = function ($event) { + return _ctx.onAllSitesClick($event); + }) + }, null, 8, ["href", "all-sites-text"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.showSitesList]])], 2)), [[_directive_focus_anywhere_but_here, { + blur: _ctx.onBlur + }]]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/SiteSelector/SiteSelector.vue?vue&type=template&id=48e19035 + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/SiteSelector/AllSitesLink.vue?vue&type=template&id=45607d28 + +var AllSitesLinkvue_type_template_id_45607d28_hoisted_1 = ["innerHTML", "href"]; +function AllSitesLinkvue_type_template_id_45607d28_render(_ctx, _cache, $props, $setup, $data, $options) { + var _this = this; + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + onClick: _cache[1] || (_cache[1] = function ($event) { + return _this.onClick($event); + }), + class: "custom_select_all" + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + onClick: _cache[0] || (_cache[0] = function ($event) { + return $event.preventDefault(); + }), + innerHTML: _ctx.$sanitize(_ctx.allSitesText), + tabindex: "4", + href: _ctx.href + }, null, 8, AllSitesLinkvue_type_template_id_45607d28_hoisted_1)]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/SiteSelector/AllSitesLink.vue?vue&type=template&id=45607d28 + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/SiteSelector/AllSitesLink.vue?vue&type=script&lang=ts + +/* harmony default export */ var AllSitesLinkvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + href: String, + allSitesText: String + }, + emits: ['click'], + methods: { + onClick: function onClick(event) { + this.$emit('click', event); + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/SiteSelector/AllSitesLink.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/SiteSelector/AllSitesLink.vue + + + +AllSitesLinkvue_type_script_lang_ts.render = AllSitesLinkvue_type_template_id_45607d28_render + +/* harmony default export */ var AllSitesLink = (AllSitesLinkvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/SiteSelector/SitesStore.ts +function SitesStore_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function SitesStore_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function SitesStore_createClass(Constructor, protoProps, staticProps) { if (protoProps) SitesStore_defineProperties(Constructor.prototype, protoProps); if (staticProps) SitesStore_defineProperties(Constructor, staticProps); return Constructor; } + +function SitesStore_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + + + +var SitesStore_SitesStore = /*#__PURE__*/function () { + function SitesStore() { + var _this = this; + + SitesStore_classCallCheck(this, SitesStore); + + SitesStore_defineProperty(this, "state", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["reactive"])({ + initialSites: [], + isInitialized: false + })); + + SitesStore_defineProperty(this, "currentRequestAbort", null); + + SitesStore_defineProperty(this, "limitRequest", void 0); + + SitesStore_defineProperty(this, "initialSites", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(_this.state.initialSites); + })); + } + + SitesStore_createClass(SitesStore, [{ + key: "loadInitialSites", + value: function loadInitialSites() { + var _this2 = this; + + if (this.state.isInitialized) { + return Promise.resolve(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(this.state.initialSites)); + } + + return this.searchSite('%').then(function (sites) { + _this2.state.isInitialized = true; + + if (sites !== null) { + _this2.state.initialSites = sites; + } + + return sites; + }); + } + }, { + key: "loadSite", + value: function loadSite(idSite) { + if (idSite === 'all') { + src_MatomoUrl_MatomoUrl.updateUrl(Object.assign(Object.assign({}, src_MatomoUrl_MatomoUrl.urlParsed.value), {}, { + module: 'MultiSites', + action: 'index', + date: src_MatomoUrl_MatomoUrl.parsed.value.date, + period: src_MatomoUrl_MatomoUrl.parsed.value.period + })); + } else { + src_MatomoUrl_MatomoUrl.updateUrl(Object.assign(Object.assign({}, src_MatomoUrl_MatomoUrl.urlParsed.value), {}, { + segment: '', + idSite: idSite + }), Object.assign(Object.assign({}, src_MatomoUrl_MatomoUrl.hashParsed.value), {}, { + segment: '', + idSite: idSite + })); + } + } + }, { + key: "searchSite", + value: function searchSite(term) { + var _this3 = this; + + var onlySitesWithAdminAccess = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + if (!term) { + return this.loadInitialSites(); + } + + if (this.currentRequestAbort) { + this.currentRequestAbort.abort(); + } + + if (!this.limitRequest) { + this.limitRequest = AjaxHelper_AjaxHelper.fetch({ + method: 'SitesManager.getNumWebsitesToDisplayPerPage' + }); + } + + return this.limitRequest.then(function (response) { + var limit = response.value; + var methodToCall = 'SitesManager.getPatternMatchSites'; + + if (onlySitesWithAdminAccess) { + methodToCall = 'SitesManager.getSitesWithAdminAccess'; + } + + _this3.currentRequestAbort = new AbortController(); + return AjaxHelper_AjaxHelper.fetch({ + method: methodToCall, + limit: limit, + pattern: term + }, { + abortController: _this3.currentRequestAbort + }); + }).then(function (response) { + if (response) { + return _this3.processWebsitesList(response); + } + + return null; + }).finally(function () { + _this3.currentRequestAbort = null; + }); + } + }, { + key: "processWebsitesList", + value: function processWebsitesList(response) { + var sites = response; + + if (!sites || !sites.length) { + return []; + } + + sites = sites.map(function (s) { + return Object.assign(Object.assign({}, s), {}, { + name: s.group ? "[".concat(s.group, "] ").concat(s.name) : s.name + }); + }); + sites.sort(function (lhs, rhs) { + if (lhs.name.toLowerCase() < rhs.name.toLowerCase()) { + return -1; + } + + return lhs.name.toLowerCase() > rhs.name.toLowerCase() ? 1 : 0; + }); + return sites; + } + }]); + + return SitesStore; +}(); + +/* harmony default export */ var SiteSelector_SitesStore = (new SitesStore_SitesStore()); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/debounce.ts +var DEFAULT_DEBOUNCE_DELAY = 300; +function debounce(fn) { + var delayInMs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_DEBOUNCE_DELAY; + var timeout; + return function wrapper() { + var _this = this; + + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + if (timeout) { + clearTimeout(timeout); + } + + timeout = setTimeout(function () { + fn.call.apply(fn, [_this].concat(args)); + }, delayInMs); + }; +} +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/SiteSelector/SiteSelector.vue?vue&type=script&lang=ts + + + + + + + + + +/* harmony default export */ var SiteSelectorvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + modelValue: { + type: Object, + default: function _default(props) { + if (props.modelValue) { + return props.modelValue; + } + + return Matomo_Matomo.idSite ? { + id: Matomo_Matomo.idSite, + name: Matomo_Matomo.helper.htmlDecode(Matomo_Matomo.siteName) + } : undefined; + } + }, + showSelectedSite: { + type: Boolean, + default: false + }, + showAllSitesItem: { + type: Boolean, + default: true + }, + switchSiteOnSelect: { + type: Boolean, + default: true + }, + onlySitesWithAdminAccess: { + type: Boolean, + default: false + }, + name: { + type: String, + default: '' + }, + allSitesText: { + type: String, + default: translate('General_MultiSitesSummary') + }, + allSitesLocation: { + type: String, + default: 'bottom' + }, + placeholder: String + }, + emits: ['update:modelValue', 'blur'], + components: { + AllSitesLink: AllSitesLink + }, + directives: { + FocusAnywhereButHere: FocusAnywhereButHere, + FocusIf: FocusIf + }, + watch: { + searchTerm: function searchTerm() { + this.onSearchTermChanged(); + } + }, + data: function data() { + return { + searchTerm: '', + activeSiteId: "".concat(Matomo_Matomo.idSite), + showSitesList: false, + isLoading: false, + sites: [], + autocompleteMinSites: parseInt(Matomo_Matomo.config.autocomplete_min_sites, 10) + }; + }, + created: function created() { + this.searchSite = debounce(this.searchSite); + }, + mounted: function mounted() { + var _this = this; + + window.initTopControls(); + this.loadInitialSites().then(function () { + if ((!_this.modelValue || !_this.modelValue.id) && !_this.hasMultipleSites && _this.sites[0]) { + _this.$emit('update:modelValue', { + id: _this.sites[0].idsite, + name: _this.sites[0].name + }); + } + }); + var shortcutTitle = translate('CoreHome_ShortcutWebsiteSelector'); + Matomo_Matomo.helper.registerShortcut('w', shortcutTitle, function (event) { + if (event.altKey) { + return; + } + + if (event.preventDefault) { + event.preventDefault(); + } else { + event.returnValue = false; // IE + } + + var selectorLink = _this.$refs.selectorLink; + + if (selectorLink) { + selectorLink.click(); + selectorLink.focus(); + } + }); + }, + computed: { + shouldFocusOnSearch: function shouldFocusOnSearch() { + return this.showSitesList && this.autocompleteMinSites <= this.sites.length || this.searchTerm; + }, + selectorLinkTitle: function selectorLinkTitle() { + var _this$modelValue; + + return this.hasMultipleSites ? translate('CoreHome_ChangeCurrentWebsite', ((_this$modelValue = this.modelValue) === null || _this$modelValue === void 0 ? void 0 : _this$modelValue.name) || this.firstSiteName) : ''; + }, + hasMultipleSites: function hasMultipleSites() { + return SiteSelector_SitesStore.initialSites.value && SiteSelector_SitesStore.initialSites.value.length > 1; + }, + firstSiteName: function firstSiteName() { + var initialSites = SiteSelector_SitesStore.initialSites.value; + return initialSites && initialSites.length > 0 ? initialSites[0].name : ''; + }, + urlAllSites: function urlAllSites() { + var newQuery = src_MatomoUrl_MatomoUrl.stringify(Object.assign(Object.assign({}, src_MatomoUrl_MatomoUrl.urlParsed.value), {}, { + module: 'MultiSites', + action: 'index', + date: src_MatomoUrl_MatomoUrl.parsed.value.date, + period: src_MatomoUrl_MatomoUrl.parsed.value.period + })); + return "?".concat(newQuery); + } + }, + methods: { + onSearchTermChanged: function onSearchTermChanged() { + if (!this.searchTerm) { + this.isLoading = false; + this.loadInitialSites(); + } else { + this.isLoading = true; + this.searchSite(this.searchTerm); + } + }, + onAllSitesClick: function onAllSitesClick(event) { + this.switchSite({ + id: 'all', + name: this.$props.allSitesText + }, event); + this.showSitesList = false; + }, + switchSite: function switchSite(site, event) { + // for Mac OS cmd key needs to be pressed, ctrl key on other systems + var controlKey = navigator.userAgent.indexOf('Mac OS X') !== -1 ? event.metaKey : event.ctrlKey; + + if (event && controlKey && event.target && event.target.href) { + window.open(event.target.href, '_blank'); + return; + } + + this.$emit('update:modelValue', { + id: site.id, + name: site.name + }); + + if (!this.switchSiteOnSelect || this.activeSiteId === site.id) { + return; + } + + SiteSelector_SitesStore.loadSite(site.id); + }, + onBlur: function onBlur() { + this.showSitesList = false; + this.$emit('blur'); + }, + onClickSelector: function onClickSelector() { + if (this.hasMultipleSites) { + this.showSitesList = !this.showSitesList; + + if (!this.isLoading && !this.searchTerm) { + this.loadInitialSites(); + } + } + }, + onPressEnter: function onPressEnter(event) { + if (event.key !== 'Enter') { + return; + } + + event.preventDefault(); + this.showSitesList = !this.showSitesList; + + if (this.showSitesList && !this.isLoading) { + this.loadInitialSites(); + } + }, + getMatchedSiteName: function getMatchedSiteName(siteName) { + var index = siteName.toUpperCase().indexOf(this.searchTerm.toUpperCase()); + + if (index === -1 || this.isLoading // only highlight when we know the displayed results are for a search + ) { + return Matomo_Matomo.helper.htmlEntities(siteName); + } + + var previousPart = Matomo_Matomo.helper.htmlEntities(siteName.substring(0, index)); + var lastPart = Matomo_Matomo.helper.htmlEntities(siteName.substring(index + this.searchTerm.length)); + return "".concat(previousPart, "").concat(this.searchTerm, "").concat(lastPart); + }, + loadInitialSites: function loadInitialSites() { + var _this2 = this; + + return SiteSelector_SitesStore.loadInitialSites().then(function (sites) { + _this2.sites = sites || []; + }); + }, + searchSite: function searchSite(term) { + var _this3 = this; + + this.isLoading = true; + SiteSelector_SitesStore.searchSite(term, this.onlySitesWithAdminAccess).then(function (sites) { + if (term !== _this3.searchTerm) { + return; // search term changed in the meantime + } + + if (sites) { + _this3.sites = sites; + } + }).finally(function () { + _this3.isLoading = false; + }); + }, + getUrlForSiteId: function getUrlForSiteId(idSite) { + var newQuery = src_MatomoUrl_MatomoUrl.stringify(Object.assign(Object.assign({}, src_MatomoUrl_MatomoUrl.urlParsed.value), {}, { + segment: '', + idSite: idSite + })); + var newHash = src_MatomoUrl_MatomoUrl.stringify(Object.assign(Object.assign({}, src_MatomoUrl_MatomoUrl.hashParsed.value), {}, { + segment: '', + idSite: idSite + })); + return "?".concat(newQuery, "#?").concat(newHash); + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/SiteSelector/SiteSelector.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/SiteSelector/SiteSelector.vue + + + +SiteSelectorvue_type_script_lang_ts.render = SiteSelectorvue_type_template_id_48e19035_render + +/* harmony default export */ var SiteSelector = (SiteSelectorvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/SiteSelector/SiteSelector.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + + + +/* harmony default export */ var SiteSelector_adapter = (createAngularJsAdapter({ + component: SiteSelector, + require: '?ngModel', + scope: { + showSelectedSite: { + angularJsBind: '=' + }, + showAllSitesItem: { + angularJsBind: '=' + }, + switchSiteOnSelect: { + angularJsBind: '=' + }, + onlySitesWithAdminAccess: { + angularJsBind: '=' + }, + name: { + angularJsBind: '@' + }, + allSitesText: { + angularJsBind: '@' + }, + allSitesLocation: { + angularJsBind: '@' + }, + placeholder: { + angularJsBind: '@' + }, + modelValue: { + default: function _default(scope, element, attrs) { + if (attrs.siteid && attrs.sitename) { + return { + id: attrs.siteid, + name: Matomo_Matomo.helper.htmlDecode(attrs.sitename) + }; + } + + if (Matomo_Matomo.idSite) { + return { + id: Matomo_Matomo.idSite, + name: Matomo_Matomo.helper.htmlDecode(Matomo_Matomo.siteName) + }; + } + + return undefined; + } + } + }, + $inject: ['$timeout'], + directiveName: 'piwikSiteselector', + events: { + 'update:modelValue': function updateModelValue(newValue, vm, scope, element, attrs, ngModel, $timeout) { + if (newValue && !vm.modelValue || !newValue && vm.modelValue || newValue.id !== vm.modelValue.id) { + $timeout(function () { + scope.value = newValue; + element.attr('siteid', newValue.id); + element.trigger('change', newValue); + + if (ngModel) { + ngModel.$setViewValue(newValue); + ngModel.$render(); // not called automatically by the digest + } + }); + } + }, + blur: function blur(event, vm, scope) { + setTimeout(function () { + return scope.$apply(); + }); + } + }, + postCreate: function postCreate(vm, scope, element, attrs, controller) { + var ngModel = controller; + scope.$watch('value', function (newVal) { + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["nextTick"])(function () { + if (newVal !== vm.modelValue) { + vm.modelValue = newVal; + } + }); + }); + + if (attrs.siteid && attrs.sitename) { + vm.modelValue = { + id: attrs.siteid, + name: Matomo_Matomo.helper.htmlDecode(attrs.sitename) + }; + } else if (Matomo_Matomo.idSite) { + vm.modelValue = { + id: Matomo_Matomo.idSite, + name: Matomo_Matomo.helper.htmlDecode(Matomo_Matomo.siteName) + }; + } // setup ng-model mapping + + + if (ngModel) { + ngModel.$setViewValue(vm.modelValue); + + ngModel.$render = function () { + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["nextTick"])(function () { + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["nextTick"])(function () { + if (window.angular.isString(ngModel.$viewValue)) { + vm.modelValue = JSON.parse(ngModel.$viewValue); + } else { + vm.modelValue = ngModel.$viewValue; + } + }); + }); + }; + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/SiteSelector/SitesStore.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + + +function siteSelectorModelAdapter() { + return { + get initialSites() { + return SiteSelector_SitesStore.initialSites.value; + }, + + loadSite: SiteSelector_SitesStore.loadSite.bind(SiteSelector_SitesStore), + loadInitialSites: function loadInitialSites() { + return cloneThenApply(SiteSelector_SitesStore.loadInitialSites()); + }, + searchSite: function searchSite() { + return cloneThenApply(SiteSelector_SitesStore.searchSite.apply(SiteSelector_SitesStore, arguments)); + } + }; +} + +window.angular.module('piwikApp.service').factory('siteSelectorModel', siteSelectorModelAdapter); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/QuickAccess/QuickAccess.vue?vue&type=template&id=7b45bbd5 + +var QuickAccessvue_type_template_id_7b45bbd5_hoisted_1 = { + ref: "root", + class: "quickAccessInside" +}; +var QuickAccessvue_type_template_id_7b45bbd5_hoisted_2 = ["title", "placeholder"]; +var QuickAccessvue_type_template_id_7b45bbd5_hoisted_3 = { + class: "dropdown" +}; +var QuickAccessvue_type_template_id_7b45bbd5_hoisted_4 = { + class: "no-result" +}; +var QuickAccessvue_type_template_id_7b45bbd5_hoisted_5 = ["onClick"]; +var QuickAccessvue_type_template_id_7b45bbd5_hoisted_6 = ["onMouseenter", "onClick"]; +var QuickAccessvue_type_template_id_7b45bbd5_hoisted_7 = { + class: "quickAccessMatomoSearch" +}; +var QuickAccessvue_type_template_id_7b45bbd5_hoisted_8 = ["onMouseenter", "onClick"]; +var QuickAccessvue_type_template_id_7b45bbd5_hoisted_9 = ["textContent"]; +var QuickAccessvue_type_template_id_7b45bbd5_hoisted_10 = { + class: "quick-access-category helpCategory" +}; +var QuickAccessvue_type_template_id_7b45bbd5_hoisted_11 = ["href"]; +function QuickAccessvue_type_template_id_7b45bbd5_render(_ctx, _cache, $props, $setup, $data, $options) { + var _directive_focus_if = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("focus-if"); + + var _directive_focus_anywhere_but_here = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("focus-anywhere-but-here"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])((Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", QuickAccessvue_type_template_id_7b45bbd5_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + class: "icon-search", + onMouseenter: _cache[0] || (_cache[0] = function ($event) { + return _ctx.searchActive = true; + }) + }, null, 32), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { + class: "s", + onKeydown: _cache[1] || (_cache[1] = function ($event) { + return _ctx.onKeypress($event); + }), + onFocus: _cache[2] || (_cache[2] = function ($event) { + return _ctx.searchActive = true; + }), + "onUpdate:modelValue": _cache[3] || (_cache[3] = function ($event) { + return _ctx.searchTerm = $event; + }), + type: "text", + tabindex: "2", + title: _ctx.quickAccessTitle, + placeholder: _ctx.translate('General_Search'), + ref: "input" + }, null, 40, QuickAccessvue_type_template_id_7b45bbd5_hoisted_2), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vModelText"], _ctx.searchTerm], [_directive_focus_if, {}, _ctx.searchActive]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", QuickAccessvue_type_template_id_7b45bbd5_hoisted_3, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("ul", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("li", QuickAccessvue_type_template_id_7b45bbd5_hoisted_4, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_SearchNoResults')), 1)], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], !(_ctx.numMenuItems > 0 || _ctx.sites.length)]]), (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.menuItems, function (subcategory) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("ul", { + key: subcategory.title + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("li", { + class: "quick-access-category", + onClick: function onClick($event) { + _ctx.searchTerm = subcategory.title; + + _ctx.searchMenu(_ctx.searchTerm); + } + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(subcategory.title), 9, QuickAccessvue_type_template_id_7b45bbd5_hoisted_5), (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(subcategory.items, function (submenuEntry) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("li", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["result", { + selected: submenuEntry.menuIndex === _ctx.searchIndex + }]), + onMouseenter: function onMouseenter($event) { + return _ctx.searchIndex = submenuEntry.menuIndex; + }, + onClick: function onClick($event) { + return _ctx.selectMenuItem(submenuEntry.index); + }, + key: submenuEntry.index + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(submenuEntry.name.trim()), 1)], 42, QuickAccessvue_type_template_id_7b45bbd5_hoisted_6); + }), 128))]); + }), 128)), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("ul", QuickAccessvue_type_template_id_7b45bbd5_hoisted_7, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("li", { + class: "quick-access-category websiteCategory" + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('SitesManager_Sites')), 513), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.hasSitesSelector && _ctx.sites.length || _ctx.isLoading]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("li", { + class: "no-result" + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('MultiSites_LoadingWebsites')), 513), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.hasSitesSelector && _ctx.isLoading]]), (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.sites, function (site, index) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])((Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("li", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["result", { + selected: _ctx.numMenuItems + index === _ctx.searchIndex + }]), + onMouseenter: function onMouseenter($event) { + return _ctx.searchIndex = _ctx.numMenuItems + index; + }, + onClick: function onClick($event) { + return _ctx.selectSite(site.idsite); + }, + key: site.idsite + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + textContent: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(site.name) + }, null, 8, QuickAccessvue_type_template_id_7b45bbd5_hoisted_9)], 42, QuickAccessvue_type_template_id_7b45bbd5_hoisted_8)), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.hasSitesSelector && !_ctx.isLoading]]); + }), 128))]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("ul", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("li", QuickAccessvue_type_template_id_7b45bbd5_hoisted_10, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_HelpResources')), 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("li", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])([{ + selected: _ctx.searchIndex === 'help' + }, "quick-access-help"]), + onMouseenter: _cache[4] || (_cache[4] = function ($event) { + return _ctx.searchIndex = 'help'; + }) + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + href: "https://matomo.org?mtm_campaign=App_Help&mtm_source=Matomo_App&mtm_keyword=QuickSearch&s=".concat(encodeURIComponent(_ctx.searchTerm)), + target: "_blank" + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('CoreHome_SearchOnMatomo', _ctx.searchTerm)), 9, QuickAccessvue_type_template_id_7b45bbd5_hoisted_11)], 34)])], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.searchTerm && _ctx.searchActive]])], 512)), [[_directive_focus_anywhere_but_here, { + blur: _ctx.onBlur + }]]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/QuickAccess/QuickAccess.vue?vue&type=template&id=7b45bbd5 + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/QuickAccess/QuickAccess.vue?vue&type=script&lang=ts + + + + + + + + +function isElementInViewport(element) { + var rect = element.getBoundingClientRect(); + var $window = window.$(window); + return rect.top >= 0 && rect.left >= 0 && rect.bottom <= $window.height() && rect.right <= $window.width(); +} + +function scrollFirstElementIntoView(element) { + if (element && element.scrollIntoView) { + // make sure search is visible + element.scrollIntoView(); + } +} + +/* harmony default export */ var QuickAccessvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + directives: { + FocusAnywhereButHere: FocusAnywhereButHere, + FocusIf: FocusIf + }, + watch: { + searchActive: function searchActive(newValue) { + var root = this.$refs.root; + + if (!root || !root.parentElement) { + return; + } + + var classes = root.parentElement.classList; + classes.toggle('active', newValue); + classes.toggle('expanded', newValue); + } + }, + mounted: function mounted() { + var _this = this; + + var root = this.$refs.root; // TODO: temporary, remove after angularjs is removed. + // this is currently needed since angularjs will render a div, then vue will render a div + // within it, but the top controls and CSS expect to have certain CSS classes in the root + // element. + // same applies to above watch for searchActive() + + if (root && root.parentElement) { + root.parentElement.classList.add('quick-access', 'piwikSelector'); + } + + if (typeof window.initTopControls !== 'undefined' && window.initTopControls) { + window.initTopControls(); + } + + Matomo_Matomo.helper.registerShortcut('f', translate('CoreHome_ShortcutSearch'), function (event) { + if (event.altKey) { + return; + } + + event.preventDefault(); + scrollFirstElementIntoView(_this.$refs.root); + + _this.activateSearch(); + }); + }, + data: function data() { + var hasSegmentSelector = !!document.querySelector('.segmentEditorPanel'); + return { + menuItems: [], + numMenuItems: 0, + searchActive: false, + searchTerm: '', + searchIndex: 0, + menuIndexCounter: -1, + topMenuItems: null, + leftMenuItems: null, + segmentItems: null, + hasSegmentSelector: hasSegmentSelector, + sites: [], + isLoading: false + }; + }, + created: function created() { + this.searchMenu = debounce(this.searchMenu.bind(this)); + }, + computed: { + hasSitesSelector: function hasSitesSelector() { + return !!document.querySelector('.top_controls [piwik-siteselector]'); + }, + quickAccessTitle: function quickAccessTitle() { + var searchAreasTitle = ''; + var searchAreas = [translate('CoreHome_MenuEntries')]; + + if (this.hasSegmentSelector) { + searchAreas.push(translate('CoreHome_Segments')); + } + + if (this.hasSitesSelector) { + searchAreas.push(translate('SitesManager_Sites')); + } + + while (searchAreas.length) { + searchAreasTitle += searchAreas.shift(); + + if (searchAreas.length >= 2) { + searchAreasTitle += ', '; + } else if (searchAreas.length === 1) { + searchAreasTitle += " ".concat(translate('General_And'), " "); + } + } + + return translate('CoreHome_QuickAccessTitle', searchAreasTitle); + } + }, + emits: ['itemSelected', 'blur'], + methods: { + onKeypress: function onKeypress(event) { + var _this2 = this; + + var areSearchResultsDisplayed = this.searchTerm && this.searchActive; + var isTabKey = event.which === 9; + var isEscKey = event.which === 27; + + if (event.which === 38) { + this.highlightPreviousItem(); + event.preventDefault(); + } else if (event.which === 40) { + this.highlightNextItem(); + event.preventDefault(); + } else if (event.which === 13) { + this.clickQuickAccessMenuItem(); + } else if (isTabKey && areSearchResultsDisplayed) { + this.deactivateSearch(); + } else if (isEscKey && areSearchResultsDisplayed) { + this.deactivateSearch(); + } else { + setTimeout(function () { + _this2.searchActive = true; + + _this2.searchMenu(_this2.searchTerm); + }); + } + }, + highlightPreviousItem: function highlightPreviousItem() { + if (this.searchIndex - 1 < 0) { + this.searchIndex = 0; + } else { + this.searchIndex -= 1; + } + + this.makeSureSelectedItemIsInViewport(); + }, + highlightNextItem: function highlightNextItem() { + var numTotal = this.$refs.root.querySelectorAll('li.result').length; + + if (numTotal <= this.searchIndex + 1) { + this.searchIndex = numTotal - 1; + } else { + this.searchIndex += 1; + } + + this.makeSureSelectedItemIsInViewport(); + }, + clickQuickAccessMenuItem: function clickQuickAccessMenuItem() { + var _this3 = this; + + var selectedMenuElement = this.getCurrentlySelectedElement(); + + if (selectedMenuElement) { + setTimeout(function () { + selectedMenuElement.click(); + + _this3.$emit('itemSelected', selectedMenuElement); + }, 20); + } + }, + deactivateSearch: function deactivateSearch() { + this.searchTerm = ''; + this.searchActive = false; + + if (this.$refs.input) { + this.$refs.input.blur(); + } + }, + makeSureSelectedItemIsInViewport: function makeSureSelectedItemIsInViewport() { + var element = this.getCurrentlySelectedElement(); + + if (element && !isElementInViewport(element)) { + scrollFirstElementIntoView(element); + } + }, + getCurrentlySelectedElement: function getCurrentlySelectedElement() { + var results = this.$refs.root.querySelectorAll('li.result'); + + if (results && results.length && results.item(this.searchIndex)) { + return results.item(this.searchIndex); + } + + return undefined; + }, + searchMenu: function searchMenu(unprocessedSearchTerm) { + var _this4 = this; + + var searchTerm = unprocessedSearchTerm.toLowerCase(); + var index = -1; + var menuItemsIndex = {}; + var menuItems = []; + + var moveToCategory = function moveToCategory(theSubmenuItem) { + // force rerender of element to prevent weird side effects + var submenuItem = Object.assign({}, theSubmenuItem); // needed for proper highlighting with arrow keys + + index += 1; + submenuItem.menuIndex = index; + var category = submenuItem.category; + + if (!(category in menuItemsIndex)) { + menuItems.push({ + title: category, + items: [] + }); + menuItemsIndex[category] = menuItems.length - 1; + } + + var indexOfCategory = menuItemsIndex[category]; + menuItems[indexOfCategory].items.push(submenuItem); + }; + + this.resetSearchIndex(); + + if (this.hasSitesSelector) { + this.isLoading = true; + SiteSelector_SitesStore.searchSite(searchTerm).then(function (sites) { + if (sites) { + _this4.sites = sites; + } + }).finally(function () { + _this4.isLoading = false; + }); + } + + var menuItemMatches = function menuItemMatches(i) { + return i.name.toLowerCase().indexOf(searchTerm) !== -1 || i.category.toLowerCase().indexOf(searchTerm) !== -1; + }; // get the menu items on first search since this component can be mounted + // before the menus are + + + if (this.topMenuItems === null) { + this.topMenuItems = this.getTopMenuItems(); + } + + if (this.leftMenuItems === null) { + this.leftMenuItems = this.getLeftMenuItems(); + } + + if (this.segmentItems === null) { + this.segmentItems = this.getSegmentItems(); + } + + var topMenuItems = this.topMenuItems.filter(menuItemMatches); + var leftMenuItems = this.leftMenuItems.filter(menuItemMatches); + var segmentItems = this.segmentItems.filter(menuItemMatches); + topMenuItems.forEach(moveToCategory); + leftMenuItems.forEach(moveToCategory); + segmentItems.forEach(moveToCategory); + this.numMenuItems = topMenuItems.length + leftMenuItems.length + segmentItems.length; + this.menuItems = menuItems; + }, + resetSearchIndex: function resetSearchIndex() { + this.searchIndex = 0; + this.makeSureSelectedItemIsInViewport(); + }, + selectSite: function selectSite(idSite) { + SiteSelector_SitesStore.loadSite(idSite); + }, + selectMenuItem: function selectMenuItem(index) { + var target = document.querySelector("[quick_access='".concat(index, "']")); + + if (target) { + this.deactivateSearch(); + var href = target.getAttribute('href'); + + if (href && href.length > 10 && target && target.click) { + try { + target.click(); + } catch (e) { + window.$(target).click(); + } + } else { + // not sure why jquery is used here and above, but only sometimes. keeping for BC. + window.$(target).click(); + } + } + }, + onBlur: function onBlur() { + this.searchActive = false; + this.$emit('blur'); + }, + activateSearch: function activateSearch() { + this.searchActive = true; + }, + getTopMenuItems: function getTopMenuItems() { + var _this5 = this; + + var category = translate('CoreHome_Menu'); + var topMenuItems = []; + document.querySelectorAll('nav .sidenav li > a').forEach(function (element) { + var _element$textContent; + + var text = (_element$textContent = element.textContent) === null || _element$textContent === void 0 ? void 0 : _element$textContent.trim(); + + if (!text) { + var _element$getAttribute; + + text = (_element$getAttribute = element.getAttribute('title')) === null || _element$getAttribute === void 0 ? void 0 : _element$getAttribute.trim(); // possibly a icon, use title instead + } + + if (text) { + topMenuItems.push({ + name: text, + index: _this5.menuIndexCounter += 1, + category: category + }); + element.setAttribute('quick_access', "".concat(_this5.menuIndexCounter)); + } + }); + return topMenuItems; + }, + getLeftMenuItems: function getLeftMenuItems() { + var _this6 = this; + + var leftMenuItems = []; + document.querySelectorAll('#secondNavBar .menuTab').forEach(function (element) { + var _categoryElement$; + + var categoryElement = window.$(element).find('> .item'); + var category = ((_categoryElement$ = categoryElement[0]) === null || _categoryElement$ === void 0 ? void 0 : _categoryElement$.innerText.trim()) || ''; + + if (category && category.lastIndexOf('\n') !== -1) { + // remove "\n\nMenu" + category = category.substr(0, category.lastIndexOf('\n')).trim(); + } + + window.$(element).find('li .item').each(function (i, subElement) { + var _subElement$textConte; + + var text = (_subElement$textConte = subElement.textContent) === null || _subElement$textConte === void 0 ? void 0 : _subElement$textConte.trim(); + + if (text) { + leftMenuItems.push({ + name: text, + category: category, + index: _this6.menuIndexCounter += 1 + }); + subElement.setAttribute('quick_access', "".concat(_this6.menuIndexCounter)); + } + }); + }); + return leftMenuItems; + }, + getSegmentItems: function getSegmentItems() { + var _this7 = this; + + if (!this.hasSegmentSelector) { + return []; + } + + var category = translate('CoreHome_Segments'); + var segmentItems = []; + document.querySelectorAll('.segmentList [data-idsegment]').forEach(function (element) { + var _element$querySelecto, _element$querySelecto2; + + var text = (_element$querySelecto = element.querySelector('.segname')) === null || _element$querySelecto === void 0 ? void 0 : (_element$querySelecto2 = _element$querySelecto.textContent) === null || _element$querySelecto2 === void 0 ? void 0 : _element$querySelecto2.trim(); + + if (text) { + segmentItems.push({ + name: text, + category: category, + index: _this7.menuIndexCounter += 1 + }); + element.setAttribute('quick_access', "".concat(_this7.menuIndexCounter)); + } + }); + return segmentItems; + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/QuickAccess/QuickAccess.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/QuickAccess/QuickAccess.vue + + + +QuickAccessvue_type_script_lang_ts.render = QuickAccessvue_type_template_id_7b45bbd5_render + +/* harmony default export */ var QuickAccess = (QuickAccessvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/QuickAccess/QuickAccess.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var QuickAccess_adapter = (createAngularJsAdapter({ + component: QuickAccess, + directiveName: 'piwikQuickAccess', + events: { + itemSelected: function itemSelected(event, vm, scope, elem, attrs, controller, $timeout) { + $timeout(); + }, + blur: function blur(event, vm, scope) { + setTimeout(function () { + return scope.$apply(); + }); + } + } +})); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/FieldArray/FieldArray.vue?vue&type=template&id=66b76384 +function FieldArrayvue_type_template_id_66b76384_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + + +var FieldArrayvue_type_template_id_66b76384_hoisted_1 = { + class: "fieldArray form-group" +}; +var FieldArrayvue_type_template_id_66b76384_hoisted_2 = { + key: 0, + class: "fieldUiControl" +}; +var FieldArrayvue_type_template_id_66b76384_hoisted_3 = ["onClick", "title"]; +function FieldArrayvue_type_template_id_66b76384_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_Field = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("Field"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", FieldArrayvue_type_template_id_66b76384_hoisted_1, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.modelValue, function (item, index) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["fieldArrayTable multiple valign-wrapper", FieldArrayvue_type_template_id_66b76384_defineProperty({}, "fieldArrayTable".concat(index), true)]), + key: index + }, [_ctx.field.uiControl ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", FieldArrayvue_type_template_id_66b76384_hoisted_2, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + "full-width": true, + "model-value": item, + options: _ctx.field.availableValues, + "onUpdate:modelValue": function onUpdateModelValue($event) { + return _ctx.onEntryChange($event, index); + }, + placeholder: ' ', + uicontrol: _ctx.field.uiControl, + title: _ctx.field.title, + name: "".concat(_ctx.name, "-").concat(index), + "template-file": _ctx.field.templateFile, + component: _ctx.field.component + }, null, 8, ["model-value", "options", "onUpdate:modelValue", "uicontrol", "title", "name", "template-file", "component"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + onClick: function onClick($event) { + return _ctx.removeEntry(index); + }, + class: "icon-minus valign", + title: _ctx.translate('General_Remove') + }, null, 8, FieldArrayvue_type_template_id_66b76384_hoisted_3), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], index + 1 !== _ctx.modelValue.length]])], 2); + }), 128))]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/FieldArray/FieldArray.vue?vue&type=template&id=66b76384 + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/FieldArray/FieldArray.vue?vue&type=script&lang=ts +function FieldArrayvue_type_script_lang_ts_toConsumableArray(arr) { return FieldArrayvue_type_script_lang_ts_arrayWithoutHoles(arr) || FieldArrayvue_type_script_lang_ts_iterableToArray(arr) || FieldArrayvue_type_script_lang_ts_unsupportedIterableToArray(arr) || FieldArrayvue_type_script_lang_ts_nonIterableSpread(); } + +function FieldArrayvue_type_script_lang_ts_nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } + +function FieldArrayvue_type_script_lang_ts_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return FieldArrayvue_type_script_lang_ts_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return FieldArrayvue_type_script_lang_ts_arrayLikeToArray(o, minLen); } + +function FieldArrayvue_type_script_lang_ts_iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } + +function FieldArrayvue_type_script_lang_ts_arrayWithoutHoles(arr) { if (Array.isArray(arr)) return FieldArrayvue_type_script_lang_ts_arrayLikeToArray(arr); } + +function FieldArrayvue_type_script_lang_ts_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } + + + // async since this is a a recursive component + +var Field = useExternalPluginComponent('CorePluginsAdmin', 'Field'); +/* harmony default export */ var FieldArrayvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + modelValue: Array, + name: String, + field: Object + }, + components: { + Field: Field + }, + emits: ['update:modelValue'], + watch: { + modelValue: function modelValue(newValue) { + this.checkEmptyModelValue(newValue); + } + }, + mounted: function mounted() { + this.checkEmptyModelValue(this.modelValue); + }, + methods: { + checkEmptyModelValue: function checkEmptyModelValue(newValue) { + // make sure there is always an empty new value + if (!newValue || !newValue.length || newValue.slice(-1)[0] !== '') { + this.$emit('update:modelValue', [].concat(FieldArrayvue_type_script_lang_ts_toConsumableArray(newValue || []), [''])); + } + }, + onEntryChange: function onEntryChange(newValue, index) { + var newArrayValue = FieldArrayvue_type_script_lang_ts_toConsumableArray(this.modelValue || []); + + newArrayValue[index] = newValue; + this.$emit('update:modelValue', newArrayValue); + }, + removeEntry: function removeEntry(index) { + if (index > -1 && this.modelValue) { + var newValue = this.modelValue.filter(function (x, i) { + return i !== index; + }); + this.$emit('update:modelValue', newValue); + } + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/FieldArray/FieldArray.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/FieldArray/FieldArray.vue + + + +FieldArrayvue_type_script_lang_ts.render = FieldArrayvue_type_template_id_66b76384_render + +/* harmony default export */ var FieldArray = (FieldArrayvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/FieldArray/FieldArray.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var FieldArray_adapter = (createAngularJsAdapter({ + component: FieldArray, + require: '?ngModel', + scope: { + name: { + angularJsBind: '=' + }, + field: { + angularJsBind: '=' + } + }, + directiveName: 'matomoFieldArray', + events: { + 'update:modelValue': function updateModelValue(newValue, vm, scope, element, attrs, ngModel) { + if (newValue !== vm.modelValue) { + element.trigger('change', newValue); + + if (ngModel) { + ngModel.$setViewValue(newValue); + } + } + } + }, + postCreate: function postCreate(vm, scope, element, attrs, controller) { + var ngModel = controller; // setup ng-model mapping + + if (ngModel) { + ngModel.$setViewValue(vm.modelValue); + + ngModel.$render = function () { + if (window.angular.isString(ngModel.$viewValue)) { + vm.modelValue = JSON.parse(ngModel.$viewValue); + } else { + vm.modelValue = ngModel.$viewValue; + } + }; + } + } +})); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/MultiPairField/MultiPairField.vue?vue&type=template&id=b0d1c4e2 +function MultiPairFieldvue_type_template_id_b0d1c4e2_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + + +var MultiPairFieldvue_type_template_id_b0d1c4e2_hoisted_1 = { + class: "multiPairField form-group" +}; +var MultiPairFieldvue_type_template_id_b0d1c4e2_hoisted_2 = { + key: 1, + class: "fieldUiControl fieldUiControl2" +}; +var MultiPairFieldvue_type_template_id_b0d1c4e2_hoisted_3 = { + key: 2, + class: "fieldUiControl fieldUiControl3" +}; +var MultiPairFieldvue_type_template_id_b0d1c4e2_hoisted_4 = { + key: 3, + class: "fieldUiControl fieldUiControl4" +}; +var MultiPairFieldvue_type_template_id_b0d1c4e2_hoisted_5 = ["onClick", "title"]; +function MultiPairFieldvue_type_template_id_b0d1c4e2_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_Field = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("Field"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", MultiPairFieldvue_type_template_id_b0d1c4e2_hoisted_1, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.modelValue, function (item, index) { + var _ref; + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["multiPairFieldTable multiple valign-wrapper", (_ref = {}, MultiPairFieldvue_type_template_id_b0d1c4e2_defineProperty(_ref, "multiPairFieldTable".concat(index), true), MultiPairFieldvue_type_template_id_b0d1c4e2_defineProperty(_ref, "has".concat(_ctx.fieldCount, "Fields"), true), _ref)]), + key: index + }, [_ctx.field1 ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + key: 0, + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["fieldUiControl fieldUiControl1", { + hasMultiFields: _ctx.field1.type && _ctx.field2.type + }]) + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + "full-width": true, + modelValue: item[_ctx.field1.key], + "onUpdate:modelValue": [function ($event) { + return item[_ctx.field1.key] = $event; + }, function ($event) { + return _ctx.onEntryChange(index, _ctx.field1.key, $event); + }], + options: _ctx.field1.availableValues, + placeholder: ' ', + uicontrol: _ctx.field1.uiControl, + name: "".concat(_ctx.name, "-p1-").concat(index), + title: _ctx.field1.title, + "template-file": _ctx.field1.templateFile, + component: _ctx.field1.component + }, null, 8, ["modelValue", "onUpdate:modelValue", "options", "uicontrol", "name", "title", "template-file", "component"])], 2)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.field2 ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", MultiPairFieldvue_type_template_id_b0d1c4e2_hoisted_2, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + "full-width": true, + options: _ctx.field2.availableValues, + "onUpdate:modelValue": [function ($event) { + return _ctx.onEntryChange(index, _ctx.field2.key, $event); + }, function ($event) { + return item[_ctx.field2.key] = $event; + }], + modelValue: item[_ctx.field2.key], + placeholder: ' ', + uicontrol: _ctx.field2.uiControl, + name: "".concat(_ctx.name, "-p2-").concat(index), + title: _ctx.field2.title, + "template-file": _ctx.field2.templateFile, + component: _ctx.field2.component + }, null, 8, ["options", "onUpdate:modelValue", "modelValue", "uicontrol", "name", "title", "template-file", "component"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.field3 ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", MultiPairFieldvue_type_template_id_b0d1c4e2_hoisted_3, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + "full-width": true, + options: _ctx.field3.availableValues, + "onUpdate:modelValue": [function ($event) { + return _ctx.onEntryChange(index, _ctx.field3.key, $event); + }, function ($event) { + return item[_ctx.field3.key] = $event; + }], + modelValue: item[_ctx.field3.key], + placeholder: ' ', + uicontrol: _ctx.field3.uiControl, + title: _ctx.field3.title, + "template-file": _ctx.field3.templateFile, + component: _ctx.field3.component + }, null, 8, ["options", "onUpdate:modelValue", "modelValue", "uicontrol", "title", "template-file", "component"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.field4 ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", MultiPairFieldvue_type_template_id_b0d1c4e2_hoisted_4, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + "full-width": true, + options: _ctx.field4.availableValues, + "onUpdate:modelValue": [function ($event) { + return _ctx.onEntryChange(index, _ctx.field4.key, $event); + }, function ($event) { + return item[_ctx.field4.key] = $event; + }], + modelValue: item[_ctx.field4.key], + placeholder: ' ', + uicontrol: _ctx.field4.uiControl, + title: _ctx.field4.title, + "template-file": _ctx.field4.templateFile, + component: _ctx.field4.component + }, null, 8, ["options", "onUpdate:modelValue", "modelValue", "uicontrol", "title", "template-file", "component"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + onClick: function onClick($event) { + return _ctx.removeEntry(index); + }, + class: "icon-minus valign", + title: _ctx.translate('General_Remove') + }, null, 8, MultiPairFieldvue_type_template_id_b0d1c4e2_hoisted_5), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], index + 1 !== _ctx.modelValue.length]])], 2); + }), 128))]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MultiPairField/MultiPairField.vue?vue&type=template&id=b0d1c4e2 + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/MultiPairField/MultiPairField.vue?vue&type=script&lang=ts +function MultiPairFieldvue_type_script_lang_ts_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function MultiPairFieldvue_type_script_lang_ts_toConsumableArray(arr) { return MultiPairFieldvue_type_script_lang_ts_arrayWithoutHoles(arr) || MultiPairFieldvue_type_script_lang_ts_iterableToArray(arr) || MultiPairFieldvue_type_script_lang_ts_unsupportedIterableToArray(arr) || MultiPairFieldvue_type_script_lang_ts_nonIterableSpread(); } + +function MultiPairFieldvue_type_script_lang_ts_nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } + +function MultiPairFieldvue_type_script_lang_ts_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return MultiPairFieldvue_type_script_lang_ts_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return MultiPairFieldvue_type_script_lang_ts_arrayLikeToArray(o, minLen); } + +function MultiPairFieldvue_type_script_lang_ts_iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } + +function MultiPairFieldvue_type_script_lang_ts_arrayWithoutHoles(arr) { if (Array.isArray(arr)) return MultiPairFieldvue_type_script_lang_ts_arrayLikeToArray(arr); } + +function MultiPairFieldvue_type_script_lang_ts_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } + + + // async since this is a a recursive component + +var MultiPairFieldvue_type_script_lang_ts_Field = useExternalPluginComponent('CorePluginsAdmin', 'Field'); +/* harmony default export */ var MultiPairFieldvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + modelValue: Array, + name: String, + field1: Object, + field2: Object, + field3: Object, + field4: Object + }, + components: { + Field: MultiPairFieldvue_type_script_lang_ts_Field + }, + computed: { + fieldCount: function fieldCount() { + if (this.field1 && this.field2 && this.field3 && this.field4) { + return 4; + } + + if (this.field1 && this.field2 && this.field3) { + return 3; + } + + if (this.field1 && this.field2) { + return 2; + } + + if (this.field1) { + return 1; + } + + return 0; + } + }, + emits: ['update:modelValue'], + watch: { + modelValue: function modelValue(newValue) { + this.checkEmptyModelValue(newValue); + } + }, + mounted: function mounted() { + this.checkEmptyModelValue(this.modelValue); + }, + methods: { + checkEmptyModelValue: function checkEmptyModelValue(newValue) { + // make sure there is always an empty new value + if (!newValue || !newValue.length || this.isEmptyValue(newValue.slice(-1)[0])) { + this.$emit('update:modelValue', [].concat(MultiPairFieldvue_type_script_lang_ts_toConsumableArray(newValue || []), [this.makeEmptyValue()])); + } + }, + onEntryChange: function onEntryChange(index, key, newValue) { + var newWholeValue = MultiPairFieldvue_type_script_lang_ts_toConsumableArray(this.modelValue); + + newWholeValue[index] = Object.assign(Object.assign({}, newWholeValue[index]), {}, MultiPairFieldvue_type_script_lang_ts_defineProperty({}, key, newValue)); + this.$emit('update:modelValue', newWholeValue); + }, + removeEntry: function removeEntry(index) { + if (index > -1 && this.modelValue) { + var newValue = this.modelValue.filter(function (x, i) { + return i !== index; + }); + this.$emit('update:modelValue', newValue); + } + }, + isEmptyValue: function isEmptyValue(value) { + var fieldCount = this.fieldCount; + + if (fieldCount === 4) { + if (!value[this.field1.key] && !value[this.field2.key] && !value[this.field3.key] && !value[this.field4.key]) { + return false; + } + } else if (fieldCount === 3) { + if (!value[this.field1.key] && !value[this.field2.key] && !value[this.field3.key]) { + return false; + } + } else if (fieldCount === 2) { + if (!value[this.field1.key] && !value[this.field2.key]) { + return false; + } + } else if (fieldCount === 1) { + if (!value[this.field1.key]) { + return false; + } + } + + return true; + }, + makeEmptyValue: function makeEmptyValue() { + var result = {}; + + if (this.field1 && this.field1.key) { + result[this.field1.key] = ''; + } + + if (this.field2 && this.field2.key) { + result[this.field2.key] = ''; + } + + if (this.field3 && this.field3.key) { + result[this.field3.key] = ''; + } + + if (this.field4 && this.field4.key) { + result[this.field4.key] = ''; + } + + return result; + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MultiPairField/MultiPairField.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MultiPairField/MultiPairField.vue + + + +MultiPairFieldvue_type_script_lang_ts.render = MultiPairFieldvue_type_template_id_b0d1c4e2_render + +/* harmony default export */ var MultiPairField = (MultiPairFieldvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MultiPairField/MultiPairField.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var MultiPairField_adapter = (createAngularJsAdapter({ + component: MultiPairField, + require: '?ngModel', + scope: { + name: { + angularJsBind: '=' + }, + field1: { + angularJsBind: '=' + }, + field2: { + angularJsBind: '=' + }, + field3: { + angularJsBind: '=' + }, + field4: { + angularJsBind: '=' + } + }, + directiveName: 'matomoMultiPairField', + events: { + 'update:modelValue': function updateModelValue(newValue, vm, scope, element, attrs, ngModel) { + if (newValue !== vm.modelValue) { + element.trigger('change', newValue); + + if (ngModel) { + ngModel.$setViewValue(newValue); + } + } + } + }, + postCreate: function postCreate(vm, scope, element, attrs, controller) { + var ngModel = controller; // setup ng-model mapping + + if (ngModel) { + ngModel.$setViewValue(vm.modelValue); + + ngModel.$render = function () { + if (window.angular.isString(ngModel.$viewValue)) { + vm.modelValue = JSON.parse(ngModel.$viewValue); + } else { + vm.modelValue = ngModel.$viewValue; + } + }; + } + } +})); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/PeriodSelector/PeriodSelector.vue?vue&type=template&id=0115e905 + +var PeriodSelectorvue_type_template_id_0115e905_hoisted_1 = { + ref: "root", + class: "periodSelector piwikSelector" +}; +var PeriodSelectorvue_type_template_id_0115e905_hoisted_2 = ["title"]; + +var PeriodSelectorvue_type_template_id_0115e905_hoisted_3 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + class: "icon icon-calendar" +}, null, -1); + +var PeriodSelectorvue_type_template_id_0115e905_hoisted_4 = { + id: "periodMore", + class: "dropdown" +}; +var PeriodSelectorvue_type_template_id_0115e905_hoisted_5 = { + class: "flex" +}; +var PeriodSelectorvue_type_template_id_0115e905_hoisted_6 = { + key: 0, + class: "period-date" +}; +var PeriodSelectorvue_type_template_id_0115e905_hoisted_7 = { + class: "period-type" +}; +var PeriodSelectorvue_type_template_id_0115e905_hoisted_8 = { + id: "otherPeriods" +}; +var PeriodSelectorvue_type_template_id_0115e905_hoisted_9 = ["onDblclick", "title"]; +var PeriodSelectorvue_type_template_id_0115e905_hoisted_10 = ["id", "checked", "onChange", "onDblclick"]; +var PeriodSelectorvue_type_template_id_0115e905_hoisted_11 = { + key: 0, + class: "compare-checkbox" +}; +var PeriodSelectorvue_type_template_id_0115e905_hoisted_12 = { + id: "comparePeriodToDropdown" +}; +var PeriodSelectorvue_type_template_id_0115e905_hoisted_13 = { + key: 1, + class: "compare-date-range" +}; +var PeriodSelectorvue_type_template_id_0115e905_hoisted_14 = { + id: "comparePeriodStartDate" +}; + +var PeriodSelectorvue_type_template_id_0115e905_hoisted_15 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + class: "compare-dates-separator" +}, null, -1); + +var _hoisted_16 = { + id: "comparePeriodEndDate" +}; +var _hoisted_17 = { + class: "apply-button-container" +}; +var _hoisted_18 = ["disabled", "value"]; +var _hoisted_19 = { + key: 2, + id: "ajaxLoadingCalendar" +}; +var _hoisted_20 = { + class: "loadingSegment" +}; +function PeriodSelectorvue_type_template_id_0115e905_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_DateRangePicker = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("DateRangePicker"); + + var _component_PeriodDatePicker = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("PeriodDatePicker"); + + var _component_Field = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("Field"); + + var _component_ActivityIndicator = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("ActivityIndicator"); + + var _directive_expand_on_click = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("expand-on-click"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])((Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", PeriodSelectorvue_type_template_id_0115e905_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + ref: "title", + id: "date", + class: "title", + tabindex: "-1", + title: _ctx.translate('General_ChooseDate', _ctx.currentlyViewingText) + }, [PeriodSelectorvue_type_template_id_0115e905_hoisted_3, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(" " + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.currentlyViewingText), 1)], 8, PeriodSelectorvue_type_template_id_0115e905_hoisted_2), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PeriodSelectorvue_type_template_id_0115e905_hoisted_4, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PeriodSelectorvue_type_template_id_0115e905_hoisted_5, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_DateRangePicker, { + class: "period-range", + "start-date": _ctx.startRangeDate, + "end-date": _ctx.endRangeDate, + onRangeChange: _cache[0] || (_cache[0] = function ($event) { + return _ctx.onRangeChange($event.start, $event.end); + }), + onSubmit: _cache[1] || (_cache[1] = function ($event) { + return _ctx.onApplyClicked(); + }) + }, null, 8, ["start-date", "end-date"]), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.selectedPeriod === 'range']]), _ctx.selectedPeriod !== 'range' ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", PeriodSelectorvue_type_template_id_0115e905_hoisted_6, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_PeriodDatePicker, { + id: "datepicker", + period: _ctx.selectedPeriod, + date: _ctx.periodValue === _ctx.selectedPeriod ? _ctx.dateValue : null, + onSelect: _cache[2] || (_cache[2] = function ($event) { + return _ctx.setPiwikPeriodAndDate(_ctx.selectedPeriod, $event.date); + }) + }, null, 8, ["period", "date"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PeriodSelectorvue_type_template_id_0115e905_hoisted_7, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h6", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_Period')), 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PeriodSelectorvue_type_template_id_0115e905_hoisted_8, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.periodsFiltered, function (period) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("p", { + key: period + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("label", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])({ + 'selected-period-label': period === _ctx.selectedPeriod + }), + onDblclick: function onDblclick($event) { + return _ctx.changeViewedPeriod(period); + }, + title: period === _ctx.periodValue ? '' : _ctx.translate('General_DoubleClickToChangePeriod') + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { + type: "radio", + name: "period", + id: "period_id_".concat(period), + "onUpdate:modelValue": _cache[3] || (_cache[3] = function ($event) { + return _ctx.selectedPeriod = $event; + }), + checked: _ctx.selectedPeriod === period, + onChange: function onChange($event) { + return _ctx.selectedPeriod = period; + }, + onDblclick: function onDblclick($event) { + return _ctx.changeViewedPeriod(period); + } + }, null, 40, PeriodSelectorvue_type_template_id_0115e905_hoisted_10), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vModelRadio"], _ctx.selectedPeriod]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.getPeriodDisplayText(period)), 1)], 42, PeriodSelectorvue_type_template_id_0115e905_hoisted_9)]); + }), 128))])])]), _ctx.isComparisonEnabled ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", PeriodSelectorvue_type_template_id_0115e905_hoisted_11, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("label", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { + id: "comparePeriodTo", + type: "checkbox", + "onUpdate:modelValue": _cache[4] || (_cache[4] = function ($event) { + return _ctx.isComparing = $event; + }) + }, null, 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vModelCheckbox"], _ctx.isComparing]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_CompareTo')), 1)]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PeriodSelectorvue_type_template_id_0115e905_hoisted_12, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + modelValue: _ctx.comparePeriodType, + "onUpdate:modelValue": _cache[5] || (_cache[5] = function ($event) { + return _ctx.comparePeriodType = $event; + }), + style: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeStyle"])({ + 'visibility': _ctx.isComparing ? 'visible' : 'hidden' + }), + name: 'comparePeriodToDropdown', + uicontrol: 'select', + options: _ctx.comparePeriodDropdownOptions, + "full-width": true, + disabled: !_ctx.isComparing + }, null, 8, ["modelValue", "style", "options", "disabled"])])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.isComparing && _ctx.comparePeriodType === 'custom' ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", PeriodSelectorvue_type_template_id_0115e905_hoisted_13, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", PeriodSelectorvue_type_template_id_0115e905_hoisted_14, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + modelValue: _ctx.compareStartDate, + "onUpdate:modelValue": _cache[6] || (_cache[6] = function ($event) { + return _ctx.compareStartDate = $event; + }), + name: 'comparePeriodStartDate', + uicontrol: 'text', + "full-width": true, + title: _ctx.translate('CoreHome_StartDate'), + placeholder: 'YYYY-MM-DD' + }, null, 8, ["modelValue", "title"])])]), PeriodSelectorvue_type_template_id_0115e905_hoisted_15, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", _hoisted_16, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + modelValue: _ctx.compareEndDate, + "onUpdate:modelValue": _cache[7] || (_cache[7] = function ($event) { + return _ctx.compareEndDate = $event; + }), + name: 'comparePeriodEndDate', + uicontrol: 'text', + "full-width": true, + title: _ctx.translate('CoreHome_EndDate'), + placeholder: 'YYYY-MM-DD' + }, null, 8, ["modelValue", "title"])])])])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", _hoisted_17, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { + type: "submit", + id: "calendarApply", + class: "btn", + onClick: _cache[8] || (_cache[8] = function ($event) { + return _ctx.onApplyClicked(); + }), + disabled: !_ctx.isApplyEnabled(), + value: _ctx.translate('General_Apply') + }, null, 8, _hoisted_18)]), _ctx.isLoadingNewPage ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", _hoisted_19, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_ActivityIndicator, { + loading: true + }), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", _hoisted_20, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('SegmentEditor_LoadingSegmentedDataMayTakeSomeTime')), 1)])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)])], 512)), [[_directive_expand_on_click, { + expander: 'title' + }]]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/PeriodSelector/PeriodSelector.vue?vue&type=template&id=0115e905 + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/ActivityIndicator/ActivityIndicator.vue?vue&type=template&id=7c5fe406 + +var ActivityIndicatorvue_type_template_id_7c5fe406_hoisted_1 = { + class: "loadingPiwik" +}; + +var ActivityIndicatorvue_type_template_id_7c5fe406_hoisted_2 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("img", { + src: "plugins/Morpheus/images/loading-blue.gif", + alt: "" +}, null, -1); + +function ActivityIndicatorvue_type_template_id_7c5fe406_render(_ctx, _cache, $props, $setup, $data, $options) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])((Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", ActivityIndicatorvue_type_template_id_7c5fe406_hoisted_1, [ActivityIndicatorvue_type_template_id_7c5fe406_hoisted_2, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.loadingMessage), 1)], 512)), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.loading]]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ActivityIndicator/ActivityIndicator.vue?vue&type=template&id=7c5fe406 + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/ActivityIndicator/ActivityIndicator.vue?vue&type=script&lang=ts + + +/* harmony default export */ var ActivityIndicatorvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + loading: { + type: Boolean, + required: true, + default: false + }, + loadingMessage: { + type: String, + required: false, + default: translate('General_LoadingData') + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ActivityIndicator/ActivityIndicator.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ActivityIndicator/ActivityIndicator.vue + + + +ActivityIndicatorvue_type_script_lang_ts.render = ActivityIndicatorvue_type_template_id_7c5fe406_render + +/* harmony default export */ var ActivityIndicator = (ActivityIndicatorvue_type_script_lang_ts); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/PeriodSelector/PeriodSelector.vue?vue&type=script&lang=ts +function PeriodSelectorvue_type_script_lang_ts_slicedToArray(arr, i) { return PeriodSelectorvue_type_script_lang_ts_arrayWithHoles(arr) || PeriodSelectorvue_type_script_lang_ts_iterableToArrayLimit(arr, i) || PeriodSelectorvue_type_script_lang_ts_unsupportedIterableToArray(arr, i) || PeriodSelectorvue_type_script_lang_ts_nonIterableRest(); } + +function PeriodSelectorvue_type_script_lang_ts_nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } + +function PeriodSelectorvue_type_script_lang_ts_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return PeriodSelectorvue_type_script_lang_ts_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return PeriodSelectorvue_type_script_lang_ts_arrayLikeToArray(o, minLen); } + +function PeriodSelectorvue_type_script_lang_ts_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } + +function PeriodSelectorvue_type_script_lang_ts_iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } + +function PeriodSelectorvue_type_script_lang_ts_arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } + + + + + + + + + + + + +var PeriodSelectorvue_type_script_lang_ts_Field = useExternalPluginComponent('CorePluginsAdmin', 'Field'); +var NBSP = Matomo_Matomo.helper.htmlDecode(' '); +var COMPARE_PERIOD_OPTIONS = [{ + key: 'custom', + value: translate('General_Custom') +}, { + key: 'previousPeriod', + value: translate('General_PreviousPeriod').replace(/\s+/, NBSP) +}, { + key: 'previousYear', + value: translate('General_PreviousYear').replace(/\s+/, NBSP) +}]; +var PeriodSelectorvue_type_script_lang_ts_piwikMinDate = new Date(Matomo_Matomo.minDateYear, Matomo_Matomo.minDateMonth - 1, Matomo_Matomo.minDateDay); +var PeriodSelectorvue_type_script_lang_ts_piwikMaxDate = new Date(Matomo_Matomo.maxDateYear, Matomo_Matomo.maxDateMonth - 1, Matomo_Matomo.maxDateDay); + +function isValidDate(d) { + if (Object.prototype.toString.call(d) !== '[object Date]') { + return false; + } + + return !Number.isNaN(d.getTime()); +} + +/* harmony default export */ var PeriodSelectorvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + periods: Array + }, + components: { + DateRangePicker: DateRangePicker, + PeriodDatePicker: PeriodDatePicker, + Field: PeriodSelectorvue_type_script_lang_ts_Field, + ActivityIndicator: ActivityIndicator + }, + directives: { + ExpandOnClick: ExpandOnClick + }, + data: function data() { + var selectedPeriod = src_MatomoUrl_MatomoUrl.parsed.value.period; + return { + comparePeriodDropdownOptions: COMPARE_PERIOD_OPTIONS, + periodValue: selectedPeriod, + dateValue: null, + selectedPeriod: selectedPeriod, + startRangeDate: null, + endRangeDate: null, + isRangeValid: null, + isLoadingNewPage: false, + isComparing: null, + comparePeriodType: 'previousPeriod', + compareStartDate: '', + compareEndDate: '' + }; + }, + mounted: function mounted() { + var _this = this; + + Matomo_Matomo.on('hidePeriodSelector', function () { + window.$(_this.$refs.root).hide(); + }); // some widgets might hide the period selector using the event above, so ensure it's + // shown again when switching the page + + Matomo_Matomo.on('piwikPageChange', function () { + window.$(_this.$refs.root).show(); + }); + this.updateSelectedValuesFromHash(); + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["watch"])(function () { + return src_MatomoUrl_MatomoUrl.parsed.value; + }, this.updateSelectedValuesFromHash); + this.isComparing = Comparisons_store_instance.isComparingPeriods(); + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["watch"])(function () { + return Comparisons_store_instance.isComparingPeriods(); + }, function (newVal) { + _this.isComparing = newVal; + }); + window.initTopControls(); // must be called when a top control changes width + + this.handleZIndexPositionRelativeCompareDropdownIssue(); + }, + computed: { + currentlyViewingText: function currentlyViewingText() { + var date; + + if (this.periodValue === 'range') { + if (!this.startRangeDate || !this.endRangeDate) { + return translate('General_Error'); + } + + date = "".concat(this.startRangeDate, ",").concat(this.endRangeDate); + } else { + if (!this.dateValue) { + return translate('General_Error'); + } + + date = format(this.dateValue); + } + + try { + return Periods_Periods.parse(this.periodValue, date).getPrettyString(); + } catch (e) { + return translate('General_Error'); + } + }, + isComparisonEnabled: function isComparisonEnabled() { + return Comparisons_store_instance.isComparisonEnabled(); + }, + periodsFiltered: function periodsFiltered() { + return (this.periods || []).filter(function (periodLabel) { + return Periods_Periods.isRecognizedPeriod(periodLabel); + }); + }, + selectedComparisonParams: function selectedComparisonParams() { + if (!this.isComparing) { + return {}; + } + + if (this.comparePeriodType === 'custom') { + return { + comparePeriods: ['range'], + compareDates: ["".concat(this.compareStartDate, ",").concat(this.compareEndDate)] + }; + } + + if (this.comparePeriodType === 'previousPeriod') { + return { + comparePeriods: [this.selectedPeriod], + compareDates: [this.previousPeriodDateToSelectedPeriod] + }; + } + + if (this.comparePeriodType === 'previousYear') { + var dateStr = this.selectedPeriod === 'range' ? "".concat(this.startRangeDate, ",").concat(this.endRangeDate) : format(this.dateValue); + var currentDateRange = Periods_Periods.parse(this.selectedPeriod, dateStr).getDateRange(); + currentDateRange[0].setFullYear(currentDateRange[0].getFullYear() - 1); + currentDateRange[1].setFullYear(currentDateRange[1].getFullYear() - 1); + + if (this.selectedPeriod === 'range') { + return { + comparePeriods: ['range'], + compareDates: ["".concat(format(currentDateRange[0]), ",").concat(format(currentDateRange[1]))] + }; + } + + return { + comparePeriods: [this.selectedPeriod], + compareDates: [format(currentDateRange[0])] + }; + } + + console.warn("Unknown compare period type: ".concat(this.comparePeriodType)); + return {}; + }, + previousPeriodDateToSelectedPeriod: function previousPeriodDateToSelectedPeriod() { + if (this.selectedPeriod === 'range') { + var currentStartRange = parseDate(this.startRangeDate); + var currentEndRange = parseDate(this.endRangeDate); + var newEndDate = Range_RangePeriod.getLastNRange('day', 2, currentStartRange).startDate; + var rangeSize = Math.floor((currentEndRange.valueOf() - currentStartRange.valueOf()) / 86400000); + var newRange = Range_RangePeriod.getLastNRange('day', 1 + rangeSize, newEndDate); + return "".concat(format(newRange.startDate), ",").concat(format(newRange.endDate)); + } + + var newStartDate = Range_RangePeriod.getLastNRange(this.selectedPeriod, 2, this.dateValue).startDate; + return format(newStartDate); + }, + selectedDateString: function selectedDateString() { + if (this.selectedPeriod === 'range') { + var dateFrom = this.startRangeDate; + var dateTo = this.endRangeDate; + var oDateFrom = parseDate(dateFrom); + var oDateTo = parseDate(dateTo); + + if (!isValidDate(oDateFrom) || !isValidDate(oDateTo) || oDateFrom > oDateTo) { + // TODO: use a notification instead? + window.$('#alert').find('h2').text(translate('General_InvalidDateRange')); + Matomo_Matomo.helper.modalConfirm('#alert', {}); + return null; + } + + return "".concat(dateFrom, ",").concat(dateTo); + } + + return format(this.dateValue); + } + }, + methods: { + handleZIndexPositionRelativeCompareDropdownIssue: function handleZIndexPositionRelativeCompareDropdownIssue() { + var $element = window.$(this.$refs.root); + $element.on('focus', '#comparePeriodToDropdown .select-dropdown', function () { + $element.addClass('compare-dropdown-open'); + }).on('blur', '#comparePeriodToDropdown .select-dropdown', function () { + $element.removeClass('compare-dropdown-open'); + }); + }, + changeViewedPeriod: function changeViewedPeriod(period) { + // only change period if it's different from what's being shown currently + if (period === this.periodValue) { + return; + } // can't just change to a range period, w/o setting two new dates + + + if (period === 'range') { + return; + } + + this.setPiwikPeriodAndDate(period, this.dateValue); + }, + setPiwikPeriodAndDate: function setPiwikPeriodAndDate(period, date) { + this.periodValue = period; + this.selectedPeriod = period; + this.dateValue = date; + var currentDateString = format(date); + this.setRangeStartEndFromPeriod(period, currentDateString); + this.propagateNewUrlParams(currentDateString, this.selectedPeriod); + window.initTopControls(); + }, + propagateNewUrlParams: function propagateNewUrlParams(date, period) { + var compareParams = this.selectedComparisonParams; + var baseParams; + + if (Matomo_Matomo.helper.isAngularRenderingThePage()) { + this.closePeriodSelector(); + baseParams = src_MatomoUrl_MatomoUrl.hashParsed.value; + } else { + this.isLoadingNewPage = true; + baseParams = src_MatomoUrl_MatomoUrl.parsed.value; + } // get params without comparePeriods/compareSegments/compareDates + + + var paramsWithoutCompare = Object.assign({}, baseParams); + delete paramsWithoutCompare.comparePeriods; + delete paramsWithoutCompare.compareDates; + src_MatomoUrl_MatomoUrl.updateLocation(Object.assign(Object.assign({}, paramsWithoutCompare), {}, { + date: date, + period: period + }, compareParams)); + }, + onApplyClicked: function onApplyClicked() { + if (this.selectedPeriod === 'range') { + var dateString = this.selectedDateString; + + if (!dateString) { + return; + } + + this.periodValue = 'range'; + this.propagateNewUrlParams(dateString, 'range'); + return; + } + + this.setPiwikPeriodAndDate(this.selectedPeriod, this.dateValue); + }, + updateSelectedValuesFromHash: function updateSelectedValuesFromHash() { + var date = src_MatomoUrl_MatomoUrl.parsed.value.date; + var period = src_MatomoUrl_MatomoUrl.parsed.value.period; + this.periodValue = period; + this.selectedPeriod = period; + this.dateValue = null; + this.startRangeDate = null; + this.endRangeDate = null; + + try { + Periods_Periods.parse(period, date); + } catch (e) { + return; + } + + if (period === 'range') { + var periodObj = Periods_Periods.get(period).parse(date); + + var _periodObj$getDateRan = periodObj.getDateRange(), + _periodObj$getDateRan2 = PeriodSelectorvue_type_script_lang_ts_slicedToArray(_periodObj$getDateRan, 2), + startDate = _periodObj$getDateRan2[0], + endDate = _periodObj$getDateRan2[1]; + + this.dateValue = startDate; + this.startRangeDate = format(startDate); + this.endRangeDate = format(endDate); + } else { + this.dateValue = parseDate(date); + this.setRangeStartEndFromPeriod(period, date); + } + }, + setRangeStartEndFromPeriod: function setRangeStartEndFromPeriod(period, dateStr) { + var dateRange = Periods_Periods.parse(period, dateStr).getDateRange(); + this.startRangeDate = format(dateRange[0] < PeriodSelectorvue_type_script_lang_ts_piwikMinDate ? PeriodSelectorvue_type_script_lang_ts_piwikMinDate : dateRange[0]); + this.endRangeDate = format(dateRange[1] > PeriodSelectorvue_type_script_lang_ts_piwikMaxDate ? PeriodSelectorvue_type_script_lang_ts_piwikMaxDate : dateRange[1]); + }, + getPeriodDisplayText: function getPeriodDisplayText(periodLabel) { + return Periods_Periods.get(periodLabel).getDisplayText(); + }, + onRangeChange: function onRangeChange(start, end) { + if (!start || !end) { + this.isRangeValid = false; + return; + } + + this.isRangeValid = true; + this.startRangeDate = start; + this.endRangeDate = end; + }, + isApplyEnabled: function isApplyEnabled() { + if (this.selectedPeriod === 'range' && !this.isRangeValid) { + return false; + } + + if (this.isComparing && this.comparePeriodType === 'custom' && !this.isCompareRangeValid()) { + return false; + } + + return true; + }, + closePeriodSelector: function closePeriodSelector() { + this.$refs.root.classList.remove('expanded'); + }, + isCompareRangeValid: function isCompareRangeValid() { + try { + parseDate(this.compareStartDate); + } catch (e) { + return false; + } + + try { + parseDate(this.compareEndDate); + } catch (e) { + return false; + } + + return true; + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/PeriodSelector/PeriodSelector.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/PeriodSelector/PeriodSelector.vue + + + +PeriodSelectorvue_type_script_lang_ts.render = PeriodSelectorvue_type_template_id_0115e905_render + +/* harmony default export */ var PeriodSelector = (PeriodSelectorvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/PeriodSelector/PeriodSelector.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var PeriodSelector_adapter = (createAngularJsAdapter({ + component: PeriodSelector, + scope: { + periods: { + angularJsBind: '<' + } + }, + directiveName: 'piwikPeriodSelector' +})); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/ReportingMenu/ReportingMenu.vue?vue&type=template&id=0f6008b2 + +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_1 = { + class: "reportingMenu" +}; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_2 = ["aria-label"]; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_3 = ["onClick"]; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_4 = { + class: "hidden" +}; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_5 = { + role: "menu" +}; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_6 = ["href", "onClick", "title"]; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_7 = ["href", "onClick"]; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_8 = ["onClick"]; + +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_9 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + class: "icon-help" +}, null, -1); + +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_10 = [ReportingMenuvue_type_template_id_0f6008b2_hoisted_9]; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_11 = { + id: "mobile-left-menu", + class: "sidenav hide-on-large-only" +}; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_12 = { + class: "collapsible collapsible-accordion" +}; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_13 = { + class: "collapsible-header" +}; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_14 = { + class: "collapsible-body" +}; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_15 = { + key: 0 +}; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_16 = ["onClick", "href"]; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_17 = { + key: 1 +}; +var ReportingMenuvue_type_template_id_0f6008b2_hoisted_18 = ["onClick", "href"]; +function ReportingMenuvue_type_template_id_0f6008b2_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_MenuItemsDropdown = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("MenuItemsDropdown"); + + var _directive_side_nav = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("side-nav"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", ReportingMenuvue_type_template_id_0f6008b2_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("ul", { + class: "navbar hide-on-med-and-down", + role: "menu", + "aria-label": _ctx.translate('CoreHome_MainNavigation') + }, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.menu, function (category) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("li", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["menuTab", { + 'active': category.id === _ctx.activeCategory + }]), + role: "menuitem", + key: category.id + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + class: "item", + tabindex: "5", + href: "", + onClick: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withModifiers"])(function ($event) { + return _ctx.loadCategory(category); + }, ["prevent"]) + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])("menu-icon ".concat(category.icon ? category.icon : 'icon-arrow-right')) + }, null, 2), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(category.name) + " ", 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", ReportingMenuvue_type_template_id_0f6008b2_hoisted_4, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('CoreHome_Menu')), 1)], 8, ReportingMenuvue_type_template_id_0f6008b2_hoisted_3), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("ul", ReportingMenuvue_type_template_id_0f6008b2_hoisted_5, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(category.subcategories, function (subcategory) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("li", { + role: "menuitem", + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])({ + 'active': (subcategory.id === _ctx.displayedSubcategory || subcategory.isGroup && _ctx.activeSubsubcategory === _ctx.displayedSubcategory) && category.id === _ctx.displayedCategory + }), + key: subcategory.id + }, [subcategory.isGroup ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createBlock"])(_component_MenuItemsDropdown, { + key: 0, + "show-search": true, + "menu-title": _ctx.htmlEntities(subcategory.name) + }, { + default: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withCtx"])(function () { + return [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(subcategory.subcategories, function (subcat) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["item", { + active: subcat.id === _ctx.activeSubsubcategory && subcategory.id === _ctx.displayedSubcategory && category.id === _ctx.displayedCategory + }]), + tabindex: "5", + href: "#?".concat(_ctx.makeUrl(category, subcat)), + onClick: function onClick($event) { + return _ctx.loadSubcategory(category, subcat, $event); + }, + title: subcat.tooltip, + key: subcat.id + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(subcat.name), 11, ReportingMenuvue_type_template_id_0f6008b2_hoisted_6); + }), 128))]; + }), + _: 2 + }, 1032, ["menu-title"])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), !subcategory.isGroup ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { + key: 1, + href: "#?".concat(_ctx.makeUrl(category, subcategory)), + class: "item", + onClick: function onClick($event) { + return _ctx.loadSubcategory(category, subcategory, $event); + } + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(subcategory.name), 9, ReportingMenuvue_type_template_id_0f6008b2_hoisted_7)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), subcategory.help ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { + key: 2, + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["item-help-icon", { + active: _ctx.helpShownCategory && _ctx.helpShownCategory.subcategory === subcategory.id && _ctx.helpShownCategory.category === category.id && subcategory.help + }]), + tabindex: "5", + href: "javascript:", + onClick: function onClick($event) { + return _ctx.showHelp(category, subcategory, $event); + } + }, ReportingMenuvue_type_template_id_0f6008b2_hoisted_10, 10, ReportingMenuvue_type_template_id_0f6008b2_hoisted_8)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)], 2); + }), 128))])], 2); + }), 128))], 8, ReportingMenuvue_type_template_id_0f6008b2_hoisted_2), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("ul", ReportingMenuvue_type_template_id_0f6008b2_hoisted_11, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.menu, function (category) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("li", { + class: "no-padding", + key: category.id + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("ul", ReportingMenuvue_type_template_id_0f6008b2_hoisted_12, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("li", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", ReportingMenuvue_type_template_id_0f6008b2_hoisted_13, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("i", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(category.icon ? category.icon : 'icon-arrow-bottom') + }, null, 2), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(category.name), 1)]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ReportingMenuvue_type_template_id_0f6008b2_hoisted_14, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("ul", null, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(category.subcategories, function (subcategory) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("li", { + key: subcategory.id + }, [subcategory.isGroup ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("span", ReportingMenuvue_type_template_id_0f6008b2_hoisted_15, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(subcategory.subcategories, function (subcat) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { + onClick: function onClick($event) { + return _ctx.loadSubcategory(category, subcat); + }, + href: "#?".concat(_ctx.makeUrl(category, subcat)), + key: subcat.id + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(subcat.name), 9, ReportingMenuvue_type_template_id_0f6008b2_hoisted_16); + }), 128))])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), !subcategory.isGroup ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("span", ReportingMenuvue_type_template_id_0f6008b2_hoisted_17, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + onClick: function onClick($event) { + return _ctx.loadSubcategory(category, subcategory); + }, + href: "#?".concat(_ctx.makeUrl(category, subcategory)) + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(subcategory.name), 9, ReportingMenuvue_type_template_id_0f6008b2_hoisted_18)])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)]); + }), 128))])])])], 512), [[_directive_side_nav, { + activator: _ctx.sideNavActivator + }]])]); + }), 128))])]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingMenu/ReportingMenu.vue?vue&type=template&id=0f6008b2 + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Notification/Notification.vue?vue&type=template&id=52166f8a + +var Notificationvue_type_template_id_52166f8a_hoisted_1 = { + key: 0 +}; +var Notificationvue_type_template_id_52166f8a_hoisted_2 = ["data-notification-instance-id"]; +var Notificationvue_type_template_id_52166f8a_hoisted_3 = { + key: 1 +}; +var Notificationvue_type_template_id_52166f8a_hoisted_4 = { + class: "notification-body" +}; +var Notificationvue_type_template_id_52166f8a_hoisted_5 = ["innerHTML"]; +var Notificationvue_type_template_id_52166f8a_hoisted_6 = { + key: 1 +}; +function Notificationvue_type_template_id_52166f8a_render(_ctx, _cache, $props, $setup, $data, $options) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Transition"], { + name: _ctx.type === 'toast' ? 'slow-fade-out' : undefined, + onAfterLeave: _cache[1] || (_cache[1] = function ($event) { + return _ctx.toastClosed(); + }) + }, { + default: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withCtx"])(function () { + return [!_ctx.deleted ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", Notificationvue_type_template_id_52166f8a_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Transition"], { + name: _ctx.type === 'toast' ? 'toast-slide-up' : undefined, + appear: "" + }, { + default: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withCtx"])(function () { + return [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Transition"], { + name: _ctx.animate ? 'fade-in' : undefined, + appear: "" + }, { + default: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withCtx"])(function () { + return [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["notification system", _ctx.cssClasses]), + style: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeStyle"])(_ctx.style), + ref: "root", + "data-notification-instance-id": _ctx.notificationInstanceId + }, [_ctx.canClose ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("button", { + key: 0, + type: "button", + class: "close", + "data-dismiss": "alert", + onClick: _cache[0] || (_cache[0] = function ($event) { + return _ctx.closeNotification($event); + }) + }, " × ")) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.title ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("strong", Notificationvue_type_template_id_52166f8a_hoisted_3, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.title), 1)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", Notificationvue_type_template_id_52166f8a_hoisted_4, [_ctx.message ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + key: 0, + innerHTML: _ctx.$sanitize(_ctx.message) + }, null, 8, Notificationvue_type_template_id_52166f8a_hoisted_5)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), !_ctx.message ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", Notificationvue_type_template_id_52166f8a_hoisted_6, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderSlot"])(_ctx.$slots, "default")])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)])], 14, Notificationvue_type_template_id_52166f8a_hoisted_2)]; + }), + _: 3 + }, 8, ["name"])])]; + }), + _: 3 + }, 8, ["name"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)]; + }), + _: 3 + }, 8, ["name"]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Notification/Notification.vue?vue&type=template&id=52166f8a + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Notification/Notification.vue?vue&type=script&lang=ts + + +var Notificationvue_type_script_lang_ts_window = window, + Notificationvue_type_script_lang_ts_$ = Notificationvue_type_script_lang_ts_window.$; +/* harmony default export */ var Notificationvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + notificationId: String, + // NOTE: notificationId refers to server side ID for notifications stored in the session. + // this ID is just so it can be selected outside of this component (just for scrolling). + notificationInstanceId: String, + title: String, + context: String, + type: String, + noclear: Boolean, + toastLength: { + type: Number, + default: 12 * 1000 + }, + style: [String, Object], + animate: Boolean, + message: String, + cssClass: String + }, + computed: { + cssClasses: function cssClasses() { + var result = {}; + + if (this.context) { + result["notification-".concat(this.context)] = true; + } + + if (this.cssClass) { + result[this.cssClass] = true; + } + + return result; + }, + canClose: function canClose() { + if (this.type === 'persistent') { + // otherwise it is never possible to dismiss the notification + return true; + } + + return !this.noclear; + } + }, + emits: ['closed'], + data: function data() { + return { + deleted: false + }; + }, + mounted: function mounted() { + var _this = this; + + var addToastEvent = function addToastEvent() { + setTimeout(function () { + _this.deleted = true; + }, _this.toastLength); + }; + + if (this.type === 'toast') { + addToastEvent(); + } + + if (this.style) { + Notificationvue_type_script_lang_ts_$(this.$refs.root).css(this.style); + } + }, + methods: { + toastClosed: function toastClosed() { + var _this2 = this; + + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["nextTick"])(function () { + _this2.$emit('closed'); + }); + }, + closeNotification: function closeNotification(event) { + var _this3 = this; + + if (this.canClose && event && event.target) { + this.deleted = true; + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["nextTick"])(function () { + _this3.$emit('closed'); + }); + } + + this.markNotificationAsRead(); + }, + markNotificationAsRead: function markNotificationAsRead() { + if (!this.notificationId) { + return; + } + + AjaxHelper_AjaxHelper.post({ + module: 'CoreHome', + action: 'markNotificationAsRead' + }, { + notificationId: this.notificationId + }, { + withTokenInUrl: true + }); + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Notification/Notification.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Notification/Notification.vue + + + +Notificationvue_type_script_lang_ts.render = Notificationvue_type_template_id_52166f8a_render + +/* harmony default export */ var Notification = (Notificationvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Notification/Notification.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var Notification_adapter = (createAngularJsAdapter({ + component: Notification, + scope: { + notificationId: { + angularJsBind: '@?' + }, + title: { + angularJsBind: '@?notificationTitle' + }, + context: { + angularJsBind: '@?' + }, + type: { + angularJsBind: '@?' + }, + noclear: { + angularJsBind: '@?', + transform: transformAngularJsBoolAttr + }, + toastLength: { + angularJsBind: '@?' + } + }, + directiveName: 'piwikNotification', + transclude: true +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Notification/Notifications.store.ts +function Notifications_store_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function Notifications_store_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function Notifications_store_createClass(Constructor, protoProps, staticProps) { if (protoProps) Notifications_store_defineProperties(Constructor.prototype, protoProps); if (staticProps) Notifications_store_defineProperties(Constructor, staticProps); return Constructor; } + +function Notifications_store_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + + + +var Notifications_store_window = window, + Notifications_store_$ = Notifications_store_window.$; + +var Notifications_store_NotificationsStore = /*#__PURE__*/function () { + function NotificationsStore() { + Notifications_store_classCallCheck(this, NotificationsStore); + + Notifications_store_defineProperty(this, "privateState", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["reactive"])({ + notifications: [] + })); + + Notifications_store_defineProperty(this, "nextNotificationId", 0); + } + + Notifications_store_createClass(NotificationsStore, [{ + key: "state", + get: function get() { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(this.privateState); + } + }, { + key: "appendNotification", + value: function appendNotification(notification) { + this.checkMessage(notification.message); // remove existing notification before adding + + if (notification.id) { + this.remove(notification.id); + } + + this.privateState.notifications.push(notification); + } + }, { + key: "prependNotification", + value: function prependNotification(notification) { + this.checkMessage(notification.message); // remove existing notification before adding + + if (notification.id) { + this.remove(notification.id); + } + + this.privateState.notifications.unshift(notification); + } + /** + * Removes a previously shown notification having the given notification id. + */ + + }, { + key: "remove", + value: function remove(id) { + this.privateState.notifications = this.privateState.notifications.filter(function (n) { + return n.id !== id; + }); + } + }, { + key: "parseNotificationDivs", + value: function parseNotificationDivs() { + var _this = this; + + var $notificationNodes = Notifications_store_$('[data-role="notification"]'); + var notificationsToShow = []; + $notificationNodes.each(function (index, notificationNode) { + var $notificationNode = Notifications_store_$(notificationNode); + var attributes = $notificationNode.data(); + var message = $notificationNode.html(); + + if (message) { + notificationsToShow.push(Object.assign(Object.assign({}, attributes), {}, { + message: message, + animate: false + })); + } + + $notificationNodes.remove(); + }); + notificationsToShow.forEach(function (n) { + return _this.show(n); + }); + } + }, { + key: "clearTransientNotifications", + value: function clearTransientNotifications() { + this.privateState.notifications = this.privateState.notifications.filter(function (n) { + return n.type !== 'transient'; + }); + } + /** + * Creates a notification and shows it to the user. + */ + + }, { + key: "show", + value: function show(notification) { + this.checkMessage(notification.message); + var addMethod = notification.prepend ? this.prependNotification : this.appendNotification; + var notificationPosition = '#notificationContainer'; + + if (notification.placeat) { + notificationPosition = notification.placeat; + } else { + // If a modal is open, we want to make sure the error message is visible and therefore + // show it within the opened modal + var modalSelector = '.modal.open .modal-content'; + var modal = document.querySelector(modalSelector); + + if (modal) { + if (!modal.querySelector('#modalNotificationContainer')) { + Notifications_store_$(modal).prepend('
'); + } + + notificationPosition = "".concat(modalSelector, " #modalNotificationContainer"); + addMethod = this.prependNotification; + } + } + + var group = notification.group || (notificationPosition ? notificationPosition.toString() : ''); + this.initializeNotificationContainer(notificationPosition, group); + var notificationInstanceId = (this.nextNotificationId += 1).toString(); + addMethod.call(this, Object.assign(Object.assign({}, notification), {}, { + noclear: !!notification.noclear, + group: group, + notificationId: notification.id, + notificationInstanceId: notificationInstanceId, + type: notification.type || 'transient' + })); + return notificationInstanceId; + } + }, { + key: "scrollToNotification", + value: function scrollToNotification(notificationInstanceId) { + setTimeout(function () { + var element = document.querySelector("[data-notification-instance-id='".concat(notificationInstanceId, "']")); + + if (element) { + Matomo_Matomo.helper.lazyScrollTo(element, 250); + } + }); + } + /** + * Shows a notification at a certain point with a quick upwards animation. + */ + + }, { + key: "toast", + value: function toast(notification) { + this.checkMessage(notification.message); + var $placeat = notification.placeat ? Notifications_store_$(notification.placeat) : undefined; + + if (!$placeat || !$placeat.length) { + throw new Error('A valid selector is required for the placeat option when using Notification.toast().'); + } + + var toastElement = document.createElement('div'); + toastElement.style.position = 'absolute'; + toastElement.style.top = "".concat($placeat.offset().top, "px"); + toastElement.style.left = "".concat($placeat.offset().left, "px"); + toastElement.style.zIndex = '1000'; + document.body.appendChild(toastElement); + var app = createVueApp({ + render: function render() { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(Notification, Object.assign(Object.assign({}, notification), {}, { + notificationId: notification.id, + type: 'toast', + onClosed: function onClosed() { + app.unmount(); + } + })); + } + }); + app.mount(toastElement); + } + }, { + key: "initializeNotificationContainer", + value: function initializeNotificationContainer(notificationPosition, group) { + if (!notificationPosition) { + return; + } + + var $container = Notifications_store_$(notificationPosition); + + if ($container.children('.notification-group').length) { + return; + } // avoiding a dependency cycle. won't need to do this when NotificationGroup's do not need + // to be dynamically initialized. + + + var NotificationGroup = window.CoreHome.NotificationGroup; // eslint-disable-line + + var app = createVueApp({ + template: '', + data: function data() { + return { + group: group + }; + } + }); + app.component('NotificationGroup', NotificationGroup); + app.mount($container[0]); + } + }, { + key: "checkMessage", + value: function checkMessage(message) { + if (!message) { + throw new Error('No message given, cannot display notification'); + } + } + }]); + + return NotificationsStore; +}(); + +var Notifications_store_instance = new Notifications_store_NotificationsStore(); +/* harmony default export */ var Notifications_store = (Notifications_store_instance); // parse notifications on dom load + +Notifications_store_$(function () { + return Notifications_store_instance.parseNotificationDivs(); +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Notification/Notifications.store.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +window.angular.module('piwikApp').factory('notifications', function () { + return Notifications_store; +}); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Notification/NotificationGroup.vue?vue&type=template&id=672051da + +var NotificationGroupvue_type_template_id_672051da_hoisted_1 = { + class: "notification-group" +}; +var NotificationGroupvue_type_template_id_672051da_hoisted_2 = ["innerHTML"]; +function NotificationGroupvue_type_template_id_672051da_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_Notification = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("Notification"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", NotificationGroupvue_type_template_id_672051da_hoisted_1, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.notifications, function (notification, index) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createBlock"])(_component_Notification, { + key: notification.id || "no-id-".concat(index), + "notification-id": notification.id, + title: notification.title, + context: notification.context, + type: notification.type, + noclear: notification.noclear, + "toast-length": notification.toastLength, + style: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeStyle"])(notification.style), + animate: notification.animate, + message: notification.message, + "notification-instance-id": notification.notificationInstanceId, + "css-class": notification.class, + onClosed: function onClosed($event) { + return _ctx.removeNotification(notification.id); + } + }, { + default: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withCtx"])(function () { + return [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", { + innerHTML: _ctx.$sanitize(notification.message) + }, null, 8, NotificationGroupvue_type_template_id_672051da_hoisted_2)]; + }), + _: 2 + }, 1032, ["notification-id", "title", "context", "type", "noclear", "toast-length", "style", "animate", "message", "notification-instance-id", "css-class", "onClosed"]); + }), 128))]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Notification/NotificationGroup.vue?vue&type=template&id=672051da + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Notification/NotificationGroup.vue?vue&type=script&lang=ts - if (binding.value.afterFocus) { - binding.value.afterFocus(); - } - }, 5); - } -} -/* harmony default export */ var FocusIf = ({ - mounted: function mounted(el, binding) { - doFocusIf(el, binding); + +/* harmony default export */ var NotificationGroupvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + group: String }, - updated: function updated(el, binding) { - doFocusIf(el, binding); + components: { + Notification: Notification + }, + computed: { + notifications: function notifications() { + var _this = this; + + return Notifications_store.state.notifications.filter(function (n) { + if (_this.group) { + return _this.group === n.group; + } + + return !n.group; + }); + } + }, + methods: { + removeNotification: function removeNotification(id) { + Notifications_store.remove(id); + } } -}); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/FocusIf/FocusIf.adapter.ts +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Notification/NotificationGroup.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Notification/NotificationGroup.vue + + + +NotificationGroupvue_type_script_lang_ts.render = NotificationGroupvue_type_template_id_672051da_render + +/* harmony default export */ var Notification_NotificationGroup = (NotificationGroupvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Notification/index.ts /*! * Matomo - free/libre analytics platform * @@ -2098,38 +8800,19 @@ function doFocusIf(el, binding) { * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ -/** - * If the given expression evaluates to true the element will be focused - * - * Example: - * - */ -function piwikFocusIf() { - return { - restrict: 'A', - link: function focusIfLink(scope, element, attrs) { - scope.$watch(attrs.piwikFocusIf, function (newValue) { - var binding = { - instance: null, - arg: newValue ? '1' : undefined, - value: { - afterFocus: function afterFocus() { - return scope.$apply(); - } - }, - oldValue: null, - modifiers: {}, - dir: {} - }; - FocusIf.updated(element[0], binding); - }); - } - }; -} -angular.module('piwikApp.directive').directive('piwikFocusIf', piwikFocusIf); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ExpandOnClick/ExpandOnClick.ts + + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingPages/ReportingPages.store.ts +function ReportingPages_store_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function ReportingPages_store_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function ReportingPages_store_createClass(Constructor, protoProps, staticProps) { if (protoProps) ReportingPages_store_defineProperties(Constructor.prototype, protoProps); if (staticProps) ReportingPages_store_defineProperties(Constructor, staticProps); return Constructor; } + +function ReportingPages_store_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + /*! * Matomo - free/libre analytics platform * @@ -2138,174 +8821,149 @@ angular.module('piwikApp.directive').directive('piwikFocusIf', piwikFocusIf); */ -function onExpand(element) { - element.classList.toggle('expanded'); - var positionElement = element.querySelector('.dropdown.positionInViewport'); +var ReportingPages_store_ReportingPagesStore = /*#__PURE__*/function () { + function ReportingPagesStore() { + var _this = this; - if (positionElement) { - Matomo_Matomo.helper.setMarginLeftToBeInViewport(positionElement); - } -} + ReportingPages_store_classCallCheck(this, ReportingPagesStore); -function ExpandOnClick_onClickOutsideElement(element, binding, event) { - var hadUsedScrollbar = binding.value.isMouseDown && binding.value.hasScrolled; - binding.value.isMouseDown = false; - binding.value.hasScrolled = false; + ReportingPages_store_defineProperty(this, "privateState", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["reactive"])({ + pages: [] + })); - if (hadUsedScrollbar) { - return; - } + ReportingPages_store_defineProperty(this, "state", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(_this.privateState); + })); - if (!element.contains(event.target)) { - element.classList.remove('expanded'); + ReportingPages_store_defineProperty(this, "fetchAllPagesPromise", void 0); + + ReportingPages_store_defineProperty(this, "pages", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return _this.state.value.pages; + })); } -} -function ExpandOnClick_onScroll(binding) { - binding.value.hasScrolled = true; -} + ReportingPages_store_createClass(ReportingPagesStore, [{ + key: "findPageInCategory", + value: function findPageInCategory(categoryId) { + // happens when user switches between sites, in this case check if the same category exists and + // if so, select first entry from that category + return this.pages.value.find(function (p) { + return p && p.category && p.category.id === categoryId && p.subcategory && p.subcategory.id; + }); + } + }, { + key: "findPage", + value: function findPage(categoryId, subcategoryId) { + return this.pages.value.find(function (p) { + return p && p.category && p.subcategory && p.category.id === categoryId && "".concat(p.subcategory.id) === subcategoryId; + }); + } + }, { + key: "reloadAllPages", + value: function reloadAllPages() { + delete this.fetchAllPagesPromise; + return this.getAllPages(); + } + }, { + key: "getAllPages", + value: function getAllPages() { + var _this2 = this; -function ExpandOnClick_onMouseDown(binding) { - binding.value.isMouseDown = true; - binding.value.hasScrolled = false; -} + if (!this.fetchAllPagesPromise) { + this.fetchAllPagesPromise = AjaxHelper_AjaxHelper.fetch({ + method: 'API.getReportPagesMetadata', + filter_limit: '-1' + }).then(function (response) { + _this2.privateState.pages = response; + return _this2.pages.value; + }); + } -function ExpandOnClick_onEscapeHandler(element, binding, event) { - if (event.which === 27) { - binding.value.isMouseDown = false; - binding.value.hasScrolled = false; - element.classList.remove('expanded'); - } -} + return this.fetchAllPagesPromise.then(function () { + return _this2.pages.value; + }); + } + }]); -var ExpandOnClick_doc = document.documentElement; -/** - * Usage (in a component): - * - * directives: { - * ExpandOnClick: ExpandOnClick(), // function call is important since we store state - * // in this directive - * } - */ + return ReportingPagesStore; +}(); +/* harmony default export */ var ReportingPages_store = (new ReportingPages_store_ReportingPagesStore()); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Orderable.ts +function Orderable_toConsumableArray(arr) { return Orderable_arrayWithoutHoles(arr) || Orderable_iterableToArray(arr) || Orderable_unsupportedIterableToArray(arr) || Orderable_nonIterableSpread(); } -/* harmony default export */ var ExpandOnClick = ({ - mounted: function mounted(el, binding) { - binding.value.isMouseDown = false; - binding.value.hasScrolled = false; - binding.value.onExpand = onExpand.bind(null, el); - binding.value.onEscapeHandler = ExpandOnClick_onEscapeHandler.bind(null, el, binding); - binding.value.onMouseDown = ExpandOnClick_onMouseDown.bind(null, binding); - binding.value.onClickOutsideElement = ExpandOnClick_onClickOutsideElement.bind(null, el, binding); - binding.value.onScroll = ExpandOnClick_onScroll.bind(null, binding); // have to use jquery here since existing code will do $(...).click(). which apparently - // doesn't work when using addEventListener. +function Orderable_nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } + +function Orderable_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return Orderable_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return Orderable_arrayLikeToArray(o, minLen); } + +function Orderable_iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } + +function Orderable_arrayWithoutHoles(arr) { if (Array.isArray(arr)) return Orderable_arrayLikeToArray(arr); } + +function Orderable_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } - window.$(binding.value.expander).click(binding.value.onExpand); - ExpandOnClick_doc.addEventListener('keyup', binding.value.onEscapeHandler); - ExpandOnClick_doc.addEventListener('mousedown', binding.value.onMouseDown); - ExpandOnClick_doc.addEventListener('mouseup', binding.value.onClickOutsideElement); - ExpandOnClick_doc.addEventListener('scroll', binding.value.onScroll); - }, - unmounted: function unmounted(el, binding) { - binding.value.expander.removeEventListener('click', binding.value.onExpand); - ExpandOnClick_doc.removeEventListener('keyup', binding.value.onEscapeHandler); - ExpandOnClick_doc.removeEventListener('mousedown', binding.value.onMouseDown); - ExpandOnClick_doc.removeEventListener('mouseup', binding.value.onClickOutsideElement); - ExpandOnClick_doc.removeEventListener('scroll', binding.value.onScroll); - } -}); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ExpandOnClick/ExpandOnClick.adapter.ts /*! * Matomo - free/libre analytics platform * * @link https://matomo.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ +function sortOrderables(menu) { + var result = Orderable_toConsumableArray(menu || []); -function piwikExpandOnClick() { - return { - restrict: 'A', - link: function expandOnClickLink(scope, element) { - var binding = { - instance: null, - value: { - expander: element.find('.title').first()[0] - }, - oldValue: null, - modifiers: {}, - dir: {} - }; - ExpandOnClick.mounted(element[0], binding); - element.on('$destroy', function () { - return ExpandOnClick.unmounted(element[0], binding); - }); + result.sort(function (lhs, rhs) { + if (lhs.order < rhs.order) { + return -1; } - }; + + if (lhs.order > rhs.order) { + return 1; + } + + return 0; + }); + return result; } -piwikExpandOnClick.$inject = []; -angular.module('piwikApp').directive('piwikExpandOnClick', piwikExpandOnClick); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ExpandOnHover/ExpandOnHover.ts +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingMenu/Category.ts /*! * Matomo - free/libre analytics platform * * @link https://matomo.org * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ +function getCategoryChildren(category) { + var container = category; - -function onMouseEnter(element) { - element.classList.add('expanded'); - var positionElement = element.querySelector('.dropdown.positionInViewport'); - - if (positionElement) { - Matomo_Matomo.helper.setMarginLeftToBeInViewport(positionElement); - } -} - -function onMouseLeave(element) { - element.classList.remove('expanded'); -} - -function ExpandOnHover_onClickOutsideElement(element, event) { - if (!element.contains(event.target)) { - element.classList.remove('expanded'); + if (container.subcategories) { + return container.subcategories; } -} -function ExpandOnHover_onEscapeHandler(element, event) { - if (event.which === 27) { - element.classList.remove('expanded'); - } + return []; } - -var ExpandOnHover_doc = document.documentElement; -/** - * Usage (in a component): +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingMenu/Subcategory.ts +/*! + * Matomo - free/libre analytics platform * - * directives: { - * ExpandOnHover: ExpandOnHover(), // function call is important since we store state - * // in this directive - * } + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later */ +function getSubcategoryChildren(subcategory) { + var container = subcategory; -/* harmony default export */ var ExpandOnHover = ({ - mounted: function mounted(el, binding) { - binding.value.onMouseEnter = onMouseEnter.bind(null, el); - binding.value.onMouseLeave = onMouseLeave.bind(null, el); - binding.value.onClickOutsideElement = ExpandOnHover_onClickOutsideElement.bind(null, el); - binding.value.onEscapeHandler = ExpandOnHover_onEscapeHandler.bind(null, el); - binding.value.expander.addEventListener('mouseenter', binding.value.onMouseEnter); - el.addEventListener('mouseleave', binding.value.onMouseLeave); - ExpandOnHover_doc.addEventListener('keyup', binding.value.onEscapeHandler); - ExpandOnHover_doc.addEventListener('mouseup', binding.value.onClickOutsideElement); - }, - unmounted: function unmounted(el, binding) { - binding.value.expander.removeEventListener('mouseenter', binding.value.onMouseEnter); - el.removeEventListener('mouseleave', binding.value.onMouseLeave); - document.removeEventListener('keyup', binding.value.onEscapeHandler); - document.removeEventListener('mouseup', binding.value.onClickOutsideElement); + if (container.subcategories) { + return container.subcategories; } -}); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ExpandOnHover/ExpandOnHover.adapter.ts + + return []; +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingMenu/ReportingMenu.store.ts +function ReportingMenu_store_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function ReportingMenu_store_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function ReportingMenu_store_createClass(Constructor, protoProps, staticProps) { if (protoProps) ReportingMenu_store_defineProperties(Constructor.prototype, protoProps); if (staticProps) ReportingMenu_store_defineProperties(Constructor, staticProps); return Constructor; } + +function ReportingMenu_store_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + /*! * Matomo - free/libre analytics platform * @@ -2314,124 +8972,221 @@ var ExpandOnHover_doc = document.documentElement; */ -function piwikExpandOnHover() { - return { - restrict: 'A', - link: function expandOnHoverLink(scope, element) { - var binding = { - instance: null, - value: { - expander: element.find('.title').first()[0] - }, - oldValue: null, - modifiers: {}, - dir: {} - }; - ExpandOnHover.mounted(element[0], binding); - element.on('$destroy', function () { - return ExpandOnHover.unmounted(element[0], binding); - }); - } - }; -} -piwikExpandOnHover.$inject = []; -angular.module('piwikApp').directive('piwikExpandOnHover', piwikExpandOnHover); -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/MatomoDialog/MatomoDialog.vue?vue&type=template&id=64e27324 -var _hoisted_1 = { - ref: "root" -}; -function MatomoDialogvue_type_template_id_64e27324_render(_ctx, _cache, $props, $setup, $data, $options) { - return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])((Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", _hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderSlot"])(_ctx.$slots, "default")], 512)), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.modelValue]]); + + + + +function isNumeric(text) { + var n = parseFloat(text); + return !Number.isNaN(n) && Number.isFinite(n); } -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MatomoDialog/MatomoDialog.vue?vue&type=template&id=64e27324 -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/@vue/cli-plugin-typescript/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-3!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/MatomoDialog/MatomoDialog.vue?vue&type=script&lang=ts +var ReportingMenu_store_ReportingMenuStore = /*#__PURE__*/function () { + function ReportingMenuStore() { + var _this = this; + ReportingMenu_store_classCallCheck(this, ReportingMenuStore); -/* harmony default export */ var MatomoDialogvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ - props: { - /** - * Whether the modal is displayed or not; - */ - modelValue: { - type: Boolean, - required: true - }, + ReportingMenu_store_defineProperty(this, "privateState", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["reactive"])({ + activeCategoryId: null, + activeSubcategoryId: null, + activeSubsubcategoryId: null + })); - /** - * Only here for backwards compatibility w/ AngularJS. If supplied, we use this - * element to launch the modal instead of the element in the slot. This should not - * be used for new Vue code. - * - * @deprecated - */ - element: { - type: HTMLElement, - required: false + ReportingMenu_store_defineProperty(this, "state", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(_this.privateState); + })); + + ReportingMenu_store_defineProperty(this, "activeCategory", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return _this.state.value.activeCategoryId || src_MatomoUrl_MatomoUrl.parsed.value.category; + })); + + ReportingMenu_store_defineProperty(this, "activeSubcategory", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return _this.state.value.activeSubcategoryId || src_MatomoUrl_MatomoUrl.parsed.value.subcategory; + })); + + ReportingMenu_store_defineProperty(this, "activeSubsubcategory", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + var manuallySetId = _this.state.value.activeSubsubcategoryId; + + if (manuallySetId) { + return manuallySetId; + } // default to activeSubcategory if the activeSubcategory is part of a group + + + var foundCategory = _this.findSubcategory(_this.activeCategory.value, _this.activeSubcategory.value); + + if (foundCategory.subsubcategory && foundCategory.subsubcategory.id === _this.activeSubcategory.value) { + return foundCategory.subsubcategory.id; + } + + return null; + })); + + ReportingMenu_store_defineProperty(this, "menu", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return _this.buildMenuFromPages(); + })); + } + + ReportingMenu_store_createClass(ReportingMenuStore, [{ + key: "fetchMenuItems", + value: function fetchMenuItems() { + var _this2 = this; + + return ReportingPages_store.getAllPages().then(function () { + return _this2.menu.value; + }); } - }, - emits: ['yes', 'no', 'closeEnd', 'close', 'validation', 'update:modelValue'], - activated: function activated() { - this.$emit('update:modelValue', false); - }, - watch: { - modelValue: function modelValue(newValue, oldValue) { - var _this = this; + }, { + key: "reloadMenuItems", + value: function reloadMenuItems() { + var _this3 = this; - if (newValue) { - var slotElement = this.element || this.$refs.root.firstElementChild; - Matomo_Matomo.helper.modalConfirm(slotElement, { - yes: function yes() { - _this.$emit('yes'); - }, - no: function no() { - _this.$emit('no'); - }, - validation: function validation() { - _this.$emit('validation'); + return ReportingPages_store.reloadAllPages().then(function () { + return _this3.menu.value; + }); + } + }, { + key: "findSubcategory", + value: function findSubcategory(categoryId, subcategoryId) { + var foundCategory = undefined; + var foundSubcategory = undefined; + var foundSubSubcategory = undefined; + this.menu.value.forEach(function (category) { + if (category.id !== categoryId) { + return; + } + + (getCategoryChildren(category) || []).forEach(function (subcategory) { + if (subcategory.id === subcategoryId) { + foundCategory = category; + foundSubcategory = subcategory; } - }, { - onCloseEnd: function onCloseEnd() { - // materialize removes the child element, so we move it back to the slot - if (!_this.element) { - _this.$refs.root.appendChild(slotElement); + + if (subcategory.isGroup) { + (getSubcategoryChildren(subcategory) || []).forEach(function (subcat) { + if (subcat.id === subcategoryId) { + foundCategory = category; + foundSubcategory = subcategory; + foundSubSubcategory = subcat; + } + }); + } + }); + }); + return { + category: foundCategory, + subcategory: foundSubcategory, + subsubcategory: foundSubSubcategory + }; + } + }, { + key: "buildMenuFromPages", + value: function buildMenuFromPages() { + var menu = []; + var displayedCategory = src_MatomoUrl_MatomoUrl.parsed.value.category; + var displayedSubcategory = src_MatomoUrl_MatomoUrl.parsed.value.subcategory; + var pages = ReportingPages_store.pages.value; + var categoriesHandled = {}; + pages.forEach(function (page) { + var category = Object.assign({}, page.category); + var categoryId = category.id; + var isCategoryDisplayed = categoryId === displayedCategory; + + if (categoriesHandled[categoryId]) { + return; + } + + categoriesHandled[categoryId] = true; + category.subcategories = []; + var categoryGroups = null; + var pagesWithCategory = pages.filter(function (p) { + return p.category.id === categoryId; + }); + pagesWithCategory.forEach(function (p) { + var subcategory = Object.assign({}, p.subcategory); + var isSubcategoryDisplayed = subcategory.id === displayedSubcategory && isCategoryDisplayed; + + if (p.widgets && p.widgets[0] && isNumeric(p.subcategory.id)) { + // we handle a goal or something like it + if (!categoryGroups) { + categoryGroups = Object.assign({}, subcategory); + categoryGroups.name = translate('CoreHome_ChooseX', [category.name]); + categoryGroups.isGroup = true; + categoryGroups.subcategories = []; + categoryGroups.order = 10; } - _this.$emit('update:modelValue', false); + if (isSubcategoryDisplayed) { + categoryGroups.name = subcategory.name; + } - _this.$emit('closeEnd'); + var entityId = page.subcategory.id; + subcategory.tooltip = "".concat(subcategory.name, " (id = ").concat(entityId, ")"); + categoryGroups.subcategories.push(subcategory); + return; } + + category.subcategories.push(subcategory); }); - } else if (newValue === false && oldValue === true) { - // the user closed the dialog, e.g. by pressing Esc or clicking away from it - this.$emit('close'); - } + + if (categoryGroups && categoryGroups.subcategories && categoryGroups.subcategories.length <= 5) { + categoryGroups.subcategories.forEach(function (sub) { + return category.subcategories.push(sub); + }); + } else if (categoryGroups) { + category.subcategories.push(categoryGroups); + } + + category.subcategories = sortOrderables(getCategoryChildren(category)); + menu.push(category); + }); + return sortOrderables(menu); } - } -})); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MatomoDialog/MatomoDialog.vue?vue&type=script&lang=ts - -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MatomoDialog/MatomoDialog.vue + }, { + key: "toggleCategory", + value: function toggleCategory(category) { + this.privateState.activeSubcategoryId = null; + this.privateState.activeSubsubcategoryId = null; + if (this.privateState.activeCategoryId === category.id) { + this.privateState.activeCategoryId = null; + return false; + } + this.privateState.activeCategoryId = category.id; + return true; + } + }, { + key: "enterSubcategory", + value: function enterSubcategory(category, subcategory, subsubcategory) { + if (!category || !subcategory) { + return; + } -MatomoDialogvue_type_script_lang_ts.render = MatomoDialogvue_type_template_id_64e27324_render + this.privateState.activeCategoryId = category.id; + this.privateState.activeSubcategoryId = subcategory.id; -/* harmony default export */ var MatomoDialog = (MatomoDialogvue_type_script_lang_ts); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/createAngularJsAdapter.ts -function createAngularJsAdapter_slicedToArray(arr, i) { return createAngularJsAdapter_arrayWithHoles(arr) || createAngularJsAdapter_iterableToArrayLimit(arr, i) || createAngularJsAdapter_unsupportedIterableToArray(arr, i) || createAngularJsAdapter_nonIterableRest(); } + if (subsubcategory) { + this.privateState.activeSubsubcategoryId = subsubcategory.id; + } + } + }]); -function createAngularJsAdapter_nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } + return ReportingMenuStore; +}(); +/* harmony default export */ var ReportingMenu_store = (new ReportingMenu_store_ReportingMenuStore()); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Widget/Widgets.store.ts +function Widgets_store_typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { Widgets_store_typeof = function _typeof(obj) { return typeof obj; }; } else { Widgets_store_typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return Widgets_store_typeof(obj); } -function createAngularJsAdapter_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return createAngularJsAdapter_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return createAngularJsAdapter_arrayLikeToArray(o, minLen); } +function Widgets_store_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } -function createAngularJsAdapter_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } +function Widgets_store_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } -function createAngularJsAdapter_iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } +function Widgets_store_createClass(Constructor, protoProps, staticProps) { if (protoProps) Widgets_store_defineProperties(Constructor.prototype, protoProps); if (staticProps) Widgets_store_defineProperties(Constructor, staticProps); return Constructor; } -function createAngularJsAdapter_arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } +function Widgets_store_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /*! * Matomo - free/libre analytics platform @@ -2441,206 +9196,363 @@ function createAngularJsAdapter_arrayWithHoles(arr) { if (Array.isArray(arr)) re */ -var transcludeCounter = 0; +function getWidgetChildren(widget) { + var container = widget; -function toKebabCase(arg) { - return arg.substring(0, 1).toLowerCase() + arg.substring(1).replace(/[A-Z]/g, function (s) { - return "-".concat(s.toLowerCase()); - }); -} + if (container.widgets) { + return container.widgets; + } -function toAngularJsCamelCase(arg) { - return arg.substring(0, 1).toLowerCase() + arg.substring(1).replace(/-([a-z])/g, function (s, p) { - return p.toUpperCase(); - }); + return []; } -function createAngularJsAdapter(options) { - var component = options.component, - _options$scope = options.scope, - scope = _options$scope === void 0 ? {} : _options$scope, - _options$events = options.events, - events = _options$events === void 0 ? {} : _options$events, - $inject = options.$inject, - directiveName = options.directiveName, - transclude = options.transclude, - mountPointFactory = options.mountPointFactory, - postCreate = options.postCreate, - noScope = options.noScope, - _options$restrict = options.restrict, - restrict = _options$restrict === void 0 ? 'A' : _options$restrict; - var currentTranscludeCounter = transcludeCounter; +var Widgets_store_WidgetsStore = /*#__PURE__*/function () { + function WidgetsStore() { + var _this = this; - if (transclude) { - transcludeCounter += 1; + Widgets_store_classCallCheck(this, WidgetsStore); + + Widgets_store_defineProperty(this, "privateState", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["reactive"])({ + isFetchedFirstTime: false, + categorizedWidgets: {} + })); + + Widgets_store_defineProperty(this, "state", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + if (!_this.privateState.isFetchedFirstTime) { + // initiating a side effect in a computed property seems wrong, but it needs to be + // executed after knowing a user's logged in and it will succeed. + _this.fetchAvailableWidgets(); + } + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(_this.privateState); + })); + + Widgets_store_defineProperty(this, "widgets", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return _this.state.value.categorizedWidgets; + })); } - var angularJsScope = {}; - Object.entries(scope).forEach(function (_ref) { - var _ref2 = createAngularJsAdapter_slicedToArray(_ref, 2), - scopeVarName = _ref2[0], - info = _ref2[1]; + Widgets_store_createClass(WidgetsStore, [{ + key: "fetchAvailableWidgets", + value: function fetchAvailableWidgets() { + var _this2 = this; - if (!info.vue) { - info.vue = scopeVarName; - } + // if there's no idSite, don't make the request since it will just fail + if (!src_MatomoUrl_MatomoUrl.parsed.value.idSite) { + return Promise.resolve(this.widgets.value); + } - if (info.angularJsBind) { - angularJsScope[scopeVarName] = info.angularJsBind; + this.privateState.isFetchedFirstTime = true; + return new Promise(function (resolve, reject) { + try { + window.widgetsHelper.getAvailableWidgets(function (widgets) { + var casted = widgets; + _this2.privateState.categorizedWidgets = casted; + resolve(_this2.widgets.value); + }); + } catch (e) { + reject(e); + } + }); } - }); + }, { + key: "reloadAvailableWidgets", + value: function reloadAvailableWidgets() { + if (Widgets_store_typeof(window.widgetsHelper) === 'object' && window.widgetsHelper.availableWidgets) { + // lets also update widgetslist so will be easier to update list of available widgets in + // dashboard selector immediately + delete window.widgetsHelper.availableWidgets; + } - function angularJsAdapter() { - for (var _len = arguments.length, injectedServices = new Array(_len), _key = 0; _key < _len; _key++) { - injectedServices[_key] = arguments[_key]; + return this.fetchAvailableWidgets(); } + }]); + + return WidgetsStore; +}(); + +/* harmony default export */ var Widgets_store = (new Widgets_store_WidgetsStore()); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/ReportingMenu/ReportingMenu.vue?vue&type=script&lang=ts + + + + + - var adapter = { - restrict: restrict, - scope: noScope ? undefined : angularJsScope, - compile: function angularJsAdapterCompile() { - return { - post: function angularJsAdapterLink(ngScope, ngElement, ngAttrs) { - var clone = transclude ? ngElement.find("[ng-transclude][counter=".concat(currentTranscludeCounter, "]")) : null; // build the root vue template - var rootVueTemplate = '
"); + Notifications_store.show({ + context: 'info', + id: REPORTING_HELP_NOTIFICATION_ID, + type: 'help', + noclear: true, + class: 'help-notification', + message: prefix + subcategory.help, + placeat: '#notificationContainer', + prepend: true + }); + this.helpShownCategory = { + category: category.id, + subcategory: subcategory.id + }; + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingMenu/ReportingMenu.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingMenu/ReportingMenu.vue - if (typeof info.default !== 'undefined' && typeof newValue === 'undefined') { - newValueFinal = info.default instanceof Function ? info.default.apply(info, [ngScope, ngElement, ngAttrs].concat(injectedServices)) : info.default; - } - if (info.transform) { - newValueFinal = info.transform(newValueFinal); - } - vm[scopeVarName] = newValueFinal; - }); - }); +ReportingMenuvue_type_script_lang_ts.render = ReportingMenuvue_type_template_id_0f6008b2_render - if (transclude) { - $(vm.transcludeTarget).append(clone); - } +/* harmony default export */ var ReportingMenu = (ReportingMenuvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingMenu/ReportingMenu.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ - if (postCreate) { - postCreate.apply(void 0, [vm, ngScope, ngElement, ngAttrs].concat(injectedServices)); - } - ngElement.on('$destroy', function () { - app.unmount(); - }); - } - }; - } - }; +/* harmony default export */ var ReportingMenu_adapter = (createAngularJsAdapter({ + component: ReportingMenu, + directiveName: 'piwikReportingMenu' +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingMenu/ReportingMenu.store.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ - if (transclude) { - adapter.transclude = true; - adapter.template = "
"); + // removed boolean active property from objects in vue so we can keep the store immutable, but, +// angularjs version should still have them + +function addActiveMenuItems(menu) { + menu.forEach(function (category) { + if (category.id === ReportingMenu_store.activeCategory.value) { + category.active = true; + (category.subcategories || []).forEach(function (subcat) { + if (subcat.id === ReportingMenu_store.activeSubcategory.value) { + subcat.active = true; + (subcat.subcategories || []).forEach(function (subsubcat) { + if (subsubcat.id === ReportingMenu_store.activeSubsubcategory.value) { + subsubcat.active = true; + } + }); + } + }); } + }); + return menu; +} - return adapter; - } +function reportingMenuModelAdapter() { + return { + get menu() { + return ReportingMenu_store.menu.value; + }, - angularJsAdapter.$inject = $inject || []; - angular.module('piwikApp').directive(directiveName, angularJsAdapter); - return angularJsAdapter; + findSubcategory: ReportingMenu_store.findSubcategory.bind(ReportingMenu_store), + reloadMenuItems: function reloadMenuItems() { + return ReportingMenu_store.reloadMenuItems().then(function (p) { + return addActiveMenuItems(cloneThenApply(p)); + }); + }, + fetchMenuItems: function fetchMenuItems() { + return ReportingMenu_store.fetchMenuItems().then(function (p) { + return addActiveMenuItems(cloneThenApply(p)); + }); + } + }; } -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/MatomoDialog/MatomoDialog.adapter.ts + +window.angular.module('piwikApp.service').factory('reportingMenuModel', reportingMenuModelAdapter); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingPages/ReportingPages.store.adapter.ts /*! * Matomo - free/libre analytics platform * @@ -2649,299 +9561,479 @@ function createAngularJsAdapter(options) { */ -/* harmony default export */ var MatomoDialog_adapter = (createAngularJsAdapter({ - component: MatomoDialog, - scope: { - show: { - vue: 'modelValue', - default: false - }, - element: { - default: function _default(scope, element) { - return element[0]; - } - } - }, - events: { - yes: function yes($event, scope, element, attrs) { - if (attrs.yes) { - scope.$eval(attrs.yes); - setTimeout(function () { - scope.$apply(); - }, 0); - } + +function reportingPagesModelAdapter() { + return { + get pages() { + return ReportingPages_store.pages.value; }, - no: function no($event, scope, element, attrs) { - if (attrs.no) { - scope.$eval(attrs.no); - setTimeout(function () { - scope.$apply(); - }, 0); - } + + findPageInCategory: function findPageInCategory() { + return clone(ReportingPages_store.findPageInCategory.apply(ReportingPages_store, arguments)); }, - validation: function validation($event, scope, element, attrs) { - if (attrs.no) { - scope.$eval(attrs.no); - setTimeout(function () { - scope.$apply(); - }, 0); - } + findPage: function findPage() { + return clone(ReportingPages_store.findPage.apply(ReportingPages_store, arguments)); }, - close: function close($event, scope, element, attrs) { - if (attrs.close) { - scope.$eval(attrs.close); - setTimeout(function () { - scope.$apply(); - }, 0); - } + reloadAllPages: function reloadAllPages() { + return ReportingPages_store.reloadAllPages().then(function (p) { + return cloneThenApply(p); + }); }, - 'update:modelValue': function updateModelValue(newValue, scope, element, attrs, $parse) { - setTimeout(function () { - scope.$apply($parse(attrs.piwikDialog).assign(scope, newValue)); - }, 0); + getAllPages: function getAllPages() { + return ReportingPages_store.getAllPages().then(function (p) { + return cloneThenApply(p); + }); } - }, - $inject: ['$parse'], - directiveName: 'piwikDialog', - transclude: true, - mountPointFactory: function mountPointFactory(scope, element) { - var vueRootPlaceholder = $('
'); - vueRootPlaceholder.appendTo(element); - return vueRootPlaceholder[0]; - }, - postCreate: function postCreate(vm, scope, element, attrs) { - scope.$watch(attrs.piwikDialog, function (newValue, oldValue) { - if (oldValue !== newValue) { - vm.modelValue = newValue || false; + }; +} + +window.angular.module('piwikApp.service').factory('reportingPagesModel', reportingPagesModelAdapter); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportMetadata/ReportMetadata.store.ts +function ReportMetadata_store_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function ReportMetadata_store_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function ReportMetadata_store_createClass(Constructor, protoProps, staticProps) { if (protoProps) ReportMetadata_store_defineProperties(Constructor.prototype, protoProps); if (staticProps) ReportMetadata_store_defineProperties(Constructor, staticProps); return Constructor; } + +function ReportMetadata_store_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + + + +var ReportMetadata_store_ReportMetadataStore = /*#__PURE__*/function () { + function ReportMetadataStore() { + var _this = this; + + ReportMetadata_store_classCallCheck(this, ReportMetadataStore); + + ReportMetadata_store_defineProperty(this, "privateState", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["reactive"])({ + reports: [] + })); + + ReportMetadata_store_defineProperty(this, "state", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(this.privateState)); + + ReportMetadata_store_defineProperty(this, "reports", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return _this.state.reports; + })); + + ReportMetadata_store_defineProperty(this, "reportsPromise", void 0); + } + + ReportMetadata_store_createClass(ReportMetadataStore, [{ + key: "findReport", + value: // TODO: it used to return an empty array when nothing was found, will that be an issue? + function findReport(reportModule, reportAction) { + return this.reports.value.find(function (r) { + return r.module === reportModule && r.action === reportAction; + }); + } + }, { + key: "fetchReportMetadata", + value: function fetchReportMetadata() { + var _this2 = this; + + if (!this.reportsPromise) { + this.reportsPromise = AjaxHelper_AjaxHelper.fetch({ + method: 'API.getReportMetadata', + filter_limit: '-1', + idSite: Matomo_Matomo.idSite || src_MatomoUrl_MatomoUrl.parsed.value.idSite + }).then(function (response) { + _this2.privateState.reports = response; + return response; + }); } - }); - }, - noScope: true -})); -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/EnrichedHeadline/EnrichedHeadline.vue?vue&type=template&id=40f81493 -var EnrichedHeadlinevue_type_template_id_40f81493_hoisted_1 = { + return this.reportsPromise.then(function () { + return _this2.reports.value; + }); + } + }]); + + return ReportMetadataStore; +}(); +/* harmony default export */ var ReportMetadata_store = (new ReportMetadata_store_ReportMetadataStore()); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportMetadata/ReportMetadata.store.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +window.angular.module('piwikApp.service').factory('reportMetadataModel', function () { + return { + get reports() { + return ReportMetadata_store.reports.value; + }, + + findReport: ReportMetadata_store.findReport.bind(ReportMetadata_store), + fetchReportMetadata: function fetchReportMetadata() { + return ReportMetadata_store.fetchReportMetadata().then(function (m) { + return cloneThenApply(m); + }); + } + }; +}); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/WidgetLoader/WidgetLoader.vue?vue&type=template&id=2dfca834 + +var WidgetLoadervue_type_template_id_2dfca834_hoisted_1 = { + key: 0 +}; +var WidgetLoadervue_type_template_id_2dfca834_hoisted_2 = { + class: "notification system notification-error" +}; +var WidgetLoadervue_type_template_id_2dfca834_hoisted_3 = { key: 0, - class: "title", - tabindex: "6" + rel: "noreferrer noopener", + target: "_blank", + href: "https://matomo.org/faq/troubleshooting/faq_19489/" }; -var _hoisted_2 = ["href", "title"]; -var _hoisted_3 = { - class: "iconsBar" +var WidgetLoadervue_type_template_id_2dfca834_hoisted_4 = { + class: "theWidgetContent", + ref: "widgetContent" }; -var _hoisted_4 = ["href", "title"]; +function WidgetLoadervue_type_template_id_2dfca834_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_ActivityIndicator = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("ActivityIndicator"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_ActivityIndicator, { + "loading-message": _ctx.loadingMessage, + loading: _ctx.loading + }, null, 8, ["loading-message", "loading"]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [_ctx.widgetName ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("h2", WidgetLoadervue_type_template_id_2dfca834_hoisted_1, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.widgetName), 1)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", WidgetLoadervue_type_template_id_2dfca834_hoisted_2, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_ErrorRequest', '', '')) + " ", 1), _ctx.hasErrorFaqLink ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", WidgetLoadervue_type_template_id_2dfca834_hoisted_3, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_ErrorRequestFaqLink')), 1)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)])], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.loadingFailed]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", WidgetLoadervue_type_template_id_2dfca834_hoisted_4, null, 512)]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/WidgetLoader/WidgetLoader.vue?vue&type=template&id=2dfca834 + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/WidgetLoader/WidgetLoader.vue?vue&type=script&lang=ts -var _hoisted_5 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { - class: "icon-help" -}, null, -1); -var _hoisted_6 = [_hoisted_5]; -var _hoisted_7 = ["title"]; -var _hoisted_8 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { - class: "icon-info" -}, null, -1); -var _hoisted_9 = [_hoisted_8]; -var _hoisted_10 = { - class: "ratingIcons" -}; -var _hoisted_11 = { - class: "inlineHelp" -}; -var _hoisted_12 = ["innerHTML"]; -var _hoisted_13 = ["href"]; -function EnrichedHeadlinevue_type_template_id_40f81493_render(_ctx, _cache, $props, $setup, $data, $options) { - var _component_RateFeature = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("RateFeature"); - return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { - class: "enrichedHeadline", - onMouseenter: _cache[1] || (_cache[1] = function ($event) { - return _ctx.showIcons = true; - }), - onMouseleave: _cache[2] || (_cache[2] = function ($event) { - return _ctx.showIcons = false; - }), - ref: "root" - }, [!_ctx.editUrl ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", EnrichedHeadlinevue_type_template_id_40f81493_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderSlot"])(_ctx.$slots, "default")])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.editUrl ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { - key: 1, - class: "title", - href: _ctx.editUrl, - title: _ctx.translate('CoreHome_ClickToEditX', _ctx.$sanitize(_ctx.actualFeatureName)) - }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderSlot"])(_ctx.$slots, "default")], 8, _hoisted_2)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", _hoisted_3, [_ctx.helpUrl && !_ctx.actualInlineHelp ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { - key: 0, - rel: "noreferrer noopener", - target: "_blank", - class: "helpIcon", - href: _ctx.helpUrl, - title: _ctx.translate('CoreHome_ExternalHelp') - }, _hoisted_6, 8, _hoisted_4)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.actualInlineHelp ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { - key: 1, - onClick: _cache[0] || (_cache[0] = function ($event) { - return _ctx.showInlineHelp = !_ctx.showInlineHelp; - }), - class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["helpIcon", { - 'active': _ctx.showInlineHelp - }]), - title: _ctx.translate(_ctx.reportGenerated ? 'General_HelpReport' : 'General_Help') - }, _hoisted_9, 10, _hoisted_7)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", _hoisted_10, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_RateFeature, { - title: _ctx.actualFeatureName - }, null, 8, ["title"])])], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.showIcons || _ctx.showInlineHelp]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", _hoisted_11, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", { - innerHTML: _ctx.$sanitize(_ctx.actualInlineHelp) - }, null, 8, _hoisted_12), _ctx.helpUrl ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { - key: 0, - rel: "noreferrer noopener", - target: "_blank", - class: "readMore", - href: _ctx.helpUrl - }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_MoreDetails')), 9, _hoisted_13)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.showInlineHelp]])], 544); -} -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/EnrichedHeadline/EnrichedHeadline.vue?vue&type=template&id=40f81493 -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/@vue/cli-plugin-typescript/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-3!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/EnrichedHeadline/EnrichedHeadline.vue?vue&type=script&lang=ts - // working around a cycle in dependencies (CoreHome depends on Feedback, Feedback depends on -// CoreHome) -// TODO: may need a generic solution at some point, but it's bad practice to have -// cyclic dependencies like this. it worked before because it was individual files -// dependening on each other, not whole plugins. - -var RateFeature = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineAsyncComponent"])(function () { - return new Promise(function (resolve) { - window.$(document).ready(function () { - var _window = window, - Feedback = _window.Feedback; // eslint-disable-line - - if (Feedback) { - resolve(Feedback.RateFeature); - } else { - // feedback plugin not loaded - resolve(null); - } - }); - }); -}); /** - * Usage: - * - *

All Websites Dashboard

- * -> uses "All Websites Dashboard" as featurename - * - *

All Websites Dashboard (Total: - * 309 Visits)

- * -> custom featurename - * - *

All Websites Dashboard

- * -> shows help icon and links to external url - * - *

All Websites - * Dashboard

- * -> makes the headline clickable linking to the specified url - * - *

Pages report

- * -> inlineHelp specified via a attribute shows help icon on headline hover + * Loads any custom widget or URL based on the given parameters. * - *

All Websites Dashboard - *
My inline help
- *

- * -> alternative definition for inline help - * -> shows help icon to display inline help on click. Note: You can combine inlinehelp and help-url + * The currently active idSite, period, date and segment (if needed) is automatically + * appended to the parameters. If this widget is removed from the DOM and requests are in + * progress, these requests will be aborted. A loading message or an error message on failure + * is shown as well. It's kinda similar to ng-include but there it is not possible to + * listen to HTTP errors etc. * - * *

Pages report

- * -> reportGenerated specified via this attribute shows a clock icon with a tooltip which - * activated by hover - * -> the tooltip shows the value of the attribute + * Example: + * */ -/* harmony default export */ var EnrichedHeadlinevue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ +/* harmony default export */ var WidgetLoadervue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ props: { - helpUrl: { - type: String, - default: '' - }, - editUrl: { - type: String, - default: '' - }, - reportGenerated: String, - featureName: String, - inlineHelp: String + widgetParams: Object, + widgetName: String }, components: { - RateFeature: RateFeature + ActivityIndicator: ActivityIndicator }, data: function data() { return { - showIcons: false, - showInlineHelp: false, - actualFeatureName: this.featureName, - actualInlineHelp: this.inlineHelp + loading: false, + loadingFailed: false, + changeCounter: 0, + currentScope: null, + lastWidgetAbortController: null }; }, watch: { - inlineHelp: function inlineHelp(newValue) { - this.actualInlineHelp = newValue; + widgetParams: function widgetParams(parameters) { + if (parameters) { + this.loadWidgetUrl(parameters, this.changeCounter += 1); + } + } + }, + computed: { + loadingMessage: function loadingMessage() { + if (!this.widgetName) { + return translate('General_LoadingData'); + } + + return translate('General_LoadingPopover', this.widgetName); }, - featureName: function featureName(newValue) { - this.actualFeatureName = newValue; + hasErrorFaqLink: function hasErrorFaqLink() { + var isGeneralSettingsAdminEnabled = Matomo_Matomo.config.enable_general_settings_admin; + var isPluginsAdminEnabled = Matomo_Matomo.config.enable_plugins_admin; + return Matomo_Matomo.hasSuperUserAccess && (isGeneralSettingsAdminEnabled || isPluginsAdminEnabled); } }, mounted: function mounted() { - var _this = this; + if (this.widgetParams) { + this.loadWidgetUrl(this.widgetParams, this.changeCounter += 1); + } + }, + beforeUnmount: function beforeUnmount() { + this.cleanupLastWidgetContent(); + }, + methods: { + abortHttpRequestIfNeeded: function abortHttpRequestIfNeeded() { + if (this.lastWidgetAbortController) { + this.lastWidgetAbortController.abort(); + this.lastWidgetAbortController = null; + } + }, + cleanupLastWidgetContent: function cleanupLastWidgetContent() { + var widgetContent = this.$refs.widgetContent; + Matomo_Matomo.helper.destroyVueComponent(widgetContent); - var root = this.$refs.root; // timeout used since angularjs does not fill out the transclude at this point + if (this.currentScope) { + this.currentScope.$destroy(); + } - setTimeout(function () { - if (!_this.actualInlineHelp) { - var helpNode = root.querySelector('.title .inlineHelp'); + if (widgetContent) { + widgetContent.innerHTML = ''; + } + }, + getWidgetUrl: function getWidgetUrl(parameters) { + var urlParams = src_MatomoUrl_MatomoUrl.parsed.value; + var fullParameters = Object.assign({}, parameters || {}); + var paramsToForward = Object.keys(Object.assign(Object.assign({}, src_MatomoUrl_MatomoUrl.hashParsed.value), {}, { + idSite: '', + period: '', + date: '', + segment: '', + widget: '' + })); + paramsToForward.forEach(function (key) { + if (key === 'category' || key === 'subcategory') { + return; + } - if (!helpNode && root.parentElement.nextElementSibling) { - // hack for reports :( - helpNode = root.parentElement.nextElementSibling.querySelector('.reportDocumentation'); + if (!(key in fullParameters)) { + fullParameters[key] = urlParams[key]; } + }); - if (helpNode) { - // hackish solution to get binded html of p tag within the help node - // at this point the ng-bind-html is not yet converted into html when report is not - // initially loaded. Using $compile doesn't work. So get and set it manually - var helpDocs = helpNode.getAttribute('data-content').trim(); + if (Comparisons_store_instance.isComparisonEnabled()) { + fullParameters = Object.assign(Object.assign({}, fullParameters), {}, { + comparePeriods: urlParams.comparePeriods, + compareDates: urlParams.compareDates, + compareSegments: urlParams.compareSegments + }); + } - if (helpDocs.length) { - _this.actualInlineHelp = "

".concat(helpDocs, "

"); - setTimeout(function () { - return helpNode.remove(); - }, 0); - } + if (!parameters || !('showtitle' in parameters)) { + fullParameters.showtitle = '1'; + } + + if (Matomo_Matomo.shouldPropagateTokenAuth && urlParams.token_auth) { + if (!Matomo_Matomo.broadcast.isWidgetizeRequestWithoutSession()) { + fullParameters.force_api_session = '1'; } + + fullParameters.token_auth = urlParams.token_auth; } - if (!_this.actualFeatureName) { - _this.actualFeatureName = root.querySelector('.title').textContent; + fullParameters.random = Math.floor(Math.random() * 10000); + return fullParameters; + }, + loadWidgetUrl: function loadWidgetUrl(parameters, thisChangeId) { + var _this = this; + + this.loading = true; + this.abortHttpRequestIfNeeded(); + this.cleanupLastWidgetContent(); + this.lastWidgetAbortController = new AbortController(); + AjaxHelper_AjaxHelper.fetch(this.getWidgetUrl(parameters), { + format: 'html', + headers: { + 'X-Requested-With': 'XMLHttpRequest' + }, + abortController: this.lastWidgetAbortController + }).then(function (response) { + if (thisChangeId !== _this.changeCounter || !response || typeof response !== 'string') { + // another widget was requested meanwhile, ignore this response + return; + } + + _this.lastWidgetAbortController = null; + _this.loading = false; + _this.loadingFailed = false; + var widgetContent = _this.$refs.widgetContent; + window.$(widgetContent).html(response); + var $content = window.$(widgetContent).children(); + + if (_this.widgetName) { + // we need to respect the widget title, which overwrites a possibly set report title + var $title = $content.find('> .card-content .card-title'); + + if (!$title.length) { + $title = $content.find('> h2'); + } + + if ($title.length) { + // required to use htmlEntities since it also escapes '{{' format items + $title.html(Matomo_Matomo.helper.htmlEntities(_this.widgetName)); + } + } + + var $rootScope = Matomo_Matomo.helper.getAngularDependency('$rootScope'); + var scope = $rootScope.$new(); + _this.currentScope = scope; // compile angularjs first since it will modify all dom nodes, breaking vue bindings + // if they are present + + Matomo_Matomo.helper.compileAngularComponents($content, { + scope: scope + }); + Matomo_Matomo.helper.compileVueEntryComponents($content); + Notifications_store.parseNotificationDivs(); + setTimeout(function () { + Matomo_Matomo.postEvent('widget:loaded', { + parameters: parameters, + element: $content + }); + }); + }).catch(function (response) { + if (thisChangeId !== _this.changeCounter) { + // another widget was requested meanwhile, ignore this response + return; + } + + _this.lastWidgetAbortController = null; + + _this.cleanupLastWidgetContent(); + + _this.loading = false; + + if (response.xhrStatus === 'abort') { + return; + } + + _this.loadingFailed = true; + }); + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/WidgetLoader/WidgetLoader.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/WidgetLoader/WidgetLoader.vue + + + +WidgetLoadervue_type_script_lang_ts.render = WidgetLoadervue_type_template_id_2dfca834_render + +/* harmony default export */ var WidgetLoader = (WidgetLoadervue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/WidgetLoader/WidgetLoader.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var WidgetLoader_adapter = (createAngularJsAdapter({ + component: WidgetLoader, + scope: { + piwikWidgetLoader: { + vue: 'widgetParams', + angularJsBind: '=' + }, + widgetName: { + angularJsBind: '@' + } + }, + directiveName: 'piwikWidgetLoader' +})); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/WidgetContainer/WidgetContainer.vue?vue&type=template&id=41745e0f + +function WidgetContainervue_type_template_id_41745e0f_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_Widget = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("Widget"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", null, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.actualContainer, function (widget, index) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + key: index + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Widget, { + widget: widget, + "prevent-recursion": true + }, null, 8, ["widget"])])]); + }), 128))]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/WidgetContainer/WidgetContainer.vue?vue&type=template&id=41745e0f + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/WidgetContainer/WidgetContainer.vue?vue&type=script&lang=ts +function WidgetContainervue_type_script_lang_ts_toConsumableArray(arr) { return WidgetContainervue_type_script_lang_ts_arrayWithoutHoles(arr) || WidgetContainervue_type_script_lang_ts_iterableToArray(arr) || WidgetContainervue_type_script_lang_ts_unsupportedIterableToArray(arr) || WidgetContainervue_type_script_lang_ts_nonIterableSpread(); } + +function WidgetContainervue_type_script_lang_ts_nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } + +function WidgetContainervue_type_script_lang_ts_iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } + +function WidgetContainervue_type_script_lang_ts_arrayWithoutHoles(arr) { if (Array.isArray(arr)) return WidgetContainervue_type_script_lang_ts_arrayLikeToArray(arr); } + +function WidgetContainervue_type_script_lang_ts_slicedToArray(arr, i) { return WidgetContainervue_type_script_lang_ts_arrayWithHoles(arr) || WidgetContainervue_type_script_lang_ts_iterableToArrayLimit(arr, i) || WidgetContainervue_type_script_lang_ts_unsupportedIterableToArray(arr, i) || WidgetContainervue_type_script_lang_ts_nonIterableRest(); } + +function WidgetContainervue_type_script_lang_ts_nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } + +function WidgetContainervue_type_script_lang_ts_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return WidgetContainervue_type_script_lang_ts_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return WidgetContainervue_type_script_lang_ts_arrayLikeToArray(o, minLen); } + +function WidgetContainervue_type_script_lang_ts_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } + +function WidgetContainervue_type_script_lang_ts_iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } + +function WidgetContainervue_type_script_lang_ts_arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } + + + // since we're recursing, don't import the plugin directly + +var Widget = useExternalPluginComponent('CoreHome', 'Widget'); +/* harmony default export */ var WidgetContainervue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + container: { + type: Array, + required: true + } + }, + components: { + Widget: Widget + }, + computed: { + actualContainer: function actualContainer() { + var _container$, _widget$parameters, _widget$parameters2; + + var container = this.container; + + if (!(container !== null && container !== void 0 && (_container$ = container[0]) !== null && _container$ !== void 0 && _container$.parameters)) { + return container; } - if (_this.reportGenerated && Periods_Periods.parse(Matomo_Matomo.period, Matomo_Matomo.currentDateString).containsToday()) { - window.$(root.querySelector('.report-generated')).tooltip({ - track: true, - content: _this.reportGenerated, - items: 'div', - show: false, - hide: false - }); - } - }); + var _container = WidgetContainervue_type_script_lang_ts_slicedToArray(container, 1), + widget = _container[0]; + + var isWidgetized = ((_widget$parameters = widget.parameters) === null || _widget$parameters === void 0 ? void 0 : _widget$parameters.widget) === '1' || ((_widget$parameters2 = widget.parameters) === null || _widget$parameters2 === void 0 ? void 0 : _widget$parameters2.widget) === 1; + var isGraphEvolution = isWidgetized && widget.viewDataTable === 'graphEvolution'; // we hide the first title for Visits Overview with Graph and Goal Overview + + var firstWidget = isGraphEvolution ? Object.assign(Object.assign({}, widget), {}, { + parameters: Object.assign(Object.assign({}, widget.parameters), {}, { + showtitle: '0' + }) + }) : widget; + return [firstWidget].concat(WidgetContainervue_type_script_lang_ts_toConsumableArray(container.slice(1))); + } } })); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/EnrichedHeadline/EnrichedHeadline.vue?vue&type=script&lang=ts +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/WidgetContainer/WidgetContainer.vue?vue&type=script&lang=ts -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/EnrichedHeadline/EnrichedHeadline.vue +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/WidgetContainer/WidgetContainer.vue -EnrichedHeadlinevue_type_script_lang_ts.render = EnrichedHeadlinevue_type_template_id_40f81493_render +WidgetContainervue_type_script_lang_ts.render = WidgetContainervue_type_template_id_41745e0f_render -/* harmony default export */ var EnrichedHeadline = (EnrichedHeadlinevue_type_script_lang_ts); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/EnrichedHeadline/EnrichedHeadline.adapter.ts +/* harmony default export */ var WidgetContainer = (WidgetContainervue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/WidgetContainer/WidgetContainer.adapter.ts /*! * Matomo - free/libre analytics platform * @@ -2950,155 +10042,142 @@ EnrichedHeadlinevue_type_script_lang_ts.render = EnrichedHeadlinevue_type_templa */ -/* harmony default export */ var EnrichedHeadline_adapter = (createAngularJsAdapter({ - component: EnrichedHeadline, +/* harmony default export */ var WidgetContainer_adapter = (createAngularJsAdapter({ + component: WidgetContainer, scope: { - helpUrl: { - angularJsBind: '@' - }, - editUrl: { - angularJsBind: '@' - }, - reportGenerated: { - angularJsBind: '@?' - }, - featureName: { - angularJsBind: '@' - }, - inlineHelp: { - angularJsBind: '@?' + container: { + angularJsBind: '=piwikWidgetContainer' } }, - directiveName: 'piwikEnrichedHeadline', - transclude: true + directiveName: 'piwikWidgetContainer' })); -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/ContentBlock/ContentBlock.vue?vue&type=template&id=09ef9e02 +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/WidgetByDimensionContainer/WidgetByDimensionContainer.vue?vue&type=template&id=3681f928 -var ContentBlockvue_type_template_id_09ef9e02_hoisted_1 = { - class: "card", - ref: "root" +var WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_1 = { + class: "reportsByDimensionView" }; -var ContentBlockvue_type_template_id_09ef9e02_hoisted_2 = { - class: "card-content" +var WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_2 = { + class: "entityList" }; -var ContentBlockvue_type_template_id_09ef9e02_hoisted_3 = { - key: 0, - class: "card-title" +var WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_3 = { + class: "listCircle" }; -var ContentBlockvue_type_template_id_09ef9e02_hoisted_4 = { - key: 1, - class: "card-title" +var WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_4 = ["onClick"]; +var WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_5 = { + class: "dimension" }; -var ContentBlockvue_type_template_id_09ef9e02_hoisted_5 = { - ref: "content" +var WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_6 = { + class: "reportContainer" }; -function ContentBlockvue_type_template_id_09ef9e02_render(_ctx, _cache, $props, $setup, $data, $options) { - var _component_EnrichedHeadline = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("EnrichedHeadline"); - return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", ContentBlockvue_type_template_id_09ef9e02_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ContentBlockvue_type_template_id_09ef9e02_hoisted_2, [_ctx.contentTitle && !_ctx.actualFeature && !_ctx.helpUrl && !_ctx.actualHelpText ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("h2", ContentBlockvue_type_template_id_09ef9e02_hoisted_3, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.contentTitle), 1)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.contentTitle && (_ctx.actualFeature || _ctx.helpUrl || _ctx.actualHelpText) ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("h2", ContentBlockvue_type_template_id_09ef9e02_hoisted_4, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_EnrichedHeadline, { - "feature-name": _ctx.actualFeature, - "help-url": _ctx.helpUrl, - "inline-help": _ctx.actualHelpText - }, { - default: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withCtx"])(function () { - return [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.contentTitle), 1)]; - }), - _: 1 - }, 8, ["feature-name", "help-url", "inline-help"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ContentBlockvue_type_template_id_09ef9e02_hoisted_5, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderSlot"])(_ctx.$slots, "default")], 512)])], 512); +var WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_7 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", { + class: "clear" +}, null, -1); + +function WidgetByDimensionContainervue_type_template_id_3681f928_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_WidgetLoader = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("WidgetLoader"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_2, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.widgetsByCategory, function (category) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + class: "dimensionCategory", + key: category.name + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(category.name) + " ", 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("ul", WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_3, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(category.widgets, function (widget) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("li", { + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["reportDimension", { + activeDimension: _ctx.selectedWidget.uniqueId === widget.uniqueId + }]), + key: widget.uniqueId, + onClick: function onClick($event) { + return _ctx.selectWidget(widget); + } + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_5, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(widget.name), 1)], 10, WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_4); + }), 128))])]); + }), 128))]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_6, [_ctx.selectedWidget.parameters ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createBlock"])(_component_WidgetLoader, { + key: 0, + "widget-params": _ctx.selectedWidget.parameters, + class: "dimensionReport" + }, null, 8, ["widget-params"])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)]), WidgetByDimensionContainervue_type_template_id_3681f928_hoisted_7]); } -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ContentBlock/ContentBlock.vue?vue&type=template&id=09ef9e02 +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/WidgetByDimensionContainer/WidgetByDimensionContainer.vue?vue&type=template&id=3681f928 -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/@vue/cli-plugin-typescript/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-3!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/ContentBlock/ContentBlock.vue?vue&type=script&lang=ts +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/WidgetByDimensionContainer/WidgetByDimensionContainer.vue?vue&type=script&lang=ts +function WidgetByDimensionContainervue_type_script_lang_ts_slicedToArray(arr, i) { return WidgetByDimensionContainervue_type_script_lang_ts_arrayWithHoles(arr) || WidgetByDimensionContainervue_type_script_lang_ts_iterableToArrayLimit(arr, i) || WidgetByDimensionContainervue_type_script_lang_ts_unsupportedIterableToArray(arr, i) || WidgetByDimensionContainervue_type_script_lang_ts_nonIterableRest(); } +function WidgetByDimensionContainervue_type_script_lang_ts_nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } -var adminContent = null; -/* harmony default export */ var ContentBlockvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ +function WidgetByDimensionContainervue_type_script_lang_ts_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return WidgetByDimensionContainervue_type_script_lang_ts_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return WidgetByDimensionContainervue_type_script_lang_ts_arrayLikeToArray(o, minLen); } + +function WidgetByDimensionContainervue_type_script_lang_ts_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } + +function WidgetByDimensionContainervue_type_script_lang_ts_iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } + +function WidgetByDimensionContainervue_type_script_lang_ts_arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } + + + + +/* harmony default export */ var WidgetByDimensionContainervue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ props: { - contentTitle: String, - feature: String, - helpUrl: String, - helpText: String, - anchor: String + widgets: Array }, components: { - EnrichedHeadline: EnrichedHeadline + WidgetLoader: WidgetLoader }, data: function data() { return { - actualFeature: this.feature, - actualHelpText: this.helpText + selectedWidget: null }; }, - watch: { - feature: function feature(newValue) { - this.actualFeature = newValue; - }, - helpText: function helpText(newValue) { - this.actualHelpText = newValue; - } - }, - mounted: function mounted() { - var _this = this; - - var _this$$refs = this.$refs, - root = _this$$refs.root, - content = _this$$refs.content; - - if (this.anchor) { - var anchorElement = document.createElement('a'); - anchorElement.id = this.anchor; - root.parentElement.prepend(anchorElement); - } + created: function created() { + var _this$widgetsSorted = WidgetByDimensionContainervue_type_script_lang_ts_slicedToArray(this.widgetsSorted, 1); - setTimeout(function () { - var inlineHelp = content.querySelector('.contentHelp'); - - if (inlineHelp) { - _this.actualHelpText = inlineHelp.innerHTML; - inlineHelp.remove(); - } - }, 0); + this.selectedWidget = _this$widgetsSorted[0]; + }, + computed: { + widgetsSorted: function widgetsSorted() { + return sortOrderables(this.widgets); + }, + widgetsByCategory: function widgetsByCategory() { + var byCategory = {}; + this.widgetsSorted.forEach(function (widget) { + var _widget$subcategory; - if (this.actualFeature && (this.actualFeature === true || this.actualFeature === 'true')) { - this.actualFeature = this.contentTitle; - } + var category = (_widget$subcategory = widget.subcategory) === null || _widget$subcategory === void 0 ? void 0 : _widget$subcategory.name; - if (adminContent === null) { - // cache admin node for further content blocks - adminContent = document.querySelector('#content.admin'); - } + if (!category) { + return; + } - var contentTopPosition; + if (!byCategory[category]) { + byCategory[category] = { + name: category, + order: widget.order, + widgets: [] + }; + } - if (adminContent) { - contentTopPosition = adminContent.offsetTop; + byCategory[category].widgets.push(widget); + }); + return sortOrderables(Object.values(byCategory)); } - - if (contentTopPosition || contentTopPosition === 0) { - var parents = root.closest('[piwik-widget-loader]'); // when shown within the widget loader, we need to get the offset of that element - // as the widget loader might be still shown. Would otherwise not position correctly - // the widgets on the admin home page - - var topThis = parents ? parents.offsetTop : root.offsetTop; - - if (topThis - contentTopPosition < 17) { - // we make sure to display the first card with no margin-top to have it on same as line as - // navigation - root.style.marginTop = 0; - } + }, + methods: { + selectWidget: function selectWidget(widget) { + // we copy to force rerender if selecting same widget + this.selectedWidget = Object.assign({}, widget); } } })); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ContentBlock/ContentBlock.vue?vue&type=script&lang=ts +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/WidgetByDimensionContainer/WidgetByDimensionContainer.vue?vue&type=script&lang=ts -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ContentBlock/ContentBlock.vue +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/WidgetByDimensionContainer/WidgetByDimensionContainer.vue -ContentBlockvue_type_script_lang_ts.render = ContentBlockvue_type_template_id_09ef9e02_render +WidgetByDimensionContainervue_type_script_lang_ts.render = WidgetByDimensionContainervue_type_template_id_3681f928_render -/* harmony default export */ var ContentBlock = (ContentBlockvue_type_script_lang_ts); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ContentBlock/ContentBlock.adapter.ts +/* harmony default export */ var WidgetByDimensionContainer = (WidgetByDimensionContainervue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/WidgetByDimensionContainer/WidgetByDimensionContainer.adapter.ts /*! * Matomo - free/libre analytics platform * @@ -3107,106 +10186,223 @@ ContentBlockvue_type_script_lang_ts.render = ContentBlockvue_type_template_id_09 */ -/* harmony default export */ var ContentBlock_adapter = (createAngularJsAdapter({ - component: ContentBlock, +/* harmony default export */ var WidgetByDimensionContainer_adapter = (createAngularJsAdapter({ + component: WidgetByDimensionContainer, scope: { - contentTitle: { - angularJsBind: '@' - }, - feature: { - angularJsBind: '@' - }, - helpUrl: { - angularJsBind: '@' - }, - helpText: { - angularJsBind: '@' - }, - anchor: { - angularJsBind: '@?' + widgets: { + angularJsBind: '=piwikWidgetByDimensionContainer', + transform: function transform(v) { + return v.widgets; + } } }, - directiveName: 'piwikContentBlock', - transclude: true + directiveName: 'piwikWidgetByDimensionContainer' })); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Segmentation/Segments.store.ts -function Segments_store_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Widget/Widget.vue?vue&type=template&id=23f53472 -function Segments_store_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } +var Widgetvue_type_template_id_23f53472_hoisted_1 = ["id"]; +var Widgetvue_type_template_id_23f53472_hoisted_2 = { + key: 1 +}; +var Widgetvue_type_template_id_23f53472_hoisted_3 = { + key: 2 +}; +function Widgetvue_type_template_id_23f53472_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_WidgetLoader = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("WidgetLoader"); -function Segments_store_createClass(Constructor, protoProps, staticProps) { if (protoProps) Segments_store_defineProperties(Constructor.prototype, protoProps); if (staticProps) Segments_store_defineProperties(Constructor, staticProps); return Constructor; } + var _component_WidgetContainer = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("WidgetContainer"); -function Segments_store_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + var _component_WidgetByDimensionContainer = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("WidgetByDimensionContainer"); -/*! - * Matomo - free/libre analytics platform - * - * @link https://matomo.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ + var _directive_tooltips = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("tooltips"); + return _ctx.actualWidget ? Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])((Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + key: 0, + class: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeClass"])(["matomo-widget", { + 'isFirstWidgetInPage': _ctx.actualWidget.isFirstInPage + }]), + id: _ctx.actualWidget.uniqueId + }, [!_ctx.actualWidget.isContainer && _ctx.actualWidget.parameters ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createBlock"])(_component_WidgetLoader, { + key: 0, + "widget-params": _ctx.actualWidget.parameters, + "widget-name": _ctx.actualWidget.name + }, null, 8, ["widget-params", "widget-name"])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.actualWidget.isContainer && _ctx.actualWidget.layout !== 'ByDimension' && !this.preventRecursion ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", Widgetvue_type_template_id_23f53472_hoisted_2, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_WidgetContainer, { + container: _ctx.actualWidget.widgets + }, null, 8, ["container"])])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.actualWidget.isContainer && _ctx.actualWidget.layout === 'ByDimension' ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", Widgetvue_type_template_id_23f53472_hoisted_3, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_WidgetByDimensionContainer, { + widgets: _ctx.actualWidget.widgets + }, null, 8, ["widgets"])])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)], 10, Widgetvue_type_template_id_23f53472_hoisted_1)), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.showWidget], [_directive_tooltips, { + content: _ctx.tooltipContent + }]]) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Widget/Widget.vue?vue&type=template&id=23f53472 +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Widget/Widget.vue?vue&type=script&lang=ts -var Segments_store_SegmentsStore = /*#__PURE__*/function () { - function SegmentsStore() { - var _this = this; - Segments_store_classCallCheck(this, SegmentsStore); - Segments_store_defineProperty(this, "segmentState", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["reactive"])({ - availableSegments: [] - })); - Matomo_Matomo.on('piwikSegmentationInited', function () { - return _this.setSegmentState(); + + + + + +function findContainer(widgetsByCategory, containerId) { + var widget = undefined; + Object.values(widgetsByCategory || {}).some(function (widgets) { + widget = widgets.find(function (w) { + var _w$parameters; + + return w && w.isContainer && ((_w$parameters = w.parameters) === null || _w$parameters === void 0 ? void 0 : _w$parameters.containerId) === containerId; }); - } + return widget; + }); + return widget; +} +/** + * Renders any kind of widget. If you have a widget and you want to have it rendered, use this + * directive. It will display a name on top and the actual widget below. It can handle any kind + * of widget, no matter whether it is a regular widget or a container. + * + * @param {Object} piwikWidget A widget object as returned by the WidgetMetadata API. + * @param {Object} piwikWidget.middlewareParameters If present, we will request a URL using the + * given parameters and only if this URL + * returns a JSON `true` the widget will be + * shown. Otherwise the widget won't be shown. + * @param {String} containerId If you do not have a widget object but a containerId we will find + * the correct widget object based on the given containerId. Be aware + * that we might not find the widget if it is for example not + * available for the current user or period/date. + * @param {Boolean} widgetized true if the widget is widgetized (eg in Dashboard or exported). + * In this case we will add a URL parameter widget=1 to all widgets. + * Eg sparklines will be then displayed one after another + * (vertically aligned) instead of two next to each other. + * + * Example: + * + * // in this case we will find the correct widget automatically + * + * // disables rating feature, no initial headline + * + */ - Segments_store_createClass(SegmentsStore, [{ - key: "state", - get: function get() { - return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(this.segmentState); - } - }, { - key: "setSegmentState", - value: function setSegmentState() { - try { - var uiControlObject = $('.segmentEditorPanel').data('uiControlObject'); - this.segmentState.availableSegments = uiControlObject.impl.availableSegments || []; - } catch (e) {// segment editor is not initialized yet + +/* harmony default export */ var Widgetvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + widget: Object, + widgetized: Boolean, + containerid: String, + preventRecursion: Boolean + }, + components: { + WidgetLoader: WidgetLoader, + WidgetContainer: WidgetContainer, + WidgetByDimensionContainer: WidgetByDimensionContainer + }, + directives: { + Tooltips: Tooltips + }, + data: function data() { + return { + showWidget: false + }; + }, + setup: function setup() { + function tooltipContent() { + var $this = window.$(this); + + if ($this.attr('piwik-field') === '' || $this.hasClass('matomo-form-field')) { + // do not show it for form fields + return ''; } + + var title = window.$(this).attr('title') || ''; + return window.vueSanitize(title.replace(/\n/g, '
')); } - }]); - return SegmentsStore; -}(); + return { + tooltipContent: tooltipContent + }; + }, + created: function created() { + var _this = this; -/* harmony default export */ var Segments_store = (new Segments_store_SegmentsStore()); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Comparisons/Comparisons.store.ts -function Comparisons_store_toConsumableArray(arr) { return Comparisons_store_arrayWithoutHoles(arr) || Comparisons_store_iterableToArray(arr) || Comparisons_store_unsupportedIterableToArray(arr) || Comparisons_store_nonIterableSpread(); } + var actualWidget = this.actualWidget; -function Comparisons_store_nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } + if (actualWidget && actualWidget.middlewareParameters) { + var params = actualWidget.middlewareParameters; + AjaxHelper_AjaxHelper.fetch(params).then(function (response) { + _this.showWidget = !!response; + }); + } else { + this.showWidget = true; + } + }, + computed: { + allWidgets: function allWidgets() { + return Widgets_store.widgets.value; + }, + actualWidget: function actualWidget() { + var _this2 = this; -function Comparisons_store_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return Comparisons_store_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return Comparisons_store_arrayLikeToArray(o, minLen); } + var widget = this.widget; -function Comparisons_store_iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } + if (widget) { + var result = Object.assign({}, widget); -function Comparisons_store_arrayWithoutHoles(arr) { if (Array.isArray(arr)) return Comparisons_store_arrayLikeToArray(arr); } + if (widget && widget.isReport && !widget.documentation) { + var report = ReportMetadata_store.findReport(widget.module, widget.action); -function Comparisons_store_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } + if (report && report.documentation) { + result.documentation = report.documentation; + } + } + + return widget; + } -function Comparisons_store_ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } + if (this.containerid) { + var containerWidget = findContainer(this.allWidgets, this.containerid); -function Comparisons_store_objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { Comparisons_store_ownKeys(Object(source), true).forEach(function (key) { Comparisons_store_defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { Comparisons_store_ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } + if (containerWidget) { + var _result = Object.assign({}, containerWidget); -function Comparisons_store_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + if (this.widgetized) { + _result.isFirstInPage = true; + _result.parameters = Object.assign(Object.assign({}, _result.parameters), {}, { + widget: '1' + }); + var widgets = getWidgetChildren(_result); + + if (widgets) { + _result.widgets = widgets.map(function (w) { + return Object.assign(Object.assign({}, w), {}, { + parameters: Object.assign(Object.assign({}, w.parameters), {}, { + widget: '1', + containerId: _this2.containerid + }) + }); + }); + } + } + + return _result; + } + } + + return null; + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Widget/Widget.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Widget/Widget.vue -function Comparisons_store_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } -function Comparisons_store_createClass(Constructor, protoProps, staticProps) { if (protoProps) Comparisons_store_defineProperties(Constructor.prototype, protoProps); if (staticProps) Comparisons_store_defineProperties(Constructor, staticProps); return Constructor; } -function Comparisons_store_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +Widgetvue_type_script_lang_ts.render = Widgetvue_type_template_id_23f53472_render +/* harmony default export */ var Widget_Widget = (Widgetvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Widget/Widget.adapter.ts /*! * Matomo - free/libre analytics platform * @@ -3215,629 +10411,479 @@ function Comparisons_store_defineProperty(obj, key, value) { if (key in obj) { O */ +/* harmony default export */ var Widget_adapter = (createAngularJsAdapter({ + component: Widget_Widget, + scope: { + widget: { + angularJsBind: '=?piwikWidget' + }, + widgetized: { + angularJsBind: '=?' + }, + containerid: { + angularJsBind: '@' + } + }, + directiveName: 'piwikWidget' +})); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/ReportingPage/ReportingPage.vue?vue&type=template&id=28b6d8b8 +var ReportingPagevue_type_template_id_28b6d8b8_hoisted_1 = { + class: "reporting-page" +}; +var ReportingPagevue_type_template_id_28b6d8b8_hoisted_2 = { + key: 1, + class: "col s12 l6 leftWidgetColumn" +}; +var ReportingPagevue_type_template_id_28b6d8b8_hoisted_3 = { + key: 2, + class: "col s12 l6 rightWidgetColumn" +}; +function ReportingPagevue_type_template_id_28b6d8b8_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_ActivityIndicator = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("ActivityIndicator"); + var _component_Widget = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("Widget"); - - -var SERIES_COLOR_COUNT = 8; -var SERIES_SHADE_COUNT = 3; - -function wrapArray(values) { - if (!values) { - return []; - } - - return values instanceof Array ? values : [values]; + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", ReportingPagevue_type_template_id_28b6d8b8_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_ActivityIndicator, { + loading: _ctx.loading + }, null, 8, ["loading"]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('CoreHome_NoSuchPage')), 513), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.hasNoPage]]), (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.widgets, function (widget) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { + class: "row", + key: widget.uniqueId + }, [!widget.group ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createBlock"])(_component_Widget, { + key: 0, + class: "col s12 fullWidgetColumn", + widget: widget + }, null, 8, ["widget"])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), widget.group ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", ReportingPagevue_type_template_id_28b6d8b8_hoisted_2, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(widget.left, function (widgetInGroup) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createBlock"])(_component_Widget, { + widget: widgetInGroup, + key: widgetInGroup.uniqueId + }, null, 8, ["widget"]); + }), 128))])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), widget.group ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", ReportingPagevue_type_template_id_28b6d8b8_hoisted_3, [(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(widget.right, function (widgetInGroup) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createBlock"])(_component_Widget, { + widget: widgetInGroup, + key: widgetInGroup.uniqueId + }, null, 8, ["widget"]); + }), 128))])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)]); + }), 128))]); } +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingPage/ReportingPage.vue?vue&type=template&id=28b6d8b8 -var Comparisons_store_ComparisonsStore = /*#__PURE__*/function () { - // for tests - function ComparisonsStore() { - var _this = this; +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingPage/ReportingPage.store.ts +function ReportingPage_store_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - Comparisons_store_classCallCheck(this, ComparisonsStore); +function ReportingPage_store_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } - Comparisons_store_defineProperty(this, "privateState", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["reactive"])({ - comparisonsDisabledFor: [] - })); +function ReportingPage_store_createClass(Constructor, protoProps, staticProps) { if (protoProps) ReportingPage_store_defineProperties(Constructor.prototype, protoProps); if (staticProps) ReportingPage_store_defineProperties(Constructor, staticProps); return Constructor; } - Comparisons_store_defineProperty(this, "state", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(this.privateState)); +function ReportingPage_store_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } - Comparisons_store_defineProperty(this, "colors", {}); +function ReportingPage_store_toConsumableArray(arr) { return ReportingPage_store_arrayWithoutHoles(arr) || ReportingPage_store_iterableToArray(arr) || ReportingPage_store_unsupportedIterableToArray(arr) || ReportingPage_store_nonIterableSpread(); } - Comparisons_store_defineProperty(this, "segmentComparisons", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { - return _this.parseSegmentComparisons(); - })); +function ReportingPage_store_nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } - Comparisons_store_defineProperty(this, "periodComparisons", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { - return _this.parsePeriodComparisons(); - })); +function ReportingPage_store_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return ReportingPage_store_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return ReportingPage_store_arrayLikeToArray(o, minLen); } - Comparisons_store_defineProperty(this, "isEnabled", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { - return _this.checkEnabledForCurrentPage(); - })); +function ReportingPage_store_iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } - this.loadComparisonsDisabledFor(); - $(function () { - _this.colors = _this.getAllSeriesColors(); - }); - Object(external_commonjs_vue_commonjs2_vue_root_Vue_["watch"])(function () { - return _this.getComparisons(); - }, function () { - return Matomo_Matomo.postEvent('piwikComparisonsChanged'); - }, { - deep: true - }); - } +function ReportingPage_store_arrayWithoutHoles(arr) { if (Array.isArray(arr)) return ReportingPage_store_arrayLikeToArray(arr); } - Comparisons_store_createClass(ComparisonsStore, [{ - key: "getComparisons", - value: function getComparisons() { - return this.getSegmentComparisons().concat(this.getPeriodComparisons()); - } - }, { - key: "isComparing", - value: function isComparing() { - return this.isComparisonEnabled() // first two in each array are for the currently selected segment/period - && (this.segmentComparisons.value.length > 1 || this.periodComparisons.value.length > 1); - } - }, { - key: "isComparingPeriods", - value: function isComparingPeriods() { - return this.getPeriodComparisons().length > 1; // first is currently selected period - } - }, { - key: "getSegmentComparisons", - value: function getSegmentComparisons() { - if (!this.isComparisonEnabled()) { - return []; - } +function ReportingPage_store_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } - return this.segmentComparisons.value; - } - }, { - key: "getPeriodComparisons", - value: function getPeriodComparisons() { - if (!this.isComparisonEnabled()) { - return []; - } +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ - return this.periodComparisons.value; - } - }, { - key: "getSeriesColor", - value: function getSeriesColor(segmentComparison, periodComparison) { - var metricIndex = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0; - var seriesIndex = this.getComparisonSeriesIndex(periodComparison.index, segmentComparison.index) % SERIES_COLOR_COUNT; - if (metricIndex === 0) { - return this.colors["series".concat(seriesIndex)]; - } - var shadeIndex = metricIndex % SERIES_SHADE_COUNT; - return this.colors["series".concat(seriesIndex, "-shade").concat(shadeIndex)]; - } - }, { - key: "getSeriesColorName", - value: function getSeriesColorName(seriesIndex, metricIndex) { - var colorName = "series".concat(seriesIndex % SERIES_COLOR_COUNT); - if (metricIndex > 0) { - colorName += "-shade".concat(metricIndex % SERIES_SHADE_COUNT); - } - return colorName; - } - }, { - key: "isComparisonEnabled", - value: function isComparisonEnabled() { - return this.isEnabled.value; - } - }, { - key: "getIndividualComparisonRowIndices", - value: function getIndividualComparisonRowIndices(seriesIndex) { - var segmentCount = this.getSegmentComparisons().length; - var segmentIndex = seriesIndex % segmentCount; - var periodIndex = Math.floor(seriesIndex / segmentCount); - return { - segmentIndex: segmentIndex, - periodIndex: periodIndex - }; - } - }, { - key: "getComparisonSeriesIndex", - value: function getComparisonSeriesIndex(periodIndex, segmentIndex) { - var segmentCount = this.getSegmentComparisons().length; - return periodIndex * segmentCount + segmentIndex; - } - }, { - key: "getAllComparisonSeries", - value: function getAllComparisonSeries() { - var _this2 = this; - var seriesInfo = []; - var seriesIndex = 0; - this.getPeriodComparisons().forEach(function (periodComp) { - _this2.getSegmentComparisons().forEach(function (segmentComp) { - seriesInfo.push({ - index: seriesIndex, - params: Comparisons_store_objectSpread(Comparisons_store_objectSpread({}, segmentComp.params), periodComp.params), - color: _this2.colors["series".concat(seriesIndex)] - }); - seriesIndex += 1; - }); - }); - return seriesInfo; - } - }, { - key: "removeSegmentComparison", - value: function removeSegmentComparison(index) { - if (!this.isComparisonEnabled()) { - throw new Error('Comparison disabled.'); - } +function shouldBeRenderedWithFullWidth(widget) { + // rather controller logic + if (widget.isContainer && widget.layout && widget.layout === 'ByDimension' || widget.viewDataTable === 'bydimension') { + return true; + } - var newComparisons = Comparisons_store_toConsumableArray(this.segmentComparisons.value); + if (widget.isWide) { + return true; + } - newComparisons.splice(index, 1); - var extraParams = {}; + return widget.viewDataTable && (widget.viewDataTable === 'tableAllColumns' || widget.viewDataTable === 'sparklines' || widget.viewDataTable === 'graphEvolution'); +} - if (index === 0) { - extraParams.segment = newComparisons[0].params.segment; - } +function markWidgetsInFirstRowOfPage(widgets) { + if (widgets && widgets[0]) { + var newWidgets = ReportingPage_store_toConsumableArray(widgets); - this.updateQueryParamsFromComparisons(newComparisons, this.periodComparisons.value, extraParams); - } - }, { - key: "addSegmentComparison", - value: function addSegmentComparison(params) { - if (!this.isComparisonEnabled()) { - throw new Error('Comparison disabled.'); - } + var groupedWidgets = widgets[0]; - var newComparisons = this.segmentComparisons.value.concat([{ - params: params, - index: -1, - title: '' - }]); - this.updateQueryParamsFromComparisons(newComparisons, this.periodComparisons.value); - } - }, { - key: "updateQueryParamsFromComparisons", - value: function updateQueryParamsFromComparisons(segmentComparisons, periodComparisons) { - var extraParams = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; - // get unique segments/periods/dates from new Comparisons - var compareSegments = {}; - var comparePeriodDatePairs = {}; - var firstSegment = false; - var firstPeriod = false; - segmentComparisons.forEach(function (comparison) { - if (firstSegment) { - compareSegments[comparison.params.segment] = true; - } else { - firstSegment = true; - } - }); - periodComparisons.forEach(function (comparison) { - if (firstPeriod) { - comparePeriodDatePairs["".concat(comparison.params.period, "|").concat(comparison.params.date)] = true; - } else { - firstPeriod = true; - } + if (groupedWidgets.group) { + newWidgets[0] = Object.assign(Object.assign({}, newWidgets[0]), {}, { + left: markWidgetsInFirstRowOfPage(groupedWidgets.left || []), + right: markWidgetsInFirstRowOfPage(groupedWidgets.right || []) }); - var comparePeriods = []; - var compareDates = []; - Object.keys(comparePeriodDatePairs).forEach(function (pair) { - var parts = pair.split('|'); - comparePeriods.push(parts[0]); - compareDates.push(parts[1]); + } else { + newWidgets[0] = Object.assign(Object.assign({}, newWidgets[0]), {}, { + isFirstInPage: true }); - var compareParams = { - compareSegments: Object.keys(compareSegments), - comparePeriods: comparePeriods, - compareDates: compareDates - }; // change the page w/ these new param values - - if (Matomo_Matomo.helper.isAngularRenderingThePage()) { - var search = src_MatomoUrl_MatomoUrl.hashParsed.value; - - var newSearch = Comparisons_store_objectSpread(Comparisons_store_objectSpread(Comparisons_store_objectSpread({}, search), compareParams), extraParams); + } - delete newSearch['compareSegments[]']; - delete newSearch['comparePeriods[]']; - delete newSearch['compareDates[]']; + return newWidgets; + } - if (JSON.stringify(newSearch) !== JSON.stringify(search)) { - src_MatomoUrl_MatomoUrl.updateHash(newSearch); - } + return widgets; +} - return; - } +var ReportingPage_store_ReportingPageStore = /*#__PURE__*/function () { + function ReportingPageStore() { + var _this = this; - var paramsToRemove = []; - ['compareSegments', 'comparePeriods', 'compareDates'].forEach(function (name) { - if (!compareParams[name].length) { - paramsToRemove.push(name); - } - }); // angular is not rendering the page (ie, we are in the embedded dashboard) or we need to change - // the segment + ReportingPage_store_classCallCheck(this, ReportingPageStore); - var url = src_MatomoUrl_MatomoUrl.stringify(extraParams); - var strHash = src_MatomoUrl_MatomoUrl.stringify(compareParams); - window.broadcast.propagateNewPage(url, undefined, strHash, paramsToRemove); - } - }, { - key: "getAllSeriesColors", - value: function getAllSeriesColors() { - var ColorManager = Matomo_Matomo.ColorManager; + ReportingPage_store_defineProperty(this, "privateState", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["reactive"])({})); - if (!ColorManager) { - return []; - } + ReportingPage_store_defineProperty(this, "state", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["readonly"])(_this.privateState); + })); - var seriesColorNames = []; + ReportingPage_store_defineProperty(this, "page", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + return _this.state.value.page; + })); - for (var i = 0; i < SERIES_COLOR_COUNT; i += 1) { - seriesColorNames.push("series".concat(i)); + ReportingPage_store_defineProperty(this, "widgets", Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { + var page = _this.page.value; - for (var j = 0; j < SERIES_SHADE_COUNT; j += 1) { - seriesColorNames.push("series".concat(i, "-shade").concat(j)); - } + if (!page) { + return []; } - return ColorManager.getColors('comparison-series-color', seriesColorNames); - } - }, { - key: "loadComparisonsDisabledFor", - value: function loadComparisonsDisabledFor() { - var _this3 = this; - - var matomoModule = src_MatomoUrl_MatomoUrl.parsed.value.module; + var widgets = []; + var reportsToIgnore = {}; - if (matomoModule === 'CoreUpdater' || matomoModule === 'Installation') { - this.privateState.comparisonsDisabledFor = []; - return; - } + var isIgnoredReport = function isIgnoredReport(widget) { + return widget.isReport && reportsToIgnore["".concat(widget.module, ".").concat(widget.action)]; + }; - AjaxHelper_AjaxHelper.fetch({ - module: 'API', - method: 'API.getPagesComparisonsDisabledFor' - }).then(function (result) { - _this3.privateState.comparisonsDisabledFor = result; - }); - } - }, { - key: "parseSegmentComparisons", - value: function parseSegmentComparisons() { - var availableSegments = Segments_store.state.availableSegments; + var getRelatedReports = function getRelatedReports(widget) { + if (!widget.isReport) { + return []; + } - var compareSegments = Comparisons_store_toConsumableArray(wrapArray(src_MatomoUrl_MatomoUrl.parsed.value.compareSegments)); // add base comparisons + var report = ReportMetadata_store.findReport(widget.module, widget.action); + if (!report || !report.relatedReports) { + return []; + } - compareSegments.unshift(src_MatomoUrl_MatomoUrl.parsed.value.segment || ''); - var newSegmentComparisons = []; - compareSegments.forEach(function (segment, idx) { - var storedSegment; - availableSegments.forEach(function (s) { - if (s.definition === segment || s.definition === decodeURIComponent(segment) || decodeURIComponent(s.definition) === segment) { - storedSegment = s; - } - }); - var segmentTitle = storedSegment ? storedSegment.name : translate('General_Unknown'); + return report.relatedReports; + }; - if (segment.trim() === '') { - segmentTitle = translate('SegmentEditor_DefaultAllVisits'); + (page.widgets || []).forEach(function (widget) { + if (isIgnoredReport(widget)) { + return; } - newSegmentComparisons.push({ - params: { - segment: segment - }, - title: Matomo_Matomo.helper.htmlDecode(segmentTitle), - index: idx + getRelatedReports(widget).forEach(function (report) { + reportsToIgnore["".concat(report.module, ".").concat(report.action)] = true; }); + widgets.push(widget); }); - return newSegmentComparisons; - } - }, { - key: "parsePeriodComparisons", - value: function parsePeriodComparisons() { - var comparePeriods = Comparisons_store_toConsumableArray(wrapArray(src_MatomoUrl_MatomoUrl.parsed.value.comparePeriods)); + widgets = sortOrderables(widgets); - var compareDates = Comparisons_store_toConsumableArray(wrapArray(src_MatomoUrl_MatomoUrl.parsed.value.compareDates)); + if (widgets.length === 1) { + // if there is only one widget, we always display it full width + return markWidgetsInFirstRowOfPage(widgets); + } - comparePeriods.unshift(src_MatomoUrl_MatomoUrl.parsed.value.period); - compareDates.unshift(src_MatomoUrl_MatomoUrl.parsed.value.date); - var newPeriodComparisons = []; + var groupedWidgets = []; - for (var i = 0; i < Math.min(compareDates.length, comparePeriods.length); i += 1) { - var title = void 0; + for (var i = 0; i < widgets.length; i += 1) { + var widget = widgets[i]; - try { - title = Periods_Periods.parse(comparePeriods[i], compareDates[i]).getPrettyString(); - } catch (e) { - title = translate('General_Error'); - } + if (shouldBeRenderedWithFullWidth(widget) || widgets[i + 1] && shouldBeRenderedWithFullWidth(widgets[i + 1])) { + groupedWidgets.push(Object.assign(Object.assign({}, widget), {}, { + widgets: sortOrderables(getWidgetChildren(widget)) + })); + } else { + var counter = 0; + var left = [widget]; + var right = []; + + while (widgets[i + 1] && !shouldBeRenderedWithFullWidth(widgets[i + 1])) { + i += 1; + counter += 1; + + if (counter % 2 === 0) { + left.push(widgets[i]); + } else { + right.push(widgets[i]); + } + } - newPeriodComparisons.push({ - params: { - date: compareDates[i], - period: comparePeriods[i] - }, - title: title, - index: i - }); + groupedWidgets.push({ + group: true, + left: left, + right: right + }); + } } - return newPeriodComparisons; + var sortedWidgets = markWidgetsInFirstRowOfPage(groupedWidgets); + return sortedWidgets; + })); + } + + ReportingPage_store_createClass(ReportingPageStore, [{ + key: "fetchPage", + value: function fetchPage(category, subcategory) { + var _this2 = this; + + this.resetPage(); + return Promise.all([ReportingPages_store.getAllPages(), ReportMetadata_store.fetchReportMetadata()]).then(function () { + _this2.privateState.page = ReportingPages_store.findPage(category, subcategory); + return _this2.page.value; + }); } }, { - key: "checkEnabledForCurrentPage", - value: function checkEnabledForCurrentPage() { - // category/subcategory is not included on top bar pages, so in that case we use module/action - var category = src_MatomoUrl_MatomoUrl.parsed.value.category || src_MatomoUrl_MatomoUrl.parsed.value.module; - var subcategory = src_MatomoUrl_MatomoUrl.parsed.value.subcategory || src_MatomoUrl_MatomoUrl.parsed.value.action; - var id = "".concat(category, ".").concat(subcategory); - var isEnabled = this.privateState.comparisonsDisabledFor.indexOf(id) === -1 && this.privateState.comparisonsDisabledFor.indexOf("".concat(category, ".*")) === -1; - document.documentElement.classList.toggle('comparisonsDisabled', !isEnabled); - return isEnabled; + key: "resetPage", + value: function resetPage() { + this.privateState.page = undefined; } }]); - return ComparisonsStore; + return ReportingPageStore; }(); +/* harmony default export */ var ReportingPage_store = (new ReportingPage_store_ReportingPageStore()); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/ReportingPage/ReportingPage.vue?vue&type=script&lang=ts -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Comparisons/Comparisons.store.instance.ts -/* harmony default export */ var Comparisons_store_instance = (new Comparisons_store_ComparisonsStore()); -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Comparisons/Comparisons.vue?vue&type=template&id=1b8ecdd2 -var Comparisonsvue_type_template_id_1b8ecdd2_hoisted_1 = { - key: 0, - ref: "root", - class: "matomo-comparisons" -}; -var Comparisonsvue_type_template_id_1b8ecdd2_hoisted_2 = { - class: "comparison-type" -}; -var Comparisonsvue_type_template_id_1b8ecdd2_hoisted_3 = ["title"]; -var Comparisonsvue_type_template_id_1b8ecdd2_hoisted_4 = ["href"]; -var Comparisonsvue_type_template_id_1b8ecdd2_hoisted_5 = ["title"]; -var Comparisonsvue_type_template_id_1b8ecdd2_hoisted_6 = { - class: "comparison-period-label" -}; -var Comparisonsvue_type_template_id_1b8ecdd2_hoisted_7 = ["onClick"]; -var Comparisonsvue_type_template_id_1b8ecdd2_hoisted_8 = ["title"]; -var Comparisonsvue_type_template_id_1b8ecdd2_hoisted_9 = { - class: "loadingPiwik", - style: { - "display": "none" - } -}; -var Comparisonsvue_type_template_id_1b8ecdd2_hoisted_10 = ["alt"]; -function Comparisonsvue_type_template_id_1b8ecdd2_render(_ctx, _cache, $props, $setup, $data, $options) { - return _ctx.isComparing ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", Comparisonsvue_type_template_id_1b8ecdd2_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h3", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_Comparisons')), 1), (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.segmentComparisons, function (comparison, $index) { - return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { - class: "comparison card", - key: comparison.index - }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", Comparisonsvue_type_template_id_1b8ecdd2_hoisted_2, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_Segment')), 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", { - class: "title", - title: comparison.title + '
' + decodeURIComponent(comparison.params.segment) - }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { - target: "_blank", - href: _ctx.getUrlToSegment(comparison.params.segment) - }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(comparison.title), 9, Comparisonsvue_type_template_id_1b8ecdd2_hoisted_4)], 8, Comparisonsvue_type_template_id_1b8ecdd2_hoisted_3), (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderList"])(_ctx.periodComparisons, function (periodComparison) { - return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", { - class: "comparison-period", - key: periodComparison.index, - title: _ctx.getComparisonTooltip(comparison, periodComparison) - }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { - class: "comparison-dot", - style: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeStyle"])({ - 'background-color': _ctx.getSeriesColor(comparison, periodComparison) - }) - }, null, 4), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", Comparisonsvue_type_template_id_1b8ecdd2_hoisted_6, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(periodComparison.title) + " (" + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.getComparisonPeriodType(periodComparison)) + ") ", 1)], 8, Comparisonsvue_type_template_id_1b8ecdd2_hoisted_5); - }), 128)), _ctx.segmentComparisons.length > 1 ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("a", { - key: 0, - class: "remove-button", - onClick: function onClick($event) { - return _ctx.removeSegmentComparison($index); - } - }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { - class: "icon icon-close", - title: _ctx.translate('General_ClickToRemoveComp') - }, null, 8, Comparisonsvue_type_template_id_1b8ecdd2_hoisted_8)], 8, Comparisonsvue_type_template_id_1b8ecdd2_hoisted_7)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)]); - }), 128)), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", Comparisonsvue_type_template_id_1b8ecdd2_hoisted_9, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("img", { - src: "plugins/Morpheus/images/loading-blue.gif", - alt: _ctx.translate('General_LoadingData') - }, null, 8, Comparisonsvue_type_template_id_1b8ecdd2_hoisted_10), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(" " + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_LoadingData')), 1)])], 512)) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true); -} -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Comparisons/Comparisons.vue?vue&type=template&id=1b8ecdd2 -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/@vue/cli-plugin-typescript/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-3!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Comparisons/Comparisons.vue?vue&type=script&lang=ts -function Comparisonsvue_type_script_lang_ts_ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } -function Comparisonsvue_type_script_lang_ts_objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { Comparisonsvue_type_script_lang_ts_ownKeys(Object(source), true).forEach(function (key) { Comparisonsvue_type_script_lang_ts_defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { Comparisonsvue_type_script_lang_ts_ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } -function Comparisonsvue_type_script_lang_ts_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } +function showOnlyRawDataNotification() { + var params = 'category=General_Visitors&subcategory=Live_VisitorLog'; + var url = window.broadcast.buildReportingUrl(params); + Notifications_store.show({ + id: 'onlyRawData', + animate: false, + context: 'info', + message: translate('CoreHome_PeriodHasOnlyRawData', ""), ''), + type: 'transient' + }); +} +function hideOnlyRawDataNoticifation() { + Notifications_store.remove('onlyRawData'); +} -/* harmony default export */ var Comparisonsvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ - props: {}, +/* harmony default export */ var ReportingPagevue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + components: { + ActivityIndicator: ActivityIndicator, + Widget: Widget_Widget + }, data: function data() { return { - comparisonTooltips: null + loading: false, + hasRawData: false, + hasNoVisits: false, + dateLastChecked: null, + hasNoPage: false }; }, - setup: function setup() { - // accessing has to be done through a computed property so we can use the computed - // instance directly in the template. unfortunately, vue won't register to changes. - var isComparing = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { - return Comparisons_store_instance.isComparing(); - }); - var segmentComparisons = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { - return Comparisons_store_instance.getSegmentComparisons(); + created: function created() { + var _this = this; + + ReportingPage_store.resetPage(); + this.loading = true; // we only set loading on initial load + + this.renderInitialPage(); + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["watch"])(function () { + return src_MatomoUrl_MatomoUrl.parsed.value; + }, function (newValue, oldValue) { + if (newValue.category === oldValue.category && newValue.subcategory === oldValue.subcategory && newValue.period === oldValue.period && newValue.date === oldValue.date && newValue.segment === oldValue.segment && JSON.stringify(newValue.compareDates) === JSON.stringify(oldValue.compareDates) && JSON.stringify(newValue.comparePeriods) === JSON.stringify(oldValue.comparePeriods) && JSON.stringify(newValue.compareSegments) === JSON.stringify(oldValue.compareSegments) && JSON.stringify(newValue.columns || '') === JSON.stringify(oldValue.columns || '')) { + // this page is already loaded + return; + } + + if (newValue.date !== oldValue.date || newValue.period !== oldValue.period) { + hideOnlyRawDataNoticifation(); + _this.dateLastChecked = null; + _this.hasRawData = false; + _this.hasNoVisits = false; + } + + _this.renderPage(newValue.category, newValue.subcategory); }); - var periodComparisons = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["computed"])(function () { - return Comparisons_store_instance.getPeriodComparisons(); + Matomo_Matomo.on('loadPage', function (category, subcategory) { + _this.renderPage(category, subcategory); }); - var getSeriesColor = Comparisons_store_instance.getSeriesColor.bind(Comparisons_store_instance); - return { - isComparing: isComparing, - segmentComparisons: segmentComparisons, - periodComparisons: periodComparisons, - getSeriesColor: getSeriesColor - }; + }, + computed: { + widgets: function widgets() { + return ReportingPage_store.widgets.value; + } }, methods: { - comparisonHasSegment: function comparisonHasSegment(comparison) { - return typeof comparison.params.segment !== 'undefined'; - }, - removeSegmentComparison: function removeSegmentComparison(index) { - // otherwise the tooltip will be stuck on the screen - window.$(this.$refs.root).tooltip('destroy'); - Comparisons_store_instance.removeSegmentComparison(index); - }, - getComparisonPeriodType: function getComparisonPeriodType(comparison) { - var period = comparison.params.period; + renderPage: function renderPage(category, subcategory) { + var _this2 = this; - if (period === 'range') { - return translate('CoreHome_PeriodRange'); + if (!category || !subcategory) { + ReportingPage_store.resetPage(); + this.loading = false; + return; } - var periodStr = translate("Intl_Period".concat(period.substring(0, 1).toUpperCase()).concat(period.substring(1))); - return periodStr.substring(0, 1).toUpperCase() + periodStr.substring(1); - }, - getComparisonTooltip: function getComparisonTooltip(segmentComparison, periodComparison) { - if (!this.comparisonTooltips || !Object.keys(this.comparisonTooltips).length) { - return undefined; + var parsedUrl = src_MatomoUrl_MatomoUrl.parsed.value; + var currentPeriod = parsedUrl.period; + var currentDate = parsedUrl.date; + + try { + Periods_Periods.parse(currentPeriod, currentDate); + } catch (e) { + Notifications_store.show({ + id: 'invalidDate', + animate: false, + context: 'error', + message: translate('CoreHome_DateInvalid'), + type: 'transient' + }); + ReportingPage_store.resetPage(); + this.loading = false; + return; } - return (this.comparisonTooltips[periodComparison.index] || {})[segmentComparison.index]; - }, - getUrlToSegment: function getUrlToSegment(segment) { - var hash = Comparisonsvue_type_script_lang_ts_objectSpread({}, src_MatomoUrl_MatomoUrl.hashParsed.value); + Notifications_store.remove('invalidDate'); + Matomo_Matomo.postEvent('piwikPageChange', {}); + Notifications_store.clearTransientNotifications(); - delete hash.comparePeriods; - delete hash.compareDates; - delete hash.compareSegments; - hash.segment = segment; - return "".concat(window.location.search, "#?").concat(src_MatomoUrl_MatomoUrl.stringify(hash)); - }, - setUpTooltips: function setUpTooltips() { - var _window = window, - $ = _window.$; - $(this.$refs.root).tooltip({ - track: true, - content: function transformTooltipContent() { - var title = $(this).attr('title'); - return window.vueSanitize(title.replace(/\n/g, '
')); - }, - show: { - delay: 200, - duration: 200 - }, - hide: false - }); - }, - onComparisonsChanged: function onComparisonsChanged() { - var _this = this; + if (Periods_Periods.parse(currentPeriod, currentDate).containsToday()) { + this.showOnlyRawDataMessageIfRequired(); + } - this.comparisonTooltips = null; + var params = { + category: category, + subcategory: subcategory + }; + Matomo_Matomo.postEvent('ReportingPage.loadPage', params); - if (!Comparisons_store_instance.isComparing()) { + if (params.promise) { + this.loading = true; + Promise.resolve(params.promise).finally(function () { + _this2.loading = false; + }); return; } - var periodComparisons = Comparisons_store_instance.getPeriodComparisons(); - var segmentComparisons = Comparisons_store_instance.getSegmentComparisons(); - AjaxHelper_AjaxHelper.fetch({ - method: 'API.getProcessedReport', - apiModule: 'VisitsSummary', - apiAction: 'get', - compare: '1', - compareSegments: src_MatomoUrl_MatomoUrl.getSearchParam('compareSegments'), - comparePeriods: src_MatomoUrl_MatomoUrl.getSearchParam('comparePeriods'), - compareDates: src_MatomoUrl_MatomoUrl.getSearchParam('compareDates'), - format_metrics: '1' - }).then(function (report) { - _this.comparisonTooltips = {}; - periodComparisons.forEach(function (periodComp) { - _this.comparisonTooltips[periodComp.index] = {}; - segmentComparisons.forEach(function (segmentComp) { - var tooltip = _this.generateComparisonTooltip(report, periodComp, segmentComp); + ReportingPage_store.fetchPage(category, subcategory).then(function () { + var hasNoPage = !ReportingPage_store.page.value; - _this.comparisonTooltips[periodComp.index][segmentComp.index] = tooltip; - }); - }); + if (hasNoPage) { + var page = ReportingPages_store.findPageInCategory(category); + + if (page && page.subcategory) { + src_MatomoUrl_MatomoUrl.updateHash(Object.assign(Object.assign({}, src_MatomoUrl_MatomoUrl.hashParsed.value), {}, { + subcategory: page.subcategory.id + })); + return; + } + } + + _this2.hasNoPage = hasNoPage; + _this2.loading = false; }); }, - generateComparisonTooltip: function generateComparisonTooltip(visitsSummary, periodComp, segmentComp) { - if (!visitsSummary.reportData.comparisons) { - // sanity check - return ''; + renderInitialPage: function renderInitialPage() { + var parsed = src_MatomoUrl_MatomoUrl.parsed.value; + this.renderPage(parsed.category, parsed.subcategory); + }, + showOnlyRawDataMessageIfRequired: function showOnlyRawDataMessageIfRequired() { + var _this3 = this; + + if (this.hasRawData && this.hasNoVisits) { + showOnlyRawDataNotification(); } - var firstRowIndex = Comparisons_store_instance.getComparisonSeriesIndex(periodComp.index, 0); - var firstRow = visitsSummary.reportData.comparisons[firstRowIndex]; - var comparisonRowIndex = Comparisons_store_instance.getComparisonSeriesIndex(periodComp.index, segmentComp.index); - var comparisonRow = visitsSummary.reportData.comparisons[comparisonRowIndex]; - var firstPeriodRow = visitsSummary.reportData.comparisons[segmentComp.index]; - var tooltip = '
'; - var visitsPercent = (comparisonRow.nb_visits / firstRow.nb_visits * 100).toFixed(2); - visitsPercent = "".concat(visitsPercent, "%"); - tooltip += translate('General_ComparisonCardTooltip1', ["'".concat(comparisonRow.compareSegmentPretty, "'"), comparisonRow.comparePeriodPretty, visitsPercent, comparisonRow.nb_visits.toString(), firstRow.nb_visits.toString()]); + var parsedUrl = src_MatomoUrl_MatomoUrl.parsed.value; + var segment = parsedUrl.segment; + + if (segment) { + hideOnlyRawDataNoticifation(); + return; + } + + var subcategoryExceptions = ['Live_VisitorLog', 'General_RealTime', 'UserCountryMap_RealTimeMap', 'MediaAnalytics_TypeAudienceLog', 'MediaAnalytics_TypeRealTime', 'FormAnalytics_TypeRealTime', 'Goals_AddNewGoal']; + var categoryExceptions = ['HeatmapSessionRecording_Heatmaps', 'HeatmapSessionRecording_SessionRecordings', 'Marketplace_Marketplace']; + var subcategory = parsedUrl.subcategory; + var category = parsedUrl.category; - if (periodComp.index > 0) { - tooltip += '

'; - tooltip += translate('General_ComparisonCardTooltip2', [comparisonRow.nb_visits_change.toString(), firstPeriodRow.compareSegmentPretty, firstPeriodRow.comparePeriodPretty]); + if (subcategoryExceptions.indexOf(subcategory) !== -1 || categoryExceptions.indexOf(category) !== -1 || subcategory.toLowerCase().indexOf('manage') !== -1) { + hideOnlyRawDataNoticifation(); + return; } - tooltip += '
'; - return tooltip; - } - }, - updated: function updated() { - var _this2 = this; + var minuteInMilliseconds = 60000; - setTimeout(function () { - return _this2.setUpTooltips(); - }); - }, - mounted: function mounted() { - var _this3 = this; + if (this.dateLastChecked && new Date().valueOf() - this.dateLastChecked.valueOf() < minuteInMilliseconds) { + return; + } - Matomo_Matomo.on('piwikComparisonsChanged', function () { - _this3.onComparisonsChanged(); - }); - this.onComparisonsChanged(); - setTimeout(function () { - return _this3.setUpTooltips(); - }); - }, - beforeUnmount: function beforeUnmount() { - try { - window.$(this.refs.root).tooltip('destroy'); - } catch (e) {// ignore + AjaxHelper_AjaxHelper.fetch({ + method: 'VisitsSummary.getVisits' + }).then(function (json) { + _this3.dateLastChecked = new Date(); + + if (json.value > 0) { + _this3.hasNoVisits = false; + hideOnlyRawDataNoticifation(); + return undefined; + } + + _this3.hasNoVisits = true; + + if (_this3.hasRawData) { + showOnlyRawDataNotification(); + return undefined; + } + + return AjaxHelper_AjaxHelper.fetch({ + method: 'Live.getLastVisitsDetails', + filter_limit: 1, + doNotFetchActions: 1 + }); + }).then(function (lastVisits) { + if (!lastVisits || lastVisits.length === 0) { + _this3.hasRawData = false; + hideOnlyRawDataNoticifation(); + return; + } + + _this3.hasRawData = true; + showOnlyRawDataNotification(); + }); } } })); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Comparisons/Comparisons.vue?vue&type=script&lang=ts +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingPage/ReportingPage.vue?vue&type=script&lang=ts -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Comparisons/Comparisons.vue +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingPage/ReportingPage.vue -Comparisonsvue_type_script_lang_ts.render = Comparisonsvue_type_template_id_1b8ecdd2_render +ReportingPagevue_type_script_lang_ts.render = ReportingPagevue_type_template_id_28b6d8b8_render -/* harmony default export */ var Comparisons = (Comparisonsvue_type_script_lang_ts); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Comparisons/Comparisons.adapter.ts +/* harmony default export */ var ReportingPage = (ReportingPagevue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportingPage/ReportingPage.adapter.ts /*! * Matomo - free/libre analytics platform * @@ -3846,535 +10892,696 @@ Comparisonsvue_type_script_lang_ts.render = Comparisonsvue_type_template_id_1b8e */ - - -function ComparisonFactory() { - return Comparisons_store_instance; -} - -ComparisonFactory.$inject = []; -angular.module('piwikApp.service').factory('piwikComparisonsService', ComparisonFactory); -/* harmony default export */ var Comparisons_adapter = (createAngularJsAdapter({ - component: Comparisons, - directiveName: 'piwikComparisons', - restrict: 'E' +/* harmony default export */ var ReportingPage_adapter = (createAngularJsAdapter({ + component: ReportingPage, + directiveName: 'piwikReportingPage' })); -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Menudropdown/Menudropdown.vue?vue&type=template&id=0349d645 +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/ReportExport/ReportExportPopover.vue?vue&type=template&id=3d203950 -var Menudropdownvue_type_template_id_0349d645_hoisted_1 = { - ref: "root", - class: "menuDropdown" +var ReportExportPopovervue_type_template_id_3d203950_hoisted_1 = { + class: "report-export-popover row", + id: "reportExport" }; -var Menudropdownvue_type_template_id_0349d645_hoisted_2 = ["title"]; -var Menudropdownvue_type_template_id_0349d645_hoisted_3 = ["innerHTML"]; - -var Menudropdownvue_type_template_id_0349d645_hoisted_4 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { - class: "icon-arrow-bottom" -}, null, -1); - -var Menudropdownvue_type_template_id_0349d645_hoisted_5 = { - class: "items" +var ReportExportPopovervue_type_template_id_3d203950_hoisted_2 = { + class: "col l6" +}; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_3 = { + name: "format" +}; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_4 = { + name: "option_flat" +}; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_5 = { + name: "option_expanded" +}; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_6 = { + name: "option_format_metrics" +}; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_7 = { + class: "col l6" +}; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_8 = { + name: "filter_type" +}; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_9 = { + class: "filter_limit" +}; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_10 = { + name: "filter_limit_all" }; -var Menudropdownvue_type_template_id_0349d645_hoisted_6 = { +var ReportExportPopovervue_type_template_id_3d203950_hoisted_11 = { key: 0, - class: "search" + name: "filter_limit" }; -var Menudropdownvue_type_template_id_0349d645_hoisted_7 = ["placeholder"]; -var Menudropdownvue_type_template_id_0349d645_hoisted_8 = ["title"]; -var Menudropdownvue_type_template_id_0349d645_hoisted_9 = ["title"]; -function Menudropdownvue_type_template_id_0349d645_render(_ctx, _cache, $props, $setup, $data, $options) { - var _directive_focus_if = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("focus-if"); +var ReportExportPopovervue_type_template_id_3d203950_hoisted_12 = { + key: 1, + name: "filter_limit" +}; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_13 = { + class: "col l12" +}; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_14 = ["value"]; - var _directive_focus_anywhere_but_here = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("focus-anywhere-but-here"); +var ReportExportPopovervue_type_template_id_3d203950_hoisted_15 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])("\n "); - return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])((Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", Menudropdownvue_type_template_id_0349d645_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { - class: "title", - onClick: _cache[0] || (_cache[0] = function ($event) { - return _ctx.showItems = !_ctx.showItems; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_16 = [ReportExportPopovervue_type_template_id_3d203950_hoisted_15]; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_17 = ["innerHTML"]; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_18 = { + class: "col l12" +}; +var ReportExportPopovervue_type_template_id_3d203950_hoisted_19 = ["href", "title"]; +function ReportExportPopovervue_type_template_id_3d203950_render(_ctx, _cache, $props, $setup, $data, $options) { + var _component_Field = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("Field"); + + var _directive_select_on_focus = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveDirective"])("select-on-focus"); + + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_2, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_3, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + uicontrol: 'radio', + name: 'format', + title: _ctx.translate('CoreHome_ExportFormat'), + modelValue: _ctx.reportFormat, + "onUpdate:modelValue": _cache[0] || (_cache[0] = function ($event) { + return _ctx.reportFormat = $event; }), - title: _ctx.tooltip - }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { - innerHTML: _ctx.$sanitize(this.actualMenuTitle) - }, null, 8, Menudropdownvue_type_template_id_0349d645_hoisted_3), Menudropdownvue_type_template_id_0349d645_hoisted_4], 8, Menudropdownvue_type_template_id_0349d645_hoisted_2), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", Menudropdownvue_type_template_id_0349d645_hoisted_5, [_ctx.showSearch && _ctx.showItems ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", Menudropdownvue_type_template_id_0349d645_hoisted_6, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { - type: "text", + "full-width": true, + options: _ctx.availableReportFormats[_ctx.reportType] + }, null, 8, ["title", "modelValue", "options"])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_4, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + uicontrol: 'checkbox', + name: 'option_flat', + title: _ctx.translate('CoreHome_FlattenReport'), + modelValue: _ctx.optionFlat, "onUpdate:modelValue": _cache[1] || (_cache[1] = function ($event) { - return _ctx.searchTerm = $event; + return _ctx.optionFlat = $event; + }) + }, null, 8, ["title", "modelValue"]), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.hasSubtables]])])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_5, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + uicontrol: 'checkbox', + name: 'option_expanded', + title: _ctx.translate('CoreHome_ExpandSubtables'), + modelValue: _ctx.optionExpanded, + "onUpdate:modelValue": _cache[2] || (_cache[2] = function ($event) { + return _ctx.optionExpanded = $event; + }) + }, null, 8, ["title", "modelValue"]), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.hasSubtables && !_ctx.optionFlat]])])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_6, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + uicontrol: 'checkbox', + name: 'option_format_metrics', + title: _ctx.translate('CoreHome_FormatMetrics'), + modelValue: _ctx.optionFormatMetrics, + "onUpdate:modelValue": _cache[3] || (_cache[3] = function ($event) { + return _ctx.optionFormatMetrics = $event; + }) + }, null, 8, ["title", "modelValue"])])])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_7, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_8, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + uicontrol: 'radio', + name: 'filter_type', + title: _ctx.translate('CoreHome_ReportType'), + modelValue: _ctx.reportType, + "onUpdate:modelValue": _cache[4] || (_cache[4] = function ($event) { + return _ctx.reportType = $event; }), - onKeydown: _cache[2] || (_cache[2] = function ($event) { - return _ctx.onSearchTermKeydown($event); + "full-width": true, + options: _ctx.availableReportTypes + }, null, 8, ["title", "modelValue", "options"])])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_9, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_10, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + uicontrol: 'radio', + name: 'filter_limit_all', + title: _ctx.translate('CoreHome_RowLimit'), + modelValue: _ctx.reportLimitAll, + "onUpdate:modelValue": _cache[5] || (_cache[5] = function ($event) { + return _ctx.reportLimitAll = $event; }), - placeholder: _ctx.translate('General_Search') - }, null, 40, Menudropdownvue_type_template_id_0349d645_hoisted_7), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vModelText"], _ctx.searchTerm], [_directive_focus_if, {}, _ctx.showItems]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("img", { - class: "search_ico", - src: "plugins/Morpheus/images/search_ico.png", - title: _ctx.translate('General_Search') - }, null, 8, Menudropdownvue_type_template_id_0349d645_hoisted_8), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], !_ctx.searchTerm]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("img", { - onClick: _cache[3] || (_cache[3] = function ($event) { - _ctx.searchTerm = ''; - - _ctx.searchItems(''); + "full-width": true, + options: _ctx.limitAllOptions + }, null, 8, ["title", "modelValue", "options"])], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], !_ctx.maxFilterLimit || _ctx.maxFilterLimit <= 0]]), _ctx.reportLimitAll === 'no' && _ctx.maxFilterLimit <= 0 ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_11, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + uicontrol: 'number', + name: "filter_limit", + min: 1, + modelValue: _ctx.reportLimit, + "onUpdate:modelValue": _cache[6] || (_cache[6] = function ($event) { + return _ctx.reportLimit = $event; }), - class: "reset", - src: "plugins/CoreHome/images/reset_search.png", - title: _ctx.translate('General_Clear') - }, null, 8, Menudropdownvue_type_template_id_0349d645_hoisted_9), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.searchTerm]])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", { - onClick: _cache[4] || (_cache[4] = function ($event) { - return _ctx.selectItem($event); - }) - }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderSlot"])(_ctx.$slots, "default")])], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.showItems]])], 512)), [[_directive_focus_anywhere_but_here, { - blur: _ctx.lostFocus - }]]); + "full-width": true + }, null, 8, ["modelValue"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true), _ctx.reportLimitAll === 'no' && _ctx.maxFilterLimit > 0 ? (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_12, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_Field, { + uicontrol: 'number', + name: 'filter_limit', + min: 1, + max: _ctx.maxFilterLimit, + modelValue: _ctx.reportLimit, + "onUpdate:modelValue": _cache[7] || (_cache[7] = function ($event) { + return _ctx.reportLimit = $event; + }), + value: _ctx.reportLimit, + "full-width": true, + title: _ctx.filterLimitTooltip + }, null, 8, ["max", "modelValue", "value", "title"])])) : Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createCommentVNode"])("", true)])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_13, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("textarea", { + readonly: "", + class: "exportFullUrl", + value: _ctx.exportLinkWithoutToken + }, ReportExportPopovervue_type_template_id_3d203950_hoisted_16, 8, ReportExportPopovervue_type_template_id_3d203950_hoisted_14), [[_directive_select_on_focus, {}]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", { + class: "tooltip", + innerHTML: _ctx.$sanitize(_ctx.translate('CoreHome_ExportTooltipWithLink', '', '', 'ENTER_YOUR_TOKEN_AUTH_HERE')) + }, null, 8, ReportExportPopovervue_type_template_id_3d203950_hoisted_17)], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.showUrl]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", ReportExportPopovervue_type_template_id_3d203950_hoisted_18, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + class: "btn", + href: _ctx.exportLink, + target: "_new", + title: _ctx.translate('CoreHome_ExportTooltip') + }, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_Export')), 9, ReportExportPopovervue_type_template_id_3d203950_hoisted_19), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("a", { + href: "javascript:", + onClick: _cache[8] || (_cache[8] = function ($event) { + return _ctx.showUrl = !_ctx.showUrl; + }), + class: "toggle-export-url" + }, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('CoreHome_ShowExportUrl')), 513), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], !_ctx.showUrl]]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", null, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('CoreHome_HideExportUrl')), 513), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], _ctx.showUrl]])])])]); } -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Menudropdown/Menudropdown.vue?vue&type=template&id=0349d645 +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportExport/ReportExportPopover.vue?vue&type=template&id=3d203950 -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/@vue/cli-plugin-typescript/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-3!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Menudropdown/Menudropdown.vue?vue&type=script&lang=ts +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/ReportExport/ReportExportPopover.vue?vue&type=script&lang=ts +function ReportExportPopovervue_type_script_lang_ts_slicedToArray(arr, i) { return ReportExportPopovervue_type_script_lang_ts_arrayWithHoles(arr) || ReportExportPopovervue_type_script_lang_ts_iterableToArrayLimit(arr, i) || ReportExportPopovervue_type_script_lang_ts_unsupportedIterableToArray(arr, i) || ReportExportPopovervue_type_script_lang_ts_nonIterableRest(); } +function ReportExportPopovervue_type_script_lang_ts_nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } +function ReportExportPopovervue_type_script_lang_ts_unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return ReportExportPopovervue_type_script_lang_ts_arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return ReportExportPopovervue_type_script_lang_ts_arrayLikeToArray(o, minLen); } -var Menudropdownvue_type_script_lang_ts_window = window, - Menudropdownvue_type_script_lang_ts_$ = Menudropdownvue_type_script_lang_ts_window.$; -/* harmony default export */ var Menudropdownvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ - props: { - menuTitle: String, - tooltip: String, - showSearch: Boolean, - menuTitleChangeOnClick: String +function ReportExportPopovervue_type_script_lang_ts_arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } + +function ReportExportPopovervue_type_script_lang_ts_iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } + +function ReportExportPopovervue_type_script_lang_ts_arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } + + + + + + + +var ReportExportPopovervue_type_script_lang_ts_Field = useExternalPluginComponent('CorePluginsAdmin', 'Field'); +/* harmony default export */ var ReportExportPopovervue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + components: { + Field: ReportExportPopovervue_type_script_lang_ts_Field }, directives: { - FocusAnywhereButHere: FocusAnywhereButHere, - FocusIf: FocusIf + SelectOnFocus: SelectOnFocus }, - emits: ['afterSelect'], - watch: { - menuTitle: function menuTitle() { - this.actualMenuTitle = this.menuTitle; + props: { + hasSubtables: Boolean, + availableReportTypes: Object, + availableReportFormats: { + type: Object, + required: true + }, + maxFilterLimit: Number, + limitAllOptions: Object, + dataTable: { + type: Object, + required: true + }, + requestParams: [Object, String], + apiMethod: { + type: String, + required: true + }, + initialReportType: { + type: String, + default: 'default' + }, + initialReportLimit: { + type: [String, Number], + default: 100 + }, + initialReportLimitAll: { + type: String, + default: 'yes' + }, + initialOptionFlat: { + type: Boolean, + default: false + }, + initialOptionExpanded: { + type: Boolean, + default: true + }, + initialOptionFormatMetrics: { + type: Boolean, + default: false + }, + initialReportFormat: { + type: String, + default: 'XML' } }, data: function data() { return { - showItems: false, - searchTerm: '', - actualMenuTitle: this.menuTitle + showUrl: false, + reportFormat: this.initialReportFormat, + optionFlat: this.initialOptionFlat, + optionExpanded: this.initialOptionExpanded, + optionFormatMetrics: this.initialOptionFormatMetrics, + reportType: this.initialReportType, + reportLimitAll: this.initialReportLimitAll, + reportLimit: typeof this.initialReportLimit === 'string' ? parseInt(this.initialReportLimit, 10) : this.initialReportLimit }; }, - methods: { - lostFocus: function lostFocus() { - this.showItems = false; - }, - selectItem: function selectItem(event) { - var targetClasses = event.target.classList; - - if (!targetClasses.contains('item') || targetClasses.contains('disabled') || targetClasses.contains('separator')) { - return; + watch: { + reportType: function reportType(newVal) { + if (!this.availableReportFormats[newVal][this.reportFormat]) { + this.reportFormat = 'XML'; } - - if (this.menuTitleChangeOnClick !== false) { - this.actualMenuTitle = event.target.textContent.replace(/[\u0000-\u2666]/g, function (c) { - return "&#".concat(c.charCodeAt(0), ";"); - }); // eslint-disable-line + }, + reportLimit: function reportLimit(newVal, oldVal) { + if (this.maxFilterLimit && this.maxFilterLimit > 0 && newVal > this.maxFilterLimit) { + this.reportLimit = oldVal; } - - this.showItems = false; - Menudropdownvue_type_script_lang_ts_$(this.$slots.default()).find('.item').removeClass('active'); - targetClasses.add('active'); - this.$emit('afterSelect'); + } + }, + computed: { + filterLimitTooltip: function filterLimitTooltip() { + var rowLimit = translate('CoreHome_RowLimit'); + var computedMetricMax = this.maxFilterLimit ? translate('General_ComputedMetricMax', this.maxFilterLimit.toString()) : ''; + return "".concat(rowLimit, " (").concat(computedMetricMax, ")"); }, - onSearchTermKeydown: function onSearchTermKeydown() { - var _this = this; - - setTimeout(function () { - _this.searchItems(_this.searchTerm); - }); + exportLink: function exportLink() { + return this.getExportLink(true); }, - searchItems: function searchItems(unprocessedSearchTerm) { - var searchTerm = unprocessedSearchTerm.toLowerCase(); - Menudropdownvue_type_script_lang_ts_$(this.$refs.root).find('.item').each(function (index, node) { - var $node = Menudropdownvue_type_script_lang_ts_$(node); - - if ($node.text().toLowerCase().indexOf(searchTerm) === -1) { - $node.hide(); - } else { - $node.show(); - } - }); + exportLinkWithoutToken: function exportLinkWithoutToken() { + return this.getExportLink(false); } - } -})); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Menudropdown/Menudropdown.vue?vue&type=script&lang=ts - -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Menudropdown/Menudropdown.vue - - - -Menudropdownvue_type_script_lang_ts.render = Menudropdownvue_type_template_id_0349d645_render + }, + methods: { + getExportLink: function getExportLink() { + var withToken = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; + var reportFormat = this.reportFormat, + apiMethod = this.apiMethod, + reportType = this.reportType; + var dataTable = this.dataTable; + + if (!reportFormat) { + return undefined; + } -/* harmony default export */ var Menudropdown = (Menudropdownvue_type_script_lang_ts); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Menudropdown/Menudropdown.adapter.ts -/*! - * Matomo - free/libre analytics platform - * - * @link https://matomo.org - * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */ + var requestParams = {}; + var limit = this.reportLimitAll === 'yes' ? -1 : this.reportLimit; + if (this.requestParams && typeof this.requestParams === 'string') { + requestParams = JSON.parse(this.requestParams); + } -/* harmony default export */ var Menudropdown_adapter = (createAngularJsAdapter({ - component: Menudropdown, - scope: { - menuTitle: { - angularJsBind: '@' - }, - tooltip: { - angularJsBind: '@' - }, - showSearch: { - angularJsBind: '=' - }, - menuTitleChangeOnClick: { - angularJsBind: '=' - } - }, - directiveName: 'piwikMenudropdown', - transclude: true, - events: { - 'after-select': function afterSelect($event, scope) { - setTimeout(function () { - scope.$apply(); - }, 0); - } - } -})); -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/DatePicker/DatePicker.vue?vue&type=template&id=c8c462d2 + var _dataTable$param = dataTable.param, + segment = _dataTable$param.segment, + label = _dataTable$param.label, + idGoal = _dataTable$param.idGoal, + idDimension = _dataTable$param.idDimension, + idSite = _dataTable$param.idSite; + var _dataTable$param2 = dataTable.param, + date = _dataTable$param2.date, + period = _dataTable$param2.period; + + if (reportFormat === 'RSS') { + date = 'last10'; + } -var DatePickervue_type_template_id_c8c462d2_hoisted_1 = { - ref: "root" -}; -function DatePickervue_type_template_id_c8c462d2_render(_ctx, _cache, $props, $setup, $data, $options) { - return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", DatePickervue_type_template_id_c8c462d2_hoisted_1, null, 512); -} -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DatePicker/DatePicker.vue?vue&type=template&id=c8c462d2 + if (typeof dataTable.param.dateUsedInGraph !== 'undefined') { + date = dataTable.param.dateUsedInGraph; + } -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/@vue/cli-plugin-typescript/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-3!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/DatePicker/DatePicker.vue?vue&type=script&lang=ts -function DatePickervue_type_script_lang_ts_ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } + var formatsUseDayNotRange = Matomo_Matomo.config.datatable_export_range_as_day.toLowerCase(); -function DatePickervue_type_script_lang_ts_objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { DatePickervue_type_script_lang_ts_ownKeys(Object(source), true).forEach(function (key) { DatePickervue_type_script_lang_ts_defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { DatePickervue_type_script_lang_ts_ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } + if (formatsUseDayNotRange.indexOf(reportFormat.toLowerCase()) !== -1 && dataTable.param.period === 'range') { + period = 'day'; + } // Below evolution graph, show daily exports -function DatePickervue_type_script_lang_ts_defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + if (dataTable.param.period === 'range' && dataTable.param.viewDataTable === 'graphEvolution') { + period = 'day'; + } + var exportUrlParams = { + module: 'API', + format: reportFormat, + idSite: idSite, + period: period, + date: date + }; + if (reportType === 'processed') { + exportUrlParams.method = 'API.getProcessedReport'; -var DEFAULT_STEP_MONTHS = 1; -var DatePickervue_type_script_lang_ts_window = window, - DatePickervue_type_script_lang_ts_$ = DatePickervue_type_script_lang_ts_window.$; -/* harmony default export */ var DatePickervue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ - props: { - selectedDateStart: Date, - selectedDateEnd: Date, - highlightedDateStart: Date, - highlightedDateEnd: Date, - viewDate: [String, Date], - stepMonths: Number, - disableMonthDropdown: Boolean, - options: Object - }, - emits: ['cellHover', 'cellHoverLeave', 'dateSelect'], - setup: function setup(props, context) { - var root = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])(null); + var _apiMethod$split = apiMethod.split('.'); - function setDateCellColor($dateCell, dateValue) { - var $dateCellLink = $dateCell.children('a'); + var _apiMethod$split2 = ReportExportPopovervue_type_script_lang_ts_slicedToArray(_apiMethod$split, 2); - if (props.selectedDateStart && props.selectedDateEnd && dateValue >= props.selectedDateStart && dateValue <= props.selectedDateEnd) { - $dateCell.addClass('ui-datepicker-current-period'); + exportUrlParams.apiModule = _apiMethod$split2[0]; + exportUrlParams.apiAction = _apiMethod$split2[1]; } else { - $dateCell.removeClass('ui-datepicker-current-period'); + exportUrlParams.method = apiMethod; } - if (props.highlightedDateStart && props.highlightedDateEnd && dateValue >= props.highlightedDateStart && dateValue <= props.highlightedDateEnd) { - // other-month cells don't have links, so the must have the ui-state-hover class - var elementToAddClassTo = $dateCellLink.length ? $dateCellLink : $dateCell; - elementToAddClassTo.addClass('ui-state-hover'); - } else { - $dateCell.removeClass('ui-state-hover'); - $dateCellLink.removeClass('ui-state-hover'); + if (dataTable.param.compareDates && dataTable.param.compareDates.length) { + exportUrlParams.compareDates = dataTable.param.compareDates; + exportUrlParams.compare = '1'; } - } - function getCellDate($dateCell, month, year) { - if ($dateCell.hasClass('ui-datepicker-other-month')) { - return getOtherMonthDate($dateCell, month, year); // eslint-disable-line + if (dataTable.param.comparePeriods && dataTable.param.comparePeriods.length) { + exportUrlParams.comparePeriods = dataTable.param.comparePeriods; + exportUrlParams.compare = '1'; } - var day = parseInt($dateCell.children('a,span').text(), 10); - return new Date(year, month, day); - } - - function getOtherMonthDate($dateCell, month, year) { - var date; - var $row = $dateCell.parent(); - var $rowCells = $row.children('td'); // if in the first row, the date cell is before the current month + if (dataTable.param.compareSegments && dataTable.param.compareSegments.length) { + exportUrlParams.compareSegments = dataTable.param.compareSegments; + exportUrlParams.compare = '1'; + } - if ($row.is(':first-child')) { - var $firstDateInMonth = $row.children('td:not(.ui-datepicker-other-month)').first(); - date = getCellDate($firstDateInMonth, month, year); - date.setDate($rowCells.index($dateCell) - $rowCells.index($firstDateInMonth) + 1); - return date; - } // the date cell is after the current month + if (typeof dataTable.param.filter_pattern !== 'undefined') { + exportUrlParams.filter_pattern = dataTable.param.filter_pattern; + } + if (typeof dataTable.param.filter_pattern_recursive !== 'undefined') { + exportUrlParams.filter_pattern_recursive = dataTable.param.filter_pattern_recursive; + } - var $lastDateInMonth = $row.children('td:not(.ui-datepicker-other-month)').last(); - date = getCellDate($lastDateInMonth, month, year); - date.setDate(date.getDate() + $rowCells.index($dateCell) - $rowCells.index($lastDateInMonth)); - return date; - } + if (window.$.isPlainObject(requestParams)) { + Object.entries(requestParams).forEach(function (_ref) { + var _ref2 = ReportExportPopovervue_type_script_lang_ts_slicedToArray(_ref, 2), + index = _ref2[0], + param = _ref2[1]; - function getMonthYearDisplayed() { - var element = DatePickervue_type_script_lang_ts_$(root.value); - var $firstCellWithMonth = element.find('td[data-month]'); - var month = parseInt($firstCellWithMonth.attr('data-month'), 10); - var year = parseInt($firstCellWithMonth.attr('data-year'), 10); - return [month, year]; - } + var value = param; - function setDatePickerCellColors() { - var element = DatePickervue_type_script_lang_ts_$(root.value); - var $calendarTable = element.find('.ui-datepicker-calendar'); - var monthYear = getMonthYearDisplayed(); // highlight the rest of the cells by first getting the date for the first cell - // in the calendar, then just incrementing by one for the rest of the cells. + if (value === true) { + value = 1; + } else if (value === false) { + value = 0; + } - var $cells = $calendarTable.find('td'); - var $firstDateCell = $cells.first(); - var currentDate = getCellDate($firstDateCell, monthYear[0], monthYear[1]); - $cells.each(function setCellColor() { - setDateCellColor(DatePickervue_type_script_lang_ts_$(this), currentDate); - currentDate.setDate(currentDate.getDate() + 1); - }); - } + exportUrlParams[index] = value; + }); + } - function viewDateChanged() { - var date = props.viewDate; + if (this.optionFlat) { + exportUrlParams.flat = 1; - if (!date) { - return false; + if (typeof dataTable.param.include_aggregate_rows !== 'undefined' && dataTable.param.include_aggregate_rows === '1') { + exportUrlParams.include_aggregate_rows = 1; + } } - if (!(date instanceof Date)) { - try { - date = parseDate(date); - } catch (e) { - return false; - } + if (!this.optionFlat && this.optionExpanded) { + exportUrlParams.expanded = 1; } - var element = DatePickervue_type_script_lang_ts_$(root.value); // only change the datepicker date if the date is outside of the current month/year. - // this avoids a re-render in other cases. + if (this.optionFormatMetrics) { + exportUrlParams.format_metrics = 1; + } - var monthYear = getMonthYearDisplayed(); + if (dataTable.param.pivotBy) { + exportUrlParams.pivotBy = dataTable.param.pivotBy; + exportUrlParams.pivotByColumnLimit = 20; - if (monthYear[0] !== date.getMonth() || monthYear[1] !== date.getFullYear()) { - element.datepicker('setDate', date); - return true; + if (dataTable.props.pivot_by_column) { + exportUrlParams.pivotByColumn = dataTable.props.pivot_by_column; + } } - return false; - } // remove the ui-state-active class & click handlers for every cell. we bypass - // the datepicker's date selection logic for smoother browser rendering. - + if (reportFormat === 'CSV' || reportFormat === 'TSV' || reportFormat === 'RSS') { + exportUrlParams.translateColumnNames = 1; + exportUrlParams.language = Matomo_Matomo.language; + } - function onJqueryUiRenderedPicker() { - var element = DatePickervue_type_script_lang_ts_$(root.value); - element.find('td[data-event]').off('click'); - element.find('.ui-state-active').removeClass('ui-state-active'); - element.find('.ui-datepicker-current-day').removeClass('ui-datepicker-current-day'); // add href to left/right nav in calendar so they can be accessed via keyboard + if (typeof segment !== 'undefined') { + exportUrlParams.segment = decodeURIComponent(segment); + } // Export Goals specific reports - element.find('.ui-datepicker-prev,.ui-datepicker-next').attr('href', ''); - } - function stepMonthsChanged() { - var element = DatePickervue_type_script_lang_ts_$(root.value); - var stepMonths = props.stepMonths || DEFAULT_STEP_MONTHS; + if (typeof idGoal !== 'undefined' && idGoal !== '-1') { + exportUrlParams.idGoal = idGoal; + } // Export Dimension specific reports - if (element.datepicker('option', 'stepMonths') === stepMonths) { - return false; - } // setting stepMonths will change the month in view back to the selected date. to avoid - // we set the selected date to the month in view. + if (typeof idDimension !== 'undefined' && idDimension !== '-1') { + exportUrlParams.idDimension = idDimension; + } - var currentMonth = DatePickervue_type_script_lang_ts_$('.ui-datepicker-month', element).val(); - var currentYear = DatePickervue_type_script_lang_ts_$('.ui-datepicker-year', element).val(); - element.datepicker('option', 'stepMonths', stepMonths).datepicker('setDate', new Date(currentYear, currentMonth)); - onJqueryUiRenderedPicker(); - return true; - } + if (label) { + var labelParts = label.split(','); - function enableDisableMonthDropdown() { - var element = DatePickervue_type_script_lang_ts_$(root.value); - element.find('.ui-datepicker-month').attr('disabled', props.disableMonthDropdown); - } + if (labelParts.length > 1) { + exportUrlParams.label = labelParts; + } else { + var _labelParts = ReportExportPopovervue_type_script_lang_ts_slicedToArray(labelParts, 1); - function handleOtherMonthClick() { - if (!DatePickervue_type_script_lang_ts_$(this).hasClass('ui-state-hover')) { - return; + exportUrlParams.label = _labelParts[0]; + } } - var $row = DatePickervue_type_script_lang_ts_$(this).parent(); - var $tbody = $row.parent(); + exportUrlParams.token_auth = 'ENTER_YOUR_TOKEN_AUTH_HERE'; - if ($row.is(':first-child')) { - // click on first of the month - $tbody.find('a').first().click(); - } else { - // click on last of month - $tbody.find('a').last().click(); + if (withToken === true) { + exportUrlParams.token_auth = Matomo_Matomo.token_auth; + exportUrlParams.force_api_session = 1; } - } - - function onCalendarViewChange() { - // clicking left/right re-enables the month dropdown, so we disable it again - enableDisableMonthDropdown(); - setDatePickerCellColors(); - } // on a prop change (NOTE: we can't watch just `props`, since then newProps and oldProps will - // have the same values (since it is a proxy object). Using a copy doesn't quite work, the - // object it returns will always be different, BUT, since we check what changes it works - // for our purposes. The only downside is that it runs on every tick basically, but since - // that is within the context of the date picker component, it's bearable. + exportUrlParams.filter_limit = limit; + var prefix = window.location.href.split('?')[0]; + return "".concat(prefix, "?").concat(src_MatomoUrl_MatomoUrl.stringify(exportUrlParams)); + } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportExport/ReportExportPopover.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportExport/ReportExportPopover.vue - Object(external_commonjs_vue_commonjs2_vue_root_Vue_["watch"])(function () { - return DatePickervue_type_script_lang_ts_objectSpread({}, props); - }, function (newProps, oldProps) { - var redraw = false; - ['selectedDateStart', 'selectedDateEnd', 'highlightedDateStart', 'highlightedDateEnd'].forEach(function (propName) { - if (redraw) { - return; - } - if (!newProps[propName] && oldProps[propName]) { - redraw = true; - } - if (newProps[propName] && !oldProps[propName]) { - redraw = true; - } +ReportExportPopovervue_type_script_lang_ts.render = ReportExportPopovervue_type_template_id_3d203950_render - if (newProps[propName] && oldProps[propName] && newProps[propName].getTime() !== oldProps[propName].getTime()) { - redraw = true; - } - }); +/* harmony default export */ var ReportExportPopover = (ReportExportPopovervue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportExport/ReportExport.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ - if (newProps.viewDate !== oldProps.viewDate && viewDateChanged()) { - redraw = true; - } - if (newProps.stepMonths !== oldProps.stepMonths) { - stepMonthsChanged(); - } - if (newProps.enableDisableMonthDropdown !== oldProps.enableDisableMonthDropdown) { - enableDisableMonthDropdown(); - } // redraw when selected/highlighted dates change - if (redraw) { - setDatePickerCellColors(); +var ReportExport_window = window, + ReportExport_$ = ReportExport_window.$; +/* harmony default export */ var ReportExport = ({ + mounted: function mounted(el, binding) { + el.addEventListener('click', function () { + var popoverParamBackup = src_MatomoUrl_MatomoUrl.hashParsed.value.popover; + var dataTable = ReportExport_$(el).closest('[data-report]').data('uiControlObject'); + var popover = window.Piwik_Popover.showLoading('Export'); + var formats = binding.value.reportFormats; + var reportLimit = dataTable.param.filter_limit; + + if (binding.value.maxFilterLimit > 0) { + reportLimit = Math.min(reportLimit, binding.value.maxFilterLimit); } - }); - Object(external_commonjs_vue_commonjs2_vue_root_Vue_["onMounted"])(function () { - var element = DatePickervue_type_script_lang_ts_$(root.value); - var customOptions = props.options || {}; - var datePickerOptions = DatePickervue_type_script_lang_ts_objectSpread(DatePickervue_type_script_lang_ts_objectSpread(DatePickervue_type_script_lang_ts_objectSpread({}, Matomo_Matomo.getBaseDatePickerOptions()), customOptions), {}, { - onChangeMonthYear: function onChangeMonthYear() { - // datepicker renders the HTML after this hook is called, so we use setTimeout - // to run some code after the render. - setTimeout(function () { - onJqueryUiRenderedPicker(); - }); + var optionFlat = dataTable.param.flat === true || dataTable.param.flat === 1 || dataTable.param.flat === '1'; + var props = { + initialReportType: 'default', + initialReportLimit: reportLimit > 0 ? reportLimit : 100, + initialReportLimitAll: reportLimit === -1 ? 'yes' : 'no', + initialOptionFlat: optionFlat, + initialOptionExpanded: true, + initialOptionFormatMetrics: false, + hasSubtables: optionFlat || dataTable.numberOfSubtables > 0, + availableReportFormats: { + default: formats, + processed: { + XML: formats.XML, + JSON: formats.JSON + } + }, + availableReportTypes: { + default: translate('CoreHome_StandardReport'), + processed: translate('CoreHome_ReportWithMetadata') + }, + limitAllOptions: { + yes: translate('General_All'), + no: translate('CoreHome_CustomLimit') + }, + maxFilterLimit: binding.value.maxFilterLimit, + dataTable: dataTable, + requestParams: binding.value.requestParams, + apiMethod: binding.value.apiMethod + }; + var app = createVueApp({ + template: "\n ", + data: function data() { + return { + bind: props + }; } }); + app.component('popover', ReportExportPopover); + var mountPoint = document.createElement('div'); + app.mount(mountPoint); + var reportTitle = binding.value.reportTitle; + window.Piwik_Popover.setTitle("".concat(translate('General_Export'), " ").concat(Matomo_Matomo.helper.htmlEntities(reportTitle))); + window.Piwik_Popover.setContent(mountPoint); + window.Piwik_Popover.onClose(function () { + app.unmount(); + + if (popoverParamBackup !== '') { + setTimeout(function () { + src_MatomoUrl_MatomoUrl.updateHash(Object.assign(Object.assign({}, src_MatomoUrl_MatomoUrl.hashParsed.value), {}, { + popover: popoverParamBackup + })); - element.datepicker(datePickerOptions); - element.on('mouseover', 'tbody td a', function (event) { - // this event is triggered when a user clicks a date as well. in that case, - // the originalEvent is null. we don't need to redraw again for that, so - // we ignore events like that. - if (event.originalEvent) { - setDatePickerCellColors(); + if (binding.value.onClose) { + binding.value.onClose(); + } + }, 100); } - }); // on hover cell, execute scope.cellHover() - - element.on('mouseenter', 'tbody td', function onMouseEnter() { - var monthYear = getMonthYearDisplayed(); - var $dateCell = DatePickervue_type_script_lang_ts_$(this); - var dateValue = getCellDate($dateCell, monthYear[0], monthYear[1]); - context.emit('cellHover', { - date: dateValue, - $cell: $dateCell + }); + setTimeout(function () { + popover.dialog(); + ReportExport_$('.exportFullUrl, .btn', popover).tooltip({ + track: true, + show: false, + hide: false }); - }); // overrides jquery UI handler that unhighlights a cell when the mouse leaves it + }, 100); + }); + } +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ReportExport/ReportExport.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +function piwikReportExport($timeout) { + return { + restrict: 'A', + scope: { + reportTitle: '@', + requestParams: '@', + reportFormats: '@', + apiMethod: '@', + maxFilterLimit: '@' + }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + link: function piwikReportExportLink(scope, element) { + var binding = { + instance: null, + value: { + reportTitle: scope.reportTitle, + requestParams: scope.requestParams, + reportFormats: typeof scope.reportFormats === 'string' ? JSON.parse(scope.reportFormats) : scope.reportFormats, + apiMethod: scope.apiMethod, + maxFilterLimit: parseInt(scope.maxFilterLimit, 10), + onClose: function onClose() { + $timeout(function () { + window.angular.element(document).injector().get('$rootScope').$apply(); + }, 10); + } + }, + oldValue: null, + modifiers: {}, + dir: {} + }; + ReportExport.mounted(element[0], binding); + } + }; +} +piwikReportExport.$inject = ['$timeout']; +window.angular.module('piwikApp').directive('piwikReportExport', piwikReportExport); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Sparkline/Sparkline.vue?vue&type=template&id=693cd955 + +var Sparklinevue_type_template_id_693cd955_hoisted_1 = ["src"]; +function Sparklinevue_type_template_id_693cd955_render(_ctx, _cache, $props, $setup, $data, $options) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("img", { + src: _ctx.sparklineUrl + }, null, 8, Sparklinevue_type_template_id_693cd955_hoisted_1); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Sparkline/Sparkline.vue?vue&type=template&id=693cd955 + +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Sparkline/Sparkline.vue?vue&type=script&lang=ts + - element.on('mouseout', 'tbody td a', function () { - setDatePickerCellColors(); - }); // call scope.cellHoverLeave() when mouse leaves table body (can't do event on tbody, for - // some reason that fails, so we do two events, one on the table & one on thead) - element.on('mouseleave', 'table', function () { - return context.emit('cellHoverLeave'); - }).on('mouseenter', 'thead', function () { - return context.emit('cellHoverLeave'); - }); // make sure whitespace is clickable when the period makes it appropriate - element.on('click', 'tbody td.ui-datepicker-other-month', function () { - return handleOtherMonthClick(); - }); // NOTE: using a selector w/ .on() doesn't seem to work for some reason... - element.on('click', function (e) { - e.preventDefault(); - var $target = DatePickervue_type_script_lang_ts_$(e.target).closest('a'); - if (!$target.is('.ui-datepicker-next') && !$target.is('.ui-datepicker-prev')) { - return; - } +/* harmony default export */ var Sparklinevue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + seriesIndices: Array, + params: Object + }, + data: function data() { + return { + isWidget: false + }; + }, + mounted: function mounted() { + this.isWidget = !!this.$el.closest('[widgetId]'); + }, + computed: { + sparklineUrl: function sparklineUrl() { + var seriesIndices = this.seriesIndices, + params = this.params; + var sparklineColors = Matomo_Matomo.getSparklineColors(); + + if (seriesIndices) { + sparklineColors.lineColor = sparklineColors.lineColor.filter(function (c, index) { + return seriesIndices.indexOf(index) !== -1; + }); + } + + var colors = JSON.stringify(sparklineColors); + var defaultParams = { + forceView: '1', + viewDataTable: 'sparkline', + widget: this.isWidget ? '1' : '0', + showtitle: '1', + colors: colors, + random: Date.now(), + date: this.defaultDate + }; + var helper = new AjaxHelper_AjaxHelper(); + var urlParams = helper.mixinDefaultGetParams(Object.assign(Object.assign({}, defaultParams), params)); // Append the token_auth to the URL if it was set (eg. embed dashboard) + + var token_auth = src_MatomoUrl_MatomoUrl.parsed.value.token_auth; + + if (token_auth && token_auth.length && Matomo_Matomo.shouldPropagateTokenAuth) { + urlParams.token_auth = token_auth; + } - onCalendarViewChange(); - }); // when a cell is clicked, invoke the onDateSelected function. this, in conjunction - // with onJqueryUiRenderedPicker(), overrides the date picker's click behavior. + return "?".concat(src_MatomoUrl_MatomoUrl.stringify(urlParams)); + }, + defaultDate: function defaultDate() { + if (Matomo_Matomo.period === 'range') { + return "".concat(Matomo_Matomo.startDateString, ",").concat(Matomo_Matomo.endDateString); + } - element.on('click', 'td[data-month]', function (event) { - var $cell = DatePickervue_type_script_lang_ts_$(event.target).closest('td'); - var month = parseInt($cell.attr('data-month'), 10); - var year = parseInt($cell.attr('data-year'), 10); - var day = parseInt($cell.children('a,span').text(), 10); - context.emit('dateSelect', { - date: new Date(year, month, day) - }); - }); - var renderPostProcessed = stepMonthsChanged(); - viewDateChanged(); - enableDisableMonthDropdown(); + var dateRange = Range_RangePeriod.getLastNRange(Matomo_Matomo.period, 30, Matomo_Matomo.currentDateString).getDateRange(); + var piwikMinDate = new Date(Matomo_Matomo.minDateYear, Matomo_Matomo.minDateMonth - 1, Matomo_Matomo.minDateDay); - if (!renderPostProcessed) { - onJqueryUiRenderedPicker(); + if (dateRange[0] < piwikMinDate) { + dateRange[0] = piwikMinDate; } - setDatePickerCellColors(); - }); - return { - root: root - }; + var startDateStr = format(dateRange[0]); + var endDateStr = format(dateRange[1]); + return "".concat(startDateStr, ",").concat(endDateStr); + } } })); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DatePicker/DatePicker.vue?vue&type=script&lang=ts +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Sparkline/Sparkline.vue?vue&type=script&lang=ts -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DatePicker/DatePicker.vue +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Sparkline/Sparkline.vue -DatePickervue_type_script_lang_ts.render = DatePickervue_type_template_id_c8c462d2_render +Sparklinevue_type_script_lang_ts.render = Sparklinevue_type_template_id_693cd955_render -/* harmony default export */ var DatePicker = (DatePickervue_type_script_lang_ts); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DatePicker/DatePicker.adapter.ts +/* harmony default export */ var Sparkline = (Sparklinevue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Sparkline/Sparkline.adapter.ts /*! * Matomo - free/libre analytics platform * @@ -4383,263 +11590,302 @@ DatePickervue_type_script_lang_ts.render = DatePickervue_type_template_id_c8c462 */ -/* harmony default export */ var DatePicker_adapter = (createAngularJsAdapter({ - component: DatePicker, +/* harmony default export */ var Sparkline_adapter = (createAngularJsAdapter({ + component: Sparkline, scope: { - selectedDateStart: { - angularJsBind: '<' - }, - selectedDateEnd: { - angularJsBind: '<' - }, - highlightedDateStart: { - angularJsBind: '<' - }, - highlightedDateEnd: { + seriesIndices: { angularJsBind: '<' }, - viewDate: { - angularJsBind: '<' - }, - stepMonths: { - angularJsBind: '<' - }, - disableMonthDropdown: { - angularJsBind: '<' - }, - options: { + params: { angularJsBind: '<' - }, - cellHover: { - angularJsBind: '&' - }, - cellHoverLeave: { - angularJsBind: '&' - }, - dateSelect: { - angularJsBind: '&' - } - }, - directiveName: 'piwikDatePicker', - events: { - 'cell-hover': function cellHover(event, scope, element, attrs, $timeout) { - $timeout(); // trigger new digest - }, - 'cell-hover-leave': function cellHoverLeave(event, scope, element, attrs, $timeout) { - $timeout(); // trigger new digest - }, - 'date-select': function dateSelect(event, scope, element, attrs, $timeout) { - $timeout(); // trigger new digest } }, - $inject: ['$timeout'] + directiveName: 'piwikSparkline', + restrict: 'E' })); -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.vue?vue&type=template&id=d9f4b538 +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Progressbar/Progressbar.vue?vue&type=template&id=0048ddd7 -var DateRangePickervue_type_template_id_d9f4b538_hoisted_1 = { - id: "calendarRangeFrom" +var Progressbarvue_type_template_id_0048ddd7_hoisted_1 = { + class: "progressbar" }; -var DateRangePickervue_type_template_id_d9f4b538_hoisted_2 = { - id: "calendarRangeTo" +var Progressbarvue_type_template_id_0048ddd7_hoisted_2 = { + class: "progress" }; -function DateRangePickervue_type_template_id_d9f4b538_render(_ctx, _cache, $props, $setup, $data, $options) { - var _component_DatePicker = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("DatePicker"); - - return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])(external_commonjs_vue_commonjs2_vue_root_Vue_["Fragment"], null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", DateRangePickervue_type_template_id_d9f4b538_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h6", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_DateRangeFrom')) + " ", 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { - type: "text", - id: "inputCalendarFrom", - name: "inputCalendarFrom", - class: "browser-default", - "onUpdate:modelValue": _cache[0] || (_cache[0] = function ($event) { - return _ctx.startDateText = $event; - }), - onChange: _cache[1] || (_cache[1] = function ($event) { - return _ctx.onRangeInputChanged('from', $event); - }), - onKeyup: _cache[2] || (_cache[2] = function ($event) { - return _ctx.handleEnterPress($event); - }) - }, null, 544), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vModelText"], _ctx.startDateText]])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_DatePicker, { - id: "calendarFrom", - "view-date": _ctx.startDate, - "selected-date-start": _ctx.fromPickerSelectedDates[0], - "selected-date-end": _ctx.fromPickerSelectedDates[1], - "highlighted-date-start": _ctx.fromPickerHighlightedDates[0], - "highlighted-date-end": _ctx.fromPickerHighlightedDates[1], - onDateSelect: _cache[3] || (_cache[3] = function ($event) { - return _ctx.setStartRangeDate($event.date); - }), - onCellHover: _cache[4] || (_cache[4] = function ($event) { - return _ctx.fromPickerHighlightedDates = _ctx.getNewHighlightedDates($event.date, $event.$cell); - }), - onCellHoverLeave: _cache[5] || (_cache[5] = function ($event) { - return _ctx.fromPickerHighlightedDates = [null, null]; - }) - }, null, 8, ["view-date", "selected-date-start", "selected-date-end", "highlighted-date-start", "highlighted-date-end"])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", DateRangePickervue_type_template_id_d9f4b538_hoisted_2, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("h6", null, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createTextVNode"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["toDisplayString"])(_ctx.translate('General_DateRangeTo')) + " ", 1), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("input", { - type: "text", - id: "inputCalendarTo", - name: "inputCalendarTo", - class: "browser-default", - "onUpdate:modelValue": _cache[6] || (_cache[6] = function ($event) { - return _ctx.endDateText = $event; - }), - onChange: _cache[7] || (_cache[7] = function ($event) { - return _ctx.onRangeInputChanged('to', $event); - }), - onKeyup: _cache[8] || (_cache[8] = function ($event) { - return _ctx.handleEnterPress($event); - }) - }, null, 544), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vModelText"], _ctx.endDateText]])]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createVNode"])(_component_DatePicker, { - id: "calendarTo", - "view-date": _ctx.endDate, - "selected-date-start": _ctx.toPickerSelectedDates[0], - "selected-date-end": _ctx.toPickerSelectedDates[1], - "highlighted-date-start": _ctx.toPickerHighlightedDates[0], - "highlighted-date-end": _ctx.toPickerHighlightedDates[1], - onDateSelect: _cache[9] || (_cache[9] = function ($event) { - return _ctx.setEndRangeDate($event.date); - }), - onCellHover: _cache[10] || (_cache[10] = function ($event) { - return _ctx.toPickerHighlightedDates = _ctx.getNewHighlightedDates($event.date, $event.$cell); - }), - onCellHoverLeave: _cache[11] || (_cache[11] = function ($event) { - return _ctx.toPickerHighlightedDates = [null, null]; - }) - }, null, 8, ["view-date", "selected-date-start", "selected-date-end", "highlighted-date-start", "highlighted-date-end"])])], 64); -} -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.vue?vue&type=template&id=d9f4b538 -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/@vue/cli-plugin-typescript/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-3!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.vue?vue&type=script&lang=ts +var Progressbarvue_type_template_id_0048ddd7_hoisted_3 = /*#__PURE__*/Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("img", { + src: "plugins/Morpheus/images/loading-blue.gif", + style: { + "margin-right": "3.5px" + } +}, null, -1); +var Progressbarvue_type_template_id_0048ddd7_hoisted_4 = ["innerHTML"]; +function Progressbarvue_type_template_id_0048ddd7_render(_ctx, _cache, $props, $setup, $data, $options) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", Progressbarvue_type_template_id_0048ddd7_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", Progressbarvue_type_template_id_0048ddd7_hoisted_2, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("div", { + class: "determinate", + style: Object(external_commonjs_vue_commonjs2_vue_root_Vue_["normalizeStyle"])([{ + "width": "0" + }, { + width: "".concat(_ctx.actualProgress, "%") + }]) + }, null, 4)]), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["withDirectives"])(Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", null, [Progressbarvue_type_template_id_0048ddd7_hoisted_3, Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementVNode"])("span", { + class: "label", + innerHTML: _ctx.$sanitize(_ctx.label) + }, null, 8, Progressbarvue_type_template_id_0048ddd7_hoisted_4)], 512), [[external_commonjs_vue_commonjs2_vue_root_Vue_["vShow"], !!_ctx.label]])]); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Progressbar/Progressbar.vue?vue&type=template&id=0048ddd7 +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/Progressbar/Progressbar.vue?vue&type=script&lang=ts -/* harmony default export */ var DateRangePickervue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ +/* harmony default export */ var Progressbarvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ props: { - startDate: String, - endDate: String - }, - components: { - DatePicker: DatePicker + progress: { + type: Number, + required: true + }, + label: String }, - data: function data() { - var startDate = null; + computed: { + actualProgress: function actualProgress() { + if (this.progress > 100) { + return 100; + } - try { - startDate = parseDate(this.startDate); - } catch (e) {// ignore + if (this.progress < 0) { + return 0; + } + + return this.progress; } + } +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Progressbar/Progressbar.vue?vue&type=script&lang=ts + +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Progressbar/Progressbar.vue - var endDate = null; - try { - endDate = parseDate(this.endDate); - } catch (e) {// ignore - } - return { - fromPickerSelectedDates: [startDate, startDate], - toPickerSelectedDates: [endDate, endDate], - fromPickerHighlightedDates: [null, null], - toPickerHighlightedDates: [null, null], - startDateText: this.startDate, - endDateText: this.endDate - }; - }, - emits: ['rangeChange', 'submit'], - watch: { - startDate: function startDate() { - this.startDateText = this.startDate; - this.setStartRangeDateFromStr(this.startDate); +Progressbarvue_type_script_lang_ts.render = Progressbarvue_type_template_id_0048ddd7_render + +/* harmony default export */ var Progressbar = (Progressbarvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/Progressbar/Progressbar.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + + +/* harmony default export */ var Progressbar_adapter = (createAngularJsAdapter({ + component: Progressbar, + scope: { + progress: { + angularJsBind: '=' }, - endDate: function endDate() { - this.endDateText = this.endDate; - this.setEndRangeDateFromStr(this.endDate); + label: { + angularJsBind: '=' } }, - mounted: function mounted() { - this.rangeChanged(); // emit with initial range pair + directiveName: 'piwikProgressbar' +})); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ContentIntro/ContentIntro.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/* harmony default export */ var ContentIntro = ({ + mounted: function mounted(el) { + el.classList.add('piwik-content-intro'); }, - methods: { - setStartRangeDate: function setStartRangeDate(date) { - this.fromPickerSelectedDates = [date, date]; - this.rangeChanged(); - }, - setEndRangeDate: function setEndRangeDate(date) { - this.toPickerSelectedDates = [date, date]; - this.rangeChanged(); - }, - onRangeInputChanged: function onRangeInputChanged(source, event) { - if (source === 'from') { - this.setStartRangeDateFromStr(event.target.value); - } else { - this.setEndRangeDateFromStr(event.target.value); - } - }, - getNewHighlightedDates: function getNewHighlightedDates(date, $cell) { - if ($cell.hasClass('ui-datepicker-unselectable')) { - return null; - } + updated: function updated(el) { + // classes can be overwritten when elements bind to :class, nextTick + using + // updated avoids this problem (and doing in both mounted and updated avoids a temporary + // state where the classes aren't added) + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["nextTick"])(function () { + el.classList.add('piwik-content-intro'); + }); + } +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ContentIntro/ContentIntro.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +function piwikContentIntro() { + return { + restrict: 'A', + link: function piwikContentIntroLink(scope, element) { + ContentIntro.mounted(element[0]); + } + }; +} +window.angular.module('piwikApp').directive('piwikContentIntro', piwikContentIntro); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ContentTable/ContentTable.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +/* harmony default export */ var ContentTable = ({ + mounted: function mounted(el) { + el.classList.add('card', 'card-table', 'entityTable'); + }, + updated: function updated(el) { + // classes can be overwritten when elements bind to :class, nextTick + using + // updated avoids this problem (and doing in both mounted and updated avoids a temporary + // state where the classes aren't added) + Object(external_commonjs_vue_commonjs2_vue_root_Vue_["nextTick"])(function () { + el.classList.add('card', 'card-table', 'entityTable'); + }); + } +}); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/ContentTable/ContentTable.adapter.ts +/*! + * Matomo - free/libre analytics platform + * + * @link https://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ - return [date, date]; - }, - handleEnterPress: function handleEnterPress($event) { - if ($event.keyCode !== 13) { - return; - } +function piwikContentTable() { + return { + restrict: 'A', + link: function piwikContentTableLink(scope, element) { + ContentTable.mounted(element[0]); + } + }; +} +window.angular.module('piwikApp').directive('piwikContentTable', piwikContentTable); +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/AjaxForm/AjaxForm.vue?vue&type=template&id=00d5220c - this.$emit('submit', { - start: this.startDate, - end: this.endDate - }); - }, - setStartRangeDateFromStr: function setStartRangeDateFromStr(dateStr) { - var startDateParsed; +var AjaxFormvue_type_template_id_00d5220c_hoisted_1 = { + ref: "root" +}; +function AjaxFormvue_type_template_id_00d5220c_render(_ctx, _cache, $props, $setup, $data, $options) { + return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createElementBlock"])("div", AjaxFormvue_type_template_id_00d5220c_hoisted_1, [Object(external_commonjs_vue_commonjs2_vue_root_Vue_["renderSlot"])(_ctx.$slots, "default", { + formData: _ctx.formData, + submitApiMethod: _ctx.submitApiMethod, + sendJsonPayload: _ctx.sendJsonPayload, + noErrorNotification: _ctx.noErrorNotification, + noSuccessNotification: _ctx.noSuccessNotification, + submitForm: _ctx.submitForm, + isSubmitting: _ctx.isSubmitting, + successfulPostResponse: _ctx.successfulPostResponse, + errorPostResponse: _ctx.errorPostResponse + })], 512); +} +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/AjaxForm/AjaxForm.vue?vue&type=template&id=00d5220c - try { - startDateParsed = parseDate(dateStr); - } catch (e) { - this.startDateText = this.startDate; - } +// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-2!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/AjaxForm/AjaxForm.vue?vue&type=script&lang=ts - if (startDateParsed) { - this.fromPickerSelectedDates = [startDateParsed, startDateParsed]; - } - this.rangeChanged(); + + +var AjaxFormvue_type_script_lang_ts_window = window, + AjaxFormvue_type_script_lang_ts_$ = AjaxFormvue_type_script_lang_ts_window.$; +/** + * Example usage: + * + * + * + * + * + * Data does not flow upwards in any way. :form-data is used for submitForm(), and the + * containing component binds to properties of the object in controls to fill the object. + */ + +/* harmony default export */ var AjaxFormvue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ + props: { + formData: { + type: Object, + required: true }, - setEndRangeDateFromStr: function setEndRangeDateFromStr(dateStr) { - var endDateParsed; + submitApiMethod: { + type: String, + required: true + }, + sendJsonPayload: Boolean, + noErrorNotification: Boolean, + noSuccessNotification: Boolean + }, + data: function data() { + return { + isSubmitting: false, + successfulPostResponse: null, + errorPostResponse: null + }; + }, + emits: ['update:modelValue'], + mounted: function mounted() { + var _this = this; - try { - endDateParsed = parseDate(dateStr); - } catch (e) { - this.endDateText = this.endDate; - } + // on submit call controller submit method + AjaxFormvue_type_script_lang_ts_$(this.$refs.root).on('click', 'input[type=submit]', function () { + _this.submitForm(); + }); + }, + methods: { + submitForm: function submitForm() { + var _this2 = this; - if (endDateParsed) { - this.toPickerSelectedDates = [endDateParsed, endDateParsed]; + this.successfulPostResponse = null; + this.errorPostResponse = null; + var postParams = this.formData; + + if (this.sendJsonPayload) { + postParams = { + data: JSON.stringify(this.formData) + }; } - this.rangeChanged(); - }, - rangeChanged: function rangeChanged() { - this.$emit('rangeChange', { - start: format(this.fromPickerSelectedDates[0]), - end: format(this.toPickerSelectedDates[0]) + this.isSubmitting = true; + AjaxHelper_AjaxHelper.post({ + module: 'API', + method: this.submitApiMethod + }, postParams, { + createErrorNotification: !this.noErrorNotification + }).then(function (response) { + _this2.successfulPostResponse = response; + + if (!_this2.noSuccessNotification) { + var notificationInstanceId = Notifications_store.show({ + message: translate('General_YourChangesHaveBeenSaved'), + context: 'success', + type: 'toast', + id: 'ajaxHelper' + }); + Notifications_store.scrollToNotification(notificationInstanceId); + } + }).catch(function (error) { + _this2.errorPostResponse = error.message; + }).finally(function () { + _this2.isSubmitting = false; }); } } })); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.vue?vue&type=script&lang=ts +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/AjaxForm/AjaxForm.vue?vue&type=script&lang=ts -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.vue +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/AjaxForm/AjaxForm.vue -DateRangePickervue_type_script_lang_ts.render = DateRangePickervue_type_template_id_d9f4b538_render +AjaxFormvue_type_script_lang_ts.render = AjaxFormvue_type_template_id_00d5220c_render -/* harmony default export */ var DateRangePicker = (DateRangePickervue_type_script_lang_ts); -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/DateRangePicker/DateRangePicker.adapter.ts +/* harmony default export */ var AjaxForm = (AjaxFormvue_type_script_lang_ts); +// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/AjaxForm/AjaxForm.adapter.ts /*! * Matomo - free/libre analytics platform * @@ -4648,137 +11894,163 @@ DateRangePickervue_type_script_lang_ts.render = DateRangePickervue_type_template */ -/* harmony default export */ var DateRangePicker_adapter = (createAngularJsAdapter({ - component: DateRangePicker, - scope: { - startDate: { - angularJsBind: '<' - }, - endDate: { - angularJsBind: '<' - }, - rangeChange: { - angularJsBind: '&' - }, - submit: { - angularJsBind: '&' - } - }, - directiveName: 'piwikDateRangePicker', - restrict: 'E' -})); -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-babel/node_modules/cache-loader/dist/cjs.js??ref--12-0!./node_modules/@vue/cli-plugin-babel/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist/templateLoader.js??ref--6!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/PeriodDatePicker/PeriodDatePicker.vue?vue&type=template&id=effd17b0 - -function PeriodDatePickervue_type_template_id_effd17b0_render(_ctx, _cache, $props, $setup, $data, $options) { - var _component_DatePicker = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["resolveComponent"])("DatePicker"); - - return Object(external_commonjs_vue_commonjs2_vue_root_Vue_["openBlock"])(), Object(external_commonjs_vue_commonjs2_vue_root_Vue_["createBlock"])(_component_DatePicker, { - "selected-date-start": _ctx.selectedDates[0], - "selected-date-end": _ctx.selectedDates[1], - "highlighted-date-start": _ctx.highlightedDates[0], - "highlighted-date-end": _ctx.highlightedDates[1], - "view-date": _ctx.viewDate, - "step-months": _ctx.period === 'year' ? 12 : 1, - "disable-month-dropdown": _ctx.period === 'year', - onCellHover: _cache[0] || (_cache[0] = function ($event) { - return _ctx.onHoverNormalCell($event.date, $event.$cell); - }), - onCellHoverLeave: _cache[1] || (_cache[1] = function ($event) { - return _ctx.onHoverLeaveNormalCells(); - }), - onDateSelect: _cache[2] || (_cache[2] = function ($event) { - return _ctx.onDateSelected($event.date); - }) - }, null, 8, ["selected-date-start", "selected-date-end", "highlighted-date-start", "highlighted-date-end", "view-date", "step-months", "disable-month-dropdown"]); -} -// CONCATENATED MODULE: ./plugins/CoreHome/vue/src/PeriodDatePicker/PeriodDatePicker.vue?vue&type=template&id=effd17b0 -// CONCATENATED MODULE: ./node_modules/@vue/cli-plugin-typescript/node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/@vue/cli-plugin-typescript/node_modules/thread-loader/dist/cjs.js!./node_modules/babel-loader/lib!./node_modules/@vue/cli-plugin-typescript/node_modules/ts-loader??ref--14-3!./node_modules/@vue/cli-service/node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/@vue/cli-service/node_modules/vue-loader-v16/dist??ref--0-1!./plugins/CoreHome/vue/src/PeriodDatePicker/PeriodDatePicker.vue?vue&type=script&lang=ts +var AjaxForm_adapter_window = window, + AjaxForm_adapter_$ = AjaxForm_adapter_window.$; +/** + * AngularJS directive that manages an AJAX form. + * + * This directive will detect inputs & selects defined within an element and when a + * submit button is clicked, will post data from the inputs & selects to a Piwik API method. + * + * When the POST request is finished the result will, by default, be displayed as a + * notification. + * + * This directive accepts the following attributes: + * + * - **submit-api-method**: **required** The Piwik API method that handles the POST request. + * - **send-json-payload**: Whether to send the data as a form encoded URL or to send it as JSON. + * If sending as JSON, the payload will still be a form encoded value, + * but will contain a JSON object like `{data: {...form data...}}`. + * + * This is for forms with lots of fields where having the same number + * of parameters in an API method would not be desired. + * - **no-error-notification**: If true, does not display an error notification if the AJAX post + * fails. + * - **no-success-notification**: If true, does not display an error notification if the AJAX + * results in success. + * + * **Custom Success/Error Handling** + * + * On success/failure, the response will be stored in controller scope. Child elements of a + * piwik-ajax-form element can access this data, and thus, can customize what happens when + * a form submit succeeds/fails. + * + * See the ajax-form.controller.js file for more info. + * + * Usage: + * + *
+ * + *

My Form

+ * + * + * + * + *
ERROR!
+ *
+ * @deprecated + */ +function piwikAjaxForm($parse) { + return { + restrict: 'A', + scope: { + submitApiMethod: '=', + sendJsonPayload: '=', + noErrorNotification: '=', + noSuccessNotification: '=', + useCustomDataBinding: '=' + }, + require: '?ngModel', + transclude: true, + compile: function piwikAjaxFormCompile(compileElement, compileAttrs) { + compileAttrs.noErrorNotification = !!compileAttrs.noErrorNotification; // eslint-disable-next-line @typescript-eslint/no-explicit-any + + return function piwikAjaxFormLink(scope, element, attrs, ngModel, transclude) { + if (!scope.submitApiMethod) { + throw new Error('submitApiMethod is required'); + } + scope.ajaxForm = {}; + scope.ajaxForm.submitApiMethod = scope.submitApiMethod; + scope.ajaxForm.sendJsonPayload = scope.sendJsonPayload; + scope.ajaxForm.noErrorNotification = scope.noErrorNotification; + scope.ajaxForm.noSuccessNotification = scope.noSuccessNotification; + scope.ajaxForm.data = {}; // if a model is supplied, initiate form data w/ model value + + if (ngModel) { + // probably redundant, but I cannot find another way to get the ng model value here + var ngModelGetter = $parse(attrs.ngModel); + scope.ajaxForm.data = ngModelGetter(scope.$parent); + } + var specialBindDirective = { + mounted: function mounted(el, binding) { + scope.ajaxForm.submitForm = binding.value.submitForm; + } + }; + var rootTemplate = "\n \n \n "; + var app = createVueApp({ + template: rootTemplate, + data: function data() { + return scope.ajaxForm; + }, + setup: function setup() { + var transcludeTarget = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])(null); + return { + transcludeTarget: transcludeTarget + }; + } + }); + app.component('AjaxForm', AjaxForm); + app.directive('SpecialBindDirective', specialBindDirective); + var vm = app.mount(element[0]); + element.on('$destroy', function () { + app.unmount(); + }); -var piwikMinDate = new Date(Matomo_Matomo.minDateYear, Matomo_Matomo.minDateMonth - 1, Matomo_Matomo.minDateDay); -var piwikMaxDate = new Date(Matomo_Matomo.maxDateYear, Matomo_Matomo.maxDateMonth - 1, Matomo_Matomo.maxDateDay); -/* harmony default export */ var PeriodDatePickervue_type_script_lang_ts = (Object(external_commonjs_vue_commonjs2_vue_root_Vue_["defineComponent"])({ - props: { - period: String, - date: [String, Date] - }, - components: { - DatePicker: DatePicker - }, - emits: ['select'], - setup: function setup(props, context) { - var viewDate = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])(props.date); - var selectedDates = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])([null, null]); - var highlightedDates = Object(external_commonjs_vue_commonjs2_vue_root_Vue_["ref"])([null, null]); + function setFormValueFromInput(inputElement, skipScopeApply) { + var name = AjaxForm_adapter_$(inputElement).attr('name'); + var val; - function getBoundedDateRange(date) { - var dates = Periods_Periods.get(props.period).parse(date).getDateRange(); // make sure highlighted date range is within min/max date range + if (AjaxForm_adapter_$(inputElement).attr('type') === 'checkbox') { + val = AjaxForm_adapter_$(inputElement).is(':checked'); + } else { + val = AjaxForm_adapter_$(inputElement).val(); + } - dates[0] = piwikMinDate < dates[0] ? dates[0] : piwikMinDate; - dates[1] = piwikMaxDate > dates[1] ? dates[1] : piwikMaxDate; - return dates; - } + scope.ajaxForm.data[name] = val; - function onHoverNormalCell(cellDate, $cell) { - var isOutOfMinMaxDateRange = cellDate < piwikMinDate || cellDate > piwikMaxDate; // don't highlight anything if the period is month or day, and we're hovering over calendar - // whitespace. since there are no dates, it's doesn't make sense what you're selecting. + if (!skipScopeApply) { + setTimeout(function () { + scope.$apply(); + }, 0); + } + } // on change of any input, change appropriate value in model, but only if requested - var shouldNotHighlightFromWhitespace = $cell.hasClass('ui-datepicker-other-month') && (props.period === 'month' || props.period === 'day'); - if (isOutOfMinMaxDateRange || shouldNotHighlightFromWhitespace) { - highlightedDates.value = [null, null]; - return; - } + if (!scope.useCustomDataBinding) { + element.on('change', 'input,select', function (event) { + setFormValueFromInput(event.target); + }); + } // make sure child elements can access this directive's scope + // eslint-disable-next-line @typescript-eslint/no-explicit-any - highlightedDates.value = getBoundedDateRange(cellDate); - } - function onHoverLeaveNormalCells() { - highlightedDates.value = [null, null]; - } + transclude(scope, function (clone, transcludeScope) { + if (!transcludeScope.useCustomDataBinding) { + var $inputs = clone.find('input,select').not('[type=submit]'); // initialize form data to input values (include