Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Steur <tsteur@users.noreply.github.com>2017-10-03 23:22:01 +0300
committerGitHub <noreply@github.com>2017-10-03 23:22:01 +0300
commit9af4e95aa976f3a6533e95b776b5298f73e5f916 (patch)
treed612cd4d32019e9e52ce1398b8bf214ec06a8e0f
parent359c3ec875b554c7b71a933b26d18cdde0bb8f4e (diff)
Better segment editor and fixes (#12040)
* column tweak * fix install * more tweaks * rename column to dimension * various fixes * added new control expandable select * starting to refactor segment selector * make segment editor work again * use translation keys * defined some metrics * set types * simplify * simplify * fix join generator * add possibility to use custom join table names when using query builder and it uses an inner query * fix bug in query selector when selecting same field name from different tables twice * more metadata * more tweaks * improve selector * add possibility to use custom entity names * also processed archived metrics * generate sql filter, suggested values callback, and accept values automatically for columns with enums * several tweaks * focus search field when opening it * various tweaks * added missing method * format and fix more metadata * more fixes * better definition * define custom filter * fix definition * fix various tests * fix more tests * fix bug in logquery builder * fix referrerurl segment was missing * fix some tests * fix more tests * add group * refactor for better definition * fix a bug in log query builder when similar columns are used in archiver * add goal metrics * various fixes * make datatable row more flexible * various fixes and visualization enhancements * simply segment editor and make it smaller * remove trailing comma * various fixes and added new dimension * fix formatting of returning customer * added missing primary key * fixes * various fixes and improvements * make sure to update segment definition when selecting a value from auto complete list * various fixes and more metrics * more metrics * more dimensions and fixes * fix some tests * fix some integration tests * update submodule * fix some system tests * fix ui tests * trigger new test run * fix more ui tests * fix system tests * update submodule * fix categories * sort segments by category for more consistency * add custom variables * some translations and fixes * add minute segment * more segments * added plurals * added some docs * fix test * fix tests * fix tests * added suggested values * fix some tests * various fixes * fix more tests * allow to select segments on any site * make sure to include file * added doc block * fix some system tests * fix most system tests * fix ui test * fix system test * adjust examples * added more tests and docs * no metrics for these dimensions * added developer changelog and made some classes public api * some fixes for entity names * add possibility to set format metrics in test * more consistency in defining the name * get idsites only if provided * fix integration tests * added another segment for visit start hour and visit start minute * more clear name for segment * use old segment name to not break bc * various fixes * more test fixes * fix no suggested values for new segment * add event value * for boolean dimensions only sum metric * update available widgets when updating reporting menu * Add new segments in developer changelog + typo * fix system tests * fix screenshot test
-rw-r--r--CHANGELOG.md32
-rw-r--r--config/global.php2
-rw-r--r--core/API/DataTableManipulator.php11
-rw-r--r--core/API/DocumentationGenerator.php10
-rw-r--r--core/ArchiveProcessor/PluginsArchiver.php7
-rw-r--r--core/Columns/ComputedMetricFactory.php57
-rw-r--r--core/Columns/Dimension.php670
-rw-r--r--core/Columns/DimensionMetricFactory.php122
-rw-r--r--core/Columns/DimensionsProvider.php62
-rw-r--r--core/Columns/Discriminator.php75
-rw-r--r--core/Columns/Join.php61
-rw-r--r--core/Columns/Join/ActionNameJoin.php24
-rw-r--r--core/Columns/Join/GoalNameJoin.php24
-rw-r--r--core/Columns/Join/SiteNameJoin.php24
-rw-r--r--core/Columns/MetricsList.php190
-rw-r--r--core/Columns/Updater.php15
-rw-r--r--core/DataAccess/LogAggregator.php7
-rw-r--r--core/DataAccess/LogQueryBuilder.php88
-rw-r--r--core/DataAccess/LogQueryBuilder/JoinGenerator.php13
-rw-r--r--core/DataAccess/LogQueryBuilder/JoinTables.php17
-rw-r--r--core/Metrics/Formatter.php4
-rw-r--r--core/Plugin/ArchivedMetric.php207
-rw-r--r--core/Plugin/ComputedMetric.php260
-rw-r--r--core/Plugin/Dimension/ActionDimension.php127
-rw-r--r--core/Plugin/Dimension/ConversionDimension.php116
-rw-r--r--core/Plugin/Dimension/VisitDimension.php65
-rw-r--r--core/Plugin/Metric.php10
-rw-r--r--core/Plugin/Report.php3
-rw-r--r--core/Plugin/Segment.php45
-rw-r--r--core/Plugin/Visualization.php14
-rw-r--r--core/RankingQuery.php8
-rw-r--r--core/Segment.php12
-rw-r--r--core/Segment/SegmentExpression.php18
-rw-r--r--core/Settings/FieldConfig.php6
-rw-r--r--core/Tracker/LogTable.php11
-rw-r--r--core/ViewDataTable/Config.php24
-rw-r--r--lang/en.json27
-rw-r--r--plugins/API/API.php22
-rw-r--r--plugins/API/ProcessedReport.php14
-rw-r--r--plugins/API/RowEvolution.php29
-rw-r--r--plugins/API/SegmentMetadata.php38
-rw-r--r--plugins/Actions/Columns/ActionType.php52
-rw-r--r--plugins/Actions/Columns/ActionUrl.php5
-rw-r--r--plugins/Actions/Columns/ClickedUrl.php26
-rw-r--r--plugins/Actions/Columns/DestinationPage.php6
-rw-r--r--plugins/Actions/Columns/DownloadUrl.php26
-rw-r--r--plugins/Actions/Columns/EntryPageTitle.php27
-rw-r--r--plugins/Actions/Columns/EntryPageUrl.php27
-rw-r--r--plugins/Actions/Columns/ExitPageTitle.php27
-rw-r--r--plugins/Actions/Columns/ExitPageUrl.php33
-rw-r--r--plugins/Actions/Columns/IdPageview.php8
-rw-r--r--plugins/Actions/Columns/InteractionPosition.php7
-rw-r--r--plugins/Actions/Columns/Keyword.php6
-rw-r--r--plugins/Actions/Columns/KeywordwithNoSearchResult.php6
-rw-r--r--plugins/Actions/Columns/Metrics/AveragePageGenerationTime.php1
-rw-r--r--plugins/Actions/Columns/PageGenerationTime.php50
-rw-r--r--plugins/Actions/Columns/PageTitle.php23
-rw-r--r--plugins/Actions/Columns/PageUrl.php24
-rw-r--r--plugins/Actions/Columns/SearchCategory.php6
-rw-r--r--plugins/Actions/Columns/SearchDestinationPage.php6
-rw-r--r--plugins/Actions/Columns/SearchKeyword.php24
-rw-r--r--plugins/Actions/Columns/SearchNoResultKeyword.php6
-rw-r--r--plugins/Actions/Columns/TimeSpentRefAction.php1
-rw-r--r--plugins/Actions/Columns/VisitTotalActions.php33
-rw-r--r--plugins/Actions/Columns/VisitTotalInteractions.php16
-rw-r--r--plugins/Actions/Columns/VisitTotalSearches.php16
-rw-r--r--plugins/Actions/lang/en.json11
-rw-r--r--plugins/Contents/Columns/ContentInteraction.php25
-rw-r--r--plugins/Contents/Columns/ContentName.php27
-rw-r--r--plugins/Contents/Columns/ContentPiece.php25
-rw-r--r--plugins/Contents/Columns/ContentTarget.php25
-rw-r--r--plugins/Contents/Contents.php4
-rw-r--r--plugins/Contents/lang/en.json3
-rw-r--r--plugins/CoreHome/Columns/IdSite.php9
-rw-r--r--plugins/CoreHome/Columns/LinkVisitActionId.php42
-rw-r--r--plugins/CoreHome/Columns/ServerMinute.php35
-rw-r--r--plugins/CoreHome/Columns/ServerTime.php21
-rw-r--r--plugins/CoreHome/Columns/UserId.php21
-rw-r--r--plugins/CoreHome/Columns/VisitFirstActionMinute.php45
-rw-r--r--plugins/CoreHome/Columns/VisitFirstActionTime.php22
-rw-r--r--plugins/CoreHome/Columns/VisitGoalBuyer.php48
-rw-r--r--plugins/CoreHome/Columns/VisitGoalConverted.php24
-rw-r--r--plugins/CoreHome/Columns/VisitId.php31
-rw-r--r--plugins/CoreHome/Columns/VisitIp.php31
-rw-r--r--plugins/CoreHome/Columns/VisitLastActionMinute.php51
-rw-r--r--plugins/CoreHome/Columns/VisitLastActionTime.php15
-rw-r--r--plugins/CoreHome/Columns/VisitTotalTime.php12
-rw-r--r--plugins/CoreHome/Columns/VisitorDaysSinceFirst.php13
-rw-r--r--plugins/CoreHome/Columns/VisitorDaysSinceOrder.php15
-rw-r--r--plugins/CoreHome/Columns/VisitorId.php33
-rw-r--r--plugins/CoreHome/Columns/VisitorReturning.php47
-rw-r--r--plugins/CoreHome/Columns/VisitsCount.php14
-rw-r--r--plugins/CoreHome/CoreHome.php24
-rw-r--r--plugins/CoreHome/Tracker/LogTable/Conversion.php5
-rw-r--r--plugins/CoreHome/Tracker/LogTable/ConversionItem.php7
-rw-r--r--plugins/CoreHome/Tracker/LogTable/LinkVisitAction.php5
-rw-r--r--plugins/CoreHome/Tracker/LogTable/Visit.php4
-rw-r--r--plugins/CoreHome/angularjs/common/services/piwik-api.js2
-rw-r--r--plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js6
-rw-r--r--plugins/CoreHome/lang/en.json4
-rw-r--r--plugins/CoreHome/templates/_dataTableActions.twig5
-rw-r--r--plugins/CorePluginsAdmin/CorePluginsAdmin.php1
-rw-r--r--plugins/CorePluginsAdmin/angularjs/field/field.directive.js6
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-expandable-select.html24
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-expandable-select.less88
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-multiselect.html2
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/field-radio.html2
-rw-r--r--plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js33
m---------plugins/CustomDimensions0
-rw-r--r--plugins/CustomVariables/Categories/CustomVariablesCategory.php18
-rw-r--r--plugins/CustomVariables/Columns/SearchCategory.php26
-rw-r--r--plugins/CustomVariables/CustomDimension.php36
-rw-r--r--plugins/CustomVariables/CustomVariables.php19
-rw-r--r--plugins/CustomVariables/Model.php29
-rw-r--r--plugins/CustomVariables/lang/en.json1
-rw-r--r--plugins/CustomVariables/tests/Integration/ModelTest.php20
-rw-r--r--plugins/DevicePlugins/Columns/Plugin.php5
-rw-r--r--plugins/DevicePlugins/Columns/PluginCookie.php1
-rw-r--r--plugins/DevicePlugins/Columns/PluginDirector.php1
-rw-r--r--plugins/DevicePlugins/Columns/PluginFlash.php1
-rw-r--r--plugins/DevicePlugins/Columns/PluginGears.php1
-rw-r--r--plugins/DevicePlugins/Columns/PluginJava.php1
-rw-r--r--plugins/DevicePlugins/Columns/PluginPdf.php1
-rw-r--r--plugins/DevicePlugins/Columns/PluginQuickTime.php1
-rw-r--r--plugins/DevicePlugins/Columns/PluginRealPlayer.php1
-rw-r--r--plugins/DevicePlugins/Columns/PluginSilverlight.php1
-rw-r--r--plugins/DevicePlugins/Columns/PluginWindowsMedia.php1
-rw-r--r--plugins/DevicesDetection/Columns/BrowserEngine.php25
-rw-r--r--plugins/DevicesDetection/Columns/BrowserName.php21
-rw-r--r--plugins/DevicesDetection/Columns/BrowserVersion.php21
-rw-r--r--plugins/DevicesDetection/Columns/DeviceBrand.php23
-rw-r--r--plugins/DevicesDetection/Columns/DeviceModel.php9
-rw-r--r--plugins/DevicesDetection/Columns/DeviceType.php32
-rw-r--r--plugins/DevicesDetection/Columns/Os.php15
-rw-r--r--plugins/DevicesDetection/Columns/OsVersion.php21
-rw-r--r--plugins/DevicesDetection/Segment.php21
-rw-r--r--plugins/DevicesDetection/lang/en.json3
-rw-r--r--plugins/Ecommerce/Columns/Items.php18
-rw-r--r--plugins/Ecommerce/Columns/Order.php37
-rw-r--r--plugins/Ecommerce/Columns/ProductCategory.php7
-rw-r--r--plugins/Ecommerce/Columns/ProductName.php21
-rw-r--r--plugins/Ecommerce/Columns/ProductPrice.php21
-rw-r--r--plugins/Ecommerce/Columns/ProductQuantity.php21
-rw-r--r--plugins/Ecommerce/Columns/ProductSku.php21
-rw-r--r--plugins/Ecommerce/Columns/Revenue.php9
-rw-r--r--plugins/Ecommerce/Columns/RevenueDiscount.php3
-rw-r--r--plugins/Ecommerce/Columns/RevenueShipping.php3
-rw-r--r--plugins/Ecommerce/Columns/RevenueSubtotal.php3
-rw-r--r--plugins/Ecommerce/Columns/RevenueTax.php3
-rw-r--r--plugins/Ecommerce/Ecommerce.php52
-rw-r--r--plugins/Ecommerce/lang/en.json4
-rw-r--r--plugins/Events/Categories/EventsCategory.php19
-rw-r--r--plugins/Events/Columns/EventAction.php23
-rw-r--r--plugins/Events/Columns/EventCategory.php23
-rw-r--r--plugins/Events/Columns/EventName.php23
-rw-r--r--plugins/Events/Columns/EventValue.php56
-rw-r--r--plugins/Events/Columns/TotalEvents.php21
-rw-r--r--plugins/Events/Segment.php22
-rw-r--r--plugins/ExampleTracker/Columns/ExampleActionDimension.php33
-rw-r--r--plugins/ExampleTracker/Columns/ExampleConversionDimension.php29
-rw-r--r--plugins/ExampleTracker/Columns/ExampleDimension.php6
-rw-r--r--plugins/ExampleTracker/Columns/ExampleVisitDimension.php29
-rw-r--r--plugins/Goals/Columns/DaysToConversion.php6
-rw-r--r--plugins/Goals/Columns/IdGoal.php23
-rw-r--r--plugins/Goals/Columns/Revenue.php20
-rw-r--r--plugins/Goals/Columns/VisitsUntilConversion.php7
-rw-r--r--plugins/Goals/GoalDimension.php41
-rw-r--r--plugins/Goals/Goals.php71
-rw-r--r--plugins/Goals/lang/en.json6
-rw-r--r--plugins/Morpheus/stylesheets/main.less20
-rw-r--r--plugins/Morpheus/stylesheets/ui/_components.less26
-rw-r--r--plugins/Morpheus/templates/demo.twig16
-rw-r--r--plugins/Provider/Columns/Provider.php23
-rw-r--r--plugins/Referrers/Columns/Campaign.php6
-rw-r--r--plugins/Referrers/Columns/Keyword.php22
-rw-r--r--plugins/Referrers/Columns/Referrer.php6
-rw-r--r--plugins/Referrers/Columns/ReferrerName.php15
-rw-r--r--plugins/Referrers/Columns/ReferrerType.php29
-rw-r--r--plugins/Referrers/Columns/ReferrerUrl.php16
-rw-r--r--plugins/Referrers/Columns/SearchEngine.php6
-rw-r--r--plugins/Referrers/Columns/SocialNetwork.php6
-rw-r--r--plugins/Referrers/Columns/Website.php8
-rw-r--r--plugins/Referrers/Columns/WebsitePage.php6
-rw-r--r--plugins/Referrers/Segment.php21
-rw-r--r--plugins/Referrers/functions.php4
-rw-r--r--plugins/Referrers/lang/en.json2
-rw-r--r--plugins/Resolution/Columns/Configuration.php5
-rw-r--r--plugins/Resolution/Columns/Resolution.php19
-rw-r--r--plugins/Resolution/Segment.php21
-rw-r--r--plugins/SegmentEditor/SegmentEditor.php19
-rw-r--r--plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator-model.js69
-rw-r--r--plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.controller.js319
-rw-r--r--plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.html61
-rw-r--r--plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.js64
-rw-r--r--plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.less204
-rw-r--r--plugins/SegmentEditor/javascripts/Segmentation.js516
-rw-r--r--plugins/SegmentEditor/stylesheets/segmentation.less264
-rw-r--r--plugins/SegmentEditor/templates/_segmentSelector.twig100
-rw-r--r--plugins/SegmentEditor/tests/Integration/SegmentEditorTest.php2
-rw-r--r--plugins/UserCountry/Categories/LocationsCategory.php17
-rw-r--r--plugins/UserCountry/Columns/City.php22
-rw-r--r--plugins/UserCountry/Columns/Continent.php23
-rw-r--r--plugins/UserCountry/Columns/Country.php30
-rw-r--r--plugins/UserCountry/Columns/Latitude.php22
-rw-r--r--plugins/UserCountry/Columns/Longitude.php22
-rw-r--r--plugins/UserCountry/Columns/Provider.php2
-rw-r--r--plugins/UserCountry/Columns/Region.php22
-rw-r--r--plugins/UserCountry/Segment.php21
-rw-r--r--plugins/UserCountry/lang/en.json4
-rw-r--r--plugins/UserId/Columns/UserId.php9
-rw-r--r--plugins/UserLanguage/Columns/Language.php21
-rw-r--r--plugins/VisitTime/Columns/DayOfTheWeek.php5
-rw-r--r--plugins/VisitTime/Columns/LocalMinute.php43
-rw-r--r--plugins/VisitTime/Columns/LocalTime.php24
-rw-r--r--plugins/VisitTime/Columns/ServerTime.php33
-rw-r--r--plugins/VisitTime/Reports/GetVisitInformationPerServerTime.php6
-rw-r--r--plugins/VisitTime/Segment.php21
-rw-r--r--plugins/VisitTime/lang/en.json8
-rw-r--r--plugins/VisitorInterest/Columns/PagesPerVisit.php5
-rw-r--r--plugins/VisitorInterest/Columns/VisitDuration.php7
-rw-r--r--plugins/VisitorInterest/Columns/VisitsByDaysSinceLastVisit.php14
-rw-r--r--plugins/VisitorInterest/Columns/VisitsbyVisitNumber.php5
-rw-r--r--tests/PHPUnit/Integration/Category/CategoryListTest.php3
-rw-r--r--tests/PHPUnit/Integration/Columns/ComputedMetricFactoryTest.php62
-rw-r--r--tests/PHPUnit/Integration/Columns/DimensionMetricFactoryTest.php159
-rw-r--r--tests/PHPUnit/Integration/Columns/DimensionTest.php415
-rw-r--r--tests/PHPUnit/Integration/Plugin/ArchivedMetricTest.php131
-rw-r--r--tests/PHPUnit/Integration/Plugin/ComputedMetricTest.php136
-rw-r--r--tests/PHPUnit/Integration/Plugin/Dimension/ActionDimensionTest.php8
-rw-r--r--tests/PHPUnit/Integration/Plugin/Dimension/ConversionDimensionTest.php8
-rw-r--r--tests/PHPUnit/Integration/Plugin/Dimension/VisitDimensionTest.php36
-rw-r--r--tests/PHPUnit/Integration/SegmentTest.php97
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerHour__API.getSuggestedValuesForSegment.xml27
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerHour__VisitsSummary.get_range.xml12
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerMinute__API.getSuggestedValuesForSegment.xml34
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerMinute__VisitsSummary.get_range.xml12
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_deviceType__API.getSuggestedValuesForSegment.xml15
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_interaction_position__API.getSuggestedValuesForSegment.xml2
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_referrerType__API.getSuggestedValuesForSegment.xml2
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitEcommerceStatus__API.getSuggestedValuesForSegment.xml3
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitEndServerMinute__API.getSuggestedValuesForSegment.xml34
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitEndServerMinute__VisitsSummary.get_range.xml12
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitLocalMinute__API.getSuggestedValuesForSegment.xml34
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitLocalMinute__VisitsSummary.get_range.xml12
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitServerMinute__API.getSuggestedValuesForSegment.xml34
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitServerMinute__VisitsSummary.get_range.xml12
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerHour__API.getSuggestedValuesForSegment.xml27
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerHour__VisitsSummary.get_range.xml12
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerMinute__API.getSuggestedValuesForSegment.xml34
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerMinute__VisitsSummary.get_range.xml12
-rw-r--r--tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitorType__API.getSuggestedValuesForSegment.xml1
-rw-r--r--tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_sortByProcessedMetric_constantRowsCountShouldKeepEmptyRows__API.getProcessedReport_day.xml4
-rw-r--r--tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml4
-rw-r--r--tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumnsWithProcessedMetrics___API.getProcessedReport_day.xml4
-rw-r--r--tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml4
-rw-r--r--tests/PHPUnit/System/expected/test_RowEvolution_pageTitlesMulti__API.getRowEvolution_day.xml2
-rw-r--r--tests/PHPUnit/System/expected/test_RowEvolution_pageTitlesMulti__API.getRowEvolution_week.xml2
-rw-r--r--tests/PHPUnit/System/expected/test_RowEvolution_pageTitles__API.getRowEvolution_day.xml2
-rw-r--r--tests/PHPUnit/System/expected/test_RowEvolution_pageTitles__API.getRowEvolution_week.xml2
-rw-r--r--tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml4
-rw-r--r--tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_month.xml4
-rw-r--r--tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml4
-rw-r--r--tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html8
-rw-r--r--tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_month.original.html8
-rw-r--r--tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdfbin551714 -> 551783 bytes
-rw-r--r--tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getReportMetadata_day.xml8
-rw-r--r--tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml267
-rwxr-xr-xtests/PHPUnit/System/expected/test_ecommerceOrderWithItems_Metadata_VisitTime.getVisitInformationPerServerTime__API.getProcessedReport_day.xml4
-rw-r--r--tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html8
-rw-r--r--tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_week.original.html8
-rw-r--r--tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdfbin568483 -> 568560 bytes
-rw-r--r--tests/PHPUnit/Unit/DataAccess/LogQueryBuilder/JoinGeneratorTest.php38
-rw-r--r--tests/PHPUnit/Unit/DataTable/Filter/AddSegmentFilterBySegmentValueTest.php8
-rw-r--r--tests/PHPUnit/Unit/DeprecatedMethodsTest.php3
-rw-r--r--tests/UI/expected-screenshots/Morpheus_load.png4
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_2_segment_editor_update.png4
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_3_segment_editor_edit_name.png4
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_4_segment_editor_expanded_dimensions.png3
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_5_segment_editor_search_dimensions.png3
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_6_segment_editor_different.png4
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_6_segment_editor_droplist.png4
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_8_segment_editor_create.png4
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_add_new_and_condition.png3
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_add_new_or_condition.png3
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted_dialog.png4
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_dimension_drag_drop.png4
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_drag_and_condition.png4
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_drag_or_condition.png4
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved_details.png4
-rw-r--r--tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated_details.png4
-rw-r--r--tests/UI/expected-screenshots/UIIntegrationTest_actions_page_titles.png4
-rw-r--r--tests/UI/expected-screenshots/UIIntegrationTest_dashboard4.png4
-rw-r--r--tests/UI/specs/SegmentSelectorEditor_spec.js82
293 files changed, 6419 insertions, 2730 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 71156289ac..9973af9365 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,24 @@ This is the Developer Changelog for Piwik platform developers. All changes in ou
The Product Changelog at **[piwik.org/changelog](https://piwik.org/changelog)** lets you see more details about any Piwik release, such as the list of new guides and FAQs, security fixes, and links to all closed issues.
+## Piwik 3.2.0
+
+### New Segments
+* New Segment added: `visitStartServerMinute` for Server time - minute (Start of visit)
+* New Segment added: `visitEndServerMinute` for Server time - minute (End of visit)
+
+
+### New APIs
+* Reports and visualizations can now hide the export icons with a new property `$view->config->show_export`.
+* The following events have been added:
+ * `Metric.addMetrics` Triggered to add new metrics that cannot be picked up automatically by the platform.
+ * `Metric.addComputedMetrics` Triggered to add computed metrics that are not generated automatically
+ * `Metric.filterMetrics` Triggered to filter metrics
+* The following new API classes have been added:
+ * `Piwik\Columns\MetricsList` Holds a list of all available metrics
+ * `Piwik\Columns\ComputedMetricFactory` Can be used to create computed metrics
+ * `Piwik\Columns\DimensionMetricFactory` Can be used to create metrics directly within a dimension
+
## Piwik 3.1.0
### Breaking Changes
@@ -99,11 +117,11 @@ If the tracker is not initialised correctly, the browser console will display th
* The "User Menu" was removed and should be replaced by "Admin Menu". Change `configureUserMenu(MenuUser $menu)` to `configureAdminMenu(MenuAdmin $menu)` in your `Menu.php`.
* The method `Piwik\Menu\MenuAbstract::add()` has been removed, use `Piwik\Menu\MenuAbstract::addItem()` instead
* The method `Piwik\Menu\MenuAdmin::addSettingsItem()` was removed, use `Piwik\Menu\MenuAdmin::addSystemItem()` instead.
-* A new methd `Piwik\Menu\MenuAdmin::addMeasurablesItem()` was added.
-* The class `Piwik\Plugin\Settings` has been splitted to `Piwik\Settings\Plugin\SystemSettings` and `Piwik\Settings\Plugin\UserSettings`.
+* A new method `Piwik\Menu\MenuAdmin::addMeasurablesItem()` was added.
+* The class `Piwik\Plugin\Settings` has been split to `Piwik\Settings\Plugin\SystemSettings` and `Piwik\Settings\Plugin\UserSettings`.
* The creation of settings has slightly changed to improve performance. It is now possible to create new settings via the method `$this->makeSetting()` see `Piwik\Plugins\ExampleSettingsPlugin\SystemSettings` for an example.
* It is no longer possible to define an introduction text for settings.
-* If requesting multipe periods for one report, the keys that define the range are no longer translated. For example before 3.0 an API response may contain: `<result date="From 2010-02-01 to 2010-02-07">` which is now `<result date="2010-02-01,2010-02-07">`.
+* If requesting multiple periods for one report, the keys that define the range are no longer translated. For example before 3.0 an API response may contain: `<result date="From 2010-02-01 to 2010-02-07">` which is now `<result date="2010-02-01,2010-02-07">`.
* The following deprecated events have been removed as mentioned.
* `Tracker.existingVisitInformation` Use [dimensions](https://developer.piwik.org/guides/dimensions) instead of using `Tracker` events.
* `Tracker.newVisitorInformation`
@@ -127,7 +145,7 @@ If the tracker is not initialised correctly, the browser console will display th
UserSettings | getPlugin | DevicePlugins | getPlugin
UserSettings | index | DevicesDetection | software
UserSettings | getBrowser | DevicesDetection | getBrowsers
- UserSettings | getBrowserVerions | DevicesDetection | getBrowserVersions
+ UserSettings | getBrowserVersions | DevicesDetection | getBrowserVersions
UserSettings | getMobileVsDesktop | DevicesDetection | getType
UserSettings | getOS | DevicesDetection | getOsVersions
UserSettings | getOSFamily | DevicesDetection | getOsFamilies
@@ -250,9 +268,9 @@ The folder containing expected screenshots was renamed from `expected-ui-screens
* New segment operators `=^` "Starts with" and `=$` "Ends with" complement the existing segment operators: Contains, Does not contain, Equals, Not equals, Greater than or equal to, Less than or equal to.
* The JavaScript Tracker method `PiwikTracker.setDomains()` can now handle paths. This means when setting eg `_paq.push(['setDomains, '*.piwik.org/website1'])` all link that goes to the same domain `piwik.org` but to any other path than `website1/*` will be treated as outlink.
* In Administration > Websites, for each website, there is a checkbox "Only track visits and actions when the action URL starts with one of the above URLs". In Piwik 2.14.0, any action URL starting with one of the Alias URLs or starting with a subdomain of the Alias URL would be tracked. As of Piwik 2.15.0, when this checkbox is enabled, it may track less data: action URLs on an Alias URL subdomain will not be tracked anymore (you must specify each sub-domain as Alias URL).
- * It is now possible to pass an option `php-cli-options` to the `core:archive` command. The given cli options will be forwarded to the actual PHP command. This allows to for example specifiy a different memory limit for the archiving process like this: `./console core:archive --php-cli-options="-d memory_limit=8G"`
+ * It is now possible to pass an option `php-cli-options` to the `core:archive` command. The given cli options will be forwarded to the actual PHP command. This allows to for example specify a different memory limit for the archiving process like this: `./console core:archive --php-cli-options="-d memory_limit=8G"`
* New less variable `@theme-color-menu-contrast-textSelected` that lets you specify the color of a selected menu item.
- * in Administration > Diagnostics, there is a new page `Config file` which lets Super User view all config values from `global.ini.php` in the UI, and whether they were overriden in your `config/config.ini.php`
+ * in Administration > Diagnostics, there is a new page `Config file` which lets Super User view all config values from `global.ini.php` in the UI, and whether they were overridden in your `config/config.ini.php`
### New commands
* New command `config:set` lets you set INI config options from the command line. This command can be used for convenience or for automation.
@@ -318,7 +336,7 @@ The folder containing expected screenshots was renamed from `expected-ui-screens
* In `piwik.js` we replaced [JSON2](https://github.com/douglascrockford/JSON-js) with [JSON3](https://bestiejs.github.io/json3/) to implement CSP (Content Security Policy) as JSON3 does not use `eval()`. JSON3 will be used if a browser does not provide a native JSON API. We are using `JSON3` in a way that it will not conflict if your website is using `JSON3` as well.
* The option `branch` of the console command `development:sync-system-test-processed` was removed as it is no longer needed.
* All numbers in reports will now appear formatted (eg. `1,000,000` instead of `1000000`)
-* Database connections now use `UTF-8` charset explicitely to force UTF-8 data handling
+* Database connections now use `UTF-8` charset explicitly to force UTF-8 data handling
## Piwik 2.14.0
diff --git a/config/global.php b/config/global.php
index 710c35eb66..3f55fe3026 100644
--- a/config/global.php
+++ b/config/global.php
@@ -64,6 +64,8 @@ return array(
return 'eagercache-' . str_replace(array('.', '-'), '', \Piwik\Version::VERSION) . '-';
},
+ 'entities.idNames' => DI\add(array('idGoal', 'idDimension')),
+
'Psr\Log\LoggerInterface' => DI\object('Psr\Log\NullLogger'),
'Piwik\Translation\Loader\LoaderInterface' => DI\object('Piwik\Translation\Loader\LoaderCache')
diff --git a/core/API/DataTableManipulator.php b/core/API/DataTableManipulator.php
index 7dad3b7a0d..e9140fecf6 100644
--- a/core/API/DataTableManipulator.php
+++ b/core/API/DataTableManipulator.php
@@ -10,6 +10,7 @@ namespace Piwik\API;
use Exception;
use Piwik\Archive\DataTableFactory;
+use Piwik\Container\StaticContainer;
use Piwik\DataTable\Row;
use Piwik\DataTable;
use Piwik\Period\Range;
@@ -153,11 +154,11 @@ abstract class DataTableManipulator
}
$apiParameters = array();
- if (!empty($request['idDimension'])) {
- $apiParameters['idDimension'] = $request['idDimension'];
- }
- if (!empty($request['idGoal'])) {
- $apiParameters['idGoal'] = $request['idGoal'];
+ $entityNames = StaticContainer::get('entities.idNames');
+ foreach ($entityNames as $idName) {
+ if (!empty($request[$idName])) {
+ $apiParameters[$idName] = $request[$idName];
+ }
}
$meta = API::getInstance()->getMetadata($idSite, $this->apiModule, $this->apiMethod, $apiParameters);
diff --git a/core/API/DocumentationGenerator.php b/core/API/DocumentationGenerator.php
index 990d232163..e0116b7dfa 100644
--- a/core/API/DocumentationGenerator.php
+++ b/core/API/DocumentationGenerator.php
@@ -10,6 +10,7 @@ namespace Piwik\API;
use Exception;
use Piwik\Common;
+use Piwik\Container\StaticContainer;
use Piwik\Piwik;
use Piwik\Url;
use ReflectionClass;
@@ -283,6 +284,15 @@ class DocumentationGenerator
$aParameters['disable_generic_filters'] = false;
$aParameters['expanded'] = false;
$aParameters['idDimenson'] = false;
+ $aParameters['format_metrics'] = false;
+
+ $entityNames = StaticContainer::get('entities.idNames');
+ foreach ($entityNames as $entityName) {
+ if (isset($aParameters[$entityName])) {
+ continue;
+ }
+ $aParameters[$entityName] = false;
+ }
$moduleName = Proxy::getInstance()->getModuleNameFromClassName($class);
$aParameters = array_merge(array('module' => 'API', 'method' => $moduleName . '.' . $methodName), $aParameters);
diff --git a/core/ArchiveProcessor/PluginsArchiver.php b/core/ArchiveProcessor/PluginsArchiver.php
index 4c963e17bc..2b46ea29f6 100644
--- a/core/ArchiveProcessor/PluginsArchiver.php
+++ b/core/ArchiveProcessor/PluginsArchiver.php
@@ -147,7 +147,12 @@ class PluginsArchiver
);
} catch (Exception $e) {
$className = get_class($e);
- $exception = new $className($e->getMessage() . " - caused by plugin $pluginName", $e->getCode(), $e);
+
+ if ($className === 'PHPUnit_Framework_Exception' || (class_exists('PHPUnit_Framework_Exception', false) && is_subclass_of($className, 'PHPUnit_Framework_Exception'))) {
+ $exception = new $className($e->getMessage() . " - caused by plugin $pluginName", $e->getCode(), $e->getFile(), $e->getLine(), $e);
+ } else {
+ $exception = new $className($e->getMessage() . " - caused by plugin $pluginName", $e->getCode(), $e);
+ }
throw $exception;
}
diff --git a/core/Columns/ComputedMetricFactory.php b/core/Columns/ComputedMetricFactory.php
new file mode 100644
index 0000000000..0b2b2f5426
--- /dev/null
+++ b/core/Columns/ComputedMetricFactory.php
@@ -0,0 +1,57 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Columns;
+
+use Piwik\Piwik;
+use Piwik\Plugin\ArchivedMetric;
+use Piwik\Plugin\ComputedMetric;
+use Piwik\Plugin\Report;
+
+/**
+ * A factory to create computed metrics.
+ *
+ * @api since Piwik 3.2.0
+ */
+class ComputedMetricFactory
+{
+ /**
+ * @var MetricsList
+ */
+ private $metricsList = null;
+
+ /**
+ * Generates a new report metric factory.
+ * @param MetricsList $list A report list instance
+ * @ignore
+ */
+ public function __construct(MetricsList $list)
+ {
+ $this->metricsList = $list;
+ }
+
+ /**
+ * @return \Piwik\Plugin\ComputedMetric
+ */
+ public function createComputedMetric($metricName1, $metricName2, $aggregation)
+ {
+ $metric1 = $this->metricsList->getMetric($metricName1);
+
+ if (!$metric1 instanceof ArchivedMetric || !$metric1->getDimension()) {
+ throw new \Exception('Only possible to create computed metric for an archived metric with a dimension');
+ }
+
+ $dimension1 = $metric1->getDimension();
+
+ $metric = new ComputedMetric($metricName1, $metricName2, $aggregation);
+ $metric->setCategory($dimension1->getCategoryId());
+
+ return $metric;
+ }
+
+} \ No newline at end of file
diff --git a/core/Columns/Dimension.php b/core/Columns/Dimension.php
index 3f31073bea..74266af05c 100644
--- a/core/Columns/Dimension.php
+++ b/core/Columns/Dimension.php
@@ -7,29 +7,48 @@
*
*/
namespace Piwik\Columns;
-
-use Exception;
-use Piwik\CacheId;
+use Piwik\Common;
+use Piwik\Db;
use Piwik\Piwik;
use Piwik\Plugin;
+use Piwik\Plugin\ArchivedMetric;
use Piwik\Plugin\ComponentFactory;
-use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugin\Dimension\ConversionDimension;
-use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Plugin\Segment;
+use Exception;
+use Piwik\CacheId;
use Piwik\Cache as PiwikCache;
use Piwik\Plugin\Manager as PluginManager;
+use Piwik\Metrics\Formatter;
/**
* @api
- * @since 2.5.0
+ * @since 3.1.0
*/
abstract class Dimension
{
const COMPONENT_SUBNAMESPACE = 'Columns';
- // TODO that we have quite a few @ignore in public methods might show we should maybe split some code into two
- // classes.
+ /**
+ * Segment type 'dimension'. Can be used along with {@link setType()}.
+ * @api
+ */
+ const TYPE_DIMENSION = 'dimension';
+ const TYPE_BINARY = 'binary';
+ const TYPE_TEXT = 'text';
+ const TYPE_ENUM = 'enum';
+ const TYPE_MONEY = 'money';
+ const TYPE_BYTE = 'byte';
+ const TYPE_DURATION_MS = 'duration_ms';
+ const TYPE_DURATION_S = 'duration_s';
+ const TYPE_NUMBER = 'number';
+ const TYPE_FLOAT = 'float';
+ const TYPE_URL = 'url';
+ const TYPE_DATE = 'date';
+ const TYPE_TIME = 'time';
+ const TYPE_DATETIME = 'datetime';
+ const TYPE_TIMESTAMP = 'timestamp';
+ const TYPE_BOOL = 'bool';
+ const TYPE_PERCENT = 'percent';
/**
* This will be the name of the column in the database table if a $columnType is specified.
@@ -54,6 +73,392 @@ abstract class Dimension
protected $segments = array();
/**
+ * Defines what kind of data type this dimension holds. By default the type is auto-detected based on
+ * `$columnType` but sometimes it may be needed to correct this value. Depending on this type, a dimension will be
+ * formatted differently for example.
+ * @var string
+ * @api since Piwik 3.2.0
+ */
+ protected $type = '';
+
+ /**
+ * Translation key for name singular
+ * @var string
+ */
+ protected $nameSingular = '';
+
+ /**
+ * Translation key for name plural
+ * @var string
+ * @api since Piwik 3.2.0
+ */
+ protected $namePlural = '';
+
+ /**
+ * Translation key for category
+ * @var string
+ */
+ protected $category = '';
+
+ /**
+ * By defining a segment name a user will be able to filter their visitors by this column. If you do not want to
+ * define a segment for this dimension, simply leave the name empty.
+ * @api since Piwik 3.2.0
+ */
+ protected $segmentName = '';
+
+ /**
+ * Sets a callback which will be executed when user will call for suggested values for segment.
+ *
+ * @var callable
+ * @api since Piwik 3.2.0
+ */
+ protected $suggestedValuesCallback;
+
+ /**
+ * Here you should explain which values are accepted/useful for your segment, for example:
+ * "1, 2, 3, etc." or "comcast.net, proxad.net, etc.". If the value needs any special encoding you should mention
+ * this as well. For example "Any URL including protocol. The URL must be URL encoded."
+ *
+ * @var string
+ * @api since Piwik 3.2.0
+ */
+ protected $acceptValues;
+
+ /**
+ * Defines to which column in the MySQL database the segment belongs (if one is conifugred). Defaults to
+ * `$this.dbTableName . '.'. $this.columnName` but you can customize it eg like `HOUR(log_visit.visit_last_action_time)`.
+ *
+ * @param string $sqlSegment
+ * @api since Piwik 3.2.0
+ */
+ protected $sqlSegment;
+
+ /**
+ * Interesting when specifying a segment. Sometimes you want users to set segment values that differ from the way
+ * they are actually stored. For instance if you want to allow to filter by any URL than you might have to resolve
+ * this URL to an action id. Or a country name maybe has to be mapped to a 2 letter country code. You can do this by
+ * specifing either a callable such as `array('Classname', 'methodName')` or by passing a closure.
+ * There will be four values passed to the given closure or callable: `string $valueToMatch`, `string $segment`
+ * (see {@link setSegment()}), `string $matchType` (eg SegmentExpression::MATCH_EQUAL or any other match constant
+ * of this class) and `$segmentName`.
+ *
+ * If the closure returns NULL, then Piwik assumes the segment sub-string will not match any visitor.
+ *
+ * @var string|\Closure
+ * @api since Piwik 3.2.0
+ */
+ protected $sqlFilter;
+
+ /**
+ * Similar to {@link $sqlFilter} you can map a given segment value to another value. For instance you could map
+ * "new" to 0, 'returning' to 1 and any other value to '2'. You can either define a callable or a closure. There
+ * will be only one value passed to the closure or callable which contains the value a user has set for this
+ * segment.
+ * @var string|array
+ * @api since Piwik 3.2.0
+ */
+ protected $sqlFilterValue;
+
+ /**
+ * Defines whether this dimension (and segment based on this dimension) is available to anonymous users.
+ * @var bool
+ * @api since Piwik 3.2.0
+ */
+ protected $allowAnonymous = true;
+
+ /**
+ * The name of the database table this dimension refers to
+ * @var string
+ * @api
+ */
+ protected $dbTableName = '';
+
+ /**
+ * By default the metricId is automatically generated based on the dimensionId. This might sometimes not be as
+ * readable and quite long. If you want more expressive metric names like `nb_visits` compared to
+ * `nb_corehomevisitid`, you can eg set a metricId `visit`.
+ *
+ * @var string
+ * @api since Piwik 3.2.0
+ */
+ protected $metricId = '';
+
+ /**
+ * To be implemented when a column references another column
+ * @return Join|null
+ * @api since Piwik 3.2.0
+ */
+ public function getDbColumnJoin()
+ {
+ return null;
+ }
+
+ /**
+ * @return Discriminator|null
+ * @api since Piwik 3.2.0
+ */
+ public function getDbDiscriminator()
+ {
+ return null;
+ }
+
+ /**
+ * To be implemented when a column represents an enum.
+ * @return array
+ * @api since Piwik 3.2.0
+ */
+ public function getEnumColumnValues()
+ {
+ return array();
+ }
+
+ /**
+ * Get the metricId which is used to generate metric names based on this dimension.
+ * @return string
+ */
+ public function getMetricId()
+ {
+ if (!empty($this->metricId)) {
+ return $this->metricId;
+ }
+
+ $id = $this->getId();
+
+ return str_replace(array('.', ' ', '-'), '_', strtolower($id));
+ }
+
+ /**
+ * Installs the action dimension in case it is not installed yet. The installation is already implemented based on
+ * the {@link $columnName} and {@link $columnType}. If you want to perform additional actions beside adding the
+ * column to the database - for instance adding an index - you can overwrite this method. We recommend to call
+ * this parent method to get the minimum required actions and then add further custom actions since this makes sure
+ * the column will be installed correctly. We also recommend to change the default install behavior only if really
+ * needed. FYI: We do not directly execute those alter table statements here as we group them together with several
+ * other alter table statements do execute those changes in one step which results in a faster installation. The
+ * column will be added to the `log_link_visit_action` MySQL table.
+ *
+ * Example:
+ * ```
+ public function install()
+ {
+ $changes = parent::install();
+ $changes['log_link_visit_action'][] = "ADD INDEX index_idsite_servertime ( idsite, server_time )";
+
+ return $changes;
+ }
+ ```
+ *
+ * @return array An array containing the table name as key and an array of MySQL alter table statements that should
+ * be executed on the given table. Example:
+ * ```
+ array(
+ 'log_link_visit_action' => array("ADD COLUMN `$this->columnName` $this->columnType", "ADD INDEX ...")
+ );
+ ```
+ * @api
+ */
+ public function install()
+ {
+ if (empty($this->columnName) || empty($this->columnType) || empty($this->dbTableName)) {
+ return array();
+ }
+
+ // TODO if table does not exist, create it with a primary key, but at this point we cannot really create it
+ // cause we need to show the query in the UI first and user needs to be able to create table manually.
+ // we cannot return something like "create table " here as it would be returned for each table etc.
+ // we need to do this in column updater etc!
+
+ return array(
+ $this->dbTableName => array("ADD COLUMN `$this->columnName` $this->columnType")
+ );
+ }
+
+ /**
+ * Updates the action dimension in case the {@link $columnType} has changed. The update is already implemented based
+ * on the {@link $columnName} and {@link $columnType}. This method is intended not to overwritten by plugin
+ * developers as it is only supposed to make sure the column has the correct type. Adding additional custom "alter
+ * table" actions would not really work since they would be executed with every {@link $columnType} change. So
+ * adding an index here would be executed whenever the columnType changes resulting in an error if the index already
+ * exists. If an index needs to be added after the first version is released a plugin update class should be
+ * created since this makes sure it is only executed once.
+ *
+ * @return array An array containing the table name as key and an array of MySQL alter table statements that should
+ * be executed on the given table. Example:
+ * ```
+ array(
+ 'log_link_visit_action' => array("MODIFY COLUMN `$this->columnName` $this->columnType", "DROP COLUMN ...")
+ );
+ ```
+ * @ignore
+ */
+ public function update()
+ {
+ if (empty($this->columnName) || empty($this->columnType) || empty($this->dbTableName)) {
+ return array();
+ }
+
+ return array(
+ $this->dbTableName => array("MODIFY COLUMN `$this->columnName` $this->columnType")
+ );
+ }
+
+ /**
+ * Uninstalls the dimension if a {@link $columnName} and {@link columnType} is set. In case you perform any custom
+ * actions during {@link install()} - for instance adding an index - you should make sure to undo those actions by
+ * overwriting this method. Make sure to call this parent method to make sure the uninstallation of the column
+ * will be done.
+ * @throws Exception
+ * @api
+ */
+ public function uninstall()
+ {
+ if (empty($this->columnName) || empty($this->columnType) || empty($this->dbTableName)) {
+ return;
+ }
+
+ try {
+ $sql = "ALTER TABLE `" . Common::prefixTable($this->dbTableName) . "` DROP COLUMN `$this->columnName`";
+ Db::exec($sql);
+ } catch (Exception $e) {
+ if (!Db::get()->isErrNo($e, '1091')) {
+ throw $e;
+ }
+ }
+ }
+
+ /**
+ * Returns the ID of the category (typically a translation key).
+ * @return string
+ */
+ public function getCategoryId()
+ {
+ return $this->category;
+ }
+
+ /**
+ * Returns the translated name of this dimension which is typically in singular.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ if (!empty($this->nameSingular)) {
+ return Piwik::translate($this->nameSingular);
+ }
+
+ return $this->nameSingular;
+ }
+
+ /**
+ * Returns a translated name in plural for this dimension.
+ * @return string
+ * @api since Piwik 3.2.0
+ */
+ public function getNamePlural()
+ {
+ if (!empty($this->namePlural)) {
+ return Piwik::translate($this->namePlural);
+ }
+
+ return $this->getName();
+ }
+
+ /**
+ * Defines whether an anonymous user is allowed to view this dimension
+ * @return bool
+ * @api since Piwik 3.2.0
+ */
+ public function isAnonymousAllowed()
+ {
+ return $this->allowAnonymous;
+ }
+
+ /**
+ * Sets (overwrites) the SQL segment
+ * @param $segment
+ * @api since Piwik 3.2.0
+ */
+ public function setSqlSegment($segment)
+ {
+ $this->sqlSegment = $segment;
+ }
+
+ /**
+ * Sets (overwrites the dimension type)
+ * @param $type
+ * @api since Piwik 3.2.0
+ */
+ public function setType($type)
+ {
+ $this->type = $type;
+ }
+
+ /**
+ * A dimension should group values by using this method. Otherwise the same row may appear several times.
+ *
+ * @param mixed $value
+ * @param int $idSite
+ * @return mixed
+ * @api since Piwik 3.2.0
+ */
+ public function groupValue($value, $idSite)
+ {
+ switch ($this->type) {
+ case Dimension::TYPE_URL:
+ return str_replace(array('http://', 'https://'), '', $value);
+ case Dimension::TYPE_BOOL:
+ return !empty($value) ? '1' : '0';
+ case Dimension::TYPE_DURATION_MS:
+ return number_format($value / 1000, 2); // because we divide we need to group them and cannot do this in formatting step
+ }
+ return $value;
+ }
+
+ /**
+ * Formats the dimension value. By default, the dimension is formatted based on the set dimension type.
+ *
+ * @param mixed $value
+ * @param int $idSite
+ * @param Formatter $formatter
+ * @return mixed
+ * @api since Piwik 3.2.0
+ */
+ public function formatValue($value, $idSite, Formatter $formatter)
+ {
+ switch ($this->type) {
+ case Dimension::TYPE_BOOL:
+ if (empty($value)) {
+ return Piwik::translate('General_No');
+ }
+
+ return Piwik::translate('General_Yes');
+ case Dimension::TYPE_ENUM:
+ $values = $this->getEnumColumnValues();
+ if (isset($values[$value])) {
+ return $values[$value];
+ }
+ break;
+ case Dimension::TYPE_MONEY:
+ return $formatter->getPrettyMoney($value, $idSite);
+ case Dimension::TYPE_FLOAT:
+ return $formatter->getPrettyNumber((float) $value, $precision = 2);
+ case Dimension::TYPE_NUMBER:
+ return $formatter->getPrettyNumber($value);
+ case Dimension::TYPE_DURATION_S:
+ return $formatter->getPrettyTimeFromSeconds($value, $displayAsSentence = false);
+ case Dimension::TYPE_DURATION_MS:
+ return $formatter->getPrettyTimeFromSeconds($value, $displayAsSentence = true);
+ case Dimension::TYPE_PERCENT:
+ return $formatter->getPrettyPercentFromQuotient($value);
+ case Dimension::TYPE_BYTE:
+ return $formatter->getPrettySizeFromBytes($value);
+ }
+
+ return $value;
+ }
+
+ /**
* Overwrite this method to configure segments. To do so just create an instance of a {@link \Piwik\Plugin\Segment}
* class, configure it and call the {@link addSegment()} method. You can add one or more segments for this
* dimension. Example:
@@ -68,6 +473,42 @@ abstract class Dimension
*/
protected function configureSegments()
{
+ if ($this->segmentName && $this->category
+ && ($this->sqlSegment || ($this->columnName && $this->dbTableName))
+ && $this->nameSingular) {
+ $segment = new Segment();
+ $this->addSegment($segment);
+ }
+ }
+
+ /**
+ * Configures metrics for this dimension.
+ *
+ * For certain dimension types, some metrics will be added automatically.
+ *
+ * @param MetricsList $metricsList
+ * @param DimensionMetricFactory $dimensionMetricFactory
+ */
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
+ {
+ if ($this->getMetricId() && $this->dbTableName && $this->columnName && $this->getNamePlural()) {
+ if (in_array($this->getType(), array(self::TYPE_DATETIME, self::TYPE_DATE, self::TYPE_TIME, self::TYPE_TIMESTAMP))) {
+ // we do not generate any metrics from these types
+ return;
+ } elseif (in_array($this->getType(), array(self::TYPE_URL, self::TYPE_TEXT, self::TYPE_BINARY, self::TYPE_ENUM))) {
+ $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_UNIQUE);
+ $metricsList->addMetric($metric);
+ } elseif (in_array($this->getType(), array(self::TYPE_BOOL))) {
+ $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_SUM);
+ $metricsList->addMetric($metric);
+ } else {
+ $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_SUM);
+ $metricsList->addMetric($metric);
+
+ $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_MAX);
+ $metricsList->addMetric($metric);
+ }
+ }
}
/**
@@ -85,16 +526,110 @@ abstract class Dimension
}
/**
- * Adds a new segment. The segment type will be set to 'dimension' automatically if not already set.
+ * Adds a new segment. It automatically sets the SQL segment depending on the column name in case none is set
+ * already.
+ * @see \Piwik\Columns\Dimension::addSegment()
* @param Segment $segment
* @api
*/
protected function addSegment(Segment $segment)
{
- $type = $segment->getType();
+ if (!$segment->getSegment() && $this->segmentName) {
+ $segment->setSegment($this->segmentName);
+ }
+
+ if (!$segment->getType()) {
+ $metricTypes = array(self::TYPE_NUMBER, self::TYPE_FLOAT, self::TYPE_MONEY, self::TYPE_DURATION_S, self::TYPE_DURATION_MS);
+ if (in_array($this->getType(), $metricTypes, $strict = true)) {
+ $segment->setType(Segment::TYPE_METRIC);
+ } else {
+ $segment->setType(Segment::TYPE_DIMENSION);
+ }
+ }
+
+ if (!$segment->getCategoryId() && $this->category) {
+ $segment->setCategory($this->category);
+ }
+
+ if (!$segment->getName() && $this->nameSingular) {
+ $segment->setName($this->nameSingular);
+ }
+
+ $sqlSegment = $segment->getSqlSegment();
+
+ if (empty($sqlSegment) && !$segment->getUnionOfSegments()) {
+ if (!empty($this->sqlSegment)) {
+ $segment->setSqlSegment($this->sqlSegment);
+ } elseif ($this->dbTableName && $this->columnName) {
+ $segment->setSqlSegment($this->dbTableName . '.' . $this->columnName);
+ } else {
+ throw new Exception('Segment cannot be added because no sql segment is set');
+ }
+ }
+
+ if (!$this->suggestedValuesCallback) {
+ // we can generate effecient value callback for enums automatically
+ $enum = $this->getEnumColumnValues();
+ if (!empty($enum)) {
+ $this->suggestedValuesCallback = function ($idSite, $maxValuesToReturn) use ($enum) {
+ $values = array_values($enum);
+ return array_slice($values, 0, $maxValuesToReturn);
+ };
+ }
+ }
+
+ if (!$this->acceptValues) {
+ // we can generate accept values for enums automatically
+ $enum = $this->getEnumColumnValues();
+ if (!empty($enum)) {
+ $enumValues = array_values($enum);
+ $enumValues = array_slice($enumValues, 0, 20);
+ $this->acceptValues = 'Eg. ' . implode(', ', $enumValues);
+ };
+ }
+
+ if ($this->acceptValues && !$segment->getAcceptValues()) {
+ $segment->setAcceptedValues($this->acceptValues);
+ }
+
+ if (!$this->sqlFilterValue && !$segment->getSqlFilter() && !$segment->getSqlFilterValue()) {
+ // no sql filter configured, we try to configure automatically for enums
+ $enum = $this->getEnumColumnValues();
+ if (!empty($enum)) {
+ $this->sqlFilterValue = function ($value, $sqlSegmentName) use ($enum) {
+ if (isset($enum[$value])) {
+ return $value;
+ }
+
+ $id = array_search($value, $enum);
+
+ if ($id === false) {
+ $id = array_search(strtolower(trim(urldecode($value))), $enum);
+
+ if ($id === false) {
+ throw new \Exception("Invalid '$sqlSegmentName' segment value $value");
+ }
+ }
+
+ return $id;
+ };
+ };
+ }
+
+ if ($this->suggestedValuesCallback && !$segment->getSuggestedValuesCallback()) {
+ $segment->setSuggestedValuesCallback($this->suggestedValuesCallback);
+ }
+
+ if ($this->sqlFilterValue && !$segment->getSqlFilterValue()) {
+ $segment->setSqlFilterValue($this->sqlFilterValue);
+ }
+
+ if ($this->sqlFilter && !$segment->getSqlFilter()) {
+ $segment->setSqlFilter($this->sqlFilter);
+ }
- if (empty($type)) {
- $segment->setType(Segment::TYPE_DIMENSION);
+ if (!$this->allowAnonymous) {
+ $segment->setRequiresAtLeastViewAccess(true);
}
$this->segments[] = $segment;
@@ -115,6 +650,16 @@ abstract class Dimension
}
/**
+ * Returns the name of the segment that this dimension defines
+ * @return string
+ * @api since Piwik 3.2.0
+ */
+ public function getSegmentName()
+ {
+ return $this->segmentName;
+ }
+
+ /**
* Get the name of the dimension column.
* @return string
* @ignore
@@ -125,6 +670,22 @@ abstract class Dimension
}
/**
+ * Returns a sql segment expression for this dimension.
+ * @return string
+ * @api since Piwik 3.2.0
+ */
+ public function getSqlSegment()
+ {
+ if (!empty($this->sqlSegment)) {
+ return $this->sqlSegment;
+ }
+
+ if ($this->dbTableName && $this->columnName) {
+ return $this->dbTableName . '.' . $this->columnName;
+ }
+ }
+
+ /**
* Check whether the dimension has a column type configured
* @return bool
* @ignore
@@ -135,13 +696,13 @@ abstract class Dimension
}
/**
- * Get the translated name of the dimension. Defaults to an empty string.
+ * Returns the name of the database table this dimension belongs to.
* @return string
- * @api
+ * @api since Piwik 3.2.0
*/
- public function getName()
+ public function getDbTableName()
{
- return '';
+ return $this->dbTableName;
}
/**
@@ -157,6 +718,17 @@ abstract class Dimension
{
$className = get_class($this);
+ return $this->generateIdFromClass($className);
+ }
+
+ /**
+ * @param string $className
+ * @return string
+ * @throws Exception
+ * @ignore
+ */
+ protected function generateIdFromClass($className)
+ {
// parse plugin name & dimension name
$regex = "/Piwik\\\\Plugins\\\\([^\\\\]+)\\\\" . self::COMPONENT_SUBNAMESPACE . "\\\\([^\\\\]+)/";
if (!preg_match($regex, $className, $matches)) {
@@ -232,11 +804,11 @@ abstract class Dimension
public static function getDimensions(Plugin $plugin)
{
- $dimensions = $plugin->findMultipleComponents('Columns', '\\Piwik\\Columns\\Dimension');
+ $columns = $plugin->findMultipleComponents('Columns', '\\Piwik\\Columns\\Dimension');
$instances = array();
- foreach ($dimensions as $dimension) {
- $instances[] = new $dimension();
+ foreach ($columns as $colum) {
+ $instances[] = new $colum();
}
return $instances;
@@ -250,6 +822,7 @@ abstract class Dimension
* $dimensionId or if the plugin that contains the Dimension is
* not loaded.
* @api
+ * @deprecated Please use DimensionProvider::factory instead
*/
public static function factory($dimensionId)
{
@@ -274,4 +847,61 @@ abstract class Dimension
$parts = explode('.', $id);
return reset($parts);
}
+
+ /**
+ * Returns the type of the dimension which defines what kind of value this dimension stores.
+ * @return string
+ * @api since Piwik 3.2.0
+ */
+ public function getType()
+ {
+ if (!empty($this->type)) {
+ return $this->type;
+ }
+
+ if ($this->getDbColumnJoin()) {
+ // best guess
+ return self::TYPE_TEXT;
+ }
+
+ if ($this->getEnumColumnValues()) {
+ // best guess
+ return self::TYPE_ENUM;
+ }
+
+ if (!empty($this->columnType)) {
+ // best guess
+ $type = strtolower($this->columnType);
+ if (strpos($type, 'datetime') !== false) {
+ return self::TYPE_DATETIME;
+ } elseif (strpos($type, 'timestamp') !== false) {
+ return self::TYPE_TIMESTAMP;
+ } elseif (strpos($type, 'date') !== false) {
+ return self::TYPE_DATE;
+ } elseif (strpos($type, 'time') !== false) {
+ return self::TYPE_TIME;
+ } elseif (strpos($type, 'float') !== false) {
+ return self::TYPE_FLOAT;
+ } elseif (strpos($type, 'decimal') !== false) {
+ return self::TYPE_FLOAT;
+ } elseif (strpos($type, 'int') !== false) {
+ return self::TYPE_NUMBER;
+ } elseif (strpos($type, 'binary') !== false) {
+ return self::TYPE_BINARY;
+ }
+ }
+
+ return self::TYPE_TEXT;
+ }
+
+ /**
+ * Get the version of the dimension which is used for update checks.
+ * @return string
+ * @ignore
+ */
+ public function getVersion()
+ {
+ return $this->columnType;
+ }
+
}
diff --git a/core/Columns/DimensionMetricFactory.php b/core/Columns/DimensionMetricFactory.php
new file mode 100644
index 0000000000..58178951ec
--- /dev/null
+++ b/core/Columns/DimensionMetricFactory.php
@@ -0,0 +1,122 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Columns;
+
+use Piwik\Piwik;
+use Piwik\Plugin\ArchivedMetric;
+use Piwik\Plugin\ComputedMetric;
+use Piwik\Plugin\Report;
+
+
+/**
+ * A factory to create metrics from a dimension.
+ *
+ * @api since Piwik 3.2.0
+ */
+class DimensionMetricFactory
+{
+ /**
+ * @var Dimension
+ */
+ private $dimension = null;
+
+ /**
+ * Generates a new dimension metric factory.
+ * @param Dimension $dimension A dimension instance the created metrics should be based on.
+ */
+ public function __construct(Dimension $dimension)
+ {
+ $this->dimension = $dimension;
+ }
+
+ /**
+ * @return ArchivedMetric
+ */
+ public function createCustomMetric($metricName, $readableName, $aggregation, $documentation = '')
+ {
+ if (!$this->dimension->getDbTableName() || !$this->dimension->getColumnName()) {
+ throw new \Exception(sprintf('Cannot make metric from dimension %s because DB table or column missing', $this->dimension->getId()));
+ }
+
+ $metric = new ArchivedMetric($this->dimension, $aggregation);
+ $metric->setType($this->dimension->getType());
+ $metric->setName($metricName);
+ $metric->setTranslatedName($readableName);
+ $metric->setDocumentation($documentation);
+ $metric->setCategory($this->dimension->getCategoryId());
+
+ return $metric;
+ }
+
+ /**
+ * @return \Piwik\Plugin\ComputedMetric
+ */
+ public function createComputedMetric($metricName1, $metricName2, $aggregation)
+ {
+ // We cannot use reuse ComputedMetricFactory here as it would result in an endless loop since ComputedMetricFactory
+ // requires a MetricsList which is just being built here...
+ $metric = new ComputedMetric($metricName1, $metricName2, $aggregation);
+ $metric->setCategory($this->dimension->getCategoryId());
+ return $metric;
+ }
+
+ /**
+ * @return ArchivedMetric
+ */
+ public function createMetric($aggregation)
+ {
+ $dimension = $this->dimension;
+
+ if (!$dimension->getNamePlural()) {
+ throw new \Exception(sprintf('No metric can be created for this dimension %s automatically because no $namePlural is set.', $dimension->getId()));
+ }
+
+ $prefix = '';
+ $translatedName = $dimension->getNamePlural();
+
+ $documentation = '';
+
+ switch ($aggregation) {
+ case ArchivedMetric::AGGREGATION_COUNT;
+ $prefix = ArchivedMetric::AGGREGATION_COUNT_PREFIX;
+ $translatedName = $dimension->getNamePlural();
+ $documentation = Piwik::translate('General_ComputedMetricCountDocumentation', $dimension->getNamePlural());
+ break;
+ case ArchivedMetric::AGGREGATION_SUM;
+ $prefix = ArchivedMetric::AGGREGATION_SUM_PREFIX;
+ $translatedName = Piwik::translate('General_ComputedMetricSum', $dimension->getNamePlural());
+ $documentation = Piwik::translate('General_ComputedMetricSumDocumentation', $dimension->getNamePlural());
+ break;
+ case ArchivedMetric::AGGREGATION_MAX;
+ $prefix = ArchivedMetric::AGGREGATION_MAX_PREFIX;
+ $translatedName = Piwik::translate('General_ComputedMetricMax', $dimension->getNamePlural());
+ $documentation = Piwik::translate('General_ComputedMetricMaxDocumentation', $dimension->getNamePlural());
+ break;
+ case ArchivedMetric::AGGREGATION_MIN;
+ $prefix = ArchivedMetric::AGGREGATION_MIN_PREFIX;
+ $translatedName = Piwik::translate('General_ComputedMetricMin', $dimension->getNamePlural());
+ $documentation = Piwik::translate('General_ComputedMetricMinDocumentation', $dimension->getNamePlural());
+ break;
+ case ArchivedMetric::AGGREGATION_UNIQUE;
+ $prefix = ArchivedMetric::AGGREGATION_UNIQUE_PREFIX;
+ $translatedName = Piwik::translate('General_ComputedMetricUniqueCount', $dimension->getNamePlural());
+ $documentation = Piwik::translate('General_ComputedMetricUniqueCountDocumentation', $dimension->getNamePlural());
+ break;
+ case ArchivedMetric::AGGREGATION_COUNT_WITH_NUMERIC_VALUE;
+ $prefix = ArchivedMetric::AGGREGATION_COUNT_WITH_NUMERIC_VALUE_PREFIX;
+ $translatedName = Piwik::translate('General_ComputedMetricCountWithValue', $dimension->getName());
+ $documentation = Piwik::translate('General_ComputedMetricCountWithValueDocumentation', $dimension->getName());
+ break;
+ }
+
+ $metricId = strtolower($dimension->getMetricId());
+
+ return $this->createCustomMetric($prefix . $metricId, $translatedName, $aggregation, $documentation);
+ }
+} \ No newline at end of file
diff --git a/core/Columns/DimensionsProvider.php b/core/Columns/DimensionsProvider.php
new file mode 100644
index 0000000000..cd9da962b6
--- /dev/null
+++ b/core/Columns/DimensionsProvider.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Columns;
+
+use Piwik\CacheId;
+use Piwik\Cache as PiwikCache;
+
+class DimensionsProvider
+{
+ /**
+ * @param $dimensionId
+ * @return Dimension
+ */
+ public function factory($dimensionId)
+ {
+ $listDimensions = self::getMapOfNameToDimension();
+
+ if (empty($listDimensions) || !is_array($listDimensions) || !$dimensionId || !array_key_exists($dimensionId, $listDimensions)) {
+ return null;
+ }
+
+ return $listDimensions[$dimensionId];
+ }
+
+ private static function getMapOfNameToDimension()
+ {
+ $cacheId = CacheId::pluginAware('DimensionFactoryMap');
+
+ $cache = PiwikCache::getTransientCache();
+ if ($cache->contains($cacheId)) {
+ $mapIdToDimension = $cache->fetch($cacheId);
+ } else {
+ $dimensions = new static();
+ $dimensions = $dimensions->getAllDimensions();
+
+ $mapIdToDimension = array();
+ foreach ($dimensions as $dimension) {
+ $mapIdToDimension[$dimension->getId()] = $dimension;
+ }
+
+ $cache->save($cacheId, $mapIdToDimension);
+ }
+
+ return $mapIdToDimension;
+ }
+
+ /**
+ * Returns a list of all available dimensions.
+ * @return Dimension[]
+ */
+ public function getAllDimensions()
+ {
+ return Dimension::getAllDimensions();
+ }
+
+} \ No newline at end of file
diff --git a/core/Columns/Discriminator.php b/core/Columns/Discriminator.php
new file mode 100644
index 0000000000..91ad6d2e38
--- /dev/null
+++ b/core/Columns/Discriminator.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Columns;
+
+use Exception;
+use Piwik\Plugins\Actions\Actions\ActionSiteSearch;
+
+/**
+ * @api
+ * @since 3.1.0
+ */
+class Discriminator
+{
+ private $table;
+ private $discriminatorColumn;
+ private $discriminatorValue;
+
+ /**
+ * Join constructor.
+ * @param string $table unprefixed table name
+ * @param null|string $discriminatorColumn
+ * @param null|int $discriminatorValue should be only hard coded, safe values.
+ * @throws Exception
+ */
+ public function __construct($table, $discriminatorColumn = null, $discriminatorValue = null)
+ {
+ if (empty($discriminatorColumn) || !isset($discriminatorValue)) {
+ throw new Exception('Both discriminatorColumn and discriminatorValue need to be defined');
+ }
+ $this->table = $table;
+ $this->discriminatorColumn = $discriminatorColumn;
+ $this->discriminatorValue = $discriminatorValue;
+
+ if (!$this->isValid()) {
+ // if adding another string value please post an event instead to get a list of allowed values
+ throw new Exception('$discriminatorValue needs to be null or numeric');
+ }
+ }
+
+ public function isValid()
+ {
+ return isset($this->discriminatorColumn)
+ && (is_numeric($this->discriminatorValue) || $this->discriminatorValue == ActionSiteSearch::CVAR_KEY_SEARCH_CATEGORY);
+ }
+
+ /**
+ * @return string
+ */
+ public function getTable()
+ {
+ return $this->table;
+ }
+
+ /**
+ * @return string
+ */
+ public function getColumn()
+ {
+ return $this->discriminatorColumn;
+ }
+
+ /**
+ * @return int|null
+ */
+ public function getValue()
+ {
+ return $this->discriminatorValue;
+ }
+}
diff --git a/core/Columns/Join.php b/core/Columns/Join.php
new file mode 100644
index 0000000000..75afec98a7
--- /dev/null
+++ b/core/Columns/Join.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Columns;
+
+use Exception;
+
+/**
+ * @api
+ * @since 3.1.0
+ */
+class Join
+{
+ private $table;
+ private $column;
+ private $targetColumn;
+
+ /**
+ * Join constructor.
+ * @param $table
+ * @param $column
+ * @param $targetColumn
+ * @throws Exception
+ */
+ public function __construct($table, $column, $targetColumn)
+ {
+ $this->table = $table;
+ $this->column = $column;
+ $this->targetColumn = $targetColumn;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTable()
+ {
+ return $this->table;
+ }
+
+ /**
+ * @return string
+ */
+ public function getColumn()
+ {
+ return $this->column;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTargetColumn()
+ {
+ return $this->targetColumn;
+ }
+
+}
diff --git a/core/Columns/Join/ActionNameJoin.php b/core/Columns/Join/ActionNameJoin.php
new file mode 100644
index 0000000000..64fdd25c8c
--- /dev/null
+++ b/core/Columns/Join/ActionNameJoin.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Columns\Join;
+
+use Piwik\Columns;
+
+/**
+ * @api
+ * @since 3.1.0
+ */
+class ActionNameJoin extends Columns\Join
+{
+ public function __construct()
+ {
+ return parent::__construct('log_action', 'idaction', 'name');
+ }
+
+}
diff --git a/core/Columns/Join/GoalNameJoin.php b/core/Columns/Join/GoalNameJoin.php
new file mode 100644
index 0000000000..ed6f875983
--- /dev/null
+++ b/core/Columns/Join/GoalNameJoin.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Columns\Join;
+
+use Piwik\Columns;
+
+/**
+ * @api
+ * @since 3.1.0
+ */
+class GoalNameJoin extends Columns\Join
+{
+ public function __construct()
+ {
+ return parent::__construct('goal', 'idgoal', 'name');
+ }
+
+}
diff --git a/core/Columns/Join/SiteNameJoin.php b/core/Columns/Join/SiteNameJoin.php
new file mode 100644
index 0000000000..206c585b75
--- /dev/null
+++ b/core/Columns/Join/SiteNameJoin.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Columns\Join;
+
+use Piwik\Columns;
+
+/**
+ * @api
+ * @since 3.1.0
+ */
+class SiteNameJoin extends Columns\Join
+{
+ public function __construct()
+ {
+ return parent::__construct('site', 'idsite', 'name');
+ }
+
+}
diff --git a/core/Columns/MetricsList.php b/core/Columns/MetricsList.php
new file mode 100644
index 0000000000..e65ec67ec7
--- /dev/null
+++ b/core/Columns/MetricsList.php
@@ -0,0 +1,190 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Columns;
+
+use Piwik\Cache;
+use Piwik\Piwik;
+use Piwik\Plugin\ArchivedMetric;
+use Piwik\Plugin\Metric;
+use Piwik\Plugin\ProcessedMetric;
+
+/**
+ * Manages the global list of metrics that can be used in reports.
+ *
+ * Metrics are added automatically by dimensions as well as through the {@hook Metric.addMetrics} and
+ * {@hook Metric.addComputedMetrics} and filtered through the {@hook Metric.filterMetrics} event.
+ * Observers for this event should call the {@link addMetric()} method to add metrics or use any of the other
+ * methods to remove metrics.
+ *
+ * @api since Piwik 3.2.0
+ */
+class MetricsList
+{
+ /**
+ * List of metrics
+ *
+ * @var Metric[]
+ */
+ private $metrics = array();
+
+ /**
+ * @param Metric $metric
+ */
+ public function addMetric(Metric $metric)
+ {
+ $this->metrics[] = $metric;
+ }
+
+ /**
+ * Get all available metrics.
+ *
+ * @return Metric[]
+ */
+ public function getMetrics()
+ {
+ return $this->metrics;
+ }
+
+ /**
+ * Removes one or more metrics from the metrics list.
+ *
+ * @param string $metricCategory The metric category id. Can be a translation token eg 'General_Visits'
+ * see {@link Metric::getCategory()}.
+ * @param string|false $metricName The name of the metric to remove eg 'nb_visits'.
+ * If not supplied, all metrics within that category will be removed.
+ */
+ public function remove($metricCategory, $metricName = false)
+ {
+ foreach ($this->metrics as $index => $metric) {
+ if ($metric->getCategoryId() === $metricCategory) {
+ if (!$metricName || $metric->getName() === $metricName) {
+ unset($this->metrics[$index]);
+ }
+ }
+ }
+ }
+
+ /**
+ * @param string $metricName
+ * @return Metric|ArchivedMetric|null
+ */
+ public function getMetric($metricName)
+ {
+ foreach ($this->metrics as $index => $metric) {
+ if ($metric->getName() === $metricName) {
+ return $metric;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get all metrics defined in the Piwik platform.
+ * @ignore
+ * @return static
+ */
+ public static function get()
+ {
+ $cache = Cache::getTransientCache();
+ $cacheKey = 'MetricsList';
+
+ foreach (array('idsite', 'idSite') as $param) {
+ if (!empty($_GET[$param]) && is_numeric($_GET[$param])) {
+ $cacheKey .= $cacheKey . '_' . $_GET[$param];
+ }
+
+ if (!empty($_POST[$param]) && is_numeric($_POST[$param])) {
+ $cacheKey .= $cacheKey . '_' . $_POST[$param];
+ }
+ }
+
+ if ($cache->contains($cacheKey)) {
+ return $cache->fetch($cacheKey);
+ }
+
+ $list = new static;
+
+ /**
+ * Triggered to add new metrics that cannot be picked up automatically by the platform.
+ * This is useful if the plugin allows a user to create metrics dynamically. For example
+ * CustomDimensions or CustomVariables.
+ *
+ * **Example**
+ *
+ * public function addMetric(&$list)
+ * {
+ * $list->addMetric(new MyCustomMetric());
+ * }
+ *
+ * @param MetricsList $list An instance of the MetricsList. You can add metrics to the list this way.
+ */
+ Piwik::postEvent('Metric.addMetrics', array($list));
+
+ $dimensions = Dimension::getAllDimensions();
+ foreach ($dimensions as $dimension) {
+ $factory = new DimensionMetricFactory($dimension);
+ $dimension->configureMetrics($list, $factory);
+ }
+
+ $computedFactory = new ComputedMetricFactory($list);
+
+ /**
+ * Triggered to add new metrics that cannot be picked up automatically by the platform.
+ * This is useful if the plugin allows a user to create metrics dynamically. For example
+ * CustomDimensions or CustomVariables.
+ *
+ * **Example**
+ *
+ * public function addMetric(&$list)
+ * {
+ * $list->addMetric(new MyCustomMetric());
+ * }
+ *
+ * @param MetricsList $list An instance of the MetricsList. You can add metrics to the list this way.
+ */
+ Piwik::postEvent('Metric.addComputedMetrics', array($list, $computedFactory));
+
+ /**
+ * Triggered to filter metrics.
+ *
+ * **Example**
+ *
+ * public function removeMetrics(Piwik\Columns\MetricsList $list)
+ * {
+ * $list->remove($category='General_Visits'); // remove all metrics having this category
+ * }
+ *
+ * @param MetricsList $list An instance of the MetricsList. You can change the list of metrics this way.
+ */
+ Piwik::postEvent('Metric.filterMetrics', array($list));
+
+ $availableMetrics = array();
+ foreach ($list->getMetrics() as $metric) {
+ $availableMetrics[] = $metric->getName();
+ }
+
+ foreach ($list->metrics as $index => $metric) {
+ if ($metric instanceof ProcessedMetric) {
+ $depMetrics = $metric->getDependentMetrics();
+ if (is_array($depMetrics)) {
+ foreach ($depMetrics as $depMetric) {
+ if (!in_array($depMetric, $availableMetrics, $strict = true)) {
+ unset($list->metrics[$index]); // not resolvable metric
+ }
+ }
+ }
+ }
+ }
+
+ $cache->save($cacheKey, $list);
+
+ return $list;
+ }
+
+}
diff --git a/core/Columns/Updater.php b/core/Columns/Updater.php
index fe63d4c146..73da658fe8 100644
--- a/core/Columns/Updater.php
+++ b/core/Columns/Updater.php
@@ -123,7 +123,7 @@ class Updater extends \Piwik\Updates
$allUpdatesToRun = array();
foreach ($this->getVisitDimensions() as $dimension) {
- $updates = $this->getUpdatesForDimension($updater, $dimension, 'log_visit.', $visitColumns, $conversionColumns);
+ $updates = $this->getUpdatesForDimension($updater, $dimension, 'log_visit.', $visitColumns);
$allUpdatesToRun = $this->mixinUpdates($allUpdatesToRun, $updates);
}
@@ -143,11 +143,9 @@ class Updater extends \Piwik\Updates
/**
* @param ActionDimension|ConversionDimension|VisitDimension $dimension
* @param string $componentPrefix
- * @param array $existingColumnsInDb
- * @param array $conversionColumns
* @return array
*/
- private function getUpdatesForDimension(PiwikUpdater $updater, $dimension, $componentPrefix, $existingColumnsInDb, $conversionColumns = array())
+ private function getUpdatesForDimension(PiwikUpdater $updater, $dimension, $componentPrefix, $existingColumnsInDb)
{
$column = $dimension->getColumnName();
$componentName = $componentPrefix . $column;
@@ -157,11 +155,7 @@ class Updater extends \Piwik\Updates
}
if (array_key_exists($column, $existingColumnsInDb)) {
- if ($dimension instanceof VisitDimension) {
- $sqlUpdates = $dimension->update($conversionColumns);
- } else {
- $sqlUpdates = $dimension->update();
- }
+ $sqlUpdates = $dimension->update();
} else {
$sqlUpdates = $dimension->install();
}
@@ -218,7 +212,8 @@ class Updater extends \Piwik\Updates
}
/**
- * @param ActionDimension|ConversionDimension|VisitDimension $dimension
+ * @param PiwikUpdater $updater
+ * @param Dimension $dimension
* @param string $componentPrefix
* @param array $columns
* @param array $versions
diff --git a/core/DataAccess/LogAggregator.php b/core/DataAccess/LogAggregator.php
index c30132f41e..ede9f81526 100644
--- a/core/DataAccess/LogAggregator.php
+++ b/core/DataAccess/LogAggregator.php
@@ -164,6 +164,11 @@ class LogAggregator
$this->logger = $logger ?: StaticContainer::get('Psr\Log\LoggerInterface');
}
+ public function getSegment()
+ {
+ return $this->segment;
+ }
+
public function setQueryOriginHint($nameOfOrigiin)
{
$this->queryOriginHint = $nameOfOrigiin;
@@ -515,7 +520,7 @@ class LogAggregator
*
* @return array
*/
- protected function getGeneralQueryBindParams()
+ public function getGeneralQueryBindParams()
{
$bind = array($this->dateStart->toString(Date::DATE_TIME_FORMAT), $this->dateEnd->toString(Date::DATE_TIME_FORMAT));
$bind = array_merge($bind, $this->sites);
diff --git a/core/DataAccess/LogQueryBuilder.php b/core/DataAccess/LogQueryBuilder.php
index 186ed0d96e..7989b72e80 100644
--- a/core/DataAccess/LogQueryBuilder.php
+++ b/core/DataAccess/LogQueryBuilder.php
@@ -22,11 +22,26 @@ class LogQueryBuilder
*/
private $logTableProvider;
+ /**
+ * Forces to use a subselect when generating the query. Set value to `false` to force not using a subselect.
+ * @var string
+ */
+ private $forcedInnerGroupBy = '';
+
public function __construct(LogTablesProvider $logTablesProvider)
{
$this->logTableProvider = $logTablesProvider;
}
+ /**
+ * Forces to use a subselect when generating the query.
+ * @var string
+ */
+ public function forceInnerGroupBySubselect($innerGroupBy)
+ {
+ $this->forcedInnerGroupBy = $innerGroupBy;
+ }
+
public function getSelectQueryString(SegmentExpression $segmentExpression, $select, $from, $where, $bind, $groupBy,
$orderBy, $limitAndOffset)
{
@@ -55,11 +70,13 @@ class LogQueryBuilder
&& $fromInitially == array('log_conversion')
&& strpos($from, 'log_link_visit_action') !== false);
- if ($useSpecialConversionGroupBy) {
+ if (!empty($this->forcedInnerGroupBy)) {
+ $sql = $this->buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset, $tables, $this->forcedInnerGroupBy);
+ } elseif ($useSpecialConversionGroupBy) {
$innerGroupBy = "CONCAT(log_conversion.idvisit, '_' , log_conversion.idgoal, '_', log_conversion.buster)";
- $sql = $this->buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset, $innerGroupBy);
+ $sql = $this->buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset, $tables, $innerGroupBy);
} elseif ($joinWithSubSelect) {
- $sql = $this->buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset);
+ $sql = $this->buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset, $tables);
} else {
$sql = $this->buildSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset);
}
@@ -91,9 +108,20 @@ class LogQueryBuilder
* @throws Exception
* @return string
*/
- private function buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset, $innerGroupBy = null)
+ private function buildWrappedSelectQuery($select, $from, $where, $groupBy, $orderBy, $limitAndOffset, JoinTables $tables, $innerGroupBy = null)
{
- $matchTables = '(' . implode('|', $this->getKnownTables()) . ')';
+ $matchTables = $this->getKnownTables();
+ foreach ($tables as $table) {
+ if (is_array($table) && isset($table['tableAlias']) && !in_array($table['tableAlias'], $matchTables, $strict = true)) {
+ $matchTables[] = $table['tableAlias'];
+ } elseif (is_array($table) && isset($table['table']) && !in_array($table['table'], $matchTables, $strict = true)) {
+ $matchTables[] = $table['table'];
+ } elseif (is_string($table) && !in_array($table, $matchTables, $strict = true)) {
+ $matchTables[] = $table;
+ }
+ }
+
+ $matchTables = '(' . implode('|', $matchTables) . ')';
preg_match_all("/". $matchTables ."\.[a-z0-9_\*]+/", $select, $matches);
$neededFields = array_unique($matches[0]);
@@ -102,6 +130,30 @@ class LogQueryBuilder
. "Please use a table prefix.");
}
+ $fieldNames = array();
+ $toBeReplaced = array();
+ $epregReplace = array();
+ foreach ($neededFields as &$neededField) {
+ $parts = explode('.', $neededField);
+ if (count($parts) === 2 && !empty($parts[1])) {
+ if (in_array($parts[1], $fieldNames, $strict = true)) {
+ // eg when selecting 2 dimensions log_action_X.name
+ $columnAs = $parts[1] . md5($neededField);
+ $fieldNames[] = $columnAs;
+ // we make sure to not replace a idvisitor column when duplicate column is idvisit
+ $toBeReplaced[$neededField . ' '] = $parts[0] . '.' . $columnAs . ' ';
+ $toBeReplaced[$neededField . ')'] = $parts[0] . '.' . $columnAs . ')';
+ $toBeReplaced[$neededField . '`'] = $parts[0] . '.' . $columnAs . '`';
+ $toBeReplaced[$neededField . ','] = $parts[0] . '.' . $columnAs . ',';
+ // replace when string ends this, we need to use regex to check for this
+ $epregReplace["/(" . $neededField . ")$/"] = $parts[0] . '.' . $columnAs;
+ $neededField .= ' as ' . $columnAs;
+ } else {
+ $fieldNames[] = $parts[1];
+ }
+ }
+ }
+
preg_match_all("/". $matchTables . "/", $from, $matchesFrom);
$innerSelect = implode(", \n", $neededFields);
@@ -110,12 +162,6 @@ class LogQueryBuilder
$innerLimitAndOffset = $limitAndOffset;
- if (!isset($innerGroupBy) && in_array('log_visit', $matchesFrom[1])) {
- $innerGroupBy = "log_visit.idvisit";
- } elseif (!isset($innerGroupBy)) {
- throw new Exception('Cannot use subselect for join as no group by rule is specified');
- }
-
$innerOrderBy = "NULL";
if ($innerLimitAndOffset && $orderBy) {
// only When LIMITing we can apply to the inner query the same ORDER BY as the parent query
@@ -126,9 +172,29 @@ class LogQueryBuilder
$innerGroupBy = false;
}
+ if (!isset($innerGroupBy) && in_array('log_visit', $matchesFrom[1])) {
+ $innerGroupBy = "log_visit.idvisit";
+ } elseif (!isset($innerGroupBy)) {
+ throw new Exception('Cannot use subselect for join as no group by rule is specified');
+ }
+
+ if (!empty($toBeReplaced)) {
+ $select = preg_replace(array_keys($epregReplace), array_values($epregReplace), $select);
+ $select = str_replace(array_keys($toBeReplaced), array_values($toBeReplaced), $select);
+ if (!empty($groupBy)) {
+ $groupBy = preg_replace(array_keys($epregReplace), array_values($epregReplace), $groupBy);
+ $groupBy = str_replace(array_keys($toBeReplaced), array_values($toBeReplaced), $groupBy);
+ }
+ if (!empty($orderBy)) {
+ $orderBy = preg_replace(array_keys($epregReplace), array_values($epregReplace), $orderBy);
+ $orderBy = str_replace(array_keys($toBeReplaced), array_values($toBeReplaced), $orderBy);
+ }
+ }
+
$innerQuery = $this->buildSelectQuery($innerSelect, $innerFrom, $innerWhere, $innerGroupBy, $innerOrderBy, $innerLimitAndOffset);
$select = preg_replace('/'.$matchTables.'\./', 'log_inner.', $select);
+
$from = "
(
$innerQuery
diff --git a/core/DataAccess/LogQueryBuilder/JoinGenerator.php b/core/DataAccess/LogQueryBuilder/JoinGenerator.php
index a7f8b5499f..82cffc6b65 100644
--- a/core/DataAccess/LogQueryBuilder/JoinGenerator.php
+++ b/core/DataAccess/LogQueryBuilder/JoinGenerator.php
@@ -120,6 +120,11 @@ class JoinGenerator
$this->joinString .= ' LEFT JOIN';
}
+ if (!isset($table['joinOn']) && $this->tables->getLogTable($table['table']) && !empty($availableLogTables)) {
+ $logTable = $this->tables->getLogTable($table['table']);
+ $table['joinOn'] = $this->findJoinCriteriasForTables($logTable, $availableLogTables);
+ }
+
$this->joinString .= ' ' . Common::prefixTable($table['table']) . " AS " . $alias
. " ON " . $table['joinOn'];
continue;
@@ -166,7 +171,7 @@ class JoinGenerator
* to be joined
* @throws Exception if table cannot be joined for segmentation
*/
- protected function findJoinCriteriasForTables(LogTable $logTable, $availableLogTables)
+ public function findJoinCriteriasForTables(LogTable $logTable, $availableLogTables)
{
$join = null;
$alternativeJoin = null;
@@ -278,10 +283,16 @@ class JoinGenerator
}
if (is_array($tA)) {
+ if (isset($tA['joinOn']) && is_string($tA['joinOn']) && strpos($tA['joinOn'] . '.', $tB) === 0) {
+ return 1; // tA requires tB so needs to be listed before
+ }
return -1;
}
if (is_array($tB)) {
+ if (isset($tB['joinOn']) && is_string($tB['joinOn']) && strpos($tB['joinOn'] . '.', $tA) === 0) {
+ return -1; // tB requires tA so needs to be listed before
+ }
return 1;
}
diff --git a/core/DataAccess/LogQueryBuilder/JoinTables.php b/core/DataAccess/LogQueryBuilder/JoinTables.php
index c773750155..c05a333d1d 100644
--- a/core/DataAccess/LogQueryBuilder/JoinTables.php
+++ b/core/DataAccess/LogQueryBuilder/JoinTables.php
@@ -48,7 +48,22 @@ class JoinTables extends \ArrayObject
public function hasJoinedTable($tableName)
{
- return in_array($tableName, $this->getTables());
+ $tables = in_array($tableName, $this->getTables());
+ if ($tables) {
+ return true;
+ }
+
+ foreach ($this as $table) {
+ if (is_array($table)) {
+ if (!isset($table['tableAlias']) && $table['table'] === $table) {
+ return true;
+ } elseif (isset($table['tableAlias']) && $table['tableAlias'] === $table) {
+ return true;
+ }
+ }
+ }
+
+ return false;
}
public function hasJoinedTableManually($tableToFind, $joinToFind)
diff --git a/core/Metrics/Formatter.php b/core/Metrics/Formatter.php
index 9ce179d769..6608b802e3 100644
--- a/core/Metrics/Formatter.php
+++ b/core/Metrics/Formatter.php
@@ -11,6 +11,7 @@ use Piwik\Common;
use Piwik\DataTable;
use Piwik\NumberFormatter;
use Piwik\Piwik;
+use Piwik\Plugin\ArchivedMetric;
use Piwik\Plugin\Metric;
use Piwik\Plugin\ProcessedMetric;
use Piwik\Plugin\Report;
@@ -214,7 +215,8 @@ class Formatter
if ($metricsToFormat !== null) {
$metricMatchRegex = $this->makeRegexToMatchMetrics($metricsToFormat);
- $metrics = array_filter($metrics, function (ProcessedMetric $metric) use ($metricMatchRegex) {
+ $metrics = array_filter($metrics, function ($metric) use ($metricMatchRegex) {
+ /** @var ProcessedMetric|ArchivedMetric $metric */
return preg_match($metricMatchRegex, $metric->getName());
});
}
diff --git a/core/Plugin/ArchivedMetric.php b/core/Plugin/ArchivedMetric.php
new file mode 100644
index 0000000000..05fa3d8665
--- /dev/null
+++ b/core/Plugin/ArchivedMetric.php
@@ -0,0 +1,207 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+namespace Piwik\Plugin;
+
+use Piwik\Archive\DataTableFactory;
+use Piwik\Columns\Dimension;
+use Piwik\Common;
+use Piwik\DataTable;
+use Piwik\DataTable\Row;
+use Piwik\Metrics\Formatter;
+use Piwik\Piwik;
+
+class ArchivedMetric extends Metric
+{
+ const AGGREGATION_COUNT = 'count(%s)';
+ const AGGREGATION_COUNT_PREFIX = 'nb_';
+ const AGGREGATION_SUM = 'sum(%s)';
+ const AGGREGATION_SUM_PREFIX = 'sum_';
+ const AGGREGATION_MAX = 'max(%s)';
+ const AGGREGATION_MAX_PREFIX = 'max_';
+ const AGGREGATION_MIN = 'min(%s)';
+ const AGGREGATION_MIN_PREFIX = 'min_';
+ const AGGREGATION_UNIQUE = 'count(distinct %s)';
+ const AGGREGATION_UNIQUE_PREFIX = 'nb_uniq_';
+ const AGGREGATION_COUNT_WITH_NUMERIC_VALUE = 'sum(if(%s > 0, 1, 0))';
+ const AGGREGATION_COUNT_WITH_NUMERIC_VALUE_PREFIX = 'nb_with_';
+
+ /**
+ * @var string
+ */
+ private $aggregation;
+
+ /**
+ * @var int
+ */
+ protected $idSite;
+
+ private $name = '';
+ private $type = '';
+ private $translatedName = '';
+ private $documentation = '';
+ private $dbTable = '';
+ private $category = '';
+ private $query = '';
+
+ /**
+ * @var Dimension
+ */
+ private $dimension;
+
+ public function __construct(Dimension $dimension, $aggregation = false)
+ {
+ if (!empty($aggregation) && strpos($aggregation, '%s') === false) {
+ throw new \Exception(sprintf('The given aggregation for %s.%s needs to include a %%s for the column name', $dimension->getDbTableName(), $dimension->getColumnName()));
+ }
+
+ $this->setDimension($dimension);
+ $this->setDbTable($dimension->getDbTableName());
+ $this->aggregation = $aggregation;
+ }
+
+ public function setDimension($dimension)
+ {
+ $this->dimension = $dimension;
+ return $this;
+ }
+
+ public function getDimension()
+ {
+ return $this->dimension;
+ }
+
+ public function setCategory($category)
+ {
+ $this->category = $category;
+ return $this;
+ }
+
+ public function getCategoryId()
+ {
+ return $this->category;
+ }
+
+ public function setDbTable($dbTable)
+ {
+ $this->dbTable = $dbTable;
+ return $this;
+ }
+
+ public function setDocumentation($documentation)
+ {
+ $this->documentation = $documentation;
+ return $this;
+ }
+
+ public function setTranslatedName($name)
+ {
+ $this->translatedName = $name;
+ return $this;
+ }
+
+ public function setType($type)
+ {
+ $this->type = $type;
+ return $this;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ return $this;
+ }
+
+ public function setQuery($query)
+ {
+ $this->query = $query;
+ return $this;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function format($value, Formatter $formatter)
+ {
+ switch ($this->type) {
+ case Dimension::TYPE_BOOL:
+ return $formatter->getPrettyNumber($value);
+ case Dimension::TYPE_ENUM:
+ return $formatter->getPrettyNumber($value);
+ case Dimension::TYPE_MONEY:
+ return $formatter->getPrettyMoney($value, $this->idSite);
+ case Dimension::TYPE_FLOAT:
+ return $formatter->getPrettyNumber((float) $value, $precision = 2);
+ case Dimension::TYPE_NUMBER:
+ return $formatter->getPrettyNumber($value);
+ case Dimension::TYPE_DURATION_S:
+ return $formatter->getPrettyTimeFromSeconds($value, $displayAsSentence = true);
+ case Dimension::TYPE_DURATION_MS:
+ $val = number_format($value / 1000, 2);
+ if ($val > 60) {
+ $val = round($val);
+ }
+ return $formatter->getPrettyTimeFromSeconds($val, $displayAsSentence = true);
+ case Dimension::TYPE_PERCENT:
+ return $formatter->getPrettyPercentFromQuotient($value);
+ case Dimension::TYPE_BYTE:
+ return $formatter->getPrettySizeFromBytes($value);
+ }
+
+ return $value;
+ }
+
+ public function getTranslatedName()
+ {
+ if (!empty($this->translatedName)) {
+ return Piwik::translate($this->translatedName);
+ }
+
+ return $this->translatedName;
+ }
+
+ public function getDependentMetrics()
+ {
+ return array($this->getName());
+ }
+
+ public function getDocumentation()
+ {
+ return $this->documentation;
+ }
+
+ public function getDbTableName()
+ {
+ return $this->dbTable;
+ }
+
+ public function getQuery()
+ {
+ if ($this->query) {
+ return $this->query;
+ }
+
+ $column = $this->dbTable . '.' . $this->dimension->getColumnName();
+
+ if (!empty($this->aggregation)) {
+ return sprintf($this->aggregation, $column);
+ }
+
+ return $column;
+ }
+
+ public function beforeFormat($report, DataTable $table)
+ {
+ $this->idSite = DataTableFactory::getSiteIdFromMetadata($table);
+ if (empty($this->idSite)) {
+ $this->idSite = Common::getRequestVar('idSite', 0, 'int');
+ }
+ return !empty($this->idSite); // skip formatting if there is no site to get currency info from
+ }
+}
diff --git a/core/Plugin/ComputedMetric.php b/core/Plugin/ComputedMetric.php
new file mode 100644
index 0000000000..7825fedde3
--- /dev/null
+++ b/core/Plugin/ComputedMetric.php
@@ -0,0 +1,260 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+namespace Piwik\Plugin;
+
+use Piwik\Archive\DataTableFactory;
+use Piwik\Columns\Dimension;
+use Piwik\Columns\MetricsList;
+use Piwik\Common;
+use Piwik\DataTable;
+use Piwik\DataTable\Row;
+use Piwik\Metrics\Formatter;
+use Piwik\Piwik;
+
+class ComputedMetric extends ProcessedMetric
+{
+ const AGGREGATION_AVG = 'avg';
+ const AGGREGATION_RATE = 'rate';
+
+ /**
+ * @var string
+ */
+ private $aggregation;
+
+ /**
+ * @var int
+ */
+ protected $idSite;
+
+ private $name = '';
+ private $type = '';
+ private $translatedName = '';
+ private $documentation = '';
+ private $metric1 = '';
+ private $metric2 = '';
+ private $category = '';
+
+ private $metricsList;
+
+ public function __construct($metric1, $metric2, $aggregation = 'avg')
+ {
+ $nameShort1 = str_replace(array('nb_'), '', $metric1);
+ $nameShort2 = str_replace(array('nb_'), '', $metric2);
+
+ if ($aggregation === ComputedMetric::AGGREGATION_AVG) {
+ $this->name = 'avg_' . $nameShort1 . '_per_' . $nameShort2;
+ } elseif ($aggregation === ComputedMetric::AGGREGATION_RATE) {
+ $this->name = $nameShort1 . '_' . $nameShort2 . '_rate';
+ } else {
+ throw new \Exception('Not supported aggregation type');
+ }
+
+ $this->setMetric1($metric1);
+ $this->setMetric2($metric2);
+ $this->aggregation = $aggregation;
+ }
+
+ public function getDependentMetrics()
+ {
+ return array($this->metric1, $this->metric2);
+ }
+
+ public function setCategory($category)
+ {
+ $this->category = $category;
+ return $this;
+ }
+
+ public function getCategoryId()
+ {
+ return $this->category;
+ }
+
+ public function setMetric1($metric1)
+ {
+ $this->metric1 = $metric1;
+ return $this;
+ }
+
+ public function setMetric2($metric2)
+ {
+ $this->metric2 = $metric2;
+ return $this;
+ }
+
+ public function setDocumentation($documentation)
+ {
+ $this->documentation = $documentation;
+ return $this;
+ }
+
+ public function setTranslatedName($name)
+ {
+ $this->translatedName = $name;
+ return $this;
+ }
+
+ public function setType($type)
+ {
+ $this->type = $type;
+ return $this;
+ }
+
+ public function setName($name)
+ {
+ $this->name = $name;
+ return $this;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function compute(Row $row)
+ {
+ $metric1 = $this->getMetric($row, $this->metric1);
+ $metric2 = $this->getMetric($row, $this->metric2);
+
+ return Piwik::getQuotientSafe($metric1, $metric2, $precision = 2);
+ }
+
+ private function getDetectedType()
+ {
+ if (!$this->type) {
+ if ($this->aggregation === self::AGGREGATION_RATE) {
+ $this->type = Dimension::TYPE_PERCENT;
+ } else {
+ $this->type = Dimension::TYPE_NUMBER; // default to number
+ $metric1 = $this->getMetricsList()->getMetric($this->metric1);
+ if ($metric1) {
+ $dimension = $metric1->getDimension();
+ if ($dimension) {
+ $this->type = $dimension->getType();
+ }
+ }
+ }
+ }
+
+ return $this->type;
+ }
+
+ public function format($value, Formatter $formatter)
+ {
+ if ($this->aggregation === self::AGGREGATION_RATE) {
+ return $formatter->getPrettyPercentFromQuotient($value);
+ }
+
+ $type = $this->getDetectedType();
+
+ switch ($type) {
+ case Dimension::TYPE_MONEY:
+ return $formatter->getPrettyMoney($value, $this->idSite);
+ case Dimension::TYPE_FLOAT:
+ return $formatter->getPrettyNumber($value, $precision = 2);
+ case Dimension::TYPE_NUMBER:
+ return $formatter->getPrettyNumber($value, 1); // we still need to round to have somewhat more accurate result
+ case Dimension::TYPE_DURATION_S:
+ return $formatter->getPrettyTimeFromSeconds(round($value), $displayAsSentence = true);
+ case Dimension::TYPE_DURATION_MS:
+ $val = number_format($value / 1000, 2);
+ if ($val > 60) {
+ $val = round($val);
+ }
+ return $formatter->getPrettyTimeFromSeconds($val, $displayAsSentence = true);
+ case Dimension::TYPE_PERCENT:
+ return $formatter->getPrettyPercentFromQuotient($value);
+ case Dimension::TYPE_BYTE:
+ return $formatter->getPrettySizeFromBytes($value);
+ }
+
+ return $value;
+ }
+
+ public function getTranslatedName()
+ {
+ if (!$this->translatedName) {
+ $metric = $this->getMetricsList();
+ $metric1 = $metric->getMetric($this->metric1);
+ $metric2 = $metric->getMetric($this->metric2);
+
+ if ($this->aggregation === self::AGGREGATION_AVG) {
+ if ($metric1 && $metric1 instanceof ArchivedMetric && $metric2 && $metric2 instanceof ArchivedMetric) {
+
+ $metric1Name = $metric1->getDimension()->getName();
+ $metric2Name = $metric2->getDimension()->getName();
+ return Piwik::translate('General_ComputedMetricAverage', array($metric1Name, $metric2Name));
+ }
+
+ if ($metric1 && $metric1 instanceof ArchivedMetric) {
+ $metric1Name = $metric1->getDimension()->getName();
+ return Piwik::translate('General_AverageX', array($metric1Name));
+ }
+
+ if ($metric1 && $metric2) {
+ return $metric1->getTranslatedName() . ' per ' . $metric2->getTranslatedName();
+ }
+
+ return $this->metric1 . ' per ' . $this->metric2;
+ } else if ($this->aggregation === self::AGGREGATION_RATE) {
+ if ($metric1 && $metric1 instanceof ArchivedMetric) {
+ return Piwik::translate('General_ComputedMetricRate', array($metric1->getTranslatedName()));
+ } else {
+ return Piwik::translate('General_ComputedMetricRate', array($this->metric1));
+ }
+ }
+ }
+ return $this->translatedName;
+ }
+
+ public function getDocumentation()
+ {
+ if (!$this->documentation) {
+ $metric = $this->getMetricsList();
+ $metric1 = $metric->getMetric($this->metric1);
+ $metric2 = $metric->getMetric($this->metric2);
+
+ if ($this->aggregation === self::AGGREGATION_AVG) {
+ if ($metric1 && $metric1 instanceof ArchivedMetric && $metric2 && $metric2 instanceof ArchivedMetric) {
+ return Piwik::translate('General_ComputedMetricAverageDocumentation', array($metric1->getDimension()->getName(), $metric2->getTranslatedName()));
+ }
+
+ if ($metric1 && $metric1 instanceof ArchivedMetric) {
+ return Piwik::translate('General_ComputedMetricAverageShortDocumentation', array($metric1->getDimension()->getName()));
+ }
+
+ return Piwik::translate('General_ComputedMetricAverageDocumentation', array($this->metric1, $this->metric2));
+
+ } else if ($this->aggregation === self::AGGREGATION_RATE) {
+ if ($metric1 && $metric1 instanceof ArchivedMetric) {
+ return Piwik::translate('General_ComputedMetricRateDocumentation', array($metric1->getDimension()->getNamePlural(), $metric2->getDimension()->getNamePlural()));
+ } else {
+ return Piwik::translate('General_ComputedMetricRateShortDocumentation', array($this->metric1));
+ }
+ }
+ }
+ return $this->documentation;
+ }
+
+ private function getMetricsList()
+ {
+ if (!$this->metricsList) {
+ $this->metricsList = MetricsList::get();
+ }
+ return $this->metricsList;
+ }
+
+ public function beforeFormat($report, DataTable $table)
+ {
+ $this->idSite = DataTableFactory::getSiteIdFromMetadata($table);
+ if (empty($this->idSite)) {
+ $this->idSite = Common::getRequestVar('idSite', 0, 'int');
+ }
+ return !empty($this->idSite); // skip formatting if there is no site to get currency info from
+ }
+}
diff --git a/core/Plugin/Dimension/ActionDimension.php b/core/Plugin/Dimension/ActionDimension.php
index ce8a1eeda2..4ed4867448 100644
--- a/core/Plugin/Dimension/ActionDimension.php
+++ b/core/Plugin/Dimension/ActionDimension.php
@@ -12,10 +12,7 @@ use Piwik\CacheId;
use Piwik\Cache as PiwikCache;
use Piwik\Columns\Dimension;
use Piwik\Plugin\Manager as PluginManager;
-use Piwik\Plugin\Segment;
-use Piwik\Common;
use Piwik\Plugin;
-use Piwik\Db;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
@@ -38,111 +35,8 @@ abstract class ActionDimension extends Dimension
{
const INSTALLER_PREFIX = 'log_link_visit_action.';
- private $tableName = 'log_link_visit_action';
-
- /**
- * Installs the action dimension in case it is not installed yet. The installation is already implemented based on
- * the {@link $columnName} and {@link $columnType}. If you want to perform additional actions beside adding the
- * column to the database - for instance adding an index - you can overwrite this method. We recommend to call
- * this parent method to get the minimum required actions and then add further custom actions since this makes sure
- * the column will be installed correctly. We also recommend to change the default install behavior only if really
- * needed. FYI: We do not directly execute those alter table statements here as we group them together with several
- * other alter table statements do execute those changes in one step which results in a faster installation. The
- * column will be added to the `log_link_visit_action` MySQL table.
- *
- * Example:
- * ```
- public function install()
- {
- $changes = parent::install();
- $changes['log_link_visit_action'][] = "ADD INDEX index_idsite_servertime ( idsite, server_time )";
-
- return $changes;
- }
- ```
- *
- * @return array An array containing the table name as key and an array of MySQL alter table statements that should
- * be executed on the given table. Example:
- * ```
- array(
- 'log_link_visit_action' => array("ADD COLUMN `$this->columnName` $this->columnType", "ADD INDEX ...")
- );
- ```
- * @api
- */
- public function install()
- {
- if (empty($this->columnName) || empty($this->columnType)) {
- return array();
- }
-
- return array(
- $this->tableName => array("ADD COLUMN `$this->columnName` $this->columnType")
- );
- }
-
- /**
- * Updates the action dimension in case the {@link $columnType} has changed. The update is already implemented based
- * on the {@link $columnName} and {@link $columnType}. This method is intended not to overwritten by plugin
- * developers as it is only supposed to make sure the column has the correct type. Adding additional custom "alter
- * table" actions would not really work since they would be executed with every {@link $columnType} change. So
- * adding an index here would be executed whenever the columnType changes resulting in an error if the index already
- * exists. If an index needs to be added after the first version is released a plugin update class should be
- * created since this makes sure it is only executed once.
- *
- * @return array An array containing the table name as key and an array of MySQL alter table statements that should
- * be executed on the given table. Example:
- * ```
- array(
- 'log_link_visit_action' => array("MODIFY COLUMN `$this->columnName` $this->columnType", "DROP COLUMN ...")
- );
- ```
- * @ignore
- */
- public function update()
- {
- if (empty($this->columnName) || empty($this->columnType)) {
- return array();
- }
-
- return array(
- $this->tableName => array("MODIFY COLUMN `$this->columnName` $this->columnType")
- );
- }
-
- /**
- * Uninstalls the dimension if a {@link $columnName} and {@link columnType} is set. In case you perform any custom
- * actions during {@link install()} - for instance adding an index - you should make sure to undo those actions by
- * overwriting this method. Make sure to call this parent method to make sure the uninstallation of the column
- * will be done.
- * @throws Exception
- * @api
- */
- public function uninstall()
- {
- if (empty($this->columnName) || empty($this->columnType)) {
- return;
- }
-
- try {
- $sql = "ALTER TABLE `" . Common::prefixTable($this->tableName) . "` DROP COLUMN `$this->columnName`";
- Db::exec($sql);
- } catch (Exception $e) {
- if (!Db::get()->isErrNo($e, '1091')) {
- throw $e;
- }
- }
- }
-
- /**
- * Get the version of the dimension which is used for update checks.
- * @return string
- * @ignore
- */
- public function getVersion()
- {
- return $this->columnType;
- }
+ protected $dbTableName = 'log_link_visit_action';
+ protected $category = 'General_Actions';
/**
* If the value you want to save for your dimension is something like a page title or page url, you usually do not
@@ -192,23 +86,6 @@ abstract class ActionDimension extends Dimension
}
/**
- * Adds a new segment. It automatically sets the SQL segment depending on the column name in case none is set
- * already.
- * @see \Piwik\Columns\Dimension::addSegment()
- * @param Segment $segment
- * @api
- */
- protected function addSegment(Segment $segment)
- {
- $sqlSegment = $segment->getSqlSegment();
- if (!empty($this->columnName) && empty($sqlSegment)) {
- $segment->setSqlSegment($this->tableName . '.' . $this->columnName);
- }
-
- parent::addSegment($segment);
- }
-
- /**
* Get all action dimensions that are defined by all activated plugins.
* @return ActionDimension[]
* @ignore
diff --git a/core/Plugin/Dimension/ConversionDimension.php b/core/Plugin/Dimension/ConversionDimension.php
index ff1bff55ae..14a242d24f 100644
--- a/core/Plugin/Dimension/ConversionDimension.php
+++ b/core/Plugin/Dimension/ConversionDimension.php
@@ -12,15 +12,11 @@ use Piwik\CacheId;
use Piwik\Cache as PiwikCache;
use Piwik\Columns\Dimension;
use Piwik\Plugin\Manager as PluginManager;
-use Piwik\Common;
-use Piwik\Db;
use Piwik\Tracker\Action;
use Piwik\Tracker\GoalManager;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
-use Piwik\Plugin\Segment;
use Piwik\Plugin;
-use Exception;
/**
* Defines a new conversion dimension that records any visit related information during tracking.
@@ -40,116 +36,8 @@ abstract class ConversionDimension extends Dimension
{
const INSTALLER_PREFIX = 'log_conversion.';
- private $tableName = 'log_conversion';
-
- /**
- * Installs the conversion dimension in case it is not installed yet. The installation is already implemented based
- * on the {@link $columnName} and {@link $columnType}. If you want to perform additional actions beside adding the
- * column to the database - for instance adding an index - you can overwrite this method. We recommend to call
- * this parent method to get the minimum required actions and then add further custom actions since this makes sure
- * the column will be installed correctly. We also recommend to change the default install behavior only if really
- * needed. FYI: We do not directly execute those alter table statements here as we group them together with several
- * other alter table statements do execute those changes in one step which results in a faster installation. The
- * column will be added to the `log_conversion` MySQL table.
- *
- * Example:
- * ```
- public function install()
- {
- $changes = parent::install();
- $changes['log_conversion'][] = "ADD INDEX index_idsite_servertime ( idsite, server_time )";
-
- return $changes;
- }
- ```
- *
- * @return array An array containing the table name as key and an array of MySQL alter table statements that should
- * be executed on the given table. Example:
- * ```
- array(
- 'log_conversion' => array("ADD COLUMN `$this->columnName` $this->columnType", "ADD INDEX ...")
- );
- ```
- * @api
- */
- public function install()
- {
- if (empty($this->columnName) || empty($this->columnType)) {
- return array();
- }
-
- return array(
- $this->tableName => array("ADD COLUMN `$this->columnName` $this->columnType")
- );
- }
-
- /**
- * @see ActionDimension::update()
- * @return array
- * @ignore
- */
- public function update()
- {
- if (empty($this->columnName) || empty($this->columnType)) {
- return array();
- }
-
- return array(
- $this->tableName => array("MODIFY COLUMN `$this->columnName` $this->columnType")
- );
- }
-
- /**
- * Uninstalls the dimension if a {@link $columnName} and {@link columnType} is set. In case you perform any custom
- * actions during {@link install()} - for instance adding an index - you should make sure to undo those actions by
- * overwriting this method. Make sure to call this parent method to make sure the uninstallation of the column
- * will be done.
- * @throws Exception
- * @api
- */
- public function uninstall()
- {
- if (empty($this->columnName) || empty($this->columnType)) {
- return;
- }
-
- try {
- $sql = "ALTER TABLE `" . Common::prefixTable($this->tableName) . "` DROP COLUMN `$this->columnName`";
- Db::exec($sql);
- } catch (Exception $e) {
- if (!Db::get()->isErrNo($e, '1091')) {
- throw $e;
- }
- }
- }
-
- /**
- * @see ActionDimension::getVersion()
- * @return string
- * @ignore
- */
- public function getVersion()
- {
- return $this->columnType;
- }
-
- /**
- * Adds a new segment. It automatically sets the SQL segment depending on the column name in case none is set
- * already.
- *
- * @see \Piwik\Columns\Dimension::addSegment()
- * @param Segment $segment
- * @api
- */
- protected function addSegment(Segment $segment)
- {
- $sqlSegment = $segment->getSqlSegment();
- if (!empty($this->columnName) && empty($sqlSegment)) {
- $segment->setSqlSegment($this->tableName . '.' . $this->columnName);
- }
-
- parent::addSegment($segment);
- }
+ protected $dbTableName = 'log_conversion';
+ protected $category = 'Goals_Conversion';
/**
* Get all conversion dimensions that are defined by all activated plugins.
diff --git a/core/Plugin/Dimension/VisitDimension.php b/core/Plugin/Dimension/VisitDimension.php
index c59495d2aa..f6e3f19205 100644
--- a/core/Plugin/Dimension/VisitDimension.php
+++ b/core/Plugin/Dimension/VisitDimension.php
@@ -13,12 +13,11 @@ use Piwik\Cache as PiwikCache;
use Piwik\Columns\Dimension;
use Piwik\Common;
use Piwik\Db;
+use Piwik\DbHelper;
use Piwik\Plugin\Manager as PluginManager;
-use Piwik\Plugin\Segment;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
-use Piwik\Tracker;
use Piwik\Plugin;
use Exception;
@@ -39,38 +38,9 @@ abstract class VisitDimension extends Dimension
{
const INSTALLER_PREFIX = 'log_visit.';
- private $tableName = 'log_visit';
+ protected $dbTableName = 'log_visit';
+ protected $category = 'General_Visitors';
- /**
- * Installs the visit dimension in case it is not installed yet. The installation is already implemented based on
- * the {@link $columnName} and {@link $columnType}. If you want to perform additional actions beside adding the
- * column to the database - for instance adding an index - you can overwrite this method. We recommend to call
- * this parent method to get the minimum required actions and then add further custom actions since this makes sure
- * the column will be installed correctly. We also recommend to change the default install behavior only if really
- * needed. FYI: We do not directly execute those alter table statements here as we group them together with several
- * other alter table statements do execute those changes in one step which results in a faster installation. The
- * column will be added to the `log_visit` MySQL table.
- *
- * Example:
- * ```
- public function install()
- {
- $changes = parent::install();
- $changes['log_visit'][] = "ADD INDEX index_idsite_servertime ( idsite, server_time )";
-
- return $changes;
- }
- ```
- *
- * @return array An array containing the table name as key and an array of MySQL alter table statements that should
- * be executed on the given table. Example:
- * ```
- array(
- 'log_visit' => array("ADD COLUMN `$this->columnName` $this->columnType", "ADD INDEX ...")
- );
- ```
- * @api
- */
public function install()
{
if (empty($this->columnType) || empty($this->columnName)) {
@@ -78,7 +48,7 @@ abstract class VisitDimension extends Dimension
}
$changes = array(
- $this->tableName => array("ADD COLUMN `$this->columnName` $this->columnType")
+ $this->dbTableName => array("ADD COLUMN `$this->columnName` $this->columnType")
);
if ($this->isHandlingLogConversion()) {
@@ -90,19 +60,20 @@ abstract class VisitDimension extends Dimension
/**
* @see ActionDimension::update()
- * @param array $conversionColumns An array of currently installed columns in the conversion table.
* @return array
* @ignore
*/
- public function update($conversionColumns)
+ public function update()
{
if (!$this->columnType) {
return array();
}
+ $conversionColumns = DbHelper::getTableColumns(Common::prefixTable('log_conversion'));
+
$changes = array();
- $changes[$this->tableName] = array("MODIFY COLUMN `$this->columnName` $this->columnType");
+ $changes[$this->dbTableName] = array("MODIFY COLUMN `$this->columnName` $this->columnType");
$handlingConversion = $this->isHandlingLogConversion();
$hasConversionColumn = array_key_exists($this->columnName, $conversionColumns);
@@ -119,7 +90,6 @@ abstract class VisitDimension extends Dimension
}
/**
- * @see ActionDimension::getVersion()
* @return string
* @ignore
*/
@@ -152,7 +122,7 @@ abstract class VisitDimension extends Dimension
}
try {
- $sql = "ALTER TABLE `" . Common::prefixTable($this->tableName) . "` DROP COLUMN `$this->columnName`";
+ $sql = "ALTER TABLE `" . Common::prefixTable($this->dbTableName) . "` DROP COLUMN `$this->columnName`";
Db::exec($sql);
} catch (Exception $e) {
if (!Db::get()->isErrNo($e, '1091')) {
@@ -175,23 +145,6 @@ abstract class VisitDimension extends Dimension
}
/**
- * Adds a new segment. It automatically sets the SQL segment depending on the column name in case none is set
- * already.
- * @see \Piwik\Columns\Dimension::addSegment()
- * @param Segment $segment
- * @api
- */
- protected function addSegment(Segment $segment)
- {
- $sqlSegment = $segment->getSqlSegment();
- if (!empty($this->columnName) && empty($sqlSegment)) {
- $segment->setSqlSegment('log_visit.' . $this->columnName);
- }
-
- parent::addSegment($segment);
- }
-
- /**
* Sometimes you may want to make sure another dimension is executed before your dimension so you can persist
* this dimensions' value depending on the value of other dimensions. You can do this by defining an array of
* dimension names. If you access any value of any other column within your events, you should require them here.
diff --git a/core/Plugin/Metric.php b/core/Plugin/Metric.php
index e1bdb78cad..464575b6c0 100644
--- a/core/Plugin/Metric.php
+++ b/core/Plugin/Metric.php
@@ -54,6 +54,16 @@ abstract class Metric
abstract public function getTranslatedName();
/**
+ * Returns the category that this metric belongs to.
+ * @return string
+ * @api since Piwik 3.2.0
+ */
+ public function getCategoryId()
+ {
+ return '';
+ }
+
+ /**
* Returns a string describing what the metric represents. The result will be included in report metadata
* API output, including processed reports.
*
diff --git a/core/Plugin/Report.php b/core/Plugin/Report.php
index 24887fb4c0..c300ad11a3 100644
--- a/core/Plugin/Report.php
+++ b/core/Plugin/Report.php
@@ -410,6 +410,7 @@ class Report
if (empty($restrictToColumns)) {
$restrictToColumns = array_merge($allMetrics, array_keys($this->getProcessedMetrics()));
}
+ $restrictToColumns = array_unique($restrictToColumns);
$processedMetricsById = $this->getProcessedMetricsById();
$metricsSet = array_flip($allMetrics);
@@ -860,7 +861,7 @@ class Report
$result = array();
foreach ($processedMetrics as $processedMetric) {
- if ($processedMetric instanceof ProcessedMetric) { // instanceof check for backwards compatibility
+ if ($processedMetric instanceof ProcessedMetric || $processedMetric instanceof ArchivedMetric) { // instanceof check for backwards compatibility
$result[$processedMetric->getName()] = $processedMetric;
}
}
diff --git a/core/Plugin/Segment.php b/core/Plugin/Segment.php
index 2a20208ea2..494e562d86 100644
--- a/core/Plugin/Segment.php
+++ b/core/Plugin/Segment.php
@@ -203,6 +203,33 @@ class Segment
}
/**
+ * @return string
+ * @ignore
+ */
+ public function getSqlFilterValue()
+ {
+ return $this->sqlFilterValue;
+ }
+
+ /**
+ * @return string
+ * @ignore
+ */
+ public function getAcceptValues()
+ {
+ return $this->acceptValues;
+ }
+
+ /**
+ * @return string
+ * @ignore
+ */
+ public function getSqlFilter()
+ {
+ return $this->sqlFilter;
+ }
+
+ /**
* Set (overwrite) the type of this segment which is usually either a 'dimension' or a 'metric'.
* @param string $type See constansts TYPE_*
* @api
@@ -231,6 +258,15 @@ class Segment
}
/**
+ * @return string
+ * @ignore
+ */
+ public function getCategoryId()
+ {
+ return $this->category;
+ }
+
+ /**
* Returns the name of this segment as it should appear in segment expressions.
*
* @return string
@@ -241,6 +277,15 @@ class Segment
}
/**
+ * @return string
+ * @ignore
+ */
+ public function getSuggestedValuesCallback()
+ {
+ return $this->suggestedValuesCallback;
+ }
+
+ /**
* Set callback which will be executed when user will call for suggested values for segment.
*
* @param callable $suggestedValuesCallback
diff --git a/core/Plugin/Visualization.php b/core/Plugin/Visualization.php
index 6ac18ea67e..6bb2c3a4f8 100644
--- a/core/Plugin/Visualization.php
+++ b/core/Plugin/Visualization.php
@@ -13,6 +13,7 @@ use Piwik\API\DataTablePostProcessor;
use Piwik\API\Proxy;
use Piwik\API\ResponseBuilder;
use Piwik\Common;
+use Piwik\Container\StaticContainer;
use Piwik\DataTable;
use Piwik\Date;
use Piwik\Log;
@@ -279,13 +280,12 @@ class Visualization extends ViewDataTable
$action = $this->requestConfig->getApiMethodToRequest();
$apiParameters = array();
- $idDimension = Common::getRequestVar('idDimension', 0, 'int');
- $idGoal = Common::getRequestVar('idGoal', 0, 'int');
- if ($idDimension > 0) {
- $apiParameters['idDimension'] = $idDimension;
- }
- if ($idGoal > 0) {
- $apiParameters['idGoal'] = $idGoal;
+ $entityNames = StaticContainer::get('entities.idNames');
+ foreach ($entityNames as $entityName) {
+ $idEntity = Common::getRequestVar($entityName, 0, 'int');
+ if ($idEntity > 0) {
+ $apiParameters[$entityName] = $idEntity;
+ }
}
$metadata = ApiApi::getInstance()->getMetadata($idSite, $module, $action, $apiParameters);
diff --git a/core/RankingQuery.php b/core/RankingQuery.php
index cd4f830669..2338cc9ba4 100644
--- a/core/RankingQuery.php
+++ b/core/RankingQuery.php
@@ -136,6 +136,14 @@ class RankingQuery
}
/**
+ * @return array
+ */
+ public function getLabelColumns()
+ {
+ return $this->labelColumns;
+ }
+
+ /**
* Add a column that has be added to the outer queries.
*
* @param $column
diff --git a/core/Segment.php b/core/Segment.php
index aa39083cab..8e3e23cd9a 100644
--- a/core/Segment.php
+++ b/core/Segment.php
@@ -107,6 +107,16 @@ class Segment
}
}
+ /**
+ * Returns the segment expression.
+ * @return SegmentExpression
+ * @api since Piwik 3.2.0
+ */
+ public function getSegmentExpression()
+ {
+ return $this->segmentExpression;
+ }
+
private function getAvailableSegments()
{
// segment metadata
@@ -253,7 +263,7 @@ class Segment
&& $matchType != SegmentExpression::MATCH_IS_NULL_OR_EMPTY) {
if (isset($segment['sqlFilterValue'])) {
- $value = call_user_func($segment['sqlFilterValue'], $value);
+ $value = call_user_func($segment['sqlFilterValue'], $value, $segment['sqlSegment']);
}
// apply presentation filter
diff --git a/core/Segment/SegmentExpression.php b/core/Segment/SegmentExpression.php
index 605cd13682..0428a5ca5f 100644
--- a/core/Segment/SegmentExpression.php
+++ b/core/Segment/SegmentExpression.php
@@ -325,9 +325,23 @@ class SegmentExpression
$table = preg_replace('/^[A-Z_]+\(/', '', $table);
$tableExists = !$table || in_array($table, $availableTables);
- if (!$tableExists) {
- $availableTables[] = $table;
+ if ($tableExists) {
+ return;
}
+
+ if (is_array($availableTables)) {
+ foreach ($availableTables as $availableTable) {
+ if (is_array($availableTable)) {
+ if (!isset($availableTable['tableAlias']) && $availableTable['table'] === $table) {
+ return;
+ } elseif (isset($availableTable['tableAlias']) && $availableTable['tableAlias'] === $table) {
+ return;
+ }
+ }
+ }
+ }
+
+ $availableTables[] = $table;
}
/**
diff --git a/core/Settings/FieldConfig.php b/core/Settings/FieldConfig.php
index 7b2ce1df03..8074db3efb 100644
--- a/core/Settings/FieldConfig.php
+++ b/core/Settings/FieldConfig.php
@@ -63,6 +63,12 @@ class FieldConfig
const UI_CONTROL_SINGLE_SELECT = 'select';
/**
+ * Shows an expandable select field which is useful when each selectable value belongs to a group.
+ * To use this field assign it to the `$uiControl` property.
+ */
+ const UI_CONTROL_SINGLE_EXPANDABLE_SELECT = 'expandable-select';
+
+ /**
* Generates a hidden form field. To use this field assign it to the `$uiControl` property.
*/
const UI_CONTROL_HIDDEN = 'hidden';
diff --git a/core/Tracker/LogTable.php b/core/Tracker/LogTable.php
index 3fedba6945..091258e1e0 100644
--- a/core/Tracker/LogTable.php
+++ b/core/Tracker/LogTable.php
@@ -82,4 +82,15 @@ abstract class LogTable {
return;
}
+ /**
+ * Get the names of the columns that represents the primary key. For example "idvisit" or "idlink_va". If the table
+ * defines the primary key based on multiple columns, you must specify them all
+ * (eg array('idvisit', 'idgoal', 'buster')).
+ *
+ * @return array
+ */
+ public function getPrimaryKey()
+ {
+ return array();
+ }
}
diff --git a/core/ViewDataTable/Config.php b/core/ViewDataTable/Config.php
index 150c3f42d7..2c7a59beff 100644
--- a/core/ViewDataTable/Config.php
+++ b/core/ViewDataTable/Config.php
@@ -11,6 +11,7 @@ namespace Piwik\ViewDataTable;
use Piwik\API\Request as ApiRequest;
use Piwik\Common;
+use Piwik\Container\StaticContainer;
use Piwik\DataTable;
use Piwik\DataTable\Filter\PivotByDimension;
use Piwik\Metrics;
@@ -82,7 +83,7 @@ use Piwik\Plugin\ReportsProvider;
*
* @api
*/
-class Config
+class Config
{
/**
* The list of ViewDataTable properties that are 'Client Side Properties'.
@@ -110,6 +111,7 @@ class Config
'show_related_reports',
'show_limit_control',
'show_search',
+ 'show_export',
'enable_sort',
'show_bar_chart',
'show_pie_chart',
@@ -320,6 +322,13 @@ class Config
public $show_search = true;
/**
+ * Controls whether the export feature under the datatable is shown.
+ *
+ * @api since Piwik 3.2.0
+ */
+ public $show_export = true;
+
+ /**
* Controls whether the user can sort DataTables by clicking on table column headings.
*/
public $enable_sort = true;
@@ -518,13 +527,12 @@ class Config
}
$apiParameters = array();
- $idDimension = Common::getRequestVar('idDimension', 0, 'int');
- $idGoal = Common::getRequestVar('idGoal', 0, 'int');
- if ($idDimension > 0) {
- $apiParameters['idDimension'] = $idDimension;
- }
- if ($idGoal > 0) {
- $apiParameters['idGoal'] = $idGoal;
+ $entityNames = StaticContainer::get('entities.idNames');
+ foreach ($entityNames as $entityName) {
+ $idEntity = Common::getRequestVar($entityName, 0, 'int');
+ if ($idEntity > 0) {
+ $apiParameters[$entityName] = $idEntity;
+ }
}
$report = API::getInstance()->getMetadata($idSite, $this->controllerName, $this->controllerAction, $apiParameters);
diff --git a/lang/en.json b/lang/en.json
index 0be70f5a08..1d1d5a332a 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -20,6 +20,7 @@
"AverageOrderValue": "Average Order Value",
"AveragePrice": "Average Price",
"AverageQuantity": "Average Quantity",
+ "AverageX": "Avg. %s",
"BackToPiwik": "Back to Piwik",
"Broken": "Broken",
"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.",
@@ -37,6 +38,7 @@
"ColumnActionsPerVisit": "Actions per Visit",
"ColumnActionsPerVisitDocumentation": "The average number of actions (page views, site searches, downloads or outlinks) that were performed during the visits.",
"ColumnAverageGenerationTime": "Avg. generation time",
+ "ColumnViewsWithGenerationTime": "Pageviews with generation time",
"ColumnAverageGenerationTimeDocumentation": "The average time it took to generate the page. This metric includes the time it took the server to generate the web page, plus the time it took for the visitor to download the response from the server. A lower 'Avg. generation time' means a faster website for your visitors!",
"ColumnAverageTimeOnPage": "Avg. time on page",
"ColumnAverageTimeOnPageDocumentation": "The average amount of time visitors spent on this page (only the page, not the entire website).",
@@ -56,8 +58,10 @@
"ColumnExits": "Exits",
"ColumnExitsDocumentation": "Number of visits that ended on this page.",
"ColumnGenerationTime": "Generation time",
+ "ColumnPageGenerationTime": "Page Generation time",
"ColumnKeyword": "Keyword",
"ColumnLabel": "Label",
+ "ColumnHits": "Hits",
"ColumnMaxActions": "Maximum actions in one visit",
"ColumnNbActions": "Actions",
"ColumnNbActionsDocumentation": "The number of actions performed by your visitors. Actions can be page views, internal site searches, downloads or outlinks.",
@@ -83,6 +87,23 @@
"ColumnViewedAfterSearchDocumentation": "The number of times this Page was visited after a visitor did a search on your website, and clicked on this page in the search results.",
"ColumnVisitDuration": "Visit Duration (in seconds)",
"ColumnVisitsWithConversions": "Visits with Conversions",
+ "ComputedMetricAverage": "Avg. %1$s per %2$s",
+ "ComputedMetricAverageDocumentation": "Average value of \"%1$s\" per \"%2$s\".",
+ "ComputedMetricAverageShortDocumentation": "Average value of \"%1$s\".",
+ "ComputedMetricRate": "%s Rate",
+ "ComputedMetricRateDocumentation": "The ratio of \"%1$s\" out of all \"%2$s\".",
+ "ComputedMetricRateShortDocumentation": "The percentage of \"%1$s\".",
+ "ComputedMetricCountDocumentation": "The number of %s",
+ "ComputedMetricSum": "Total %s",
+ "ComputedMetricSumDocumentation": "The total number (sum) of %s",
+ "ComputedMetricMax": "Max %s",
+ "ComputedMetricMaxDocumentation": "The maximum value for %s",
+ "ComputedMetricMin": "Min %s",
+ "ComputedMetricMinDocumentation": "The minimum value for %s",
+ "ComputedMetricUniqueCount": "Unique %s",
+ "ComputedMetricUniqueCountDocumentation": "The unique number of %s",
+ "ComputedMetricCountWithValue": "Entries with %s",
+ "ComputedMetricCountWithValueDocumentation": "The number of entries that have a value set for %s",
"ConfigFileIsNotWritable": "The Piwik configuration file %1$s is not writable, some of your changes might not be saved. %2$s Please change permissions of the config file to make it writable.",
"Continue": "Continue",
"ContinueToPiwik": "Continue to Piwik",
@@ -212,6 +233,7 @@
"IP": "IP",
"JsTrackingTag": "JavaScript Tracking Code",
"Language": "Language",
+ "Languages": "Languages",
"LastDays": "Last %s days (including today)",
"LastDaysShort": "Last %s days",
"LearnMore": "%1$slearn more%2$s",
@@ -245,6 +267,7 @@
"NbSearches": "Number of Internal Searches",
"NeedMoreHelp": "Need more help?",
"Never": "Never",
+ "New": "New",
"NewReportsWillBeProcessedByCron": "When Piwik archiving is not triggered by the browser, new reports will be processed by the crontab.",
"NewUpdatePiwikX": "New Update: Piwik %s",
"NewVisitor": "New Visitor",
@@ -387,6 +410,7 @@
"Upload": "Upload",
"UsePlusMinusIconsDocumentation": "Use the plus and minus icons on the left to navigate.",
"UserId": "User ID",
+ "UserIds": "User IDs",
"Username": "Username",
"UseSMTPServerForEmail": "Use SMTP server for e-mail",
"Value": "Value",
@@ -394,6 +418,7 @@
"View": "View",
"ViewDocumentationFor": "View documentation for %1$s",
"Visit": "Visit",
+ "VisitId": "Visit ID",
"VisitConvertedGoal": "Visit converted at least one Goal",
"VisitConvertedGoalId": "Visit converted a specific Goal Id",
"VisitConvertedNGoals": "Visit converted %s Goals",
@@ -401,10 +426,12 @@
"Visitor": "Visitor",
"VisitorID": "Visitor ID",
"VisitorIP": "Visitor IP",
+ "VisitorIPs": "Visitor IPs",
"Visitors": "Visitors",
"VisitsWith": "Visits with %s",
"VisitorSettings": "Visitor Settings",
"VisitType": "Visit type",
+ "VisitTypes": "Visit types",
"VisitTypeExample": "For example, to select all visitors who have returned to the website, including those who have bought something in their previous visits, the API request would contain %s",
"Warning": "Warning",
"Warnings": "Warnings",
diff --git a/plugins/API/API.php b/plugins/API/API.php
index 2100ea825f..4e27e5e663 100644
--- a/plugins/API/API.php
+++ b/plugins/API/API.php
@@ -29,6 +29,7 @@ use Piwik\Plugin\SettingsProvider;
use Piwik\Plugins\API\DataTable\MergeDataTables;
use Piwik\Plugins\CoreAdminHome\CustomLogo;
use Piwik\Plugins\CorePluginsAdmin\SettingsMetadata;
+use Piwik\Site;
use Piwik\Translation\Translator;
use Piwik\Measurable\Type\TypeManager;
use Piwik\Version;
@@ -152,6 +153,7 @@ class API extends \Piwik\Plugin\API
if (empty($idSites)) {
Piwik::checkUserHasSomeViewAccess();
} else {
+ $idSites = Site::getIdSitesFromIdSitesString($idSites);
Piwik::checkUserHasViewAccess($idSites);
}
@@ -433,9 +435,27 @@ class API extends \Piwik\Plugin\API
{
Piwik::checkUserHasViewAccess($idSite);
+ $apiParameters = array();
+ $entityNames = StaticContainer::get('entities.idNames');
+ foreach ($entityNames as $entityName) {
+ if ($entityName === 'idGoal' && $idGoal) {
+ $apiParameters['idGoal'] = $idGoal;
+ } elseif ($entityName === 'idDimension' && $idDimension) {
+ $apiParameters['idDimension'] = $idDimension;
+ } else {
+ // ideally it would get the value from API params but dynamic params is not possible yet in API. If this
+ // method is called eg in Request::processRequest, it could in theory pick up a param from the original request
+ // and not from the API request within the original request.
+ $idEntity = Common::getRequestVar($entityName, 0, 'int');
+ if ($idEntity > 0) {
+ $apiParameters[$entityName] = $idEntity;
+ }
+ }
+ }
+
$rowEvolution = new RowEvolution();
return $rowEvolution->getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label, $segment, $column,
- $language, $idGoal, $legendAppendMetric, $labelUseAbsoluteUrl, $idDimension);
+ $language, $apiParameters, $legendAppendMetric, $labelUseAbsoluteUrl);
}
/**
diff --git a/plugins/API/ProcessedReport.php b/plugins/API/ProcessedReport.php
index 360d6b7ddf..1cd327c8c7 100644
--- a/plugins/API/ProcessedReport.php
+++ b/plugins/API/ProcessedReport.php
@@ -14,6 +14,7 @@ use Piwik\Archive\DataTableFactory;
use Piwik\CacheId;
use Piwik\Cache as PiwikCache;
use Piwik\Common;
+use Piwik\Container\StaticContainer;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\DataTable\Simple;
@@ -48,6 +49,18 @@ class ProcessedReport
{
$reportsMetadata = $this->getReportMetadata($idSite, $period, $date, $hideMetricsDoc, $showSubtableReports);
+ $entityNames = StaticContainer::get('entities.idNames');
+ foreach ($entityNames as $entityName) {
+ if ($entityName === 'idGoal' || $entityName === 'idDimension') {
+ continue; // idGoal and idDimension is passed directly but for other entities we need to "workaround" and
+ // check for eg idFoo from GET/POST because we cannot add parameters to API dynamically
+ }
+ $idEntity = Common::getRequestVar($entityName, 0, 'int');
+ if ($idEntity > 0) {
+ $apiParameters[$entityName] = $idEntity;
+ }
+ }
+
foreach ($reportsMetadata as $report) {
// See ArchiveProcessor/Aggregator.php - unique visitors are not processed for period != day
// todo: should use SettingsPiwik::isUniqueVisitorsEnabled instead
@@ -73,6 +86,7 @@ class ProcessedReport
}
}
}
+
return false;
}
diff --git a/plugins/API/RowEvolution.php b/plugins/API/RowEvolution.php
index f7af6b93b1..41c75bffde 100644
--- a/plugins/API/RowEvolution.php
+++ b/plugins/API/RowEvolution.php
@@ -36,7 +36,7 @@ class RowEvolution
'getPageUrl'
);
- public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true, $idDimension = false)
+ public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $apiParameters = array(), $legendAppendMetric = true, $labelUseAbsoluteUrl = true)
{
// validation of requested $period & $date
if ($period == 'range') {
@@ -51,9 +51,9 @@ class RowEvolution
$label = DataTablePostProcessor::unsanitizeLabelParameter($label);
$labels = Piwik::getArrayFromApiParameter($label);
- $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal, $idDimension);
+ $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $apiParameters);
- $dataTable = $this->loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $idGoal, $idDimension);
+ $dataTable = $this->loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $apiParameters);
if (empty($labels)) {
$labels = $this->getLabelsFromDataTable($dataTable, $labels);
@@ -244,11 +244,11 @@ class RowEvolution
* @param string $apiAction
* @param string|bool $label
* @param string|bool $segment
- * @param int|bool $idGoal
+ * @param array $apiParameters
* @throws Exception
* @return DataTable\Map|DataTable
*/
- private function loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $idGoal = false, $idDimension = false)
+ private function loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $apiParameters)
{
if (!is_array($label)) {
$label = array($label);
@@ -264,9 +264,6 @@ class RowEvolution
'format' => 'original',
'serialize' => '0',
'segment' => $segment,
- 'idGoal' => $idGoal,
- 'idDimension' => $idDimension,
-
// data for row evolution should NOT be limited
'filter_limit' => -1,
@@ -275,6 +272,11 @@ class RowEvolution
// can be sorted in a different order)
'labelFilterAddLabelIndex' => count($label) > 1 ? 1 : 0,
);
+ if (!empty($apiParameters) && is_array($apiParameters)) {
+ foreach ($apiParameters as $param => $value) {
+ $parameters[$param] = $value;
+ }
+ }
// add "processed metrics" like actions per visit or bounce rate
// note: some reports should not be filtered with AddColumnProcessedMetrics
@@ -306,19 +308,12 @@ class RowEvolution
* @param $apiModule
* @param $apiAction
* @param $language
- * @param $idGoal
+ * @param $apiParameters
* @throws Exception
* @return array
*/
- private function getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal = false, $idDimension = false)
+ private function getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $apiParameters)
{
- $apiParameters = array();
- if (!empty($idGoal) && $idGoal > 0) {
- $apiParameters = array('idGoal' => $idGoal);
- }
- if (!empty($idDimension) && $idDimension > 0) {
- $apiParameters = array('idDimension' => (int) $idDimension);
- }
$reportMetadata = API::getInstance()->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language,
$period, $date, $hideMetricsDoc = false, $showSubtableReports = true);
diff --git a/plugins/API/SegmentMetadata.php b/plugins/API/SegmentMetadata.php
index 1cce5fd3dd..03f6f64336 100644
--- a/plugins/API/SegmentMetadata.php
+++ b/plugins/API/SegmentMetadata.php
@@ -8,12 +8,19 @@
*/
namespace Piwik\Plugins\API;
+use Piwik\Category\CategoryList;
use Piwik\Columns\Dimension;
use Piwik\Piwik;
use Piwik\Plugin\Segment;
class SegmentMetadata
{
+ /**
+ * Map of category name to order
+ * @var array
+ */
+ private $categoryOrder = array();
+
public function getSegmentsMetadata($idSites = array(), $_hideImplementationData = true, $isAuthenticatedWithViewAccess)
{
$segments = array();
@@ -55,9 +62,21 @@ class SegmentMetadata
$segments[] = $segment->toArray();
}
+ $categoryList = CategoryList::get();
+
foreach ($segments as &$segment) {
+ $categoryId = $segment['category'];
$segment['name'] = Piwik::translate($segment['name']);
- $segment['category'] = Piwik::translate($segment['category']);
+ $segment['category'] = Piwik::translate($categoryId);
+
+ if (!isset($this->categoryOrder[$segment['category']])) {
+ $category = $categoryList->getCategory($categoryId);
+ if (!empty($category)) {
+ $this->categoryOrder[$segment['category']] = $category->getOrder();
+ } else {
+ $this->categoryOrder[$segment['category']] = 999;
+ }
+ }
if ($_hideImplementationData) {
unset($segment['sqlFilter']);
@@ -81,12 +100,25 @@ class SegmentMetadata
{
$customVarCategory = Piwik::translate('CustomVariables_CustomVariables');
- $columns = array('type', 'category', 'name', 'segment');
+ $columns = array('category', 'type', 'name', 'segment');
foreach ($columns as $column) {
// Keep segments ordered alphabetically inside categories..
$type = -1;
- if ($column == 'name') $type = 1;
+ if ($column == 'name') {
+ $type = 1;
+ }
+
+ if ($column === 'category') {
+ $idOrder1 = $this->categoryOrder[$row1[$column]];
+ $idOrder2 = $this->categoryOrder[$row2[$column]];
+
+ if ($idOrder1 === $idOrder2) {
+ continue;
+ }
+
+ return $idOrder1 > $idOrder2 ? 1 : -1;
+ }
$compare = $type * strcmp($row1[$column], $row2[$column]);
diff --git a/plugins/Actions/Columns/ActionType.php b/plugins/Actions/Columns/ActionType.php
index 59f09c87e9..c407403588 100644
--- a/plugins/Actions/Columns/ActionType.php
+++ b/plugins/Actions/Columns/ActionType.php
@@ -8,9 +8,9 @@
*/
namespace Piwik\Plugins\Actions\Columns;
-use Piwik\Piwik;
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\MetricsList;
use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugins\Actions\Segment;
use Piwik\Tracker\Action;
use Exception;
@@ -30,41 +30,27 @@ class ActionType extends ActionDimension
Action::TYPE_DOWNLOAD => 'downloads'
);
- /**
- * The name of the dimension which will be visible for instance in the UI of a related report and in the mobile app.
- * @return string
- */
- public function getName()
+ protected $columnName = 'type';
+ protected $dbTableName = 'log_action';
+ protected $segmentName = 'actionType';
+ protected $type = self::TYPE_ENUM;
+ protected $nameSingular = 'Actions_ActionType';
+ protected $namePlural = 'Actions_ActionTypes';
+ protected $category = 'General_Actions';
+
+ public function __construct()
{
- return Piwik::translate('Actions_ActionType');
+ $this->acceptValues = sprintf('A type of action, such as: %s', implode(', ', $this->types));
}
- protected function configureSegments()
+ public function getEnumColumnValues()
{
- $types = $this->types;
-
- $segment = new Segment();
- $segment->setSegment('actionType');
- $segment->setName('Actions_ActionType');
- $segment->setSqlSegment('log_action.type');
- $segment->setType(Segment::TYPE_DIMENSION);
- $segment->setAcceptedValues(sprintf('A type of action, such as: %s', implode(', ', $types)));
- $segment->setSqlFilter(function ($type) use ($types) {
- if (array_key_exists($type, $types)) {
- return $type;
- }
-
- $index = array_search(strtolower(trim(urldecode($type))), $types);
-
- if ($index === false) {
- throw new Exception("actionType must be one of: " . implode(', ', $types));
- }
+ return $this->types;
+ }
- return $index;
- });
- $segment->setSuggestedValuesCallback(function ($idSite, $maxSuggestionsToReturn) use ($types) {
- return array_slice(array_values($types), 0, $maxSuggestionsToReturn);
- });
- $this->addSegment($segment);
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
+ {
+ // do not genereate any metric for this
}
+
} \ No newline at end of file
diff --git a/plugins/Actions/Columns/ActionUrl.php b/plugins/Actions/Columns/ActionUrl.php
index 5fa80d1efa..13fdecc033 100644
--- a/plugins/Actions/Columns/ActionUrl.php
+++ b/plugins/Actions/Columns/ActionUrl.php
@@ -14,10 +14,7 @@ use Piwik\Plugins\Actions\Segment;
class ActionUrl extends ActionDimension
{
- public function getName()
- {
- return Piwik::translate('Actions_ColumnActionURL');
- }
+ protected $nameSingular = 'Actions_ColumnActionURL';
protected function configureSegments()
{
diff --git a/plugins/Actions/Columns/ClickedUrl.php b/plugins/Actions/Columns/ClickedUrl.php
index cd9bc3b7dc..861b6d5202 100644
--- a/plugins/Actions/Columns/ClickedUrl.php
+++ b/plugins/Actions/Columns/ClickedUrl.php
@@ -8,24 +8,28 @@
*/
namespace Piwik\Plugins\Actions\Columns;
-use Piwik\Piwik;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugins\Actions\Segment;
+use Piwik\Tracker\Action;
class ClickedUrl extends ActionDimension
{
- public function getName()
+ protected $columnName = 'idaction_url';
+ protected $segmentName = 'outlinkUrl';
+ protected $nameSingular = 'Actions_ColumnClickedURL';
+ protected $namePlural = 'Actions_ColumnClickedURLs';
+ protected $category = 'General_Actions';
+ protected $sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
+ protected $type = self::TYPE_URL;
+
+ public function getDbColumnJoin()
{
- return Piwik::translate('Actions_ColumnClickedURL');
+ return new ActionNameJoin();
}
- protected function configureSegments()
+ public function getDbDiscriminator()
{
- $segment = new Segment();
- $segment->setSegment('outlinkUrl');
- $segment->setName('Actions_ColumnClickedURL');
- $segment->setSqlSegment('log_link_visit_action.idaction_url');
- $this->addSegment($segment);
+ return new Discriminator('log_action', 'type', Action::TYPE_OUTLINK);
}
-
}
diff --git a/plugins/Actions/Columns/DestinationPage.php b/plugins/Actions/Columns/DestinationPage.php
index e47b7195be..4b2b3eb184 100644
--- a/plugins/Actions/Columns/DestinationPage.php
+++ b/plugins/Actions/Columns/DestinationPage.php
@@ -13,8 +13,6 @@ use Piwik\Piwik;
class DestinationPage extends Dimension
{
- public function getName()
- {
- return Piwik::translate('General_ColumnDestinationPage');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'General_ColumnDestinationPage';
} \ No newline at end of file
diff --git a/plugins/Actions/Columns/DownloadUrl.php b/plugins/Actions/Columns/DownloadUrl.php
index d611e86f21..6375bba69b 100644
--- a/plugins/Actions/Columns/DownloadUrl.php
+++ b/plugins/Actions/Columns/DownloadUrl.php
@@ -8,24 +8,28 @@
*/
namespace Piwik\Plugins\Actions\Columns;
-use Piwik\Piwik;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugins\Actions\Segment;
+use Piwik\Tracker\Action;
class DownloadUrl extends ActionDimension
{
- public function getName()
+ protected $segmentName = 'downloadUrl';
+ protected $nameSingular = 'Actions_ColumnDownloadURL';
+ protected $namePlural = 'Actions_ColumnDownloadURLs';
+ protected $columnName = 'idaction_url';
+ protected $category = 'General_Actions';
+ protected $sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
+ protected $type = self::TYPE_URL;
+
+ public function getDbColumnJoin()
{
- return Piwik::translate('Actions_ColumnDownloadURL');
+ return new ActionNameJoin();
}
- protected function configureSegments()
+ public function getDbDiscriminator()
{
- $segment = new Segment();
- $segment->setSegment('downloadUrl');
- $segment->setName('Actions_ColumnDownloadURL');
- $segment->setSqlSegment('log_link_visit_action.idaction_url');
- $this->addSegment($segment);
+ return new Discriminator('log_action', 'type', Action::TYPE_DOWNLOAD);
}
-
}
diff --git a/plugins/Actions/Columns/EntryPageTitle.php b/plugins/Actions/Columns/EntryPageTitle.php
index 30445f9549..5034f72408 100644
--- a/plugins/Actions/Columns/EntryPageTitle.php
+++ b/plugins/Actions/Columns/EntryPageTitle.php
@@ -8,8 +8,8 @@
*/
namespace Piwik\Plugins\Actions\Columns;
-use Piwik\Piwik;
-use Piwik\Plugins\Actions\Segment;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -19,13 +19,21 @@ class EntryPageTitle extends VisitDimension
{
protected $columnName = 'visit_entry_idaction_name';
protected $columnType = 'INTEGER(10) UNSIGNED NULL';
+ protected $type = self::TYPE_TEXT;
+ protected $segmentName = 'entryPageTitle';
+ protected $nameSingular = 'Actions_ColumnEntryPageTitle';
+ protected $namePlural = 'Actions_WidgetEntryPageTitles';
+ protected $category = 'General_Actions';
+ protected $sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
- protected function configureSegments()
+ public function getDbColumnJoin()
{
- $segment = new Segment();
- $segment->setSegment('entryPageTitle');
- $segment->setName('Actions_ColumnEntryPageTitle');
- $this->addSegment($segment);
+ return new ActionNameJoin();
+ }
+
+ public function getDbDiscriminator()
+ {
+ return new Discriminator('log_action', 'type', Action::TYPE_PAGE_TITLE);
}
/**
@@ -44,9 +52,4 @@ class EntryPageTitle extends VisitDimension
return (int) $idActionName;
}
-
- public function getName()
- {
- return Piwik::translate('Actions_ColumnEntryPageTitle');
- }
}
diff --git a/plugins/Actions/Columns/EntryPageUrl.php b/plugins/Actions/Columns/EntryPageUrl.php
index 6b67b9a092..25750024da 100644
--- a/plugins/Actions/Columns/EntryPageUrl.php
+++ b/plugins/Actions/Columns/EntryPageUrl.php
@@ -8,8 +8,8 @@
*/
namespace Piwik\Plugins\Actions\Columns;
-use Piwik\Piwik;
-use Piwik\Plugins\Actions\Segment;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -19,13 +19,21 @@ class EntryPageUrl extends VisitDimension
{
protected $columnName = 'visit_entry_idaction_url';
protected $columnType = 'INTEGER(11) UNSIGNED NULL DEFAULT NULL';
+ protected $segmentName = 'entryPageUrl';
+ protected $nameSingular = 'Actions_ColumnEntryPageURL';
+ protected $namePlural = 'Actions_ColumnEntryPageURLs';
+ protected $category = 'General_Actions';
+ protected $sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
+ protected $type = self::TYPE_URL;
- protected function configureSegments()
+ public function getDbColumnJoin()
{
- $segment = new Segment();
- $segment->setSegment('entryPageUrl');
- $segment->setName('Actions_ColumnEntryPageURL');
- $this->addSegment($segment);
+ return new ActionNameJoin();
+ }
+
+ public function getDbDiscriminator()
+ {
+ return new Discriminator('log_action', 'type', Action::TYPE_PAGE_URL);
}
/**
@@ -69,9 +77,4 @@ class EntryPageUrl extends VisitDimension
return false;
}
- public function getName()
- {
- return Piwik::translate('Actions_ColumnEntryPageURL');
- }
-
}
diff --git a/plugins/Actions/Columns/ExitPageTitle.php b/plugins/Actions/Columns/ExitPageTitle.php
index 97428d9ad9..6207eff5fd 100644
--- a/plugins/Actions/Columns/ExitPageTitle.php
+++ b/plugins/Actions/Columns/ExitPageTitle.php
@@ -8,8 +8,8 @@
*/
namespace Piwik\Plugins\Actions\Columns;
-use Piwik\Piwik;
-use Piwik\Plugins\Actions\Segment;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -19,13 +19,21 @@ class ExitPageTitle extends VisitDimension
{
protected $columnName = 'visit_exit_idaction_name';
protected $columnType = 'INTEGER(10) UNSIGNED NULL';
+ protected $segmentName = 'exitPageTitle';
+ protected $nameSingular = 'Actions_ColumnExitPageTitle';
+ protected $namePlural = 'Actions_WidgetExitPageTitles';
+ protected $category = 'General_Actions';
+ protected $sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
+ protected $type = self::TYPE_TEXT;
- protected function configureSegments()
+ public function getDbColumnJoin()
{
- $segment = new Segment();
- $segment->setSegment('exitPageTitle');
- $segment->setName('Actions_ColumnExitPageTitle');
- $this->addSegment($segment);
+ return new ActionNameJoin();
+ }
+
+ public function getDbDiscriminator()
+ {
+ return new Discriminator('log_action', 'type', Action::TYPE_PAGE_TITLE);
}
/**
@@ -59,9 +67,4 @@ class ExitPageTitle extends VisitDimension
return $action->getIdActionNameForEntryAndExitIds();
}
-
- public function getName()
- {
- return Piwik::translate('Actions_ColumnExitPageTitle');
- }
}
diff --git a/plugins/Actions/Columns/ExitPageUrl.php b/plugins/Actions/Columns/ExitPageUrl.php
index 094719bb75..c9e35e9da4 100644
--- a/plugins/Actions/Columns/ExitPageUrl.php
+++ b/plugins/Actions/Columns/ExitPageUrl.php
@@ -8,8 +8,11 @@
*/
namespace Piwik\Plugins\Actions\Columns;
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join;
+use Piwik\Columns\MetricsList;
use Piwik\Piwik;
-use Piwik\Plugins\Actions\Segment;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -19,13 +22,26 @@ class ExitPageUrl extends VisitDimension
{
protected $columnName = 'visit_exit_idaction_url';
protected $columnType = 'INTEGER(10) UNSIGNED NULL DEFAULT 0';
+ protected $type = self::TYPE_URL;
+ protected $segmentName = 'exitPageUrl';
+ protected $nameSingular = 'Actions_ColumnExitPageURL';
+ protected $namePlural = 'Actions_ColumnExitPageURLs';
+ protected $category = 'General_Actions';
+ protected $sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
- protected function configureSegments()
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
{
- $segment = new Segment();
- $segment->setSegment('exitPageUrl');
- $segment->setName('Actions_ColumnExitPageURL');
- $this->addSegment($segment);
+ parent::configureMetrics($metricsList, $dimensionMetricFactory);
+ }
+
+ public function getDbColumnJoin()
+ {
+ return new Join\ActionNameJoin();
+ }
+
+ public function getDbDiscriminator()
+ {
+ return new Discriminator('log_action', 'type', Action::TYPE_PAGE_URL);
}
/**
@@ -65,9 +81,4 @@ class ExitPageUrl extends VisitDimension
return $id;
}
-
- public function getName()
- {
- return Piwik::translate('Actions_ColumnExitPageURL');
- }
}
diff --git a/plugins/Actions/Columns/IdPageview.php b/plugins/Actions/Columns/IdPageview.php
index c1d5352153..219fa7cbc0 100644
--- a/plugins/Actions/Columns/IdPageview.php
+++ b/plugins/Actions/Columns/IdPageview.php
@@ -8,6 +8,8 @@
*/
namespace Piwik\Plugins\Actions\Columns;
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\MetricsList;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
@@ -18,6 +20,8 @@ class IdPageview extends ActionDimension
{
protected $columnName = 'idpageview';
protected $columnType = 'CHAR(6) NULL DEFAULT NULL';
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'Actions_ColumnIdPageview';
/**
* @param Request $request
@@ -32,9 +36,9 @@ class IdPageview extends ActionDimension
return substr($request->getParam('pv_id'), 0, 6);
}
- public function getName()
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
{
- return Piwik::translate('Actions_ColumnIdPageview');
+ // metrics for idpageview do not really make any sense
}
} \ No newline at end of file
diff --git a/plugins/Actions/Columns/InteractionPosition.php b/plugins/Actions/Columns/InteractionPosition.php
index 13c85ede65..c825669450 100644
--- a/plugins/Actions/Columns/InteractionPosition.php
+++ b/plugins/Actions/Columns/InteractionPosition.php
@@ -19,6 +19,8 @@ class InteractionPosition extends ActionDimension
{
protected $columnName = 'interaction_position';
protected $columnType = 'SMALLINT UNSIGNED DEFAULT NULL';
+ protected $nameSingular = 'Actions_ColumnInteractionPosition';
+ protected $type = self::TYPE_NUMBER;
/**
* @param Request $request
@@ -46,9 +48,4 @@ class InteractionPosition extends ActionDimension
return false;
}
- public function getName()
- {
- return Piwik::translate('Actions_ColumnInteractionPosition');
- }
-
} \ No newline at end of file
diff --git a/plugins/Actions/Columns/Keyword.php b/plugins/Actions/Columns/Keyword.php
index 3d7051c451..7c7a5aa915 100644
--- a/plugins/Actions/Columns/Keyword.php
+++ b/plugins/Actions/Columns/Keyword.php
@@ -13,8 +13,6 @@ use Piwik\Piwik;
class Keyword extends Dimension
{
- public function getName()
- {
- return Piwik::translate('General_ColumnKeyword');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'General_ColumnKeyword';
} \ No newline at end of file
diff --git a/plugins/Actions/Columns/KeywordwithNoSearchResult.php b/plugins/Actions/Columns/KeywordwithNoSearchResult.php
index d6ffaf2044..e96e50eaa3 100644
--- a/plugins/Actions/Columns/KeywordwithNoSearchResult.php
+++ b/plugins/Actions/Columns/KeywordwithNoSearchResult.php
@@ -13,8 +13,6 @@ use Piwik\Piwik;
class KeywordwithNoSearchResult extends Dimension
{
- public function getName()
- {
- return Piwik::translate('Actions_ColumnNoResultKeyword');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'Actions_ColumnNoResultKeyword';
} \ No newline at end of file
diff --git a/plugins/Actions/Columns/Metrics/AveragePageGenerationTime.php b/plugins/Actions/Columns/Metrics/AveragePageGenerationTime.php
index 9b44e88943..ed89226ac0 100644
--- a/plugins/Actions/Columns/Metrics/AveragePageGenerationTime.php
+++ b/plugins/Actions/Columns/Metrics/AveragePageGenerationTime.php
@@ -13,7 +13,6 @@ use Piwik\Metrics;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugin\ProcessedMetric;
-use Piwik\Plugin\Report;
/**
* The average amount of time it takes to generate a page. Calculated as
diff --git a/plugins/Actions/Columns/PageGenerationTime.php b/plugins/Actions/Columns/PageGenerationTime.php
new file mode 100644
index 0000000000..d7c8344db4
--- /dev/null
+++ b/plugins/Actions/Columns/PageGenerationTime.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Actions\Columns;
+
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\MetricsList;
+use Piwik\Piwik;
+use Piwik\Plugin\ArchivedMetric;
+use Piwik\Plugin\ComputedMetric;
+use Piwik\Plugin\Dimension\ActionDimension;
+use Piwik\Tracker\Action;
+
+class PageGenerationTime extends ActionDimension
+{
+ protected $nameSingular = 'General_ColumnPageGenerationTime';
+ protected $columnName = 'custom_float';
+ protected $category = 'General_Actions';
+ protected $type = self::TYPE_DURATION_MS;
+
+ public function getDbDiscriminator()
+ {
+ return new Discriminator('log_action', 'type', Action::TYPE_PAGE_URL);
+ }
+
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
+ {
+ $metric1 = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_SUM);
+ $metricsList->addMetric($metric1);
+
+ $metric2 = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_MAX);
+ $metricsList->addMetric($metric2);
+
+ $metric3 = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_COUNT_WITH_NUMERIC_VALUE);
+ $metric3->setName('pageviews_with_generation_time');
+ $metric3->setTranslatedName(Piwik::translate('General_ColumnViewsWithGenerationTime'));
+ $metricsList->addMetric($metric3);
+
+ $metric = $dimensionMetricFactory->createComputedMetric($metric1->getName(), $metric3->getName(), ComputedMetric::AGGREGATION_AVG);
+ $metric->setName('avg_page_generation_time');
+ $metric->setTranslatedName(Piwik::translate('General_ColumnAverageGenerationTime'));
+ $metricsList->addMetric($metric);
+ }
+}
diff --git a/plugins/Actions/Columns/PageTitle.php b/plugins/Actions/Columns/PageTitle.php
index 6517bede92..77bdac1099 100644
--- a/plugins/Actions/Columns/PageTitle.php
+++ b/plugins/Actions/Columns/PageTitle.php
@@ -8,26 +8,29 @@
*/
namespace Piwik\Plugins\Actions\Columns;
-use Piwik\Piwik;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugins\Actions\Segment;
+use Piwik\Tracker\Action;
class PageTitle extends ActionDimension
{
protected $columnName = 'idaction_name';
protected $columnType = 'INTEGER(10) UNSIGNED';
+ protected $type = self::TYPE_TEXT;
+ protected $segmentName = 'pageTitle';
+ protected $nameSingular = 'Goals_PageTitle';
+ protected $namePlural = 'Actions_WidgetPageTitles';
+ protected $category = 'General_Actions';
+ protected $sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
- protected function configureSegments()
+ public function getDbColumnJoin()
{
- $segment = new Segment();
- $segment->setSegment('pageTitle');
- $segment->setName('Actions_ColumnPageName');
- $this->addSegment($segment);
+ return new ActionNameJoin();
}
- public function getName()
+ public function getDbDiscriminator()
{
- return Piwik::translate('Actions_ColumnPageName');
+ return new Discriminator('log_action', 'type', Action::TYPE_PAGE_TITLE);
}
-
}
diff --git a/plugins/Actions/Columns/PageUrl.php b/plugins/Actions/Columns/PageUrl.php
index 1f9a7035e5..a236f0e28e 100644
--- a/plugins/Actions/Columns/PageUrl.php
+++ b/plugins/Actions/Columns/PageUrl.php
@@ -8,26 +8,32 @@
*/
namespace Piwik\Plugins\Actions\Columns;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugins\Actions\Segment;
+use Piwik\Tracker\Action;
class PageUrl extends ActionDimension
{
protected $columnName = 'idaction_url';
protected $columnType = 'INTEGER(10) UNSIGNED DEFAULT NULL';
+ protected $segmentName = 'pageUrl';
+ protected $nameSingular = 'Actions_ColumnPageURL';
+ protected $namePlural = 'Actions_PageUrls';
+ protected $type = self::TYPE_URL;
+ protected $acceptValues = 'All these segments must be URL encoded, for example: http%3A%2F%2Fexample.com%2Fpath%2Fpage%3Fquery';
+ protected $category = 'General_Actions';
+ protected $sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
- protected function configureSegments()
+ public function getDbColumnJoin()
{
- $segment = new Segment();
- $segment->setSegment('pageUrl');
- $segment->setName('Actions_ColumnPageURL');
- $segment->setAcceptedValues('All these segments must be URL encoded, for example: ' . urlencode('http://example.com/path/page?query'));
- $this->addSegment($segment);
+ return new ActionNameJoin();
}
- public function getName()
+ public function getDbDiscriminator()
{
- return Piwik::translate('Actions_ColumnPageURL');
+ return new Discriminator('log_action', 'type', Action::TYPE_PAGE_URL);
}
+
}
diff --git a/plugins/Actions/Columns/SearchCategory.php b/plugins/Actions/Columns/SearchCategory.php
index 6dc3c0b11b..e61ac25136 100644
--- a/plugins/Actions/Columns/SearchCategory.php
+++ b/plugins/Actions/Columns/SearchCategory.php
@@ -13,8 +13,6 @@ use Piwik\Piwik;
class SearchCategory extends Dimension
{
- public function getName()
- {
- return Piwik::translate('Actions_ColumnSearchCategory');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'Actions_ColumnSearchCategory';
} \ No newline at end of file
diff --git a/plugins/Actions/Columns/SearchDestinationPage.php b/plugins/Actions/Columns/SearchDestinationPage.php
index bf36621a9c..4e3271892d 100644
--- a/plugins/Actions/Columns/SearchDestinationPage.php
+++ b/plugins/Actions/Columns/SearchDestinationPage.php
@@ -13,8 +13,6 @@ use Piwik\Piwik;
class SearchDestinationPage extends Dimension
{
- public function getName()
- {
- return Piwik::translate('General_ColumnDestinationPage');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'General_ColumnDestinationPage';
}
diff --git a/plugins/Actions/Columns/SearchKeyword.php b/plugins/Actions/Columns/SearchKeyword.php
index f3248da5cf..48283eae02 100644
--- a/plugins/Actions/Columns/SearchKeyword.php
+++ b/plugins/Actions/Columns/SearchKeyword.php
@@ -8,23 +8,27 @@
*/
namespace Piwik\Plugins\Actions\Columns;
-use Piwik\Piwik;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugins\Actions\Segment;
+use Piwik\Tracker\Action;
class SearchKeyword extends ActionDimension
{
- protected function configureSegments()
+ protected $columnName = 'idaction_name';
+ protected $segmentName = 'siteSearchKeyword';
+ protected $nameSingular = 'Actions_SiteSearchKeyword';
+ protected $namePlural = 'Actions_SiteSearchKeywords';
+ protected $type = self::TYPE_TEXT;
+ protected $sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
+
+ public function getDbColumnJoin()
{
- $segment = new Segment();
- $segment->setSegment('siteSearchKeyword');
- $segment->setName('Actions_SiteSearchKeyword');
- $segment->setSqlSegment('log_link_visit_action.idaction_name');
- $this->addSegment($segment);
+ return new ActionNameJoin();
}
- public function getName()
+ public function getDbDiscriminator()
{
- return Piwik::translate('General_ColumnKeyword');
+ return new Discriminator('log_action', 'type', Action::TYPE_SITE_SEARCH);
}
}
diff --git a/plugins/Actions/Columns/SearchNoResultKeyword.php b/plugins/Actions/Columns/SearchNoResultKeyword.php
index f4b57c6431..3245d31efc 100644
--- a/plugins/Actions/Columns/SearchNoResultKeyword.php
+++ b/plugins/Actions/Columns/SearchNoResultKeyword.php
@@ -13,8 +13,6 @@ use Piwik\Piwik;
class SearchNoResultKeyword extends Dimension
{
- public function getName()
- {
- return Piwik::translate('Actions_ColumnNoResultKeyword');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'Actions_ColumnNoResultKeyword';
}
diff --git a/plugins/Actions/Columns/TimeSpentRefAction.php b/plugins/Actions/Columns/TimeSpentRefAction.php
index a51cac9d59..2b0a8c2526 100644
--- a/plugins/Actions/Columns/TimeSpentRefAction.php
+++ b/plugins/Actions/Columns/TimeSpentRefAction.php
@@ -17,6 +17,7 @@ class TimeSpentRefAction extends ActionDimension
{
protected $columnName = 'time_spent_ref_action';
protected $columnType = 'INTEGER(10) UNSIGNED NULL';
+ protected $type = self::TYPE_DURATION_S;
public function onNewAction(Request $request, Visitor $visitor, Action $action)
{
diff --git a/plugins/Actions/Columns/VisitTotalActions.php b/plugins/Actions/Columns/VisitTotalActions.php
index 750cfc8667..85ddc9336d 100644
--- a/plugins/Actions/Columns/VisitTotalActions.php
+++ b/plugins/Actions/Columns/VisitTotalActions.php
@@ -8,25 +8,42 @@
*/
namespace Piwik\Plugins\Actions\Columns;
+use Piwik\Columns\MetricsList;
+use Piwik\Piwik;
+use Piwik\Plugin\ArchivedMetric;
+use Piwik\Plugin\ComputedMetric;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugins\CoreHome\Segment;
+use Piwik\Columns\DimensionMetricFactory;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
-use Piwik\Tracker;
use Piwik\Tracker\Visitor;
class VisitTotalActions extends VisitDimension
{
protected $columnName = 'visit_total_actions';
protected $columnType = 'INT(11) UNSIGNED NULL';
+ protected $metricId = 'actions';
+ protected $nameSingular = 'Actions_ActionsInVisit';
+ protected $segmentName = 'actions';
+ protected $type = self::TYPE_NUMBER;
- protected function configureSegments()
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
{
- $segment = new Segment();
- $segment->setType(Segment::TYPE_METRIC);
- $segment->setSegment('actions');
- $segment->setName('General_NbActions');
- $this->addSegment($segment);
+ $metric1 = $dimensionMetricFactory->createCustomMetric('bounce_count', Piwik::translate('General_ColumnBounces'), 'sum(case %s when 1 then 1 when 0 then 1 else 0 end)');
+ $metric1->setDocumentation(Piwik::translate('General_ColumnBouncesDocumentation'));
+ $metricsList->addMetric($metric1);
+
+ $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_SUM);
+ $metricsList->addMetric($metric);
+
+ $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_MAX);
+ $metricsList->addMetric($metric);
+
+ $metric = $dimensionMetricFactory->createComputedMetric($metric1->getName(), 'nb_visits', ComputedMetric::AGGREGATION_RATE);
+ $metric->setTranslatedName(Piwik::translate('General_ColumnBounceRate'));
+ $metric->setName('bounce_rate');
+ $metric->setDocumentation(Piwik::translate('General_ColumnBounceRateDocumentation'));
+ $metricsList->addMetric($metric);
}
/**
diff --git a/plugins/Actions/Columns/VisitTotalInteractions.php b/plugins/Actions/Columns/VisitTotalInteractions.php
index 9c10b1e03a..a41244211c 100644
--- a/plugins/Actions/Columns/VisitTotalInteractions.php
+++ b/plugins/Actions/Columns/VisitTotalInteractions.php
@@ -18,20 +18,18 @@ class VisitTotalInteractions extends VisitDimension
{
protected $columnName = 'visit_total_interactions';
protected $columnType = 'SMALLINT UNSIGNED DEFAULT 0';
+ protected $type = self::TYPE_NUMBER;
+ protected $segmentName = 'interactions';
+ protected $nameSingular = 'General_NbInteractions';
+ protected $acceptValues = 'Any positive integer';
- protected function configureSegments()
+ public function __construct()
{
- $segment = new Segment();
- $segment->setType(Segment::TYPE_METRIC);
- $segment->setSegment('interactions');
- $segment->setName('General_NbInteractions');
- $segment->setAcceptedValues('Any positive integer');
- $segment->setSuggestedValuesCallback(function ($idSite, $maxValuesToReturn) {
+ $this->suggestedValuesCallback = function ($idSite, $maxValuesToReturn) {
$positions = range(1,50);
return array_slice($positions, 0, $maxValuesToReturn);
- });
- $this->addSegment($segment);
+ };
}
/**
diff --git a/plugins/Actions/Columns/VisitTotalSearches.php b/plugins/Actions/Columns/VisitTotalSearches.php
index e3ebb83247..559b8090bf 100644
--- a/plugins/Actions/Columns/VisitTotalSearches.php
+++ b/plugins/Actions/Columns/VisitTotalSearches.php
@@ -9,26 +9,18 @@
namespace Piwik\Plugins\Actions\Columns;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugins\CoreHome\Segment;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
-use Piwik\Tracker;
use Piwik\Tracker\Visitor;
class VisitTotalSearches extends VisitDimension
{
protected $columnName = 'visit_total_searches';
protected $columnType = 'SMALLINT(5) UNSIGNED NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setType(Segment::TYPE_METRIC);
- $segment->setSegment('searches');
- $segment->setName('General_NbSearches');
- $segment->setAcceptedValues('To select all visits who used internal Site Search, use: &segment=searches>0');
- $this->addSegment($segment);
- }
+ protected $segmentName = 'searches';
+ protected $nameSingular = 'General_NbSearches';
+ protected $acceptValues = 'To select all visits who used internal Site Search, use: &segment=searches>0';
+ protected $type = self::TYPE_NUMBER;
/**
* @param Request $request
diff --git a/plugins/Actions/lang/en.json b/plugins/Actions/lang/en.json
index ab9fbff5f7..8725fc8e3f 100644
--- a/plugins/Actions/lang/en.json
+++ b/plugins/Actions/lang/en.json
@@ -2,14 +2,18 @@
"Actions": {
"AvgGenerationTimeTooltip": "Average based on %1$s hit(s) %2$s between %3$s and %4$s",
"ColumnClickedURL": "Clicked URL",
+ "ColumnClickedURLs": "Clicked URLs",
"ColumnActionURL": "Action URL",
"ColumnClicks": "Clicks",
"ColumnClicksDocumentation": "The number of times this link was clicked.",
"ColumnDownloadURL": "Download URL",
+ "ColumnDownloadURLs": "Download URLs",
"ColumnEntryPageTitle": "Entry Page title",
"ColumnEntryPageURL": "Entry Page URL",
+ "ColumnEntryPageURLs": "Entry Page URLs",
"ColumnExitPageTitle": "Exit Page Title",
"ColumnExitPageURL": "Exit Page URL",
+ "ColumnExitPageURLs": "Exit Page URLs",
"ColumnNoResultKeyword": "Keyword with No Search Result",
"ColumnPageName": "Page Name",
"ColumnPagesPerSearch": "Search Results pages",
@@ -28,6 +32,7 @@
"ColumnUniqueOutlinks": "Unique Outlinks",
"ColumnIdPageview": "Pageview ID",
"ColumnInteractionPosition": "Interaction Position",
+ "ActionsInVisit": "Actions In Visit",
"DownloadsReportDocumentation": "In this report, you can see which files your visitors have downloaded. %s What Piwik counts as a download is the click on a download link. Whether the download was completed or not isn't known to Piwik.",
"EntryPagesReportDocumentation": "This report contains information about the entry pages that were used during the specified period. An entry page is the first page that a user views during their visit. %s The entry URLs are displayed as a folder structure.",
"EntryPageTitles": "Entry page titles",
@@ -49,7 +54,10 @@
"SiteSearchCategories2": "For example, Ecommerce websites typically have a \"Category\" selector so that visitors can restrict their searches to all products in a specific Category.",
"SiteSearchFollowingPagesDoc": "When visitors search on your website, they are looking for a particular page, content, product, or service. This report lists the pages that were clicked the most after an internal search. In other words, the list of pages the most searched for by visitors already on your website.",
"SiteSearchIntro": "Tracking searches that visitors make on your website is a very effective way to learn more about what your audience is looking for, it can help find ideas for new content, new Ecommerce products that potential customers might be searching for, and generally improve the visitors' experience on your website.",
+ "SiteSearchCategory": "Category (Site Search)",
+ "SiteSearchCategories": "Categories (Site Search)",
"SiteSearchKeyword": "Keyword (Site Search)",
+ "SiteSearchKeywords": "Keywords (Site Search)",
"SiteSearchKeywordsDocumentation": "This report lists the Search Keywords that visitors searched for on your internal Search Engine.",
"SiteSearchKeywordsNoResultDocumentation": "This report lists the Search Keywords that did not return any Search result: maybe the search engine algorithm can be improved, or maybe your visitors are looking for content that is not (yet) on your website?",
"SubmenuPagesEntry": "Entry pages",
@@ -66,6 +74,7 @@
"WidgetSearchCategories": "Search Categories",
"WidgetSearchKeywords": "Site Search Keywords",
"WidgetSearchNoResultKeywords": "Search Keywords with No Results",
- "ActionType": "Action Type"
+ "ActionType": "Action Type",
+ "ActionTypes": "Action Types"
}
} \ No newline at end of file
diff --git a/plugins/Contents/Columns/ContentInteraction.php b/plugins/Contents/Columns/ContentInteraction.php
index a50467f9d6..e188fab521 100644
--- a/plugins/Contents/Columns/ContentInteraction.php
+++ b/plugins/Contents/Columns/ContentInteraction.php
@@ -8,9 +8,9 @@
*/
namespace Piwik\Plugins\Contents\Columns;
-use Piwik\Piwik;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugins\Actions\Segment;
use Piwik\Plugins\Contents\Actions\ActionContent;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -19,19 +19,22 @@ class ContentInteraction extends ActionDimension
{
protected $columnName = 'idaction_content_interaction';
protected $columnType = 'INTEGER(10) UNSIGNED DEFAULT NULL';
-
- protected function configureSegments()
+ protected $type = self::TYPE_TEXT;
+ protected $acceptValues = 'The type of interaction with the content. For instance "click" or "submit".';
+ protected $segmentName = 'contentInteraction';
+ protected $nameSingular = 'Contents_ContentInteraction';
+ protected $namePlural = 'Contents_ContentInteractions';
+ protected $category = 'General_Actions';
+ protected $sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
+
+ public function getDbColumnJoin()
{
- $segment = new Segment();
- $segment->setSegment('contentInteraction');
- $segment->setName('Contents_ContentInteraction');
- $segment->setAcceptedValues('The type of interaction with the content. For instance "click" or "submit".');
- $this->addSegment($segment);
+ return new ActionNameJoin();
}
- public function getName()
+ public function getDbDiscriminator()
{
- return Piwik::translate('Contents_ContentInteraction');
+ return new Discriminator('log_action', 'type', $this->getActionId());
}
public function getActionId()
diff --git a/plugins/Contents/Columns/ContentName.php b/plugins/Contents/Columns/ContentName.php
index 6168a48725..47255787d6 100644
--- a/plugins/Contents/Columns/ContentName.php
+++ b/plugins/Contents/Columns/ContentName.php
@@ -8,10 +8,10 @@
*/
namespace Piwik\Plugins\Contents\Columns;
-use Piwik\Exception\InvalidRequestParameterException;
-use Piwik\Piwik;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugins\Actions\Segment;
+use Piwik\Exception\InvalidRequestParameterException;
use Piwik\Plugins\Contents\Actions\ActionContent;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -20,19 +20,22 @@ class ContentName extends ActionDimension
{
protected $columnName = 'idaction_content_name';
protected $columnType = 'INTEGER(10) UNSIGNED DEFAULT NULL';
-
- protected function configureSegments()
+ protected $segmentName = 'contentName';
+ protected $nameSingular = 'Contents_ContentName';
+ protected $namePlural = 'Contents_ContentNames';
+ protected $acceptValues = 'The name of a content block, for instance "Ad Sale"';
+ protected $type = self::TYPE_TEXT;
+ protected $category = 'General_Actions';
+ protected $sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
+
+ public function getDbColumnJoin()
{
- $segment = new Segment();
- $segment->setSegment('contentName');
- $segment->setName('Contents_ContentName');
- $segment->setAcceptedValues('The name of a content block, for instance "Ad Sale"');
- $this->addSegment($segment);
+ return new ActionNameJoin();
}
- public function getName()
+ public function getDbDiscriminator()
{
- return Piwik::translate('Contents_ContentName');
+ return new Discriminator('log_action', 'type', $this->getActionId());
}
public function getActionId()
diff --git a/plugins/Contents/Columns/ContentPiece.php b/plugins/Contents/Columns/ContentPiece.php
index 4cd0722e5a..0bf5464611 100644
--- a/plugins/Contents/Columns/ContentPiece.php
+++ b/plugins/Contents/Columns/ContentPiece.php
@@ -8,9 +8,9 @@
*/
namespace Piwik\Plugins\Contents\Columns;
-use Piwik\Piwik;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugins\Actions\Segment;
use Piwik\Plugins\Contents\Actions\ActionContent;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -19,19 +19,22 @@ class ContentPiece extends ActionDimension
{
protected $columnName = 'idaction_content_piece';
protected $columnType = 'INTEGER(10) UNSIGNED DEFAULT NULL';
-
- protected function configureSegments()
+ protected $segmentName = 'contentPiece';
+ protected $nameSingular = 'Contents_ContentPiece';
+ protected $namePlural = 'Contents_ContentPieces';
+ protected $acceptValues = 'The actual content. For instance "ad.jpg" or "My text ad"';
+ protected $type = self::TYPE_TEXT;
+ protected $category = 'General_Actions';
+ protected $sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
+
+ public function getDbColumnJoin()
{
- $segment = new Segment();
- $segment->setSegment('contentPiece');
- $segment->setName('Contents_ContentPiece');
- $segment->setAcceptedValues('The actual content. For instance "ad.jpg" or "My text ad"');
- $this->addSegment($segment);
+ return new ActionNameJoin();
}
- public function getName()
+ public function getDbDiscriminator()
{
- return Piwik::translate('Contents_ContentPiece');
+ return new Discriminator('log_action', 'type', $this->getActionId());
}
public function getActionId()
diff --git a/plugins/Contents/Columns/ContentTarget.php b/plugins/Contents/Columns/ContentTarget.php
index 9e81c687f3..7448770749 100644
--- a/plugins/Contents/Columns/ContentTarget.php
+++ b/plugins/Contents/Columns/ContentTarget.php
@@ -8,9 +8,9 @@
*/
namespace Piwik\Plugins\Contents\Columns;
-use Piwik\Piwik;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugins\Actions\Segment;
use Piwik\Plugins\Contents\Actions\ActionContent;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -19,19 +19,22 @@ class ContentTarget extends ActionDimension
{
protected $columnName = 'idaction_content_target';
protected $columnType = 'INTEGER(10) UNSIGNED DEFAULT NULL';
-
- protected function configureSegments()
+ protected $type = self::TYPE_URL;
+ protected $nameSingular = 'Contents_ContentTarget';
+ protected $namePlural = 'Contents_ContentTargets';
+ protected $segmentName = 'contentTarget';
+ protected $category = 'General_Actions';
+ protected $sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
+ protected $acceptValues = 'For instance the URL of a landing page: "http://landingpage.example.com"';
+
+ public function getDbColumnJoin()
{
- $segment = new Segment();
- $segment->setSegment('contentTarget');
- $segment->setName('Contents_ContentTarget');
- $segment->setAcceptedValues('For instance the URL of a landing page: "http://landingpage.example.com"');
- $this->addSegment($segment);
+ return new ActionNameJoin();
}
- public function getName()
+ public function getDbDiscriminator()
{
- return Piwik::translate('Contents_ContentTarget');
+ return new Discriminator('log_action', 'type', $this->getActionId());
}
public function getActionId()
diff --git a/plugins/Contents/Contents.php b/plugins/Contents/Contents.php
index 289bdb25ec..580b752a53 100644
--- a/plugins/Contents/Contents.php
+++ b/plugins/Contents/Contents.php
@@ -14,7 +14,7 @@ use Piwik\Piwik;
class Contents extends \Piwik\Plugin
{
/**
- * @see Piwik\Plugin::registerEvents
+ * @see \Piwik\Plugin::registerEvents
*/
public function registerEvents()
{
@@ -59,4 +59,4 @@ class Contents extends \Piwik\Plugin
$joins[] = 'LEFT JOIN ' . Common::prefixTable('log_action') . ' AS log_action_content_interaction
ON log_link_visit_action.idaction_content_interaction = log_action_content_interaction.idaction';
}
-}
+} \ No newline at end of file
diff --git a/plugins/Contents/lang/en.json b/plugins/Contents/lang/en.json
index 0947590f69..e4bef887a0 100644
--- a/plugins/Contents/lang/en.json
+++ b/plugins/Contents/lang/en.json
@@ -7,8 +7,11 @@
"ContentInteractions": "Content Interactions",
"InteractionRate": "Interaction Rate",
"ContentName": "Content Name",
+ "ContentNames": "Content Names",
"ContentPiece": "Content Piece",
+ "ContentPieces": "Content Pieces",
"ContentTarget": "Content Target",
+ "ContentTargets": "Content Targets",
"Contents": "Contents",
"InteractionsMetricDocumentation": "The number of times a content block was interacted with (eg, a 'click' on a banner or ad).",
"ImpressionsMetricDocumentation": "The number of times a content block, such as a banner or an ad, was displayed on a page.",
diff --git a/plugins/CoreHome/Columns/IdSite.php b/plugins/CoreHome/Columns/IdSite.php
index 996cae629b..f757b5bf1c 100644
--- a/plugins/CoreHome/Columns/IdSite.php
+++ b/plugins/CoreHome/Columns/IdSite.php
@@ -8,10 +8,10 @@
*/
namespace Piwik\Plugins\CoreHome\Columns;
+use Piwik\Columns\Join\SiteNameJoin;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
-use Piwik\Tracker;
use Piwik\Tracker\Visitor;
class IdSite extends VisitDimension
@@ -23,6 +23,13 @@ class IdSite extends VisitDimension
// INDEX(idsite, config_id, visit_last_action_time) and we maybe not be sure whether config_id already exists at
// installing point (we do not know whether visit_last_action_time or idsite column would be added first).
+ protected $type = self::TYPE_TEXT;
+
+ public function getDbColumnJoin()
+ {
+ return new SiteNameJoin();
+ }
+
/**
* @param Request $request
* @param Visitor $visitor
diff --git a/plugins/CoreHome/Columns/LinkVisitActionId.php b/plugins/CoreHome/Columns/LinkVisitActionId.php
new file mode 100644
index 0000000000..6cc55c04bb
--- /dev/null
+++ b/plugins/CoreHome/Columns/LinkVisitActionId.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\CoreHome\Columns;
+
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\MetricsList;
+use Piwik\Piwik;
+use Piwik\Plugin\ArchivedMetric;
+use Piwik\Plugin\Dimension\ActionDimension;
+use Piwik\Plugin\Dimension\VisitDimension;
+use Piwik\Plugin\Segment;
+
+
+class LinkVisitActionId extends ActionDimension
+{
+ protected $columnName = 'idlink_va';
+ protected $acceptValues = 'Any integer.';
+ protected $category = 'General_Actions';
+ protected $nameSingular = 'General_Actions';
+ protected $metricId = 'hits';
+ protected $type = self::TYPE_NUMBER;
+
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
+ {
+ $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_UNIQUE);
+ $metric->setTranslatedName(Piwik::translate('General_ColumnHits'));
+ $metric->setName('hits');
+ $metricsList->addMetric($metric);
+
+ $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_UNIQUE);
+ $metric->setTranslatedName(Piwik::translate('General_ColumnPageviews'));
+ $metric->setDocumentation(Piwik::translate('General_ColumnPageviewsDocumentation'));
+ $metric->setName('pageviews');
+ $metricsList->addMetric($metric);
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreHome/Columns/ServerMinute.php b/plugins/CoreHome/Columns/ServerMinute.php
new file mode 100644
index 0000000000..ee095dcb3c
--- /dev/null
+++ b/plugins/CoreHome/Columns/ServerMinute.php
@@ -0,0 +1,35 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CoreHome\Columns;
+
+use Piwik\Metrics\Formatter;
+use Piwik\Plugin\Dimension\ActionDimension;
+
+class ServerMinute extends ActionDimension
+{
+ protected $columnName = 'server_time';
+ protected $segmentName = 'actionServerMinute';
+ protected $sqlSegment = 'MINUTE(log_link_visit_action.server_time)';
+ protected $nameSingular = 'VisitTime_ColumnServerMinute';
+ protected $type = self::TYPE_DATETIME;
+ protected $acceptValues = '0, 1, 2, 3, ..., 56, 57, 58, 59';
+
+ public function __construct()
+ {
+ $this->suggestedValuesCallback = function ($idSite, $maxValuesToReturn) {
+ return range(0, min(59, $maxValuesToReturn));
+ };
+ }
+
+ public function formatValue($value, $idSite, Formatter $formatter)
+ {
+ return $value;
+ }
+
+}
diff --git a/plugins/CoreHome/Columns/ServerTime.php b/plugins/CoreHome/Columns/ServerTime.php
index 297c410058..4e308786f1 100644
--- a/plugins/CoreHome/Columns/ServerTime.php
+++ b/plugins/CoreHome/Columns/ServerTime.php
@@ -9,17 +9,34 @@
namespace Piwik\Plugins\CoreHome\Columns;
use Piwik\Date;
-use Piwik\Db;
+use Piwik\Metrics\Formatter;
use Piwik\Plugin\Dimension\ActionDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
-use Piwik\Tracker;
+
+require_once PIWIK_INCLUDE_PATH . '/plugins/VisitTime/functions.php';
class ServerTime extends ActionDimension
{
protected $columnName = 'server_time';
protected $columnType = 'DATETIME NOT NULL';
+ protected $segmentName = 'actionServerHour';
+ protected $sqlSegment = 'HOUR(log_link_visit_action.server_time)';
+ protected $nameSingular = 'VisitTime_ColumnServerHour';
+ protected $type = self::TYPE_DATETIME;
+
+ public function __construct()
+ {
+ $this->suggestedValuesCallback = function ($idSite, $maxValuesToReturn) {
+ return range(0, min(23, $maxValuesToReturn));
+ };
+ }
+
+ public function formatValue($value, $idSite, Formatter $formatter)
+ {
+ return \Piwik\Plugins\VisitTime\getTimeLabel($value);
+ }
public function install()
{
diff --git a/plugins/CoreHome/Columns/UserId.php b/plugins/CoreHome/Columns/UserId.php
index 50d1be09bc..c5423ae93a 100644
--- a/plugins/CoreHome/Columns/UserId.php
+++ b/plugins/CoreHome/Columns/UserId.php
@@ -12,9 +12,7 @@ use Piwik\Cache;
use Piwik\DataTable;
use Piwik\DataTable\Map;
use Piwik\Metrics;
-use Piwik\Piwik;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugin\Segment;
use Piwik\Plugins\VisitsSummary\API as VisitsSummaryApi;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
@@ -29,25 +27,18 @@ class UserId extends VisitDimension
* @var string
*/
protected $columnName = 'user_id';
+ protected $type = self::TYPE_TEXT;
+ protected $allowAnonymous = false;
+ protected $segmentName = 'userId';
+ protected $nameSingular = 'General_UserId';
+ protected $namePlural = 'General_UserIds';
+ protected $acceptValues = 'any non empty unique string identifying the user (such as an email address or a username).';
/**
* @var string
*/
protected $columnType = 'VARCHAR(200) NULL';
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setType('dimension');
- $segment->setSegment('userId');
- $segment->setCategory(Piwik::translate('General_Visit'));
- $segment->setName('General_UserId');
- $segment->setAcceptedValues('any non empty unique string identifying the user (such as an email address or a username).');
- $segment->setSqlSegment('log_visit.user_id');
- $segment->setRequiresAtLeastViewAccess(true);
- $this->addSegment($segment);
- }
-
/**
* @param Request $request
* @param Visitor $visitor
diff --git a/plugins/CoreHome/Columns/VisitFirstActionMinute.php b/plugins/CoreHome/Columns/VisitFirstActionMinute.php
new file mode 100644
index 0000000000..c3e16a9215
--- /dev/null
+++ b/plugins/CoreHome/Columns/VisitFirstActionMinute.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CoreHome\Columns;
+
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\MetricsList;
+use Piwik\Plugin\Dimension\VisitDimension;
+use Piwik\Metrics\Formatter;
+
+require_once PIWIK_INCLUDE_PATH . '/plugins/VisitTime/functions.php';
+
+class VisitFirstActionMinute extends VisitDimension
+{
+ protected $columnName = 'visit_first_action_time';
+ protected $type = self::TYPE_DATETIME;
+
+ protected $sqlSegment = 'HOUR(log_visit.visit_first_action_time)';
+ protected $segmentName = 'visitStartServerMinute';
+ protected $acceptValues = '0, 1, 2, 3, ..., 56, 57, 58, 59';
+ protected $nameSingular = 'VisitTime_ColumnVisitStartServerMinute';
+
+ public function __construct()
+ {
+ $this->suggestedValuesCallback = function ($idSite, $maxValuesToReturn) {
+ return range(0, min(59, $maxValuesToReturn));
+ };
+ }
+
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
+ {
+ // no metrics to be generated
+ }
+
+ public function formatValue($value, $idSite, Formatter $formatter)
+ {
+ return $value;
+ }
+
+} \ No newline at end of file
diff --git a/plugins/CoreHome/Columns/VisitFirstActionTime.php b/plugins/CoreHome/Columns/VisitFirstActionTime.php
index 3985d53339..f9318d6771 100644
--- a/plugins/CoreHome/Columns/VisitFirstActionTime.php
+++ b/plugins/CoreHome/Columns/VisitFirstActionTime.php
@@ -12,13 +12,33 @@ use Piwik\Date;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
-use Piwik\Tracker;
use Piwik\Tracker\Visitor;
+use Piwik\Metrics\Formatter;
+
+require_once PIWIK_INCLUDE_PATH . '/plugins/VisitTime/functions.php';
class VisitFirstActionTime extends VisitDimension
{
protected $columnName = 'visit_first_action_time';
protected $columnType = 'DATETIME NOT NULL';
+ protected $type = self::TYPE_DATETIME;
+
+ protected $sqlSegment = 'HOUR(log_visit.visit_first_action_time)';
+ protected $segmentName = 'visitStartServerHour';
+ protected $acceptValues = '0, 1, 2, 3, ..., 20, 21, 22, 23';
+ protected $nameSingular = 'VisitTime_ColumnVisitStartServerHour';
+
+ public function __construct()
+ {
+ $this->suggestedValuesCallback = function ($idSite, $maxValuesToReturn) {
+ return range(0, min(23, $maxValuesToReturn));
+ };
+ }
+
+ public function formatValue($value, $idSite, Formatter $formatter)
+ {
+ return $value;
+ }
/**
* @param Request $request
diff --git a/plugins/CoreHome/Columns/VisitGoalBuyer.php b/plugins/CoreHome/Columns/VisitGoalBuyer.php
index 779d5fd5be..5c6f908173 100644
--- a/plugins/CoreHome/Columns/VisitGoalBuyer.php
+++ b/plugins/CoreHome/Columns/VisitGoalBuyer.php
@@ -8,6 +8,7 @@
*/
namespace Piwik\Plugins\CoreHome\Columns;
+use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Plugins\CoreHome\Segment;
@@ -33,19 +34,39 @@ class VisitGoalBuyer extends VisitDimension
protected $columnName = 'visit_goal_buyer';
protected $columnType = 'TINYINT(1) NULL';
+ protected $segmentName = 'visitEcommerceStatus';
+ protected $nameSingular = 'General_EcommerceVisitStatusDesc';
+ protected $type = self::TYPE_ENUM;
- protected function configureSegments()
+ public function __construct()
{
$example = Piwik::translate('General_EcommerceVisitStatusEg', '"&segment=visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart"');
- $acceptedValues = implode(", ", self::$visitEcommerceStatus) . '. ' . $example;
+ $this->acceptValues = implode(", ", self::$visitEcommerceStatus) . '. ' . $example;
+ }
+
+ public function formatValue($value, $idSite, Formatter $formatter)
+ {
+ switch ($value) {
+ case 'ordered':
+ case self::TYPE_BUYER_ORDERED:
+ return Piwik::translate('CoreHome_VisitStatusOrdered');
+ case 'abandonedCart':
+ case self::TYPE_BUYER_OPEN_CART:
+ return Piwik::translate('Goals_AbandonedCart');
+ case 'orderedThenAbandonedCart':
+ case self::TYPE_BUYER_ORDERED_AND_OPEN_CART:
+ return Piwik::translate('CoreHome_VisitStatusOrderedThenAbandoned');
+ case 'none';
+ case self::TYPE_BUYER_NONE:
+ return Piwik::translate('UserCountryMap_None');
+ }
- $segment = new Segment();
- $segment->setSegment('visitEcommerceStatus');
- $segment->setName('General_EcommerceVisitStatusDesc');
- $segment->setAcceptedValues($acceptedValues);
- $segment->setSqlFilterValue(__NAMESPACE__ . '\VisitGoalBuyer::getVisitEcommerceStatus');
+ return $value;
+ }
- $this->addSegment($segment);
+ public function getEnumColumnValues()
+ {
+ return self::$visitEcommerceStatus;
}
/**
@@ -83,17 +104,6 @@ class VisitGoalBuyer extends VisitDimension
return false;
}
- public static function getVisitEcommerceStatus($status)
- {
- $id = array_search($status, self::$visitEcommerceStatus);
-
- if ($id === false) {
- throw new \Exception("Invalid 'visitEcommerceStatus' segment value $status");
- }
-
- return $id;
- }
-
/**
* @ignore
*/
diff --git a/plugins/CoreHome/Columns/VisitGoalConverted.php b/plugins/CoreHome/Columns/VisitGoalConverted.php
index 1c0cc40e65..26d87b7835 100644
--- a/plugins/CoreHome/Columns/VisitGoalConverted.php
+++ b/plugins/CoreHome/Columns/VisitGoalConverted.php
@@ -8,8 +8,11 @@
*/
namespace Piwik\Plugins\CoreHome\Columns;
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\MetricsList;
+use Piwik\Piwik;
+use Piwik\Plugin\ComputedMetric;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugins\CoreHome\Segment;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
@@ -18,14 +21,21 @@ class VisitGoalConverted extends VisitDimension
{
protected $columnName = 'visit_goal_converted';
protected $columnType = 'TINYINT(1) NULL';
+ protected $type = self::TYPE_BOOL;
+ protected $segmentName = 'visitConverted';
+ protected $nameSingular = 'General_VisitConvertedGoal';
+ protected $acceptValues = '0, 1';
- protected function configureSegments()
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
{
- $segment = new Segment();
- $segment->setSegment('visitConverted');
- $segment->setName('General_VisitConvertedGoal');
- $segment->setAcceptedValues('0, 1');
- $this->addSegment($segment);
+ $metric1 = $dimensionMetricFactory->createCustomMetric('nb_visits_converted', Piwik::translate('General_ColumnVisitsWithConversions'), 'sum(case %s when 1 then 1 else 0 end)');
+ $metricsList->addMetric($metric1);
+
+ $metric = $dimensionMetricFactory->createComputedMetric($metric1->getName(), 'nb_visits', ComputedMetric::AGGREGATION_RATE);
+ $metric->setTranslatedName(Piwik::translate('General_ColumnConversionRate'));
+ $metric->setDocumentation(Piwik::translate('General_ColumnConversionRateDocumentation'));
+ $metric->setName('visits_conversion_rate');
+ $metricsList->addMetric($metric);
}
/**
diff --git a/plugins/CoreHome/Columns/VisitId.php b/plugins/CoreHome/Columns/VisitId.php
index 9add50888a..b87f1b58d4 100644
--- a/plugins/CoreHome/Columns/VisitId.php
+++ b/plugins/CoreHome/Columns/VisitId.php
@@ -8,7 +8,10 @@
namespace Piwik\Plugins\CoreHome\Columns;
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\MetricsList;
use Piwik\Piwik;
+use Piwik\Plugin\ArchivedMetric;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Plugin\Segment;
@@ -18,18 +21,28 @@ use Piwik\Plugin\Segment;
*/
class VisitId extends VisitDimension
{
+ protected $columnName = 'idvisit';
+ protected $acceptValues = 'Any integer.';
+ protected $nameSingular = 'General_Visit';
+ protected $namePlural = 'General_ColumnNbVisits';
+ protected $segmentName = 'visitId';
+ protected $allowAnonymous = false;
+ protected $metricId = 'visits';
+ protected $type = self::TYPE_TEXT;
+
protected function configureSegments()
{
- parent::configureSegments();
-
$segment = new Segment();
- $segment->setType('dimension');
- $segment->setCategory(Piwik::translate('General_Visit'));
- $segment->setName(Piwik::translate('General_Visit') . " ID");
- $segment->setSegment('visitId');
- $segment->setAcceptedValues('Any integer.');
- $segment->setSqlSegment('log_visit.idvisit');
- $segment->setRequiresAtLeastViewAccess(true);
+ $segment->setName('General_VisitId');
$this->addSegment($segment);
}
+
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
+ {
+ $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_UNIQUE);
+ $metric->setTranslatedName(Piwik::translate('General_ColumnNbVisits'));
+ $metric->setDocumentation(Piwik::translate('General_ColumnNbVisitsDocumentation'));
+ $metric->setName('nb_visits');
+ $metricsList->addMetric($metric);
+ }
} \ No newline at end of file
diff --git a/plugins/CoreHome/Columns/VisitIp.php b/plugins/CoreHome/Columns/VisitIp.php
index 6dd2e5d6b9..ed8b196fd2 100644
--- a/plugins/CoreHome/Columns/VisitIp.php
+++ b/plugins/CoreHome/Columns/VisitIp.php
@@ -8,7 +8,9 @@
namespace Piwik\Plugins\CoreHome\Columns;
-use Piwik\Piwik;
+use Piwik\Common;
+use Piwik\Metrics\Formatter;
+use Piwik\Network\IPUtils;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Plugin\Segment;
@@ -18,19 +20,26 @@ use Piwik\Plugin\Segment;
*/
class VisitIp extends VisitDimension
{
- protected function configureSegments()
+ protected $columnName = 'location_ip';
+ protected $type = self::TYPE_BINARY;
+ protected $allowAnonymous = false;
+ protected $segmentName = 'visitIp';
+ protected $nameSingular = 'General_VisitorIP';
+ protected $namePlural = 'General_VisitorIPs';
+ protected $acceptValues = '13.54.122.1. </code>Select IP ranges with notation: <code>visitIp>13.54.122.0;visitIp<13.54.122.255';
+ protected $sqlFilterValue = array('Piwik\Network\IPUtils', 'stringToBinaryIP');
+
+ public function formatValue($value, $idSite, Formatter $formatter)
{
- parent::configureSegments();
+ $value = Common::hex2bin($value);
+ $value = IPUtils::binaryToStringIP($value);
+ return $value;
+ }
+ protected function configureSegments()
+ {
$segment = new Segment();
- $segment->setType('metric');
- $segment->setCategory(Piwik::translate('General_Visit'));
- $segment->setName('General_VisitorIP');
- $segment->setSegment('visitIp');
- $segment->setAcceptedValues('13.54.122.1. </code>Select IP ranges with notation: <code>visitIp>13.54.122.0;visitIp<13.54.122.255');
- $segment->setSqlSegment('log_visit.location_ip');
- $segment->setSqlFilterValue(array('Piwik\Network\IPUtils', 'stringToBinaryIP'));
- $segment->setRequiresAtLeastViewAccess(true);
+ $segment->setType(Segment::TYPE_METRIC); // we cannot remove this for now as it would assign dimension based on text type
$this->addSegment($segment);
}
}
diff --git a/plugins/CoreHome/Columns/VisitLastActionMinute.php b/plugins/CoreHome/Columns/VisitLastActionMinute.php
new file mode 100644
index 0000000000..06611fb2fc
--- /dev/null
+++ b/plugins/CoreHome/Columns/VisitLastActionMinute.php
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CoreHome\Columns;
+
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\MetricsList;
+use Piwik\Plugin\Dimension\VisitDimension;
+use Piwik\Metrics\Formatter;
+
+require_once PIWIK_INCLUDE_PATH . '/plugins/VisitTime/functions.php';
+
+/**
+ * This dimension holds the best guess for a visit's end time. It is set the last action
+ * time for each visit. `ping=1` requests can be sent to update the dimension value so
+ * it can be a more accurate guess of the time the visitor spent on the site.
+ *
+ * Note: though it is named 'visit last action time' it actually refers to the visit's last action's
+ * end time.
+ */
+class VisitLastActionMinute extends VisitDimension
+{
+ protected $columnName = 'visit_last_action_time';
+ protected $type = self::TYPE_DATETIME;
+ protected $segmentName = 'visitEndServerMinute';
+ protected $nameSingular = 'VisitTime_ColumnVisitEndServerMinute';
+ protected $sqlSegment = 'MINUTE(log_visit.visit_last_action_time)';
+ protected $acceptValues = '0, 1, 2, 3, ..., 56, 57, 58, 59';
+
+ public function __construct()
+ {
+ $this->suggestedValuesCallback = function ($idSite, $maxValuesToReturn) {
+ return range(0, min(59, $maxValuesToReturn));
+ };
+ }
+
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
+ {
+ // no metrics for this dimension
+ }
+
+ public function formatValue($value, $idSite, Formatter $formatter)
+ {
+ return $value;
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreHome/Columns/VisitLastActionTime.php b/plugins/CoreHome/Columns/VisitLastActionTime.php
index 4ba0761c2a..3b297fb68b 100644
--- a/plugins/CoreHome/Columns/VisitLastActionTime.php
+++ b/plugins/CoreHome/Columns/VisitLastActionTime.php
@@ -12,8 +12,10 @@ use Piwik\Date;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
-use Piwik\Tracker;
use Piwik\Tracker\Visitor;
+use Piwik\Metrics\Formatter;
+
+require_once PIWIK_INCLUDE_PATH . '/plugins/VisitTime/functions.php';
/**
* This dimension holds the best guess for a visit's end time. It is set the last action
@@ -26,6 +28,17 @@ use Piwik\Tracker\Visitor;
class VisitLastActionTime extends VisitDimension
{
protected $columnName = 'visit_last_action_time';
+ protected $type = self::TYPE_DATETIME;
+ protected $nameSingular = 'VisitTime_ColumnVisitEndServerHour';
+ protected $sqlSegment = 'HOUR(log_visit.visit_last_action_time)';
+ protected $segmentName = 'visitServerHour';
+ protected $acceptValues = '0, 1, 2, 3, ..., 20, 21, 22, 23';
+
+ public function formatValue($value, $idSite, Formatter $formatter)
+ {
+ return \Piwik\Plugins\VisitTime\getTimeLabel($value);
+ }
+
// we do not install or define column definition here as we need to create this column when installing as there is
// an index on it. Currently we do not define the index here... although we could overwrite the install() method
// and add column 'visit_last_action_time' and add index. Problem is there is also an index
diff --git a/plugins/CoreHome/Columns/VisitTotalTime.php b/plugins/CoreHome/Columns/VisitTotalTime.php
index 87025a90f9..44bdc39958 100644
--- a/plugins/CoreHome/Columns/VisitTotalTime.php
+++ b/plugins/CoreHome/Columns/VisitTotalTime.php
@@ -19,15 +19,9 @@ class VisitTotalTime extends VisitDimension
{
protected $columnName = 'visit_total_time';
protected $columnType = 'INT(11) UNSIGNED NOT NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('visitDuration');
- $segment->setName('General_ColumnVisitDuration');
- $segment->setType(Segment::TYPE_METRIC);
- $this->addSegment($segment);
- }
+ protected $segmentName = 'visitDuration';
+ protected $nameSingular = 'General_ColumnVisitDuration';
+ protected $type = self::TYPE_DURATION_S;
/**
* @param Request $request
diff --git a/plugins/CoreHome/Columns/VisitorDaysSinceFirst.php b/plugins/CoreHome/Columns/VisitorDaysSinceFirst.php
index 3445f21f59..e0ce8fa5be 100644
--- a/plugins/CoreHome/Columns/VisitorDaysSinceFirst.php
+++ b/plugins/CoreHome/Columns/VisitorDaysSinceFirst.php
@@ -9,7 +9,6 @@
namespace Piwik\Plugins\CoreHome\Columns;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugins\CoreHome\Segment;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
@@ -18,15 +17,9 @@ class VisitorDaysSinceFirst extends VisitDimension
{
protected $columnName = 'visitor_days_since_first';
protected $columnType = 'SMALLINT(5) UNSIGNED NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setType(Segment::TYPE_METRIC);
- $segment->setSegment('daysSinceFirstVisit');
- $segment->setName('General_DaysSinceFirstVisit');
- $this->addSegment($segment);
- }
+ protected $segmentName = 'daysSinceFirstVisit';
+ protected $nameSingular = 'General_DaysSinceFirstVisit';
+ protected $type = self::TYPE_NUMBER;
/**
* @param Request $request
diff --git a/plugins/CoreHome/Columns/VisitorDaysSinceOrder.php b/plugins/CoreHome/Columns/VisitorDaysSinceOrder.php
index c126b5957c..ad6334e838 100644
--- a/plugins/CoreHome/Columns/VisitorDaysSinceOrder.php
+++ b/plugins/CoreHome/Columns/VisitorDaysSinceOrder.php
@@ -9,7 +9,6 @@
namespace Piwik\Plugins\CoreHome\Columns;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugins\CoreHome\Segment;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
@@ -18,16 +17,10 @@ class VisitorDaysSinceOrder extends VisitDimension
{
protected $columnName = 'visitor_days_since_order';
protected $columnType = 'SMALLINT(5) UNSIGNED NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('daysSinceLastEcommerceOrder');
- $segment->setName('General_DaysSinceLastEcommerceOrder');
- $segment->setType(Segment::TYPE_METRIC);
-
- $this->addSegment($segment);
- }
+ protected $segmentName = 'daysSinceLastEcommerceOrder';
+ protected $nameSingular = 'General_DaysSinceLastEcommerceOrder';
+ protected $category = 'General_Visitors'; // todo put into ecommerce category?
+ protected $type = self::TYPE_NUMBER;
/**
* @param Request $request
diff --git a/plugins/CoreHome/Columns/VisitorId.php b/plugins/CoreHome/Columns/VisitorId.php
index ce797ae57a..728e1e3d94 100644
--- a/plugins/CoreHome/Columns/VisitorId.php
+++ b/plugins/CoreHome/Columns/VisitorId.php
@@ -8,9 +8,12 @@
namespace Piwik\Plugins\CoreHome\Columns;
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\MetricsList;
+use Piwik\Metrics\Formatter;
use Piwik\Piwik;
+use Piwik\Plugin\ArchivedMetric;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugin\Segment;
/**
* Dimension for the log_visit.idvisitor column. This column is added in the CREATE TABLE
@@ -18,19 +21,21 @@ use Piwik\Plugin\Segment;
*/
class VisitorId extends VisitDimension
{
- protected function configureSegments()
- {
- parent::configureSegments();
+ protected $columnName = 'idvisitor';
+ protected $metricId = 'visitors';
+ protected $nameSingular = 'General_VisitorID';
+ protected $namePlural = 'General_Visitors';
+ protected $segmentName = 'visitorId';
+ protected $acceptValues = '34c31e04394bdc63 - any 16 Hexadecimal chars ID, which can be fetched using the Tracking API function getVisitorId()';
+ protected $allowAnonymous = false;
+ protected $sqlFilterValue = array('Piwik\Common', 'convertVisitorIdToBin');
+ protected $type = self::TYPE_BINARY;
- $segment = new Segment();
- $segment->setType('dimension');
- $segment->setCategory(Piwik::translate('General_Visit'));
- $segment->setName('General_VisitorID');
- $segment->setSegment('visitorId');
- $segment->setAcceptedValues('34c31e04394bdc63 - any 16 Hexadecimal chars ID, which can be fetched using the Tracking API function getVisitorId()');
- $segment->setSqlSegment('log_visit.idvisitor');
- $segment->setSqlFilterValue(array('Piwik\Common', 'convertVisitorIdToBin'));
- $segment->setRequiresAtLeastViewAccess(true);
- $this->addSegment($segment);
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
+ {
+ $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_UNIQUE);
+ $metric->setTranslatedName(Piwik::translate('General_ColumnNbUniqVisitors'));
+ $metric->setName('nb_uniq_visitors');
+ $metricsList->addMetric($metric);
}
}
diff --git a/plugins/CoreHome/Columns/VisitorReturning.php b/plugins/CoreHome/Columns/VisitorReturning.php
index ca1ef11652..862fb12c6e 100644
--- a/plugins/CoreHome/Columns/VisitorReturning.php
+++ b/plugins/CoreHome/Columns/VisitorReturning.php
@@ -8,9 +8,9 @@
*/
namespace Piwik\Plugins\CoreHome\Columns;
+use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugins\CoreHome\Segment;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
@@ -23,22 +23,44 @@ class VisitorReturning extends VisitDimension
protected $columnName = 'visitor_returning';
protected $columnType = 'TINYINT(1) NULL';
+ protected $segmentName = 'visitorType';
+ protected $nameSingular = 'General_VisitType';
+ protected $namePlural = 'General_VisitTypes';
protected $conversionField = true;
+ protected $type = self::TYPE_ENUM;
- protected function configureSegments()
+ public function __construct()
{
- $acceptedValues = 'new, returning, returningCustomer. ';
- $acceptedValues .= Piwik::translate('General_VisitTypeExample', '"&segment=visitorType==returning,visitorType==returningCustomer"');
-
- $segment = new Segment();
- $segment->setSegment('visitorType');
- $segment->setName('General_VisitType');
- $segment->setAcceptedValues($acceptedValues);
- $segment->setSqlFilterValue(function ($type) {
+ $this->acceptValues = 'new, returning, returningCustomer. ';
+ $this->acceptValues .= Piwik::translate('General_VisitTypeExample', '"&segment=visitorType==returning,visitorType==returningCustomer"');
+ $this->sqlFilterValue = function ($type) {
+ if (is_numeric($type)) {
+ return $type;
+ }
return $type == "new" ? 0 : ($type == "returning" ? 1 : 2);
- });
+ };
+ }
+
+ public function formatValue($value, $idSite, Formatter $formatter)
+ {
+ if ($value === 1 || $value === '1' || $value === 'returning') {
+ return Piwik::translate('CoreHome_VisitTypeReturning');
+ } elseif ($value === 2 || $value === '2' || $value === 'returningCustomer'){
+ return Piwik::translate('CoreHome_VisitTypeReturningCustomer');
+ } elseif ($value === 0 || $value === '0' || $value === 'new'){
+ return Piwik::translate('General_New');
+ }
- $this->addSegment($segment);
+ return $value;
+ }
+
+ public function getEnumColumnValues()
+ {
+ return array(
+ self::IS_RETURNING_CUSTOMER => 'returningCustomer',
+ self::IS_RETURNING => 'returning',
+ self::IS_NEW => 'new',
+ );
}
/**
@@ -49,7 +71,6 @@ class VisitorReturning extends VisitDimension
*/
public function onNewVisit(Request $request, Visitor $visitor, $action)
{
-
$daysSinceLastOrder = $request->getDaysSinceLastOrder();
$isReturningCustomer = ($daysSinceLastOrder !== false);
diff --git a/plugins/CoreHome/Columns/VisitsCount.php b/plugins/CoreHome/Columns/VisitsCount.php
index 584b242ae4..c334f175bb 100644
--- a/plugins/CoreHome/Columns/VisitsCount.php
+++ b/plugins/CoreHome/Columns/VisitsCount.php
@@ -8,8 +8,9 @@
*/
namespace Piwik\Plugins\CoreHome\Columns;
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\MetricsList;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugins\CoreHome\Segment;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
@@ -18,14 +19,13 @@ class VisitsCount extends VisitDimension
{
protected $columnName = 'visitor_count_visits';
protected $columnType = 'INT(11) UNSIGNED NOT NULL';
+ protected $segmentName = 'visitCount';
+ protected $nameSingular = 'General_NumberOfVisits';
+ protected $type = self::TYPE_NUMBER;
- protected function configureSegments()
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
{
- $segment = new Segment();
- $segment->setType(Segment::TYPE_METRIC);
- $segment->setSegment('visitCount');
- $segment->setName('General_NumberOfVisits');
- $this->addSegment($segment);
+ // no metrics for this dimension, it would be rather confusing I think
}
/**
diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php
index be56c144f8..ff800ebe1e 100644
--- a/plugins/CoreHome/CoreHome.php
+++ b/plugins/CoreHome/CoreHome.php
@@ -7,6 +7,10 @@
*
*/
namespace Piwik\Plugins\CoreHome;
+use Piwik\Columns\ComputedMetricFactory;
+use Piwik\Columns\MetricsList;
+use Piwik\Plugin\ArchivedMetric;
+use Piwik\Plugin\ComputedMetric;
/**
*
@@ -21,7 +25,7 @@ class CoreHome extends \Piwik\Plugin
const WIDGET_CONTAINER_LAYOUT_BY_DIMENSION = 'ByDimension';
/**
- * @see Piwik\Plugin::registerEvents
+ * @see \Piwik\Plugin::registerEvents
*/
public function registerEvents()
{
@@ -30,9 +34,27 @@ class CoreHome extends \Piwik\Plugin
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'AssetManager.filterMergedJavaScripts' => 'filterMergedJavaScripts',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
+ 'Metric.addComputedMetrics' => 'addComputedMetrics'
);
}
+ public function addComputedMetrics(MetricsList $list, ComputedMetricFactory $computedMetricFactory)
+ {
+ $metrics = $list->getMetrics();
+ foreach ($metrics as $metric) {
+ if ($metric instanceof ArchivedMetric && $metric->getDimension()) {
+ $metricName = $metric->getName();
+ if ($metric->getDbTableName() === 'log_visit'
+ && $metricName !== 'nb_uniq_visitors'
+ && $metricName !== 'nb_visits'
+ && strpos($metricName, ArchivedMetric::AGGREGATION_SUM_PREFIX) === 0) {
+ $metric = $computedMetricFactory->createComputedMetric($metric->getName(), 'nb_visits', ComputedMetric::AGGREGATION_AVG);
+ $list->addMetric($metric);
+ }
+ }
+ }
+ }
+
public function filterMergedJavaScripts(&$mergedContent)
{
$mergedContent = preg_replace('/(sourceMappingURL=(.*?).map)/', '', $mergedContent);
diff --git a/plugins/CoreHome/Tracker/LogTable/Conversion.php b/plugins/CoreHome/Tracker/LogTable/Conversion.php
index b579b32d81..2a75d52c41 100644
--- a/plugins/CoreHome/Tracker/LogTable/Conversion.php
+++ b/plugins/CoreHome/Tracker/LogTable/Conversion.php
@@ -26,4 +26,9 @@ class Conversion extends LogTable
{
return 'idvisit';
}
+
+ public function getPrimaryKey()
+ {
+ return array('idvisit', 'idgoal', 'buster');
+ }
} \ No newline at end of file
diff --git a/plugins/CoreHome/Tracker/LogTable/ConversionItem.php b/plugins/CoreHome/Tracker/LogTable/ConversionItem.php
index cf24614458..8423ff8010 100644
--- a/plugins/CoreHome/Tracker/LogTable/ConversionItem.php
+++ b/plugins/CoreHome/Tracker/LogTable/ConversionItem.php
@@ -26,5 +26,10 @@ class ConversionItem extends LogTable
{
return 'idvisit';
}
-
+
+ public function getPrimaryKey()
+ {
+ return array('idvisit', 'idorder', 'idaction_sku');
+ }
+
}
diff --git a/plugins/CoreHome/Tracker/LogTable/LinkVisitAction.php b/plugins/CoreHome/Tracker/LogTable/LinkVisitAction.php
index 8dabf66a66..c2090a1c8c 100644
--- a/plugins/CoreHome/Tracker/LogTable/LinkVisitAction.php
+++ b/plugins/CoreHome/Tracker/LogTable/LinkVisitAction.php
@@ -31,4 +31,9 @@ class LinkVisitAction extends LogTable
{
return 'idvisit';
}
+
+ public function getPrimaryKey()
+ {
+ return array('idlink_va');
+ }
}
diff --git a/plugins/CoreHome/Tracker/LogTable/Visit.php b/plugins/CoreHome/Tracker/LogTable/Visit.php
index a72fb24d11..62877f1cd1 100644
--- a/plugins/CoreHome/Tracker/LogTable/Visit.php
+++ b/plugins/CoreHome/Tracker/LogTable/Visit.php
@@ -32,4 +32,8 @@ class Visit extends LogTable
return true;
}
+ public function getPrimaryKey()
+ {
+ return array('idvisit');
+ }
} \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/common/services/piwik-api.js b/plugins/CoreHome/angularjs/common/services/piwik-api.js
index 67f9756f4f..65127ece8a 100644
--- a/plugins/CoreHome/angularjs/common/services/piwik-api.js
+++ b/plugins/CoreHome/angularjs/common/services/piwik-api.js
@@ -214,7 +214,7 @@ var hasBlockedContent = false;
}
for (var key in defaultParams) {
- if (!getParamsToMixin[key] && !postParams[key] && defaultParams[key]) {
+ if (!(key in getParamsToMixin) && !(key in postParams) && defaultParams[key]) {
getParamsToMixin[key] = defaultParams[key];
}
}
diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js
index 1c94136827..d284034cd6 100644
--- a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js
+++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js
@@ -133,6 +133,12 @@
}
}
});
+ if ('object' === typeof widgetsHelper && widgetsHelper.availableWidgets) {
+ // lets also update widgetslist so will be easier to update list of available widgets in dashboard selector
+ // immediately
+ delete widgetsHelper.availableWidgets;
+ widgetsHelper.getAvailableWidgets();
+ }
});
$rootScope.$on('$locationChangeSuccess', function () {
diff --git a/plugins/CoreHome/lang/en.json b/plugins/CoreHome/lang/en.json
index a013ab30ae..1677e05d28 100644
--- a/plugins/CoreHome/lang/en.json
+++ b/plugins/CoreHome/lang/en.json
@@ -29,6 +29,10 @@
"InjectedHostSuperUserWarning": "Piwik may be misconfigured (for example, if Piwik was recently moved to a new server or URL). You can either %1$sclick here and add %2$s as the valid Piwik hostname (if you trust it)%3$s, or %4$sclick here and go to %5$s to access Piwik safely%6$s.",
"InjectedHostWarningIntro": "You are now accessing Piwik from %1$s, but Piwik has been configured to run at this address: %2$s.",
"JavascriptDisabled": "JavaScript must be enabled in order for you to use Piwik in standard view.<br \/>However, it seems JavaScript is either disabled or not supported by your browser.<br \/>To use standard view, enable JavaScript by changing your browser options, then %1$stry again%2$s.<br \/>",
+ "VisitStatusOrdered": "Ordered",
+ "VisitStatusOrderedThenAbandoned": "Ordered then Abandoned Cart",
+ "VisitTypeReturning": "Returning",
+ "VisitTypeReturningCustomer": "Returning Customer",
"MainNavigation": "Main navigation",
"MakeOneTimeDonation": "Make a one time donation, instead.",
"Menu": "Menu",
diff --git a/plugins/CoreHome/templates/_dataTableActions.twig b/plugins/CoreHome/templates/_dataTableActions.twig
index dca99c47c7..537fdffd97 100644
--- a/plugins/CoreHome/templates/_dataTableActions.twig
+++ b/plugins/CoreHome/templates/_dataTableActions.twig
@@ -46,9 +46,11 @@
{{ visualizationIcons|raw }}
{% endif %}
+ {% if properties.show_export %}
<a class='dropdown-button dataTableAction activateExportSelection'
href='javascript:;' title="{{ 'General_ExportThisReport'|translate|e('html_attr') }}"
data-activates='dropdownExport{{ randomIdForDropdown }}'><span class="icon-export"></span></a>
+ {% endif %}
{% if isPluginLoaded('Annotations') and not properties.hide_annotations_view %}
<a class='dataTableAction annotationView'
@@ -68,6 +70,7 @@
</a>
{% endif %}
+ {% if properties.show_export %}
<ul id='dropdownExport{{ randomIdForDropdown }}' class='dropdown-content exportToFormatItems'>
{% set requestParams = properties.request_parameters_to_modify|json_encode %}
<li><a target="_blank" requestParams="{{ requestParams|e('html_attr') }}" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="CSV" filter_limit="{{ properties.filter_limit }}">CSV</a></li>
@@ -89,6 +92,8 @@
</li>
{% endif %}
</ul>
+ {% endif %}
+
<ul id='dropdownConfigure{{ randomIdForDropdown }}' class='dropdown-content tableConfiguration'>
{% if properties.show_flatten_table %}
{% if clientSideParameters.flat is defined and clientSideParameters.flat == 1 %}
diff --git a/plugins/CorePluginsAdmin/CorePluginsAdmin.php b/plugins/CorePluginsAdmin/CorePluginsAdmin.php
index 8008fe529c..c12ad281bd 100644
--- a/plugins/CorePluginsAdmin/CorePluginsAdmin.php
+++ b/plugins/CorePluginsAdmin/CorePluginsAdmin.php
@@ -38,6 +38,7 @@ class CorePluginsAdmin extends Plugin
{
$stylesheets[] = "plugins/CorePluginsAdmin/stylesheets/plugins_admin.less";
$stylesheets[] = "plugins/CorePluginsAdmin/angularjs/plugin-settings/plugin-settings.directive.less";
+ $stylesheets[] = "plugins/CorePluginsAdmin/angularjs/form-field/field-expandable-select.less";
}
public static function isPluginsAdminEnabled()
diff --git a/plugins/CorePluginsAdmin/angularjs/field/field.directive.js b/plugins/CorePluginsAdmin/angularjs/field/field.directive.js
index 1268fb881f..fb07808201 100644
--- a/plugins/CorePluginsAdmin/angularjs/field/field.directive.js
+++ b/plugins/CorePluginsAdmin/angularjs/field/field.directive.js
@@ -134,6 +134,12 @@
}
});
+ $scope.$watch('title', function (val, oldVal) {
+ if (val !== oldVal) {
+ $scope.field.title = val;
+ }
+ });
+
if ('undefined' !== typeof $scope.placeholder && $scope.placeholder !== null) {
$scope.$watch('placeholder', function (val, oldVal) {
if (val !== oldVal) {
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-expandable-select.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-expandable-select.html
new file mode 100644
index 0000000000..2854e8b134
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-expandable-select.html
@@ -0,0 +1,24 @@
+<div class="expandableSelector" piwik-focus-anywhere-but-here="formField.showSelect = false">
+ <div ng-click="formField.showSelect = !formField.showSelect" class="select-wrapper"
+ ><span class="caret">â–¼</span><input type="text" class="select-dropdown"
+ readonly="true" value="{{ formField.title }}">
+ </div>
+
+ <div ng-show="formField.showSelect" class="expandableList z-depth-2">
+
+ <div class="searchContainer">
+ <input type="text" placeholder="Search"
+ ng-model="formField.searchTerm" class="expandableSearch browser-default"
+ piwik-focus-if="formField.showSelect">
+ </div>
+ <ul class="collection firstLevel">
+ <li ng-repeat="options in formField.availableOptions" class="collection-item" ng-show="options.values|filter:formField.searchTerm|length">
+ <h4 class="expandableListCategory" ng-click="formField.showCategory == options.group ? formField.showCategory = '' : formField.showCategory = options.group">{{ options.group }} <span class="secondary-content" ng-class='{"icon-arrow-right": formField.showCategory != options.group, "icon-arrow-bottom": formField.showCategory == options.group}'></span></h4>
+
+ <ul ng-show="formField.showCategory == options.group || formField.searchTerm" class="collection secondLevel">
+ <li class="expandableListItem collection-item valign-wrapper" ng-repeat="children in options.values|filter:formField.searchTerm" ng-click="formField.value = children.key;formField.showSelect = false;"><span class="primary-content">{{ children.value }}</span> <span ng-show="children.tooltip" title="{{ children.tooltip }}" class="secondary-content icon-help"></span></li>
+ </ul>
+ </li>
+ </ul>
+ </div>
+</div> \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-expandable-select.less b/plugins/CorePluginsAdmin/angularjs/form-field/field-expandable-select.less
new file mode 100644
index 0000000000..75a8c2428c
--- /dev/null
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-expandable-select.less
@@ -0,0 +1,88 @@
+.expandableSelector {
+
+ position: relative;
+
+ .secondary-content {
+ font-size: 16px;
+ margin-top: -3px;
+ color: @theme-color-link;
+ cursor: help;
+ }
+
+ ul {
+ min-width: 250px;
+
+ &.collection.firstLevel {
+ border-top: 0;
+ margin-top: 0;
+ margin-bottom: 0;
+ font-size: 12px;
+
+ > li {
+ padding: 0 !important;
+ }
+ }
+
+ .expandableListCategory {
+ padding: 10px 20px;
+ color: @theme-color-link;
+ }
+
+ li {
+ &:hover {
+ background: #f2f2f2 !important;
+ }
+
+ &.collection-item {
+ cursor: pointer;
+ }
+ }
+
+ ul {
+ margin-top: 0;
+ margin-bottom: 0;
+
+ .primary-content {
+ width: 100%;
+ }
+ .secondary-content {
+ margin-top: 3px;
+ }
+
+ li {
+ padding-top: 6px !important;
+ padding-bottom: 6px !important;
+ padding-left: 30px !important;
+ min-width: 200px;
+
+ &:hover {
+ background: #f2f2f2 !important;
+ }
+ }
+ }
+ }
+
+ .searchContainer {
+ padding: 5px;
+ border-left: 1px solid #e0e0e0;
+ border-right: 1px solid #e0e0e0;
+ border-top: 1px solid #e0e0e0;
+ }
+
+ .expandableSearch {
+ vertical-align: top;
+ padding: 7px 6px !important;
+ border: 1px solid #d0d0d0 !important;
+ background: #fff !important;
+ font-size: 11px !important;
+ color: #454545 !important;
+ width: 100% !important;
+ }
+
+ .expandableList {
+ position: absolute;
+ z-index: 9999;
+ margin-top: -48px;
+ background: #fff;
+ }
+} \ No newline at end of file
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-multiselect.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-multiselect.html
index b6dd7bcd94..72a0379f28 100644
--- a/plugins/CorePluginsAdmin/angularjs/form-field/field-multiselect.html
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-multiselect.html
@@ -2,7 +2,7 @@
<select multiple
name="{{ formField.name }}"
ng-model="formField.value"
- ng-options="t.key as t.value for t in formField.availableOptions"
+ ng-options="t.key as t.value group by t.group for t in formField.availableOptions"
piwik-attributes="{{formField.uiControlAttributes}}">
</select>
<label for="{{ formField.name }}" ng-bind-html="formField.title"></label>
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/field-radio.html b/plugins/CorePluginsAdmin/angularjs/form-field/field-radio.html
index 449d09621f..1363f4192a 100644
--- a/plugins/CorePluginsAdmin/angularjs/form-field/field-radio.html
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/field-radio.html
@@ -8,7 +8,7 @@
type="radio"
id="{{ formField.name + radioModel.key }}"
name="{{ formField.name }}"
- ng-disabled="radioModel.disabled"
+ ng-disabled="radioModel.disabled || formField.disabled"
piwik-attributes="{{formField.uiControlAttributes}}"
>
<label for="{{ formField.name + radioModel.key }}">{{ radioModel.value }}
diff --git a/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js b/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js
index 00fe79523f..ebfc0df6cb 100644
--- a/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js
+++ b/plugins/CorePluginsAdmin/angularjs/form-field/form-field.directive.js
@@ -214,6 +214,39 @@
return flatValues;
}
+ if (hasUiControl(field, 'expandable-select')) {
+ var availableValues = field.availableValues;
+ var flatValues = [];
+
+ var groups = {};
+ angular.forEach(availableValues, function (value) {
+
+ if (!value.group) {
+ value.group = '';
+ }
+
+ if (!(value.group in groups) || !groups[value.group]) {
+ groups[value.group] = {values: [], group: value.group}
+ }
+
+ var formatted = {key: value.key, value: value.value};
+
+ if ('tooltip' in value && value.tooltip) {
+ formatted.tooltip = value.tooltip;
+ }
+
+ groups[value.group].values.push(formatted);
+ });
+
+ angular.forEach(groups, function (group) {
+ if (group.values.length) {
+ flatValues.push(group);
+ }
+ });
+
+ return flatValues;
+ }
+
if (isSelectControl(field)) {
var availableValues = field.availableValues;
diff --git a/plugins/CustomDimensions b/plugins/CustomDimensions
-Subproject 453568bb424628b0759db99dd93acfe88222187
+Subproject 2926fb575a7e55c1ff9f2573694dd8908737ccb
diff --git a/plugins/CustomVariables/Categories/CustomVariablesCategory.php b/plugins/CustomVariables/Categories/CustomVariablesCategory.php
new file mode 100644
index 0000000000..ced9de0563
--- /dev/null
+++ b/plugins/CustomVariables/Categories/CustomVariablesCategory.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CustomVariables\Categories;
+
+use Piwik\Category\Category;
+
+class CustomVariablesCategory extends Category
+{
+ protected $id = 'CustomVariables_CustomVariables';
+ protected $order = 8;
+
+}
diff --git a/plugins/CustomVariables/Columns/SearchCategory.php b/plugins/CustomVariables/Columns/SearchCategory.php
new file mode 100644
index 0000000000..abeac9f865
--- /dev/null
+++ b/plugins/CustomVariables/Columns/SearchCategory.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CustomVariables\Columns;
+
+use Piwik\Columns\Discriminator;
+use Piwik\Plugin\Dimension\ActionDimension;
+use Piwik\Plugins\Actions\Actions\ActionSiteSearch;
+
+class SearchCategory extends ActionDimension
+{
+ protected $type = self::TYPE_TEXT;
+ protected $columnName = 'custom_var_v4';
+ protected $nameSingular = 'Actions_SiteSearchCategory';
+ protected $namePlural = 'Actions_SiteSearchCategories';
+
+ public function getDbDiscriminator()
+ {
+ return new Discriminator($this->dbTableName, 'custom_var_k4', ActionSiteSearch::CVAR_KEY_SEARCH_CATEGORY);
+ }
+} \ No newline at end of file
diff --git a/plugins/CustomVariables/CustomDimension.php b/plugins/CustomVariables/CustomDimension.php
new file mode 100644
index 0000000000..cd899503cf
--- /dev/null
+++ b/plugins/CustomVariables/CustomDimension.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CustomVariables;
+
+use Piwik\Columns\Dimension;
+use Piwik\Piwik;
+
+class CustomDimension extends Dimension
+{
+ protected $type = self::TYPE_TEXT;
+
+ private $id;
+
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ public function initCustomDimension($index, Model $scope)
+ {
+ $category = $scope->getScopeDescription();
+
+ $this->id = 'CustomVariables.CustomVariable' . ucfirst($scope->getScope()) . $index;
+ $this->nameSingular = Piwik::translate('CustomVariables_ColumnCustomVariableValue') . ' ' . $index . ' (' . $category .')';
+ $this->columnName = 'custom_var_v' . (int) $index;
+ $this->category = 'CustomVariables_CustomVariables';
+ $this->dbTableName = $scope->getUnprefixedTableName();
+ }
+
+} \ No newline at end of file
diff --git a/plugins/CustomVariables/CustomVariables.php b/plugins/CustomVariables/CustomVariables.php
index e5a13bc52a..c18eaf946c 100644
--- a/plugins/CustomVariables/CustomVariables.php
+++ b/plugins/CustomVariables/CustomVariables.php
@@ -23,6 +23,7 @@ class CustomVariables extends \Piwik\Plugin
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
+ 'Dimension.addDimensions' => 'addDimensions',
'Actions.getCustomActionDimensionFieldsAndJoins' => 'provideActionDimensionFields'
);
}
@@ -37,6 +38,24 @@ class CustomVariables extends \Piwik\Plugin
Model::uninstall();
}
+ public function addDimensions(&$instances)
+ {
+ foreach (Model::getScopes() as $scope) {
+ $model = new Model($scope);
+ try {
+ $highestIndex = $model->getHighestCustomVarIndex();
+ } catch (\Exception $e) {
+ continue; // ignore error for tests to work as this might be executed before Piwik tables are installed
+ }
+
+ foreach (range(1, $highestIndex) as $index) {
+ $custom = new CustomDimension();
+ $custom->initCustomDimension($index, $model);
+ $instances[] = $custom;
+ }
+ }
+ }
+
/**
* There are also some hardcoded places in JavaScript
* @return int
diff --git a/plugins/CustomVariables/Model.php b/plugins/CustomVariables/Model.php
index 235809e1da..5abb327f0e 100644
--- a/plugins/CustomVariables/Model.php
+++ b/plugins/CustomVariables/Model.php
@@ -12,6 +12,7 @@ use Piwik\Common;
use Piwik\DataTable;
use Piwik\Db;
use Piwik\Log;
+use Piwik\Piwik;
class Model
{
@@ -23,6 +24,7 @@ class Model
private $scope = null;
private $table = null;
+ private $tableUnprefixed = null;
public function __construct($scope)
{
@@ -31,7 +33,13 @@ class Model
}
$this->scope = $scope;
- $this->table = Common::prefixTable($this->getTableNameFromScope($scope));
+ $this->tableUnprefixed = $this->getTableNameFromScope($scope);
+ $this->table = Common::prefixTable($this->tableUnprefixed);
+ }
+
+ public function getUnprefixedTableName()
+ {
+ return $this->tableUnprefixed;
}
private function getTableNameFromScope($scope)
@@ -47,11 +55,30 @@ class Model
}
}
+ public function getScope()
+ {
+ return $this->scope;
+ }
+
public function getScopeName()
{
return ucfirst($this->scope);
}
+ public function getScopeDescription()
+ {
+ switch ($this->scope) {
+ case Model::SCOPE_PAGE:
+ return Piwik::translate('CustomVariables_ScopePage');
+ case Model::SCOPE_VISIT:
+ return Piwik::translate('CustomVariables_ScopeVisit');
+ case Model::SCOPE_CONVERSION:
+ return Piwik::translate('CustomVariables_ScopeConversion');
+ }
+
+ return ucfirst($this->scope);
+ }
+
/**
* @see getHighestCustomVarIndex()
* @return int
diff --git a/plugins/CustomVariables/lang/en.json b/plugins/CustomVariables/lang/en.json
index e065886759..56a6d3380b 100644
--- a/plugins/CustomVariables/lang/en.json
+++ b/plugins/CustomVariables/lang/en.json
@@ -7,6 +7,7 @@
"PluginDescription": "Custom Variables are (name, value) pairs that you can assign using the Javascript API to visitors or any of their action. Piwik will then report how many visits, pages, conversions for each of these custom names and values. View the detailed Custom Variables for each user and action in the Visitor Log.<br />Required to use <a href=\"http://piwik.org/docs/ecommerce-analytics/\">Ecommerce Analytics</a> feature!",
"ScopePage": "scope page",
"ScopeVisit": "scope visit",
+ "ScopeConversion": "scope conversion",
"ManageDescription": "This overview shows all custom variable slots and their usages for website '%s'. The names within each slot are ordered by how often they were used in total.",
"ScopeX": "Scope %s",
"Index": "Index",
diff --git a/plugins/CustomVariables/tests/Integration/ModelTest.php b/plugins/CustomVariables/tests/Integration/ModelTest.php
index 905a5f6a1d..6f504f9525 100644
--- a/plugins/CustomVariables/tests/Integration/ModelTest.php
+++ b/plugins/CustomVariables/tests/Integration/ModelTest.php
@@ -102,6 +102,26 @@ class ModelTest extends IntegrationTestCase
$this->assertEquals('Conversion', $this->getConversionScope()->getScopeName());
}
+ public function testGetScopeDescription()
+ {
+ $this->assertEquals('scope page', $this->getPageScope()->getScopeDescription());
+ $this->assertEquals('scope visit', $this->getVisitScope()->getScopeDescription());
+ $this->assertEquals('scope conversion', $this->getConversionScope()->getScopeDescription());
+ }
+
+ public function testGetUnprefixedTableName()
+ {
+ $this->assertEquals('log_link_visit_action', $this->getPageScope()->getUnprefixedTableName());
+ $this->assertEquals('log_visit', $this->getVisitScope()->getUnprefixedTableName());
+ $this->assertEquals('log_conversion', $this->getConversionScope()->getUnprefixedTableName());
+ }
+
+ public function testGetScope()
+ {
+ $this->assertEquals(Model::SCOPE_PAGE, $this->getPageScope()->getScope());
+ $this->assertEquals(Model::SCOPE_VISIT, $this->getVisitScope()->getScope());
+ }
+
public function test_getCurrentNumCustomVars()
{
$this->assertEquals(5, $this->getPageScope()->getCurrentNumCustomVars());
diff --git a/plugins/DevicePlugins/Columns/Plugin.php b/plugins/DevicePlugins/Columns/Plugin.php
index 0066c5ce19..a01d01c57c 100644
--- a/plugins/DevicePlugins/Columns/Plugin.php
+++ b/plugins/DevicePlugins/Columns/Plugin.php
@@ -13,8 +13,5 @@ use Piwik\Piwik;
class Plugin extends Dimension
{
- public function getName()
- {
- return Piwik::translate('General_Plugin');
- }
+ protected $nameSingular = 'General_Plugin';
} \ No newline at end of file
diff --git a/plugins/DevicePlugins/Columns/PluginCookie.php b/plugins/DevicePlugins/Columns/PluginCookie.php
index 19dd1e226d..b07aad3587 100644
--- a/plugins/DevicePlugins/Columns/PluginCookie.php
+++ b/plugins/DevicePlugins/Columns/PluginCookie.php
@@ -17,6 +17,7 @@ class PluginCookie extends DevicePluginColumn
{
protected $columnName = 'config_cookie';
protected $columnType = 'TINYINT(1) NULL';
+ protected $type = self::TYPE_BOOL;
/**
* @param Request $request
diff --git a/plugins/DevicePlugins/Columns/PluginDirector.php b/plugins/DevicePlugins/Columns/PluginDirector.php
index b1b479d92d..209e718c6e 100644
--- a/plugins/DevicePlugins/Columns/PluginDirector.php
+++ b/plugins/DevicePlugins/Columns/PluginDirector.php
@@ -17,6 +17,7 @@ class PluginDirector extends DevicePluginColumn
{
protected $columnName = 'config_director';
protected $columnType = 'TINYINT(1) NULL';
+ protected $type = self::TYPE_BOOL;
/**
* @param Request $request
diff --git a/plugins/DevicePlugins/Columns/PluginFlash.php b/plugins/DevicePlugins/Columns/PluginFlash.php
index e307ef0c84..2b17c70681 100644
--- a/plugins/DevicePlugins/Columns/PluginFlash.php
+++ b/plugins/DevicePlugins/Columns/PluginFlash.php
@@ -17,6 +17,7 @@ class PluginFlash extends DevicePluginColumn
{
protected $columnName = 'config_flash';
protected $columnType = 'TINYINT(1) NULL';
+ protected $type = self::TYPE_BOOL;
/**
* @param Request $request
diff --git a/plugins/DevicePlugins/Columns/PluginGears.php b/plugins/DevicePlugins/Columns/PluginGears.php
index c0845f0904..c8b40235f4 100644
--- a/plugins/DevicePlugins/Columns/PluginGears.php
+++ b/plugins/DevicePlugins/Columns/PluginGears.php
@@ -17,6 +17,7 @@ class PluginGears extends DevicePluginColumn
{
protected $columnName = 'config_gears';
protected $columnType = 'TINYINT(1) NULL';
+ protected $type = self::TYPE_BOOL;
/**
* @param Request $request
diff --git a/plugins/DevicePlugins/Columns/PluginJava.php b/plugins/DevicePlugins/Columns/PluginJava.php
index bd9877a1b5..d23c3a3c3e 100644
--- a/plugins/DevicePlugins/Columns/PluginJava.php
+++ b/plugins/DevicePlugins/Columns/PluginJava.php
@@ -17,6 +17,7 @@ class PluginJava extends DevicePluginColumn
{
protected $columnName = 'config_java';
protected $columnType = 'TINYINT(1) NULL';
+ protected $type = self::TYPE_BOOL;
/**
* @param Request $request
diff --git a/plugins/DevicePlugins/Columns/PluginPdf.php b/plugins/DevicePlugins/Columns/PluginPdf.php
index bd1fccfad3..69945b3d66 100644
--- a/plugins/DevicePlugins/Columns/PluginPdf.php
+++ b/plugins/DevicePlugins/Columns/PluginPdf.php
@@ -17,6 +17,7 @@ class PluginPdf extends DevicePluginColumn
{
protected $columnName = 'config_pdf';
protected $columnType = 'TINYINT(1) NULL';
+ protected $type = self::TYPE_BOOL;
/**
* @param Request $request
diff --git a/plugins/DevicePlugins/Columns/PluginQuickTime.php b/plugins/DevicePlugins/Columns/PluginQuickTime.php
index a203dedccb..57e6bc754e 100644
--- a/plugins/DevicePlugins/Columns/PluginQuickTime.php
+++ b/plugins/DevicePlugins/Columns/PluginQuickTime.php
@@ -17,6 +17,7 @@ class PluginQuickTime extends DevicePluginColumn
{
protected $columnName = 'config_quicktime';
protected $columnType = 'TINYINT(1) NULL';
+ protected $type = self::TYPE_BOOL;
/**
* @param Request $request
diff --git a/plugins/DevicePlugins/Columns/PluginRealPlayer.php b/plugins/DevicePlugins/Columns/PluginRealPlayer.php
index d805aafccd..2de970caa4 100644
--- a/plugins/DevicePlugins/Columns/PluginRealPlayer.php
+++ b/plugins/DevicePlugins/Columns/PluginRealPlayer.php
@@ -17,6 +17,7 @@ class PluginRealPlayer extends DevicePluginColumn
{
protected $columnName = 'config_realplayer';
protected $columnType = 'TINYINT(1) NULL';
+ protected $type = self::TYPE_BOOL;
/**
* @param Request $request
diff --git a/plugins/DevicePlugins/Columns/PluginSilverlight.php b/plugins/DevicePlugins/Columns/PluginSilverlight.php
index 897d2939da..154aea8975 100644
--- a/plugins/DevicePlugins/Columns/PluginSilverlight.php
+++ b/plugins/DevicePlugins/Columns/PluginSilverlight.php
@@ -17,6 +17,7 @@ class PluginSilverlight extends DevicePluginColumn
{
protected $columnName = 'config_silverlight';
protected $columnType = 'TINYINT(1) NULL';
+ protected $type = self::TYPE_BOOL;
/**
* @param Request $request
diff --git a/plugins/DevicePlugins/Columns/PluginWindowsMedia.php b/plugins/DevicePlugins/Columns/PluginWindowsMedia.php
index 99e25f4bdc..6318fd0f55 100644
--- a/plugins/DevicePlugins/Columns/PluginWindowsMedia.php
+++ b/plugins/DevicePlugins/Columns/PluginWindowsMedia.php
@@ -17,6 +17,7 @@ class PluginWindowsMedia extends DevicePluginColumn
{
protected $columnName = 'config_windowsmedia';
protected $columnType = 'TINYINT(1) NULL';
+ protected $type = self::TYPE_BOOL;
/**
* @param Request $request
diff --git a/plugins/DevicesDetection/Columns/BrowserEngine.php b/plugins/DevicesDetection/Columns/BrowserEngine.php
index 1639ec59fe..c9f60cd73e 100644
--- a/plugins/DevicesDetection/Columns/BrowserEngine.php
+++ b/plugins/DevicesDetection/Columns/BrowserEngine.php
@@ -8,8 +8,7 @@
*/
namespace Piwik\Plugins\DevicesDetection\Columns;
-use Piwik\Piwik;
-use Piwik\Plugins\DevicesDetection\Segment;
+use Piwik\Metrics\Formatter;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
@@ -18,20 +17,16 @@ class BrowserEngine extends Base
{
protected $columnName = 'config_browser_engine';
protected $columnType = 'VARCHAR(10) NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('browserEngine');
- $segment->setName('DevicesDetection_BrowserEngine');
- $segment->setAcceptedValues('Trident, WebKit, Presto, Gecko, Blink, etc.');
- $segment->setSuggestedValuesCallback('\DeviceDetector\Parser\Client\Browser\Engine::getAvailableEngines');
- $this->addSegment($segment);
- }
-
- public function getName()
+ protected $segmentName = 'browserEngine';
+ protected $nameSingular = 'DevicesDetection_BrowserEngine';
+ protected $namePlural = 'DevicesDetection_BrowserEngines';
+ protected $acceptValues = 'Trident, WebKit, Presto, Gecko, Blink, etc.';
+ protected $suggestedValuesCallback = '\DeviceDetector\Parser\Client\Browser\Engine::getAvailableEngines';
+ protected $type = self::TYPE_TEXT;
+
+ public function formatValue($value, $idSite, Formatter $formatter)
{
- return Piwik::translate('DevicesDetection_BrowserEngine');
+ return \Piwik\Plugins\DevicesDetection\getBrowserEngineName($value);
}
/**
diff --git a/plugins/DevicesDetection/Columns/BrowserName.php b/plugins/DevicesDetection/Columns/BrowserName.php
index b60b87f5af..0fe0d724d5 100644
--- a/plugins/DevicesDetection/Columns/BrowserName.php
+++ b/plugins/DevicesDetection/Columns/BrowserName.php
@@ -8,8 +8,7 @@
*/
namespace Piwik\Plugins\DevicesDetection\Columns;
-use Piwik\Piwik;
-use Piwik\Plugins\DevicesDetection\Segment;
+use Piwik\Metrics\Formatter;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
@@ -18,19 +17,15 @@ class BrowserName extends Base
{
protected $columnName = 'config_browser_name';
protected $columnType = 'VARCHAR(10) NULL';
+ protected $segmentName = 'browserCode';
+ protected $nameSingular = 'DevicesDetection_ColumnBrowser';
+ protected $namePlural = 'DevicesDetection_Browsers';
+ protected $acceptValues = 'FF, IE, CH, SF, OP, etc.';
+ protected $type = self::TYPE_TEXT;
- protected function configureSegments()
+ public function formatValue($value, $idSite, Formatter $formatter)
{
- $segment = new Segment();
- $segment->setSegment('browserCode');
- $segment->setName('DevicesDetection_ColumnBrowser');
- $segment->setAcceptedValues('FF, IE, CH, SF, OP, etc.');
- $this->addSegment($segment);
- }
-
- public function getName()
- {
- return Piwik::translate('DevicesDetection_ColumnBrowser');
+ return \Piwik\Plugins\DevicesDetection\getBrowserName($value);
}
/**
diff --git a/plugins/DevicesDetection/Columns/BrowserVersion.php b/plugins/DevicesDetection/Columns/BrowserVersion.php
index aabca4e9fb..9fd4593aac 100644
--- a/plugins/DevicesDetection/Columns/BrowserVersion.php
+++ b/plugins/DevicesDetection/Columns/BrowserVersion.php
@@ -8,8 +8,6 @@
*/
namespace Piwik\Plugins\DevicesDetection\Columns;
-use Piwik\Piwik;
-use Piwik\Plugins\DevicesDetection\Segment;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
@@ -18,20 +16,11 @@ class BrowserVersion extends Base
{
protected $columnName = 'config_browser_version';
protected $columnType = 'VARCHAR(20) NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('browserVersion');
- $segment->setName('DevicesDetection_BrowserVersion');
- $segment->setAcceptedValues('1.0, 8.0, etc.');
- $this->addSegment($segment);
- }
-
- public function getName()
- {
- return Piwik::translate('DevicesDetection_BrowserVersion');
- }
+ protected $segmentName = 'browserVersion';
+ protected $nameSingular = 'DevicesDetection_BrowserVersion';
+ protected $namePlural = 'DevicesDetection_BrowserVersions';
+ protected $acceptValues = '1.0, 8.0, etc.';
+ protected $type = self::TYPE_TEXT;
/**
* @param Request $request
diff --git a/plugins/DevicesDetection/Columns/DeviceBrand.php b/plugins/DevicesDetection/Columns/DeviceBrand.php
index 8b0bfdc35e..ee4db69954 100644
--- a/plugins/DevicesDetection/Columns/DeviceBrand.php
+++ b/plugins/DevicesDetection/Columns/DeviceBrand.php
@@ -9,8 +9,8 @@
namespace Piwik\Plugins\DevicesDetection\Columns;
use DeviceDetector\Parser\Device\DeviceParserAbstract;
+use Piwik\Metrics\Formatter;
use Piwik\Piwik;
-use Piwik\Plugins\DevicesDetection\Segment;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
@@ -19,23 +19,25 @@ class DeviceBrand extends Base
{
protected $columnName = 'config_device_brand';
protected $columnType = 'VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL';
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'DevicesDetection_DeviceBrand';
+ protected $namePlural = 'DevicesDetection_DeviceBrands';
+ protected $segmentName = 'deviceBrand';
- public function getName()
+
+ public function formatValue($value, $idSite, Formatter $formatter)
{
- return Piwik::translate('DevicesDetection_DeviceBrand');
+ return \Piwik\Plugins\DevicesDetection\getDeviceBrandLabel($value);
}
- protected function configureSegments()
+ public function __construct()
{
$brands = DeviceParserAbstract::$deviceBrands;
natcasesort ($brands);
$brandList = implode(", ", $brands);
+ $this->acceptValues = $brandList;
- $segment = new Segment();
- $segment->setSegment('deviceBrand');
- $segment->setName('DevicesDetection_DeviceBrand');
- $segment->setAcceptedValues($brandList);
- $segment->setSqlFilter(function ($brand) use ($brandList, $brands) {
+ $this->sqlFilter = function ($brand) use ($brandList, $brands) {
if ($brand == Piwik::translate('General_Unknown')) {
return '';
}
@@ -44,8 +46,7 @@ class DeviceBrand extends Base
throw new \Exception("deviceBrand segment must be one of: $brandList");
}
return $index;
- });
- $this->addSegment($segment);
+ };
}
/**
diff --git a/plugins/DevicesDetection/Columns/DeviceModel.php b/plugins/DevicesDetection/Columns/DeviceModel.php
index 5f74686795..4ac962f0e1 100644
--- a/plugins/DevicesDetection/Columns/DeviceModel.php
+++ b/plugins/DevicesDetection/Columns/DeviceModel.php
@@ -8,7 +8,6 @@
*/
namespace Piwik\Plugins\DevicesDetection\Columns;
-use Piwik\Piwik;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
@@ -17,11 +16,9 @@ class DeviceModel extends Base
{
protected $columnName = 'config_device_model';
protected $columnType = 'VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL';
-
- public function getName()
- {
- return Piwik::translate('DevicesDetection_DeviceModel');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'DevicesDetection_DeviceModel';
+ protected $namePlural = 'DevicesDetection_DeviceModels';
/**
* @param Request $request
diff --git a/plugins/DevicesDetection/Columns/DeviceType.php b/plugins/DevicesDetection/Columns/DeviceType.php
index 6e18d7b3b0..10233e7c92 100644
--- a/plugins/DevicesDetection/Columns/DeviceType.php
+++ b/plugins/DevicesDetection/Columns/DeviceType.php
@@ -8,8 +8,7 @@
*/
namespace Piwik\Plugins\DevicesDetection\Columns;
-use Piwik\Piwik;
-use Piwik\Plugin\Segment;
+use Piwik\Metrics\Formatter;
use Piwik\Tracker\Request;
use Exception;
use Piwik\Tracker\Visitor;
@@ -20,31 +19,28 @@ class DeviceType extends Base
{
protected $columnName = 'config_device_type';
protected $columnType = 'TINYINT( 100 ) NULL DEFAULT NULL';
+ protected $segmentName = 'deviceType';
+ protected $type = self::TYPE_ENUM;
+ protected $nameSingular = 'DevicesDetection_DeviceType';
+ protected $namePlural = 'DevicesDetection_DeviceTypes';
- protected function configureSegments()
+ public function __construct()
{
$deviceTypes = DeviceParser::getAvailableDeviceTypeNames();
$deviceTypeList = implode(", ", $deviceTypes);
- $segment = new Segment();
- $segment->setCategory('General_Visit');
- $segment->setSegment('deviceType');
- $segment->setName('DevicesDetection_DeviceType');
- $segment->setAcceptedValues($deviceTypeList);
- $segment->setSqlFilter(function ($type) use ($deviceTypeList, $deviceTypes) {
- $index = array_search(strtolower(trim(urldecode($type))), $deviceTypes);
- if ($index === false) {
- throw new Exception("deviceType segment must be one of: $deviceTypeList");
- }
- return $index;
- });
+ $this->acceptValues = $deviceTypeList;
+ }
- $this->addSegment($segment);
+ public function formatValue($value, $idSite, Formatter $formatter)
+ {
+ return \Piwik\Plugins\DevicesDetection\getDeviceTypeLabel($value);
}
- public function getName()
+ public function getEnumColumnValues()
{
- return Piwik::translate('DevicesDetection_DeviceType');
+ $values = DeviceParser::getAvailableDeviceTypes();
+ return array_flip($values);
}
/**
diff --git a/plugins/DevicesDetection/Columns/Os.php b/plugins/DevicesDetection/Columns/Os.php
index a787340dc2..455b971486 100644
--- a/plugins/DevicesDetection/Columns/Os.php
+++ b/plugins/DevicesDetection/Columns/Os.php
@@ -8,8 +8,8 @@
*/
namespace Piwik\Plugins\DevicesDetection\Columns;
+use Piwik\Metrics\Formatter;
use Piwik\Piwik;
-use Piwik\Plugins\DevicesDetection\Segment;
use Piwik\Tracker\Request;
use Piwik\Tracker\Settings;
use Piwik\Tracker\Visitor;
@@ -19,14 +19,15 @@ class Os extends Base
{
protected $columnName = 'config_os';
protected $columnType = 'CHAR(3) NULL';
+ protected $segmentName = 'operatingSystemCode';
+ protected $nameSingular = 'DevicesDetection_ColumnOperatingSystem';
+ protected $namePlural = 'DevicesDetection_OperatingSystems';
+ protected $acceptValues = 'WIN, MAC, LIN, AND, IPD, etc.';
+ protected $type = self::TYPE_TEXT;
- protected function configureSegments()
+ public function formatValue($value, $idSite, Formatter $formatter)
{
- $segment = new Segment();
- $segment->setSegment('operatingSystemCode');
- $segment->setName('DevicesDetection_ColumnOperatingSystem');
- $segment->setAcceptedValues('WIN, MAC, LIN, AND, IPD, etc.');
- $this->addSegment($segment);
+ return \Piwik\Plugins\DevicesDetection\getOSFamilyFullName($value);
}
public function getName()
diff --git a/plugins/DevicesDetection/Columns/OsVersion.php b/plugins/DevicesDetection/Columns/OsVersion.php
index d1c3cb422a..ef7d9b6dca 100644
--- a/plugins/DevicesDetection/Columns/OsVersion.php
+++ b/plugins/DevicesDetection/Columns/OsVersion.php
@@ -8,8 +8,6 @@
*/
namespace Piwik\Plugins\DevicesDetection\Columns;
-use Piwik\Piwik;
-use Piwik\Plugins\DevicesDetection\Segment;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
@@ -18,20 +16,11 @@ class OsVersion extends Base
{
protected $columnName = 'config_os_version';
protected $columnType = 'VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('operatingSystemVersion');
- $segment->setName('DevicesDetection_ColumnOperatingSystemVersion');
- $segment->setAcceptedValues('XP, 7, 2.3, 5.1, ...');
- $this->addSegment($segment);
- }
-
- public function getName()
- {
- return Piwik::translate('DevicesDetection_OperatingSystemVersions');
- }
+ protected $nameSingular = 'DevicesDetection_ColumnOperatingSystemVersion';
+ protected $namePlural = 'DevicesDetection_OperatingSystemVersions';
+ protected $segmentName = 'operatingSystemVersion';
+ protected $acceptValues = 'XP, 7, 2.3, 5.1, ...';
+ protected $type = self::TYPE_TEXT;
/**
* @param Request $request
diff --git a/plugins/DevicesDetection/Segment.php b/plugins/DevicesDetection/Segment.php
deleted file mode 100644
index 8f0858facb..0000000000
--- a/plugins/DevicesDetection/Segment.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-namespace Piwik\Plugins\DevicesDetection;
-
-/**
- * UserSettings segment base class.
- *
- */
-class Segment extends \Piwik\Plugin\Segment
-{
- protected function init()
- {
- $this->setCategory('General_Visit');
- }
-}
diff --git a/plugins/DevicesDetection/lang/en.json b/plugins/DevicesDetection/lang/en.json
index 7015cefe04..c31c5726be 100644
--- a/plugins/DevicesDetection/lang/en.json
+++ b/plugins/DevicesDetection/lang/en.json
@@ -20,10 +20,13 @@
"dataTableLabelTypes": "Type",
"Device": "Device",
"DeviceBrand": "Device brand",
+ "DeviceBrands": "Device brands",
"DeviceDetection": "Device detection",
"DeviceModel": "Device model",
+ "DeviceModels": "Device models",
"DevicesDetection": "Visitor Devices",
"DeviceType": "Device type",
+ "DeviceTypes": "Device types",
"FeaturePhone": "Feature phone",
"OperatingSystemFamilies": "Operating System families",
"OperatingSystemFamily": "Operating system family",
diff --git a/plugins/Ecommerce/Columns/Items.php b/plugins/Ecommerce/Columns/Items.php
new file mode 100644
index 0000000000..834b020577
--- /dev/null
+++ b/plugins/Ecommerce/Columns/Items.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Ecommerce\Columns;
+
+class Items extends BaseConversion
+{
+ protected $columnName = 'items';
+ protected $type = self::TYPE_NUMBER;
+ protected $category = 'Goals_Ecommerce';
+ protected $nameSingular = 'Ecommerce_NumberOfItems';
+
+} \ No newline at end of file
diff --git a/plugins/Ecommerce/Columns/Order.php b/plugins/Ecommerce/Columns/Order.php
new file mode 100644
index 0000000000..346c474753
--- /dev/null
+++ b/plugins/Ecommerce/Columns/Order.php
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Ecommerce\Columns;
+
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\MetricsList;
+use Piwik\Plugin\ArchivedMetric;
+use Piwik\Tracker\GoalManager;
+
+class Order extends BaseConversion
+{
+ protected $columnName = 'idorder';
+ protected $type = self::TYPE_NUMBER;
+ protected $category = 'Goals_Ecommerce';
+ protected $nameSingular = 'Ecommerce_Order';
+ protected $namePlural = 'Ecommerce_Orders';
+ protected $metricId = 'orders';
+
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
+ {
+ $metric = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_UNIQUE);
+ $metricsList->addMetric($metric);
+ }
+
+ public function getDbDiscriminator()
+ {
+ return new Discriminator($this->dbTableName, 'idgoal', GoalManager::IDGOAL_ORDER);
+ }
+
+} \ No newline at end of file
diff --git a/plugins/Ecommerce/Columns/ProductCategory.php b/plugins/Ecommerce/Columns/ProductCategory.php
index 1b5aa0b2e2..c080dde65a 100644
--- a/plugins/Ecommerce/Columns/ProductCategory.php
+++ b/plugins/Ecommerce/Columns/ProductCategory.php
@@ -13,8 +13,7 @@ use Piwik\Piwik;
class ProductCategory extends Dimension
{
- public function getName()
- {
- return Piwik::translate('Goals_ProductCategory');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $category = 'Goals_Ecommerce';
+ protected $nameSingular = 'Goals_ProductCategory';
} \ No newline at end of file
diff --git a/plugins/Ecommerce/Columns/ProductName.php b/plugins/Ecommerce/Columns/ProductName.php
index 8c9d43c8a6..ab7033a507 100644
--- a/plugins/Ecommerce/Columns/ProductName.php
+++ b/plugins/Ecommerce/Columns/ProductName.php
@@ -9,12 +9,27 @@
namespace Piwik\Plugins\Ecommerce\Columns;
use Piwik\Columns\Dimension;
-use Piwik\Piwik;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
+use Piwik\Tracker\Action;
class ProductName extends Dimension
{
- public function getName()
+ protected $type = self::TYPE_TEXT;
+ protected $dbTableName = 'log_conversion_item';
+ protected $columnName = 'idaction_name';
+ protected $nameSingular = 'Goals_ProductName';
+ protected $namePlural = 'Goals_ProductNames';
+ protected $category = 'Goals_Ecommerce';
+
+ public function getDbColumnJoin()
{
- return Piwik::translate('Goals_ProductName');
+ return new ActionNameJoin();
}
+
+ public function getDbDiscriminator()
+ {
+ return new Discriminator('log_action', 'type', Action::TYPE_ECOMMERCE_ITEM_NAME);
+ }
+
} \ No newline at end of file
diff --git a/plugins/Ecommerce/Columns/ProductPrice.php b/plugins/Ecommerce/Columns/ProductPrice.php
new file mode 100644
index 0000000000..9d935d8253
--- /dev/null
+++ b/plugins/Ecommerce/Columns/ProductPrice.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Ecommerce\Columns;
+
+use Piwik\Columns\Dimension;
+
+class ProductPrice extends Dimension
+{
+ protected $type = self::TYPE_MONEY;
+ protected $dbTableName = 'log_conversion_item';
+ protected $columnName = 'price';
+ protected $nameSingular = 'Goals_ProductPrice';
+ protected $category = 'Goals_Ecommerce';
+
+} \ No newline at end of file
diff --git a/plugins/Ecommerce/Columns/ProductQuantity.php b/plugins/Ecommerce/Columns/ProductQuantity.php
new file mode 100644
index 0000000000..18d50c94c2
--- /dev/null
+++ b/plugins/Ecommerce/Columns/ProductQuantity.php
@@ -0,0 +1,21 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Ecommerce\Columns;
+
+use Piwik\Columns\Dimension;
+
+class ProductQuantity extends Dimension
+{
+ protected $type = self::TYPE_NUMBER;
+ protected $dbTableName = 'log_conversion_item';
+ protected $columnName = 'quantity';
+ protected $nameSingular = 'Goals_ProductQuantity';
+ protected $category = 'Goals_Ecommerce';
+
+} \ No newline at end of file
diff --git a/plugins/Ecommerce/Columns/ProductSku.php b/plugins/Ecommerce/Columns/ProductSku.php
index c0a53c503b..2bd97b4988 100644
--- a/plugins/Ecommerce/Columns/ProductSku.php
+++ b/plugins/Ecommerce/Columns/ProductSku.php
@@ -9,12 +9,27 @@
namespace Piwik\Plugins\Ecommerce\Columns;
use Piwik\Columns\Dimension;
-use Piwik\Piwik;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
+use Piwik\Tracker\Action;
class ProductSku extends Dimension
{
- public function getName()
+ protected $type = self::TYPE_TEXT;
+ protected $dbTableName = 'log_conversion_item';
+ protected $columnName = 'idaction_sku';
+ protected $nameSingular = 'Goals_ProductSKU';
+ protected $namePlural = 'Goals_ProductSKUs';
+ protected $category = 'Goals_Ecommerce';
+
+ public function getDbColumnJoin()
{
- return Piwik::translate('Goals_ProductSKU');
+ return new ActionNameJoin();
}
+
+ public function getDbDiscriminator()
+ {
+ return new Discriminator('log_action', 'type', Action::TYPE_ECOMMERCE_ITEM_SKU);
+ }
+
} \ No newline at end of file
diff --git a/plugins/Ecommerce/Columns/Revenue.php b/plugins/Ecommerce/Columns/Revenue.php
index ae37315ac7..c5b39edd8c 100644
--- a/plugins/Ecommerce/Columns/Revenue.php
+++ b/plugins/Ecommerce/Columns/Revenue.php
@@ -8,6 +8,7 @@
*/
namespace Piwik\Plugins\Ecommerce\Columns;
+use Piwik\Columns\Discriminator;
use Piwik\Tracker\GoalManager;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -17,6 +18,14 @@ class Revenue extends BaseConversion
{
protected $columnName = 'revenue';
protected $columnType = 'float default NULL';
+ protected $type = self::TYPE_MONEY;
+ protected $category = 'Goals_Ecommerce';
+ protected $nameSingular = 'Ecommerce_OrderValue';
+
+ public function getDbDiscriminator()
+ {
+ return new Discriminator($this->dbTableName, 'idgoal', GoalManager::IDGOAL_ORDER);
+ }
/**
* @param Request $request
diff --git a/plugins/Ecommerce/Columns/RevenueDiscount.php b/plugins/Ecommerce/Columns/RevenueDiscount.php
index 679d6c4ac7..b734be2235 100644
--- a/plugins/Ecommerce/Columns/RevenueDiscount.php
+++ b/plugins/Ecommerce/Columns/RevenueDiscount.php
@@ -17,6 +17,9 @@ class RevenueDiscount extends BaseConversion
{
protected $columnName = 'revenue_discount';
protected $columnType = 'float default NULL';
+ protected $type = self::TYPE_MONEY;
+ protected $category = 'Goals_Ecommerce';
+ protected $nameSingular = 'General_Discount';
/**
* @param Request $request
diff --git a/plugins/Ecommerce/Columns/RevenueShipping.php b/plugins/Ecommerce/Columns/RevenueShipping.php
index 41591ca2e1..c65a4ca174 100644
--- a/plugins/Ecommerce/Columns/RevenueShipping.php
+++ b/plugins/Ecommerce/Columns/RevenueShipping.php
@@ -17,6 +17,9 @@ class RevenueShipping extends BaseConversion
{
protected $columnName = 'revenue_shipping';
protected $columnType = 'float default NULL';
+ protected $type = self::TYPE_MONEY;
+ protected $category = 'Goals_Ecommerce';
+ protected $nameSingular = 'General_Shipping';
/**
* @param Request $request
diff --git a/plugins/Ecommerce/Columns/RevenueSubtotal.php b/plugins/Ecommerce/Columns/RevenueSubtotal.php
index 7b09df0014..86409dbb3d 100644
--- a/plugins/Ecommerce/Columns/RevenueSubtotal.php
+++ b/plugins/Ecommerce/Columns/RevenueSubtotal.php
@@ -17,6 +17,9 @@ class RevenueSubtotal extends BaseConversion
{
protected $columnName = 'revenue_subtotal';
protected $columnType = 'float default NULL';
+ protected $type = self::TYPE_MONEY;
+ protected $category = 'Goals_Ecommerce';
+ protected $nameSingular = 'General_Subtotal';
/**
* @param Request $request
diff --git a/plugins/Ecommerce/Columns/RevenueTax.php b/plugins/Ecommerce/Columns/RevenueTax.php
index 4a2df65d64..820ee95037 100644
--- a/plugins/Ecommerce/Columns/RevenueTax.php
+++ b/plugins/Ecommerce/Columns/RevenueTax.php
@@ -17,6 +17,9 @@ class RevenueTax extends BaseConversion
{
protected $columnName = 'revenue_tax';
protected $columnType = 'float default NULL';
+ protected $type = self::TYPE_MONEY;
+ protected $category = 'Goals_Ecommerce';
+ protected $nameSingular = 'General_Tax';
/**
* @param Request $request
diff --git a/plugins/Ecommerce/Ecommerce.php b/plugins/Ecommerce/Ecommerce.php
new file mode 100644
index 0000000000..922c51648b
--- /dev/null
+++ b/plugins/Ecommerce/Ecommerce.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Ecommerce;
+use Piwik\Columns\ComputedMetricFactory;
+use Piwik\Columns\MetricsList;
+use Piwik\Piwik;
+use Piwik\Plugin\ArchivedMetric;
+use Piwik\Plugin\ComputedMetric;
+
+/**
+ *
+ */
+class Ecommerce extends \Piwik\Plugin
+{
+
+ /**
+ * @see \Piwik\Plugin::registerEvents
+ */
+ public function registerEvents()
+ {
+ $hooks = array(
+ 'Metric.addComputedMetrics' => 'addComputedMetrics'
+ );
+ return $hooks;
+ }
+
+ public function addComputedMetrics(MetricsList $list, ComputedMetricFactory $computedMetricFactory)
+ {
+ $category = 'Goals_Ecommerce';
+
+ $metrics = $list->getMetrics();
+ foreach ($metrics as $metric) {
+ if ($metric instanceof ArchivedMetric && $metric->getDimension()) {
+ $metricName = $metric->getName();
+ if ($metric->getDbTableName() === 'log_conversion'
+ && $metricName !== 'nb_uniq_orders'
+ && strpos($metricName, ArchivedMetric::AGGREGATION_SUM_PREFIX) === 0
+ && $metric->getCategoryId() === $category) {
+ $metric = $computedMetricFactory->createComputedMetric($metric->getName(), 'nb_uniq_orders', ComputedMetric::AGGREGATION_AVG);
+ $list->addMetric($metric);
+ }
+ }
+ }
+ }
+
+}
diff --git a/plugins/Ecommerce/lang/en.json b/plugins/Ecommerce/lang/en.json
index 82419ad05c..850ecfb661 100644
--- a/plugins/Ecommerce/lang/en.json
+++ b/plugins/Ecommerce/lang/en.json
@@ -2,8 +2,12 @@
"Ecommerce": {
"PluginDescription": "Ecommerce lets you track when users add products to carts, and when they convert to a ecommerce sale. Also track products and product categories views and abandoned carts.",
"Sales": "Sales",
+ "Order": "Order",
+ "Orders": "Orders",
"SalesBy": "Sales by %s",
"SalesAdjective": "Sales %s",
+ "NumberOfItems": "Number of Items in Cart",
+ "OrderValue": "Order value",
"LifeTimeValue": "Ecommerce Life Time Value",
"LifeTimeValueDescription": "Total Ecommerce revenue attributed to this customer across all visits: the sum of revenue of all Ecommerce orders for Visitor ID %s.",
"VisitorProfileLTV": "Generated a Life Time Revenue of %1$s.",
diff --git a/plugins/Events/Categories/EventsCategory.php b/plugins/Events/Categories/EventsCategory.php
new file mode 100644
index 0000000000..3e82e4887c
--- /dev/null
+++ b/plugins/Events/Categories/EventsCategory.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Events\Categories;
+
+use Piwik\Category\Category;
+
+// Needed for dimensions and metrics
+class EventsCategory extends Category
+{
+ protected $id = 'Events_Events';
+ protected $order = 12;
+
+}
diff --git a/plugins/Events/Columns/EventAction.php b/plugins/Events/Columns/EventAction.php
index 30c33fe8da..e08325e0b7 100644
--- a/plugins/Events/Columns/EventAction.php
+++ b/plugins/Events/Columns/EventAction.php
@@ -8,10 +8,10 @@
*/
namespace Piwik\Plugins\Events\Columns;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Exception\InvalidRequestParameterException;
-use Piwik\Piwik;
use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugins\Events\Segment;
use Piwik\Plugins\Events\Actions\ActionEvent;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -20,18 +20,21 @@ class EventAction extends ActionDimension
{
protected $columnName = 'idaction_event_action';
protected $columnType = 'INTEGER(10) UNSIGNED DEFAULT NULL';
-
- protected function configureSegments()
+ protected $type = self::TYPE_TEXT;
+ protected $segmentName = 'eventAction';
+ protected $nameSingular = 'Events_EventAction';
+ protected $namePlural = 'Events_EventActions';
+ protected $category = 'Events_Events';
+ protected $sqlFilter = '\Piwik\Tracker\TableLogAction::getIdActionFromSegment';
+
+ public function getDbColumnJoin()
{
- $segment = new Segment();
- $segment->setSegment('eventAction');
- $segment->setName('Events_EventAction');
- $this->addSegment($segment);
+ return new ActionNameJoin();
}
- public function getName()
+ public function getDbDiscriminator()
{
- return Piwik::translate('Events_EventAction');
+ return new Discriminator('log_action', 'type', $this->getActionId());
}
public function getActionId()
diff --git a/plugins/Events/Columns/EventCategory.php b/plugins/Events/Columns/EventCategory.php
index 731dd9615c..b10b1a8406 100644
--- a/plugins/Events/Columns/EventCategory.php
+++ b/plugins/Events/Columns/EventCategory.php
@@ -8,10 +8,10 @@
*/
namespace Piwik\Plugins\Events\Columns;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Exception\InvalidRequestParameterException;
-use Piwik\Piwik;
use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugins\Events\Segment;
use Piwik\Plugins\Events\Actions\ActionEvent;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -20,18 +20,21 @@ class EventCategory extends ActionDimension
{
protected $columnName = 'idaction_event_category';
protected $columnType = 'INTEGER(10) UNSIGNED DEFAULT NULL';
-
- protected function configureSegments()
+ protected $type = self::TYPE_TEXT;
+ protected $category = 'Events_Events';
+ protected $sqlFilter = '\Piwik\Tracker\TableLogAction::getIdActionFromSegment';
+ protected $segmentName = 'eventCategory';
+ protected $nameSingular = 'Events_EventCategory';
+ protected $namePlural = 'Events_EventCategories';
+
+ public function getDbColumnJoin()
{
- $segment = new Segment();
- $segment->setSegment('eventCategory');
- $segment->setName('Events_EventCategory');
- $this->addSegment($segment);
+ return new ActionNameJoin();
}
- public function getName()
+ public function getDbDiscriminator()
{
- return Piwik::translate('Events_EventCategory');
+ return new Discriminator('log_action', 'type', $this->getActionId());
}
public function getActionId()
diff --git a/plugins/Events/Columns/EventName.php b/plugins/Events/Columns/EventName.php
index bbe01ae0ff..71876dfc28 100644
--- a/plugins/Events/Columns/EventName.php
+++ b/plugins/Events/Columns/EventName.php
@@ -8,9 +8,9 @@
*/
namespace Piwik\Plugins\Events\Columns;
-use Piwik\Piwik;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\Join\ActionNameJoin;
use Piwik\Plugin\Dimension\ActionDimension;
-use Piwik\Plugins\Events\Segment;
use Piwik\Plugins\Events\Actions\ActionEvent;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -18,18 +18,21 @@ use Piwik\Tracker\Request;
class EventName extends ActionDimension
{
protected $columnName = 'idaction_name';
-
- protected function configureSegments()
+ protected $type = self::TYPE_TEXT;
+ protected $category = 'Events_Events';
+ protected $sqlFilter = '\Piwik\Tracker\TableLogAction::getIdActionFromSegment';
+ protected $segmentName = 'eventName';
+ protected $nameSingular = 'Events_EventName';
+ protected $namePlural = 'Events_EventNames';
+
+ public function getDbColumnJoin()
{
- $segment = new Segment();
- $segment->setSegment('eventName');
- $segment->setName('Events_EventName');
- $this->addSegment($segment);
+ return new ActionNameJoin();
}
- public function getName()
+ public function getDbDiscriminator()
{
- return Piwik::translate('Events_EventName');
+ return new Discriminator('log_action', 'type', $this->getActionId());
}
public function getActionId()
diff --git a/plugins/Events/Columns/EventValue.php b/plugins/Events/Columns/EventValue.php
new file mode 100644
index 0000000000..1a174553a0
--- /dev/null
+++ b/plugins/Events/Columns/EventValue.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Events\Columns;
+
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\Discriminator;
+use Piwik\Columns\MetricsList;
+use Piwik\Piwik;
+use Piwik\Plugin\ArchivedMetric;
+use Piwik\Plugin\ComputedMetric;
+use Piwik\Plugin\Dimension\ActionDimension;
+use Piwik\Tracker\Action;
+
+class EventValue extends ActionDimension
+{
+ protected $nameSingular = 'Events_EventValue';
+ protected $columnName = 'custom_float';
+ protected $category = 'Events_Events';
+ protected $type = self::TYPE_FLOAT;
+
+ public function getDbDiscriminator()
+ {
+ return new Discriminator('log_action', 'type', Action::TYPE_EVENT);
+ }
+
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
+ {
+ $metric1 = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_SUM);
+ $metricsList->addMetric($metric1);
+
+ $metric2 = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_MAX);
+ $metric2->setDocumentation(Piwik::translate('Events_MaxValueDocumentation'));
+ $metricsList->addMetric($metric2);
+
+ $metric4 = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_MIN);
+ $metric4->setDocumentation(Piwik::translate('Events_MinValueDocumentation'));
+ $metricsList->addMetric($metric4);
+
+ $metric3 = $dimensionMetricFactory->createMetric(ArchivedMetric::AGGREGATION_COUNT_WITH_NUMERIC_VALUE);
+ $metric3->setName('events_with_event_value');
+ $metric3->setTranslatedName(Piwik::translate('Events_EventsWithValue'));
+ $metric3->setDocumentation(Piwik::translate('Events_EventsWithValueDocumentation'));
+ $metricsList->addMetric($metric3);
+
+ $metric = $dimensionMetricFactory->createComputedMetric($metric1->getName(), $metric3->getName(), ComputedMetric::AGGREGATION_AVG);
+ $metric->setName('avg_event_value');
+ $metric->setTranslatedName(Piwik::translate('Events_AvgValue'));
+ $metricsList->addMetric($metric);
+ }
+}
diff --git a/plugins/Events/Columns/TotalEvents.php b/plugins/Events/Columns/TotalEvents.php
index 410144e3a9..f96392c194 100644
--- a/plugins/Events/Columns/TotalEvents.php
+++ b/plugins/Events/Columns/TotalEvents.php
@@ -8,9 +8,7 @@
*/
namespace Piwik\Plugins\Events\Columns;
-use Piwik\Piwik;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugin\Segment;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
@@ -19,22 +17,11 @@ class TotalEvents extends VisitDimension
{
protected $columnName = 'visit_total_events';
protected $columnType = 'INT(11) UNSIGNED NULL';
+ protected $segmentName = 'events';
+ protected $nameSingular = 'Events_TotalEvents';
+ protected $acceptValues = 'To select all visits who triggered an Event, use: &segment=events>0';
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('events');
- $segment->setName('Events_TotalEvents');
- $segment->setAcceptedValues('To select all visits who triggered an Event, use: &segment=events>0');
- $segment->setCategory('General_Visit');
- $segment->setType(Segment::TYPE_METRIC);
- $this->addSegment($segment);
- }
-
- public function getName()
- {
- return Piwik::translate('Events_EventName');
- }
+ protected $type = self::TYPE_NUMBER;
/**
* @param Request $request
diff --git a/plugins/Events/Segment.php b/plugins/Events/Segment.php
deleted file mode 100644
index aa586e3987..0000000000
--- a/plugins/Events/Segment.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-namespace Piwik\Plugins\Events;
-
-/**
- * Events segment base class.
- *
- */
-class Segment extends \Piwik\Plugin\Segment
-{
- protected function init()
- {
- $this->setCategory('Events_Events');
- $this->setSqlFilter('\Piwik\Tracker\TableLogAction::getIdActionFromSegment');
- }
-}
diff --git a/plugins/ExampleTracker/Columns/ExampleActionDimension.php b/plugins/ExampleTracker/Columns/ExampleActionDimension.php
index b0ce98f7c9..3710659d6d 100644
--- a/plugins/ExampleTracker/Columns/ExampleActionDimension.php
+++ b/plugins/ExampleTracker/Columns/ExampleActionDimension.php
@@ -27,6 +27,12 @@ use Piwik\Tracker\Action;
class ExampleActionDimension extends ActionDimension
{
/**
+ * The name of the dimension which will be visible for instance in the UI of a related report and in the mobile app.
+ * @return string
+ */
+ protected $nameSingular = 'ExampleTracker_DimensionName';
+
+ /**
* This will be the name of the column in the log_link_visit_action table if a $columnType is specified.
* @var string
*/
@@ -41,28 +47,21 @@ class ExampleActionDimension extends ActionDimension
protected $columnType = 'VARCHAR(255) DEFAULT NULL';
/**
- * The name of the dimension which will be visible for instance in the UI of a related report and in the mobile app.
- * @return string
+ * The type of the dimension is automatically detected by the columnType. If the type of the dimension is not
+ * detected correctly, you may want to adjust the type manually. The configured type will affect how the dimension
+ * is formatted in the UI.
+ * @var string
*/
- public function getName()
- {
- return Piwik::translate('ExampleTracker_DimensionName');
- }
+ // protected $type = self::TYPE_TEXT;
/**
- * By defining one or multiple segments a user will be able to filter their visitors by this column. For instance
+ * By defining a segment a user will be able to filter their visitors by this column. For instance
* show all actions only considering users having more than 10 achievement points. If you do not want to define a
- * segment for this dimension just remove the column.
+ * segment for this dimension, simply leave the name empty.
*/
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('keywords');
- $segment->setCategory('General_Actions');
- $segment->setName('ExampleTracker_DimensionName');
- $segment->setAcceptedValues('Here you should explain which values are accepted/useful: Any word, for instance MyKeyword1, MyKeyword2');
- $this->addSegment($segment);
- }
+ protected $segmentName = 'keywords';
+
+ protected $acceptValues = 'Here you should explain which values are accepted/useful for segments: Any word, for instance MyKeyword1, MyKeyword2';
/**
* This event is triggered before a new action is logged to the log_link_visit_action table. It overwrites any
diff --git a/plugins/ExampleTracker/Columns/ExampleConversionDimension.php b/plugins/ExampleTracker/Columns/ExampleConversionDimension.php
index 6b6bf3d51f..752104c1c8 100644
--- a/plugins/ExampleTracker/Columns/ExampleConversionDimension.php
+++ b/plugins/ExampleTracker/Columns/ExampleConversionDimension.php
@@ -43,28 +43,27 @@ class ExampleConversionDimension extends ConversionDimension
protected $columnType = 'INTEGER(11) DEFAULT 0 NULL';
/**
+ * The type of the dimension is automatically detected by the columnType. If the type of the dimension is not
+ * detected correctly, you may want to adjust the type manually. The configured type will affect how the dimension
+ * is formatted in the UI.
+ * @var string
+ */
+ // protected $type = self::TYPE_NUMBER;
+
+ /**
* The name of the dimension which will be visible for instance in the UI of a related report and in the mobile app.
* @return string
*/
- public function getName()
- {
- return Piwik::translate('ExampleTracker_DimensionName');
- }
+ protected $nameSingular = 'ExampleTracker_DimensionName';
/**
- * By defining one or multiple segments a user will be able to filter their visitors by this column. For instance
+ * By defining a segment a user will be able to filter their visitors by this column. For instance
* show all reports only considering users having more than 10 achievement points. If you do not want to define a
- * segment for this dimension just remove the column.
+ * segment for this dimension, simply leave the name empty.
*/
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('myConversionSegmentName');
- $segment->setCategory('General_Visit');
- $segment->setName('ExampleTracker_DimensionName');
- $segment->setAcceptedValues('Here you should explain which values are accepted/useful: Any number, for instance 1, 2, 3 , 99');
- $this->addSegment($segment);
- }
+ protected $segmentName = 'myConversionSegmentName';
+
+ protected $acceptValues = 'Here you should explain which values are accepted/useful for segments: Any number, for instance 1, 2, 3 , 99';
/**
* This event is triggered when an ecommerce order is converted. In this example we would store a "0" in case it
diff --git a/plugins/ExampleTracker/Columns/ExampleDimension.php b/plugins/ExampleTracker/Columns/ExampleDimension.php
index 738d0be9e1..25e48bb306 100644
--- a/plugins/ExampleTracker/Columns/ExampleDimension.php
+++ b/plugins/ExampleTracker/Columns/ExampleDimension.php
@@ -23,8 +23,6 @@ class ExampleDimension extends Dimension
* The name of the dimension which will be visible for instance in the UI of a related report and in the mobile app.
* @return string
*/
- public function getName()
- {
- return Piwik::translate('ExampleTracker_DimensionName');
- }
+ protected $nameSingular = 'ExampleTracker_DimensionName';
+
} \ No newline at end of file
diff --git a/plugins/ExampleTracker/Columns/ExampleVisitDimension.php b/plugins/ExampleTracker/Columns/ExampleVisitDimension.php
index 15d32592a6..0997a18ee7 100644
--- a/plugins/ExampleTracker/Columns/ExampleVisitDimension.php
+++ b/plugins/ExampleTracker/Columns/ExampleVisitDimension.php
@@ -41,28 +41,27 @@ class ExampleVisitDimension extends VisitDimension
protected $columnType = 'INTEGER(11) DEFAULT 0 NULL';
/**
+ * The type of the dimension is automatically detected by the columnType. If the type of the dimension is not
+ * detected correctly, you may want to adjust the type manually. The configured type will affect how the dimension
+ * is formatted in the UI.
+ * @var string
+ */
+ // protected $type = self::TYPE_NUMBER;
+
+ /**
* The name of the dimension which will be visible for instance in the UI of a related report and in the mobile app.
* @return string
*/
- public function getName()
- {
- return Piwik::translate('ExampleTracker_DimensionName');
- }
+ protected $nameSingular = 'ExampleTracker_DimensionName';
/**
- * By defining one or multiple segments a user will be able to filter their visitors by this column. For instance
+ * By defining a segment a user will be able to filter their visitors by this column. For instance
* show all reports only considering users having more than 10 achievement points. If you do not want to define a
- * segment for this dimension just remove the column.
+ * segment for this dimension, simply leave the name empty.
*/
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('achievementPoints');
- $segment->setCategory('General_Visit');
- $segment->setName('ExampleTracker_DimensionName');
- $segment->setAcceptedValues('Here you should explain which values are accepted/useful: Any number, for instance 1, 2, 3 , 99');
- $this->addSegment($segment);
- }
+ protected $segmentName = 'achievementPoints';
+
+ protected $acceptValues = 'Here you should explain which values are accepted/useful for segments: Any number, for instance 1, 2, 3 , 99';
/**
* The onNewVisit method is triggered when a new visitor is detected. This means here you can define an initial
diff --git a/plugins/Goals/Columns/DaysToConversion.php b/plugins/Goals/Columns/DaysToConversion.php
index e1bd941d6b..576babb8d7 100644
--- a/plugins/Goals/Columns/DaysToConversion.php
+++ b/plugins/Goals/Columns/DaysToConversion.php
@@ -13,8 +13,6 @@ use Piwik\Piwik;
class DaysToConversion extends Dimension
{
- public function getName()
- {
- return Piwik::translate('Goals_DaysToConv');
- }
+ protected $type = self::TYPE_NUMBER;
+ protected $nameSingular = 'Goals_DaysToConv';
} \ No newline at end of file
diff --git a/plugins/Goals/Columns/IdGoal.php b/plugins/Goals/Columns/IdGoal.php
index 2243dbdb1f..a8ca5ac5b6 100644
--- a/plugins/Goals/Columns/IdGoal.php
+++ b/plugins/Goals/Columns/IdGoal.php
@@ -8,26 +8,27 @@
*/
namespace Piwik\Plugins\Goals\Columns;
-use Piwik\Piwik;
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\Join;
+use Piwik\Columns\MetricsList;
use Piwik\Plugin\Dimension\ConversionDimension;
-use Piwik\Plugin\Segment;
class IdGoal extends ConversionDimension
{
protected $columnName = 'idgoal';
+ protected $type = self::TYPE_TEXT;
+ protected $category = 'General_Visitors'; // todo move into goal category?
+ protected $nameSingular = 'General_VisitConvertedGoalId';
+ protected $segmentName = 'visitConvertedGoalId';
+ protected $acceptValues = '1, 2, 3, etc.';
- protected function configureSegments()
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
{
- $segment = new Segment();
- $segment->setCategory('General_Visit');
- $segment->setName('General_VisitConvertedGoalId');
- $segment->setSegment('visitConvertedGoalId');
- $segment->setAcceptedValues('1, 2, 3, etc.');
- $this->addSegment($segment);
+ // do not create any metrics for this dimension, they don't really make much sense and are rather confusing
}
- public function getName()
+ public function getDbColumnJoin()
{
- return Piwik::translate('General_VisitConvertedGoalId');
+ return new Join\GoalNameJoin();
}
} \ No newline at end of file
diff --git a/plugins/Goals/Columns/Revenue.php b/plugins/Goals/Columns/Revenue.php
new file mode 100644
index 0000000000..d18af2eda9
--- /dev/null
+++ b/plugins/Goals/Columns/Revenue.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Goals\Columns;
+
+use Piwik\Plugin\Dimension\ConversionDimension;
+
+class Revenue extends ConversionDimension
+{
+ protected $columnName = 'revenue';
+ protected $type = self::TYPE_MONEY;
+ protected $category = 'Goals_Goals';
+ protected $nameSingular = 'Goals_ColumnOverallRevenue';
+
+} \ No newline at end of file
diff --git a/plugins/Goals/Columns/VisitsUntilConversion.php b/plugins/Goals/Columns/VisitsUntilConversion.php
index 13f54e9dd7..05c988a479 100644
--- a/plugins/Goals/Columns/VisitsUntilConversion.php
+++ b/plugins/Goals/Columns/VisitsUntilConversion.php
@@ -13,8 +13,7 @@ use Piwik\Piwik;
class VisitsUntilConversion extends Dimension
{
- public function getName()
- {
- return Piwik::translate('Goals_VisitsUntilConv');
- }
+ protected $type = self::TYPE_NUMBER;
+ protected $nameSingular = 'Goals_VisitsUntilConv';
+
} \ No newline at end of file
diff --git a/plugins/Goals/GoalDimension.php b/plugins/Goals/GoalDimension.php
new file mode 100644
index 0000000000..91cbde16df
--- /dev/null
+++ b/plugins/Goals/GoalDimension.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Goals;
+
+use Piwik\Columns\Dimension;
+use Piwik\Columns\Discriminator;
+
+class GoalDimension extends Dimension
+{
+ protected $type = self::TYPE_TEXT;
+ private $goal;
+ private $id;
+
+ public function __construct($goal, $column, $name)
+ {
+ $this->goal = $goal;
+ $this->category = 'Goals_Goals';
+ $this->dbTableName = 'log_conversion';
+ $this->columnName = $column;
+ $this->nameSingular = $name;
+
+ $this->id = 'Goals.Goal' . ucfirst($column) . $goal['idgoal'];
+ }
+
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ public function getDbDiscriminator()
+ {
+ return new Discriminator('log_conversion', 'idgoal', $this->goal['idgoal']);
+ }
+
+} \ No newline at end of file
diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php
index 0700d8aa75..b1d10e16f3 100644
--- a/plugins/Goals/Goals.php
+++ b/plugins/Goals/Goals.php
@@ -8,8 +8,13 @@
*/
namespace Piwik\Plugins\Goals;
+use Piwik\Columns\ComputedMetricFactory;
+use Piwik\Columns\Dimension;
+use Piwik\Columns\MetricsList;
use Piwik\Common;
use Piwik\Piwik;
+use Piwik\Plugin\ArchivedMetric;
+use Piwik\Plugin\ComputedMetric;
use Piwik\Plugin\ReportsProvider;
use Piwik\Tracker\GoalManager;
use Piwik\Category\Subcategory;
@@ -77,11 +82,74 @@ class Goals extends \Piwik\Plugin
'SitesManager.deleteSite.end' => 'deleteSiteGoals',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
'Metrics.getDefaultMetricTranslations' => 'addMetricTranslations',
- 'Category.addSubcategories' => 'addSubcategories'
+ 'Category.addSubcategories' => 'addSubcategories',
+ 'Metric.addMetrics' => 'addMetrics',
+ 'Metric.addComputedMetrics' => 'addComputedMetrics'
);
return $hooks;
}
+ public function addComputedMetrics(MetricsList $list, ComputedMetricFactory $computedMetricFactory)
+ {
+ $idSite = Common::getRequestVar('idSite', 0, 'int');
+ $goals = API::getInstance()->getGoals($idSite);
+
+ foreach ($goals as $goal) {
+ $metric = $computedMetricFactory->createComputedMetric('goal_' . $goal['idgoal'] . '_conversion', 'nb_uniq_visitors', ComputedMetric::AGGREGATION_RATE);
+ $goalName = Piwik::translate('Goals_GoalX', $goal['name']);
+ $metricName = Piwik::translate('Goals_ConversionRate', $goalName);
+ $metric->setTranslatedName($metricName);
+ $list->addMetric($metric);
+ }
+ }
+
+ public function addMetrics(MetricsList $metricsList)
+ {
+ $idSite = Common::getRequestVar('idSite', 0, 'int');
+ $goals = API::getInstance()->getGoals($idSite);
+
+ foreach ($goals as $goal) {
+ $custom = new GoalDimension($goal, 'idgoal', 'Conversions goal "' . $goal['name'] . '" (ID ' . $goal['idgoal'] .' )');
+ $custom->setType(Dimension::TYPE_NUMBER);
+ $custom->setSqlSegment('count(distinct log_conversion.idvisit, log_conversion.buster)');
+
+ $metric = new ArchivedMetric($custom, ArchivedMetric::AGGREGATION_SUM);
+ $metric->setQuery('count(distinct log_conversion.idvisit, log_conversion.buster)');
+ $metric->setTranslatedName($custom->getName());
+ $metric->setDocumentation('The number of times this goal was converted.');
+ $metric->setCategory($custom->getCategoryId());
+ $metric->setName('goal_' . $goal['idgoal'] . '_conversion');
+ $metricsList->addMetric($metric);
+
+ $custom = new GoalDimension($goal, 'revenue', 'Revenue goal "' . $goal['name'] . '" (ID ' . $goal['idgoal'] .' )');
+ $custom->setType(Dimension::TYPE_MONEY);
+ $metric = new ArchivedMetric($custom, ArchivedMetric::AGGREGATION_SUM);
+ $metric->setTranslatedName($custom->getName());
+ $metric->setName('goal_' . $goal['idgoal'] . '_revenue');
+ $metric->setDocumentation('The amount of revenue that was generated by converting this goal.');
+ $metric->setCategory($custom->getCategoryId());
+ $metricsList->addMetric($metric);
+
+ $custom = new GoalDimension($goal, 'visitor_days_since_first', 'Days to conversion goal "' . $goal['name'] . '" (ID ' . $goal['idgoal'] .' )');
+ $custom->setType(Dimension::TYPE_NUMBER);
+ $metric = new ArchivedMetric($custom, ArchivedMetric::AGGREGATION_SUM);
+ $metric->setTranslatedName($custom->getName());
+ $metric->setCategory($custom->getCategoryId());
+ $metric->setDocumentation('The number of days it took a visitor to convert this goal.');
+ $metric->setName('goal_' . $goal['idgoal'] . '_daystoconversion');
+ $metricsList->addMetric($metric);
+
+ $custom = new GoalDimension($goal, 'visitor_count_visits', 'Visits to conversion goal "' . $goal['name'] . '" (ID ' . $goal['idgoal'] .' )');
+ $custom->setType(Dimension::TYPE_NUMBER);
+ $metric = new ArchivedMetric($custom, ArchivedMetric::AGGREGATION_SUM);
+ $metric->setTranslatedName($custom->getName());
+ $metric->setCategory($custom->getCategoryId());
+ $metric->setDocumentation('The number of visits it took a visitor to convert this goal.');
+ $metric->setName('goal_' . $goal['idgoal'] . '_visitstoconversion');
+ $metricsList->addMetric($metric);
+ }
+ }
+
public function addSubcategories(&$subcategories)
{
$idSite = Common::getRequestVar('idSite', 0, 'int');
@@ -108,7 +176,6 @@ class Goals extends \Piwik\Plugin
}
}
-
public function addMetricTranslations(&$translations)
{
$metrics = array(
diff --git a/plugins/Goals/lang/en.json b/plugins/Goals/lang/en.json
index d78a22a276..3b72998105 100644
--- a/plugins/Goals/lang/en.json
+++ b/plugins/Goals/lang/en.json
@@ -24,6 +24,7 @@
"ColumnConversionRateDocumentation": "The percentage of visits that triggered the goal %s.",
"ColumnConversionRateProductDocumentation": "The %s conversion rate is the number of orders containing this product divided by number of visits on the product page.",
"ColumnConversions": "Conversions",
+ "Conversion": "Conversion",
"ColumnConversionsDocumentation": "The number of conversions for %s.",
"ColumnOrdersDocumentation": "The total number of Ecommerce orders which contained this %s at least once.",
"ColumnPurchasedProductsDocumentation": "The number of purchased products is the sum of Product quantities sold in all Ecommerce orders.",
@@ -81,14 +82,19 @@
"NeedAccess": "Only an Administrator or a user with Super User access can manage Goals for a given website.",
"Optional": "(optional)",
"OverallConversionRate": "overall conversion rate (visits with a completed goal)",
+ "ColumnOverallRevenue": "Overall revenue",
"OverallRevenue": "overall revenue",
"PageTitle": "Page Title",
"Pattern": "Pattern",
"PluginDescription": "Create Goals and see detailed reports about your goal conversions: evolution over time, revenue per visit, conversions per referrer, per keyword, and more.",
"ProductCategory": "Product Category",
"ProductName": "Product Name",
+ "ProductNames": "Product Names",
+ "ProductPrice": "Product Price",
+ "ProductQuantity": "Product Quantity",
"Products": "Products",
"ProductSKU": "Product SKU",
+ "ProductSKUs": "Product SKUs",
"ReturningVisitorsConversionRateIs": "Returning visitors conversion rate is %s",
"SingleGoalOverviewDocumentation": "This is an overview of the conversions for a single goal. %s The sparklines below the graph can be enlarged by clicking on them.",
"ThereIsNoGoalToManage": "There is no goal to manage for website %s",
diff --git a/plugins/Morpheus/stylesheets/main.less b/plugins/Morpheus/stylesheets/main.less
index 1eaf784455..cbf86d85cd 100644
--- a/plugins/Morpheus/stylesheets/main.less
+++ b/plugins/Morpheus/stylesheets/main.less
@@ -401,10 +401,28 @@ table.dataTable {
background-color: @color-silver-l95;
}
+ // for third level tables
+ td.cellSubDataTable {
+ table {
+ table {
+ tr td {
+ background: @theme-color-background-contrast !important;
+ }
+ tr:hover td {
+ background: @theme-color-background-contrast !important;
+
+ &:not(.cellSubDataTable) {
+ background-color: @color-silver-l95 !important;
+ }
+ }
+ }
+ }
+ }
+
.cellSubDataTable td {
background: @theme-color-background-contrast;
}
- .cellSubDataTable tr:hover td {
+ .cellSubDataTable tr:hover td:not(.cellSubDataTable) {
background-color: @theme-color-background-base;
}
}
diff --git a/plugins/Morpheus/stylesheets/ui/_components.less b/plugins/Morpheus/stylesheets/ui/_components.less
index 675f4a87f6..3508938b71 100644
--- a/plugins/Morpheus/stylesheets/ui/_components.less
+++ b/plugins/Morpheus/stylesheets/ui/_components.less
@@ -81,32 +81,6 @@
}
}
}
- .segment-nav {
- div > ul > li {
- padding: 5px 0;
- a {
- font-weight: normal;
- color: #333333;
- text-shadow: none;
- }
- li {
- padding: 3px 0;
- &:hover {
- background: @theme-color-background-tinyContrast;
- border: 0;
- padding: 4px 0 3px;
-
- a {
- border: 0;
- background-color: @theme-color-background-tinyContrast;
- padding-right: 15px;
-
- }
- }
- }
-
- }
- }
.segment-top {
.font-default(10px, 12px);
diff --git a/plugins/Morpheus/templates/demo.twig b/plugins/Morpheus/templates/demo.twig
index 84d6a2fec4..5c2fdae95a 100644
--- a/plugins/Morpheus/templates/demo.twig
+++ b/plugins/Morpheus/templates/demo.twig
@@ -337,6 +337,14 @@
options='{1: "0123456789",2:"9876543210",3:"5432109876"}'>
</div>
+ <div piwik-field uicontrol="expandable-select" name="selectexpand"
+ title="{{ "{{ view.selectedExpand ? view.selectedExpand : 'Select word' }}"|raw }}"
+ value="Select value"
+ inline-help="Expandable select"
+ ng-model="view.selectedExpand"
+ options='[{group: "Group 1",key:"1",value:"Hello"}, {group: "Group 1",key:"2",value:"How",tooltip: "Help text"}, {group: "Group 1",key:"3",value:"Are"}, {group: "Group 2",key:"4",value:"You"}]'>
+ </div>
+
<div piwik-field uicontrol="checkbox" name="enableFeature"
title="Enable feature"
introduction="Radio and checkboxes"
@@ -441,6 +449,14 @@
options='{1: &quot;0123456789&quot;,2:&quot;9876543210&quot;,3:&quot;5432109876&quot;}'&gt;
&lt;/div&gt;
+ &lt;div piwik-field uicontrol=&quot;expandable-select&quot; name=&quot;selectexpand&quot;
+ title=&quot;{{ "{{ view.selectedExpand ? view.selectedExpand : 'Select word' }}"|raw }}&quot;
+ value=&quot;Select value&quot;
+ inline-help=&quot;Expandable select&quot;
+ ng-model=&quot;view.selectedExpand&quot;
+ options='[{group: &quot;Group 1&quot;,key:&quot;1&quot;,value:&quot;Hello&quot;}, {group: &quot;Group 1&quot;,key:&quot;2&quot;,value:&quot;How&quot;,tooltip: &quot;Help text&quot;}, {group: &quot;Group 1&quot;,key:&quot;3&quot;,value:&quot;Are&quot;}, {group: &quot;Group 2&quot;,key:&quot;4&quot;,value:&quot;You&quot;}]'&gt;
+ &lt;/div&gt;
+
&lt;div piwik-field uicontrol=&quot;checkbox&quot; name=&quot;enableFeature&quot;
title=&quot;Enable feature&quot;
introduction=&quot;Radio and checkboxes&quot;
diff --git a/plugins/Provider/Columns/Provider.php b/plugins/Provider/Columns/Provider.php
index b7ca57e9c9..111bd84aa1 100644
--- a/plugins/Provider/Columns/Provider.php
+++ b/plugins/Provider/Columns/Provider.php
@@ -11,9 +11,7 @@ namespace Piwik\Plugins\Provider\Columns;
use Piwik\Common;
use Piwik\Network\IP;
use Piwik\Network\IPUtils;
-use Piwik\Piwik;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugin\Segment;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
@@ -23,16 +21,12 @@ use Piwik\Plugins\Provider\Provider as ProviderPlugin;
class Provider extends VisitDimension
{
protected $columnName = 'location_provider';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('provider');
- $segment->setCategory('Visit Location');
- $segment->setName('Provider_ColumnProvider');
- $segment->setAcceptedValues('comcast.net, proxad.net, etc.');
- $this->addSegment($segment);
- }
+ protected $segmentName = 'provider';
+ protected $category = 'UserCountry_VisitLocation';
+ protected $nameSingular = 'Provider_ColumnProvider';
+ protected $namePlural = 'Provider_WidgetProviders';
+ protected $acceptValues = 'comcast.net, proxad.net, etc.';
+ protected $type = self::TYPE_TEXT;
/**
* @param Request $request
@@ -99,9 +93,4 @@ class Provider extends VisitDimension
return trim(strtolower($host));
}
-
- public function getName()
- {
- return Piwik::translate('Provider_ColumnProvider');
- }
} \ No newline at end of file
diff --git a/plugins/Referrers/Columns/Campaign.php b/plugins/Referrers/Columns/Campaign.php
index be42acb34b..158876a7ef 100644
--- a/plugins/Referrers/Columns/Campaign.php
+++ b/plugins/Referrers/Columns/Campaign.php
@@ -24,17 +24,13 @@ class Campaign extends Base
* @var bool
*/
protected $createNewVisitWhenCampaignChanges;
+ protected $nameSingular = 'Referrers_ColumnCampaign';
public function __construct()
{
$this->createNewVisitWhenCampaignChanges = TrackerConfig::getConfigValue('create_new_visit_when_campaign_changes') == 1;
}
- public function getName()
- {
- return Piwik::translate('Referrers_ColumnCampaign');
- }
-
/**
* If we should create a new visit when the campaign changes, check if the campaign info changed and if so
* force the tracker to create a new visit.i
diff --git a/plugins/Referrers/Columns/Keyword.php b/plugins/Referrers/Columns/Keyword.php
index 7c927d6492..e496740c66 100644
--- a/plugins/Referrers/Columns/Keyword.php
+++ b/plugins/Referrers/Columns/Keyword.php
@@ -9,8 +9,6 @@
namespace Piwik\Plugins\Referrers\Columns;
use Piwik\Common;
-use Piwik\Piwik;
-use Piwik\Plugins\Referrers\Segment;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
@@ -19,20 +17,12 @@ class Keyword extends Base
{
protected $columnName = 'referer_keyword';
protected $columnType = 'VARCHAR(255) NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('referrerKeyword');
- $segment->setName('General_ColumnKeyword');
- $segment->setAcceptedValues('Encoded%20Keyword, keyword');
- $this->addSegment($segment);
- }
-
- public function getName()
- {
- return Piwik::translate('General_ColumnKeyword');
- }
+ protected $nameSingular = 'General_ColumnKeyword';
+ protected $namePlural = 'Referrers_Keywords';
+ protected $segmentName = 'referrerKeyword';
+ protected $acceptValues = 'Encoded%20Keyword, keyword';
+ protected $type = self::TYPE_TEXT;
+ protected $category = 'Referrers_Referrers';
/**
* @param Request $request
diff --git a/plugins/Referrers/Columns/Referrer.php b/plugins/Referrers/Columns/Referrer.php
index 5d1daf16ba..e8afa1a1ac 100644
--- a/plugins/Referrers/Columns/Referrer.php
+++ b/plugins/Referrers/Columns/Referrer.php
@@ -13,8 +13,6 @@ use Piwik\Piwik;
class Referrer extends Dimension
{
- public function getName()
- {
- return Piwik::translate('Referrers_Referrer');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'Referrers_Referrer';
} \ No newline at end of file
diff --git a/plugins/Referrers/Columns/ReferrerName.php b/plugins/Referrers/Columns/ReferrerName.php
index ef21e7a277..f5989ecaf7 100644
--- a/plugins/Referrers/Columns/ReferrerName.php
+++ b/plugins/Referrers/Columns/ReferrerName.php
@@ -9,7 +9,6 @@
namespace Piwik\Plugins\Referrers\Columns;
use Piwik\Common;
-use Piwik\Plugins\Referrers\Segment;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
@@ -18,15 +17,13 @@ class ReferrerName extends Base
{
protected $columnName = 'referer_name';
protected $columnType = 'VARCHAR(70) NULL';
+ protected $type = self::TYPE_TEXT;
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('referrerName');
- $segment->setName('Referrers_ReferrerName');
- $segment->setAcceptedValues('twitter.com, www.facebook.com, Bing, Google, Yahoo, CampaignName');
- $this->addSegment($segment);
- }
+ protected $nameSingular = 'Referrers_ReferrerName';
+ protected $namePlural = 'Referrers_ReferrerNames';
+ protected $segmentName = 'referrerName';
+ protected $acceptValues = 'twitter.com, www.facebook.com, Bing, Google, Yahoo, CampaignName';
+ protected $category = 'Referrers_Referrers';
/**
* @param Request $request
diff --git a/plugins/Referrers/Columns/ReferrerType.php b/plugins/Referrers/Columns/ReferrerType.php
index ef3a66325c..e348d323c2 100644
--- a/plugins/Referrers/Columns/ReferrerType.php
+++ b/plugins/Referrers/Columns/ReferrerType.php
@@ -8,8 +8,8 @@
*/
namespace Piwik\Plugins\Referrers\Columns;
-use Piwik\Piwik;
-use Piwik\Plugins\Referrers\Segment;
+use Piwik\Common;
+use Piwik\Metrics\Formatter;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
@@ -18,20 +18,27 @@ class ReferrerType extends Base
{
protected $columnName = 'referer_type';
protected $columnType = 'TINYINT(1) UNSIGNED NULL';
+ protected $type = self::TYPE_ENUM;
+ protected $segmentName = 'referrerType';
+ protected $nameSingular = 'Referrers_Type';
+ protected $namePlural = 'Referrers_ReferrerTypes';
+ protected $sqlFilterValue = 'Piwik\Plugins\Referrers\getReferrerTypeFromShortName';
+ protected $acceptValues = 'direct, search, website, campaign';
+ protected $category = 'Referrers_Referrers';
- protected function configureSegments()
+ public function formatValue($value, $idSite, Formatter $formatter)
{
- $segment = new Segment();
- $segment->setSegment('referrerType');
- $segment->setName('Referrers_Type');
- $segment->setSqlFilterValue('Piwik\Plugins\Referrers\getReferrerTypeFromShortName');
- $segment->setAcceptedValues('direct, search, website, campaign');
- $this->addSegment($segment);
+ return \Piwik\Plugins\Referrers\getReferrerTypeLabel($value);
}
- public function getName()
+ public function getEnumColumnValues()
{
- return Piwik::translate('Referrers_Type');
+ return array(
+ Common::REFERRER_TYPE_DIRECT_ENTRY => 'direct',
+ Common::REFERRER_TYPE_WEBSITE => 'website',
+ Common::REFERRER_TYPE_SEARCH_ENGINE => 'search',
+ Common::REFERRER_TYPE_CAMPAIGN => 'campaign',
+ );
}
/**
diff --git a/plugins/Referrers/Columns/ReferrerUrl.php b/plugins/Referrers/Columns/ReferrerUrl.php
index e70f75c9ef..2e7aa0ba63 100644
--- a/plugins/Referrers/Columns/ReferrerUrl.php
+++ b/plugins/Referrers/Columns/ReferrerUrl.php
@@ -8,7 +8,6 @@
*/
namespace Piwik\Plugins\Referrers\Columns;
-use Piwik\Plugins\Referrers\Segment;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
@@ -17,15 +16,12 @@ class ReferrerUrl extends Base
{
protected $columnName = 'referer_url';
protected $columnType = 'TEXT NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('referrerUrl');
- $segment->setName('Live_Referrer_URL');
- $segment->setAcceptedValues('http%3A%2F%2Fwww.example.org%2Freferer-page.htm');
- $this->addSegment($segment);
- }
+ protected $type = self::TYPE_TEXT;
+ protected $segmentName = 'referrerUrl';
+ protected $nameSingular = 'Live_Referrer_URL';
+ protected $namePlural = 'Referrers_ReferrerURLs';
+ protected $category = 'Referrers_Referrers';
+ protected $acceptValues = 'http%3A%2F%2Fwww.example.org%2Freferer-page.htm';
/**
* @param Request $request
diff --git a/plugins/Referrers/Columns/SearchEngine.php b/plugins/Referrers/Columns/SearchEngine.php
index c314a801af..d72e169a9a 100644
--- a/plugins/Referrers/Columns/SearchEngine.php
+++ b/plugins/Referrers/Columns/SearchEngine.php
@@ -13,8 +13,6 @@ use Piwik\Piwik;
class SearchEngine extends Dimension
{
- public function getName()
- {
- return Piwik::translate('Referrers_ColumnSearchEngine');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'Referrers_ColumnSearchEngine';
} \ No newline at end of file
diff --git a/plugins/Referrers/Columns/SocialNetwork.php b/plugins/Referrers/Columns/SocialNetwork.php
index 66478d167f..6655a08e85 100644
--- a/plugins/Referrers/Columns/SocialNetwork.php
+++ b/plugins/Referrers/Columns/SocialNetwork.php
@@ -13,8 +13,6 @@ use Piwik\Piwik;
class SocialNetwork extends Dimension
{
- public function getName()
- {
- return Piwik::translate('Referrers_ColumnSocial');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'Referrers_ColumnSocial';
} \ No newline at end of file
diff --git a/plugins/Referrers/Columns/Website.php b/plugins/Referrers/Columns/Website.php
index 33484b6de1..80a9cbd10e 100644
--- a/plugins/Referrers/Columns/Website.php
+++ b/plugins/Referrers/Columns/Website.php
@@ -17,6 +17,9 @@ use Piwik\Tracker\Visitor;
class Website extends Base
{
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'General_Website';
+
/**
* Set using the `[Tracker] create_new_visit_when_website_referrer_changes` INI config option.
* If true, will force new visits if the referrer website changes.
@@ -30,11 +33,6 @@ class Website extends Base
$this->createNewVisitWhenWebsiteReferrerChanges = TrackerConfig::getConfigValue('create_new_visit_when_website_referrer_changes') == 1;
}
- public function getName()
- {
- return Piwik::translate('General_Website');
- }
-
public function shouldForceNewVisit(Request $request, Visitor $visitor, Action $action = null)
{
if (!$this->createNewVisitWhenWebsiteReferrerChanges) {
diff --git a/plugins/Referrers/Columns/WebsitePage.php b/plugins/Referrers/Columns/WebsitePage.php
index 963c83414b..0d3fea8e1b 100644
--- a/plugins/Referrers/Columns/WebsitePage.php
+++ b/plugins/Referrers/Columns/WebsitePage.php
@@ -13,8 +13,6 @@ use Piwik\Piwik;
class WebsitePage extends Dimension
{
- public function getName()
- {
- return Piwik::translate('Referrers_ColumnWebsitePage');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $nameSingular = 'Referrers_ColumnWebsitePage';
} \ No newline at end of file
diff --git a/plugins/Referrers/Segment.php b/plugins/Referrers/Segment.php
deleted file mode 100644
index c51b4e8230..0000000000
--- a/plugins/Referrers/Segment.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-namespace Piwik\Plugins\Referrers;
-
-/**
- * Referrers segment base class.
- *
- */
-class Segment extends \Piwik\Plugin\Segment
-{
- protected function init()
- {
- $this->setCategory('Referrers_Referrers');
- }
-}
diff --git a/plugins/Referrers/functions.php b/plugins/Referrers/functions.php
index c91ebc816d..b38c3bb5f6 100644
--- a/plugins/Referrers/functions.php
+++ b/plugins/Referrers/functions.php
@@ -36,15 +36,19 @@ function getPathFromUrl($url)
function getReferrerTypeLabel($label)
{
switch ($label) {
+ case 'direct':
case Common::REFERRER_TYPE_DIRECT_ENTRY:
$indexTranslation = 'Referrers_DirectEntry';
break;
+ case 'search':
case Common::REFERRER_TYPE_SEARCH_ENGINE:
$indexTranslation = 'Referrers_SearchEngines';
break;
+ case 'website':
case Common::REFERRER_TYPE_WEBSITE:
$indexTranslation = 'Referrers_Websites';
break;
+ case 'campaign':
case Common::REFERRER_TYPE_CAMPAIGN:
$indexTranslation = 'Referrers_Campaigns';
break;
diff --git a/plugins/Referrers/lang/en.json b/plugins/Referrers/lang/en.json
index 7cfcb99281..d19cecfde0 100644
--- a/plugins/Referrers/lang/en.json
+++ b/plugins/Referrers/lang/en.json
@@ -23,9 +23,11 @@
"PluginDescription": "Reports the Referrers data: Search Engines, Keywords, Websites, Campaigns, Social media, Direct entry.",
"Referrer": "Referrer",
"ReferrerName": "Referrer Name",
+ "ReferrerNames": "Referrer Names",
"Referrers": "Referrers",
"ReferrersOverview": "Referrers Overview",
"ReferrerTypes": "Referrer Types",
+ "ReferrerURLs": "Referrer URLs",
"SearchEngines": "Search Engines",
"SearchEnginesDocumentation": "A visitor was referred to your website by a search engine. %1$s See the %2$s report for more details.",
"SearchEnginesReportDocumentation": "This report shows which search engines referred users to your website. %s By clicking on a row in the table, you can see what users were searching for using a specific search engine.",
diff --git a/plugins/Resolution/Columns/Configuration.php b/plugins/Resolution/Columns/Configuration.php
index 6929457495..22f2392897 100644
--- a/plugins/Resolution/Columns/Configuration.php
+++ b/plugins/Resolution/Columns/Configuration.php
@@ -13,8 +13,5 @@ use Piwik\Piwik;
class Configuration extends Dimension
{
- public function getName()
- {
- return Piwik::translate('Resolution_ColumnConfiguration');
- }
+ protected $nameSingular = 'Resolution_ColumnConfiguration';
} \ No newline at end of file
diff --git a/plugins/Resolution/Columns/Resolution.php b/plugins/Resolution/Columns/Resolution.php
index 53a5f8cc4d..b13ce11b31 100644
--- a/plugins/Resolution/Columns/Resolution.php
+++ b/plugins/Resolution/Columns/Resolution.php
@@ -10,7 +10,6 @@ namespace Piwik\Plugins\Resolution\Columns;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugins\Resolution\Segment;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
@@ -19,15 +18,11 @@ class Resolution extends VisitDimension
{
protected $columnName = 'config_resolution';
protected $columnType = 'VARCHAR(18) NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('resolution');
- $segment->setName('Resolution_ColumnResolution');
- $segment->setAcceptedValues('1280x1024, 800x600, etc.');
- $this->addSegment($segment);
- }
+ protected $acceptValues = '1280x1024, 800x600, etc.';
+ protected $segmentName = 'resolution';
+ protected $nameSingular = 'Resolution_ColumnResolution';
+ protected $namePlural = 'Resolution_Resolutions';
+ protected $type = self::TYPE_TEXT;
/**
* @param Request $request
@@ -46,8 +41,4 @@ class Resolution extends VisitDimension
return $resolution;
}
- public function getName()
- {
- return Piwik::translate('Resolution_ColumnResolution');
- }
} \ No newline at end of file
diff --git a/plugins/Resolution/Segment.php b/plugins/Resolution/Segment.php
deleted file mode 100644
index e21a2973d7..0000000000
--- a/plugins/Resolution/Segment.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-namespace Piwik\Plugins\Resolution;
-
-/**
- * Resolution segment base class.
- *
- */
-class Segment extends \Piwik\Plugin\Segment
-{
- protected function init()
- {
- $this->setCategory('General_Visit');
- }
-}
diff --git a/plugins/SegmentEditor/SegmentEditor.php b/plugins/SegmentEditor/SegmentEditor.php
index 63cee53c30..9af29e2194 100644
--- a/plugins/SegmentEditor/SegmentEditor.php
+++ b/plugins/SegmentEditor/SegmentEditor.php
@@ -79,11 +79,15 @@ class SegmentEditor extends \Piwik\Plugin
public function getJsFiles(&$jsFiles)
{
$jsFiles[] = "plugins/SegmentEditor/javascripts/Segmentation.js";
+ $jsFiles[] = "plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator-model.js";
+ $jsFiles[] = "plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.controller.js";
+ $jsFiles[] = "plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.js";
}
public function getStylesheetFiles(&$stylesheets)
{
$stylesheets[] = "plugins/SegmentEditor/stylesheets/segmentation.less";
+ $stylesheets[] = "plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.less";
}
/**
@@ -103,5 +107,20 @@ class SegmentEditor extends \Piwik\Plugin
$translationKeys[] = 'SegmentEditor_SharedWithYou';
$translationKeys[] = 'SegmentEditor_ChooseASegment';
$translationKeys[] = 'SegmentEditor_CurrentlySelectedSegment';
+ $translationKeys[] = 'SegmentEditor_OperatorAND';
+ $translationKeys[] = 'SegmentEditor_OperatorOR';
+ $translationKeys[] = 'SegmentEditor_AddANDorORCondition';
+ $translationKeys[] = 'General_OperationEquals';
+ $translationKeys[] = 'General_OperationNotEquals';
+ $translationKeys[] = 'General_OperationAtMost';
+ $translationKeys[] = 'General_OperationAtLeast';
+ $translationKeys[] = 'General_OperationLessThan';
+ $translationKeys[] = 'General_OperationGreaterThan';
+ $translationKeys[] = 'General_OperationIs';
+ $translationKeys[] = 'General_OperationIsNot';
+ $translationKeys[] = 'General_OperationContains';
+ $translationKeys[] = 'General_OperationDoesNotContain';
+ $translationKeys[] = 'General_OperationStartsWith';
+ $translationKeys[] = 'General_OperationEndsWith';
}
}
diff --git a/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator-model.js b/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator-model.js
new file mode 100644
index 0000000000..113352c9d3
--- /dev/null
+++ b/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator-model.js
@@ -0,0 +1,69 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').factory('segmentGeneratorModel', segmentGeneratorModel);
+
+ segmentGeneratorModel.$inject = ['piwikApi', 'piwik'];
+
+ function segmentGeneratorModel(piwikApi, piwik) {
+
+ var initialSegments = null;
+ var limitPromise = null;
+ var fetchedSiteId = null;
+
+ var model = {
+ isLoading: false,
+ segments : [],
+ loadSegments: loadSegments
+ };
+
+ return model;
+
+ function loadSegments(siteId) {
+ if (model.isLoading) {
+ if (limitPromise) {
+ limitPromise.abort();
+ limitPromise = null;
+ }
+ }
+
+ model.isLoading = true;
+
+ // we need to clear last limit result because we now fetch different data
+ if (fetchedSiteId != siteId) {
+ limitPromise = null;
+ fetchedSiteId = siteId;
+ }
+
+ if (!limitPromise) {
+ var params = {method: 'API.getSegmentsMetadata',filter_limit: '-1'};
+
+ if (siteId === 'all' || !siteId) {
+ params.idSites = 'all';
+ params.idSite = 'all';
+ } else if (siteId) {
+ params.idSites = siteId;
+ params.idSite = siteId;
+ }
+
+ limitPromise = piwikApi.fetch(params);
+ }
+
+ return limitPromise.then(function (response) {
+ model.isLoading = false;
+
+ if (angular.isDefined(response)) {
+ model.segments = response;
+ }
+
+ return response;
+ }).finally(function () {
+ model.isLoading = false;
+ });
+ }
+ }
+})(); \ No newline at end of file
diff --git a/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.controller.js b/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.controller.js
new file mode 100644
index 0000000000..21eed81367
--- /dev/null
+++ b/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.controller.js
@@ -0,0 +1,319 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('SegmentGeneratorController', SegmentGeneratorController);
+
+ SegmentGeneratorController.$inject = ['$scope', 'piwik', 'piwikApi', 'segmentGeneratorModel', '$filter', '$timeout'];
+
+ var findAndExplodeByMatch = function(metric){
+ var matches = ["==" , "!=" , "<=", ">=", "=@" , "!@","<",">", "=^", "=$"];
+ var newMetric = {};
+ var minPos = metric.length;
+ var match, index;
+ var singleChar = false;
+
+ for (var key=0; key < matches.length; key++) {
+ match = matches[key];
+ index = metric.indexOf(match);
+ if(index !== -1){
+ if(index < minPos){
+ minPos = index;
+ if(match.length === 1){
+ singleChar = true;
+ }
+ }
+ }
+ }
+
+ if (minPos < metric.length) {
+ // sth found - explode
+ if(singleChar == true){
+ newMetric.segment = metric.substr(0,minPos);
+ newMetric.matches = metric.substr(minPos,1);
+ newMetric.value = metric.substr(minPos+1);
+ } else {
+ newMetric.segment = metric.substr(0,minPos);
+ newMetric.matches = metric.substr(minPos,2);
+ newMetric.value = metric.substr(minPos+2);
+ }
+ // if value is only "" -> change to empty string
+ if(newMetric.value === '""')
+ {
+ newMetric.value = "";
+ }
+ }
+
+ newMetric.value = decodeURIComponent(newMetric.value);
+ return newMetric;
+ };
+
+ function generateUniqueId() {
+ var id = '';
+ var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
+
+ for (var i = 1; i <= 10; i++) {
+ id += chars.charAt(Math.floor(Math.random() * chars.length));
+ }
+
+ return id;
+ }
+
+ function stripTags(text) {
+ if (text) {
+ text = ('' + text).replace(/(<([^>]+)>)/ig,"");
+ }
+ return text;
+ }
+
+ function SegmentGeneratorController($scope, piwik, piwikApi, segmentGeneratorModel, $filter, $timeout) {
+ var translate = $filter('translate');
+
+ var self = this;
+ var firstSegment = '';
+ var firstMatch = '';
+ this.conditions = [];
+ this.model = segmentGeneratorModel;
+
+ this.segments = {};
+
+ this.matches = {
+ metric: [
+ {key: '==', value: translate('General_OperationEquals')},
+ {key: '!=', value: translate('General_OperationNotEquals')},
+ {key: '<=', value: translate('General_OperationAtMost')},
+ {key: '>=', value: translate('General_OperationAtLeast')},
+ {key: '<', value: translate('General_OperationLessThan')},
+ {key: '>', value: translate('General_OperationGreaterThan')}
+ ],
+ dimension: [
+ {key: '==', value: translate('General_OperationIs')},
+ {key: '!=', value: translate('General_OperationIsNot')},
+ {key: '=@', value: translate('General_OperationContains')},
+ {key: '!@', value: translate('General_OperationDoesNotContain')},
+ {key: '=^', value: translate('General_OperationStartsWith')},
+ {key: '=$', value: translate('General_OperationEndsWith')}
+ ],
+ };
+ this.matches[''] = this.matches.dimension;
+
+ this.andConditionLabel = '';
+
+ this.addNewAndCondition = function () {
+ var condition = {orConditions: []};
+
+ this.addAndCondition(condition);
+ this.addNewOrCondition(condition);
+
+ return condition;
+ };
+
+ this.addAndCondition = function (condition) {
+ this.andConditionLabel = translate('SegmentEditor_OperatorAND');
+ this.conditions.push(condition);
+ this.updateSegmentDefinition();
+ }
+
+ this.addNewOrCondition = function (condition) {
+ var orCondition = {
+ segment: firstSegment,
+ matches: firstMatch,
+ value: ''
+ };
+
+ this.addOrCondition(condition, orCondition);
+ };
+
+ this.addOrCondition = function (condition, orCondition) {
+ orCondition.isLoading = false;
+ orCondition.id = generateUniqueId();
+
+ condition.orConditions.push(orCondition);
+ this.updateSegmentDefinition();
+
+ $timeout(function () {
+ self.updateAutocomplete(orCondition);
+ });
+ };
+
+ this.updateAutocomplete = function (orCondition) {
+ orCondition.isLoading = true;
+
+ this.updateSegmentDefinition();
+
+ var resolved = false;
+
+ var promise = piwikApi.fetch({
+ module: 'API',
+ format: 'json',
+ method: 'API.getSuggestedValuesForSegment',
+ segmentName: orCondition.segment
+ }, {createErrorNotification: false})
+
+ promise.then(function(response) {
+ orCondition.isLoading = false;
+ resolved = true;
+
+ var inputElement = $('.orCondId' + orCondition.id + " .metricValueBlock input");
+
+ if (response && response.result != 'error') {
+
+ inputElement.autocomplete({
+ source: response,
+ minLength: 0,
+ select: function(event, ui){
+ event.preventDefault();
+ orCondition.value = ui.item.value;
+ self.updateSegmentDefinition();
+ $timeout(function () {
+ $scope.$apply();
+ });
+ }
+ });
+ }
+
+ inputElement.off('click');
+ inputElement.click(function (e) {
+ $(inputElement).autocomplete('search', orCondition.value);
+ });
+ }, function(response) {
+ resolved = true;
+ orCondition.isLoading = false;
+
+ var inputElement = $('.orCondId' + orCondition.id + " .metricValueBlock input");
+ inputElement.autocomplete({
+ source: [],
+ minLength: 0
+ });
+ $(inputElement).autocomplete('search', orCondition.value);
+ });
+
+ $timeout(function () {
+ if (!resolved) {
+ promise.abort();
+ }
+ }, 20000);
+ }
+
+ this.removeOrCondition = function (condition, orCondition) {
+ var index = condition.orConditions.indexOf(orCondition);
+ if (index > -1) {
+ condition.orConditions.splice(index, 1);
+ }
+
+ if (condition.orConditions.length === 0) {
+ var index = self.conditions.indexOf(condition);
+ if (index > -1) {
+ self.conditions.splice(index, 1);
+ }
+ if (self.conditions.length === 0) {
+ self.andConditionLabel = '';
+ }
+ }
+
+ this.updateSegmentDefinition();
+ };
+
+ this.getSegmentString = function () {
+ var segmentStr = '';
+
+ angular.forEach(this.conditions, function (conditions) {
+ var subSegmentStr = '';
+
+ angular.forEach(conditions.orConditions, function (orCondition){
+ if (subSegmentStr !== ''){
+ subSegmentStr += ","; // OR operator
+ }
+
+ subSegmentStr += orCondition.segment + orCondition.matches + encodeURIComponent(orCondition.value);
+ })
+
+ if (segmentStr !== '') {
+ segmentStr += ";"; // add AND operator between segment blocks
+ }
+
+ segmentStr += subSegmentStr;
+ });
+
+ return segmentStr
+ };
+
+ this.setSegmentString = function (segmentStr) {
+ var orCondition, condition;
+
+ this.conditions = [];
+
+ if (!segmentStr) {
+ return;
+ }
+
+ var blocks = segmentStr.split(';');
+
+ for (var key = 0; key < blocks.length; key++) {
+ var condition = {orConditions: []};
+ this.addAndCondition(condition);
+
+ blocks[key] = blocks[key].split(',');
+ for (var innerkey = 0; innerkey < blocks[key].length; innerkey++) {
+ orCondition = findAndExplodeByMatch(blocks[key][innerkey]);
+ this.addOrCondition(condition, orCondition);
+ }
+ }
+ };
+
+ this.updateSegmentDefinition = function () {
+ $scope.segmentDefinition = this.getSegmentString();
+ }
+
+ if ($scope.segmentDefinition) {
+ this.setSegmentString($scope.segmentDefinition);
+ }
+
+ $scope.$watch('idsite', function (newValue, oldValue) {
+ if (newValue != oldValue) {
+ reloadSegments(newValue);
+ }
+ });
+
+ reloadSegments($scope.idsite);
+
+ function reloadSegments(idsite) {
+ segmentGeneratorModel.loadSegments(idsite).then(function (segments) {
+
+ self.segmentList = [];
+
+ var groups = {};
+ angular.forEach(segments, function (segment) {
+ if (!segment.category) {
+ segment.category = 'Others';
+ }
+
+ if (!firstSegment) {
+ firstSegment = segment.segment;
+ if (segment.type && self.matches[segment.type]) {
+ firstMatch = self.matches[segment.type][0].key;
+ } else {
+ firstMatch = self.matches[''][0].key
+ }
+ }
+
+ self.segments[segment.segment] = segment;
+
+ var segmentData = {group: segment.category, key: segment.segment, value: segment.name};
+ if ('acceptedValues' in segment && segment.acceptedValues) {
+ segmentData.tooltip = stripTags(segment.acceptedValues);
+ }
+ self.segmentList.push(segmentData);
+ });
+
+ if ($scope.addInitialCondition && self.conditions.length === 0) {
+ self.addNewAndCondition();
+ }
+ });
+ }
+ }
+
+})();
diff --git a/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.html b/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.html
new file mode 100644
index 0000000000..9fd8ab548f
--- /dev/null
+++ b/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.html
@@ -0,0 +1,61 @@
+<div class="segment-generator">
+ <div piwik-activity-indicator loading="segmentGenerator.model.isLoading"></div>
+
+ <div ng-repeat="(conditionIndex,condition) in segmentGenerator.conditions" class="segmentRow{{conditionIndex}}">
+ <div class="segment-rows">
+ <div ng-repeat="orCondition in condition.orConditions" class="orCondId{{ orCondition.id }}">
+ <div class="segment-row">
+ <a ng-click="segmentGenerator.removeOrCondition(condition, orCondition)" class="segment-close"></a>
+ <a href="#" class="segment-loading" ng-show="orCondition.isLoading"></a>
+ <div class="segment-row-inputs valign-wrapper">
+ <div class="segment-input metricListBlock valign-wrapper">
+ <div piwik-field uicontrol="expandable-select" name="segments"
+ title="{{ segmentGenerator.segments[orCondition.segment].name }}"
+ full-width="true"
+ style="width: 100%;"
+ ng-change="segmentGenerator.updateAutocomplete(orCondition)"
+ ng-model="orCondition.segment"
+ options="segmentGenerator.segmentList">
+ </div>
+ </div>
+ <div class="segment-input metricMatchBlock valign-wrapper">
+ <div piwik-field uicontrol="select" name="matches"
+ style="display: inline-block"
+ full-width="true"
+ ng-change="segmentGenerator.updateSegmentDefinition()"
+ ng-model="orCondition.matches"
+ options="segmentGenerator.matches[segmentGenerator.segments[orCondition.segment].type]">
+ </div>
+ </div>
+ <div class="segment-input metricValueBlock valign-wrapper">
+ <div class="form-group row" style="width: 100%;">
+ <div class="input-field col s12">
+ <span role="status" aria-live="polite" class="ui-helper-hidden-accessible"></span>
+ <input ng-model="orCondition.value" placeholder="Value"
+ ng-change="segmentGenerator.updateSegmentDefinition()"
+ type="text" class="autocomplete" title="Value" autocomplete="off">
+ </div>
+ </div>
+ </div>
+ <div class="clear"></div>
+ </div>
+ </div>
+
+ <div class="segment-or">{{ 'SegmentEditor_OperatorOR'|translate }}</div>
+ </div>
+
+ <div class="segment-add-or" ng-click="segmentGenerator.addNewOrCondition(condition)">
+ <div>
+ <a ng-bind-html="'+' + ('SegmentEditor_AddANDorORCondition'|translate:'&lt;span>' + ('SegmentEditor_OperatorOR'|translate) + '&lt;/span>')"></a>
+ </div>
+ </div>
+ </div>
+ <div class="segment-and">{{ 'SegmentEditor_OperatorAND'|translate }}</div>
+ </div>
+
+ <div class="segment-add-row initial" ng-click="segmentGenerator.addNewAndCondition()">
+ <div>
+ <a ng-bind-html="'+' + ('SegmentEditor_AddANDorORCondition'|translate:'&lt;span>' + segmentGenerator.andConditionLabel + '&lt;/span>')"></a>
+ </div>
+ </div>
+</div>
diff --git a/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.js b/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.js
new file mode 100644
index 0000000000..25a0141f19
--- /dev/null
+++ b/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.js
@@ -0,0 +1,64 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-segment-generator>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikSegmentGenerator', piwikSegmentGenerator);
+
+ piwikSegmentGenerator.$inject = ['$document', 'piwik', '$filter', '$timeout'];
+
+ function piwikSegmentGenerator($document, piwik, $filter, $timeout){
+ var defaults = {
+ segmentDefinition: '',
+ addInitialCondition: false,
+ idsite: piwik.idSite
+ };
+
+ return {
+ restrict: 'A',
+ scope: {
+ segmentDefinition: '@',
+ addInitialCondition: '=',
+ idsite: '='
+ },
+ require: "?ngModel",
+ templateUrl: 'plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.html?cb=' + piwik.cacheBuster,
+ controller: 'SegmentGeneratorController',
+ controllerAs: 'segmentGenerator',
+ compile: function (element, attrs) {
+
+ for (var index in defaults) {
+ if (attrs[index] === undefined) {
+ attrs[index] = defaults[index];
+ }
+ }
+
+ return function (scope, element, attrs, ngModel) {
+ if (ngModel) {
+ ngModel.$render = function() {
+ scope.segmentDefinition = ngModel.$viewValue;
+ if (scope.segmentDefinition) {
+ scope.segmentGenerator.setSegmentString(scope.segmentDefinition);
+ } else {
+ scope.segmentGenerator.setSegmentString('');
+ }
+ };
+ }
+
+ scope.$watch('segmentDefinition', function (newValue) {
+ if (ngModel) {
+ ngModel.$setViewValue(newValue);
+ }
+ });
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.less b/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.less
new file mode 100644
index 0000000000..782db0a88e
--- /dev/null
+++ b/plugins/SegmentEditor/angularjs/segment-generator/segmentgenerator.directive.less
@@ -0,0 +1,204 @@
+.segment-generator {
+ width: 900px;
+
+ .segment-row-inputs {
+ .form-group {
+ margin-top: 0;
+ margin-bottom: 0;
+
+ .input-field {
+ margin-top: 0;
+ }
+ }
+ }
+
+ .segment-input input {
+ display: block;
+ width: 96%;
+ padding: 8px 2%;
+ }
+ .segment-input label {
+ display: block;
+ margin: 0 0 5px 0;
+ font-size: 11px;
+ color: #505050;
+ }
+ .segment-input {
+ float: left;
+ padding: 6px 0 5px 3px;
+ border: 2px dashed #EFEFEB;
+ margin-right: 3px;
+ }
+
+ .segment-rows {
+ padding: 4px;
+ margin: 0 3px 0 0;
+ border: 1px solid #a9a399;
+ border-radius: 3px 3px 3px 3px;
+ position: relative;
+ box-shadow: 0 12px 6px -10px rgba(0, 0, 0, 0.42);
+ }
+
+ .segment-add-row,
+ .segment-add-or {
+ cursor: pointer;
+ font-size: 14px;
+ font-weight: bold;
+ background: @theme-color-background-contrast;
+ color: #b9b9b9;
+ text-align: center;
+ position: relative;
+ .border-radius(5px);
+ }
+
+ .segment-add-row > div,
+ .segment-add-or > div {
+ border-radius: 4px;
+ border: 2px dashed #fff;
+ padding: 10px 0;
+ }
+
+ .segment-add-row > div a,
+ .segment-add-or > div a {
+ color: #b9b9b9;
+ text-decoration: none;
+ }
+
+ .segment-input {
+ select, input {
+ .font-default(12px, 14px);
+ color: @theme-color-text;
+ font-weight: 600;
+ margin: 0;
+ height: 32px;
+ }
+ }
+
+ .segment-add-row > div a span,
+ .segment-add-or > div a span {
+ color: @theme-color-brand;
+ text-shadow: none;
+ }
+
+ .segment-add-row {
+ margin: 0 3px 0 0;
+ padding: 0 12px;
+ border: 1px solid #a9a399;
+ border-radius: 3px 3px 3px 3px;
+ box-shadow: 0 12px 6px -10px rgba(0, 0, 0, 0.42);
+ }
+
+ .segment-add-or {
+ text-shadow: 0 1px 0 #fff;
+ display: inline-block;
+ width: 100%;
+ padding: 0 1%;
+ background: #efefeb;
+ border-radius: 3px 3px 3px 3px;
+ }
+
+ .segment-add-or > div {
+ border: 2px dashed #EFEFEB;
+ background-color: #efefeb;
+ }
+
+ .segment-row {
+ border-radius: 3px;
+ display: inline-block;
+ position: relative;
+ width: 887px;
+ padding: 12px 1%;
+ background: #efefeb;
+ padding: 0 5px 0 5px;
+ }
+
+ .segment-row .segment-close {
+ top: 15px;
+ right: 6px;
+ position: absolute;
+ width: 15px;
+ height: 15px;
+ background: url(plugins/SegmentEditor/images/segment-close.png) 0 0 no-repeat;
+ z-index: 9999;
+ }
+
+ .segment-row .segment-loading {
+ top: 25px;
+ right: 30px;
+ position: absolute;
+ width: 15px;
+ height: 15px;
+ background: url(plugins/Morpheus/images/loading-blue.gif) 0 0 no-repeat;
+ }
+
+ .segment-or {
+ display: inline-block;
+ margin: 0 0 0 6%;
+ position: relative;
+ background: #efefeb;
+ padding: 5px 28px;
+ color: #4f4f4f;
+ font-weight: bold;
+ font-size: 14px;
+ text-shadow: 0 1px 0 #fff;
+ }
+
+ .segment-or:before,
+ .segment-or:after {
+ content: '';
+ position: absolute;
+ background: @theme-color-background-base;
+ border: 1px solid #efefeb;
+ width: 10px;
+ top: -1px;
+ bottom: -1px;
+ }
+
+ .segment-or:before {
+ border-left: none;
+ left: 0;
+ border-radius: 0 5px 5px 0;
+ }
+
+ .segment-or:after {
+ border-right: none;
+ right: 0;
+ border-radius: 5px 0 0 5px;
+ }
+
+ .segment-and {
+ display: inline-block;
+ margin: -1px 0 -1px 6%;
+ z-index: 1;
+ position: relative;
+ background: @theme-color-background-contrast;
+ padding: 5px 35px;
+ color: #4f4f4f;
+ font-size: 14px;
+ font-weight: bold;
+ text-shadow: 0 1px 0 #fff;
+ }
+
+ .segment-and:before,
+ .segment-and:after {
+ content: '';
+ position: absolute;
+ background: url(plugins/SegmentEditor/images/bg-inverted-corners.png);
+ border: 1px solid #a9a399;
+ width: 10px;
+ top: 0;
+ bottom: 0;
+ }
+
+ .segment-and:before {
+ border-left: none;
+ left: 0;
+ border-radius: 0 5px 5px 0;
+ }
+
+ .segment-and:after {
+ border-right: none;
+ right: 0;
+ border-radius: 5px 0 0 5px;
+ }
+}
diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js
index 0153a6b491..e018fec7cd 100644
--- a/plugins/SegmentEditor/javascripts/Segmentation.js
+++ b/plugins/SegmentEditor/javascripts/Segmentation.js
@@ -19,11 +19,6 @@ Segmentation = (function($) {
$('.segmentListContainer .segmentationContainer .title').trigger('click').focus();
});
- function preselectFirstMetricMatch(rowNode)
- {
- var matchValue = $(rowNode).find('.metricMatchBlock option:first').attr('value');
- $(rowNode).find('.metricMatchBlock select').val(matchValue);
- }
var segmentation = function segmentation(config) {
if (!config.target) {
@@ -128,98 +123,11 @@ Segmentation = (function($) {
}
};
- var getAndDiv = function(){
- if(typeof andDiv === "undefined"){
- var andDiv = self.editorTemplate.find("> div.segment-and").clone();
- }
- return andDiv.clone();
- };
-
- var getOrDiv = function(){
- if(typeof orDiv === "undefined"){
- var orDiv = self.editorTemplate.find("> div.segment-or").clone();
- }
- return orDiv.clone();
- };
-
- var getMockedInputSet = function(){
- var mockedInputSet = self.editorTemplate.find("div.segment-row-inputs").clone();
- var clonedInput = mockedInputSet.clone();
- preselectFirstMetricMatch(clonedInput);
-
- return clonedInput;
- };
-
- var getMockedInputRowHtml = function(){
- return '<div class="segment-row"><a class="segment-close" href="#"></a><div class="segment-row-inputs">'+getMockedInputSet().html()+'</div></div>';
- };
-
- var getMockedFormRow = function(){
- var mockedFormRow = self.editorTemplate.find("div.segment-rows").clone();
- $(mockedFormRow).find(".segment-row").append(getMockedInputSet()).after(getAddOrBlockButtonHtml).after(getOrDiv());
- var clonedRow = mockedFormRow.clone();
- preselectFirstMetricMatch(clonedRow);
-
- return clonedRow;
- };
-
- var getInitialStateRowsHtml = function(){
- if(typeof initialStateRows === "undefined"){
- var content = self.editorTemplate.find("div.initial-state-rows").html();
- var initialStateRows = $(content).clone();
- }
- return initialStateRows;
- };
-
- var revokeInitialStateRows = function(){
- $(self.form).find(".segment-add-row").remove();
- $(self.form).find(".segment-and").remove();
- };
-
- var appendSpecifiedRowHtml= function(metric) {
- var mockedRow = getMockedFormRow();
- $(self.form).find(".segment-content > h3").after(mockedRow);
- $(self.form).find(".segment-content").append(getAndDiv());
- $(self.form).find(".segment-content").append(getAddNewBlockButtonHtml());
- doDragDropBindings();
- $(self.form).find(".metricList").val(metric).trigger("change");
- preselectFirstMetricMatch(mockedRow);
- };
-
- var appendComplexRowHtml = function(block){
- var key;
- var newRow = getMockedFormRow();
-
- var x = $(newRow).find(".metricMatchBlock select");
- $(newRow).find(".metricListBlock select").val(block[0].metric);
- $(newRow).find(".metricMatchBlock select").val(block[0].match);
- $(newRow).find(".metricValueBlock input").val(block[0].value);
-
- if(block.length > 1) {
- $(newRow).find(".segment-add-or").remove();
- for(key = 1; key < block.length;key++) {
- var newSubRow = $(getMockedInputRowHtml()).clone();
- $(newSubRow).find(".metricListBlock select").val(block[key].metric);
- $(newSubRow).find(".metricMatchBlock select").val(block[key].match);
- $(newSubRow).find(".metricValueBlock input").val(block[key].value);
- $(newRow).append(newSubRow).append(getOrDiv());
- }
- $(newRow).append(getAddOrBlockButtonHtml());
- }
- $(self.form).find(".segment-content").append(newRow).append(getAndDiv());
- };
-
- var applyInitialStateModification = function(){
- $(self.form).find(".segment-add-row").remove();
- $(self.form).find(".segment-content").append(getInitialStateRowsHtml());
- doDragDropBindings();
- };
-
var getSegmentFromId = function (id) {
if(self.availableSegments.length > 0) {
for(var i = 0; i < self.availableSegments.length; i++)
{
- segment = self.availableSegments[i];
+ var segment = self.availableSegments[i];
if(segment.idsegment == id) {
return segment;
}
@@ -361,7 +269,7 @@ Segmentation = (function($) {
newOption = '<option data-idsegment="'+segment.idsegment+'" data-definition="'+(segment.definition).replace(/"/g, '&quot;')+'" title="'+getSegmentTooltipEnrichedWithUsername(segment)+'">'+getSegmentName(segment)+'</option>';
segmentsDropdown.append(newOption);
}
- $(html).find(".segment-content > h3").after(getInitialStateRowsHtml()).show();
+ $(html).find(".segment-content > h3").after('<div piwik-segment-generator add-initial-condition="true"></div>').show();
return html;
};
@@ -372,62 +280,6 @@ Segmentation = (function($) {
});
};
- var findAndExplodeByMatch = function(metric){
- var matches = ["==" , "!=" , "<=", ">=", "=@" , "!@","<",">", "=^", "=$"];
- var newMetric = {};
- var minPos = metric.length;
- var match, index;
- var singleChar = false;
-
- for(var key=0; key < matches.length; key++)
- {
- match = matches[key];
- index = metric.indexOf(match);
- if( index != -1){
- if(index < minPos){
- minPos = index;
- if(match.length == 1){
- singleChar = true;
- }
- }
- }
- }
-
- if(minPos < metric.length){
- // sth found - explode
- if(singleChar == true){
- newMetric.metric = metric.substr(0,minPos);
- newMetric.match = metric.substr(minPos,1);
- newMetric.value = metric.substr(minPos+1);
- } else {
- newMetric.metric = metric.substr(0,minPos);
- newMetric.match = metric.substr(minPos,2);
- newMetric.value = metric.substr(minPos+2);
- }
- // if value is only "" -> change to empty string
- if(newMetric.value == '""')
- {
- newMetric.value = "";
- }
- }
-
- newMetric.value = decodeURIComponent(newMetric.value);
- return newMetric;
- };
-
- var parseSegmentStr = function(segmentStr)
- {
- var blocks;
- blocks = segmentStr.split(";");
- for(var key in blocks){
- blocks[key] = blocks[key].split(",");
- for(var innerkey = 0; innerkey < blocks[key].length; innerkey++){
- blocks[key][innerkey] = findAndExplodeByMatch(blocks[key][innerkey]);
- }
- }
- return blocks;
- };
-
var openEditForm = function(segment){
addForm("edit", segment);
@@ -441,24 +293,14 @@ Segmentation = (function($) {
.html( getSegmentName(segment) )
.prop( 'title', getSegmentTooltipEnrichedWithUsername(segment));
- if(segment.definition != ""){
- revokeInitialStateRows();
- var blocks = parseSegmentStr(segment.definition);
- for(var key in blocks){
- appendComplexRowHtml(blocks[key]);
- }
- $(self.form).find(".segment-content").append(getAddNewBlockButtonHtml());
- }
$(self.form).find(".metricList").each( function(){
$(this).trigger("change", true);
});
- doDragDropBindings();
};
var displayFormAddNewSegment = function (e) {
closeAllOpenLists();
addForm("new");
- doDragDropBindings();
};
var filterSegmentList = function (keyword) {
@@ -561,17 +403,6 @@ Segmentation = (function($) {
displayFormAddNewSegment(e);
});
- self.target.on('change', "select.metricList", function (e, persist) {
- if (typeof persist === "undefined") {
- persist = false;
- }
- alterMatchesList(this, true);
-
- doDragDropBindings();
-
- autoSuggestValues(this, persist);
- });
-
// attach event that will clear segment list filtering input after clicking x
self.target.on('click', ".segmentFilterContainer span", function (e) {
$(e.target).parent().find(".segmentFilter").val(self.translations['General_Search']).trigger('keyup');
@@ -613,8 +444,10 @@ Segmentation = (function($) {
//
self.target.on('click', "a.editSegmentName", function (e) {
- var oldName = $(e.currentTarget).parents("h3").find("span").text();
- $(e.currentTarget).parents("h3").find("span").hide();
+ var $h3 = $(e.currentTarget).parents("h3");
+ $h3.css({'margin': '0 0 0 6px'});
+ var oldName = $h3.find("span").text();
+ $h3.find("span").hide();
$(e.currentTarget).hide();
$(e.currentTarget).before('<input class="edit_segment_name" type="text"/>');
$(e.currentTarget).siblings(".edit_segment_name").focus().val(oldName);
@@ -645,48 +478,6 @@ Segmentation = (function($) {
openEditFormGivenSegment(option);
});
- // attach event that shows/hides child elements of each metric category
- self.target.on('click', '.segment-nav a.metric_category', function (e) {
- $(e.currentTarget).siblings("ul").toggle();
- });
-
- self.target.on('click', ".custom_select_search a", function (e) {
- $(self.form).find(".segmentSearch").val("").trigger("keyup").val(self.translations['General_Search']);
- });
-
- // attach event that will clear search input upon focus if its content is default
- self.target.on('focus', '.segmentSearch', function (e) {
- var search = $(e.currentTarget).val();
- if(search == self.translations['General_Search'])
- $(e.currentTarget).val("");
- });
-
- // attach event that will set search input value upon blur if its content is not null
- self.target.on('blur', '.segmentSearch', function (e) {
- var search = $(e.currentTarget).val();
- if(search == ""){
- clearSearchMetricHighlight();
- $(e.currentTarget).val(self.translations['General_Search']);
- }
- });
-
- // bind search action triggering - only when input text is longer than 2 chars
- self.target.on('keyup', '.segmentSearch', function (e) {
- var search = $(e.currentTarget).val();
- if( search.length >= 2)
- {
- clearTimeout(self.timer);
- self.searchAllowed = true;
- self.timer = setTimeout(function(){
- searchSegments(search);
- }, 500);
- }
- else{
- self.searchAllowed = false;
- clearSearchMetricHighlight();
- }
- });
-
self.target.on('click', ".delete", function() {
var segmentName = $(self.form).find(".segment-content > h3 > span").text();
var segmentId = $(self.form).find(".available_segments_select option:selected").attr("data-idsegment");
@@ -709,7 +500,10 @@ Segmentation = (function($) {
});
$("body").on("keyup", function (e) {
- if(e.keyCode == "27"){
+ if(e.keyCode == "27" || e.which === 27) {
+ if (self.target.find('[uicontrol="expandable-select"] .expandableList:visible').length) {
+ return;
+ }
$(".segmentListContainer", self.target).show();
closeForm();
}
@@ -719,146 +513,6 @@ Segmentation = (function($) {
// segment manipulation events
//
- // upon clicking - add new segment block, then bind 'x' action to newly added row
- self.target.on('click', ".segment-add-row a", function(event, data){
- var mockedRow = getMockedFormRow();
- $(self.form).find(".segment-and:last").after(getAndDiv()).after(mockedRow);
- if(typeof data !== "undefined"){
- $(self.form).find(".metricList:last").val(data);
- }
- $(self.form).find(".metricList:last").trigger('change');
- preselectFirstMetricMatch(mockedRow);
- doDragDropBindings();
- });
-
- self.target.on("click", ".segment-add-row span", function(event, data){
- if(typeof data !== "undefined") {
- var mockedRow = getMockedFormRow();
- $(self.form).find(".segment-and:last").after(getAndDiv()).after(mockedRow);
- preselectFirstMetricMatch(mockedRow);
- $(self.form).find(".metricList:last").val(data).trigger('change');
- doDragDropBindings();
- }
- });
-
- // add new OR block
- self.target.on("click", ".segment-add-or a", function(event, data){
- var parentRows = $(event.currentTarget).parents(".segment-rows");
-
- parentRows.find(".segment-or:last").after(getOrDiv()).after(getMockedInputRowHtml());
- if(typeof data !== "undefined"){
- parentRows.find(".metricList:last").val(data);
- }
- parentRows.find(".metricList:last").trigger('change');
-
- var addedRow = parentRows.find('.segment-row:last');
- preselectFirstMetricMatch(addedRow);
- doDragDropBindings();
- });
-
- self.target.on("click", ".segment-close", function (e) {
- var target = e.currentTarget;
- var rowCnt = $(target).parents(".segment-rows").find(".segment-row").length;
- var globalRowCnt = $(self.form).find(".segment-close").length;
- if(rowCnt > 1){
- $(target).parents(".segment-row").next().remove();
- $(target).parents(".segment-row").remove();
- }
- else if(rowCnt == 1){
- $(target).parents(".segment-rows").next().remove();
- $(target).parents(".segment-rows").remove();
- if(globalRowCnt == 1){
- applyInitialStateModification();
- }
- }
- });
- };
-
- // Request auto-suggest values
- var autoSuggestValues = function(select, persist) {
- var type = $(select).find("option:selected").attr("value");
- if(!persist) {
- var parents = $(select).parents('.segment-row');
- var loadingElement = parents.find(".segment-loading");
- loadingElement.show();
- var inputElement = parents.find(".metricValueBlock input");
- var segmentName = $('option:selected',select).attr('value');
-
- var ajaxHandler = new ajaxHelper();
- ajaxHandler.addParams({
- module: 'API',
- format: 'json',
- method: 'API.getSuggestedValuesForSegment',
- segmentName: segmentName
- }, 'GET');
- ajaxHandler.useRegularCallbackInCaseOfError = true;
- ajaxHandler.setTimeout(20000);
- ajaxHandler.setErrorCallback(function(response) {
- loadingElement.hide();
- inputElement.autocomplete({
- source: [],
- minLength: 0
- });
- $(inputElement).autocomplete('search', $(inputElement).val());
- });
- ajaxHandler.setCallback(function(response) {
- loadingElement.hide();
-
- if (response && response.result != 'error') {
-
- inputElement.autocomplete({
- source: response,
- minLength: 0,
- select: function(event, ui){
- event.preventDefault();
- $(inputElement).val(ui.item.value);
- }
- });
- }
-
- inputElement.click(function (e) {
- $(inputElement).autocomplete('search', $(inputElement).val());
- });
- });
- ajaxHandler.send();
- }
- };
-
- var alterMatchesList = function(select, persist){
- var oldMatch;
- var type = $(select).find("option:selected").attr("data-type");
- var matchSelector = $(select).parents(".segment-input").siblings(".metricMatchBlock").find("select");
- if(persist === true){
- oldMatch = matchSelector.find("option:selected").val();
- } else {
- oldMatch = "";
- }
-
- if(type === "dimension" || type === "metric"){
- matchSelector.empty();
- var optionsHtml = "";
- for(var key in self.availableMatches[type]){
- optionsHtml += '<option value="'+key+'">'+self.availableMatches[type][key]+'</option>';
- }
- }
-
- matchSelector.append(optionsHtml);
-
- if (matchSelector.find('option[value="' + oldMatch + '"]').length) {
- matchSelector.val(oldMatch);
- } else {
- preselectFirstMetricMatch(matchSelector.parent());
- }
- };
-
- var getAddNewBlockButtonHtml = function()
- {
- if(typeof addNewBlockButton === "undefined")
- {
- var addNewBlockButton = self.editorTemplate.find("> div.segment-add-row").clone();
- }
- return addNewBlockButton.clone();
-
};
var getAddOrBlockButtonHtml = function(){
@@ -868,16 +522,6 @@ Segmentation = (function($) {
return addOrBlockButton.clone();
};
- var placeSegmentationFormControls = function(){
- doDragDropBindings();
- $(self.form).find(".scrollable").jScrollPane({
- showArrows: true,
- autoReinitialise: true,
- verticalArrowPositions: 'os',
- horizontalArrowPositions: 'os'
- });
- };
-
function openEditFormGivenSegment(option) {
var idsegment = option.attr("data-idsegment");
@@ -890,103 +534,6 @@ Segmentation = (function($) {
}
}
- var doDragDropBindings = function(){
- $(self.form).find(".segment-nav div > ul > li > ul > li").sortable({
- cursor: 'move',
- revert: 10,
- revertDuration: 0,
- snap: false,
- helper: 'clone',
- appendTo: 'body'
- });
-
- $(self.form).find(".metricListBlock").droppable({
- hoverClass: "hovered",
- drop: function( event, ui ) {
- $(this).find("select").val(ui.draggable.parent().attr("data-metric")).trigger("change");
- }
- });
-
- $(self.form).find(".segment-add-row > div").droppable({
- hoverClass: "hovered",
- drop: function( event, ui ) {
- $(this).find("a").trigger("click", [ui.draggable.parent().attr("data-metric")]);
- if($(this).find("a > span").length == 0){
- revokeInitialStateRows();
- appendSpecifiedRowHtml([ui.draggable.parent().attr("data-metric")]);
- }
- }
- });
-
- $(self.form).find(".segment-add-or > div").droppable({
- hoverClass: "hovered",
- drop: function( event, ui ) {
- $(this).find("a").trigger("click", [ui.draggable.parent().attr("data-metric")]);
- }
- });
- };
-
- var searchSegments = function(search){
- // pre-process search string to normalized form
- search = normalizeSearchString(search);
-
- // ---
- // clear all previous search highlights and hide all categories
- // to allow further showing only matching ones, while others remain invisible
- clearSearchMetricHighlight();
- $(self.form).find('.segment-nav div > ul > li').hide();
- var curStr = "";
- var curMetric = "";
-
- // 1 - do most obvious selection -> mark whole categories matching search string
- // also expand whole category
- $(self.form).find('.segment-nav div > ul > li').each( function(){
- curStr = normalizeSearchString($(this).find("a.metric_category").text());
- if(curStr.indexOf(search) > -1) {
- $(this).addClass("searchFound");
- $(this).find("ul").show();
- $(this).find("li").show();
- $(this).show();
- }
- }
- );
-
- // 2 - among all unselected categories find metrics which match and mark parent as search result
- $(self.form).find(".segment-nav div > ul > li:not(.searchFound)").each(function(){
- var parent = this;
- $(this).find("li").each( function(){
- var curStr = normalizeSearchString($(this).text());
- var curMetric = normalizeSearchString($(this).attr("data-metric"));
- $(this).hide();
- if(curStr.indexOf(search) > -1 || curMetric.indexOf(search) > -1){
- $(this).show();
- $(parent).find("ul").show();
- $(parent).addClass("searchFound").show();
- }
- });
- });
-
- if( $(self.form).find("li.searchFound").length == 0)
- {
- $(self.form).find("div > ul").prepend('<li class="no_results"><a>'+self.translations['General_SearchNoResults']+'</a></li>').show();
- }
- // check if search allow flag was revoked - then clear all search results
- if(self.searchAllowed == false)
- {
- clearSearchMetricHighlight();
- self.searchAllowed = true;
- }
-
- };
-
- var clearSearchMetricHighlight = function(){
- $(self.form).find('.no_results').remove();
- $(self.form).find('.segment-nav div > ul > li').removeClass("searchFound").show();
- $(self.form).find('.segment-nav div > ul > li').removeClass("others").show();
- $(self.form).find('.segment-nav div > ul > li > ul > li').show();
- $(self.form).find('.segment-nav div > ul > li > ul').hide();
- };
-
var normalizeSearchString = function(search){
search = search.replace(/^\s+|\s+$/g, ''); // trim
search = search.toLowerCase();
@@ -1012,6 +559,7 @@ Segmentation = (function($) {
}
// remove any remaining forms
+
self.form = getFormHtml();
self.target.prepend(self.form);
@@ -1025,13 +573,8 @@ Segmentation = (function($) {
self.form.addClass('anchorRight');
}
- placeSegmentationFormControls();
-
- // needs to be done before jQuery selects are built, as angular compiler screws up the selected values
- piwikHelper.compileAngularComponents(self.target);
-
if(mode == "edit") {
- $(self.form).find('.enable_all_users_select > option[value="' + segment.enable_all_users + '"]').prop("selected",true);
+ var userSelector = $(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)) {
@@ -1040,6 +583,9 @@ Segmentation = (function($) {
$(self.form).find('.visible_to_website_select > option[value="'+segment.enable_only_idsite+'"]').prop("selected",true);
$(self.form).find('.auto_archive_select > option[value="'+segment.auto_archive+'"]').prop("selected",true);
+ if (segment.definition != ""){
+ self.form.find('[piwik-segment-generator]').attr('segment-definition', segment.definition);
+ }
}
makeDropList(".enable_all_users" , ".enable_all_users_select");
@@ -1058,6 +604,8 @@ Segmentation = (function($) {
$(".segmentListContainer", self.target).hide();
self.target.closest('.segmentEditorPanel').addClass('editing');
+
+ piwikHelper.compileAngularComponents(self.target);
};
var closeForm = function () {
@@ -1065,34 +613,14 @@ Segmentation = (function($) {
self.target.closest('.segmentEditorPanel').removeClass('editing');
};
- var parseForm = function(){
- var segmentStr = "";
- $(self.form).find(".segment-rows").each( function(){
- var subSegmentStr = "";
-
- $(this).find(".segment-row").each( function(){
- if(subSegmentStr != ""){
- subSegmentStr += ","; // OR operator
- }
- $(this).find(".segment-row-inputs").each( function(){
- var metric = $(this).find(".metricList option:selected").val();
- var match = $(this).find(".metricMatchBlock > select option:selected").val();
- var value = $(this).find(".segment-input input").val();
- subSegmentStr += metric + match + encodeURIComponent(value);
- });
- });
- if(segmentStr != "")
- {
- segmentStr += ";"; // add AND operator between segment blocks
- }
- segmentStr += subSegmentStr;
- });
- return segmentStr
- };
+ function getSegmentGeneratorController()
+ {
+ return angular.element(self.form.find('.segment-generator')).scope().segmentGenerator;
+ }
var parseFormAndSave = function(){
var segmentName = $(self.form).find(".segment-content > h3 >span").text();
- var segmentStr = parseForm();
+ var segmentStr = getSegmentGeneratorController().getSegmentString();
var segmentId = $(self.form).find('.available_segments_select > option:selected').attr("data-idsegment");
var user = $(self.form).find(".enable_all_users_select option:selected").val();
var autoArchive = $(self.form).find(".auto_archive_select option:selected").val() || 0;
diff --git a/plugins/SegmentEditor/stylesheets/segmentation.less b/plugins/SegmentEditor/stylesheets/segmentation.less
index a4e291ed46..a38df86939 100644
--- a/plugins/SegmentEditor/stylesheets/segmentation.less
+++ b/plugins/SegmentEditor/stylesheets/segmentation.less
@@ -41,12 +41,11 @@
}
.metricMatchBlock {
- width: 120px;
margin-right: 11px;
}
.metricValueBlock {
- width: 352px;
+ width: 500px;
}
div.scrollable {
@@ -98,81 +97,16 @@ div.scrollable {
background: url(plugins/SegmentEditor/images/reset_search.png);
}
-.segment-element .segment-nav {
- position: absolute;
- top: 7px;
- left: 5px;
- bottom: 135px;
- float: left;
- width: 170px;
-}
-
-.segment-element .segment-nav h4 {
- font-size: 14px;
- font-weight: bold;
- padding: 0 0 8px 0;
-}
-
-.segment-element .segment-nav h4 a {
- color: #255792;
- text-decoration: none;
-}
-
-.segment-element .segment-nav div > ul {
- padding: 0 0 0 15px;
-}
-
-.segment-element .segment-nav div > ul > li {
- padding: 2px 0;
- line-height: 14px;
-}
-
-.segment-element .segment-nav div > ul > li li {
- padding: 1px;
- border-radius: 3px 3px 3px 3px;
-}
-
-.segment-element .segment-nav div > ul > li li:hover {
- padding: 0;
- border: 1px solid #cfccbd;
- border-bottom: 1px solid #7c7a72;
-}
-
-.segment-element .segment-nav div > ul > li li:hover a {
- cursor: move;
- padding: 1px 0 2px 8px;
- border-top: 1px solid #fff;
- background: #eae8e3 url(plugins/SegmentEditor/images/segment-move.png) 100% 50% no-repeat;
-}
-
-.segment-element .segment-nav div > ul > li li a {
- padding: 2px 0 2px 8px;
- font-weight: normal;
- display: block;
-}
-
-.segment-element .segment-nav div > ul > li ul {
- margin: 2px 0 -3px 10px;
-}
-
-.segment-element .segment-nav div > ul > li a {
- color: @theme-color-text-light;
- font-size: 11px;
- font-weight: bold;
- text-decoration: none;
- text-shadow: 0 1px 0 #fff;
-}
-
.segment-element .segment-content {
min-height: 300px;
- padding: 0 0 20px 203px;
+ padding: 0 0 20px 0;
}
.segment-element .segment-content h3 {
font-size: 16px;
font-weight: bold;
color: #505050;
- margin: 11px 0 0 0;
+ margin: 0 0 0 0;
text-shadow: 0 1px 0 #fff;
}
@@ -182,194 +116,10 @@ div.scrollable {
margin: -1px 0 0 0;
}
-.segment-element .segment-content .segment-rows {
- padding: 4px;
- margin: 0 3px 0 0;
- border: 1px solid #a9a399;
- border-radius: 3px 3px 3px 3px;
- position: relative;
- box-shadow: 0 12px 6px -10px rgba(0, 0, 0, 0.42);
-}
-
-.segment-element .segment-content .segment-add-row,
-.segment-element .segment-content .segment-add-or {
- font-size: 14px;
- font-weight: bold;
- background: @theme-color-background-contrast;
- color: #b9b9b9;
- text-align: center;
- position: relative;
-}
-
-.segment-element .segment-content .segment-add-row > div,
-.segment-element .segment-content .segment-add-or > div {
- border-radius: 4px;
- border: 2px dashed #fff;
- padding: 10px 0;
-}
-
-.segment-element .segment-content .segment-add-row > div a,
-.segment-element .segment-content .segment-add-or > div a {
- color: #b9b9b9;
- text-decoration: none;
-}
-
-.segment-element .segment-content .segment-add-row > div a span,
-.segment-element .segment-content .segment-add-or > div a span {
- color: #255792;
-}
-
-.segment-element .segment-content .segment-add-row {
- margin: 0 3px 0 0;
- padding: 0 12px;
- border: 1px solid #a9a399;
- border-radius: 3px 3px 3px 3px;
- box-shadow: 0 12px 6px -10px rgba(0, 0, 0, 0.42);
-}
-
-.segment-element .segment-content .segment-add-or {
- text-shadow: 0 1px 0 #fff;
- display: inline-block;
- width: 100%;
- padding: 0 1%;
- background: #efefeb;
- border-radius: 3px 3px 3px 3px;
-}
-
-.segment-element .segment-content .segment-add-or > div {
- border: 2px dashed #EFEFEB;
- background-color: #efefeb;
-}
-
-.segment-element .segment-content .segment-row {
- border-radius: 3px;
- display: inline-block;
- position: relative;
- width: 811px;
- padding: 12px 1%;
- background: #efefeb;
- padding: 7px 5px 0 5px;
-}
-
-.segment-element .segment-content .segment-row .segment-close {
- top: 15px;
- right: 6px;
- position: absolute;
- width: 15px;
- height: 15px;
- background: url(plugins/SegmentEditor/images/segment-close.png) 0 0 no-repeat;
-}
-
-.segment-element .segment-content .segment-row .segment-loading {
- display: none;
- top: 25px;
- right: 30px;
- position: absolute;
- width: 15px;
- height: 15px;
- background: url(plugins/Morpheus/images/loading-blue.gif) 0 0 no-repeat;
-}
-
-.segment-element .segment-content .segment-or {
- display: inline-block;
- margin: 0 0 0 6%;
- position: relative;
- background: #efefeb;
- padding: 5px 28px;
- color: #4f4f4f;
- font-weight: bold;
- font-size: 14px;
- text-shadow: 0 1px 0 #fff;
-}
-
-.segment-element .segment-content .segment-or:before,
-.segment-element .segment-content .segment-or:after {
- content: '';
- position: absolute;
- background: @theme-color-background-base;
- border: 1px solid #efefeb;
- width: 10px;
- top: -1px;
- bottom: -1px;
-}
-
-.segment-element .segment-content .segment-or:before {
- border-left: none;
- left: 0px;
- border-radius: 0 5px 5px 0;
-}
-
-.segment-element .segment-content .segment-or:after {
- border-right: none;
- right: 0px;
- border-radius: 5px 0 0 5px;
-}
-
.segment-element .edit_segment_name {
width: 200px;
}
-.segment-element .segment-content .segment-and {
- display: inline-block;
- margin: -1px 0 -1px 6%;
- z-index: 1;
- position: relative;
- background: @theme-color-background-contrast;
- padding: 5px 35px;
- color: #4f4f4f;
- font-size: 14px;
- font-weight: bold;
- text-shadow: 0 1px 0 #fff;
-}
-
-.segment-element .segment-content .segment-and:before,
-.segment-element .segment-content .segment-and:after {
- content: '';
- position: absolute;
- background: url(plugins/SegmentEditor/images/bg-inverted-corners.png);
- border: 1px solid #a9a399;
- width: 10px;
- top: 0px;
- bottom: 0px;
-}
-
-.segment-element .segment-content .segment-and:before {
- border-left: none;
- left: 0px;
- border-radius: 0 5px 5px 0;
-}
-
-.segment-element .segment-content .segment-and:after {
- border-right: none;
- right: 0px;
- border-radius: 5px 0 0 5px;
-}
-
-.segment-element .segment-content .segment-input {
- float: left;
- padding: 6px 0 5px 3px;
- border: 2px dashed #EFEFEB;
- margin-right: 3px;
-}
-
-.segment-element .segment-content .segment-input label {
- display: block;
- margin: 0 0 5px 0;
- font-size: 11px;
- color: #505050;
-}
-
-.segment-element .segment-content .segment-input select,
-.segment-element .segment-content .segment-input input {
- display: block;
- width: 96%;
- padding-top: 7px;
- padding-bottom: 7px;
-}
-
-.segment-element .segment-content .segment-input input {
- padding: 8px 2%;
-}
.segment-element .segment-top {
font-size: 11px;
@@ -627,7 +377,7 @@ a.metric_category {
.segment-element {
z-index: 999;
- width: 1040px;
+ width: 908px;
}
.segmentationSelectorContainer {
@@ -688,12 +438,6 @@ a.metric_category {
left: auto;
}
-.available_segments {
- display: inline-block;
- width: 150px;
- padding-left: 5px;
-}
-
.segmentationTitle,
.segment-element .segment-nav a.dropdown,
.segname {
diff --git a/plugins/SegmentEditor/templates/_segmentSelector.twig b/plugins/SegmentEditor/templates/_segmentSelector.twig
index 8a4ae950c8..ae9b9d6c16 100644
--- a/plugins/SegmentEditor/templates/_segmentSelector.twig
+++ b/plugins/SegmentEditor/templates/_segmentSelector.twig
@@ -32,103 +32,15 @@
</div>
</div>
- <div class="initial-state-rows">{# no space here important for jQuery #}<div class="segment-add-row initial"><div>
- <span>+ {{ 'SegmentEditor_DragDropCondition'|translate|raw }}</span>
- </div></div>
- <div class="segment-and">{{ 'SegmentEditor_OperatorAND'|translate|raw }}</div>
- <div class="segment-add-row initial"><div>
- <span>+ {{ 'SegmentEditor_DragDropCondition'|translate|raw }}</span>
- </div></div>
- </div>
-
- <div class="segment-row-inputs">
- <div class="segment-input metricListBlock">
- <select title="{{ 'SegmentEditor_ChooseASegment'|translate }}" class="metricList browser-default">
- {% for category,segmentsInCategory in segmentsByCategory %}
- <optgroup label="{{ category }}">
- {% for segmentInCategory in segmentsInCategory %}
- <option data-type="{{ segmentInCategory.type }}" value="{{ segmentInCategory.segment }}">{{ segmentInCategory.name }}</option>
- {% endfor %}
- </optgroup>
- {% endfor %}
- </select>
- </div>
- <div class="segment-input metricMatchBlock">
- <select title="{{ 'General_Matches'|translate }}" class="browser-default">
- <option value="==">{{ 'General_OperationEquals'|translate }}</option>
- <option value="!=">{{ 'General_OperationNotEquals'|translate }}</option>
- <option value="<=">{{ 'General_OperationAtMost'|translate }}</option>
- <option value=">=">{{ 'General_OperationAtLeast'|translate }}</option>
- <option value="<">{{ 'General_OperationLessThan'|translate }}</option>
- <option value=">">{{ 'General_OperationGreaterThan'|translate }}</option>
- <option value="=@">{{ 'General_OperationContains'|translate }}</option>
- <option value="!@">{{ 'General_OperationDoesNotContain'|translate }}</option>
- <option value="=^">{{ 'General_OperationStartsWith'|translate }}</option>
- <option value="=$">{{ 'General_OperationEndsWith'|translate }}</option>
- </select>
- </div>
- <div class="segment-input metricValueBlock">
- <input type="text" class="browser-default" title="{{ 'General_Value'|translate }}">
- </div>
- <div class="clear"></div>
- </div>
- <div class="segment-rows">
- <div class="segment-row">
- <a href="#" class="segment-close"></a>
- <a href="#" class="segment-loading"></a>
- </div>
- </div>
- <div class="segment-or">{{ 'SegmentEditor_OperatorOR'|translate }}</div>
- <div class="segment-add-or"><div>
- {% set orCondition %}<span>{{ 'SegmentEditor_OperatorOR'|translate }}</span>{% endset %}
- <a href="#"> + {{ 'SegmentEditor_AddANDorORCondition'|translate(orCondition)|raw }} </a>
- </div>
- </div>
- <div class="segment-and">{{ 'SegmentEditor_OperatorAND'|translate }}</div>
- <div class="segment-add-row"><div>
- {% set andCondition %}<span>{{ 'SegmentEditor_OperatorAND'|translate }}</span>{% endset %}
- <a href="#">+ {{ 'SegmentEditor_AddANDorORCondition'|translate(andCondition)|raw }}</a>
- </div>
- </div>
<div class="segment-element borderedControl expanded">
- <div class="segment-nav">
- <h4 class="visits">
- <span class="icon-segment"></span><span class="available_segments"><strong>
- <select class="available_segments_select browser-default"></select>
- </strong></span></h4>
-
- <div class="custom_select_search">
- <a href="#"></a>
- <input type="text" aria-haspopup="true" aria-autocomplete="list" role="textbox" autocomplete="off" class="inp ui-autocomplete-input segmentSearch browser-default" value="{{ 'General_Search'|translate }}" length="15">
- </div>
- <div class="scrollable">
- <ul>
- {% for category,segmentsInCategory in segmentsByCategory %}
- <li data="visit">
- <a class="metric_category" href="#">{{ category }}</a>
- <ul style="display:none;">
- {% for segmentInCategory in segmentsInCategory %}
- {% set title = segmentInCategory.name %}
- {% if segmentInCategory.unionOfSegments is defined and segmentInCategory.unionOfSegments %}
- {% set title = 'SegmentEditor_SegmentXIsAUnionOf'|translate(title) %}
- {% for unionSegment in segmentInCategory.unionOfSegments %}
- {% set title = title ~ ' ' ~ unionSegment %}
- {% if not loop.last %}
- {% set title = title ~ ',' %}
- {% endif %}
- {% endfor %}
- {% endif %}
- <li data-metric="{{ segmentInCategory.segment }}" title="{{ title|e('html_attr') }}"><a class="ddmetric" href="#">{{ segmentInCategory.name }}</a></li>
- {% endfor %}
- </ul>
- </li>
- {% endfor %}
- </ul>
- </div>
- </div>
<div class="segment-content">
<div class="segment-top" {% if not isSuperUser %}style="display:none"{% endif %}>
+
+ <span class="icon-segment"></span><span class="available_segments"><strong>
+ <select class="available_segments_select browser-default"></select>
+ </strong></span>
+
{{ 'SegmentEditor_ThisSegmentIsVisibleTo'|translate }} <span class="enable_all_users"><strong>
<select class="enable_all_users_select">
<option selected="1" value="0">{{ 'SegmentEditor_VisibleToMe'|translate }}</option>
@@ -152,7 +64,7 @@
</strong></span>
</div>
- <h3>{{ 'General_Name'|translate }}: <span class="segmentName"></span> <a class="editSegmentName" href="#">{{ 'General_Edit'|translate|lower }}</a></h3>
+ <h3 style="margin: 12px 6px;">{{ 'General_Name'|translate }}: <span class="segmentName"></span> <a class="editSegmentName" href="#">{{ 'General_Edit'|translate|lower }}</a></h3>
</div>
<div class="segment-footer">
<div piwik-rate-feature title="Segment Editor" style="display:inline-block;float: left;margin-top: 2px;margin-right: 10px;"></div>
diff --git a/plugins/SegmentEditor/tests/Integration/SegmentEditorTest.php b/plugins/SegmentEditor/tests/Integration/SegmentEditorTest.php
index 2635db94f2..37ba8ffb65 100644
--- a/plugins/SegmentEditor/tests/Integration/SegmentEditorTest.php
+++ b/plugins/SegmentEditor/tests/Integration/SegmentEditorTest.php
@@ -200,7 +200,7 @@ class SegmentEditorTest extends IntegrationTestCase
$timestampProperties = array('ts_last_edit', 'ts_created');
foreach ($timestampProperties as $propertyName) {
if (isset($segmentInfo[$propertyName])) {
- $segmentInfo[$propertyName] = substr($segmentInfo[$propertyName], 0, strlen($segmentInfo[$propertyName] - 2));
+ $segmentInfo[$propertyName] = substr($segmentInfo[$propertyName], 0, strlen($segmentInfo[$propertyName]) - 2);
}
}
}
diff --git a/plugins/UserCountry/Categories/LocationsCategory.php b/plugins/UserCountry/Categories/LocationsCategory.php
new file mode 100644
index 0000000000..59bc376906
--- /dev/null
+++ b/plugins/UserCountry/Categories/LocationsCategory.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\UserCountry\Categories;
+
+use Piwik\Category\Category;
+
+class LocationsCategory extends Category
+{
+ protected $id = 'UserCountry_VisitLocation';
+ protected $order = 7;
+}
diff --git a/plugins/UserCountry/Columns/City.php b/plugins/UserCountry/Columns/City.php
index 3521768f4d..8357d3e551 100644
--- a/plugins/UserCountry/Columns/City.php
+++ b/plugins/UserCountry/Columns/City.php
@@ -8,31 +8,21 @@
*/
namespace Piwik\Plugins\UserCountry\Columns;
-use Piwik\Piwik;
use Piwik\Plugins\UserCountry\LocationProvider;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
-use Piwik\Plugins\UserCountry\Segment;
class City extends Base
{
protected $columnName = 'location_city';
protected $columnType = 'varchar(255) DEFAULT NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('city');
- $segment->setName('UserCountry_City');
- $segment->setAcceptedValues('Sydney, Sao Paolo, Rome, etc.');
- $this->addSegment($segment);
- }
-
- public function getName()
- {
- return Piwik::translate('UserCountry_City');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $segmentName = 'city';
+ protected $nameSingular = 'UserCountry_City';
+ protected $namePlural = 'UserCountryMap_Cities';
+ protected $acceptValues = 'Sydney, Sao Paolo, Rome, etc.';
+ protected $category = 'UserCountry_VisitLocation';
/**
* @param Request $request
diff --git a/plugins/UserCountry/Columns/Continent.php b/plugins/UserCountry/Columns/Continent.php
index 089215dcf1..9080a37743 100644
--- a/plugins/UserCountry/Columns/Continent.php
+++ b/plugins/UserCountry/Columns/Continent.php
@@ -9,12 +9,29 @@
namespace Piwik\Plugins\UserCountry\Columns;
use Piwik\Columns\Dimension;
-use Piwik\Piwik;
+use Piwik\Common;
+use Piwik\Metrics\Formatter;
class Continent extends Dimension
{
- public function getName()
+ protected $dbTableName = 'log_visit';
+ protected $columnName = 'location_country';
+ protected $type = self::TYPE_TEXT;
+ protected $category = 'UserCountry_VisitLocation';
+ protected $nameSingular = 'UserCountry_Continent';
+ protected $namePlural = 'UserCountry_Continents';
+ protected $segmentName = 'continentCode';
+ protected $acceptValues = 'eur, asi, amc, amn, ams, afr, ant, oce';
+ protected $sqlFilter = 'Piwik\Plugins\UserCountry\UserCountry::getCountriesForContinent';
+
+ public function groupValue($value, $idSite)
{
- return Piwik::translate('UserCountry_Continent');
+ return Common::getContinent($value);
}
+
+ public function formatValue($value, $idSite, Formatter $formatter)
+ {
+ return \Piwik\Plugins\UserCountry\continentTranslate($value);
+ }
+
} \ No newline at end of file
diff --git a/plugins/UserCountry/Columns/Country.php b/plugins/UserCountry/Columns/Country.php
index 9ce817ae9d..d6e89d3789 100644
--- a/plugins/UserCountry/Columns/Country.php
+++ b/plugins/UserCountry/Columns/Country.php
@@ -12,41 +12,33 @@ use Piwik\Common;
use Piwik\Config;
use Piwik\Container\StaticContainer;
use Piwik\Intl\Data\Provider\RegionDataProvider;
+use Piwik\Metrics\Formatter;
use Piwik\Network\IP;
-use Piwik\Piwik;
use Piwik\Plugin\Manager;
use Piwik\Plugins\Provider\Provider as ProviderProvider;
use Piwik\Plugins\UserCountry\LocationProvider;
-use Piwik\Plugins\UserCountry\Segment;
use Piwik\Tracker\Visit;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
+require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/functions.php';
+
class Country extends Base
{
protected $columnName = 'location_country';
protected $columnType = 'CHAR(3) NULL';
+ protected $type = self::TYPE_TEXT;
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('countryCode');
- $segment->setName('UserCountry_Country');
- $segment->setAcceptedValues('de, us, fr, in, es, etc.');
- $this->addSegment($segment);
-
- $segment = new Segment();
- $segment->setSegment('continentCode');
- $segment->setName('UserCountry_Continent');
- $segment->setSqlFilter('Piwik\Plugins\UserCountry\UserCountry::getCountriesForContinent');
- $segment->setAcceptedValues('eur, asi, amc, amn, ams, afr, ant, oce');
- $this->addSegment($segment);
- }
+ protected $category = 'UserCountry_VisitLocation';
+ protected $nameSingular = 'UserCountry_Country';
+ protected $namePlural = 'UserCountryMap_Countries';
+ protected $segmentName = 'countryCode';
+ protected $acceptValues = 'de, us, fr, in, es, etc.';
- public function getName()
+ public function formatValue($value, $idSite, Formatter $formatter)
{
- return Piwik::translate('UserCountry_Country');
+ return \Piwik\Plugins\UserCountry\countryTranslate($value);
}
/**
diff --git a/plugins/UserCountry/Columns/Latitude.php b/plugins/UserCountry/Columns/Latitude.php
index 4cb29bf719..462b4812a1 100644
--- a/plugins/UserCountry/Columns/Latitude.php
+++ b/plugins/UserCountry/Columns/Latitude.php
@@ -8,9 +8,7 @@
*/
namespace Piwik\Plugins\UserCountry\Columns;
-use Piwik\Piwik;
use Piwik\Plugins\UserCountry\LocationProvider;
-use Piwik\Plugins\UserCountry\Segment;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
@@ -19,20 +17,12 @@ class Latitude extends Base
{
protected $columnName = 'location_latitude';
protected $columnType = 'decimal(9, 6) DEFAULT NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('latitude');
- $segment->setName('UserCountry_Latitude');
- $segment->setAcceptedValues('-33.578, 40.830, etc.<br/>You can select visitors within a lat/long range using &segment=lat&gt;X;lat&lt;Y;long&gt;M;long&lt;N.');
- $this->addSegment($segment);
- }
-
- public function getName()
- {
- return Piwik::translate('UserCountry_Latitude');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $category = 'UserCountry_VisitLocation';
+ protected $segmentName = 'latitude';
+ protected $nameSingular = 'UserCountry_Latitude';
+ protected $namePlural = 'UserCountry_Latitudes';
+ protected $acceptValues = '-33.578, 40.830, etc.<br/>You can select visitors within a lat/long range using &segment=lat&gt;X;lat&lt;Y;long&gt;M;long&lt;N.';
/**
* @param Request $request
diff --git a/plugins/UserCountry/Columns/Longitude.php b/plugins/UserCountry/Columns/Longitude.php
index d54a66ce6a..ab46fb27ef 100644
--- a/plugins/UserCountry/Columns/Longitude.php
+++ b/plugins/UserCountry/Columns/Longitude.php
@@ -8,9 +8,7 @@
*/
namespace Piwik\Plugins\UserCountry\Columns;
-use Piwik\Piwik;
use Piwik\Plugins\UserCountry\LocationProvider;
-use Piwik\Plugins\UserCountry\Segment;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
@@ -19,20 +17,12 @@ class Longitude extends Base
{
protected $columnName = 'location_longitude';
protected $columnType = 'decimal(9, 6) DEFAULT NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('longitude');
- $segment->setName('UserCountry_Longitude');
- $segment->setAcceptedValues('-70.664, 14.326, etc.');
- $this->addSegment($segment);
- }
-
- public function getName()
- {
- return Piwik::translate('UserCountry_Longitude');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $category = 'UserCountry_VisitLocation';
+ protected $acceptValues = '-70.664, 14.326, etc.';
+ protected $segmentName = 'longitude';
+ protected $nameSingular = 'UserCountry_Longitude';
+ protected $namePlural = 'UserCountry_Longitudes';
/**
* @param Request $request
diff --git a/plugins/UserCountry/Columns/Provider.php b/plugins/UserCountry/Columns/Provider.php
index 98c5bf97ca..19ce528a2c 100644
--- a/plugins/UserCountry/Columns/Provider.php
+++ b/plugins/UserCountry/Columns/Provider.php
@@ -17,6 +17,8 @@ use Piwik\Tracker\Request;
class Provider extends Base
{
protected $columnName = 'location_provider';
+ protected $type = self::TYPE_TEXT;
+ protected $category = 'UserCountry_VisitLocation';
/**
* @param Request $request
diff --git a/plugins/UserCountry/Columns/Region.php b/plugins/UserCountry/Columns/Region.php
index 5b749673cf..1f14275e7b 100644
--- a/plugins/UserCountry/Columns/Region.php
+++ b/plugins/UserCountry/Columns/Region.php
@@ -8,9 +8,7 @@
*/
namespace Piwik\Plugins\UserCountry\Columns;
-use Piwik\Piwik;
use Piwik\Plugins\UserCountry\LocationProvider;
-use Piwik\Plugins\UserCountry\Segment;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
use Piwik\Tracker\Action;
@@ -19,20 +17,12 @@ class Region extends Base
{
protected $columnName = 'location_region';
protected $columnType = 'char(2) DEFAULT NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('regionCode');
- $segment->setName('UserCountry_Region');
- $segment->setAcceptedValues('01 02, OR, P8, etc.<br/>eg. region=A1;country=fr');
- $this->addSegment($segment);
- }
-
- public function getName()
- {
- return Piwik::translate('UserCountry_Region');
- }
+ protected $type = self::TYPE_TEXT;
+ protected $category = 'UserCountry_VisitLocation';
+ protected $segmentName = 'regionCode';
+ protected $nameSingular = 'UserCountry_Region';
+ protected $namePlural = 'UserCountryMap_Regions';
+ protected $acceptValues = '01 02, OR, P8, etc.<br/>eg. region=A1;country=fr';
/**
* @param Request $request
diff --git a/plugins/UserCountry/Segment.php b/plugins/UserCountry/Segment.php
deleted file mode 100644
index 2bae6f2f28..0000000000
--- a/plugins/UserCountry/Segment.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-namespace Piwik\Plugins\UserCountry;
-
-/**
- * UserCountry segment base class.
- *
- */
-class Segment extends \Piwik\Plugin\Segment
-{
- protected function init()
- {
- $this->setCategory('Visit Location');
- }
-}
diff --git a/plugins/UserCountry/lang/en.json b/plugins/UserCountry/lang/en.json
index 80eafa2349..4355539e83 100644
--- a/plugins/UserCountry/lang/en.json
+++ b/plugins/UserCountry/lang/en.json
@@ -11,11 +11,13 @@
"City": "City",
"CityAndCountry": "%1$s, %2$s",
"Continent": "Continent",
+ "Continents": "Continents",
"Country": "Country",
"country_a1": "Anonymous Proxy",
"country_a2": "Satellite Provider",
"country_cat": "Catalan-speaking communities",
"country_o1": "Other Country",
+ "VisitLocation": "Visit Location",
"CurrentLocationIntro": "According to this provider, your current location is",
"DefaultLocationProviderDesc1": "The default location provider guesses a visitor's country based on the language they use.",
"DefaultLocationProviderDesc2": "This is not very accurate, so %1$swe recommend installing and using %2$sGeoIP%3$s.%4$s",
@@ -67,11 +69,13 @@
"ISPDatabase": "ISP Database",
"IWantToDownloadFreeGeoIP": "I want to download the free GeoIP database...",
"Latitude": "Latitude",
+ "Latitudes": "Latitudes",
"Location": "Location",
"LocationDatabase": "Location Database",
"LocationDatabaseHint": "A location database is either a country, region or city database.",
"LocationProvider": "Location Provider",
"Longitude": "Longitude",
+ "Longitudes": "Longitudes",
"NoDataForGeoIPReport1": "There is no data for this report because there is either no location data available or visitor IP addresses cannot be geolocated.",
"NoDataForGeoIPReport2": "To enable accurate geolocation, change the settings %1$shere%2$s and use a %3$scity level database%4$s.",
"Organization": "Organization",
diff --git a/plugins/UserId/Columns/UserId.php b/plugins/UserId/Columns/UserId.php
index 7ac474cf73..e635f757d6 100644
--- a/plugins/UserId/Columns/UserId.php
+++ b/plugins/UserId/Columns/UserId.php
@@ -17,13 +17,6 @@ use Piwik\Plugin\Dimension\VisitDimension;
class UserId extends VisitDimension
{
- /**
- * The name of the dimension which will be visible for instance in the UI of a related report and in the mobile app.
- * @return string
- */
- public function getName()
- {
- return Piwik::translate('UserId_UserId');
- }
+ protected $nameSingular = 'UserId_UserId';
} \ No newline at end of file
diff --git a/plugins/UserLanguage/Columns/Language.php b/plugins/UserLanguage/Columns/Language.php
index fa665ed998..6d6ab2861b 100644
--- a/plugins/UserLanguage/Columns/Language.php
+++ b/plugins/UserLanguage/Columns/Language.php
@@ -9,7 +9,7 @@
namespace Piwik\Plugins\UserLanguage\Columns;
use Piwik\Common;
-use Piwik\Piwik;
+use Piwik\Metrics\Formatter;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
@@ -19,10 +19,16 @@ class Language extends VisitDimension
{
protected $columnName = 'location_browser_lang';
protected $columnType = 'VARCHAR(20) NULL';
+ protected $category = 'UserCountry_VisitLocation';
+ protected $nameSingular = 'General_Language';
+ protected $namePlural = 'General_Languages';
+ protected $segmentName = 'languageCode';
+ protected $acceptValues = 'de, fr, en-gb, zh-cn, etc.';
+ protected $type = self::TYPE_TEXT;
- public function getName()
+ public function formatValue($value, $idSite, Formatter $formatter)
{
- return Piwik::translate('General_Language');
+ return \Piwik\Plugins\UserLanguage\languageTranslateWithCode($value);
}
/**
@@ -52,13 +58,4 @@ class Language extends VisitDimension
return $languageCode;
}
- protected function configureSegments()
- {
- $segment = new \Piwik\Plugin\Segment();
- $segment->setCategory('Visit Location');
- $segment->setSegment('languageCode');
- $segment->setName('General_Language');
- $segment->setAcceptedValues('de, fr, en-gb, zh-cn, etc.');
- $this->addSegment($segment);
- }
}
diff --git a/plugins/VisitTime/Columns/DayOfTheWeek.php b/plugins/VisitTime/Columns/DayOfTheWeek.php
index 7728c07061..57c0e05856 100644
--- a/plugins/VisitTime/Columns/DayOfTheWeek.php
+++ b/plugins/VisitTime/Columns/DayOfTheWeek.php
@@ -13,8 +13,5 @@ use Piwik\Piwik;
class DayOfTheWeek extends Dimension
{
- public function getName()
- {
- return Piwik::translate('VisitTime_DayOfWeek');
- }
+ protected $nameSingular = 'VisitTime_DayOfWeek';
} \ No newline at end of file
diff --git a/plugins/VisitTime/Columns/LocalMinute.php b/plugins/VisitTime/Columns/LocalMinute.php
new file mode 100644
index 0000000000..e5a48b38c1
--- /dev/null
+++ b/plugins/VisitTime/Columns/LocalMinute.php
@@ -0,0 +1,43 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\VisitTime\Columns;
+
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\MetricsList;
+use Piwik\Metrics\Formatter;
+use Piwik\Plugin\Dimension\VisitDimension;
+
+require_once PIWIK_INCLUDE_PATH . '/plugins/VisitTime/functions.php';
+
+class LocalMinute extends VisitDimension
+{
+ protected $columnName = 'visitor_localtime';
+ protected $type = self::TYPE_NUMBER;
+ protected $segmentName = 'visitLocalMinute';
+ protected $nameSingular = 'VisitTime_ColumnLocalMinute';
+ protected $sqlSegment = 'MINUTE(log_visit.visitor_localtime)';
+ protected $acceptValues = '0, 1, 2, 3, ..., 67, 57, 58, 59';
+
+ public function __construct()
+ {
+ $this->suggestedValuesCallback = function ($idSite, $maxValuesToReturn) {
+ return range(0, min(59, $maxValuesToReturn));
+ };
+ }
+
+ public function configureMetrics(MetricsList $metricsList, DimensionMetricFactory $dimensionMetricFactory)
+ {
+ // no metrics for this dimension
+ }
+
+ public function formatValue($value, $idSite, Formatter $formatter)
+ {
+ return $value;
+ }
+} \ No newline at end of file
diff --git a/plugins/VisitTime/Columns/LocalTime.php b/plugins/VisitTime/Columns/LocalTime.php
index a37367cd6e..aef5f93471 100644
--- a/plugins/VisitTime/Columns/LocalTime.php
+++ b/plugins/VisitTime/Columns/LocalTime.php
@@ -8,31 +8,27 @@
*/
namespace Piwik\Plugins\VisitTime\Columns;
-use Piwik\Piwik;
+use Piwik\Metrics\Formatter;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugins\VisitTime\Segment;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
+require_once PIWIK_INCLUDE_PATH . '/plugins/VisitTime/functions.php';
+
class LocalTime extends VisitDimension
{
protected $columnName = 'visitor_localtime';
protected $columnType = 'TIME NULL';
+ protected $type = self::TYPE_TIME;
+ protected $segmentName = 'visitLocalHour';
+ protected $nameSingular = 'VisitTime_ColumnLocalHour';
+ protected $sqlSegment = 'HOUR(log_visit.visitor_localtime)';
+ protected $acceptValues = '0, 1, 2, 3, ..., 20, 21, 22, 23';
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('visitLocalHour');
- $segment->setName('VisitTime_ColumnLocalTime');
- $segment->setSqlSegment('HOUR(log_visit.visitor_localtime)');
- $segment->setAcceptedValues('0, 1, 2, 3, ..., 20, 21, 22, 23');
- $this->addSegment($segment);
- }
-
- public function getName()
+ public function formatValue($value, $idSite, Formatter $formatter)
{
- return Piwik::translate('VisitTime_ColumnLocalTime');
+ return \Piwik\Plugins\VisitTime\getTimeLabel($value);
}
/**
diff --git a/plugins/VisitTime/Columns/ServerTime.php b/plugins/VisitTime/Columns/ServerTime.php
deleted file mode 100644
index 5ee82706a5..0000000000
--- a/plugins/VisitTime/Columns/ServerTime.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-namespace Piwik\Plugins\VisitTime\Columns;
-
-use Piwik\Piwik;
-use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugins\VisitTime\Segment;
-
-class ServerTime extends VisitDimension
-{
- protected $columnName = 'visit_last_action_time';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('visitServerHour');
- $segment->setName('VisitTime_ColumnServerTime');
- $segment->setSqlSegment('HOUR(log_visit.visit_last_action_time)');
- $segment->setAcceptedValues('0, 1, 2, 3, ..., 20, 21, 22, 23');
- $this->addSegment($segment);
- }
-
- public function getName()
- {
- return Piwik::translate('VisitTime_ColumnServerTime');
- }
-} \ No newline at end of file
diff --git a/plugins/VisitTime/Reports/GetVisitInformationPerServerTime.php b/plugins/VisitTime/Reports/GetVisitInformationPerServerTime.php
index 871f760046..2aa2c89fc5 100644
--- a/plugins/VisitTime/Reports/GetVisitInformationPerServerTime.php
+++ b/plugins/VisitTime/Reports/GetVisitInformationPerServerTime.php
@@ -10,8 +10,8 @@ namespace Piwik\Plugins\VisitTime\Reports;
use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
+use Piwik\Plugins\CoreHome\Columns\VisitLastActionTime;
use Piwik\Plugins\CoreVisualizations\Visualizations\Graph;
-use Piwik\Plugins\VisitTime\Columns\ServerTime;
class GetVisitInformationPerServerTime extends Base
{
@@ -21,7 +21,7 @@ class GetVisitInformationPerServerTime extends Base
protected function init()
{
parent::init();
- $this->dimension = new ServerTime();
+ $this->dimension = new VisitLastActionTime();
$this->name = Piwik::translate('VisitTime_ServerTime');
$this->documentation = Piwik::translate('VisitTime_WidgetServerTimeDocumentation', array('<strong>', '</strong>'));
$this->constantRowsCount = true;
@@ -38,7 +38,7 @@ class GetVisitInformationPerServerTime extends Base
$view->requestConfig->filter_limit = 24;
$view->requestConfig->request_parameters_to_modify['hideFutureHoursWhenToday'] = 1;
- $view->config->addTranslation('label', $this->dimension->getName());
+ $view->config->addTranslation('label', Piwik::translate('VisitTime_ColumnServerHour'));
if ($view->isViewDataTableId(Graph::ID)) {
$view->config->max_graph_elements = false;
diff --git a/plugins/VisitTime/Segment.php b/plugins/VisitTime/Segment.php
deleted file mode 100644
index 30bc58f608..0000000000
--- a/plugins/VisitTime/Segment.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-namespace Piwik\Plugins\VisitTime;
-
-/**
- * VisitTime segment base class.
- *
- */
-class Segment extends \Piwik\Plugin\Segment
-{
- protected function init()
- {
- $this->setCategory('Visit');
- }
-}
diff --git a/plugins/VisitTime/lang/en.json b/plugins/VisitTime/lang/en.json
index 15fb00915c..e2cba5062a 100644
--- a/plugins/VisitTime/lang/en.json
+++ b/plugins/VisitTime/lang/en.json
@@ -1,7 +1,15 @@
{
"VisitTime": {
"ColumnLocalTime": "Local time",
+ "ColumnLocalHour": "Local time - hour (Start of visit)",
+ "ColumnLocalMinute": "Local time - minute (Start of visit)",
"ColumnServerTime": "Server time",
+ "ColumnServerHour": "Server time - hour",
+ "ColumnVisitEndServerHour": "Server time - hour (End of visit)",
+ "ColumnVisitEndServerMinute": "Server time - minute (End of visit)",
+ "ColumnVisitStartServerHour": "Server time - hour (Start of visit)",
+ "ColumnVisitStartServerMinute": "Server time - minute (Start of visit)",
+ "ColumnServerMinute": "Server time - minute",
"DayOfWeek": "Day of the week",
"LocalTime": "Visits per local time",
"NHour": "%sh",
diff --git a/plugins/VisitorInterest/Columns/PagesPerVisit.php b/plugins/VisitorInterest/Columns/PagesPerVisit.php
index 0d4bbe1a18..1f52ceedb9 100644
--- a/plugins/VisitorInterest/Columns/PagesPerVisit.php
+++ b/plugins/VisitorInterest/Columns/PagesPerVisit.php
@@ -13,8 +13,5 @@ use Piwik\Piwik;
class PagesPerVisit extends Dimension
{
- public function getName()
- {
- return Piwik::translate('VisitorInterest_ColumnPagesPerVisit');
- }
+ protected $nameSingular = 'VisitorInterest_ColumnPagesPerVisit';
} \ No newline at end of file
diff --git a/plugins/VisitorInterest/Columns/VisitDuration.php b/plugins/VisitorInterest/Columns/VisitDuration.php
index d0286bedd2..d3f88d4926 100644
--- a/plugins/VisitorInterest/Columns/VisitDuration.php
+++ b/plugins/VisitorInterest/Columns/VisitDuration.php
@@ -13,8 +13,7 @@ use Piwik\Piwik;
class VisitDuration extends Dimension
{
- public function getName()
- {
- return Piwik::translate('VisitorInterest_ColumnVisitDuration');
- }
+
+ protected $type = self::TYPE_DURATION_S;
+ protected $nameSingular = 'VisitorInterest_ColumnVisitDuration';
} \ No newline at end of file
diff --git a/plugins/VisitorInterest/Columns/VisitsByDaysSinceLastVisit.php b/plugins/VisitorInterest/Columns/VisitsByDaysSinceLastVisit.php
index b83cc9d72a..5415136975 100644
--- a/plugins/VisitorInterest/Columns/VisitsByDaysSinceLastVisit.php
+++ b/plugins/VisitorInterest/Columns/VisitsByDaysSinceLastVisit.php
@@ -10,7 +10,6 @@ namespace Piwik\Plugins\VisitorInterest\Columns;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\VisitDimension;
-use Piwik\Plugins\CoreHome\Segment;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
@@ -19,16 +18,9 @@ class VisitsByDaysSinceLastVisit extends VisitDimension
{
protected $columnName = 'visitor_days_since_last';
protected $columnType = 'SMALLINT(5) UNSIGNED NULL';
-
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('daysSinceLastVisit');
- $segment->setName('General_DaysSinceLastVisit');
- $segment->setType(Segment::TYPE_METRIC);
-
- $this->addSegment($segment);
- }
+ protected $type = self::TYPE_NUMBER;
+ protected $segmentName = 'daysSinceLastVisit';
+ protected $nameSingular = 'General_DaysSinceLastVisit';
public function getName()
{
diff --git a/plugins/VisitorInterest/Columns/VisitsbyVisitNumber.php b/plugins/VisitorInterest/Columns/VisitsbyVisitNumber.php
index 7993fb1c24..9a2640af1f 100644
--- a/plugins/VisitorInterest/Columns/VisitsbyVisitNumber.php
+++ b/plugins/VisitorInterest/Columns/VisitsbyVisitNumber.php
@@ -13,8 +13,5 @@ use Piwik\Piwik;
class VisitsbyVisitNumber extends Dimension
{
- public function getName()
- {
- return Piwik::translate('VisitorInterest_visitsByVisitCount');
- }
+ protected $nameSingular = 'VisitorInterest_visitsByVisitCount';
} \ No newline at end of file
diff --git a/tests/PHPUnit/Integration/Category/CategoryListTest.php b/tests/PHPUnit/Integration/Category/CategoryListTest.php
index 4c8cc4ec57..824339fb13 100644
--- a/tests/PHPUnit/Integration/Category/CategoryListTest.php
+++ b/tests/PHPUnit/Integration/Category/CategoryListTest.php
@@ -42,7 +42,10 @@ class CategoryListTest extends IntegrationTestCase
'Referrers_Referrers',
'Goals_Goals',
'Goals_Ecommerce',
+ 'Events_Events',
+ 'UserCountry_VisitLocation',
'Live!',
+ 'CustomVariables_CustomVariables',
'ExampleUI_UiFramework'
), array_keys($list->getCategories()));
}
diff --git a/tests/PHPUnit/Integration/Columns/ComputedMetricFactoryTest.php b/tests/PHPUnit/Integration/Columns/ComputedMetricFactoryTest.php
new file mode 100644
index 0000000000..8995c1e1b6
--- /dev/null
+++ b/tests/PHPUnit/Integration/Columns/ComputedMetricFactoryTest.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Columns;
+
+use Piwik\Columns\ComputedMetricFactory;
+use Piwik\Columns\MetricsList;
+use Piwik\Plugin\ComputedMetric;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Translate;
+
+/**
+ * @group Core
+ */
+class ComputedMetricFactoryTest extends IntegrationTestCase
+{
+ /**
+ * @var ComputedMetricFactory
+ */
+ private $factory;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ Translate::loadEnglishTranslation();
+
+ $this->factory = new ComputedMetricFactory(MetricsList::get());
+ }
+
+ public function tearDown()
+ {
+ Translate::unloadEnglishTranslation();
+ parent::tearDown();
+ }
+
+ public function test_createComputedMetric_createAvgMetric()
+ {
+ $metric = $this->factory->createComputedMetric('bounce_count', 'nb_visits', ComputedMetric::AGGREGATION_AVG);
+
+ $this->assertSame('avg_bounce_count_per_visits', $metric->getName());
+ $this->assertSame('Avg. Actions In Visit per Visit', $metric->getTranslatedName());
+ $this->assertSame('Average value of "Actions In Visit" per "Visits".', $metric->getDocumentation());
+ $this->assertSame('General_Visitors', $metric->getCategoryId());
+ }
+
+ public function test_createComputedMetric_createRateMetric()
+ {
+ $metric = $this->factory->createComputedMetric('bounce_count', 'nb_visits', ComputedMetric::AGGREGATION_RATE);
+
+ $this->assertSame('bounce_count_visits_rate', $metric->getName());
+ $this->assertSame('Bounces Rate', $metric->getTranslatedName());
+ $this->assertSame('The ratio of "Actions In Visit" out of all "Visits".', $metric->getDocumentation());
+ $this->assertSame('General_Visitors', $metric->getCategoryId());
+ }
+
+} \ No newline at end of file
diff --git a/tests/PHPUnit/Integration/Columns/DimensionMetricFactoryTest.php b/tests/PHPUnit/Integration/Columns/DimensionMetricFactoryTest.php
new file mode 100644
index 0000000000..4f03b9f8cc
--- /dev/null
+++ b/tests/PHPUnit/Integration/Columns/DimensionMetricFactoryTest.php
@@ -0,0 +1,159 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Columns;
+
+use Piwik\Columns\ComputedMetricFactory;
+use Piwik\Columns\Dimension;
+use Piwik\Columns\DimensionMetricFactory;
+use Piwik\Columns\MetricsList;
+use Piwik\Plugin\ArchivedMetric;
+use Piwik\Plugin\ComputedMetric;
+use Piwik\Plugins\UserCountry\Columns\Country;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Translate;
+
+/**
+ * @group Core
+ */
+class DimensionMetricFactoryTest extends IntegrationTestCase
+{
+ /** @var Dimension */
+ private $country;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ Translate::loadEnglishTranslation();
+
+ $this->country = new Country();
+ }
+
+ public function tearDown()
+ {
+ Translate::unloadEnglishTranslation();
+ parent::tearDown();
+ }
+
+ private function makeFactory($dimension)
+ {
+ return new DimensionMetricFactory($dimension);
+ }
+
+ public function test_createMetric_count()
+ {
+ $factory = $this->makeFactory($this->country);
+ $metric = $factory->createMetric(ArchivedMetric::AGGREGATION_COUNT);
+
+ $this->assertSame('nb_usercountry_country', $metric->getName());
+ $this->assertSame('Countries', $metric->getTranslatedName());
+ $this->assertSame('The number of Countries', $metric->getDocumentation());
+ $this->assertSame('UserCountry_VisitLocation', $metric->getCategoryId());
+ $this->assertSame('count(log_visit.location_country)', $metric->getQuery());
+ }
+
+ public function test_createMetric_uniqueCount()
+ {
+ $factory = $this->makeFactory($this->country);
+ $metric = $factory->createMetric(ArchivedMetric::AGGREGATION_UNIQUE);
+
+ $this->assertSame('nb_uniq_usercountry_country', $metric->getName());
+ $this->assertSame('Unique Countries', $metric->getTranslatedName());
+ $this->assertSame('The unique number of Countries', $metric->getDocumentation());
+ $this->assertSame('UserCountry_VisitLocation', $metric->getCategoryId());
+ $this->assertSame('count(distinct log_visit.location_country)', $metric->getQuery());
+ }
+
+ public function test_createMetric_sum()
+ {
+ $factory = $this->makeFactory($this->country);
+ $metric = $factory->createMetric(ArchivedMetric::AGGREGATION_SUM);
+
+ $this->assertSame('sum_usercountry_country', $metric->getName());
+ $this->assertSame('Total Countries', $metric->getTranslatedName());
+ $this->assertSame('The total number (sum) of Countries', $metric->getDocumentation());
+ $this->assertSame('UserCountry_VisitLocation', $metric->getCategoryId());
+ $this->assertSame('sum(log_visit.location_country)', $metric->getQuery());
+ }
+
+ public function test_createMetric_min()
+ {
+ $factory = $this->makeFactory($this->country);
+ $metric = $factory->createMetric(ArchivedMetric::AGGREGATION_MIN);
+
+ $this->assertSame('min_usercountry_country', $metric->getName());
+ $this->assertSame('Min Countries', $metric->getTranslatedName());
+ $this->assertSame('The minimum value for Countries', $metric->getDocumentation());
+ $this->assertSame('UserCountry_VisitLocation', $metric->getCategoryId());
+ $this->assertSame('min(log_visit.location_country)', $metric->getQuery());
+ }
+
+ public function test_createMetric_max()
+ {
+ $factory = $this->makeFactory($this->country);
+ $metric = $factory->createMetric(ArchivedMetric::AGGREGATION_MAX);
+
+ $this->assertSame('max_usercountry_country', $metric->getName());
+ $this->assertSame('Max Countries', $metric->getTranslatedName());
+ $this->assertSame('The maximum value for Countries', $metric->getDocumentation());
+ $this->assertSame('UserCountry_VisitLocation', $metric->getCategoryId());
+ $this->assertSame('max(log_visit.location_country)', $metric->getQuery());
+ }
+
+ public function test_createMetric_withValue()
+ {
+ $factory = $this->makeFactory($this->country);
+ $metric = $factory->createMetric(ArchivedMetric::AGGREGATION_COUNT_WITH_NUMERIC_VALUE);
+
+ $this->assertSame('nb_with_usercountry_country', $metric->getName());
+ $this->assertSame('Entries with Country', $metric->getTranslatedName());
+ $this->assertSame('The number of entries that have a value set for Country', $metric->getDocumentation());
+ $this->assertSame('UserCountry_VisitLocation', $metric->getCategoryId());
+ $this->assertSame('sum(if(log_visit.location_country > 0, 1, 0))', $metric->getQuery());
+ }
+
+
+ public function test_createCustomMetric()
+ {
+ $factory = $this->makeFactory($this->country);
+ $metric = $factory->createCustomMetric($name = 'sum_times_10', $translated = 'MyMetric', $aggregation = 'sum(%s) * 10', $documentation = 'FoobarBaz');
+
+ $this->assertSame($name, $metric->getName());
+ $this->assertSame($translated, $metric->getTranslatedName());
+ $this->assertSame($documentation, $metric->getDocumentation());
+ $this->assertSame('UserCountry_VisitLocation', $metric->getCategoryId());
+ $this->assertSame('sum(log_visit.location_country) * 10', $metric->getQuery());
+ }
+
+ public function test_createComputedMetric_average()
+ {
+ $factory = $this->makeFactory($this->country);
+ $metric = $factory->createComputedMetric($metricName1 = 'bounce_count', 'nb_visits', ComputedMetric::AGGREGATION_AVG);
+
+ $this->assertSame('avg_bounce_count_per_visits', $metric->getName());
+ $this->assertSame('Avg. Actions In Visit per Visit', $metric->getTranslatedName());
+ $this->assertSame('Average value of "Actions In Visit" per "Visits".', $metric->getDocumentation());
+ $this->assertSame('UserCountry_VisitLocation', $metric->getCategoryId());
+ $this->assertCount(2, $metric->getDependentMetrics());
+ }
+
+ public function test_createComputedMetric_rate()
+ {
+ $factory = $this->makeFactory($this->country);
+ $metric = $factory->createComputedMetric($metricName1 = 'bounce_count', 'nb_visits', ComputedMetric::AGGREGATION_RATE);
+
+ $this->assertSame('bounce_count_visits_rate', $metric->getName());
+ $this->assertSame('Bounces Rate', $metric->getTranslatedName());
+ $this->assertSame('The ratio of "Actions In Visit" out of all "Visits".', $metric->getDocumentation());
+ $this->assertSame('UserCountry_VisitLocation', $metric->getCategoryId());
+ $this->assertCount(2, $metric->getDependentMetrics());
+ }
+
+
+} \ No newline at end of file
diff --git a/tests/PHPUnit/Integration/Columns/DimensionTest.php b/tests/PHPUnit/Integration/Columns/DimensionTest.php
index 0e51177673..286ee4fb55 100644
--- a/tests/PHPUnit/Integration/Columns/DimensionTest.php
+++ b/tests/PHPUnit/Integration/Columns/DimensionTest.php
@@ -6,217 +6,310 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-namespace Piwik\Plugins\Test\Columns
-{
+namespace Piwik\Tests\Integration\Columns;
+
// there is a test that requires the class to be defined in a plugin
- use Piwik\Columns\Dimension;
- use Piwik\Plugin\Segment;
+use Piwik\Columns\Dimension;
+use Piwik\Plugin\Segment;
+use Piwik\Metrics\Formatter;
+use Piwik\Plugin\Dimension\ActionDimension;
+use Piwik\Plugin\Dimension\ConversionDimension;
+use Piwik\Plugin\Dimension\VisitDimension;
+use Piwik\Plugin\Manager;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Translate;
+
+class CustomDimensionTest extends Dimension
+{
+ protected $columnName = 'test_dimension';
+ protected $columnType = 'INTEGER (10) DEFAULT 0';
+ protected $dbTableName = 'log_visit';
- class DimensionTest extends Dimension
+ public function getId()
{
- protected $columnName = 'test_dimension';
- protected $columnType = 'INTEGER (10) DEFAULT 0';
+ return $this->generateIdFromClass('Piwik\Plugins\Test\Columns\DimensionTest');
+ }
- public function set($param, $value)
- {
- $this->$param = $value;
- }
+ public function hasImplementedEvent($method)
+ {
+ $method = new \ReflectionMethod($this, $method);
+ $declaringClass = $method->getDeclaringClass();
- protected function configureSegments()
- {
- $segment = new Segment();
- $segment->setSegment('exitPageUrl');
- $segment->setName('Actions_ColumnExitPageURL');
- $segment->setCategory('General_Visit');
- $this->addSegment($segment);
-
- // custom type and sqlSegment
- $segment = new Segment();
- $segment->setSegment('exitPageUrl');
- $segment->setSqlSegment('customValue');
- $segment->setType(Segment::TYPE_METRIC);
- $segment->setName('Actions_ColumnExitPageURL');
- $segment->setCategory('General_Visit');
- $this->addSegment($segment);
- }
+ return 0 === strpos($declaringClass->name, 'Piwik\Tests');
+ }
+
+ public function set($param, $value)
+ {
+ $this->$param = $value;
+ }
+
+ public function setColumnType($columnType)
+ {
+ $this->columnType = $columnType;
+ }
+
+ protected function configureSegments()
+ {
+ $segment = new Segment();
+ $segment->setSegment('exitPageUrl');
+ $segment->setName('Actions_ColumnExitPageURL');
+ $segment->setCategory('General_Visit');
+ $this->addSegment($segment);
+
+ // custom type and sqlSegment
+ $segment = new Segment();
+ $segment->setSegment('exitPageUrl');
+ $segment->setSqlSegment('customValue');
+ $segment->setType(Segment::TYPE_METRIC);
+ $segment->setName('Actions_ColumnExitPageURL');
+ $segment->setCategory('General_Visit');
+ $this->addSegment($segment);
}
}
-namespace Piwik\Tests\Integration\Columns
-{
- use Piwik\Columns\Dimension;
- use Piwik\Config;
- use Piwik\Plugin\Dimension\ActionDimension;
- use Piwik\Plugin\Dimension\ConversionDimension;
- use Piwik\Plugin\Dimension\VisitDimension;
- use Piwik\Plugin\Segment;
- use Piwik\Plugin\Manager;
- use Piwik\Plugins\Test\Columns\DimensionTest;
- use Piwik\Plugins\Test\FakeActionDimension;
- use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+/**
+ * @group Core
+ */
+class ColumnDimensionTest extends IntegrationTestCase
+{
/**
- * @group Core
+ * @var CustomDimensionTest
*/
- class ColumnDimensionTest extends IntegrationTestCase
+ private $dimension;
+
+ public function setUp()
{
- /**
- * @var FakeActionDimension
- */
- private $dimension;
+ parent::setUp();
- public function setUp()
- {
- parent::setUp();
+ Translate::loadEnglishTranslation();
- Manager::getInstance()->unloadPlugins();
- Manager::getInstance()->doNotLoadAlwaysActivatedPlugins();
+ Fixture::createWebsite('2014-04-05 01:02:03');
- $this->dimension = new DimensionTest();
- }
+ Manager::getInstance()->unloadPlugins();
+ Manager::getInstance()->doNotLoadAlwaysActivatedPlugins();
- public function test_hasImplementedEvent_shouldDetectWhetherAMethodWasOverwrittenInTheActualPluginClass()
- {
- $this->assertTrue($this->dimension->hasImplementedEvent('set'));
- $this->assertTrue($this->dimension->hasImplementedEvent('configureSegments'));
+ $this->dimension = new CustomDimensionTest();
+ }
- $this->assertFalse($this->dimension->hasImplementedEvent('getSegments'));
- }
+ public function tearDown()
+ {
+ Translate::unloadEnglishTranslation();
+ parent::tearDown();
+ }
- public function test_getColumnName_shouldReturnTheNameOfTheColumn()
- {
- $this->assertSame('test_dimension', $this->dimension->getColumnName());
- }
+ public function test_hasImplementedEvent_shouldDetectWhetherAMethodWasOverwrittenInTheActualPluginClass()
+ {
+ $this->assertTrue($this->dimension->hasImplementedEvent('set'));
+ $this->assertTrue($this->dimension->hasImplementedEvent('configureSegments'));
- public function test_hasColumnType_shouldDetectWhetherAColumnTypeIsSet()
- {
- $this->assertTrue($this->dimension->hasColumnType());
+ $this->assertFalse($this->dimension->hasImplementedEvent('getSegments'));
+ }
- $this->dimension->set('columnType', '');
- $this->assertFalse($this->dimension->hasColumnType());
- }
+ public function test_getColumnName_shouldReturnTheNameOfTheColumn()
+ {
+ $this->assertSame('test_dimension', $this->dimension->getColumnName());
+ }
- public function test_getName_ShouldNotReturnANameByDefault()
- {
- $this->assertSame('', $this->dimension->getName());
- }
+ public function test_hasColumnType_shouldDetectWhetherAColumnTypeIsSet()
+ {
+ $this->assertTrue($this->dimension->hasColumnType());
+
+ $this->dimension->set('columnType', '');
+ $this->assertFalse($this->dimension->hasColumnType());
+ }
+
+ public function test_getName_ShouldNotReturnANameByDefault()
+ {
+ $this->assertSame('', $this->dimension->getName());
+ }
- public function test_getAllDimensions_shouldReturnAllKindOfDimensions()
- {
- Manager::getInstance()->loadPlugins(array('Actions', 'Events', 'DevicesDetector', 'Goals', 'CustomVariables'));
-
- $dimensions = Dimension::getAllDimensions();
-
- $this->assertGreaterThan(20, count($dimensions));
-
- $foundConversion = false;
- $foundVisit = false;
- $foundAction = false;
- $foundNormal = false;
-
- foreach ($dimensions as $dimension) {
- if ($dimension instanceof ConversionDimension) {
- $foundConversion = true;
- } else if ($dimension instanceof ActionDimension) {
- $foundAction = true;
- } else if ($dimension instanceof VisitDimension) {
- $foundVisit = true;
- } else if ($dimension instanceof Dimension) {
- $foundNormal = true;
- } else {
- $this->fail('Unexpected dimension class found');
- }
-
- $this->assertRegExp('/Piwik.Plugins.(Actions|Events|DevicesDetector|Goals|CustomVariables).Columns/', get_class($dimension));
+ public function test_getAllDimensions_shouldReturnAllKindOfDimensions()
+ {
+ Manager::getInstance()->loadPlugins(array('Actions', 'Events', 'DevicesDetector', 'Goals', 'CustomVariables'));
+
+ $dimensions = Dimension::getAllDimensions();
+
+ $this->assertGreaterThan(20, count($dimensions));
+
+ $foundConversion = false;
+ $foundVisit = false;
+ $foundAction = false;
+ $foundNormal = false;
+
+ foreach ($dimensions as $dimension) {
+ if ($dimension instanceof ConversionDimension) {
+ $foundConversion = true;
+ } else if ($dimension instanceof ActionDimension) {
+ $foundAction = true;
+ } else if ($dimension instanceof VisitDimension) {
+ $foundVisit = true;
+ } else if ($dimension instanceof Dimension) {
+ $foundNormal = true;
+ } else {
+ $this->fail('Unexpected dimension class found');
}
- $this->assertTrue($foundConversion);
- $this->assertTrue($foundAction);
- $this->assertTrue($foundVisit);
- $this->assertTrue($foundNormal);
+ if (get_class($dimension) === 'Piwik\Plugins\CustomVariables\CustomDimension') {
+ continue;
+ }
+
+ $this->assertRegExp('/Piwik.Plugins.(Actions|Events|DevicesDetector|Goals|CustomVariables).Columns/', get_class($dimension));
}
- public function test_getDimensions_shouldReturnAllKindOfDimensionsThatBelongToASpecificPlugin()
- {
- Manager::getInstance()->loadPlugins(array('Actions', 'Events', 'DevicesDetector', 'Goals'));
+ $this->assertTrue($foundConversion);
+ $this->assertTrue($foundAction);
+ $this->assertTrue($foundVisit);
+ $this->assertTrue($foundNormal);
+ }
- $dimensions = Dimension::getDimensions(Manager::getInstance()->loadPlugin('Actions'));
+ public function test_getDimensions_shouldReturnAllKindOfDimensionsThatBelongToASpecificPlugin()
+ {
+ Manager::getInstance()->loadPlugins(array('Actions', 'Events', 'DevicesDetector', 'Goals'));
- $this->assertGreaterThan(10, count($dimensions));
+ $dimensions = Dimension::getDimensions(Manager::getInstance()->loadPlugin('Actions'));
- $foundVisit = false;
- $foundAction = false;
+ $this->assertGreaterThan(10, count($dimensions));
- foreach ($dimensions as $dimension) {
- if ($dimension instanceof ActionDimension) {
- $foundAction = true;
- } else if ($dimension instanceof VisitDimension) {
- $foundVisit = true;
- }
+ $foundVisit = false;
+ $foundAction = false;
- $this->assertRegExp('/Piwik.Plugins.Actions.Columns/', get_class($dimension));
+ foreach ($dimensions as $dimension) {
+ if ($dimension instanceof ActionDimension) {
+ $foundAction = true;
+ } else if ($dimension instanceof VisitDimension) {
+ $foundVisit = true;
}
- $this->assertTrue($foundAction);
- $this->assertTrue($foundVisit);
+ $this->assertRegExp('/Piwik.Plugins.Actions.Columns/', get_class($dimension));
}
- public function test_getDimensions_shouldReturnConversionDimensionsThatBelongToASpecificPlugin()
- {
- Manager::getInstance()->loadPlugins(array('Actions', 'Events', 'DevicesDetector', 'Goals'));
+ $this->assertTrue($foundAction);
+ $this->assertTrue($foundVisit);
+ }
- $dimensions = Dimension::getDimensions(Manager::getInstance()->loadPlugin('Goals'));
+ public function test_getDimensions_shouldReturnConversionDimensionsThatBelongToASpecificPlugin()
+ {
+ Manager::getInstance()->loadPlugins(array('Actions', 'Events', 'DevicesDetector', 'Goals'));
- $this->assertGreaterThan(2, count($dimensions));
+ $dimensions = Dimension::getDimensions(Manager::getInstance()->loadPlugin('Goals'));
- $foundConversion = false;
+ $this->assertGreaterThan(2, count($dimensions));
- foreach ($dimensions as $dimension) {
- if ($dimension instanceof ConversionDimension) {
- $foundConversion = true;
- }
+ $foundConversion = false;
- $this->assertRegExp('/Piwik.Plugins.Goals.Columns/', get_class($dimension));
+ foreach ($dimensions as $dimension) {
+ if ($dimension instanceof ConversionDimension) {
+ $foundConversion = true;
}
- $this->assertTrue($foundConversion);
+ $this->assertRegExp('/Piwik.Plugins.Goals.Columns/', get_class($dimension));
}
- public function test_getSegment_ShouldReturnConfiguredSegments()
- {
- $segments = $this->dimension->getSegments();
+ $this->assertTrue($foundConversion);
+ }
- $this->assertCount(2, $segments);
- $this->assertInstanceOf('\Piwik\Plugin\Segment', $segments[0]);
- $this->assertInstanceOf('\Piwik\Plugin\Segment', $segments[1]);
- }
+ public function test_getSegment_ShouldReturnConfiguredSegments()
+ {
+ $segments = $this->dimension->getSegments();
- public function test_addSegment_ShouldPrefilSomeSegmentValuesIfNotDefinedYet()
- {
- $segments = $this->dimension->getSegments();
+ $this->assertCount(2, $segments);
+ $this->assertInstanceOf('\Piwik\Plugin\Segment', $segments[0]);
+ $this->assertInstanceOf('\Piwik\Plugin\Segment', $segments[1]);
+ }
- $this->assertEquals(Segment::TYPE_DIMENSION, $segments[0]->getType());
- }
+ /**
+ * @param $expectedType
+ * @param $columnType
+ * @dataProvider getTypeProvider
+ */
+ public function test_getType_shouldGuessTypeBasedOnColumnType($expectedType, $columnType)
+ {
+ $this->dimension->setColumnType($columnType);
+ $this->assertSame($expectedType, $this->dimension->getType());
+ }
- public function test_addSegment_ShouldNotOverwritePreAssignedValues()
- {
- $segments = $this->dimension->getSegments();
+ public function getTypeProvider()
+ {
+ return array(
+ array($expected = Dimension::TYPE_NUMBER, $columnType = 'INTEGER (10) DEFAULT 0'),
+ array($expected = Dimension::TYPE_NUMBER, $columnType = 'INTEGER(10) DEFAULT 0'),
+ array($expected = Dimension::TYPE_NUMBER, $columnType = 'INT(10) DEFAULT 0'),
+ array($expected = Dimension::TYPE_NUMBER, $columnType = 'int(10) DEFAULT 0'),
+ array($expected = Dimension::TYPE_NUMBER, $columnType = 'SMALLINT(10) DEFAULT 0'),
+ array($expected = Dimension::TYPE_FLOAT, $columnType = 'FLOAT (10) DEFAULT 0'),
+ array($expected = Dimension::TYPE_FLOAT, $columnType = 'DECIMAL(10) DEFAULT 0'),
+ array($expected = Dimension::TYPE_BINARY, $columnType = 'BINARY(8)'),
+ array($expected = Dimension::TYPE_TIMESTAMP, $columnType = 'timestamp null'),
+ array($expected = Dimension::TYPE_TIMESTAMP, $columnType = 'timeStAmp null'),
+ array($expected = Dimension::TYPE_DATETIME, $columnType = 'DATETIME NOT NULL'),
+ array($expected = Dimension::TYPE_DATE, $columnType = 'DATE NOT NULL'),
+ array($expected = Dimension::TYPE_TEXT, $columnType = ''),
+ );
+ }
- $this->assertEquals(Segment::TYPE_METRIC, $segments[1]->getType());
- }
+ public function test_addSegment_ShouldPrefilSomeSegmentValuesIfNotDefinedYetAndGuessTypeMetric()
+ {
+ $segments = $this->dimension->getSegments();
- public function test_getId_ShouldCorrectlyGenerateIdFromDimensionsQualifiedClassName()
- {
- $this->assertEquals("Test.DimensionTest", $this->dimension->getId());
- }
+ $this->assertEquals(Segment::TYPE_METRIC, $segments[0]->getType());
+ }
- public function test_factory_ShouldCreateDimensionFromDimensionId()
- {
- Manager::getInstance()->loadPlugins(array('ExampleTracker'));
+ public function test_addSegment_ShouldPrefilSomeSegmentValuesIfNotDefinedYetAndGuessTypeDimension()
+ {
+ $this->dimension->setColumnType('TEXT NOT NULL');
+ $segments = $this->dimension->getSegments();
- $dimension = Dimension::factory("ExampleTracker.ExampleDimension");
- $this->assertInstanceOf('Piwik\Plugins\ExampleTracker\Columns\ExampleDimension', $dimension);
- }
- }
-} \ No newline at end of file
+ $this->assertEquals(Segment::TYPE_DIMENSION, $segments[0]->getType());
+ }
+
+ public function test_addSegment_ShouldNotOverwritePreAssignedValues()
+ {
+ $segments = $this->dimension->getSegments();
+
+ $this->assertEquals(Segment::TYPE_METRIC, $segments[1]->getType());
+ }
+
+ public function test_getId_ShouldCorrectlyGenerateIdFromDimensionsQualifiedClassName()
+ {
+ $this->assertEquals("Test.DimensionTest", $this->dimension->getId());
+ }
+
+ public function test_factory_ShouldCreateDimensionFromDimensionId()
+ {
+ Manager::getInstance()->loadPlugins(array('ExampleTracker'));
+
+ $dimension = Dimension::factory("ExampleTracker.ExampleDimension");
+ $this->assertInstanceOf('Piwik\Plugins\ExampleTracker\Columns\ExampleDimension', $dimension);
+ }
+
+
+ /**
+ * @dataProvider getFormatValueProvider
+ */
+ public function test_formatValue($type, $value, $expected)
+ {
+ $formatter = new Formatter();
+ $this->dimension->setType($type);
+ $formatted = $this->dimension->formatValue($value, $idSite = 1, $formatter);
+
+ $this->assertEquals($expected, $formatted);
+ }
+
+ public function getFormatValueProvider()
+ {
+ return array(
+ array($type = Dimension::TYPE_NUMBER, $value = 5.354, $expected = 5),
+ array($type = Dimension::TYPE_FLOAT, $value = 5.354, $expected = 5.35),
+ array($type = Dimension::TYPE_MONEY, $value = 5.392, $expected = '$ 5.39'),
+ array($type = Dimension::TYPE_PERCENT, $value = 0.343, $expected = '34.3%'),
+ array($type = Dimension::TYPE_DURATION_S, $value = 121, $expected = '00:02:01'),
+ array($type = Dimension::TYPE_DURATION_MS, $value = 0.392, $expected = '0.39s'),
+ array($type = Dimension::TYPE_BYTE, $value = 3912, $expected = '3.8 K'),
+ array($type = Dimension::TYPE_BOOL, $value = 0, $expected = 'No'),
+ array($type = Dimension::TYPE_BOOL, $value = 1, $expected = 'Yes'),
+ );
+ }
+}
diff --git a/tests/PHPUnit/Integration/Plugin/ArchivedMetricTest.php b/tests/PHPUnit/Integration/Plugin/ArchivedMetricTest.php
new file mode 100644
index 0000000000..1a9a19526a
--- /dev/null
+++ b/tests/PHPUnit/Integration/Plugin/ArchivedMetricTest.php
@@ -0,0 +1,131 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Plugin;
+
+use Piwik\Columns\Dimension;
+use Piwik\DataTable;
+use Piwik\Metrics\Formatter;
+use Piwik\Plugin\ArchivedMetric;
+use Piwik\Plugin\ComputedMetric;
+use Piwik\Plugins\UserCountry\Columns\City;
+use Piwik\Plugins\UserCountry\Columns\Country;
+use Piwik\Site;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Translate;
+
+/**
+ * @group ArchivedMetric
+ * @group ArchivedMetricTest
+ */
+class ArchivedMetricTest extends IntegrationTestCase
+{
+ /**
+ * @var Country
+ */
+ private $dimension;
+
+ /**
+ * @var ArchivedMetric
+ */
+ private $metric;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ Translate::loadEnglishTranslation();
+
+ Fixture::createWebsite('2015-01-01 00:00:00');
+
+ $this->dimension = new Country();
+ $this->metric = $this->makeMetric('%s');
+ }
+
+ public function tearDown()
+ {
+ Translate::unloadEnglishTranslation();
+ parent::tearDown();
+ }
+
+ private function makeMetric($aggregation)
+ {
+ return new ArchivedMetric($this->dimension, $aggregation);
+ }
+
+ /**
+ * @dataProvider getFormatValueProvider
+ */
+ public function test_formatValue($type, $value, $expected)
+ {
+ $formatter = new Formatter();
+ $this->metric->setType($type);
+
+ $table = new DataTable();
+ $table->setMetadata('site', new Site(1));
+ $this->metric->beforeFormat(null, $table);
+ $formatted = $this->metric->format($value, $formatter);
+
+ $this->assertEquals($expected, $formatted);
+ }
+
+ public function getFormatValueProvider()
+ {
+ return array(
+ array($type = Dimension::TYPE_NUMBER, $value = 5.354, $expected = 5),
+ array($type = Dimension::TYPE_FLOAT, $value = 5.354, $expected = 5.35),
+ array($type = Dimension::TYPE_MONEY, $value = 5.392, $expected = '$ 5.39'),
+ array($type = Dimension::TYPE_PERCENT, $value = 0.343, $expected = '34.3%'),
+ array($type = Dimension::TYPE_DURATION_S, $value = 121, $expected = '2 min 1s'),
+ array($type = Dimension::TYPE_DURATION_MS, $value = 392, $expected = '0.39s'),
+ array($type = Dimension::TYPE_BYTE, $value = 3912, $expected = '3.8 K'),
+ );
+ }
+ public function test_getQuery_returnsDefaultColumns()
+ {
+ $this->assertSame('log_visit.location_country', $this->metric->getQuery());
+ }
+
+ public function test_getQuery_whenNoAggregationSet()
+ {
+ $metric = $this->makeMetric('');
+ $this->assertSame('log_visit.location_country', $metric->getQuery());
+ }
+
+ public function test_getQuery_whenAggregationSet()
+ {
+ $metric = $this->makeMetric('count(%s)');
+ $this->assertSame('count(log_visit.location_country)', $metric->getQuery());
+ }
+
+ public function test_setQuery()
+ {
+ $this->metric->setQuery('count(log_visit.foobar) + 1');
+ $this->assertSame('count(log_visit.foobar) + 1', $this->metric->getQuery());
+ }
+
+ public function test_setDimension()
+ {
+ $this->assertSame($this->dimension, $this->metric->getDimension());
+ }
+
+ public function test_getDbTableName()
+ {
+ $this->assertSame('log_visit', $this->metric->getDbTableName());
+ }
+
+ public function test_setCategory_getCategoryId()
+ {
+ $this->assertSame('', $this->metric->getCategoryId());
+ $this->metric->setCategory('General_Visitors');
+ $this->assertSame('General_Visitors', $this->metric->getCategoryId());
+ }
+
+
+}
diff --git a/tests/PHPUnit/Integration/Plugin/ComputedMetricTest.php b/tests/PHPUnit/Integration/Plugin/ComputedMetricTest.php
new file mode 100644
index 0000000000..7149271535
--- /dev/null
+++ b/tests/PHPUnit/Integration/Plugin/ComputedMetricTest.php
@@ -0,0 +1,136 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Tests\Integration\Plugin;
+
+use Piwik\Columns\Dimension;
+use Piwik\DataTable;
+use Piwik\Metrics\Formatter;
+use Piwik\Plugin\ComputedMetric;
+use Piwik\Site;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Translate;
+
+/**
+ * @group ComputedMetric
+ * @group ComputedMetricTest
+ */
+class ComputedMetricTest extends IntegrationTestCase
+{
+ public function setUp()
+ {
+ parent::setUp();
+
+ Translate::loadEnglishTranslation();
+
+ Fixture::createWebsite('2015-01-01 00:00:00');
+ }
+
+ public function tearDown()
+ {
+ Translate::unloadEnglishTranslation();
+ parent::tearDown();
+ }
+
+ private function makeMetric($metric1, $metric2, $aggregation)
+ {
+ return new ComputedMetric($metric1, $metric2, $aggregation);
+ }
+
+ /**
+ * @dataProvider getFormatValueProvider
+ */
+ public function test_formatValue($type, $value, $expected)
+ {
+ $metric = $this->makeMetric('bonuce_count', 'nb_visits', ComputedMetric::AGGREGATION_AVG);
+
+ $formatter = new Formatter();
+ $metric->setType($type);
+
+ $table = new DataTable();
+ $table->setMetadata('site', new Site(1));
+ $metric->beforeFormat(null, $table);
+ $formatted = $metric->format($value, $formatter);
+
+ $this->assertEquals($expected, $formatted);
+ }
+
+ public function getFormatValueProvider()
+ {
+ return array(
+ array($type = Dimension::TYPE_NUMBER, $value = 5.354, $expected = 5.4),
+ array($type = Dimension::TYPE_FLOAT, $value = 5.354, $expected = 5.35),
+ array($type = Dimension::TYPE_MONEY, $value = 5.392, $expected = '$ 5.39'),
+ array($type = Dimension::TYPE_PERCENT, $value = 0.343, $expected = '34.3%'),
+ array($type = Dimension::TYPE_DURATION_S, $value = 121, $expected = '2 min 1s'),
+ array($type = Dimension::TYPE_DURATION_MS, $value = 392, $expected = '0.39s'),
+ array($type = Dimension::TYPE_BYTE, $value = 392, $expected = '392 B'),
+ );
+ }
+
+ public function test_getName()
+ {
+ $metric = $this->makeMetric('bounces', 'nb_visits', ComputedMetric::AGGREGATION_AVG);
+ $this->assertSame('avg_bounces_per_visits', $metric->getName());
+
+ $metric = $this->makeMetric('bounces', 'nb_uniq_visits', ComputedMetric::AGGREGATION_AVG);
+ $this->assertSame('avg_bounces_per_uniq_visits', $metric->getName());
+
+ $metric = $this->makeMetric('sum_bounces', 'nb_uniq_visits', ComputedMetric::AGGREGATION_AVG);
+ $this->assertSame('avg_sum_bounces_per_uniq_visits', $metric->getName());
+
+ $metric = $this->makeMetric('bounces', 'nb_visits', ComputedMetric::AGGREGATION_RATE);
+ $this->assertSame('bounces_visits_rate', $metric->getName());
+
+ $metric = $this->makeMetric('bounces', 'nb_uniq_visits', ComputedMetric::AGGREGATION_RATE);
+ $this->assertSame('bounces_uniq_visits_rate', $metric->getName());
+
+ $metric = $this->makeMetric('sum_bounces', 'nb_uniq_visits', ComputedMetric::AGGREGATION_RATE);
+ $this->assertSame('sum_bounces_uniq_visits_rate', $metric->getName());
+ }
+
+ public function test_setName()
+ {
+ $metric = $this->makeMetric('bounces', 'nb_visits', ComputedMetric::AGGREGATION_AVG);
+ $metric->setName('avg_bounces');
+ $this->assertSame('avg_bounces', $metric->getName());
+ }
+
+ public function test_getTranslatedName()
+ {
+ $metric = $this->makeMetric('bounce_count', 'nb_visits', ComputedMetric::AGGREGATION_AVG);
+ $this->assertSame('Avg. Actions In Visit per Visit', $metric->getTranslatedName());
+
+ $metric = $this->makeMetric('bounce_count', 'nb_visits', ComputedMetric::AGGREGATION_RATE);
+ $this->assertSame('Bounces Rate', $metric->getTranslatedName());
+ }
+
+ public function test_getDocumentation()
+ {
+ $metric = $this->makeMetric('bounce_count', 'nb_visits', ComputedMetric::AGGREGATION_AVG);
+ $this->assertSame('Average value of "Actions In Visit" per "Visits".', $metric->getDocumentation());
+
+ $metric = $this->makeMetric('bounce_count', 'nb_visits', ComputedMetric::AGGREGATION_RATE);
+ $this->assertSame('The ratio of "Actions In Visit" out of all "Visits".', $metric->getDocumentation());
+ }
+
+ public function test_getDependentMetrics()
+ {
+ $metric = $this->makeMetric('bounce_count', 'nb_visits', ComputedMetric::AGGREGATION_AVG);
+ $this->assertSame(array('bounce_count', 'nb_visits'), $metric->getDependentMetrics());
+ }
+
+ public function test_setCategory()
+ {
+ $metric = $this->makeMetric('bounce_count', 'nb_visits', ComputedMetric::AGGREGATION_AVG);
+ $metric->setCategory('123');
+ $this->assertSame('123', $metric->getCategoryId());
+ }
+
+}
diff --git a/tests/PHPUnit/Integration/Plugin/Dimension/ActionDimensionTest.php b/tests/PHPUnit/Integration/Plugin/Dimension/ActionDimensionTest.php
index 1666b193db..006399bffd 100644
--- a/tests/PHPUnit/Integration/Plugin/Dimension/ActionDimensionTest.php
+++ b/tests/PHPUnit/Integration/Plugin/Dimension/ActionDimensionTest.php
@@ -17,7 +17,7 @@ use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
class FakeActionDimension extends ActionDimension
{
protected $columnName = 'fake_action_dimension_column';
- protected $columnType = 'INTEGER (10) DEFAULT 0';
+ protected $columnType = 'VARCHAR (255) DEFAULT 0';
public function set($param, $value)
{
@@ -79,7 +79,7 @@ class ActionDimensionTest extends IntegrationTestCase
{
$expected = array(
'log_link_visit_action' => array(
- "ADD COLUMN `fake_action_dimension_column` INTEGER (10) DEFAULT 0"
+ "ADD COLUMN `fake_action_dimension_column` VARCHAR (255) DEFAULT 0"
)
);
@@ -90,7 +90,7 @@ class ActionDimensionTest extends IntegrationTestCase
{
$expected = array(
'log_link_visit_action' => array(
- "MODIFY COLUMN `fake_action_dimension_column` INTEGER (10) DEFAULT 0"
+ "MODIFY COLUMN `fake_action_dimension_column` VARCHAR (255) DEFAULT 0"
)
);
@@ -99,7 +99,7 @@ class ActionDimensionTest extends IntegrationTestCase
public function test_getVersion_shouldUseColumnTypeAsVersion()
{
- $this->assertEquals('INTEGER (10) DEFAULT 0', $this->dimension->getVersion());
+ $this->assertEquals('VARCHAR (255) DEFAULT 0', $this->dimension->getVersion());
}
public function test_getSegment_ShouldReturnConfiguredSegments()
diff --git a/tests/PHPUnit/Integration/Plugin/Dimension/ConversionDimensionTest.php b/tests/PHPUnit/Integration/Plugin/Dimension/ConversionDimensionTest.php
index 7e6ac7191c..35cd30615d 100644
--- a/tests/PHPUnit/Integration/Plugin/Dimension/ConversionDimensionTest.php
+++ b/tests/PHPUnit/Integration/Plugin/Dimension/ConversionDimensionTest.php
@@ -17,7 +17,7 @@ use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
class FakeConversionDimension extends ConversionDimension
{
protected $columnName = 'fake_conversion_dimension_column';
- protected $columnType = 'INTEGER (10) DEFAULT 0';
+ protected $columnType = 'VARCHAR (255) DEFAULT 0';
public function set($param, $value)
{
@@ -79,7 +79,7 @@ class ConversionDimensionTest extends IntegrationTestCase
{
$expected = array(
'log_conversion' => array(
- "ADD COLUMN `fake_conversion_dimension_column` INTEGER (10) DEFAULT 0"
+ "ADD COLUMN `fake_conversion_dimension_column` VARCHAR (255) DEFAULT 0"
)
);
@@ -90,7 +90,7 @@ class ConversionDimensionTest extends IntegrationTestCase
{
$expected = array(
'log_conversion' => array(
- "MODIFY COLUMN `fake_conversion_dimension_column` INTEGER (10) DEFAULT 0"
+ "MODIFY COLUMN `fake_conversion_dimension_column` VARCHAR (255) DEFAULT 0"
)
);
@@ -99,7 +99,7 @@ class ConversionDimensionTest extends IntegrationTestCase
public function test_getVersion_shouldUseColumnTypeAsVersion()
{
- $this->assertEquals('INTEGER (10) DEFAULT 0', $this->dimension->getVersion());
+ $this->assertEquals('VARCHAR (255) DEFAULT 0', $this->dimension->getVersion());
}
public function test_getSegment_ShouldReturnConfiguredSegments()
diff --git a/tests/PHPUnit/Integration/Plugin/Dimension/VisitDimensionTest.php b/tests/PHPUnit/Integration/Plugin/Dimension/VisitDimensionTest.php
index 505880b8a8..5642feefef 100644
--- a/tests/PHPUnit/Integration/Plugin/Dimension/VisitDimensionTest.php
+++ b/tests/PHPUnit/Integration/Plugin/Dimension/VisitDimensionTest.php
@@ -19,7 +19,7 @@ use Piwik\Tracker\Visitor;
class FakeVisitDimension extends VisitDimension
{
protected $columnName = 'fake_visit_dimension_column';
- protected $columnType = 'INTEGER (10) DEFAULT 0';
+ protected $columnType = 'VARCHAR (255) DEFAULT 0';
public $requiredFields = array();
public function set($param, $value)
@@ -101,7 +101,7 @@ class VisitDimensionTest extends IntegrationTestCase
{
$expected = array(
'log_visit' => array(
- "ADD COLUMN `fake_visit_dimension_column` INTEGER (10) DEFAULT 0"
+ "ADD COLUMN `fake_visit_dimension_column` VARCHAR (255) DEFAULT 0"
)
);
@@ -112,10 +112,10 @@ class VisitDimensionTest extends IntegrationTestCase
{
$expected = array(
'log_visit' => array(
- "ADD COLUMN `fake_visit_dimension_column` INTEGER (10) DEFAULT 0"
+ "ADD COLUMN `fake_visit_dimension_column` VARCHAR (255) DEFAULT 0"
),
'log_conversion' => array(
- "ADD COLUMN `fake_visit_dimension_column` INTEGER (10) DEFAULT 0"
+ "ADD COLUMN `fake_visit_dimension_column` VARCHAR (255) DEFAULT 0"
)
);
@@ -126,49 +126,35 @@ class VisitDimensionTest extends IntegrationTestCase
{
$expected = array(
'log_visit' => array(
- "MODIFY COLUMN `fake_visit_dimension_column` INTEGER (10) DEFAULT 0"
+ "MODIFY COLUMN `fake_visit_dimension_column` VARCHAR (255) DEFAULT 0"
)
);
- $this->assertEquals($expected, $this->dimension->update(array()));
+ $this->assertEquals($expected, $this->dimension->update());
}
public function test_update_shouldUpdateLogVisitAndAddConversion_IfConversionMethodIsImplementedButNotInstalledYet()
{
$expected = array(
'log_visit' => array(
- "MODIFY COLUMN `fake_visit_dimension_column` INTEGER (10) DEFAULT 0"
+ "MODIFY COLUMN `fake_visit_dimension_column` VARCHAR (255) DEFAULT 0"
),
'log_conversion' => array(
- "ADD COLUMN `fake_visit_dimension_column` INTEGER (10) DEFAULT 0"
+ "ADD COLUMN `fake_visit_dimension_column` VARCHAR (255) DEFAULT 0"
)
);
- $this->assertEquals($expected, $this->conversionDimension->update(array()));
- }
-
- public function test_update_shouldUpdateLogVisitAndConversion_IfConversionMethodIsImplementedAndInstalled()
- {
- $expected = array(
- 'log_visit' => array(
- "MODIFY COLUMN `fake_visit_dimension_column` INTEGER (10) DEFAULT 0"
- ),
- 'log_conversion' => array(
- "MODIFY COLUMN `fake_visit_dimension_column` INTEGER (10) DEFAULT 0"
- )
- );
-
- $this->assertEquals($expected, $this->conversionDimension->update(array('fake_visit_dimension_column' => array())));
+ $this->assertEquals($expected, $this->conversionDimension->update());
}
public function test_getVersion_shouldUseColumnTypeAsVersion()
{
- $this->assertEquals('INTEGER (10) DEFAULT 0', $this->dimension->getVersion());
+ $this->assertEquals('VARCHAR (255) DEFAULT 0', $this->dimension->getVersion());
}
public function test_getVersion_shouldIncludeConversionMethodIntoVersionNumber_ToMakeSureUpdateMethodWillBeTriggeredWhenPluginAddedConversionMethodInNewVersion()
{
- $this->assertEquals('INTEGER (10) DEFAULT 01', $this->conversionDimension->getVersion());
+ $this->assertEquals('VARCHAR (255) DEFAULT 01', $this->conversionDimension->getVersion());
}
public function test_getSegment_ShouldReturnNoSegments_IfNoneConfigured()
diff --git a/tests/PHPUnit/Integration/SegmentTest.php b/tests/PHPUnit/Integration/SegmentTest.php
index 20b23065db..0558d6a381 100644
--- a/tests/PHPUnit/Integration/SegmentTest.php
+++ b/tests/PHPUnit/Integration/SegmentTest.php
@@ -512,7 +512,7 @@ class SegmentTest extends IntegrationTestCase
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
- public function test_getSelectQuery_whenJoinLogLinkVisitActionOnActionOnVisit_WithSameTableAliasButDifferentJoin()
+ public function test_getSelectQuery_whenJoinLogLinkVisitActionOnActionOnVisit_WithNoTableAliasButDifferentJoin()
{
$actionType = 3;
$idSite = 1;
@@ -549,7 +549,7 @@ class SegmentTest extends IntegrationTestCase
LEFT JOIN $logVisitTable AS log_visit
ON log_visit.idvisit = log_link_visit_action.idvisit
LEFT JOIN $logActionTable AS log_action
- ON (log_link_visit_action.idaction_name = log_action.idaction AND log_link_visit_action.idaction_url = log_action.idaction)
+ ON log_link_visit_action.idaction_name = log_action.idaction
WHERE ( log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ? )
@@ -929,6 +929,99 @@ class SegmentTest extends IntegrationTestCase
$this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
}
+ public function test_getSelectQuery_whenLimit_withCustomJoinsAndSameColumns()
+ {
+ $select = "log_action_visit_entry_idaction_name.name AS 'EntryPageTitle', log_action_idaction_event_action.name AS 'EventAction', count(distinct log_visit.idvisit) AS 'nb_uniq_visits', count(distinct log_visit.idvisitor) AS 'nb_uniq_visitors', sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) AS 'bounce_count', sum(log_visit.visit_total_actions) AS 'sum_actions', sum(log_visit.visit_goal_converted) AS 'sum_visit_goal_converted'";
+ $from = array('log_visit', array('table' => 'log_action', 'tableAlias' => 'log_action_visit_entry_idaction_name', 'joinOn' => 'log_visit.visit_entry_idaction_name = log_action_visit_entry_idaction_name.idaction'), 'log_link_visit_action', array('table' => 'log_action', 'tableAlias' => 'log_action_idaction_event_action', 'joinOn' => 'log_link_visit_action.idaction_event_action = log_action_idaction_event_action.idaction'));
+ $where = '';
+ $bind = array(1);
+
+ $segment = '';
+ $segment = new Segment($segment, $idSites = array());
+
+ $orderBy = 'nb_uniq_visits, log_action_idaction_event_action.name';
+ $groupBy = 'log_action_visit_entry_idaction_name.name, log_action_idaction_event_action.name';
+ $limit = 33;
+
+ $query = $segment->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy, $limit);
+
+ $expected = array(
+ "sql" => "
+ SELECT log_inner.name AS 'EntryPageTitle', log_inner.name02fd90a35677a359ea5611a4bc456a6f AS 'EventAction', count(distinct log_inner.idvisit) AS 'nb_uniq_visits', count(distinct log_inner.idvisitor) AS 'nb_uniq_visitors', sum(case log_inner.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) AS 'bounce_count', sum(log_inner.visit_total_actions) AS 'sum_actions', sum(log_inner.visit_goal_converted) AS 'sum_visit_goal_converted'
+ FROM (
+ SELECT log_action_visit_entry_idaction_name.name, log_action_idaction_event_action.name as name02fd90a35677a359ea5611a4bc456a6f, log_visit.idvisit, log_visit.idvisitor, log_visit.visit_total_actions, log_visit.visit_goal_converted
+ FROM log_visit AS log_visit
+ LEFT JOIN log_action AS log_action_visit_entry_idaction_name ON log_visit.visit_entry_idaction_name = log_action_visit_entry_idaction_name.idaction
+ LEFT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
+ LEFT JOIN log_action AS log_action_idaction_event_action ON log_link_visit_action.idaction_event_action = log_action_idaction_event_action.idaction
+ ORDER BY nb_uniq_visits, log_action_idaction_event_action.name LIMIT 0, 33 )
+ AS log_inner
+ GROUP BY log_inner.name, log_inner.name02fd90a35677a359ea5611a4bc456a6f
+ ORDER BY nb_uniq_visits, log_inner.name02fd90a35677a359ea5611a4bc456a6f",
+ "bind" => array(1));
+
+ $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
+ }
+
+ public function test_getSelectQuery_whenLimit_withCustomJoinsAndSameColumnsAndSimilarColumns()
+ {
+ $select = 'log_link_visit_action.idvisit,
+ log_visit.idvisit,
+ count(log_visit.idvisit) as numvisits,
+ count(distinct log_visit.idvisit ) as numvisitors,
+ log_visit.idvisitor,
+ log_action.name as url,
+ sum(log_link_visit_action.time_spent) as `13`,
+ sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`';
+ $from = array('log_visit', 'log_link_visit_action');
+ $where = '';
+ $bind = array(1);
+
+ $segment = '';
+ $segment = new Segment($segment, $idSites = array());
+
+ $orderBy = 'url, log_visit.idvisit';
+ $groupBy = 'log_visit.idvisit, log_visit.idvisit , log_visit.idvisitor, log_visit.idvisitor , log_link_visit_action.idvisit';
+ $limit = 33;
+
+ $query = $segment->getSelectQuery($select, $from, $where, $bind, $orderBy, $groupBy, $limit);
+
+ // should have replaced some idvisit columns but not idvisitor column
+ $expected = array(
+ "sql" => "
+ SELECT
+ log_inner.idvisit,
+ log_inner.idvisit5d489886e80b4258a9407b219a4e2811,
+ count(log_inner.idvisit5d489886e80b4258a9407b219a4e2811) as numvisits,
+ count(distinct log_inner.idvisit5d489886e80b4258a9407b219a4e2811 ) as numvisitors,
+ log_inner.idvisitor,
+ log_inner.name as url,
+ sum(log_inner.time_spent) as `13`,
+ sum(case log_inner.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `6`
+ FROM
+
+ (
+
+ SELECT
+ log_link_visit_action.idvisit,
+log_visit.idvisit as idvisit5d489886e80b4258a9407b219a4e2811,
+log_visit.idvisitor,
+log_action.name,
+log_link_visit_action.time_spent,
+log_visit.visit_total_actions
+ FROM
+ log_visit AS log_visit LEFT JOIN log_link_visit_action AS log_link_visit_action ON log_link_visit_action.idvisit = log_visit.idvisit
+ ORDER BY
+ url, log_visit.idvisit LIMIT 0, 33
+ ) AS log_inner
+ GROUP BY
+ log_inner.idvisit5d489886e80b4258a9407b219a4e2811, log_inner.idvisit5d489886e80b4258a9407b219a4e2811 , log_inner.idvisitor, log_inner.idvisitor , log_inner.idvisit
+ ORDER BY
+ url, log_inner.idvisit5d489886e80b4258a9407b219a4e2811",
+ "bind" => array(1));
+
+ $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query));
+ }
public function test_getSelectQuery_whenLimitAndOffset_outerQueryShouldNotHaveOffset()
{
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerHour__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerHour__API.getSuggestedValuesForSegment.xml
new file mode 100644
index 0000000000..98314b9ea4
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerHour__API.getSuggestedValuesForSegment.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>0</row>
+ <row>1</row>
+ <row>2</row>
+ <row>3</row>
+ <row>4</row>
+ <row>5</row>
+ <row>6</row>
+ <row>7</row>
+ <row>8</row>
+ <row>9</row>
+ <row>10</row>
+ <row>11</row>
+ <row>12</row>
+ <row>13</row>
+ <row>14</row>
+ <row>15</row>
+ <row>16</row>
+ <row>17</row>
+ <row>18</row>
+ <row>19</row>
+ <row>20</row>
+ <row>21</row>
+ <row>22</row>
+ <row>23</row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerHour__VisitsSummary.get_range.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerHour__VisitsSummary.get_range.xml
new file mode 100644
index 0000000000..7ace3fcbe7
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerHour__VisitsSummary.get_range.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <nb_visits>18</nb_visits>
+ <nb_actions>18</nb_actions>
+ <nb_visits_converted>18</nb_visits_converted>
+ <bounce_count>18</bounce_count>
+ <sum_visit_length>0</sum_visit_length>
+ <max_actions>1</max_actions>
+ <bounce_rate>100%</bounce_rate>
+ <nb_actions_per_visit>1</nb_actions_per_visit>
+ <avg_time_on_site>0</avg_time_on_site>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerMinute__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerMinute__API.getSuggestedValuesForSegment.xml
new file mode 100644
index 0000000000..6e7cfc1a1d
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerMinute__API.getSuggestedValuesForSegment.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>0</row>
+ <row>1</row>
+ <row>2</row>
+ <row>3</row>
+ <row>4</row>
+ <row>5</row>
+ <row>6</row>
+ <row>7</row>
+ <row>8</row>
+ <row>9</row>
+ <row>10</row>
+ <row>11</row>
+ <row>12</row>
+ <row>13</row>
+ <row>14</row>
+ <row>15</row>
+ <row>16</row>
+ <row>17</row>
+ <row>18</row>
+ <row>19</row>
+ <row>20</row>
+ <row>21</row>
+ <row>22</row>
+ <row>23</row>
+ <row>24</row>
+ <row>25</row>
+ <row>26</row>
+ <row>27</row>
+ <row>28</row>
+ <row>29</row>
+ <row>30</row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerMinute__VisitsSummary.get_range.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerMinute__VisitsSummary.get_range.xml
new file mode 100644
index 0000000000..f3bee672d6
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_actionServerMinute__VisitsSummary.get_range.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <nb_visits>35</nb_visits>
+ <nb_actions>95</nb_actions>
+ <nb_visits_converted>35</nb_visits_converted>
+ <bounce_count>18</bounce_count>
+ <sum_visit_length>27557</sum_visit_length>
+ <max_actions>5</max_actions>
+ <bounce_rate>51%</bounce_rate>
+ <nb_actions_per_visit>2.7</nb_actions_per_visit>
+ <avg_time_on_site>787</avg_time_on_site>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_deviceType__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_deviceType__API.getSuggestedValuesForSegment.xml
index 7b9c37df18..811b7dac21 100644
--- a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_deviceType__API.getSuggestedValuesForSegment.xml
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_deviceType__API.getSuggestedValuesForSegment.xml
@@ -1,7 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<result>
- <row>Desktop</row>
- <row>Smartphone</row>
- <row>Phablet</row>
- <row>Tablet</row>
+ <row>desktop</row>
+ <row>smartphone</row>
+ <row>tablet</row>
+ <row>feature phone</row>
+ <row>console</row>
+ <row>tv</row>
+ <row>car browser</row>
+ <row>smart display</row>
+ <row>camera</row>
+ <row>portable media player</row>
+ <row>phablet</row>
</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_interaction_position__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_interaction_position__API.getSuggestedValuesForSegment.xml
new file mode 100644
index 0000000000..c234bed59e
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_interaction_position__API.getSuggestedValuesForSegment.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result /> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_referrerType__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_referrerType__API.getSuggestedValuesForSegment.xml
index 9b61eb17b1..000a8d45c4 100644
--- a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_referrerType__API.getSuggestedValuesForSegment.xml
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_referrerType__API.getSuggestedValuesForSegment.xml
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<result>
<row>direct</row>
+ <row>website</row>
<row>search</row>
+ <row>campaign</row>
</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitEcommerceStatus__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitEcommerceStatus__API.getSuggestedValuesForSegment.xml
index 405fd9750c..5acd8f333f 100644
--- a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitEcommerceStatus__API.getSuggestedValuesForSegment.xml
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitEcommerceStatus__API.getSuggestedValuesForSegment.xml
@@ -1,4 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<result>
<row>none</row>
+ <row>ordered</row>
+ <row>abandonedCart</row>
+ <row>orderedThenAbandonedCart</row>
</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitEndServerMinute__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitEndServerMinute__API.getSuggestedValuesForSegment.xml
new file mode 100644
index 0000000000..6e7cfc1a1d
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitEndServerMinute__API.getSuggestedValuesForSegment.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>0</row>
+ <row>1</row>
+ <row>2</row>
+ <row>3</row>
+ <row>4</row>
+ <row>5</row>
+ <row>6</row>
+ <row>7</row>
+ <row>8</row>
+ <row>9</row>
+ <row>10</row>
+ <row>11</row>
+ <row>12</row>
+ <row>13</row>
+ <row>14</row>
+ <row>15</row>
+ <row>16</row>
+ <row>17</row>
+ <row>18</row>
+ <row>19</row>
+ <row>20</row>
+ <row>21</row>
+ <row>22</row>
+ <row>23</row>
+ <row>24</row>
+ <row>25</row>
+ <row>26</row>
+ <row>27</row>
+ <row>28</row>
+ <row>29</row>
+ <row>30</row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitEndServerMinute__VisitsSummary.get_range.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitEndServerMinute__VisitsSummary.get_range.xml
new file mode 100644
index 0000000000..7ace3fcbe7
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitEndServerMinute__VisitsSummary.get_range.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <nb_visits>18</nb_visits>
+ <nb_actions>18</nb_actions>
+ <nb_visits_converted>18</nb_visits_converted>
+ <bounce_count>18</bounce_count>
+ <sum_visit_length>0</sum_visit_length>
+ <max_actions>1</max_actions>
+ <bounce_rate>100%</bounce_rate>
+ <nb_actions_per_visit>1</nb_actions_per_visit>
+ <avg_time_on_site>0</avg_time_on_site>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitLocalMinute__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitLocalMinute__API.getSuggestedValuesForSegment.xml
new file mode 100644
index 0000000000..6e7cfc1a1d
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitLocalMinute__API.getSuggestedValuesForSegment.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>0</row>
+ <row>1</row>
+ <row>2</row>
+ <row>3</row>
+ <row>4</row>
+ <row>5</row>
+ <row>6</row>
+ <row>7</row>
+ <row>8</row>
+ <row>9</row>
+ <row>10</row>
+ <row>11</row>
+ <row>12</row>
+ <row>13</row>
+ <row>14</row>
+ <row>15</row>
+ <row>16</row>
+ <row>17</row>
+ <row>18</row>
+ <row>19</row>
+ <row>20</row>
+ <row>21</row>
+ <row>22</row>
+ <row>23</row>
+ <row>24</row>
+ <row>25</row>
+ <row>26</row>
+ <row>27</row>
+ <row>28</row>
+ <row>29</row>
+ <row>30</row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitLocalMinute__VisitsSummary.get_range.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitLocalMinute__VisitsSummary.get_range.xml
new file mode 100644
index 0000000000..d8bcdace18
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitLocalMinute__VisitsSummary.get_range.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <nb_visits>0</nb_visits>
+ <nb_actions>0</nb_actions>
+ <nb_visits_converted>0</nb_visits_converted>
+ <bounce_count>0</bounce_count>
+ <sum_visit_length>0</sum_visit_length>
+ <max_actions>0</max_actions>
+ <bounce_rate>0%</bounce_rate>
+ <nb_actions_per_visit>0</nb_actions_per_visit>
+ <avg_time_on_site>0</avg_time_on_site>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitServerMinute__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitServerMinute__API.getSuggestedValuesForSegment.xml
new file mode 100644
index 0000000000..6e7cfc1a1d
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitServerMinute__API.getSuggestedValuesForSegment.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>0</row>
+ <row>1</row>
+ <row>2</row>
+ <row>3</row>
+ <row>4</row>
+ <row>5</row>
+ <row>6</row>
+ <row>7</row>
+ <row>8</row>
+ <row>9</row>
+ <row>10</row>
+ <row>11</row>
+ <row>12</row>
+ <row>13</row>
+ <row>14</row>
+ <row>15</row>
+ <row>16</row>
+ <row>17</row>
+ <row>18</row>
+ <row>19</row>
+ <row>20</row>
+ <row>21</row>
+ <row>22</row>
+ <row>23</row>
+ <row>24</row>
+ <row>25</row>
+ <row>26</row>
+ <row>27</row>
+ <row>28</row>
+ <row>29</row>
+ <row>30</row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitServerMinute__VisitsSummary.get_range.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitServerMinute__VisitsSummary.get_range.xml
new file mode 100644
index 0000000000..7ace3fcbe7
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitServerMinute__VisitsSummary.get_range.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <nb_visits>18</nb_visits>
+ <nb_actions>18</nb_actions>
+ <nb_visits_converted>18</nb_visits_converted>
+ <bounce_count>18</bounce_count>
+ <sum_visit_length>0</sum_visit_length>
+ <max_actions>1</max_actions>
+ <bounce_rate>100%</bounce_rate>
+ <nb_actions_per_visit>1</nb_actions_per_visit>
+ <avg_time_on_site>0</avg_time_on_site>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerHour__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerHour__API.getSuggestedValuesForSegment.xml
new file mode 100644
index 0000000000..98314b9ea4
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerHour__API.getSuggestedValuesForSegment.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>0</row>
+ <row>1</row>
+ <row>2</row>
+ <row>3</row>
+ <row>4</row>
+ <row>5</row>
+ <row>6</row>
+ <row>7</row>
+ <row>8</row>
+ <row>9</row>
+ <row>10</row>
+ <row>11</row>
+ <row>12</row>
+ <row>13</row>
+ <row>14</row>
+ <row>15</row>
+ <row>16</row>
+ <row>17</row>
+ <row>18</row>
+ <row>19</row>
+ <row>20</row>
+ <row>21</row>
+ <row>22</row>
+ <row>23</row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerHour__VisitsSummary.get_range.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerHour__VisitsSummary.get_range.xml
new file mode 100644
index 0000000000..7ace3fcbe7
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerHour__VisitsSummary.get_range.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <nb_visits>18</nb_visits>
+ <nb_actions>18</nb_actions>
+ <nb_visits_converted>18</nb_visits_converted>
+ <bounce_count>18</bounce_count>
+ <sum_visit_length>0</sum_visit_length>
+ <max_actions>1</max_actions>
+ <bounce_rate>100%</bounce_rate>
+ <nb_actions_per_visit>1</nb_actions_per_visit>
+ <avg_time_on_site>0</avg_time_on_site>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerMinute__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerMinute__API.getSuggestedValuesForSegment.xml
new file mode 100644
index 0000000000..6e7cfc1a1d
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerMinute__API.getSuggestedValuesForSegment.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>0</row>
+ <row>1</row>
+ <row>2</row>
+ <row>3</row>
+ <row>4</row>
+ <row>5</row>
+ <row>6</row>
+ <row>7</row>
+ <row>8</row>
+ <row>9</row>
+ <row>10</row>
+ <row>11</row>
+ <row>12</row>
+ <row>13</row>
+ <row>14</row>
+ <row>15</row>
+ <row>16</row>
+ <row>17</row>
+ <row>18</row>
+ <row>19</row>
+ <row>20</row>
+ <row>21</row>
+ <row>22</row>
+ <row>23</row>
+ <row>24</row>
+ <row>25</row>
+ <row>26</row>
+ <row>27</row>
+ <row>28</row>
+ <row>29</row>
+ <row>30</row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerMinute__VisitsSummary.get_range.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerMinute__VisitsSummary.get_range.xml
new file mode 100644
index 0000000000..7ace3fcbe7
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitStartServerMinute__VisitsSummary.get_range.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <nb_visits>18</nb_visits>
+ <nb_actions>18</nb_actions>
+ <nb_visits_converted>18</nb_visits_converted>
+ <bounce_count>18</bounce_count>
+ <sum_visit_length>0</sum_visit_length>
+ <max_actions>1</max_actions>
+ <bounce_rate>100%</bounce_rate>
+ <nb_actions_per_visit>1</nb_actions_per_visit>
+ <avg_time_on_site>0</avg_time_on_site>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitorType__API.getSuggestedValuesForSegment.xml b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitorType__API.getSuggestedValuesForSegment.xml
index 84613aaaf2..e598eaeee8 100644
--- a/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitorType__API.getSuggestedValuesForSegment.xml
+++ b/tests/PHPUnit/System/expected/test_AutoSuggestAPITest_visitorType__API.getSuggestedValuesForSegment.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<result>
<row>returningCustomer</row>
+ <row>returning</row>
<row>new</row>
</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_sortByProcessedMetric_constantRowsCountShouldKeepEmptyRows__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_sortByProcessedMetric_constantRowsCountShouldKeepEmptyRows__API.getProcessedReport_day.xml
index b6cff4f6c8..66b9eb6bd3 100644
--- a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_sortByProcessedMetric_constantRowsCountShouldKeepEmptyRows__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_sortByProcessedMetric_constantRowsCountShouldKeepEmptyRows__API.getProcessedReport_day.xml
@@ -8,7 +8,7 @@
<name>Visits per server time</name>
<module>VisitTime</module>
<action>getVisitInformationPerServerTime</action>
- <dimension>Server time</dimension>
+ <dimension>Server time - hour (End of visit)</dimension>
<documentation>This graph shows what time it was in the &lt;strong&gt; server's time zone &lt;/strong&gt; during the visits.</documentation>
<metrics>
<nb_visits>Visits</nb_visits>
@@ -43,7 +43,7 @@
<uniqueId>VisitTime_getVisitInformationPerServerTime</uniqueId>
</metadata>
<columns>
- <label>Server time</label>
+ <label>Server time - hour (End of visit)</label>
<nb_visits>Visits</nb_visits>
<nb_uniq_visitors>Unique visitors</nb_uniq_visitors>
<nb_actions>Actions</nb_actions>
diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml
index f59cb9522a..442db1a9b3 100644
--- a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml
@@ -8,7 +8,7 @@
<name>Page titles</name>
<module>Actions</module>
<action>getPageTitles</action>
- <dimension>Page Name</dimension>
+ <dimension>Page Title</dimension>
<documentation>This report contains information about the titles of the pages that have been visited. &lt;br /&gt; The page title is the HTML &lt;title&gt; Tag that most browsers show in their window title.</documentation>
<metricsDocumentation>
<exit_rate>The percentage of visits that left the website after viewing this page.</exit_rate>
@@ -34,7 +34,7 @@
<uniqueId>Actions_getPageTitles</uniqueId>
</metadata>
<columns>
- <label>Page Name</label>
+ <label>Page Title</label>
<exit_rate>Exit rate</exit_rate>
</columns>
<reportData>
diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumnsWithProcessedMetrics___API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumnsWithProcessedMetrics___API.getProcessedReport_day.xml
index 92feaea19a..2137a5f645 100644
--- a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumnsWithProcessedMetrics___API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumnsWithProcessedMetrics___API.getProcessedReport_day.xml
@@ -8,7 +8,7 @@
<name>Visits per server time</name>
<module>VisitTime</module>
<action>getVisitInformationPerServerTime</action>
- <dimension>Server time</dimension>
+ <dimension>Server time - hour (End of visit)</dimension>
<documentation>This graph shows what time it was in the &lt;strong&gt; server's time zone &lt;/strong&gt; during the visits.</documentation>
<metrics>
<nb_visits>Visits</nb_visits>
@@ -28,7 +28,7 @@
<uniqueId>VisitTime_getVisitInformationPerServerTime</uniqueId>
</metadata>
<columns>
- <label>Server time</label>
+ <label>Server time - hour (End of visit)</label>
<nb_visits>Visits</nb_visits>
<revenue>Revenue</revenue>
</columns>
diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml
index a9e1f34081..33c6b4c63d 100644
--- a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml
@@ -8,7 +8,7 @@
<name>Page titles</name>
<module>Actions</module>
<action>getPageTitles</action>
- <dimension>Page Name</dimension>
+ <dimension>Page Title</dimension>
<documentation>This report contains information about the titles of the pages that have been visited. &lt;br /&gt; The page title is the HTML &lt;title&gt; Tag that most browsers show in their window title.</documentation>
<metrics>
<nb_hits>Pageviews</nb_hits>
@@ -38,7 +38,7 @@
<uniqueId>Actions_getPageTitles</uniqueId>
</metadata>
<columns>
- <label>Page Name</label>
+ <label>Page Title</label>
<nb_hits>Pageviews</nb_hits>
<bounce_rate>Bounce Rate</bounce_rate>
</columns>
diff --git a/tests/PHPUnit/System/expected/test_RowEvolution_pageTitlesMulti__API.getRowEvolution_day.xml b/tests/PHPUnit/System/expected/test_RowEvolution_pageTitlesMulti__API.getRowEvolution_day.xml
index 649d127ab5..55f88165ef 100644
--- a/tests/PHPUnit/System/expected/test_RowEvolution_pageTitlesMulti__API.getRowEvolution_day.xml
+++ b/tests/PHPUnit/System/expected/test_RowEvolution_pageTitlesMulti__API.getRowEvolution_day.xml
@@ -190,7 +190,7 @@
<max>1</max>
</nb_hits_1>
</metrics>
- <dimension>Page Name</dimension>
+ <dimension>Page Title</dimension>
<columns>
<nb_hits>Pageviews</nb_hits>
<nb_visits>Unique Pageviews</nb_visits>
diff --git a/tests/PHPUnit/System/expected/test_RowEvolution_pageTitlesMulti__API.getRowEvolution_week.xml b/tests/PHPUnit/System/expected/test_RowEvolution_pageTitlesMulti__API.getRowEvolution_week.xml
index 573c484414..1da0cd6ff6 100644
--- a/tests/PHPUnit/System/expected/test_RowEvolution_pageTitlesMulti__API.getRowEvolution_week.xml
+++ b/tests/PHPUnit/System/expected/test_RowEvolution_pageTitlesMulti__API.getRowEvolution_week.xml
@@ -48,7 +48,7 @@
<change>+100%</change>
</nb_hits_1>
</metrics>
- <dimension>Page Name</dimension>
+ <dimension>Page Title</dimension>
<columns>
<nb_hits>Pageviews</nb_hits>
<nb_visits>Unique Pageviews</nb_visits>
diff --git a/tests/PHPUnit/System/expected/test_RowEvolution_pageTitles__API.getRowEvolution_day.xml b/tests/PHPUnit/System/expected/test_RowEvolution_pageTitles__API.getRowEvolution_day.xml
index 5b049977a4..b5a37adbc8 100644
--- a/tests/PHPUnit/System/expected/test_RowEvolution_pageTitles__API.getRowEvolution_day.xml
+++ b/tests/PHPUnit/System/expected/test_RowEvolution_pageTitles__API.getRowEvolution_day.xml
@@ -153,6 +153,6 @@
<max>2.8</max>
</avg_time_generation>
</metrics>
- <dimension>Page Name</dimension>
+ <dimension>Page Title</dimension>
</metadata>
</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_RowEvolution_pageTitles__API.getRowEvolution_week.xml b/tests/PHPUnit/System/expected/test_RowEvolution_pageTitles__API.getRowEvolution_week.xml
index a4d4962bb2..a30e3f3a2e 100644
--- a/tests/PHPUnit/System/expected/test_RowEvolution_pageTitles__API.getRowEvolution_week.xml
+++ b/tests/PHPUnit/System/expected/test_RowEvolution_pageTitles__API.getRowEvolution_week.xml
@@ -89,6 +89,6 @@
<change>-92%</change>
</avg_time_generation>
</metrics>
- <dimension>Page Name</dimension>
+ <dimension>Page Title</dimension>
</metadata>
</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml
index 3808a4f5b5..289959a721 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml
@@ -8,7 +8,7 @@
<name>Page titles</name>
<module>Actions</module>
<action>getPageTitles</action>
- <dimension>Page Name</dimension>
+ <dimension>Page Title</dimension>
<documentation>This report contains information about the titles of the pages that have been visited. &lt;br /&gt; The page title is the HTML &lt;title&gt; Tag that most browsers show in their window title.</documentation>
<metrics>
<nb_hits>Pageviews</nb_hits>
@@ -45,7 +45,7 @@
<uniqueId>Actions_getPageTitles</uniqueId>
</metadata>
<columns>
- <label>Page Name</label>
+ <label>Page Title</label>
<nb_hits>Pageviews</nb_hits>
<nb_visits>Unique Pageviews</nb_visits>
<avg_time_on_page>Avg. time on page</avg_time_on_page>
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_month.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_month.xml
index f88c7b0657..a61bf4e2b8 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_month.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_month.xml
@@ -8,7 +8,7 @@
<name>Page titles</name>
<module>Actions</module>
<action>getPageTitles</action>
- <dimension>Page Name</dimension>
+ <dimension>Page Title</dimension>
<documentation>This report contains information about the titles of the pages that have been visited. &lt;br /&gt; The page title is the HTML &lt;title&gt; Tag that most browsers show in their window title.</documentation>
<metrics>
<nb_hits>Pageviews</nb_hits>
@@ -45,7 +45,7 @@
<uniqueId>Actions_getPageTitles</uniqueId>
</metadata>
<columns>
- <label>Page Name</label>
+ <label>Page Title</label>
<nb_hits>Pageviews</nb_hits>
<nb_visits>Unique Pageviews</nb_visits>
<avg_time_on_page>Avg. time on page</avg_time_on_page>
diff --git a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml
index ba0c0464ac..2ba4f93a54 100644
--- a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageTitles_firstSite_lastN__API.getProcessedReport_day.xml
@@ -8,7 +8,7 @@
<name>Page titles</name>
<module>Actions</module>
<action>getPageTitles</action>
- <dimension>Page Name</dimension>
+ <dimension>Page Title</dimension>
<documentation>This report contains information about the titles of the pages that have been visited. &lt;br /&gt; The page title is the HTML &lt;title&gt; Tag that most browsers show in their window title.</documentation>
<metrics>
<nb_hits>Pageviews</nb_hits>
@@ -46,7 +46,7 @@
<uniqueId>Actions_getPageTitles</uniqueId>
</metadata>
<columns>
- <label>Page Name</label>
+ <label>Page Title</label>
<nb_hits>Pageviews</nb_hits>
<nb_visits>Unique Pageviews</nb_visits>
<avg_time_on_page>Avg. time on page</avg_time_on_page>
diff --git a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html
index e30020a41d..5135698bdc 100644
--- a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html
+++ b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_month.original.html
@@ -898,7 +898,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Operating System versions&nbsp;&nbsp;
+ &nbsp;Operating system version&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Visits&nbsp;&nbsp;
@@ -2878,7 +2878,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Local time&nbsp;&nbsp;
+ &nbsp;Local time - hour (Start of visit)&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Visits&nbsp;&nbsp;
@@ -3475,7 +3475,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Server time&nbsp;&nbsp;
+ &nbsp;Server time - hour (End of visit)&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Visits&nbsp;&nbsp;
@@ -4898,7 +4898,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Page Name&nbsp;&nbsp;
+ &nbsp;Page Title&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Pageviews&nbsp;&nbsp;
diff --git a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_month.original.html b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_month.original.html
index 4ccc27edec..d503e5ebd0 100644
--- a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_month.original.html
+++ b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_month.original.html
@@ -849,7 +849,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Operating System versions&nbsp;&nbsp;
+ &nbsp;Operating system version&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Visits&nbsp;&nbsp;
@@ -2703,7 +2703,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Local time&nbsp;&nbsp;
+ &nbsp;Local time - hour (Start of visit)&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Visits&nbsp;&nbsp;
@@ -3293,7 +3293,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Server time&nbsp;&nbsp;
+ &nbsp;Server time - hour (End of visit)&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Visits&nbsp;&nbsp;
@@ -4653,7 +4653,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Page Name&nbsp;&nbsp;
+ &nbsp;Page Title&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Pageviews&nbsp;&nbsp;
diff --git a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdf b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdf
index 4b292c91f7..130ab4d3a0 100644
--- a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdf
+++ b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_month.original.pdf
Binary files differ
diff --git a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getReportMetadata_day.xml b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getReportMetadata_day.xml
index 50e1fd9532..fe04019899 100644
--- a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getReportMetadata_day.xml
+++ b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getReportMetadata_day.xml
@@ -332,7 +332,7 @@
<name>Operating System versions</name>
<module>DevicesDetection</module>
<action>getOsVersions</action>
- <dimension>Operating System versions</dimension>
+ <dimension>Operating system version</dimension>
<metrics>
<nb_visits>Visits</nb_visits>
<nb_uniq_visitors>Unique visitors</nb_uniq_visitors>
@@ -918,7 +918,7 @@
<name>Visits per local time</name>
<module>VisitTime</module>
<action>getVisitInformationPerLocalTime</action>
- <dimension>Local time</dimension>
+ <dimension>Local time - hour (Start of visit)</dimension>
<documentation>This graph shows what time it was in the &lt;strong&gt; visitors' time zones &lt;/strong&gt; during their visits.</documentation>
<metrics>
<nb_visits>Visits</nb_visits>
@@ -959,7 +959,7 @@
<name>Visits per server time</name>
<module>VisitTime</module>
<action>getVisitInformationPerServerTime</action>
- <dimension>Server time</dimension>
+ <dimension>Server time - hour (End of visit)</dimension>
<documentation>This graph shows what time it was in the &lt;strong&gt; server's time zone &lt;/strong&gt; during the visits.</documentation>
<metrics>
<nb_visits>Visits</nb_visits>
@@ -1265,7 +1265,7 @@
<name>Page titles</name>
<module>Actions</module>
<action>getPageTitles</action>
- <dimension>Page Name</dimension>
+ <dimension>Page Title</dimension>
<documentation>This report contains information about the titles of the pages that have been visited. &lt;br /&gt; The page title is the HTML &lt;title&gt; Tag that most browsers show in their window title.</documentation>
<metrics>
<nb_hits>Pageviews</nb_hits>
diff --git a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml
index 326487de23..d72a0e6802 100644
--- a/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml
+++ b/tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getSegmentsMetadata.xml
@@ -2,135 +2,86 @@
<result>
<row>
<type>metric</type>
- <category>Visit</category>
+ <category>Visitors</category>
+ <name>Actions In Visit</name>
+ <segment>actions</segment>
+ </row>
+ <row>
+ <type>metric</type>
+ <category>Visitors</category>
<name>Days since first visit</name>
<segment>daysSinceFirstVisit</segment>
</row>
<row>
<type>metric</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Days since last Ecommerce order</name>
<segment>daysSinceLastEcommerceOrder</segment>
</row>
<row>
<type>metric</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Days since last visit</name>
<segment>daysSinceLastVisit</segment>
</row>
<row>
<type>metric</type>
- <category>Visit</category>
- <name>Number of Actions</name>
- <segment>actions</segment>
+ <category>Visitors</category>
+ <name>Local time - minute (Start of visit)</name>
+ <segment>visitLocalMinute</segment>
+ <acceptedValues>0, 1, 2, 3, ..., 67, 57, 58, 59</acceptedValues>
</row>
<row>
<type>metric</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Number of Interactions</name>
<segment>interactions</segment>
<acceptedValues>Any positive integer</acceptedValues>
</row>
<row>
<type>metric</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Number of Internal Searches</name>
<segment>searches</segment>
<acceptedValues>To select all visits who used internal Site Search, use: &amp;segment=searches&gt;0</acceptedValues>
</row>
<row>
<type>metric</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Number of visits</name>
<segment>visitCount</segment>
</row>
<row>
<type>metric</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Total events</name>
<segment>events</segment>
<acceptedValues>To select all visits who triggered an Event, use: &amp;segment=events&gt;0</acceptedValues>
</row>
<row>
<type>metric</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Visit Duration (in seconds)</name>
<segment>visitDuration</segment>
</row>
<row>
<type>metric</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Visitor IP</name>
<segment>visitIp</segment>
<acceptedValues>13.54.122.1. &lt;/code&gt;Select IP ranges with notation: &lt;code&gt;visitIp&gt;13.54.122.0;visitIp&lt;13.54.122.255</acceptedValues>
<permission>1</permission>
</row>
<row>
- <type>metric</type>
- <category>Actions</category>
- <name>Bandwidth</name>
- <segment>bandwidth</segment>
- <acceptedValues>Any number in bytes, eg. 1000</acceptedValues>
- </row>
- <row>
- <type>dimension</type>
- <category>Visit Location</category>
- <name>City</name>
- <segment>city</segment>
- <acceptedValues>Sydney, Sao Paolo, Rome, etc.</acceptedValues>
- </row>
- <row>
- <type>dimension</type>
- <category>Visit Location</category>
- <name>Continent</name>
- <segment>continentCode</segment>
- <acceptedValues>eur, asi, amc, amn, ams, afr, ant, oce</acceptedValues>
- </row>
- <row>
- <type>dimension</type>
- <category>Visit Location</category>
- <name>Country</name>
- <segment>countryCode</segment>
- <acceptedValues>de, us, fr, in, es, etc.</acceptedValues>
- </row>
- <row>
- <type>dimension</type>
- <category>Visit Location</category>
- <name>Language</name>
- <segment>languageCode</segment>
- <acceptedValues>de, fr, en-gb, zh-cn, etc.</acceptedValues>
- </row>
- <row>
- <type>dimension</type>
- <category>Visit Location</category>
- <name>Latitude</name>
- <segment>latitude</segment>
- <acceptedValues>-33.578, 40.830, etc.&lt;br/&gt;You can select visitors within a lat/long range using &amp;segment=lat&gt;X;lat&lt;Y;long&gt;M;long&lt;N.</acceptedValues>
- </row>
- <row>
<type>dimension</type>
- <category>Visit Location</category>
- <name>Longitude</name>
- <segment>longitude</segment>
- <acceptedValues>-70.664, 14.326, etc.</acceptedValues>
- </row>
- <row>
- <type>dimension</type>
- <category>Visit Location</category>
- <name>Region</name>
- <segment>regionCode</segment>
- <acceptedValues>01 02, OR, P8, etc.&lt;br/&gt;eg. region=A1;country=fr</acceptedValues>
- </row>
- <row>
- <type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Browser</name>
<segment>browserCode</segment>
<acceptedValues>FF, IE, CH, SF, OP, etc.</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Browser engine</name>
<segment>browserEngine</segment>
<acceptedValues>Trident, WebKit, Presto, Gecko, Blink, etc.</acceptedValues>
@@ -138,63 +89,84 @@
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Browser version</name>
<segment>browserVersion</segment>
<acceptedValues>1.0, 8.0, etc.</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Device brand</name>
<segment>deviceBrand</segment>
<acceptedValues>3Q, 4Good, Acer, Ainol, Airness, Airties, Alcatel, Allview, Altech UEC, Amazon, Amoi, Apple, Archos, Arnova, ARRIS, Asus, Audiovox, Avvio, Axxion, BangOlufsen, Barnes &amp; Noble, BBK, Becker, Beetel, BenQ, BenQ-Siemens, Bird, Blackview, Blaupunkt, Blu, Bmobile, Boway, bq, Bravis, Brondi, Bush, Capitel, Captiva, Carrefour, Casio, Cat, Celkon, Changhong, Cherry Mobile, China Mobile, CnM, Coby Kyros, Compal, Compaq, ConCorde, Coolpad, Cowon, CreNova, Cricket, Crius Mea, Crosscall, Cube, CUBOT, Cyrus, Danew, Datang, Dbtel, Dell, Denver, Desay, DEXP, Dicam, Digma, DMM, DNS, DoCoMo, Doogee, Doov, Dopod, Dune HD, E-Boda, Easypix, EBEST, ECS, Elephone, Energy Sistem, Ericsson, Ericy, Eton, eTouch, Evertek, Evolveo, Explay, Ezio, Ezze, Fairphone, Fly, Foxconn, Freetel, Fujitsu, Garmin-Asus, Gateway, Gemini, Gigabyte, Gigaset, Gionee, GOCLEVER, Goly, Google, Gradiente, Grundig, Haier, Hasee, Hi-Level, Hisense, Homtom, Hosin, HP, HTC, Huawei, Humax, Hyrican, Hyundai, i-Joy, i-mate, i-mobile, iBall, iBerry, IconBIT, Ikea, iKoMo, iNew, Infinix, Inkti, Innostream, INQ, Intek, Intex, Inverto, iOcean, iTel, JAY-Tech, Jiayu, Jolla, K-Touch, Karbonn, Kazam, KDDI, Kiano, Kingsun, Komu, Konka, Konrow, Koobee, KOPO, Koridy, KT-Tech, Kumai, Kyocera, Landvo, Lanix, Lava, LCT, LeEco, Lenco, Lenovo, Le Pan, Lexibook, LG, Lingwin, Loewe, Logicom, LYF, M.T.T., Majestic, Manta Multimedia, Mecer, Mediacom, MediaTek, Medion, MEEG, Meizu, Memup, Metz, MEU, MicroMax, Microsoft, Mio, Mitsubishi, MIXC, MLLED, Mobistel, Modecom, Mofut, Motorola, Mpman, MSI, MyPhone, NEC, Neffos, Netgear, Newgen, Nexian, NextBook, NGM, Nikon, Nintendo, Noain, Nokia, Nomi, Nous, Nvidia, O2, Odys, Onda, OnePlus, OPPO, Opsson, Orange, Ouki, OUYA, Overmax, Oysters, Palm, Panasonic, Pantech, PEAQ, Pentagram, Philips, phoneOne, Pioneer, Ployer, Point of View, Polaroid, PolyPad, Pomp, Positivo, PPTV, Prestigio, ProScan, PULID, Qilive, QMobile, Qtek, Quechua, Ramos, RCA Tablets, Readboy, Rikomagic, RIM, Roku, Rover, Sagem, Samsung, Sanyo, Sega, Selevision, Sencor, Sendo, Senseit, SFR, Sharp, Siemens, Skyworth, Smart, Smartfren, Smartisan, Softbank, Sony, Sony Ericsson, Spice, Star, Stonex, Storex, Sumvision, SunVan, SuperSonic, Supra, Symphony, T-Mobile, TB Touch, TCL, TechniSat, TechnoTrend, Tecno Mobile, Telefunken, Telenor, Telit, Tesco, Tesla, teXet, ThL, Thomson, TIANYU, TiPhone, Tolino, Toplux, Toshiba, TrekStor, Trevi, Tunisie Telecom, Turbo-X, TVC, UMIDIGI, Uniscope, Unknown, Unowhy, UTStarcom, Vastking, Vertu, Vestel, Videocon, Videoweb, ViewSonic, Vitelcom, Vivo, Vizio, VK Mobile, Vodafone, Voto, Voxtel, Walton, Web TV, WellcoM, Wexler, Wiko, Wileyfox, Wolder, Wolfgang, Wonu, Woxter, Xiaomi, Xolo, Yarvik, Ytone, Yuandao, Yusun, Zeemi, Zen, Zonda, Zopo, ZTE</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Device type</name>
<segment>deviceType</segment>
<acceptedValues>desktop, smartphone, tablet, feature phone, console, tv, car browser, smart display, camera, portable media player, phablet</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
- <name>Local time</name>
+ <category>Visitors</category>
+ <name>Local time - hour (Start of visit)</name>
<segment>visitLocalHour</segment>
<acceptedValues>0, 1, 2, 3, ..., 20, 21, 22, 23</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Operating system</name>
<segment>operatingSystemCode</segment>
<acceptedValues>WIN, MAC, LIN, AND, IPD, etc.</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Operating system version</name>
<segment>operatingSystemVersion</segment>
<acceptedValues>XP, 7, 2.3, 5.1, ...</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Resolution</name>
<segment>resolution</segment>
<acceptedValues>1280x1024, 800x600, etc.</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
- <name>Server time</name>
+ <category>Visitors</category>
+ <name>Server time - hour (End of visit)</name>
<segment>visitServerHour</segment>
<acceptedValues>0, 1, 2, 3, ..., 20, 21, 22, 23</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
+ <name>Server time - hour (Start of visit)</name>
+ <segment>visitStartServerHour</segment>
+ <acceptedValues>0, 1, 2, 3, ..., 20, 21, 22, 23</acceptedValues>
+ </row>
+ <row>
+ <type>dimension</type>
+ <category>Visitors</category>
+ <name>Server time - minute (End of visit)</name>
+ <segment>visitEndServerMinute</segment>
+ <acceptedValues>0, 1, 2, 3, ..., 56, 57, 58, 59</acceptedValues>
+ </row>
+ <row>
+ <type>dimension</type>
+ <category>Visitors</category>
+ <name>Server time - minute (Start of visit)</name>
+ <segment>visitStartServerMinute</segment>
+ <acceptedValues>0, 1, 2, 3, ..., 56, 57, 58, 59</acceptedValues>
+ </row>
+ <row>
+ <type>dimension</type>
+ <category>Visitors</category>
<name>User ID</name>
<segment>userId</segment>
<acceptedValues>any non empty unique string identifying the user (such as an email address or a username).</acceptedValues>
@@ -202,14 +174,14 @@
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Visit Ecommerce status at the end of the visit</name>
<segment>visitEcommerceStatus</segment>
<acceptedValues>none, ordered, abandonedCart, orderedThenAbandonedCart. For example, to select all visits that have made an Ecommerce order, the API request would contain &quot;&amp;segment=visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart&quot;</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Visit ID</name>
<segment>visitId</segment>
<acceptedValues>Any integer.</acceptedValues>
@@ -217,28 +189,28 @@
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Visit converted a specific Goal Id</name>
<segment>visitConvertedGoalId</segment>
<acceptedValues>1, 2, 3, etc.</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Visit converted at least one Goal</name>
<segment>visitConverted</segment>
<acceptedValues>0, 1</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Visit type</name>
<segment>visitorType</segment>
<acceptedValues>new, returning, returningCustomer. For example, to select all visitors who have returned to the website, including those who have bought something in their previous visits, the API request would contain &quot;&amp;segment=visitorType==returning,visitorType==returningCustomer&quot;</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Visit</category>
+ <category>Visitors</category>
<name>Visitor ID</name>
<segment>visitorId</segment>
<acceptedValues>34c31e04394bdc63 - any 16 Hexadecimal chars ID, which can be fetched using the Tracking API function getVisitorId()</acceptedValues>
@@ -246,49 +218,52 @@
</row>
<row>
<type>dimension</type>
- <category>Referrers</category>
- <name>Keyword</name>
- <segment>referrerKeyword</segment>
- <acceptedValues>Encoded%20Keyword, keyword</acceptedValues>
+ <category>Visit Location</category>
+ <name>City</name>
+ <segment>city</segment>
+ <acceptedValues>Sydney, Sao Paolo, Rome, etc.</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Referrers</category>
- <name>Referrer Name</name>
- <segment>referrerName</segment>
- <acceptedValues>twitter.com, www.facebook.com, Bing, Google, Yahoo, CampaignName</acceptedValues>
+ <category>Visit Location</category>
+ <name>Continent</name>
+ <segment>continentCode</segment>
+ <acceptedValues>eur, asi, amc, amn, ams, afr, ant, oce</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Referrers</category>
- <name>Referrer Type</name>
- <segment>referrerType</segment>
- <acceptedValues>direct, search, website, campaign</acceptedValues>
+ <category>Visit Location</category>
+ <name>Country</name>
+ <segment>countryCode</segment>
+ <acceptedValues>de, us, fr, in, es, etc.</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Referrers</category>
- <name>Referrer URL</name>
- <segment>referrerUrl</segment>
- <acceptedValues>http%3A%2F%2Fwww.example.org%2Freferer-page.htm</acceptedValues>
+ <category>Visit Location</category>
+ <name>Language</name>
+ <segment>languageCode</segment>
+ <acceptedValues>de, fr, en-gb, zh-cn, etc.</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Events</category>
- <name>Event Action</name>
- <segment>eventAction</segment>
+ <category>Visit Location</category>
+ <name>Latitude</name>
+ <segment>latitude</segment>
+ <acceptedValues>-33.578, 40.830, etc.&lt;br/&gt;You can select visitors within a lat/long range using &amp;segment=lat&gt;X;lat&lt;Y;long&gt;M;long&lt;N.</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Events</category>
- <name>Event Category</name>
- <segment>eventCategory</segment>
+ <category>Visit Location</category>
+ <name>Longitude</name>
+ <segment>longitude</segment>
+ <acceptedValues>-70.664, 14.326, etc.</acceptedValues>
</row>
<row>
<type>dimension</type>
- <category>Events</category>
- <name>Event Name</name>
- <segment>eventName</segment>
+ <category>Visit Location</category>
+ <name>Region</name>
+ <segment>regionCode</segment>
+ <acceptedValues>01 02, OR, P8, etc.&lt;br/&gt;eg. region=A1;country=fr</acceptedValues>
</row>
<row>
<type>dimension</type>
@@ -463,6 +438,13 @@
<segment>customVariableValue5</segment>
</row>
<row>
+ <type>metric</type>
+ <category>Actions</category>
+ <name>Bandwidth</name>
+ <segment>bandwidth</segment>
+ <acceptedValues>Any number in bytes, eg. 1000</acceptedValues>
+ </row>
+ <row>
<type>dimension</type>
<category>Actions</category>
<name>Action Type</name>
@@ -553,7 +535,7 @@
<row>
<type>dimension</type>
<category>Actions</category>
- <name>Page Name</name>
+ <name>Page Title</name>
<segment>pageTitle</segment>
</row>
<row>
@@ -563,4 +545,63 @@
<segment>pageUrl</segment>
<acceptedValues>All these segments must be URL encoded, for example: http%3A%2F%2Fexample.com%2Fpath%2Fpage%3Fquery</acceptedValues>
</row>
+ <row>
+ <type>dimension</type>
+ <category>Actions</category>
+ <name>Server time - hour</name>
+ <segment>actionServerHour</segment>
+ </row>
+ <row>
+ <type>dimension</type>
+ <category>Actions</category>
+ <name>Server time - minute</name>
+ <segment>actionServerMinute</segment>
+ <acceptedValues>0, 1, 2, 3, ..., 56, 57, 58, 59</acceptedValues>
+ </row>
+ <row>
+ <type>dimension</type>
+ <category>Events</category>
+ <name>Event Action</name>
+ <segment>eventAction</segment>
+ </row>
+ <row>
+ <type>dimension</type>
+ <category>Events</category>
+ <name>Event Category</name>
+ <segment>eventCategory</segment>
+ </row>
+ <row>
+ <type>dimension</type>
+ <category>Events</category>
+ <name>Event Name</name>
+ <segment>eventName</segment>
+ </row>
+ <row>
+ <type>dimension</type>
+ <category>Referrers</category>
+ <name>Keyword</name>
+ <segment>referrerKeyword</segment>
+ <acceptedValues>Encoded%20Keyword, keyword</acceptedValues>
+ </row>
+ <row>
+ <type>dimension</type>
+ <category>Referrers</category>
+ <name>Referrer Name</name>
+ <segment>referrerName</segment>
+ <acceptedValues>twitter.com, www.facebook.com, Bing, Google, Yahoo, CampaignName</acceptedValues>
+ </row>
+ <row>
+ <type>dimension</type>
+ <category>Referrers</category>
+ <name>Referrer Type</name>
+ <segment>referrerType</segment>
+ <acceptedValues>direct, search, website, campaign</acceptedValues>
+ </row>
+ <row>
+ <type>dimension</type>
+ <category>Referrers</category>
+ <name>Referrer URL</name>
+ <segment>referrerUrl</segment>
+ <acceptedValues>http%3A%2F%2Fwww.example.org%2Freferer-page.htm</acceptedValues>
+ </row>
</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_Metadata_VisitTime.getVisitInformationPerServerTime__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_Metadata_VisitTime.getVisitInformationPerServerTime__API.getProcessedReport_day.xml
index 6c39c185b3..f4fea49b13 100755
--- a/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_Metadata_VisitTime.getVisitInformationPerServerTime__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_Metadata_VisitTime.getVisitInformationPerServerTime__API.getProcessedReport_day.xml
@@ -8,7 +8,7 @@
<name>Visits per server time</name>
<module>VisitTime</module>
<action>getVisitInformationPerServerTime</action>
- <dimension>Server time</dimension>
+ <dimension>Server time - hour (End of visit)</dimension>
<documentation>This graph shows what time it was in the &lt;strong&gt; server's time zone &lt;/strong&gt; during the visits.</documentation>
<metrics>
<nb_visits>Visits</nb_visits>
@@ -43,7 +43,7 @@
<uniqueId>VisitTime_getVisitInformationPerServerTime</uniqueId>
</metadata>
<columns>
- <label>Server time</label>
+ <label>Server time - hour (End of visit)</label>
<nb_visits>Visits</nb_visits>
<nb_uniq_visitors>Unique visitors</nb_uniq_visitors>
<nb_actions>Actions</nb_actions>
diff --git a/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html b/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html
index 03c3ee692b..a6c31c80a0 100644
--- a/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html
+++ b/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_and_graph__ScheduledReports.generateReport_week.original.html
@@ -1018,7 +1018,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Operating System versions&nbsp;&nbsp;
+ &nbsp;Operating system version&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Visits&nbsp;&nbsp;
@@ -2733,7 +2733,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Local time&nbsp;&nbsp;
+ &nbsp;Local time - hour (Start of visit)&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Visits&nbsp;&nbsp;
@@ -3330,7 +3330,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Server time&nbsp;&nbsp;
+ &nbsp;Server time - hour (End of visit)&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Visits&nbsp;&nbsp;
@@ -4591,7 +4591,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Page Name&nbsp;&nbsp;
+ &nbsp;Page Title&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Pageviews&nbsp;&nbsp;
diff --git a/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_week.original.html b/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_week.original.html
index 6177dda553..0a0b0ba34f 100644
--- a/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_week.original.html
+++ b/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_html_tables_only__ScheduledReports.generateReport_week.original.html
@@ -962,7 +962,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Operating System versions&nbsp;&nbsp;
+ &nbsp;Operating system version&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Visits&nbsp;&nbsp;
@@ -2551,7 +2551,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Local time&nbsp;&nbsp;
+ &nbsp;Local time - hour (Start of visit)&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Visits&nbsp;&nbsp;
@@ -3141,7 +3141,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Server time&nbsp;&nbsp;
+ &nbsp;Server time - hour (End of visit)&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Visits&nbsp;&nbsp;
@@ -4339,7 +4339,7 @@
<table style="border-collapse:collapse; border:1px solid rgb(231,231,231); padding:5px;">
<thead style="background-color: rgb(242,242,242);">
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif;">
- &nbsp;Page Name&nbsp;&nbsp;
+ &nbsp;Page Title&nbsp;&nbsp;
</th>
<th style="border-bottom:1px solid rgb(231,231,231);font-size: 15px;text-align: left;font-weight:normal;padding:13px 0 13px 10px;color:#0d0d0d;font-family:-apple-system, BlinkMacSystemFont, &#039;Segoe UI&#039;, Roboto, Oxygen-Sans, Cantarell, &#039;Helvetica Neue&#039;, sans-serif; text-align:right;">
&nbsp;Pageviews&nbsp;&nbsp;
diff --git a/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdf b/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdf
index a7165b7710..fa8a66e86f 100644
--- a/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdf
+++ b/tests/PHPUnit/System/expected/test_ecommerceOrderWithItems_scheduled_report_in_pdf_tables_only__ScheduledReports.generateReport_week.original.pdf
Binary files differ
diff --git a/tests/PHPUnit/Unit/DataAccess/LogQueryBuilder/JoinGeneratorTest.php b/tests/PHPUnit/Unit/DataAccess/LogQueryBuilder/JoinGeneratorTest.php
index 8e1ead3aec..0f75716c9c 100644
--- a/tests/PHPUnit/Unit/DataAccess/LogQueryBuilder/JoinGeneratorTest.php
+++ b/tests/PHPUnit/Unit/DataAccess/LogQueryBuilder/JoinGeneratorTest.php
@@ -255,6 +255,7 @@ class JoinGeneratorTest extends \PHPUnit_Framework_TestCase
usort($tables, array($generator, 'sortTablesForJoin'));
$expected = array(
+ 'log_link_visit_action',
array (
'table' => 'log_link_visit_action',
'tableAlias' => 'log_link_visit_action_foo',
@@ -265,7 +266,6 @@ class JoinGeneratorTest extends \PHPUnit_Framework_TestCase
'tableAlias' => 'log_action_foo',
'joinOn' => 'log_link_visit_action_foo.idaction_url = log_action_foo.idaction',
),
- 'log_link_visit_action',
'log_action',
);
@@ -293,6 +293,42 @@ class JoinGeneratorTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expected, $tables);
}
+ public function test_sortTablesForJoin_shouldSortTablesWithCustomJoinRequiringEachOther2()
+ {
+ $tables = array(
+ array(
+ 'table' => 'log_action',
+ 'tableAlias' => 'log_action_visit_entry_idaction_name',
+ 'joinOn' => "log_visit.visit_entry_idaction_name = log_action_visit_entry_idaction_name.idaction"
+ ),
+ 'log_link_visit_action',
+ array(
+ 'table' => 'log_action',
+ 'tableAlias' => 'log_action_idaction_event_action',
+ 'joinOn' => "log_link_visit_action.idaction_event_action = log_action_idaction_event_action.idaction"
+ )
+ );
+
+ $generator = $this->makeGenerator($tables);
+ usort($tables, array($generator, 'sortTablesForJoin'));
+
+ $expected = array(
+ array(
+ 'table' => 'log_action',
+ 'tableAlias' => 'log_action_visit_entry_idaction_name',
+ 'joinOn' => "log_visit.visit_entry_idaction_name = log_action_visit_entry_idaction_name.idaction"
+ ),
+ 'log_link_visit_action',
+ array(
+ 'table' => 'log_action',
+ 'tableAlias' => 'log_action_idaction_event_action',
+ 'joinOn' => "log_link_visit_action.idaction_event_action = log_action_idaction_event_action.idaction"
+ )
+ );
+
+ $this->assertEquals($expected, $tables);
+ }
+
private function generate($tables)
{
$generator = $this->makeGenerator($tables);
diff --git a/tests/PHPUnit/Unit/DataTable/Filter/AddSegmentFilterBySegmentValueTest.php b/tests/PHPUnit/Unit/DataTable/Filter/AddSegmentFilterBySegmentValueTest.php
index 050009d6df..8291db620a 100644
--- a/tests/PHPUnit/Unit/DataTable/Filter/AddSegmentFilterBySegmentValueTest.php
+++ b/tests/PHPUnit/Unit/DataTable/Filter/AddSegmentFilterBySegmentValueTest.php
@@ -80,14 +80,6 @@ class AddSegmentBySegmentValueTest extends \PHPUnit_Framework_TestCase
$this->assertSegment('city==mytest', $row);
}
- public function test_filter_shouldUseTheFirstSegment_IfAReportHasMultiple()
- {
- $report = new GetCountry();
- $this->assertCount(2, $report->getDimension()->getSegments());
-
- $this->assertSegmentForSegmentValueAndReport($report, $segmentValue = 'existing', 'countryCode==existing');
- }
-
public function test_filter_shouldNotGenerateASegment_IfReportHasNoDimension()
{
$report = new Get(); // VisitsSummary.get has no dimension
diff --git a/tests/PHPUnit/Unit/DeprecatedMethodsTest.php b/tests/PHPUnit/Unit/DeprecatedMethodsTest.php
index bea7551e1e..a0cd9905f0 100644
--- a/tests/PHPUnit/Unit/DeprecatedMethodsTest.php
+++ b/tests/PHPUnit/Unit/DeprecatedMethodsTest.php
@@ -81,6 +81,9 @@ class DeprecatedMethodsTest extends \PHPUnit_Framework_TestCase
$this->assertDeprecatedMethodIsRemovedInPiwik4('Piwik\Updates', 'update');
$this->assertDeprecatedMethodIsRemovedInPiwik4('Piwik\Updates', 'getMigrationQueries');
$this->assertDeprecatedMethodIsRemovedInPiwik4('Piwik\Updater', 'executeMigrationQueries');
+
+ // THIS IS A REMINDER FOR PIWIK 4: We need to rename getColumnType() to getDbColumnType() and $columnType to $dbColumnType
+ $this->assertDeprecatedMethodIsRemovedInPiwik4('Piwik\Columns\Dimension', 'getType');
}
diff --git a/tests/UI/expected-screenshots/Morpheus_load.png b/tests/UI/expected-screenshots/Morpheus_load.png
index b482c0dd35..45fa067941 100644
--- a/tests/UI/expected-screenshots/Morpheus_load.png
+++ b/tests/UI/expected-screenshots/Morpheus_load.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:cbd2ad65a2e01659e930b8fbde4c9ed44cbd132cdf4be1b774c6d5c6bb85d7e0
-size 1196543
+oid sha256:53d9c858e3c6852165b334a0dae64c7b018432179daf374045a8cd1af3171e57
+size 1229610
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_2_segment_editor_update.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_2_segment_editor_update.png
index e99caa0e59..03e0c56f48 100644
--- a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_2_segment_editor_update.png
+++ b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_2_segment_editor_update.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:3a1cd08f745e680fa7e167a198ed5fef4e069791821936c6f340051b222a84c0
-size 48560
+oid sha256:d8c64dbf0967a53830724553e2ab427d0189c937a5004cef1b1a0b8b6d258b72
+size 39998
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_3_segment_editor_edit_name.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_3_segment_editor_edit_name.png
index c7526f6363..f1a25d50cd 100644
--- a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_3_segment_editor_edit_name.png
+++ b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_3_segment_editor_edit_name.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:af83134ac7d91edfa60300eaad7b709304faafe868656bcf17ae4cd6dd7787a1
-size 47393
+oid sha256:7a39e2534b7ace4d8b76f1d6b34443244a34456ce28ddec73bc157bc49b4fea9
+size 38875
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_4_segment_editor_expanded_dimensions.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_4_segment_editor_expanded_dimensions.png
deleted file mode 100644
index 525a904f9e..0000000000
--- a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_4_segment_editor_expanded_dimensions.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:4f4f99126835ee863ea6eb295eceb633abe8fbbee8c6ea3414484811b832da84
-size 52667
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_5_segment_editor_search_dimensions.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_5_segment_editor_search_dimensions.png
deleted file mode 100644
index bb9daa3135..0000000000
--- a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_5_segment_editor_search_dimensions.png
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:e8098f472b93da640ebfac6ce43641f8eea3d230ca42ddba359a93089fdfdcd8
-size 43896
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_6_segment_editor_different.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_6_segment_editor_different.png
index 466bf58cd5..86efd4de02 100644
--- a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_6_segment_editor_different.png
+++ b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_6_segment_editor_different.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a863803d25a5a883293faeaed5b13bd27e838a13bd0539f182c266ad7cc16c61
-size 38469
+oid sha256:c102d179de46e56a6ef40bf49cba2099f956bc948c4003b10e30b21e48062737
+size 33460
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_6_segment_editor_droplist.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_6_segment_editor_droplist.png
index 617aa2636d..5fe2bf713d 100644
--- a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_6_segment_editor_droplist.png
+++ b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_6_segment_editor_droplist.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:8c02271897f5e217b9ad31390734db144f2c9c044aeff007523e4324682faa6e
-size 47865
+oid sha256:470e13de83fa2ca37b84c721b8fbc4f6e1dc7cd793885c8fb91c0bc3ae01f7e2
+size 42896
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_8_segment_editor_create.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_8_segment_editor_create.png
index 466bf58cd5..86efd4de02 100644
--- a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_8_segment_editor_create.png
+++ b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_8_segment_editor_create.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a863803d25a5a883293faeaed5b13bd27e838a13bd0539f182c266ad7cc16c61
-size 38469
+oid sha256:c102d179de46e56a6ef40bf49cba2099f956bc948c4003b10e30b21e48062737
+size 33460
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_add_new_and_condition.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_add_new_and_condition.png
new file mode 100644
index 0000000000..43014a624c
--- /dev/null
+++ b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_add_new_and_condition.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:39c5f4d12eefe2ea6c81be275b030bd4eb4b57a869c5ced24df910f64cbe1237
+size 47076
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_add_new_or_condition.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_add_new_or_condition.png
new file mode 100644
index 0000000000..f507fd3c3b
--- /dev/null
+++ b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_add_new_or_condition.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5ca1e4765b7ac5950b4bbed18b56fdb18b17d198ff8d25a1e1fd8ca970907566
+size 37838
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted_dialog.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted_dialog.png
index a9f7b5e861..4d780d87b8 100644
--- a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted_dialog.png
+++ b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_deleted_dialog.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:9655da5134da6faa4fe83d724f3082ef1e561a18984fb22e31f7ef80e061f5c2
-size 8368
+oid sha256:396ae1c9489599e69ecf8e74eef8efc95de9e2515c9e3c402f28048532979855
+size 8373
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_dimension_drag_drop.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_dimension_drag_drop.png
index 92f6acedd6..eceb62dc8b 100644
--- a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_dimension_drag_drop.png
+++ b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_dimension_drag_drop.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:f70824336d7d9c0a1c8a14a7403b6db3f58da8d43a0c27bc6b29bc81e443eb8f
-size 46992
+oid sha256:9b31258b6107c788956ea9045bd5bdf7038c7fd63e6ddcd8213413951119dc23
+size 32387
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_drag_and_condition.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_drag_and_condition.png
index 75b8683897..9b68cd9264 100644
--- a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_drag_and_condition.png
+++ b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_drag_and_condition.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:1d2128e676dc819478dc335cf3bf0dba57d319253c63db395b1c31ed9a17469b
-size 72886
+oid sha256:acb6e8b5191754511eeac473d5adfd5e213e787aa56dee3eb5a3f74ae009f56e
+size 46134
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_drag_or_condition.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_drag_or_condition.png
index 72bf4942cb..49f31ddc47 100644
--- a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_drag_or_condition.png
+++ b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_drag_or_condition.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:cafa8d1220332631af64551a1fedf0c53465143fce357dd52e380ee59be07b97
-size 55733
+oid sha256:fe4a958c3ed1ad930dd60790afe06543f266e2af99f4f12178656c42e0def8ca
+size 36872
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved_details.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved_details.png
index 405309fdbd..a917b4738a 100644
--- a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved_details.png
+++ b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_saved_details.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:ca5729bb552eb7b66a2560350440cc011fc5a8cf6deff2b40d29cafb64720058
-size 59151
+oid sha256:62fd830e3e554776558cc08c7a36027e6f59e1b77372bf78b4228b5b700046d9
+size 48396
diff --git a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated_details.png b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated_details.png
index 239a6fda47..9b21f52b8d 100644
--- a/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated_details.png
+++ b/tests/UI/expected-screenshots/SegmentSelectorEditorTest_updated_details.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:a42b505a2246f54f55189c2ca8e78e44fb027accd0dd7b7b9dbc044d183c931b
-size 61411
+oid sha256:c09755847bb89564af7534d9a08b1b2f9a88f736ab13263d355497b87b62e814
+size 51114
diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_actions_page_titles.png b/tests/UI/expected-screenshots/UIIntegrationTest_actions_page_titles.png
index 15e1c3d174..9d651f8d6d 100644
--- a/tests/UI/expected-screenshots/UIIntegrationTest_actions_page_titles.png
+++ b/tests/UI/expected-screenshots/UIIntegrationTest_actions_page_titles.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:657f8c37a1d08051373d42a40dd1d7c9e167f44d370269eaf8e1cc0fe1b9bcf3
-size 395358
+oid sha256:d051d154ebaf07bf92478ed155ee8017569d2b2fa781fbc5dfdd7a75202f256b
+size 395061
diff --git a/tests/UI/expected-screenshots/UIIntegrationTest_dashboard4.png b/tests/UI/expected-screenshots/UIIntegrationTest_dashboard4.png
index 9207e1c17b..59c5ac6f18 100644
--- a/tests/UI/expected-screenshots/UIIntegrationTest_dashboard4.png
+++ b/tests/UI/expected-screenshots/UIIntegrationTest_dashboard4.png
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
-oid sha256:dac7e17fbe0d1ccb02f8a1626b819dccd0328915f1a0c59add788fb163dd80f8
-size 290854
+oid sha256:a73c3c4275010a12a735d7f28a6c04189323f62bd2458f38c61e7cefe4838c17
+size 291192
diff --git a/tests/UI/specs/SegmentSelectorEditor_spec.js b/tests/UI/specs/SegmentSelectorEditor_spec.js
index d144a9be54..ef0213d6f6 100644
--- a/tests/UI/specs/SegmentSelectorEditor_spec.js
+++ b/tests/UI/specs/SegmentSelectorEditor_spec.js
@@ -15,6 +15,27 @@ describe("SegmentSelectorEditorTest", function () {
var generalParams = 'idSite=1&period=year&date=2012-08-09';
var url = '?module=CoreHome&action=index&' + generalParams + '#?' + generalParams + '&category=General_Actions&subcategory=General_Pages';
+ function selectFieldValue(page, fieldName, textToSelect)
+ {
+ page.execCallback(function () {
+ page.webpage.evaluate(function(fieldName) {
+ $(fieldName + ' input.select-dropdown').click();
+ }, fieldName);
+ });
+ page.execCallback(function () {
+ page.webpage.evaluate(function(fieldName, textToSelect) {
+ $(fieldName + ' .dropdown-content.active li:contains("' + textToSelect + '"):first').click();
+ }, fieldName, textToSelect);
+ });
+ }
+
+ function selectDimension(page, prefixSelector, category, name)
+ {
+ page.click(prefixSelector + ' .metricListBlock .select-wrapper');
+ page.click(prefixSelector + ' .metricListBlock .expandableList h4:contains(' + category + ')');
+ page.click(prefixSelector + ' .metricListBlock .expandableList .secondLevel li:contains(' + name + ')');
+ }
+
it("should load correctly", function (done) {
expect.screenshot("0_initial").to.be.captureSelector(selectorsToCapture, function (page) {
page.load(url);
@@ -41,18 +62,6 @@ describe("SegmentSelectorEditorTest", function () {
}, done);
});
- it("should expand segment dimension category when category name clicked in segment editor", function (done) {
- expect.screenshot("4_segment_editor_expanded_dimensions").to.be.captureSelector(selectorsToCapture, function (page) {
- page.click('.segmentEditorPanel .metric_category:contains(Actions)');
- }, done);
- });
-
- it("should search segment dimensions when text entered in dimension search input", function (done) {
- expect.screenshot("5_segment_editor_search_dimensions").to.be.captureSelector(selectorsToCapture, function (page) {
- page.sendKeys('.segmentEditorPanel .segmentSearch', 'page title');
- }, done);
- });
-
it("should show the egment editor's available segments dropdown", function (done) {
expect.screenshot("6_segment_editor_droplist").to.be.captureSelector(selectorsToCapture, function (page) {
page.mouseMove('.available_segments a.dropList');
@@ -81,10 +90,9 @@ describe("SegmentSelectorEditorTest", function () {
}, done);
});
- it("should add new segment expression when segment dimension drag dropped", function (done) {
+ it("should update segment expression when selecting different segment", function (done) {
expect.screenshot("dimension_drag_drop").to.be.captureSelector(selectorsToCapture, function (page) {
- page.click('.segmentEditorPanel .metric_category:contains(Actions)');
- page.dragDrop('.segmentEditorPanel li[data-metric=actionUrl]', '.segmentEditorPanel .ui-droppable');
+ selectDimension(page, '.segmentRow0', 'Actions', 'Action URL');
}, done);
});
@@ -95,32 +103,40 @@ describe("SegmentSelectorEditorTest", function () {
}, done);
});
- it("should add an OR condition when a segment dimension is dragged to the OR placeholder section", function (done) {
+ it("should add an OR condition when clicking on add OR", function (done) {
+ expect.screenshot("add_new_or_condition").to.be.captureSelector(selectorsToCapture, function (page) {
+ page.click('.segmentEditorPanel .segment-add-or');
+ }, done);
+ });
+
+ it("should add an OR condition when a segment dimension is selected in the OR placeholder section", function (done) {
expect.screenshot("drag_or_condition").to.be.captureSelector(selectorsToCapture, function (page) {
- page.dragDrop('.segmentEditorPanel li[data-metric=outlinkUrl]', '.segmentEditorPanel .segment-add-or .ui-droppable');
+ selectDimension(page, '.segmentRow0 .segment-row:last', 'Actions', 'Clicked URL');
+ }, done);
+ });
+
+ it("should add an AND condition when clicking on add AND", function (done) {
+ expect.screenshot("add_new_and_condition").to.be.captureSelector(selectorsToCapture, function (page) {
+ page.click('.segmentEditorPanel .segment-add-row');
}, done);
});
it("should add an AND condition when a segment dimension is dragged to the AND placeholder section", function (done) {
expect.screenshot("drag_and_condition").to.be.captureSelector(selectorsToCapture, function (page) {
- page.dragDrop('.segmentEditorPanel li[data-metric=outlinkUrl]', '.segmentEditorPanel .segment-add-row .ui-droppable');
+ selectDimension(page, '.segmentRow1', 'Actions', 'Clicked URL');
}, done);
});
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", function (done) {
expect.screenshot("saved").to.be.captureSelector(selectorsToCapture, function (page) {
page.evaluate(function () {
- $('.metricMatchBlock>select').each(function () {
- $(this).val('==');
- });
-
- $('.metricValueBlock>input').each(function (index) {
- $(this).val('value ' + index);
+ $('.metricValueBlock input').each(function (index) {
+ $(this).val('value ' + index).change();
});
});
page.sendKeys('input.edit_segment_name', 'new segment');
- page.click('.segmentEditorPanel .metric_category:contains(Actions)'); // click somewhere else to save new name
+ page.click('.segmentRow0 .segment-or'); // click somewhere else to save new name
page.evaluate(function () {
$('button.saveAndApply').click();
@@ -147,18 +163,18 @@ describe("SegmentSelectorEditorTest", function () {
expect.screenshot("update_confirmation").to.be.captureSelector('.modal.open', function (page) {
page.click('.segmentEditorPanel .editSegmentName');
page.evaluate(function () {
- $('input.edit_segment_name').val('');
+ $('input.edit_segment_name').val('').change();
});
page.sendKeys('input.edit_segment_name', 'edited segment');
- page.click('.segmentEditorPanel .metric_category:contains(Actions)'); // click somewhere else to save new name
+ page.click('.segmentRow0 .segment-or:first'); // click somewhere else to save new name
- page.evaluate(function () {
- $('.metricMatchBlock>select').each(function () {
- $(this).val('!=');
- });
+ selectFieldValue(page, '.segmentRow0 .segment-row:first .metricMatchBlock', 'Is not');
+ selectFieldValue(page, '.segmentRow0 .segment-row:last .metricMatchBlock', 'Is not');
+ selectFieldValue(page, '.segmentRow1 .segment-row .metricMatchBlock', 'Is not');
- $('.metricValueBlock>input').each(function (index) {
- $(this).val('new value ' + index);
+ page.evaluate(function () {
+ $('.metricValueBlock input').each(function (index) {
+ $(this).val('new value ' + index).change();
});
});