diff options
author | diosmosis <diosmosis@users.noreply.github.com> | 2019-05-15 02:43:15 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-05-15 02:43:15 +0300 |
commit | 0af1f22f728b2bb2232f84ace5f4b5f0c3c60c57 (patch) | |
tree | 0eb34d450b850c1fe4253261d5e6c7de9edd3b33 /plugins | |
parent | 4b81b3b8eea9d0e361a5219164b08b850a30392d (diff) |
Indent actions belonging to a pageview (#14063)
* Proof of concept for grouping actions by the page they occur in.
* Add pageview to goals/ecommerce actionDetails in Live.getLastVisitsDetails.
* Make count of actions to display when collapsed configurable.
* Quick selector fix.
* unfinished commit
* Collapse multiple adjacent content items in the visitor log.
* Get content collapsing to work w/ 3.x-dev changes.
* Forgot to add Live config file.
* Get to work w/ visitor profile and make sure last action does not have border so it looks like it correctly ends.
* Fix some issues from review.
* More styling tweaks.
* another styling tweak
* Update screenshots.
* Show page refreshes and allow expanding them in new implementation.
* Update some screenshots.
* Make sure tooltip is replaced correctly when showing refreshes.
* Another styling tweak.
* Add UI test + fix page refresh issue.
* Fix action group merging logic.
* Fix another actions grouping issue.
* Fixes for ending left border in certain edge cases.
* Another UI tweak.
* comparison threshold, random failure fix, update screenshots + another css tweak
* more css tweaks
* possible bug fix
* Last couple CSS fixes.
* More test fixes.
Diffstat (limited to 'plugins')
32 files changed, 488 insertions, 84 deletions
diff --git a/plugins/Actions/tests/UI/expected-screenshots/ActionsDataTable_segmented_visitor_log.png b/plugins/Actions/tests/UI/expected-screenshots/ActionsDataTable_segmented_visitor_log.png index dd3d8011c3..0a9cfc6f93 100644 --- a/plugins/Actions/tests/UI/expected-screenshots/ActionsDataTable_segmented_visitor_log.png +++ b/plugins/Actions/tests/UI/expected-screenshots/ActionsDataTable_segmented_visitor_log.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5db8ec5cc759d0aa6903f7b83c5e3e547e4fa8adfc0fc42cc84f119fba35c489 -size 401604 +oid sha256:b6b328f08cccee3ed58d392d3261f09d6f514f0f01e1e2190985a784b0a7e608 +size 382907 diff --git a/plugins/Contents/templates/_actionContent.twig b/plugins/Contents/templates/_actionContent.twig index 801974064b..0eb5f86d81 100644 --- a/plugins/Contents/templates/_actionContent.twig +++ b/plugins/Contents/templates/_actionContent.twig @@ -9,9 +9,9 @@ class="action-list-action-icon content-impression"> {% endif %} {% if action.contentInteraction %} - [{{ action.contentInteraction }}] + <span class="content-interaction">[{{ action.contentInteraction }}]</span> {% endif %} - {{ action.contentName }} - - {{ action.contentPiece }} + <span class="content-name">{{ action.contentName }}</span> - + <span class="content-piece">{{ action.contentPiece }}</span> </div> </li> diff --git a/plugins/Ecommerce/VisitorDetails.php b/plugins/Ecommerce/VisitorDetails.php index 3dfd7eb3e6..0819160939 100644 --- a/plugins/Ecommerce/VisitorDetails.php +++ b/plugins/Ecommerce/VisitorDetails.php @@ -136,8 +136,8 @@ class VisitorDetails extends VisitorDetailsAbstract */ protected function queryEcommerceConversionsForVisits($idVisits) { - $sql = "SELECT - idvisit, + $sql = "SELECT + log_conversion.idvisit, case idgoal when " . GoalManager::IDGOAL_CART . " then '" . Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART . "' else '" . Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER . "' end as type, @@ -149,11 +149,14 @@ class VisitorDetails extends VisitorDetailsAbstract " . LogAggregator::getSqlRevenue('revenue_discount') . " as revenueDiscount, items as items, log_conversion.server_time as serverTimePretty, - log_conversion.idlink_va + log_conversion.idlink_va, + log_link_visit_action.idpageview FROM " . Common::prefixTable('log_conversion') . " AS log_conversion - WHERE idvisit IN ('" . implode("','", $idVisits) . "') + LEFT JOIN " . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action + ON log_link_visit_action.idlink_va = log_conversion.idlink_va + WHERE log_conversion.idvisit IN ('" . implode("','", $idVisits) . "') AND idgoal <= " . GoalManager::IDGOAL_ORDER . " - ORDER BY idvisit, server_time ASC"; + ORDER BY log_conversion.idvisit, log_conversion.server_time ASC"; $ecommerceDetails = Db::fetchAll($sql); return $ecommerceDetails; } diff --git a/plugins/Goals/VisitorDetails.php b/plugins/Goals/VisitorDetails.php index 2f94476093..7c52c0b899 100644 --- a/plugins/Goals/VisitorDetails.php +++ b/plugins/Goals/VisitorDetails.php @@ -70,12 +70,15 @@ class VisitorDetails extends VisitorDetailsAbstract 'goal' as type, goal.name as goalName, goal.idgoal as goalId, + log_link_visit_action.idpageview, log_conversion.revenue as revenue, log_conversion.idlink_va, log_conversion.idlink_va as goalPageId, log_conversion.server_time as serverTimePretty, log_conversion.url as url FROM " . Common::prefixTable('log_conversion') . " AS log_conversion + LEFT JOIN " . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action + ON log_link_visit_action.idlink_va = log_conversion.idlink_va LEFT JOIN " . Common::prefixTable('goal') . " AS goal ON (goal.idsite = log_conversion.idsite AND @@ -83,7 +86,7 @@ class VisitorDetails extends VisitorDetailsAbstract AND goal.deleted = 0 WHERE log_conversion.idvisit IN ('" . implode("','", $idVisits) . "') AND log_conversion.idgoal > 0 - ORDER BY log_conversion.idvisit, server_time ASC + ORDER BY log_conversion.idvisit, log_conversion.server_time ASC "; return Db::fetchAll($sql); } diff --git a/plugins/Live/Controller.php b/plugins/Live/Controller.php index e8556fe215..0ae82af98b 100644 --- a/plugins/Live/Controller.php +++ b/plugins/Live/Controller.php @@ -14,6 +14,7 @@ use Piwik\Config; use Piwik\Container\StaticContainer; use Piwik\Piwik; use Piwik\Plugins\Goals\API as APIGoals; +use Piwik\Plugins\Live\Visualizations\VisitorLog; use Piwik\Url; use Piwik\View; @@ -129,7 +130,9 @@ class Controller extends \Piwik\Plugin\Controller if (empty($visitorData)) { throw new \Exception('Visitor could not be found'); // for example when URL parameter is not set } - + + VisitorLog::groupActionsByPageviewId($visitorData['lastVisits']); + $view = new View('@Live/getVisitorProfilePopup.twig'); $view->idSite = $this->idSite; $view->goals = Request::processRequest('Goals.getGoals', ['idSite' => $this->idSite, 'filter_limit' => '-1'], $default = []); diff --git a/plugins/Live/Live.php b/plugins/Live/Live.php index 83103630a6..29be212907 100644 --- a/plugins/Live/Live.php +++ b/plugins/Live/Live.php @@ -12,6 +12,7 @@ use Piwik\Cache; use Piwik\CacheId; use Piwik\API\Request; use Piwik\Common; +use Piwik\Container\StaticContainer; /** * @@ -32,9 +33,18 @@ class Live extends \Piwik\Plugin 'Live.renderActionTooltip' => 'renderActionTooltip', 'Live.renderVisitorDetails' => 'renderVisitorDetails', 'Live.renderVisitorIcons' => 'renderVisitorIcons', + 'Template.jsGlobalVariables' => 'addJsGlobalVariables', ); } + public function addJsGlobalVariables(&$out) + { + $actionsToDisplayCollapsed = (int)StaticContainer::get('Live.pageViewActionsToDisplayCollapsed'); + $out .= " + piwik.visitorLogActionsToDisplayCollapsed = $actionsToDisplayCollapsed; + "; + } + public function getStylesheetFiles(&$stylesheets) { $stylesheets[] = "plugins/Live/stylesheets/live.less"; @@ -67,6 +77,7 @@ class Live extends \Piwik\Plugin $translationKeys[] = "Live_SegmentedVisitorLogTitle"; $translationKeys[] = "General_Segment"; $translationKeys[] = "General_And"; + $translationKeys[] = 'Live_ClickToSeeAllContents'; } public function renderAction(&$renderedAction, $action, $previousAction, $visitorDetails) diff --git a/plugins/Live/Visualizations/VisitorLog.php b/plugins/Live/Visualizations/VisitorLog.php index 2e05eee788..4b8797510e 100644 --- a/plugins/Live/Visualizations/VisitorLog.php +++ b/plugins/Live/Visualizations/VisitorLog.php @@ -10,12 +10,15 @@ namespace Piwik\Plugins\Live\Visualizations; use Piwik\Common; use Piwik\Config; +use Piwik\Container\StaticContainer; use Piwik\DataTable; use Piwik\Piwik; use Piwik\Plugin; use Piwik\Plugin\ViewDataTable; use Piwik\Plugin\Visualization; use Piwik\Plugins\PrivacyManager\PrivacyManager; +use Piwik\Plugins\TagManager\Model\Container\StaticContainerIdGenerator; +use Piwik\Tracker\Action; use Piwik\View; /** @@ -65,6 +68,9 @@ class VisitorLog extends Visualization } } }; + $this->config->filters[] = function (DataTable $table) { + $this->groupActionsByPageviewId($table); + }; } public function afterGenericFiltersAreAppliedToLoadedDataTable() @@ -119,6 +125,8 @@ class VisitorLog extends Visualization ) ); + $this->assignTemplateVar('actionsToDisplayCollapsed', StaticContainer::get('Live.pageViewActionsToDisplayCollapsed')); + $enableAddNewSegment = Common::getRequestVar('enableAddNewSegment', false); if ($enableAddNewSegment) { $this->config->datatable_actions[] = [ @@ -133,4 +141,92 @@ class VisitorLog extends Visualization { return ($view->requestConfig->getApiModuleToRequest() === 'Live'); } + + // TODO: need to unit test this + public static function groupActionsByPageviewId(DataTable $table) + { + foreach ($table->getRows() as $row) { + $actionGroups = []; + foreach ($row->getColumn('actionDetails') as $key => $action) { + // if action is not a pageview action + if (empty($action['idpageview']) + && self::isPageviewAction($action) + ) { + $actionGroups[] = [ + 'pageviewAction' => null, + 'actionsOnPage' => [$action], + 'refreshActions' => [], + ]; + continue; + } + + // if there is no idpageview for wahtever reason, invent one + $idPageView = !empty($action['idpageview']) ? $action['idpageview'] : count($actionGroups); + if (empty($actionGroups[$idPageView])) { + $actionGroups[$idPageView] = [ + 'pageviewAction' => null, + 'actionsOnPage' => [], + 'refreshActions' => [], + ]; + } + + if ($action['type'] == 'action') { + if (empty($actionGroups[$idPageView]['pageviewAction'])) { + $actionGroups[$idPageView]['pageviewAction'] = $action; + } else if (empty($actionGroups[$idPageView]['pageviewAction']['url'])) { + // set this action as the pageview action either if there isn't one set already, or the existing one + // has no URL + $actionGroups[$idPageView]['refreshActions'][] = $actionGroups[$idPageView]['pageviewAction']; + $actionGroups[$idPageView]['pageviewAction'] = $action; + } else { + $actionGroups[$idPageView]['refreshActions'][] = $actionGroups[$idPageView]['pageviewAction']; + } + } else { + $actionGroups[$idPageView]['actionsOnPage'][] = $action; + } + } + + // merge action groups that have the same page url/action and no pageviewactions + $actionGroups = self::mergeRefreshes($actionGroups); + + $row->setColumn('actionGroups', $actionGroups); + } + } + + private static function mergeRefreshes(array $actionGroups) + { + $previousId = null; + foreach ($actionGroups as $idPageview => $group) { + if (empty($previousId)) { + $previousId = $idPageview; + continue; + } + + $action = $group['pageviewAction']; + $lastActionGroup = $actionGroups[$previousId]; + + $isLastGroupEmpty = empty($actionGroups[$previousId]['actionsOnPage']); + $isPageviewActionSame = $lastActionGroup['pageviewAction']['url'] == $action['url'] + && $lastActionGroup['pageviewAction']['pageTitle'] == $action['pageTitle']; + + // if the current action has the same url/action name as the last, merge w/ the last action group + if ($isLastGroupEmpty + && $isPageviewActionSame + ) { + $actionGroups[$previousId]['refreshActions'][] = $action; + $actionGroups[$previousId]['actionsOnPage'] = array_merge($actionGroups[$previousId]['actionsOnPage'], $actionGroups[$idPageview]['actionsOnPage']); + unset($actionGroups[$idPageview]); + } else { + $previousId = $idPageview; + } + } + return $actionGroups; + } + + private static function isPageviewAction($action) + { + return $action['type'] != 'action' + && $action['type'] != Action::TYPE_PAGE_URL + && $action['type'] != Action::TYPE_PAGE_TITLE; + } } diff --git a/plugins/Live/config/config.php b/plugins/Live/config/config.php new file mode 100644 index 0000000000..0fc02cad3a --- /dev/null +++ b/plugins/Live/config/config.php @@ -0,0 +1,11 @@ +<?php +/** + * Matomo - free/libre analytics platform + * + * @link http://matomo.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +return [ + 'Live.pageViewActionsToDisplayCollapsed' => 3, +]; diff --git a/plugins/Live/javascripts/visitorActions.js b/plugins/Live/javascripts/visitorActions.js index f462bcfb8e..d85476c86a 100644 --- a/plugins/Live/javascripts/visitorActions.js +++ b/plugins/Live/javascripts/visitorActions.js @@ -35,58 +35,160 @@ function initializeVisitorActions(elem) { close: function() { tooltipIsOpened = false; } }); - // show refresh icon for duplicate page views in a row + // collapse adjacent content interactions $("ol.visitorLog", elem).each(function () { - var prevelement; - var prevhtml; - var counter = 0, duplicateCounter = 0; - $(this).find("> li").each(function () { - counter++; - $(this).val(counter); - var current = $(this).html(); - - if (current == prevhtml) { - duplicateCounter++; - $(this).find('>div').prepend($("<span>"+(duplicateCounter+1)+"</span>").attr({'class': 'repeat icon-refresh', 'title': _pk_translate('Live_PageRefreshed')})); - prevelement.addClass('duplicate'); - - } else { - duplicateCounter = 0; + var $actions = $(this).find("li"); + $actions.each(function (index) { + var $li = $(this); + if (!$li.is('.content')) { + return; + } + + if (!$actions[index - 1] + || !$($actions[index - 1]).is('.content') + || !$actions[index - 2] + || !$($actions[index - 2]).is('.content') + ) { + return; + } + + var $collapsedContents = $li; + while ($collapsedContents.prev().is('.content')) { + $collapsedContents = $collapsedContents.prev(); } - prevhtml = current; - prevelement = $(this); + if (!$collapsedContents.is('.collapsed-contents')) { + $collapsedContents = makeCollapsedContents(); + $collapsedContents.insertBefore($($actions[index - 2])); + + addContentItem($collapsedContents, $($actions[index - 2])); + addContentItem($collapsedContents, $($actions[index - 1])); + } + + addContentItem($collapsedContents, $li); + + function makeCollapsedContents() { + var $li = $('<li/>') + .attr('class', 'content collapsed-contents') + .attr('title', _pk_translate('Live_ClickToSeeAllContents')); + + $('<div>') + .html('<img src="plugins/Morpheus/images/contentimpression.svg" class="action-list-action-icon"/>' + + ' <span class="content-impressions">0</span> content impressions <span class="content-interactions">0</span> interactions') + .appendTo($li); - var $this = $(this); - var tooltipIsOpened = false; + return $li; + } - $('a', $this).on('focus', function () { - // see https://github.com/piwik/piwik/issues/4099 - if (tooltipIsOpened) { - $this.tooltip('close'); + function addContentItem($collapsedContents, $otherLi) { + if ($otherLi.find('.content-interaction').length) { + var $interactions = $collapsedContents.find('.content-interactions'); + $interactions.text(parseInt($interactions.text()) + 1); + } else { + var $impressions = $collapsedContents.find('.content-impressions'); + $impressions.text(parseInt($impressions.text()) + 1); } - }); + $otherLi.addClass('duplicate').addClass('collapsed-content-item').val('').attr('style', ''); + } }); }); - $("ol.visitorLog > li:not(.duplicate)", elem).each(function(){ - if (!$('.icon-refresh', $(this)).length) { + // show refresh icon for duplicate page views in a row + $("li.pageviewActions", elem).each(function () { + var $divider = $(this).find('.refresh-divider'); + $divider.prevUntil().addClass('duplicate'); + $divider.remove(); + + var viewCount = +$(this).attr('data-view-count'); + if (viewCount <= 1 + || isNaN(viewCount) + ) { return; } - $(this).attr('origtitle', $(this).attr('title')); - $(this).attr('title', _pk_translate('Live_ClickToViewAllActions')); - $(this).click(function(e){ + + var $pageviewAction = $(this).prev(); + $pageviewAction.find('>div').prepend($("<span>"+viewCount+"</span>").attr({'class': 'repeat icon-refresh', 'title': _pk_translate('Live_PageRefreshed')})); + + var actionsCount = +$(this).attr('data-actions-on-page'); + if (actionsCount === 0) { + $pageviewAction.addClass('noPageviewActions'); + } + + $('a', $(this)).on('focus', function () { + // see https://github.com/piwik/piwik/issues/4099 + if (tooltipIsOpened) { + $(this).tooltip('close'); + } + }); + + var $this = $(this); + $pageviewAction.attr('origtitle', $pageviewAction.attr('title')); + $pageviewAction.attr('title', _pk_translate('Live_ClickToViewAllActions')); + $pageviewAction.click(function (e) { e.preventDefault(); - $(this).prevUntil('li:not(.duplicate)').removeClass('duplicate').find('.icon-refresh').hide(); - var elem = $(this); + e.stopPropagation(); + + $pageviewAction.addClass('refreshesExpanded'); + $this.children('.actionList').children().first().removeClass('duplicate').nextUntil('li:not(.duplicate)').removeClass('duplicate'); + window.setTimeout(function() { - elem.attr('title', elem.attr('origtitle')); - elem.attr('origtitle', null); + $pageviewAction.attr('title', $pageviewAction.attr('origtitle')); + $pageviewAction.attr('origtitle', null); }, 150); - $(this).off('click').find('.icon-refresh').hide(); - return false; + + $pageviewAction.off('click').find('.icon-refresh').hide(); + $pageviewAction.triggerHandler('mouseleave'); // close tooltip so the title will replace }); }); + + // hide expanders if content collapsing removed enough items + $("ol.actionList", elem).each(function () { + var actionsToDisplayCollapsed = +piwik.visitorLogActionsToDisplayCollapsed; + + var $items = $(this).find("li:not(.pageviewActions):not(.actionsForPageExpander):not(.duplicate)"); + var hasMoreItemsThanLimit = $items.length > actionsToDisplayCollapsed; + + $(this).children('.actionsForPageExpander') + .toggle(hasMoreItemsThanLimit) + .find('.show-actions-count').text($items.length - actionsToDisplayCollapsed); + + // add last-action class to the last action in each list + setLastActionClass($(this)); + }); + + // event handler for content expander/collapser + elem.on('click', '.collapsed-contents', function () { + $(this).nextUntil(':not(.content)').toggleClass('duplicate'); + setLastActionClass($(this).closest('ol.actionList')); + }); + + // event handler for action expander/collapser + elem.on('click', '.show-less-actions,.show-more-actions', function (e) { + e.preventDefault(); + + var actionsToDisplayCollapsed = +piwik.visitorLogActionsToDisplayCollapsed; + + var $actions = $(e.target).closest('.actionList').find('li:not(.duplicate):not(.actionsForPageExpander)'); + $actions.each(function () { + if ($actions.index(this) >= actionsToDisplayCollapsed) { + $(this).toggle({ + duration: 250 + }); + } + }); + + $(e.target) + .parent().find('.show-less-actions,.show-more-actions').toggle(); + $(e.target) + .closest('li') + .toggleClass('expanded collapsed'); + }); + + elem.find('.show-less-actions:visible').click(); + + function setLastActionClass($list) { + $list.children(':not(.actionsForPageExpander):not(.duplicate)').removeClass('last-action').last().addClass('last-action'); + } } diff --git a/plugins/Live/lang/en.json b/plugins/Live/lang/en.json index 208051b927..62d85f3a0e 100644 --- a/plugins/Live/lang/en.json +++ b/plugins/Live/lang/en.json @@ -50,6 +50,7 @@ "RowActionTooltipTitle": "Open segmented Visit Log", "SegmentedVisitorLogTitle": "Visit Log showing visits where %1$s is \"%2$s\"", "OnClickPause": "%s is started. Click to pause.", - "OnClickStart": "%s is stopped. Click to start." + "OnClickStart": "%s is stopped. Click to start.", + "ClickToSeeAllContents": "Click to see each content interaction/impression" } } diff --git a/plugins/Live/stylesheets/live.less b/plugins/Live/stylesheets/live.less index 925356f8b2..f91451dea7 100644 --- a/plugins/Live/stylesheets/live.less +++ b/plugins/Live/stylesheets/live.less @@ -129,11 +129,11 @@ ol.visitorLog { overflow: -moz-hidden-unscrollable; } -ol.visitorLog > li { - margin-bottom: 7px; +ol.actionList > li:not(.pageviewActions) { + margin-bottom: 10px; line-height: 20px; position: relative; - min-height: 25px; + min-height: 30px; &:before { vertical-align: top; @@ -155,12 +155,12 @@ ol.visitorLog > li { border-left: 2px solid #d2d2d2; position: absolute; left: -10px; - height: 100%; + height: calc(~"100% - 20px"); margin-top: 20px; z-index: 1; } - &:last-child:after { + &:last-of-type:after { border-left: none; } @@ -182,6 +182,96 @@ ol.visitorLog > li { vertical-align: middle; } } + + &.actionsForPageExpander { + a { + color: @theme-color-text; + &:hover { + text-decoration:underline; + } + } + + &:before { + margin-left: -5px; + visibility: hidden; + } + + &.expanded:before { + content: "\f102"; + } + + &.collapsed:before { + content: "\f103"; + } + + &:before { + margin-top: 2px; + float: left; + margin-right: 5px; + + // copied frim [class^=icon-] style + font-family: 'matomo' !important; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + } +} + +ol.actionList > li.pageviewActions { + position: relative; + margin-top: -6px; + + &:after { + content: " "; + border-left: 2px solid #d2d2d2; + position: absolute; + left: -10px; + height: calc(~"100% + 8px"); + z-index: 1; + top: 0; + margin-top: -8px; + } + + &:last-child:after { + height: calc(~"100% - 4px"); + } + + > ol > li:nth-last-child(2):after { + border-left: none; + } +} + +// when last action group is just refreshes +ol.actionList > li:nth-last-child(2).noPageviewActions:not(.refreshesExpanded):after { + border-left: none; +} + +ol.actionList > li:not(.pageviewActions).last-action { + &:after { + border-left: none; + } +} + +.pageviewActions.last-action > ol.actionList > li.last-action { + margin-bottom: 0; +} + +li.collapsed-contents > div { + cursor: pointer; +} + +li.collapsed-content-item { + margin-left: 1.5rem; +} + +li.pageviewActions > ol.actionList { + margin-left: 1.5rem; } #visitsLive img { @@ -294,7 +384,7 @@ ol.visitorLog p { } } - .visitorLog > li > div { + .actionList > li > div { width: 95%; .segmentedVisitorLogPopover & { @@ -352,7 +442,7 @@ a.visitor-log-visitor-profile-link { } } -.visitorLog { +.actionList { > li > div { display: inline-block; width: 90%; @@ -371,6 +461,7 @@ a.visitor-log-visitor-profile-link { background-color: #fff; z-index: 3; margin-top: 1px; + color: #999; } .action-list-url { @@ -500,6 +591,10 @@ a.visitor-log-visitor-profile-link { } } +.refresh-divider { + display: none; +} + @media only screen and (min-width: 800px) { .card #visitsLive .visitorLogIcons:before { content: none; diff --git a/plugins/Live/stylesheets/visitor_profile.less b/plugins/Live/stylesheets/visitor_profile.less index fdcf75e2db..b60ecbc885 100644 --- a/plugins/Live/stylesheets/visitor_profile.less +++ b/plugins/Live/stylesheets/visitor_profile.less @@ -383,6 +383,7 @@ display: inline-block; float: left; margin-right: 15px; + margin-left: -3px; } .visitorDetails { @@ -438,3 +439,8 @@ ol.visitor-profile-actions { color: #999; font-size: 13px; } + +.visitor-profile-visits li.pageviewActions.last-action > ol.actionList > li.last-action { + margin-bottom: 0; + padding-bottom: 0; +} diff --git a/plugins/Live/templates/_actionEcommerce.twig b/plugins/Live/templates/_actionEcommerce.twig index f353781ac3..229067d998 100644 --- a/plugins/Live/templates/_actionEcommerce.twig +++ b/plugins/Live/templates/_actionEcommerce.twig @@ -12,7 +12,7 @@ {# TODO: would be nice to have the icons Orders / Cart in the ecommerce log footer #} {% endif %} <p> - <span {% if not isWidget %}style='margin-left:22px;'{% endif %}> + <span> {% if action.type == 'ecommerceOrder' %} {# spacing is important for tooltip to look nice #} {% set ecommerceOrderTooltip %}{{ 'General_ColumnRevenue'|translate }}: {{ action.revenue|money(visitInfo.idSite)|raw }} @@ -36,7 +36,7 @@ {# Ecommerce items in Cart/Order #} {% if action.itemDetails is not empty %} - <ul style='list-style:square;margin-left:{% if isWidget %}15{% else %}50{% endif %}px;'> + <ul style='list-style:square;margin-left:{% if isWidget %}15{% else %}22{% endif %}px;margin-bottom: 4px;'> {% for product in action.itemDetails %} <li> {{ product.itemSKU }}{% if product.itemName is not empty %}: {{ product.itemName }}{% endif -%} diff --git a/plugins/Live/templates/_actionsList.twig b/plugins/Live/templates/_actionsList.twig index 1f6ff3e7c9..78ca780135 100644 --- a/plugins/Live/templates/_actionsList.twig +++ b/plugins/Live/templates/_actionsList.twig @@ -1,9 +1,40 @@ {% set previousAction = false %} -{% for action in actionDetails %} +{% for actionGroup in actionGroups %} + {% if actionGroup.pageviewAction is not empty %} + {{ postEvent('Live.renderAction', actionGroup.pageviewAction, previousAction, visitInfo) }} - {{ postEvent('Live.renderAction', action, previousAction, visitInfo) }} + {% set previousAction = actionGroup.pageviewAction %} + {% endif %} -{% set previousAction = action %} + {% if actionGroup.actionsOnPage is not empty or actionGroup.refreshActions is not empty %} + {% if actionGroup.pageviewAction is not empty %} + <li class="pageviewActions" data-view-count="{{ actionGroup.refreshActions|length + 1 }}" data-actions-on-page="{{ actionGroup.actionsOnPage|length }}"> + <ol class="actionList"> + {% endif %} + {% for action in actionGroup.refreshActions %} + + {{ postEvent('Live.renderAction', action, previousAction, visitInfo) }} + + {% set previousAction = action %} + {% endfor %} + <li class="refresh-divider"></li> + {% for action in actionGroup.actionsOnPage %} + + {{ postEvent('Live.renderAction', action, previousAction, visitInfo) }} + + {% set previousAction = action %} + {% endfor %} + {% if actionGroup.pageviewAction is not empty %} + <li class="actionsForPageExpander expanded" style="display:none;"> + <span> + <a class="show-more-actions" href="javascript:" style="display:none;">Show <span class="show-actions-count"></span> more actions that occurred on this page...</a> + <a class="show-less-actions" href="javascript:">Show less actions...</a> + </span> + </li> + </ol> + </li> + {% endif %} + {% endif %} {% endfor %} {% if visitInfo.truncatedActionsCount is defined %} diff --git a/plugins/Live/templates/_dataTableViz_visitorLog.twig b/plugins/Live/templates/_dataTableViz_visitorLog.twig index f9d0b21a32..a04ff69966 100644 --- a/plugins/Live/templates/_dataTableViz_visitorLog.twig +++ b/plugins/Live/templates/_dataTableViz_visitorLog.twig @@ -41,8 +41,8 @@ </strong> <div class="visitor-log-page-list"> - <ol class='visitorLog'> - {% include "@Live/_actionsList.twig" with {'actionDetails': visitor.getColumn('actionDetails'), 'visitInfo': visitor} %} + <ol class='visitorLog actionList'> + {% include "@Live/_actionsList.twig" with {'actionGroups': visitor.getColumn('actionGroups'), 'visitInfo': visitor} %} </ol> </div> {{ postEvent('Live.visitorLogViewAfterActionsInfo', visitor) }} diff --git a/plugins/Live/templates/getVisitList.twig b/plugins/Live/templates/getVisitList.twig index b64ed585c5..d214fb8416 100644 --- a/plugins/Live/templates/getVisitList.twig +++ b/plugins/Live/templates/getVisitList.twig @@ -25,8 +25,9 @@ {% endif %} </a> </div> - <ol class="visitorLog visitor-profile-actions"> + <ol class="visitorLog visitor-profile-actions actionList"> {% include "@Live/_actionsList.twig" with {'actionDetails': visitInfo.getColumn('actionDetails'), + 'actionGroups': visitInfo.getColumn('actionGroups'), 'visitInfo': visitInfo} %} </ol> </div> diff --git a/plugins/Live/tests/Fixtures/VisitsWithAllActionsAndDevices.php b/plugins/Live/tests/Fixtures/VisitsWithAllActionsAndDevices.php index 1603e8526a..1bd509ff1e 100644 --- a/plugins/Live/tests/Fixtures/VisitsWithAllActionsAndDevices.php +++ b/plugins/Live/tests/Fixtures/VisitsWithAllActionsAndDevices.php @@ -223,5 +223,8 @@ class VisitsWithAllActionsAndDevices extends Fixture self::checkResponse($t->doTrackPageView('home')); $t->doTrackContentImpression('product slider', 'product_16.jpg', 'http://example.org/product16'); + $t->doTrackContentImpression('product slider', 'product_17.jpg', 'http://example.org/product17'); + $t->doTrackContentImpression('product slider', 'product_18.jpg', 'http://example.org/product18'); + $t->doTrackContentImpression('product zoom', 'product_18.jpg', 'http://example.org/product18'); } }
\ No newline at end of file diff --git a/plugins/Live/tests/UI/Live_spec.js b/plugins/Live/tests/UI/Live_spec.js index 1c0d111fc0..acbd621f03 100644 --- a/plugins/Live/tests/UI/Live_spec.js +++ b/plugins/Live/tests/UI/Live_spec.js @@ -8,8 +8,6 @@ */ describe("Live", function () { - this.timeout(0); - this.fixture = "Piwik\\Plugins\\Live\\tests\\Fixtures\\VisitsWithAllActionsAndDevices"; after(function () { @@ -23,6 +21,7 @@ describe("Live", function () { await page.goto("?module=CoreHome&action=index&idSite=1&period=year&date=2010-01-03#?idSite=1&period=year&date=2010-01-03&category=General_Visitors&subcategory=Live_VisitorLog"); await page.waitForNetworkIdle(); + await page.waitFor('.dataTableVizVisitorLog'); var report = await page.$('.reporting-page'); expect(await report.screenshot()).to.matchImage('visitor_log'); @@ -36,7 +35,35 @@ describe("Live", function () { expect(await report.screenshot()).to.matchImage('visitor_log_expand_actions'); }); + it('should expand collapsed pageview actions', async function() { + const link = await page.jQuery('.dataTableVizVisitorLog .card.row:eq(1) .show-more-actions:visible'); + await link.click(); + + await page.mouse.move(-10, -10); + + const report = await page.jQuery('.dataTableVizVisitorLog .card.row:eq(1)'); + expect(await report.screenshot()).to.matchImage('visitor_log_expand_pageview_actions'); + }); + + it('should expand collapsed content actions', async function() { + // collapse previously expanded section + const prevlink = await page.jQuery('.dataTableVizVisitorLog .card.row:eq(1) .show-less-actions:visible'); + await prevlink.click(); + + const link = await page.jQuery('.dataTableVizVisitorLog .card.row:eq(2) .collapsed-contents:visible'); + await link.click(); + + await page.mouse.move(-10, -10); + + const report = await page.jQuery('.dataTableVizVisitorLog .card.row:eq(2)'); + expect(await report.screenshot()).to.matchImage('visitor_log_expand_content_actions'); + }); + it('should show visitor profile', async function() { + // collapse previously expanded section + const prevlink = await page.jQuery('.dataTableVizVisitorLog .card.row:eq(2) .collapsed-contents:visible'); + await prevlink.click(); + await page.evaluate(function(){ $('.card:first-child .visitor-log-visitor-profile-link').click(); }); diff --git a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log.png b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log.png index 5fcc726b2d..92152ce679 100644 --- a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log.png +++ b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:403d52d525566412b7f6556d9ea109f883fd0fdd79f1a36b0a97ab610f403d81 -size 382454 +oid sha256:ad5ef092dfd4f680395950d1c5e8e0f154c7294138bb9a8091e99d215d0a9966 +size 385839 diff --git a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log_expand_actions.png b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log_expand_actions.png index 7670bd0320..7fdc3de326 100644 --- a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log_expand_actions.png +++ b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log_expand_actions.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61e520ac19eaf7e2ad0a3c000d4a9f2b422337031ec76c7895f3b92d84157e87 -size 51024 +oid sha256:9fe7032dc1800897a554a6ed30d495efe80998cea1316b6447d299bba13c69f4 +size 51479 diff --git a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log_expand_content_actions.png b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log_expand_content_actions.png new file mode 100644 index 0000000000..0aab20b622 --- /dev/null +++ b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log_expand_content_actions.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48c1a68c50cfcc7ee6bbe9e1d1854911fe6b75311520ca01b32bf964e80cd777 +size 39805 diff --git a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log_expand_pageview_actions.png b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log_expand_pageview_actions.png new file mode 100644 index 0000000000..51e72333da --- /dev/null +++ b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_log_expand_pageview_actions.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c9f4ce6ba4fca038288a79d6b6d63e997991d055b89104ab425b2640a3e5200 +size 102079 diff --git a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile.png b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile.png index 01108007f1..893944edcf 100644 --- a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile.png +++ b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:239c3cff34bdc37ef6e1a0a8f85ee537d548b2b0fcf255b25ce90f436c17caa8 -size 417219 +oid sha256:2d08854b59c88e7ab971f23df667daa6bb307955879063d561673b72a6c5e7f2 +size 421449 diff --git a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_action_details.png b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_action_details.png index c420414212..892c9813f6 100644 --- a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_action_details.png +++ b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_action_details.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3baf4e26d25cb2cc08ba78a0c6d199ffadc88aff819af2882abbe7706824b85f -size 286558 +oid sha256:cce674e80509201b636013a54e144992c16065983810638677d9aea905dcded0 +size 288310 diff --git a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_actions_hidden.png b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_actions_hidden.png index 4fc4cfc765..3991102d4e 100644 --- a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_actions_hidden.png +++ b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_actions_hidden.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13493d125dc03edd1e3b698e9b86056f5e38ab4744aa117cf3d350a470d79b48 -size 254843 +oid sha256:d3d4d7a3ec7beb72d14853b5a99be9a8aa2c45995975de689f2a5b8e7a17ed68 +size 255311 diff --git a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_limited.png b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_limited.png index 690d51b874..9223b651f6 100644 --- a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_limited.png +++ b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_limited.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:407bbf4891ff749ec90af841adf6c26848848dcc2146d016c3f9c3a4c7d8abad -size 309267 +oid sha256:f370ad5825ad62e3e9288062040046f9f15f2c6b522418747543218b912c47c4 +size 313341 diff --git a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_visit_details.png b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_visit_details.png index c1880e2da4..9b07892f77 100644 --- a/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_visit_details.png +++ b/plugins/Live/tests/UI/expected-screenshots/Live_visitor_profile_visit_details.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ff64c409f8124f250da602fb6a4cfe2d7068245a0b70820b8ea7909b52ed1c4 -size 264543 +oid sha256:8326abd7243618a9b71f4cf341c4775c23766b5b244a5fed30db82f15b0091e1 +size 264957 diff --git a/plugins/LoginLdap b/plugins/LoginLdap -Subproject 16912a9537e542be8edef0d34ed921101628d01 +Subproject c2de63df1887ec0409dceb5fa64f7fb735bce8e diff --git a/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_visits_showprofile.png b/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_visits_showprofile.png index b91410eaca..d8c991eca7 100644 --- a/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_visits_showprofile.png +++ b/plugins/PrivacyManager/tests/UI/expected-screenshots/PrivacyManager_gdpr_tools_visits_showprofile.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:481716a3e85183d3ae7fa0b79ab52d4e990bcff3a9f3a541e2c5872fdf5139ab -size 323585 +oid sha256:35ff9b4db313b8731142e24d9408481c1891726a359b001e53db0bc91b270c20 +size 327167 diff --git a/plugins/Referrers/tests/UI/CampaignBuilder_spec.js b/plugins/Referrers/tests/UI/CampaignBuilder_spec.js index 986ae89465..d9fe67fc31 100644 --- a/plugins/Referrers/tests/UI/CampaignBuilder_spec.js +++ b/plugins/Referrers/tests/UI/CampaignBuilder_spec.js @@ -52,6 +52,7 @@ describe("CampaignBuilder", function () { it('can reset form', async function () { await captureUrlBuilder('generate_url_reset', async function () { await page.click('.resetCampaignUrl'); + await page.waitFor(500); // wait to re-render }); }); diff --git a/plugins/UsersManager/tests/UI/UsersManager_spec.js b/plugins/UsersManager/tests/UI/UsersManager_spec.js index 7a824728bd..6a7482ba5d 100644 --- a/plugins/UsersManager/tests/UI/UsersManager_spec.js +++ b/plugins/UsersManager/tests/UI/UsersManager_spec.js @@ -165,6 +165,9 @@ describe("UsersManager", function () { await (await page.jQuery('.delete-user-confirm-modal .modal-close:not(.modal-no):visible')).click(); await page.waitForNetworkIdle(); + await page.mouse.move(-10, -10); + await page.waitFor('.pagedUsersList:not(.loading)'); + expect(await page.screenshotSelector('.usersManager')).to.matchImage('delete_single'); }); @@ -177,6 +180,7 @@ describe("UsersManager", function () { await page.waitForNetworkIdle(); await page.mouse.move(-10, -10); + await page.waitFor('.pagedUsersList:not(.loading)'); expect(await page.screenshotSelector('.usersManager')).to.matchImage('delete_bulk_access'); }); diff --git a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_delete_single.png b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_delete_single.png index a9e289c2e3..39c1435865 100644 --- a/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_delete_single.png +++ b/plugins/UsersManager/tests/UI/expected-screenshots/UsersManager_delete_single.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82cb3825149f0ca9e53a28628d1dcc3d6f70cfe5901a57724a9376c7a07d2ee2 -size 139627 +oid sha256:0058fc64d1b2a0d22990a34be329806d127f164c6e92aa8ef35b8cad1445a108 +size 138570 |