From 6d80b16cf4f9b261596f843a71a13ed5c5f5b8a3 Mon Sep 17 00:00:00 2001 From: Thomas ZILLIOX Date: Tue, 11 Nov 2025 16:23:36 +0100 Subject: [PATCH 01/17] Allow to start/unstar stored segment --- core/Updates/5.6.0-b2.php | 39 +++++++++++++ core/Version.php | 2 +- plugins/SegmentEditor/API.php | 31 ++++++++++ plugins/SegmentEditor/Model.php | 1 + .../SegmentEditor/javascripts/Segmentation.js | 57 +++++++++++++++---- .../stylesheets/segmentation.less | 51 +++++++++++++---- 6 files changed, 157 insertions(+), 24 deletions(-) create mode 100644 core/Updates/5.6.0-b2.php diff --git a/core/Updates/5.6.0-b2.php b/core/Updates/5.6.0-b2.php new file mode 100644 index 00000000000..f22c6342edb --- /dev/null +++ b/core/Updates/5.6.0-b2.php @@ -0,0 +1,39 @@ +migration = $factory; + } + + public function getMigrations(Updater $updater) + { + return [ + $this->migration->db->addColumn('segment', 'starred', 'TINYINT(1) NOT NULL DEFAULT 0'), + ]; + } + + public function doUpdate(Updater $updater) + { + $updater->executeMigrations(__FILE__, $this->getMigrations($updater)); + } +} diff --git a/core/Version.php b/core/Version.php index bff370eca44..ae44c6228be 100644 --- a/core/Version.php +++ b/core/Version.php @@ -22,7 +22,7 @@ final class Version * The current Matomo version. * @var string */ - public const VERSION = '5.7.0-alpha'; + public const VERSION = '5.6.0-b2'; public const MAJOR_VERSION = 5; diff --git a/plugins/SegmentEditor/API.php b/plugins/SegmentEditor/API.php index deaa0e51daa..278eab9be9c 100644 --- a/plugins/SegmentEditor/API.php +++ b/plugins/SegmentEditor/API.php @@ -329,6 +329,7 @@ public function add( 'enable_only_idsite' => (int) $idSite, 'auto_archive' => (int) $autoArchive, 'ts_created' => Date::now()->getDatetime(), + 'starred' => 0, 'deleted' => 0, ); @@ -348,6 +349,36 @@ public function add( return $id; } + /** + * Stars a stored segment. + * + * @param int $idSegment + * @throws Exception if the user is not logged in or does not have the required permissions. + */ + public function star(int $idSegment): void + { + $segment = $this->getSegmentOrFail($idSegment); + $this->checkUserCanEditOrDeleteSegment($segment); + $bind = array('starred' => 1); + + $this->getModel()->updateSegment($idSegment, $bind); + } + + /** + * Unstars a stored segment. + * + * @param int $idSegment + * @throws Exception if the user is not logged in or does not have the required permissions. + */ + public function unstar(int $idSegment): void + { + $segment = $this->getSegmentOrFail($idSegment); + $this->checkUserCanEditOrDeleteSegment($segment); + $bind = array('starred' => 0); + + $this->getModel()->updateSegment($idSegment, $bind); + } + /** * Returns a stored segment by ID * diff --git a/plugins/SegmentEditor/Model.php b/plugins/SegmentEditor/Model.php index 10f9332ba3c..b9e683b1159 100644 --- a/plugins/SegmentEditor/Model.php +++ b/plugins/SegmentEditor/Model.php @@ -282,6 +282,7 @@ public static function install() `auto_archive` tinyint(4) NOT NULL default 0, `ts_created` TIMESTAMP NULL, `ts_last_edit` TIMESTAMP NULL, + `starred` tinyint(4) NOT NULL default 0, `deleted` tinyint(4) NOT NULL default 0, PRIMARY KEY (`idsegment`)"; diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js index 5a08f8f8834..0fce52370cf 100644 --- a/plugins/SegmentEditor/javascripts/Segmentation.js +++ b/plugins/SegmentEditor/javascripts/Segmentation.js @@ -233,21 +233,35 @@ Segmentation = (function($) { } - injClass = ""; + injClass = []; var checkSelected = segment.definition; + var escapedSegmentName = (segment.definition).replace(/"/g, '"'); - if( checkSelected == self.currentSegmentStr || - checkSelected == decodeURIComponent(self.currentSegmentStr) - ) { - injClass = 'class="segmentSelected"'; + if (checkSelected === self.currentSegmentStr || checkSelected === decodeURIComponent(self.currentSegmentStr)) { + injClass.push('segmentSelected'); + } + if (segment.starred) { + injClass.push('segmentStarred'); } - listHtml += '
  • '+getSegmentName(segment)+''; - if(self.segmentAccess == "write") { - listHtml += ''; + listHtml += '' + + '
  • ' + + '' + getSegmentName(segment) + ''; + if (self.segmentAccess === "write") { + listHtml += '' + + ''; + listHtml += ''; } - if (comparisonService.isComparisonEnabled() - || comparisonService.isComparisonEnabled() === null // may not be initialized since this code is outside of Vue + if ( + comparisonService.isComparisonEnabled() || + comparisonService.isComparisonEnabled() === null // may not be initialized since this code is outside of Vue ) { listHtml += ''; } @@ -427,6 +441,27 @@ Segmentation = (function($) { e.preventDefault(); }); + self.target.on('click', '[data-star]', function (e) { + e.stopPropagation(); + e.preventDefault(); + const $root = $(this).closest('li'); + const idSegment = $root.data('idsegment'); + const segment = getSegmentFromId(idSegment); + segment.starred = !segment.starred; + const method = segment.starred ? 'star' : 'unstar'; + $root.toggleClass('segmentStarred', segment.starred); + + var ajaxHandler = new ajaxHelper(); + ajaxHandler.addParams({ + "module": 'API', + "format": 'json', + "method": 'SegmentEditor.' + method, + "userLogin": piwik.userLogin, + "idSegment": idSegment, + }, 'GET'); + ajaxHandler.send(); + }); + self.target.on('click', '.compareSegment', function (e) { e.stopPropagation(); e.preventDefault(); diff --git a/plugins/SegmentEditor/stylesheets/segmentation.less b/plugins/SegmentEditor/stylesheets/segmentation.less index 0522ac4e95d..7b188ea8101 100644 --- a/plugins/SegmentEditor/stylesheets/segmentation.less +++ b/plugins/SegmentEditor/stylesheets/segmentation.less @@ -233,6 +233,8 @@ div.scrollable { } .segmentationContainer .submenu ul { + display: flex; + flex-direction: column; color: @theme-color-text-light; float: none; font-size: 11px; @@ -243,11 +245,19 @@ div.scrollable { padding-top: 10px; } - .segmentationContainer .submenu ul li { padding: 2px 0 2px 0; margin: 3px 0 0 0; cursor: pointer; + order: 2; +} + +.segmentationContainer .submenu ul li[data-idsegment=""] { + order: 0; +} + +.segmentationContainer .submenu ul li.segmentStarred { + order: 1; } .segmentationContainer .submenu ul li:hover, @@ -269,18 +279,25 @@ div.scrollable { } .segmentationContainer ul.submenu > li { - span.editSegment, span.compareSegment { - display: block; - float: right; - text-align: center; - margin-right: 4px; - font-weight: normal; + span.editSegment, + span.compareSegment, + .starSegment { + flex: none; width: 16px; height: 16px; - .opacity(0.5); + + // Button reset + padding: 0; + border: 0; + background: transparent; + cursor: pointer; + + font-weight: normal; + text-align: center; + opacity: 0.5; &:hover { - .opacity(1); + opacity: 1; } } @@ -289,13 +306,22 @@ div.scrollable { order: 3; } + .starSegment { + order: 2; + } + + .segmentStarred .starSegment path { + fill: black; + } + span.compareSegment { background: url(plugins/Morpheus/images/compare.svg) no-repeat; background-size: cover; order: 2; &.allVisitsCompareSegment { - margin-right: 24px; + margin-left: 20px; + margin-right: 20px; } } span.compareSegment.no-click { @@ -398,6 +424,8 @@ html.comparisonsDisabled .segmentationContainer ul.submenu { li { display: flex; + align-items: center; + gap: 4px; } } @@ -523,9 +551,8 @@ a.metric_category { } .segname { - width: ~"calc(100% - 40px)"; + flex: auto; padding-right: 10px; - display: inline-block; order: 1; } From a3a209c9de9a59d7a4dba0ff47600303787aa157 Mon Sep 17 00:00:00 2001 From: Thomas ZILLIOX Date: Fri, 14 Nov 2025 20:07:37 +0100 Subject: [PATCH 02/17] Add and Update tests --- .../tests/Integration/SegmentEditorTest.php | 25 +++++++++++++++++-- .../tests/UI/SegmentSelectorEditor_spec.js | 12 +++++++++ ...mentSelectorEditorTest_1_selector_open.png | 4 +-- ...tSelectorEditorTest_1_selector_starred.png | 3 +++ ...electorEditorTest_1_selector_unstarred.png | 3 +++ ...ctorEditorTest_2_segment_editor_update.png | 4 +-- .../SegmentSelectorEditorTest_deleted.png | 4 +-- ...enabled_create_realtime_segments_saved.png | 4 +-- .../SegmentSelectorEditorTest_saved.png | 4 +-- .../SegmentSelectorEditorTest_updated.png | 4 +-- .../Segment/SegmentUnavailableTest.php | 1 + 11 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_starred.png create mode 100644 plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_unstarred.png diff --git a/plugins/SegmentEditor/tests/Integration/SegmentEditorTest.php b/plugins/SegmentEditor/tests/Integration/SegmentEditorTest.php index 6da40901e2c..589b84ff0c9 100644 --- a/plugins/SegmentEditor/tests/Integration/SegmentEditorTest.php +++ b/plugins/SegmentEditor/tests/Integration/SegmentEditorTest.php @@ -82,6 +82,7 @@ public function testAddAndGetSimpleSegment() 'enable_only_idsite' => '0', 'auto_archive' => '0', 'ts_last_edit' => null, + 'starred' => '0', 'deleted' => '0', ); @@ -112,6 +113,7 @@ public function testAddAndGetAnotherSegment() 'enable_only_idsite' => '1', 'auto_archive' => '1', 'ts_last_edit' => null, + 'starred' => '0', 'deleted' => '0', ); unset($segment['ts_created']); @@ -145,7 +147,7 @@ public function testUpdateSegment() $this->clearReArchiveList(); $updatedSegment = array( - 'idsegment' => $idSegment2, + 'idsegment' => '' . $idSegment2, 'name' => 'NEW name', 'definition' => 'searches==0', 'hash' => md5('searches==0'), @@ -155,6 +157,7 @@ public function testUpdateSegment() 'ts_last_edit' => Date::now()->getDatetime(), 'ts_created' => Date::now()->getDatetime(), 'login' => Piwik::getCurrentUserLogin(), + 'starred' => '0', 'deleted' => '0', ); API::getInstance()->update( @@ -178,11 +181,29 @@ public function testUpdateSegment() $this->assertEquals($newSegment, $updatedSegment); - // Check the other segmenet was not updated + // Check the other segment was not updated $newSegment = API::getInstance()->get($idSegment1); $this->assertEquals($newSegment['name'], $nameSegment1); } + public function testStarUnstarSegment() + { + // Set up initial conditions + $idSegment = API::getInstance()->add('hello', 'searches==0'); + $segment = API::getInstance()->get($idSegment); + $this->assertEquals('0', $segment['starred']); + + // Star segment + API::getInstance()->star($idSegment); + $starredSegment = API::getInstance()->get($idSegment); + $this->assertEquals('1', $starredSegment['starred']); + + // Unstar segment + API::getInstance()->unstar($idSegment); + $unstarredSegment = API::getInstance()->get($idSegment); + $this->assertEquals('0', $unstarredSegment['starred']); + } + public function testDeleteSegment() { $this->expectNotToPerformAssertions(); diff --git a/plugins/SegmentEditor/tests/UI/SegmentSelectorEditor_spec.js b/plugins/SegmentEditor/tests/UI/SegmentSelectorEditor_spec.js index 6ec4a34cb1d..6571d363625 100644 --- a/plugins/SegmentEditor/tests/UI/SegmentSelectorEditor_spec.js +++ b/plugins/SegmentEditor/tests/UI/SegmentSelectorEditor_spec.js @@ -43,6 +43,18 @@ describe("SegmentSelectorEditorTest", function () { expect(await page.screenshotSelector(selectorsToCapture)).to.matchImage('1_selector_open'); }); + it("should unstar all segments", async function() { + await page.click('.segmentList li:nth-child(2) .starSegment'); + await page.click('.segmentList li:nth-child(3) .starSegment'); + await page.click('.segmentList li:nth-child(4) .starSegment'); + expect(await page.screenshotSelector(selectorsToCapture)).to.matchImage('1_selector_unstarred'); + }); + + it("should star last segment", async function() { + await page.click('.segmentList li:last-child .starSegment'); + expect(await page.screenshotSelector(selectorsToCapture)).to.matchImage('1_selector_starred'); + }); + it("should open segment editor when edit link clicked for existing segment", async function() { await page.evaluate(function() { $('.segmentList .editSegment:first').click() diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_open.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_open.png index 48ac7f70794..dd31fd48340 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_open.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_open.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e586e785ae27693d9755ac7b8a27db91aa28c12077056c9208878656c1815355 -size 15993 +oid sha256:d2e643dce210334c4a8b9318ec6cac7a86baf42b935f1b65668834ed9657a463 +size 16399 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_starred.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_starred.png new file mode 100644 index 00000000000..9aae6eee326 --- /dev/null +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_starred.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f8d3f0b6a3935655057d3c2f6ad0cdf25ec677f709806394062bf1778c6b33c3 +size 18878 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_unstarred.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_unstarred.png new file mode 100644 index 00000000000..0ab2f687cdb --- /dev/null +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_unstarred.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aae8ed8c81310e2b4b9b126be86b9a4f68155ab1c76e5d39ffa144eaeef04fc4 +size 19007 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_2_segment_editor_update.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_2_segment_editor_update.png index 7d203da33a3..629c3bb61cd 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_2_segment_editor_update.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_2_segment_editor_update.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7dd1dcc19eda71d47f719324a18181a827838ab4c4511ca6ff01c5066139d558 -size 35667 +oid sha256:bb703841188ab795a8f03eb4784d2b48362f15a72dbf609a54e57eb1157d1741 +size 35421 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted.png index 48ac7f70794..dd31fd48340 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e586e785ae27693d9755ac7b8a27db91aa28c12077056c9208878656c1815355 -size 15993 +oid sha256:d2e643dce210334c4a8b9318ec6cac7a86baf42b935f1b65668834ed9657a463 +size 16399 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_enabled_create_realtime_segments_saved.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_enabled_create_realtime_segments_saved.png index e7641ce2b2f..9345151767d 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_enabled_create_realtime_segments_saved.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_enabled_create_realtime_segments_saved.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f9b999f26ab808b11c9cc2c2e9e8388d66de22aab90533cce3cd6bcb27916fa8 -size 21289 +oid sha256:dd77396fa93d384f59d846a7e2403292b9f53dba1ab23f0c6a5a0819ddce1816 +size 21846 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved.png index 13eef5d9e7f..dc5fca36497 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1ff9036d74dbc4b3fac73d94247fd5eed415ad386a64df31cc66697fc9b9e379 -size 17846 +oid sha256:760f0cc55e2e8178c6d89d15400bf4582a974ebd5cfac674223083965f3c6ce3 +size 18365 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated.png index 14c0c47ee82..1b864092424 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1c054fb1eb65d7da38e5fba720a81a52c4bfd36c1f3a3c53e57981fa8b2a400 -size 18507 +oid sha256:80dd8bddaa3cc2941cb354a18b944cd7fe697df1c0f9a5f165ee1c641ade5a97 +size 18964 diff --git a/tests/PHPUnit/Integration/Segment/SegmentUnavailableTest.php b/tests/PHPUnit/Integration/Segment/SegmentUnavailableTest.php index 27b503ab155..2694976e12e 100644 --- a/tests/PHPUnit/Integration/Segment/SegmentUnavailableTest.php +++ b/tests/PHPUnit/Integration/Segment/SegmentUnavailableTest.php @@ -172,6 +172,7 @@ private function checkSegmentAvailable(string $definition, string $name, bool $s 'auto_archive' => 1, 'ts_last_edit' => null, 'deleted' => 0, + 'starred' => 0, ], ]; } From 6e670b7975f6faffbc7092f5acf33a8f499f0d63 Mon Sep 17 00:00:00 2001 From: Thomas ZILLIOX Date: Mon, 17 Nov 2025 20:16:25 +0100 Subject: [PATCH 03/17] Add tooltip on stars and fix existing tooltips --- lang/en.json | 2 + .../SegmentEditor/SegmentSelectorControl.php | 3 ++ .../SegmentEditor/javascripts/Segmentation.js | 39 ++++++++++--------- 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/lang/en.json b/lang/en.json index ad60dd48cea..0f906374eda 100644 --- a/lang/en.json +++ b/lang/en.json @@ -8,6 +8,7 @@ "Action": "Action", "Actions": "Actions", "Add": "Add", + "AddToFavorites": "Add to favorites", "AfterEntry": "after entering here", "All": "All", "AllWebsitesDashboard": "All Websites dashboard", @@ -418,6 +419,7 @@ "RelatedReport": "Related report", "RelatedReports": "Related reports", "Remove": "Remove", + "RemoveFromFavorites": "Remove from favorites", "Report": "Report", "ReportGeneratedFrom": "This report was generated using data from %s.", "ReportRatioTooltip": "'%1$s' represents %2$s of %3$s %4$s in segment %5$s with %6$s.", diff --git a/plugins/SegmentEditor/SegmentSelectorControl.php b/plugins/SegmentEditor/SegmentSelectorControl.php index 02857f84e31..4fe395eed69 100644 --- a/plugins/SegmentEditor/SegmentSelectorControl.php +++ b/plugins/SegmentEditor/SegmentSelectorControl.php @@ -127,6 +127,9 @@ private function getTranslations() 'General_DefaultAppended', 'SegmentEditor_AddNewSegment', 'General_Edit', + 'General_AddToFavorites', + 'General_RemoveFromFavorites', + 'General_Edit', 'General_Search', 'General_SearchNoResults', ); diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js index 0fce52370cf..10530a45def 100644 --- a/plugins/SegmentEditor/javascripts/Segmentation.js +++ b/plugins/SegmentEditor/javascripts/Segmentation.js @@ -75,16 +75,9 @@ Segmentation = (function($) { }; segmentation.prototype.setTooltip = function (segmentDescription) { - - var title = _pk_translate('SegmentEditor_ChooseASegment') + '.'; - title += ' '+ _pk_translate('SegmentEditor_CurrentlySelectedSegment', [segmentDescription]); - - $('a.title', this.content).attr('title', title).tooltip({ - track: true, - show: {delay: 700, duration: 200}, // default from Tooltips.js - hide: false, - content: title, - }); + var title = _pk_translate('SegmentEditor_ChooseASegment') + '.'; + title += ' '+ _pk_translate('SegmentEditor_CurrentlySelectedSegment', [segmentDescription]); + addTooltip($('a.title', this.content), title); }; // We will listen to changes in the Segment Comparison Store // so we can mark compared segments properly. This will now include deletion of compared segments. @@ -208,7 +201,7 @@ Segmentation = (function($) { var isSharedWithMeBySuperUserNoticeAlreadyDisplayedOnce = false; var isSharedWithMeBySuperUserNoticeShouldBeClosed = false; - if(self.availableSegments.length > 0) { + if (self.availableSegments.length > 0) { for(var i = 0; i < self.availableSegments.length; i++) { @@ -247,12 +240,11 @@ Segmentation = (function($) { '
  • ' + - '' + getSegmentName(segment) + ''; + '' + getSegmentName(segment) + ''; if (self.segmentAccess === "write") { listHtml += '' + - '
  • '; diff --git a/plugins/SegmentEditor/stylesheets/segmentation.less b/plugins/SegmentEditor/stylesheets/segmentation.less index 8461cbee06c..46080fe4619 100644 --- a/plugins/SegmentEditor/stylesheets/segmentation.less +++ b/plugins/SegmentEditor/stylesheets/segmentation.less @@ -232,6 +232,10 @@ div.scrollable { min-width: 206px; } +.segmentationContainer hr { + margin: 10px 0; +} + .segmentationContainer .submenu ul { display: flex; flex-direction: column; @@ -387,7 +391,9 @@ div.scrollable { &.allVisitsCompareSegment { margin-left: 20px; - margin-right: 20px; + } + &.allVisitsCompareSegment--write { + margin-right: 20px; } } span.compareSegment.no-click { From 80c0f522f44ccacfbef6a8925034c9e0c4ad176b Mon Sep 17 00:00:00 2001 From: Thomas ZILLIOX Date: Fri, 28 Nov 2025 16:00:59 +0100 Subject: [PATCH 11/17] Feedbacks from @sgiehl --- core/Updates/{5.6.0-b2.php => 5.7.0-b1.php} | 2 +- core/Version.php | 2 +- plugins/SegmentEditor/API.php | 2 ++ plugins/SegmentEditor/javascripts/Segmentation.js | 3 +++ plugins/SegmentEditor/stylesheets/segmentation.less | 8 ++------ 5 files changed, 9 insertions(+), 8 deletions(-) rename core/Updates/{5.6.0-b2.php => 5.7.0-b1.php} (95%) diff --git a/core/Updates/5.6.0-b2.php b/core/Updates/5.7.0-b1.php similarity index 95% rename from core/Updates/5.6.0-b2.php rename to core/Updates/5.7.0-b1.php index 0e3222de741..50027715640 100644 --- a/core/Updates/5.6.0-b2.php +++ b/core/Updates/5.7.0-b1.php @@ -13,7 +13,7 @@ use Piwik\Updater\Migration\Factory as MigrationFactory; use Piwik\Updates; -class Updates_5_6_0_b2 extends Updates +class Updates_5_7_0_b1 extends Updates { /** * @var MigrationFactory diff --git a/core/Version.php b/core/Version.php index ae44c6228be..4d6958c8be7 100644 --- a/core/Version.php +++ b/core/Version.php @@ -22,7 +22,7 @@ final class Version * The current Matomo version. * @var string */ - public const VERSION = '5.6.0-b2'; + public const VERSION = '5.7.0-b1'; public const MAJOR_VERSION = 5; diff --git a/plugins/SegmentEditor/API.php b/plugins/SegmentEditor/API.php index 26c0ae1d391..6dd31337ac5 100644 --- a/plugins/SegmentEditor/API.php +++ b/plugins/SegmentEditor/API.php @@ -359,6 +359,7 @@ public function add( */ public function star(int $idSegment): array { + Piwik::checkUserHasSomeViewAccess(); $segment = $this->getSegmentOrFail($idSegment); $this->checkUserCanEditOrDeleteSegment($segment); $login = Piwik::getCurrentUserLogin(); @@ -384,6 +385,7 @@ public function star(int $idSegment): array */ public function unstar(int $idSegment): array { + Piwik::checkUserHasSomeViewAccess(); $segment = $this->getSegmentOrFail($idSegment); $this->checkUserCanEditOrDeleteSegment($segment); $bind = [ diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js index df1f625193a..e79dbc442a8 100644 --- a/plugins/SegmentEditor/javascripts/Segmentation.js +++ b/plugins/SegmentEditor/javascripts/Segmentation.js @@ -207,6 +207,9 @@ Segmentation = (function($) { { segment = self.availableSegments[i]; + // starred is an int but it could be converted as string, and !"0" is false instead of true + segment.starred = Boolean(parseInt(segment.starred, 10)); + if(isSegmentSharedWithMeBySuperUser(segment) && !isSharedWithMeBySuperUserNoticeAlreadyDisplayedOnce) { isSharedWithMeBySuperUserNoticeAlreadyDisplayedOnce = true; isSharedWithMeBySuperUserNoticeShouldBeClosed = true; diff --git a/plugins/SegmentEditor/stylesheets/segmentation.less b/plugins/SegmentEditor/stylesheets/segmentation.less index 46080fe4619..dc9ea6fa7bd 100644 --- a/plugins/SegmentEditor/stylesheets/segmentation.less +++ b/plugins/SegmentEditor/stylesheets/segmentation.less @@ -264,9 +264,7 @@ div.scrollable { order: 1; } -.segmentationContainer .submenu ul li:hover, -.segmentationContainer .submenu ul li:focus, -.segmentationContainer .submenu ul li:focus-within { +.segmentationContainer .submenu ul li:hover { color: #255792; background: @color-silver-l95; outline: none; @@ -546,9 +544,7 @@ a.metric_category { .segmentSelected, .segmentSelected:hover, -.segmentEditorPanel .segmentationContainer .submenu li .segmentSelected, -.segmentEditorPanel .segmentationContainer .submenu li:focus, -.segmentEditorPanel .segmentationContainer .submenu li:focus-within { +.segmentEditorPanel .segmentationContainer .submenu li .segmentSelected { font-weight: bold; } From d149101f1ed7158187699e8f7fbe16fe11e40342 Mon Sep 17 00:00:00 2001 From: Thomas ZILLIOX Date: Tue, 2 Dec 2025 11:05:21 +0100 Subject: [PATCH 12/17] Handle star segment button in disable mode, for anonymous user, update edit icon --- plugins/SegmentEditor/images/edit_segment.png | Bin 2999 -> 0 bytes plugins/SegmentEditor/images/edit_segment.svg | 3 + .../SegmentEditor/javascripts/Segmentation.js | 72 ++++++++---------- .../stylesheets/segmentation.less | 9 ++- 4 files changed, 42 insertions(+), 42 deletions(-) delete mode 100644 plugins/SegmentEditor/images/edit_segment.png create mode 100644 plugins/SegmentEditor/images/edit_segment.svg diff --git a/plugins/SegmentEditor/images/edit_segment.png b/plugins/SegmentEditor/images/edit_segment.png deleted file mode 100644 index 6eb039254da67e3e2de83ffdda96d53653d43da9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2999 zcmV;o3rO^dP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0002uNklm z5(J)LA>}Z8WYgTWn{?dH?Eg=8C)2Cz+B%UzQ4}e6bKKz&r+6#NQa`a#jtXP!;R(A) z5@Z-)Gs^AY@&5@4o!@bZ2b_i4^D04(DXzk~4w4x@al1^=z}Hc35+oP+!Msb5wZKgb z@r~am(QkpP4!FWi3@-|nb-oYqe6bJ<_HohykK5o_aDZ)`<0}=81;=4LTajcX91D)H t5lKcVa6HCg_~jRT;5Bs{d+V + + diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js index e79dbc442a8..a6185d8e401 100644 --- a/plugins/SegmentEditor/javascripts/Segmentation.js +++ b/plugins/SegmentEditor/javascripts/Segmentation.js @@ -195,11 +195,8 @@ Segmentation = (function($) { } listHtml += ''; - var isVisibleToSuperUserNoticeAlreadyDisplayedOnce = false; - var isVisibleToSuperUserNoticeShouldBeClosed = false; - - var isSharedWithMeBySuperUserNoticeAlreadyDisplayedOnce = false; - var isSharedWithMeBySuperUserNoticeShouldBeClosed = false; + let isVisibleToSuperUserNoticeAlreadyDisplayedOnce = false; + let isSharedWithMeBySuperUserNoticeAlreadyDisplayedOnce = false; if (self.availableSegments.length > 0) { @@ -207,25 +204,18 @@ Segmentation = (function($) { { segment = self.availableSegments[i]; - // starred is an int but it could be converted as string, and !"0" is false instead of true + // starred should be an int, but it could be converted as string + // and !"0" would then be false instead of true segment.starred = Boolean(parseInt(segment.starred, 10)); - if(isSegmentSharedWithMeBySuperUser(segment) && !isSharedWithMeBySuperUserNoticeAlreadyDisplayedOnce) { + if (isSegmentSharedWithMeBySuperUser(segment) && !isSharedWithMeBySuperUserNoticeAlreadyDisplayedOnce) { isSharedWithMeBySuperUserNoticeAlreadyDisplayedOnce = true; - isSharedWithMeBySuperUserNoticeShouldBeClosed = true; - listHtml += '
    ' + _pk_translate('SegmentEditor_SharedWithYou') + ':

    '; + listHtml += '
    ' + _pk_translate('SegmentEditor_SharedWithYou') + ':
    '; } - if(isSegmentVisibleToSuperUserOnly(segment) && !isVisibleToSuperUserNoticeAlreadyDisplayedOnce) { - // close - if(isSharedWithMeBySuperUserNoticeShouldBeClosed) { - isSharedWithMeBySuperUserNoticeShouldBeClosed = false; - listHtml += ''; - } - + if (isSegmentVisibleToSuperUserOnly(segment) && !isVisibleToSuperUserNoticeAlreadyDisplayedOnce) { isVisibleToSuperUserNoticeAlreadyDisplayedOnce = true; - isVisibleToSuperUserNoticeShouldBeClosed = true; - listHtml += '
    ' + _pk_translate('SegmentEditor_VisibleToSuperUser') + ':

    '; + listHtml += '
    ' + _pk_translate('SegmentEditor_VisibleToSuperUser') + ':
    '; } @@ -245,15 +235,20 @@ Segmentation = (function($) { 'data-definition="' + escapedSegmentName + '"' + '>' + '' + getSegmentName(segment) + ''; - if (self.segmentAccess === "write") { - listHtml += '' + - ''; - listHtml += ''; + + const canStarUnstarSegment = getIsUserCanStarUnstarSegment(segment); + const disabledAttribute = canStarUnstarSegment ? '' : 'disabled'; + const titleAttribute = 'title="' + getStarSegmentTitle(segment, canStarUnstarSegment) + '"'; + listHtml += '' + + ''; + if (self.segmentAccess === 'write') { + listHtml += ''; } + if ( comparisonService.isComparisonEnabled() || comparisonService.isComparisonEnabled() === null // may not be initialized since this code is outside of Vue @@ -263,26 +258,16 @@ Segmentation = (function($) { listHtml += ''; } - if(isVisibleToSuperUserNoticeShouldBeClosed) { - listHtml += '
    '; - } - - if(isSharedWithMeBySuperUserNoticeShouldBeClosed) { - listHtml += '
    '; - } - $(html).find(".segmentList > ul").append(listHtml); - if(self.segmentAccess === "write"){ + if (self.segmentAccess === "write"){ $(html).find(".add_new_segment").html(self.translations['SegmentEditor_AddNewSegment']); - } - else { + } else { $(html).find(".add_new_segment").hide(); } - } - else - { + } else { $(html).find(".segmentList > ul").append(listHtml); } + return html; }; @@ -611,6 +596,13 @@ Segmentation = (function($) { }; + function getIsUserCanStarUnstarSegment(segment) { + if (self.segmentAccess !== 'write') { + return false; + } + return true; + } + function getStarSegmentTitle(segment) { if (segment.starred) { return self.translations['General_StarredBy'] + ' ' + (segment.starred_by || ''); diff --git a/plugins/SegmentEditor/stylesheets/segmentation.less b/plugins/SegmentEditor/stylesheets/segmentation.less index dc9ea6fa7bd..7dd8d4278a2 100644 --- a/plugins/SegmentEditor/stylesheets/segmentation.less +++ b/plugins/SegmentEditor/stylesheets/segmentation.less @@ -339,10 +339,15 @@ div.scrollable { &:hover { opacity: 1; } + + &:disabled { + opacity: 0.2; + } } span.editSegment { - background: url(plugins/SegmentEditor/images/edit_segment.png) no-repeat; + background: url(plugins/SegmentEditor/images/edit_segment.svg) no-repeat; + background-size: cover; order: 3; } @@ -401,7 +406,7 @@ div.scrollable { li.segmentSelected, li.comparedSegment { span.compareSegment { pointer-events: none; - opacity: 0.2; + opacity: 0.5; } } } From 912a775d426e74c45f7b62be49c810a97dbbd05b Mon Sep 17 00:00:00 2001 From: Thomas ZILLIOX Date: Tue, 2 Dec 2025 11:11:39 +0100 Subject: [PATCH 13/17] Make edit and compare buttons accessible --- plugins/SegmentEditor/javascripts/Segmentation.js | 4 ++-- .../SegmentEditor/stylesheets/segmentation.less | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js index a6185d8e401..4a3b7e0dc66 100644 --- a/plugins/SegmentEditor/javascripts/Segmentation.js +++ b/plugins/SegmentEditor/javascripts/Segmentation.js @@ -246,14 +246,14 @@ Segmentation = (function($) { '' + ''; if (self.segmentAccess === 'write') { - listHtml += ''; + listHtml += ''; } if ( comparisonService.isComparisonEnabled() || comparisonService.isComparisonEnabled() === null // may not be initialized since this code is outside of Vue ) { - listHtml += ''; + listHtml += ''; } listHtml += ''; } diff --git a/plugins/SegmentEditor/stylesheets/segmentation.less b/plugins/SegmentEditor/stylesheets/segmentation.less index 7dd8d4278a2..661ffec5059 100644 --- a/plugins/SegmentEditor/stylesheets/segmentation.less +++ b/plugins/SegmentEditor/stylesheets/segmentation.less @@ -318,8 +318,8 @@ div.scrollable { } .segmentationContainer ul.submenu > li { - span.editSegment, - span.compareSegment, + .editSegment, + .compareSegment, .starSegment { flex: none; display: flex; @@ -345,7 +345,7 @@ div.scrollable { } } - span.editSegment { + .editSegment { background: url(plugins/SegmentEditor/images/edit_segment.svg) no-repeat; background-size: cover; order: 3; @@ -387,7 +387,7 @@ div.scrollable { fill: black; } - span.compareSegment { + .compareSegment { background: url(plugins/Morpheus/images/compare.svg) no-repeat; background-size: cover; order: 2; @@ -399,12 +399,12 @@ div.scrollable { margin-right: 20px; } } - span.compareSegment.no-click { + .compareSegment.no-click { pointer-events: none; } li.segmentSelected, li.comparedSegment { - span.compareSegment { + .compareSegment { pointer-events: none; opacity: 0.5; } @@ -412,7 +412,7 @@ div.scrollable { } html.comparisonsDisabled .segmentationContainer ul.submenu { - span.compareSegment { + .compareSegment { display: none; } } From c8ff8d35dbf1813f1adbe7cf121c0560f4dfeaf1 Mon Sep 17 00:00:00 2001 From: Thomas ZILLIOX Date: Tue, 2 Dec 2025 16:43:48 +0100 Subject: [PATCH 14/17] Handle all tooltip cases, fix edit and compare behaviour and tooltip --- lang/en.json | 13 ++ .../SegmentEditor/SegmentSelectorControl.php | 14 +- .../SegmentEditor/javascripts/Segmentation.js | 138 +++++++++++++----- .../stylesheets/segmentation.less | 21 ++- 4 files changed, 140 insertions(+), 46 deletions(-) diff --git a/lang/en.json b/lang/en.json index f07f45add6c..2f406ce111a 100644 --- a/lang/en.json +++ b/lang/en.json @@ -28,6 +28,18 @@ "BrokenDownReportDocumentation": "It is broken down into various reports, which are displayed in sparklines at the bottom of the page. You can enlarge the graphs by clicking on the report you'd like to see.", "Cancel": "Cancel", "CannotUnzipFile": "Cannot unzip file %1$s: %2$s", + "CanNotEditGlobalSegment": "This is a global segment. Only super users can edit global segments.", + "CanNotStarGlobalSegment": "This is a global segment. Only super users can star global segments.", + "CanNotUnstarGlobalSegment": "This is a global segment. Only super users can unstar global segments.", + "CanEditGlobalSegment": "This is a global segment. Any changes will apply across all websites.", + "CanStarGlobalSegment": "This is a global segment. Adding to Starred will apply across all websites.", + "CanUnstarGlobalSegment": "This is a global segment. Removing from Starred will apply across all websites.", + "CanNotEditSiteSegment": "You can only edit the segments you created yourself.", + "CanNotStarSiteSegment": "You can only add to Starred the segments you created yourself.", + "CanNotUnstarSiteSegment": "You can only remove from Starred the segments you created yourself.", + "CanEditSiteSegment": "Edit the segment for this website.", + "CanStarSiteSegment": "Add to Starred segments for this website.", + "CanUnstarSiteSegment": "Remove from Starred segments for this website.", "ChangeInX": "Change in %1$s", "ChangePassword": "Change password", "ChangeTagCloudView": "Please note, that you can view the report in other ways than as a tag cloud. Use the controls at the bottom of the report to do so.", @@ -465,6 +477,7 @@ "Source": "Source", "Star": "Star", "StarredBy": "Starred by", + "StarredByYou": "Starred by you", "StatisticsAreNotRecorded": "Matomo Visitor Tracking is currently disabled! Re-enable tracking by setting record_statistics = 1 in your config/config.ini.php file.", "Subtotal": "Subtotal", "Summary": "Summary", diff --git a/plugins/SegmentEditor/SegmentSelectorControl.php b/plugins/SegmentEditor/SegmentSelectorControl.php index a268d07d26e..f3a3c2cee22 100644 --- a/plugins/SegmentEditor/SegmentSelectorControl.php +++ b/plugins/SegmentEditor/SegmentSelectorControl.php @@ -109,6 +109,18 @@ private function wouldApplySegment($savedSegment) private function getTranslations() { $translationKeys = array( + 'General_CanNotEditGlobalSegment', + 'General_CanNotStarGlobalSegment', + 'General_CanNotUnstarGlobalSegment', + 'General_CanEditGlobalSegment', + 'General_CanStarGlobalSegment', + 'General_CanUnstarGlobalSegment', + 'General_CanNotEditSiteSegment', + 'General_CanNotStarSiteSegment', + 'General_CanNotUnstarSiteSegment', + 'General_CanEditSiteSegment', + 'General_CanStarSiteSegment', + 'General_CanUnstarSiteSegment', 'General_OperationEquals', 'General_OperationNotEquals', 'General_OperationAtMost', @@ -127,8 +139,8 @@ private function getTranslations() 'General_DefaultAppended', 'SegmentEditor_AddNewSegment', 'General_Edit', - 'General_Star', 'General_StarredBy', + 'General_StarredByYou', 'General_Edit', 'General_Search', 'General_SearchNoResults', diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js index 4a3b7e0dc66..a0237294e09 100644 --- a/plugins/SegmentEditor/javascripts/Segmentation.js +++ b/plugins/SegmentEditor/javascripts/Segmentation.js @@ -83,15 +83,19 @@ Segmentation = (function($) { }); segmentation.prototype.markComparedSegments = function() { - var comparisonService = window.CoreHome.ComparisonsStoreInstance; - var comparedSegments = comparisonService.getSegmentComparisons().map(function (comparison) { + const comparisonService = window.CoreHome.ComparisonsStoreInstance; + const comparedSegments = comparisonService.getSegmentComparisons().map(function (comparison) { return comparison.params.segment; }); - $('div.segmentList ul li[data-definition]', this.target).removeClass('comparedSegment').filter(function () { - var definition = $(this).attr('data-definition'); - return comparedSegments.indexOf(definition) !== -1 || comparedSegments.indexOf(decodeURIComponent(definition)) !== -1; - }).each(function () { - $(this).addClass('comparedSegment'); + $('div.segmentList ul li[data-definition]', this.target).each(function () { + const $segment = $(this); + const definition = $segment.attr('data-definition'); + const isCompared = ( + comparedSegments.indexOf(definition) !== -1 || + comparedSegments.indexOf(decodeURIComponent(definition)) !== -1 + ); + $segment.toggleClass('comparedSegment', isCompared); + $segment.find('.compareSegment').attr('data-state', isCompared ? 'active' : ''); }); self.checkIfComparedSegmentsHasReachedLimit(); }; @@ -99,15 +103,19 @@ Segmentation = (function($) { const limit = piwik.config.data_comparison_segment_limit + 1; const comparisonService = window.CoreHome.ComparisonsStoreInstance; const comparedSegmentsLength = comparisonService.getSegmentComparisons().length; - $('div.segmentList ul li[data-definition] span.compareSegment').each(function() { + $('div.segmentList ul li[data-definition] .compareSegment').each(function() { + const $compareButton = $(this); + const $segment = $compareButton.parent(); + const currentState = $compareButton.attr('data-state'); + if (currentState === 'active') { + return; + } if (comparedSegmentsLength >= limit) { - $(this).addClass('no-click'); - $(this).parent().attr('title', _pk_translate('General_MaximumNumberOfSegmentsComparedIs', [limit])); + $compareButton.attr('data-state','disabled'); + addTooltip($compareButton, _pk_translate('General_MaximumNumberOfSegmentsComparedIs', [limit])); } else { - $(this).removeClass('no-click'); - var idSegment = $(this).parent().attr('data-idsegment'); - const title = getSegmentName(getSegmentFromId(idSegment)); - $(this).parent().attr('title', title); + $compareButton.attr('data-state',''); + addTooltip($compareButton, _pk_translate('SegmentEditor_CompareThisSegment')); } }); return false; @@ -236,17 +244,19 @@ Segmentation = (function($) { '>' + '' + getSegmentName(segment) + ''; - const canStarUnstarSegment = getIsUserCanStarUnstarSegment(segment); - const disabledAttribute = canStarUnstarSegment ? '' : 'disabled'; - const titleAttribute = 'title="' + getStarSegmentTitle(segment, canStarUnstarSegment) + '"'; + const canEdit = getIsUserCanEditSegment(segment); + // We do not use "disabled" attribute here because it remove pointer events and we want to show tooltips + const disabledAttribute = canEdit ? '' : 'data-state="disabled"'; + const starTitleAttribute = 'title="' + getStarSegmentTitle(segment, canEdit) + '"'; listHtml += '' + - ''; if (self.segmentAccess === 'write') { - listHtml += ''; + const editTitleAttribute = 'title="' + getEditSegmentTitle(segment, canEdit) + '"'; + listHtml += ''; } if ( @@ -413,10 +423,14 @@ Segmentation = (function($) { }); self.target.on('click', '.editSegment', function(e) { - $(this).closest(".segmentationContainer").trigger("click"); - var target = $(this).parent("li"); + const $button = $(this); + if ($button.attr('data-state') === 'disabled') { + return false; + } + const $segment = $button.parent("li"); + $segment.closest(".segmentationContainer").trigger("click"); - openEditFormGivenSegment(target); + openEditFormGivenSegment($segment); e.stopPropagation(); e.preventDefault(); }); @@ -424,7 +438,11 @@ Segmentation = (function($) { self.target.on('click', '[data-star]', function (e) { e.stopPropagation(); e.preventDefault(); - const $root = $(this).closest('li'); + const $button = $(this); + if ($button.attr('data-state') === 'disabled') { + return false; + } + const $root = $button.closest('li'); const idSegment = $root.data('idsegment'); const segment = getSegmentFromId(idSegment); segment.starred = !segment.starred; @@ -453,14 +471,18 @@ Segmentation = (function($) { self.target.on('click', '.compareSegment', function (e) { e.stopPropagation(); e.preventDefault(); - var comparisonService = window.CoreHome.ComparisonsStoreInstance; + const $button = $(this); + if ($button.attr('data-state') === 'disabled') { + return false; + } + const comparisonService = window.CoreHome.ComparisonsStoreInstance; comparisonService.addSegmentComparison({ - segment: $(e.target).closest('li').data('definition'), + segment: $button.closest('li').data('definition'), }); closeAllOpenLists(); }); - self.target.on("click", ".segmentList li span.segname", function (e) { + self.target.on("click", ".segmentList li .segname", function (e) { let parentLi = $(this).parent(); if (parentLi.hasClass("grayed") !== true) { var segmentDefinition = $(parentLi).data("definition"); @@ -485,7 +507,7 @@ Segmentation = (function($) { // emulate a click when pressing enter on one of the segments or the add button self.target.on("keyup", ".segmentList li, .add_new_segment", function (event) { var keycode = (event.keyCode ? event.keyCode : (event.which ? event.which : event.key)); - if(keycode == '13'){ + if (keycode == '13'){ $(this).trigger('click'); } }); @@ -596,23 +618,71 @@ Segmentation = (function($) { }; - function getIsUserCanStarUnstarSegment(segment) { + function getIsUserCanEditSegment(segment) { if (self.segmentAccess !== 'write') { return false; } - return true; + return (segment.login === piwik.userLogin || piwik.hasSuperUserAccess); } - function getStarSegmentTitle(segment) { + function getStarredByTitlePart(segment) { + const login = segment.starred_by || ''; + if (login === piwik.userLogin) { + return ' (' + self.translations['General_StarredByYou'] + ')' + } + return ' (' + self.translations['General_StarredBy'] + ' ' + login + ')' + } + + function getStarSegmentTitle(segment, canEdit) { + // Site-specific segments + if (segment.enable_only_idsite) { + if (canEdit) { + if (segment.starred) { + return self.translations['General_CanUnstarSiteSegment'] + ' ' + getStarredByTitlePart(segment); + } + return self.translations['General_CanStarSiteSegment']; + } else { + if (segment.starred) { + return self.translations['General_CanNotUnstarSiteSegment']; + } + return self.translations['General_CanNotStarSiteSegment']; + } + } + + // Global segments + if (canEdit) { + if (segment.starred) { + return self.translations['General_CanUnstarGlobalSegment'] + ' ' + getStarredByTitlePart(segment); + } + return self.translations['General_CanStarGlobalSegment']; + } if (segment.starred) { - return self.translations['General_StarredBy'] + ' ' + (segment.starred_by || ''); + return self.translations['General_CanNotUnstarGlobalSegment']; + } + return self.translations['General_CanNotStarGlobalSegment']; + } + + function getEditSegmentTitle(segment, canEdit) { + // Site-specific segments + if (segment.enable_only_idsite) { + if (canEdit) { + return self.translations['General_CanEditSiteSegment']; + } else { + return self.translations['General_CanNotEditSiteSegment']; + } + } + + // Global segments + if (canEdit) { + return self.translations['General_CanEditGlobalSegment']; } - return self.translations['General_Star']; + return self.translations['General_CanNotEditGlobalSegment']; } function updateStarSegmentTooltip($segment, segment) { const $starButton = $segment.find('.starSegment'); - addTooltip($starButton, getStarSegmentTitle(segment)); + const canEdit = getIsUserCanEditSegment(segment); + addTooltip($starButton, getStarSegmentTitle(segment, canEdit)); } function updateStarredSegment($segment, segment, isError = false) { @@ -687,7 +757,7 @@ Segmentation = (function($) { $(self.form).find('.enable_all_users_select > option[value="' + segment.enable_all_users + '"]').prop("selected",true); // Replace "Visible to me" by "Visible to $login" when user is super user - if(hasSuperUserAccessAndSegmentCreatedByAnotherUser(segment)) { + if (hasSuperUserAccessAndSegmentCreatedByAnotherUser(segment)) { $(self.form).find('.enable_all_users_select > option[value="' + 0 + '"]').text(segment.login); } $(self.form).find('.visible_to_website_select > option[value="'+segment.enable_only_idsite+'"]').prop("selected",true); diff --git a/plugins/SegmentEditor/stylesheets/segmentation.less b/plugins/SegmentEditor/stylesheets/segmentation.less index 661ffec5059..6ee68ad987e 100644 --- a/plugins/SegmentEditor/stylesheets/segmentation.less +++ b/plugins/SegmentEditor/stylesheets/segmentation.less @@ -340,8 +340,17 @@ div.scrollable { opacity: 1; } - &:disabled { + &[data-state=disabled] { opacity: 0.2; + cursor: not-allowed; + } + + &[data-state=active] { + border-radius: 50%; + background-color: white; + background-size: 70%; + background-position: center; + filter: invert(1); } } @@ -399,16 +408,6 @@ div.scrollable { margin-right: 20px; } } - .compareSegment.no-click { - pointer-events: none; - } - - li.segmentSelected, li.comparedSegment { - .compareSegment { - pointer-events: none; - opacity: 0.5; - } - } } html.comparisonsDisabled .segmentationContainer ul.submenu { From f1630eb21ea4eeef14e8cc1e1f1e9dca10896e8c Mon Sep 17 00:00:00 2001 From: Thomas ZILLIOX Date: Wed, 3 Dec 2025 17:17:47 +0100 Subject: [PATCH 15/17] Update UI screenshots --- .../SegmentSelectorEditorTest_1_selector_open.png | 4 ++-- .../SegmentSelectorEditorTest_1_selector_starred.png | 4 ++-- .../SegmentSelectorEditorTest_1_selector_unstarred.png | 4 ++-- .../SegmentSelectorEditorTest_deleted.png | 4 ++-- ...ectorEditorTest_enabled_create_realtime_segments_saved.png | 4 ++-- .../expected-screenshots/SegmentSelectorEditorTest_saved.png | 4 ++-- .../SegmentSelectorEditorTest_updated.png | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_open.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_open.png index dd31fd48340..21b562a0fa5 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_open.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_open.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2e643dce210334c4a8b9318ec6cac7a86baf42b935f1b65668834ed9657a463 -size 16399 +oid sha256:3927ef177014f6f43ce385be4bcd9dc8a77a390e7b2cc714c48d39ca9bc6e08b +size 16750 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_starred.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_starred.png index 9aae6eee326..4863d89d953 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_starred.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_starred.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f8d3f0b6a3935655057d3c2f6ad0cdf25ec677f709806394062bf1778c6b33c3 -size 18878 +oid sha256:6eb43ab8f5069cfd80e04bb93113983862ba307534bbc68eb8fafca4c7368338 +size 17291 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_unstarred.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_unstarred.png index 0ab2f687cdb..305536fcdd4 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_unstarred.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_unstarred.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aae8ed8c81310e2b4b9b126be86b9a4f68155ab1c76e5d39ffa144eaeef04fc4 -size 19007 +oid sha256:d0a79771f8de5c7cbc536127c2390ce3c1eee0fa6cb9a4ce1955dba0525a2f1c +size 17092 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted.png index dd31fd48340..705e5debe0f 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2e643dce210334c4a8b9318ec6cac7a86baf42b935f1b65668834ed9657a463 -size 16399 +oid sha256:7522eac65db89e386be8bbdab4545a5a311c44c7d181137f016f763bf2940a73 +size 16703 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_enabled_create_realtime_segments_saved.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_enabled_create_realtime_segments_saved.png index 9345151767d..18711d8f528 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_enabled_create_realtime_segments_saved.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_enabled_create_realtime_segments_saved.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd77396fa93d384f59d846a7e2403292b9f53dba1ab23f0c6a5a0819ddce1816 -size 21846 +oid sha256:8974b57e944c677e6632962d2a19b5c824b41567b39701f3fd04618374a83b58 +size 22132 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved.png index dc5fca36497..fd2e41adad1 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:760f0cc55e2e8178c6d89d15400bf4582a974ebd5cfac674223083965f3c6ce3 -size 18365 +oid sha256:5eef4eb48a5c2e3ef8bb503f7a186aa4fe3cb2e53d3a9ffdfc9f067cb29bfe15 +size 18614 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated.png index 1b864092424..787ea858eb4 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:80dd8bddaa3cc2941cb354a18b944cd7fe697df1c0f9a5f165ee1c641ade5a97 -size 18964 +oid sha256:99ae645a4e18027b0ef49e7472129f05e83302b7c9be988432fffe5590ea1715 +size 18996 From 754051c34a8fbaeeebdbab6c1310920b26112a2c Mon Sep 17 00:00:00 2001 From: Thomas ZILLIOX Date: Mon, 8 Dec 2025 12:02:23 +0100 Subject: [PATCH 16/17] Tests and feedbacks from Matthieu --- core/Plugin/Controller.php | 1 + plugins/CoreHome/vue/dist/CoreHome.umd.js | 14 ++++ plugins/CoreHome/vue/dist/CoreHome.umd.min.js | 2 +- .../vue/src/Comparisons/Comparisons.store.ts | 15 +++++ plugins/CoreVue/types/index.d.ts | 2 + .../templates/_jsGlobalVariables.twig | 2 + plugins/SegmentEditor/API.php | 2 +- .../SegmentEditor/javascripts/Segmentation.js | 34 +++++++--- .../stylesheets/segmentation.less | 17 ++--- .../tests/UI/SegmentSelectorEditor_spec.js | 64 +++++++++++++++---- 10 files changed, 120 insertions(+), 33 deletions(-) diff --git a/core/Plugin/Controller.php b/core/Plugin/Controller.php index 57f5bcc9e56..42f99dcc9fc 100644 --- a/core/Plugin/Controller.php +++ b/core/Plugin/Controller.php @@ -726,6 +726,7 @@ protected function setBasicVariablesNoneAdminView($view) { $view->clientSideConfig = PiwikConfig::getInstance()->getClientSideOptions(); $view->isSuperUser = Access::getInstance()->hasSuperUserAccess(); + $view->userCurrentRole = Access::getInstance()->getRoleForSite($this->idSite); $view->hasSomeAdminAccess = Piwik::isUserHasSomeAdminAccess(); $view->hasSomeViewAccess = Piwik::isUserHasSomeViewAccess(); $view->isUserIsAnonymous = Piwik::isUserIsAnonymous(); diff --git a/plugins/CoreHome/vue/dist/CoreHome.umd.js b/plugins/CoreHome/vue/dist/CoreHome.umd.js index a8e7fb5910b..79e899c768c 100644 --- a/plugins/CoreHome/vue/dist/CoreHome.umd.js +++ b/plugins/CoreHome/vue/dist/CoreHome.umd.js @@ -3610,6 +3610,20 @@ class Comparisons_store_ComparisonsStore { } this.updateQueryParamsFromComparisons(newComparisons, this.periodComparisons.value, extraParams); } + removeSegmentComparisonByDefinition(segmentDefinition) { + if (!this.isComparisonEnabled()) { + throw new Error('Comparison disabled.'); + } + let segmentIndex = null; + this.getSegmentComparisons().forEach((segment, index) => { + if (segment && segment.params && segment.params.segment === segmentDefinition) { + segmentIndex = index; + } + }); + if (segmentIndex !== null) { + this.removeSegmentComparison(segmentIndex); + } + } addSegmentComparison(params) { if (!this.isComparisonEnabled()) { throw new Error('Comparison disabled.'); diff --git a/plugins/CoreHome/vue/dist/CoreHome.umd.min.js b/plugins/CoreHome/vue/dist/CoreHome.umd.min.js index bec11ccfdec..e7efebabcc2 100644 --- a/plugins/CoreHome/vue/dist/CoreHome.umd.min.js +++ b/plugins/CoreHome/vue/dist/CoreHome.umd.min.js @@ -224,7 +224,7 @@ function $e(e,t,o){const i=t.value.isMouseDown&&t.value.hasScrolled;t.value.isMo * * @link https://matomo.org * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later - */const yo=8,wo=3;function ko(e){return e?Array.isArray(e)?e:[e]:[]}class So{constructor(){jo(this,"privateState",Object(E["reactive"])({comparisonsDisabledFor:[]})),jo(this,"state",Object(E["readonly"])(this.privateState)),jo(this,"colors",{}),jo(this,"segmentComparisons",Object(E["computed"])(()=>this.parseSegmentComparisons())),jo(this,"periodComparisons",Object(E["computed"])(()=>this.parsePeriodComparisons())),jo(this,"isEnabled",Object(E["computed"])(()=>this.checkEnabledForCurrentPage())),"complete"===document.readyState||"interactive"===document.readyState?this.loadComparisonsDisabledFor():document.addEventListener("DOMContentLoaded",()=>{this.loadComparisonsDisabledFor()}),$(()=>{this.colors=this.getAllSeriesColors()}),Object(E["watch"])(()=>this.getComparisons(),()=>x.postEvent("piwikComparisonsChanged"),{deep:!0})}getComparisons(){return this.getSegmentComparisons().concat(this.getPeriodComparisons())}isComparing(){return this.isComparisonEnabled()&&(this.segmentComparisons.value.length>1||this.periodComparisons.value.length>1)}isComparingPeriods(){return this.getPeriodComparisons().length>1}getSegmentComparisons(){return this.isComparisonEnabled()?this.segmentComparisons.value:[]}getPeriodComparisons(){return this.isComparisonEnabled()?this.periodComparisons.value:[]}getSeriesColor(e,t,o=0){const i=this.getComparisonSeriesIndex(t.index,e.index)%yo;if(0===o)return this.colors["series"+i];const n=o%wo;return this.colors[`series${i}-shade${n}`]}getSeriesColorName(e,t){let o="series"+e%yo;return t>0&&(o+="-shade"+t%wo),o}isComparisonEnabled(){return this.isEnabled.value}getIndividualComparisonRowIndices(e){const t=this.getSegmentComparisons().length,o=e%t,i=Math.floor(e/t);return{segmentIndex:o,periodIndex:i}}getComparisonSeriesIndex(e,t){const o=this.getSegmentComparisons().length;return e*o+t}getAllComparisonSeries(){const e=[];let t=0;return this.getPeriodComparisons().forEach(o=>{this.getSegmentComparisons().forEach(i=>{e.push({index:t,params:Object.assign(Object.assign({},i.params),o.params),color:this.colors["series"+t]}),t+=1})}),e}removeSegmentComparison(e){if(!this.isComparisonEnabled())throw new Error("Comparison disabled.");const t=[...this.segmentComparisons.value];t.splice(e,1);const o={};0===e&&(o.segment=t[0].params.segment),this.updateQueryParamsFromComparisons(t,this.periodComparisons.value,o)}addSegmentComparison(e){if(!this.isComparisonEnabled())throw new Error("Comparison disabled.");const t=this.segmentComparisons.value.concat([{params:e,index:-1,title:""}]);this.updateQueryParamsFromComparisons(t,this.periodComparisons.value)}updateQueryParamsFromComparisons(e,t,o={}){const i={},n={};let a=!1,r=!1;e.forEach(e=>{a?i[e.params.segment]=!0:a=!0}),t.forEach(e=>{r?n[`${e.params.period}|${e.params.date}`]=!0:r=!0});const s=[],l=[];Object.keys(n).forEach(e=>{const t=e.split("|");s.push(t[0]),l.push(t[1])});const c={compareSegments:Object.keys(i),comparePeriods:s,compareDates:l},d=x.helper.isReportingPage()?_.hashParsed.value:_.urlParsed.value;_.updateLocation(Object.assign(Object.assign(Object.assign({},d),c),o))}getAllSeriesColors(){const{ColorManager:e}=x;if(!e)return[];const t=[];for(let o=0;o{this.privateState.comparisonsDisabledFor=e})}parseSegmentComparisons(){const{availableSegments:e}=Oo.state,t=[...ko(_.parsed.value.compareSegments)];t.unshift(_.parsed.value.segment||"");const o=[];return t.forEach((t,i)=>{let n;e.forEach(e=>{e.definition!==t&&e.definition!==decodeURIComponent(t)&&decodeURIComponent(e.definition)!==t||(n=e)});let r=n?n.name:a("General_Unknown");""===t.trim()&&(r=a("SegmentEditor_DefaultAllVisits")),o.push({params:{segment:t},title:x.helper.htmlDecode(r),index:i})}),o}parsePeriodComparisons(){const e=[...ko(_.parsed.value.comparePeriods)],t=[...ko(_.parsed.value.compareDates)];e.unshift(_.parsed.value.period),t.unshift(_.parsed.value.date);const o=[];for(let n=0;nCo.isComparing()&&!window.broadcast.isNoDataPage()),t=Object(E["computed"])(()=>Co.getSegmentComparisons()),o=Object(E["computed"])(()=>Co.getPeriodComparisons()),i=Co.getSeriesColor.bind(Co);function n(){const e=window.$(this).attr("title");return e?window.vueSanitize(e.replace(/\n/g,"
    ")):e}return{isComparing:e,segmentComparisons:t,periodComparisons:o,getSeriesColor:i,transformTooltipContent:n}},methods:{comparisonHasSegment(e){return"undefined"!==typeof e.params.segment},removeSegmentComparison(e){window.$(this.$refs.root).tooltip("destroy"),Co.removeSegmentComparison(e)},getComparisonPeriodType(e){const{period:t}=e.params;if("range"===t)return a("CoreHome_PeriodRange");const o=a(`Intl_Period${t.substring(0,1).toUpperCase()}${t.substring(1)}`);return o.substring(0,1).toUpperCase()+o.substring(1)},getComparisonTooltip(e,t){if(this.comparisonTooltips&&Object.keys(this.comparisonTooltips).length)return(this.comparisonTooltips[t.index]||{})[e.index]},getTitleTooltip(e){return this.htmlentities(e.title)+"
    "+this.htmlentities(decodeURIComponent(e.params.segment))},getUrlToSegment(e){const t=Object.assign({},_.hashParsed.value);return delete t.comparePeriods,delete t.compareDates,delete t.compareSegments,t.segment=e,`${window.location.search}#?${_.stringify(t)}`},onComparisonsChanged(){if(this.comparisonTooltips=null,!Co.isComparing())return;const e=Co.getPeriodComparisons(),t=Co.getSegmentComparisons();W.fetch({method:"API.getProcessedReport",apiModule:"VisitsSummary",apiAction:"get",compare:"1",compareSegments:_.getSearchParam("compareSegments"),comparePeriods:_.getSearchParam("comparePeriods"),compareDates:_.getSearchParam("compareDates"),format_metrics:"1"}).then(o=>{this.comparisonTooltips={},e.forEach(e=>{this.comparisonTooltips[e.index]={},t.forEach(t=>{const i=this.generateComparisonTooltip(o,e,t);this.comparisonTooltips[e.index][t.index]=i})})})},generateComparisonTooltip(e,t,o){if(!e.reportData.comparisons)return"";const i=Co.getComparisonSeriesIndex(t.index,0),n=e.reportData.comparisons[i],r=Co.getComparisonSeriesIndex(t.index,o.index),s=e.reportData.comparisons[r],l=e.reportData.comparisons[o.index];let c='
    ',d=(s.nb_visits/n.nb_visits*100).toFixed(2);return d+="%",c+=a("General_ComparisonCardTooltip1",[`'${this.htmlentities(s.compareSegmentPretty)}'`,s.comparePeriodPretty,d,s.nb_visits.toString(),n.nb_visits.toString()]),t.index>0&&(c+="

    ",c+=a("General_ComparisonCardTooltip2",[s.nb_visits_change.toString(),this.htmlentities(l.compareSegmentPretty),l.comparePeriodPretty])),c+="
    ",c},htmlentities(e){return x.helper.htmlEntities(e)}},mounted(){x.on("piwikComparisonsChanged",()=>{this.onComparisonsChanged()}),this.onComparisonsChanged()}});Eo.render=bo;var Do=Eo;const Po={ref:"root",class:"menuDropdown"},To=["title"],Vo=["innerHTML"],No=Object(E["createElementVNode"])("span",{class:"icon-chevron-down reporting-menu-sub-icon"},null,-1),xo={class:"items"},Bo={key:0,class:"search"},Io=["placeholder"],Mo=["title"],Lo=["title"];function Fo(e,t,o,i,n,a){const r=Object(E["resolveDirective"])("focus-if"),s=Object(E["resolveDirective"])("focus-anywhere-but-here");return Object(E["withDirectives"])((Object(E["openBlock"])(),Object(E["createElementBlock"])("div",Po,[Object(E["createElementVNode"])("span",{class:"title",onClick:t[0]||(t[0]=t=>e.showItems=!e.showItems),title:e.tooltip},[Object(E["createElementVNode"])("span",{class:"title-label",innerHTML:e.$sanitize(this.actualMenuTitle)},null,8,Vo),No],8,To),Object(E["withDirectives"])(Object(E["createElementVNode"])("div",xo,[e.showSearch&&e.showItems?(Object(E["openBlock"])(),Object(E["createElementBlock"])("div",Bo,[Object(E["withDirectives"])(Object(E["createElementVNode"])("input",{type:"text","onUpdate:modelValue":t[1]||(t[1]=t=>e.searchTerm=t),onKeydown:t[2]||(t[2]=t=>e.onSearchTermKeydown(t)),placeholder:e.translate("General_Search")},null,40,Io),[[E["vModelText"],e.searchTerm],[r,{focused:e.showItems}]]),Object(E["withDirectives"])(Object(E["createElementVNode"])("div",{class:"search_ico icon-search",title:e.translate("General_Search")},null,8,Mo),[[E["vShow"],!e.searchTerm]]),Object(E["withDirectives"])(Object(E["createElementVNode"])("div",{onClick:t[3]||(t[3]=t=>{e.searchTerm="",e.searchItems("")}),class:"reset icon-close",title:e.translate("General_Clear")},null,8,Lo),[[E["vShow"],e.searchTerm]])])):Object(E["createCommentVNode"])("",!0),Object(E["createElementVNode"])("div",{onClick:t[4]||(t[4]=t=>e.selectItem(t))},[Object(E["renderSlot"])(e.$slots,"default")])],512),[[E["vShow"],e.showItems]])])),[[s,{blur:e.lostFocus}]])}const{$:Ao}=window;var _o=Object(E["defineComponent"])({props:{menuTitle:String,tooltip:String,showSearch:Boolean,menuTitleChangeOnClick:Boolean},directives:{FocusAnywhereButHere:Ge,FocusIf:Je},emits:["afterSelect"],watch:{menuTitle(){this.actualMenuTitle=this.menuTitle}},data(){return{showItems:!1,searchTerm:"",actualMenuTitle:this.menuTitle}},methods:{lostFocus(){this.showItems=!1},selectItem(e){const t=e.target.classList;!t.contains("item")||t.contains("disabled")||t.contains("separator")||(this.menuTitleChangeOnClick&&(this.actualMenuTitle=(e.target.textContent||"").replace(/[\u0000-\u2666]/g,e=>`&#${e.charCodeAt(0)};`)),this.showItems=!1,Ao(this.$slots.default()[0].el).find(".item").removeClass("active"),t.add("active"),this.$emit("afterSelect",e.target))},onSearchTermKeydown(){setTimeout(()=>{this.searchItems(this.searchTerm)})},searchItems(e){const t=e.toLowerCase();Ao(this.$refs.root).find(".item").each((e,o)=>{const i=Ao(o);-1===i.text().toLowerCase().indexOf(t)?i.hide():i.show()})}}});_o.render=Fo;var Ro=_o;const Ho={ref:"root"};function $o(e,t,o,i,n,a){return Object(E["openBlock"])(),Object(E["createElementBlock"])("div",Ho,null,512)}const Uo=1,{$:qo}=window;var Wo=Object(E["defineComponent"])({props:{selectedDateStart:Date,selectedDateEnd:Date,highlightedDateStart:Date,highlightedDateEnd:Date,viewDate:[String,Date],stepMonths:Number,disableMonthDropdown:Boolean,options:Object},emits:["cellHover","cellHoverLeave","dateSelect"],setup(e,t){const o=Object(E["ref"])(null);function i(t,o){const i=t.children("a");if(e.selectedDateStart&&e.selectedDateEnd&&o>=e.selectedDateStart&&o<=e.selectedDateEnd?t.addClass("ui-datepicker-current-period"):t.removeClass("ui-datepicker-current-period"),e.highlightedDateStart&&e.highlightedDateEnd&&o>=e.highlightedDateStart&&o<=e.highlightedDateEnd){const e=i.length?i:t;e.addClass("ui-state-hover")}else t.removeClass("ui-state-hover"),i.removeClass("ui-state-hover")}function n(e,t,o){if(e.hasClass("ui-datepicker-other-month"))return a(e,t,o);const i=parseInt(e.children("a,span").text(),10);return new Date(o,t,i)}function a(e,t,o){let i;const a=e.parent(),r=a.children("td");if(a.is(":first-child")){const s=a.children("td:not(.ui-datepicker-other-month)").first();return i=n(s,t,o),i.setDate(r.index(e)-r.index(s)+1),i}const s=a.children("td:not(.ui-datepicker-other-month)").last();return i=n(s,t,o),i.setDate(i.getDate()+r.index(e)-r.index(s)),i}function r(){const e=qo(o.value),t=e.find("td[data-month]"),i=parseInt(t.attr("data-month"),10),n=parseInt(t.attr("data-year"),10);return[i,n]}function s(){const e=qo(o.value),t=e.find(".ui-datepicker-calendar"),a=r(),s=t.find("td"),l=s.first(),c=n(l,a[0],a[1]);s.each((function(){i(qo(this),c),c.setDate(c.getDate()+1)}))}function l(){if(!e.viewDate)return!1;let t;if("string"===typeof e.viewDate)try{t=m(e.viewDate)}catch(a){return!1}else t=e.viewDate;const i=qo(o.value),n=r();return(n[0]!==t.getMonth()||n[1]!==t.getFullYear())&&(i.datepicker("setDate",t),!0)}function c(){const e=qo(o.value);e.find("td[data-event]").off("click"),e.find(".ui-state-active").removeClass("ui-state-active"),e.find(".ui-datepicker-current-day").removeClass("ui-datepicker-current-day"),e.find(".ui-datepicker-prev,.ui-datepicker-next").attr("href","")}function d(){const t=qo(o.value),i=e.stepMonths||Uo;if(t.datepicker("option","stepMonths")===i)return!1;const n=qo(".ui-datepicker-month",t).val(),a=qo(".ui-datepicker-year",t).val();return t.datepicker("option","stepMonths",i).datepicker("setDate",new Date(a,n)),c(),!0}function u(){const t=qo(o.value),i=t.find(".ui-datepicker-month")[0];i&&(i.disabled=e.disableMonthDropdown)}function p(){if(!qo(this).hasClass("ui-state-hover"))return;const e=qo(this).parent(),t=e.parent();e.is(":first-child")?t.find("a").first().click():t.find("a").last().click()}function h(){u(),s()}return Object(E["watch"])(()=>Object.assign({},e),(e,t)=>{let o=!1;[e=>e.selectedDateStart,e=>e.selectedDateEnd,e=>e.highlightedDateStart,e=>e.highlightedDateEnd].forEach(i=>{if(o)return;const n=i(e),a=i(t);!n&&a&&(o=!0),n&&!a&&(o=!0),n&&a&&n.getTime()!==a.getTime()&&(o=!0)}),e.viewDate!==t.viewDate&&l()&&(o=!0),e.stepMonths!==t.stepMonths&&d(),e.disableMonthDropdown!==t.disableMonthDropdown&&u(),o&&s()}),Object(E["onMounted"])(()=>{const i=qo(o.value),a=e.options||{},m=Object.assign(Object.assign(Object.assign({},x.getBaseDatePickerOptions()),a),{},{onChangeMonthYear:()=>{setTimeout(()=>{c()})}});i.datepicker(m),i.on("mouseover","tbody td a",e=>{e.originalEvent&&s()}),i.on("mouseenter","tbody td",(function(){const e=r(),o=qo(this),i=n(o,e[0],e[1]);t.emit("cellHover",{date:i,$cell:o})})),i.on("mouseout","tbody td a",()=>{s()}),i.on("mouseleave","table",()=>t.emit("cellHoverLeave")).on("mouseenter","thead",()=>t.emit("cellHoverLeave")),i.on("click","tbody td.ui-datepicker-other-month",p),i.on("click",e=>{e.preventDefault();const t=qo(e.target).closest("a");(t.is(".ui-datepicker-next")||t.is(".ui-datepicker-prev"))&&h()}),i.on("click","td[data-month]",e=>{const o=qo(e.target).closest("td"),i=parseInt(o.attr("data-month"),10),n=parseInt(o.attr("data-year"),10),a=parseInt(o.children("a,span").text(),10);t.emit("dateSelect",{date:new Date(n,i,a)})});const g=d();l(),u(),g||c(),s()}),{root:o}}});Wo.render=$o;var zo=Wo;const Go={class:"dateRangePicker"},Yo={id:"calendarRangeFrom"},Jo={id:"calendarRangeTo"};function Ko(e,t,o,i,n,a){const r=Object(E["resolveComponent"])("DatePicker");return Object(E["openBlock"])(),Object(E["createElementBlock"])("div",Go,[Object(E["createElementVNode"])("div",Yo,[Object(E["createElementVNode"])("h6",null,[Object(E["createTextVNode"])(Object(E["toDisplayString"])(e.translate("General_DateRangeFrom"))+" ",1),Object(E["withDirectives"])(Object(E["createElementVNode"])("input",{type:"text",id:"inputCalendarFrom",name:"inputCalendarFrom",class:"browser-default","onUpdate:modelValue":t[0]||(t[0]=t=>e.startDateText=t),onKeydown:t[1]||(t[1]=t=>e.onRangeInputChanged("from",t)),onKeyup:t[2]||(t[2]=t=>e.handleEnterPress(t))},null,544),[[E["vModelText"],e.startDateText]])]),Object(E["createVNode"])(r,{id:"calendarFrom","view-date":e.startDate,"selected-date-start":e.fromPickerSelectedDates[0],"selected-date-end":e.fromPickerSelectedDates[1],"highlighted-date-start":e.fromPickerHighlightedDates[0],"highlighted-date-end":e.fromPickerHighlightedDates[1],onDateSelect:t[3]||(t[3]=t=>e.setStartRangeDate(t.date)),onCellHover:t[4]||(t[4]=t=>e.fromPickerHighlightedDates=e.getNewHighlightedDates(t.date,t.$cell)),onCellHoverLeave:t[5]||(t[5]=t=>e.fromPickerHighlightedDates=[null,null])},null,8,["view-date","selected-date-start","selected-date-end","highlighted-date-start","highlighted-date-end"])]),Object(E["createElementVNode"])("div",Jo,[Object(E["createElementVNode"])("h6",null,[Object(E["createTextVNode"])(Object(E["toDisplayString"])(e.translate("General_DateRangeTo"))+" ",1),Object(E["withDirectives"])(Object(E["createElementVNode"])("input",{type:"text",id:"inputCalendarTo",name:"inputCalendarTo",class:"browser-default","onUpdate:modelValue":t[6]||(t[6]=t=>e.endDateText=t),onKeydown:t[7]||(t[7]=t=>e.onRangeInputChanged("to",t)),onKeyup:t[8]||(t[8]=t=>e.handleEnterPress(t))},null,544),[[E["vModelText"],e.endDateText]])]),Object(E["createVNode"])(r,{id:"calendarTo","view-date":e.endDate,"selected-date-start":e.toPickerSelectedDates[0],"selected-date-end":e.toPickerSelectedDates[1],"highlighted-date-start":e.toPickerHighlightedDates[0],"highlighted-date-end":e.toPickerHighlightedDates[1],onDateSelect:t[9]||(t[9]=t=>e.setEndRangeDate(t.date)),onCellHover:t[10]||(t[10]=t=>e.toPickerHighlightedDates=e.getNewHighlightedDates(t.date,t.$cell)),onCellHoverLeave:t[11]||(t[11]=t=>e.toPickerHighlightedDates=[null,null])},null,8,["view-date","selected-date-start","selected-date-end","highlighted-date-start","highlighted-date-end"])])])}const Qo="YYYY-MM-DD";var Xo=Object(E["defineComponent"])({props:{startDate:String,endDate:String},components:{DatePicker:zo},data(){let e=null;try{this.startDate&&(e=m(this.startDate))}catch(o){}let t=null;try{this.endDate&&(t=m(this.endDate))}catch(o){}return{fromPickerSelectedDates:[e,e],toPickerSelectedDates:[t,t],fromPickerHighlightedDates:[null,null],toPickerHighlightedDates:[null,null],startDateText:this.startDate,endDateText:this.endDate,startDateInvalid:!1,endDateInvalid:!1}},emits:["rangeChange","submit"],watch:{startDate(){this.startDateText=this.startDate,this.setStartRangeDateFromStr(this.startDate)},endDate(){this.endDateText=this.endDate,this.setEndRangeDateFromStr(this.endDate)}},mounted(){this.rangeChanged()},methods:{setStartRangeDate(e){this.fromPickerSelectedDates=[e,e],this.rangeChanged()},setEndRangeDate(e){this.toPickerSelectedDates=[e,e],this.rangeChanged()},onRangeInputChanged(e,t){setTimeout(()=>{"from"===e?this.setStartRangeDateFromStr(t.target.value):this.setEndRangeDateFromStr(t.target.value)})},getNewHighlightedDates(e,t){return t.hasClass("ui-datepicker-unselectable")?null:[e,e]},handleEnterPress(e){13===e.keyCode&&this.$emit("submit",{start:this.startDate,end:this.endDate})},setStartRangeDateFromStr(e){this.startDateInvalid=!0;let t=null;try{e&&e.length===Qo.length&&(t=m(e))}catch(o){}t&&(this.fromPickerSelectedDates=[t,t],this.startDateInvalid=!1,this.rangeChanged())},setEndRangeDateFromStr(e){this.endDateInvalid=!0;let t=null;try{e&&e.length===Qo.length&&(t=m(e))}catch(o){}t&&(this.toPickerSelectedDates=[t,t],this.endDateInvalid=!1,this.rangeChanged())},rangeChanged(){this.$emit("rangeChange",{start:this.fromPickerSelectedDates[0]?d(this.fromPickerSelectedDates[0]):null,end:this.toPickerSelectedDates[0]?d(this.toPickerSelectedDates[0]):null})}}});Xo.render=Ko;var Zo=Xo;function ei(e,t,o,i,n,a){const r=Object(E["resolveComponent"])("DatePicker");return Object(E["openBlock"])(),Object(E["createBlock"])(r,{"selected-date-start":e.selectedDates[0],"selected-date-end":e.selectedDates[1],"highlighted-date-start":e.highlightedDates[0],"highlighted-date-end":e.highlightedDates[1],"view-date":e.viewDate,"step-months":"year"===e.period?12:1,"disable-month-dropdown":"year"===e.period,onCellHover:t[0]||(t[0]=t=>e.onHoverNormalCell(t.date,t.$cell)),onCellHoverLeave:t[1]||(t[1]=t=>e.onHoverLeaveNormalCells()),onDateSelect:t[2]||(t[2]=t=>e.onDateSelected(t.date))},null,8,["selected-date-start","selected-date-end","highlighted-date-start","highlighted-date-end","view-date","step-months","disable-month-dropdown"])}const ti=new Date(x.minDateYear,x.minDateMonth-1,x.minDateDay),oi=new Date(x.maxDateYear,x.maxDateMonth-1,x.maxDateDay);var ii=Object(E["defineComponent"])({props:{period:{type:String,required:!0},date:[String,Date]},components:{DatePicker:zo},emits:["select"],setup(e,t){const o=Object(E["ref"])(e.date),i=Object(E["ref"])([null,null]),n=Object(E["ref"])([null,null]);function a(t){const o=c.get(e.period).parse(t).getDateRange();return o[0]=tio[1]?o[1]:oi,o}function r(t,o){const i=toi,r=o.hasClass("ui-datepicker-other-month")&&("month"===e.period||"day"===e.period);n.value=i||r?[null,null]:a(t)}function s(){n.value=[null,null]}function l(e){t.emit("select",{date:e})}function d(){if(!e.period||!e.date)return i.value=[null,null],void(o.value=null);i.value=a(e.date),o.value=m(e.date)}return Object(E["watch"])(e,d),d(),{selectedDates:i,highlightedDates:n,viewDate:o,onHoverNormalCell:r,onHoverLeaveNormalCells:s,onDateSelected:l}}});ii.render=ei;var ni=ii;const ai={key:0},ri=["data-notification-instance-id"],si={key:1},li={class:"notification-body"},ci=["innerHTML"],di={key:1};function ui(e,t,o,i,n,a){return Object(E["openBlock"])(),Object(E["createBlock"])(E["Transition"],{name:"toast"===e.type?"slow-fade-out":void 0,onAfterLeave:t[1]||(t[1]=t=>e.toastClosed())},{default:Object(E["withCtx"])(()=>[e.deleted?Object(E["createCommentVNode"])("",!0):(Object(E["openBlock"])(),Object(E["createElementBlock"])("div",ai,[Object(E["createVNode"])(E["Transition"],{name:"toast"===e.type?"toast-slide-up":void 0,appear:""},{default:Object(E["withCtx"])(()=>[Object(E["createElementVNode"])("div",null,[Object(E["createVNode"])(E["Transition"],{name:e.animate?"fade-in":void 0,appear:""},{default:Object(E["withCtx"])(()=>[Object(E["createElementVNode"])("div",{class:Object(E["normalizeClass"])(["notification system",e.cssClasses]),style:Object(E["normalizeStyle"])(e.style),ref:"root","data-notification-instance-id":e.notificationInstanceId},[e.canClose?(Object(E["openBlock"])(),Object(E["createElementBlock"])("button",{key:0,type:"button",class:"close","data-dismiss":"alert",onClick:t[0]||(t[0]=t=>e.closeNotification(t))}," × ")):Object(E["createCommentVNode"])("",!0),e.title?(Object(E["openBlock"])(),Object(E["createElementBlock"])("strong",si,Object(E["toDisplayString"])(e.title),1)):Object(E["createCommentVNode"])("",!0),Object(E["createElementVNode"])("div",li,[e.message?(Object(E["openBlock"])(),Object(E["createElementBlock"])("div",{key:0,innerHTML:e.$sanitize(e.message)},null,8,ci)):Object(E["createCommentVNode"])("",!0),e.message?Object(E["createCommentVNode"])("",!0):(Object(E["openBlock"])(),Object(E["createElementBlock"])("div",di,[Object(E["renderSlot"])(e.$slots,"default")]))])],14,ri)]),_:3},8,["name"])])]),_:3},8,["name"])]))]),_:3},8,["name"])}const{$:mi}=window;var pi=Object(E["defineComponent"])({props:{notificationId:String,notificationInstanceId:String,title:String,context:String,type:String,noclear:Boolean,toastLength:{type:Number,default:12e3},style:[String,Object],animate:Boolean,message:String,cssClass:String},computed:{cssClasses(){const e={};return this.context&&(e["notification-"+this.context]=!0),this.cssClass&&(e[this.cssClass]=!0),e},canClose(){return"persistent"===this.type||!this.noclear}},emits:["closed"],data(){return{deleted:!1}},mounted(){const e=()=>{setTimeout(()=>{this.deleted=!0},this.toastLength)};"toast"===this.type&&e(),this.style&&mi(this.$refs.root).css(this.style)},methods:{toastClosed(){Object(E["nextTick"])(()=>{this.$emit("closed")})},closeNotification(e){this.canClose&&e&&e.target&&(this.deleted=!0,Object(E["nextTick"])(()=>{this.$emit("closed")})),this.markNotificationAsRead()},markNotificationAsRead(){this.notificationId&&W.post({module:"CoreHome",action:"markNotificationAsRead"},{notificationId:this.notificationId},{withTokenInUrl:!0})}}});pi.render=ui;var hi=pi;const gi={class:"notification-group"},bi=["innerHTML"];function fi(e,t,o,i,n,a){const r=Object(E["resolveComponent"])("Notification");return Object(E["openBlock"])(),Object(E["createElementBlock"])("div",gi,[(Object(E["openBlock"])(!0),Object(E["createElementBlock"])(E["Fragment"],null,Object(E["renderList"])(e.notifications,(t,o)=>(Object(E["openBlock"])(),Object(E["createBlock"])(r,{key:t.id||"no-id-"+o,"notification-id":t.id,title:t.title,context:t.context,type:t.type,noclear:t.noclear,"toast-length":t.toastLength,style:Object(E["normalizeStyle"])(t.style),animate:t.animate,message:t.message,"notification-instance-id":t.notificationInstanceId,"css-class":t.class,onClosed:o=>e.removeNotification(t.id)},{default:Object(E["withCtx"])(()=>[Object(E["createElementVNode"])("div",{innerHTML:e.$sanitize(t.message)},null,8,bi)]),_:2},1032,["notification-id","title","context","type","noclear","toast-length","style","animate","message","notification-instance-id","css-class","onClosed"]))),128))])}function vi(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e} + */const yo=8,wo=3;function ko(e){return e?Array.isArray(e)?e:[e]:[]}class So{constructor(){jo(this,"privateState",Object(E["reactive"])({comparisonsDisabledFor:[]})),jo(this,"state",Object(E["readonly"])(this.privateState)),jo(this,"colors",{}),jo(this,"segmentComparisons",Object(E["computed"])(()=>this.parseSegmentComparisons())),jo(this,"periodComparisons",Object(E["computed"])(()=>this.parsePeriodComparisons())),jo(this,"isEnabled",Object(E["computed"])(()=>this.checkEnabledForCurrentPage())),"complete"===document.readyState||"interactive"===document.readyState?this.loadComparisonsDisabledFor():document.addEventListener("DOMContentLoaded",()=>{this.loadComparisonsDisabledFor()}),$(()=>{this.colors=this.getAllSeriesColors()}),Object(E["watch"])(()=>this.getComparisons(),()=>x.postEvent("piwikComparisonsChanged"),{deep:!0})}getComparisons(){return this.getSegmentComparisons().concat(this.getPeriodComparisons())}isComparing(){return this.isComparisonEnabled()&&(this.segmentComparisons.value.length>1||this.periodComparisons.value.length>1)}isComparingPeriods(){return this.getPeriodComparisons().length>1}getSegmentComparisons(){return this.isComparisonEnabled()?this.segmentComparisons.value:[]}getPeriodComparisons(){return this.isComparisonEnabled()?this.periodComparisons.value:[]}getSeriesColor(e,t,o=0){const i=this.getComparisonSeriesIndex(t.index,e.index)%yo;if(0===o)return this.colors["series"+i];const n=o%wo;return this.colors[`series${i}-shade${n}`]}getSeriesColorName(e,t){let o="series"+e%yo;return t>0&&(o+="-shade"+t%wo),o}isComparisonEnabled(){return this.isEnabled.value}getIndividualComparisonRowIndices(e){const t=this.getSegmentComparisons().length,o=e%t,i=Math.floor(e/t);return{segmentIndex:o,periodIndex:i}}getComparisonSeriesIndex(e,t){const o=this.getSegmentComparisons().length;return e*o+t}getAllComparisonSeries(){const e=[];let t=0;return this.getPeriodComparisons().forEach(o=>{this.getSegmentComparisons().forEach(i=>{e.push({index:t,params:Object.assign(Object.assign({},i.params),o.params),color:this.colors["series"+t]}),t+=1})}),e}removeSegmentComparison(e){if(!this.isComparisonEnabled())throw new Error("Comparison disabled.");const t=[...this.segmentComparisons.value];t.splice(e,1);const o={};0===e&&(o.segment=t[0].params.segment),this.updateQueryParamsFromComparisons(t,this.periodComparisons.value,o)}removeSegmentComparisonByDefinition(e){if(!this.isComparisonEnabled())throw new Error("Comparison disabled.");let t=null;this.getSegmentComparisons().forEach((o,i)=>{o&&o.params&&o.params.segment===e&&(t=i)}),null!==t&&this.removeSegmentComparison(t)}addSegmentComparison(e){if(!this.isComparisonEnabled())throw new Error("Comparison disabled.");const t=this.segmentComparisons.value.concat([{params:e,index:-1,title:""}]);this.updateQueryParamsFromComparisons(t,this.periodComparisons.value)}updateQueryParamsFromComparisons(e,t,o={}){const i={},n={};let a=!1,r=!1;e.forEach(e=>{a?i[e.params.segment]=!0:a=!0}),t.forEach(e=>{r?n[`${e.params.period}|${e.params.date}`]=!0:r=!0});const s=[],l=[];Object.keys(n).forEach(e=>{const t=e.split("|");s.push(t[0]),l.push(t[1])});const c={compareSegments:Object.keys(i),comparePeriods:s,compareDates:l},d=x.helper.isReportingPage()?_.hashParsed.value:_.urlParsed.value;_.updateLocation(Object.assign(Object.assign(Object.assign({},d),c),o))}getAllSeriesColors(){const{ColorManager:e}=x;if(!e)return[];const t=[];for(let o=0;o{this.privateState.comparisonsDisabledFor=e})}parseSegmentComparisons(){const{availableSegments:e}=Oo.state,t=[...ko(_.parsed.value.compareSegments)];t.unshift(_.parsed.value.segment||"");const o=[];return t.forEach((t,i)=>{let n;e.forEach(e=>{e.definition!==t&&e.definition!==decodeURIComponent(t)&&decodeURIComponent(e.definition)!==t||(n=e)});let r=n?n.name:a("General_Unknown");""===t.trim()&&(r=a("SegmentEditor_DefaultAllVisits")),o.push({params:{segment:t},title:x.helper.htmlDecode(r),index:i})}),o}parsePeriodComparisons(){const e=[...ko(_.parsed.value.comparePeriods)],t=[...ko(_.parsed.value.compareDates)];e.unshift(_.parsed.value.period),t.unshift(_.parsed.value.date);const o=[];for(let n=0;nCo.isComparing()&&!window.broadcast.isNoDataPage()),t=Object(E["computed"])(()=>Co.getSegmentComparisons()),o=Object(E["computed"])(()=>Co.getPeriodComparisons()),i=Co.getSeriesColor.bind(Co);function n(){const e=window.$(this).attr("title");return e?window.vueSanitize(e.replace(/\n/g,"
    ")):e}return{isComparing:e,segmentComparisons:t,periodComparisons:o,getSeriesColor:i,transformTooltipContent:n}},methods:{comparisonHasSegment(e){return"undefined"!==typeof e.params.segment},removeSegmentComparison(e){window.$(this.$refs.root).tooltip("destroy"),Co.removeSegmentComparison(e)},getComparisonPeriodType(e){const{period:t}=e.params;if("range"===t)return a("CoreHome_PeriodRange");const o=a(`Intl_Period${t.substring(0,1).toUpperCase()}${t.substring(1)}`);return o.substring(0,1).toUpperCase()+o.substring(1)},getComparisonTooltip(e,t){if(this.comparisonTooltips&&Object.keys(this.comparisonTooltips).length)return(this.comparisonTooltips[t.index]||{})[e.index]},getTitleTooltip(e){return this.htmlentities(e.title)+"
    "+this.htmlentities(decodeURIComponent(e.params.segment))},getUrlToSegment(e){const t=Object.assign({},_.hashParsed.value);return delete t.comparePeriods,delete t.compareDates,delete t.compareSegments,t.segment=e,`${window.location.search}#?${_.stringify(t)}`},onComparisonsChanged(){if(this.comparisonTooltips=null,!Co.isComparing())return;const e=Co.getPeriodComparisons(),t=Co.getSegmentComparisons();W.fetch({method:"API.getProcessedReport",apiModule:"VisitsSummary",apiAction:"get",compare:"1",compareSegments:_.getSearchParam("compareSegments"),comparePeriods:_.getSearchParam("comparePeriods"),compareDates:_.getSearchParam("compareDates"),format_metrics:"1"}).then(o=>{this.comparisonTooltips={},e.forEach(e=>{this.comparisonTooltips[e.index]={},t.forEach(t=>{const i=this.generateComparisonTooltip(o,e,t);this.comparisonTooltips[e.index][t.index]=i})})})},generateComparisonTooltip(e,t,o){if(!e.reportData.comparisons)return"";const i=Co.getComparisonSeriesIndex(t.index,0),n=e.reportData.comparisons[i],r=Co.getComparisonSeriesIndex(t.index,o.index),s=e.reportData.comparisons[r],l=e.reportData.comparisons[o.index];let c='
    ',d=(s.nb_visits/n.nb_visits*100).toFixed(2);return d+="%",c+=a("General_ComparisonCardTooltip1",[`'${this.htmlentities(s.compareSegmentPretty)}'`,s.comparePeriodPretty,d,s.nb_visits.toString(),n.nb_visits.toString()]),t.index>0&&(c+="

    ",c+=a("General_ComparisonCardTooltip2",[s.nb_visits_change.toString(),this.htmlentities(l.compareSegmentPretty),l.comparePeriodPretty])),c+="
    ",c},htmlentities(e){return x.helper.htmlEntities(e)}},mounted(){x.on("piwikComparisonsChanged",()=>{this.onComparisonsChanged()}),this.onComparisonsChanged()}});Eo.render=bo;var Do=Eo;const Po={ref:"root",class:"menuDropdown"},To=["title"],Vo=["innerHTML"],No=Object(E["createElementVNode"])("span",{class:"icon-chevron-down reporting-menu-sub-icon"},null,-1),xo={class:"items"},Bo={key:0,class:"search"},Io=["placeholder"],Mo=["title"],Lo=["title"];function Fo(e,t,o,i,n,a){const r=Object(E["resolveDirective"])("focus-if"),s=Object(E["resolveDirective"])("focus-anywhere-but-here");return Object(E["withDirectives"])((Object(E["openBlock"])(),Object(E["createElementBlock"])("div",Po,[Object(E["createElementVNode"])("span",{class:"title",onClick:t[0]||(t[0]=t=>e.showItems=!e.showItems),title:e.tooltip},[Object(E["createElementVNode"])("span",{class:"title-label",innerHTML:e.$sanitize(this.actualMenuTitle)},null,8,Vo),No],8,To),Object(E["withDirectives"])(Object(E["createElementVNode"])("div",xo,[e.showSearch&&e.showItems?(Object(E["openBlock"])(),Object(E["createElementBlock"])("div",Bo,[Object(E["withDirectives"])(Object(E["createElementVNode"])("input",{type:"text","onUpdate:modelValue":t[1]||(t[1]=t=>e.searchTerm=t),onKeydown:t[2]||(t[2]=t=>e.onSearchTermKeydown(t)),placeholder:e.translate("General_Search")},null,40,Io),[[E["vModelText"],e.searchTerm],[r,{focused:e.showItems}]]),Object(E["withDirectives"])(Object(E["createElementVNode"])("div",{class:"search_ico icon-search",title:e.translate("General_Search")},null,8,Mo),[[E["vShow"],!e.searchTerm]]),Object(E["withDirectives"])(Object(E["createElementVNode"])("div",{onClick:t[3]||(t[3]=t=>{e.searchTerm="",e.searchItems("")}),class:"reset icon-close",title:e.translate("General_Clear")},null,8,Lo),[[E["vShow"],e.searchTerm]])])):Object(E["createCommentVNode"])("",!0),Object(E["createElementVNode"])("div",{onClick:t[4]||(t[4]=t=>e.selectItem(t))},[Object(E["renderSlot"])(e.$slots,"default")])],512),[[E["vShow"],e.showItems]])])),[[s,{blur:e.lostFocus}]])}const{$:Ao}=window;var _o=Object(E["defineComponent"])({props:{menuTitle:String,tooltip:String,showSearch:Boolean,menuTitleChangeOnClick:Boolean},directives:{FocusAnywhereButHere:Ge,FocusIf:Je},emits:["afterSelect"],watch:{menuTitle(){this.actualMenuTitle=this.menuTitle}},data(){return{showItems:!1,searchTerm:"",actualMenuTitle:this.menuTitle}},methods:{lostFocus(){this.showItems=!1},selectItem(e){const t=e.target.classList;!t.contains("item")||t.contains("disabled")||t.contains("separator")||(this.menuTitleChangeOnClick&&(this.actualMenuTitle=(e.target.textContent||"").replace(/[\u0000-\u2666]/g,e=>`&#${e.charCodeAt(0)};`)),this.showItems=!1,Ao(this.$slots.default()[0].el).find(".item").removeClass("active"),t.add("active"),this.$emit("afterSelect",e.target))},onSearchTermKeydown(){setTimeout(()=>{this.searchItems(this.searchTerm)})},searchItems(e){const t=e.toLowerCase();Ao(this.$refs.root).find(".item").each((e,o)=>{const i=Ao(o);-1===i.text().toLowerCase().indexOf(t)?i.hide():i.show()})}}});_o.render=Fo;var Ro=_o;const Ho={ref:"root"};function $o(e,t,o,i,n,a){return Object(E["openBlock"])(),Object(E["createElementBlock"])("div",Ho,null,512)}const Uo=1,{$:qo}=window;var Wo=Object(E["defineComponent"])({props:{selectedDateStart:Date,selectedDateEnd:Date,highlightedDateStart:Date,highlightedDateEnd:Date,viewDate:[String,Date],stepMonths:Number,disableMonthDropdown:Boolean,options:Object},emits:["cellHover","cellHoverLeave","dateSelect"],setup(e,t){const o=Object(E["ref"])(null);function i(t,o){const i=t.children("a");if(e.selectedDateStart&&e.selectedDateEnd&&o>=e.selectedDateStart&&o<=e.selectedDateEnd?t.addClass("ui-datepicker-current-period"):t.removeClass("ui-datepicker-current-period"),e.highlightedDateStart&&e.highlightedDateEnd&&o>=e.highlightedDateStart&&o<=e.highlightedDateEnd){const e=i.length?i:t;e.addClass("ui-state-hover")}else t.removeClass("ui-state-hover"),i.removeClass("ui-state-hover")}function n(e,t,o){if(e.hasClass("ui-datepicker-other-month"))return a(e,t,o);const i=parseInt(e.children("a,span").text(),10);return new Date(o,t,i)}function a(e,t,o){let i;const a=e.parent(),r=a.children("td");if(a.is(":first-child")){const s=a.children("td:not(.ui-datepicker-other-month)").first();return i=n(s,t,o),i.setDate(r.index(e)-r.index(s)+1),i}const s=a.children("td:not(.ui-datepicker-other-month)").last();return i=n(s,t,o),i.setDate(i.getDate()+r.index(e)-r.index(s)),i}function r(){const e=qo(o.value),t=e.find("td[data-month]"),i=parseInt(t.attr("data-month"),10),n=parseInt(t.attr("data-year"),10);return[i,n]}function s(){const e=qo(o.value),t=e.find(".ui-datepicker-calendar"),a=r(),s=t.find("td"),l=s.first(),c=n(l,a[0],a[1]);s.each((function(){i(qo(this),c),c.setDate(c.getDate()+1)}))}function l(){if(!e.viewDate)return!1;let t;if("string"===typeof e.viewDate)try{t=m(e.viewDate)}catch(a){return!1}else t=e.viewDate;const i=qo(o.value),n=r();return(n[0]!==t.getMonth()||n[1]!==t.getFullYear())&&(i.datepicker("setDate",t),!0)}function c(){const e=qo(o.value);e.find("td[data-event]").off("click"),e.find(".ui-state-active").removeClass("ui-state-active"),e.find(".ui-datepicker-current-day").removeClass("ui-datepicker-current-day"),e.find(".ui-datepicker-prev,.ui-datepicker-next").attr("href","")}function d(){const t=qo(o.value),i=e.stepMonths||Uo;if(t.datepicker("option","stepMonths")===i)return!1;const n=qo(".ui-datepicker-month",t).val(),a=qo(".ui-datepicker-year",t).val();return t.datepicker("option","stepMonths",i).datepicker("setDate",new Date(a,n)),c(),!0}function u(){const t=qo(o.value),i=t.find(".ui-datepicker-month")[0];i&&(i.disabled=e.disableMonthDropdown)}function p(){if(!qo(this).hasClass("ui-state-hover"))return;const e=qo(this).parent(),t=e.parent();e.is(":first-child")?t.find("a").first().click():t.find("a").last().click()}function h(){u(),s()}return Object(E["watch"])(()=>Object.assign({},e),(e,t)=>{let o=!1;[e=>e.selectedDateStart,e=>e.selectedDateEnd,e=>e.highlightedDateStart,e=>e.highlightedDateEnd].forEach(i=>{if(o)return;const n=i(e),a=i(t);!n&&a&&(o=!0),n&&!a&&(o=!0),n&&a&&n.getTime()!==a.getTime()&&(o=!0)}),e.viewDate!==t.viewDate&&l()&&(o=!0),e.stepMonths!==t.stepMonths&&d(),e.disableMonthDropdown!==t.disableMonthDropdown&&u(),o&&s()}),Object(E["onMounted"])(()=>{const i=qo(o.value),a=e.options||{},m=Object.assign(Object.assign(Object.assign({},x.getBaseDatePickerOptions()),a),{},{onChangeMonthYear:()=>{setTimeout(()=>{c()})}});i.datepicker(m),i.on("mouseover","tbody td a",e=>{e.originalEvent&&s()}),i.on("mouseenter","tbody td",(function(){const e=r(),o=qo(this),i=n(o,e[0],e[1]);t.emit("cellHover",{date:i,$cell:o})})),i.on("mouseout","tbody td a",()=>{s()}),i.on("mouseleave","table",()=>t.emit("cellHoverLeave")).on("mouseenter","thead",()=>t.emit("cellHoverLeave")),i.on("click","tbody td.ui-datepicker-other-month",p),i.on("click",e=>{e.preventDefault();const t=qo(e.target).closest("a");(t.is(".ui-datepicker-next")||t.is(".ui-datepicker-prev"))&&h()}),i.on("click","td[data-month]",e=>{const o=qo(e.target).closest("td"),i=parseInt(o.attr("data-month"),10),n=parseInt(o.attr("data-year"),10),a=parseInt(o.children("a,span").text(),10);t.emit("dateSelect",{date:new Date(n,i,a)})});const g=d();l(),u(),g||c(),s()}),{root:o}}});Wo.render=$o;var zo=Wo;const Go={class:"dateRangePicker"},Yo={id:"calendarRangeFrom"},Jo={id:"calendarRangeTo"};function Ko(e,t,o,i,n,a){const r=Object(E["resolveComponent"])("DatePicker");return Object(E["openBlock"])(),Object(E["createElementBlock"])("div",Go,[Object(E["createElementVNode"])("div",Yo,[Object(E["createElementVNode"])("h6",null,[Object(E["createTextVNode"])(Object(E["toDisplayString"])(e.translate("General_DateRangeFrom"))+" ",1),Object(E["withDirectives"])(Object(E["createElementVNode"])("input",{type:"text",id:"inputCalendarFrom",name:"inputCalendarFrom",class:"browser-default","onUpdate:modelValue":t[0]||(t[0]=t=>e.startDateText=t),onKeydown:t[1]||(t[1]=t=>e.onRangeInputChanged("from",t)),onKeyup:t[2]||(t[2]=t=>e.handleEnterPress(t))},null,544),[[E["vModelText"],e.startDateText]])]),Object(E["createVNode"])(r,{id:"calendarFrom","view-date":e.startDate,"selected-date-start":e.fromPickerSelectedDates[0],"selected-date-end":e.fromPickerSelectedDates[1],"highlighted-date-start":e.fromPickerHighlightedDates[0],"highlighted-date-end":e.fromPickerHighlightedDates[1],onDateSelect:t[3]||(t[3]=t=>e.setStartRangeDate(t.date)),onCellHover:t[4]||(t[4]=t=>e.fromPickerHighlightedDates=e.getNewHighlightedDates(t.date,t.$cell)),onCellHoverLeave:t[5]||(t[5]=t=>e.fromPickerHighlightedDates=[null,null])},null,8,["view-date","selected-date-start","selected-date-end","highlighted-date-start","highlighted-date-end"])]),Object(E["createElementVNode"])("div",Jo,[Object(E["createElementVNode"])("h6",null,[Object(E["createTextVNode"])(Object(E["toDisplayString"])(e.translate("General_DateRangeTo"))+" ",1),Object(E["withDirectives"])(Object(E["createElementVNode"])("input",{type:"text",id:"inputCalendarTo",name:"inputCalendarTo",class:"browser-default","onUpdate:modelValue":t[6]||(t[6]=t=>e.endDateText=t),onKeydown:t[7]||(t[7]=t=>e.onRangeInputChanged("to",t)),onKeyup:t[8]||(t[8]=t=>e.handleEnterPress(t))},null,544),[[E["vModelText"],e.endDateText]])]),Object(E["createVNode"])(r,{id:"calendarTo","view-date":e.endDate,"selected-date-start":e.toPickerSelectedDates[0],"selected-date-end":e.toPickerSelectedDates[1],"highlighted-date-start":e.toPickerHighlightedDates[0],"highlighted-date-end":e.toPickerHighlightedDates[1],onDateSelect:t[9]||(t[9]=t=>e.setEndRangeDate(t.date)),onCellHover:t[10]||(t[10]=t=>e.toPickerHighlightedDates=e.getNewHighlightedDates(t.date,t.$cell)),onCellHoverLeave:t[11]||(t[11]=t=>e.toPickerHighlightedDates=[null,null])},null,8,["view-date","selected-date-start","selected-date-end","highlighted-date-start","highlighted-date-end"])])])}const Qo="YYYY-MM-DD";var Xo=Object(E["defineComponent"])({props:{startDate:String,endDate:String},components:{DatePicker:zo},data(){let e=null;try{this.startDate&&(e=m(this.startDate))}catch(o){}let t=null;try{this.endDate&&(t=m(this.endDate))}catch(o){}return{fromPickerSelectedDates:[e,e],toPickerSelectedDates:[t,t],fromPickerHighlightedDates:[null,null],toPickerHighlightedDates:[null,null],startDateText:this.startDate,endDateText:this.endDate,startDateInvalid:!1,endDateInvalid:!1}},emits:["rangeChange","submit"],watch:{startDate(){this.startDateText=this.startDate,this.setStartRangeDateFromStr(this.startDate)},endDate(){this.endDateText=this.endDate,this.setEndRangeDateFromStr(this.endDate)}},mounted(){this.rangeChanged()},methods:{setStartRangeDate(e){this.fromPickerSelectedDates=[e,e],this.rangeChanged()},setEndRangeDate(e){this.toPickerSelectedDates=[e,e],this.rangeChanged()},onRangeInputChanged(e,t){setTimeout(()=>{"from"===e?this.setStartRangeDateFromStr(t.target.value):this.setEndRangeDateFromStr(t.target.value)})},getNewHighlightedDates(e,t){return t.hasClass("ui-datepicker-unselectable")?null:[e,e]},handleEnterPress(e){13===e.keyCode&&this.$emit("submit",{start:this.startDate,end:this.endDate})},setStartRangeDateFromStr(e){this.startDateInvalid=!0;let t=null;try{e&&e.length===Qo.length&&(t=m(e))}catch(o){}t&&(this.fromPickerSelectedDates=[t,t],this.startDateInvalid=!1,this.rangeChanged())},setEndRangeDateFromStr(e){this.endDateInvalid=!0;let t=null;try{e&&e.length===Qo.length&&(t=m(e))}catch(o){}t&&(this.toPickerSelectedDates=[t,t],this.endDateInvalid=!1,this.rangeChanged())},rangeChanged(){this.$emit("rangeChange",{start:this.fromPickerSelectedDates[0]?d(this.fromPickerSelectedDates[0]):null,end:this.toPickerSelectedDates[0]?d(this.toPickerSelectedDates[0]):null})}}});Xo.render=Ko;var Zo=Xo;function ei(e,t,o,i,n,a){const r=Object(E["resolveComponent"])("DatePicker");return Object(E["openBlock"])(),Object(E["createBlock"])(r,{"selected-date-start":e.selectedDates[0],"selected-date-end":e.selectedDates[1],"highlighted-date-start":e.highlightedDates[0],"highlighted-date-end":e.highlightedDates[1],"view-date":e.viewDate,"step-months":"year"===e.period?12:1,"disable-month-dropdown":"year"===e.period,onCellHover:t[0]||(t[0]=t=>e.onHoverNormalCell(t.date,t.$cell)),onCellHoverLeave:t[1]||(t[1]=t=>e.onHoverLeaveNormalCells()),onDateSelect:t[2]||(t[2]=t=>e.onDateSelected(t.date))},null,8,["selected-date-start","selected-date-end","highlighted-date-start","highlighted-date-end","view-date","step-months","disable-month-dropdown"])}const ti=new Date(x.minDateYear,x.minDateMonth-1,x.minDateDay),oi=new Date(x.maxDateYear,x.maxDateMonth-1,x.maxDateDay);var ii=Object(E["defineComponent"])({props:{period:{type:String,required:!0},date:[String,Date]},components:{DatePicker:zo},emits:["select"],setup(e,t){const o=Object(E["ref"])(e.date),i=Object(E["ref"])([null,null]),n=Object(E["ref"])([null,null]);function a(t){const o=c.get(e.period).parse(t).getDateRange();return o[0]=tio[1]?o[1]:oi,o}function r(t,o){const i=toi,r=o.hasClass("ui-datepicker-other-month")&&("month"===e.period||"day"===e.period);n.value=i||r?[null,null]:a(t)}function s(){n.value=[null,null]}function l(e){t.emit("select",{date:e})}function d(){if(!e.period||!e.date)return i.value=[null,null],void(o.value=null);i.value=a(e.date),o.value=m(e.date)}return Object(E["watch"])(e,d),d(),{selectedDates:i,highlightedDates:n,viewDate:o,onHoverNormalCell:r,onHoverLeaveNormalCells:s,onDateSelected:l}}});ii.render=ei;var ni=ii;const ai={key:0},ri=["data-notification-instance-id"],si={key:1},li={class:"notification-body"},ci=["innerHTML"],di={key:1};function ui(e,t,o,i,n,a){return Object(E["openBlock"])(),Object(E["createBlock"])(E["Transition"],{name:"toast"===e.type?"slow-fade-out":void 0,onAfterLeave:t[1]||(t[1]=t=>e.toastClosed())},{default:Object(E["withCtx"])(()=>[e.deleted?Object(E["createCommentVNode"])("",!0):(Object(E["openBlock"])(),Object(E["createElementBlock"])("div",ai,[Object(E["createVNode"])(E["Transition"],{name:"toast"===e.type?"toast-slide-up":void 0,appear:""},{default:Object(E["withCtx"])(()=>[Object(E["createElementVNode"])("div",null,[Object(E["createVNode"])(E["Transition"],{name:e.animate?"fade-in":void 0,appear:""},{default:Object(E["withCtx"])(()=>[Object(E["createElementVNode"])("div",{class:Object(E["normalizeClass"])(["notification system",e.cssClasses]),style:Object(E["normalizeStyle"])(e.style),ref:"root","data-notification-instance-id":e.notificationInstanceId},[e.canClose?(Object(E["openBlock"])(),Object(E["createElementBlock"])("button",{key:0,type:"button",class:"close","data-dismiss":"alert",onClick:t[0]||(t[0]=t=>e.closeNotification(t))}," × ")):Object(E["createCommentVNode"])("",!0),e.title?(Object(E["openBlock"])(),Object(E["createElementBlock"])("strong",si,Object(E["toDisplayString"])(e.title),1)):Object(E["createCommentVNode"])("",!0),Object(E["createElementVNode"])("div",li,[e.message?(Object(E["openBlock"])(),Object(E["createElementBlock"])("div",{key:0,innerHTML:e.$sanitize(e.message)},null,8,ci)):Object(E["createCommentVNode"])("",!0),e.message?Object(E["createCommentVNode"])("",!0):(Object(E["openBlock"])(),Object(E["createElementBlock"])("div",di,[Object(E["renderSlot"])(e.$slots,"default")]))])],14,ri)]),_:3},8,["name"])])]),_:3},8,["name"])]))]),_:3},8,["name"])}const{$:mi}=window;var pi=Object(E["defineComponent"])({props:{notificationId:String,notificationInstanceId:String,title:String,context:String,type:String,noclear:Boolean,toastLength:{type:Number,default:12e3},style:[String,Object],animate:Boolean,message:String,cssClass:String},computed:{cssClasses(){const e={};return this.context&&(e["notification-"+this.context]=!0),this.cssClass&&(e[this.cssClass]=!0),e},canClose(){return"persistent"===this.type||!this.noclear}},emits:["closed"],data(){return{deleted:!1}},mounted(){const e=()=>{setTimeout(()=>{this.deleted=!0},this.toastLength)};"toast"===this.type&&e(),this.style&&mi(this.$refs.root).css(this.style)},methods:{toastClosed(){Object(E["nextTick"])(()=>{this.$emit("closed")})},closeNotification(e){this.canClose&&e&&e.target&&(this.deleted=!0,Object(E["nextTick"])(()=>{this.$emit("closed")})),this.markNotificationAsRead()},markNotificationAsRead(){this.notificationId&&W.post({module:"CoreHome",action:"markNotificationAsRead"},{notificationId:this.notificationId},{withTokenInUrl:!0})}}});pi.render=ui;var hi=pi;const gi={class:"notification-group"},bi=["innerHTML"];function fi(e,t,o,i,n,a){const r=Object(E["resolveComponent"])("Notification");return Object(E["openBlock"])(),Object(E["createElementBlock"])("div",gi,[(Object(E["openBlock"])(!0),Object(E["createElementBlock"])(E["Fragment"],null,Object(E["renderList"])(e.notifications,(t,o)=>(Object(E["openBlock"])(),Object(E["createBlock"])(r,{key:t.id||"no-id-"+o,"notification-id":t.id,title:t.title,context:t.context,type:t.type,noclear:t.noclear,"toast-length":t.toastLength,style:Object(E["normalizeStyle"])(t.style),animate:t.animate,message:t.message,"notification-instance-id":t.notificationInstanceId,"css-class":t.class,onClosed:o=>e.removeNotification(t.id)},{default:Object(E["withCtx"])(()=>[Object(E["createElementVNode"])("div",{innerHTML:e.$sanitize(t.message)},null,8,bi)]),_:2},1032,["notification-id","title","context","type","noclear","toast-length","style","animate","message","notification-instance-id","css-class","onClosed"]))),128))])}function vi(e,t,o){return t in e?Object.defineProperty(e,t,{value:o,enumerable:!0,configurable:!0,writable:!0}):e[t]=o,e} /*! * Matomo - free/libre analytics platform * diff --git a/plugins/CoreHome/vue/src/Comparisons/Comparisons.store.ts b/plugins/CoreHome/vue/src/Comparisons/Comparisons.store.ts index af8176f1062..aecbe1df97e 100644 --- a/plugins/CoreHome/vue/src/Comparisons/Comparisons.store.ts +++ b/plugins/CoreHome/vue/src/Comparisons/Comparisons.store.ts @@ -216,6 +216,21 @@ export default class ComparisonsStore { ); } + removeSegmentComparisonByDefinition(segmentDefinition: string): void { + if (!this.isComparisonEnabled()) { + throw new Error('Comparison disabled.'); + } + let segmentIndex = null; + this.getSegmentComparisons().forEach((segment: SegmentComparison, index: number) => { + if (segment && segment.params && segment.params.segment === segmentDefinition) { + segmentIndex = index; + } + }); + if (segmentIndex !== null) { + this.removeSegmentComparison(segmentIndex); + } + } + addSegmentComparison(params: { [name: string]: string }): void { if (!this.isComparisonEnabled()) { throw new Error('Comparison disabled.'); diff --git a/plugins/CoreVue/types/index.d.ts b/plugins/CoreVue/types/index.d.ts index a25ce2f6c7e..951112c8e09 100644 --- a/plugins/CoreVue/types/index.d.ts +++ b/plugins/CoreVue/types/index.d.ts @@ -171,6 +171,8 @@ declare global { languageName: string; isPagesComparisonApiDisabled: boolean; // can be set to avoid checks on Api.getPagesComparisonsDisabledFor userLogin?: string; + userCurrentRole: string; + isUserAnonymous: boolean; userHasSomeAdminAccess: boolean; requiresPasswordConfirmation: boolean; disableTrackingMatomoAppLinks: boolean; diff --git a/plugins/Morpheus/templates/_jsGlobalVariables.twig b/plugins/Morpheus/templates/_jsGlobalVariables.twig index ade81671f7c..43f91787235 100644 --- a/plugins/Morpheus/templates/_jsGlobalVariables.twig +++ b/plugins/Morpheus/templates/_jsGlobalVariables.twig @@ -68,6 +68,8 @@ piwik.hasSuperUserAccess = {{ hasSuperUserAccess|default(0)|e('js')}}; piwik.userHasSomeAdminAccess = {{ userHasSomeAdminAccess|json_encode|raw }}; + piwik.userCurrentRole = "{{ userCurrentRole|default('view')|e('js') }}"; + piwik.isUserIsAnonymous = {{ isUserIsAnonymous|default(0)|e('js') }}; piwik.userCapabilities = {{ userCapabilities|default([])|json_encode|raw }}; piwik.config = {}; {% if clientSideConfig is defined %} diff --git a/plugins/SegmentEditor/API.php b/plugins/SegmentEditor/API.php index 6dd31337ac5..eb0e5526235 100644 --- a/plugins/SegmentEditor/API.php +++ b/plugins/SegmentEditor/API.php @@ -192,7 +192,7 @@ protected function checkUserCanEditOrDeleteSegment(array $segment): void throw new Exception($this->getMessageCannotEditSegmentCreatedBySuperUser()); } - if ((int) $segment['enable_only_idsite'] === 0 && !Piwik::hasUserSuperUserAccess()) { + if ((int) $segment['enable_only_idsite'] === 0) { throw new Exception(Piwik::translate('SegmentEditor_UpdatingAllSitesSegmentPermittedToSuperUser')); } } diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js index a0237294e09..2322b6f456a 100644 --- a/plugins/SegmentEditor/javascripts/Segmentation.js +++ b/plugins/SegmentEditor/javascripts/Segmentation.js @@ -105,7 +105,6 @@ Segmentation = (function($) { const comparedSegmentsLength = comparisonService.getSegmentComparisons().length; $('div.segmentList ul li[data-definition] .compareSegment').each(function() { const $compareButton = $(this); - const $segment = $compareButton.parent(); const currentState = $compareButton.attr('data-state'); if (currentState === 'active') { return; @@ -126,8 +125,7 @@ Segmentation = (function($) { var segmentationTitle = $(this.content).find(".segmentationTitle"); var title; - if( current != "") - { + if (current != "") { // this code is mad, and may drive you mad. // the whole segmentation editor needs to be rewritten in Vue with clean code var selector = 'div.segmentList ul li[data-definition="'+current+'"]'; @@ -368,6 +366,7 @@ Segmentation = (function($) { } var filterSegmentList = function (keyword) { + var search = normalizeSearchString(keyword); var curTitle; clearFilterSegmentList(); $(self.target).find(".filterNoResults").remove(); @@ -375,7 +374,7 @@ Segmentation = (function($) { $(self.target).find(".segmentList li").each(function () { curTitle = $(this).find('.segname').prop('title'); $(this).hide(); - if (curTitle.toLowerCase().indexOf(keyword.toLowerCase()) !== -1) { + if (normalizeSearchString(curTitle).indexOf(search) !== -1) { $(this).show(); } }); @@ -476,9 +475,14 @@ Segmentation = (function($) { return false; } const comparisonService = window.CoreHome.ComparisonsStoreInstance; - comparisonService.addSegmentComparison({ - segment: $button.closest('li').data('definition'), - }); + const segmentDefinition = $button.closest('li').data('definition'); + if ($button.attr('data-state') === 'active') { + comparisonService.removeSegmentComparisonByDefinition(segmentDefinition); + } else { + comparisonService.addSegmentComparison({ + segment: segmentDefinition, + }); + } closeAllOpenLists(); }); @@ -622,18 +626,28 @@ Segmentation = (function($) { if (self.segmentAccess !== 'write') { return false; } - return (segment.login === piwik.userLogin || piwik.hasSuperUserAccess); + if (piwik.hasSuperUserAccess || piwik.userCurrentRole === 'admin' || piwik.userCurrentRole === 'write') { + return true; + } + + return (segment.login === piwik.userLogin); } function getStarredByTitlePart(segment) { const login = segment.starred_by || ''; if (login === piwik.userLogin) { - return ' (' + self.translations['General_StarredByYou'] + ')' + return ' (' + self.translations['General_StarredByYou'] + ')'; } - return ' (' + self.translations['General_StarredBy'] + ' ' + login + ')' + + return ' (' + self.translations['General_StarredBy'] + ' ' + login + ')'; } function getStarSegmentTitle(segment, canEdit) { + // Anonymous users do not have any action + if (piwik.isUserAnonymous) { + return ''; + } + // Site-specific segments if (segment.enable_only_idsite) { if (canEdit) { diff --git a/plugins/SegmentEditor/stylesheets/segmentation.less b/plugins/SegmentEditor/stylesheets/segmentation.less index 6ee68ad987e..ce68984498c 100644 --- a/plugins/SegmentEditor/stylesheets/segmentation.less +++ b/plugins/SegmentEditor/stylesheets/segmentation.less @@ -264,6 +264,10 @@ div.scrollable { order: 1; } +.segmentationContainer .submenu ul li.comparedSegment { + font-weight: bold; +} + .segmentationContainer .submenu ul li:hover { color: #255792; background: @color-silver-l95; @@ -344,14 +348,6 @@ div.scrollable { opacity: 0.2; cursor: not-allowed; } - - &[data-state=active] { - border-radius: 50%; - background-color: white; - background-size: 70%; - background-position: center; - filter: invert(1); - } } .editSegment { @@ -392,6 +388,11 @@ div.scrollable { animation-direction: reverse; } + /* We do not want filled & transparent stars, design wanted by Matthieu */ + .segmentStarred .starSegment[data-state=disabled] { + opacity: 0.5; + } + .segmentStarred .starSegment path { fill: black; } diff --git a/plugins/SegmentEditor/tests/UI/SegmentSelectorEditor_spec.js b/plugins/SegmentEditor/tests/UI/SegmentSelectorEditor_spec.js index ccbbab4bbc9..053f6650465 100644 --- a/plugins/SegmentEditor/tests/UI/SegmentSelectorEditor_spec.js +++ b/plugins/SegmentEditor/tests/UI/SegmentSelectorEditor_spec.js @@ -33,6 +33,26 @@ describe("SegmentSelectorEditorTest", function () { await (await page.jQuery(prefixSelector + ' .metricListBlock .expandableList .secondLevel li:contains(' + name + ')', { waitFor: true })).click(); } + async function switchToAnonymousUser() { + await testEnvironment.callApi('UsersManager.setUserAccess', { + userLogin: 'anonymous', + access: 'view', + idSites: [1], + }); + testEnvironment.testUseMockAuth = 0; + testEnvironment.save(); + } + + async function switchToConnectedUser() { + testEnvironment.testUseMockAuth = 1; + testEnvironment.save(); + await testEnvironment.callApi('UsersManager.setUserAccess', { + userLogin: 'anonymous', + access: 'noaccess', + idSites: [1], + }); + } + it("should load correctly", async function() { await page.goto(url); expect(await page.screenshotSelector(selectorsToCapture)).to.matchImage('0_initial'); @@ -43,21 +63,39 @@ describe("SegmentSelectorEditorTest", function () { expect(await page.screenshotSelector(selectorsToCapture)).to.matchImage('1_selector_open'); }); - it("should unstar all segments", async function() { - await page.click('.segmentList li:nth-child(2) .starSegment'); - await page.click('.segmentList li:nth-child(3) .starSegment'); - await page.click('.segmentList li:nth-child(4) .starSegment'); - expect(await page.screenshotSelector(selectorsToCapture)).to.matchImage('1_selector_unstarred'); + it("should star all segments", async function() { + await page.click('.segmentList li:nth-child(2) .starSegment'); + await page.click('.segmentList li:nth-child(3) .starSegment'); + await page.click('.segmentList li:nth-child(4) .starSegment'); + const firstSegment = await page.$('.segmentList li:nth-child(2)'); + expect(firstSegment.className).to.contain('segmentStarred'); + expect(firstSegment.find('.starSegment').attr('data-state')).to.equal(''); + expect(await page.screenshotSelector(selectorsToCapture)).to.matchImage('1_selector_starred'); }); - it("should star last segment", async function() { - await page.click('.segmentList li:last-child .starSegment'); - expect(await page.screenshotSelector(selectorsToCapture)).to.matchImage('1_selector_starred'); + it("should unstar first segment", async function() { + await page.click('.segmentList li:nth-child(2) .starSegment'); + const firstSegment = await page.$('.segmentList li:nth-child(2)'); + expect(firstSegment.className).to.not.contain('segmentStarred'); + expect(firstSegment.find('.starSegment').attr('data-state')).to.equal(''); + expect(await page.screenshotSelector(selectorsToCapture)).to.matchImage('1b_selector_unstarred'); + }); + + it("should have disabled star for anonymous users", async function() { + await switchToAnonymousUser(); + await page.goto(url); + await page.click('.segmentationContainer .title'); + const firstSegment = await page.$('.segmentList li:nth-child(2)'); + expect(firstSegment.className).to.contain('segmentStarred'); + expect(firstSegment.find('.starSegment').attr('data-state')).to.equal(''); }); it("should open segment editor when edit link clicked for existing segment", async function() { + await switchToConnectedUser(); + await page.goto(url); + await page.click('.segmentationContainer .title'); await page.evaluate(function() { - $('.segmentList .editSegment:first').click() + $('.segmentList .editSegment:first').click(); }); await page.waitForNetworkIdle(); expect(await page.screenshotSelector(selectorsToCapture)).to.matchImage('2_segment_editor_update'); @@ -127,10 +165,10 @@ describe("SegmentSelectorEditorTest", function () { it("should save a new segment and add it to the segment list when the form is filled out and the save button is clicked", async function() { for (let i = 0; i < 3; i += 1) { - await page.evaluate(function (i) { - $(`.metricValueBlock input:eq(${i})`).val('value ' + i).change(); - }, i); - await page.waitForTimeout(250); + await page.evaluate(function (i) { + $(`.metricValueBlock input:eq(${i})`).val('value ' + i).change(); + }, i); + await page.waitForTimeout(250); } await page.type('input.edit_segment_name', 'new segment'); From 57caa8e4435a998ff972fd1b811b45b646c9a302 Mon Sep 17 00:00:00 2001 From: Thomas ZILLIOX Date: Mon, 8 Dec 2025 13:30:43 +0100 Subject: [PATCH 17/17] Fix PHPStan snapshot and UI screenshots --- phpstan-baseline.neon | 5 ---- .../tests/UI/SegmentSelectorEditor_spec.js | 29 ++++++++++--------- ...mentSelectorEditorTest_1_selector_open.png | 4 +-- ...tSelectorEditorTest_1_selector_starred.png | 4 +-- ...lectorEditorTest_1b_selector_unstarred.png | 3 ++ ...ctorEditorTest_2_segment_editor_update.png | 4 +-- .../SegmentSelectorEditorTest_deleted.png | 4 +-- ...enabled_create_realtime_segments_saved.png | 4 +-- .../SegmentSelectorEditorTest_saved.png | 4 +-- .../SegmentSelectorEditorTest_updated.png | 4 +-- .../UIIntegrationTest_api_listing.png | 4 +-- 11 files changed, 35 insertions(+), 34 deletions(-) create mode 100644 plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1b_selector_unstarred.png diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a351575671f..245feecc6be 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -4658,11 +4658,6 @@ parameters: count: 1 path: plugins/ScheduledReports/ScheduledReports.php - - - message: "#^Negated boolean expression is always true\\.$#" - count: 1 - path: plugins/SegmentEditor/API.php - - message: "#^Parameter \\#2 \\$idSites of class Piwik\\\\Segment constructor expects array, int\\|null given\\.$#" count: 1 diff --git a/plugins/SegmentEditor/tests/UI/SegmentSelectorEditor_spec.js b/plugins/SegmentEditor/tests/UI/SegmentSelectorEditor_spec.js index 053f6650465..be740816c80 100644 --- a/plugins/SegmentEditor/tests/UI/SegmentSelectorEditor_spec.js +++ b/plugins/SegmentEditor/tests/UI/SegmentSelectorEditor_spec.js @@ -8,6 +8,8 @@ */ describe("SegmentSelectorEditorTest", function () { + const getSegmentQuery = n => '.segmentList li:nth-of-type(' + (n+1) + ')'; + const getSegmentStarQuery = n => getSegmentQuery(n) + ' .starSegment'; var selectorsToCapture = ".segmentEditorPanel,.segmentEditorPanel .dropdown-body,.segment-element"; var generalParams = 'idSite=1&period=year&date=2012-08-09'; var url = '?module=CoreHome&action=index&' + generalParams + '#?' + generalParams + '&category=General_Actions&subcategory=General_Pages'; @@ -64,20 +66,22 @@ describe("SegmentSelectorEditorTest", function () { }); it("should star all segments", async function() { - await page.click('.segmentList li:nth-child(2) .starSegment'); - await page.click('.segmentList li:nth-child(3) .starSegment'); - await page.click('.segmentList li:nth-child(4) .starSegment'); - const firstSegment = await page.$('.segmentList li:nth-child(2)'); - expect(firstSegment.className).to.contain('segmentStarred'); - expect(firstSegment.find('.starSegment').attr('data-state')).to.equal(''); + await page.click(getSegmentStarQuery(1)); + await page.click(getSegmentStarQuery(2)); + await page.click(getSegmentStarQuery(3)); + const firstSegmentClassName = await page.evaluate(() => $('.segmentList li:nth-of-type(2)').attr('class')); + expect(firstSegmentClassName).to.match(/segmentStarred/); + const firstSegmentStarState = await page.evaluate(() => $('.segmentList li:nth-of-type(2) .starSegment').attr('data-state') || ''); + expect(firstSegmentStarState).to.equal(''); expect(await page.screenshotSelector(selectorsToCapture)).to.matchImage('1_selector_starred'); }); it("should unstar first segment", async function() { - await page.click('.segmentList li:nth-child(2) .starSegment'); - const firstSegment = await page.$('.segmentList li:nth-child(2)'); - expect(firstSegment.className).to.not.contain('segmentStarred'); - expect(firstSegment.find('.starSegment').attr('data-state')).to.equal(''); + await page.click(getSegmentStarQuery(1)); + const firstSegmentClassName = await page.evaluate(() => $('.segmentList li:nth-of-type(2)').attr('class')); + expect(firstSegmentClassName).to.not.match(/segmentStarred/); + const firstSegmentStarState = await page.evaluate(() => $('.segmentList li:nth-of-type(2) .starSegment').attr('data-state') || ''); + expect(firstSegmentStarState).to.equal(''); expect(await page.screenshotSelector(selectorsToCapture)).to.matchImage('1b_selector_unstarred'); }); @@ -85,9 +89,8 @@ describe("SegmentSelectorEditorTest", function () { await switchToAnonymousUser(); await page.goto(url); await page.click('.segmentationContainer .title'); - const firstSegment = await page.$('.segmentList li:nth-child(2)'); - expect(firstSegment.className).to.contain('segmentStarred'); - expect(firstSegment.find('.starSegment').attr('data-state')).to.equal(''); + const firstSegmentStarState = await page.evaluate(() => $('.segmentList li:nth-of-type(2) .starSegment').attr('data-state') || ''); + expect(firstSegmentStarState).to.equal('disabled'); }); it("should open segment editor when edit link clicked for existing segment", async function() { diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_open.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_open.png index 21b562a0fa5..45ad65c5c5c 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_open.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_open.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3927ef177014f6f43ce385be4bcd9dc8a77a390e7b2cc714c48d39ca9bc6e08b -size 16750 +oid sha256:09573fe50a1af9797bff7fe43b6d33754d173e3f6ddce5c31856938d7f280320 +size 16297 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_starred.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_starred.png index 4863d89d953..0b9dcdddf1b 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_starred.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1_selector_starred.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6eb43ab8f5069cfd80e04bb93113983862ba307534bbc68eb8fafca4c7368338 -size 17291 +oid sha256:6e989b1f587fd2e99969b8e523ff16246002a5f29664b7a87ae94cc71eeabe06 +size 16770 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1b_selector_unstarred.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1b_selector_unstarred.png new file mode 100644 index 00000000000..a860a8b2137 --- /dev/null +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_1b_selector_unstarred.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:92b8e4a315ff2a1a2cca6ee3e6de59c6101580e4571a478032edcd84e37774bf +size 17569 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_2_segment_editor_update.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_2_segment_editor_update.png index 629c3bb61cd..7d203da33a3 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_2_segment_editor_update.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_2_segment_editor_update.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb703841188ab795a8f03eb4784d2b48362f15a72dbf609a54e57eb1157d1741 -size 35421 +oid sha256:7dd1dcc19eda71d47f719324a18181a827838ab4c4511ca6ff01c5066139d558 +size 35667 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted.png index 705e5debe0f..23212025840 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7522eac65db89e386be8bbdab4545a5a311c44c7d181137f016f763bf2940a73 -size 16703 +oid sha256:cff19e28b4c7be4f9ca9982f5cdd5b93d5e5c2f04b76c2ded226a3a961ee0995 +size 16658 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_enabled_create_realtime_segments_saved.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_enabled_create_realtime_segments_saved.png index 18711d8f528..8a05d5c6bf0 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_enabled_create_realtime_segments_saved.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_enabled_create_realtime_segments_saved.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8974b57e944c677e6632962d2a19b5c824b41567b39701f3fd04618374a83b58 -size 22132 +oid sha256:f78a6030d13ffa4936754403a632671851493d43e797e3ea5f7accb4dfb605c8 +size 21583 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved.png index fd2e41adad1..2143094cfb1 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5eef4eb48a5c2e3ef8bb503f7a186aa4fe3cb2e53d3a9ffdfc9f067cb29bfe15 -size 18614 +oid sha256:86818388be210b0ec14cfd5cadc6ae83abf5134edb5092702161b9e6888d7259 +size 18500 diff --git a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated.png b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated.png index 787ea858eb4..b0464625c31 100644 --- a/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated.png +++ b/plugins/SegmentEditor/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:99ae645a4e18027b0ef49e7472129f05e83302b7c9be988432fffe5590ea1715 -size 18996 +oid sha256:ae6c4cc195426fceff6f16b05d0ef3be8b34a2cf4fcc9da913206fcae689deb5 +size 18485 diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_api_listing.png b/tests/UI/expected-screenshots/UIIntegrationTest_api_listing.png index 823a22cc406..eb393f285e4 100644 --- a/tests/UI/expected-screenshots/UIIntegrationTest_api_listing.png +++ b/tests/UI/expected-screenshots/UIIntegrationTest_api_listing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0cb8605d210a59121c6c81183e98fcf4c78a05ae82457c672b3b7243fde56b86 -size 5010159 +oid sha256:163989f3c5e6b7138d9e2a31800fefcc39d0228d90ff65214036af79b55c9580 +size 5021683