From 9ba8f216fd7856ce5fef06bf82ecb8f8a2e7e630 Mon Sep 17 00:00:00 2001
From: Thomas Steur
Date: Fri, 3 Jul 2015 00:54:27 +0000
Subject: generate pages instead of implementing them in each controller
---
CHANGELOG.md | 23 +
.../ReportTotalsCalculator.php | 4 +-
core/API/DataTablePostProcessor.php | 3 +-
core/Application/Kernel/EnvironmentValidator.php | 1 -
core/Category/Category.php | 100 +
core/Category/CategoryList.php | 95 +
core/Category/Subcategory.php | 146 +
core/DataTable/Filter/PivotByDimension.php | 3 +-
core/Http/ControllerResolver.php | 49 +-
core/Menu/MenuMain.php | 19 -
core/Menu/MenuReporting.php | 143 -
core/Period/Day.php | 1 -
core/Period/Range.php | 1 -
core/Period/Week.php | 1 -
core/Plugin.php | 2 +-
core/Plugin/Categories.php | 71 +
core/Plugin/Controller.php | 61 +-
core/Plugin/Menu.php | 12 +-
core/Plugin/Report.php | 274 +-
core/Plugin/Reports.php | 189 +
core/Plugin/ViewDataTable.php | 18 +-
core/Plugin/Visualization.php | 7 +-
core/Plugin/Widgets.php | 233 +-
core/Report/ReportWidgetConfig.php | 92 +
core/Report/ReportWidgetFactory.php | 119 +
core/Tracker.php | 1 -
core/Updates/3.0.0-b1.php | 351 +
core/Version.php | 2 +-
core/View/ReportsByDimension.php | 129 -
core/ViewDataTable/Config.php | 4 +-
core/ViewDataTable/Factory.php | 11 +-
core/Widget/Widget.php | 37 +
core/Widget/WidgetConfig.php | 343 +
core/Widget/WidgetContainerConfig.php | 131 +
core/Widget/WidgetsList.php | 247 +
core/WidgetsList.php | 287 -
lang/en.json | 1 +
misc/others/iframeWidget_localhost.php | 31 +-
plugins/API/API.php | 162 +-
plugins/API/ProcessedReport.php | 86 +-
plugins/API/Reports/Get.php | 6 +-
plugins/API/SegmentMetadata.php | 167 +
plugins/API/WidgetMetadata.php | 282 +
plugins/API/tests/Unit/WidgetMetadataTest.php | 278 +
plugins/Actions/API.php | 3 +-
plugins/Actions/Actions.php | 1 +
.../Actions/Categories/DownloadsSubcategory.php | 19 +
.../Actions/Categories/EntryPagesSubcategory.php | 19 +
.../Actions/Categories/ExitPagesSubcategory.php | 19 +
plugins/Actions/Categories/OutlinksSubcategory.php | 19 +
.../Actions/Categories/PageTitlesSubcategory.php | 19 +
plugins/Actions/Categories/PagesSubcategory.php | 19 +
.../Actions/Categories/SiteSearchSubcategory.php | 19 +
plugins/Actions/Controller.php | 45 -
plugins/Actions/Menu.php | 29 -
plugins/Actions/Reports/Base.php | 2 +-
plugins/Actions/Reports/GetDownloads.php | 3 +-
plugins/Actions/Reports/GetEntryPageTitles.php | 12 +-
plugins/Actions/Reports/GetEntryPageUrls.php | 8 +-
plugins/Actions/Reports/GetExitPageTitles.php | 14 +-
plugins/Actions/Reports/GetExitPageUrls.php | 8 +-
plugins/Actions/Reports/GetOutlinks.php | 3 +-
plugins/Actions/Reports/GetPageTitles.php | 10 +-
.../Reports/GetPageTitlesFollowingSiteSearch.php | 6 +-
plugins/Actions/Reports/GetPageUrls.php | 11 +-
.../Reports/GetPageUrlsFollowingSiteSearch.php | 8 +-
.../Actions/Reports/GetSiteSearchCategories.php | 5 +-
plugins/Actions/Reports/GetSiteSearchKeywords.php | 2 +-
.../Reports/GetSiteSearchNoResultKeywords.php | 5 +-
plugins/Actions/Reports/SiteSearchBase.php | 2 +-
plugins/Actions/templates/indexSiteSearch.twig | 21 -
.../Contents/Categories/ContentsSubcategory.php | 19 +
plugins/Contents/Contents.php | 2 +-
plugins/Contents/Controller.php | 51 -
plugins/Contents/Menu.php | 24 -
plugins/Contents/Reports/Base.php | 13 +-
plugins/Contents/Reports/GetContentNames.php | 1 -
plugins/Contents/Reports/GetContentPieces.php | 2 -
plugins/Contents/Widgets/ContentsByDimension.php | 21 +
plugins/Contents/tests/System/expected/.gitkeep | 0
...tentNames_lastN__API.getProcessedReport_day.xml | 1 +
...entPieces_lastN__API.getProcessedReport_day.xml | 1 +
plugins/CoreConsole/Commands/GenerateReport.php | 24 +-
plugins/CoreConsole/Commands/GenerateWidget.php | 76 +-
plugins/CoreHome/Categories/ActionsCategory.php | 17 +
plugins/CoreHome/Categories/DevicesSubcategory.php | 19 +
.../CoreHome/Categories/EngagementSubcategory.php | 19 +
.../CoreHome/Categories/SoftwareSubcategory.php | 19 +
plugins/CoreHome/Categories/VisitorsCategory.php | 17 +
.../Categories/VisitorsOverviewSubcategory.php | 19 +
.../CoreHome/Columns/Metrics/AverageTimeOnSite.php | 2 +-
plugins/CoreHome/Controller.php | 35 +-
plugins/CoreHome/CoreHome.php | 32 +-
plugins/CoreHome/Widgets.php | 63 -
plugins/CoreHome/Widgets/GetDonateForm.php | 48 +
plugins/CoreHome/Widgets/GetPromoVideo.php | 44 +
.../activityindicator.directive.js | 31 +
.../activity-indicator/activityindicator.html | 3 +
.../angularjs/ajax-form/ajax-form.directive.js | 4 +-
.../common/directives/focus-anywhere-but-here.js | 8 +-
.../CoreHome/angularjs/common/filters/escape.js | 16 +
.../angularjs/common/services/global-ajax-queue.js | 14 +
.../angularjs/common/services/piwik-url.js | 54 +
.../common/services/report-metadata-model.js | 52 +
.../common/services/reporting-pages-model.js | 58 +
.../CoreHome/angularjs/history/history.service.js | 21 +-
plugins/CoreHome/angularjs/http404check.js | 6 +-
.../menudropdown/menudropdown.directive.html | 2 +-
.../menudropdown/menudropdown.directive.js | 13 +-
.../popover-handler/popover-handler.directive.js | 74 +
.../reporting-menu/reportingmenu-model.js | 153 +
.../reporting-menu/reportingmenu.controller.js | 125 +
.../reporting-menu/reportingmenu.directive.html | 40 +
.../reporting-menu/reportingmenu.directive.js | 31 +
.../reporting-page/reportingpage-model.js | 196 +
.../reporting-page/reportingpage.controller.js | 56 +
.../reporting-page/reportingpage.directive.html | 25 +
.../reporting-page/reportingpage.directive.js | 31 +
.../widget-bydimension-container.directive.html | 25 +
.../widget-bydimension-container.directive.js | 67 +
.../widgetcontainer.directive.html | 10 +
.../widget-container/widgetcontainer.directive.js | 32 +
.../widget-loader/widgetloader.directive.html | 13 +
.../widget-loader/widgetloader.directive.js | 134 +
.../angularjs/widget/widget.directive.html | 23 +
.../CoreHome/angularjs/widget/widget.directive.js | 93 +
plugins/CoreHome/javascripts/broadcast.js | 67 +-
plugins/CoreHome/javascripts/corehome.js | 54 -
plugins/CoreHome/javascripts/dataTable.js | 1 +
plugins/CoreHome/javascripts/menu.js | 114 -
plugins/CoreHome/javascripts/menu_init.js | 19 -
plugins/CoreHome/javascripts/sparkline.js | 28 +-
plugins/CoreHome/lang/en.json | 3 +-
plugins/CoreHome/stylesheets/coreHome.less | 6 +
plugins/CoreHome/stylesheets/zen-mode.less | 16 +-
.../ReportsByDimension/_reportsByDimension.twig | 29 -
plugins/CoreHome/templates/_indexContent.twig | 3 +
.../CoreHome/templates/getDefaultIndexView.twig | 5 +-
plugins/CoreHome/templates/widgetContainer.twig | 18 +
.../JqplotDataGenerator/Evolution.php | 41 +-
.../Visualizations/Sparkline.php | 4 +-
.../Visualizations/Sparklines.php | 147 +
.../Visualizations/Sparklines/Config.php | 354 +
.../javascripts/jqplotEvolutionGraph.js | 35 +-
.../templates/_dataTableViz_sparklines.twig | 31 +
plugins/CoreVisualizations/templates/macros.twig | 32 +
.../tests/Integration/SparklinesConfigTest.php | 128 +
.../tests/Unit/SparklinesConfigTest.php | 130 +
.../Categories/CustomVariablesSubcategory.php | 19 +
plugins/CustomVariables/Reports/Base.php | 2 +-
.../CustomVariables/Reports/GetCustomVariables.php | 4 +-
plugins/DBStats/Reports/GetMetricDataSummary.php | 3 +-
.../DBStats/Reports/GetMetricDataSummaryByYear.php | 3 +-
plugins/DBStats/Reports/GetReportDataSummary.php | 3 +-
.../DBStats/Reports/GetReportDataSummaryByYear.php | 3 +-
plugins/Dashboard/API.php | 54 +-
plugins/Dashboard/Categories/DashboardCategory.php | 17 +
plugins/Dashboard/Controller.php | 17 +-
plugins/Dashboard/Dashboard.php | 64 +-
plugins/Dashboard/Menu.php | 19 -
plugins/Dashboard/Model.php | 8 +-
.../angularjs/common/services/dashboards-model.js | 68 +
.../angularjs/dashboard/dashboard.directive.js | 103 +
plugins/Dashboard/javascripts/dashboard.js | 106 +-
plugins/Dashboard/javascripts/dashboardObject.js | 143 +-
plugins/Dashboard/javascripts/dashboardWidget.js | 9 +-
plugins/Dashboard/javascripts/widgetMenu.js | 102 +-
plugins/Dashboard/templates/embeddedIndex.twig | 8 +-
plugins/DevicePlugins/Reports/Base.php | 2 +-
plugins/DevicePlugins/Reports/GetPlugin.php | 5 +-
plugins/DevicesDetection/Controller.php | 41 -
plugins/DevicesDetection/Menu.php | 7 -
plugins/DevicesDetection/Reports/Base.php | 2 +-
plugins/DevicesDetection/Reports/GetBrand.php | 5 +-
.../DevicesDetection/Reports/GetBrowserEngines.php | 5 +-
.../Reports/GetBrowserVersions.php | 8 +-
plugins/DevicesDetection/Reports/GetBrowsers.php | 10 +-
plugins/DevicesDetection/Reports/GetModel.php | 3 +-
plugins/DevicesDetection/Reports/GetOsFamilies.php | 8 +-
plugins/DevicesDetection/Reports/GetOsVersions.php | 8 +-
plugins/DevicesDetection/Reports/GetType.php | 3 +-
plugins/DevicesDetection/templates/devices.twig | 19 -
plugins/DevicesDetection/templates/software.twig | 23 -
plugins/Ecommerce/Categories/EcommerceCategory.php | 17 +
.../Categories/EcommerceLogSubcategory.php | 19 +
.../Categories/EcommerceOverviewSubcategory.php | 19 +
.../Ecommerce/Categories/ProductSubcategory.php | 19 +
plugins/Ecommerce/Categories/SalesSubcategory.php | 19 +
plugins/Ecommerce/Controller.php | 117 +-
plugins/Ecommerce/Menu.php | 41 -
plugins/Ecommerce/Reports/Base.php | 5 +-
plugins/Ecommerce/Reports/BaseItem.php | 45 +-
plugins/Ecommerce/Reports/GetItemsCategory.php | 3 +-
plugins/Ecommerce/Reports/GetItemsName.php | 3 +-
plugins/Ecommerce/Reports/GetItemsSku.php | 3 +-
plugins/Ecommerce/Widgets.php | 35 -
plugins/Ecommerce/Widgets/GetEcommerceLog.php | 28 +
plugins/Ecommerce/Widgets/ProductsByDimension.php | 34 +
.../Ecommerce/templates/conversionOverview.twig | 15 +
plugins/Ecommerce/templates/ecommerceLog.twig | 3 -
plugins/Ecommerce/templates/getSparklines.twig | 55 +
plugins/Events/Categories/EventsSubcategory.php | 19 +
plugins/Events/Controller.php | 104 -
plugins/Events/Events.php | 3 +-
plugins/Events/Menu.php | 21 -
plugins/Events/Reports/Base.php | 20 +-
plugins/Events/Reports/GetAction.php | 1 -
plugins/Events/Reports/GetActionFromCategoryId.php | 2 +-
plugins/Events/Reports/GetActionFromNameId.php | 2 +-
plugins/Events/Reports/GetCategory.php | 1 -
plugins/Events/Reports/GetCategoryFromActionId.php | 2 +-
plugins/Events/Reports/GetCategoryFromNameId.php | 2 +-
plugins/Events/Reports/GetName.php | 1 -
plugins/Events/Reports/GetNameFromActionId.php | 2 +-
plugins/Events/Reports/GetNameFromCategoryId.php | 2 +-
plugins/Events/Widgets/EventsByDimension.php | 22 +
plugins/Events/templates/index.twig | 2 -
plugins/ExamplePlugin/Menu.php | 13 +-
plugins/ExamplePlugin/Widgets.php | 67 -
plugins/ExamplePlugin/Widgets/MyExampleWidget.php | 80 +
plugins/ExampleReport/Reports/Base.php | 2 +-
plugins/ExampleReport/Reports/GetExampleReport.php | 8 +-
plugins/ExampleRssWidget/Widgets.php | 63 -
plugins/ExampleRssWidget/Widgets/RssChangelog.php | 49 +
plugins/ExampleRssWidget/Widgets/RssPiwik.php | 47 +
plugins/ExampleUI/API.php | 1 +
plugins/ExampleUI/Categories/ExampleUiCategory.php | 17 +
plugins/ExampleUI/Controller.php | 154 -
plugins/ExampleUI/Menu.php | 25 -
plugins/ExampleUI/Reports/Base.php | 19 +
plugins/ExampleUI/Reports/GetPlanetRatios.php | 74 +
.../ExampleUI/Reports/GetPlanetRatiosWithLogos.php | 44 +
plugins/ExampleUI/Reports/GetTemperatures.php | 93 +
.../ExampleUI/Reports/GetTemperaturesEvolution.php | 95 +
plugins/ExampleUI/lang/en.json | 8 +
plugins/ExampleUI/plugin.json | 17 +-
plugins/Goals/API.php | 3 +-
.../Goals/Categories/AddANewGoalSubcategory.php | 19 +
plugins/Goals/Categories/GoalsCategory.php | 17 +
.../Goals/Categories/GoalsOverviewSubcategory.php | 19 +
.../Goals/Categories/ManageGoalsSubcategory.php | 19 +
plugins/Goals/Controller.php | 251 +-
plugins/Goals/Conversions.php | 45 +
plugins/Goals/Goals.php | 58 +-
plugins/Goals/Menu.php | 43 -
plugins/Goals/Pages.php | 339 +
plugins/Goals/Reports/Base.php | 2 +-
plugins/Goals/Reports/Get.php | 111 +
plugins/Goals/Widgets.php | 39 -
plugins/Goals/Widgets/AddNewGoal.php | 38 +
plugins/Goals/Widgets/EditGoals.php | 37 +
plugins/Goals/lang/en.json | 5 +-
.../Goals/templates/_titleAndEvolutionGraph.twig | 86 -
plugins/Goals/templates/addNewGoal.twig | 1 -
plugins/Goals/templates/conversionOverview.twig | 15 +
plugins/Goals/templates/editGoals.twig | 2 -
plugins/Goals/templates/getGoalReportView.twig | 66 -
plugins/Goals/templates/getOverviewView.twig | 58 -
plugins/Insights/Widgets.php | 20 -
plugins/Insights/Widgets/GetInsightsOverview.php | 20 +
.../Widgets/GetOverallMoversAndShakers.php | 20 +
plugins/Live/Categories/LiveCategory.php | 17 +
plugins/Live/Categories/VisitorLogSubcategory.php | 19 +
plugins/Live/Reports/Base.php | 2 +-
plugins/Live/Reports/GetLastVisitsDetails.php | 24 +-
plugins/Live/Reports/GetSimpleLastVisitCount.php | 9 +-
plugins/Live/Widgets.php | 27 -
plugins/Live/Widgets/GetVisitorProfilePopup.php | 33 +
plugins/Live/Widgets/Widget.php | 21 +
plugins/Live/templates/index.twig | 2 +-
plugins/Morpheus/javascripts/piwikHelper.js | 8 +-
plugins/Morpheus/stylesheets/ui/_map.less | 4 +
plugins/Morpheus/templates/layout.twig | 2 +
.../MultiSites/Categories/MultiSitesCategory.php | 17 +
plugins/MultiSites/Reports/Base.php | 2 +-
plugins/Provider/Reports/GetProvider.php | 13 +-
.../Categories/AllReferrersSubcategory.php | 19 +
.../Referrers/Categories/CampaignsSubcategory.php | 19 +
plugins/Referrers/Categories/ReferrersCategory.php | 17 +
.../Categories/ReferrersOverviewSubcategory.php | 19 +
.../Categories/SearchEnginesSubcategory.php | 19 +
.../Referrers/Categories/WebsitesSubcategory.php | 19 +
plugins/Referrers/Controller.php | 192 +-
plugins/Referrers/Menu.php | 23 -
plugins/Referrers/Reports/Base.php | 2 +-
plugins/Referrers/Reports/GetAll.php | 12 +-
plugins/Referrers/Reports/GetCampaigns.php | 4 +-
plugins/Referrers/Reports/GetKeywords.php | 4 +-
plugins/Referrers/Reports/GetReferrerType.php | 36 +-
plugins/Referrers/Reports/GetSearchEngines.php | 3 +-
plugins/Referrers/Reports/GetSocials.php | 11 +-
plugins/Referrers/Reports/GetWebsites.php | 5 +-
plugins/Referrers/Widgets.php | 24 -
plugins/Referrers/Widgets/GetKeywordsForPage.php | 23 +
plugins/Referrers/templates/allReferrers.twig | 11 -
.../templates/getSearchEnginesAndKeywords.twig | 13 -
plugins/Referrers/templates/index.twig | 89 -
plugins/Referrers/templates/indexWebsites.twig | 13 -
plugins/Resolution/Reports/Base.php | 2 +-
plugins/Resolution/Reports/GetConfiguration.php | 8 +-
plugins/Resolution/Reports/GetResolution.php | 8 +-
plugins/SEO/Widgets.php | 55 -
plugins/SEO/Widgets/GetRank.php | 56 +
plugins/Transitions/Controller.php | 6 +-
plugins/Transitions/lang/en.json | 3 +
.../Categories/LocationsSubcategory.php | 19 +
plugins/UserCountry/Controller.php | 9 +-
plugins/UserCountry/Menu.php | 6 -
plugins/UserCountry/Reports/Base.php | 2 +-
plugins/UserCountry/Reports/GetCity.php | 5 +-
plugins/UserCountry/Reports/GetContinent.php | 17 +-
plugins/UserCountry/Reports/GetCountry.php | 3 +-
plugins/UserCountry/Reports/GetRegion.php | 4 +-
.../templates/getDistinctCountries.twig | 5 +
plugins/UserCountry/templates/index.twig | 29 -
.../Categories/RealTimeMapSubcategory.php | 19 +
plugins/UserCountryMap/Menu.php | 24 -
plugins/UserCountryMap/UserCountryMap.php | 17 +-
plugins/UserCountryMap/Widgets/GetRealtimeMap.php | 29 +
plugins/UserCountryMap/Widgets/GetVisitorMap.php | 23 +
plugins/UserCountryMap/templates/visitorMap.twig | 2 +-
plugins/UserLanguage/Reports/Base.php | 3 +-
plugins/UserLanguage/Reports/GetLanguage.php | 6 +-
plugins/UserLanguage/Reports/GetLanguageCode.php | 4 +-
plugins/VisitFrequency/Controller.php | 63 +-
plugins/VisitFrequency/Menu.php | 19 -
plugins/VisitFrequency/Reports/Get.php | 62 +-
plugins/VisitFrequency/Widgets.php | 23 -
plugins/VisitFrequency/lang/en.json | 10 +-
plugins/VisitFrequency/templates/_sparklines.twig | 39 -
.../VisitFrequency/templates/getSparklines.twig | 1 -
plugins/VisitFrequency/templates/index.twig | 9 -
plugins/VisitTime/Categories/TimesSubcategory.php | 19 +
plugins/VisitTime/Controller.php | 25 -
plugins/VisitTime/Menu.php | 19 -
plugins/VisitTime/Reports/Base.php | 2 +-
plugins/VisitTime/Reports/GetByDayOfWeek.php | 10 +-
.../Reports/GetVisitInformationPerLocalTime.php | 18 +-
.../Reports/GetVisitInformationPerServerTime.php | 7 +-
plugins/VisitTime/templates/index.twig | 13 -
plugins/VisitorInterest/Controller.php | 24 -
plugins/VisitorInterest/Menu.php | 20 -
plugins/VisitorInterest/Reports/Base.php | 3 +-
.../Reports/GetNumberOfVisitsByDaysSinceLast.php | 11 +-
.../Reports/GetNumberOfVisitsByVisitCount.php | 1 -
.../Reports/GetNumberOfVisitsPerPage.php | 10 +-
.../Reports/GetNumberOfVisitsPerVisitDuration.php | 10 +-
plugins/VisitorInterest/templates/index.twig | 21 -
plugins/VisitsSummary/API.php | 3 +-
plugins/VisitsSummary/Controller.php | 134 +-
plugins/VisitsSummary/Menu.php | 20 -
plugins/VisitsSummary/Reports/Get.php | 147 +-
plugins/VisitsSummary/Widgets.php | 22 -
plugins/VisitsSummary/Widgets/Index.php | 47 +
plugins/VisitsSummary/lang/en.json | 30 +-
plugins/Widgetize/Controller.php | 4 +-
plugins/Widgetize/templates/iframe.twig | 1 +
plugins/Widgetize/templates/index.twig | 1 -
plugins/Widgetize/tests/System/WidgetTest.php | 1932 +--
plugins/ZenMode/ZenMode.php | 1 +
.../angularjs/zen-mode/zen-mode-disabler.js | 35 +
plugins/ZenMode/javascripts/zen-mode.js | 10 +-
tests/PHPUnit/Fixtures/OmniFixture.php | 13 +-
tests/PHPUnit/Fixtures/UITestFixture.php | 17 +-
.../Framework/Mock/Category/Categeories.php | 63 +
.../Integration/Category/CategoryListTest.php | 133 +
.../DataTable/Filter/PivotByDimensionTest.php | 2 +-
.../PHPUnit/Integration/Menu/MenuReportingTest.php | 80 -
.../PHPUnit/Integration/Plugin/CategoriesTest.php | 89 +
tests/PHPUnit/Integration/Plugin/WidgetsTest.php | 81 +
tests/PHPUnit/Integration/Report/ReportsTest.php | 72 +
tests/PHPUnit/Integration/ReportTest.php | 148 +-
tests/PHPUnit/Integration/WidgetsListTest.php | 147 +-
tests/PHPUnit/System/ApiGetReportMetadataTest.php | 1 +
tests/PHPUnit/System/AutoSuggestAPITest.php | 2 +
....getAction_flat__API.getProcessedReport_day.xml | 3 +-
...getAction_lastN__API.getProcessedReport_day.xml | 3 +-
...etCategory_flat__API.getProcessedReport_day.xml | 3 +-
...tCategory_lastN__API.getProcessedReport_day.xml | 3 +-
...ts.getName_flat__API.getProcessedReport_day.xml | 3 +-
...s.getName_lastN__API.getProcessedReport_day.xml | 3 +-
...ProcessedMetric__API.getProcessedReport_day.xml | 1 +
...ldKeepEmptyRows__API.getProcessedReport_day.xml | 5 +-
...sits__subtable__API.getProcessedReport_week.xml | 1 +
...ts_hideColumns___API.getProcessedReport_day.xml | 13 +
...ocessedMetrics___API.getProcessedReport_day.xml | 5 +-
...ts_showColumns___API.getProcessedReport_day.xml | 13 +
...Columns_onlyOne__API.getProcessedReport_day.xml | 3 +-
...firstSite_lastN__API.getProcessedReport_day.xml | 10 +-
...rstSite_lastN__API.getProcessedReport_month.xml | 10 +-
...firstSite_lastN__API.getProcessedReport_day.xml | 13 +
...rstSite_lastN__API.getProcessedReport_month.xml | 13 +
...firstSite_lastN__API.getProcessedReport_day.xml | 10 +-
...rstSite_lastN__API.getProcessedReport_month.xml | 10 +-
...firstSite_lastN__API.getProcessedReport_day.xml | 1 +
...rstSite_lastN__API.getProcessedReport_month.xml | 1 +
...firstSite_lastN__API.getProcessedReport_day.xml | 3 +-
...rstSite_lastN__API.getProcessedReport_month.xml | 3 +-
...firstSite_lastN__API.getProcessedReport_day.xml | 3 +-
...rstSite_lastN__API.getProcessedReport_month.xml | 3 +-
...firstSite_lastN__API.getProcessedReport_day.xml | 3 +-
...rstSite_lastN__API.getProcessedReport_month.xml | 3 +-
...firstSite_lastN__API.getProcessedReport_day.xml | 1 +
...rstSite_lastN__API.getProcessedReport_month.xml | 1 +
...firstSite_lastN__API.getProcessedReport_day.xml | 1 +
...firstSite_lastN__API.getProcessedReport_day.xml | 13 +
...firstSite_lastN__API.getProcessedReport_day.xml | 1 +
...firstSite_lastN__API.getProcessedReport_day.xml | 1 +
...firstSite_lastN__API.getProcessedReport_day.xml | 1 +
...firstSite_lastN__API.getProcessedReport_day.xml | 1 +
...firstSite_lastN__API.getProcessedReport_day.xml | 3 +-
...eduledReports.generateReport_month.original.csv | 436 +-
...duledReports.generateReport_month.original.html | 4929 ++++----
...t_apiGetReportMetadata__API.getMetadata_day.xml | 1 +
...tReportMetadata__API.getProcessedReport_day.xml | 1 +
...etReportMetadata__API.getReportMetadata_day.xml | 2220 ++--
...tReportMetadata__API.getReportPagesMetadata.xml | 4074 ++++++
...apiGetReportMetadata__API.getWidgetMetadata.xml | 2694 ++++
...etReportMetadata__API.getWidgetMetadata_day.xml | 1987 +++
..._showRawMetrics__API.getProcessedReport_day.xml | 1 +
...tMetadata_year__API.getProcessedReport_year.xml | 1 +
...a_ItemsCategory__API.getProcessedReport_day.xml | 1 +
...tadata_ItemsSku__API.getProcessedReport_day.xml | 1 +
...onPerServerTime__API.getProcessedReport_day.xml | 5 +-
...eOrderWithItems__API.getProcessedReport_day.xml | 1 +
...heduledReports.generateReport_week.original.csv | 1 -
...eduledReports.generateReport_week.original.html | 12575 ++++++++++---------
...aAndNormalAPI__API.getProcessedReport_range.xml | 1 +
...I_pagesegment__API.getProcessedReport_range.xml | 1 +
tests/PHPUnit/Unit/Category/CategoryListTest.php | 92 +
tests/PHPUnit/Unit/Category/CategoryTest.php | 141 +
tests/PHPUnit/Unit/Category/SubcategoryTest.php | 89 +
.../PHPUnit/Unit/Report/ReportWidgetConfigTest.php | 262 +
.../Unit/Report/ReportWidgetFactoryTest.php | 112 +
tests/PHPUnit/Unit/Widget/WidgetConfigTest.php | 224 +
.../Unit/Widget/WidgetContainerConfigTest.php | 280 +
tests/PHPUnit/Unit/Widget/WidgetsListTest.php | 177 +
tests/UI/diff.png | Bin 0 -> 13648 bytes
tests/UI/specs/DashboardManager_spec.js | 6 +-
tests/UI/specs/Dashboard_spec.js | 26 +-
tests/UI/specs/Menus_spec.js | 21 +-
tests/UI/specs/Overlay_spec.js | 2 +-
tests/UI/specs/PeriodSelector_spec.js | 3 +-
tests/UI/specs/PivotByDimension_spec.js | 6 +-
tests/UI/specs/ReportExporting_spec.js | 4 +-
tests/UI/specs/SegmentSelectorEditor_spec.js | 7 +-
tests/UI/specs/Transitions_spec.js | 4 +-
tests/UI/specs/UIIntegration_spec.js | 104 +-
.../lib/screenshot-testing/support/chai-extras.js | 8 +-
.../screenshot-testing/support/page-renderer.js | 4 +-
450 files changed, 31746 insertions(+), 15336 deletions(-)
create mode 100644 core/Category/Category.php
create mode 100644 core/Category/CategoryList.php
create mode 100644 core/Category/Subcategory.php
delete mode 100644 core/Menu/MenuMain.php
delete mode 100644 core/Menu/MenuReporting.php
create mode 100644 core/Plugin/Categories.php
create mode 100644 core/Plugin/Reports.php
create mode 100644 core/Report/ReportWidgetConfig.php
create mode 100644 core/Report/ReportWidgetFactory.php
create mode 100644 core/Updates/3.0.0-b1.php
delete mode 100644 core/View/ReportsByDimension.php
create mode 100644 core/Widget/Widget.php
create mode 100644 core/Widget/WidgetConfig.php
create mode 100644 core/Widget/WidgetContainerConfig.php
create mode 100644 core/Widget/WidgetsList.php
delete mode 100644 core/WidgetsList.php
create mode 100644 plugins/API/SegmentMetadata.php
create mode 100644 plugins/API/WidgetMetadata.php
create mode 100644 plugins/API/tests/Unit/WidgetMetadataTest.php
create mode 100644 plugins/Actions/Categories/DownloadsSubcategory.php
create mode 100644 plugins/Actions/Categories/EntryPagesSubcategory.php
create mode 100644 plugins/Actions/Categories/ExitPagesSubcategory.php
create mode 100644 plugins/Actions/Categories/OutlinksSubcategory.php
create mode 100644 plugins/Actions/Categories/PageTitlesSubcategory.php
create mode 100644 plugins/Actions/Categories/PagesSubcategory.php
create mode 100644 plugins/Actions/Categories/SiteSearchSubcategory.php
delete mode 100644 plugins/Actions/Controller.php
delete mode 100644 plugins/Actions/Menu.php
delete mode 100644 plugins/Actions/templates/indexSiteSearch.twig
create mode 100644 plugins/Contents/Categories/ContentsSubcategory.php
delete mode 100644 plugins/Contents/Controller.php
delete mode 100644 plugins/Contents/Menu.php
create mode 100644 plugins/Contents/Widgets/ContentsByDimension.php
create mode 100644 plugins/Contents/tests/System/expected/.gitkeep
create mode 100644 plugins/CoreHome/Categories/ActionsCategory.php
create mode 100644 plugins/CoreHome/Categories/DevicesSubcategory.php
create mode 100644 plugins/CoreHome/Categories/EngagementSubcategory.php
create mode 100644 plugins/CoreHome/Categories/SoftwareSubcategory.php
create mode 100644 plugins/CoreHome/Categories/VisitorsCategory.php
create mode 100644 plugins/CoreHome/Categories/VisitorsOverviewSubcategory.php
delete mode 100644 plugins/CoreHome/Widgets.php
create mode 100644 plugins/CoreHome/Widgets/GetDonateForm.php
create mode 100644 plugins/CoreHome/Widgets/GetPromoVideo.php
create mode 100644 plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js
create mode 100644 plugins/CoreHome/angularjs/activity-indicator/activityindicator.html
create mode 100644 plugins/CoreHome/angularjs/common/filters/escape.js
create mode 100644 plugins/CoreHome/angularjs/common/services/global-ajax-queue.js
create mode 100644 plugins/CoreHome/angularjs/common/services/piwik-url.js
create mode 100644 plugins/CoreHome/angularjs/common/services/report-metadata-model.js
create mode 100644 plugins/CoreHome/angularjs/common/services/reporting-pages-model.js
create mode 100644 plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js
create mode 100644 plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js
create mode 100644 plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js
create mode 100644 plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html
create mode 100644 plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js
create mode 100644 plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js
create mode 100644 plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js
create mode 100644 plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html
create mode 100644 plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js
create mode 100644 plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html
create mode 100644 plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js
create mode 100644 plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html
create mode 100644 plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js
create mode 100644 plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html
create mode 100644 plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js
create mode 100644 plugins/CoreHome/angularjs/widget/widget.directive.html
create mode 100644 plugins/CoreHome/angularjs/widget/widget.directive.js
delete mode 100644 plugins/CoreHome/javascripts/menu.js
delete mode 100644 plugins/CoreHome/javascripts/menu_init.js
delete mode 100644 plugins/CoreHome/templates/ReportsByDimension/_reportsByDimension.twig
create mode 100755 plugins/CoreHome/templates/widgetContainer.twig
create mode 100644 plugins/CoreVisualizations/Visualizations/Sparklines.php
create mode 100644 plugins/CoreVisualizations/Visualizations/Sparklines/Config.php
create mode 100644 plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig
create mode 100644 plugins/CoreVisualizations/templates/macros.twig
create mode 100644 plugins/CoreVisualizations/tests/Integration/SparklinesConfigTest.php
create mode 100644 plugins/CoreVisualizations/tests/Unit/SparklinesConfigTest.php
create mode 100644 plugins/CustomVariables/Categories/CustomVariablesSubcategory.php
create mode 100644 plugins/Dashboard/Categories/DashboardCategory.php
create mode 100644 plugins/Dashboard/angularjs/common/services/dashboards-model.js
create mode 100644 plugins/Dashboard/angularjs/dashboard/dashboard.directive.js
delete mode 100644 plugins/DevicesDetection/templates/devices.twig
delete mode 100644 plugins/DevicesDetection/templates/software.twig
create mode 100644 plugins/Ecommerce/Categories/EcommerceCategory.php
create mode 100644 plugins/Ecommerce/Categories/EcommerceLogSubcategory.php
create mode 100644 plugins/Ecommerce/Categories/EcommerceOverviewSubcategory.php
create mode 100644 plugins/Ecommerce/Categories/ProductSubcategory.php
create mode 100644 plugins/Ecommerce/Categories/SalesSubcategory.php
delete mode 100644 plugins/Ecommerce/Menu.php
delete mode 100644 plugins/Ecommerce/Widgets.php
create mode 100644 plugins/Ecommerce/Widgets/GetEcommerceLog.php
create mode 100644 plugins/Ecommerce/Widgets/ProductsByDimension.php
create mode 100644 plugins/Ecommerce/templates/conversionOverview.twig
delete mode 100644 plugins/Ecommerce/templates/ecommerceLog.twig
create mode 100644 plugins/Ecommerce/templates/getSparklines.twig
create mode 100644 plugins/Events/Categories/EventsSubcategory.php
delete mode 100644 plugins/Events/Controller.php
delete mode 100644 plugins/Events/Menu.php
create mode 100644 plugins/Events/Widgets/EventsByDimension.php
delete mode 100644 plugins/Events/templates/index.twig
delete mode 100644 plugins/ExamplePlugin/Widgets.php
create mode 100644 plugins/ExamplePlugin/Widgets/MyExampleWidget.php
delete mode 100644 plugins/ExampleRssWidget/Widgets.php
create mode 100644 plugins/ExampleRssWidget/Widgets/RssChangelog.php
create mode 100644 plugins/ExampleRssWidget/Widgets/RssPiwik.php
create mode 100644 plugins/ExampleUI/Categories/ExampleUiCategory.php
create mode 100644 plugins/ExampleUI/Reports/Base.php
create mode 100644 plugins/ExampleUI/Reports/GetPlanetRatios.php
create mode 100644 plugins/ExampleUI/Reports/GetPlanetRatiosWithLogos.php
create mode 100644 plugins/ExampleUI/Reports/GetTemperatures.php
create mode 100644 plugins/ExampleUI/Reports/GetTemperaturesEvolution.php
create mode 100644 plugins/ExampleUI/lang/en.json
create mode 100644 plugins/Goals/Categories/AddANewGoalSubcategory.php
create mode 100644 plugins/Goals/Categories/GoalsCategory.php
create mode 100644 plugins/Goals/Categories/GoalsOverviewSubcategory.php
create mode 100644 plugins/Goals/Categories/ManageGoalsSubcategory.php
create mode 100644 plugins/Goals/Conversions.php
create mode 100644 plugins/Goals/Pages.php
delete mode 100644 plugins/Goals/Widgets.php
create mode 100644 plugins/Goals/Widgets/AddNewGoal.php
create mode 100644 plugins/Goals/Widgets/EditGoals.php
delete mode 100644 plugins/Goals/templates/_titleAndEvolutionGraph.twig
create mode 100644 plugins/Goals/templates/conversionOverview.twig
delete mode 100644 plugins/Goals/templates/getGoalReportView.twig
delete mode 100644 plugins/Goals/templates/getOverviewView.twig
delete mode 100644 plugins/Insights/Widgets.php
create mode 100644 plugins/Insights/Widgets/GetInsightsOverview.php
create mode 100644 plugins/Insights/Widgets/GetOverallMoversAndShakers.php
create mode 100644 plugins/Live/Categories/LiveCategory.php
create mode 100644 plugins/Live/Categories/VisitorLogSubcategory.php
delete mode 100644 plugins/Live/Widgets.php
create mode 100644 plugins/Live/Widgets/GetVisitorProfilePopup.php
create mode 100644 plugins/Live/Widgets/Widget.php
create mode 100644 plugins/MultiSites/Categories/MultiSitesCategory.php
create mode 100644 plugins/Referrers/Categories/AllReferrersSubcategory.php
create mode 100644 plugins/Referrers/Categories/CampaignsSubcategory.php
create mode 100644 plugins/Referrers/Categories/ReferrersCategory.php
create mode 100644 plugins/Referrers/Categories/ReferrersOverviewSubcategory.php
create mode 100644 plugins/Referrers/Categories/SearchEnginesSubcategory.php
create mode 100644 plugins/Referrers/Categories/WebsitesSubcategory.php
delete mode 100644 plugins/Referrers/Menu.php
delete mode 100644 plugins/Referrers/Widgets.php
create mode 100644 plugins/Referrers/Widgets/GetKeywordsForPage.php
delete mode 100644 plugins/Referrers/templates/allReferrers.twig
delete mode 100644 plugins/Referrers/templates/getSearchEnginesAndKeywords.twig
delete mode 100644 plugins/Referrers/templates/index.twig
delete mode 100644 plugins/Referrers/templates/indexWebsites.twig
delete mode 100644 plugins/SEO/Widgets.php
create mode 100644 plugins/SEO/Widgets/GetRank.php
create mode 100644 plugins/UserCountry/Categories/LocationsSubcategory.php
create mode 100644 plugins/UserCountry/templates/getDistinctCountries.twig
delete mode 100644 plugins/UserCountry/templates/index.twig
create mode 100644 plugins/UserCountryMap/Categories/RealTimeMapSubcategory.php
delete mode 100644 plugins/UserCountryMap/Menu.php
create mode 100644 plugins/UserCountryMap/Widgets/GetRealtimeMap.php
create mode 100644 plugins/UserCountryMap/Widgets/GetVisitorMap.php
delete mode 100644 plugins/VisitFrequency/Menu.php
delete mode 100644 plugins/VisitFrequency/Widgets.php
delete mode 100644 plugins/VisitFrequency/templates/_sparklines.twig
delete mode 100644 plugins/VisitFrequency/templates/getSparklines.twig
delete mode 100644 plugins/VisitFrequency/templates/index.twig
create mode 100644 plugins/VisitTime/Categories/TimesSubcategory.php
delete mode 100644 plugins/VisitTime/Controller.php
delete mode 100644 plugins/VisitTime/Menu.php
delete mode 100644 plugins/VisitTime/templates/index.twig
delete mode 100644 plugins/VisitorInterest/Controller.php
delete mode 100644 plugins/VisitorInterest/Menu.php
delete mode 100644 plugins/VisitorInterest/templates/index.twig
delete mode 100644 plugins/VisitsSummary/Menu.php
delete mode 100644 plugins/VisitsSummary/Widgets.php
create mode 100644 plugins/VisitsSummary/Widgets/Index.php
create mode 100644 plugins/ZenMode/angularjs/zen-mode/zen-mode-disabler.js
create mode 100644 tests/PHPUnit/Framework/Mock/Category/Categeories.php
create mode 100644 tests/PHPUnit/Integration/Category/CategoryListTest.php
delete mode 100644 tests/PHPUnit/Integration/Menu/MenuReportingTest.php
create mode 100644 tests/PHPUnit/Integration/Plugin/CategoriesTest.php
create mode 100644 tests/PHPUnit/Integration/Plugin/WidgetsTest.php
create mode 100644 tests/PHPUnit/Integration/Report/ReportsTest.php
create mode 100644 tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getReportPagesMetadata.xml
create mode 100644 tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getWidgetMetadata.xml
create mode 100644 tests/PHPUnit/System/expected/test_apiGetReportMetadata__API.getWidgetMetadata_day.xml
create mode 100644 tests/PHPUnit/Unit/Category/CategoryListTest.php
create mode 100644 tests/PHPUnit/Unit/Category/CategoryTest.php
create mode 100644 tests/PHPUnit/Unit/Category/SubcategoryTest.php
create mode 100644 tests/PHPUnit/Unit/Report/ReportWidgetConfigTest.php
create mode 100644 tests/PHPUnit/Unit/Report/ReportWidgetFactoryTest.php
create mode 100644 tests/PHPUnit/Unit/Widget/WidgetConfigTest.php
create mode 100644 tests/PHPUnit/Unit/Widget/WidgetContainerConfigTest.php
create mode 100644 tests/PHPUnit/Unit/Widget/WidgetsListTest.php
create mode 100644 tests/UI/diff.png
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c1f7965c30..caa2b2ebc0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,29 @@ This is a changelog for Piwik platform developers. All changes for our HTTP API'
## Piwik 3.0.0
+### Breaking Changes
+* The menu classes `Piwik\Menu\MenuReporting` and `Piwik\Menu\MenuMain` have been removed
+* The class `Piwik\Plugin\Widgets` has been removed and replaced by `Piwik\Widget\Widget`. For each widget one class is needed from now on. You can generate a widget via `./console generate:widget`.
+* The class `Piwik\WidgetList` class has been moved to `Piwik\Widget\WidgetsList`.
+* The method `Piwik\Plugin\Menu::configureReportingMenu` has been removed. To add something to the reporting menu you need to create widgets
+* The method `Report::configureWidget()`, `Report::getWidgetTitle()` and `Report::configureReportingMenu()` have been removed, use the new method `Report::configureWidgets()` instead.
+* The method `Report::getCategory()` does no longer return the translated category but the translation key of the category
+* The methods `Report::factory()`, `Report::getAllReportClasses()`, `Report::getAllReports` have been moved to the `Piwik\Plugin\Reports` class.
+* The properties `Report::$widgetTitle`, `Report::$widgetParams` and `Report::$menuTitle` were removed, use the method `Report::configureWidgets()` to create widgets instead
+* In the HTTP API methods `Dashboard.getDefaultDashboard` and `Dashboard.getUserDashboards` we do no longer remove not existing widgets as it is up to the client which widgets actually exist
+* The method `Piwik\Plugin\Controller::getEvolutionHtml` has been removed without a replacement as it should be no longer needed. The evolution is generated by ViewDataTables directly
+
+### New APIs
+* Multiple widgets for one report can now be created via the `Report::configureWidgets()` method via the new classes `Piwik\Widget\ReportWidgetFactory` and `Piwik\Widget\ReportWidgetConfig`
+* There is a new property `Report::$subCategory` that let's you add a report to the reporting UI. If a page having that name does not exist yet, it will be created automatically. The newly added method `Report::getSubCategory()` let's you get this value.
+* The new classes `Piwik\Widget\Widget`, `Piwik\Widget\WidgetConfig` and `Piwik\Widget\WidgetContainerConfig` let's you create a new widget.
+* The new class `Piwik\Category\Subcategory` let you change the name and order of menu items
+* New HTTP API method `API.getWidgetMetadata` to get a list of available widgets
+* New HTTP API method `API.getReportPagesMetadata` to get a list of all available pages that exist including the widgets they include
+
+### New features
+* New "Sparklines" visualization that let's you create a widget showing multiple sparklines
+
### Library updates
* Updated AngularJS from 1.2.28 to 1.4.3
diff --git a/core/API/DataTableManipulator/ReportTotalsCalculator.php b/core/API/DataTableManipulator/ReportTotalsCalculator.php
index 83fef53a27..7906077b1e 100644
--- a/core/API/DataTableManipulator/ReportTotalsCalculator.php
+++ b/core/API/DataTableManipulator/ReportTotalsCalculator.php
@@ -13,6 +13,7 @@ use Piwik\DataTable;
use Piwik\Metrics;
use Piwik\Period;
use Piwik\Plugin\Report;
+use Piwik\Plugin\Reports;
/**
* This class is responsible for setting the metadata property 'totals' on each dataTable if the report
@@ -211,7 +212,8 @@ class ReportTotalsCalculator extends DataTableManipulator
private function findFirstLevelReport()
{
- foreach (Report::getAllReports() as $report) {
+ $reports = new Reports();
+ foreach ($reports->getAllReports() as $report) {
$actionToLoadSubtables = $report->getActionToLoadSubTables();
if ($actionToLoadSubtables == $this->apiMethod
&& $this->apiModule == $report->getModule()
diff --git a/core/API/DataTablePostProcessor.php b/core/API/DataTablePostProcessor.php
index 9a67311679..c6424ea793 100644
--- a/core/API/DataTablePostProcessor.php
+++ b/core/API/DataTablePostProcessor.php
@@ -19,6 +19,7 @@ use Piwik\DataTable\Filter\PivotByDimension;
use Piwik\Metrics\Formatter;
use Piwik\Plugin\ProcessedMetric;
use Piwik\Plugin\Report;
+use Piwik\Plugin\Reports;
/**
* Processes DataTables that should be served through Piwik's APIs. This processing handles
@@ -71,7 +72,7 @@ class DataTablePostProcessor
$this->apiMethod = $apiMethod;
$this->setRequest($request);
- $this->report = Report::factory($apiModule, $apiMethod);
+ $this->report = Reports::factory($apiModule, $apiMethod);
$this->apiInconsistencies = new Inconsistencies();
$this->setFormatter(new Formatter());
}
diff --git a/core/Application/Kernel/EnvironmentValidator.php b/core/Application/Kernel/EnvironmentValidator.php
index c8960c48eb..321be28ef1 100644
--- a/core/Application/Kernel/EnvironmentValidator.php
+++ b/core/Application/Kernel/EnvironmentValidator.php
@@ -11,7 +11,6 @@ namespace Piwik\Application\Kernel;
use Piwik\Common;
use Piwik\Piwik;
use Piwik\SettingsServer;
-use Piwik\Translate;
use Piwik\Translation\Translator;
/**
diff --git a/core/Category/Category.php b/core/Category/Category.php
new file mode 100644
index 0000000000..cda49032e0
--- /dev/null
+++ b/core/Category/Category.php
@@ -0,0 +1,100 @@
+order = (int) $order;
+ return $this;
+ }
+
+ public function getOrder()
+ {
+ return $this->order;
+ }
+
+ public function setId($id)
+ {
+ $this->id = $id;
+ return $this;
+ }
+
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ public function addSubcategory(Subcategory $subcategory)
+ {
+ $subcategoryId = $subcategory->getId();
+
+ if ($this->hasSubcategory($subcategoryId)) {
+ throw new \Exception(sprintf('Subcategory %s already exists', $subcategoryId));
+ }
+
+ $this->subcategories[$subcategoryId] = $subcategory;
+ }
+
+ public function hasSubcategory($subcategoryId)
+ {
+ return isset($this->subcategories[$subcategoryId]);
+ }
+
+ public function getSubcategory($subcategoryId)
+ {
+ if ($this->hasSubcategory($subcategoryId)) {
+ return $this->subcategories[$subcategoryId];
+ }
+ }
+
+ /**
+ * @return Subcategory[]
+ */
+ public function getSubcategories()
+ {
+ return array_values($this->subcategories);
+ }
+
+ public function hasSubCategories()
+ {
+ return !empty($this->subcategories);
+ }
+}
\ No newline at end of file
diff --git a/core/Category/CategoryList.php b/core/Category/CategoryList.php
new file mode 100644
index 0000000000..298d9039ce
--- /dev/null
+++ b/core/Category/CategoryList.php
@@ -0,0 +1,95 @@
+getId();
+
+ if ($this->hasCategory($categoryId)) {
+ throw new \Exception(sprintf('Category %s already exists', $categoryId));
+ }
+
+ $this->categories[$categoryId] = $category;
+ }
+
+ public function getCategories()
+ {
+ return $this->categories;
+ }
+
+ public function hasCategory($categoryId)
+ {
+ return isset($this->categories[$categoryId]);
+ }
+
+ /**
+ * Get the category having the given id, if possible.
+ *
+ * @param string $categoryId
+ * @return Category|null
+ */
+ public function getCategory($categoryId)
+ {
+ if ($this->hasCategory($categoryId)) {
+ return $this->categories[$categoryId];
+ }
+ }
+
+ /**
+ * @return CategoryList
+ */
+ public static function get()
+ {
+ $list = new CategoryList();
+
+ $categories = StaticContainer::get('Piwik\Plugin\Categories');
+
+ foreach ($categories->getAllCategories() as $category) {
+ $list->addCategory($category);
+ }
+
+ // move subcategories into categories
+ foreach ($categories->getAllSubcategories() as $subcategory) {
+ $categoryId = $subcategory->getCategoryId();
+
+ if (!$categoryId) {
+ continue;
+ }
+
+ if ($list->hasCategory($categoryId)) {
+ $category = $list->getCategory($categoryId);
+ } else {
+ $category = new Category();
+ $category->setId($categoryId);
+ $list->addCategory($category);
+ }
+
+ $category->addSubcategory($subcategory);
+ }
+
+ return $list;
+ }
+}
\ No newline at end of file
diff --git a/core/Category/Subcategory.php b/core/Category/Subcategory.php
new file mode 100644
index 0000000000..7b6854f95c
--- /dev/null
+++ b/core/Category/Subcategory.php
@@ -0,0 +1,146 @@
+id = $id;
+ return $this;
+ }
+
+ /**
+ * Get the id of the subcategory.
+ * @return string
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Get the specifed categoryId see {@link $categoryId}.
+ *
+ * @return string
+ */
+ public function getCategoryId()
+ {
+ return $this->categoryId;
+ }
+
+ /**
+ * Sets (overwrites) the categoryId see {@link $categoryId}.
+ *
+ * @param string $categoryId
+ * @return static
+ */
+ public function setCategoryId($categoryId)
+ {
+ $this->categoryId = $categoryId;
+ return $this;
+ }
+
+ /**
+ * Sets (overwrites) the name see {@link $name} and {@link $id}.
+ *
+ * @param string $name A translation key eg 'General_Overview'.
+ * @return static
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ return $this;
+ }
+
+ /**
+ * Get the name of the subcategory.
+ * @return string
+ */
+ public function getName()
+ {
+ if (!empty($this->name)) {
+ return $this->name;
+ }
+
+ return $this->id;
+ }
+
+ /**
+ * Sets (overwrites) the order see {@link $order}.
+ *
+ * @param int $order
+ * @return static
+ */
+ public function setOrder($order)
+ {
+ $this->order = (int) $order;
+ return $this;
+ }
+
+ /**
+ * Get the order of the subcategory.
+ * @return int
+ */
+ public function getOrder()
+ {
+ return $this->order;
+ }
+}
\ No newline at end of file
diff --git a/core/DataTable/Filter/PivotByDimension.php b/core/DataTable/Filter/PivotByDimension.php
index b7f5ca1cf2..bbc72f46d9 100644
--- a/core/DataTable/Filter/PivotByDimension.php
+++ b/core/DataTable/Filter/PivotByDimension.php
@@ -21,6 +21,7 @@ use Piwik\Period;
use Piwik\Piwik;
use Piwik\Plugin\Report;
use Piwik\Plugin\Segment;
+use Piwik\Plugin\Reports;
use Piwik\Site;
/**
@@ -328,7 +329,7 @@ class PivotByDimension extends BaseFilter
{
list($module, $method) = explode('.', $report);
- $this->thisReport = Report::factory($module, $method);
+ $this->thisReport = Reports::factory($module, $method);
if (empty($this->thisReport)) {
throw new Exception("Unable to find report '$report'.");
}
diff --git a/core/Http/ControllerResolver.php b/core/Http/ControllerResolver.php
index 569fbee49e..ee1af9acc0 100644
--- a/core/Http/ControllerResolver.php
+++ b/core/Http/ControllerResolver.php
@@ -10,8 +10,10 @@ namespace Piwik\Http;
use DI\FactoryInterface;
use Exception;
+use Piwik\Plugin;
use Piwik\Plugin\Controller;
-use Piwik\Plugin\Report;
+use Piwik\Plugin\Reports;
+use Piwik\Session;
use Piwik\Plugin\Widgets;
/**
@@ -26,9 +28,15 @@ class ControllerResolver
*/
private $abstractFactory;
- public function __construct(FactoryInterface $abstractFactory)
+ /**
+ * @var Widgets
+ */
+ private $widgets;
+
+ public function __construct(FactoryInterface $abstractFactory, Widgets $widgets)
{
$this->abstractFactory = $abstractFactory;
+ $this->widgets = $widgets;
}
/**
@@ -55,11 +63,6 @@ class ControllerResolver
return $controller;
}
- $controller = $this->createReportMenuController($module, $action, $parameters);
- if ($controller) {
- return $controller;
- }
-
throw new Exception(sprintf("Action '%s' not found in the module '%s'", $action, $module));
}
@@ -84,21 +87,20 @@ class ControllerResolver
private function createWidgetController($module, $action, array &$parameters)
{
- $widget = Widgets::factory($module, $action);
+ $widget = $this->widgets->factory($module, $action);
if (!$widget) {
- return null;
+ return;
}
$parameters['widget'] = $widget;
- $parameters['method'] = $action;
return array($this->createCoreHomeController(), 'renderWidget');
}
private function createReportController($module, $action, array &$parameters)
{
- $report = Report::factory($module, $action);
+ $report = Reports::factory($module, $action);
if (!$report) {
return null;
@@ -109,31 +111,6 @@ class ControllerResolver
return array($this->createCoreHomeController(), 'renderReportWidget');
}
- private function createReportMenuController($module, $action, array &$parameters)
- {
- if (!$this->isReportMenuAction($action)) {
- return null;
- }
-
- $action = lcfirst(substr($action, 4)); // menuGetPageUrls => getPageUrls
- $report = Report::factory($module, $action);
-
- if (!$report) {
- return null;
- }
-
- $parameters['report'] = $report;
-
- return array($this->createCoreHomeController(), 'renderReportMenu');
- }
-
- private function isReportMenuAction($action)
- {
- $startsWithMenu = (Report::PREFIX_ACTION_IN_MENU === substr($action, 0, strlen(Report::PREFIX_ACTION_IN_MENU)));
-
- return !empty($action) && $startsWithMenu;
- }
-
private function createCoreHomeController()
{
return $this->abstractFactory->make('Piwik\Plugins\CoreHome\Controller');
diff --git a/core/Menu/MenuMain.php b/core/Menu/MenuMain.php
deleted file mode 100644
index adb6b538e9..0000000000
--- a/core/Menu/MenuMain.php
+++ /dev/null
@@ -1,19 +0,0 @@
-add(
- * 'MyPlugin_MyTranslatedMenuCategory',
- * 'MyPlugin_MyTranslatedMenuName',
- * array('module' => 'MyPlugin', 'action' => 'index'),
- * Piwik::isUserHasSomeAdminAccess(),
- * $order = 2
- * );
- * }
- *
- * @api
- * @method static \Piwik\Menu\MenuReporting getInstance()
- */
-class MenuReporting extends MenuAbstract
-{
-
- /**
- * See {@link add()}. Adds a new menu item to the visitors section of the reporting menu.
- * @param string $menuName
- * @param array $url
- * @param int $order
- * @param bool|string $tooltip
- * @api
- * @since 2.5.0
- */
- public function addVisitorsItem($menuName, $url, $order = 50, $tooltip = false)
- {
- $this->addItem('General_Visitors', $menuName, $url, $order, $tooltip);
- }
-
- /**
- * See {@link add()}. Adds a new menu item to the actions section of the reporting menu.
- * @param string $menuName
- * @param array $url
- * @param int $order
- * @param bool|string $tooltip
- * @api
- * @since 2.5.0
- */
- public function addActionsItem($menuName, $url, $order = 50, $tooltip = false)
- {
- $this->addItem('General_Actions', $menuName, $url, $order, $tooltip);
- }
-
- /**
- * Should not be a public API yet. We probably have to change the API once we have another use case.
- * @ignore
- */
- public function addGroup($menuName, $defaultTitle, Group $group, $order = 50, $tooltip = false)
- {
- $this->menuEntries[] = array(
- $menuName,
- $defaultTitle,
- $group,
- $order,
- $tooltip
- );
- }
-
- /**
- * See {@link add()}. Adds a new menu item to the referrers section of the reporting menu.
- * @param string $menuName
- * @param array $url
- * @param int $order
- * @param bool|string $tooltip
- * @api
- * @since 2.5.0
- */
- public function addReferrersItem($menuName, $url, $order = 50, $tooltip = false)
- {
- $this->addItem('Referrers_Referrers', $menuName, $url, $order, $tooltip);
- }
-
- /**
- * Returns if the URL was found in the menu.
- *
- * @param string $url
- * @return boolean
- */
- public function isUrlFound($url)
- {
- $menu = $this->getMenu();
-
- foreach ($menu as $subMenus) {
- foreach ($subMenus as $subMenuName => $menuUrl) {
- if (strpos($subMenuName, '_') !== 0 && $menuUrl['_url'] == $url) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Triggers the Menu.Reporting.addItems hook and returns the menu.
- *
- * @return Array
- */
- public function getMenu()
- {
- if (!$this->menu) {
-
- /**
- * @ignore
- * @deprecated
- */
- Piwik::postEvent('Menu.Reporting.addItems', array());
-
- foreach (Report::getAllReports() as $report) {
- if ($report->isEnabled()) {
- $report->configureReportingMenu($this);
- }
- }
-
- foreach ($this->getAllMenus() as $menu) {
- $menu->configureReportingMenu($this);
- }
- }
-
- return parent::getMenu();
- }
-}
diff --git a/core/Period/Day.php b/core/Period/Day.php
index 4933ddedf6..4ce864df8a 100644
--- a/core/Period/Day.php
+++ b/core/Period/Day.php
@@ -11,7 +11,6 @@ namespace Piwik\Period;
use Exception;
use Piwik\Date;
use Piwik\Period;
-use Piwik\Piwik;
/**
*/
diff --git a/core/Period/Range.php b/core/Period/Range.php
index e2b654ca21..1dff3f56ae 100644
--- a/core/Period/Range.php
+++ b/core/Period/Range.php
@@ -14,7 +14,6 @@ use Piwik\Common;
use Piwik\Container\StaticContainer;
use Piwik\Date;
use Piwik\Period;
-use Piwik\Piwik;
/**
* Arbitrary date range representation.
diff --git a/core/Period/Week.php b/core/Period/Week.php
index ff2a23a844..d2724694dd 100644
--- a/core/Period/Week.php
+++ b/core/Period/Week.php
@@ -9,7 +9,6 @@
namespace Piwik\Period;
use Piwik\Period;
-use Piwik\Piwik;
/**
*/
diff --git a/core/Plugin.php b/core/Plugin.php
index ff938a659f..852e09522e 100644
--- a/core/Plugin.php
+++ b/core/Plugin.php
@@ -490,7 +490,7 @@ class Plugin
require_once $file;
$fileName = str_replace(array($baseDir . '/', '.php'), '', $file);
- $klassName = sprintf('Piwik\\Plugins\\%s\\%s\\%s', $this->pluginName, $directoryWithinPlugin, str_replace('/', '\\', $fileName));
+ $klassName = sprintf('Piwik\\Plugins\\%s\\%s\\%s', $this->pluginName, str_replace('/', '\\', $directoryWithinPlugin), str_replace('/', '\\', $fileName));
if (!class_exists($klassName)) {
continue;
diff --git a/core/Plugin/Categories.php b/core/Plugin/Categories.php
new file mode 100644
index 0000000000..4d0543e014
--- /dev/null
+++ b/core/Plugin/Categories.php
@@ -0,0 +1,71 @@
+pluginManager = $pluginManager;
+ }
+
+ /** @return \Piwik\Category\Category[] */
+ public function getAllCategories()
+ {
+ $categories = $this->pluginManager->findMultipleComponents('Categories', '\\Piwik\\Category\\Category');
+
+ $instances = array();
+ foreach ($categories as $category) {
+ $cat = StaticContainer::getContainer()->make($category);
+ $instances[$cat->getId()] = $cat;
+ }
+
+ return $instances;
+ }
+
+ /** @return \Piwik\Category\Subcategory[] */
+ public function getAllSubcategories()
+ {
+ $subcategories = array();
+
+ /**
+ * Triggered to add custom subcategories.
+ *
+ * **Example**
+ *
+ * public function addSubcategories(&$subcategories)
+ * {
+ * $subcategory = new Subcategory();
+ * $subcategory->setId('General_Overview');
+ * $subcategory->setCategoryId('General_Visits');
+ * $subcategory->setOrder(5);
+ * $subcategories[] = $subcategory;
+ * }
+ *
+ * @param array &$subcategories An array containing a list of subcategories.
+ */
+ Piwik::postEvent('Category.addSubcategories', array(&$subcategories));
+
+ $classes = $this->pluginManager->findMultipleComponents('Categories', '\\Piwik\\Category\\Subcategory');
+
+ foreach ($classes as $subcategory) {
+ $subcategories[] = StaticContainer::getContainer()->make($subcategory);
+ }
+
+ return $subcategories;
+ }
+}
\ No newline at end of file
diff --git a/core/Plugin/Controller.php b/core/Plugin/Controller.php
index 3aab8374fb..deeebf234d 100644
--- a/core/Plugin/Controller.php
+++ b/core/Plugin/Controller.php
@@ -33,6 +33,7 @@ use Piwik\Piwik;
use Piwik\Plugins\CoreAdminHome\CustomLogo;
use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution;
use Piwik\Plugins\LanguagesManager\LanguagesManager;
+use Piwik\Plugin\Reports;
use Piwik\SettingsPiwik;
use Piwik\Site;
use Piwik\Url;
@@ -313,7 +314,7 @@ abstract class Controller
protected function renderReport($apiAction, $controllerAction = false)
{
if (empty($controllerAction) && is_string($apiAction)) {
- $report = Report::factory($this->pluginName, $apiAction);
+ $report = Reports::factory($this->pluginName, $apiAction);
if (!empty($report)) {
$apiAction = $report;
@@ -949,64 +950,6 @@ abstract class Controller
return self::getCalendarPrettyDate(Period\Factory::build($period, Date::factory($date)));
}
- /**
- * Calculates the evolution from one value to another and returns HTML displaying
- * the evolution percent. The HTML includes an up/down arrow and is colored red, black or
- * green depending on whether the evolution is negative, 0 or positive.
- *
- * No HTML is returned if the current value and evolution percent are both 0.
- *
- * @param string $date The date of the current value.
- * @param int $currentValue The value to calculate evolution to.
- * @param string $pastDate The date of past value.
- * @param int $pastValue The value in the past to calculate evolution from.
- * @return string|false The HTML or `false` if the evolution is 0 and the current value is 0.
- * @api
- */
- protected function getEvolutionHtml($date, $currentValue, $pastDate, $pastValue)
- {
- $evolutionPercent = CalculateEvolutionFilter::calculate(
- $currentValue, $pastValue, $precision = 1);
-
- // do not display evolution if evolution percent is 0 and current value is 0
- if ($evolutionPercent == 0
- && $currentValue == 0
- ) {
- return false;
- }
-
- $titleEvolutionPercent = $evolutionPercent;
- if ($evolutionPercent < 0) {
- $class = "negative-evolution";
- $img = "arrow_down.png";
- } elseif ($evolutionPercent == 0) {
- $class = "neutral-evolution";
- $img = "stop.png";
- } else {
- $class = "positive-evolution";
- $img = "arrow_up.png";
- $titleEvolutionPercent = '+' . $titleEvolutionPercent;
- }
-
- $title = Piwik::translate('General_EvolutionSummaryGeneric', array(
- Piwik::translate('General_NVisits', $currentValue),
- $date,
- Piwik::translate('General_NVisits', $pastValue),
- $pastDate,
- $titleEvolutionPercent
- ));
-
- $result = ' ';
-
- return $result;
- }
-
protected function checkSitePermission()
{
if (!empty($this->idSite) && empty($this->site)) {
diff --git a/core/Plugin/Menu.php b/core/Plugin/Menu.php
index dd1dcaf7f3..c90e6d851f 100644
--- a/core/Plugin/Menu.php
+++ b/core/Plugin/Menu.php
@@ -11,11 +11,11 @@ namespace Piwik\Plugin;
use Piwik\Common;
use Piwik\Development;
use Piwik\Menu\MenuAdmin;
-use Piwik\Menu\MenuReporting;
use Piwik\Menu\MenuTop;
use Piwik\Menu\MenuUser;
use Piwik\Plugin\Manager as PluginManager;
use Piwik\Plugins\UsersManager\UserPreferences;
+use Piwik\Plugin\Reports;
/**
* Base class of all plugin menu providers. Plugins that define their own menu items can extend this class to easily
@@ -209,14 +209,6 @@ class Menu
);
}
- /**
- * Configures the reporting menu which should only contain links to reports of a specific site such as
- * "Search Engines", "Page Titles" or "Locations & Provider".
- */
- public function configureReportingMenu(MenuReporting $menu)
- {
- }
-
/**
* Configures the top menu which is supposed to contain analytics related items such as the
* "All Websites Dashboard".
@@ -254,7 +246,7 @@ class Menu
}
$reportAction = lcfirst(substr($action, 4));
- if (Report::factory($module, $reportAction)) {
+ if (Reports::factory($module, $reportAction)) {
return;
}
diff --git a/core/Plugin/Report.php b/core/Plugin/Report.php
index 5ab8583a5a..716bd92e31 100644
--- a/core/Plugin/Report.php
+++ b/core/Plugin/Report.php
@@ -11,19 +11,20 @@ namespace Piwik\Plugin;
use Piwik\API\Proxy;
use Piwik\API\Request;
use Piwik\Cache;
-use Piwik\CacheId;
use Piwik\Columns\Dimension;
+use Piwik\Common;
use Piwik\DataTable;
use Piwik\DataTable\Filter\Sort;
-use Piwik\Menu\MenuReporting;
use Piwik\Metrics;
use Piwik\Cache as PiwikCache;
use Piwik\Piwik;
-use Piwik\Plugin\Manager as PluginManager;
use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
-use Piwik\WidgetsList;
+use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution;
+use Piwik\Plugin\Reports;
use Piwik\ViewDataTable\Factory as ViewDataTableFactory;
use Exception;
+use Piwik\Widget\WidgetsList;
+use Piwik\Report\ReportWidgetFactory;
/**
* Defines a new report. This class contains all information a report defines except the corresponding API method which
@@ -82,30 +83,14 @@ class Report
* @var string
* @api
*/
- protected $category;
+ protected $categoryId;
/**
- * The translation key of the widget title. If a widget title is set, the platform will automatically configure/add
- * a widget for this report. Alternatively, this behavior can be overwritten in {@link configureWidget()}.
+ * The translation key of the subcategory the report belongs to.
* @var string
* @api
*/
- protected $widgetTitle;
-
- /**
- * Optional widget params that will be appended to the widget URL if a {@link $widgetTitle} is set.
- * @var array
- * @api
- */
- protected $widgetParams = array();
-
- /**
- * The translation key of the menu title. If a menu title is set, the platform will automatically add a menu item
- * to the reporting menu. Alternatively, this behavior can be overwritten in {@link configureReportingMenu()}.
- * @var string
- * @api
- */
- protected $menuTitle;
+ protected $subcategoryId;
/**
* An array of supported metrics. Eg `array('nb_visits', 'nb_actions', ...)`. Defaults to the platform default
@@ -203,25 +188,6 @@ class Report
*/
protected $defaultSortOrderDesc = true;
- /**
- * @var array
- * @ignore
- */
- public static $orderOfReports = array(
- 'General_MultiSitesSummary',
- 'VisitsSummary_VisitsSummary',
- 'Goals_Ecommerce',
- 'General_Actions',
- 'Events_Events',
- 'Actions_SubmenuSitesearch',
- 'Referrers_Referrers',
- 'Goals_Goals',
- 'General_Visitors',
- 'DevicesDetection_DevicesDetection',
- 'General_VisitorSettings',
- 'API'
- );
-
/**
* The constructur initializes the module, action and the default metrics. If you want to overwrite any of those
* values or if you want to do any work during initializing overwrite the method {@link init()}.
@@ -229,8 +195,8 @@ class Report
*/
final public function __construct()
{
- $classname = get_class($this);
- $parts = explode('\\', $classname);
+ $classname = get_class($this);
+ $parts = explode('\\', $classname);
if (5 === count($parts)) {
$this->module = $parts[2];
@@ -268,9 +234,9 @@ class Report
* containing a message that will be displayed to the user. You can overwrite this message in case you want to
* customize the error message. Eg.
* ```
- if (!$this->isEnabled()) {
- throw new Exception('Setting XYZ is not enabled or the user has not enough permission');
- }
+ * if (!$this->isEnabled()) {
+ * throw new Exception('Setting XYZ is not enabled or the user has not enough permission');
+ * }
* ```
* @throws \Exception
* @api
@@ -325,55 +291,29 @@ class Report
*/
public function render()
{
+ $viewDataTable = Common::getRequestVar('viewDataTable', false, 'string');
+ $fixed = Common::getRequestVar('forceView', 0, 'int');
+
+ $module = $this->getModule();
+ $action = $this->getAction();
+
$apiProxy = Proxy::getInstance();
- if (!$apiProxy->isExistingApiAction($this->module, $this->action)) {
- throw new Exception("Invalid action name '$this->action' for '$this->module' plugin.");
+ if (!$apiProxy->isExistingApiAction($module, $action)) {
+ throw new Exception("Invalid action name '$module' for '$action' plugin.");
}
- $apiAction = $apiProxy->buildApiActionName($this->module, $this->action);
-
- $view = ViewDataTableFactory::build(null, $apiAction, $this->module . '.' . $this->action);
+ $apiAction = $apiProxy->buildApiActionName($module, $action);
- $rendered = $view->render();
+ $view = ViewDataTableFactory::build($viewDataTable, $apiAction, $module . '.' . $action, $fixed);
- return $rendered;
+ return $view->render();
}
- /**
- * By default a widget will be configured for this report if a {@link $widgetTitle} is set. If you want to customize
- * the way the widget is added or modify any other behavior you can overwrite this method.
- * @param WidgetsList $widget
- * @api
- */
- public function configureWidget(WidgetsList $widget)
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
{
- if ($this->widgetTitle) {
- $params = array();
- if (!empty($this->widgetParams) && is_array($this->widgetParams)) {
- $params = $this->widgetParams;
- }
- $widget->add($this->category, $this->widgetTitle, $this->module, $this->action, $params);
- }
- }
-
- /**
- * By default a menu item will be added to the reporting menu if a {@link $menuTitle} is set. If you want to
- * customize the way the item is added or modify any other behavior you can overwrite this method. For instance
- * in case you need to add additional url properties beside module and action which are added by default.
- * @param \Piwik\Menu\MenuReporting $menu
- * @api
- */
- public function configureReportingMenu(MenuReporting $menu)
- {
- if ($this->menuTitle) {
- $action = $this->getMenuControllerAction();
- if ($this->isEnabled()) {
- $menu->addItem($this->category,
- $this->menuTitle,
- array('module' => $this->module, 'action' => $action),
- $this->order);
- }
+ if ($this->categoryId && $this->subcategoryId) {
+ $widgetsList->addWidgetConfig($factory->createWidget());
}
}
@@ -549,11 +489,14 @@ class Report
* {@link configureReportMetadata()}.
* @return array
* @ignore
+ *
+ * TODO we should move this out to API::getReportMetadata
*/
protected function buildReportMetadata()
{
$report = array(
- 'category' => $this->getCategory(),
+ 'category' => $this->getCategoryId(),
+ 'subcategory' => $this->getSubcategoryId(),
'name' => $this->getName(),
'module' => $this->getModule(),
'action' => $this->getAction()
@@ -587,6 +530,20 @@ class Report
$report['constantRowsCount'] = $this->constantRowsCount;
}
+ $relatedReports = $this->getRelatedReports();
+ if (!empty($relatedReports)) {
+ $report['relatedReports'] = array();
+ foreach ($relatedReports as $relatedReport) {
+ if (!empty($relatedReport)) {
+ $report['relatedReports'][] = array(
+ 'name' => $relatedReport->getName(),
+ 'module' => $relatedReport->getModule(),
+ 'action' => $relatedReport->getAction()
+ );
+ }
+ }
+ }
+
$report['order'] = $this->order;
return $report;
@@ -624,18 +581,6 @@ class Report
return array();
}
- /**
- * Gets the translated widget title if one is defined.
- * @return string
- * @ignore
- */
- public function getWidgetTitle()
- {
- if ($this->widgetTitle) {
- return Piwik::translate($this->widgetTitle);
- }
- }
-
/**
* Get the name of the report
* @return string
@@ -666,24 +611,29 @@ class Report
return $this->action;
}
+ public function getParameters()
+ {
+ return $this->parameters;
+ }
+
/**
* Get the translated name of the category the report belongs to.
* @return string
* @ignore
*/
- public function getCategory()
+ public function getCategoryId()
{
- return Piwik::translate($this->category);
+ return $this->categoryId;
}
/**
- * Get the translation key of the category the report belongs to.
+ * Get the translated name of the subcategory the report belongs to.
* @return string
* @ignore
*/
- public function getCategoryKey()
+ public function getSubcategoryId()
{
- return $this->category;
+ return $this->subcategoryId;
}
/**
@@ -705,16 +655,6 @@ class Report
return $this->order;
}
- /**
- * Get the menu title if one is defined.
- * @return string
- * @ignore
- */
- public function getMenuTitle()
- {
- return $this->menuTitle;
- }
-
/**
* Get the action to load sub tables if one is defined.
* @return string
@@ -740,7 +680,7 @@ class Report
list($subtableReportModule, $subtableReportAction) = $this->getSubtableApiMethod();
- $subtableReport = self::factory($subtableReportModule, $subtableReportAction);
+ $subtableReport = Reports::factory($subtableReportModule, $subtableReportAction);
if (empty($subtableReport)) {
return null;
}
@@ -786,103 +726,6 @@ class Report
return Request::processRequest($module . '.' . $action, $paramOverride);
}
- /**
- * Get an instance of a specific report belonging to the given module and having the given action.
- * @param string $module
- * @param string $action
- * @return null|\Piwik\Plugin\Report
- * @api
- */
- public static function factory($module, $action)
- {
- $listApiToReport = self::getMapOfModuleActionsToReport();
- $api = $module . '.' . ucfirst($action);
-
- if (!array_key_exists($api, $listApiToReport)) {
- return null;
- }
-
- $klassName = $listApiToReport[$api];
-
- return new $klassName;
- }
-
- private static function getMapOfModuleActionsToReport()
- {
- $cacheId = CacheId::pluginAware('ReportFactoryMap');
-
- $cache = Cache::getEagerCache();
- if ($cache->contains($cacheId)) {
- $mapApiToReport = $cache->fetch($cacheId);
- } else {
- $reports = self::getAllReports();
-
- $mapApiToReport = array();
- foreach ($reports as $report) {
- $key = $report->getModule() . '.' . ucfirst($report->getAction());
- $mapApiToReport[$key] = get_class($report);
- }
-
- $cache->save($cacheId, $mapApiToReport);
- }
-
- return $mapApiToReport;
- }
-
- /**
- * Returns a list of all available reports. Even not enabled reports will be returned. They will be already sorted
- * depending on the order and category of the report.
- * @return \Piwik\Plugin\Report[]
- * @api
- */
- public static function getAllReports()
- {
- $reports = self::getAllReportClasses();
- $cacheId = CacheId::languageAware('Reports' . md5(implode('', $reports)));
- $cache = PiwikCache::getTransientCache();
-
-
- if (!$cache->contains($cacheId)) {
- $instances = array();
-
- foreach ($reports as $report) {
- $instances[] = new $report();
- }
-
- usort($instances, array('self', 'sort'));
-
- $cache->save($cacheId, $instances);
- }
-
- return $cache->fetch($cacheId);
- }
-
- /**
- * Returns class names of all Report metadata classes.
- *
- * @return string[]
- * @api
- */
- public static function getAllReportClasses()
- {
- return PluginManager::getInstance()->findMultipleComponents('Reports', '\\Piwik\\Plugin\\Report');
- }
-
- /**
- * API metadata are sorted by category/name,
- * with a little tweak to replicate the standard Piwik category ordering
- *
- * @param Report $a
- * @param Report $b
- * @return int
- */
- private static function sort($a, $b)
- {
- return ($category = strcmp(array_search($a->category, self::$orderOfReports), array_search($b->category, self::$orderOfReports))) == 0
- ? ($a->order < $b->order ? -1 : 1)
- : $category;
- }
-
private function getMetricTranslations($metricsToTranslate)
{
$translations = Metrics::getDefaultMetricTranslations();
@@ -903,11 +746,6 @@ class Report
return $metrics;
}
- private function getMenuControllerAction()
- {
- return self::PREFIX_ACTION_IN_MENU . ucfirst($this->action);
- }
-
private function getSubtableApiMethod()
{
if (strpos($this->actionToLoadSubTables, '.') !== false) {
diff --git a/core/Plugin/Reports.php b/core/Plugin/Reports.php
new file mode 100644
index 0000000000..56d69e5453
--- /dev/null
+++ b/core/Plugin/Reports.php
@@ -0,0 +1,189 @@
+contains($cacheId)) {
+ $mapApiToReport = $cache->fetch($cacheId);
+ } else {
+ $reports = new static();
+ $reports = $reports->getAllReports();
+
+ $mapApiToReport = array();
+ foreach ($reports as $report) {
+ $key = $report->getModule() . '.' . ucfirst($report->getAction());
+
+ if (isset($mapApiToReport[$key]) && $report->getParameters()) {
+ // sometimes there are multiple reports with same module/action but different parameters.
+ // we might pick the "wrong" one. At some point we should compare all parameters and if there is
+ // a report which parameters mach $_REQUEST then we should prefer that report
+ continue;
+ }
+ $mapApiToReport[$key] = get_class($report);
+ }
+
+ $cache->save($cacheId, $mapApiToReport);
+ }
+
+ return $mapApiToReport;
+ }
+
+ /**
+ * Returns a list of all available reports. Even not enabled reports will be returned. They will be already sorted
+ * depending on the order and category of the report.
+ * @return \Piwik\Plugin\Report[]
+ * @api
+ */
+ public function getAllReports()
+ {
+ $reports = $this->getAllReportClasses();
+ $cacheId = CacheId::languageAware('Reports' . md5(implode('', $reports)));
+ $cache = PiwikCache::getTransientCache();
+
+ if (!$cache->contains($cacheId)) {
+ $instances = array();
+
+ foreach ($reports as $report) {
+ $instances[] = new $report();
+ }
+
+ usort($instances, array($this, 'sort'));
+
+ $cache->save($cacheId, $instances);
+ }
+
+ return $cache->fetch($cacheId);
+ }
+
+ /**
+ * API metadata are sorted by category/name,
+ * with a little tweak to replicate the standard Piwik category ordering
+ *
+ * @param Report $a
+ * @param Report $b
+ * @return int
+ */
+ private function sort($a, $b)
+ {
+ return $this->compareCategories($a->getCategoryId(), $a->getSubcategoryId(), $a->getOrder(), $b->getCategoryId(), $b->getSubcategoryId(), $b->getOrder());
+ }
+
+ public function compareCategories($catIdA, $subcatIdA, $orderA, $catIdB, $subcatIdB, $orderB)
+ {
+ static $categoryList;
+
+ if (!isset($categoryList)) {
+ $categoryList = CategoryList::get();
+ }
+
+ $catA = $categoryList->getCategory($catIdA);
+ $catB = $categoryList->getCategory($catIdB);
+
+ // in case there is a category class for both reports
+ if (isset($catA) && isset($catB)) {
+
+ if ($catA->getOrder() == $catB->getOrder()) {
+ // same category order, compare subcategory order
+ $subcatA = $catA->getSubcategory($subcatIdA);
+ $subcatB = $catB->getSubcategory($subcatIdB);
+
+ // both reports have a subcategory with custom subcategory class
+ if ($subcatA && $subcatB) {
+ if ($subcatA->getOrder() == $subcatB->getOrder()) {
+ // same subcategory order, compare order of report
+
+ if ($orderA == $orderB) {
+ return 0;
+ }
+
+ return $orderA < $orderB ? -1 : 1;
+ }
+
+ return $subcatA->getOrder() < $subcatB->getOrder() ? -1 : 1;
+
+ } elseif ($subcatA) {
+ return -1;
+ } elseif ($subcatB) {
+ return 1;
+ }
+
+ if ($orderA == $orderB) {
+ return 0;
+ }
+
+ return $orderA < $orderB ? -1 : 1;
+ }
+
+ return $catA->getOrder() < $catB->getOrder() ? -1 : 1;
+
+ } elseif (isset($catA)) {
+ return -1;
+ } elseif (isset($catB)) {
+ return 1;
+ }
+
+ if ($catIdA === $catIdB) {
+ // both have same category, compare order
+ if ($orderA == $orderB) {
+ return 0;
+ }
+
+ return $orderA < $orderB ? -1 : 1;
+ }
+
+ return strnatcasecmp($catIdA, $catIdB);
+ }
+
+ /**
+ * Returns class names of all Report metadata classes.
+ *
+ * @return string[]
+ * @api
+ */
+ public function getAllReportClasses()
+ {
+ return Plugin\Manager::getInstance()->findMultipleComponents('Reports', '\\Piwik\\Plugin\\Report');
+ }
+}
\ No newline at end of file
diff --git a/core/Plugin/ViewDataTable.php b/core/Plugin/ViewDataTable.php
index 6e190388b6..5ca7e2e9ba 100644
--- a/core/Plugin/ViewDataTable.php
+++ b/core/Plugin/ViewDataTable.php
@@ -13,6 +13,7 @@ use Piwik\Common;
use Piwik\DataTable;
use Piwik\Period;
use Piwik\Piwik;
+use Piwik\Plugin\Reports;
use Piwik\View;
use Piwik\View\ViewInterface;
use Piwik\ViewDataTable\Config as VizConfig;
@@ -191,7 +192,7 @@ abstract class ViewDataTable implements ViewInterface
$this->requestConfig->apiMethodToRequestDataTable = $apiMethodToRequestDataTable;
- $report = Report::factory($this->requestConfig->getApiModuleToRequest(), $this->requestConfig->getApiMethodToRequest());
+ $report = Reports::factory($this->requestConfig->getApiModuleToRequest(), $this->requestConfig->getApiMethodToRequest());
if (!empty($report)) {
/** @var Report $report */
@@ -205,13 +206,7 @@ abstract class ViewDataTable implements ViewInterface
$relatedReports = $report->getRelatedReports();
if (!empty($relatedReports)) {
foreach ($relatedReports as $relatedReport) {
- $widgetTitle = $relatedReport->getWidgetTitle();
-
- if ($widgetTitle && Common::getRequestVar('widget', 0, 'int')) {
- $relatedReportName = $widgetTitle;
- } else {
- $relatedReportName = $relatedReport->getName();
- }
+ $relatedReportName = $relatedReport->getName();
$this->config->addRelatedReport($relatedReport->getModule() . '.' . $relatedReport->getAction(),
$relatedReportName);
@@ -273,7 +268,7 @@ abstract class ViewDataTable implements ViewInterface
$this->overrideViewPropertiesWithQueryParams();
}
- protected function assignRelatedReportsTitle()
+ private function assignRelatedReportsTitle()
{
if (!empty($this->config->related_reports_title)) {
// title already assigned by a plugin
@@ -411,12 +406,9 @@ abstract class ViewDataTable implements ViewInterface
*/
public function render()
{
- $view = $this->buildView();
- return $view->render();
+ return '';
}
- abstract protected function buildView();
-
protected function getDefaultDataTableCssClass()
{
return 'dataTableViz' . Piwik::getUnnamespacedClassName(get_class($this));
diff --git a/core/Plugin/Visualization.php b/core/Plugin/Visualization.php
index b4ff042932..81e99c9f25 100644
--- a/core/Plugin/Visualization.php
+++ b/core/Plugin/Visualization.php
@@ -23,6 +23,7 @@ use Piwik\Period;
use Piwik\Piwik;
use Piwik\Plugins\API\API as ApiApi;
use Piwik\Plugins\PrivacyManager\PrivacyManager;
+use Piwik\Plugin\Reports;
use Piwik\View;
use Piwik\ViewDataTable\Manager as ViewDataTableManager;
use Piwik\Plugin\Manager as PluginManager;
@@ -168,10 +169,10 @@ class Visualization extends ViewDataTable
parent::__construct($controllerAction, $apiMethodToRequestDataTable, $params);
- $this->report = Report::factory($this->requestConfig->getApiModuleToRequest(), $this->requestConfig->getApiMethodToRequest());
+ $this->report = Reports::factory($this->requestConfig->getApiModuleToRequest(), $this->requestConfig->getApiMethodToRequest());
}
- protected function buildView()
+ public function render()
{
$this->overrideSomeConfigPropertiesIfNeeded();
@@ -233,7 +234,7 @@ class Visualization extends ViewDataTable
$view->footerIcons = $this->config->footer_icons;
$view->isWidget = Common::getRequestVar('widget', 0, 'int');
- return $view;
+ return $view->render();
}
/**
diff --git a/core/Plugin/Widgets.php b/core/Plugin/Widgets.php
index 10566a9a7a..e67f2bce61 100644
--- a/core/Plugin/Widgets.php
+++ b/core/Plugin/Widgets.php
@@ -8,191 +8,162 @@
*/
namespace Piwik\Plugin;
-use Piwik\Development;
-use Piwik\Plugin\Manager as PluginManager;
-use Piwik\WidgetsList;
+use Piwik\Container\StaticContainer;
+use Piwik\Piwik;
+use Piwik\Plugin;
+use Exception;
+use Piwik\Widget\Widget;
+use Piwik\Widget\WidgetConfig;
+use Piwik\Widget\WidgetContainerConfig;
/**
- * Base class of all plugin widget providers. Plugins that define their own widgets can extend this class to easily
- * add new widgets or to remove widgets defined by other plugins.
- *
- * For an example, see the {@link https://github.com/piwik/piwik/blob/master/plugins/ExamplePlugin/Widgets.php} plugin.
- *
- * @api
+ * Get widgets that are defined by plugins.
*/
class Widgets
{
- protected $category = '';
- protected $widgets = array();
-
- public function __construct()
- {
- // Constructor kept for BC (because called in implementations)
- }
-
/**
- * @ignore
+ * @var Plugin\Manager
*/
- public function getCategory()
- {
- return $this->category;
- }
+ private $pluginManager;
- private function getModule()
+ public function __construct(Plugin\Manager $pluginManager)
{
- $className = get_class($this);
- $className = explode('\\', $className);
-
- return $className[2];
- }
-
- /**
- * Adds a widget. You can add a widget by calling this method and passing the name of the widget as well as a method
- * name that will be executed to render the widget. The method can be defined either directly here in this widget
- * class or in the controller in case you want to reuse the same action for instance in the menu etc.
- * @api
- */
- protected function addWidget($name, $method, $parameters = array())
- {
- $this->addWidgetWithCustomCategory($this->category, $name, $method, $parameters);
+ $this->pluginManager = $pluginManager;
}
/**
- * Adds a widget with a custom category. By default all widgets that you define in your class will be added under
- * the same category which is defined in the {@link $category} property. Sometimes you may have a widget that
- * belongs to a different category where this method comes handy. It does the same as {@link addWidget()} but
- * allows you to define the category name as well.
- * @api
+ * Get all existing widget configs.
+ *
+ * @return WidgetConfig[]
*/
- protected function addWidgetWithCustomCategory($category, $name, $method, $parameters = array())
+ public function getWidgetConfigs()
{
- $this->checkIsValidWidget($name, $method);
-
- $this->widgets[] = array('category' => $category,
- 'name' => $name,
- 'params' => $parameters,
- 'method' => $method,
- 'module' => $this->getModule());
- }
+ $widgetClasses = $this->getAllWidgetClassNames();
+
+ $configs = array();
+
+ /**
+ * Triggered to add custom widget configs. To filder widgets have a look at the {@hook Widget.filterWidgets}
+ * event.
+ *
+ * **Example**
+ *
+ * public function addWidgetConfigs(&$configs)
+ * {
+ * $config = new WidgetConfig();
+ * $config->setModule('PluginName');
+ * $config->setAction('renderDashboard');
+ * $config->setCategoryId('Dashboard_Dashboard');
+ * $config->setSubcategoryId('dashboardId');
+ * $configs[] = $config;
+ * }
+ *
+ * @param array &$configs An array containing a list of widget config entries.
+ */
+ Piwik::postEvent('Widget.addWidgetConfigs', array(&$configs));
+
+ foreach ($widgetClasses as $widgetClass) {
+ $configs[] = $this->getWidgetConfigForClassName($widgetClass);
+ }
- /**
- * Here you can add one or multiple widgets. To do so call the method {@link addWidget()} or
- * {@link addWidgetWithCustomCategory()}.
- * @api
- */
- protected function init()
- {
+ return $configs;
}
/**
- * @ignore
+ * Get all existing widget container configs.
+ * @return WidgetContainerConfig[]
*/
- public function getWidgets()
+ public function getWidgetContainerConfigs()
{
- $this->widgets = array();
+ $configs = array();
- $this->init();
+ $widgetContainerConfigs = $this->getAllWidgetContainerConfigClassNames();
+ foreach ($widgetContainerConfigs as $widgetClass) {
+ $configs[] = StaticContainer::get($widgetClass);
+ }
- return $this->widgets;
+ return $configs;
}
/**
- * Allows you to configure previously added widgets.
- * For instance you can remove any widgets defined by any plugin by calling the
- * {@link \Piwik\WidgetsList::remove()} method.
+ * Get the widget defined by the given module and action.
*
- * @param WidgetsList $widgetsList
- * @api
- */
- public function configureWidgetsList(WidgetsList $widgetsList)
- {
- }
-
- /**
- * @return \Piwik\Plugin\Widgets[]
- * @ignore
- */
- public static function getAllWidgets()
- {
- return PluginManager::getInstance()->findComponents('Widgets', 'Piwik\\Plugin\\Widgets');
- }
-
- /**
- * @ignore
- * @return Widgets|null
+ * @param string $module Aka plugin name, eg 'CoreHome'
+ * @param string $action An action eg 'renderMe'
+ * @return Widget|null
+ * @throws \Exception Throws an exception if the widget is not enabled.
*/
- public static function factory($module, $action)
+ public function factory($module, $action)
{
if (empty($module) || empty($action)) {
return;
}
- $pluginManager = PluginManager::getInstance();
-
try {
- if (!$pluginManager->isPluginActivated($module)) {
+ if (!$this->pluginManager->isPluginActivated($module)) {
return;
}
- $plugin = $pluginManager->getLoadedPlugin($module);
+ $plugin = $this->pluginManager->getLoadedPlugin($module);
} catch (\Exception $e) {
// we are not allowed to use possible widgets, plugin is not active
return;
}
- /** @var Widgets $widgetContainer */
- $widgetContainer = $plugin->findComponent('Widgets', 'Piwik\\Plugin\\Widgets');
-
- if (empty($widgetContainer)) {
- // plugin does not define any widgets, we cannot do anything
- return;
- }
+ /** @var Widget[] $widgetContainer */
+ $widgets = $plugin->findMultipleComponents('Widgets', 'Piwik\\Widget\\Widget');
- if (!is_callable(array($widgetContainer, $action))) {
- // widget does not implement such a method, we cannot do anything
- return;
+ foreach ($widgets as $widgetClass) {
+ $config = $this->getWidgetConfigForClassName($widgetClass);
+ if ($config->getAction() === $action) {
+ $config->checkIsEnabled();
+ return StaticContainer::get($widgetClass);
+ }
}
+ }
- // the widget class implements such an action, but we have to check whether it is actually exposed and whether
- // it was maybe disabled by another plugin, this is only possible by checking the widgetslist, unfortunately
- if (!WidgetsList::isDefined($module, $action)) {
- return;
- }
+ private function getWidgetConfigForClassName($widgetClass)
+ {
+ /** @var string|Widget $widgetClass */
+ $config = new WidgetConfig();
+ $config->setModule($this->getModuleFromWidgetClassName($widgetClass));
+ $config->setAction($this->getActionFromWidgetClassName($widgetClass));
+ $widgetClass::configure($config);
- return $widgetContainer;
+ return $config;
}
- private function checkIsValidWidget($name, $method)
+ /**
+ * @return string[]
+ */
+ private function getAllWidgetClassNames()
{
- if (!Development::isEnabled()) {
- return;
- }
+ return $this->pluginManager->findMultipleComponents('Widgets', 'Piwik\\Widget\\Widget');
+ }
- if (empty($name)) {
- Development::error('No name is defined for added widget having method "' . $method . '" in ' . get_class($this));
- }
+ private function getModuleFromWidgetClassName($widgetClass)
+ {
+ $parts = explode('\\', $widgetClass);
- if (Development::isCallableMethod($this, $method)) {
- return;
- }
+ return $parts[2];
+ }
- $controllerClass = 'Piwik\\Plugins\\' . $this->getModule() . '\\Controller';
+ private function getActionFromWidgetClassName($widgetClass)
+ {
+ $parts = explode('\\', $widgetClass);
- if (!Development::methodExists($this, $method) &&
- !Development::methodExists($controllerClass, $method)) {
- Development::error('The added method "' . $method . '" neither exists in "' . get_class($this) . '" nor "' . $controllerClass . '". Make sure to define such a method.');
+ if (count($parts) >= 4) {
+ return lcfirst(end($parts));
}
- $definedInClass = get_class($this);
-
- if (Development::methodExists($controllerClass, $method)) {
- if (Development::isCallableMethod($controllerClass, $method)) {
- return;
- }
-
- $definedInClass = $controllerClass;
- }
+ return '';
+ }
- Development::error('The method "' . $method . '" is not callable on "' . $definedInClass . '". Make sure the method is public.');
+ /**
+ * @return string[]
+ */
+ private function getAllWidgetContainerConfigClassNames()
+ {
+ return $this->pluginManager->findMultipleComponents('Widgets', 'Piwik\\Widget\\WidgetContainerConfig');
}
}
diff --git a/core/Report/ReportWidgetConfig.php b/core/Report/ReportWidgetConfig.php
new file mode 100644
index 0000000000..cb3b576b4d
--- /dev/null
+++ b/core/Report/ReportWidgetConfig.php
@@ -0,0 +1,92 @@
+viewDataTable = $viewDataTableId;
+ return $this;
+ }
+
+ /**
+ * Forces the usage of the given viewDataTable in order to render the report.
+ *
+ * @param string $viewDataTableId eg 'table' or 'graph'
+ * @return $this
+ */
+ public function forceViewDataTable($viewDataTableId)
+ {
+ $this->forceViewDataTable = true;
+ $this->setDefaultViewDataTable($viewDataTableId);
+
+ return $this;
+ }
+
+ /**
+ * Detect whether a defined viewDataTable should be forced in order to render a report.
+ * @return bool
+ */
+ public function isViewDataTableForced()
+ {
+ return $this->forceViewDataTable;
+ }
+
+ /**
+ * Get the specified viewDataTable.
+ * @return string
+ */
+ public function getViewDataTable()
+ {
+ return $this->viewDataTable;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getParameters()
+ {
+ $parameters = parent::getParameters();
+
+ $defaultParams = array();
+
+ if ($this->forceViewDataTable) {
+ $defaultParams['forceView'] = '1';
+ }
+
+ if ($this->viewDataTable &&
+ ($this->forceViewDataTable || $this->viewDataTable !== Factory::DEFAULT_VIEW)) {
+ // URL param is not needed for default view dataTable
+ $defaultParams['viewDataTable'] = $this->viewDataTable;
+ }
+
+ return $defaultParams + $parameters;
+ }
+
+}
\ No newline at end of file
diff --git a/core/Report/ReportWidgetFactory.php b/core/Report/ReportWidgetFactory.php
new file mode 100644
index 0000000000..b3ea165ec4
--- /dev/null
+++ b/core/Report/ReportWidgetFactory.php
@@ -0,0 +1,119 @@
+createWidget()` is all one has to do in
+ * order to create a new widget.
+ *
+ * @api since Piwik 3.0.0
+ */
+class ReportWidgetFactory
+{
+ /**
+ * @var Report
+ */
+ private $report = null;
+
+ /**
+ * Generates a new report widget factory.
+ * @param Report $report A report instance, widgets will be created based on the data provided by this report.
+ */
+ public function __construct(Report $report)
+ {
+ $this->report = $report;
+ }
+
+ /**
+ * Creates a widget based on the specified report in {@link construct()}.
+ *
+ * It will automatically use the report's name, categoryId, subcategoryId (if specified),
+ * defaultViewDataTable, module, action, order and parameters in order to create the widget.
+ *
+ * @return ReportWidgetConfig
+ */
+ public function createWidget()
+ {
+ $widget = new ReportWidgetConfig();
+ $widget->setName($this->report->getName());
+ $widget->setCategoryId($this->report->getCategoryId());
+
+ if ($this->report->getDefaultTypeViewDataTable()) {
+ $widget->setDefaultViewDataTable($this->report->getDefaultTypeViewDataTable());
+ }
+
+ if ($this->report->getSubcategoryId()) {
+ $widget->setSubcategoryId($this->report->getSubcategoryId());
+ }
+
+ $widget->setModule($this->report->getModule());
+ $widget->setAction($this->report->getAction());
+
+ $orderThatListsReportsAtTheEndOfEachCategory = 100 + $this->report->getOrder();
+ $widget->setOrder($orderThatListsReportsAtTheEndOfEachCategory);
+
+ $parameters = $this->report->getParameters();
+ if (!empty($parameters)) {
+ $widget->setParameters($parameters);
+ }
+
+ return $widget;
+ }
+
+ /**
+ * Creates a new container widget based on the specified report in {@link construct()}.
+ *
+ * It will automatically use the report's categoryId, subcategoryId (if specified) and order in order to
+ * create the container.
+ *
+ * @param string $containerId eg 'Products' or 'Contents' see {Piwik\Widget\WidgetContainerConfig::setId()}.
+ * Other reports or widgets will be able to add more widgets to this container.
+ * This is useful when you want to show for example multiple related widgets
+ * together.
+ * @return WidgetContainerConfig
+ */
+ public function createContainerWidget($containerId)
+ {
+ $widget = new WidgetContainerConfig();
+ $widget->setCategoryId($this->report->getCategoryId());
+ $widget->setId($containerId);
+
+ if ($this->report->getSubcategoryId()) {
+ $widget->setSubcategoryId($this->report->getSubcategoryId());
+ }
+
+ $orderThatListsReportsAtTheEndOfEachCategory = 100 + $this->report->getOrder();
+ $widget->setOrder($orderThatListsReportsAtTheEndOfEachCategory);
+
+ return $widget;
+ }
+
+ /**
+ * Creates a custom widget that doesn't use a viewDataTable to render the report but instead a custom
+ * controller action. Make sure the specified `$action` exists in the plugin's controller. Otherwise
+ * behaves as {@link createWidget()}.
+ *
+ * @param string $action eg 'conversionReports' (requires a method `public function conversionReports()` in
+ * the plugin's controller).
+ * @return ReportWidgetConfig
+ */
+ public function createCustomWidget($action)
+ {
+ $widget = $this->createWidget();
+ $widget->setDefaultViewDataTable(null);
+ $widget->setAction($action);
+
+ return $widget;
+ }
+}
\ No newline at end of file
diff --git a/core/Tracker.php b/core/Tracker.php
index fa774112c3..d64f1b7fa8 100644
--- a/core/Tracker.php
+++ b/core/Tracker.php
@@ -12,7 +12,6 @@ use Exception;
use Piwik\Plugins\BulkTracking\Tracker\Requests;
use Piwik\Plugins\PrivacyManager\Config as PrivacyManagerConfig;
use Piwik\Config;
-use Piwik\Tests\Framework\TestingEnvironmentVariables;
use Piwik\Tracker\Db as TrackerDb;
use Piwik\Tracker\Db\DbException;
use Piwik\Tracker\Handler;
diff --git a/core/Updates/3.0.0-b1.php b/core/Updates/3.0.0-b1.php
new file mode 100644
index 0000000000..301bc44a1b
--- /dev/null
+++ b/core/Updates/3.0.0-b1.php
@@ -0,0 +1,351 @@
+fetchAll(sprintf("SELECT DISTINCT idgoal FROM %s", Common::prefixTable('goal')));
+ $allDashboards = $db->fetchAll(sprintf("SELECT * FROM %s", Common::prefixTable('user_dashboard')));
+
+ return $this->getDashboardMigrationSqls($allDashboards, $allGoals);
+ }
+
+ public function doUpdate(Updater $updater)
+ {
+ $updater->executeMigrationQueries(__FILE__, $this->getMigrationQueries($updater));
+ }
+
+ private function getDashboardMigrationSqls($allDashboards, $allGoals)
+ {
+ $sqls = array();
+
+
+ // update dashboard to use new widgets
+ $oldWidgets = array(
+ array (
+ 'module' => 'VisitTime',
+ 'action' => 'getVisitInformationPerServerTime',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'VisitTime',
+ 'action' => 'getVisitInformationPerLocalTime',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'VisitTime',
+ 'action' => 'getByDayOfWeek',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'VisitsSummary',
+ 'action' => 'getEvolutionGraph',
+ 'params' =>
+ array (
+ 'columns' => array ('nb_visits'),
+ ),
+ ),array (
+ 'module' => 'VisitsSummary',
+ 'action' => 'getSparklines',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'VisitsSummary',
+ 'action' => 'index',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'Live',
+ 'action' => 'getVisitorLog',
+ 'params' =>
+ array ('small' => 1),
+ ),array (
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsPerVisitDuration',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsPerPage',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'VisitFrequency',
+ 'action' => 'getSparklines',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'VisitFrequency',
+ 'action' => 'getEvolutionGraph',
+ 'params' =>
+ array (
+ 'columns' => array ('nb_visits_returning'),
+ ),
+ ),array (
+ 'module' => 'DevicesDetection',
+ 'action' => 'getBrowserEngines',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'Referrers',
+ 'action' => 'getReferrerType',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'Referrers',
+ 'action' => 'getAll',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'Referrers',
+ 'action' => 'getSocials',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'Goals',
+ 'action' => 'widgetGoalsOverview',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'Goals',
+ 'action' => 'getItemsSku',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'Goals',
+ 'action' => 'getItemsName',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'Goals',
+ 'action' => 'getItemsCategory',
+ 'params' =>
+ array (
+ ),
+ ),array (
+ 'module' => 'Ecommerce',
+ 'action' => 'widgetGoalReport',
+ 'params' =>
+ array (
+ 'idGoal' => 'ecommerceOrder',
+ ),
+ ),
+ );
+
+ foreach ($allGoals as $goal) {
+ $oldWidgets[] = array (
+ 'module' => 'Goals',
+ 'action' => 'widgetGoalReport',
+ 'params' =>
+ array (
+ 'idGoal' => (int) $goal['idgoal'],
+ ));
+ }
+
+ $newWidgets = array(
+ array (
+ 'module' => 'VisitTime',
+ 'action' => 'getVisitInformationPerServerTime',
+ 'params' =>
+ array (
+ 'viewDataTable' => 'graphVerticalBar',
+ ),
+ ),array (
+ 'module' => 'VisitTime',
+ 'action' => 'getVisitInformationPerLocalTime',
+ 'params' =>
+ array (
+ 'viewDataTable' => 'graphVerticalBar',
+ ),
+ ),array (
+ 'module' => 'VisitTime',
+ 'action' => 'getByDayOfWeek',
+ 'params' =>
+ array (
+ 'viewDataTable' => 'graphVerticalBar'
+ ),
+ ),array (
+ 'module' => 'VisitsSummary',
+ 'action' => 'getEvolutionGraph',
+ 'params' =>
+ array (
+ 'forceView' => '1',
+ 'viewDataTable' => 'graphEvolution',
+ ),
+ ),array (
+ 'module' => 'VisitsSummary',
+ 'action' => 'get',
+ 'params' =>
+ array (
+ 'forceView' => '1',
+ 'viewDataTable' => 'sparklines',
+ ),
+ ),array (
+ 'module' => 'CoreHome',
+ 'action' => 'renderWidgetContainer',
+ 'uniqueId' => 'widgetVisitOverviewWithGraph',
+ 'params' =>
+ array (
+ 'containerId' => 'VisitOverviewWithGraph',
+ ),
+ ),array (
+ 'module' => 'Live',
+ 'action' => 'getLastVisitsDetails',
+ 'params' =>
+ array (
+ 'forceView' => '1',
+ 'viewDataTable' => 'Piwik\\Plugins\\Live\\VisitorLog',
+ 'small' => '1',
+ ),
+ ),array (
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsPerVisitDuration',
+ 'params' =>
+ array (
+ 'viewDataTable' => 'cloud',
+ ),
+ ),array (
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsPerPage',
+ 'params' =>
+ array (
+ 'viewDataTable' => 'cloud',
+ ),
+ ),array (
+ 'module' => 'VisitFrequency',
+ 'action' => 'get',
+ 'params' =>
+ array (
+ 'forceView' => '1',
+ 'viewDataTable' => 'sparklines'
+ ),
+ ),array (
+ 'module' => 'VisitFrequency',
+ 'action' => 'getEvolutionGraph',
+ 'params' =>
+ array (
+ 'forceView' => 1,
+ 'viewDataTable' => 'graphEvolution',
+ ),
+ ),array (
+ 'module' => 'DevicesDetection',
+ 'action' => 'getBrowserEngines',
+ 'params' =>
+ array (
+ 'viewDataTable' => 'graphPie',
+ ),
+ ),array (
+ 'module' => 'Referrers',
+ 'action' => 'getReferrerType',
+ 'params' =>
+ array (
+ 'viewDataTable' => 'tableAllColumns',
+ ),
+ ),array (
+ 'module' => 'Referrers',
+ 'action' => 'getAll',
+ 'params' =>
+ array (
+ 'viewDataTable' => 'tableAllColumns',
+ ),
+ ),array (
+ 'module' => 'Referrers',
+ 'action' => 'getSocials',
+ 'params' =>
+ array (
+ 'viewDataTable' => 'graphPie',
+ ),
+ ),array (
+ 'module' => 'CoreHome',
+ 'action' => 'renderWidgetContainer',
+ 'uniqueId' => 'widgetGoalsOverview',
+ 'params' =>
+ array (
+ 'containerId' => 'GoalsOverview'
+ ),
+ ),array (
+ 'module' => 'Goals',
+ 'action' => 'getItemsSku',
+ 'params' => array (),
+ ),array (
+ 'module' => 'Goals',
+ 'action' => 'getItemsName',
+ 'params' => array (),
+ ),array (
+ 'module' => 'Goals',
+ 'action' => 'getItemsCategory',
+ 'params' => array (),
+ ),array (
+ 'module' => 'CoreHome',
+ 'action' => 'renderWidgetContainer',
+ 'uniqueId' => 'widgetEcommerceOverview',
+ 'params' =>
+ array (
+ 'containerId' => 'EcommerceOverview',
+ ),
+ ),
+ );
+
+ foreach ($allGoals as $goal) {
+ $newWidgets[] = array (
+ 'module' => 'CoreHome',
+ 'action' => 'renderWidgetContainer',
+ 'uniqueId' => 'widgetGoal_' . (int) $goal['idgoal'],
+ 'params' =>
+ array (
+ 'containerId' => 'Goal_' . (int) $goal['idgoal'],
+ ));
+ }
+
+ foreach ($allDashboards as $dashboard) {
+ $dashboardLayout = json_decode($dashboard['layout']);
+
+ $dashboardLayout = Dashboard\Model::replaceDashboardWidgets($dashboardLayout, $oldWidgets, $newWidgets);
+
+ $newLayout = json_encode($dashboardLayout);
+ if ($newLayout != $dashboard['layout']) {
+ $sqls["UPDATE " . Common::prefixTable('user_dashboard') . " SET layout = '".addslashes($newLayout)."' WHERE iddashboard = ".$dashboard['iddashboard']] = false;
+ }
+ }
+
+ return $sqls;
+ }
+}
diff --git a/core/Version.php b/core/Version.php
index c9a04c200a..224db01a2c 100644
--- a/core/Version.php
+++ b/core/Version.php
@@ -20,7 +20,7 @@ final class Version
* The current Piwik version.
* @var string
*/
- const VERSION = '2.15.0-b15';
+ const VERSION = '3.0.0-b1';
public function isStableVersion($version)
{
diff --git a/core/View/ReportsByDimension.php b/core/View/ReportsByDimension.php
deleted file mode 100644
index dbb59be930..0000000000
--- a/core/View/ReportsByDimension.php
+++ /dev/null
@@ -1,129 +0,0 @@
-dimensionCategories = array();
- $this->id = $id;
- }
-
- /**
- * Adds a report to the list of reports to display.
- *
- * @param string $category The report's category. Can be a i18n token.
- * @param string $title The report's title. Can be a i18n token.
- * @param string $action The controller action used to load the report, ie, Referrers.getAll
- * @param array $params The list of query parameters to use when loading the report.
- * This list overrides query parameters currently in use. For example,
- * array('idSite' => 2, 'viewDataTable' => 'goalsTable')
- * would mean the goals report for site w/ ID=2 will always be loaded.
- */
- public function addReport($category, $title, $action, $params = array())
- {
- list($module, $action) = explode('.', $action);
- $params = array('module' => $module, 'action' => $action) + $params;
-
- $categories = $this->dimensionCategories;
- $categories[$category][] = array(
- 'title' => $title,
- 'params' => $params,
- 'url' => Url::getCurrentQueryStringWithParametersModified($params)
- );
- $this->dimensionCategories = $categories;
- }
-
- /**
- * Adds a set of reports to the list of reports to display.
- *
- * @param array $reports An array containing report information. The array requires
- * the 'category', 'title', 'action' and 'params' elements.
- * For information on what they should contain, @see addReport.
- */
- public function addReports($reports)
- {
- foreach ($reports as $report) {
- $this->addReport($report['category'], $report['title'], $report['action'], $report['params']);
- }
- }
-
- /**
- * @return string The ID specified in the constructor, usually the plugin name
- */
- public function getId()
- {
- return $this->id;
- }
-
- /**
- * Renders this view.
- *
- * @return string The rendered view.
- */
- public function render()
- {
- /**
- * Triggered before rendering {@link ReportsByDimension} views.
- *
- * Plugins can use this event to configure {@link ReportsByDimension} instances by
- * adding or removing reports to display.
- *
- * @param ReportsByDimension $this The view instance.
- */
- Piwik::postEvent('View.ReportsByDimension.render', array($this));
-
- $this->firstReport = "";
-
- // if there are reports & report categories added, render the first one so we can
- // display it initially
- $categories = $this->dimensionCategories;
- if (!empty($categories)) {
- $firstCategory = reset($categories);
- $firstReportInfo = reset($firstCategory);
-
- $oldGet = $_GET;
- $oldPost = $_POST;
-
- foreach ($firstReportInfo['params'] as $key => $value) {
- $_GET[$key] = $value;
- }
-
- $_POST = array();
-
- $module = $firstReportInfo['params']['module'];
- $action = $firstReportInfo['params']['action'];
- $this->firstReport = FrontController::getInstance()->fetchDispatch($module, $action);
-
- $_GET = $oldGet;
- $_POST = $oldPost;
- }
-
- return parent::render();
- }
-}
diff --git a/core/ViewDataTable/Config.php b/core/ViewDataTable/Config.php
index e856473d46..22cb8c4429 100644
--- a/core/ViewDataTable/Config.php
+++ b/core/ViewDataTable/Config.php
@@ -13,8 +13,8 @@ use Piwik\API\Request as ApiRequest;
use Piwik\DataTable;
use Piwik\DataTable\Filter\PivotByDimension;
use Piwik\Metrics;
-use Piwik\Plugin\Report;
use Piwik\Plugins\API\API;
+use Piwik\Plugin\Reports;
/**
* Contains base display properties for {@link Piwik\Plugin\ViewDataTable}s. Manipulating these
@@ -684,7 +684,7 @@ class Config
private function setShouldShowPivotBySubtable()
{
- $report = Report::factory($this->controllerName, $this->controllerAction);
+ $report = Reports::factory($this->controllerName, $this->controllerAction);
if (empty($report)) {
$this->show_pivot_by_subtable = false;
diff --git a/core/ViewDataTable/Factory.php b/core/ViewDataTable/Factory.php
index 43db3161e9..543f1280cf 100644
--- a/core/ViewDataTable/Factory.php
+++ b/core/ViewDataTable/Factory.php
@@ -12,6 +12,7 @@ use Piwik\Common;
use Piwik\Piwik;
use Piwik\Plugin\Report;
use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
+use Piwik\Plugin\Reports;
/**
* Provides a means of creating {@link Piwik\Plugin\ViewDataTable} instances by ID.
@@ -58,6 +59,8 @@ use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
*/
class Factory
{
+ const DEFAULT_VIEW = HtmlTable::ID;
+
/**
* Cache for getDefaultTypeViewDataTable result.
*
@@ -132,7 +135,7 @@ class Factory
// Common::getRequestVar removes backslashes from the defaultValue in case magic quotes are enabled.
// therefore do not pass this as a default value to getRequestVar()
if ('' === $type) {
- $type = $defaultType ?: HtmlTable::ID;
+ $type = $defaultType ?: self::DEFAULT_VIEW;
}
} else {
$type = $defaultViewType;
@@ -150,8 +153,8 @@ class Factory
return self::createViewDataTableInstance($visualizations[$defaultType], $controllerAction, $apiAction, $params);
}
- if (array_key_exists(HtmlTable::ID, $visualizations)) {
- return self::createViewDataTableInstance($visualizations[HtmlTable::ID], $controllerAction, $apiAction, $params);
+ if (array_key_exists(self::DEFAULT_VIEW, $visualizations)) {
+ return self::createViewDataTableInstance($visualizations[self::DEFAULT_VIEW], $controllerAction, $apiAction, $params);
}
throw new \Exception('No visualization found to render ViewDataTable');
@@ -165,7 +168,7 @@ class Factory
private static function getReport($apiAction)
{
list($module, $action) = explode('.', $apiAction);
- $report = Report::factory($module, $action);
+ $report = Reports::factory($module, $action);
return $report;
}
diff --git a/core/Widget/Widget.php b/core/Widget/Widget.php
new file mode 100644
index 0000000000..3a27331bd1
--- /dev/null
+++ b/core/Widget/Widget.php
@@ -0,0 +1,37 @@
+categoryId = $categoryId;
+
+ return $this;
+ }
+
+ /**
+ * Get the id of the category the widget belongs to.
+ * @return string
+ */
+ public function getCategoryId()
+ {
+ return $this->categoryId;
+ }
+
+ /**
+ * Set the id of the subcategory the widget belongs to. If a subcategory is specified, the widget
+ * will be shown in the Piwik reporting UI. The subcategoryId will be used as a translation key for
+ * the submenu item.
+ *
+ * @param string $subcategoryId Usually a translation key, eg 'General_Overview', 'Actions_Pages', ...
+ * @return static
+ */
+ public function setSubcategoryId($subcategoryId)
+ {
+ $this->subcategoryId = $subcategoryId;
+
+ return $this;
+ }
+
+ public function getSubcategoryId()
+ {
+ return $this->subcategoryId;
+ }
+
+ /**
+ * Set the module (aka plugin name) of the widget. The correct module is usually detected automatically and
+ * not needed to be configured manually.
+ *
+ * @param string $module eg 'CoreHome'
+ * @return static
+ */
+ public function setModule($module)
+ {
+ $this->module = $module;
+
+ return $this;
+ }
+
+ public function getModule()
+ {
+ return $this->module;
+ }
+
+ /**
+ * Set the action of the widget that shall be used in the URL to render the widget.
+ * The correct action is usually detected automatically and not needed to be configured manually.
+ *
+ * @param string $action eg 'renderMyWidget'
+ * @return static
+ */
+ public function setAction($action)
+ {
+ $this->action = $action;
+
+ return $this;
+ }
+
+ public function getAction()
+ {
+ return $this->action;
+ }
+
+ /**
+ * Sets (overwrites) the parameters of the widget. These parameters will be added to the URL when rendering the
+ * widget. You can access these parameters via `Piwik\Common::getRequestVar(...)`.
+ *
+ * @param array $parameters eg. ('urlparam' => 'urlvalue')
+ * @return static
+ */
+ public function setParameters($parameters)
+ {
+ $this->parameters = $parameters;
+
+ return $this;
+ }
+
+ /**
+ * Add new parameters and only overwrite parameters that have the same name. See {@link setParameters()}
+ *
+ * @param array $parameters eg. ('urlparam' => 'urlvalue')
+ * @return static
+ */
+ public function addParameters($parameters)
+ {
+ $this->parameters = array_merge($this->parameters, $parameters);
+
+ return $this;
+ }
+
+ /**
+ * Get all URL parameters needed to render this widget.
+ * @return array Eg ('urlparam' => 'urlvalue').
+ */
+ public function getParameters()
+ {
+ $defaultParams = array(
+ 'module' => $this->getModule(),
+ 'action' => $this->getAction()
+ );
+
+ return $defaultParams + $this->parameters;
+ }
+
+ /**
+ * Set the name of the widget.
+ *
+ * @param string $name Usually a translation key, eg 'VisitTime_ByServerTimeWidgetName'
+ * @return static
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ /**
+ * Get the name of the widget.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Set the order of the widget.
+ *
+ * @param int $order eg. 5
+ * @return static
+ */
+ public function setOrder($order)
+ {
+ $this->order = (int) $order;
+
+ return $this;
+ }
+
+ /**
+ * Returns the order of the widget.
+ * @return int
+ */
+ public function getOrder()
+ {
+ return $this->order;
+ }
+
+ /**
+ * Defines whether a widget is enabled or not. For instance some widgets might not be available to every user or
+ * might depend on a setting (such as Ecommerce) of a site. In such a case you can perform any checks and then
+ * return `true` or `false`. If your report is only available to users having super user access you can do the
+ * following: `return Piwik::hasUserSuperUserAccess();`
+ * @return bool
+ * @api
+ */
+ public function isEnabled()
+ {
+ return $this->isEnabled;
+ }
+
+ /**
+ * Enable / disable the widget. See {@link isEnabled()}
+ *
+ * @param bool $isEnabled
+ * @return static
+ */
+ public function setIsEnabled($isEnabled)
+ {
+ $this->isEnabled = (bool) $isEnabled;
+ return $this;
+ }
+
+ /**
+ * Enables the widget. See {@link isEnabled()}
+ */
+ public function enable()
+ {
+ $this->setIsEnabled(true);
+ }
+
+ /**
+ * Disables the widget. See {@link isEnabled()}
+ */
+ public function disable()
+ {
+ $this->setIsEnabled(false);
+ }
+
+ /**
+ * This method checks whether the widget is available, see {@link isEnabled()}. If not, it triggers an exception
+ * containing a message that will be displayed to the user. You can overwrite this message in case you want to
+ * customize the error message. Eg.
+ * ```
+ * if (!$this->isEnabled()) {
+ * throw new Exception('Setting XYZ is not enabled or the user has not enough permission');
+ * }
+ * ```
+ * @throws \Exception
+ * @api
+ */
+ public function checkIsEnabled()
+ {
+ if (!$this->isEnabled()) {
+ throw new Exception(Piwik::translate('General_ExceptionWidgetNotEnabled'));
+ }
+ }
+
+ /**
+ * Returns the unique id of an widget based on module, action and the set parameters.
+ *
+ * @return string
+ */
+ public function getUniqueId()
+ {
+ $parameters = $this->getParameters();
+ unset($parameters['module']);
+ unset($parameters['action']);
+
+ return WidgetsList::getWidgetUniqueId($this->getModule(), $this->getAction(), $parameters);
+ }
+
+ /**
+ * Sets the widget as not widgetizable {@link isWidgetizeable()}.
+ *
+ * @return static
+ */
+ public function setIsNotWidgetizable()
+ {
+ $this->isWidgetizable = false;
+ return $this;
+ }
+
+ /**
+ * Sets the widget as widgetizable {@link isWidgetizeable()}.
+ *
+ * @return static
+ */
+ public function setIsWidgetizable()
+ {
+ $this->isWidgetizable = true;
+ return $this;
+ }
+
+ /**
+ * Detect whether the widget is widgetizable meaning it won't be able to add it to the dashboard and it won't
+ * be possible to export the widget via an iframe if it is not widgetizable. This is usually not needed but useful
+ * when you eg want to display a widget within the Piwik UI but not want to have it widgetizable.
+ *
+ * @return bool
+ */
+ public function isWidgetizeable()
+ {
+ return $this->isWidgetizable;
+ }
+
+ /**
+ * If middleware parameters are specified, the corresponding action will be executed before showing the
+ * actual widget in the UI. Only if this action (can be a controller method or API method) returns JSON `true`
+ * the widget will be actually shown. It is similar to `isEnabled()` but the specified action is performed each
+ * time the widget is requested in the UI whereas `isEnabled` is only checked once on the inital page load when
+ * we load the inital list of widgets. So if your widget's visibility depends on archived data
+ * (aka idSite/period/date) you should specify middle parameters. This has mainly two reasons:
+ *
+ * - This way the inital page load time is faster as we won't have to request archived data on the initial page
+ * load for widgets that are potentially never shown.
+ * - We execute that action every time before showing it. As the initial list of widgets is loaded on page load
+ * it is possible that some archives have no data yet, but at a later time there might be actually archived data.
+ * As we never reload the initial list of widgets we would still not show the widget even there we should. Example:
+ * On page load there are no conversions, a few minutes later there might be conversions. As the middleware is
+ * executed before showing it, we detect correctly that there are now conversions whereas `isEnabled` is only
+ * checked once on the initial Piwik page load.
+ *
+ * @param array $parameters URL parameters eg array('module' => 'Goals', 'action' => 'Conversions')
+ * @return static
+ */
+ public function setMiddlewareParameters($parameters)
+ {
+ $this->middlewareParameters = $parameters;
+ return $this;
+ }
+
+ /**
+ * Get defined middleware parameters (if any).
+ *
+ * @return array
+ */
+ public function getMiddlewareParameters()
+ {
+ return $this->middlewareParameters;
+ }
+
+
+}
\ No newline at end of file
diff --git a/core/Widget/WidgetContainerConfig.php b/core/Widget/WidgetContainerConfig.php
new file mode 100644
index 0000000000..258d182ab4
--- /dev/null
+++ b/core/Widget/WidgetContainerConfig.php
@@ -0,0 +1,131 @@
+id = $id;
+ return $this;
+ }
+
+ /**
+ * Get the id of the widget.
+ * @return string
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * Sets the layout of the container widget.
+ *
+ * By default widgets within a container are displayed one after another. In case you want to change this
+ * behaviour you can specify a layout that will be recognized by the UI. It is not yet possible to define
+ * custom layouts.
+ *
+ * @param string $layout eg 'ByDimension' see {@link Piwik\Plugins\CoreHome\CoreHome::WIDGET_CONTAINER_LAYOUT_BY_DIMENSION}
+ * @return static
+ */
+ public function setLayout($layout)
+ {
+ $this->layout = $layout;
+ return $this;
+ }
+
+ /**
+ * Gets the currently set layout.
+ * @return string
+ */
+ public function getLayout()
+ {
+ return $this->layout;
+ }
+
+ /**
+ * Adds a new widget to the container widget.
+ *
+ * @param WidgetConfig $widget
+ * @return static
+ */
+ public function addWidgetConfig(WidgetConfig $widget)
+ {
+ $this->widgets[] = $widget;
+
+ return $this;
+ }
+
+ /**
+ * Get all added widget configs.
+ *
+ * @return WidgetConfig[]
+ */
+ public function getWidgetConfigs()
+ {
+ return $this->widgets;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getUniqueId()
+ {
+ $parameters = $this->getParameters();
+ unset($parameters['module']);
+ unset($parameters['action']);
+ unset($parameters['containerId']);
+
+ return WidgetsList::getWidgetUniqueId($this->id, '', $parameters);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public function getParameters()
+ {
+ $params = parent::getParameters();
+ $params['containerId'] = $this->getId();
+ return $params;
+ }
+
+}
\ No newline at end of file
diff --git a/core/Widget/WidgetsList.php b/core/Widget/WidgetsList.php
new file mode 100644
index 0000000000..dfeb004277
--- /dev/null
+++ b/core/Widget/WidgetsList.php
@@ -0,0 +1,247 @@
+addContainer($widget);
+ } elseif (Development::isEnabled()) {
+ $this->checkIsValidWidget($widget);
+ }
+
+ $this->widgets[] = $widget;
+ }
+
+ /**
+ * Add multiple widget configs at once. See {@link addWidgetConfig()}.
+ *
+ * @param WidgetConfig[] $widgets
+ */
+ public function addWidgetConfigs($widgets)
+ {
+ foreach ($widgets as $widget) {
+ $this->addWidgetConfig($widget);
+ }
+ }
+
+ private function addContainer(WidgetContainerConfig $containerWidget)
+ {
+ $widgetId = $containerWidget->getId();
+
+ $this->container[$widgetId] = $containerWidget;
+
+ // widgets were added to this container, but the container did not exist yet.
+ if (isset($this->containerWidgets[$widgetId])) {
+ foreach ($this->containerWidgets[$widgetId] as $widget) {
+ $containerWidget->addWidgetConfig($widget);
+ }
+ unset($this->containerWidgets[$widgetId]);
+ }
+ }
+
+ /**
+ * Get all added widget configs.
+ *
+ * @return WidgetConfig[]
+ */
+ public function getWidgetConfigs()
+ {
+ return $this->widgets;
+ }
+
+ private function checkIsValidWidget(WidgetConfig $widget)
+ {
+ if (!$widget->getModule()) {
+ Development::error('No module is defined for added widget having name "' . $widget->getName());
+ }
+
+ if (!$widget->getAction()) {
+ Development::error('No action is defined for added widget having name "' . $widget->getName());
+ }
+ }
+
+ /**
+ * Add a widget to a widget container. It doesn't matter whether the container was added to this list already
+ * or whether the container is added later. As long as a container having the same containerId is added at
+ * some point the widget will be added to that container. If no container having this id is added the widget
+ * will not be recognized.
+ *
+ * @param string $containerId eg 'Products' or 'Contents'. See {@link WidgetContainerConfig::setId}
+ * @param WidgetConfig $widget
+ */
+ public function addToContainerWidget($containerId, WidgetConfig $widget)
+ {
+ if (isset($this->container[$containerId])) {
+ $this->container[$containerId]->addWidgetConfig($widget);
+ } else {
+ if (!isset($this->containerWidgets[$containerId])) {
+ $this->containerWidgets[$containerId] = array();
+ }
+
+ $this->containerWidgets[$containerId][] = $widget;
+ }
+ }
+
+ /**
+ * Removes one or more widgets from the widget list.
+ *
+ * @param string $widgetCategoryId The widget category id. Can be a translation token eg 'General_Visits'
+ * see {@link WidgetConfig::setCategoryId()}.
+ * @param string|false $widgetName The name of the widget to remove eg 'VisitTime_ByServerTimeWidgetName'.
+ * If not supplied, all widgets within that category will be removed.
+ */
+ public function remove($widgetCategoryId, $widgetName = false)
+ {
+ foreach ($this->widgets as $index => $widget) {
+ if ($widget->getCategoryId() === $widgetCategoryId) {
+ if (!$widgetName || $widget->getName() === $widgetName) {
+ unset($this->widgets[$index]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns `true` if a widget exists in the widget list, `false` if otherwise.
+ *
+ * @param string $module The controller name of the widget.
+ * @param string $action The controller action of the widget.
+ * @return bool
+ */
+ public function isDefined($module, $action)
+ {
+ foreach ($this->widgets as $widget) {
+ if ($widget->getModule() === $module && $widget->getAction() === $action) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get all widgets defined in the Piwik platform.
+ * @ignore
+ * @return static
+ */
+ public static function get()
+ {
+ $list = new static;
+
+ $widgets = StaticContainer::get('Piwik\Plugin\Widgets');
+
+ $widgetContainerConfigs = $widgets->getWidgetContainerConfigs();
+ foreach ($widgetContainerConfigs as $config) {
+ if ($config->isEnabled()) {
+ $list->addWidgetConfig($config);
+ }
+ }
+
+ $widgetConfigs = $widgets->getWidgetConfigs();
+ foreach ($widgetConfigs as $widget) {
+ if ($widget->isEnabled()) {
+ $list->addWidgetConfig($widget);
+ }
+ }
+
+ $reports = StaticContainer::get('Piwik\Plugin\Reports');
+ $reports = $reports->getAllReports();
+ foreach ($reports as $report) {
+ if ($report->isEnabled()) {
+ $factory = new ReportWidgetFactory($report);
+ $report->configureWidgets($list, $factory);
+ }
+ }
+
+ /**
+ * Triggered to filter widgets.
+ *
+ * **Example**
+ *
+ * public function addWidgetConfigs(Piwik\Widget\WidgetsList $list)
+ * {
+ * $list->remove($category='General_Visits'); // remove all widgets having this category
+ * }
+ *
+ * @param WidgetsList $list An instance of the WidgetsList. You can change the list of widgets this way.
+ */
+ Piwik::postEvent('Widget.filterWidgets', array($list));
+
+ return $list;
+ }
+
+ /**
+ * CAUTION! If you ever change this method, existing updates will fail as they currently use that method!
+ * If you change the output the uniqueId for existing widgets would not be found anymore
+ *
+ * Returns the unique id of an widget with the given parameters
+ *
+ * @param $controllerName
+ * @param $controllerAction
+ * @param array $customParameters
+ * @return string
+ */
+ public static function getWidgetUniqueId($controllerName, $controllerAction, $customParameters = array())
+ {
+ $widgetUniqueId = 'widget' . $controllerName . $controllerAction;
+
+ foreach ($customParameters as $name => $value) {
+ if (is_array($value)) {
+ // use 'Array' for backward compatibility;
+ // could we switch to using $value[0]?
+ $value = 'Array';
+ }
+ $widgetUniqueId .= $name . urlencode($value);
+ }
+
+ return $widgetUniqueId;
+ }
+
+}
diff --git a/core/WidgetsList.php b/core/WidgetsList.php
deleted file mode 100644
index e91a0f2b38..0000000000
--- a/core/WidgetsList.php
+++ /dev/null
@@ -1,287 +0,0 @@
- array(
- * array(...), // info about first widget in this category
- * array(...) // info about second widget in this category, etc.
- * ),
- * 'Visits' => array(
- * array(...),
- * array(...)
- * ),
- * )
- * ```
- */
- public static function get()
- {
- $cache = self::getCacheForCompleteList();
- $cacheId = self::getCacheId();
-
- if (!self::$listCacheToBeInvalidated && $cache->contains($cacheId)) {
- return $cache->fetch($cacheId);
- }
-
- self::addWidgets();
-
- uksort(self::$widgets, array('Piwik\WidgetsList', '_sortWidgetCategories'));
-
- $widgets = array();
- foreach (self::$widgets as $key => $v) {
- $category = Piwik::translate($key);
-
- if (isset($widgets[$category])) {
- $v = array_merge($widgets[$category], $v);
- }
-
- $widgets[$category] = $v;
- }
-
- $cache->save($cacheId, $widgets);
- self::$listCacheToBeInvalidated = false;
-
- return $widgets;
- }
-
- private static function addWidgets()
- {
- if (!self::$hookCalled) {
- self::$hookCalled = true;
-
- /**
- * @ignore
- * @deprecated
- */
- Piwik::postEvent('WidgetsList.addWidgets');
-
- $widgetsList = self::getInstance();
-
- foreach (Report::getAllReports() as $report) {
- if ($report->isEnabled()) {
- $report->configureWidget($widgetsList);
- }
- }
-
- $widgetContainers = Widgets::getAllWidgets();
- foreach ($widgetContainers as $widgetContainer) {
- $widgets = $widgetContainer->getWidgets();
-
- foreach ($widgets as $widget) {
- $widgetsList->add($widget['category'], $widget['name'], $widget['module'], $widget['method'], $widget['params']);
- }
- }
-
- foreach ($widgetContainers as $widgetContainer) {
- $widgetContainer->configureWidgetsList($widgetsList);
- }
- }
- }
-
- /**
- * Sorting method for widget categories
- *
- * @param string $a
- * @param string $b
- * @return bool
- */
- protected static function _sortWidgetCategories($a, $b)
- {
- $order = array(
- 'VisitsSummary_VisitsSummary',
- 'Live!',
- 'General_Visitors',
- 'General_VisitorSettings',
- 'DevicesDetection_DevicesDetection',
- 'General_Actions',
- 'Events_Events',
- 'Actions_SubmenuSitesearch',
- 'Referrers_Referrers',
- 'Goals_Goals',
- 'Goals_Ecommerce',
- '_others_',
- 'Example Widgets',
- 'ExamplePlugin_exampleWidgets',
- );
-
- if (($oa = array_search($a, $order)) === false) {
- $oa = array_search('_others_', $order);
- }
- if (($ob = array_search($b, $order)) === false) {
- $ob = array_search('_others_', $order);
- }
- return $oa > $ob;
- }
-
- /**
- * Returns the unique id of an widget with the given parameters
- *
- * @param $controllerName
- * @param $controllerAction
- * @param array $customParameters
- * @return string
- */
- public static function getWidgetUniqueId($controllerName, $controllerAction, $customParameters = array())
- {
- $widgetUniqueId = 'widget' . $controllerName . $controllerAction;
-
- foreach ($customParameters as $name => $value) {
- if (is_array($value)) {
- // use 'Array' for backward compatibility;
- // could we switch to using $value[0]?
- $value = 'Array';
- }
- $widgetUniqueId .= $name . $value;
- }
-
- return $widgetUniqueId;
- }
-
- /**
- * Adds a report to the list of dashboard widgets.
- *
- * @param string $widgetCategory The widget category. This can be a translation token.
- * @param string $widgetName The name of the widget. This can be a translation token.
- * @param string $controllerName The report's controller name (same as the plugin name).
- * @param string $controllerAction The report's controller action method name.
- * @param array $customParameters Extra query parameters that should be sent while getting
- * this report.
- */
- public static function add($widgetCategory, $widgetName, $controllerName, $controllerAction, $customParameters = array())
- {
- $widgetName = Piwik::translate($widgetName);
- $widgetUniqueId = self::getWidgetUniqueId($controllerName, $controllerAction, $customParameters);
-
- if (!array_key_exists($widgetCategory, self::$widgets)) {
- self::$widgets[$widgetCategory] = array();
- }
-
- self::$listCacheToBeInvalidated = true;
- self::$widgets[$widgetCategory][] = array(
- 'name' => $widgetName,
- 'uniqueId' => $widgetUniqueId,
- 'parameters' => array('module' => $controllerName,
- 'action' => $controllerAction
- ) + $customParameters
- );
- }
-
- /**
- * Removes one or more widgets from the widget list.
- *
- * @param string $widgetCategory The widget category. Can be a translation token.
- * @param string|false $widgetName The name of the widget to remove. Cannot be a
- * translation token. If not supplied, the entire category
- * will be removed.
- */
- public static function remove($widgetCategory, $widgetName = false)
- {
- if (!isset(self::$widgets[$widgetCategory])) {
- return;
- }
-
- if (empty($widgetName)) {
- unset(self::$widgets[$widgetCategory]);
- self::$listCacheToBeInvalidated = true;
- return;
- }
- foreach (self::$widgets[$widgetCategory] as $id => $widget) {
- if ($widget['name'] == $widgetName || $widget['name'] == Piwik::translate($widgetName)) {
- unset(self::$widgets[$widgetCategory][$id]);
- self::$listCacheToBeInvalidated = true;
- return;
- }
- }
- }
-
- /**
- * Returns `true` if a report exists in the widget list, `false` if otherwise.
- *
- * @param string $controllerName The controller name of the report.
- * @param string $controllerAction The controller action of the report.
- * @return bool
- */
- public static function isDefined($controllerName, $controllerAction)
- {
- $widgetsList = self::get();
- foreach ($widgetsList as $widgets) {
- foreach ($widgets as $widget) {
- if ($widget['parameters']['module'] == $controllerName
- && $widget['parameters']['action'] == $controllerAction
- ) {
- return true;
- }
- }
- }
- return false;
- }
-
- /**
- * Method to reset the widget list
- * For testing only
- * @ignore
- */
- public static function _reset()
- {
- self::$widgets = array();
- self::$hookCalled = false;
- self::getCacheForCompleteList()->delete(self::getCacheId());
- }
-
- private static function getCacheId()
- {
- return CacheId::pluginAware('WidgetsList');
- }
-
- private static function getCacheForCompleteList()
- {
- return PiwikCache::getTransientCache();
- }
-}
diff --git a/lang/en.json b/lang/en.json
index ff5dd3738b..5cdeef30a8 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -166,6 +166,7 @@
"ExceptionReportNotFound": "The requested report does not exist.",
"ExceptionWidgetNotFound": "The requested widget does not exist.",
"ExceptionReportNotEnabled": "The requested report is not enabled. This means usually either the plugin that defines the report is deactivated or you do not have enough permission to access this report.",
+ "ExceptionWidgetNotEnabled": "The requested widget is not enabled. This means usually either the plugin that defines the widget is deactivated or you do not have enough permission to access this widget.",
"ExpandDataTableFooter": "Change the visualization or configure the report",
"Export": "Export",
"ExportAsImage": "Export as Image",
diff --git a/misc/others/iframeWidget_localhost.php b/misc/others/iframeWidget_localhost.php
index 73bfc5c59b..a124b9767a 100644
--- a/misc/others/iframeWidget_localhost.php
+++ b/misc/others/iframeWidget_localhost.php
@@ -2,7 +2,7 @@
use Piwik\FrontController;
use Piwik\Url;
use Piwik\UrlHelper;
-use Piwik\WidgetsList;
+use Piwik\Widget\WidgetsList;
exit;
$date = date('Y-m-d');
@@ -34,15 +34,28 @@ require_once PIWIK_INCLUDE_PATH . "/index.php";
require_once PIWIK_INCLUDE_PATH . "/core/API/Request.php";
FrontController::getInstance()->init();
-$widgets = WidgetsList::get();
-foreach ($widgets as $category => $widgetsInCategory) {
+$widgets = WidgetsList::get()->getWidgetConfigs();
+$widgetCategoriesHandled = array();
+foreach ($widgets as $widgetConfig) {
+ $category = $widgetConfig->getCategoryId();
+
+ if (!empty($widgetCategoriesHandled[$category])) {
+ continue;
+ }
+ $widgetCategoriesHandled[$category] = true;
+
echo '' . $category . ' ';
- foreach ($widgetsInCategory as $widget) {
- echo '' . $widget['name'] . ' ';
+
+ foreach ($widgets as $widget) {
+ if ($category !== $widget->getCategoryId()) {
+ continue;
+ }
+
+ echo '' . \Piwik\Piwik::translate($widget->getName()) . ' ';
$widgetUrl = UrlHelper::getArrayFromQueryString($url);
- $widgetUrl['moduleToWidgetize'] = $widget['parameters']['module'];
- $widgetUrl['actionToWidgetize'] = $widget['parameters']['action'];
- $parameters = $widget['parameters'];
+ $widgetUrl['moduleToWidgetize'] = $widget->getModule();
+ $widgetUrl['actionToWidgetize'] = $widget->getAction();
+ $parameters = $widget->getParameters();
unset($parameters['module']);
unset($parameters['action']);
foreach ($parameters as $name => $value) {
@@ -54,7 +67,7 @@ foreach ($widgets as $category => $widgetsInCategory) {
$widgetUrl = Url::getQueryStringFromParameters($widgetUrl);
echo '
';
+ src="' . $widgetUrl . '" scrolling="no" frameborder="0" marginheight="0" marginwidth="0">';
}
}
diff --git a/plugins/API/API.php b/plugins/API/API.php
index 4ea0cb4055..8245d0712e 100644
--- a/plugins/API/API.php
+++ b/plugins/API/API.php
@@ -10,7 +10,10 @@ namespace Piwik\Plugins\API;
use Piwik\API\Proxy;
use Piwik\API\Request;
-use Piwik\Columns\Dimension;
+use Piwik\Cache;
+use Piwik\CacheId;
+use Piwik\Category\CategoryList;
+use Piwik\Common;
use Piwik\Config;
use Piwik\Container\StaticContainer;
use Piwik\DataTable;
@@ -23,11 +26,13 @@ use Piwik\Period;
use Piwik\Period\Range;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\VisitDimension;
+use Piwik\Plugin\Report;
use Piwik\Plugins\API\DataTable\MergeDataTables;
use Piwik\Plugins\CoreAdminHome\CustomLogo;
use Piwik\Translation\Translator;
use Piwik\Measurable\Type\TypeManager;
use Piwik\Version;
+use Piwik\Widget\WidgetsList;
require_once PIWIK_INCLUDE_PATH . '/core/Config.php';
@@ -122,83 +127,20 @@ class API extends \Piwik\Plugin\API
{
$isAuthenticatedWithViewAccess = Piwik::isUserHasViewAccess($idSites) && !Piwik::isUserIsAnonymous();
- $segments = array();
- foreach (Dimension::getAllDimensions() as $dimension) {
- foreach ($dimension->getSegments() as $segment) {
- if ($segment->isRequiresAtLeastViewAccess()) {
- $segment->setPermission($isAuthenticatedWithViewAccess);
- }
+ $sites = (is_array($idSites) ? implode('.', $idSites) : (int) $idSites);
+ $cache = Cache::getTransientCache();
+ $cachKey = 'API.getSegmentsMetadata' . $sites . '_' . (int) $_hideImplementationData . '_' . (int) $isAuthenticatedWithViewAccess;
+ $cachKey = CacheId::pluginAware($cachKey);
- $segments[] = $segment->toArray();
- }
+ if ($cache->contains($cachKey)) {
+ return $cache->fetch($cachKey);
}
- /**
- * Triggered when gathering all available segment dimensions.
- *
- * This event can be used to make new segment dimensions available.
- *
- * **Example**
- *
- * public function getSegmentsMetadata(&$segments, $idSites)
- * {
- * $segments[] = array(
- * 'type' => 'dimension',
- * 'category' => Piwik::translate('General_Visit'),
- * 'name' => 'General_VisitorIP',
- * 'segment' => 'visitIp',
- * 'acceptedValues' => '13.54.122.1, etc.',
- * 'sqlSegment' => 'log_visit.location_ip',
- * 'sqlFilter' => array('Piwik\IP', 'P2N'),
- * 'permission' => $isAuthenticatedWithViewAccess,
- * );
- * }
- *
- * @param array &$dimensions The list of available segment dimensions. Append to this list to add
- * new segments. Each element in this list must contain the
- * following information:
- *
- * - **type**: Either `'metric'` or `'dimension'`. `'metric'` means
- * the value is a numeric and `'dimension'` means it is
- * a string. Also, `'metric'` values will be displayed
- * under **Visit (metrics)** in the Segment Editor.
- * - **category**: The segment category name. This can be an existing
- * segment category visible in the segment editor.
- * - **name**: The pretty name of the segment. Can be a translation token.
- * - **segment**: The segment name, eg, `'visitIp'` or `'searches'`.
- * - **acceptedValues**: A string describing one or two exacmple values, eg
- * `'13.54.122.1, etc.'`.
- * - **sqlSegment**: The table column this segment will segment by.
- * For example, `'log_visit.location_ip'` for the
- * **visitIp** segment.
- * - **sqlFilter**: A PHP callback to apply to segment values before
- * they are used in SQL.
- * - **permission**: True if the current user has view access to this
- * segment, false if otherwise.
- * @param array $idSites The list of site IDs we're getting the available segments
- * for. Some segments (such as Goal segments) depend on the
- * site.
- */
- Piwik::postEvent('API.getSegmentDimensionMetadata', array(&$segments, $idSites));
-
- foreach ($segments as &$segment) {
- $segment['name'] = Piwik::translate($segment['name']);
- $segment['category'] = Piwik::translate($segment['category']);
-
- if ($_hideImplementationData) {
- unset($segment['sqlFilter']);
- unset($segment['sqlFilterValue']);
- unset($segment['sqlSegment']);
-
- if (isset($segment['suggestedValuesCallback'])
- && !is_string($segment['suggestedValuesCallback'])
- ) {
- unset($segment['suggestedValuesCallback']);
- }
- }
- }
+ $metadata = new SegmentMetadata();
+ $segments = $metadata->getSegmentsMetadata($idSites, $_hideImplementationData, $isAuthenticatedWithViewAccess);
+
+ $cache->save($cachKey, $segments);
- usort($segments, array($this, 'sortSegments'));
return $segments;
}
@@ -219,32 +161,6 @@ class API extends \Piwik\Plugin\API
return $values;
}
- private function sortSegments($row1, $row2)
- {
- $customVarCategory = Piwik::translate('CustomVariables_CustomVariables');
-
- $columns = array('type', 'category', 'name', 'segment');
- foreach ($columns as $column) {
- // Keep segments ordered alphabetically inside categories..
- $type = -1;
- if ($column == 'name') $type = 1;
-
- $compare = $type * strcmp($row1[$column], $row2[$column]);
-
- // hack so that custom variables "page" are grouped together in the doc
- if ($row1['category'] == $customVarCategory
- && $row1['category'] == $row2['category']
- ) {
- $compare = strcmp($row1['segment'], $row2['segment']);
- return $compare;
- }
- if ($compare != 0) {
- return $compare;
- }
- }
- return $compare;
- }
-
/**
* Returns the url to application logo (~280x110px)
*
@@ -342,6 +258,41 @@ class API extends \Piwik\Plugin\API
return $processed;
}
+ /**
+ * Get a list of all pages that shall be shown in a Piwik UI including a list of all widgets that shall
+ * be shown within each page.
+ *
+ * @param int $idSite
+ * @return array
+ */
+ public function getReportPagesMetadata($idSite)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+
+ $widgetsList = WidgetsList::get();
+ $categoryList = CategoryList::get();
+ $metadata = new WidgetMetadata();
+
+ return $metadata->getPagesMetadata($categoryList, $widgetsList);
+ }
+
+ /**
+ * Get a list of all widgetizable widgets.
+ *
+ * @param int $idSite
+ * @return array
+ */
+ public function getWidgetMetadata($idSite)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+
+ $widgetsList = WidgetsList::get();
+ $categoryList = CategoryList::get();
+ $metadata = new WidgetMetadata();
+
+ return $metadata->getWidgetMetadata($categoryList, $widgetsList);
+ }
+
/**
* Get a combined report of the *.get API methods.
*/
@@ -555,7 +506,12 @@ class API extends \Piwik\Plugin\API
if ($suggestedValuesCallbackRequiresTable) {
$values = call_user_func($segmentFound['suggestedValuesCallback'], $idSite, $maxSuggestionsToReturn, $table);
} else {
- $values = $this->getSegmentValuesFromVisitorLog($segmentName, $table);
+ // Cleanup data to return the top suggested (non empty) labels for this segment
+ $values = $table->getColumn($segmentName);
+
+ // Select also flattened keys (custom variables "page" scope, page URLs for one visit, page titles for one visit)
+ $valuesBis = $table->getColumnsStartingWith($segmentName . ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP);
+ $values = array_merge($values, $valuesBis);
}
$values = $this->getMostFrequentValues($values);
@@ -600,9 +556,7 @@ class API extends \Piwik\Plugin\API
// If you update this, also update flattenVisitorDetailsArray
$segmentsNeedActionsInfo = array('visitConvertedGoalId',
'pageUrl', 'pageTitle', 'siteSearchKeyword',
- 'entryPageTitle', 'entryPageUrl', 'exitPageTitle', 'exitPageUrl',
- 'outlinkUrl', 'downloadUrl'
- );
+ 'entryPageTitle', 'entryPageUrl', 'exitPageTitle', 'exitPageUrl');
$isCustomVariablePage = stripos($segmentName, 'customVariablePage') !== false;
$isEventSegment = stripos($segmentName, 'event') !== false;
$isContentSegment = stripos($segmentName, 'content') !== false;
diff --git a/plugins/API/ProcessedReport.php b/plugins/API/ProcessedReport.php
index d9d315b4c0..65b59a2051 100644
--- a/plugins/API/ProcessedReport.php
+++ b/plugins/API/ProcessedReport.php
@@ -23,9 +23,11 @@ use Piwik\Metrics\Formatter;
use Piwik\Period;
use Piwik\Piwik;
use Piwik\Plugin\Report;
+use Piwik\Plugin\Reports;
use Piwik\Site;
use Piwik\Timer;
use Piwik\Url;
+use Piwik\Category\Category;
class ProcessedReport
{
@@ -166,77 +168,14 @@ class ProcessedReport
$availableReports = array();
- foreach (Report::getAllReports() as $report) {
+ $reports = new Reports();
+ foreach ($reports->getAllReports() as $report) {
$report->configureReportMetadata($availableReports, $parameters);
}
- /**
- * Triggered when gathering metadata for all available reports.
- *
- * Plugins that define new reports should use this event to make them available in via
- * the metadata API. By doing so, the report will become available in scheduled reports
- * as well as in the Piwik Mobile App. In fact, any third party app that uses the metadata
- * API will automatically have access to the new report.
- *
- * @param string &$availableReports The list of available reports. Append to this list
- * to make a report available.
- *
- * Every element of this array must contain the following
- * information:
- *
- * - **category**: A translated string describing the report's category.
- * - **name**: The translated display title of the report.
- * - **module**: The plugin of the report.
- * - **action**: The API method that serves the report.
- *
- * The following information is optional:
- *
- * - **dimension**: The report's [dimension](/guides/all-about-analytics-data#dimensions) if any.
- * - **metrics**: An array mapping metric names with their display names.
- * - **metricsDocumentation**: An array mapping metric names with their
- * translated documentation.
- * - **processedMetrics**: The array of metrics in the report that are
- * calculated using existing metrics. Can be set to
- * `false` if the report contains no processed
- * metrics.
- * - **order**: The order of the report in the list of reports
- * with the same category.
- *
- * @param array $parameters Contains the values of the sites and period we are
- * getting reports for. Some reports depend on this data.
- * For example, Goals reports depend on the site IDs being
- * requested. Contains the following information:
- *
- * - **idSites**: The array of site IDs we are getting reports for.
- * - **period**: The period type, eg, `'day'`, `'week'`, `'month'`,
- * `'year'`, `'range'`.
- * - **date**: A string date within the period or a date range, eg,
- * `'2013-01-01'` or `'2012-01-01,2013-01-01'`.
- *
- * TODO: put dimensions section in all about analytics data
- * @deprecated since 2.5.0 Use Report Classes instead.
- * @ignore
- */
- Piwik::postEvent('API.getReportMetadata', array(&$availableReports, $parameters));
-
- // TODO we can remove this one once we remove API.getReportMetadata event (except hideMetricsDoc)
foreach ($availableReports as &$availableReport) {
- // can be removed once we remove hook API.getReportMetadata
- if (!isset($availableReport['metrics'])) {
- $availableReport['metrics'] = Metrics::getDefaultMetrics();
- }
- // can be removed once we remove hook API.getReportMetadata
- if (!isset($availableReport['processedMetrics'])) {
- $availableReport['processedMetrics'] = Metrics::getDefaultProcessedMetrics();
- }
-
- if ($hideMetricsDoc) // remove metric documentation if it's not wanted
- {
+ if ($hideMetricsDoc) {
unset($availableReport['metricsDocumentation']);
- } else if (!isset($availableReport['metricsDocumentation'])) {
- // set metric documentation to default if it's not set
- // can be removed once we remove hook API.getReportMetadata
- $availableReport['metricsDocumentation'] = Metrics::getDefaultMetricsDocumentation();
}
}
@@ -270,6 +209,9 @@ class ProcessedReport
$columnsToRemove = $this->getColumnsToRemove();
foreach ($availableReports as &$availableReport) {
+ $availableReport['category'] = Piwik::translate($availableReport['category']);
+ $availableReport['subcategory'] = Piwik::translate($availableReport['subcategory']);
+
// Ensure all metrics have a translation
$metrics = $availableReport['metrics'];
$cleanedMetrics = array();
@@ -349,16 +291,8 @@ class ProcessedReport
*/
private static function sortReports($a, $b)
{
- static $order = null;
- if (is_null($order)) {
- $order = array();
- foreach (Report::$orderOfReports as $category) {
- $order[] = Piwik::translate($category);
- }
- }
- return ($category = strcmp(array_search($a['category'], $order), array_search($b['category'], $order))) == 0
- ? (@$a['order'] < @$b['order'] ? -1 : 1)
- : $category;
+ $reports = new Reports();
+ return $reports->compareCategories($a['category'], $a['subcategory'], $a['order'], $b['category'], $b['subcategory'], $b['order']);
}
public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false,
diff --git a/plugins/API/Reports/Get.php b/plugins/API/Reports/Get.php
index de087d0a86..61cded3856 100644
--- a/plugins/API/Reports/Get.php
+++ b/plugins/API/Reports/Get.php
@@ -10,6 +10,7 @@ namespace Piwik\Plugins\API\Reports;
use Piwik\Piwik;
use Piwik\Plugin\Report;
+use Piwik\Plugin\Reports;
class Get extends Report
{
@@ -29,7 +30,7 @@ class Get extends Report
$this->module = 'API';
$this->action = 'get';
- $this->category = 'API';
+ $this->categoryId = 'API';
$this->name = Piwik::translate('General_MainMetrics');
$this->documentation = '';
@@ -80,8 +81,9 @@ class Get extends Report
*/
private function getReportsToMerge()
{
+ $reports = new Reports();
$result = array();
- foreach (Report::getAllReportClasses() as $reportClass) {
+ foreach ($reports->getAllReportClasses() as $reportClass) {
if ($reportClass == 'Piwik\\Plugins\\API\\Reports\\Get') {
continue;
}
diff --git a/plugins/API/SegmentMetadata.php b/plugins/API/SegmentMetadata.php
new file mode 100644
index 0000000000..9d7e563bfb
--- /dev/null
+++ b/plugins/API/SegmentMetadata.php
@@ -0,0 +1,167 @@
+getSegments() as $segment) {
+ $segments[] = $segment->toArray();
+ }
+ }
+
+ /**
+ * Triggered when gathering all available segment dimensions.
+ *
+ * This event can be used to make new segment dimensions available.
+ *
+ * **Example**
+ *
+ * public function getSegmentsMetadata(&$segments, $idSites)
+ * {
+ * $segments[] = array(
+ * 'type' => 'dimension',
+ * 'category' => Piwik::translate('General_Visit'),
+ * 'name' => 'General_VisitorIP',
+ * 'segment' => 'visitIp',
+ * 'acceptedValues' => '13.54.122.1, etc.',
+ * 'sqlSegment' => 'log_visit.location_ip',
+ * 'sqlFilter' => array('Piwik\IP', 'P2N'),
+ * 'permission' => $isAuthenticatedWithViewAccess,
+ * );
+ * }
+ *
+ * @param array &$dimensions The list of available segment dimensions. Append to this list to add
+ * new segments. Each element in this list must contain the
+ * following information:
+ *
+ * - **type**: Either `'metric'` or `'dimension'`. `'metric'` means
+ * the value is a numeric and `'dimension'` means it is
+ * a string. Also, `'metric'` values will be displayed
+ * under **Visit (metrics)** in the Segment Editor.
+ * - **category**: The segment category name. This can be an existing
+ * segment category visible in the segment editor.
+ * - **name**: The pretty name of the segment. Can be a translation token.
+ * - **segment**: The segment name, eg, `'visitIp'` or `'searches'`.
+ * - **acceptedValues**: A string describing one or two exacmple values, eg
+ * `'13.54.122.1, etc.'`.
+ * - **sqlSegment**: The table column this segment will segment by.
+ * For example, `'log_visit.location_ip'` for the
+ * **visitIp** segment.
+ * - **sqlFilter**: A PHP callback to apply to segment values before
+ * they are used in SQL.
+ * - **permission**: True if the current user has view access to this
+ * segment, false if otherwise.
+ * @param array $idSites The list of site IDs we're getting the available segments
+ * for. Some segments (such as Goal segments) depend on the
+ * site.
+ */
+ Piwik::postEvent('API.getSegmentDimensionMetadata', array(&$segments, $idSites));
+
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => Piwik::translate('General_Visit'),
+ 'name' => 'General_UserId',
+ 'segment' => 'userId',
+ 'acceptedValues' => 'any non empty unique string identifying the user (such as an email address or a username).',
+ 'sqlSegment' => 'log_visit.user_id',
+ 'permission' => $isAuthenticatedWithViewAccess,
+ );
+
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => Piwik::translate('General_Visit'),
+ 'name' => 'General_VisitorID',
+ 'segment' => 'visitorId',
+ 'acceptedValues' => '34c31e04394bdc63 - any 16 Hexadecimal chars ID, which can be fetched using the Tracking API function getVisitorId()',
+ 'sqlSegment' => 'log_visit.idvisitor',
+ 'sqlFilterValue' => array('Piwik\Common', 'convertVisitorIdToBin'),
+ 'permission' => $isAuthenticatedWithViewAccess,
+ );
+
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => Piwik::translate('General_Visit'),
+ 'name' => Piwik::translate('General_Visit') . " ID",
+ 'segment' => 'visitId',
+ 'acceptedValues' => 'Any integer. ',
+ 'sqlSegment' => 'log_visit.idvisit',
+ 'permission' => $isAuthenticatedWithViewAccess,
+ );
+
+ $segments[] = array(
+ 'type' => 'metric',
+ 'category' => Piwik::translate('General_Visit'),
+ 'name' => 'General_VisitorIP',
+ 'segment' => 'visitIp',
+ 'acceptedValues' => '13.54.122.1. Select IP ranges with notation: visitIp>13.54.122.0;visitIp<13.54.122.255',
+ 'sqlSegment' => 'log_visit.location_ip',
+ 'sqlFilterValue' => array('Piwik\Network\IPUtils', 'stringToBinaryIP'),
+ 'permission' => $isAuthenticatedWithViewAccess,
+ );
+
+ foreach ($segments as &$segment) {
+ $segment['name'] = Piwik::translate($segment['name']);
+ $segment['category'] = Piwik::translate($segment['category']);
+
+ if ($_hideImplementationData) {
+ unset($segment['sqlFilter']);
+ unset($segment['sqlFilterValue']);
+ unset($segment['sqlSegment']);
+ }
+
+ if (isset($segment['suggestedValuesCallback'])
+ && !is_string($segment['suggestedValuesCallback'])
+ ) {
+ unset($segment['suggestedValuesCallback']);
+ }
+ }
+
+ usort($segments, array($this, 'sortSegments'));
+
+ return $segments;
+ }
+
+ private function sortSegments($row1, $row2)
+ {
+ $customVarCategory = Piwik::translate('CustomVariables_CustomVariables');
+
+ $columns = array('type', 'category', 'name', 'segment');
+
+ foreach ($columns as $column) {
+ // Keep segments ordered alphabetically inside categories..
+ $type = -1;
+ if ($column == 'name') $type = 1;
+
+ $compare = $type * strcmp($row1[$column], $row2[$column]);
+
+ // hack so that custom variables "page" are grouped together in the doc
+ if ($row1['category'] == $customVarCategory
+ && $row1['category'] == $row2['category']
+ ) {
+ $compare = strcmp($row1['segment'], $row2['segment']);
+ return $compare;
+ }
+
+ if ($compare != 0) {
+ return $compare;
+ }
+ }
+
+ return $compare;
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/API/WidgetMetadata.php b/plugins/API/WidgetMetadata.php
new file mode 100644
index 0000000000..ef12800a39
--- /dev/null
+++ b/plugins/API/WidgetMetadata.php
@@ -0,0 +1,282 @@
+createMissingCategoriesAndSubcategories($categoryList, $widgetsList->getWidgetConfigs());
+
+ return $this->buildPagesMetadata($categoryList, $widgetsList);
+ }
+
+ public function getWidgetMetadata(CategoryList $categoryList, WidgetsList $widgetsList)
+ {
+ $this->createMissingCategoriesAndSubcategories($categoryList, $widgetsList->getWidgetConfigs());
+
+ $flat = array();
+
+ foreach ($widgetsList->getWidgetConfigs() as $widgetConfig) {
+
+ /** @var WidgetConfig[] $widgets */
+ $widgets = array($widgetConfig);
+ if ($widgetConfig instanceof WidgetContainerConfig) {
+ // so far we go only one level down, in theory these widgetConfigs could have again containers containing configs
+ $widgets = array_merge($widgets, $widgetConfig->getWidgetConfigs());
+ }
+
+ foreach ($widgets as $widget) {
+ // make sure to include only widgetizable widgets
+ if (!$widget->isWidgetizeable() || !$widget->getName()) {
+ continue;
+ }
+
+ $flat[] = $this->buildWidgetMetadata($widget, $categoryList);
+ }
+ }
+
+ usort($flat, array($this, 'sortWidgets'));
+
+ return $flat;
+ }
+
+ /**
+ * @param WidgetConfig $widget
+ * @param CategoryList|null $categoryList If null, no category information will be added to the widgets in first
+ * level (they will be added to nested widgets as potentially needed eg for
+ * widgets in ByDimensionView where they are needed to build the left menu)
+ * @return array
+ */
+ public function buildWidgetMetadata(WidgetConfig $widget, $categoryList = null)
+ {
+ $item = array(
+ 'name' => Piwik::translate($widget->getName())
+ );
+
+ if (isset($categoryList)) {
+ $category = $categoryList->getCategory($widget->getCategoryId());
+ $subcategory = $category ? $category->getSubcategory($widget->getSubcategoryId()) : null;
+
+ $item['category'] = $this->buildCategoryMetadata($category);
+ $item['subcategory'] = $this->buildSubcategoryMetadata($subcategory);
+ }
+
+ $item['module'] = $widget->getModule();
+ $item['action'] = $widget->getAction();
+ $item['order'] = $widget->getOrder();
+ $item['parameters'] = $widget->getParameters();
+ $item['uniqueId'] = $widget->getUniqueId();
+
+ $middleware = $widget->getMiddlewareParameters();
+
+ if (!empty($middleware)) {
+ $item['middlewareParameters'] = $middleware;
+ }
+
+ if ($widget instanceof ReportWidgetConfig) {
+ $item['viewDataTable'] = $widget->getViewDataTable();
+ $item['isReport'] = true;
+ }
+
+ if ($widget instanceof WidgetContainerConfig) {
+ $item['layout'] = $widget->getLayout();
+ $item['isContainer'] = true;
+
+ // we do not want to create categories to the inital categoryList. Otherwise we'd maybe display more pages
+ // etc.
+ $subCategoryList = new CategoryList();
+ $this->createMissingCategoriesAndSubcategories($subCategoryList, $widget->getWidgetConfigs());
+
+ $children = array();
+ foreach ($widget->getWidgetConfigs() as $widgetConfig) {
+ $children[] = $this->buildWidgetMetadata($widgetConfig, $subCategoryList);
+ }
+ $item['widgets'] = $children;
+ }
+
+ return $item;
+ }
+
+ private function sortWidgets($widgetA, $widgetB) {
+ $orderA = $widgetA['category']['order'];
+ $orderB = $widgetB['category']['order'];
+
+ if ($orderA === $orderB) {
+ if (!empty($widgetA['subcategory']['order']) && !empty($widgetB['subcategory']['order'])) {
+
+ $subOrderA = $widgetA['subcategory']['order'];
+ $subOrderB = $widgetB['subcategory']['order'];
+
+ if ($subOrderA === $subOrderB) {
+ return 0;
+ }
+
+ return $subOrderA > $subOrderB ? 1 : -1;
+
+ } elseif (!empty($orderA)) {
+
+ return 1;
+ }
+
+ return -1;
+ }
+
+ return $orderA > $orderB ? 1 : -1;
+ }
+
+ /**
+ * @param Category|null $category
+ * @return array
+ */
+ private function buildCategoryMetadata($category)
+ {
+ if (!isset($category)) {
+ return null;
+ }
+
+ return array(
+ 'id' => (string) $category->getId(),
+ 'name' => Piwik::translate($category->getId()),
+ 'order' => $category->getOrder(),
+ );
+ }
+
+ /**
+ * @param Subcategory|null $subcategory
+ * @return array
+ */
+ private function buildSubcategoryMetadata($subcategory)
+ {
+ if (!isset($subcategory)) {
+ return null;
+ }
+
+ return array(
+ 'id' => (string) $subcategory->getId(),
+ 'name' => Piwik::translate($subcategory->getName()),
+ 'order' => $subcategory->getOrder(),
+ );
+ }
+
+ /**
+ * @param CategoryList $categoryList
+ * @param WidgetConfig[] $widgetConfigs
+ */
+ private function createMissingCategoriesAndSubcategories($categoryList, $widgetConfigs)
+ {
+ // move reports into categories/subcategories and create missing ones if needed
+ foreach ($widgetConfigs as $widgetConfig) {
+ $categoryId = $widgetConfig->getCategoryId();
+ $subcategoryId = $widgetConfig->getSubcategoryId();
+
+ if (!$categoryId) {
+ continue;
+ }
+
+ if ($widgetConfig instanceof WidgetContainerConfig && !$widgetConfig->getWidgetConfigs()) {
+ // if a container does not contain any widgets, ignore it
+ continue;
+ }
+
+ if (!$categoryList->hasCategory($categoryId)) {
+ $categoryList->addCategory($this->createCategory($categoryId));
+ }
+
+ if (!$subcategoryId) {
+ continue;
+ }
+
+ $category = $categoryList->getCategory($categoryId);
+
+ if (!$category->hasSubcategory($subcategoryId)) {
+ $category->addSubcategory($this->createSubcategory($categoryId, $subcategoryId));
+ }
+ }
+ }
+
+ private function createCategory($categoryId)
+ {
+ $category = new Category();
+ $category->setId($categoryId);
+ return $category;
+ }
+
+ private function createSubcategory($categoryId, $subcategoryId)
+ {
+ $subcategory = new Subcategory();
+ $subcategory->setCategoryId($categoryId);
+ $subcategory->setId($subcategoryId);
+ return $subcategory;
+ }
+
+ /**
+ * @param CategoryList $categoryList
+ * @param WidgetsList $widgetsList
+ * @return array
+ */
+ private function buildPagesMetadata(CategoryList $categoryList, WidgetsList $widgetsList)
+ {
+ $pages = array();
+
+ $widgets = array();
+ foreach ($widgetsList->getWidgetConfigs() as $config) {
+ $pageId = $this->buildPageId($config->getCategoryId(), $config->getSubcategoryId());
+
+ if (!isset($widgets[$pageId])) {
+ $widgets[$pageId] = array();
+ }
+
+ $widgets[$pageId][] = $config;
+ }
+
+ foreach ($categoryList->getCategories() as $category) {
+ foreach ($category->getSubcategories() as $subcategory) {
+ $pageId = $this->buildPageId($category->getId(), $subcategory->getId());
+
+ if (!empty($widgets[$pageId])) {
+ $pages[] = $this->buildPageMetadata($category, $subcategory, $widgets[$pageId]);
+ }
+ }
+ }
+
+ return $pages;
+ }
+
+ private function buildPageId($categoryId, $subcategoryId)
+ {
+ return $categoryId . '.' . $subcategoryId;
+ }
+
+ public function buildPageMetadata(Category $category, Subcategory $subcategory, $widgetConfigs)
+ {
+ $ca = array(
+ 'uniqueId' => $this->buildPageId($category->getId(), $subcategory->getId()),
+ 'category' => $this->buildCategoryMetadata($category),
+ 'subcategory' => $this->buildSubcategoryMetadata($subcategory),
+ 'widgets' => array()
+ );
+
+ foreach ($widgetConfigs as $config) {
+ $ca['widgets'][] = $this->buildWidgetMetadata($config);
+ }
+
+ return $ca;
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/API/tests/Unit/WidgetMetadataTest.php b/plugins/API/tests/Unit/WidgetMetadataTest.php
new file mode 100644
index 0000000000..bb4379a010
--- /dev/null
+++ b/plugins/API/tests/Unit/WidgetMetadataTest.php
@@ -0,0 +1,278 @@
+metadata = new WidgetMetadata();
+ }
+
+ public function test_buildWidgetMetadata_ShouldGenerateMetadata()
+ {
+ $config = $this->createWidgetConfig('Test', 'CategoryId', 'SubcategoryId');
+ $list = $this->createCategoryList(array('CategoryId' => array('SubcategoryId')));
+ $metadata = $this->metadata->buildWidgetMetadata($config, $list);
+
+ $this->assertEquals(array(
+ 'name' => 'Test',
+ 'category' => array(
+ 'id' => 'CategoryId',
+ 'name' => 'CategoryId',
+ 'order' => 99,
+ ),
+ 'subcategory' => array(
+ 'id' => 'SubcategoryId',
+ 'name' => 'SubcategoryIdName',
+ 'order' => 99,
+ ),
+ 'module' => 'CoreHome',
+ 'action' => 'render',
+ 'order' => 99,
+ 'parameters' => array (
+ 'module' => 'CoreHome',
+ 'action' => 'render'
+ ),
+ 'uniqueId' => 'widgetCoreHomerender'
+ ), $metadata);
+ }
+
+ public function test_buildWidgetMetadata_ShouldSetCategoryAndSubcategoryToNull_IfBothGivenButNotExistInList()
+ {
+ $config = $this->createWidgetConfig('Test', 'CategoryId', 'SubcategoryId');
+ $list = $this->createCategoryList();
+ $metadata = $this->metadata->buildWidgetMetadata($config, $list);
+
+ $this->assertNull($metadata['category']);
+ $this->assertNull($metadata['subcategory']);
+ }
+
+ public function test_buildWidgetMetadata_ShouldSetSubcategoryToNull_IfCategoryGivenInListButSubcategoryNot()
+ {
+ $config = $this->createWidgetConfig('Test', 'CategoryId', 'SubcategoryId');
+ $list = $this->createCategoryList(array('CategoryId' => array()));
+ $metadata = $this->metadata->buildWidgetMetadata($config, $list);
+
+ $this->assertSame(array(
+ 'id' => 'CategoryId',
+ 'name' => 'CategoryId',
+ 'order' => 99,
+ ), $metadata['category']);
+ $this->assertNull($metadata['subcategory']);
+ }
+
+ public function test_buildWidgetMetadata_ShouldNotAddCategoryAndSubcategoryToNull_IfNoCategoryListGiven()
+ {
+ $config = $this->createWidgetConfig('Test', 'CategoryId', 'SubcategoryId');
+ $metadata = $this->metadata->buildWidgetMetadata($config);
+
+ $this->assertArrayNotHasKey('category', $metadata);
+ $this->assertArrayNotHasKey('subcategory', $metadata);
+ }
+
+ public function test_buildWidgetMetadata_ShouldAddOptionalMiddlewareParameters()
+ {
+ $config = $this->createWidgetConfig('Test', 'CategoryId', 'SubcategoryId');
+ $config->setMiddlewareParameters(array('module' => 'Goals', 'action' => 'hasAnyConversions'));
+ $metadata = $this->metadata->buildWidgetMetadata($config);
+
+ $this->assertSame(array('module' => 'Goals', 'action' => 'hasAnyConversions'), $metadata['middlewareParameters']);
+ }
+
+ public function test_buildWidgetMetadata_ShouldAddReportInformtion_IfReportWidgetConfigGiven()
+ {
+ $config = new ReportWidgetConfig();
+ $config->setDefaultViewDataTable('graph');
+ $metadata = $this->metadata->buildWidgetMetadata($config);
+
+ $this->assertSame('graph', $metadata['viewDataTable']);
+ $this->assertTrue($metadata['isReport']);
+ }
+
+ public function test_buildWidgetMetadata_ShouldAddContainerInformtion_IfWidgetContainerConfigGiven()
+ {
+ $config = new WidgetContainerConfig();
+ $config->setLayout('ByDimension');
+ $config->addWidgetConfig($this->createWidgetConfig('NestedName1', 'NestedCategory1', 'NestedSubcategory1'));
+ $config->addWidgetConfig($this->createWidgetConfig('NestedName2', 'NestedCategory2', 'NestedSubcategory2'));
+ $metadata = $this->metadata->buildWidgetMetadata($config);
+
+ $this->assertSame('ByDimension', $metadata['layout']);
+ $this->assertTrue($metadata['isContainer']);
+ $this->assertCount(2, $metadata['widgets']);
+
+ $widget1 = $metadata['widgets'][0];
+ $widget2 = $metadata['widgets'][1];
+ $this->assertSame(array(
+ 'name' => 'NestedName1',
+ 'category' => array (
+ 'id' => 'NestedCategory1',
+ 'name' => 'NestedCategory1',
+ 'order' => 99
+ ),
+ 'subcategory' => array (
+ 'id' => 'NestedSubcategory1',
+ 'name' => 'NestedSubcategory1',
+ 'order' => 99
+ ),
+ 'module' => 'CoreHome',
+ 'action' => 'render',
+ 'order' => 99,
+ 'parameters' => array (
+ 'module' => 'CoreHome',
+ 'action' => 'render',
+ ),
+ 'uniqueId' => 'widgetCoreHomerender'
+ ), $widget1);
+ $this->assertSame(array(
+ 'name' => 'NestedName2',
+ 'category' => array (
+ 'id' => 'NestedCategory2',
+ 'name' => 'NestedCategory2',
+ 'order' => 99
+ ),
+ 'subcategory' => array (
+ 'id' => 'NestedSubcategory2',
+ 'name' => 'NestedSubcategory2',
+ 'order' => 99
+ ),
+ 'module' => 'CoreHome',
+ 'action' => 'render',
+ 'order' => 99,
+ 'parameters' => array (
+ 'module' => 'CoreHome',
+ 'action' => 'render',
+ ),
+ 'uniqueId' => 'widgetCoreHomerender'
+ ), $widget2);
+ }
+
+ public function test_buildPageMetadata_ShouldAddContainerInformtion_IfWidgetContainerConfigGiven()
+ {
+ $config = new WidgetContainerConfig();
+ $config->setLayout('ByDimension');
+
+ $widgets = array(
+ $this->createWidgetConfig('NestedName1', 'NestedCategory1', 'NestedSubcategory1'),
+ $this->createWidgetConfig('NestedName2', 'NestedCategory2', 'NestedSubcategory1'),
+ );
+
+ $category = $this->createCategory('NestedCategory1');
+ $subcategory = $this->createSubcategory('NestedCategory1' ,'NestedSubcategory1');
+
+ $metadata = $this->metadata->buildPageMetadata($category, $subcategory, $widgets);
+
+ $this->assertSame(array(
+ 'uniqueId' => 'NestedCategory1.NestedSubcategory1',
+ 'category' => array (
+ 'id' => 'NestedCategory1',
+ 'name' => 'NestedCategory1',
+ 'order' => 99,
+ ),
+ 'subcategory' => array (
+ 'id' => 'NestedSubcategory1',
+ 'name' => 'NestedSubcategory1Name',
+ 'order' => 99,
+ ),
+ 'widgets' => array (
+ 0 => array ( // widgets should not have category / subcategory again, it's already present above
+ 'name' => 'NestedName1',
+ 'module' => 'CoreHome',
+ 'action' => 'render',
+ 'order' => 99,
+ 'parameters' => array (
+ 'module' => 'CoreHome',
+ 'action' => 'render',
+ ),
+ 'uniqueId' => 'widgetCoreHomerender',
+ ), array (
+ 'name' => 'NestedName2',
+ 'module' => 'CoreHome',
+ 'action' => 'render',
+ 'order' => 99,
+ 'parameters' => array (
+ 'module' => 'CoreHome',
+ 'action' => 'render',
+ ),
+ 'uniqueId' => 'widgetCoreHomerender'
+ )
+ )
+ ), $metadata);
+ }
+
+ private function createWidgetConfig($name, $categoryId, $subcategoryId = '')
+ {
+ $widgetConfig = new WidgetConfig();
+ $widgetConfig->setName($name);
+ $widgetConfig->setCategoryId($categoryId);
+ $widgetConfig->setSubcategoryId($subcategoryId);
+ $widgetConfig->setModule('CoreHome');
+ $widgetConfig->setAction('render');
+
+ return $widgetConfig;
+ }
+
+ private function createCategoryList($categories = array())
+ {
+ $list = new CategoryList();
+
+ foreach ($categories as $categoryId => $subcategoryIds) {
+ $category = $this->createCategory($categoryId);
+ $list->addCategory($category);
+
+ foreach ($subcategoryIds as $subcategoryId) {
+ $subcategory = $this->createSubcategory($categoryId, $subcategoryId);
+ $category->addSubcategory($subcategory);
+ }
+ }
+
+ return $list;
+ }
+
+ private function createSubcategory($categoryId, $subcategoryId)
+ {
+ $subcategory = new Subcategory();
+ $subcategory->setCategoryId($categoryId);
+ $subcategory->setId($subcategoryId);
+ $subcategory->setName($subcategoryId . 'Name');
+
+ return $subcategory;
+ }
+
+ private function createCategory($categoryId)
+ {
+ $category = new Category();
+ $category->setId($categoryId);
+ return $category;
+ }
+
+}
diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php
index 891400e122..e475f4240c 100644
--- a/plugins/Actions/API.php
+++ b/plugins/Actions/API.php
@@ -22,6 +22,7 @@ use Piwik\Plugins\Actions\Columns\Metrics\BounceRate;
use Piwik\Plugins\Actions\Columns\Metrics\ExitRate;
use Piwik\Plugins\CustomVariables\API as APICustomVariables;
use Piwik\Plugins\Actions\Actions\ActionSiteSearch;
+use Piwik\Plugin\Reports;
use Piwik\Tracker\Action;
use Piwik\Tracker\PageUrl;
@@ -55,7 +56,7 @@ class API extends \Piwik\Plugin\API
{
Piwik::checkUserHasViewAccess($idSite);
- $report = Report::factory("Actions", "get");
+ $report = Reports::factory("Actions", "get");
$archive = Archive::build($idSite, $period, $date, $segment);
$requestedColumns = Piwik::getArrayFromApiParameter($columns);
diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php
index 557e4c77b8..bca4396271 100644
--- a/plugins/Actions/Actions.php
+++ b/plugins/Actions/Actions.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\Actions;
use Piwik\ArchiveProcessor;
use Piwik\Common;
use Piwik\Db;
+use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines;
use Piwik\Site;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
diff --git a/plugins/Actions/Categories/DownloadsSubcategory.php b/plugins/Actions/Categories/DownloadsSubcategory.php
new file mode 100644
index 0000000000..ac432b11d1
--- /dev/null
+++ b/plugins/Actions/Categories/DownloadsSubcategory.php
@@ -0,0 +1,19 @@
+pluginName, 'getSiteSearchKeywords');
- $noResult = Report::factory($this->pluginName, 'getSiteSearchNoResultKeywords');
- $pageUrls = Report::factory($this->pluginName, 'getPageUrlsFollowingSiteSearch');
-
- $view->keywords = $keyword->render();
- $view->noResultKeywords = $noResult->render();
- $view->pagesUrlsFollowingSiteSearch = $pageUrls->render();
-
- $categoryTrackingEnabled = Actions::isCustomVariablesPluginsEnabled();
- if ($categoryTrackingEnabled) {
- $categories = Report::factory($this->pluginName, 'getSiteSearchCategories');
- $view->categories = $categories->render();
- }
-
- return $view->render();
- }
-
-}
diff --git a/plugins/Actions/Menu.php b/plugins/Actions/Menu.php
deleted file mode 100644
index 9a7424b4e9..0000000000
--- a/plugins/Actions/Menu.php
+++ /dev/null
@@ -1,29 +0,0 @@
-addActionsItem('', $this->urlForAction('menuGetPageUrls'), 15);
-
- $idSite = Common::getRequestVar('idSite', 0, 'int');
- $idSites = Common::getRequestVar('idSites', '', 'string');
-
- $actions = new Actions();
- if ($actions->isSiteSearchEnabled($idSites, $idSite)) {
- $menu->addActionsItem('Actions_SubmenuSitesearch', $this->urlForAction('indexSiteSearch'), 5);
- }
- }
-
-}
diff --git a/plugins/Actions/Reports/Base.php b/plugins/Actions/Reports/Base.php
index 45c9c0af9c..20e35593d8 100644
--- a/plugins/Actions/Reports/Base.php
+++ b/plugins/Actions/Reports/Base.php
@@ -21,7 +21,7 @@ abstract class Base extends \Piwik\Plugin\Report
{
protected function init()
{
- $this->category = 'General_Actions';
+ $this->categoryId = 'General_Actions';
$this->processedMetrics = false;
$this->recursiveLabelSeparator = '/';
}
diff --git a/plugins/Actions/Reports/GetDownloads.php b/plugins/Actions/Reports/GetDownloads.php
index f168cba878..92975b969e 100644
--- a/plugins/Actions/Reports/GetDownloads.php
+++ b/plugins/Actions/Reports/GetDownloads.php
@@ -26,8 +26,7 @@ class GetDownloads extends Base
$this->actionToLoadSubTables = $this->action;
$this->order = 9;
- $this->menuTitle = 'General_Downloads';
- $this->widgetTitle = 'General_Downloads';
+ $this->subcategoryId = 'General_Downloads';
}
public function getMetrics()
diff --git a/plugins/Actions/Reports/GetEntryPageTitles.php b/plugins/Actions/Reports/GetEntryPageTitles.php
index 7c029f4b02..9df1d74450 100644
--- a/plugins/Actions/Reports/GetEntryPageTitles.php
+++ b/plugins/Actions/Reports/GetEntryPageTitles.php
@@ -15,6 +15,9 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime;
use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage;
use Piwik\Plugins\Actions\Columns\Metrics\BounceRate;
use Piwik\Plugins\Actions\Columns\Metrics\ExitRate;
+use Piwik\Plugin\Reports;
+use Piwik\Report\ReportWidgetFactory;
+use Piwik\Widget\WidgetsList;
class GetEntryPageTitles extends Base
{
@@ -35,8 +38,11 @@ class GetEntryPageTitles extends Base
);
$this->order = 6;
$this->actionToLoadSubTables = $this->action;
+ }
- $this->widgetTitle = 'Actions_WidgetEntryPageTitles';
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
+ {
+ $widgetsList->addWidgetConfig($factory->createWidget()->setName('Actions_WidgetEntryPageTitles'));
}
public function getProcessedMetrics()
@@ -79,8 +85,8 @@ class GetEntryPageTitles extends Base
public function getRelatedReports()
{
return array(
- self::factory('Actions', 'getPageTitles'),
- self::factory('Actions', 'getEntryPageUrls')
+ Reports::factory('Actions', 'getPageTitles'),
+ Reports::factory('Actions', 'getEntryPageUrls')
);
}
}
diff --git a/plugins/Actions/Reports/GetEntryPageUrls.php b/plugins/Actions/Reports/GetEntryPageUrls.php
index f3795db099..c934c7ab10 100644
--- a/plugins/Actions/Reports/GetEntryPageUrls.php
+++ b/plugins/Actions/Reports/GetEntryPageUrls.php
@@ -17,6 +17,9 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime;
use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage;
use Piwik\Plugins\Actions\Columns\Metrics\BounceRate;
use Piwik\Plugins\Actions\Columns\Metrics\ExitRate;
+use Piwik\Plugin\Reports;
+use Piwik\Report\ReportWidgetFactory;
+use Piwik\Widget\WidgetsList;
class GetEntryPageUrls extends Base
{
@@ -40,8 +43,7 @@ class GetEntryPageUrls extends Base
$this->actionToLoadSubTables = $this->action;
- $this->menuTitle = 'Actions_SubmenuPagesEntry';
- $this->widgetTitle = 'Actions_WidgetPagesEntry';
+ $this->subcategoryId = 'Actions_SubmenuPagesEntry';
}
public function getProcessedMetrics()
@@ -83,7 +85,7 @@ class GetEntryPageUrls extends Base
public function getRelatedReports()
{
return array(
- self::factory('Actions', 'getEntryPageTitles'),
+ Reports::factory('Actions', 'getEntryPageTitles'),
);
}
}
diff --git a/plugins/Actions/Reports/GetExitPageTitles.php b/plugins/Actions/Reports/GetExitPageTitles.php
index 6cc5b1f320..53f235209d 100644
--- a/plugins/Actions/Reports/GetExitPageTitles.php
+++ b/plugins/Actions/Reports/GetExitPageTitles.php
@@ -15,6 +15,9 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime;
use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage;
use Piwik\Plugins\Actions\Columns\Metrics\BounceRate;
use Piwik\Plugins\Actions\Columns\Metrics\ExitRate;
+use Piwik\Plugin\Reports;
+use Piwik\Report\ReportWidgetFactory;
+use Piwik\Widget\WidgetsList;
class GetExitPageTitles extends Base
{
@@ -37,8 +40,13 @@ class GetExitPageTitles extends Base
$this->order = 7;
$this->actionToLoadSubTables = $this->action;
+ }
- $this->widgetTitle = 'Actions_WidgetExitPageTitles';
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
+ {
+ // we have to do it manually since it's only done automatically if a subcategoryId is specified,
+ // we do not set a subcategoryId since this report is not supposed to be shown in the UI
+ $widgetsList->addWidgetConfig($factory->createWidget());
}
public function getProcessedMetrics()
@@ -86,8 +94,8 @@ class GetExitPageTitles extends Base
public function getRelatedReports()
{
return array(
- self::factory('Actions', 'getPageTitles'),
- self::factory('Actions', 'getExitPageUrls'),
+ Reports::factory('Actions', 'getPageTitles'),
+ Reports::factory('Actions', 'getExitPageUrls'),
);
}
}
diff --git a/plugins/Actions/Reports/GetExitPageUrls.php b/plugins/Actions/Reports/GetExitPageUrls.php
index 2ef5d7a957..f46ac478cd 100644
--- a/plugins/Actions/Reports/GetExitPageUrls.php
+++ b/plugins/Actions/Reports/GetExitPageUrls.php
@@ -17,6 +17,9 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime;
use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage;
use Piwik\Plugins\Actions\Columns\Metrics\BounceRate;
use Piwik\Plugins\Actions\Columns\Metrics\ExitRate;
+use Piwik\Plugin\Reports;
+use Piwik\Report\ReportWidgetFactory;
+use Piwik\Widget\WidgetsList;
class GetExitPageUrls extends Base
{
@@ -40,8 +43,7 @@ class GetExitPageUrls extends Base
$this->order = 4;
- $this->menuTitle = 'Actions_SubmenuPagesExit';
- $this->widgetTitle = 'Actions_WidgetPagesExit';
+ $this->subcategoryId = 'Actions_SubmenuPagesExit';
}
public function getProcessedMetrics()
@@ -97,7 +99,7 @@ class GetExitPageUrls extends Base
public function getRelatedReports()
{
return array(
- self::factory('Actions', 'getExitPageTitles'),
+ Reports::factory('Actions', 'getExitPageTitles'),
);
}
diff --git a/plugins/Actions/Reports/GetOutlinks.php b/plugins/Actions/Reports/GetOutlinks.php
index adf21c572c..fb5e13e831 100644
--- a/plugins/Actions/Reports/GetOutlinks.php
+++ b/plugins/Actions/Reports/GetOutlinks.php
@@ -29,8 +29,7 @@ class GetOutlinks extends Base
$this->actionToLoadSubTables = $this->action;
- $this->menuTitle = 'General_Outlinks';
- $this->widgetTitle = 'General_Outlinks';
+ $this->subcategoryId = 'General_Outlinks';
}
public function getMetrics()
diff --git a/plugins/Actions/Reports/GetPageTitles.php b/plugins/Actions/Reports/GetPageTitles.php
index 8f7e193f9b..0c70086611 100644
--- a/plugins/Actions/Reports/GetPageTitles.php
+++ b/plugins/Actions/Reports/GetPageTitles.php
@@ -17,6 +17,9 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime;
use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage;
use Piwik\Plugins\Actions\Columns\Metrics\BounceRate;
use Piwik\Plugins\Actions\Columns\Metrics\ExitRate;
+use Piwik\Plugin\Reports;
+use Piwik\Report\ReportWidgetFactory;
+use Piwik\Widget\WidgetsList;
class GetPageTitles extends Base
{
@@ -40,8 +43,7 @@ class GetPageTitles extends Base
$this->actionToLoadSubTables = $this->action;
- $this->menuTitle = 'Actions_SubmenuPageTitles';
- $this->widgetTitle = 'Actions_WidgetPageTitles';
+ $this->subcategoryId = 'Actions_SubmenuPageTitles';
}
public function getMetrics()
@@ -81,8 +83,8 @@ class GetPageTitles extends Base
public function getRelatedReports()
{
return array(
- self::factory('Actions', 'getEntryPageTitles'),
- self::factory('Actions', 'getExitPageTitles'),
+ Reports::factory('Actions', 'getEntryPageTitles'),
+ Reports::factory('Actions', 'getExitPageTitles'),
);
}
}
diff --git a/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php b/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php
index 657e88211d..3d50ba4f95 100644
--- a/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php
+++ b/plugins/Actions/Reports/GetPageTitlesFollowingSiteSearch.php
@@ -15,6 +15,7 @@ use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime;
use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage;
use Piwik\Plugins\Actions\Columns\Metrics\BounceRate;
use Piwik\Plugins\Actions\Columns\Metrics\ExitRate;
+use Piwik\Plugin\Reports;
class GetPageTitlesFollowingSiteSearch extends SiteSearchBase
{
@@ -32,7 +33,8 @@ class GetPageTitlesFollowingSiteSearch extends SiteSearchBase
new AveragePageGenerationTime()
);
$this->order = 19;
- $this->widgetTitle = 'Actions_WidgetPageTitlesFollowingSearch';
+
+ $this->subcategoryId = 'Actions_SubmenuSitesearch';
}
public function configureView(ViewDataTable $view)
@@ -80,7 +82,7 @@ class GetPageTitlesFollowingSiteSearch extends SiteSearchBase
public function getRelatedReports()
{
return array(
- self::factory('Actions', 'getPageUrlsFollowingSiteSearch'),
+ Reports::factory('Actions', 'getPageUrlsFollowingSiteSearch'),
);
}
}
diff --git a/plugins/Actions/Reports/GetPageUrls.php b/plugins/Actions/Reports/GetPageUrls.php
index 0af899ef0e..2c850a5339 100644
--- a/plugins/Actions/Reports/GetPageUrls.php
+++ b/plugins/Actions/Reports/GetPageUrls.php
@@ -15,6 +15,8 @@ use Piwik\Plugins\Actions\Columns\Metrics\BounceRate;
use Piwik\Plugins\Actions\Columns\PageUrl;
use Piwik\Plugins\Actions\Columns\Metrics\ExitRate;
use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage;
+use Piwik\Report\ReportWidgetFactory;
+use Piwik\Widget\WidgetsList;
class GetPageUrls extends Base
{
@@ -37,10 +39,13 @@ class GetPageUrls extends Base
new AveragePageGenerationTime()
);
- $this->segmentSql = 'log_visit.visit_entry_idaction_url';
+ $this->segmentSql = 'log_visit.visit_entry_idaction_url';
+ $this->subcategoryId = 'General_Pages';
+ }
- $this->menuTitle = 'General_Pages';
- $this->widgetTitle = 'General_Pages';
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
+ {
+ $widgetsList->addWidgetConfig($factory->createWidget()->setName($this->subcategoryId));
}
public function getMetrics()
diff --git a/plugins/Actions/Reports/GetPageUrlsFollowingSiteSearch.php b/plugins/Actions/Reports/GetPageUrlsFollowingSiteSearch.php
index 2d8ba1a3a2..063d3855b5 100644
--- a/plugins/Actions/Reports/GetPageUrlsFollowingSiteSearch.php
+++ b/plugins/Actions/Reports/GetPageUrlsFollowingSiteSearch.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\Actions\Reports;
use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\Actions\Columns\DestinationPage;
+use Piwik\Plugin\Reports;
class GetPageUrlsFollowingSiteSearch extends GetPageTitlesFollowingSiteSearch
{
@@ -20,8 +21,9 @@ class GetPageUrlsFollowingSiteSearch extends GetPageTitlesFollowingSiteSearch
$this->dimension = new DestinationPage();
$this->name = Piwik::translate('Actions_WidgetPageUrlsFollowingSearch');
$this->documentation = Piwik::translate('Actions_SiteSearchFollowingPagesDoc') . ' ' . Piwik::translate('General_UsePlusMinusIconsDocumentation');
- $this->order = 18;
- $this->widgetTitle = 'Actions_WidgetPageUrlsFollowingSearch';
+ $this->order = 16;
+
+ $this->subcategoryId = 'Actions_SubmenuSitesearch';
}
public function configureView(ViewDataTable $view)
@@ -34,7 +36,7 @@ class GetPageUrlsFollowingSiteSearch extends GetPageTitlesFollowingSiteSearch
public function getRelatedReports()
{
return array(
- self::factory('Actions', 'getPageTitlesFollowingSiteSearch'),
+ Reports::factory('Actions', 'getPageTitlesFollowingSiteSearch'),
);
}
}
diff --git a/plugins/Actions/Reports/GetSiteSearchCategories.php b/plugins/Actions/Reports/GetSiteSearchCategories.php
index 6d41e3c334..5388ff9198 100644
--- a/plugins/Actions/Reports/GetSiteSearchCategories.php
+++ b/plugins/Actions/Reports/GetSiteSearchCategories.php
@@ -23,8 +23,9 @@ class GetSiteSearchCategories extends SiteSearchBase
$this->name = Piwik::translate('Actions_WidgetSearchCategories');
$this->documentation = Piwik::translate('Actions_SiteSearchCategories1') . ' ' . Piwik::translate('Actions_SiteSearchCategories2');
$this->metrics = array('nb_visits', 'nb_pages_per_search', 'exit_rate');
- $this->order = 17;
- $this->widgetTitle = 'Actions_WidgetSearchCategories';
+ $this->order = 20;
+
+ $this->subcategoryId = 'Actions_SubmenuSitesearch';
}
protected function isEnabledForIdSites($idSites, $idSite)
diff --git a/plugins/Actions/Reports/GetSiteSearchKeywords.php b/plugins/Actions/Reports/GetSiteSearchKeywords.php
index d88684cd15..dc4b5e2d16 100644
--- a/plugins/Actions/Reports/GetSiteSearchKeywords.php
+++ b/plugins/Actions/Reports/GetSiteSearchKeywords.php
@@ -33,7 +33,7 @@ class GetSiteSearchKeywords extends SiteSearchBase
new AveragePageGenerationTime()
);
$this->order = 15;
- $this->widgetTitle = 'Actions_WidgetSearchKeywords';
+ $this->subcategoryId = 'Actions_SubmenuSitesearch';
}
public function getMetrics()
diff --git a/plugins/Actions/Reports/GetSiteSearchNoResultKeywords.php b/plugins/Actions/Reports/GetSiteSearchNoResultKeywords.php
index 94b19e8566..c1c01ca504 100644
--- a/plugins/Actions/Reports/GetSiteSearchNoResultKeywords.php
+++ b/plugins/Actions/Reports/GetSiteSearchNoResultKeywords.php
@@ -31,8 +31,9 @@ class GetSiteSearchNoResultKeywords extends SiteSearchBase
new ExitRate(),
new AveragePageGenerationTime()
);
- $this->order = 16;
- $this->widgetTitle = 'Actions_WidgetSearchNoResultKeywords';
+ $this->order = 18;
+
+ $this->subcategoryId = 'Actions_SubmenuSitesearch';
}
public function getMetrics()
diff --git a/plugins/Actions/Reports/SiteSearchBase.php b/plugins/Actions/Reports/SiteSearchBase.php
index e30a2243d8..d4715e04e6 100644
--- a/plugins/Actions/Reports/SiteSearchBase.php
+++ b/plugins/Actions/Reports/SiteSearchBase.php
@@ -18,7 +18,7 @@ abstract class SiteSearchBase extends Base
protected function init()
{
parent::init();
- $this->category = 'Actions_SubmenuSitesearch';
+ $this->categoryId = 'General_Actions';
}
public function isEnabled()
diff --git a/plugins/Actions/templates/indexSiteSearch.twig b/plugins/Actions/templates/indexSiteSearch.twig
deleted file mode 100644
index 8d9eaa0909..0000000000
--- a/plugins/Actions/templates/indexSiteSearch.twig
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
{{ 'Actions_WidgetSearchKeywords'|translate }}
- {{ keywords|raw }}
-
- {{ 'Actions_WidgetSearchNoResultKeywords'|translate }}
- {{ noResultKeywords|raw }}
-
- {% if categories is defined %}
- {{ 'Actions_WidgetSearchCategories'|translate }}
- {{ categories|raw }}
- {% endif %}
-
-
-
-
{{ 'Actions_WidgetPageUrlsFollowingSearch'|translate }}
- {{ pagesUrlsFollowingSiteSearch|raw }}
-
-
-
diff --git a/plugins/Contents/Categories/ContentsSubcategory.php b/plugins/Contents/Categories/ContentsSubcategory.php
new file mode 100644
index 0000000000..735d376e4f
--- /dev/null
+++ b/plugins/Contents/Categories/ContentsSubcategory.php
@@ -0,0 +1,19 @@
+ 'addMetricTranslations',
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
- 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
+ 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles'
);
}
diff --git a/plugins/Contents/Controller.php b/plugins/Contents/Controller.php
deleted file mode 100644
index a17ec3fdce..0000000000
--- a/plugins/Contents/Controller.php
+++ /dev/null
@@ -1,51 +0,0 @@
-pluginName, 'getContentNames');
- $contentPieces = Report::factory($this->pluginName, 'getContentPieces');
- $reports = array($contentNames, $contentPieces);
-
- foreach($reports as $report) {
- $reportsView->addReport(
- $report->getCategory(),
- $report->getName(),
- 'Contents.' . Report::PREFIX_ACTION_IN_MENU . ucfirst($report->getAction())
- );
- }
-
- return $reportsView->render();
- }
-
- public function menuGetContentNames()
- {
- $report = Report::factory($this->pluginName, 'getContentNames');
-
- return View::singleReport($report->getName(), $report->render());
- }
-
- public function menuGetContentPieces()
- {
- $report = Report::factory($this->pluginName, 'getContentPieces');
-
- return View::singleReport($report->getName(), $report->render());
- }
-
-}
diff --git a/plugins/Contents/Menu.php b/plugins/Contents/Menu.php
deleted file mode 100644
index 2e0d25bb7f..0000000000
--- a/plugins/Contents/Menu.php
+++ /dev/null
@@ -1,24 +0,0 @@
-addActionsItem('Contents_Contents', array('module' => 'Contents', 'action' => 'index'), $orderId = 40);
- }
-}
diff --git a/plugins/Contents/Reports/Base.php b/plugins/Contents/Reports/Base.php
index f07baf973d..e323bebdbe 100644
--- a/plugins/Contents/Reports/Base.php
+++ b/plugins/Contents/Reports/Base.php
@@ -8,18 +8,27 @@
*/
namespace Piwik\Plugins\Contents\Reports;
-use Piwik\Columns\Dimension;
use Piwik\Common;
use Piwik\Piwik;
use Piwik\Plugin\Report;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\Contents\Dimensions;
+use Piwik\Report\ReportWidgetFactory;
+use Piwik\Widget\WidgetsList;
abstract class Base extends Report
{
protected function init()
{
- $this->category = 'General_Actions';
+ $this->categoryId = 'General_Actions';
+ $this->subcategoryId = 'Contents_Contents';
+ }
+
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
+ {
+ $widget = $factory->createWidget();
+
+ $widgetsList->addToContainerWidget('Contents', $widget);
}
/**
diff --git a/plugins/Contents/Reports/GetContentNames.php b/plugins/Contents/Reports/GetContentNames.php
index d217af5d71..6afb8e071e 100644
--- a/plugins/Contents/Reports/GetContentNames.php
+++ b/plugins/Contents/Reports/GetContentNames.php
@@ -32,7 +32,6 @@ class GetContentNames extends Base
$this->order = 35;
$this->actionToLoadSubTables = 'getContentNames';
- $this->widgetTitle = 'Contents_ContentName';
$this->metrics = array('nb_impressions', 'nb_interactions');
$this->processedMetrics = array(new InteractionRate());
}
diff --git a/plugins/Contents/Reports/GetContentPieces.php b/plugins/Contents/Reports/GetContentPieces.php
index 7ab3f1ed1c..ede8da9405 100644
--- a/plugins/Contents/Reports/GetContentPieces.php
+++ b/plugins/Contents/Reports/GetContentPieces.php
@@ -32,8 +32,6 @@ class GetContentPieces extends Base
$this->order = 36;
$this->actionToLoadSubTables = 'getContentPieces';
- $this->widgetTitle = 'Contents_ContentPiece';
-
$this->metrics = array('nb_impressions', 'nb_interactions');
$this->processedMetrics = array(new InteractionRate());
}
diff --git a/plugins/Contents/Widgets/ContentsByDimension.php b/plugins/Contents/Widgets/ContentsByDimension.php
new file mode 100644
index 0000000000..3f2291e8d8
--- /dev/null
+++ b/plugins/Contents/Widgets/ContentsByDimension.php
@@ -0,0 +1,21 @@
+January 3 – 9, 2010
Actions
+ Contents
Content Name
Contents
getContentNames
diff --git a/plugins/Contents/tests/System/expected/test_Contents_Contents.getContentPieces_lastN__API.getProcessedReport_day.xml b/plugins/Contents/tests/System/expected/test_Contents_Contents.getContentPieces_lastN__API.getProcessedReport_day.xml
index b43860be63..f0115ea581 100644
--- a/plugins/Contents/tests/System/expected/test_Contents_Contents.getContentPieces_lastN__API.getProcessedReport_day.xml
+++ b/plugins/Contents/tests/System/expected/test_Contents_Contents.getContentPieces_lastN__API.getProcessedReport_day.xml
@@ -4,6 +4,7 @@
January 3 – 9, 2010
Actions
+ Contents
Content Piece
Contents
getContentPieces
diff --git a/plugins/CoreConsole/Commands/GenerateReport.php b/plugins/CoreConsole/Commands/GenerateReport.php
index 9eb7313400..3f9fda4939 100644
--- a/plugins/CoreConsole/Commands/GenerateReport.php
+++ b/plugins/CoreConsole/Commands/GenerateReport.php
@@ -10,8 +10,10 @@
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Columns\Dimension;
+use Piwik\Piwik;
use Piwik\Plugin\Manager;
use Piwik\Plugin\Report;
+use Piwik\Plugin\Reports;
use Piwik\Translate;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
@@ -67,8 +69,8 @@ class GenerateReport extends GeneratePluginBase
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
$this->writeSuccessMessage($output, array(
- sprintf('Reports/%s.php for %s generated.', ucfirst($apiName), $pluginName),
- 'You should now implement the method called "' . lcfirst($apiName) . '" in API.php',
+ sprintf('plugins/%s/Reports/%s.php for %s generated.', $pluginName, ucfirst($apiName)),
+ 'You should now implement the method called "' . lcfirst($apiName) . '()" in API.php',
// 'Read more about this here: link to developer guide',
'Enjoy!'
));
@@ -78,8 +80,10 @@ class GenerateReport extends GeneratePluginBase
{
$order = 1;
- foreach (Report::getAllReports() as $report) {
- if ($report->getCategory() === $category) {
+ $reports = new Reports();
+
+ foreach ($reports->getAllReports() as $report) {
+ if ($report->getCategoryId() === $category) {
if ($report->getOrder() > $order) {
$order = $report->getOrder() + 1;
}
@@ -189,10 +193,12 @@ class GenerateReport extends GeneratePluginBase
$category = $input->getOption('category');
+ $reports = new Reports();
+
$categories = array();
- foreach (Report::getAllReports() as $report) {
- if ($report->getCategory()) {
- $categories[] = $report->getCategory();
+ foreach ($reports->getAllReports() as $report) {
+ if ($report->getCategoryId()) {
+ $categories[] = Piwik::translate($report->getCategoryId());
}
}
$categories = array_values(array_unique($categories));
@@ -226,7 +232,9 @@ class GenerateReport extends GeneratePluginBase
$dimensions = array();
$dimensionNames = array();
- foreach (Report::getAllReports() as $report) {
+ $reports = new Reports();
+
+ foreach ($reports->getAllReports() as $report) {
$dimension = $report->getDimension();
if (is_object($dimension)) {
$name = $dimension->getName();
diff --git a/plugins/CoreConsole/Commands/GenerateWidget.php b/plugins/CoreConsole/Commands/GenerateWidget.php
index da4f8e349d..05f92bd4af 100644
--- a/plugins/CoreConsole/Commands/GenerateWidget.php
+++ b/plugins/CoreConsole/Commands/GenerateWidget.php
@@ -10,8 +10,8 @@
namespace Piwik\Plugins\CoreConsole\Commands;
use Piwik\Piwik;
-use Piwik\Plugin\Widgets;
use Piwik\Translate;
+use Piwik\Widget\WidgetsList;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
@@ -25,6 +25,7 @@ class GenerateWidget extends GeneratePluginBase
$this->setName('generate:widget')
->setDescription('Adds a plugin widget class to an existing plugin')
->addOption('pluginname', null, InputOption::VALUE_REQUIRED, 'The name of an existing plugin which does not have any widgets defined yet')
+ ->addOption('widgetname', null, InputOption::VALUE_REQUIRED, 'The name of the widget you want to create')
->addOption('category', null, InputOption::VALUE_REQUIRED, 'The name of the category the widget should belong to');
}
@@ -33,6 +34,7 @@ class GenerateWidget extends GeneratePluginBase
$pluginName = $this->getPluginName($input, $output);
$this->checkAndUpdateRequiredPiwikVersion($pluginName, $output);
+ $widgetName = $this->getWidgetName($input, $output);
$category = $this->getCategory($input, $output);
if ($category === Piwik::translate($category)) {
@@ -40,26 +42,78 @@ class GenerateWidget extends GeneratePluginBase
$category = $this->makeTranslationIfPossible($pluginName, $category);
}
+ $widgetMethod = $this->getWidgetMethodName($widgetName);
+ $widgetClass = ucfirst($widgetMethod);
+
$exampleFolder = PIWIK_INCLUDE_PATH . '/plugins/ExamplePlugin';
- $replace = array('ExamplePlugin' => $pluginName,
- 'Example Category' => $category);
- $whitelistFiles = array('/Widgets.php');
+ $replace = array('ExamplePlugin' => $pluginName,
+ 'MyExampleWidget' => $widgetClass,
+ 'Example Widget Name' => $this->makeTranslationIfPossible($pluginName, $widgetName),
+ 'Example Widgets' => $category);
+ $whitelistFiles = array('/Widgets', '/Widgets/MyExampleWidget.php');
$this->copyTemplateToPlugin($exampleFolder, $pluginName, $replace, $whitelistFiles);
$this->writeSuccessMessage($output, array(
- sprintf('Widgets.php for %s generated.', $pluginName),
- 'You can now start defining your plugin widgets',
+ sprintf('plugins/%s/Widgets/%s.php generated.', $pluginName, $widgetClass),
+ 'You can now start implementing the render() method.',
'Enjoy!'
));
}
+ private function getWidgetMethodName($methodName)
+ {
+ $methodName = trim($methodName);
+ $methodName = str_replace(' ', '', $methodName);
+ $methodName = preg_replace("/[^A-Za-z0-9]/", '', $methodName);
+
+ if (0 !== strpos(strtolower($methodName), 'get')) {
+ $methodName = 'get' . ucfirst($methodName);
+ }
+
+ return lcfirst($methodName);
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @return array
+ * @throws \RuntimeException
+ */
+ protected function getWidgetName(InputInterface $input, OutputInterface $output)
+ {
+ $validate = function ($widgetName) {
+ if (empty($widgetName)) {
+ throw new \InvalidArgumentException('Please enter the name of your widget');
+ }
+
+ if (preg_match("/[^A-Za-z0-9 ]/", $widgetName)) {
+ throw new \InvalidArgumentException('Only alpha numerical characters and whitespaces are allowed');
+ }
+
+ return $widgetName;
+ };
+
+ $widgetName = $input->getOption('widgetname');
+
+ if (empty($widgetName)) {
+ $dialog = $this->getHelperSet()->get('dialog');
+ $widgetName = $dialog->askAndValidate($output, 'Enter the name of your Widget, for instance "Browser Families": ', $validate);
+ } else {
+ $validate($widgetName);
+ }
+
+ $widgetName = ucfirst($widgetName);
+
+ return $widgetName;
+ }
+
protected function getExistingCategories()
{
$categories = array();
- foreach (Widgets::getAllWidgets() as $widget) {
- if ($widget->getCategory()) {
- $categories[] = Piwik::translate($widget->getCategory());
+ foreach (WidgetsList::get()->getWidgetConfigs() as $widget) {
+ if ($widget->getCategoryId()) {
+ $categories[] = Piwik::translate($widget->getCategoryId());
}
}
$categories = array_values(array_unique($categories));
@@ -111,8 +165,8 @@ class GenerateWidget extends GeneratePluginBase
*/
protected function getPluginName(InputInterface $input, OutputInterface $output)
{
- $pluginNames = $this->getPluginNamesHavingNotSpecificFile('Widgets.php');
- $invalidName = 'You have to enter the name of an existing plugin which does not already have any widgets defined';
+ $pluginNames = $this->getPluginNames();
+ $invalidName = 'You have to enter a name of an existing plugin.';
return $this->askPluginNameAndValidate($input, $output, $pluginNames, $invalidName);
}
diff --git a/plugins/CoreHome/Categories/ActionsCategory.php b/plugins/CoreHome/Categories/ActionsCategory.php
new file mode 100644
index 0000000000..e995df110d
--- /dev/null
+++ b/plugins/CoreHome/Categories/ActionsCategory.php
@@ -0,0 +1,17 @@
+getPrettyTimeFromSeconds($value);
+ return $formatter->getPrettyTimeFromSeconds($value, true);
}
public function getTranslatedName()
diff --git a/plugins/CoreHome/Controller.php b/plugins/CoreHome/Controller.php
index a61419e5fb..a0866c0ff2 100644
--- a/plugins/CoreHome/Controller.php
+++ b/plugins/CoreHome/Controller.php
@@ -11,12 +11,13 @@ namespace Piwik\Plugins\CoreHome;
use Exception;
use Piwik\API\Request;
use Piwik\Common;
+use Piwik\DataTable\Renderer\Json;
use Piwik\Date;
use Piwik\FrontController;
-use Piwik\Menu\MenuReporting;
use Piwik\Notification\Manager as NotificationManager;
use Piwik\Piwik;
use Piwik\Plugin\Report;
+use Piwik\Widget\Widget;
use Piwik\Plugins\CoreHome\DataTableRowAction\MultiRowEvolution;
use Piwik\Plugins\CoreHome\DataTableRowAction\RowEvolution;
use Piwik\Plugins\CorePluginsAdmin\MarketplaceApiClient;
@@ -28,7 +29,6 @@ use Piwik\UpdateCheck;
use Piwik\Url;
use Piwik\View;
use Piwik\ViewDataTable\Manager as ViewDataTableManager;
-use Piwik\Plugin\Widgets as PluginWidgets;
class Controller extends \Piwik\Plugin\Controller
{
@@ -49,40 +49,38 @@ class Controller extends \Piwik\Plugin\Controller
return 'redirectToCoreHomeIndex';
}
- public function renderReportMenu(Report $report)
+ public function renderReportWidget(Report $report)
{
Piwik::checkUserHasSomeViewAccess();
$this->checkSitePermission();
$report->checkIsEnabled();
- $menuTitle = $report->getMenuTitle();
-
- if (empty($menuTitle)) {
- throw new Exception('This report is not supposed to be displayed in the menu, please define a $menuTitle in your report.');
- }
-
- $menuTitle = $this->translator->translate($menuTitle);
- $content = $this->renderReportWidget($report);
-
- return View::singleReport($menuTitle, $content);
+ return $report->render();
}
- public function renderReportWidget(Report $report)
+ public function renderWidgetContainer()
{
Piwik::checkUserHasSomeViewAccess();
$this->checkSitePermission();
- $report->checkIsEnabled();
+ $view = new View('@CoreHome/widgetContainer');
+ $view->isWidgetized = (bool) Common::getRequestVar('widget', 0, 'int');
+ $view->containerId = Common::getRequestVar('containerId', null, 'string');
- return $report->render();
+ return $view->render();
}
- public function renderWidget(PluginWidgets $widget, $method)
+ /**
+ * @param Widget $widget
+ * @return mixed
+ * @throws Exception
+ */
+ public function renderWidget($widget)
{
Piwik::checkUserHasSomeViewAccess();
- return $widget->$method();
+ return $widget->render();
}
function redirectToCoreHomeIndex()
@@ -133,7 +131,6 @@ class Controller extends \Piwik\Plugin\Controller
{
$view = new View('@CoreHome/getDefaultIndexView');
$this->setGeneralVariablesView($view);
- $view->menu = MenuReporting::getInstance()->getMenu();
$view->dashboardSettingsControl = new DashboardManagerControl();
$view->content = '';
return $view;
diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php
index 23da8326aa..c8b2b428d0 100644
--- a/plugins/CoreHome/CoreHome.php
+++ b/plugins/CoreHome/CoreHome.php
@@ -13,6 +13,13 @@ namespace Piwik\Plugins\CoreHome;
*/
class CoreHome extends \Piwik\Plugin
{
+ /**
+ * Defines a widget container layout that will display all widgets within a container inside a "tab" menu
+ * where on the left side a link is shown for each widget and on the right side the selected widget.
+ * @api
+ */
+ const WIDGET_CONTAINER_LAYOUT_BY_DIMENSION = 'ByDimension';
+
/**
* @see Piwik\Plugin::registerEvents
*/
@@ -105,8 +112,6 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/javascripts/dataTable_rowactions.js";
$jsFiles[] = "plugins/CoreHome/javascripts/popover.js";
$jsFiles[] = "plugins/CoreHome/javascripts/broadcast.js";
- $jsFiles[] = "plugins/CoreHome/javascripts/menu.js";
- $jsFiles[] = "plugins/CoreHome/javascripts/menu_init.js";
$jsFiles[] = "plugins/CoreHome/javascripts/calendar.js";
$jsFiles[] = "plugins/CoreHome/javascripts/sparkline.js";
$jsFiles[] = "plugins/CoreHome/javascripts/corehome.js";
@@ -120,8 +125,12 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/piwikApp.config.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/services/service.module.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/services/global-ajax-queue.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/services/piwik.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/services/piwik-api.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/services/piwik-url.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/services/report-metadata-model.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/services/reporting-pages-model.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/filter.module.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/translate.js";
@@ -130,6 +139,7 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/length.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/trim.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/pretty-url.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/escape.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/directive.module.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/autocomplete-matched.js";
@@ -146,6 +156,8 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/history/history.service.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js";
+
$jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector-model.service.js";
$jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector.controller.js";
$jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector.directive.js";
@@ -163,6 +175,21 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/ajax-form/ajax-form.controller.js";
$jsFiles[] = "plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js";
+
+ $jsFiles[] = "plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/widget/widget.directive.js";
+
+ $jsFiles[] = "plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js";
+
+ $jsFiles[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js";
+
+ $jsFiles[] = "plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js";
}
public function getClientSideTranslationKeys(&$translationKeys)
@@ -258,5 +285,6 @@ class CoreHome extends \Piwik\Plugin
$translationKeys[] = 'CoreHome_UndoPivotBySubtable';
$translationKeys[] = 'CoreHome_PivotBySubtable';
$translationKeys[] = 'General_LearnMore';
+ $translationKeys[] = 'CoreHome_NoSuchPage';
}
}
diff --git a/plugins/CoreHome/Widgets.php b/plugins/CoreHome/Widgets.php
deleted file mode 100644
index 17d888bd35..0000000000
--- a/plugins/CoreHome/Widgets.php
+++ /dev/null
@@ -1,63 +0,0 @@
-translator = $translator;
- }
-
- protected function init()
- {
- $this->addWidget('CoreHome_SupportPiwik', 'getDonateForm');
- $this->addWidget('Installation_Welcome', 'getPromoVideo');
- }
-
- /**
- * Renders and echo's the in-app donate form w/ slider.
- */
- public function getDonateForm()
- {
- $view = new View('@CoreHome/getDonateForm');
-
- if (Common::getRequestVar('widget', false)
- && Piwik::hasUserSuperUserAccess()) {
- $view->footerMessage = $this->translator->translate('CoreHome_OnlyForSuperUserAccess');
- }
-
- return $view->render();
- }
-
- /**
- * Renders and echo's HTML that displays the Piwik promo video.
- */
- public function getPromoVideo()
- {
- $view = new View('@CoreHome/getPromoVideo');
- $view->shareText = $this->translator->translate('CoreHome_SharePiwikShort');
- $view->shareTextLong = $this->translator->translate('CoreHome_SharePiwikLong');
- $view->promoVideoUrl = 'https://www.youtube.com/watch?v=OslfF_EH81g';
-
- return $view->render();
- }
-}
diff --git a/plugins/CoreHome/Widgets/GetDonateForm.php b/plugins/CoreHome/Widgets/GetDonateForm.php
new file mode 100644
index 0000000000..093fe8c3a2
--- /dev/null
+++ b/plugins/CoreHome/Widgets/GetDonateForm.php
@@ -0,0 +1,48 @@
+translator = $translator;
+ }
+
+ public static function configure(WidgetConfig $config)
+ {
+ $config->setCategoryId('Example Widgets');
+ $config->setName('CoreHome_SupportPiwik');
+ $config->setOrder(5);
+ }
+
+ public function render()
+ {
+ $view = new View('@CoreHome/getDonateForm');
+
+ if (Common::getRequestVar('widget', false)
+ && Piwik::hasUserSuperUserAccess()) {
+ $view->footerMessage = $this->translator->translate('CoreHome_OnlyForSuperUserAccess');
+ }
+
+ return $view->render();
+ }
+}
\ No newline at end of file
diff --git a/plugins/CoreHome/Widgets/GetPromoVideo.php b/plugins/CoreHome/Widgets/GetPromoVideo.php
new file mode 100644
index 0000000000..49636b5e0e
--- /dev/null
+++ b/plugins/CoreHome/Widgets/GetPromoVideo.php
@@ -0,0 +1,44 @@
+translator = $translator;
+ }
+
+ public static function configure(WidgetConfig $config)
+ {
+ $config->setCategoryId('Example Widgets');
+ $config->setName('Installation_Welcome');
+ $config->setOrder(10);
+ }
+
+ public function render()
+ {
+ $view = new View('@CoreHome/getPromoVideo');
+ $view->shareText = $this->translator->translate('CoreHome_SharePiwikShort');
+ $view->shareTextLong = $this->translator->translate('CoreHome_SharePiwikLong');
+ $view->promoVideoUrl = 'https://www.youtube.com/watch?v=OslfF_EH81g';
+
+ return $view->render();
+ }
+}
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js
new file mode 100644
index 0000000000..4417cea7ac
--- /dev/null
+++ b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js
@@ -0,0 +1,31 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Shows a general loading message while [loading] is set to true.
+ *
+ * @param {Boolean} loading If true, the activity indicator is shown, otherwise the indicator is hidden.
+ *
+ * Example:
+ *
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikActivityIndicator', piwikActivityIndicator);
+
+ piwikActivityIndicator.$inject = ['piwik'];
+
+ function piwikActivityIndicator(piwik){
+ return {
+ restrict: 'A',
+ transclude: true,
+ scope: {
+ loading: '='
+ },
+ templateUrl: 'plugins/CoreHome/angularjs/activity-indicator/activityindicator.html?cb=' + piwik.cacheBuster
+ };
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/activity-indicator/activityindicator.html b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.html
new file mode 100644
index 0000000000..f6080ae817
--- /dev/null
+++ b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.html
@@ -0,0 +1,3 @@
+
+
{{ 'General_LoadingData'|translate }}
+
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js b/plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js
index 89d572cedf..2eef346ec6 100644
--- a/plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js
+++ b/plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js
@@ -132,7 +132,9 @@
scope.ajaxForm.data[name] = val;
if (!skipScopeApply) {
- scope.$apply();
+ setTimeout(function () {
+ scope.$apply();
+ }, 0);
}
}
};
diff --git a/plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js b/plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js
index 0617c5df88..ee1a2cf219 100644
--- a/plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js
+++ b/plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js
@@ -24,13 +24,17 @@
function onClickOutsideElement (event) {
if (element.has(event.target).length === 0) {
- scope.$apply(attr.piwikFocusAnywhereButHere);
+ setTimeout(function () {
+ scope.$apply(attr.piwikFocusAnywhereButHere);
+ }, 0);
}
}
function onEscapeHandler (event) {
if (event.which === 27) {
- scope.$apply(attr.piwikFocusAnywhereButHere);
+ setTimeout(function () {
+ scope.$apply(attr.piwikFocusAnywhereButHere);
+ }, 0);
}
}
diff --git a/plugins/CoreHome/angularjs/common/filters/escape.js b/plugins/CoreHome/angularjs/common/filters/escape.js
new file mode 100644
index 0000000000..382e84b5ac
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/filters/escape.js
@@ -0,0 +1,16 @@
+/*!
+ * 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.filter').filter('escape', escape);
+
+ function escape() {
+
+ return function(value) {
+ return piwikHelper.escape(piwikHelper.htmlEntities(value));
+ };
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/common/services/global-ajax-queue.js b/plugins/CoreHome/angularjs/common/services/global-ajax-queue.js
new file mode 100644
index 0000000000..f831995810
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/services/global-ajax-queue.js
@@ -0,0 +1,14 @@
+/*!
+ * 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.service').service('globalAjaxQueue', ajaxQueue);
+
+ function ajaxQueue() {
+
+ return globalAjaxQueue;
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/common/services/piwik-url.js b/plugins/CoreHome/angularjs/common/services/piwik-url.js
new file mode 100644
index 0000000000..f2d6e9bb0c
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/services/piwik-url.js
@@ -0,0 +1,54 @@
+/*!
+ * 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.service').service('piwikUrl', piwikUrl);
+
+ piwikUrl.$inject = ['$location', 'piwik'];
+
+ /**
+ * Similar to angulars $location but works around some limitation. Use it if you need to access search params
+ */
+ function piwikUrl($location, piwik) {
+
+ var model = {
+ getSearchParam: getSearchParam
+ }
+
+ return model;
+
+ function getSearchParam(paramName)
+ {
+ if (paramName === 'segment') {
+ var hash = window.location.href.split('#');
+ if (hash && hash[1]) {
+ return piwik.broadcast.getValueFromHash(paramName, hash[1]);
+ }
+
+ return broadcast.getValueFromUrl(paramName);
+ }
+
+ // available in global scope
+ var search = $location.search();
+
+ if (!search[paramName]) {
+ // see https://github.com/angular/angular.js/issues/7239 (issue is resolved but problem still exists)
+ search[paramName] = piwik.broadcast.getValueFromUrl(paramName);
+ }
+
+ if (search[paramName]) {
+ var value = search[paramName];
+
+ if (angular.isArray(search[paramName])) {
+ // use last one. Eg when having period=day&period=year angular would otherwise return ['day', 'year']
+ return value[value.length - 1];
+ }
+
+ return value;
+ }
+ }
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/common/services/report-metadata-model.js b/plugins/CoreHome/angularjs/common/services/report-metadata-model.js
new file mode 100644
index 0000000000..f158861423
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/services/report-metadata-model.js
@@ -0,0 +1,52 @@
+/*!
+ * 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.service').factory('reportMetadataModel', reportMetadataModel);
+
+ reportMetadataModel.$inject = ['piwik', 'piwikApi'];
+
+ function reportMetadataModel (piwik, piwikApi) {
+
+ var reportsPromise = null;
+
+ var model = {
+ reports: [],
+ fetchReportMetadata: fetchReportMetadata,
+ findReport: findReport
+ };
+
+ return model;
+
+ function findReport(module, action)
+ {
+ var found = [];
+
+ angular.forEach(model.reports, function (report) {
+ if (report.module === module && report.action === action) {
+ found = report;
+ }
+ });
+
+ return found;
+ }
+
+ function fetchReportMetadata()
+ {
+ if (!reportsPromise) {
+ reportsPromise = piwikApi.fetch({
+ method: 'API.getReportMetadata',
+ idSites: piwik.idSite || piwik.broadcast.getValueFromUrl('idSite'),
+ }).then(function (response) {
+ model.reports = response;
+ return response;
+ });
+ }
+
+ return reportsPromise;
+ }
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/common/services/reporting-pages-model.js b/plugins/CoreHome/angularjs/common/services/reporting-pages-model.js
new file mode 100644
index 0000000000..1b1e406c95
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/services/reporting-pages-model.js
@@ -0,0 +1,58 @@
+/*!
+ * 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.service').factory('reportingPagesModel', reportingPagesModelService);
+
+ reportingPagesModelService.$inject = ['piwikApi'];
+
+ function reportingPagesModelService (piwikApi) {
+ var fetchAllPagesPromise = false;
+
+ // those sites are going to be displayed
+ var model = {
+ pages : [],
+ findPage: findPage,
+ reloadAllPages : reloadAllPages,
+ getAllPages : getAllPages
+ };
+
+ return model;
+
+ function findPage(categoryId, subcategoryId)
+ {
+ var found = null;
+
+ angular.forEach(model.pages, function (page) {
+ if (page &&
+ page.category && page.subcategory &&
+ page.category.id === categoryId && ('' + page.subcategory.id) === subcategoryId) {
+ found = page;
+ }
+ });
+
+ return found;
+ }
+
+ function reloadAllPages()
+ {
+ fetchAllPagesPromise = null;
+ return getAllPages();
+ }
+
+ function getAllPages()
+ {
+ if (!fetchAllPagesPromise) {
+ fetchAllPagesPromise = piwikApi.fetch({method: 'API.getReportPagesMetadata'}).then(function (response) {
+ model.pages = response;
+ return response;
+ });
+ }
+
+ return fetchAllPagesPromise;
+ }
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/history/history.service.js b/plugins/CoreHome/angularjs/history/history.service.js
index 2c6486a517..7dadf35ad0 100644
--- a/plugins/CoreHome/angularjs/history/history.service.js
+++ b/plugins/CoreHome/angularjs/history/history.service.js
@@ -36,6 +36,19 @@
loadCurrentPage();
}
+ function cleanHash(hash)
+ {
+ var chars = ['#', '/', '?'];
+ for (var i = 0; i != chars.length; ++i) {
+ var charToRemove = chars[i];
+ if (hash.charAt(0) == charToRemove) {
+ hash = hash.substring(1);
+ }
+ }
+
+ return hash;
+ }
+
// currently, the AJAX content URL is stored in $location.search(), but before it was stored in $location.path().
// this function makes sure URLs like http://piwik.net/?...#/module=Whatever&action=whatever still work.
function changePathToSearch() {
@@ -82,13 +95,7 @@
function load(hash) {
// make sure the hash is just the query parameter values, w/o a starting #, / or ? char. broadcast.pageload & $location.path should get neither
- var chars = ['#', '/', '?'];
- for (var i = 0; i != chars.length; ++i) {
- var charToRemove = chars[i];
- if (hash.charAt(0) == charToRemove) {
- hash = hash.substring(1);
- }
- }
+ hash = cleanHash(hash);
if (location.hash === '#?' + hash) {
loadCurrentPage(); // it would not trigger a location change success event as URL is the same, call it manually
diff --git a/plugins/CoreHome/angularjs/http404check.js b/plugins/CoreHome/angularjs/http404check.js
index f2e2a86edf..5b720c9472 100644
--- a/plugins/CoreHome/angularjs/http404check.js
+++ b/plugins/CoreHome/angularjs/http404check.js
@@ -1,9 +1,9 @@
(function () {
angular.module('piwikApp').factory('http404CheckInterceptor', http404CheckInterceptor);
- http404CheckInterceptor.$inject = ['$q'];
+ http404CheckInterceptor.$inject = ['$q', 'globalAjaxQueue'];
- function http404CheckInterceptor($q) {
+ function http404CheckInterceptor($q, globalAjaxQueue) {
function isClientError(rejection)
{
@@ -15,8 +15,8 @@
}
return {
-
'responseError': function(rejection) {
+
if (rejection &&
isClientError(rejection) &&
rejection.config &&
diff --git a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html
index d1964d1ac4..14191ac20e 100644
--- a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html
+++ b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html
@@ -23,7 +23,7 @@
class="reset"
src="plugins/CoreHome/images/reset_search.png"/>
-
diff --git a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js
index 00fbf87d7b..313ef00d6c 100644
--- a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js
+++ b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js
@@ -34,10 +34,10 @@
templateUrl: 'plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html?cb=' + piwik.cacheBuster,
link: function(scope, element, attrs) {
- element.find('.item').on('click', function () {
- var $self = angular.element(this);
+ scope.selectItem = function (event) {
+ var $self = angular.element(event.target);
- if ($self.hasClass('disabled') || $self.hasClass('separator')) {
+ if (!$self.hasClass('item') || $self.hasClass('disabled') || $self.hasClass('separator')) {
return;
}
@@ -47,11 +47,14 @@
});
}
scope.$eval('view.showItems = false');
- scope.$apply();
+
+ setTimeout(function () {
+ scope.$apply();
+ }, 0);
element.find('.item').removeClass('active');
$self.addClass('active');
- });
+ };
scope.searchItems = function (searchTerm)
{
diff --git a/plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js b/plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js
new file mode 100644
index 0000000000..ea15f025b0
--- /dev/null
+++ b/plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js
@@ -0,0 +1,74 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * When present in the page it listens to a popover URL parameter.
+ *
+ * If present it will try to load the related content in a popover or if the URL is empty it will close an
+ * opened popover.
+ *
+ * Example:
+ *
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikPopoverHandler', piwikPopoverHandler);
+
+ piwikPopoverHandler.$inject = ['$location', '$rootScope', 'piwik'];
+
+ function piwikPopoverHandler($location, $rootScope, piwik){
+
+ return {
+ restrict: 'A',
+ scope: {},
+ controller: function () {
+
+ function close()
+ {
+ Piwik_Popover.close();
+ }
+
+ function open(popoverParam)
+ {
+ // in case the $ was encoded (e.g. when using copy&paste on urls in some browsers)
+ popoverParam = decodeURIComponent(popoverParam);
+ // revert special encoding from broadcast.propagateNewPopoverParameter()
+ popoverParam = popoverParam.replace(/\$/g, '%');
+ popoverParam = decodeURIComponent(popoverParam);
+
+ var popoverParamParts = popoverParam.split(':');
+ var handlerName = popoverParamParts[0];
+ popoverParamParts.shift();
+ var param = popoverParamParts.join(':');
+ if (typeof piwik.broadcast.popoverHandlers[handlerName] != 'undefined'
+ && !piwik.broadcast.isLoginPage()) {
+ piwik.broadcast.popoverHandlers[handlerName](param);
+ }
+ }
+
+ function openOrClose()
+ {
+ // should be rather done by routing
+ var popoverParam = $location.search().popover;
+ if (popoverParam) {
+ open(popoverParam);
+ } else {
+ close();
+ }
+ }
+
+ $rootScope.$on('$locationChangeSuccess', function () {
+ // should be rather done by routing
+ $(function () {
+ // make sure all popover handles were registered
+ openOrClose();
+ });
+ });
+
+ }
+ };
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js
new file mode 100644
index 0000000000..adc1b97936
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js
@@ -0,0 +1,153 @@
+/*!
+ * 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('reportingMenuModel', reportingMenuModelService);
+
+ reportingMenuModelService.$inject = ['$filter', '$q', 'piwikApi', 'reportingPagesModel', '$location'];
+
+ function reportingMenuModelService ($filter, $q, piwikApi, reportingPagesModel, $location) {
+
+ // those sites are going to be displayed
+ var model = {
+ menu: [],
+ selected: [],
+ fetchMenuItems: fetchMenuItems,
+ reloadMenuItems: reloadMenuItems,
+ findSubcategory: findSubcategory
+ };
+
+ return model;
+
+ function isNumeric(text) {
+ return !isNaN(parseFloat(text)) && isFinite(text);
+ }
+
+ function findSubcategory(categoryId, subcategoryId)
+ {
+ var foundCategory = null;
+ var foundSubcategory = null;
+
+ angular.forEach(model.menu, function (category) {
+ if (category.id !== categoryId) {
+ return;
+ }
+ angular.forEach(category.subcategories, function (subcategory) {
+ if (subcategory.id === subcategoryId) {
+ foundCategory = category;
+ foundSubcategory = subcategory;
+ }
+ });
+ });
+
+ return {category: foundCategory, subcategory: foundSubcategory};
+ }
+
+ function buildMenuFromPages(pages)
+ {
+ var menu = [];
+
+ var activeCategory = $location.search().category;
+ var activeSubcategory = $location.search().subcategory;
+
+ var categoriesHandled = {};
+ angular.forEach(pages, function (page, key) {
+ var category = page.category;
+ var categoryId = category.id;
+
+ if (categoriesHandled[categoryId]) {
+ return;
+ }
+
+ categoriesHandled[categoryId] = true;
+
+ if (activeCategory && category.id === activeCategory) {
+ // this doesn't really belong here but placed it here for convenience
+ category.active = true;
+ category.hover = true;
+ }
+
+ category.subcategories = [];
+
+ var goalsGroup = false;
+
+ angular.forEach(pages, function (page, key) {
+ if (page.category.id === categoryId) {
+ var subcategory = page.subcategory;
+
+ if (subcategory.id === activeSubcategory) {
+ subcategory.active = true;
+ }
+
+ if (page.widgets && page.widgets[0] && page.category.id === 'Goals_Goals' && isNumeric(page.subcategory.id)) {
+ // we handle a goal
+ if (!goalsGroup) {
+ goalsGroup = angular.copy(subcategory);
+ goalsGroup.name = $filter('translate')('Goals_ChooseGoal');
+ goalsGroup.isGroup = true;
+ goalsGroup.subcategories = [];
+ goalsGroup.order = 10;
+ }
+
+ if (subcategory.active) {
+ goalsGroup.name = subcategory.name;
+ }
+
+ var goalId = page.subcategory.id;
+ subcategory.tooltip = subcategory.name + ' (id = ' + goalId + ' )';
+
+ goalsGroup.subcategories.push(subcategory);
+ return;
+ }
+
+ category.subcategories.push(subcategory);
+ }
+ });
+
+ if (goalsGroup && goalsGroup.subcategories && goalsGroup.subcategories.length <= 3) {
+ angular.forEach(goalsGroup.subcategories, function (subcategory) {
+ category.subcategories.push(subcategory);
+ });
+ } else if(goalsGroup) {
+ category.subcategories.push(goalsGroup);
+ }
+
+ category.subcategories = sortMenuItems(category.subcategories);
+
+ menu.push(category);
+
+ return menu;
+ });
+
+ menu = sortMenuItems(menu);
+
+ return menu;
+ }
+
+ function sortMenuItems(menu) {
+ return $filter('orderBy')(menu, 'order');
+ };
+
+ function reloadMenuItems()
+ {
+ var pagesPromise = reportingPagesModel.reloadAllPages();
+ return pagesPromise.then(function (pages) {
+ model.menu = buildMenuFromPages(pages);
+ });
+ }
+
+ function fetchMenuItems()
+ {
+ var pagesPromise = reportingPagesModel.getAllPages();
+
+ return pagesPromise.then(function (pages) {
+ model.menu = buildMenuFromPages(pages);
+
+ return model.menu;
+ });
+ }
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js
new file mode 100644
index 0000000000..85dc0f17c5
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js
@@ -0,0 +1,125 @@
+/*!
+ * 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('ReportingMenuController', ReportingMenuController);
+
+ ReportingMenuController.$inject = ['$scope', 'piwik', '$location', '$timeout', 'reportingMenuModel', '$rootScope'];
+
+ function ReportingMenuController($scope, piwik, $location, $timeout, menuModel, $rootScope) {
+
+ function markAllCategoriesAsInactive()
+ {
+ angular.forEach(menuModel.menu, function (cat) {
+ cat.active = false;
+ cat.hover = false;
+ angular.forEach(cat.subcategories, function (subcat) {
+ subcat.active = false;
+ });
+ });
+ }
+
+ function getUrlParam(param)
+ {
+ var value = piwik.broadcast.getValueFromHash(param);
+ if (!value) {
+ value = piwik.broadcast.getValueFromUrl(param);
+ }
+ return value;
+ }
+
+ $scope.menuModel = menuModel;
+
+ var timeoutPromise = null;
+
+ // show subcategories of the currently hovered category
+ $scope.enterCategory = function (category) {
+
+ if (timeoutPromise) {
+ $timeout.cancel(timeoutPromise);
+ }
+
+ angular.forEach(menuModel.menu, function (cat) {
+ cat.hover = false;
+ });
+
+ category.hover = true;
+ };
+
+ // show subcategories of the current active category again (after 2 sec max)
+ $scope.leaveCategory = function (category) {
+
+ if (timeoutPromise) {
+ $timeout.cancel(timeoutPromise);
+ }
+
+ timeoutPromise = $timeout(function () {
+ angular.forEach(menuModel.menu, function (cat) {
+ if (cat.active) {
+ cat.hover = true;
+ } else {
+ cat.hover = false;
+ }
+ });
+ }, 2000);
+ };
+
+ // highlight the currently hovered subcategory (and category)
+ $scope.enterSubcategory = function (category, subcategory) {
+ if (!category || !subcategory) {
+ return;
+ }
+
+ markAllCategoriesAsInactive();
+
+ category.active = true;
+ category.hover = true;
+ subcategory.active = true;
+ };
+
+ var idSite = getUrlParam('idSite');
+ var period = getUrlParam('period');
+ var date = getUrlParam('date');
+ var segment = getUrlParam('segment');
+
+ $scope.makeUrl = function (category, subcategory) {
+ var url = 'idSite=' + idSite + '&period=' + period + '&date=' + date + '&category=' + category.id + '&subcategory=' + subcategory.id;
+ if (segment) {
+ url+= '&segment='+ segment;
+ }
+ return url;
+ }
+
+ $scope.loadSubcategory = function (category, subcategory) {
+ if (subcategory && subcategory.active) {
+ // this menu item is already active, a location change success would not be triggered,
+ // instead trigger an event
+ $rootScope.$emit('loadPage', category.id, subcategory.id);
+ }
+ };
+
+ menuModel.fetchMenuItems().then(function (menu) {
+ if (!$location.search().subcategory) {
+ // load first, initial page if no subcategory is present
+ $scope.enterSubcategory(menu[0], menu[0].subcategories[0]);
+ $location.search($scope.makeUrl(menu[0], menu[0].subcategories[0]));
+ }
+ });
+
+ $rootScope.$on('$locationChangeSuccess', function () {
+ var category = $location.search().category;
+ var subcategory = $location.search().subcategory;
+
+ if (!category || !subcategory) {
+ return;
+ }
+
+ var found = menuModel.findSubcategory(category, subcategory);
+ $scope.enterSubcategory(found.category, found.subcategory);
+ });
+
+ }
+})();
diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html
new file mode 100644
index 0000000000..bbfd687517
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html
@@ -0,0 +1,40 @@
+
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js
new file mode 100644
index 0000000000..7cdd9e951b
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js
@@ -0,0 +1,31 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Shows the Piwik reporting menu.
+ *
+ * It automatically calls the API to fetch all data.
+ *
+ * Example:
+ *
+ */
+
+(function () {
+ angular.module('piwikApp').directive('piwikReportingMenu', piwikReportingMenu);
+
+ piwikReportingMenu.$inject = ['piwik'];
+
+ function piwikReportingMenu(piwik){
+
+ return {
+ restrict: 'A',
+ scope: {},
+ templateUrl: 'plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html?cb=' + piwik.cacheBuster,
+ controller: 'ReportingMenuController'
+ };
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js b/plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js
new file mode 100644
index 0000000000..4018fcee8c
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js
@@ -0,0 +1,196 @@
+/*!
+ * 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('reportingPageModel', reportingPageModelService);
+
+ reportingPageModelService.$inject = ['$filter', 'piwikApi', 'reportingPagesModel', 'reportMetadataModel'];
+
+ function reportingPageModelService ($filter, piwikApi, reportingPagesModel, reportMetadataModel) {
+ var init = false;
+
+ // those sites are going to be displayed
+ var model = {
+ fetchPage: fetchPage,
+ resetPage: resetPage,
+ widgets: [],
+ page: null,
+ pageContentUrl: '',
+ evolutionReports: [],
+ sparklineReports: []
+ };
+
+ return model;
+
+ function resetPage()
+ {
+ model.page = null;
+ model.widgets = [];
+ model.pageContentUrl = '';
+ model.evolutionReports = [];
+ model.sparklineReports = [];
+ }
+
+ function sortWidgets(widgets)
+ {
+ return $filter('orderBy')(widgets, 'order');
+ }
+
+ function shouldBeRenderedWithFullWidth(widget)
+ {
+ // rather controller logic
+ if ((widget.isContainer && widget.layout && widget.layout === 'ByDimension')
+ || widget.viewDataTable === 'bydimension') {
+ return true;
+ }
+
+ return widget.viewDataTable && widget.viewDataTable === 'tableAllColumns';
+ }
+
+ function buildPage(page)
+ {
+ if (!page) {
+ return;
+ }
+
+ var widgets = [];
+ var evolutionReports = [];
+ var sparklineReports = [];
+ var reportsToIgnore = [];
+
+ angular.forEach(page.widgets, function (widget) {
+
+ if (isIgnoredReport(reportsToIgnore, widget)) {
+ return;
+ }
+
+ reportsToIgnore = reportsToIgnore.concat(getRelatedReports(widget));
+
+ if (widget.viewDataTable && widget.viewDataTable === 'graphEvolution') {
+ evolutionReports.push(widget);
+ } else if (widget.viewDataTable && widget.viewDataTable === 'sparklines') {
+ sparklineReports.push(widget);
+ } else {
+ widgets.push(widget);
+ }
+ });
+
+ widgets = sortWidgets(widgets);
+
+ var groupedWidgets = [];
+
+ if (widgets.length === 1) {
+ // if there is only one widget, we always display it full width
+ groupedWidgets = widgets;
+ } else {
+ for (var i = 0; i < widgets.length; i++) {
+ var widget = widgets[i];
+
+ if (shouldBeRenderedWithFullWidth(widget) || (widgets[i+1] && shouldBeRenderedWithFullWidth(widgets[i+1]))) {
+ widget.widgets = sortWidgets(widget.widgets);
+
+ groupedWidgets.push(widget);
+ } else {
+
+ var counter = 0;
+ var left = [widget];
+ var right = [];
+
+ while (widgets[i+1] && !shouldBeRenderedWithFullWidth(widgets[i+1])) {
+ i++;
+ counter++;
+ if (counter % 2 === 0) {
+ left.push(widgets[i]);
+ } else {
+ right.push(widgets[i]);
+ }
+ }
+
+ groupedWidgets.push({group: true, left: left, right: right});
+ }
+ }
+ }
+
+ var copyWidgets = angular.copy(groupedWidgets);
+ var copyEvolution = angular.copy(evolutionReports);
+ var copySparklines = angular.copy(sparklineReports);
+
+ if (copyEvolution.length) {
+ copyEvolution = markWidgetsInFirstRowOfPage(copyEvolution);
+ } else if (copySparklines.length) {
+ copySparklines = markWidgetsInFirstRowOfPage(copySparklines);
+ } else {
+ copyWidgets = markWidgetsInFirstRowOfPage(copyWidgets);
+ }
+
+ // angular.copy forces the page to re-render. Otherwise it won't reload some pages
+ model.evolutionReports = copyEvolution;
+ model.sparklineReports = copySparklines;
+ model.widgets = copyWidgets;
+ }
+
+ function markWidgetsInFirstRowOfPage(widgets)
+ {
+ if (widgets && widgets[0]) {
+ if (widgets[0].group) {
+ markWidgetsInFirstRowOfPage(widgets[0].left);
+ markWidgetsInFirstRowOfPage(widgets[0].right);
+ } else {
+ widgets[0].isFirstInPage = true;
+ }
+ }
+
+ return widgets;
+ }
+
+ function getRelatedReports(widget)
+ {
+ if (widget.isReport) {
+ var report = reportMetadataModel.findReport(widget.module, widget.action);
+
+ if (report && report.relatedReports) {
+ return report.relatedReports;
+ }
+ }
+
+ return [];
+ }
+
+ function isIgnoredReport(reportsToIgnore, widget)
+ {
+ var found = false;
+
+ if (widget.isReport) {
+ angular.forEach(reportsToIgnore, function (report) {
+ if (report.module === widget.module &&
+ report.action === widget.action) {
+ found = true;
+ }
+ });
+ }
+
+ return found;
+ }
+
+ function fetchPage(category, subcategory)
+ {
+ resetPage();
+
+ var pagesPromise = reportingPagesModel.getAllPages();
+ var reportsPromise = reportMetadataModel.fetchReportMetadata();
+
+ return pagesPromise.then(function () {
+ model.page = reportingPagesModel.findPage(category, subcategory);
+
+ reportsPromise.then(function () {
+ buildPage(model.page);
+ });
+
+ return model.page;
+ });
+ }
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js b/plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js
new file mode 100644
index 0000000000..cf0d424b55
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js
@@ -0,0 +1,56 @@
+/*!
+ * 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('ReportingPageController', ReportingPageController);
+
+ ReportingPageController.$inject = ['$scope', 'piwik', '$rootScope', '$location', 'reportingPageModel'];
+
+ function ReportingPageController($scope, piwik, $rootScope, $location, pageModel) {
+ pageModel.resetPage();
+ $scope.pageModel = pageModel;
+
+ var currentCategory = null;
+ var currentSubcategory = null;
+
+ $scope.renderPage = function (category, subcategory) {
+ if (!category || !subcategory) {
+ pageModel.resetPage();
+ $scope.loading = false;
+ return;
+ }
+
+ currentCategory = category;
+ currentSubcategory = subcategory;
+
+ pageModel.fetchPage(category, subcategory).then(function () {
+ $scope.hasNoPage = !pageModel.page;
+ $scope.loading = false;
+ });
+ }
+
+ $scope.loading = true; // we only set loading on initial load
+
+ $scope.renderPage($location.search().category, $location.search().subcategory);
+
+ $rootScope.$on('$locationChangeSuccess', function () {
+ // should be handled by $route
+ var category = $location.search().category;
+ var subcategory = $location.search().subcategory;
+
+ if (category === currentCategory && subcategory === currentSubcategory) {
+ // this page is already loaded
+ return;
+ }
+
+ $scope.renderPage(category, subcategory);
+ });
+
+ $rootScope.$on('loadPage', function (event, category, subcategory) {
+ $scope.renderPage(category, subcategory);
+ });
+ }
+})();
diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html
new file mode 100644
index 0000000000..9a8974e313
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html
@@ -0,0 +1,25 @@
+
+
+
+
+
{{ 'CoreHome_NoSuchPage'|translate }}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js
new file mode 100644
index 0000000000..a146ed3112
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js
@@ -0,0 +1,31 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Shows a piwik reporting page.
+ *
+ * The content to be displayed is automatically loaded via API based on the current URL. The URL parameters
+ * 'category' and 'subcategory' need to be present in the URL in order to see something in the reporting page.
+ *
+ * Example:
+ *
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikReportingPage', piwikReportingPage);
+
+ piwikReportingPage.$inject = ['piwik'];
+
+ function piwikReportingPage(piwik){
+
+ return {
+ restrict: 'A',
+ scope: {},
+ templateUrl: 'plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html?cb=' + piwik.cacheBuster,
+ controller: 'ReportingPageController'
+ };
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html
new file mode 100644
index 0000000000..db98dee895
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html
@@ -0,0 +1,25 @@
+
+
+
+
+ {{ category.name }}
+
+
+
+
+
+
{{ selectedWidget.name }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js
new file mode 100644
index 0000000000..1620be2a9d
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js
@@ -0,0 +1,67 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Renders a widget that is a container widget having the layout "ByDimension".
+ *
+ * The "ByDimension" layout shows a menu on the left letting you choose any widgets within this container. The
+ * currently selected widget is shown on the right.
+ *
+ * @param {Object} piwikWidgetByDimensionContainer a widget object as returned by the WidgetMetadata API.
+ *
+ * Example:
+ *
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikWidgetByDimensionContainer', piwikWidgetContainer);
+
+ piwikWidgetContainer.$inject = ['piwik', '$filter'];
+
+ function piwikWidgetContainer(piwik, $filter){
+ return {
+ restrict: 'A',
+ scope: {
+ container: '=piwikWidgetByDimensionContainer'
+ },
+ templateUrl: 'plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html?cb=' + piwik.cacheBuster,
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs, ngModel) {
+
+ var widgetsSorted = $filter('orderBy')(scope.container.widgets, 'order');
+ var widgetsByCategory = {};
+
+ angular.forEach(widgetsSorted, function (widget) {
+ var category = widget.subcategory.name;
+
+ if (!widgetsByCategory[category]) {
+ widgetsByCategory[category] = {name: category, order: widget.order, widgets: []};
+ }
+
+ widgetsByCategory[category].widgets.push(widget);
+ });
+
+ // only an array can be sorted
+ var finalWidgetsByCategory = [];
+ angular.forEach(widgetsByCategory, function (category) {
+ finalWidgetsByCategory.push(category);
+ });
+
+ scope.widgetsByCategory = $filter('orderBy')(finalWidgetsByCategory, 'order');
+
+ scope.selectWidget = function (widget) {
+ scope.selectedWidget = widget;
+ }
+
+ if (widgetsSorted && widgetsSorted.length) {
+ scope.selectWidget(widgetsSorted[0]);
+ }
+ };
+ }
+ };
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html
new file mode 100644
index 0000000000..21ebd3d0cd
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html
@@ -0,0 +1,10 @@
+
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js
new file mode 100644
index 0000000000..c378637c71
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js
@@ -0,0 +1,32 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Renders a widget that is a container widget having no specific layout (which is the default).
+ *
+ * It shows all widgets vertically aligned one widget after another.
+ *
+ * @param {Object} piwikWidgetContainer a widget object as returned by the WidgetMetadata API.
+ *
+ * Example:
+ *
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikWidgetContainer', piwikWidgetContainer);
+
+ piwikWidgetContainer.$inject = ['piwik'];
+
+ function piwikWidgetContainer(piwik){
+ return {
+ restrict: 'A',
+ scope: {
+ container: '=piwikWidgetContainer'
+ },
+ templateUrl: 'plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html?cb=' + piwik.cacheBuster
+ };
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html
new file mode 100644
index 0000000000..d1f0cb51f1
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ {{ 'General_ErrorRequest'|translate:(''):('') }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js
new file mode 100644
index 0000000000..4e1859e789
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js
@@ -0,0 +1,134 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Loads any custom widget or URL based on the given parameters.
+ *
+ * The currently active idSite, period, date and segment (if needed) is automatically appended to the parameters. If
+ * this widget is removed from the DOM and requests are in progress, these requests will be aborted. A loading message
+ * or an error message on failure is shown as well. It's kinda similar to ng-include but there it is not possible to
+ * listen to HTTP errors etc.
+ *
+ * Example:
+ *
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikWidgetLoader', piwikWidgetLoader);
+
+ piwikWidgetLoader.$inject = ['piwik', 'piwikUrl', '$http', '$compile', '$q'];
+
+ function piwikWidgetLoader(piwik, piwikUrl, $http, $compile, $q){
+ return {
+ restrict: 'A',
+ transclude: true,
+ scope: {
+ piwikWidgetLoader: '='
+ },
+ templateUrl: 'plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html?cb=' + piwik.cacheBuster,
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs, ngModel) {
+ var changeCounter = 0,
+ currentScope,
+ currentElement,
+ httpCanceler,
+ contentNode = element.find('.theWidgetContent');
+
+ var cleanupLastWidgetContent = function() {
+ if (currentElement) {
+ currentElement.remove();
+ currentElement = null;
+ }
+ if (currentScope) {
+ currentScope.$destroy();
+ currentScope = null;
+ }
+ };
+
+ var abortHttpRequestIfNeeded = function () {
+ if (httpCanceler) {
+ httpCanceler.resolve();
+ httpCanceler = null;
+ }
+ }
+
+ function getFullWidgetUrl(parameters) {
+
+ var url = $.param(parameters);
+
+ var idSite = piwikUrl.getSearchParam('idSite');
+ var period = piwikUrl.getSearchParam('period');
+ var date = piwikUrl.getSearchParam('date');
+ var segment = piwikUrl.getSearchParam('segment');
+
+ url += '&idSite=' + idSite + '&period=' + period;
+ url += '&date=' + date + '&random=' + parseInt(Math.random() * 10000);
+
+ if (segment) {
+ url += '&segment=' + segment;
+ }
+
+ return '?' + url;
+ }
+
+ function loadWidgetUrl(parameters, thisChangeId)
+ {
+ scope.loading = true;
+
+ var url = getFullWidgetUrl(parameters);
+
+ abortHttpRequestIfNeeded();
+ cleanupLastWidgetContent();
+
+ httpCanceler = $q.defer();
+
+ $http.get(url, {timeout: httpCanceler.promise}).success(function(response) {
+ if (thisChangeId !== changeCounter || !response) {
+ // another widget was requested meanwhile, ignore this response
+ return;
+ }
+
+ httpCanceler = null;
+
+ var newScope = scope.$new();
+ currentScope = newScope;
+
+ scope.loading = false;
+ scope.loadingFailed = false;
+
+ currentElement = contentNode.html(response).children();
+ $compile(currentElement)(newScope);
+
+ }).error(function () {
+ if (thisChangeId !== changeCounter) {
+ // another widget was requested meanwhile, ignore this response
+ return;
+ }
+
+ httpCanceler = null;
+
+ cleanupLastWidgetContent();
+
+ scope.loading = false;
+ scope.loadingFailed = true;
+ });
+ }
+
+ scope.$watch('piwikWidgetLoader', function (parameters, oldUrl) {
+ if (parameters) {
+ loadWidgetUrl(parameters, ++changeCounter);
+ }
+ });
+
+ element.on('$destroy', function() {
+ abortHttpRequestIfNeeded();
+ });
+ };
+ }
+ };
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget/widget.directive.html b/plugins/CoreHome/angularjs/widget/widget.directive.html
new file mode 100644
index 0000000000..86cdc91291
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget/widget.directive.html
@@ -0,0 +1,23 @@
+
\ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget/widget.directive.js b/plugins/CoreHome/angularjs/widget/widget.directive.js
new file mode 100644
index 0000000000..86574870f2
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget/widget.directive.js
@@ -0,0 +1,93 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Renders any kind of widget. If you have a widget and you want to have it rendered, use this directive. It will
+ * display a name on top and the actual widget below. It can handle any kind of widget, no matter whether it is a
+ * regular widget or a container.
+ *
+ * @param {Object} piwikWidget A widget object as returned by the WidgetMetadata API.
+ * @param {Object} piwikWidget.middlewareParameters If present, we will request a URL using the given parameters and
+ * only if this URL returns a JSON `true` the widget will be shown.
+ * Otherwise the widget won't be shown.
+ * @param {String} containerId If you do not have a widget object but a containerId we will find the correct widget
+ * object based on the given containerId. Be aware that we might not find the widget if
+ * it is for example not available for the current user or period/date.
+ * @param {Boolean} widgetized true if the widget is widgetized (eg in Dashboard or exported). In this case we will add
+ * a URL parameter widget=1 to all widgets. Eg sparklines will be then displayed one after
+ * another (vertically aligned) instead of two next to each other.
+ *
+ * Example:
+ *
+ *
// in this case we will find the correct widget automatically
+ *
// disables rating feature, no initial headline
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikWidget', piwikWidget);
+
+ piwikWidget.$inject = ['piwik', 'piwikApi'];
+
+ function piwikWidget(piwik, piwikApi){
+
+ function findContainerWidget(containerId, scope) {
+ widgetsHelper.getAvailableWidgets(function (categorizedWidgets) {
+
+ angular.forEach(categorizedWidgets, function (widgets) {
+ angular.forEach(widgets, function (widget) {
+
+ if (widget && widget.isContainer && widget.parameters.containerId === containerId) {
+ widget = angular.copy(widget);
+ if (scope.widgetized) {
+ widget.isFirstInPage = '1';
+ widget.parameters.widget = '1';
+ angular.forEach(widget.widgets, function (widget) {
+ widget.parameters.widget = '1';
+ });
+ }
+ scope.widget = widget;
+ applyMiddleware(scope);
+ }
+ });
+ });
+
+ });
+ }
+
+ function applyMiddleware(scope)
+ {
+ if (!scope.widget.middlewareParameters) {
+ scope.$eval('view.showWidget = true');
+ } else {
+ var params = angular.copy(scope.widget.middlewareParameters);
+ piwikApi.fetch(params).then(function (response) {
+ var enabled = response ? 'true' : 'false';
+ scope.$eval('view.showWidget = ' + enabled);
+ });
+ }
+ }
+
+ return {
+ restrict: 'A',
+ scope: {
+ widget: '=?piwikWidget',
+ widgetized: '=?',
+ containerid: '='
+ },
+ templateUrl: 'plugins/CoreHome/angularjs/widget/widget.directive.html?cb=' + piwik.cacheBuster,
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs, ngModel) {
+ if (scope.widget) {
+ applyMiddleware(scope);
+ } else if (attrs.containerid) {
+ findContainerWidget(attrs.containerid, scope);
+ }
+ }
+ }
+ };
+ }
+})();
\ No newline at end of file
diff --git a/plugins/CoreHome/javascripts/broadcast.js b/plugins/CoreHome/javascripts/broadcast.js
index 706a8e59d5..3d5fb0432c 100644
--- a/plugins/CoreHome/javascripts/broadcast.js
+++ b/plugins/CoreHome/javascripts/broadcast.js
@@ -159,7 +159,6 @@ var broadcast = {
} else {
// start page
Piwik_Popover.close();
-
$('.pageWrap #content:not(.admin)').empty();
}
},
@@ -173,6 +172,7 @@ var broadcast = {
},
/**
+ * ONLY USED BY OVERLAY
* propagateAjax -- update hash values then make ajax calls.
* example :
* 1) View keywords report
@@ -228,6 +228,47 @@ var broadcast = {
}
},
+ /**
+ * propagateAjax -- update hash values then make ajax calls.
+ * example :
+ * 1) View keywords report
+ * 2) Main menu li also goes through this function.
+ *
+ * Will propagate your new value into the current hash string and make ajax calls.
+ *
+ * NOTE: this method will only make ajax call and replacing main content.
+ *
+ * @param {string} ajaxUrl querystring with parameters to be updated
+ * @param {boolean} [disableHistory] the hash change won't be available in the browser history
+ * @return {void}
+ */
+ buildReportingUrl: function (ajaxUrl, disableHistory) {
+
+ // available in global scope
+ var currentHashStr = broadcast.getHash();
+
+ ajaxUrl = ajaxUrl.replace(/^\?|/, '');
+
+ var params_vals = ajaxUrl.split("&");
+ for (var i = 0; i < params_vals.length; i++) {
+ currentHashStr = broadcast.updateParamValue(params_vals[i], currentHashStr);
+ }
+
+ // if the module is not 'Goals', we specifically unset the 'idGoal' parameter
+ // this is to ensure that the URLs are clean (and that clicks on graphs work as expected - they are broken with the extra parameter)
+ var action = broadcast.getParamValue('action', currentHashStr);
+ if (action != 'goalReport' && action != 'ecommerceReport' && action != 'products' && action != 'sales') {
+ currentHashStr = broadcast.updateParamValue('idGoal=', currentHashStr);
+ }
+ // unset idDashboard if use doesn't display a dashboard
+ var module = broadcast.getParamValue('module', currentHashStr);
+ if (module != 'Dashboard') {
+ currentHashStr = broadcast.updateParamValue('idDashboard=', currentHashStr);
+ }
+
+ return '#' + currentHashStr;
+ },
+
/**
* propagateNewPage() -- update url value and load new page,
* Example:
@@ -349,9 +390,9 @@ var broadcast = {
*/
propagateNewPopoverParameter: function (handlerName, value) {
// init broadcast if not already done (it is required to make popovers work in widgetize mode)
- broadcast.init(true);
+ //broadcast.init(true);
- var hash = broadcast.getHashFromUrl(window.location.href);
+ var $location = angular.element(document).injector().get('$location');
var popover = '';
if (handlerName) {
@@ -365,24 +406,14 @@ var broadcast = {
}
if ('' == value || 'undefined' == typeof value) {
- var newHash = hash.replace(/(&?popover=.*)/, '');
- } else if (broadcast.getParamValue('popover', hash)) {
- var newHash = broadcast.updateParamValue('popover='+popover, hash);
- } else if (hash && hash != '#') {
- var newHash = hash + '&popover=' + popover
+ $location.search('popover', '');
} else {
- var newHash = '#popover='+popover;
- }
-
- // never use an empty hash, as that might reload the page
- if ('' == newHash) {
- newHash = '#';
+ $location.search('popover', popover);
}
- broadcast.forceReload = false;
- angular.element(document).injector().invoke(function (historyService) {
- historyService.load(newHash);
- });
+ setTimeout(function () {
+ angular.element(document).injector().get('$rootScope').$apply();
+ }, 1);
},
/**
diff --git a/plugins/CoreHome/javascripts/corehome.js b/plugins/CoreHome/javascripts/corehome.js
index 5bddc23f9e..6e8729611b 100755
--- a/plugins/CoreHome/javascripts/corehome.js
+++ b/plugins/CoreHome/javascripts/corehome.js
@@ -114,60 +114,6 @@
handleSectionToggle(this, 'inline', !$(this).is(':checked'));
});
- //
- // reports by dimension list behavior
- //
-
- // when a report dimension is clicked, load the appropriate report
- var currentWidgetLoading = null;
- $('body').on('click', '.reportDimension', function (e) {
- var view = $(this).closest('.reportsByDimensionView'),
- report = $('.dimensionReport', view),
- loading = $('.loadingPiwik', view);
-
- // make this dimension the active one
- $('.activeDimension', view).removeClass('activeDimension');
- $(this).addClass('activeDimension');
-
- // hide the visible report & show the loading elem
- report.hide();
- loading.show();
-
- // load the report using the data-url attribute (which holds the URL to the report)
- var widgetParams = broadcast.getValuesFromUrl($(this).attr('data-url'));
- for (var key in widgetParams) {
- widgetParams[key] = decodeURIComponent(widgetParams[key]);
- }
-
- var widgetUniqueId = widgetParams.module + widgetParams.action;
- currentWidgetLoading = widgetUniqueId;
-
- widgetsHelper.loadWidgetAjax(widgetUniqueId, widgetParams, function (response) {
- // if the widget that was loaded was not for the latest clicked link, do nothing w/ the response
- if (widgetUniqueId != currentWidgetLoading) {
- return;
- }
-
- loading.hide();
- report.css('display', 'inline-block').html($(response));
-
- // scroll to report
- piwikHelper.lazyScrollTo(report, 400);
- }, function (deferred, status) {
- if (status == 'abort' || !deferred || deferred.status < 400 || deferred.status >= 600) {
- return;
- }
-
- loading.hide();
-
- var errorMessage = _pk_translate('General_ErrorRequest', ['', '']);
- if ($('#loadingError').html()) {
- errorMessage = $('#loadingError').html();
- }
-
- report.css('display', 'inline-block').html('' + errorMessage + '
');
- });
- });
});
}(jQuery));
diff --git a/plugins/CoreHome/javascripts/dataTable.js b/plugins/CoreHome/javascripts/dataTable.js
index 0561185de8..4bd5fb609a 100644
--- a/plugins/CoreHome/javascripts/dataTable.js
+++ b/plugins/CoreHome/javascripts/dataTable.js
@@ -1,3 +1,4 @@
+
/*!
* Piwik - free/libre analytics platform
*
diff --git a/plugins/CoreHome/javascripts/menu.js b/plugins/CoreHome/javascripts/menu.js
deleted file mode 100644
index 2f5e16c827..0000000000
--- a/plugins/CoreHome/javascripts/menu.js
+++ /dev/null
@@ -1,114 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-/**
- * @constructor
- */
-function menu() {
- this.param = {};
-}
-
-menu.prototype =
-{
- resetTimer: null,
-
- adaptSubMenuHeight: function() {
- var subNavHeight = $('.sfHover > ul').outerHeight();
- $('.nav_sep').height(subNavHeight);
- },
-
- overMainLI: function () {
- var $this = $(this);
- $this.siblings().removeClass('sfHover');
- $this.addClass('sfHover');
- menu.prototype.adaptSubMenuHeight();
- clearTimeout(menu.prototype.resetTimer);
- },
-
- outMainLI: function () {
- clearTimeout(menu.prototype.resetTimer);
- menu.prototype.resetTimer = setTimeout(function() {
- $('.Menu-tabList > .sfHover', this.menuNode).removeClass('sfHover');
- $('.Menu-tabList > .sfActive', this.menuNode).addClass('sfHover');
- menu.prototype.adaptSubMenuHeight();
- }, 2000);
- },
-
- onItemClick: function (e) {
- if (e.which === 2) {
- return;
- }
- $('.Menu--dashboard').trigger('piwikSwitchPage', this);
- broadcast.propagateAjax( $(this).attr('href').substr(1) );
- return false;
- },
-
- init: function () {
- this.menuNode = $('.Menu--dashboard');
-
- this.menuNode.find("li:has(ul),li#Searchmenu").hover(this.overMainLI, this.outMainLI);
- this.menuNode.find("li:has(ul),li#Searchmenu").focusin(this.overMainLI);
-
- this.menuNode.find('a.menuItem').click(this.onItemClick);
-
- menu.prototype.adaptSubMenuHeight();
- },
-
- activateMenu: function (module, action, params) {
- params = params || {};
- params.module = module;
- params.action = action;
-
- this.menuNode.find('li').removeClass('sfHover').removeClass('sfActive');
- var $activeLink = this.menuNode.find('a').filter(function () {
- var url = $(this).attr('href');
- if (!url) {
- return false;
- }
-
- for (var key in params) {
- if (!params.hasOwnProperty(key)
- || !params[key]
- ) {
- continue;
- }
-
- var actual = broadcast.getValueFromHash(key, url);
- if (actual != params[key]) {
- return false;
- }
- }
-
- return true;
- });
-
- $activeLink.closest('li').addClass('sfHover');
- $activeLink.closest('li.menuTab').addClass('sfActive').addClass('sfHover');
- },
-
- // getting the right li is a little tricky since goals uses idGoal, and overview is index.
- getSubmenuID: function (module, id, action) {
- var $li = '';
- // So, if module is Goals, id is present, and action is not Index, must be one of the goals
- if ((module == 'Goals' || module == 'Ecommerce') && id != '' && (action != 'index')) {
- $li = $("#" + module + "_" + action + "_" + id);
- // if module is Dashboard and id is present, must be one of the dashboards
- } else if (module == 'Dashboard') {
- if (!id) id = 1;
- $li = $("#" + module + "_" + action + "_" + id);
- } else {
- $li = $("#" + module + "_" + action);
- }
- return $li;
- },
-
- loadFirstSection: function () {
- if (broadcast.isHashExists() == false) {
- $('li:first a:first', this.menuNode).click().addClass('sfHover').addClass('sfActive');
- }
- }
-};
diff --git a/plugins/CoreHome/javascripts/menu_init.js b/plugins/CoreHome/javascripts/menu_init.js
deleted file mode 100644
index 490c859185..0000000000
--- a/plugins/CoreHome/javascripts/menu_init.js
+++ /dev/null
@@ -1,19 +0,0 @@
-$(function () {
- var isPageHasMenu = $('.Menu--dashboard').size();
- var isPageIsAdmin = $('#content.admin').size();
- if (isPageHasMenu) {
- piwikMenu = new menu();
- piwikMenu.init();
- piwikMenu.loadFirstSection();
- }
-
- if(isPageIsAdmin) {
- // don't use broadcast in admin pages
- return;
- }
- if(isPageHasMenu) {
- broadcast.init();
- } else {
- broadcast.init(true);
- }
-});
diff --git a/plugins/CoreHome/javascripts/sparkline.js b/plugins/CoreHome/javascripts/sparkline.js
index fc3b74f692..256e6f1589 100644
--- a/plugins/CoreHome/javascripts/sparkline.js
+++ b/plugins/CoreHome/javascripts/sparkline.js
@@ -35,14 +35,30 @@ piwik.initSparklines = function() {
};
window.initializeSparklines = function () {
- var sparklineUrlParamsToIgnore = ['module', 'action', 'idSite', 'period', 'date', 'viewDataTable'];
+ var sparklineUrlParamsToIgnore = ['module', 'action', 'idSite', 'period', 'date', 'viewDataTable', 'forceView', 'random'];
- $("[data-graph-id]").each(function () {
+ $('.graphEvolution [data-report]').each(function () {
var graph = $(this);
- // try to find sparklines and add them clickable behaviour
- graph.parent().find('div.sparkline').each(function () {
+ // we search for .widget to make sure eg in the Dashboard to not update any graph of another report
+ var selectorsToFindParent = ['.widget', '.reporting-page', 'body'];
+ var index = 0, selector, parent;
+ for (index; index < selectorsToFindParent.length; index++) {
+ selector = selectorsToFindParent[index];
+ parent = graph.parents(selector).first();
+ if (parent && parent.length) {
+ break;
+ }
+ }
+
+ if (!parent || !parent.length) {
+ return;
+ }
+ var sparklines = parent.find('div.sparkline');
+
+ // try to find sparklines and add them clickable behaviour
+ sparklines.each(function () {
// find the sparkline and get it's src attribute
var sparklineUrl = $('img', this).attr('data-src');
@@ -66,8 +82,8 @@ window.initializeSparklines = function () {
// on click, reload the graph with the new url
$(this).off('click.sparkline');
$(this).on('click.sparkline', function () {
- var reportId = graph.attr('data-graph-id'),
- dataTable = $(require('piwik/UI').DataTable.getDataTableByReport(reportId));
+ var reportId = graph.attr('data-report'),
+ dataTable = graph;
// when the metrics picker is used, the id of the data table might be updated (which is correct behavior).
// for example, in goal reports it might change from GoalsgetEvolutionGraph to GoalsgetEvolutionGraph1.
diff --git a/plugins/CoreHome/lang/en.json b/plugins/CoreHome/lang/en.json
index a6bab4a544..7b3d4bbcf3 100644
--- a/plugins/CoreHome/lang/en.json
+++ b/plugins/CoreHome/lang/en.json
@@ -48,6 +48,7 @@
"YouAreUsingTheLatestVersion": "You are using the latest version of Piwik!",
"ClickRowToExpandOrContract": "Click this row to expand or contract the subtable.",
"UndoPivotBySubtable": "This report has been pivoted %s Undo pivot",
- "PivotBySubtable": "This report is not pivoted %s Pivot by %s"
+ "PivotBySubtable": "This report is not pivoted %s Pivot by %s",
+ "NoSuchPage": "This page does not exist"
}
}
diff --git a/plugins/CoreHome/stylesheets/coreHome.less b/plugins/CoreHome/stylesheets/coreHome.less
index 5474eae3d2..63607d749f 100644
--- a/plugins/CoreHome/stylesheets/coreHome.less
+++ b/plugins/CoreHome/stylesheets/coreHome.less
@@ -120,6 +120,12 @@ div.ui-datepicker {
display: none;
}
+.reporting-page {
+ .sparklines {
+ max-width: 1250px;
+ }
+}
+
div .sparkline {
float: left;
clear: both;
diff --git a/plugins/CoreHome/stylesheets/zen-mode.less b/plugins/CoreHome/stylesheets/zen-mode.less
index 124c3395ff..112c6b2da6 100644
--- a/plugins/CoreHome/stylesheets/zen-mode.less
+++ b/plugins/CoreHome/stylesheets/zen-mode.less
@@ -77,13 +77,25 @@
#content:not(.admin), .widget {
- h2:nth-of-type(n+2) {
- margin-top: 40px;
+ .reporting-page {
+ .smallTopMargin:not(.graphEvolution) {
+ margin-top: 20px;
+ }
}
h2 {
+ margin-top: 40px;
padding-left: 10px;
font-size: 24px;
+
+ &.noTopMargin {
+ margin-top: 0px;
+ }
+ }
+
+ .widget [piwik-widget-container] [piwik-widget]:first-child h2 {
+ // eg Visits Overview with Graph should not have a margin-top
+ margin-top: 0px;
}
}
diff --git a/plugins/CoreHome/templates/ReportsByDimension/_reportsByDimension.twig b/plugins/CoreHome/templates/ReportsByDimension/_reportsByDimension.twig
deleted file mode 100644
index 57ec8279d5..0000000000
--- a/plugins/CoreHome/templates/ReportsByDimension/_reportsByDimension.twig
+++ /dev/null
@@ -1,29 +0,0 @@
-
-
-
- {% for category, dimensions in dimensionCategories %}
- {% set firstCategory = (loop.index0 == 0) %}
-
- {{ category|translate }}
-
- {% for idx, dimension in dimensions %}
-
- {{ dimension.title|translate }}
-
- {% endfor %}
-
-
- {% endfor %}
-
-
-
-
-
{{ 'General_LoadingData'|translate }}
-
-
-
{{ firstReport|raw }}
-
-
-
-
diff --git a/plugins/CoreHome/templates/_indexContent.twig b/plugins/CoreHome/templates/_indexContent.twig
index 46d2f13579..671c26e782 100644
--- a/plugins/CoreHome/templates/_indexContent.twig
+++ b/plugins/CoreHome/templates/_indexContent.twig
@@ -12,8 +12,11 @@
{{ ajax.loadingDiv() }}
+
+
{% if content %}{{ content }}{% endif %}
+
diff --git a/plugins/CoreHome/templates/getDefaultIndexView.twig b/plugins/CoreHome/templates/getDefaultIndexView.twig
index 7b46ca3f76..4efd453159 100644
--- a/plugins/CoreHome/templates/getDefaultIndexView.twig
+++ b/plugins/CoreHome/templates/getDefaultIndexView.twig
@@ -4,9 +4,8 @@
{% include "@CoreHome/_siteSelectHeader.twig" %}
-{% if (menu is defined and menu) %}
- {% include "@CoreHome/_menu.twig" %}
-{% endif %}
+
+
{% include "@CoreHome/_indexContent.twig" %}
diff --git a/plugins/CoreHome/templates/widgetContainer.twig b/plugins/CoreHome/templates/widgetContainer.twig
new file mode 100755
index 0000000000..b3eda12997
--- /dev/null
+++ b/plugins/CoreHome/templates/widgetContainer.twig
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php b/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php
index 5675153c34..5114167364 100644
--- a/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php
+++ b/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php
@@ -12,7 +12,6 @@ use Piwik\Archive\DataTableFactory;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\DataTable\Row;
-use Piwik\Menu\MenuMain;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\Url;
@@ -76,7 +75,6 @@ class Evolution extends JqplotDataGenerator
$periodLabel = reset($dataTables)->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX)->getLabel();
$axisXOnClick = array();
- $queryStringAsHash = $this->getQueryStringAsHash();
foreach ($dataTable->getDataTables() as $metadataDataTable) {
$dateInUrl = $metadataDataTable->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX)->getDateStart();
$parameters = array(
@@ -85,16 +83,7 @@ class Evolution extends JqplotDataGenerator
'date' => $dateInUrl->toString(),
'segment' => \Piwik\API\Request::getRawSegmentFromRequest()
);
- $hash = '';
- if (!empty($queryStringAsHash)) {
- $hash = '#' . Url::getQueryStringFromParameters($queryStringAsHash + $parameters);
- }
- $link = 'index.php?' .
- Url::getQueryStringFromParameters(array(
- 'module' => 'CoreHome',
- 'action' => 'index',
- ) + $parameters)
- . $hash;
+ $link = Url::getQueryStringFromParameters($parameters);
$axisXOnClick[] = $link;
}
$visualization->setAxisXOnClick($axisXOnClick);
@@ -144,34 +133,6 @@ class Evolution extends JqplotDataGenerator
return $label;
}
- /**
- * We link the graph dots to the same report as currently being displayed (only the date would change).
- *
- * In some cases the widget is loaded within a report that doesn't exist as such.
- * For example, the dashboards loads the 'Last visits graph' widget which can't be directly linked to.
- * Instead, the graph must link back to the dashboard.
- *
- * In other cases, like Visitors>Overview or the Goals graphs, we can link the graph clicks to the same report.
- *
- * To detect whether or not we can link to a report, we simply check if the current URL from which it was loaded
- * belongs to the menu or not. If it doesn't belong to the menu, we do not append the hash to the URL,
- * which results in loading the dashboard.
- *
- * @return array Query string array to append to the URL hash or false if the dashboard should be displayed
- */
- private function getQueryStringAsHash()
- {
- $queryString = Url::getArrayFromCurrentQueryString();
- $piwikParameters = array('idSite', 'date', 'period', 'XDEBUG_SESSION_START', 'KEY');
- foreach ($piwikParameters as $parameter) {
- unset($queryString[$parameter]);
- }
- if (MenuMain::getInstance()->isUrlFound($queryString)) {
- return $queryString;
- }
- return false;
- }
-
private function isLinkEnabled()
{
static $linkEnabled;
diff --git a/plugins/CoreVisualizations/Visualizations/Sparkline.php b/plugins/CoreVisualizations/Visualizations/Sparkline.php
index 2ca75bbcfe..3c1dbf5a56 100644
--- a/plugins/CoreVisualizations/Visualizations/Sparkline.php
+++ b/plugins/CoreVisualizations/Visualizations/Sparkline.php
@@ -25,7 +25,7 @@ class Sparkline extends ViewDataTable
* @see ViewDataTable::main()
* @return mixed
*/
- protected function buildView()
+ public function render()
{
// If period=range, we force the sparkline to draw daily data points
$period = Common::getRequestVar('period');
@@ -58,7 +58,7 @@ class Sparkline extends ViewDataTable
$graph->main();
- return $graph;
+ return $graph->render();
}
/**
diff --git a/plugins/CoreVisualizations/Visualizations/Sparklines.php b/plugins/CoreVisualizations/Visualizations/Sparklines.php
new file mode 100644
index 0000000000..3b576c885e
--- /dev/null
+++ b/plugins/CoreVisualizations/Visualizations/Sparklines.php
@@ -0,0 +1,147 @@
+config->addSparklineMetric('nb_visits'); // if an array of metrics given, they will be displayed comma separated
+ * $view->config->addTranslation('nb_visits', 'Visits');
+ * Results in: [sparkline image] X visits
+ * Data is fetched from the configured $view->requestConfig->apiMethodToRequestDataTable.
+ *
+ * In case you want to add any custom sparklines from any other API method you can call
+ * {@link Sparklines\Config::addSparkline()}.
+ *
+ * Example:
+ * $sparklineUrlParams = array('columns' => array('nb_visits));
+ * $evolution = array('currentValue' => 5, 'pastValue' => 10, 'tooltip' => 'Foo bar');
+ * $view->config->addSparkline($sparklineUrlParams, $value = 5, $description = 'Visits', $evolution);
+ *
+ * @property Sparklines\Config $config
+ */
+class Sparklines extends ViewDataTable
+{
+ const ID = 'sparklines';
+
+ public static function getDefaultConfig()
+ {
+ return new Sparklines\Config();
+ }
+
+ /**
+ * @see ViewDataTable::main()
+ * @return mixed
+ */
+ public function render()
+ {
+ $view = new View('@CoreVisualizations/_dataTableViz_sparklines.twig');
+
+ $columnsList = array();
+ if ($this->config->hasSparklineMetrics()) {
+ foreach ($this->config->getSparklineMetrics() as $cols) {
+ $columnsList = array_merge($cols['columns'], $columnsList);
+ }
+ }
+
+ $this->requestConfig->request_parameters_to_modify['columns'] = $columnsList;
+ $this->requestConfig->request_parameters_to_modify['format_metrics'] = '1';
+
+ if (!empty($this->requestConfig->apiMethodToRequestDataTable)) {
+ $this->fetchConfiguredSparklines();
+ }
+
+ $view->sparklines = $this->config->getSortedSparklines();
+
+ return $view->render();
+ }
+
+ private function fetchConfiguredSparklines()
+ {
+ $data = $this->loadDataTableFromAPI();
+
+ $this->applyFilters($data);
+
+ if (!$this->config->hasSparklineMetrics()) {
+ foreach ($data->getColumns() as $column) {
+ $this->config->addSparklineMetric($column);
+ }
+ }
+
+ $translations = $this->config->translations;
+
+ $firstRow = $data->getFirstRow();
+
+ foreach ($this->config->getSparklineMetrics() as $sparklineMetric) {
+ $column = $sparklineMetric['columns'];
+ $order = $sparklineMetric['order'];
+
+ if ($column === 'label') {
+ continue;
+ }
+
+ if (empty($column)) {
+ $this->config->addPlaceholder($order);
+ continue;
+ }
+
+ if (!is_array($column)) {
+ $column = array($column);
+ }
+
+ $values = array();
+ $descriptions = array();
+
+ foreach ($column as $col) {
+ $value = $firstRow->getColumn($col);
+
+ if ($value === false) {
+ $value = 0;
+ }
+
+ $values[] = $value;
+ $descriptions[] = isset($translations[$col]) ? $translations[$col] : $col;
+ }
+
+ $sparklineUrlParams = array(
+ 'columns' => $column,
+ 'module' => $this->requestConfig->getApiModuleToRequest(),
+ 'action' => $this->requestConfig->getApiMethodToRequest()
+ );
+
+ $this->config->addSparkline($sparklineUrlParams, $values, $descriptions, null, $order);
+ }
+ }
+
+ private function applyFilters(DataTable\DataTableInterface $table)
+ {
+ foreach ($this->config->getPriorityFilters() as $filter) {
+ $table->filter($filter[0], $filter[1]);
+ }
+
+ // queue other filters so they can be applied later if queued filters are disabled
+ foreach ($this->config->getPresentationFilters() as $filter) {
+ $table->queueFilter($filter[0], $filter[1]);
+ }
+
+ $table->applyQueuedFilters();
+ }
+}
diff --git a/plugins/CoreVisualizations/Visualizations/Sparklines/Config.php b/plugins/CoreVisualizations/Visualizations/Sparklines/Config.php
new file mode 100644
index 0000000000..ca54a6d564
--- /dev/null
+++ b/plugins/CoreVisualizations/Visualizations/Sparklines/Config.php
@@ -0,0 +1,354 @@
+translations = Metrics::getDefaultMetricTranslations();
+ }
+
+ /**
+ * @ignore
+ * @return array
+ */
+ public function getSparklineMetrics()
+ {
+ return $this->sparkline_metrics;
+ }
+
+ /**
+ * @ignore
+ * @return bool
+ */
+ public function hasSparklineMetrics()
+ {
+ return !empty($this->sparkline_metrics);
+ }
+
+ /**
+ * Removes an existing sparkline entry. Especially useful in dataTable filters in case sparklines should be not
+ * displayed depending on the fetched data.
+ *
+ * Example:
+ * $config->addSparklineMetric('nb_users');
+ * $config->filters[] = function ($dataTable) use ($config) {
+ * if ($dataTable->getFirstRow()->getColumn('nb_users') == 0) {
+ * // do not show a sparkline if there are no recorded users
+ * $config->removeSparklineMetric('nb_users');
+ * }
+ * }
+ *
+ * @param array|string $metricNames The name of the metrics in the same format they were used when added via
+ * {@link addSparklineMetric}
+ */
+ public function removeSparklineMetric($metricNames)
+ {
+ foreach ($this->sparkline_metrics as $index => $metric) {
+ if ($metric['columns'] === $metricNames) {
+ array_splice($this->sparkline_metrics, $index, 1);
+
+ break;
+ }
+ }
+ }
+
+ /**
+ * Replaces an existing sparkline entry with different columns. Especially useful in dataTable filters in case
+ * sparklines should be not displayed depending on the fetched data.
+ *
+ * Example:
+ * $config->addSparklineMetric('nb_users');
+ * $config->filters[] = function ($dataTable) use ($config) {
+ * if ($dataTable->getFirstRow()->getColumn('nb_users') == 0) {
+ * // instead of showing the sparklines for users, show a placeholder if there are no recorded users
+ * $config->replaceSparklineMetric(array('nb_users'), '');
+ * }
+ * }
+ *
+ * @param array|string $metricNames The name of the metrics in the same format they were used when added via
+ * {@link addSparklineMetric}
+ * @param array|string $replacementColumns The removed columns will be replaced with these columns
+ */
+ public function replaceSparklineMetric($metricNames, $replacementColumns)
+ {
+ foreach ($this->sparkline_metrics as $index => $metric) {
+ if ($metric['columns'] === $metricNames) {
+ $this->sparkline_metrics[$index]['columns'] = $replacementColumns;
+ }
+ }
+ }
+
+ /**
+ * Adds a new sparkline.
+ *
+ * It will show a sparkline image, the value of the resolved metric name and a descrption. Optionally, multiple
+ * values can be shown after a sparkline image by passing multiple metric names
+ * (eg array('nb_visits', 'nb_actions')). The data will be requested from the configured api method see
+ * {@link Piwik\ViewDataTable\RequestConfig::$apiMethodToRequestDataTable}.
+ *
+ * Example:
+ * $config->addSparklineMetric('nb_visits');
+ * $config->addTranslation('nb_visits', 'Visits');
+ * Results in: [sparkline image] X visits
+ *
+ * Example:
+ * $config->addSparklineMetric(array('nb_visits', 'nb_actions'));
+ * $config->addTranslations(array('nb_visits' => 'Visits', 'nb_actions' => 'Actions'));
+ * Results in: [sparkline image] X visits, Y actions
+ *
+ * @param string|array $metricName Either one metric name (eg 'nb_visits') or an array of metric names
+ * @param int|null $order Defines the order. The lower the order the earlier the sparkline will be displayed.
+ * By default the sparkline will be appended to the end.
+ */
+ public function addSparklineMetric($metricName, $order = null)
+ {
+ $this->sparkline_metrics[] = array(
+ 'columns' => $metricName,
+ 'order' => $order
+ );
+ }
+
+ /**
+ * Adds a placeholder. In this case nothing will be shown, neither a sparkline nor any description. This can be
+ * useful if you want to have some kind of separator. Eg if you want to have a sparkline on the left side but
+ * not sparkline on the right side.
+ *
+ * @param int|null $order Defines the order. The lower the order the earlier the sparkline will be displayed.
+ * By default the sparkline will be appended to the end.
+ */
+ public function addPlaceholder($order = null)
+ {
+ $this->sparklines[] = array(
+ 'url' => '',
+ 'metrics' => array(),
+ 'order' => $this->getSparklineOrder($order)
+ );
+ }
+
+ /**
+ * Add a new sparkline to be displayed to the view.
+ *
+ * Each sparkline can consist of one or multiple metrics. One metric consists of a value and a description. By
+ * default the value is shown first, then the description. The description can optionally contain a '%s' in case
+ * the value shall be displayed within the description. If multiple metrics are given, they will be separated by
+ * a comma.
+ *
+ * @param array $requestParamsForSparkline You need to at least set a module / action eg
+ * array('columns' => array('nb_visit'), 'module' => '', 'action' => '')
+ * @param int|float|string|array $value Either the metric value or an array of values.
+ * @param string|array $description Either one description or an array of descriptions. If an array, both
+ * $value and $description need the same amount of array entries.
+ * $description[0] should be the description for $value[0].
+ * $description should be already translated. If $value should appear
+ * somewhere within the text a `%s` can be used in the translation.
+ * @param array|null $evolution Optional array containing at least the array keys 'currentValue' and
+ * 'pastValue' which are needed to calculate the correct percentage.
+ * An optional 'tooltip' can be set as well. Eg
+ * array('currentValue' => 10, 'pastValue' => 20,
+ * 'tooltip' => '10 visits in 2015-07-26 compared to 20 visits in 2015-07-25')
+ * @param int $order Defines the order. The lower the order the earlier the sparkline will be
+ * displayed. By default the sparkline will be appended to the end.
+ * @throws \Exception In case an evolution parameter is set but has wrong data structure
+ */
+ public function addSparkline($requestParamsForSparkline, $value, $description, $evolution = null, $order = null)
+ {
+ $metrics = array();
+
+ if (is_array($value)) {
+ $values = $value;
+ } else {
+ $values = array($value);
+ }
+
+ if (!is_array($description)) {
+ $description = array($description);
+ }
+
+ if (count($values) === count($description)) {
+ foreach ($values as $index => $value) {
+ $metrics[] = array(
+ 'value' => $value,
+ 'description' => $description[$index]
+ );
+ }
+ } else {
+ $msg = 'The number of values and descriptions need to be the same to add a sparkline. ';
+ $msg .= 'Values: ' . implode(', ', $values). ' Descriptions: ' . implode(', ', $description);
+ throw new \Exception($msg);
+ }
+
+ if (empty($metrics)) {
+ return;
+ }
+
+ $sparkline = array(
+ 'url' => $this->getUrlSparkline($requestParamsForSparkline),
+ 'metrics' => $metrics,
+ 'order' => $this->getSparklineOrder($order)
+ );
+
+ if (!empty($evolution)) {
+ if (!is_array($evolution) ||
+ !array_key_exists('currentValue', $evolution) ||
+ !array_key_exists('pastValue', $evolution)) {
+ throw new \Exception('In order to show an evolution in the sparklines view a currentValue and pastValue array key needs to be present');
+ }
+
+ $evolutionPercent = CalculateEvolutionFilter::calculate($evolution['currentValue'], $evolution['pastValue'], $precision = 1);
+
+ // do not display evolution if evolution percent is 0 and current value is 0
+ if ($evolutionPercent != 0 || $evolution['currentValue'] != 0) {
+ $sparkline['evolution'] = array(
+ 'percent' => $evolutionPercent,
+ 'tooltip' => !empty($evolution['tooltip']) ? $evolution['tooltip'] : null
+ );
+ }
+
+ }
+
+ $this->sparklines[] = $sparkline;
+ }
+
+ /**
+ * @return array
+ * @ignore
+ */
+ public function getSortedSparklines()
+ {
+ usort($this->sparklines, function ($a, $b) {
+ if ($a['order'] == $b['order']) {
+ return 0;
+ }
+ return ($a['order'] < $b['order']) ? -1 : 1;
+ });
+
+ return $this->sparklines;
+ }
+
+ private function getSparklineOrder($order)
+ {
+ if (!isset($order)) {
+ // make sure to append to the end if nothing set (in the order they are added)
+ $order = 999 + count($this->sparklines);
+ }
+
+ return (int) $order;
+ }
+
+ /**
+ * Returns a URL to a sparkline image for a report served by the current plugin.
+ *
+ * The result of this URL should be used with the [sparkline()](/api-reference/Piwik/View#twig) twig function.
+ *
+ * The current site ID and period will be used.
+ *
+ * @param array $customParameters The array of query parameter name/value pairs that
+ * should be set in result URL.
+ * @return string The generated URL.
+ */
+ private function getUrlSparkline($customParameters = array())
+ {
+ $customParameters['viewDataTable'] = 'sparkline';
+
+ $params = $this->getGraphParamsModified($customParameters);
+
+ // convert array values to comma separated
+ foreach ($params as &$value) {
+ if (is_array($value)) {
+ $value = rawurlencode(implode(',', $value));
+ }
+ }
+ $url = Url::getCurrentQueryStringWithParametersModified($params);
+ return $url;
+ }
+
+ /**
+ * Returns the array of new processed parameters once the parameters are applied.
+ * For example: if you set range=last30 and date=2008-03-10,
+ * the date element of the returned array will be "2008-02-10,2008-03-10"
+ *
+ * Parameters you can set:
+ * - range: last30, previous10, etc.
+ * - date: YYYY-MM-DD, today, yesterday
+ * - period: day, week, month, year
+ *
+ * @param array $paramsToSet array( 'date' => 'last50', 'viewDataTable' =>'sparkline' )
+ * @throws \Piwik\NoAccessException
+ * @return array
+ */
+ private function getGraphParamsModified($paramsToSet = array())
+ {
+ if (!isset($paramsToSet['period'])) {
+ $period = Common::getRequestVar('period');
+ } else {
+ $period = $paramsToSet['period'];
+ }
+
+ if ($period == 'range') {
+ return $paramsToSet;
+ }
+
+ if (!isset($paramsToSet['range'])) {
+ $range = 'last30';
+ } else {
+ $range = $paramsToSet['range'];
+ }
+
+ if (!isset($paramsToSet['idSite'])) {
+ $idSite = Common::getRequestVar('idSite');
+ } else {
+ $idSite = $paramsToSet['idSite'];
+ }
+
+ if (!isset($paramsToSet['date'])) {
+ $endDate = Common::getRequestVar('date', 'yesterday', 'string');
+ } else {
+ $endDate = $paramsToSet['date'];
+ }
+
+ $site = new Site($idSite);
+
+ if (is_null($site)) {
+ throw new NoAccessException("Website not initialized, check that you are logged in and/or using the correct token_auth.");
+ }
+
+ $paramDate = Range::getRelativeToEndDate($period, $range, $endDate, $site);
+
+ $params = array_merge($paramsToSet, array('date' => $paramDate));
+ return $params;
+ }
+
+}
diff --git a/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js b/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js
index 3b62e9197e..b77add4962 100644
--- a/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js
+++ b/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js
@@ -90,36 +90,7 @@
&& typeof self.jqplotParams.axes.xaxis.onclick[lastTick] == 'string') {
var url = self.jqplotParams.axes.xaxis.onclick[lastTick];
- if (url && -1 === url.indexOf('#')) {
- var module = broadcast.getValueFromHash('module');
- var action = broadcast.getValueFromHash('action');
- var idGoal = broadcast.getValueFromHash('idGoal');
- var idSite = broadcast.getValueFromUrl('idSite', url);
- var period = broadcast.getValueFromUrl('period', url);
- var date = broadcast.getValueFromUrl('date', url);
-
- if (module && action) {
- url += '#module=' + module + '&action=' + action;
-
- if (idSite) {
- url += '&idSite=' + idSite;
- }
-
- if (idGoal) {
- url += '&idGoal=' + idGoal;
- }
-
- if (period) {
- url += '&period=' + period;
- }
-
- if (period) {
- url += '&date=' + date;
- }
- }
- }
-
- piwikHelper.redirectToUrl(url);
+ broadcast.propagateNewPage(url);
}
})
.on('jqplotPiwikTickOver', function (e, tick) {
@@ -161,6 +132,10 @@
render: function () {
JqplotGraphDataTablePrototype.render.call(this);
+
+ if (initializeSparklines) {
+ initializeSparklines();
+ }
}
});
diff --git a/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig b/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig
new file mode 100644
index 0000000000..359c0f9768
--- /dev/null
+++ b/plugins/CoreVisualizations/templates/_dataTableViz_sparklines.twig
@@ -0,0 +1,31 @@
+{% import '@CoreVisualizations/macros.twig' as macros %}
+
+{% if not isWidget %}
+
+
+{% endif %}
+
+ {% for key, sparkline in sparklines %}
+ {% if key is even %}
+ {{ macros.singleSparkline(sparkline) }}
+ {% endif %}
+ {% endfor %}
+
+{% if not isWidget %}
+
+
+{% endif %}
+
+ {% for key, sparkline in sparklines %}
+ {% if key is odd %}
+ {{ macros.singleSparkline(sparkline) }}
+ {% endif %}
+ {% endfor %}
+
+{% if not isWidget %}
+
+
+{% endif %}
+
+{% include "_sparklineFooter.twig" %}
+
diff --git a/plugins/CoreVisualizations/templates/macros.twig b/plugins/CoreVisualizations/templates/macros.twig
new file mode 100644
index 0000000000..ffd1885be1
--- /dev/null
+++ b/plugins/CoreVisualizations/templates/macros.twig
@@ -0,0 +1,32 @@
+{% macro singleSparkline(sparkline) %}
+
+ {% if sparkline.url %}{{ sparkline(sparkline.url)|raw }}{% endif %}
+ {% for metric in sparkline.metrics %}
+ {% if '%s' in metric.description -%}
+ {{ metric.description|translate("
"~metric.value~" ")|raw }}
+ {%- else %}
+
{{ metric.value }} {{ metric.description }}
+ {%- endif %}{% if not loop.last %}, {% endif %}
+ {% endfor %}
+ {% if sparkline.evolution is defined %}
+
+ {% set evolutionPretty = sparkline.evolution.percent %}
+
+ {% if sparkline.evolution.percent < 0 %}
+ {% set evolutionClass = 'negative-evolution' %}
+ {% set evolutionIcon = 'arrow_down.png' %}
+ {% elseif sparkline.evolution.percent == 0 %}
+ {% set evolutionClass = 'neutral-evolution' %}
+ {% set evolutionIcon = 'stop.png' %}
+ {% else %}
+ {% set evolutionClass = 'positive-evolution' %}
+ {% set evolutionIcon = 'arrow_up.png' %}
+ {% set evolutionPretty = '+' ~ sparkline.evolution.percent %}
+ {% endif %}
+
+
+ {{ evolutionPretty }}
+ {% endif %}
+
+{% endmacro %}
diff --git a/plugins/CoreVisualizations/tests/Integration/SparklinesConfigTest.php b/plugins/CoreVisualizations/tests/Integration/SparklinesConfigTest.php
new file mode 100644
index 0000000000..3541344f04
--- /dev/null
+++ b/plugins/CoreVisualizations/tests/Integration/SparklinesConfigTest.php
@@ -0,0 +1,128 @@
+config = new Config();
+ }
+
+ public function test_addSparkline_shouldAddAMinimalSparklineWithOneValueAndUseDefaultOrder()
+ {
+ $this->config->addSparkline($this->sparklineParams(), $value = 10, $description = 'Visits');
+
+ $expectedSparkline = array(
+ 'url' => '?period=day&date=2012-03-06,2012-04-04&idSite=1&module=CoreHome&action=renderMe&viewDataTable=sparkline',
+ 'metrics' => array (
+ array ('value' => 10, 'description' => 'Visits'),
+ ),
+ 'order' => 999
+ );
+
+ $this->assertSame(array($expectedSparkline), $this->config->getSortedSparklines());
+ }
+
+ public function test_addSparkline_shouldAddSparklineWithMultipleValues()
+ {
+ $this->config->addSparkline($this->sparklineParams(), $values = array(10, 20), $description = array('Visits', 'Actions'));
+
+ $sparklines = $this->config->getSortedSparklines();
+
+ $this->assertSame(array (
+ array ('value' => 10, 'description' => 'Visits'),
+ array ('value' => 20, 'description' => 'Actions'),
+ ), $sparklines[0]['metrics']);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Values: 10, 20, 30 Descriptions: Visits, Actions
+ */
+ public function test_addSparkline_shouldThrowAnException_IfValuesDoesNotMatchAmountOfDescriptions()
+ {
+ $this->config->addSparkline($this->sparklineParams(), $values = array(10, 20, 30), $description = array('Visits', 'Actions'));
+ }
+
+ public function test_addSparkline_shouldAddEvolution()
+ {
+ $evolution = array('currentValue' => 10, 'pastValue' => 21,
+ 'tooltip' => '1 visit compared to 2 visits');
+ $this->config->addSparkline($this->sparklineParams(), $value = 10, $description = 'Visits', $evolution);
+
+ $sparklines = $this->config->getSortedSparklines();
+
+ $this->assertSame(array (
+ 'percent' => '-52.4%',
+ 'tooltip' => '1 visit compared to 2 visits'
+ ), $sparklines[0]['evolution']);
+ }
+
+ public function test_addSparkline_shouldAddOrder()
+ {
+ $this->config->addSparkline($this->sparklineParams(), $value = 10, $description = 'Visits', $evolution = null, $order = '42');
+
+ $sparklines = $this->config->getSortedSparklines();
+
+ $this->assertSame(42, $sparklines[0]['order']);
+ }
+
+ public function test_addSparkline_shouldBeAbleToBuildSparklineUrlBasedOnGETparams()
+ {
+ $oldGet = $_GET;
+ $_GET = $this->sparklineParams();
+ $this->config->addSparkline(array('columns' => 'nb_visits'), $value = 10, $description = 'Visits');
+ $_GET = $oldGet;
+
+ $sparklines = $this->config->getSortedSparklines();
+
+ $this->assertSame('?columns=nb_visits&viewDataTable=sparkline&date=2012-03-06,2012-04-04', $sparklines[0]['url']);
+ }
+
+ private function sparklineParams($params = array())
+ {
+ $params['period'] = 'day';
+ $params['date'] = '2012-04-04';
+ $params['idSite'] = '1';
+ $params['module'] = 'CoreHome';
+ $params['action'] = 'renderMe';
+
+ return $params;
+ }
+
+ public function provideContainerConfig()
+ {
+ return array(
+ 'Piwik\Access' => new FakeAccess()
+ );
+ }
+}
diff --git a/plugins/CoreVisualizations/tests/Unit/SparklinesConfigTest.php b/plugins/CoreVisualizations/tests/Unit/SparklinesConfigTest.php
new file mode 100644
index 0000000000..516f19f8e1
--- /dev/null
+++ b/plugins/CoreVisualizations/tests/Unit/SparklinesConfigTest.php
@@ -0,0 +1,130 @@
+config = new Config();
+ }
+
+ public function test_hasSparklineMetrics_shouldNotHaveSparklineMetrics_ByDefault()
+ {
+ $this->assertFalse($this->config->hasSparklineMetrics());
+ }
+
+ public function test_hasSparklineMetrics_shouldHaveSparklineMetrics_IfAtLeastOneWasAdded()
+ {
+ $this->config->addSparklineMetric('nb_visits');
+
+ $this->assertTrue($this->config->hasSparklineMetrics());
+ }
+
+ public function test_getSparklineMetrics_shouldNotHaveSparklineMetrics_ByDefault()
+ {
+ $this->assertSame(array(), $this->config->getSparklineMetrics());
+ }
+
+ public function test_addSparklineMetric_getSparklineMetrics_shouldReturnAllAddedSparklineMetrics()
+ {
+ $this->addFewSparklines();
+
+ $this->assertSame(array(
+ array('columns' => 'nb_visits', 'order' => null),
+ array('columns' => 'nb_unique_visitors', 'order' => 99),
+ array('columns' => array('nb_downloads', 'nb_outlinks'), 'order' => null),
+ ), $this->config->getSparklineMetrics());
+ }
+
+ public function test_removeSparklineMetric_shouldRemoveMetric_IfOnlySingleMetricIsGiven()
+ {
+ $this->addFewSparklines();
+
+ $this->config->removeSparklineMetric('nb_unique_visitors');
+
+ $this->assertSame(array(
+ array('columns' => 'nb_visits', 'order' => null),
+ array('columns' => array('nb_downloads', 'nb_outlinks'), 'order' => null),
+ ), $this->config->getSparklineMetrics());
+ }
+
+ public function test_removeSparklineMetric_shouldRemoveMetric_IfMultipleMetricsAreGiven()
+ {
+ $this->addFewSparklines();
+
+ $this->config->removeSparklineMetric(array('nb_downloads', 'nb_outlinks'));
+
+ $this->assertSame(array(
+ array('columns' => 'nb_visits', 'order' => null),
+ array('columns' => 'nb_unique_visitors', 'order' => 99),
+ ), $this->config->getSparklineMetrics());
+ }
+
+ public function test_replaceSparklineMetric_shouldBeAbleToReplaceColumns_IfSingleMetricIsGiven()
+ {
+ $this->addFewSparklines();
+
+ $this->config->replaceSparklineMetric('nb_unique_visitors', '');
+
+ $this->assertSame(array(
+ array('columns' => 'nb_visits', 'order' => null),
+ array('columns' => '', 'order' => 99),
+ array('columns' => array('nb_downloads', 'nb_outlinks'), 'order' => null),
+ ), $this->config->getSparklineMetrics());
+ }
+
+ public function test_replaceSparklineMetric_shouldBeAbleToReplaceColumns_IfMultipleMetricsAreGiven()
+ {
+ $this->addFewSparklines();
+
+ $this->config->replaceSparklineMetric(array('nb_downloads', 'nb_outlinks'), '');
+
+ $this->assertSame(array(
+ array('columns' => 'nb_visits', 'order' => null),
+ array('columns' => 'nb_unique_visitors', 'order' => 99),
+ array('columns' => '', 'order' => null),
+ ), $this->config->getSparklineMetrics());
+ }
+
+ public function test_addPlaceholder_getSortedSparklines()
+ {
+ $this->config->addPlaceholder();
+ $this->config->addPlaceholder($order = 10);
+ $this->config->addPlaceholder();
+ $this->config->addPlaceholder($order = 3);
+
+ $this->assertSame(array(
+ array('url' => '', 'metrics' => array(), 'order' => 3),
+ array('url' => '', 'metrics' => array(), 'order' => 10),
+ array('url' => '', 'metrics' => array(), 'order' => 999),
+ array('url' => '', 'metrics' => array(), 'order' => 1001),
+ ), $this->config->getSortedSparklines());
+ }
+
+ private function addFewSparklines()
+ {
+ $this->config->addSparklineMetric('nb_visits');
+ $this->config->addSparklineMetric('nb_unique_visitors', 99);
+ $this->config->addSparklineMetric(array('nb_downloads', 'nb_outlinks'));
+ }
+
+}
diff --git a/plugins/CustomVariables/Categories/CustomVariablesSubcategory.php b/plugins/CustomVariables/Categories/CustomVariablesSubcategory.php
new file mode 100644
index 0000000000..d81b56568c
--- /dev/null
+++ b/plugins/CustomVariables/Categories/CustomVariablesSubcategory.php
@@ -0,0 +1,19 @@
+category = 'General_Visitors';
+ $this->categoryId = 'General_Visitors';
}
}
diff --git a/plugins/CustomVariables/Reports/GetCustomVariables.php b/plugins/CustomVariables/Reports/GetCustomVariables.php
index 2ddedc4e97..c6b28b6562 100644
--- a/plugins/CustomVariables/Reports/GetCustomVariables.php
+++ b/plugins/CustomVariables/Reports/GetCustomVariables.php
@@ -23,8 +23,8 @@ class GetCustomVariables extends Base
array(' ', '', ' '));
$this->actionToLoadSubTables = 'getCustomVariablesValuesFromNameId';
$this->order = 10;
- $this->widgetTitle = 'CustomVariables_CustomVariables';
- $this->menuTitle = 'CustomVariables_CustomVariables';
+
+ $this->subcategoryId = 'CustomVariables_CustomVariables';
$this->hasGoalMetrics = true;
}
diff --git a/plugins/DBStats/Reports/GetMetricDataSummary.php b/plugins/DBStats/Reports/GetMetricDataSummary.php
index 92b93fce9b..94735c4e03 100644
--- a/plugins/DBStats/Reports/GetMetricDataSummary.php
+++ b/plugins/DBStats/Reports/GetMetricDataSummary.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\DBStats\Reports;
use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\CoreVisualizations\Visualizations\Graph;
+use Piwik\Plugin\Reports;
/**
* Shows a datatable that displays the amount of space each numeric archive table
@@ -34,7 +35,7 @@ class GetMetricDataSummary extends Base
public function getRelatedReports()
{
return array(
- self::factory('DBStats', 'getMetricDataSummaryByYear'),
+ Reports::factory('DBStats', 'getMetricDataSummaryByYear'),
);
}
diff --git a/plugins/DBStats/Reports/GetMetricDataSummaryByYear.php b/plugins/DBStats/Reports/GetMetricDataSummaryByYear.php
index bc5e8e5c3c..81b89d5d99 100644
--- a/plugins/DBStats/Reports/GetMetricDataSummaryByYear.php
+++ b/plugins/DBStats/Reports/GetMetricDataSummaryByYear.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\DBStats\Reports;
use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\CoreVisualizations\Visualizations\Graph;
+use Piwik\Plugin\Reports;
/**
* Shows a datatable that displays the amount of space each numeric archive table
@@ -35,7 +36,7 @@ class GetMetricDataSummaryByYear extends Base
public function getRelatedReports()
{
return array(
- self::factory('DBStats', 'getMetricDataSummary'),
+ Reports::factory('DBStats', 'getMetricDataSummary'),
);
}
diff --git a/plugins/DBStats/Reports/GetReportDataSummary.php b/plugins/DBStats/Reports/GetReportDataSummary.php
index d38f3ee717..58c1b065c5 100644
--- a/plugins/DBStats/Reports/GetReportDataSummary.php
+++ b/plugins/DBStats/Reports/GetReportDataSummary.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\DBStats\Reports;
use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\CoreVisualizations\Visualizations\Graph;
+use Piwik\Plugin\Reports;
/**
* Shows a datatable that displays the amount of space each blob archive table
@@ -34,7 +35,7 @@ class GetReportDataSummary extends Base
public function getRelatedReports()
{
return array(
- self::factory('DBStats', 'getReportDataSummaryByYear'),
+ Reports::factory('DBStats', 'getReportDataSummaryByYear'),
);
}
}
diff --git a/plugins/DBStats/Reports/GetReportDataSummaryByYear.php b/plugins/DBStats/Reports/GetReportDataSummaryByYear.php
index 1bbe67e467..7ff78a638b 100644
--- a/plugins/DBStats/Reports/GetReportDataSummaryByYear.php
+++ b/plugins/DBStats/Reports/GetReportDataSummaryByYear.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\DBStats\Reports;
use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\CoreVisualizations\Visualizations\Graph;
+use Piwik\Plugin\Reports;
/**
* Shows a datatable that displays the amount of space each blob archive table
@@ -35,7 +36,7 @@ class GetReportDataSummaryByYear extends Base
public function getRelatedReports()
{
return array(
- self::factory('DBStats', 'getReportDataSummary'),
+ Reports::factory('DBStats', 'getReportDataSummary'),
);
}
diff --git a/plugins/Dashboard/API.php b/plugins/Dashboard/API.php
index f4637e862a..7bd5bd73a5 100644
--- a/plugins/Dashboard/API.php
+++ b/plugins/Dashboard/API.php
@@ -8,7 +8,6 @@
namespace Piwik\Plugins\Dashboard;
use Piwik\Piwik;
-use Piwik\WidgetsList;
/**
* This API is the Dashboard API : it gives information about dashboards.
@@ -43,25 +42,22 @@ class API extends \Piwik\Plugin\API
/**
* Get the default dashboard.
- *
- * @return array[]
+ * @return \array[]
*/
private function getDefaultDashboard()
{
$defaultLayout = $this->dashboard->getDefaultLayout();
$defaultLayout = $this->dashboard->decodeLayout($defaultLayout);
+ $defaultDashboard = array('name' => Piwik::translate('Dashboard_Dashboard'), 'layout' => $defaultLayout, 'iddashboard' => 1);
- $defaultDashboard = array('name' => Piwik::translate('Dashboard_Dashboard'), 'layout' => $defaultLayout);
-
- $widgets = $this->getExistingWidgetsWithinDashboard($defaultDashboard);
+ $widgets = $this->getVisibleWidgetsWithinDashboard($defaultDashboard);
return $this->buildDashboard($defaultDashboard, $widgets);
}
/**
* Get all dashboards which a user has created.
- *
- * @return array[]
+ * @return \array[]
*/
private function getUserDashboards()
{
@@ -71,17 +67,14 @@ class API extends \Piwik\Plugin\API
$dashboards = array();
foreach ($userDashboards as $userDashboard) {
-
- if ($this->hasDashboardColumns($userDashboard)) {
- $widgets = $this->getExistingWidgetsWithinDashboard($userDashboard);
- $dashboards[] = $this->buildDashboard($userDashboard, $widgets);
- }
+ $widgets = $this->getVisibleWidgetsWithinDashboard($userDashboard);
+ $dashboards[] = $this->buildDashboard($userDashboard, $widgets);
}
return $dashboards;
}
- private function getExistingWidgetsWithinDashboard($dashboard)
+ private function getVisibleWidgetsWithinDashboard($dashboard)
{
$columns = $this->getColumnsFromDashboard($dashboard);
@@ -91,7 +84,7 @@ class API extends \Piwik\Plugin\API
foreach ($columns as $column) {
foreach ($column as $widget) {
- if ($this->widgetIsNotHidden($widget) && $this->widgetExists($widget)) {
+ if ($this->widgetIsNotHidden($widget) && !empty($widget->parameters->module)) {
$module = $widget->parameters->module;
$action = $widget->parameters->action;
@@ -105,39 +98,24 @@ class API extends \Piwik\Plugin\API
private function getColumnsFromDashboard($dashboard)
{
- if (is_array($dashboard['layout'])) {
-
- return $dashboard['layout'];
+ if (empty($dashboard['layout'])) {
+ return array();
}
- return $dashboard['layout']->columns;
- }
-
- private function hasDashboardColumns($dashboard)
- {
if (is_array($dashboard['layout'])) {
+ return $dashboard['layout'];
+ }
- return !empty($dashboard['layout']);
+ if (!empty($dashboard['layout']->columns)) {
+ return $dashboard['layout']->columns;
}
- return !empty($dashboard['layout']->columns);
+ return array();
}
private function buildDashboard($dashboard, $widgets)
{
- return array('name' => $dashboard['name'], 'widgets' => $widgets);
- }
-
- private function widgetExists($widget)
- {
- if (empty($widget->parameters->module)) {
- return false;
- }
-
- $module = $widget->parameters->module;
- $action = $widget->parameters->action;
-
- return WidgetsList::isDefined($module, $action);
+ return array('name' => $dashboard['name'], 'id' => $dashboard['iddashboard'], 'widgets' => $widgets);
}
private function widgetIsNotHidden($widget)
diff --git a/plugins/Dashboard/Categories/DashboardCategory.php b/plugins/Dashboard/Categories/DashboardCategory.php
new file mode 100644
index 0000000000..99a3785c84
--- /dev/null
+++ b/plugins/Dashboard/Categories/DashboardCategory.php
@@ -0,0 +1,17 @@
+setGeneralVariablesView($view);
- $view->availableWidgets = json_encode(WidgetsList::get());
$view->availableLayouts = $this->getAvailableLayouts();
$view->dashboardId = Common::getRequestVar('idDashboard', 1, 'int');
- // get the layout via FrontController so controller events are posted
- $view->dashboardLayout = FrontController::getInstance()->dispatch('Dashboard', 'getDashboardLayout',
- array($checkToken = false));
-
return $view;
}
+ // this
public function embeddedIndex()
{
$view = $this->_getDashboardView('@Dashboard/embeddedIndex');
return $view->render();
}
+ // this is the exported widget
public function index()
{
$view = $this->_getDashboardView('@Dashboard/index');
@@ -70,14 +65,6 @@ class Controller extends \Piwik\Plugin\Controller
return $view->render();
}
- public function getAvailableWidgets()
- {
- $this->checkTokenInUrl();
-
- Json::sendHeaderJSON();
- return json_encode(WidgetsList::get());
- }
-
public function getDashboardLayout($checkToken = true)
{
if ($checkToken) {
diff --git a/plugins/Dashboard/Dashboard.php b/plugins/Dashboard/Dashboard.php
index 9ccbb822e4..5a0a7cc0ec 100644
--- a/plugins/Dashboard/Dashboard.php
+++ b/plugins/Dashboard/Dashboard.php
@@ -11,7 +11,8 @@ namespace Piwik\Plugins\Dashboard;
use Piwik\Common;
use Piwik\Db;
use Piwik\Piwik;
-use Piwik\WidgetsList;
+use Piwik\Category\Subcategory;
+use Piwik\Widget\WidgetConfig;
/**
*/
@@ -26,10 +27,43 @@ class Dashboard extends \Piwik\Plugin
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'UsersManager.deleteUser' => 'deleteDashboardLayout',
- 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys'
+ 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
+ 'Widget.addWidgetConfigs' => 'addWidgetConfigs',
+ 'Category.addSubcategories' => 'addSubcategories'
);
}
+ public function addWidgetConfigs(&$widgets)
+ {
+ $dashboards = API::getInstance()->getDashboards();
+
+ foreach ($dashboards as $dashboard) {
+ $config = new WidgetConfig();
+ $config->setIsNotWidgetizable();
+ $config->setModule('Dashboard');
+ $config->setAction('embeddedIndex');
+ $config->setCategoryId('Dashboard_Dashboard');
+ $config->setSubcategoryId($dashboard['id']);
+ $config->setParameters(array('idDashboard' => $dashboard['id']));
+ $widgets[] = $config;
+ }
+ }
+
+ public function addSubcategories(&$subcategories)
+ {
+ $dashboards = API::getInstance()->getDashboards();
+
+ $order = 0;
+ foreach ($dashboards as $dashboard) {
+ $subcategory = new Subcategory();
+ $subcategory->setName($dashboard['name']);
+ $subcategory->setCategoryId('Dashboard_Dashboard');
+ $subcategory->setId($dashboard['id']);
+ $subcategory->setOrder($order++);
+ $subcategories[] = $subcategory;
+ }
+ }
+
/**
* Returns the layout in the DB for the given user, or false if the layout has not been set yet.
* Parameters must be checked BEFORE this function call
@@ -70,14 +104,14 @@ class Dashboard extends \Piwik\Plugin
$defaultLayout = '[
[
- {"uniqueId":"widgetVisitsSummarygetEvolutionGraphcolumnsArray","parameters":{"module":"VisitsSummary","action":"getEvolutionGraph","columns":"nb_visits"}},
+ {"uniqueId":"widgetVisitsSummarygetEvolutionGraphforceView1viewDataTablegraphEvolution","parameters":{"forceView":"1","viewDataTable":"graphEvolution","module":"VisitsSummary","action":"getEvolutionGraph"}},
{"uniqueId":"widgetLivewidget","parameters":{"module":"Live","action":"widget"}},
- {"uniqueId":"widgetVisitorInterestgetNumberOfVisitsPerVisitDuration","parameters":{"module":"VisitorInterest","action":"getNumberOfVisitsPerVisitDuration"}}
+ {"uniqueId":"widgetVisitorInterestgetNumberOfVisitsPerVisitDurationviewDataTablecloud","parameters":{"viewDataTable":"cloud","module":"VisitorInterest","action":"getNumberOfVisitsPerVisitDuration"}}
],
[
' . $topWidget . '
{"uniqueId":"widgetReferrersgetWebsites","parameters":{"module":"Referrers","action":"getWebsites"}},
- {"uniqueId":"widgetVisitTimegetVisitInformationPerServerTime","parameters":{"module":"VisitTime","action":"getVisitInformationPerServerTime"}}
+ {"uniqueId":"widgetVisitTimegetVisitInformationPerServerTimeviewDataTablegraphVerticalBar","parameters":{"viewDataTable": "graphVerticalBar","module":"VisitTime","action":"getVisitInformationPerServerTime"}}
],
[
{"uniqueId":"widgetUserCountryMapvisitorMap","parameters":{"module":"UserCountryMap","action":"visitorMap"}},
@@ -160,24 +194,6 @@ class Dashboard extends \Piwik\Plugin
);
}
- foreach ($layoutObject->columns as &$row) {
- if (!is_array($row)) {
- $row = array();
- continue;
- }
-
- foreach ($row as $widgetId => $widget) {
- if (isset($widget->parameters->module)) {
- $controllerName = $widget->parameters->module;
- $controllerAction = $widget->parameters->action;
- if (!WidgetsList::isDefined($controllerName, $controllerAction)) {
- unset($row[$widgetId]);
- }
- } else {
- unset($row[$widgetId]);
- }
- }
- }
$layout = $this->encodeLayout($layoutObject);
return $layout;
}
@@ -202,11 +218,13 @@ class Dashboard extends \Piwik\Plugin
public function getJsFiles(&$jsFiles)
{
+ $jsFiles[] = "plugins/Dashboard/angularjs/common/services/dashboards-model.js";
$jsFiles[] = "plugins/Dashboard/javascripts/widgetMenu.js";
$jsFiles[] = "libs/javascript/json2.js";
$jsFiles[] = "plugins/Dashboard/javascripts/dashboardObject.js";
$jsFiles[] = "plugins/Dashboard/javascripts/dashboardWidget.js";
$jsFiles[] = "plugins/Dashboard/javascripts/dashboard.js";
+ $jsFiles[] = "plugins/Dashboard/angularjs/dashboard/dashboard.directive.js";
}
public function getStylesheetFiles(&$stylesheets)
diff --git a/plugins/Dashboard/Menu.php b/plugins/Dashboard/Menu.php
index f90c117e10..7967635f97 100644
--- a/plugins/Dashboard/Menu.php
+++ b/plugins/Dashboard/Menu.php
@@ -9,7 +9,6 @@
namespace Piwik\Plugins\Dashboard;
use Piwik\Db;
-use Piwik\Menu\MenuReporting;
use Piwik\Menu\MenuTop;
use Piwik\Piwik;
use Piwik\Plugins\UsersManager\UserPreferences;
@@ -19,24 +18,6 @@ use Piwik\Site;
*/
class Menu extends \Piwik\Plugin\Menu
{
- public function configureReportingMenu(MenuReporting $menu)
- {
- $menu->addItem('Dashboard_Dashboard', '', $this->urlForAction('embeddedIndex', array('idDashboard' => 1)), 5);
-
- if (!Piwik::isUserIsAnonymous()) {
- $login = Piwik::getCurrentUserLogin();
-
- $dashboard = new Dashboard();
- $dashboards = $dashboard->getAllDashboards($login);
-
- $pos = 0;
- foreach ($dashboards as $dashboard) {
- $menu->addItem('Dashboard_Dashboard', $dashboard['name'], $this->urlForAction('embeddedIndex', array('idDashboard' => $dashboard['iddashboard'])), $pos);
- $pos++;
- }
- }
- }
-
public function configureTopMenu(MenuTop $menu)
{
$userPreferences = new UserPreferences();
diff --git a/plugins/Dashboard/Model.php b/plugins/Dashboard/Model.php
index 790076c1a9..0ef02aeea9 100644
--- a/plugins/Dashboard/Model.php
+++ b/plugins/Dashboard/Model.php
@@ -10,7 +10,7 @@ namespace Piwik\Plugins\Dashboard;
use Piwik\Common;
use Piwik\Db;
use Piwik\DbHelper;
-use Piwik\WidgetsList;
+use Piwik\Widget\WidgetsList;
class Model
{
@@ -189,7 +189,11 @@ class Model
if ($widget->uniqueId == $oldWidgetId) {
- $newWidgetId = WidgetsList::getWidgetUniqueId($newWidget['module'], $newWidget['action'], $newWidget['params']);
+ if (!empty($newWidget['uniqueId'])) {
+ $newWidgetId = $newWidget['uniqueId'];
+ } else {
+ $newWidgetId = WidgetsList::getWidgetUniqueId($newWidget['module'], $newWidget['action'], $newWidget['params']);
+ }
// is new widget already is on dashboard just remove the old one
if (self::layoutContainsWidget($dashboardLayout, $newWidgetId)) {
diff --git a/plugins/Dashboard/angularjs/common/services/dashboards-model.js b/plugins/Dashboard/angularjs/common/services/dashboards-model.js
new file mode 100644
index 0000000000..e0d861c2ba
--- /dev/null
+++ b/plugins/Dashboard/angularjs/common/services/dashboards-model.js
@@ -0,0 +1,68 @@
+/*!
+ * 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.service').factory('dashboardsModel', dashboardsModel);
+
+ dashboardsModel.$inject = ['piwikApi'];
+
+ function dashboardsModel (piwikApi) {
+
+ var dashboardsPromise = null;
+
+ var model = {
+ dashboards: [],
+ getAllDashboards: getAllDashboards,
+ reloadAllDashboards: reloadAllDashboards,
+ getDashboard: getDashboard,
+ getDashboardLayout: getDashboardLayout
+ };
+
+ return model;
+
+ function getDashboard(dashboardId)
+ {
+ return getAllDashboards().then(function (dashboards) {
+ var dashboard = null;
+ angular.forEach(dashboards, function (board) {
+ if (parseInt(board.id, 10) === parseInt(dashboardId, 10)) {
+ dashboard = board;
+ }
+ });
+ return dashboard;
+ });
+ }
+
+ function getDashboardLayout(dashboardId)
+ {
+ return piwikApi.fetch({module: 'Dashboard', action: 'getDashboardLayout', idDashboard: dashboardId});
+ }
+
+ function reloadAllDashboards()
+ {
+ if (dashboardsPromise) {
+ dashboardsPromise = null;
+ }
+
+ return getAllDashboards();
+ }
+
+ function getAllDashboards()
+ {
+ if (!dashboardsPromise) {
+ dashboardsPromise = piwikApi.fetch({method: 'Dashboard.getDashboards'}).then(function (response) {
+ if (response) {
+ model.dashboards = response;
+ }
+
+ return response;
+ });
+ }
+
+ return dashboardsPromise;
+ }
+ }
+})();
\ No newline at end of file
diff --git a/plugins/Dashboard/angularjs/dashboard/dashboard.directive.js b/plugins/Dashboard/angularjs/dashboard/dashboard.directive.js
new file mode 100644
index 0000000000..578737940c
--- /dev/null
+++ b/plugins/Dashboard/angularjs/dashboard/dashboard.directive.js
@@ -0,0 +1,103 @@
+/*!
+ * 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').directive('piwikDashboard', piwikDashboard);
+
+ piwikDashboard.$inject = ['dashboardsModel', '$rootScope', '$q'];
+
+ function piwikDashboard(dashboardsModel, $rootScope, $q) {
+
+ function renderDashboard(dashboardId, dashboard, layout)
+ {
+ $('.dashboardSettings').show();
+ initTopControls();
+
+ // Embed dashboard
+ if (!$('#topBars').length) {
+ $('.dashboardSettings').after($('#Dashboard'));
+ $('#Dashboard_embeddedIndex_' + dashboardId).addClass('sfHover');
+ }
+
+ widgetsHelper.getAvailableWidgets();
+
+ $('#dashboardWidgetsArea')
+ .on('dashboardempty', showEmptyDashboardNotification)
+ .dashboard({
+ idDashboard: dashboardId,
+ layout: layout,
+ name: dashboard ? dashboard.name : ''
+ });
+
+ $('#columnPreview').find('>div').each(function () {
+ var width = [];
+ $('div', this).each(function () {
+ width.push(this.className.replace(/width-/, ''));
+ });
+ $(this).attr('layout', width.join('-'));
+ });
+
+ $('#columnPreview').find('>div').on('click', function () {
+ $('#columnPreview').find('>div').removeClass('choosen');
+ $(this).addClass('choosen');
+ });
+ }
+
+ function fetchDashboard(dashboardId) {
+ $('#dashboardWidgetsArea').innerHTML ='';
+
+ var getDashboard = dashboardsModel.getDashboard(dashboardId);
+ var getLayout = dashboardsModel.getDashboardLayout(dashboardId);
+
+ $q.all([getDashboard, getLayout]).then(function (response) {
+ var dashboard = response[0];
+ var layout = response[1];
+
+ $(function() {
+ renderDashboard(dashboardId, dashboard, layout);
+ });
+ });
+ }
+
+ function clearDashboard () {
+ $('.top_controls .dashboard-manager').hide();
+ $('#dashboardWidgetsArea').dashboard('destroy');
+ }
+
+ return {
+ restrict: 'A',
+ scope: {
+ dashboardid: '=',
+ layout: '='
+ },
+ link: function (scope, element, attrs) {
+
+ scope.$parent.fetchDashboard = function (dashboardId) {
+ scope.dashboardId = dashboardId;
+ fetchDashboard(dashboardId)
+ };
+
+ fetchDashboard(scope.dashboardid);
+
+ function onLocationChange(event, url1, url2)
+ {
+ if (url1 !== url2) {
+ clearDashboard();
+ }
+ }
+
+ // should be rather handled in route or so.
+ var unbind = $rootScope.$on('$locationChangeSuccess', onLocationChange);
+ scope.$on('$destroy', onLocationChange);
+ scope.$on('$destroy', unbind);
+ }
+ };
+ }
+})();
\ No newline at end of file
diff --git a/plugins/Dashboard/javascripts/dashboard.js b/plugins/Dashboard/javascripts/dashboard.js
index 238aca5197..f78814cdbb 100644
--- a/plugins/Dashboard/javascripts/dashboard.js
+++ b/plugins/Dashboard/javascripts/dashboard.js
@@ -5,45 +5,12 @@
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
-function initDashboard(dashboardId, dashboardLayout) {
-
- $('.dashboardSettings').show();
- initTopControls();
-
- // Embed dashboard
- if (!$('#topBars').length) {
- $('.dashboardSettings').after($('#Dashboard'));
- $('#Dashboard_embeddedIndex_' + dashboardId).addClass('sfHover');
- }
-
- widgetsHelper.getAvailableWidgets();
-
- $('#dashboardWidgetsArea')
- .on('dashboardempty', showEmptyDashboardNotification)
- .dashboard({
- idDashboard: dashboardId,
- layout: dashboardLayout
- });
-
- $('#columnPreview').find('>div').each(function () {
- var width = [];
- $('div', this).each(function () {
- width.push(this.className.replace(/width-/, ''));
- });
- $(this).attr('layout', width.join('-'));
- });
-
- $('#columnPreview').find('>div').on('click', function () {
- $('#columnPreview').find('>div').removeClass('choosen');
- $(this).addClass('choosen');
- });
-}
-
function createDashboard() {
- $('#createDashboardName').val('');
- piwikHelper.modalConfirm('#createDashboardConfirm', {yes: function () {
- var dashboardName = $('#createDashboardName').val();
- var type = ($('#dashboard_type_empty:checked').length > 0) ? 'empty' : 'default';
+ $(makeSelectorLastId('createDashboardName')).val('');
+
+ piwikHelper.modalConfirm(makeSelectorLastId('createDashboardConfirm'), {yes: function () {
+ var dashboardName = $(makeSelectorLastId('createDashboardName')).val();
+ var type = ($('[id=dashboard_type_empty]:last:checked').length > 0) ? 'empty' : 'default';
var ajaxRequest = new ajaxHelper();
ajaxRequest.setLoadingElement();
@@ -57,50 +24,74 @@ function createDashboard() {
}, 'post');
ajaxRequest.setCallback(
function (id) {
- $('#dashboardWidgetsArea').dashboard('loadDashboard', id);
+ angular.element(document).injector().invoke(function ($location, reportingMenuModel, dashboardsModel) {
+ dashboardsModel.reloadAllDashboards().then(function () {
+ $('#dashboardWidgetsArea').dashboard('loadDashboard', id);
+ $('#dashboardWidgetsArea').dashboard('rebuildMenu');
+ });
+ });
}
);
ajaxRequest.send(true);
}});
}
+function makeSelectorLastId(domElementId)
+{
+ // there can be many elements with this id, we prefer the last one
+ return '[id=' + domElementId + ']:last';
+}
+
function resetDashboard() {
- piwikHelper.modalConfirm('#resetDashboardConfirm', {yes: function () { $('#dashboardWidgetsArea').dashboard('resetLayout'); }});
+ piwikHelper.modalConfirm(makeSelectorLastId('resetDashboardConfirm'), {yes:
+ function () { $('#dashboardWidgetsArea').dashboard('resetLayout');
+ }});
}
function renameDashboard() {
- $('#newDashboardName').val($('#dashboardWidgetsArea').dashboard('getDashboardName'));
- piwikHelper.modalConfirm('#renameDashboardConfirm', {yes: function () { $('#dashboardWidgetsArea').dashboard('setDashboardName', $('#newDashboardName').val()); }});
+ $(makeSelectorLastId('newDashboardName')).val($('#dashboardWidgetsArea').dashboard('getDashboardName'));
+
+ piwikHelper.modalConfirm(makeSelectorLastId('renameDashboardConfirm'), {yes: function () {
+ var newDashboardName = $(makeSelectorLastId('newDashboardName')).val();
+ $('#dashboardWidgetsArea').dashboard('setDashboardName', newDashboardName);
+ }});
}
function removeDashboard() {
- $('#removeDashboardConfirm').find('h2 span').text($('#dashboardWidgetsArea').dashboard('getDashboardName'));
- piwikHelper.modalConfirm('#removeDashboardConfirm', {yes: function () { $('#dashboardWidgetsArea').dashboard('removeDashboard'); }});
+ $(makeSelectorLastId('removeDashboardConfirm')).find('h2 span').text($('#dashboardWidgetsArea').dashboard('getDashboardName'));
+
+ piwikHelper.modalConfirm(makeSelectorLastId('removeDashboardConfirm'), {yes: function () {
+ $('#dashboardWidgetsArea').dashboard('removeDashboard');
+ }});
}
function showChangeDashboardLayoutDialog() {
$('#columnPreview').find('>div').removeClass('choosen');
$('#columnPreview').find('>div[layout=' + $('#dashboardWidgetsArea').dashboard('getColumnLayout') + ']').addClass('choosen');
- piwikHelper.modalConfirm('#changeDashboardLayout', {yes: function () {
- $('#dashboardWidgetsArea').dashboard('setColumnLayout', $('#changeDashboardLayout').find('.choosen').attr('layout'));
+
+ var id = makeSelectorLastId('changeDashboardLayout');
+ piwikHelper.modalConfirm(id, {yes: function () {
+ $('#dashboardWidgetsArea').dashboard('setColumnLayout', $(id).find('.choosen').attr('layout'));
}});
}
function showEmptyDashboardNotification() {
- piwikHelper.modalConfirm('#dashboardEmptyNotification', {
+ piwikHelper.modalConfirm(makeSelectorLastId('dashboardEmptyNotification'), {
resetDashboard: function () { $('#dashboardWidgetsArea').dashboard('resetLayout'); },
addWidget: function () { $('.dashboardSettings').trigger('click'); }
});
}
function setAsDefaultWidgets() {
- piwikHelper.modalConfirm('#setAsDefaultWidgetsConfirm', {
- yes: function () { $('#dashboardWidgetsArea').dashboard('saveLayoutAsDefaultWidgetLayout'); }
+ piwikHelper.modalConfirm(makeSelectorLastId('setAsDefaultWidgetsConfirm'), {
+ yes: function () {
+ $('#dashboardWidgetsArea').dashboard('saveLayoutAsDefaultWidgetLayout');
+ }
});
}
function copyDashboardToUser() {
- $('#copyDashboardName').val($('#dashboardWidgetsArea').dashboard('getDashboardName'));
+ $(makeSelectorLastId('copyDashboardName')).val($('#dashboardWidgetsArea').dashboard('getDashboardName'));
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
module: 'API',
@@ -109,13 +100,13 @@ function copyDashboardToUser() {
}, 'get');
ajaxRequest.setCallback(
function (availableUsers) {
- $('#copyDashboardUser').empty();
- $('#copyDashboardUser').append(
+ $(makeSelectorLastId('copyDashboardUser')).empty();
+ $(makeSelectorLastId('copyDashboardUser')).append(
$(' ').val(piwik.userLogin).text(piwik.userLogin)
);
$.each(availableUsers, function (index, user) {
if (user.login != 'anonymous' && user.login != piwik.userLogin) {
- $('#copyDashboardUser').append(
+ $(makeSelectorLastId('copyDashboardUser')).append(
$(' ').val(user.login).text(user.login + ' (' + user.alias + ')')
);
}
@@ -124,10 +115,10 @@ function copyDashboardToUser() {
);
ajaxRequest.send(true);
- piwikHelper.modalConfirm('#copyDashboardToUserConfirm', {
+ piwikHelper.modalConfirm(makeSelectorLastId('copyDashboardToUserConfirm'), {
yes: function () {
- var copyDashboardName = $('#copyDashboardName').val();
- var copyDashboardUser = $('#copyDashboardUser').val();
+ var copyDashboardName = $(makeSelectorLastId('copyDashboardName')).val();
+ var copyDashboardUser = $(makeSelectorLastId('copyDashboardUser')).val();
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
@@ -291,7 +282,7 @@ function copyDashboardToUser() {
},
isWidgetAvailable: function (widgetUniqueId) {
- return !$('#dashboardWidgetsArea').find('[widgetId=' + widgetUniqueId + ']').length;
+ return !$('#dashboardWidgetsArea').find('[widgetId="' + widgetUniqueId + '"]').length;
},
widgetSelected: function (widget) {
@@ -301,6 +292,7 @@ function copyDashboardToUser() {
DashboardManagerControl.initElements = function () {
UIControl.initElements(this, '.dashboard-manager');
+ $('.top_controls .dashboard-manager').hide(); // initially hide the manager
};
exports.DashboardManagerControl = DashboardManagerControl;
diff --git a/plugins/Dashboard/javascripts/dashboardObject.js b/plugins/Dashboard/javascripts/dashboardObject.js
index 7808d22c11..08bb36dd79 100644
--- a/plugins/Dashboard/javascripts/dashboardObject.js
+++ b/plugins/Dashboard/javascripts/dashboardObject.js
@@ -57,9 +57,6 @@
if (options.layout) {
generateLayout(options.layout);
- buildMenu();
- } else {
- methods.loadDashboard.apply(this, [dashboardId]);
}
return this;
@@ -89,11 +86,11 @@
dashboardName = '';
dashboardLayout = null;
dashboardId = dashboardIdToLoad;
- piwikHelper.showAjaxLoading();
- broadcast.updateHashOnly = true;
- broadcast.propagateAjax('?idDashboard=' + dashboardIdToLoad);
- fetchLayout(generateLayout);
- buildMenu();
+
+ var element = $('[piwik-dashboard]');
+ var scope = angular.element(element).scope();
+ scope.fetchDashboard(dashboardIdToLoad);
+
return this;
},
@@ -179,6 +176,8 @@
ajaxRequest.send(true);
},
+ rebuildMenu: rebuildMenu,
+
/**
* Removes the current dashboard
*/
@@ -201,6 +200,39 @@
}
};
+ function removeNonExistingWidgets(availableWidgets, layout)
+ {
+ var existingModuleAction = {};
+ $.each(availableWidgets, function (category, widgets) {
+ $.each(widgets, function (index, widget) {
+ existingModuleAction[widget.module + '.' + widget.action] = true;
+ });
+ });
+
+ var columns = [];
+ $.each(layout.columns, function (i, column) {
+ var widgets = [];
+
+ $.each(column, function (j, widget) {
+ if (!widget.parameters || !widget.parameters.module) {
+ return;
+ }
+
+ var method = widget.parameters.module + '.' + widget.parameters.action
+ if (existingModuleAction[method]) {
+ widgets.push(widget);
+ }
+
+ });
+
+ columns[i] = widgets;
+ });
+
+ layout.columns = columns;
+
+ return layout;
+ }
+
/**
* Generates the dashboard out of the given layout
*
@@ -209,46 +241,33 @@
function generateLayout(layout) {
dashboardLayout = parseLayout(layout);
- piwikHelper.hideAjaxLoading();
- adjustDashboardColumns(dashboardLayout.config.layout);
-
- var dashboardContainsWidgets = false;
- for (var column = 0; column < dashboardLayout.columns.length; column++) {
- for (var i in dashboardLayout.columns[column]) {
- if (typeof dashboardLayout.columns[column][i] != 'object') {
- // Fix IE8 bug: the "i in" loop contains i="indexOf", which would yield type function.
- // If we would continue with i="indexOf", an invalid widget would be created.
- continue;
+
+ widgetsHelper.getAvailableWidgets(function (availableWidgets) {
+ dashboardLayout = removeNonExistingWidgets(availableWidgets, dashboardLayout);
+
+ piwikHelper.hideAjaxLoading();
+ adjustDashboardColumns(dashboardLayout.config.layout);
+
+ var dashboardContainsWidgets = false;
+ for (var column = 0; column < dashboardLayout.columns.length; column++) {
+ for (var i in dashboardLayout.columns[column]) {
+ if (typeof dashboardLayout.columns[column][i] != 'object') {
+ // Fix IE8 bug: the "i in" loop contains i="indexOf", which would yield type function.
+ // If we would continue with i="indexOf", an invalid widget would be created.
+ continue;
+ }
+ var widget = dashboardLayout.columns[column][i];
+ dashboardContainsWidgets = true;
+ addWidgetTemplate(widget.uniqueId, column + 1, widget.parameters, false, widget.isHidden)
}
- var widget = dashboardLayout.columns[column][i];
- dashboardContainsWidgets = true;
- addWidgetTemplate(widget.uniqueId, column + 1, widget.parameters, false, widget.isHidden)
}
- }
-
- if (!dashboardContainsWidgets) {
- $(dashboardElement).trigger('dashboardempty');
- }
- makeWidgetsSortable();
- }
+ if (!dashboardContainsWidgets) {
+ $(dashboardElement).trigger('dashboardempty');
+ }
- /**
- * Fetches the layout for the currently set dashboard id
- * and passes the response to given callback function
- *
- * @param {function} callback
- */
- function fetchLayout(callback) {
- globalAjaxQueue.abort();
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.addParams({
- module: 'Dashboard',
- action: 'getDashboardLayout',
- idDashboard: dashboardId
- }, 'get');
- ajaxRequest.setCallback(callback);
- ajaxRequest.send(false);
+ makeWidgetsSortable();
+ });
}
/**
@@ -388,7 +407,7 @@
* @param {String} uniqueId
*/
function reloadWidget(uniqueId) {
- $('[widgetId=' + uniqueId + ']', dashboardElement).dashboardWidget('reload', false, true);
+ $('[widgetId="' + uniqueId + '"]', dashboardElement).dashboardWidget('reload', false, true);
}
/**
@@ -417,7 +436,7 @@
$('.col:nth-child(' + columnNumber + ')', dashboardElement).append(widgetContent);
}
- $('[widgetId=' + uniqueId + ']', dashboardElement).dashboardWidget({
+ $('[widgetId="' + uniqueId + '"]', dashboardElement).dashboardWidget({
uniqueId: uniqueId,
widgetParameters: widgetParameters,
onChange: function () {
@@ -468,7 +487,19 @@
/**
* Handle clicks for menu items for choosing between available dashboards
*/
- function buildMenu() {
+ function rebuildMenu() {
+
+ if ($('[piwik-reporting-menu]').length) {
+ // dashboard in reporting page (regular Piwik UI)
+ angular.element(document).injector().invoke(function (reportingMenuModel) {
+ reportingMenuModel.reloadMenuItems();
+ });
+ return;
+ }
+
+ var _self = this;
+
+ // widgetized
var success = function (dashboards) {
var dashboardMenuList = $('#Dashboard').find('> ul');
var dashboardMenuListItems = dashboardMenuList.find('>li');
@@ -484,7 +515,7 @@
for (var i = 0; i < dashboards.length; i++) {
var $link = $(' ').attr('data-idDashboard', dashboards[i].iddashboard).text(dashboards[i].name);
var $li = $(' ').attr('id', 'Dashboard_embeddedIndex_' + dashboards[i].iddashboard)
- .addClass('dashboardMenuItem').append($link);
+ .addClass('dashboardMenuItem').append($link);
items.push($li);
if (dashboards[i].iddashboard == dashboardId) {
@@ -502,15 +533,10 @@
var idDashboard = $(this).attr('data-idDashboard');
- if (typeof piwikMenu != 'undefined') {
- piwikMenu.activateMenu('Dashboard', 'embeddedIndex');
- }
$('#Dashboard ul li').removeClass('sfHover');
- if ($(dashboardElement).length) {
- $(dashboardElement).dashboard('loadDashboard', idDashboard);
- } else {
- broadcast.propagateAjax('module=Dashboard&action=embeddedIndex&idDashboard=' + idDashboard);
- }
+
+ methods.loadDashboard.apply(_self, [idDashboard]);
+
$(this).closest('li').addClass('sfHover');
});
};
@@ -521,7 +547,7 @@
action: 'getAllDashboards'
}, 'get');
ajaxRequest.setCallback(success);
- ajaxRequest.send(false);
+ ajaxRequest.send();
}
/**
@@ -568,7 +594,7 @@
function () {
if (dashboardChanged) {
dashboardChanged = false;
- buildMenu();
+ rebuildMenu();
}
}
);
@@ -594,6 +620,7 @@
}, 'get');
ajaxRequest.setCallback(
function () {
+ rebuildMenu();
methods.loadDashboard.apply(this, [1]);
}
);
diff --git a/plugins/Dashboard/javascripts/dashboardWidget.js b/plugins/Dashboard/javascripts/dashboardWidget.js
index accd0aa2c2..969ed9db28 100755
--- a/plugins/Dashboard/javascripts/dashboardWidget.js
+++ b/plugins/Dashboard/javascripts/dashboardWidget.js
@@ -70,7 +70,7 @@
*/
destroy: function () {
if (this.isMaximised) {
- $('[widgetId=' + this.uniqueId + ']').dialog('destroy');
+ $('[widgetId="' + this.uniqueId + '"]').dialog('destroy');
}
$('*', this.element).off('.dashboardWidget'); // unbind all events
$('.widgetContent', this.element).trigger('widget:destroy');
@@ -197,7 +197,7 @@
var emptyWidgetContent = require('piwik/UI/Dashboard').WidgetFactory.make(uniqueId, title);
this.element.html(emptyWidgetContent);
- var widgetElement = $('#' + uniqueId, this.element);
+ var widgetElement = $('[id="' + uniqueId + '"]', this.element);
var self = this;
widgetElement
.on('mouseenter.dashboardWidget', function () {
@@ -312,7 +312,7 @@
}
$('body').off('.dashboardWidget');
$(this).dialog("destroy");
- $('#' + self.uniqueId + '-placeholder').replaceWith(this);
+ $('[id="' + self.uniqueId + '-placeholder"]').replaceWith(this);
$(this).removeAttr('style');
self.options.onChange();
$(this).find('div.piwik-graph').trigger('resizeGraph');
@@ -339,6 +339,9 @@
*/
detachWidget: function () {
this.element.before('
');
+ var placeholder = $('[id="' + self.uniqueId + '-placeholder"]')
+
+
$('#' + this.uniqueId + '-placeholder').height(this.element.height());
$('#' + this.uniqueId + '-placeholder').width(this.element.width() - 16);
diff --git a/plugins/Dashboard/javascripts/widgetMenu.js b/plugins/Dashboard/javascripts/widgetMenu.js
index 6d58415cb9..169768eb03 100644
--- a/plugins/Dashboard/javascripts/widgetMenu.js
+++ b/plugins/Dashboard/javascripts/widgetMenu.js
@@ -13,21 +13,82 @@ function widgetsHelper() {
*
* @return {object} object containing available widgets
*/
-widgetsHelper.getAvailableWidgets = function () {
+widgetsHelper.getAvailableWidgets = function (callback) {
+
+ function mergeCategoriesAndSubCategories(availableWidgets)
+ {
+ var categorized = {};
+
+ $.each(availableWidgets, function (index, widget) {
+ var category = widget.category.name;
+
+ if (!categorized[category]) {
+ categorized[category] = {'-': []};
+ }
+
+ var subcategory = '-';
+ if (widget.subcategory && widget.subcategory.name) {
+ subcategory = widget.subcategory.name;
+ }
+
+ if (!categorized[category][subcategory]) {
+ categorized[category][subcategory] = [];
+ }
+
+ categorized[category][subcategory].push(widget);
+ });
+
+ var moved = {};
+
+ $.each(categorized, function (category, widgets) {
+ $.each(widgets, function (subcategory, subwidgets) {
+
+ var categoryToUse = category;
+ if (subwidgets.length >= 3 && subcategory !== '-') {
+ categoryToUse = category + ' - ' + subcategory;
+ }
+
+ if (!moved[categoryToUse]) {
+ moved[categoryToUse] = [];
+ }
+
+ $.each(subwidgets, function (index, widget) {
+ moved[categoryToUse].push(widget);
+ });
+ });
+ });
+
+ return moved;
+ }
+
if (!widgetsHelper.availableWidgets) {
var ajaxRequest = new ajaxHelper();
+ ajaxRequest._mixinDefaultGetParams = function (params) {
+ return params;
+ };
ajaxRequest.addParams({
- module: 'Dashboard',
- action: 'getAvailableWidgets'
+ module: 'API',
+ method: 'API.getWidgetMetadata',
+ format: 'JSON',
+ deep: '1',
+ idSite: piwik.idSite || broadcast.getValueFromUrl('idSite')
}, 'get');
ajaxRequest.setCallback(
function (data) {
- widgetsHelper.availableWidgets = data;
+ widgetsHelper.availableWidgets = mergeCategoriesAndSubCategories(data);
+
+ if (callback) {
+ callback(widgetsHelper.availableWidgets);
+ }
}
);
ajaxRequest.send(true);
}
+ if (callback) {
+ callback(widgetsHelper.availableWidgets);
+ }
+
return widgetsHelper.availableWidgets;
};
@@ -191,6 +252,7 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid
* @return {$} category list element
*/
function createWidgetCategoryList(widgetPreview, availableWidgets) {
+
var settings = widgetPreview.settings;
if (!$('.' + settings.categorylistClass, widgetPreview).length) {
@@ -200,7 +262,6 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid
}
for (var widgetCategory in availableWidgets) {
-
$('.' + settings.categorylistClass, widgetPreview).append('' + widgetCategory + ' ');
}
@@ -257,6 +318,8 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid
widgetClass += ' ' + settings.unavailableClass;
}
+ widgetName = piwikHelper.escape(piwikHelper.htmlEntities(widgetName));
+
widgetList.append('' + widgetName + ' ');
}
@@ -278,7 +341,7 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid
clearTimeout(widgetPreview);
});
- $('li:not(.' + settings.unavailableClass + ')', widgetList).on('click', function () {
+ $('li', widgetList).on('click', function () {
if (!$('.widgetLoading', widgetPreview).length) {
settings.onSelect($(this).attr('uniqueid'));
if (settings.resetOnSelect) {
@@ -317,7 +380,7 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid
*/
function showPreview(widgetUniqueId, widgetPreview) {
// do not reload id widget already displayed
- if ($('#' + widgetUniqueId, widgetPreview).length) return;
+ if ($('[id="' + widgetUniqueId + '"]', widgetPreview).length) return;
var settings = widgetPreview.settings;
@@ -335,7 +398,8 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid
previewElement.html(emptyWidgetHtml);
var onWidgetLoadedCallback = function (response) {
- var widgetElement = $('#' + widgetUniqueId);
+ var widgetElement = $(document.getElementById(widgetUniqueId));
+ // document.getElementById needed for widgets with uniqueid like widgetOpens+Contact+Form
$('.widgetContent', widgetElement).html($(response));
$('.widgetContent', widgetElement).trigger('widget:create');
settings.onPreviewLoaded(widgetUniqueId, widgetElement);
@@ -398,19 +462,21 @@ widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWid
this.onPreviewLoaded = this.settings.onPreviewLoaded;
}
- availableWidgets = widgetsHelper.getAvailableWidgets();
+ var self = this;
+ widgetsHelper.getAvailableWidgets(function (availableWidgets) {
- var categoryList = createWidgetCategoryList(this, availableWidgets);
+ var categoryList = createWidgetCategoryList(self, availableWidgets);
- var self = this;
- $('li', categoryList).on('mouseover', function () {
- var category = $(this).text();
- var widgets = availableWidgets[category];
- $('li', categoryList).removeClass(self.settings.choosenClass);
- $(this).addClass(self.settings.choosenClass);
- showWidgetList(widgets, self);
- createPreviewElement(self); // empty preview
+ $('li', categoryList).on('mouseover', function () {
+ var category = $(this).text();
+ var widgets = availableWidgets[category];
+ $('li', categoryList).removeClass(self.settings.choosenClass);
+ $(this).addClass(self.settings.choosenClass);
+ showWidgetList(widgets, self);
+ createPreviewElement(self); // empty preview
+ });
});
+
};
}
});
diff --git a/plugins/Dashboard/templates/embeddedIndex.twig b/plugins/Dashboard/templates/embeddedIndex.twig
index af1dbeb887..a7633ca193 100644
--- a/plugins/Dashboard/templates/embeddedIndex.twig
+++ b/plugins/Dashboard/templates/embeddedIndex.twig
@@ -1,10 +1,4 @@
-
-
+
{{ 'Dashboard_DeleteWidgetConfirm'|translate }}
diff --git a/plugins/DevicePlugins/Reports/Base.php b/plugins/DevicePlugins/Reports/Base.php
index 81116f4d1d..b6f8755b13 100644
--- a/plugins/DevicePlugins/Reports/Base.php
+++ b/plugins/DevicePlugins/Reports/Base.php
@@ -15,7 +15,7 @@ abstract class Base extends \Piwik\Plugin\Report
{
protected function init()
{
- $this->category = 'General_VisitorSettings';
+ $this->categoryId = 'General_Visitors';
}
protected function getBasicDevicePluginsDisplayProperties(ViewDataTable $view)
diff --git a/plugins/DevicePlugins/Reports/GetPlugin.php b/plugins/DevicePlugins/Reports/GetPlugin.php
index 151bf3e1a6..42fef1cb89 100644
--- a/plugins/DevicePlugins/Reports/GetPlugin.php
+++ b/plugins/DevicePlugins/Reports/GetPlugin.php
@@ -23,8 +23,9 @@ class GetPlugin extends Base
$this->metrics = array('nb_visits');
$this->constantRowsCount = true;
$this->processedMetrics = array('nb_visits_percentage');
- $this->order = 4;
- $this->widgetTitle = 'DevicePlugins_WidgetPlugins';
+ $this->order = 13;
+
+ $this->subcategoryId = 'DevicesDetection_Software';
}
public function configureView(ViewDataTable $view)
diff --git a/plugins/DevicesDetection/Controller.php b/plugins/DevicesDetection/Controller.php
index 10c55a8a96..d19d0cf403 100644
--- a/plugins/DevicesDetection/Controller.php
+++ b/plugins/DevicesDetection/Controller.php
@@ -13,52 +13,11 @@ use Piwik\Common;
use Piwik\Db;
use Piwik\Piwik;
use Piwik\Plugin\ControllerAdmin;
-use Piwik\Plugin\Manager AS PluginManager;
use Piwik\Plugin\Report;
use Piwik\View;
class Controller extends \Piwik\Plugin\Controller
{
- public function index()
- {
- return $this->devices();
- }
-
- public function devices()
- {
- $view = new View('@DevicesDetection/devices');
- $view->deviceTypes = $this->renderReport('getType');
- $view->deviceBrands = $this->renderReport('getBrand');
- $view->deviceModels = $this->renderReport('getModel');
-
- $isResolutionEnabled = PluginManager::getInstance()->isPluginActivated('Resolution');
- if ($isResolutionEnabled) {
- $view->resolutions = $this->renderReport(Report::factory('Resolution', 'getResolution'));
- }
-
- return $view->render();
- }
-
- public function software()
- {
- $view = new View('@DevicesDetection/software');
- $view->osReport = $this->renderReport('getOsVersions');
- $view->browserReport = $this->renderReport('getBrowsers');
- $view->browserEngineReport = $this->renderReport('getBrowserEngines');
-
- $isResolutionEnabled = PluginManager::getInstance()->isPluginActivated('Resolution');
- if ($isResolutionEnabled) {
- $view->configurations = $this->renderReport(Report::factory('Resolution', 'getConfiguration'));
- }
-
- $isDevicePluginsEnabled = PluginManager::getInstance()->isPluginActivated('DevicePlugins');
- if ($isDevicePluginsEnabled) {
- $view->browserPlugins = $this->renderReport(Report::factory('DevicePlugins', 'getPlugin'));
- }
-
- return $view->render();
- }
-
public function detection()
{
Piwik::checkUserHasSomeAdminAccess();
diff --git a/plugins/DevicesDetection/Menu.php b/plugins/DevicesDetection/Menu.php
index 7ddb1dc668..067f8b9afa 100644
--- a/plugins/DevicesDetection/Menu.php
+++ b/plugins/DevicesDetection/Menu.php
@@ -9,7 +9,6 @@
namespace Piwik\Plugins\DevicesDetection;
use Piwik\Menu\MenuAdmin;
-use Piwik\Menu\MenuReporting;
use Piwik\Piwik;
/**
@@ -24,10 +23,4 @@ class Menu extends \Piwik\Plugin\Menu
$order = 40);
}
}
-
- public function configureReportingMenu(MenuReporting $menu)
- {
- $menu->addVisitorsItem('DevicesDetection_Devices', $this->urlForAction('devices'));
- $menu->addVisitorsItem('DevicesDetection_Software', $this->urlForAction('software'));
- }
}
diff --git a/plugins/DevicesDetection/Reports/Base.php b/plugins/DevicesDetection/Reports/Base.php
index 15eac96a4a..cb24a376e4 100644
--- a/plugins/DevicesDetection/Reports/Base.php
+++ b/plugins/DevicesDetection/Reports/Base.php
@@ -14,6 +14,6 @@ abstract class Base extends \Piwik\Plugin\Report
{
protected function init()
{
- $this->category = 'DevicesDetection_DevicesDetection';
+ $this->categoryId = 'General_Visitors';
}
}
diff --git a/plugins/DevicesDetection/Reports/GetBrand.php b/plugins/DevicesDetection/Reports/GetBrand.php
index 070b2d8918..41b9fd5366 100644
--- a/plugins/DevicesDetection/Reports/GetBrand.php
+++ b/plugins/DevicesDetection/Reports/GetBrand.php
@@ -20,8 +20,9 @@ class GetBrand extends Base
$this->dimension = new DeviceBrand();
$this->name = Piwik::translate('DevicesDetection_DeviceBrand');
$this->documentation = ''; // TODO
- $this->order = 1;
- $this->widgetTitle = 'DevicesDetection_DeviceBrand';
+ $this->order = 4;
+
+ $this->subcategoryId = 'DevicesDetection_Devices';
}
public function configureView(ViewDataTable $view)
diff --git a/plugins/DevicesDetection/Reports/GetBrowserEngines.php b/plugins/DevicesDetection/Reports/GetBrowserEngines.php
index 7c47ffca63..403955c66c 100644
--- a/plugins/DevicesDetection/Reports/GetBrowserEngines.php
+++ b/plugins/DevicesDetection/Reports/GetBrowserEngines.php
@@ -21,8 +21,9 @@ class GetBrowserEngines extends Base
$this->dimension = new BrowserEngine();
$this->name = Piwik::translate('DevicesDetection_BrowserEngines');
$this->documentation = Piwik::translate('DevicesDetection_BrowserEngineDocumentation', '
');
- $this->order = 7;
- $this->widgetTitle = 'DevicesDetection_BrowserEngines';
+ $this->order = 10;
+
+ $this->subcategoryId = 'DevicesDetection_Software';
}
public function getDefaultTypeViewDataTable()
diff --git a/plugins/DevicesDetection/Reports/GetBrowserVersions.php b/plugins/DevicesDetection/Reports/GetBrowserVersions.php
index 4f5875199a..66c7f91f55 100644
--- a/plugins/DevicesDetection/Reports/GetBrowserVersions.php
+++ b/plugins/DevicesDetection/Reports/GetBrowserVersions.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\DevicesDetection\Reports;
use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\DevicesDetection\Columns\BrowserVersion;
+use Piwik\Plugin\Reports;
class GetBrowserVersions extends Base
{
@@ -20,8 +21,9 @@ class GetBrowserVersions extends Base
$this->dimension = new BrowserVersion();
$this->name = Piwik::translate('DevicesDetection_BrowserVersion');
$this->documentation = ''; // TODO
- $this->order = 2;
- $this->widgetTitle = 'DevicesDetection_BrowserVersion';
+ $this->order = 6;
+
+ $this->subcategoryId = 'DevicesDetection_Software';
}
public function configureView(ViewDataTable $view)
@@ -34,7 +36,7 @@ class GetBrowserVersions extends Base
public function getRelatedReports()
{
return array(
- self::factory('DevicesDetection', 'getBrowsers'),
+ Reports::factory('DevicesDetection', 'getBrowsers'),
);
}
}
diff --git a/plugins/DevicesDetection/Reports/GetBrowsers.php b/plugins/DevicesDetection/Reports/GetBrowsers.php
index 7d08a51ce5..5c39a275ee 100644
--- a/plugins/DevicesDetection/Reports/GetBrowsers.php
+++ b/plugins/DevicesDetection/Reports/GetBrowsers.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\DevicesDetection\Reports;
use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\DevicesDetection\Columns\BrowserName;
+use Piwik\Plugin\Reports;
class GetBrowsers extends Base
{
@@ -18,10 +19,11 @@ class GetBrowsers extends Base
{
parent::init();
$this->dimension = new BrowserName();
- $this->name = Piwik::translate('DevicesDetection_WidgetBrowsers');
+ $this->name = Piwik::translate('DevicesDetection_Browsers');
$this->documentation = Piwik::translate('DevicesDetection_WidgetBrowsersDocumentation', '
');
- $this->order = 1;
- $this->widgetTitle = 'DevicesDetection_WidgetBrowsers';
+ $this->order = 5;
+
+ $this->subcategoryId = 'DevicesDetection_Software';
}
public function configureView(ViewDataTable $view)
@@ -35,7 +37,7 @@ class GetBrowsers extends Base
public function getRelatedReports()
{
return array(
- self::factory('DevicesDetection', 'getBrowserVersions'),
+ Reports::factory('DevicesDetection', 'getBrowserVersions'),
);
}
}
diff --git a/plugins/DevicesDetection/Reports/GetModel.php b/plugins/DevicesDetection/Reports/GetModel.php
index e2241e1ad4..154464691c 100644
--- a/plugins/DevicesDetection/Reports/GetModel.php
+++ b/plugins/DevicesDetection/Reports/GetModel.php
@@ -21,7 +21,8 @@ class GetModel extends Base
$this->name = Piwik::translate('DevicesDetection_DeviceModel');
$this->documentation = ''; // TODO
$this->order = 2;
- $this->widgetTitle = 'DevicesDetection_DeviceModel';
+
+ $this->subcategoryId = 'DevicesDetection_Devices';
}
public function configureView(ViewDataTable $view)
diff --git a/plugins/DevicesDetection/Reports/GetOsFamilies.php b/plugins/DevicesDetection/Reports/GetOsFamilies.php
index 61b393cb44..a283a83f7b 100644
--- a/plugins/DevicesDetection/Reports/GetOsFamilies.php
+++ b/plugins/DevicesDetection/Reports/GetOsFamilies.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\DevicesDetection\Reports;
use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\DevicesDetection\Columns\Os;
+use Piwik\Plugin\Reports;
class GetOsFamilies extends Base
{
@@ -20,8 +21,9 @@ class GetOsFamilies extends Base
$this->dimension = new Os();
$this->name = Piwik::translate('DevicesDetection_OperatingSystemFamilies');
$this->documentation = ''; // TODO
- $this->order = 3;
- $this->widgetTitle = 'DevicesDetection_OperatingSystemFamilies';
+ $this->order = 8;
+
+ $this->subcategoryId = 'DevicesDetection_Software';
}
public function configureView(ViewDataTable $view)
@@ -35,7 +37,7 @@ class GetOsFamilies extends Base
public function getRelatedReports()
{
return array(
- self::factory('DevicesDetection', 'getOsVersions'),
+ Reports::factory('DevicesDetection', 'getOsVersions'),
);
}
diff --git a/plugins/DevicesDetection/Reports/GetOsVersions.php b/plugins/DevicesDetection/Reports/GetOsVersions.php
index 8676f38776..b330054c6f 100644
--- a/plugins/DevicesDetection/Reports/GetOsVersions.php
+++ b/plugins/DevicesDetection/Reports/GetOsVersions.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\DevicesDetection\Reports;
use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\DevicesDetection\Columns\OsVersion;
+use Piwik\Plugin\Reports;
class GetOsVersions extends Base
{
@@ -20,8 +21,9 @@ class GetOsVersions extends Base
$this->dimension = new OsVersion();
$this->name = Piwik::translate('DevicesDetection_OperatingSystemVersions');
$this->documentation = ''; // TODO
- $this->order = 4;
- $this->widgetTitle = 'DevicesDetection_OperatingSystemVersions';
+ $this->order = 2;
+
+ $this->subcategoryId = 'DevicesDetection_Software';
}
public function configureView(ViewDataTable $view)
@@ -35,7 +37,7 @@ class GetOsVersions extends Base
public function getRelatedReports()
{
return array(
- self::factory('DevicesDetection', 'getOsFamilies'),
+ Reports::factory('DevicesDetection', 'getOsFamilies'),
);
}
}
diff --git a/plugins/DevicesDetection/Reports/GetType.php b/plugins/DevicesDetection/Reports/GetType.php
index cbfa69db3d..b12f19f319 100644
--- a/plugins/DevicesDetection/Reports/GetType.php
+++ b/plugins/DevicesDetection/Reports/GetType.php
@@ -22,7 +22,8 @@ class GetType extends Base
$this->name = Piwik::translate('DevicesDetection_DeviceType');
$this->documentation = ''; // TODO
$this->order = 0;
- $this->widgetTitle = 'DevicesDetection_DeviceType';
+
+ $this->subcategoryId = 'DevicesDetection_Devices';
}
public function configureView(ViewDataTable $view)
diff --git a/plugins/DevicesDetection/templates/devices.twig b/plugins/DevicesDetection/templates/devices.twig
deleted file mode 100644
index a37079c9fd..0000000000
--- a/plugins/DevicesDetection/templates/devices.twig
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
{{ "DevicesDetection_DeviceType"|translate }}
- {{ deviceTypes | raw}}
- {{ "DevicesDetection_DeviceBrand"|translate }}
- {{ deviceBrands | raw }}
-
-
-
-
{{ "DevicesDetection_DeviceModel"|translate }}
- {{ deviceModels | raw }}
- {% if resolutions|default is not empty %}
- {{ 'Resolution_Resolutions'|translate }}
- {{ resolutions|raw }}
- {% endif %}
-
-
-
diff --git a/plugins/DevicesDetection/templates/software.twig b/plugins/DevicesDetection/templates/software.twig
deleted file mode 100644
index 8b0dab0631..0000000000
--- a/plugins/DevicesDetection/templates/software.twig
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
-
-
{{ "DevicesDetection_OperatingSystems"|translate }}
- {{ osReport | raw}}
- {% if configurations|default is not empty %}
- {{ 'Resolution_Configurations'|translate }}
- {{ configurations|raw }}
- {% endif %}
-
-
-
-
{{ "DevicesDetection_Browsers"|translate }}
- {{ browserReport | raw }}
- {{ "DevicesDetection_BrowserEngines"|translate }}
- {{ browserEngineReport | raw }}
- {% if browserPlugins|default is not empty %}
- {{ 'General_Plugins'|translate }}
- {{ browserPlugins|raw }}
- {% endif %}
-
-
-
diff --git a/plugins/Ecommerce/Categories/EcommerceCategory.php b/plugins/Ecommerce/Categories/EcommerceCategory.php
new file mode 100644
index 0000000000..39cd5bb3ea
--- /dev/null
+++ b/plugins/Ecommerce/Categories/EcommerceCategory.php
@@ -0,0 +1,17 @@
+isPluginActivated('CustomVariables')) {
- throw new Exception("Ecommerce Tracking requires that the plugin Custom Variables is enabled. Please enable the plugin CustomVariables (or ask your admin).");
- }
+ $idGoal = Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER;
- $view = $this->getGoalReportView($idGoal = Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER);
- $view->displayFullReport = false;
- $view->headline = $this->translator->translate('General_EvolutionOverPeriod');
+ $view = new View('@Ecommerce/getSparklines');
+ $view->onlyConversionOverview = false;
+ $view->conversionsOverViewEnabled = true;
- return $view->render();
- }
+ if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
+ $goalDefinition['name'] = $this->translator->translate('Goals_Ecommerce');
+ $goalDefinition['allow_multiple'] = true;
+ } else {
+ if (!isset($this->goals[$idGoal])) {
+ Piwik::redirectToModule('Goals', 'index', array('idGoal' => null));
+ }
+ $goalDefinition = $this->goals[$idGoal];
+ }
- public function ecommerceLogReport($fetch = false)
- {
- $view = new View('@Ecommerce/ecommerceLog');
$this->setGeneralVariablesView($view);
- $view->ecommerceLog = $this->getEcommerceLog($fetch);
-
- return $view->render();
- }
+ $goal = $this->getMetricsForGoal($idGoal);
+ foreach ($goal as $name => $value) {
+ $view->$name = $value;
+ }
- public function getEcommerceLog($fetch = false)
- {
- $saveGET = $_GET;
- $_GET['segment'] = urlencode('visitEcommerceStatus!=none');
- $_GET['widget'] = 1;
- $output = FrontController::getInstance()->dispatch('Live', 'getVisitorLog', array($fetch));
- $_GET = $saveGET;
+ if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
+ $goal = $this->getMetricsForGoal(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART);
+ foreach ($goal as $name => $value) {
+ $name = 'cart_' . $name;
+ $view->$name = $value;
+ }
+ }
- return $output;
- }
+ $view->idGoal = $idGoal;
+ $view->goalAllowMultipleConversionsPerVisit = $goalDefinition['allow_multiple'];
- public function index()
- {
- return $this->ecommerceReport();
+ return $view->render();
}
- public function products()
+ public function getConversionsOverview()
{
- $goal = $this->getMetricsForGoal(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER);
- $conversions = 0;
- if (!empty($goal['nb_conversions'])) {
- $conversions = $goal['nb_conversions'];
- }
-
- $goal = $this->getMetricsForGoal(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART);
-
- $cartNbConversions = 0;
- if (!empty($goal) && array_key_exists('nb_conversions', $goal)) {
- $cartNbConversions = $goal['nb_conversions'];
- }
+ $view = new View('@Ecommerce/conversionOverview');
+ $idGoal = Common::getRequestVar('idGoal', null, 'string');
- $preloadAbandonedCart = $cartNbConversions !== false && $conversions == 0;
+ $goalMetrics = Request::processRequest('Goals.get', array('idGoal' => $idGoal));
+ $dataRow = $goalMetrics->getFirstRow();
- $goalReportsByDimension = new View\ReportsByDimension('Goals');
+ $view->idSite = Common::getRequestVar('idSite', null, 'int');
- $ecommerceCustomParams = array();
- if ($preloadAbandonedCart) {
- $ecommerceCustomParams['abandonedCarts'] = '1';
- } else {
- $ecommerceCustomParams['abandonedCarts'] = '0';
+ if ($dataRow) {
+ $view->revenue = $dataRow->getColumn('revenue');
+ $view->revenue_subtotal = $dataRow->getColumn('revenue_subtotal');
+ $view->revenue_tax = $dataRow->getColumn('revenue_tax');
+ $view->revenue_shipping = $dataRow->getColumn('revenue_shipping');
+ $view->revenue_discount = $dataRow->getColumn('revenue_discount');
}
- $goalReportsByDimension->addReport(
- 'Goals_Products', 'Goals_ProductSKU', 'Goals.getItemsSku', $ecommerceCustomParams);
- $goalReportsByDimension->addReport(
- 'Goals_Products', 'Goals_ProductName', 'Goals.getItemsName', $ecommerceCustomParams);
- $goalReportsByDimension->addReport(
- 'Goals_Products', 'Goals_ProductCategory', 'Goals.getItemsCategory', $ecommerceCustomParams);
-
- $view = new View('@Ecommerce/products');
- $this->setGeneralVariablesView($view);
-
- $view->productsByDimension = $goalReportsByDimension->render();
return $view->render();
}
- public function sales()
+ public function getEcommerceLog($fetch = false)
{
- $viewOverview = $this->getGoalReportView(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER);
- $reportsByDimension = $viewOverview->goalReportsByDimension;
-
- $view = new View('@Ecommerce/sales');
- $this->setGeneralVariablesView($view);
+ $saveGET = $_GET;
+ $_GET['segment'] = urlencode('visitEcommerceStatus!=none');
+ $_GET['widget'] = 1;
+ $output = FrontController::getInstance()->dispatch('Live', 'getVisitorLog', array($fetch));
+ $_GET = $saveGET;
- $view->goalReportsByDimension = $reportsByDimension;
- return $view->render();
+ return $output;
}
}
diff --git a/plugins/Ecommerce/Menu.php b/plugins/Ecommerce/Menu.php
deleted file mode 100644
index 331715be4f..0000000000
--- a/plugins/Ecommerce/Menu.php
+++ /dev/null
@@ -1,41 +0,0 @@
-isEcommerceEnabled()) {
- $ecommerceParams = array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER);
- $ecommerceUrl = $this->urlForAction('ecommerceReport', $ecommerceParams);
-
- $menu->addItem('Goals_Ecommerce', '', $ecommerceUrl, 24);
- $menu->addItem('Goals_Ecommerce', 'General_Overview', $ecommerceUrl, 1);
- $menu->addItem('Goals_Ecommerce', 'Goals_EcommerceLog', $this->urlForAction('ecommerceLogReport'), 2);
- $menu->addItem('Goals_Ecommerce', 'Goals_Products', $this->urlForAction('products', $ecommerceParams), 3);
- $menu->addItem('Goals_Ecommerce', 'Ecommerce_Sales', $this->urlForAction('sales', $ecommerceParams), 4);
- }
-
- }
-}
diff --git a/plugins/Ecommerce/Reports/Base.php b/plugins/Ecommerce/Reports/Base.php
index 08a766792a..28a3a4a36f 100644
--- a/plugins/Ecommerce/Reports/Base.php
+++ b/plugins/Ecommerce/Reports/Base.php
@@ -8,20 +8,17 @@
*/
namespace Piwik\Plugins\Ecommerce\Reports;
-use Piwik\API\Proxy;
use Piwik\Common;
use Piwik\Piwik;
use Piwik\Plugin\Report;
use Piwik\Site;
-use Piwik\ViewDataTable\Factory as ViewDataTableFactory;
-use Piwik\WidgetsList;
abstract class Base extends Report
{
protected function init()
{
$this->module = 'Goals';
- $this->category = 'Goals_Ecommerce';
+ $this->categoryId = 'Goals_Ecommerce';
}
public function isEnabled()
diff --git a/plugins/Ecommerce/Reports/BaseItem.php b/plugins/Ecommerce/Reports/BaseItem.php
index 5caf770a6b..5896710dc2 100644
--- a/plugins/Ecommerce/Reports/BaseItem.php
+++ b/plugins/Ecommerce/Reports/BaseItem.php
@@ -9,15 +9,18 @@
namespace Piwik\Plugins\Ecommerce\Reports;
use Piwik\Common;
+use Piwik\DataTable;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
-use Piwik\Plugin\Report;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution;
-use Piwik\Plugins\Goals\Goals;
use Piwik\Plugins\Goals\Columns\Metrics\AveragePrice;
use Piwik\Plugins\Goals\Columns\Metrics\AverageQuantity;
use Piwik\Plugins\Goals\Columns\Metrics\ProductConversionRate;
+use Piwik\Plugins\Goals\Conversions;
+use Piwik\Plugins\Goals\Model;
+use Piwik\Report\ReportWidgetFactory;
+use Piwik\Widget\WidgetsList;
abstract class BaseItem extends Base
{
@@ -46,7 +49,8 @@ abstract class BaseItem extends Base
public function getMetricsDocumentation()
{
- if ($this->isAbandonedCart()) {
+ // we do not check whether it is abondon carts if not set re performance improvements
+ if ($this->isAbandonedCart($fetchIfNotSet = false)) {
return array(
'revenue' => Piwik::translate('Goals_ColumnRevenueDocumentation',
Piwik::translate('Goals_DocumentationRevenueGeneratedByProductSales')),
@@ -62,6 +66,11 @@ abstract class BaseItem extends Base
return array();
}
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
+ {
+ $widgetsList->addToContainerWidget('Products', $factory->createWidget());
+ }
+
public function configureView(ViewDataTable $view)
{
$idSite = Common::getRequestVar('idSite');
@@ -100,7 +109,7 @@ abstract class BaseItem extends Base
$view->config->custom_parameters['viewDataTable'] = 'table';
$abandonedCart = true;
} else {
- $abandonedCart = $this->isAbandonedCart();
+ $abandonedCart = $this->isAbandonedCart($fetchIfNotSet = true);
}
if ($abandonedCart) {
@@ -127,8 +136,32 @@ abstract class BaseItem extends Base
$view->config->columns_to_display = $columnsOrdered;
}
- private function isAbandonedCart()
+ private function isAbandonedCart($fetchIfNotSet)
{
- return Common::getRequestVar('abandonedCarts', '0', 'string') == 1;
+ $abandonedCarts = Common::getRequestVar('abandonedCarts', '', 'string');
+
+ if ($abandonedCarts === '') {
+ if ($fetchIfNotSet) {
+
+ $idSite = Common::getRequestVar('idSite', 0, 'int');
+ $period = Common::getRequestVar('period', '', 'string');
+ $date = Common::getRequestVar('date', '', 'string');
+
+ $conversion = new Conversions();
+ $conversions = $conversion->getConversionForGoal(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER, $idSite, $period, $date);
+ $cartNbConversions = $conversion->getConversionForGoal(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART, $idSite, $period, $date);
+ $preloadAbandonedCart = $cartNbConversions !== false && $conversions == 0;
+
+ if ($preloadAbandonedCart) {
+ $abandonedCarts = '1';
+ } else {
+ $abandonedCarts = '0';
+ }
+ } else {
+ $abandonedCarts = '0';
+ }
+ }
+
+ return $abandonedCarts == '1';
}
}
diff --git a/plugins/Ecommerce/Reports/GetItemsCategory.php b/plugins/Ecommerce/Reports/GetItemsCategory.php
index bf652ca10d..efd4a9489a 100644
--- a/plugins/Ecommerce/Reports/GetItemsCategory.php
+++ b/plugins/Ecommerce/Reports/GetItemsCategory.php
@@ -20,6 +20,7 @@ class GetItemsCategory extends BaseItem
$this->name = Piwik::translate('Goals_ProductCategory');
$this->dimension = new ProductCategory();
$this->order = 32;
- $this->widgetTitle = 'Goals_ProductCategory';
+
+ $this->subcategoryId = 'Goals_Products';
}
}
diff --git a/plugins/Ecommerce/Reports/GetItemsName.php b/plugins/Ecommerce/Reports/GetItemsName.php
index a320b760e4..db8c7f5d3d 100644
--- a/plugins/Ecommerce/Reports/GetItemsName.php
+++ b/plugins/Ecommerce/Reports/GetItemsName.php
@@ -20,6 +20,7 @@ class GetItemsName extends BaseItem
$this->name = Piwik::translate('Goals_ProductName');
$this->dimension = new ProductName();
$this->order = 31;
- $this->widgetTitle = 'Goals_ProductName';
+
+ $this->subcategoryId = 'Goals_Products';
}
}
diff --git a/plugins/Ecommerce/Reports/GetItemsSku.php b/plugins/Ecommerce/Reports/GetItemsSku.php
index a2b20eb3f9..9056b2ff6c 100644
--- a/plugins/Ecommerce/Reports/GetItemsSku.php
+++ b/plugins/Ecommerce/Reports/GetItemsSku.php
@@ -21,7 +21,8 @@ class GetItemsSku extends BaseItem
$this->name = Piwik::translate('Goals_ProductSKU');
$this->dimension = new ProductSku();
$this->order = 30;
- $this->widgetTitle = 'Goals_ProductSKU';
+
+ $this->subcategoryId = 'Goals_Products';
}
}
diff --git a/plugins/Ecommerce/Widgets.php b/plugins/Ecommerce/Widgets.php
deleted file mode 100644
index e20a73850e..0000000000
--- a/plugins/Ecommerce/Widgets.php
+++ /dev/null
@@ -1,35 +0,0 @@
-getIdSite();
-
- $site = new Site($idSite);
- if ($site->isEcommerceEnabled()) {
- $this->addWidget('General_Overview', 'widgetGoalReport', array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER));
- $this->addWidget('Goals_EcommerceLog', 'getEcommerceLog');
- }
- }
-
- private function getIdSite()
- {
- return Common::getRequestVar('idSite', null, 'int');
- }
-
-}
diff --git a/plugins/Ecommerce/Widgets/GetEcommerceLog.php b/plugins/Ecommerce/Widgets/GetEcommerceLog.php
new file mode 100644
index 0000000000..d1a68a435a
--- /dev/null
+++ b/plugins/Ecommerce/Widgets/GetEcommerceLog.php
@@ -0,0 +1,28 @@
+setCategoryId('Goals_Ecommerce');
+ $config->setSubcategoryId('Goals_EcommerceLog');
+ $config->setName('Goals_EcommerceLog');
+
+ $idSite = Common::getRequestVar('idSite', null, 'int');
+ $site = new Site($idSite);
+ $config->setIsEnabled($site->isEcommerceEnabled());
+ }
+
+}
diff --git a/plugins/Ecommerce/Widgets/ProductsByDimension.php b/plugins/Ecommerce/Widgets/ProductsByDimension.php
new file mode 100644
index 0000000000..2f25e23b76
--- /dev/null
+++ b/plugins/Ecommerce/Widgets/ProductsByDimension.php
@@ -0,0 +1,34 @@
+isEcommerceEnabled();
+ }
+}
diff --git a/plugins/Ecommerce/templates/conversionOverview.twig b/plugins/Ecommerce/templates/conversionOverview.twig
new file mode 100644
index 0000000000..9b6864c03c
--- /dev/null
+++ b/plugins/Ecommerce/templates/conversionOverview.twig
@@ -0,0 +1,15 @@
+
+ {{ 'General_ColumnRevenue'|translate }}: {{ revenue|money(idSite)|raw -}}
+ {% if revenue_subtotal is not empty %},
+ {{ 'General_Subtotal'|translate }}: {{ revenue_subtotal|money(idSite)|raw -}}
+ {% endif %}
+ {%- if revenue_tax is not empty -%},
+ {{ 'General_Tax'|translate }}: {{ revenue_tax|money(idSite)|raw -}}
+ {% endif %}
+ {%- if revenue_shipping is not empty -%},
+ {{ 'General_Shipping'|translate }}: {{ revenue_shipping|money(idSite)|raw -}}
+ {% endif %}
+ {%- if revenue_discount is not empty -%},
+ {{ 'General_Discount'|translate }}: {{ revenue_discount|money(idSite)|raw -}}
+ {% endif %}
+
\ No newline at end of file
diff --git a/plugins/Ecommerce/templates/ecommerceLog.twig b/plugins/Ecommerce/templates/ecommerceLog.twig
deleted file mode 100644
index 4c40a94d99..0000000000
--- a/plugins/Ecommerce/templates/ecommerceLog.twig
+++ /dev/null
@@ -1,3 +0,0 @@
-
{{ 'Goals_EcommerceLog'|translate }}
-
-{{ ecommerceLog|raw }}
diff --git a/plugins/Ecommerce/templates/getSparklines.twig b/plugins/Ecommerce/templates/getSparklines.twig
new file mode 100644
index 0000000000..0952c081a5
--- /dev/null
+++ b/plugins/Ecommerce/templates/getSparklines.twig
@@ -0,0 +1,55 @@
+
+
{{ sparkline(urlSparklineConversions) }}
+
{{ nb_conversions }}
+ {{ 'General_EcommerceOrders'|translate }}
+
+
+ {% if goalAllowMultipleConversionsPerVisit is defined and goalAllowMultipleConversionsPerVisit %}
+ ({{ 'General_NVisits'|translate("
"~nb_visits_converted~" ")|raw }})
+ {% endif %}
+
+
+
+ {{ sparkline(urlSparklineRevenue) }}
+ {% set revenue=revenue|money(idSite) %}
+ {{ revenue|raw }} {{ 'General_TotalRevenue'|translate }}
+
+
+
{{ sparkline(urlSparklineAverageOrderValue) }}
+ {{ avg_order_revenue|money(idSite)|raw }}
+ {{ 'General_AverageOrderValue'|translate }}
+
+
+
+
{{ sparkline(urlSparklineConversionRate) }}
+ {% set ecommerceOrdersText %}{{ 'General_EcommerceOrders'|translate }}{% endset %}
+ {{ 'Goals_ConversionRate'|translate(""~conversion_rate~" "~ecommerceOrdersText)|raw }}
+
+
{{ sparkline(urlSparklinePurchasedProducts) }}
+ {{ items }} {{ 'General_PurchasedProducts'|translate }}
+
+
+
+
{{ 'General_AbandonedCarts'|translate }}
+
+
+
+ {{ sparkline(cart_urlSparklineConversions) }}
+ {% set ecommerceAbandonedCartsText %}{{ 'Goals_AbandonedCart'|translate }}{% endset %}
+ {{ cart_nb_conversions }} {{ 'General_VisitsWith'|translate(ecommerceAbandonedCartsText) }}
+
+
+
+ {{ sparkline(cart_urlSparklineRevenue) }}
+ {% set revenue %}{{ cart_revenue|money(idSite)|raw }}{% endset %}
+ {% set revenueText %}{{ 'General_ColumnRevenue'|translate }}{% endset %}
+ {{ revenue }} {{ 'Goals_LeftInCart'|translate(revenueText) }}
+
+
+
+ {{ sparkline(cart_urlSparklineConversionRate) }}
+ {{ cart_conversion_rate }}
+ {{ 'General_VisitsWith'|translate(ecommerceAbandonedCartsText) }}
+
+
+{% include "_sparklineFooter.twig" %}
diff --git a/plugins/Events/Categories/EventsSubcategory.php b/plugins/Events/Categories/EventsSubcategory.php
new file mode 100644
index 0000000000..e969029e13
--- /dev/null
+++ b/plugins/Events/Categories/EventsSubcategory.php
@@ -0,0 +1,19 @@
+leftMenuReports = $this->getLeftMenuReports();
- return $view->render();
- }
-
- private function getLeftMenuReports()
- {
- $reports = new View\ReportsByDimension('Events');
- foreach(Events::getLabelTranslations() as $apiAction => $translations) {
- // 'getCategory' is the API method, but we are loading 'indexCategory' which displays
- $count = 1;
- $controllerAction = str_replace("get", "index", $apiAction, $count);
- $params = array(
- 'secondaryDimension' => API::getInstance()->getDefaultSecondaryDimension($apiAction)
- );
- $reports->addReport('Events_TopEvents', $translations[0], 'Events.' . $controllerAction, $params);
- }
- return $reports->render();
- }
-
- public function indexCategory()
- {
- return $this->indexEvent(__FUNCTION__);
- }
-
- public function indexAction()
- {
- return $this->indexEvent(__FUNCTION__);
- }
-
- public function indexName()
- {
- return $this->indexEvent(__FUNCTION__);
- }
-
- public function getActionFromCategoryId()
- {
- return $this->renderReport(__FUNCTION__);
- }
-
- public function getNameFromCategoryId()
- {
- return $this->renderReport(__FUNCTION__);
- }
-
- public function getCategoryFromActionId()
- {
- return $this->renderReport(__FUNCTION__);
- }
-
- public function getNameFromActionId()
- {
- return $this->renderReport(__FUNCTION__);
- }
-
- public function getActionFromNameId()
- {
- return $this->renderReport(__FUNCTION__);
- }
-
- public function getCategoryFromNameId()
- {
- return $this->renderReport(__FUNCTION__);
- }
-
- protected function indexEvent($controllerMethod)
- {
- $count = 1;
- $apiMethod = str_replace('index', 'get', $controllerMethod, $count);
- $events = new Events;
- $title = $events->getReportTitleTranslation($apiMethod);
-
- if (method_exists($this, $apiMethod)) {
- $content = $this->$apiMethod();
- } else {
- $content = $this->renderReport($apiMethod);
- }
-
- return View::singleReport(
- $title,
- $content
- );
- }
-}
diff --git a/plugins/Events/Events.php b/plugins/Events/Events.php
index ba3e96465d..f09e1a9925 100644
--- a/plugins/Events/Events.php
+++ b/plugins/Events/Events.php
@@ -12,6 +12,7 @@ use Piwik\Common;
use Piwik\Piwik;
use Piwik\Plugin\Report;
use Piwik\Plugin\ViewDataTable;
+use Piwik\Plugin\Reports;
class Events extends \Piwik\Plugin
{
@@ -154,7 +155,7 @@ class Events extends \Piwik\Plugin
$this->addRelatedReports($view, $secondaryDimension);
$this->addTooltipEventValue($view);
- $subtableReport = Report::factory('Events', $view->config->subtable_controller_action);
+ $subtableReport = Reports::factory('Events', $view->config->subtable_controller_action);
$view->config->pivot_by_dimension = $subtableReport->getDimension()->getId();
$view->config->pivot_by_column = 'nb_events';
}
diff --git a/plugins/Events/Menu.php b/plugins/Events/Menu.php
deleted file mode 100644
index 440a35978e..0000000000
--- a/plugins/Events/Menu.php
+++ /dev/null
@@ -1,21 +0,0 @@
-addActionsItem('Events_Events', $this->urlForAction('index'), 30);
- }
-}
diff --git a/plugins/Events/Reports/Base.php b/plugins/Events/Reports/Base.php
index 4430b31407..a43ba0dbfa 100644
--- a/plugins/Events/Reports/Base.php
+++ b/plugins/Events/Reports/Base.php
@@ -10,19 +10,31 @@ namespace Piwik\Plugins\Events\Reports;
use Piwik\Plugins\Events\API;
use Piwik\Plugins\Events\Columns\Metrics\AverageEventValue;
+use Piwik\Report\ReportWidgetFactory;
+use Piwik\Widget\WidgetsList;
abstract class Base extends \Piwik\Plugin\Report
{
protected function init()
{
- $this->category = 'Events_Events';
+ $this->categoryId = 'General_Actions';
+ $this->subcategoryId = 'Events_Events';
+
$this->processedMetrics = array(
new AverageEventValue()
);
+ }
- $this->widgetParams = array(
- 'secondaryDimension' => API::getInstance()->getDefaultSecondaryDimension($this->action)
- );
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
+ {
+ if (!$this->isSubtableReport) {
+ $widget = $factory->createWidget()->setParameters(array(
+ 'secondaryDimension' => API::getInstance()->getDefaultSecondaryDimension($this->action)
+ ));
+
+ $widgetsList->addToContainerWidget('Events', $widget);
+ }
}
+
}
diff --git a/plugins/Events/Reports/GetAction.php b/plugins/Events/Reports/GetAction.php
index 84f750e264..bbdadd7ed4 100644
--- a/plugins/Events/Reports/GetAction.php
+++ b/plugins/Events/Reports/GetAction.php
@@ -27,6 +27,5 @@ class GetAction extends Base
$this->actionToLoadSubTables = 'getNameFromActionId';
}
$this->order = 1;
- $this->widgetTitle = 'Events_EventActions';
}
}
diff --git a/plugins/Events/Reports/GetActionFromCategoryId.php b/plugins/Events/Reports/GetActionFromCategoryId.php
index e5de6af9ca..9c355d4de1 100644
--- a/plugins/Events/Reports/GetActionFromCategoryId.php
+++ b/plugins/Events/Reports/GetActionFromCategoryId.php
@@ -19,7 +19,7 @@ class GetActionFromCategoryId extends Report
{
protected function init()
{
- $this->category = 'Events_Events';
+ $this->categoryId = 'Events_Events';
$this->processedMetrics = false;
$this->dimension = new EventAction();
$this->name = Piwik::translate('Events_EventActions');
diff --git a/plugins/Events/Reports/GetActionFromNameId.php b/plugins/Events/Reports/GetActionFromNameId.php
index 55bd628459..1da175e47b 100644
--- a/plugins/Events/Reports/GetActionFromNameId.php
+++ b/plugins/Events/Reports/GetActionFromNameId.php
@@ -19,7 +19,7 @@ class GetActionFromNameId extends Report
{
protected function init()
{
- $this->category = 'Events_Events';
+ $this->categoryId = 'Events_Events';
$this->processedMetrics = false;
$this->dimension = new EventAction();
$this->name = Piwik::translate('Events_EventActions');
diff --git a/plugins/Events/Reports/GetCategory.php b/plugins/Events/Reports/GetCategory.php
index be867705ea..2049868e7c 100644
--- a/plugins/Events/Reports/GetCategory.php
+++ b/plugins/Events/Reports/GetCategory.php
@@ -27,6 +27,5 @@ class GetCategory extends Base
$this->actionToLoadSubTables = 'getActionFromCategoryId';
}
$this->order = 0;
- $this->widgetTitle = 'Events_EventCategories';
}
}
diff --git a/plugins/Events/Reports/GetCategoryFromActionId.php b/plugins/Events/Reports/GetCategoryFromActionId.php
index 51f5f6918d..47e62c9260 100644
--- a/plugins/Events/Reports/GetCategoryFromActionId.php
+++ b/plugins/Events/Reports/GetCategoryFromActionId.php
@@ -19,7 +19,7 @@ class GetCategoryFromActionId extends Report
{
protected function init()
{
- $this->category = 'Events_Events';
+ $this->categoryId = 'Events_Events';
$this->processedMetrics = false;
$this->dimension = new EventCategory();
$this->name = Piwik::translate('Events_EventCategories');
diff --git a/plugins/Events/Reports/GetCategoryFromNameId.php b/plugins/Events/Reports/GetCategoryFromNameId.php
index 4806c97d0f..0eef6b5f32 100644
--- a/plugins/Events/Reports/GetCategoryFromNameId.php
+++ b/plugins/Events/Reports/GetCategoryFromNameId.php
@@ -19,7 +19,7 @@ class GetCategoryFromNameId extends Report
{
protected function init()
{
- $this->category = 'Events_Events';
+ $this->categoryId = 'Events_Events';
$this->processedMetrics = false;
$this->dimension = new EventCategory();
$this->name = Piwik::translate('Events_EventCategories');
diff --git a/plugins/Events/Reports/GetName.php b/plugins/Events/Reports/GetName.php
index aa21f968be..08d97b6ac4 100644
--- a/plugins/Events/Reports/GetName.php
+++ b/plugins/Events/Reports/GetName.php
@@ -27,6 +27,5 @@ class GetName extends Base
$this->actionToLoadSubTables = 'getActionFromNameId';
}
$this->order = 2;
- $this->widgetTitle = 'Events_EventNames';
}
}
diff --git a/plugins/Events/Reports/GetNameFromActionId.php b/plugins/Events/Reports/GetNameFromActionId.php
index 7b4899b672..4c0325a706 100644
--- a/plugins/Events/Reports/GetNameFromActionId.php
+++ b/plugins/Events/Reports/GetNameFromActionId.php
@@ -19,7 +19,7 @@ class GetNameFromActionId extends Report
{
protected function init()
{
- $this->category = 'Events_Events';
+ $this->categoryId = 'Events_Events';
$this->processedMetrics = false;
$this->dimension = new EventName();
$this->name = Piwik::translate('Events_Names');
diff --git a/plugins/Events/Reports/GetNameFromCategoryId.php b/plugins/Events/Reports/GetNameFromCategoryId.php
index d1f43f7931..6e28ccf1e9 100644
--- a/plugins/Events/Reports/GetNameFromCategoryId.php
+++ b/plugins/Events/Reports/GetNameFromCategoryId.php
@@ -19,7 +19,7 @@ class GetNameFromCategoryId extends Report
{
protected function init()
{
- $this->category = 'Events_Events';
+ $this->categoryId = 'Events_Events';
$this->processedMetrics = false;
$this->dimension = new EventName();
$this->name = Piwik::translate('Events_EventNames');
diff --git a/plugins/Events/Widgets/EventsByDimension.php b/plugins/Events/Widgets/EventsByDimension.php
new file mode 100644
index 0000000000..ef04e0d761
--- /dev/null
+++ b/plugins/Events/Widgets/EventsByDimension.php
@@ -0,0 +1,22 @@
+addVisitorsItem('Report 1', $this->urlForAction('showList'), $orderId = 30);
- // $menu->addActionsItem('Report 1', $this->urlForAction('showList'), $orderId = 30);
-
- // or create a custom category 'UI Framework'
- // $menu->addItem('UI Framework', '', $this->urlForDefaultAction(), $orderId = 30);
- // $menu->addItem('UI Framework', 'Report 1', $this->urlForAction('showList'), $orderId = 30);
- }
public function configureAdminMenu(MenuAdmin $menu)
{
diff --git a/plugins/ExamplePlugin/Widgets.php b/plugins/ExamplePlugin/Widgets.php
deleted file mode 100644
index a5670902ef..0000000000
--- a/plugins/ExamplePlugin/Widgets.php
+++ /dev/null
@@ -1,67 +0,0 @@
-addWidget('Example Widget Name', $method = 'myExampleWidget');
- // $this->addWidget('Example Widget 2', $method = 'myExampleWidget', $params = array('myparam' => 'myvalue'));
- }
-
- /**
- * This method renders a widget as defined in "init()". It's on you how to generate the content of the
- * widget. As long as you return a string everything is fine. You can use for instance a "Piwik\View" to render a
- * twig template. In such a case don't forget to create a twig template (eg. myViewTemplate.twig) in the
- * "templates" directory of your plugin.
- *
- * @return string
- */
- public function myExampleWidget()
- {
- // $view = new View('@ExamplePlugin/myViewTemplate');
- // return $view->render();
-
- return 'My Widget Text';
- }
-
- /**
- * Here you can remove any widgets defined by any plugin.
- *
- * @param WidgetsList $widgetsList
- */
- public function configureWidgetsList(WidgetsList $widgetsList)
- {
- // $widgetsList->remove('NameOfWidgetCategory'); // will remove all widgets having this category
- // $widgetsList->remove('NameOfWidgetCategory', 'Widget name'); // will only remove a specific widget
- }
-}
diff --git a/plugins/ExamplePlugin/Widgets/MyExampleWidget.php b/plugins/ExamplePlugin/Widgets/MyExampleWidget.php
new file mode 100644
index 0000000000..687ee6df67
--- /dev/null
+++ b/plugins/ExamplePlugin/Widgets/MyExampleWidget.php
@@ -0,0 +1,80 @@
+setCategoryId('Example Widgets');
+
+ /**
+ * Set the subcategory the widget belongs to. If a subcategory is set, the widget will be shown in the UI.
+ */
+ // $config->setSubcategoryId('General_Overview');
+
+ /**
+ * Set the name of the widget belongs to.
+ */
+ $config->setName('Example Widget Name');
+
+ /**
+ * Set the order of the widget. The lower the number, the earlier the widget will be listed within a category.
+ */
+ $config->setOrder(99);
+
+ /**
+ * Optionally set URL parameters that will be used when this widget is requested.
+ * $config->setParameters(array('myparam' => 'myvalue'));
+ */
+
+ /**
+ * Define whether a widget is enabled or not. For instance some widgets might not be available to every user or
+ * might depend on a setting (such as Ecommerce) of a site. In such a case you can perform any checks and then
+ * set `true` or `false`. If your widget is only available to users having super user access you can do the
+ * following:
+ *
+ * $config->setIsEnabled(\Piwik\Piwik::hasUserSuperUserAccess());
+ * or
+ * if (!\Piwik\Piwik::hasUserSuperUserAccess())
+ * $config->disable();
+ */
+ }
+
+ /**
+ * This method renders the widget. It's on you how to generate the content of the widget.
+ * As long as you return a string everything is fine. You can use for instance a "Piwik\View" to render a
+ * twig template. In such a case don't forget to create a twig template (eg. myViewTemplate.twig) in the
+ * "templates" directory of your plugin.
+ *
+ * @return string
+ */
+ public function render()
+ {
+ // $view = new View('@ExamplePlugin/myViewTemplate');
+ // return $view->render();
+
+ return ' My Widget Text
';
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/ExampleReport/Reports/Base.php b/plugins/ExampleReport/Reports/Base.php
index c09da78e4b..35af935d9e 100644
--- a/plugins/ExampleReport/Reports/Base.php
+++ b/plugins/ExampleReport/Reports/Base.php
@@ -14,6 +14,6 @@ abstract class Base extends Report
{
protected function init()
{
- $this->category = 'ExampleCategory';
+ $this->categoryId = 'ExampleCategory';
}
}
diff --git a/plugins/ExampleReport/Reports/GetExampleReport.php b/plugins/ExampleReport/Reports/GetExampleReport.php
index 42af54d04a..9e0980f050 100644
--- a/plugins/ExampleReport/Reports/GetExampleReport.php
+++ b/plugins/ExampleReport/Reports/GetExampleReport.php
@@ -49,12 +49,8 @@ class GetExampleReport extends Base
// 24 rows for 1-24hours
// $this->constantRowsCount = true;
- // If a menu title is specified, the report will be displayed in the menu
- // $this->menuTitle = 'ExampleReportName';
-
- // If a widget title is specified, the report will be displayed in the list of widgets and the report can be
- // exported as a widget
- // $this->widgetTitle = 'ExampleReportName';
+ // If a subcategory is specified, the report will be displayed in the menu under this menu item
+ // $this->subCategory = 'ExampleReportName';
}
/**
diff --git a/plugins/ExampleRssWidget/Widgets.php b/plugins/ExampleRssWidget/Widgets.php
deleted file mode 100644
index 5fc0667fa8..0000000000
--- a/plugins/ExampleRssWidget/Widgets.php
+++ /dev/null
@@ -1,63 +0,0 @@
-addWidget('Piwik.org Blog', 'rssPiwik');
- $this->addWidget('Piwik Changelog', 'rssChangelog');
- }
-
- public function rssPiwik()
- {
- try {
- $rss = new RssRenderer('http://feeds.feedburner.com/Piwik');
- $rss->showDescription(true);
-
- return $rss->get();
-
- } catch (\Exception $e) {
-
- return $this->error($e);
- }
- }
-
- public function rssChangelog()
- {
- try {
- $rss = new RssRenderer('http://feeds.feedburner.com/PiwikReleases');
- $rss->setCountPosts(1);
- $rss->showDescription(true);
- $rss->showContent(false);
-
- return $rss->get();
-
- } catch (\Exception $e) {
-
- return $this->error($e);
- }
- }
-
- /**
- * @param \Exception $e
- * @return string
- */
- private function error($e)
- {
- return ''
- . Piwik::translate('General_ErrorRequest', array('', ''))
- . ' - ' . $e->getMessage() . '
';
- }
-}
diff --git a/plugins/ExampleRssWidget/Widgets/RssChangelog.php b/plugins/ExampleRssWidget/Widgets/RssChangelog.php
new file mode 100644
index 0000000000..70ba5eb0c0
--- /dev/null
+++ b/plugins/ExampleRssWidget/Widgets/RssChangelog.php
@@ -0,0 +1,49 @@
+setCategoryId('Example Widgets');
+ $config->setName('Piwik Changelog');
+ }
+
+ public function render()
+ {
+ try {
+ $rss = new RssRenderer('http://feeds.feedburner.com/PiwikReleases');
+ $rss->setCountPosts(1);
+ $rss->showDescription(true);
+ $rss->showContent(false);
+
+ return $rss->get();
+
+ } catch (\Exception $e) {
+
+ return $this->error($e);
+ }
+ }
+
+ /**
+ * @param \Exception $e
+ * @return string
+ */
+ private function error($e)
+ {
+ return ''
+ . Piwik::translate('General_ErrorRequest', array('', ''))
+ . ' - ' . $e->getMessage() . '
';
+ }
+}
diff --git a/plugins/ExampleRssWidget/Widgets/RssPiwik.php b/plugins/ExampleRssWidget/Widgets/RssPiwik.php
new file mode 100644
index 0000000000..c3c7c76a16
--- /dev/null
+++ b/plugins/ExampleRssWidget/Widgets/RssPiwik.php
@@ -0,0 +1,47 @@
+setCategoryId('Example Widgets');
+ $config->setName('Piwik.org Blog');
+ }
+
+ public function render()
+ {
+ try {
+ $rss = new RssRenderer('http://feeds.feedburner.com/Piwik');
+ $rss->showDescription(true);
+
+ return $rss->get();
+
+ } catch (\Exception $e) {
+
+ return $this->error($e);
+ }
+ }
+
+ /**
+ * @param \Exception $e
+ * @return string
+ */
+ private function error($e)
+ {
+ return ''
+ . Piwik::translate('General_ErrorRequest', array('', ''))
+ . ' - ' . $e->getMessage() . '
';
+ }
+}
diff --git a/plugins/ExampleUI/API.php b/plugins/ExampleUI/API.php
index 7b6e0c508a..ae9beae05f 100644
--- a/plugins/ExampleUI/API.php
+++ b/plugins/ExampleUI/API.php
@@ -99,4 +99,5 @@ class API extends \Piwik\Plugin\API
return $planetsDataTable;
}
+
}
diff --git a/plugins/ExampleUI/Categories/ExampleUiCategory.php b/plugins/ExampleUI/Categories/ExampleUiCategory.php
new file mode 100644
index 0000000000..cc7d2fd219
--- /dev/null
+++ b/plugins/ExampleUI/Categories/ExampleUiCategory.php
@@ -0,0 +1,17 @@
+pluginName . '.' . __FUNCTION__;
- $apiAction = 'ExampleUI.getTemperatures';
-
- $view = ViewDataTableFactory::build('table', $apiAction, $controllerAction);
-
- $view->config->translations['value'] = 'Temperature in °C';
- $view->config->translations['label'] = 'Hour of day';
- $view->requestConfig->filter_sort_column = 'label';
- $view->requestConfig->filter_sort_order = 'asc';
- $view->requestConfig->filter_limit = 24;
- $view->config->columns_to_display = array('label', 'value');
- $view->config->y_axis_unit = '°C'; // useful if the user requests the bar graph
- $view->config->show_exclude_low_population = false;
- $view->config->show_table_all_columns = false;
- $view->config->disable_row_evolution = true;
- $view->config->max_graph_elements = 24;
- $view->config->metrics_documentation = array('value' => 'Documentation for temperature metric');
-
- return $view->render();
- }
-
- public function evolutionGraph()
- {
- $view = new View('@ExampleUI/evolutiongraph');
-
- $this->setPeriodVariablesView($view);
- $view->evolutionGraph = $this->getEvolutionGraph(array(), array('server1', 'server2'));
-
- return $view->render();
- }
public function notifications()
{
@@ -78,126 +46,4 @@ class Controller extends \Piwik\Plugin\Controller
return $view->render();
}
- public function getEvolutionGraph(array $columns = array(), array $defaultColumns = array())
- {
- if (empty($columns)) {
- $columns = Common::getRequestVar('columns', false);
- if (false !== $columns) {
- $columns = Piwik::getArrayFromApiParameter($columns);
- }
- }
-
- $view = $this->getLastUnitGraphAcrossPlugins($this->pluginName, __FUNCTION__, $columns,
- $selectableColumns = array('server1', 'server2'), 'My documentation', 'ExampleUI.getTemperaturesEvolution');
- $view->requestConfig->filter_sort_column = 'label';
- $view->requestConfig->filter_sort_order = 'asc';
-
- if (empty($view->config->columns_to_display) && !empty($defaultColumns)) {
- $view->config->columns_to_display = $defaultColumns;
- }
-
- return $this->renderView($view);
- }
-
- public function barGraph()
- {
- $view = ViewDataTableFactory::build(
- 'graphVerticalBar', 'ExampleUI.getTemperatures', $controllerAction = 'ExampleUI.barGraph');
-
- $view->config->y_axis_unit = '°C';
- $view->config->show_footer = false;
- $view->config->translations['value'] = "Temperature";
- $view->config->selectable_columns = array("value");
- $view->config->max_graph_elements = 24;
-
- return $view->render();
- }
-
- public function pieGraph()
- {
- $view = ViewDataTableFactory::build(
- 'graphPie', 'ExampleUI.getPlanetRatios', $controllerAction = 'ExampleUI.pieGraph');
-
- $view->config->columns_to_display = array('value');
- $view->config->translations['value'] = "times the diameter of Earth";
- $view->config->show_footer_icons = false;
- $view->config->selectable_columns = array("value");
- $view->config->max_graph_elements = 10;
-
- return $view->render();
- }
-
- public function tagClouds()
- {
- $output = "Simple tag cloud ";
- $output .= $this->echoSimpleTagClouds();
-
- $output .= "Advanced tag cloud: with logos and links
-
- The logo size is proportional to the value returned by the API
- The logo is linked to a specific URL
- ";
- $output .= $this->echoAdvancedTagClouds();
-
- return $output;
- }
-
- public function echoSimpleTagClouds()
- {
- $view = ViewDataTableFactory::build(
- 'cloud', 'ExampleUI.getPlanetRatios', $controllerAction = 'ExampleUI.echoSimpleTagClouds');
-
- $view->config->columns_to_display = array('label', 'value');
- $view->config->translations['value'] = "times the diameter of Earth";
- $view->config->show_footer = false;
-
- return $view->render();
- }
-
- public function echoAdvancedTagClouds()
- {
- $view = ViewDataTableFactory::build(
- 'cloud', 'ExampleUI.getPlanetRatiosWithLogos', $controllerAction = 'ExampleUI.echoAdvancedTagClouds');
-
- $view->config->display_logo_instead_of_label = true;
- $view->config->columns_to_display = array('label', 'value');
- $view->config->translations['value'] = "times the diameter of Earth";
-
- return $view->render();
- }
-
- public function sparklines()
- {
- $view = new View('@ExampleUI/sparklines');
- $view->urlSparkline1 = $this->getUrlSparkline('generateSparkline', array('server' => 'server1', 'rand' => mt_rand()));
- $view->urlSparkline2 = $this->getUrlSparkline('generateSparkline', array('server' => 'server2', 'rand' => mt_rand()));
-
- return $view->render();
- }
-
- public function generateSparkline()
- {
- $view = ViewDataTableFactory::build(
- 'sparkline', 'ExampleUI.getTemperaturesEvolution', $controllerAction = 'ExampleUI.generateSparkline');
-
- $serverRequested = Common::getRequestVar('server', false);
- if (false !== $serverRequested) {
- $view->config->columns_to_display = array($serverRequested);
- }
-
- return $view->render();
- }
-
- public function treemap()
- {
- $view = ViewDataTableFactory::build(
- 'infoviz-treemap', 'ExampleUI.getTemperatures', $controllerAction = 'ExampleUI.treemap');
-
- $view->config->translations['value'] = "Temperature";
- $view->config->columns_to_display = array("label", "value");
- $view->config->selectable_columns = array("value");
- $view->config->show_evolution_values = 0;
-
- return $view->render();
- }
}
diff --git a/plugins/ExampleUI/Menu.php b/plugins/ExampleUI/Menu.php
index e25bd10817..35cf0d70b5 100644
--- a/plugins/ExampleUI/Menu.php
+++ b/plugins/ExampleUI/Menu.php
@@ -8,37 +8,12 @@
*/
namespace Piwik\Plugins\ExampleUI;
-use Piwik\Menu\MenuReporting;
use Piwik\Menu\MenuUser;
-use Piwik\Plugin\Manager as PluginManager;
-/**
- */
class Menu extends \Piwik\Plugin\Menu
{
- public function configureReportingMenu(MenuReporting $menu)
- {
- $menu->addItem('UI Framework', '', $this->urlForAction('dataTables'), 30);
-
- $this->addSubMenu($menu, 'Data tables', 'dataTables', 1);
- $this->addSubMenu($menu, 'Bar graph', 'barGraph', 2);
- $this->addSubMenu($menu, 'Pie graph', 'pieGraph', 3);
- $this->addSubMenu($menu, 'Tag clouds', 'tagClouds', 4);
- $this->addSubMenu($menu, 'Sparklines', 'sparklines', 5);
- $this->addSubMenu($menu, 'Evolution Graph', 'evolutionGraph', 6);
-
- if (PluginManager::getInstance()->isPluginActivated('TreemapVisualization')) {
- $this->addSubMenu($menu, 'Treemap', 'treemap', 7);
- }
- }
-
public function configureUserMenu(MenuUser $menu)
{
$menu->addPlatformItem('UI Notifications', $this->urlForAction('notifications'), $order = 10);
}
-
- private function addSubMenu(MenuReporting $menu, $subMenu, $action, $order)
- {
- $menu->addItem('UI Framework', $subMenu, $this->urlForAction($action), $order);
- }
}
diff --git a/plugins/ExampleUI/Reports/Base.php b/plugins/ExampleUI/Reports/Base.php
new file mode 100644
index 0000000000..88fa676754
--- /dev/null
+++ b/plugins/ExampleUI/Reports/Base.php
@@ -0,0 +1,19 @@
+categoryId = 'ExampleUI_UiFramework';
+ }
+}
diff --git a/plugins/ExampleUI/Reports/GetPlanetRatios.php b/plugins/ExampleUI/Reports/GetPlanetRatios.php
new file mode 100644
index 0000000000..244e8f79c8
--- /dev/null
+++ b/plugins/ExampleUI/Reports/GetPlanetRatios.php
@@ -0,0 +1,74 @@
+name = 'Pie graph';
+ $this->subcategoryId = $this->name;
+ $this->order = 112;
+ }
+
+ public function getDefaultTypeViewDataTable()
+ {
+ return PIE::ID;
+ }
+
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
+ {
+ $widgetsList->addWidgetConfig(
+ // in this case it will render PIE as configured as default
+ $factory->createWidget()
+ );
+
+ $widgetsList->addWidgetConfig(
+ $factory->createWidget()
+ ->setName('Simple tag cloud')
+ ->setSubcategoryId('Tag clouds')
+ ->forceViewDataTable(Cloud::ID)
+ ->setOrder(5)
+ );
+ }
+
+ public function configureView(ViewDataTable $view)
+ {
+ $view->config->addTranslation('value', 'times the diameter of Earth');
+
+ if ($view->isViewDataTableId(PIE::ID)) {
+
+ $view->config->columns_to_display = array('value');
+ $view->config->selectable_columns = array('value');
+ $view->config->show_footer_icons = false;
+ $view->config->max_graph_elements = 10;
+
+ } else if ($view->isViewDataTableId(Cloud::ID)) {
+
+ $view->config->columns_to_display = array('label', 'value');
+ $view->config->show_footer = false;
+
+ }
+ }
+
+}
diff --git a/plugins/ExampleUI/Reports/GetPlanetRatiosWithLogos.php b/plugins/ExampleUI/Reports/GetPlanetRatiosWithLogos.php
new file mode 100644
index 0000000000..d4d205e6c9
--- /dev/null
+++ b/plugins/ExampleUI/Reports/GetPlanetRatiosWithLogos.php
@@ -0,0 +1,44 @@
+name = Piwik::translate('Advanced tag cloud: with logos and links');
+ $this->subcategoryId = 'Tag clouds';
+ $this->order = 113;
+ }
+
+ public function getDefaultTypeViewDataTable()
+ {
+ return Cloud::ID;
+ }
+
+ public function configureView(ViewDataTable $view)
+ {
+ $view->config->display_logo_instead_of_label = true;
+ $view->config->columns_to_display = array('label', 'value');
+ $view->config->addTranslation('value', 'times the diameter of Earth');
+ }
+
+}
diff --git a/plugins/ExampleUI/Reports/GetTemperatures.php b/plugins/ExampleUI/Reports/GetTemperatures.php
new file mode 100644
index 0000000000..0449e8b3e2
--- /dev/null
+++ b/plugins/ExampleUI/Reports/GetTemperatures.php
@@ -0,0 +1,93 @@
+name = Piwik::translate('ExampleUI_GetTemperaturesDataTable');
+ $this->subcategoryId = 'ExampleUI_GetTemperaturesDataTable';
+ $this->order = 110;
+ }
+
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
+ {
+ // this will render the default view, in this case an Html Table
+ $widgetsList->addWidgetConfig($factory->createWidget());
+
+ $widgetsList->addWidgetConfig(
+ $factory->createWidget()
+ ->forceViewDataTable(Bar::ID)
+ ->setSubcategoryId('Bar graph')
+ );
+
+ if (PluginManager::getInstance()->isPluginActivated('TreemapVisualization')) {
+ $widgetsList->addWidgetConfig(
+ $factory->createWidget()
+ ->setName('Treemap example')
+ ->setSubcategoryId('Treemap')
+ ->forceViewDataTable('infoviz-treemap')
+ );
+
+ }
+ }
+
+ public function configureView(ViewDataTable $view)
+ {
+ if ($view->isViewDataTableId(BAR::ID)) {
+
+ $view->config->y_axis_unit = '°C';
+ $view->config->show_footer = false;
+ $view->config->translations['value'] = "Temperature";
+ $view->config->selectable_columns = array("value");
+ $view->config->max_graph_elements = 24;
+
+ } elseif ($view->isViewDataTableId('infoviz-treemap')) {
+
+ $view->config->translations['value'] = "Temperature";
+ $view->config->columns_to_display = array("label", "value");
+ $view->config->selectable_columns = array("value");
+ $view->config->show_evolution_values = 0;
+
+ } else {
+ // for default view datatable, eg HtmlTable
+
+ $view->config->translations['value'] = 'Temperature in °C';
+ $view->config->translations['label'] = 'Hour of day';
+ $view->requestConfig->filter_sort_column = 'label';
+ $view->requestConfig->filter_sort_order = 'asc';
+ $view->requestConfig->filter_limit = 24;
+ $view->config->columns_to_display = array('label', 'value');
+ $view->config->y_axis_unit = '°C'; // useful if the user requests the bar graph
+ $view->config->show_exclude_low_population = false;
+ $view->config->show_table_all_columns = false;
+ $view->config->disable_row_evolution = true;
+ $view->config->max_graph_elements = 24;
+ $view->config->metrics_documentation = array('value' => 'Documentation for temperature metric');
+ }
+ }
+
+}
diff --git a/plugins/ExampleUI/Reports/GetTemperaturesEvolution.php b/plugins/ExampleUI/Reports/GetTemperaturesEvolution.php
new file mode 100644
index 0000000000..ff8ea66e10
--- /dev/null
+++ b/plugins/ExampleUI/Reports/GetTemperaturesEvolution.php
@@ -0,0 +1,95 @@
+name = Piwik::translate('ExampleUI_GetTemperaturesEvolution');
+ $this->order = 111;
+ }
+
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
+ {
+ $widgetsList->addWidgetConfig(
+ $factory->createWidget()
+ ->setSubcategoryId('Sparklines')
+ ->forceViewDataTable(Sparklines::ID)
+ );
+
+ $widgetsList->addWidgetConfig(
+ $factory->createWidget()
+ ->setName('ExampleUI_TemperaturesEvolution')
+ ->setSubcategoryId('Evolution Graph')
+ ->forceViewDataTable(Evolution::ID)
+ ->setParameters(array('columns' => array('server1', 'server2')))
+ );
+
+ }
+
+ /**
+ * Here you can configure how your report should be displayed. For instance whether your report supports a search
+ * etc. You can also change the default request config. For instance change how many rows are displayed by default.
+ *
+ * @param ViewDataTable $view
+ */
+ public function configureView(ViewDataTable $view)
+ {
+ if ($view->isViewDataTableId(Sparklines::ID)) {
+
+ /** @var Sparklines $view */
+ $view->config->addSparklineMetric(array('server1'));
+ $view->config->addSparklineMetric(array('server2'));
+ $view->config->addTranslations(array('server1' => 'Evolution of temperature for server piwik.org'));
+ $view->config->addTranslations(array('server2' => 'Evolution of temperature for server dev.piwik.org'));
+
+ } elseif ($view->isViewDataTableId(Evolution::ID)) {
+
+ /** @var Evolution $view */
+ $selectableColumns = array('server1', 'server2');
+
+ $columns = Common::getRequestVar('columns', false);
+ if (!empty($columns)) {
+ $columns = Piwik::getArrayFromApiParameter($columns);
+ }
+
+ $columns = array_merge($columns ? $columns : array(), $selectableColumns);
+ $view->config->columns_to_display = $columns;
+
+ $view->config->addTranslations(array_combine($columns, $columns));
+ $view->config->selectable_columns = $selectableColumns;
+ $view->requestConfig->filter_sort_column = 'label';
+ $view->requestConfig->filter_sort_order = 'asc';
+ $view->config->documentation = 'My documentation';
+ $view->config->show_goals = false;
+ }
+ }
+
+}
diff --git a/plugins/ExampleUI/lang/en.json b/plugins/ExampleUI/lang/en.json
new file mode 100644
index 0000000000..e0c05c5411
--- /dev/null
+++ b/plugins/ExampleUI/lang/en.json
@@ -0,0 +1,8 @@
+{
+ "ExampleUI": {
+ "UiFramework": "UI Framework",
+ "GetTemperaturesDataTable": "Data tables",
+ "GetTemperaturesEvolution": "Temperatures evolution over time",
+ "TemperaturesEvolution": "Evolution of server temperatures over the last few days"
+ }
+}
\ No newline at end of file
diff --git a/plugins/ExampleUI/plugin.json b/plugins/ExampleUI/plugin.json
index ec2fcfe025..8a39e0a2a2 100644
--- a/plugins/ExampleUI/plugin.json
+++ b/plugins/ExampleUI/plugin.json
@@ -2,14 +2,23 @@
"name": "ExampleUI",
"description": "Piwik Platform showcase: how to display data tables, graphs, and the UI framework.",
"version": "1.0.1",
- "keywords": ["example", "framework", "platform", "ui", "visualization"],
- "homepage": "http://piwik.org",
+ "keywords": [
+ "example",
+ "framework",
+ "platform",
+ "ui",
+ "visualization"
+ ],
+ "homepage": "http:\/\/piwik.org",
"license": "GPL v3+",
"authors": [
{
"name": "Piwik",
"email": "hello@piwik.org",
- "homepage": "http://piwik.org"
+ "homepage": "http:\/\/piwik.org"
}
- ]
+ ],
+ "require": {
+ "piwik": ">=2.13.1"
+ }
}
\ No newline at end of file
diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php
index 80a898fe34..351e700782 100644
--- a/plugins/Goals/API.php
+++ b/plugins/Goals/API.php
@@ -22,6 +22,7 @@ use Piwik\Plugin\Report;
use Piwik\Plugins\API\DataTable\MergeDataTables;
use Piwik\Plugins\CoreHome\Columns\Metrics\ConversionRate;
use Piwik\Plugins\Goals\Columns\Metrics\AverageOrderRevenue;
+use Piwik\Plugin\Reports;
use Piwik\Segment\SegmentExpression;
use Piwik\Site;
use Piwik\Tracker\Cache;
@@ -414,7 +415,7 @@ class API extends \Piwik\Plugin\API
$requestedColumns = array_unique(array_merge($requestedColumns, $metricsToAdd));
}
- $report = Report::factory('Goals', 'getMetrics');
+ $report = Reports::factory('Goals', 'getMetrics');
$columnsToGet = $report->getMetricsRequiredForReport($allMetrics, $requestedColumns);
$inDbMetricNames = array_map(function ($name) use ($idGoal) {
diff --git a/plugins/Goals/Categories/AddANewGoalSubcategory.php b/plugins/Goals/Categories/AddANewGoalSubcategory.php
new file mode 100644
index 0000000000..9b613f2e55
--- /dev/null
+++ b/plugins/Goals/Categories/AddANewGoalSubcategory.php
@@ -0,0 +1,19 @@
+translator = $translator;
- $this->translationHelper = $translationHelper;
$this->idSite = Common::getRequestVar('idSite', null, 'int');
$this->goals = API::getInstance()->getGoals($this->idSite);
}
- public function widgetGoalReport()
- {
- $view = $this->getGoalReportView($idGoal = Common::getRequestVar('idGoal', null, 'string'));
- $view->displayFullReport = false;
- return $view->render();
- }
-
- public function goalReport()
- {
- $view = $this->getGoalReportView($idGoal = Common::getRequestVar('idGoal', null, 'string'));
- $view->displayFullReport = true;
- return $view->render();
- }
-
- protected function getGoalReportView($idGoal = false)
- {
- $view = new View('@Goals/getGoalReportView');
- if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
- $goalDefinition['name'] = $this->translator->translate('Goals_Ecommerce');
- $goalDefinition['allow_multiple'] = true;
- $ecommerce = $view->ecommerce = true;
- } else {
- if (!isset($this->goals[$idGoal])) {
- Piwik::redirectToModule('Goals', 'index', array('idGoal' => null));
- }
- $goalDefinition = $this->goals[$idGoal];
- }
- $this->setGeneralVariablesView($view);
- $goal = $this->getMetricsForGoal($idGoal);
- foreach ($goal as $name => $value) {
- $view->$name = $value;
- }
- if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
- $goal = $this->getMetricsForGoal(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART);
- foreach ($goal as $name => $value) {
- $name = 'cart_' . $name;
- $view->$name = $value;
- }
- }
- $view->showHeadline = false;
- $view->idGoal = $idGoal;
- $view->goalName = $goalDefinition['name'];
- $view->goalAllowMultipleConversionsPerVisit = $goalDefinition['allow_multiple'];
- $view->graphEvolution = $this->getEvolutionGraph(array(), $idGoal, array('nb_conversions'));
- $view->nameGraphEvolution = 'Goals.getEvolutionGraph' . $idGoal;
- $view->topDimensions = $this->getTopDimensions($idGoal);
-
- $goalMetrics = Request::processRequest('Goals.get', array('idGoal' => $idGoal));
-
- // conversion rate for new and returning visitors
- $view->conversion_rate_returning = $this->formatConversionRate($goalMetrics, 'conversion_rate_returning_visit');
- $view->conversion_rate_new = $this->formatConversionRate($goalMetrics, 'conversion_rate_new_visit');
-
- $view->goalReportsByDimension = $this->getGoalReportsByDimensionTable(
- $view->nb_conversions, isset($ecommerce), !empty($view->cart_nb_conversions));
- return $view;
- }
-
- public function index()
- {
- $view = $this->getOverviewView();
-
- // unsanitize goal names and other text data (not done in API so as not to break
- // any other code/cause security issues)
- $goals = $this->goals;
- $view->goalsJSON = json_encode($goals);
-
- $view->ecommerceEnabled = $this->site->isEcommerceEnabled();
- $view->displayFullReport = true;
- return $view->render();
- }
-
public function manage()
{
Piwik::checkUserHasAdminAccess($this->idSite);
@@ -164,48 +86,20 @@ class Controller extends \Piwik\Plugin\Controller
return $view->render();
}
- public function widgetGoalsOverview()
+ public function goalConversionsOverview()
{
- $view = $this->getOverviewView();
- $view->displayFullReport = false;
- return $view->render();
- }
+ $view = new View('@Goals/conversionOverview');
+ $idGoal = Common::getRequestVar('idGoal', null, 'string');
- protected function getOverviewView()
- {
- $view = new View('@Goals/getOverviewView');
- $this->setGeneralVariablesView($view);
-
- $view->graphEvolution = $this->getEvolutionGraph(array(), false, array('nb_conversions'));
- $view->nameGraphEvolution = 'GoalsgetEvolutionGraph';
-
- // sparkline for the historical data of the above values
- $view->urlSparklineConversions = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_conversions'), 'idGoal' => ''));
- $view->urlSparklineConversionRate = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('conversion_rate'), 'idGoal' => ''));
- $view->urlSparklineRevenue = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('revenue'), 'idGoal' => ''));
-
- // Pass empty idGoal will return Goal overview
- $request = new Request("method=Goals.get&format=original&idGoal=");
- $datatable = $request->process();
- $dataRow = $datatable->getFirstRow();
+ $view->topDimensions = $this->getTopDimensions($idGoal);
- $view->nb_conversions = $dataRow->getColumn('nb_conversions');
- $view->nb_visits_converted = $dataRow->getColumn('nb_visits_converted');
- $view->conversion_rate = $this->formatConversionRate($dataRow->getColumn('conversion_rate'));
- $view->revenue = $dataRow->getColumn('revenue');
+ $goalMetrics = Request::processRequest('Goals.get', array('idGoal' => $idGoal));
- $goalMetrics = array();
- foreach ($this->goals as $idGoal => $goal) {
- $goalMetrics[$idGoal] = $this->getMetricsForGoal($idGoal);
- $goalMetrics[$idGoal]['name'] = $goal['name'];
- $goalMetrics[$idGoal]['goalAllowMultipleConversionsPerVisit'] = $goal['allow_multiple'];
- }
+ // conversion rate for new and returning visitors
+ $view->conversion_rate_returning = $this->formatConversionRate($goalMetrics, 'conversion_rate_returning_visit');
+ $view->conversion_rate_new = $this->formatConversionRate($goalMetrics, 'conversion_rate_new_visit');
- $view->goalMetrics = $goalMetrics;
- $view->goals = $this->goals;
- $view->goalReportsByDimension = $this->getGoalReportsByDimensionTable(
- $view->nb_conversions, $ecommerce = false, !empty($view->cart_nb_conversions));
- return $view;
+ return $view->render();
}
public function getLastNbConversionsGraph()
@@ -244,6 +138,26 @@ class Controller extends \Piwik\Plugin\Controller
return $view->render();
}
+ public function hasConversions()
+ {
+ $this->checkSitePermission();
+
+ $idGoal = Common::getRequestVar('idGoal', '', 'string');
+ $idSite = Common::getRequestVar('idSite', null, 'int');
+ $period = Common::getRequestVar('period', null, 'string');
+ $date = Common::getRequestVar('date', null, 'string');
+
+ Piwik::checkUserHasViewAccess($idSite);
+
+ $conversions = new Conversions();
+
+ Json::sendHeaderJSON();
+
+ $numConversions = $conversions->getConversionForGoal($idGoal, $idSite, $period, $date);
+
+ return json_encode($numConversions > 0);
+ }
+
public function getEvolutionGraph(array $columns = array(), $idGoal = false, array $defaultColumns = array())
{
if (empty($columns)) {
@@ -334,11 +248,11 @@ class Controller extends \Piwik\Plugin\Controller
$topDimensions = array();
foreach ($topDimensionsToLoad as $dimensionName => $apiMethod) {
$request = new Request("method=$apiMethod
- &format=original
- &filter_update_columns_when_show_all_goals=1
- &idGoal=" . AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE . "
- &filter_sort_order=desc
- &filter_sort_column=$columnNbConversions" .
+ &format=original
+ &filter_update_columns_when_show_all_goals=1
+ &idGoal=" . AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE . "
+ &filter_sort_order=desc
+ &filter_sort_column=$columnNbConversions" .
// select a couple more in case some are not valid (ie. conversions==0 or they are "Keyword not defined")
"&filter_limit=" . (self::COUNT_TOP_ROWS_TO_DISPLAY + 2));
$datatable = $request->process();
@@ -402,72 +316,11 @@ class Controller extends \Piwik\Plugin\Controller
'avg_order_revenue' => $aov ? $aov : 0,
'urlSparklinePurchasedProducts' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('items'), 'idGoal' => $idGoal)),
'urlSparklineAverageOrderValue' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('avg_order_revenue'), 'idGoal' => $idGoal)),
- ));
+ ));
}
return $return;
}
- /**
- * Utility function that returns HTML that displays Goal information for reports. This
- * is the HTML that is at the bottom of every goals page.
- *
- * @param int $conversions The number of conversions for this goal (or all goals
- * in case of the overview).
- * @param bool $ecommerce Whether to show ecommerce reports or not.
- * @param bool $cartNbConversions Whether there are cart conversions or not for this
- * goal.
- * @return string
- */
- private function getGoalReportsByDimensionTable($conversions, $ecommerce = false, $cartNbConversions = false)
- {
- $preloadAbandonedCart = $cartNbConversions !== false && $conversions == 0;
-
- $goalReportsByDimension = new ReportsByDimension('Goals');
-
- // add ecommerce reports
- $ecommerceCustomParams = array();
- if ($ecommerce) {
- if ($preloadAbandonedCart) {
- $ecommerceCustomParams['abandonedCarts'] = '1';
- } else {
- $ecommerceCustomParams['abandonedCarts'] = '0';
- }
- }
-
- if ($conversions > 0 || $ecommerce) {
- // for non-Goals reports, we show the goals table
- $customParams = $ecommerceCustomParams + array('documentationForGoalsPage' => '1');
-
- if (Common::getRequestVar('idGoal', '') === '') // if no idGoal, use 0 for overview
- {
- $customParams['idGoal'] = '0'; // NOTE: Must be string! Otherwise Piwik_View_HtmlTable_Goals fails.
- }
-
- $allReports = Goals::getReportsWithGoalMetrics();
- foreach ($allReports as $category => $reports) {
- if ($ecommerce) {
- $categoryText = $this->translationHelper->translateEcommerceMetricCategory($category);
- } else {
- $categoryText = $this->translationHelper->translateGoalMetricCategory($category);
- }
-
- foreach ($reports as $report) {
- if (empty($report['viewDataTable'])
- && empty($report['abandonedCarts'])
- ) {
- $report['viewDataTable'] = 'tableGoals';
- }
- $customParams['viewDataTable'] = $report['viewDataTable'];
-
- $goalReportsByDimension->addReport(
- $categoryText, $report['name'], $report['module'] . '.' . $report['action'], $customParams);
- }
- }
- }
-
- return $goalReportsByDimension->render();
- }
-
private function setEditGoalsViewVariables($view)
{
$goals = $this->goals;
@@ -496,4 +349,32 @@ class Controller extends \Piwik\Plugin\Controller
{
$view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
}
+
+ /**
+ * @deprecated used to be a widgetized URL. There to not break widget URLs
+ */
+ public function widgetGoalReport()
+ {
+ $idGoal = Common::getRequestVar('idGoal', '', 'string');
+
+ if ($idGoal === Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
+ $_GET['containerId'] = 'EcommerceOverview';
+ } elseif (!empty($idGoal)) {
+ $_GET['containerId'] = 'Goal_' . (int) $idGoal;
+ } else {
+ return '';
+ }
+
+ return FrontController::getInstance()->fetchDispatch('CoreHome', 'renderWidgetContainer');
+ }
+
+ /**
+ * @deprecated used to be a widgetized URL. There to not break widget URLs
+ */
+ public function widgetGoalsOverview()
+ {
+ $_GET['containerId'] = 'GoalsOverview';
+
+ return FrontController::getInstance()->fetchDispatch('CoreHome', 'renderWidgetContainer');
+ }
}
diff --git a/plugins/Goals/Conversions.php b/plugins/Goals/Conversions.php
new file mode 100644
index 0000000000..fbbbef0078
--- /dev/null
+++ b/plugins/Goals/Conversions.php
@@ -0,0 +1,45 @@
+ $idGoal,
+ 'period' => $period,
+ 'date' => $date,
+ 'idSite' => $idSite,
+ 'serialize' => 0,
+ 'segment' => false
+ ));
+
+ // we ignore the segment even if there is one set. We still want to show conversion overview if there are conversions
+ // in general but not for this segment
+
+ $dataRow = $datatable->getFirstRow();
+
+ if (!$dataRow) {
+ return false;
+ }
+
+ return $dataRow->getColumn('nb_conversions');
+ }
+}
diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php
index d26e2cf77e..609d98c419 100644
--- a/plugins/Goals/Goals.php
+++ b/plugins/Goals/Goals.php
@@ -13,8 +13,9 @@ use Piwik\Common;
use Piwik\Db;
use Piwik\Piwik;
use Piwik\Plugin\Report;
+use Piwik\Plugin\Reports;
use Piwik\Tracker\GoalManager;
-use Piwik\Translate;
+use Piwik\Category\Subcategory;
/**
*
@@ -29,36 +30,17 @@ class Goals extends \Piwik\Plugin
foreach ($dimensions as $dimension) {
$group = $dimension['category'];
// move "Custom Variables" report to the "Goals/Sales by User attribute" category
- if ($dimension['module'] === 'CustomVariables') {
+ if ($dimension['module'] === 'CustomVariables'
+ || $dimension['action'] == 'getVisitInformationPerServerTime') {
$group = 'VisitsSummary_VisitsSummary';
}
unset($dimension['category']);
$dimensionsByGroup[$group][] = $dimension;
}
- uksort($dimensionsByGroup, array('self', 'sortGoalDimensionsByModule'));
return $dimensionsByGroup;
}
- public static function sortGoalDimensionsByModule($a, $b)
- {
- static $order = null;
-
- if (is_null($order)) {
- $order = array(
- 'Referrers_Referrers',
- 'General_Visit',
- 'General_Visitors',
- 'VisitsSummary_VisitsSummary',
- 'VisitTime_ColumnServerTime',
- );
- }
-
- $orderA = array_search($a, $order);
- $orderB = array_search($b, $order);
- return $orderA > $orderB;
- }
-
public static function getGoalColumns($idGoal)
{
$columns = array(
@@ -98,11 +80,34 @@ class Goals extends \Piwik\Plugin
'SitesManager.deleteSite.end' => 'deleteSiteGoals',
'Goals.getReportsWithGoalMetrics' => 'getActualReportsWithGoalMetrics',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
- 'Metrics.getDefaultMetricTranslations' => 'addMetricTranslations'
+ 'Metrics.getDefaultMetricTranslations' => 'addMetricTranslations',
+ 'Category.addSubcategories' => 'addSubcategories'
);
return $hooks;
}
+ public function addSubcategories(&$subcategories)
+ {
+ $idSite = Common::getRequestVar('idSite', 0, 'int');
+
+ if (!$idSite) {
+ return;
+ }
+
+ $goals = API::getInstance()->getGoals($idSite);
+
+ $order = 900;
+ foreach ($goals as $goal) {
+ $category = new Subcategory();
+ $category->setName($goal['name']);
+ $category->setCategoryId('Goals_Goals');
+ $category->setId($goal['idgoal']);
+ $category->setOrder($order++);
+ $subcategories[] = $category;
+ }
+ }
+
+
public function addMetricTranslations(&$translations)
{
$metrics = array(
@@ -176,10 +181,12 @@ class Goals extends \Piwik\Plugin
{
$reportsWithGoals = array();
- foreach (Report::getAllReports() as $report) {
+ $reports = new Reports();
+
+ foreach ($reports->getAllReports() as $report) {
if ($report->hasGoalMetrics()) {
$reportsWithGoals[] = array(
- 'category' => $report->getCategoryKey(),
+ 'category' => $report->getCategoryId(),
'name' => $report->getName(),
'module' => $report->getModule(),
'action' => $report->getAction(),
@@ -271,5 +278,6 @@ class Goals extends \Piwik\Plugin
$translationKeys[] = 'Goals_DeleteGoalConfirm';
$translationKeys[] = 'Goals_Ecommerce';
$translationKeys[] = 'Goals_Optional';
+ $translationKeys[] = 'Goals_ChooseGoal';
}
}
diff --git a/plugins/Goals/Menu.php b/plugins/Goals/Menu.php
index 85db036797..503c7e0430 100644
--- a/plugins/Goals/Menu.php
+++ b/plugins/Goals/Menu.php
@@ -10,7 +10,6 @@ namespace Piwik\Plugins\Goals;
use Piwik\Common;
use Piwik\Menu\Group;
-use Piwik\Menu\MenuReporting;
use Piwik\Menu\MenuUser;
use Piwik\Piwik;
use Piwik\Plugins\UsersManager\UserPreferences;
@@ -18,48 +17,6 @@ use Piwik\Translate;
class Menu extends \Piwik\Plugin\Menu
{
- public function configureReportingMenu(MenuReporting $menu)
- {
- $idSite = $this->getIdSite();
- $goals = API::getInstance()->getGoals($idSite);
- $mainGoalMenu = 'Goals_Goals';
-
- if (count($goals) == 0) {
- $linkToAddNewGoal = $this->urlForAction('addNewGoal', array(
- 'idGoal' => null,
- ));
- $menu->addItem($mainGoalMenu, '', $linkToAddNewGoal, 25);
- $menu->addItem($mainGoalMenu, 'Goals_AddNewGoal', $linkToAddNewGoal, 1);
- return;
- }
-
- $order = 1;
-
- $url = $this->urlForAction('index', array('idGoal' => null));
-
- $menu->addItem($mainGoalMenu, '', $url, 25);
- $menu->addItem($mainGoalMenu, 'General_Overview', $url, ++$order);
-
- $group = new Group();
- foreach ($goals as $goal) {
- $subMenuName = str_replace('%', '%%', Translate::clean($goal['name']));
- $params = $this->urlForAction('goalReport', array('idGoal' => $goal['idgoal']));
- $tooltip = sprintf('%s (id = %d)', $subMenuName, $goal['idgoal']);
-
- if (count($goals) > 3) {
- $group->add($subMenuName, $params, $tooltip);
- } else {
- $menu->addItem($mainGoalMenu, $subMenuName, $params, ++$order, $tooltip);
- }
- }
-
- if (count($goals) > 3) {
- $menu->addGroup($mainGoalMenu, 'Goals_ChooseGoal', $group, ++$order, $tooltip = false);
- }
-
- $menu->addItem($mainGoalMenu, 'Goals_ManageGoals', $this->urlForAction('editGoals'), ++$order);
- }
-
public function configureUserMenu(MenuUser $menu)
{
$userPreferences = new UserPreferences();
diff --git a/plugins/Goals/Pages.php b/plugins/Goals/Pages.php
new file mode 100644
index 0000000000..9ab7a96d66
--- /dev/null
+++ b/plugins/Goals/Pages.php
@@ -0,0 +1,339 @@
+factory = $reportFactory;
+ $this->allReports = $reportsWithGoalMetrics;
+ $this->conversions = new Conversions();
+ }
+
+ /**
+ * @param array $goals
+ * @return WidgetConfig[]
+ */
+ public function createGoalsOverviewPage($goals)
+ {
+ $subcategory = 'General_Overview';
+
+ $widgets = array();
+
+ $config = $this->factory->createWidget();
+ $config->forceViewDataTable(Evolution::ID);
+ $config->setSubcategoryId($subcategory);
+ $config->setAction('getEvolutionGraph');
+ $config->setOrder(++$this->orderId);
+ $config->setIsNotWidgetizable();
+ $widgets[] = $config;
+
+ $config = $this->factory->createWidget();
+ $config->forceViewDataTable(Sparklines::ID);
+ $config->setSubcategoryId($subcategory);
+ $config->setName('');
+ $config->setOrder(++$this->orderId);
+ $config->setIsNotWidgetizable();
+ $widgets[] = $config;
+
+ foreach ($goals as $goal) {
+ $name = Common::sanitizeInputValue($goal['name']);
+ $goalTranslated = Piwik::translate('Goals_GoalX', array($name));
+
+ $config = $this->factory->createWidget();
+ $config->setName($goalTranslated);
+ $config->setSubcategoryId($subcategory);
+ $config->forceViewDataTable(Sparklines::ID);
+ $config->setParameters(array('idGoal' => $goal['idgoal']));
+ $config->setOrder(++$this->orderId);
+ $config->setIsNotWidgetizable();
+ $config->addParameters(array('allow_multiple' => (int) $goal['allow_multiple'], 'only_summary' => '1'));
+ $widgets[] = $config;
+ }
+
+ $container = $this->createWidgetizableWidgetContainer('GoalsOverview', $subcategory, $widgets);
+
+ $config = $this->factory->createContainerWidget('Goals');
+ $config->setSubcategoryId($subcategory);
+ $config->setName('Goals_ConversionsOverviewBy');
+ $config->setOrder(++$this->orderId);
+ $config->setIsNotWidgetizable();
+ $this->buildGoalByDimensionView('', $config);
+ $config->setMiddlewareParameters(array(
+ 'module' => 'Goals',
+ 'action' => 'hasConversions'
+ ));
+
+ return array($container, $config);
+ }
+
+ /**
+ * @return WidgetConfig[]
+ */
+ public function createEcommerceOverviewPage()
+ {
+ $category = 'Goals_Ecommerce';
+ $subcategory = 'General_Overview';
+ $idGoal = Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER;
+
+ $widgets = array();
+ $config = $this->factory->createWidget();
+ $config->forceViewDataTable(Evolution::ID);
+ $config->setCategoryId($category);
+ $config->setSubcategoryId($subcategory);
+ $config->setAction('getEvolutionGraph');
+ $config->setOrder(++$this->orderId);
+ $config->setIsNotWidgetizable();
+ $config->setParameters(array('idGoal' => $idGoal));
+ $widgets[] = $config;
+
+ $config = $this->factory->createWidget();
+ $config->setCategoryId($category);
+ $config->forceViewDataTable(Sparklines::ID);
+ $config->setSubcategoryId($subcategory);
+ $config->setName('');
+ $config->setModule('Ecommerce');
+ $config->setAction('getSparklines');
+ $config->setParameters(array('idGoal' => $idGoal));
+ $config->setOrder(++$this->orderId);
+ $config->setIsNotWidgetizable();
+ $widgets[] = $config;
+
+ $config = $this->factory->createWidget();
+ $config->setModule('Ecommerce');
+ $config->setAction('getConversionsOverview');
+ $config->setSubcategoryId($idGoal);
+ $config->setName('Goals_ConversionsOverview');
+ $config->setParameters(array('idGoal' => $idGoal));
+ $config->setOrder(++$this->orderId);
+ $config->setIsNotWidgetizable();
+ $config->setMiddlewareParameters(array(
+ 'module' => 'Goals',
+ 'action' => 'hasConversions',
+ 'idGoal' => $idGoal
+ ));
+
+ $widgets[] = $config;
+
+ $container = $this->createWidgetizableWidgetContainer('EcommerceOverview', $subcategory, $widgets);
+ return array($container);
+ }
+
+ /**
+ * @return WidgetConfig[]
+ */
+ public function createEcommerceSalesPage()
+ {
+ $category = 'Goals_Ecommerce';
+ $subcategory = 'Ecommerce_Sales';
+
+ $config = $this->factory->createContainerWidget('GoalsOrder');
+ $config->setCategoryId($category);
+ $config->setSubcategoryId($subcategory);
+ $config->setName('');
+ $config->setParameters(array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER));
+ $config->setOrder(++$this->orderId);
+ $config->setIsNotWidgetizable();
+ $this->buildGoalByDimensionView(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER, $config);
+
+ return array($config);
+ }
+
+ /**
+ * @param array $goal
+ * @return WidgetConfig[]
+ */
+ public function createGoalDetailPage($goal)
+ {
+ $widgets = array();
+
+ $idGoal = (int) $goal['idgoal'];
+ $name = Common::sanitizeInputValue($goal['name']);
+ $params = array('idGoal' => $idGoal);
+
+ $config = $this->factory->createWidget();
+ $config->setSubcategoryId($idGoal);
+ $config->forceViewDataTable(Evolution::ID);
+ $config->setAction('getEvolutionGraph');
+ $config->setParameters($params);
+ $config->setOrder(++$this->orderId);
+ $config->setIsNotWidgetizable();
+ $widgets[] = $config;
+
+ $config = $this->factory->createWidget();
+ $config->setSubcategoryId($idGoal);
+ $config->setName('');
+ $config->forceViewDataTable(Sparklines::ID);
+ $config->setParameters($params);
+ $config->addParameters(array('allow_multiple' => (int) $goal['allow_multiple']));
+ $config->setOrder(++$this->orderId);
+ $config->setIsNotWidgetizable();
+ $config->setIsNotWidgetizable();
+ $widgets[] = $config;
+
+ $config = $this->factory->createWidget();
+ $config->setAction('goalConversionsOverview');
+ $config->setSubcategoryId($idGoal);
+ $config->setName('Goals_ConversionsOverview');
+ $config->setParameters($params);
+ $config->setOrder(++$this->orderId);
+ $config->setIsNotWidgetizable();
+ $config->setMiddlewareParameters(array(
+ 'module' => 'Goals',
+ 'action' => 'hasConversions',
+ 'idGoal' => $idGoal
+ ));
+ $widgets[] = $config;
+
+ $container = $this->createWidgetizableWidgetContainer('Goal_' . $idGoal, $name, $widgets);
+
+ $configs = array($container);
+
+ $config = $this->factory->createContainerWidget('Goals' . $idGoal);
+ $config->setName(Piwik::translate('Goals_GoalConversionsBy', array($name)));
+ $config->setSubcategoryId($idGoal);
+ $config->setParameters(array());
+ $config->setOrder(++$this->orderId);
+ $config->setIsNotWidgetizable();
+ $config->setMiddlewareParameters(array(
+ 'module' => 'Goals',
+ 'action' => 'hasConversions',
+ 'idGoal' => $idGoal
+ ));
+ $this->buildGoalByDimensionView($idGoal, $config);
+
+ $configs[] = $config;
+
+ return $configs;
+ }
+
+ private function createWidgetizableWidgetContainer($containerId, $pageName, $widgets)
+ {
+ /** @var \Piwik\Widget\WidgetConfig[] $widgets */
+ $firstWidget = reset($widgets);
+ /** @var \Piwik\Report\ReportWidgetConfig $firstWidget */
+
+ if (!empty($pageName)) {
+ // make sure to not show two titles (one for this container and one for the first widget)
+ $firstWidget->setName('');
+ }
+
+ $config = $this->factory->createContainerWidget($containerId);
+ $config->setName($pageName);
+ $config->setCategoryId($firstWidget->getCategoryId());
+ $config->setSubcategoryId($firstWidget->getSubcategoryId());
+ $config->setIsWidgetizable();
+ $config->setOrder($this->orderId++);
+
+ foreach ($widgets as $widget) {
+ $config->addWidgetConfig($widget);
+ }
+
+ return $config;
+ }
+
+ private function buildGoalByDimensionView($idGoal, WidgetContainerConfig $container)
+ {
+ $container->setLayout('ByDimension');
+ $ecommerce = ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER);
+
+ // for non-Goals reports, we show the goals table
+ $customParams = array('documentationForGoalsPage' => '1');
+
+ if ($idGoal === '') {
+ // if no idGoal, use 0 for overview. Must be string! Otherwise Piwik_View_HtmlTable_Goals fails.
+ $customParams['idGoal'] = '0';
+ } else {
+ $customParams['idGoal'] = $idGoal;
+ }
+
+ $translationHelper = new TranslationHelper();
+
+ foreach ($this->allReports as $category => $reports) {
+ $order = ($this->getSortOrderOfCategory($category) * 100);
+
+ if ($ecommerce) {
+ $categoryText = $translationHelper->translateEcommerceMetricCategory($category);
+ } else {
+ $categoryText = $translationHelper->translateGoalMetricCategory($category);
+ }
+
+ foreach ($reports as $report) {
+ $order++;
+
+ if (empty($report['viewDataTable'])
+ && empty($report['abandonedCarts'])
+ ) {
+ $report['viewDataTable'] = 'tableGoals';
+ }
+
+ $widget = $this->createWidgetForReport($report['module'], $report['action']);
+ $widget->setParameters($customParams);
+ $widget->setCategoryId($categoryText);
+ $widget->setSubcategoryId($categoryText);
+ $widget->setOrder($order);
+ $widget->setIsNotWidgetizable();
+
+ if (!empty($report['viewDataTable'])) {
+ $widget->setDefaultViewDataTable($report['viewDataTable']);
+ }
+
+ $container->addWidgetConfig($widget);
+ }
+ }
+ }
+
+ private function getSortOrderOfCategory($category)
+ {
+ static $order = null;
+
+ if (is_null($order)) {
+ $order = array(
+ 'Referrers_Referrers',
+ 'General_Visit',
+ 'General_Visitors',
+ 'VisitsSummary_VisitsSummary',
+ );
+ }
+
+ $value = array_search($category, $order);
+
+ if (false === $value) {
+ $value = count($order) + 1;
+ }
+
+ return $value;
+ }
+
+ private function createWidgetForReport($module, $action)
+ {
+ $factory = new ReportWidgetFactory(Reports::factory($module, $action));
+ return $factory->createWidget();
+ }
+
+}
diff --git a/plugins/Goals/Reports/Base.php b/plugins/Goals/Reports/Base.php
index fd732035d3..3f9cb9c98a 100644
--- a/plugins/Goals/Reports/Base.php
+++ b/plugins/Goals/Reports/Base.php
@@ -18,7 +18,7 @@ abstract class Base extends \Piwik\Plugin\Report
protected function init()
{
- $this->category = 'Goals_Goals';
+ $this->categoryId = 'Goals_Goals';
}
protected function addReportMetadataForEachGoal(&$availableReports, $infos, $goalNameFormatter)
diff --git a/plugins/Goals/Reports/Get.php b/plugins/Goals/Reports/Get.php
index bccecda146..66f167fb24 100644
--- a/plugins/Goals/Reports/Get.php
+++ b/plugins/Goals/Reports/Get.php
@@ -8,7 +8,20 @@
*/
namespace Piwik\Plugins\Goals\Reports;
+use Piwik\Common;
+use Piwik\DataTable;
+use Piwik\Metrics\Formatter;
use Piwik\Piwik;
+use Piwik\Plugin;
+use Piwik\Plugin\ViewDataTable;
+use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution;
+use Piwik\Plugins\CoreVisualizations\Visualizations\Sparklines;
+use Piwik\Plugins\Goals\API;
+use Piwik\Plugins\Goals\Goals;
+use Piwik\Plugins\Goals\Pages;
+use Piwik\Report\ReportWidgetFactory;
+use Piwik\Site;
+use Piwik\Widget\WidgetsList;
class Get extends Base
{
@@ -25,6 +38,104 @@ class Get extends Base
$this->parameters = null;
}
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
+ {
+ $idSite = $this->getIdSite();
+ $goals = API::getInstance()->getGoals($idSite);
+ $reports = Goals::getReportsWithGoalMetrics();
+
+ $page = new Pages($factory, $reports);
+
+ $widgetsList->addWidgetConfigs($page->createGoalsOverviewPage($goals));
+
+ if ($this->isEcommerceEnabled($idSite)) {
+ $widgetsList->addWidgetConfigs($page->createEcommerceOverviewPage());
+ $widgetsList->addWidgetConfigs($page->createEcommerceSalesPage());
+ }
+
+ foreach ($goals as $goal) {
+ $widgetsList->addWidgetConfigs($page->createGoalDetailPage($goal));
+ }
+ }
+
+ private function getIdSite()
+ {
+ return Common::getRequestVar('idSite', null, 'int');
+ }
+
+ private function isEcommerceEnabled($idSite)
+ {
+ if (!Plugin\Manager::getInstance()->isPluginActivated('Ecommerce')) {
+ return false;
+ }
+
+ $site = new Site($idSite);
+ return $site->isEcommerceEnabled();
+ }
+
+ public function configureView(ViewDataTable $view)
+ {
+ if ($view->isViewDataTableId(Sparklines::ID)) {
+ /** @var Sparklines $view */
+ $idSite = $this->getIdSite();
+ $isEcommerceEnabled = $this->isEcommerceEnabled($idSite);
+
+ $idGoal = Common::getRequestVar('idGoal', 0, 'int');
+
+ $formatter = new Formatter();
+ $view->config->filters[] = function (DataTable $table) use ($formatter, $idSite) {
+ $firstRow = $table->getFirstRow();
+ if ($firstRow) {
+ $revenue = $firstRow->getColumn('revenue');
+ $firstRow->setColumn('revenue', $formatter->getPrettyMoney($revenue, $idSite));
+ }
+ };
+
+ $view->config->addTranslations(array(
+ 'nb_visits' => Piwik::translate('VisitsSummary_NbVisitsDescription'),
+ 'nb_conversions' => Piwik::translate('Goals_ConversionsDescription'),
+ 'nb_visits_converted' => Piwik::translate('General_NVisits'),
+ 'conversion_rate' => Piwik::translate('Goals_OverallConversionRate'),
+ 'revenue' => Piwik::translate('Goals_OverallRevenue'),
+ ));
+
+ $allowMultiple = Common::getRequestVar('allow_multiple', 0, 'int');
+
+ if ($allowMultiple) {
+ $view->config->addSparklineMetric(array('nb_conversions', 'nb_visits_converted'), $order = 10);
+ } else {
+ $view->config->addSparklineMetric(array('nb_conversions'), $order = 10);
+ }
+
+ $view->config->addSparklineMetric(array('conversion_rate'), $order = 20);
+
+ if (empty($idGoal)) {
+ // goals overview sparklines below evolution graph
+
+ if ($isEcommerceEnabled) {
+ // this would be ideally done in Ecommerce plugin but then it is hard to keep same order
+ $view->config->addSparklineMetric(array('revenue'), $order = 30);
+ }
+
+ } else {
+ $onlySummary = Common::getRequestVar('only_summary', 0, 'int');
+
+ if ($onlySummary) {
+ // in Goals Overview we list an overview for each goal....
+ $view->config->addTranslation('conversion_rate', Piwik::translate('Goals_ConversionRate'));
+
+ } elseif ($isEcommerceEnabled) {
+ // in Goals detail page...
+ $view->config->addSparklineMetric(array('revenue'), $order = 30);
+ }
+ }
+ } else if ($view->isViewDataTableId(Evolution::ID)) {
+ if (empty($view->config->columns_to_display)) {
+ $view->config->columns_to_display = array('nb_conversions');
+ }
+ }
+ }
+
public function configureReportMetadata(&$availableReports, $infos)
{
if (!$this->isEnabled()) {
diff --git a/plugins/Goals/Widgets.php b/plugins/Goals/Widgets.php
deleted file mode 100644
index 94d91bda5c..0000000000
--- a/plugins/Goals/Widgets.php
+++ /dev/null
@@ -1,39 +0,0 @@
-addWidget('Goals_GoalsOverview', 'widgetGoalsOverview');
-
- $idSite = $this->getIdSite();
- $goals = API::getInstance()->getGoals($idSite);
-
- if (count($goals) > 0) {
- foreach ($goals as $goal) {
- $name = Common::sanitizeInputValue($goal['name']);
- $params = array('idGoal' => $goal['idgoal']);
-
- $this->addWidget($name, 'widgetGoalReport', $params);
- }
- }
- }
-
- private function getIdSite()
- {
- return Common::getRequestVar('idSite', null, 'int');
- }
-
-}
diff --git a/plugins/Goals/Widgets/AddNewGoal.php b/plugins/Goals/Widgets/AddNewGoal.php
new file mode 100644
index 0000000000..b692309b9d
--- /dev/null
+++ b/plugins/Goals/Widgets/AddNewGoal.php
@@ -0,0 +1,38 @@
+getGoals($idSite);
+
+ $config->setCategoryId('Goals_Goals');
+ $config->setSubcategoryId('Goals_AddNewGoal');
+ $config->setParameters(array('idGoal' => ''));
+ $config->setIsNotWidgetizable();
+
+ if (Piwik::isUserHasAdminAccess($idSite)) {
+ $config->setName('Goals_AddNewGoal');
+ } else {
+ $config->setName('Goals_CreateNewGOal');
+ }
+
+ if (count($goals) !== 0) {
+ $config->disable();
+ }
+ }
+}
diff --git a/plugins/Goals/Widgets/EditGoals.php b/plugins/Goals/Widgets/EditGoals.php
new file mode 100644
index 0000000000..d0bb52667c
--- /dev/null
+++ b/plugins/Goals/Widgets/EditGoals.php
@@ -0,0 +1,37 @@
+getGoals($idSite);
+
+ $config->setCategoryId('Goals_Goals');
+ $config->setSubcategoryId('Goals_ManageGoals');
+ $config->setIsNotWidgetizable();
+
+ if (Piwik::isUserHasAdminAccess($idSite)) {
+ $config->setName('Goals_ManageGoals');
+ } else {
+ $config->setName('Goals_CreateNewGOal');
+ }
+
+ if (count($goals) === 0) {
+ $config->disable();
+ }
+ }
+}
diff --git a/plugins/Goals/lang/en.json b/plugins/Goals/lang/en.json
index 0545d0640f..8820b333e7 100644
--- a/plugins/Goals/lang/en.json
+++ b/plugins/Goals/lang/en.json
@@ -36,6 +36,7 @@
"ConversionByTypeReportDocumentation": "This report provides detailed information about the goal performance (conversions, conversion rates and revenue per visit) for each of the categories available in the left panel. %s Please click on one of the categories to view the report. %s For more information, read the %sTracking Goals documentation%s",
"ConversionRate": "%s conversion rate",
"Conversions": "%s conversions",
+ "ConversionsDescription": "conversions",
"ConversionsOverview": "Conversions Overview",
"ConversionsOverviewBy": "Conversions overview by type of visit",
"DaysToConv": "Days to Conversion",
@@ -79,8 +80,8 @@
"NoGoalsNeedAccess": "Only an Administrator or a user with Super User access can manage Goals for a given website. Please ask your Piwik administrator to set up a Goal for your website. Tracking Goals is a great way to help understand and maximize your website performance!",
"NeedAccess": "Only an Administrator or a user with Super User access can manage Goals for a given website.",
"Optional": "(optional)",
- "OverallConversionRate": "%s overall conversion rate (visits with a completed goal)",
- "OverallRevenue": "%s overall revenue",
+ "OverallConversionRate": "overall conversion rate (visits with a completed goal)",
+ "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.",
diff --git a/plugins/Goals/templates/_titleAndEvolutionGraph.twig b/plugins/Goals/templates/_titleAndEvolutionGraph.twig
deleted file mode 100644
index 0a2fdc1e31..0000000000
--- a/plugins/Goals/templates/_titleAndEvolutionGraph.twig
+++ /dev/null
@@ -1,86 +0,0 @@
-
-
-{% if displayFullReport or headline is defined %}
- {% if headline is defined %}{{ headline }}{% elseif goalName is defined %}{{ 'Goals_GoalX'|translate(goalName) }}{% else %}{{ 'General_EvolutionOverPeriod'|translate }}{% endif %}
-{% endif %}
-{{ graphEvolution|raw }}
-
-
-
{{ sparkline(urlSparklineConversions) }}
- {% if ecommerce is defined %}
-
{{ nb_conversions }}
- {{ 'General_EcommerceOrders'|translate }}
-
- {% else %}
- {{ 'Goals_Conversions'|translate("
"~nb_conversions~" ")|raw }}
- {% endif %}
- {% if goalAllowMultipleConversionsPerVisit is defined and goalAllowMultipleConversionsPerVisit %}
- ({{ 'General_NVisits'|translate("
"~nb_visits_converted~" ")|raw }})
- {% endif %}
-
- {% if revenue != 0 or ecommerce is defined %}
-
- {{ sparkline(urlSparklineRevenue) }}
- {% set revenue=revenue|money(idSite) %}
- {% if ecommerce is defined %}
- {{ revenue|raw }} {{ 'General_TotalRevenue'|translate }}
- {% else %}
- {{ 'Goals_OverallRevenue'|translate(""~revenue~" ")|raw }}
- {% endif %}
-
- {% endif %}
- {% if ecommerce is defined %}
-
{{ sparkline(urlSparklineAverageOrderValue) }}
- {{ avg_order_revenue|money(idSite)|raw }}
- {{ 'General_AverageOrderValue'|translate }}
-
- {% endif %}
-
-
-
-
{{ sparkline(urlSparklineConversionRate) }}
- {% if ecommerce is defined %}
- {% set ecommerceOrdersText %}{{ 'General_EcommerceOrders'|translate }}{% endset %}
- {{ 'Goals_ConversionRate'|translate(""~conversion_rate~" "~ecommerceOrdersText)|raw }}
- {% else %}
- {{ 'Goals_OverallConversionRate'|translate(""~conversion_rate~" ")|raw }}
- {% endif %}
-
- {% if ecommerce is defined %}
-
{{ sparkline(urlSparklinePurchasedProducts) }}
- {{ items }} {{ 'General_PurchasedProducts'|translate }}
- {% endif %}
-
-{% if ecommerce is defined %}
-
-
-
{{ 'General_AbandonedCarts'|translate }}
-
-
-
- {{ sparkline(cart_urlSparklineConversions) }}
- {% set ecommerceAbandonedCartsText %}{{ 'Goals_AbandonedCart'|translate }}{% endset %}
- {{ cart_nb_conversions }} {{ 'General_VisitsWith'|translate(ecommerceAbandonedCartsText) }}
-
-
-
- {{ sparkline(cart_urlSparklineRevenue) }}
- {% set revenue %}{{ cart_revenue|money(idSite)|raw }}{% endset %}
- {% set revenueText %}{{ 'General_ColumnRevenue'|translate }}{% endset %}
- {{ revenue }} {{ 'Goals_LeftInCart'|translate(revenueText) }}
-
-
-
- {{ sparkline(cart_urlSparklineConversionRate) }}
- {{ cart_conversion_rate }}
- {{ 'General_VisitsWith'|translate(ecommerceAbandonedCartsText) }}
-
-
-{% endif %}
-{% include "_sparklineFooter.twig" %}
-
diff --git a/plugins/Goals/templates/addNewGoal.twig b/plugins/Goals/templates/addNewGoal.twig
index a2b44b88e9..844892ecbc 100644
--- a/plugins/Goals/templates/addNewGoal.twig
+++ b/plugins/Goals/templates/addNewGoal.twig
@@ -1,5 +1,4 @@
{% if userCanEditGoals %}
- {{ 'Goals_AddNewGoal'|translate }}
{{ 'Goals_NewGoalIntro'|translate }}
{{ 'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate(""," ")|raw }}
{{ 'Goals_ManageGoalsOrCreateANewGoal'|translate(""," ")|raw }}
diff --git a/plugins/Goals/templates/conversionOverview.twig b/plugins/Goals/templates/conversionOverview.twig
new file mode 100644
index 0000000000..2697527eb7
--- /dev/null
+++ b/plugins/Goals/templates/conversionOverview.twig
@@ -0,0 +1,15 @@
+
+ {% if topDimensions.country is defined %}
+ {{ 'Goals_BestCountries'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.country} %}
+ {% endif %}
+ {% if topDimensions.keyword is defined and topDimensions.keyword|length > 0 %}
+ {{ 'Goals_BestKeywords'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.keyword} %}
+ {% endif %}
+ {% if topDimensions.website is defined and topDimensions.website|length > 0 %}
+ {{ 'Goals_BestReferrers'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.website} %}
+ {% endif %}
+
+ {{ 'Goals_ReturningVisitorsConversionRateIs'|translate(""~conversion_rate_returning~" ")|raw }}
+ , {{ 'Goals_NewVisitorsConversionRateIs'|translate(""~conversion_rate_new~" ")|raw }}
+
+
\ No newline at end of file
diff --git a/plugins/Goals/templates/editGoals.twig b/plugins/Goals/templates/editGoals.twig
index 76b64b9918..1bc8956511 100644
--- a/plugins/Goals/templates/editGoals.twig
+++ b/plugins/Goals/templates/editGoals.twig
@@ -1,7 +1,5 @@
{% if userCanEditGoals %}
-
{{ 'Goals_ManageGoals'|translate }}
-
{% include "@Goals/_addEditGoal.twig" %}
{% else %}
diff --git a/plugins/Goals/templates/getGoalReportView.twig b/plugins/Goals/templates/getGoalReportView.twig
deleted file mode 100644
index 2cb3b7f4d0..0000000000
--- a/plugins/Goals/templates/getGoalReportView.twig
+++ /dev/null
@@ -1,66 +0,0 @@
-
-{% include "@Goals/_titleAndEvolutionGraph.twig" | raw %}
-
-
-{% if nb_conversions > 0 %}
-
{{ 'Goals_ConversionsOverview'|translate }}
-
- {% if ecommerce is not defined %}
- {% if topDimensions.country is defined %}
- {{ 'Goals_BestCountries'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.country} %}
- {% endif %}
- {% if topDimensions.keyword is defined and topDimensions.keyword|length > 0 %}
- {{ 'Goals_BestKeywords'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.keyword} %}
- {% endif %}
- {% if topDimensions.website is defined and topDimensions.website|length > 0 %}
- {{ 'Goals_BestReferrers'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.website} %}
- {% endif %}
-
- {{ 'Goals_ReturningVisitorsConversionRateIs'|translate(""~conversion_rate_returning~" ")|raw }}
- , {{ 'Goals_NewVisitorsConversionRateIs'|translate(""~conversion_rate_new~" ")|raw }}
-
- {% else %}
-
- {{ 'General_ColumnRevenue'|translate }}: {{ revenue|money(idSite)|raw -}}
- {% if revenue_subtotal is not empty %},
- {{ 'General_Subtotal'|translate }}: {{ revenue_subtotal|money(idSite)|raw -}}
- {% endif %}
- {%- if revenue_tax is not empty -%},
- {{ 'General_Tax'|translate }}: {{ revenue_tax|money(idSite)|raw -}}
- {% endif %}
- {%- if revenue_shipping is not empty -%},
- {{ 'General_Shipping'|translate }}: {{ revenue_shipping|money(idSite)|raw -}}
- {% endif %}
- {%- if revenue_discount is not empty -%},
- {{ 'General_Discount'|translate }}: {{ revenue_discount|money(idSite)|raw -}}
- {% endif %}
-
- {% endif %}
-
-{% endif %}
-
-
-
-{% if displayFullReport %}
- {% if nb_conversions > 0 or cart_nb_conversions is defined %}
-
- {% if idGoal is defined %}
- {{ 'Goals_GoalConversionsBy'|translate(goalName) }}
- {% else %}
- {{ 'Goals_ConversionsOverviewBy'|translate }}
- {% endif %}
-
- {{ goalReportsByDimension|raw }}
- {% endif %}
-{% endif %}
diff --git a/plugins/Goals/templates/getOverviewView.twig b/plugins/Goals/templates/getOverviewView.twig
deleted file mode 100644
index 909fd01711..0000000000
--- a/plugins/Goals/templates/getOverviewView.twig
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-{% include "@Goals/_titleAndEvolutionGraph.twig" %}
-{% set sum_nb_conversions=nb_conversions %}
-
-{% for goal in goalMetrics %}
- {% set nb_conversions=goal.nb_conversions %}
- {% set nb_visits_converted=goal.nb_visits_converted %}
- {% set conversion_rate=goal.conversion_rate %}
- {% set name=goal.name %}
-
-
-
- {% if not isWidget %}
-
-
- {% endif %}
-
-
{{ sparkline(goal.urlSparklineConversions) }}
- {{ 'Goals_Conversions'|translate(""~nb_conversions~" ")|raw }}
- {% if goal.goalAllowMultipleConversionsPerVisit %}
- ({{ 'General_NVisits'|translate(""~nb_visits_converted~" ") | raw }})
- {% endif %}
-
-
- {% if not isWidget %}
-
-
- {% endif %}
-
-
{{ sparkline(goal.urlSparklineConversionRate) }}
- {{ 'Goals_ConversionRate'|translate(""~conversion_rate~" ")|raw }}
-
-
- {% if not isWidget %}
-
-
- {% endif %}
-
-
-{% endfor %}
-
-{% if displayFullReport %}
- {% if sum_nb_conversions != 0 %}
-
- {% if idGoal is defined %}
- {{ 'Goals_GoalConversionsBy'|translate(goalName) }}
- {% else %}
- {{ 'Goals_ConversionsOverviewBy'|translate }}
- {% endif %}
-
- {{ goalReportsByDimension|raw }}
- {% endif %}
-{% endif %}
diff --git a/plugins/Insights/Widgets.php b/plugins/Insights/Widgets.php
deleted file mode 100644
index 799662deb5..0000000000
--- a/plugins/Insights/Widgets.php
+++ /dev/null
@@ -1,20 +0,0 @@
-addWidget('Insights_OverviewWidgetTitle', 'getInsightsOverview');
- $this->addWidget('Insights_MoversAndShakersWidgetTitle', 'getOverallMoversAndShakers');
- }
-}
diff --git a/plugins/Insights/Widgets/GetInsightsOverview.php b/plugins/Insights/Widgets/GetInsightsOverview.php
new file mode 100644
index 0000000000..e715865a41
--- /dev/null
+++ b/plugins/Insights/Widgets/GetInsightsOverview.php
@@ -0,0 +1,20 @@
+setCategoryId('Insights_WidgetCategory');
+ $config->setName('Insights_OverviewWidgetTitle');
+ }
+}
diff --git a/plugins/Insights/Widgets/GetOverallMoversAndShakers.php b/plugins/Insights/Widgets/GetOverallMoversAndShakers.php
new file mode 100644
index 0000000000..e175e76519
--- /dev/null
+++ b/plugins/Insights/Widgets/GetOverallMoversAndShakers.php
@@ -0,0 +1,20 @@
+setCategoryId('Insights_WidgetCategory');
+ $config->setName('Insights_MoversAndShakersWidgetTitle');
+ }
+}
diff --git a/plugins/Live/Categories/LiveCategory.php b/plugins/Live/Categories/LiveCategory.php
new file mode 100644
index 0000000000..c3b1a52c91
--- /dev/null
+++ b/plugins/Live/Categories/LiveCategory.php
@@ -0,0 +1,17 @@
+category = 'Live!';
+ $this->categoryId = 'Live!';
}
public function configureReportMetadata(&$availableReports, $infos)
diff --git a/plugins/Live/Reports/GetLastVisitsDetails.php b/plugins/Live/Reports/GetLastVisitsDetails.php
index 8cf2268d28..f714989b75 100644
--- a/plugins/Live/Reports/GetLastVisitsDetails.php
+++ b/plugins/Live/Reports/GetLastVisitsDetails.php
@@ -8,10 +8,10 @@
*/
namespace Piwik\Plugins\Live\Reports;
-use Piwik\Menu\MenuReporting;
use Piwik\Plugin\Report;
use Piwik\Plugins\Live\Visualizations\VisitorLog;
-use Piwik\WidgetsList;
+use Piwik\Report\ReportWidgetFactory;
+use Piwik\Widget\WidgetsList;
class GetLastVisitsDetails extends Base
{
@@ -20,8 +20,9 @@ class GetLastVisitsDetails extends Base
protected function init()
{
parent::init();
- $this->widgetTitle = 'Live_VisitorLog';
$this->order = 2;
+ $this->categoryId = 'General_Visitors';
+ $this->subcategoryId = 'Live_VisitorLog';
}
public function getDefaultTypeViewDataTable()
@@ -34,17 +35,14 @@ class GetLastVisitsDetails extends Base
return true;
}
- public function configureReportingMenu(MenuReporting $menu)
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
{
- if ($this->isEnabled()) {
- $url = array('module' => $this->module, 'action' => 'indexVisitorLog');
- $menu->addVisitorsItem($this->widgetTitle, $url, $order = 5);
- }
- }
-
- public function configureWidget(WidgetsList $widget)
- {
- $widget->add($this->category, $this->widgetTitle, $this->module, 'getVisitorLog', array('small' => 1));
+ $widget = $factory->createWidget()
+ ->forceViewDataTable(VisitorLog::ID)
+ ->setName('Live_VisitorLog')
+ ->setOrder(10)
+ ->setParameters(array('small' => 1));
+ $widgetsList->addWidgetConfig($widget);
}
}
diff --git a/plugins/Live/Reports/GetSimpleLastVisitCount.php b/plugins/Live/Reports/GetSimpleLastVisitCount.php
index 9b0db30769..d2f535ff4e 100644
--- a/plugins/Live/Reports/GetSimpleLastVisitCount.php
+++ b/plugins/Live/Reports/GetSimpleLastVisitCount.php
@@ -14,17 +14,24 @@ use Piwik\Piwik;
use Piwik\Plugin\Report;
use Piwik\Plugins\Live\Controller;
use Piwik\API\Request;
+use Piwik\Report\ReportWidgetFactory;
use Piwik\View;
+use Piwik\Widget\WidgetsList;
class GetSimpleLastVisitCount extends Base
{
protected function init()
{
parent::init();
- $this->widgetTitle = 'Live_RealTimeVisitorCount';
$this->order = 3;
}
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
+ {
+ $widget = $factory->createWidget()->setName('Live_RealTimeVisitorCount')->setOrder(15);
+ $widgetsList->addWidgetConfig($widget);
+ }
+
public function render()
{
$lastMinutes = Config::getInstance()->General[Controller::SIMPLE_VISIT_COUNT_WIDGET_LAST_MINUTES_CONFIG_KEY];
diff --git a/plugins/Live/Widgets.php b/plugins/Live/Widgets.php
deleted file mode 100644
index c4c96c53e9..0000000000
--- a/plugins/Live/Widgets.php
+++ /dev/null
@@ -1,27 +0,0 @@
-addWidget('Live_VisitorsInRealTime', 'widget');
-
- // the visitor profile uses a segment that is not accessible to the anonymous user, so don't bother showing this widget
- if (!Piwik::isUserIsAnonymous()) {
- $this->addWidget('Live_VisitorProfile', 'getVisitorProfilePopup');
- }
- }
-
-}
diff --git a/plugins/Live/Widgets/GetVisitorProfilePopup.php b/plugins/Live/Widgets/GetVisitorProfilePopup.php
new file mode 100644
index 0000000000..0d01a7abca
--- /dev/null
+++ b/plugins/Live/Widgets/GetVisitorProfilePopup.php
@@ -0,0 +1,33 @@
+setCategoryId('General_Visitors');
+ $config->setName('Live_VisitorProfile');
+ $config->setOrder(25);
+
+ if (Piwik::isUserIsAnonymous()) {
+ $config->disable();
+ }
+ }
+
+ public function render()
+ {
+
+ }
+
+}
diff --git a/plugins/Live/Widgets/Widget.php b/plugins/Live/Widgets/Widget.php
new file mode 100644
index 0000000000..e058423b66
--- /dev/null
+++ b/plugins/Live/Widgets/Widget.php
@@ -0,0 +1,21 @@
+setCategoryId('Live!');
+ $config->setName('Live_VisitorsInRealTime');
+ $config->setOrder(20);
+ }
+}
diff --git a/plugins/Live/templates/index.twig b/plugins/Live/templates/index.twig
index ced0699bb9..b0a9382b65 100644
--- a/plugins/Live/templates/index.twig
+++ b/plugins/Live/templates/index.twig
@@ -43,7 +43,7 @@
{% if not disableLink %}
-
{{ 'Live_LinkVisitorLog'|translate }}
+
{{ 'Live_LinkVisitorLog'|translate }}
{% endif %}
{% endspaceless %}
diff --git a/plugins/Morpheus/javascripts/piwikHelper.js b/plugins/Morpheus/javascripts/piwikHelper.js
index 163ea90160..cc6ae5dde5 100644
--- a/plugins/Morpheus/javascripts/piwikHelper.js
+++ b/plugins/Morpheus/javascripts/piwikHelper.js
@@ -125,6 +125,10 @@ var piwikHelper = {
compileAngularComponents: function (selector) {
var $element = $(selector);
+ if (!$element || !$element.length) {
+ return;
+ }
+
angular.element(document).injector().invoke(function($compile) {
var scope = angular.element($element).scope();
$compile($element)(scope);
@@ -155,9 +159,9 @@ var piwikHelper = {
var button = {text: text};
if(typeof handles[role] == 'function') {
- button.click = function(){$(this).dialog("close"); handles[role].apply()};
+ button.click = function(){ $(this).dialog("close"); handles[role].apply()};
} else {
- button.click = function(){$(this).dialog("close");};
+ button.click = function(){ $(this).dialog("close");};
}
if (title) {
diff --git a/plugins/Morpheus/stylesheets/ui/_map.less b/plugins/Morpheus/stylesheets/ui/_map.less
index 0446610c2d..731f4e369b 100644
--- a/plugins/Morpheus/stylesheets/ui/_map.less
+++ b/plugins/Morpheus/stylesheets/ui/_map.less
@@ -56,6 +56,10 @@
font-size: 14px;
}
+.uiTest .realTimeMap_datetime {
+ visibility: hidden;
+}
+
.realtime-map[data-name=white-fill] {
color: #f2f2f2 !important;
}
diff --git a/plugins/Morpheus/templates/layout.twig b/plugins/Morpheus/templates/layout.twig
index 1e9f1f0d5c..87cb863356 100644
--- a/plugins/Morpheus/templates/layout.twig
+++ b/plugins/Morpheus/templates/layout.twig
@@ -47,6 +47,8 @@
{% endblock %}
+
+
{% endblock %}
+
{{ content|raw }}
diff --git a/plugins/Widgetize/templates/index.twig b/plugins/Widgetize/templates/index.twig
index 49a2b5f6c1..a798723928 100644
--- a/plugins/Widgetize/templates/index.twig
+++ b/plugins/Widgetize/templates/index.twig
@@ -24,7 +24,6 @@
$('#widgetPreview').widgetPreview({
onPreviewLoaded: widgetized.callbackAddExportButtonsUnderWidget
});
- broadcast.init();
});
diff --git a/plugins/Widgetize/tests/System/WidgetTest.php b/plugins/Widgetize/tests/System/WidgetTest.php
index d2d6ec3260..2425ed3c9a 100644
--- a/plugins/Widgetize/tests/System/WidgetTest.php
+++ b/plugins/Widgetize/tests/System/WidgetTest.php
@@ -11,10 +11,11 @@ namespace Piwik\Plugins\Widgetize\tests\Integration;
use Piwik\Container\StaticContainer;
use Piwik\Http\ControllerResolver;
use Piwik\Piwik;
+use Piwik\Plugins\API;
use Piwik\Plugins\Goals;
use Piwik\Plugins\Widgetize\tests\Fixtures\WidgetizeFixture;
use Piwik\Tests\Framework\TestCase\SystemTestCase;
-use Piwik\WidgetsList;
+use Piwik\Widget\WidgetsList;
/**
* @group Widgetize
@@ -35,7 +36,7 @@ class WidgetTest extends SystemTestCase
$_GET = array();
$_GET['idSite'] = self::$fixture->idSite;
$_GET['period'] = 'year';
- $_GET['date'] = 'today';
+ $_GET['date'] = '2013-01-23';
}
public function tearDown()
@@ -44,28 +45,35 @@ class WidgetTest extends SystemTestCase
parent::tearDown();
}
+ public function test_allWidgetUniqueIdsAreActuallyUnique()
+ {
+ $uniqueIds = array();
+ foreach (WidgetsList::get()->getWidgetConfigs() as $widget) {
+ $uniqueIds[] = $widget->getUniqueId();
+ }
+
+ $this->assertEquals(array_unique($uniqueIds), $uniqueIds);
+ }
+
public function test_AvailableWidgetListIsUpToDate()
{
- $namesOfWidgetsThatAreAPI = $this->getWidgetNames($this->getWidgetsThatAreAPI());
+ $namesOfWidgetsThatAreAPI = array_map(function ($widget) {
+ return $widget['uniqueId'];
+ }, $this->getWidgetsThatAreAPICurrently());
Piwik::postEvent('Platform.initialized'); // userCountryMap defines it's Widgets via this event currently
- $currentWidgetNames = array();
- foreach (WidgetsList::get() as $widgets) {
- $currentWidgetNames = array_merge($this->getWidgetNames($widgets), $currentWidgetNames);
- }
+ $widgets = API\API::getInstance()->getWidgetMetadata($_GET['idSite']);
- $allWidgetNames = array_merge($namesOfWidgetsThatAreAPI, $currentWidgetNames);
- $regressedWidgetNames = array_diff($allWidgetNames, $currentWidgetNames);
+ $currentUniqueIds = array();
+ foreach ($widgets as $widget) {
+ $currentUniqueIds[] = $widget['uniqueId'];
+ }
- $this->assertEmpty($regressedWidgetNames, 'The widgets list is no longer up to date. If you added, removed or renamed a widget please update `getAvailableWidgets()` otherwise you will need to fix it. Different names: ' . var_export($regressedWidgetNames, 1));
- }
+ $allWidgetNames = array_merge($namesOfWidgetsThatAreAPI, $currentUniqueIds);
+ $regressedWidgetIds = array_diff($allWidgetNames, $currentUniqueIds);
- private function getWidgetNames($widgets)
- {
- return array_map(function ($widget) {
- return $widget['name'];
- }, $widgets);
+ $this->assertEmpty($regressedWidgetIds, 'The widgets list is no longer up to date. If you added or changed a widget please update `getWidgetsThatAreAPICurrently()`, if you removed a widget please add it to `getWidgetsThatAreDeprecatedButStillAPI()`. If the uniqueId changed you might need to create an update for Dashboards and Scheduled Reports! Different names: ' . var_export($regressedWidgetIds, 1));
}
/**
@@ -78,7 +86,8 @@ class WidgetTest extends SystemTestCase
$params = $widget['parameters'];
$parameters = array();
- $resolver = new ControllerResolver(StaticContainer::getContainer());
+ /** @var ControllerResolver $resolver */
+ $resolver = StaticContainer::get('Piwik\Http\ControllerResolver');
$controller = $resolver->getController($params['module'], $params['action'], $parameters);
$this->assertNotEmpty($controller, $widget['name'] . ' is not renderable with following params: ' . json_encode($params) . '. This breaks the API, please make sure to keep the URL working');
@@ -86,11 +95,15 @@ class WidgetTest extends SystemTestCase
public function availableWidgetsProvider()
{
- $widgets = $this->getWidgetsThatAreAPI();
-
$data = array();
- foreach ($widgets as $widget) {
+ foreach ($this->getWidgetsThatAreAPICurrently() as $widget) {
+ if (!empty($widget)) {
+ $data[] = array($widget);
+ }
+ }
+
+ foreach ($this->getWidgetsThatAreDeprecatedButStillAPI() as $widget) {
if (!empty($widget)) {
$data[] = array($widget);
}
@@ -99,746 +112,1163 @@ class WidgetTest extends SystemTestCase
return $data;
}
+ public function getWidgetsThatAreAPICurrently()
+ {
+ return array(
+ array (
+ 'name' => 'Visits Overview (with graph)',
+ 'uniqueId' => 'widgetVisitOverviewWithGraph',
+ 'parameters' =>
+ array (
+ 'module' => 'CoreHome',
+ 'action' => 'renderWidgetContainer',
+ 'containerId' => 'VisitOverviewWithGraph',
+ ),
+ ),array (
+ 'name' => 'Support Piwik!',
+ 'uniqueId' => 'widgetCoreHomegetDonateForm',
+ 'parameters' =>
+ array (
+ 'module' => 'CoreHome',
+ 'action' => 'getDonateForm',
+ ),
+ ),array (
+ 'name' => 'Welcome!',
+ 'uniqueId' => 'widgetCoreHomegetPromoVideo',
+ 'parameters' =>
+ array (
+ 'module' => 'CoreHome',
+ 'action' => 'getPromoVideo',
+ ),
+ ),array (
+ 'name' => 'Example Widget Name',
+ 'uniqueId' => 'widgetExamplePluginmyExampleWidget',
+ 'parameters' =>
+ array (
+ 'module' => 'ExamplePlugin',
+ 'action' => 'myExampleWidget',
+ ),
+ ),array (
+ 'name' => 'Top Keywords for Page URL',
+ 'uniqueId' => 'widgetReferrersgetKeywordsForPage',
+ 'parameters' =>
+ array (
+ 'module' => 'Referrers',
+ 'action' => 'getKeywordsForPage',
+ ),
+ ),array (
+ 'name' => 'Ecommerce Log',
+ 'uniqueId' => 'widgetEcommercegetEcommerceLog',
+ 'parameters' =>
+ array (
+ 'module' => 'Ecommerce',
+ 'action' => 'getEcommerceLog',
+ ),
+ ),array (
+ 'name' => 'SEO Rankings',
+ 'uniqueId' => 'widgetSEOgetRank',
+ 'parameters' =>
+ array (
+ 'module' => 'SEO',
+ 'action' => 'getRank',
+ ),
+ ),array (
+ 'name' => 'Piwik Changelog',
+ 'uniqueId' => 'widgetExampleRssWidgetrssChangelog',
+ 'parameters' =>
+ array (
+ 'module' => 'ExampleRssWidget',
+ 'action' => 'rssChangelog',
+ ),
+ ),array (
+ 'name' => 'Piwik.org Blog',
+ 'uniqueId' => 'widgetExampleRssWidgetrssPiwik',
+ 'parameters' =>
+ array (
+ 'module' => 'ExampleRssWidget',
+ 'action' => 'rssPiwik',
+ ),
+ ),array (
+ 'name' => 'Real-time Map',
+ 'uniqueId' => 'widgetUserCountryMaprealtimeMap',
+ 'parameters' =>
+ array (
+ 'module' => 'UserCountryMap',
+ 'action' => 'realtimeMap',
+ ),
+ ),array (
+ 'name' => 'Visitor Map',
+ 'uniqueId' => 'widgetUserCountryMapvisitorMap',
+ 'parameters' =>
+ array (
+ 'module' => 'UserCountryMap',
+ 'action' => 'visitorMap',
+ ),
+ ),array (
+ 'name' => 'Visitor profile',
+ 'uniqueId' => 'widgetLivegetVisitorProfilePopup',
+ 'parameters' =>
+ array (
+ 'module' => 'Live',
+ 'action' => 'getVisitorProfilePopup',
+ ),
+ ),array (
+ 'name' => 'Visitors in Real-time',
+ 'uniqueId' => 'widgetLivewidget',
+ 'parameters' =>
+ array (
+ 'module' => 'Live',
+ 'action' => 'widget',
+ ),
+ ),array (
+ 'name' => 'Insights Overview',
+ 'uniqueId' => 'widgetInsightsgetInsightsOverview',
+ 'parameters' =>
+ array (
+ 'module' => 'Insights',
+ 'action' => 'getInsightsOverview',
+ ),
+ ),array (
+ 'name' => 'Movers and Shakers',
+ 'uniqueId' => 'widgetInsightsgetOverallMoversAndShakers',
+ 'parameters' =>
+ array (
+ 'module' => 'Insights',
+ 'action' => 'getOverallMoversAndShakers',
+ ),
+ ),array (
+ 'name' => 'Real Time Visitor Count',
+ 'uniqueId' => 'widgetLivegetSimpleLastVisitCount',
+ 'parameters' =>
+ array (
+ 'module' => 'Live',
+ 'action' => 'getSimpleLastVisitCount',
+ ),
+ ),array (
+ 'name' => 'Visits Over Time',
+ 'uniqueId' => 'widgetVisitsSummarygetEvolutionGraphforceView1viewDataTablegraphEvolution',
+ 'parameters' =>
+ array (
+ 'forceView' => 1,
+ 'viewDataTable' => 'graphEvolution',
+ 'module' => 'VisitsSummary',
+ 'action' => 'getEvolutionGraph',
+ ),
+ ),array (
+ 'name' => 'Visits Overview',
+ 'uniqueId' => 'widgetVisitsSummarygetforceView1viewDataTablesparklines',
+ 'parameters' =>
+ array (
+ 'forceView' => 1,
+ 'viewDataTable' => 'sparklines',
+ 'module' => 'VisitsSummary',
+ 'action' => 'get',
+ ),
+ ),array (
+ 'name' => 'Visitor Log',
+ 'uniqueId' => 'widgetLivegetLastVisitsDetailsforceView1viewDataTablePiwik%5CPlugins%5CLive%5CVisitorLogsmall1',
+ 'parameters' =>
+ array (
+ 'forceView' => 1,
+ 'viewDataTable' => 'Piwik\\Plugins\\Live\\VisitorLog',
+ 'module' => 'Live',
+ 'action' => 'getLastVisitsDetails',
+ 'small' => 1,
+ ),
+ ),array (
+ 'name' => 'Custom Variables',
+ 'uniqueId' => 'widgetCustomVariablesgetCustomVariables',
+ 'parameters' =>
+ array (
+ 'module' => 'CustomVariables',
+ 'action' => 'getCustomVariables',
+ ),
+ ),array (
+ 'name' => 'Device type',
+ 'uniqueId' => 'widgetDevicesDetectiongetType',
+ 'parameters' =>
+ array (
+ 'module' => 'DevicesDetection',
+ 'action' => 'getType',
+ ),
+ ),array (
+ 'name' => 'Device model',
+ 'uniqueId' => 'widgetDevicesDetectiongetModel',
+ 'parameters' =>
+ array (
+ 'module' => 'DevicesDetection',
+ 'action' => 'getModel',
+ ),
+ ),array (
+ 'name' => 'Device brand',
+ 'uniqueId' => 'widgetDevicesDetectiongetBrand',
+ 'parameters' =>
+ array (
+ 'module' => 'DevicesDetection',
+ 'action' => 'getBrand',
+ ),
+ ),array (
+ 'name' => 'Screen Resolution',
+ 'uniqueId' => 'widgetResolutiongetResolution',
+ 'parameters' =>
+ array (
+ 'module' => 'Resolution',
+ 'action' => 'getResolution',
+ ),
+ ),array (
+ 'name' => 'Operating System versions',
+ 'uniqueId' => 'widgetDevicesDetectiongetOsVersions',
+ 'parameters' =>
+ array (
+ 'module' => 'DevicesDetection',
+ 'action' => 'getOsVersions',
+ ),
+ ),array (
+ 'name' => 'Browsers',
+ 'uniqueId' => 'widgetDevicesDetectiongetBrowsers',
+ 'parameters' =>
+ array (
+ 'module' => 'DevicesDetection',
+ 'action' => 'getBrowsers',
+ ),
+ ),array (
+ 'name' => 'Browser version',
+ 'uniqueId' => 'widgetDevicesDetectiongetBrowserVersions',
+ 'parameters' =>
+ array (
+ 'module' => 'DevicesDetection',
+ 'action' => 'getBrowserVersions',
+ ),
+ ),array (
+ 'name' => 'Configurations',
+ 'uniqueId' => 'widgetResolutiongetConfiguration',
+ 'parameters' =>
+ array (
+ 'module' => 'Resolution',
+ 'action' => 'getConfiguration',
+ ),
+ ),array (
+ 'name' => 'Operating System families',
+ 'uniqueId' => 'widgetDevicesDetectiongetOsFamilies',
+ 'parameters' =>
+ array (
+ 'module' => 'DevicesDetection',
+ 'action' => 'getOsFamilies',
+ ),
+ ),array (
+ 'name' => 'Browser engines',
+ 'uniqueId' => 'widgetDevicesDetectiongetBrowserEnginesviewDataTablegraphPie',
+ 'parameters' =>
+ array (
+ 'viewDataTable' => 'graphPie',
+ 'module' => 'DevicesDetection',
+ 'action' => 'getBrowserEngines',
+ ),
+ ),array (
+ 'name' => 'Browser Plugins',
+ 'uniqueId' => 'widgetDevicePluginsgetPlugin',
+ 'parameters' =>
+ array (
+ 'module' => 'DevicePlugins',
+ 'action' => 'getPlugin',
+ ),
+ ),array (
+ 'name' => 'Country',
+ 'uniqueId' => 'widgetUserCountrygetCountry',
+ 'parameters' =>
+ array (
+ 'module' => 'UserCountry',
+ 'action' => 'getCountry',
+ ),
+ ),array (
+ 'name' => 'Region',
+ 'uniqueId' => 'widgetUserCountrygetRegion',
+ 'parameters' =>
+ array (
+ 'module' => 'UserCountry',
+ 'action' => 'getRegion',
+ ),
+ ),array (
+ 'name' => 'Browser language',
+ 'uniqueId' => 'widgetUserLanguagegetLanguage',
+ 'parameters' =>
+ array (
+ 'module' => 'UserLanguage',
+ 'action' => 'getLanguage',
+ ),
+ ),array (
+ 'name' => 'City',
+ 'uniqueId' => 'widgetUserCountrygetCity',
+ 'parameters' =>
+ array (
+ 'module' => 'UserCountry',
+ 'action' => 'getCity',
+ ),
+ ),array (
+ 'name' => 'Language code',
+ 'uniqueId' => 'widgetUserLanguagegetLanguageCode',
+ 'parameters' =>
+ array (
+ 'module' => 'UserLanguage',
+ 'action' => 'getLanguageCode',
+ ),
+ ),array (
+ 'name' => 'Visits per visit duration',
+ 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerVisitDurationviewDataTablecloud',
+ 'parameters' =>
+ array (
+ 'viewDataTable' => 'cloud',
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsPerVisitDuration',
+ ),
+ ),array (
+ 'name' => 'Visits per number of pages',
+ 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerPageviewDataTablecloud',
+ 'parameters' =>
+ array (
+ 'viewDataTable' => 'cloud',
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsPerPage',
+ ),
+ ),array (
+ 'name' => 'Visits by Visit Number',
+ 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsByVisitCount',
+ 'parameters' =>
+ array (
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsByVisitCount',
+ ),
+ ),array (
+ 'name' => 'Visits by Days Since Last Visit',
+ 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsByDaysSinceLast',
+ 'parameters' =>
+ array (
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsByDaysSinceLast',
+ ),
+ ),array (
+ 'name' => 'Returning Visits Over Time',
+ 'uniqueId' => 'widgetVisitFrequencygetEvolutionGraphforceView1viewDataTablegraphEvolution',
+ 'parameters' =>
+ array (
+ 'forceView' => 1,
+ 'viewDataTable' => 'graphEvolution',
+ 'module' => 'VisitFrequency',
+ 'action' => 'getEvolutionGraph',
+ ),
+ ),array (
+ 'name' => 'Frequency Overview',
+ 'uniqueId' => 'widgetVisitFrequencygetforceView1viewDataTablesparklines',
+ 'parameters' =>
+ array (
+ 'forceView' => 1,
+ 'viewDataTable' => 'sparklines',
+ 'module' => 'VisitFrequency',
+ 'action' => 'get',
+ ),
+ ),array (
+ 'name' => 'Visits per local time',
+ 'uniqueId' => 'widgetVisitTimegetVisitInformationPerLocalTimeviewDataTablegraphVerticalBar',
+ 'parameters' =>
+ array (
+ 'viewDataTable' => 'graphVerticalBar',
+ 'module' => 'VisitTime',
+ 'action' => 'getVisitInformationPerLocalTime',
+ ),
+ ),array (
+ 'name' => 'Visits per server time',
+ 'uniqueId' => 'widgetVisitTimegetVisitInformationPerServerTimeviewDataTablegraphVerticalBar',
+ 'parameters' =>
+ array (
+ 'viewDataTable' => 'graphVerticalBar',
+ 'module' => 'VisitTime',
+ 'action' => 'getVisitInformationPerServerTime',
+ ),
+ ),array (
+ 'name' => 'Visits by Day of Week',
+ 'uniqueId' => 'widgetVisitTimegetByDayOfWeekviewDataTablegraphVerticalBar',
+ 'parameters' =>
+ array (
+ 'viewDataTable' => 'graphVerticalBar',
+ 'module' => 'VisitTime',
+ 'action' => 'getByDayOfWeek',
+ ),
+ ),array (
+ 'name' => 'Pages',
+ 'uniqueId' => 'widgetActionsgetPageUrls',
+ 'parameters' =>
+ array (
+ 'module' => 'Actions',
+ 'action' => 'getPageUrls',
+ ),
+ ),array (
+ 'name' => 'Entry pages',
+ 'uniqueId' => 'widgetActionsgetEntryPageUrls',
+ 'parameters' =>
+ array (
+ 'module' => 'Actions',
+ 'action' => 'getEntryPageUrls',
+ ),
+ ),array (
+ 'name' => 'Exit pages',
+ 'uniqueId' => 'widgetActionsgetExitPageUrls',
+ 'parameters' =>
+ array (
+ 'module' => 'Actions',
+ 'action' => 'getExitPageUrls',
+ ),
+ ),array (
+ 'name' => 'Page titles',
+ 'uniqueId' => 'widgetActionsgetPageTitles',
+ 'parameters' =>
+ array (
+ 'module' => 'Actions',
+ 'action' => 'getPageTitles',
+ ),
+ ),array (
+ 'name' => 'Site Search Keywords',
+ 'uniqueId' => 'widgetActionsgetSiteSearchKeywords',
+ 'parameters' =>
+ array (
+ 'module' => 'Actions',
+ 'action' => 'getSiteSearchKeywords',
+ ),
+ ),array (
+ 'name' => 'Pages Following a Site Search',
+ 'uniqueId' => 'widgetActionsgetPageUrlsFollowingSiteSearch',
+ 'parameters' =>
+ array (
+ 'module' => 'Actions',
+ 'action' => 'getPageUrlsFollowingSiteSearch',
+ ),
+ ),array (
+ 'name' => 'Search Keywords with No Results',
+ 'uniqueId' => 'widgetActionsgetSiteSearchNoResultKeywords',
+ 'parameters' =>
+ array (
+ 'module' => 'Actions',
+ 'action' => 'getSiteSearchNoResultKeywords',
+ ),
+ ),array (
+ 'name' => 'Page Titles Following a Site Search',
+ 'uniqueId' => 'widgetActionsgetPageTitlesFollowingSiteSearch',
+ 'parameters' =>
+ array (
+ 'module' => 'Actions',
+ 'action' => 'getPageTitlesFollowingSiteSearch',
+ ),
+ ),array (
+ 'name' => 'Search Categories',
+ 'uniqueId' => 'widgetActionsgetSiteSearchCategories',
+ 'parameters' =>
+ array (
+ 'module' => 'Actions',
+ 'action' => 'getSiteSearchCategories',
+ ),
+ ),array (
+ 'name' => 'Outlinks',
+ 'uniqueId' => 'widgetActionsgetOutlinks',
+ 'parameters' =>
+ array (
+ 'module' => 'Actions',
+ 'action' => 'getOutlinks',
+ ),
+ ),array (
+ 'name' => 'Downloads',
+ 'uniqueId' => 'widgetActionsgetDownloads',
+ 'parameters' =>
+ array (
+ 'module' => 'Actions',
+ 'action' => 'getDownloads',
+ ),
+ ),array (
+ 'name' => 'Entry Page Titles',
+ 'uniqueId' => 'widgetActionsgetEntryPageTitles',
+ 'parameters' =>
+ array (
+ 'module' => 'Actions',
+ 'action' => 'getEntryPageTitles',
+ ),
+ ),array (
+ 'name' => 'Exit page titles',
+ 'uniqueId' => 'widgetActionsgetExitPageTitles',
+ 'parameters' =>
+ array (
+ 'module' => 'Actions',
+ 'action' => 'getExitPageTitles',
+ ),
+ ),array (
+ 'name' => 'Referrer Types',
+ 'uniqueId' => 'widgetReferrersgetReferrerTypeviewDataTabletableAllColumns',
+ 'parameters' =>
+ array (
+ 'viewDataTable' => 'tableAllColumns',
+ 'module' => 'Referrers',
+ 'action' => 'getReferrerType',
+ ),
+ ),array (
+ 'name' => 'Referrers',
+ 'uniqueId' => 'widgetReferrersgetAllviewDataTabletableAllColumns',
+ 'parameters' =>
+ array (
+ 'viewDataTable' => 'tableAllColumns',
+ 'module' => 'Referrers',
+ 'action' => 'getAll',
+ ),
+ ),array (
+ 'name' => 'Keywords',
+ 'uniqueId' => 'widgetReferrersgetKeywords',
+ 'parameters' =>
+ array (
+ 'module' => 'Referrers',
+ 'action' => 'getKeywords',
+ ),
+ ),array (
+ 'name' => 'Search Engines',
+ 'uniqueId' => 'widgetReferrersgetSearchEngines',
+ 'parameters' =>
+ array (
+ 'module' => 'Referrers',
+ 'action' => 'getSearchEngines',
+ ),
+ ),array (
+ 'name' => 'Websites',
+ 'uniqueId' => 'widgetReferrersgetWebsites',
+ 'parameters' =>
+ array (
+ 'module' => 'Referrers',
+ 'action' => 'getWebsites',
+ ),
+ ),array (
+ 'name' => 'Social Networks',
+ 'uniqueId' => 'widgetReferrersgetSocialsviewDataTablegraphPie',
+ 'parameters' =>
+ array (
+ 'viewDataTable' => 'graphPie',
+ 'module' => 'Referrers',
+ 'action' => 'getSocials',
+ ),
+ ),array (
+ 'name' => 'Campaigns',
+ 'uniqueId' => 'widgetReferrersgetCampaigns',
+ 'parameters' =>
+ array (
+ 'module' => 'Referrers',
+ 'action' => 'getCampaigns',
+ ),
+ ),array (
+ 'name' => 'Overview',
+ 'uniqueId' => 'widgetGoalsOverview',
+ 'parameters' =>
+ array (
+ 'module' => 'CoreHome',
+ 'action' => 'renderWidgetContainer',
+ 'containerId' => 'GoalsOverview',
+ ),
+ ),array (
+ 'name' => 'Overview',
+ 'uniqueId' => 'widgetEcommerceOverview',
+ 'parameters' =>
+ array (
+ 'module' => 'CoreHome',
+ 'action' => 'renderWidgetContainer',
+ 'containerId' => 'EcommerceOverview',
+ ),
+ ),array (
+ 'name' => 'Download Software',
+ 'uniqueId' => 'widgetGoal_1',
+ 'parameters' =>
+ array (
+ 'module' => 'CoreHome',
+ 'action' => 'renderWidgetContainer',
+ 'containerId' => 'Goal_1',
+ ),
+ ),array (
+ 'name' => 'Download Software2',
+ 'uniqueId' => 'widgetGoal_2',
+ 'parameters' =>
+ array (
+ 'module' => 'CoreHome',
+ 'action' => 'renderWidgetContainer',
+ 'containerId' => 'Goal_2',
+ ),
+ ),array (
+ 'name' => 'Opens Contact Form',
+ 'uniqueId' => 'widgetGoal_3',
+ 'parameters' =>
+ array (
+ 'module' => 'CoreHome',
+ 'action' => 'renderWidgetContainer',
+ 'containerId' => 'Goal_3',
+ ),
+ ),array (
+ 'name' => 'Visit Docs',
+ 'uniqueId' => 'widgetGoal_4',
+ 'parameters' =>
+ array (
+ 'module' => 'CoreHome',
+ 'action' => 'renderWidgetContainer',
+ 'containerId' => 'Goal_4',
+ ),
+ ),array (
+ 'name' => 'Data tables',
+ 'uniqueId' => 'widgetExampleUIgetTemperatures',
+ 'parameters' =>
+ array (
+ 'module' => 'ExampleUI',
+ 'action' => 'getTemperatures',
+ ),
+ ),array (
+ 'name' => 'Data tables',
+ 'uniqueId' => 'widgetExampleUIgetTemperaturesforceView1viewDataTablegraphVerticalBar',
+ 'parameters' =>
+ array (
+ 'forceView' => 1,
+ 'viewDataTable' => 'graphVerticalBar',
+ 'module' => 'ExampleUI',
+ 'action' => 'getTemperatures',
+ ),
+ ),array (
+ 'name' => 'Treemap example',
+ 'uniqueId' => 'widgetExampleUIgetTemperaturesforceView1viewDataTableinfoviz-treemap',
+ 'parameters' =>
+ array (
+ 'forceView' => 1,
+ 'viewDataTable' => 'infoviz-treemap',
+ 'module' => 'ExampleUI',
+ 'action' => 'getTemperatures',
+ ),
+ ),array (
+ 'name' => 'Temperatures evolution over time',
+ 'uniqueId' => 'widgetExampleUIgetTemperaturesEvolutionforceView1viewDataTablesparklines',
+ 'parameters' =>
+ array (
+ 'forceView' => 1,
+ 'viewDataTable' => 'sparklines',
+ 'module' => 'ExampleUI',
+ 'action' => 'getTemperaturesEvolution',
+ ),
+ ),array (
+ 'name' => 'Evolution of server temperatures over the last few days',
+ 'uniqueId' => 'widgetExampleUIgetTemperaturesEvolutionforceView1viewDataTablegraphEvolutioncolumnsArray',
+ 'parameters' =>
+ array (
+ 'forceView' => 1,
+ 'viewDataTable' => 'graphEvolution',
+ 'module' => 'ExampleUI',
+ 'action' => 'getTemperaturesEvolution',
+ 'columns' =>
+ array (
+ 0 => 'server1',
+ 1 => 'server2',
+ ),
+ ),
+ ),array (
+ 'name' => 'Pie graph',
+ 'uniqueId' => 'widgetExampleUIgetPlanetRatiosviewDataTablegraphPie',
+ 'parameters' =>
+ array (
+ 'viewDataTable' => 'graphPie',
+ 'module' => 'ExampleUI',
+ 'action' => 'getPlanetRatios',
+ ),
+ ),array (
+ 'name' => 'Simple tag cloud',
+ 'uniqueId' => 'widgetExampleUIgetPlanetRatiosforceView1viewDataTablecloud',
+ 'parameters' =>
+ array (
+ 'forceView' => 1,
+ 'viewDataTable' => 'cloud',
+ 'module' => 'ExampleUI',
+ 'action' => 'getPlanetRatios',
+ ),
+ ),array (
+ 'name' => 'Advanced tag cloud: with logos and links',
+ 'uniqueId' => 'widgetExampleUIgetPlanetRatiosWithLogosviewDataTablecloud',
+ 'parameters' =>
+ array (
+ 'viewDataTable' => 'cloud',
+ 'module' => 'ExampleUI',
+ 'action' => 'getPlanetRatiosWithLogos',
+ )
+ ),array (
+ 'name' => 'Continent',
+ 'uniqueId' => 'widgetUserCountrygetContinent',
+ 'parameters' =>
+ array (
+ 'module' => 'UserCountry',
+ 'action' => 'getContinent',
+ ),
+ ), array (
+ 'name' => 'Event Categories',
+ 'uniqueId' => 'widgetEventsgetCategorysecondaryDimensioneventAction',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getCategory',
+ 'secondaryDimension' => 'eventAction',
+ ),
+ ), array (
+ 'name' => 'Event Categories',
+ 'uniqueId' => 'widgetEventsgetCategorysecondaryDimensioneventAction',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getCategory',
+ 'secondaryDimension' => 'eventAction',
+ ),
+ ), array (
+ 'name' => 'Event Actions',
+ 'uniqueId' => 'widgetEventsgetActionsecondaryDimensioneventName',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getAction',
+ 'secondaryDimension' => 'eventName',
+ ),
+ ), array (
+ 'name' => 'Event Actions',
+ 'uniqueId' => 'widgetEventsgetActionsecondaryDimensioneventName',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getAction',
+ 'secondaryDimension' => 'eventName',
+ ),
+ ), array (
+ 'name' => 'Event Actions',
+ 'uniqueId' => 'widgetEventsgetActionsecondaryDimensioneventName',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getAction',
+ 'secondaryDimension' => 'eventName',
+ ),
+ ), array (
+ 'name' => 'Event Actions',
+ 'uniqueId' => 'widgetEventsgetActionsecondaryDimensioneventName',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getAction',
+ 'secondaryDimension' => 'eventName',
+ ),
+ ), array (
+ 'name' => 'Event Names',
+ 'uniqueId' => 'widgetEventsgetNamesecondaryDimensioneventAction',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getName',
+ 'secondaryDimension' => 'eventAction',
+ ),
+ ), array (
+ 'name' => 'Event Names',
+ 'uniqueId' => 'widgetEventsgetNamesecondaryDimensioneventAction',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getName',
+ 'secondaryDimension' => 'eventAction',
+ ),
+ ), array (
+ 'name' => 'Event Names',
+ 'uniqueId' => 'widgetEventsgetNamesecondaryDimensioneventAction',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getName',
+ 'secondaryDimension' => 'eventAction',
+ ),
+ ), array (
+ 'name' => 'Event Names',
+ 'uniqueId' => 'widgetEventsgetNamesecondaryDimensioneventAction',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getName',
+ 'secondaryDimension' => 'eventAction',
+ ),
+ ), array (
+ 'name' => 'Event Categories',
+ 'uniqueId' => 'widgetEventsgetCategorysecondaryDimensioneventAction',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getCategory',
+ 'secondaryDimension' => 'eventAction',
+ ),
+ ), array (
+ 'name' => 'Event Categories',
+ 'uniqueId' => 'widgetEventsgetCategorysecondaryDimensioneventAction',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getCategory',
+ 'secondaryDimension' => 'eventAction',
+ ),
+ ), array (
+ 'name' => 'Content Piece',
+ 'uniqueId' => 'widgetContentsgetContentPieces',
+ 'parameters' =>
+ array (
+ 'module' => 'Contents',
+ 'action' => 'getContentPieces',
+ ),
+ ), array (
+ 'name' => 'Content Piece',
+ 'uniqueId' => 'widgetContentsgetContentPieces',
+ 'parameters' =>
+ array (
+ 'module' => 'Contents',
+ 'action' => 'getContentPieces',
+ ),
+ ), array (
+ 'name' => 'Content Name',
+ 'uniqueId' => 'widgetContentsgetContentNames',
+ 'parameters' =>
+ array (
+ 'module' => 'Contents',
+ 'action' => 'getContentNames',
+ ),
+ ), array (
+ 'name' => 'Content Name',
+ 'uniqueId' => 'widgetContentsgetContentNames',
+ 'parameters' =>
+ array (
+ 'module' => 'Contents',
+ 'action' => 'getContentNames',
+ ),
+ ), array (
+ 'name' => 'Content Name',
+ 'uniqueId' => 'widgetContentsgetContentNames',
+ 'parameters' =>
+ array (
+ 'module' => 'Contents',
+ 'action' => 'getContentNames',
+ ),
+ ), array (
+ 'name' => 'Content Name',
+ 'uniqueId' => 'widgetContentsgetContentNames',
+ 'parameters' =>
+ array (
+ 'module' => 'Contents',
+ 'action' => 'getContentNames',
+ ),
+ ), array (
+ 'name' => 'Content Piece',
+ 'uniqueId' => 'widgetContentsgetContentPieces',
+ 'parameters' =>
+ array (
+ 'module' => 'Contents',
+ 'action' => 'getContentPieces',
+ ),
+ ), array (
+ 'name' => 'Content Piece',
+ 'uniqueId' => 'widgetContentsgetContentPieces',
+ 'parameters' =>
+ array (
+ 'module' => 'Contents',
+ 'action' => 'getContentPieces',
+ ),
+ ), array (
+ 'name' => 'Product SKU',
+ 'uniqueId' => 'widgetGoalsgetItemsSku',
+ 'parameters' =>
+ array (
+ 'module' => 'Goals',
+ 'action' => 'getItemsSku',
+ ),
+ ), array (
+ 'name' => 'Product SKU',
+ 'uniqueId' => 'widgetGoalsgetItemsSku',
+ 'parameters' =>
+ array (
+ 'module' => 'Goals',
+ 'action' => 'getItemsSku',
+ ),
+ ), array (
+ 'name' => 'Product Category',
+ 'uniqueId' => 'widgetGoalsgetItemsCategory',
+ 'parameters' =>
+ array (
+ 'module' => 'Goals',
+ 'action' => 'getItemsCategory',
+ ),
+ ),
+ );
+ }
+
/**
* This is a list of all widgets that we consider API. We need to make sure the widgets will be still renderable
* etc.
* @return array
*/
- public function getWidgetsThatAreAPI()
+ public function getWidgetsThatAreDeprecatedButStillAPI()
{
- return array (
- array (
- 'name' => 'Visits by Server Time',
- 'uniqueId' => 'widgetVisitTimegetVisitInformationPerServerTime',
- 'parameters' =>
- array (
- 'module' => 'VisitTime',
- 'action' => 'getVisitInformationPerServerTime',
- ),
- ),
- array (
- 'name' => 'Visits by Local Time',
- 'uniqueId' => 'widgetVisitTimegetVisitInformationPerLocalTime',
- 'parameters' =>
- array (
- 'module' => 'VisitTime',
- 'action' => 'getVisitInformationPerLocalTime',
- ),
- ),
- array (
- 'name' => 'Visits by Day of Week',
- 'uniqueId' => 'widgetVisitTimegetByDayOfWeek',
- 'parameters' =>
- array (
- 'module' => 'VisitTime',
- 'action' => 'getByDayOfWeek',
- ),
- ),
- array (
- 'name' => 'Visits Over Time',
- 'uniqueId' => 'widgetVisitsSummarygetEvolutionGraphcolumnsArray',
- 'parameters' =>
- array (
- 'module' => 'VisitsSummary',
- 'action' => 'getEvolutionGraph',
- 'columns' =>
- array (
- 0 => 'nb_visits',
- ),
- ),
- ),
- array (
- 'name' => 'Visits Overview',
- 'uniqueId' => 'widgetVisitsSummarygetSparklines',
- 'parameters' =>
- array (
- 'module' => 'VisitsSummary',
- 'action' => 'getSparklines',
- ),
- ),
- array (
- 'name' => 'Visits Overview (with graph)',
- 'uniqueId' => 'widgetVisitsSummaryindex',
- 'parameters' =>
- array (
- 'module' => 'VisitsSummary',
- 'action' => 'index',
- ),
- ),
- array (
- 'name' => 'Real-time Map',
- 'uniqueId' => 'widgetUserCountryMaprealtimeMap',
- 'parameters' =>
- array (
- 'module' => 'UserCountryMap',
- 'action' => 'realtimeMap',
- ),
- ),
- array (
- 'name' => 'Visitor Log',
- 'uniqueId' => 'widgetLivegetVisitorLogsmall1',
- 'parameters' =>
- array (
- 'module' => 'Live',
- 'action' => 'getVisitorLog',
- 'small' => 1,
- ),
- ),
- array (
- 'name' => 'Real Time Visitor Count',
- 'uniqueId' => 'widgetLivegetSimpleLastVisitCount',
- 'parameters' =>
- array (
- 'module' => 'Live',
- 'action' => 'getSimpleLastVisitCount',
- ),
- ),
- array (
- 'name' => 'Visitors in Real-time',
- 'uniqueId' => 'widgetLivewidget',
- 'parameters' =>
- array (
- 'module' => 'Live',
- 'action' => 'widget',
- ),
- ),
- array (
- 'name' => 'Visitor profile',
- 'uniqueId' => 'widgetLivegetVisitorProfilePopup',
- 'parameters' =>
- array (
- 'module' => 'Live',
- 'action' => 'getVisitorProfilePopup',
- ),
- ),
- array (
- 'name' => 'Visitor Map',
- 'uniqueId' => 'widgetUserCountryMapvisitorMap',
- 'parameters' =>
- array (
- 'module' => 'UserCountryMap',
- 'action' => 'visitorMap',
- ),
- ),
- array (
- 'name' => 'Visitor Location (Country)',
- 'uniqueId' => 'widgetUserCountrygetCountry',
- 'parameters' =>
- array (
- 'module' => 'UserCountry',
- 'action' => 'getCountry',
- ),
- ),
- array (
- 'name' => 'Visitor Location (Continent)',
- 'uniqueId' => 'widgetUserCountrygetContinent',
- 'parameters' =>
- array (
- 'module' => 'UserCountry',
- 'action' => 'getContinent',
- ),
- ),
- array (
- 'name' => 'Visitor Location (Region)',
- 'uniqueId' => 'widgetUserCountrygetRegion',
- 'parameters' =>
- array (
- 'module' => 'UserCountry',
- 'action' => 'getRegion',
- ),
- ),
- array (
- 'name' => 'Visitor Location (City)',
- 'uniqueId' => 'widgetUserCountrygetCity',
- 'parameters' =>
- array (
- 'module' => 'UserCountry',
- 'action' => 'getCity',
- ),
- ),
- array (
- 'name' => 'Custom Variables',
- 'uniqueId' => 'widgetCustomVariablesgetCustomVariables',
- 'parameters' =>
- array (
- 'module' => 'CustomVariables',
- 'action' => 'getCustomVariables',
- ),
- ),
- array (
- 'name' => 'Length of Visits',
- 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerVisitDuration',
- 'parameters' =>
- array (
- 'module' => 'VisitorInterest',
- 'action' => 'getNumberOfVisitsPerVisitDuration',
- ),
- ),
- array (
- 'name' => 'Pages per Visit',
- 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerPage',
- 'parameters' =>
- array (
- 'module' => 'VisitorInterest',
- 'action' => 'getNumberOfVisitsPerPage',
- ),
- ),
- array (
- 'name' => 'Visits by Visit Number',
- 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsByVisitCount',
- 'parameters' =>
- array (
- 'module' => 'VisitorInterest',
- 'action' => 'getNumberOfVisitsByVisitCount',
- ),
- ),
- array (
- 'name' => 'Visits by Days Since Last Visit',
- 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsByDaysSinceLast',
- 'parameters' =>
- array (
- 'module' => 'VisitorInterest',
- 'action' => 'getNumberOfVisitsByDaysSinceLast',
- ),
- ),
- array (
- 'name' => 'Frequency Overview',
- 'uniqueId' => 'widgetVisitFrequencygetSparklines',
- 'parameters' =>
- array (
- 'module' => 'VisitFrequency',
- 'action' => 'getSparklines',
- ),
- ),
- array (
- 'name' => 'Returning Visits Over Time',
- 'uniqueId' => 'widgetVisitFrequencygetEvolutionGraphcolumnsArray',
- 'parameters' =>
- array (
- 'module' => 'VisitFrequency',
- 'action' => 'getEvolutionGraph',
- 'columns' =>
- array (
- 0 => 'nb_visits_returning',
- ),
- ),
- ),
- array (
- 'name' => 'Screen Resolution',
- 'uniqueId' => 'widgetResolutiongetResolution',
- 'parameters' =>
- array (
- 'module' => 'Resolution',
- 'action' => 'getResolution',
- ),
- ),
- array (
- 'name' => 'Browser Plugins',
- 'uniqueId' => 'widgetDevicePluginsgetPlugin',
- 'parameters' =>
- array (
- 'module' => 'DevicePlugins',
- 'action' => 'getPlugin',
- ),
- ),
- array (
- 'name' => 'Visitor Configuration',
- 'uniqueId' => 'widgetResolutiongetConfiguration',
- 'parameters' =>
- array (
- 'module' => 'Resolution',
- 'action' => 'getConfiguration',
- ),
- ),
- array (
- 'name' => 'Browser language',
- 'uniqueId' => 'widgetUserLanguagegetLanguage',
- 'parameters' =>
- array (
- 'module' => 'UserLanguage',
- 'action' => 'getLanguage',
- ),
- ),
- array (
- 'name' => 'Language code',
- 'uniqueId' => 'widgetUserLanguagegetLanguageCode',
- 'parameters' =>
- array (
- 'module' => 'UserLanguage',
- 'action' => 'getLanguageCode',
- ),
- ),
- array (
- 'name' => 'Device type',
- 'uniqueId' => 'widgetDevicesDetectiongetType',
- 'parameters' =>
- array (
- 'module' => 'DevicesDetection',
- 'action' => 'getType',
- ),
- ),
- array (
- 'name' => 'Device brand',
- 'uniqueId' => 'widgetDevicesDetectiongetBrand',
- 'parameters' =>
- array (
- 'module' => 'DevicesDetection',
- 'action' => 'getBrand',
- ),
- ),
- array (
- 'name' => 'Visitor Browser',
- 'uniqueId' => 'widgetDevicesDetectiongetBrowsers',
- 'parameters' =>
- array (
- 'module' => 'DevicesDetection',
- 'action' => 'getBrowsers',
- ),
- ),
- array (
- 'name' => 'Device model',
- 'uniqueId' => 'widgetDevicesDetectiongetModel',
- 'parameters' =>
- array (
- 'module' => 'DevicesDetection',
- 'action' => 'getModel',
- ),
- ),
- array (
- 'name' => 'Browser version',
- 'uniqueId' => 'widgetDevicesDetectiongetBrowserVersions',
- 'parameters' =>
- array (
- 'module' => 'DevicesDetection',
- 'action' => 'getBrowserVersions',
- ),
- ),
- array (
- 'name' => 'Operating System families',
- 'uniqueId' => 'widgetDevicesDetectiongetOsFamilies',
- 'parameters' =>
- array (
- 'module' => 'DevicesDetection',
- 'action' => 'getOsFamilies',
- ),
- ),
- array (
- 'name' => 'Operating System versions',
- 'uniqueId' => 'widgetDevicesDetectiongetOsVersions',
- 'parameters' =>
- array (
- 'module' => 'DevicesDetection',
- 'action' => 'getOsVersions',
- ),
- ),
- array (
- 'name' => 'Browser engines',
- 'uniqueId' => 'widgetDevicesDetectiongetBrowserEngines',
- 'parameters' =>
- array (
- 'module' => 'DevicesDetection',
- 'action' => 'getBrowserEngines',
- ),
- ),
- array (
- 'name' => 'Pages',
- 'uniqueId' => 'widgetActionsgetPageUrls',
- 'parameters' =>
- array (
- 'module' => 'Actions',
- 'action' => 'getPageUrls',
- ),
- ),
- array (
- 'name' => 'Entry Pages',
- 'uniqueId' => 'widgetActionsgetEntryPageUrls',
- 'parameters' =>
- array (
- 'module' => 'Actions',
- 'action' => 'getEntryPageUrls',
- ),
- ),
- array (
- 'name' => 'Exit Pages',
- 'uniqueId' => 'widgetActionsgetExitPageUrls',
- 'parameters' =>
- array (
- 'module' => 'Actions',
- 'action' => 'getExitPageUrls',
- ),
- ),
- array (
- 'name' => 'Page Titles',
- 'uniqueId' => 'widgetActionsgetPageTitles',
- 'parameters' =>
- array (
- 'module' => 'Actions',
- 'action' => 'getPageTitles',
- ),
- ),
- array (
- 'name' => 'Entry Page Titles',
- 'uniqueId' => 'widgetActionsgetEntryPageTitles',
- 'parameters' =>
- array (
- 'module' => 'Actions',
- 'action' => 'getEntryPageTitles',
- ),
- ),
- array (
- 'name' => 'Exit Page Titles',
- 'uniqueId' => 'widgetActionsgetExitPageTitles',
- 'parameters' =>
- array (
- 'module' => 'Actions',
- 'action' => 'getExitPageTitles',
- ),
- ),
- array (
- 'name' => 'Outlinks',
- 'uniqueId' => 'widgetActionsgetOutlinks',
- 'parameters' =>
- array (
- 'module' => 'Actions',
- 'action' => 'getOutlinks',
- ),
- ),
- array (
- 'name' => 'Downloads',
- 'uniqueId' => 'widgetActionsgetDownloads',
- 'parameters' =>
- array (
- 'module' => 'Actions',
- 'action' => 'getDownloads',
- ),
- ),
- array (
- 'name' => 'Content Name',
- 'uniqueId' => 'widgetContentsgetContentNames',
- 'parameters' =>
- array (
- 'module' => 'Contents',
- 'action' => 'getContentNames',
- ),
- ),
- array (
- 'name' => 'Content Piece',
- 'uniqueId' => 'widgetContentsgetContentPieces',
- 'parameters' =>
- array (
- 'module' => 'Contents',
- 'action' => 'getContentPieces',
- ),
- ),
- array (
- 'name' => 'Event Categories',
- 'uniqueId' => 'widgetEventsgetCategorysecondaryDimensioneventAction',
- 'parameters' =>
- array (
- 'module' => 'Events',
- 'action' => 'getCategory',
- 'secondaryDimension' => 'eventAction',
- ),
- ),
- array (
- 'name' => 'Event Actions',
- 'uniqueId' => 'widgetEventsgetActionsecondaryDimensioneventName',
- 'parameters' =>
- array (
- 'module' => 'Events',
- 'action' => 'getAction',
- 'secondaryDimension' => 'eventName',
- ),
- ),
- array (
- 'name' => 'Event Names',
- 'uniqueId' => 'widgetEventsgetNamesecondaryDimensioneventAction',
- 'parameters' =>
- array (
- 'module' => 'Events',
- 'action' => 'getName',
- 'secondaryDimension' => 'eventAction',
- ),
- ),
- array (
- 'name' => 'Site Search Keywords',
- 'uniqueId' => 'widgetActionsgetSiteSearchKeywords',
- 'parameters' =>
- array (
- 'module' => 'Actions',
- 'action' => 'getSiteSearchKeywords',
- ),
- ),
- array (
- 'name' => 'Search Keywords with No Results',
- 'uniqueId' => 'widgetActionsgetSiteSearchNoResultKeywords',
- 'parameters' =>
- array (
- 'module' => 'Actions',
- 'action' => 'getSiteSearchNoResultKeywords',
- ),
- ),
- array (
- 'name' => 'Search Categories',
- 'uniqueId' => 'widgetActionsgetSiteSearchCategories',
- 'parameters' =>
- array (
- 'module' => 'Actions',
- 'action' => 'getSiteSearchCategories',
- ),
- ),
- array (
- 'name' => 'Pages Following a Site Search',
- 'uniqueId' => 'widgetActionsgetPageUrlsFollowingSiteSearch',
- 'parameters' =>
- array (
- 'module' => 'Actions',
- 'action' => 'getPageUrlsFollowingSiteSearch',
- ),
- ),
- array (
- 'name' => 'Page Titles Following a Site Search',
- 'uniqueId' => 'widgetActionsgetPageTitlesFollowingSiteSearch',
- 'parameters' =>
- array (
- 'module' => 'Actions',
- 'action' => 'getPageTitlesFollowingSiteSearch',
- ),
- ),
- array (
- 'name' => 'Overview',
- 'uniqueId' => 'widgetReferrersgetReferrerType',
- 'parameters' =>
- array (
- 'module' => 'Referrers',
- 'action' => 'getReferrerType',
- ),
- ),
- array (
- 'name' => 'All Referrers',
- 'uniqueId' => 'widgetReferrersgetAll',
- 'parameters' =>
- array (
- 'module' => 'Referrers',
- 'action' => 'getAll',
- ),
- ),
- array (
- 'name' => 'Keywords',
- 'uniqueId' => 'widgetReferrersgetKeywords',
- 'parameters' =>
- array (
- 'module' => 'Referrers',
- 'action' => 'getKeywords',
- ),
- ),
- array (
- 'name' => 'Referrer Websites',
- 'uniqueId' => 'widgetReferrersgetWebsites',
- 'parameters' =>
- array (
- 'module' => 'Referrers',
- 'action' => 'getWebsites',
- ),
- ),
- array (
- 'name' => 'Search Engines',
- 'uniqueId' => 'widgetReferrersgetSearchEngines',
- 'parameters' =>
- array (
- 'module' => 'Referrers',
- 'action' => 'getSearchEngines',
- ),
- ),
- array (
- 'name' => 'Campaigns',
- 'uniqueId' => 'widgetReferrersgetCampaigns',
- 'parameters' =>
- array (
- 'module' => 'Referrers',
- 'action' => 'getCampaigns',
- ),
- ),
- array (
- 'name' => 'List of social networks',
- 'uniqueId' => 'widgetReferrersgetSocials',
- 'parameters' =>
- array (
- 'module' => 'Referrers',
- 'action' => 'getSocials',
- ),
- ),
- array (
- 'name' => 'Goals Overview',
- 'uniqueId' => 'widgetGoalswidgetGoalsOverview',
- 'parameters' =>
- array (
- 'module' => 'Goals',
- 'action' => 'widgetGoalsOverview',
- ),
- ),
- array (
- 'name' => 'Download Software',
- 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal1',
- 'parameters' =>
- array (
- 'module' => 'Goals',
- 'action' => 'widgetGoalReport',
- 'idGoal' => '1',
- ),
- ),
- array (
- 'name' => 'Download Software2',
- 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal2',
- 'parameters' =>
- array (
- 'module' => 'Goals',
- 'action' => 'widgetGoalReport',
- 'idGoal' => '2',
- ),
- ),
- array (
- 'name' => 'Opens Contact Form',
- 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal3',
- 'parameters' =>
- array (
- 'module' => 'Goals',
- 'action' => 'widgetGoalReport',
- 'idGoal' => '3',
- ),
- ),
- array (
- 'name' => 'Visit Docs',
- 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal4',
- 'parameters' =>
- array (
- 'module' => 'Goals',
- 'action' => 'widgetGoalReport',
- 'idGoal' => '4',
- ),
- ),
- array (
- 'name' => 'Product SKU',
- 'uniqueId' => 'widgetGoalsgetItemsSku',
- 'parameters' =>
- array (
- 'module' => 'Goals',
- 'action' => 'getItemsSku',
- ),
- ),
- array (
- 'name' => 'Product Name',
- 'uniqueId' => 'widgetGoalsgetItemsName',
- 'parameters' =>
- array (
- 'module' => 'Goals',
- 'action' => 'getItemsName',
- ),
- ),
- array (
- 'name' => 'Product Category',
- 'uniqueId' => 'widgetGoalsgetItemsCategory',
- 'parameters' =>
- array (
- 'module' => 'Goals',
- 'action' => 'getItemsCategory',
- ),
- ),
- array (
- 'name' => 'Overview',
- 'uniqueId' => 'widgetEcommercewidgetGoalReportidGoalecommerceOrder',
- 'parameters' =>
- array (
- 'module' => 'Ecommerce',
- 'action' => 'widgetGoalReport',
- 'idGoal' => 'ecommerceOrder',
- ),
- ),
- array (
- 'name' => 'Ecommerce Log',
- 'uniqueId' => 'widgetEcommercegetEcommerceLog',
- 'parameters' =>
- array (
- 'module' => 'Ecommerce',
- 'action' => 'getEcommerceLog',
- ),
- ),
- array (
- 'name' => 'Insights Overview',
- 'uniqueId' => 'widgetInsightsgetInsightsOverview',
- 'parameters' =>
- array (
- 'module' => 'Insights',
- 'action' => 'getInsightsOverview',
- ),
- ),
- array (
- 'name' => 'Movers and Shakers',
- 'uniqueId' => 'widgetInsightsgetOverallMoversAndShakers',
- 'parameters' =>
- array (
- 'module' => 'Insights',
- 'action' => 'getOverallMoversAndShakers',
- ),
- ),
- array (
- 'name' => 'Top Keywords for Page URL',
- 'uniqueId' => 'widgetReferrersgetKeywordsForPage',
- 'parameters' =>
- array (
- 'module' => 'Referrers',
- 'action' => 'getKeywordsForPage',
- ),
- ),
- array (
- 'name' => 'SEO Rankings',
- 'uniqueId' => 'widgetSEOgetRank',
- 'parameters' =>
- array (
- 'module' => 'SEO',
- 'action' => 'getRank',
- ),
- ),
- array (
- 'name' => 'Support Piwik!',
- 'uniqueId' => 'widgetCoreHomegetDonateForm',
- 'parameters' =>
- array (
- 'module' => 'CoreHome',
- 'action' => 'getDonateForm',
- ),
- ),
- array (
- 'name' => 'Welcome!',
- 'uniqueId' => 'widgetCoreHomegetPromoVideo',
- 'parameters' =>
- array (
- 'module' => 'CoreHome',
- 'action' => 'getPromoVideo',
- ),
- ),
- array (
- 'name' => 'Piwik.org Blog',
- 'uniqueId' => 'widgetExampleRssWidgetrssPiwik',
- 'parameters' =>
- array (
- 'module' => 'ExampleRssWidget',
- 'action' => 'rssPiwik',
- ),
- ),
- array (
- 'name' => 'Piwik Changelog',
- 'uniqueId' => 'widgetExampleRssWidgetrssChangelog',
- 'parameters' =>
- array (
- 'module' => 'ExampleRssWidget',
- 'action' => 'rssChangelog',
- ),
- ),
+ return array(
+ array (
+ 'name' => 'Visits per server time',
+ 'uniqueId' => 'widgetVisitTimegetVisitInformationPerServerTime',
+ 'parameters' =>
+ array (
+ 'module' => 'VisitTime',
+ 'action' => 'getVisitInformationPerServerTime',
+ ),
+ ), array (
+ 'name' => 'Visits per local time',
+ 'uniqueId' => 'widgetVisitTimegetVisitInformationPerLocalTime',
+ 'parameters' =>
+ array (
+ 'module' => 'VisitTime',
+ 'action' => 'getVisitInformationPerLocalTime',
+ ),
+ ), array (
+ 'name' => 'Visits by Day of Week',
+ 'uniqueId' => 'widgetVisitTimegetByDayOfWeek',
+ 'parameters' =>
+ array (
+ 'module' => 'VisitTime',
+ 'action' => 'getByDayOfWeek',
+ ),
+ ), array (
+ 'name' => 'Visits Over Time',
+ 'uniqueId' => 'widgetVisitsSummarygetEvolutionGraphcolumnsArray',
+ 'parameters' =>
+ array (
+ 'module' => 'VisitsSummary',
+ 'action' => 'getEvolutionGraph',
+ 'columns' =>
+ array (
+ 0 => 'nb_visits',
+ ),
+ ),
+ ), array (
+ 'name' => 'Visits Overview',
+ 'uniqueId' => 'widgetVisitsSummarygetSparklines',
+ 'parameters' =>
+ array (
+ 'module' => 'VisitsSummary',
+ 'action' => 'getSparklines',
+ ),
+ ), array (
+ 'name' => 'Visits Overview (with graph)',
+ 'uniqueId' => 'widgetVisitsSummaryindex',
+ 'parameters' =>
+ array (
+ 'module' => 'VisitsSummary',
+ 'action' => 'index',
+ ),
+ ), array (
+ 'name' => 'Visitor Log',
+ 'uniqueId' => 'widgetLivegetVisitorLogsmall1',
+ 'parameters' =>
+ array (
+ 'module' => 'Live',
+ 'action' => 'getVisitorLog',
+ 'small' => 1,
+ ),
+ ), array (
+ 'name' => 'Continent',
+ 'uniqueId' => 'widgetUserCountrygetContinent',
+ 'parameters' =>
+ array (
+ 'module' => 'UserCountry',
+ 'action' => 'getContinent',
+ ),
+ ), array (
+ 'name' => 'Visits per visit duration',
+ 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerVisitDuration',
+ 'parameters' =>
+ array (
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsPerVisitDuration',
+ ),
+ ), array (
+ 'name' => 'Pages per Visit',
+ 'uniqueId' => 'widgetVisitorInterestgetNumberOfVisitsPerPage',
+ 'parameters' =>
+ array (
+ 'module' => 'VisitorInterest',
+ 'action' => 'getNumberOfVisitsPerPage',
+ ),
+ ), array (
+ 'name' => 'Frequency Overview',
+ 'uniqueId' => 'widgetVisitFrequencygetSparklines',
+ 'parameters' =>
+ array (
+ 'module' => 'VisitFrequency',
+ 'action' => 'getSparklines',
+ ),
+ ), array (
+ 'name' => 'Returning Visits Over Time',
+ 'uniqueId' => 'widgetVisitFrequencygetEvolutionGraphcolumnsArray',
+ 'parameters' =>
+ array (
+ 'module' => 'VisitFrequency',
+ 'action' => 'getEvolutionGraph',
+ 'columns' =>
+ array (
+ 0 => 'nb_visits_returning',
+ ),
+ ),
+ ), array (
+ 'name' => 'Browser engines',
+ 'uniqueId' => 'widgetDevicesDetectiongetBrowserEngines',
+ 'parameters' =>
+ array (
+ 'module' => 'DevicesDetection',
+ 'action' => 'getBrowserEngines',
+ ),
+ ), array (
+ 'name' => 'Content Name',
+ 'uniqueId' => 'widgetContentsgetContentNames',
+ 'parameters' =>
+ array (
+ 'module' => 'Contents',
+ 'action' => 'getContentNames',
+ ),
+ ), array (
+ 'name' => 'Content Piece',
+ 'uniqueId' => 'widgetContentsgetContentPieces',
+ 'parameters' =>
+ array (
+ 'module' => 'Contents',
+ 'action' => 'getContentPieces',
+ ),
+ ), array (
+ 'name' => 'Event Categories',
+ 'uniqueId' => 'widgetEventsgetCategorysecondaryDimensioneventAction',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getCategory',
+ 'secondaryDimension' => 'eventAction',
+ ),
+ ), array (
+ 'name' => 'Event Actions',
+ 'uniqueId' => 'widgetEventsgetActionsecondaryDimensioneventName',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getAction',
+ 'secondaryDimension' => 'eventName',
+ ),
+ ), array (
+ 'name' => 'Event Names',
+ 'uniqueId' => 'widgetEventsgetNamesecondaryDimensioneventAction',
+ 'parameters' =>
+ array (
+ 'module' => 'Events',
+ 'action' => 'getName',
+ 'secondaryDimension' => 'eventAction',
+ ),
+ ), array (
+ 'name' => 'Overview',
+ 'uniqueId' => 'widgetReferrersgetReferrerType',
+ 'parameters' =>
+ array (
+ 'module' => 'Referrers',
+ 'action' => 'getReferrerType',
+ ),
+ ), array (
+ 'name' => 'All Referrers',
+ 'uniqueId' => 'widgetReferrersgetAll',
+ 'parameters' =>
+ array (
+ 'module' => 'Referrers',
+ 'action' => 'getAll',
+ ),
+ ), array (
+ 'name' => 'List of social networks',
+ 'uniqueId' => 'widgetReferrersgetSocials',
+ 'parameters' =>
+ array (
+ 'module' => 'Referrers',
+ 'action' => 'getSocials',
+ ),
+ ), array (
+ 'name' => 'Goals Overview',
+ 'uniqueId' => 'widgetGoalswidgetGoalsOverview',
+ 'parameters' =>
+ array (
+ 'module' => 'Goals',
+ 'action' => 'widgetGoalsOverview',
+ ),
+ ), array (
+ 'name' => 'Download Software',
+ 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal1',
+ 'parameters' =>
+ array (
+ 'module' => 'Goals',
+ 'action' => 'widgetGoalReport',
+ 'idGoal' => '1',
+ ),
+ ), array (
+ 'name' => 'Download Software2',
+ 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal2',
+ 'parameters' =>
+ array (
+ 'module' => 'Goals',
+ 'action' => 'widgetGoalReport',
+ 'idGoal' => '2',
+ ),
+ ), array (
+ 'name' => 'Opens Contact Form',
+ 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal3',
+ 'parameters' =>
+ array (
+ 'module' => 'Goals',
+ 'action' => 'widgetGoalReport',
+ 'idGoal' => '3',
+ ),
+ ), array (
+ 'name' => 'Visit Docs',
+ 'uniqueId' => 'widgetGoalswidgetGoalReportidGoal4',
+ 'parameters' =>
+ array (
+ 'module' => 'Goals',
+ 'action' => 'widgetGoalReport',
+ 'idGoal' => '4',
+ ),
+ ), array (
+ 'name' => 'Product SKU',
+ 'uniqueId' => 'widgetGoalsgetItemsSku',
+ 'parameters' =>
+ array (
+ 'module' => 'Goals',
+ 'action' => 'getItemsSku',
+ ),
+ ), array (
+ 'name' => 'Product Name',
+ 'uniqueId' => 'widgetGoalsgetItemsName',
+ 'parameters' =>
+ array (
+ 'module' => 'Goals',
+ 'action' => 'getItemsName',
+ ),
+ ), array (
+ 'name' => 'Product Category',
+ 'uniqueId' => 'widgetGoalsgetItemsCategory',
+ 'parameters' =>
+ array (
+ 'module' => 'Goals',
+ 'action' => 'getItemsCategory',
+ ),
+ ), array (
+ 'name' => 'Overview',
+ 'uniqueId' => 'widgetEcommercewidgetGoalReportidGoalecommerceOrder',
+ 'parameters' =>
+ array (
+ 'module' => 'Ecommerce',
+ 'action' => 'widgetGoalReport',
+ 'idGoal' => 'ecommerceOrder',
+ ),
+ )
);
}
}
-WidgetTest::$fixture = new WidgetizeFixture();
\ No newline at end of file
+WidgetTest::$fixture = new WidgetizeFixture();
diff --git a/plugins/ZenMode/ZenMode.php b/plugins/ZenMode/ZenMode.php
index bbacb157c3..eee60c4603 100644
--- a/plugins/ZenMode/ZenMode.php
+++ b/plugins/ZenMode/ZenMode.php
@@ -40,6 +40,7 @@ class ZenMode extends \Piwik\Plugin
{
$jsFiles[] = "plugins/ZenMode/javascripts/zen-mode.js";
$jsFiles[] = "plugins/ZenMode/angularjs/quick-access/quick-access.directive.js";
+ $jsFiles[] = "plugins/ZenMode/angularjs/zen-mode/zen-mode-disabler.js";
$jsFiles[] = "plugins/ZenMode/angularjs/zen-mode/zen-mode-switcher.directive.js";
}
diff --git a/plugins/ZenMode/angularjs/zen-mode/zen-mode-disabler.js b/plugins/ZenMode/angularjs/zen-mode/zen-mode-disabler.js
new file mode 100644
index 0000000000..420020b75f
--- /dev/null
+++ b/plugins/ZenMode/angularjs/zen-mode/zen-mode-disabler.js
@@ -0,0 +1,35 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * ...
+ * Will toggle the zen mode on click on this element.
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikReportingMenu', piwikZenModeSwitcher);
+
+ piwikZenModeSwitcher.$inject = ['$rootElement', '$filter'];
+
+ function piwikZenModeSwitcher($rootElement, $filter) {
+
+ return {
+ restrict: 'A',
+ compile: function (element, attrs) {
+
+ element.find('.Menu--dashboard').prepend(
+ ''
+ + ' '
+ + ' ');
+
+ return function () {
+ };
+ }
+ };
+
+ }
+})();
\ No newline at end of file
diff --git a/plugins/ZenMode/javascripts/zen-mode.js b/plugins/ZenMode/javascripts/zen-mode.js
index bc2a793942..e4e163db8b 100644
--- a/plugins/ZenMode/javascripts/zen-mode.js
+++ b/plugins/ZenMode/javascripts/zen-mode.js
@@ -19,13 +19,6 @@ $(document).ready(function () {
piwikHelper.compileAngularComponents(addedElement);
- addedElement = $('.Menu--dashboard').prepend(
- ''
- + ' '
- + ' ');
-
- piwikHelper.compileAngularComponents(addedElement);
-
angular.element(document).injector().invoke(handleZenMode);
function handleZenMode ($rootElement, $cookies) {
@@ -77,7 +70,7 @@ $(document).ready(function () {
function isDashboard()
{
- return !!$('.Menu--dashboard').length;
+ return !!$('[piwik-reporting-menu]').length;
}
function initMenu () {
@@ -98,7 +91,6 @@ $(document).ready(function () {
$('#Searchmenu').off('keydown focus', '.quick-access input', showQuickAccessMenu);
$('#Searchmenu').off('blur', '.quick-access input', hideQuickAccessMenu);
- menu.prototype.adaptSubMenuHeight();
}
function overMainLI () {
diff --git a/tests/PHPUnit/Fixtures/OmniFixture.php b/tests/PHPUnit/Fixtures/OmniFixture.php
index 123e214bd3..dd680e9085 100644
--- a/tests/PHPUnit/Fixtures/OmniFixture.php
+++ b/tests/PHPUnit/Fixtures/OmniFixture.php
@@ -73,12 +73,15 @@ class OmniFixture extends Fixture
}
}
- $this->now = $this->fixtures['Piwik\\Tests\\Fixtures\\ManySitesImportedLogsWithXssAttempts']->now;
- // make sure ManySitesImportedLogsWithXssAttempts is the first fixture
- $fixture = $this->fixtures['Piwik\\Tests\\Fixtures\\ManySitesImportedLogsWithXssAttempts'];
- unset($this->fixtures['Piwik\\Tests\\Fixtures\\ManySitesImportedLogsWithXssAttempts']);
- $this->fixtures = array_merge(array('Piwik\\Tests\\Fixtures\\ManySitesImportedLogsWithXssAttempts' => $fixture), $this->fixtures);
+ if (!empty($this->fixtures['Piwik\\Tests\\Fixtures\\ManySitesImportedLogsWithXssAttempts'])) {
+ $this->now = $this->fixtures['Piwik\\Tests\\Fixtures\\ManySitesImportedLogsWithXssAttempts']->now;
+
+ // make sure ManySitesImportedLogsWithXssAttempts is the first fixture
+ $fixture = $this->fixtures['Piwik\\Tests\\Fixtures\\ManySitesImportedLogsWithXssAttempts'];
+ unset($this->fixtures['Piwik\\Tests\\Fixtures\\ManySitesImportedLogsWithXssAttempts']);
+ $this->fixtures = array_merge(array('Piwik\\Tests\\Fixtures\\ManySitesImportedLogsWithXssAttempts' => $fixture), $this->fixtures);
+ }
}
private function adjustDateTime($dateTime, $adjustToDate)
diff --git a/tests/PHPUnit/Fixtures/UITestFixture.php b/tests/PHPUnit/Fixtures/UITestFixture.php
index eb35b24114..34c63a22e9 100644
--- a/tests/PHPUnit/Fixtures/UITestFixture.php
+++ b/tests/PHPUnit/Fixtures/UITestFixture.php
@@ -8,6 +8,7 @@
namespace Piwik\Tests\Fixtures;
use Exception;
+use Piwik\API\Request;
use Piwik\AssetManager;
use Piwik\Access;
use Piwik\Common;
@@ -22,9 +23,6 @@ use Piwik\Plugins\UserCountry\LocationProvider;
use Piwik\Plugins\UsersManager\API as UsersManagerAPI;
use Piwik\Plugins\SitesManager\API as SitesManagerAPI;
use Piwik\Tests\Framework\Fixture;
-use Piwik\WidgetsList;
-use Piwik\Tests\Framework\OverrideLogin;
-use Piwik\Tests\Framework\TestCase\SystemTestCase;
/**
* Fixture for UI tests.
@@ -222,10 +220,10 @@ class UITestFixture extends SqlDump
$_GET['token_auth'] = Fixture::getTokenAuth();
// collect widgets & sort them so widget order is not important
- $allWidgets = array();
- foreach (WidgetsList::get() as $category => $widgets) {
- $allWidgets = array_merge($allWidgets, $widgets);
- }
+ $allWidgets = Request::processRequest('API.getWidgetMetadata', array(
+ 'idSite' => 1
+ ));
+
usort($allWidgets, function ($lhs, $rhs) {
return strcmp($lhs['uniqueId'], $rhs['uniqueId']);
});
@@ -290,11 +288,12 @@ class UITestFixture extends SqlDump
$dashboard = array(
array(
array(
- 'uniqueId' => "widgetVisitsSummarygetEvolutionGraphcolumnsArray",
+ 'uniqueId' => "widgetVisitsSummarygetEvolutionGraphforceView1viewDataTablegraphEvolution",
'parameters' => array(
'module' => 'VisitsSummary',
'action' => 'getEvolutionGraph',
- 'columns' => 'nb_visits'
+ 'forceView' => '1',
+ 'viewDataTable' => 'graphEvolution'
)
)
),
diff --git a/tests/PHPUnit/Framework/Mock/Category/Categeories.php b/tests/PHPUnit/Framework/Mock/Category/Categeories.php
new file mode 100644
index 0000000000..a7e7f0ce01
--- /dev/null
+++ b/tests/PHPUnit/Framework/Mock/Category/Categeories.php
@@ -0,0 +1,63 @@
+getId()] = $category;
+ }
+
+ $this->categories = $cats;
+ }
+
+ /**
+ * @param Subcategory[] $subcategories
+ */
+ public function setSubcategories($subcategories)
+ {
+ $this->subcategories = $subcategories;
+ }
+
+ public function getAllCategories()
+ {
+ if ($this->categories) {
+ return $this->categories;
+ }
+
+ return parent::getAllCategories();
+ }
+
+ public function getAllSubcategories()
+ {
+ if ($this->subcategories) {
+ return $this->subcategories;
+ }
+
+ return parent::getAllSubcategories();
+ }
+
+}
\ No newline at end of file
diff --git a/tests/PHPUnit/Integration/Category/CategoryListTest.php b/tests/PHPUnit/Integration/Category/CategoryListTest.php
new file mode 100644
index 0000000000..4c8cc4ec57
--- /dev/null
+++ b/tests/PHPUnit/Integration/Category/CategoryListTest.php
@@ -0,0 +1,133 @@
+assertSame(array(
+ 'General_Actions',
+ 'General_Visitors',
+ 'Dashboard_Dashboard',
+ 'General_MultiSitesSummary',
+ 'Referrers_Referrers',
+ 'Goals_Goals',
+ 'Goals_Ecommerce',
+ 'Live!',
+ 'ExampleUI_UiFramework'
+ ), array_keys($list->getCategories()));
+ }
+
+ public function testGetAllCategoriesWithSubcategories_shouldFindSubcategories()
+ {
+ $list = CategoryList::get();
+
+ $this->assertTrue(5 < count($list->getCategory('General_Actions')->getSubcategories()));
+ $this->assertTrue(5 < count($list->getCategory('General_Visitors')->getSubcategories()));
+ $this->assertTrue($list->getCategory('General_Actions')->hasSubcategory('General_Pages'));
+ }
+
+ public function test_getAllCategoriesWithSubcategories_shouldMergeCategoriesAndSubcategories()
+ {
+ $this->categories->setCategories(array(
+ $this->createCategory('General_Visits'),
+ $this->createCategory('General_Actions'),
+ $this->createCategory('Goals_Goals'),
+ $this->createCategory('Goals_Ecommerce'),
+ $this->createCategory('Referrers_Referrers'),
+ ));
+ $this->categories->setSubcategories(array(
+ $subcat1 = $this->createSubcategory('General_Actions', 'General_Pages'),
+ $subcat2 = $this->createSubcategory('Goals_Goals', 'General_Overview'),
+ $subcat3 = $this->createSubcategory('General_Actions', 'Actions_Downloads'),
+ $subcat4 = $this->createSubcategory('General_AnyThingNotExist', 'General_MySubcategoryId'),
+ $subcat5 = $this->createSubcategory('General_Visits', 'Visits'),
+ $subcat6 = $this->createSubcategory('Goals_Goals', '4'),
+ $subcat7 = $this->createSubcategory('General_Visits', 'General_Engagement'),
+ $subcat8 = $this->createSubcategory('Goals_Ecommerce', 'General_Overview'),
+ ));
+
+ /** @var CategoryList $list */
+ $list = CategoryList::get();
+
+ $categoryNames = array(
+ 'General_Visits',
+ 'General_Actions',
+ 'Goals_Goals',
+ 'Goals_Ecommerce',
+ 'Referrers_Referrers',
+ 'General_AnyThingNotExist' // should be created dynamically as none exists
+ );
+ $this->assertSame($categoryNames, array_keys($list->getCategories()));
+
+ $this->assertSubcategoriesInCategoryEquals(array($subcat5, $subcat7), 'General_Visits', $list);
+ $this->assertSubcategoriesInCategoryEquals(array($subcat1, $subcat3), 'General_Actions', $list);
+ $this->assertSubcategoriesInCategoryEquals(array($subcat2, $subcat6), 'Goals_Goals', $list);
+ $this->assertSubcategoriesInCategoryEquals(array($subcat8), 'Goals_Ecommerce', $list);
+ $this->assertSubcategoriesInCategoryEquals(array(), 'Referrers_Referrers', $list);
+ $this->assertSubcategoriesInCategoryEquals(array($subcat4), 'General_AnyThingNotExist', $list);
+
+ // make sure id was actually set
+ $this->assertSame('General_AnyThingNotExist', $list->getCategory('General_AnyThingNotExist')->getId());
+ }
+
+ private function assertSubcategoriesInCategoryEquals($expectedSubcategories, $categoryId, CategoryList $list)
+ {
+ $this->assertSame($expectedSubcategories, $list->getCategory($categoryId)->getSubcategories());
+ }
+
+ private function createCategory($categoryId)
+ {
+ $config = new Category();
+ $config->setId($categoryId);
+
+ return $config;
+ }
+
+ private function createSubcategory($categoryId, $subcategoryId)
+ {
+ $config = new Subcategory();
+ $config->setId($subcategoryId);
+ $config->setCategoryId($categoryId);
+
+ return $config;
+ }
+
+ public function provideContainerConfig()
+ {
+ $this->categories = new Categories(StaticContainer::get('Piwik\Plugin\Manager'));
+
+ return array(
+ 'Piwik\Plugin\Categories' => $this->categories
+ );
+ }
+}
diff --git a/tests/PHPUnit/Integration/DataTable/Filter/PivotByDimensionTest.php b/tests/PHPUnit/Integration/DataTable/Filter/PivotByDimensionTest.php
index 64ee7dac1c..dea1e7d7e9 100644
--- a/tests/PHPUnit/Integration/DataTable/Filter/PivotByDimensionTest.php
+++ b/tests/PHPUnit/Integration/DataTable/Filter/PivotByDimensionTest.php
@@ -91,7 +91,7 @@ class PivotByDimensionTest extends IntegrationTestCase
/**
* @expectedException Exception
- * @expectedExceptionMessage Unsupported pivot: No segment for dimension of report 'Resolution.Resolution_WidgetGlobalVisitors'
+ * @expectedExceptionMessage Unsupported pivot: No segment for dimension of report 'Resolution.Resolution_Configurations'
*/
public function test_construction_ShouldFail_WhenDimensionIsNotSubtableAndSegmentFetchingIsEnabledButThereIsNoSegment()
{
diff --git a/tests/PHPUnit/Integration/Menu/MenuReportingTest.php b/tests/PHPUnit/Integration/Menu/MenuReportingTest.php
deleted file mode 100644
index a9d06afa13..0000000000
--- a/tests/PHPUnit/Integration/Menu/MenuReportingTest.php
+++ /dev/null
@@ -1,80 +0,0 @@
-unloadPlugins();
- $this->menu = MenuReporting::getInstance();
- }
-
- public function tearDown()
- {
- MenuReporting::getInstance()->unsetInstance();
- parent::tearDown();
- }
-
- public function test_getMenu_shouldBeNull_IfNoItems()
- {
- $this->assertNull($this->menu->getMenu());
- }
-
- public function test_getMenu_shouldTriggerAddItemsEvent_toBeBackwardsCompatible()
- {
- $this->loadSomePlugins();
-
- $triggered = false;
- Piwik::addAction('Menu.Reporting.addItems', function () use (&$triggered) {
- $triggered = true;
- });
-
- $this->menu->getMenu();
-
- $this->assertTrue($triggered);
- }
-
- public function test_getMenu_shouldAddMenuItemsOfReports()
- {
- $this->loadSomePlugins();
-
- $items = $this->menu->getMenu();
-
- $this->assertNotEmpty($items);
- $this->assertGreaterThan(20, $items);
- $this->assertEquals(array('General_Actions', 'General_Visitors'), array_keys($items));
- $this->assertNotEmpty($items['General_Actions']['General_Pages']);
- $this->assertEquals('menuGetPageUrls', $items['General_Actions']['General_Pages']['_url']['action']);
- }
-
- private function loadSomePlugins()
- {
- PluginManager::getInstance()->loadPlugins(array(
- 'Actions', 'DevicesDetection', 'CoreVisualizations', 'API', 'Morpheus'
- ));
- }
-}
diff --git a/tests/PHPUnit/Integration/Plugin/CategoriesTest.php b/tests/PHPUnit/Integration/Plugin/CategoriesTest.php
new file mode 100644
index 0000000000..925abc81b8
--- /dev/null
+++ b/tests/PHPUnit/Integration/Plugin/CategoriesTest.php
@@ -0,0 +1,89 @@
+categories = new Categories(StaticContainer::get('Piwik\Plugin\Manager'));
+ }
+
+ public function tearDown()
+ {
+ parent::tearDown();
+ unset($_GET['idSite']);
+ }
+
+ public function test_getAllCategories_shouldOnlyFindCategories()
+ {
+ $categories = $this->categories->getAllCategories();
+
+ $this->assertGreaterThanOrEqual(4, count($categories));
+
+ foreach ($categories as $category) {
+ $this->assertTrue($category instanceof Category);
+ }
+ }
+
+ public function test_getAllCategories_shouldHaveACategoryIdDefined()
+ {
+ $categories = $this->categories->getAllCategories();
+
+ foreach ($categories as $category) {
+ $this->assertNotEmpty($category->getId());
+ }
+ }
+
+ public function test_getAllSubcategories_shouldOnlyFindSubcategories()
+ {
+ $subcategories = $this->categories->getAllSubcategories();
+
+ $this->assertGreaterThanOrEqual(10, count($subcategories));
+
+ foreach ($subcategories as $subcategory) {
+ $this->assertTrue($subcategory instanceof Subcategory);
+ $this->assertNotEmpty($subcategory->getId());
+ }
+ }
+
+ public function test_getAllSubcategories_shouldHaveACategoryIdAndSubcategoryIdDefined()
+ {
+ $subcategories = $this->categories->getAllSubcategories();
+
+ foreach ($subcategories as $subcategory) {
+ $this->assertNotEmpty($subcategory->getId());
+ $this->assertNotEmpty($subcategory->getCategoryId());
+ }
+ }
+}
diff --git a/tests/PHPUnit/Integration/Plugin/WidgetsTest.php b/tests/PHPUnit/Integration/Plugin/WidgetsTest.php
new file mode 100644
index 0000000000..185faba064
--- /dev/null
+++ b/tests/PHPUnit/Integration/Plugin/WidgetsTest.php
@@ -0,0 +1,81 @@
+widgets = new Widgets(StaticContainer::get('Piwik\Plugin\Manager'));
+ }
+
+ public function tearDown()
+ {
+ parent::tearDown();
+ unset($_GET['idSite']);
+ }
+
+ public function test_getWidgetContainerConfigs_shouldOnlyFindWidgetContainerConfigs()
+ {
+ $configs = $this->widgets->getWidgetContainerConfigs();
+
+ $this->assertGreaterThanOrEqual(3, count($configs));
+
+ foreach ($configs as $config) {
+ $this->assertTrue($config instanceof WidgetContainerConfig);
+ }
+ }
+
+ public function test_getWidgetConfigs_shouldFindWidgetConfigs()
+ {
+ $configs = $this->widgets->getWidgetConfigs();
+
+ $this->assertGreaterThanOrEqual(10, count($configs));
+
+ foreach ($configs as $config) {
+ $this->assertTrue($config instanceof WidgetConfig);
+ $this->assertFalse($config instanceof WidgetContainerConfig);
+ }
+ }
+
+ public function test_getWidgetConfigs_shouldSetModuleAndActionForEachConfig()
+ {
+ $configs = $this->widgets->getWidgetConfigs();
+
+ foreach ($configs as $config) {
+ $this->assertNotEmpty($config->getModule());
+ $this->assertNotEmpty($config->getAction());
+ }
+ }
+}
diff --git a/tests/PHPUnit/Integration/Report/ReportsTest.php b/tests/PHPUnit/Integration/Report/ReportsTest.php
new file mode 100644
index 0000000000..9681d73e64
--- /dev/null
+++ b/tests/PHPUnit/Integration/Report/ReportsTest.php
@@ -0,0 +1,72 @@
+unloadAllPlugins();
+
+ $reports = new Reports();
+ $report = $reports->getAllReports();
+
+ $this->assertEquals(array(), $report);
+ }
+
+ public function test_getAllReports_ShouldFindAllAvailableReports()
+ {
+ $this->loadExampleReportPlugin();
+ $this->loadMorePlugins();
+
+ $reports = new Reports();
+ $reports = $reports->getAllReports();
+
+ $this->assertGreaterThan(20, count($reports));
+
+ foreach ($reports as $report) {
+ $this->assertInstanceOf('Piwik\Plugin\Report', $report);
+ }
+ }
+
+ private function loadExampleReportPlugin()
+ {
+ PluginManager::getInstance()->loadPlugins(array('ExampleReport'));
+ }
+
+ private function loadMorePlugins()
+ {
+ PluginManager::getInstance()->loadPlugins(array('Actions', 'DevicesDetection', 'CoreVisualizations', 'API', 'Morpheus'));
+ }
+
+ private function unloadAllPlugins()
+ {
+ PluginManager::getInstance()->unloadPlugins();
+ }
+}
diff --git a/tests/PHPUnit/Integration/ReportTest.php b/tests/PHPUnit/Integration/ReportTest.php
index 8e535be0f2..3475cee9cf 100644
--- a/tests/PHPUnit/Integration/ReportTest.php
+++ b/tests/PHPUnit/Integration/ReportTest.php
@@ -16,12 +16,13 @@ use Piwik\Piwik;
use Piwik\Metrics;
use Piwik\Plugins\ExampleTracker\Columns\ExampleDimension;
use Piwik\Plugins\Referrers\Columns\Keyword;
-use Piwik\WidgetsList;
+use Piwik\Plugin\Reports;
+use Piwik\Report\ReportWidgetFactory;
use Piwik\Translate;
-use Piwik\Menu\MenuReporting;
use Piwik\Plugin\Manager as PluginManager;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Widget\WidgetsList;
class GetBasicReport extends Report
{
@@ -33,7 +34,7 @@ class GetBasicReport extends Report
$this->order = 20;
$this->module = 'TestPlugin';
$this->action = 'getBasicReport';
- $this->category = 'Goals_Goals';
+ $this->categoryId = 'Goals_Goals';
$this->actionToLoadSubTables = 'invalidReport';
}
}
@@ -45,8 +46,7 @@ class GetAdvancedReport extends GetBasicReport
parent::init();
$this->action = 'getAdvancedReport';
- $this->widgetTitle = 'Actions_WidgetPageTitlesFollowingSearch';
- $this->menuTitle = 'Actions_SubmenuPageTitles';
+ $this->subcategoryId = 'Actions_SubmenuPageTitles';
$this->documentation = Piwik::translate('ExampleReportDocumentation');
$this->dimension = new ExitPageUrl();
$this->metrics = array('nb_actions', 'nb_visits');
@@ -57,6 +57,12 @@ class GetAdvancedReport extends GetBasicReport
$this->constantRowsCount = true;
}
+ public function configureWidgets(WidgetsList $widgetsList, ReportWidgetFactory $factory)
+ {
+ $widget = $factory->createWidget()->setName('Actions_WidgetPageTitlesFollowingSearch');
+ $widgetsList->addWidgetConfig($widget);
+ }
+
public function set($param, $value)
{
$this->$param = $value;
@@ -114,8 +120,6 @@ class ReportTest extends IntegrationTestCase
public function tearDown()
{
- WidgetsList::getInstance()->_reset();
- MenuReporting::getInstance()->unsetInstance();
unset($_GET['idSite']);
parent::tearDown();
}
@@ -149,106 +153,13 @@ class ReportTest extends IntegrationTestCase
$this->disabledReport->checkIsEnabled();
}
- public function test_getWidgetTitle_shouldReturnNullIfNoTitleIsSet()
- {
- $this->assertNull($this->basicReport->getWidgetTitle());
- }
-
- public function test_getWidgetTitle_shouldReturnTranslatedTitleIfSet()
- {
- Translate::loadAllTranslations();
- $this->assertEquals('Page Titles Following a Site Search', $this->advancedReport->getWidgetTitle());
- Translate::reset();
- }
-
public function test_getCategory_shouldReturnTranslatedCategory()
{
Translate::loadAllTranslations();
- $this->assertEquals('Goals', $this->advancedReport->getCategory());
+ $this->assertEquals('Goals_Goals', $this->advancedReport->getCategoryId());
Translate::reset();
}
- public function test_configureWidget_shouldNotAddAWidgetIfNoWidgetTitleIsSet()
- {
- $widgets = WidgetsList::get();
- $this->assertCount(0, $widgets);
-
- $this->basicReport->configureWidget(WidgetsList::getInstance());
-
- $widgets = WidgetsList::get();
- $this->assertCount(0, $widgets);
- }
-
- public function test_configureWidget_shouldAddAWidgetIfAWidgetTitleIsSet()
- {
- $widgets = WidgetsList::get();
- $this->assertCount(0, $widgets);
-
- $this->advancedReport->configureWidget(WidgetsList::getInstance());
-
- $widgets = WidgetsList::get();
- $this->assertCount(1, $widgets);
- $this->assertEquals(array(array(
- 'name' => 'Actions_WidgetPageTitlesFollowingSearch',
- 'uniqueId' => 'widgetTestPlugingetAdvancedReport',
- 'parameters' => array('module' => 'TestPlugin', 'action' => 'getAdvancedReport')
- )), $widgets['Goals_Goals']);
- }
-
- public function test_configureWidget_shouldMixinWidgetParametersIfSet()
- {
- $widgets = WidgetsList::get();
- $this->assertCount(0, $widgets);
-
- $this->advancedReport->set('widgetParams', array('foo' => 'bar'));
- $this->advancedReport->configureWidget(WidgetsList::getInstance());
-
- $widgets = WidgetsList::get();
- $this->assertCount(1, $widgets);
- $this->assertEquals(array('module' => 'TestPlugin', 'action' => 'getAdvancedReport', 'foo' => 'bar'),
- $widgets['Goals_Goals'][0]['parameters']);
- }
-
- public function test_configureReportingMenu_shouldNotAddAMenuIfNoWidgetTitleIsSet()
- {
- $menu = MenuReporting::getInstance();
- $menuItems = $menu->getMenu();
- $this->assertNull($menuItems);
-
- $this->basicReport->configureReportingMenu($menu);
-
- $menuItems = $menu->getMenu();
- $this->assertNull($menuItems);
- }
-
- public function test_configureReportingMenu_shouldAddAMenuIfATitleIsSet()
- {
- $menu = MenuReporting::getInstance();
- $menuItems = $menu->getMenu();
- $this->assertNull($menuItems);
-
- $this->advancedReport->configureReportingMenu($menu);
-
- $menuItems = $menu->getMenu();
-
- $expected = array(
- '_tooltip' => false,
- '_order' => 20,
- '_hasSubmenu' => true,
- 'Actions_SubmenuPageTitles' => array(
- '_url' => array(
- 'module' => 'TestPlugin',
- 'action' => 'menuGetAdvancedReport'
- ),
- '_order' => 20,
- '_name' => 'Actions_SubmenuPageTitles',
- '_tooltip' => false,
- ));
-
- $this->assertCount(1, $menuItems);
- $this->assertEquals($expected, $menuItems['Goals_Goals']);
- }
-
public function test_getMetrics_shouldUseDefaultMetrics()
{
$this->assertEquals(Metrics::getDefaultMetrics(), $this->basicReport->getMetrics());
@@ -353,7 +264,8 @@ class ReportTest extends IntegrationTestCase
'conversion_rate' => 'General_ColumnConversionRate',
),
'actionToLoadSubTables' => 'invalidReport',
- 'order' => 20
+ 'order' => 20,
+ 'subcategory' => null
)
), $reports);
}
@@ -390,7 +302,8 @@ class ReportTest extends IntegrationTestCase
),
'actionToLoadSubTables' => 'GetBasicReport',
'constantRowsCount' => true,
- 'order' => '20'
+ 'order' => '20',
+ 'subcategory' => 'Actions_SubmenuPageTitles'
)
), $reports);
}
@@ -402,43 +315,20 @@ class ReportTest extends IntegrationTestCase
$module = 'ExampleReport';
$action = 'getExampleReport';
- $report = Report::factory($module, $action);
+ $report = Reports::factory($module, $action);
$this->assertInstanceOf('Piwik\Plugins\ExampleReport\Reports\GetExampleReport', $report);
$this->assertEquals($module, $report->getModule());
$this->assertEquals($action, $report->getAction());
// action ucfirst should work as well
- $report = Report::factory($module, ucfirst($action));
+ $report = Reports::factory($module, ucfirst($action));
$this->assertInstanceOf('Piwik\Plugins\ExampleReport\Reports\GetExampleReport', $report);
$this->assertEquals($module, $report->getModule());
$this->assertEquals($action, $report->getAction());
}
- public function test_getAllReports_shouldNotFindAReport_IfNoPluginLoaded()
- {
- $this->unloadAllPlugins();
-
- $report = Report::getAllReports();
-
- $this->assertEquals(array(), $report);
- }
-
- public function test_getAllReports_ShouldFindAllAvailableReports()
- {
- $this->loadExampleReportPlugin();
- $this->loadMorePlugins();
-
- $reports = Report::getAllReports();
-
- $this->assertGreaterThan(20, count($reports));
-
- foreach ($reports as $report) {
- $this->assertInstanceOf('Piwik\Plugin\Report', $report);
- }
- }
-
public function test_getSubtableDimension_ShouldReturnNullIfNoSubtableActionExists()
{
$report = new GetExampleReport();
@@ -455,7 +345,7 @@ class ReportTest extends IntegrationTestCase
{
PluginManager::getInstance()->loadPlugins(array('Referrers'));
- $report = Report::factory('Referrers', 'getSearchEngines');
+ $report = Reports::factory('Referrers', 'getSearchEngines');
$subtableDimension = $report->getSubtableDimension();
$this->assertNotNull($subtableDimension);
diff --git a/tests/PHPUnit/Integration/WidgetsListTest.php b/tests/PHPUnit/Integration/WidgetsListTest.php
index f741f813f3..4ca355ffe5 100644
--- a/tests/PHPUnit/Integration/WidgetsListTest.php
+++ b/tests/PHPUnit/Integration/WidgetsListTest.php
@@ -8,163 +8,166 @@
namespace Piwik\Tests\Integration;
+use Piwik\Access;
+use Piwik\Widget\WidgetConfig;
use Piwik\Plugins\Goals\API;
use Piwik\Tests\Framework\Mock\FakeAccess;
use Piwik\Translate;
-use Piwik\WidgetsList;
+use Piwik\Widget\WidgetsList;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
/**
- * @group WidgetsListTest
* @group Core
*/
class WidgetsListTest extends IntegrationTestCase
{
- public function testGet()
+ public function setIp()
{
- // setup the access layer
+ parent::setUp();
+
FakeAccess::$superUser = true;
+ }
+ public function testGet()
+ {
Fixture::createWebsite('2009-01-04 00:11:42');
$_GET['idSite'] = 1;
- WidgetsList::_reset();
$widgets = WidgetsList::get();
- WidgetsList::_reset();
+
+ $widgetsPerCategory = $this->getWidgetsPerCategory($widgets);
// check if each category has the right number of widgets
$numberOfWidgets = array(
- 'VisitsSummary_VisitsSummary' => 6,
- 'Live!' => 4,
- 'General_Visitors' => 11,
- 'General_VisitorSettings' => 5,
- 'General_Actions' => 10,
- 'Events_Events' => 3,
- 'Actions_SubmenuSitesearch' => 5,
- 'Referrers_Referrers' => 7,
- 'Goals_Goals' => 1,
- 'SEO' => 2,
- 'Example Widgets' => 4,
- 'DevicesDetection_DevicesDetection' => 8,
- 'Insights_WidgetCategory' => 2
+ 'Dashboard_Dashboard' => 1,
+ 'General_Actions' => 15,
+ 'General_Visitors' => 34,
+ 'Example Widgets' => 5,
+ 'SEO' => 2,
+ 'Goals_Goals' => 3,
+ 'Live!' => 2,
+ 'Insights_WidgetCategory' => 2,
+ 'ExampleUI_UiFramework' => 8,
+ 'Referrers_Referrers' => 9,
);
// number of main categories
- $this->assertEquals(count($numberOfWidgets), count($widgets));
+ $this->assertEquals(count($numberOfWidgets), count($widgetsPerCategory));
foreach ($numberOfWidgets as $category => $widgetCount) {
- $this->assertEquals($widgetCount, count($widgets[$category]), sprintf("Widget: %s", $category));
+ $this->assertEquals($widgetCount, count($widgetsPerCategory[$category]), sprintf("Widget: %s", $category));
}
}
- public function testGetWithGoals()
+ private function getWidgetsPerCategory(WidgetsList $list)
{
- // setup the access layer
- FakeAccess::$superUser = true;
+ $widgetsPerCategory = array();
+ foreach ($list->getWidgetConfigs() as $widgetConfig) {
+ $category = $widgetConfig->getCategoryId();
+ if (!isset($widgetsPerCategory[$category])) {
+ $widgetsPerCategory[$category] = array();
+ }
+
+ $widgetsPerCategory[$category][] = $widgetConfig;
+ }
+ return $widgetsPerCategory;
+ }
+
+ public function testGetWithGoals()
+ {
Fixture::createWebsite('2009-01-04 00:11:42');
- API::getInstance()->addGoal(1, 'Goal 1 - Thank you', 'title', 'Thank you', 'contains', $caseSensitive = false, $revenue = 10, $allowMultipleConversions = 1);
+
+ $initialGoalsWidgets = 3;
$_GET['idSite'] = 1;
- WidgetsList::_reset();
- $widgets = WidgetsList::get();
- WidgetsList::_reset();
+ $perCategory = $this->getWidgetsPerCategory(WidgetsList::get());
+ $this->assertEquals($initialGoalsWidgets, count($perCategory['Goals_Goals']));
- // number of main categories
- $this->assertEquals(13, count($widgets));
- // check that the goal widget was added
- $numberOfWidgets = array(
- 'Goals_Goals' => 2,
- );
+ API::getInstance()->addGoal(1, 'Goal 1 - Thank you', 'title', 'Thank you', 'contains', $caseSensitive = false, $revenue = 10, $allowMultipleConversions = 1);
- foreach ($numberOfWidgets as $category => $widgetCount) {
- $this->assertEquals($widgetCount, count($widgets[$category]));
- }
+ $perCategory = $this->getWidgetsPerCategory(WidgetsList::get());
+
+ // number of main categories
+ $this->assertEquals(10, count($perCategory));
+ $this->assertEquals($initialGoalsWidgets + 2, count($perCategory['Goals_Goals'])); // make sure widgets for that goal were added
}
public function testGetWithGoalsAndEcommerce()
{
- // setup the access layer
- FakeAccess::$superUser = true;
-
Fixture::createWebsite('2009-01-04 00:11:42', true);
API::getInstance()->addGoal(1, 'Goal 1 - Thank you', 'title', 'Thank you', 'contains', $caseSensitive = false, $revenue = 10, $allowMultipleConversions = 1);
$_GET['idSite'] = 1;
- WidgetsList::_reset();
- $widgets = WidgetsList::get();
- WidgetsList::_reset();
+ $perCategory = $this->getWidgetsPerCategory(WidgetsList::get());
// number of main categories
- $this->assertEquals(14, count($widgets));
+ $this->assertEquals(11, count($perCategory));
// check if each category has the right number of widgets
$numberOfWidgets = array(
- 'Goals_Goals' => 2,
- 'Goals_Ecommerce' => 5,
+ 'Goals_Goals' => 5,
+ 'Goals_Ecommerce' => 4,
);
foreach ($numberOfWidgets as $category => $widgetCount) {
- $this->assertEquals($widgetCount, count($widgets[$category]));
+ $this->assertEquals($widgetCount, count($perCategory[$category]));
}
}
public function testRemove()
{
- // setup the access layer
- FakeAccess::$superUser = true;
-
Fixture::createWebsite('2009-01-04 00:11:42', true);
API::getInstance()->addGoal(1, 'Goal 1 - Thank you', 'title', 'Thank you', 'contains', $caseSensitive = false, $revenue = 10, $allowMultipleConversions = 1);
$_GET['idSite'] = 1;
- WidgetsList::_reset();
- $widgets = WidgetsList::get();
+ $list = WidgetsList::get();
- $this->assertCount(14, $widgets);
- WidgetsList::remove('SEO', 'NoTeXiStInG');
+ $this->assertCount(11, $this->getWidgetsPerCategory($list));
- $widgets = WidgetsList::get();
- $this->assertCount(14, $widgets);
+ $list->remove('SEO', 'NoTeXiStInG');
- $this->assertArrayHasKey('SEO', $widgets);
- $this->assertCount(2, $widgets['SEO']);
+ $perCategory = $this->getWidgetsPerCategory($list);
+ $this->assertCount(11, $perCategory);
- WidgetsList::remove('SEO', 'SEO_SeoRankings');
- $widgets = WidgetsList::get();
+ $this->assertArrayHasKey('SEO', $perCategory);
+ $this->assertCount(2, $perCategory['SEO']);
- $this->assertCount(1, $widgets['SEO']);
+ $list->remove('SEO', 'SEO_SeoRankings');
- WidgetsList::remove('SEO');
- $widgets = WidgetsList::get();
+ $perCategory = $this->getWidgetsPerCategory($list);
+ $this->assertCount(1, $perCategory['SEO']);
- $this->assertArrayNotHasKey('SEO', $widgets);
+ $list->remove('SEO');
- WidgetsList::_reset();
+ $perCategory = $this->getWidgetsPerCategory($list);
+ $this->assertArrayNotHasKey('SEO', $perCategory);
}
public function testIsDefined()
{
- // setup the access layer
- FakeAccess::$superUser = true;
-
Translate::loadAllTranslations();
Fixture::createWebsite('2009-01-04 00:11:42', true);
$_GET['idSite'] = 1;
- WidgetsList::_reset();
- WidgetsList::add('Actions', 'Pages', 'Actions', 'getPageUrls');
+ $config = new WidgetConfig();
+ $config->setCategoryId('Actions');
+ $config->setName('Pages');
+ $config->setModule('Actions');
+ $config->setAction('getPageUrls');
+ $list = WidgetsList::get();
+ $list->addWidgetConfig($config);
- $this->assertTrue(WidgetsList::isDefined('Actions', 'getPageUrls'));
- $this->assertFalse(WidgetsList::isDefined('Actions', 'inValiD'));
+ $this->assertTrue($list->isDefined('Actions', 'getPageUrls'));
+ $this->assertFalse($list->isDefined('Actions', 'inValiD'));
Translate::reset();
}
diff --git a/tests/PHPUnit/System/ApiGetReportMetadataTest.php b/tests/PHPUnit/System/ApiGetReportMetadataTest.php
index 1270361c39..6b20309230 100755
--- a/tests/PHPUnit/System/ApiGetReportMetadataTest.php
+++ b/tests/PHPUnit/System/ApiGetReportMetadataTest.php
@@ -8,6 +8,7 @@
namespace Piwik\Tests\System;
use Piwik\API\Proxy;
+use Piwik\Cache;
use Piwik\Tests\Framework\TestCase\SystemTestCase;
use Piwik\Tests\Fixtures\ThreeGoalsOnePageview;
diff --git a/tests/PHPUnit/System/AutoSuggestAPITest.php b/tests/PHPUnit/System/AutoSuggestAPITest.php
index 2cb5e0282d..62a0175d01 100644
--- a/tests/PHPUnit/System/AutoSuggestAPITest.php
+++ b/tests/PHPUnit/System/AutoSuggestAPITest.php
@@ -19,6 +19,7 @@ use Piwik\Tests\Framework\TestCase\SystemTestCase;
use Piwik\Tests\Fixtures\ManyVisitsWithGeoIP;
use Piwik\Tests\Framework\Fixture;
use Piwik\Tracker\Cache;
+use Piwik\Cache as PiwikCache;
/**
* testing a the auto suggest API for all known segments
@@ -155,6 +156,7 @@ class AutoSuggestAPITest extends SystemTestCase
{
// Refresh cache for CustomVariables\Model
Cache::clearCacheGeneral();
+ PiwikCache::getTransientCache()->flushAll();
$segments = array();
diff --git a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getAction_flat__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getAction_flat__API.getProcessedReport_day.xml
index 108dc6b91f..38b110885e 100644
--- a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getAction_flat__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getAction_flat__API.getProcessedReport_day.xml
@@ -3,7 +3,8 @@
Piwik test
3 Jan 10 - 9 Jan 10
- Events
+ Actions
+ Events
Event Actions
Events
getAction
diff --git a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getAction_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getAction_lastN__API.getProcessedReport_day.xml
index dfc90321e5..fc22061c34 100644
--- a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getAction_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getAction_lastN__API.getProcessedReport_day.xml
@@ -3,7 +3,8 @@
Piwik test
3 Jan 10 - 9 Jan 10
- Events
+ Actions
+ Events
Event Actions
Events
getAction
diff --git a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getCategory_flat__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getCategory_flat__API.getProcessedReport_day.xml
index 5536cff251..cc9871433d 100644
--- a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getCategory_flat__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getCategory_flat__API.getProcessedReport_day.xml
@@ -3,7 +3,8 @@
Piwik test
3 Jan 10 - 9 Jan 10
- Events
+ Actions
+ Events
Event Categories
Events
getCategory
diff --git a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getCategory_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getCategory_lastN__API.getProcessedReport_day.xml
index 3f683f0611..51b565f354 100644
--- a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getCategory_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getCategory_lastN__API.getProcessedReport_day.xml
@@ -3,7 +3,8 @@
Piwik test
3 Jan 10 - 9 Jan 10
- Events
+ Actions
+ Events
Event Categories
Events
getCategory
diff --git a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getName_flat__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getName_flat__API.getProcessedReport_day.xml
index 20d04cc26b..d5f1a6a6ff 100644
--- a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getName_flat__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getName_flat__API.getProcessedReport_day.xml
@@ -3,7 +3,8 @@
Piwik test
3 Jan 10 - 9 Jan 10
- Events
+ Actions
+ Events
Event Names
Events
getName
diff --git a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getName_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getName_lastN__API.getProcessedReport_day.xml
index 7a029ed288..44a57040f1 100644
--- a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getName_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getName_lastN__API.getProcessedReport_day.xml
@@ -3,7 +3,8 @@
Piwik test
3 Jan 10 - 9 Jan 10
- Events
+ Actions
+ Events
Event Names
Events
getName
diff --git a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_sortByProcessedMetric__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_sortByProcessedMetric__API.getProcessedReport_day.xml
index 5de040b945..8d58df8c09 100644
--- a/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_sortByProcessedMetric__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_ManyVisitorsOneWebsiteTest_sortByProcessedMetric__API.getProcessedReport_day.xml
@@ -4,6 +4,7 @@
Sunday 3 January 2010
Actions
+ Pages
Page URLs
Actions
getPageUrls
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 99d027df37..5eb322e4f5 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
@@ -3,8 +3,9 @@
Site 1
Sunday 3 January 2010
- Visits Summary
- Visits by Server Time
+ Visitors
+ Times
+ Visits per server time
VisitTime
getVisitInformationPerServerTime
Server time
diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__subtable__API.getProcessedReport_week.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__subtable__API.getProcessedReport_week.xml
index fc8e3ded56..14843d6b99 100644
--- a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__subtable__API.getProcessedReport_week.xml
+++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__subtable__API.getProcessedReport_week.xml
@@ -4,6 +4,7 @@
Week 1 March - 7 March 2010
Actions
+ Pages
Page URLs
Actions
getPageUrls
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 279acfa5ea..e87d558301 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
@@ -4,6 +4,7 @@
Saturday 6 March 2010
Actions
+ Page titles
Page titles
Actions
getPageTitles
@@ -16,6 +17,18 @@
Exit rate
getPageTitles
+
+
+ Entry page titles
+ Actions
+ getEntryPageTitles
+
+
+ Exit page titles
+ Actions
+ getExitPageTitles
+
+
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitles&period=day&date=2010-03-06
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitles&period=day&date=2010-02-05,2010-03-06
Actions_getPageTitles
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 209766871b..1d04867af7 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
@@ -3,8 +3,9 @@
new name
Saturday 6 March 2010
- Visits Summary
- Visits by Server Time
+ Visitors
+ Times
+ Visits per server time
VisitTime
getVisitInformationPerServerTime
Server time
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 f8e3c26999..ca84fcf6db 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
@@ -4,6 +4,7 @@
Saturday 6 March 2010
Actions
+ Page titles
Page titles
Actions
getPageTitles
@@ -20,6 +21,18 @@
Bounce Rate
getPageTitles
+
+
+ Entry page titles
+ Actions
+ getEntryPageTitles
+
+
+ Exit page titles
+ Actions
+ getExitPageTitles
+
+
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitles&period=day&date=2010-03-06
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitles&period=day&date=2010-02-05,2010-03-06
Actions_getPageTitles
diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns_onlyOne__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns_onlyOne__API.getProcessedReport_day.xml
index 7484c9c620..99afd65e6f 100644
--- a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns_onlyOne__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns_onlyOne__API.getProcessedReport_day.xml
@@ -3,7 +3,8 @@
new name
Saturday 6 March 2010
- Visits Summary
+ Visitors
+ Overview
Visits Summary
VisitsSummary
get
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitlesFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitlesFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_day.xml
index e1254c611b..dbee08ed4d 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitlesFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitlesFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_day.xml
@@ -3,7 +3,8 @@
Site 1 - Site search
3 Jan 10 - 9 Jan 10
- Site Search
+ Actions
+ Site Search
Page Titles Following a Site Search
Actions
getPageTitlesFollowingSiteSearch
@@ -17,6 +18,13 @@
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.
The number of times this page was visited.
+
+
+ Pages Following a Site Search
+ Actions
+ getPageUrlsFollowingSiteSearch
+
+
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitlesFollowingSiteSearch&period=range&date=2010-01-03,2010-01-09
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitlesFollowingSiteSearch&period=day&date=2010-01-03,2010-01-09
Actions_getPageTitlesFollowingSiteSearch
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitlesFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_month.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitlesFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_month.xml
index 358b09c180..ff965bfe37 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitlesFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_month.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageTitlesFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_month.xml
@@ -3,7 +3,8 @@
Site 1 - Site search
1 Jan 10 - 31 Jul 10
- Site Search
+ Actions
+ Site Search
Page Titles Following a Site Search
Actions
getPageTitlesFollowingSiteSearch
@@ -17,6 +18,13 @@
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.
The number of times this page was visited.
+
+
+ Pages Following a Site Search
+ Actions
+ getPageUrlsFollowingSiteSearch
+
+
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitlesFollowingSiteSearch&period=range&date=2010-01-03,2010-07-03
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitlesFollowingSiteSearch&period=month&date=2010-01-03,2010-07-03
Actions_getPageTitlesFollowingSiteSearch
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 6a327dba4b..df1c9c2dba 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
@@ -4,6 +4,7 @@
3 Jan 10 - 9 Jan 10
Actions
+ Page titles
Page titles
Actions
getPageTitles
@@ -27,6 +28,18 @@
Avg. generation time
getPageTitles
+
+
+ Entry page titles
+ Actions
+ getEntryPageTitles
+
+
+ Exit page titles
+ Actions
+ getExitPageTitles
+
+
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitles&period=range&date=2010-01-03,2010-01-09
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitles&period=day&date=2010-01-03,2010-01-09
Actions_getPageTitles
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 e1f70f727c..2854fd1690 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
@@ -4,6 +4,7 @@
1 Jan 10 - 31 Jul 10
Actions
+ Page titles
Page titles
Actions
getPageTitles
@@ -27,6 +28,18 @@
Avg. generation time
getPageTitles
+
+
+ Entry page titles
+ Actions
+ getEntryPageTitles
+
+
+ Exit page titles
+ Actions
+ getExitPageTitles
+
+
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitles&period=range&date=2010-01-03,2010-07-03
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitles&period=month&date=2010-01-03,2010-07-03
Actions_getPageTitles
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrlsFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrlsFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_day.xml
index a139b8c186..b70c19da05 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrlsFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrlsFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_day.xml
@@ -3,7 +3,8 @@
Site 1 - Site search
3 Jan 10 - 9 Jan 10
- Site Search
+ Actions
+ Site Search
Pages Following a Site Search
Actions
getPageUrlsFollowingSiteSearch
@@ -17,6 +18,13 @@
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.
The number of times this page was visited.
+
+
+ Page Titles Following a Site Search
+ Actions
+ getPageTitlesFollowingSiteSearch
+
+
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageUrlsFollowingSiteSearch&period=range&date=2010-01-03,2010-01-09
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageUrlsFollowingSiteSearch&period=day&date=2010-01-03,2010-01-09
Actions_getPageUrlsFollowingSiteSearch
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrlsFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_month.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrlsFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_month.xml
index ddbfc27323..814c5bd443 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrlsFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_month.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrlsFollowingSiteSearch_firstSite_lastN__API.getProcessedReport_month.xml
@@ -3,7 +3,8 @@
Site 1 - Site search
1 Jan 10 - 31 Jul 10
- Site Search
+ Actions
+ Site Search
Pages Following a Site Search
Actions
getPageUrlsFollowingSiteSearch
@@ -17,6 +18,13 @@
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.
The number of times this page was visited.
+
+
+ Page Titles Following a Site Search
+ Actions
+ getPageTitlesFollowingSiteSearch
+
+
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageUrlsFollowingSiteSearch&period=range&date=2010-01-03,2010-07-03
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageUrlsFollowingSiteSearch&period=month&date=2010-01-03,2010-07-03
Actions_getPageUrlsFollowingSiteSearch
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrls_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrls_firstSite_lastN__API.getProcessedReport_day.xml
index 540d585878..84e770988d 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrls_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrls_firstSite_lastN__API.getProcessedReport_day.xml
@@ -4,6 +4,7 @@
3 Jan 10 - 9 Jan 10
Actions
+ Pages
Page URLs
Actions
getPageUrls
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrls_firstSite_lastN__API.getProcessedReport_month.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrls_firstSite_lastN__API.getProcessedReport_month.xml
index d1babd5956..0d002112b6 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrls_firstSite_lastN__API.getProcessedReport_month.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getPageUrls_firstSite_lastN__API.getProcessedReport_month.xml
@@ -4,6 +4,7 @@
1 Jan 10 - 31 Jul 10
Actions
+ Pages
Page URLs
Actions
getPageUrls
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchCategories_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchCategories_firstSite_lastN__API.getProcessedReport_day.xml
index 48dec5534c..eef856b9c5 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchCategories_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchCategories_firstSite_lastN__API.getProcessedReport_day.xml
@@ -3,7 +3,8 @@
Site 1 - Site search
3 Jan 10 - 9 Jan 10
- Site Search
+ Actions
+ Site Search
Search Categories
Actions
getSiteSearchCategories
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchCategories_firstSite_lastN__API.getProcessedReport_month.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchCategories_firstSite_lastN__API.getProcessedReport_month.xml
index cb33390062..69fdfe1242 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchCategories_firstSite_lastN__API.getProcessedReport_month.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchCategories_firstSite_lastN__API.getProcessedReport_month.xml
@@ -3,7 +3,8 @@
Site 1 - Site search
1 Jan 10 - 31 Jul 10
- Site Search
+ Actions
+ Site Search
Search Categories
Actions
getSiteSearchCategories
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchKeywords_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchKeywords_firstSite_lastN__API.getProcessedReport_day.xml
index 12f71c1e5f..40ecf0f41b 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchKeywords_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchKeywords_firstSite_lastN__API.getProcessedReport_day.xml
@@ -3,7 +3,8 @@
Site 1 - Site search
3 Jan 10 - 9 Jan 10
- Site Search
+ Actions
+ Site Search
Site Search Keywords
Actions
getSiteSearchKeywords
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchKeywords_firstSite_lastN__API.getProcessedReport_month.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchKeywords_firstSite_lastN__API.getProcessedReport_month.xml
index 547fb4f58e..10c1eeeb9a 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchKeywords_firstSite_lastN__API.getProcessedReport_month.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchKeywords_firstSite_lastN__API.getProcessedReport_month.xml
@@ -3,7 +3,8 @@
Site 1 - Site search
1 Jan 10 - 31 Jul 10
- Site Search
+ Actions
+ Site Search
Site Search Keywords
Actions
getSiteSearchKeywords
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchNoResultKeywords_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchNoResultKeywords_firstSite_lastN__API.getProcessedReport_day.xml
index 1f90a947fa..e1fe80b601 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchNoResultKeywords_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchNoResultKeywords_firstSite_lastN__API.getProcessedReport_day.xml
@@ -3,7 +3,8 @@
Site 1 - Site search
3 Jan 10 - 9 Jan 10
- Site Search
+ Actions
+ Site Search
Search Keywords with No Results
Actions
getSiteSearchNoResultKeywords
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchNoResultKeywords_firstSite_lastN__API.getProcessedReport_month.xml b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchNoResultKeywords_firstSite_lastN__API.getProcessedReport_month.xml
index 8619ee693c..8ca73782a9 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchNoResultKeywords_firstSite_lastN__API.getProcessedReport_month.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_Actions.getSiteSearchNoResultKeywords_firstSite_lastN__API.getProcessedReport_month.xml
@@ -3,7 +3,8 @@
Site 1 - Site search
1 Jan 10 - 31 Jul 10
- Site Search
+ Actions
+ Site Search
Search Keywords with No Results
Actions
getSiteSearchNoResultKeywords
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_day.xml
index 7b71c193d5..bbc0938754 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_day.xml
@@ -4,6 +4,7 @@
3 Jan 10 - 9 Jan 10
Visitors
+ Custom Variables
Custom Variables
CustomVariables
getCustomVariables
diff --git a/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_month.xml b/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_month.xml
index b43322948f..e0aa4d5446 100644
--- a/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_month.xml
+++ b/tests/PHPUnit/System/expected/test_SiteSearch_CustomVariables.getCustomVariables_firstSite_lastN__API.getProcessedReport_month.xml
@@ -4,6 +4,7 @@
1 Jan 10 - 31 Jul 10
Visitors
+ Custom Variables
Custom Variables
CustomVariables
getCustomVariables
diff --git a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getOutlinks_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getOutlinks_firstSite_lastN__API.getProcessedReport_day.xml
index 85885d4462..c937a953c7 100644
--- a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getOutlinks_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getOutlinks_firstSite_lastN__API.getProcessedReport_day.xml
@@ -4,6 +4,7 @@
3 Jan 10 - 9 Jan 10
Actions
+ Outlinks
Outlinks
Actions
getOutlinks
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 8051442f3d..2bc3553366 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
@@ -4,6 +4,7 @@
3 Jan 10 - 9 Jan 10
Actions
+ Page titles
Page titles
Actions
getPageTitles
@@ -28,6 +29,18 @@
Avg. generation time
getPageTitles
+
+
+ Entry page titles
+ Actions
+ getEntryPageTitles
+
+
+ Exit page titles
+ Actions
+ getExitPageTitles
+
+
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitles&period=range&date=2010-01-03,2010-01-09
index.php?module=API&method=ImageGraph.get&idSite=1&apiModule=Actions&apiAction=getPageTitles&period=day&date=2010-01-03,2010-01-09
Actions_getPageTitles
diff --git a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageUrls_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageUrls_firstSite_lastN__API.getProcessedReport_day.xml
index e7b22c9604..a67ceb3264 100644
--- a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageUrls_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Actions.getPageUrls_firstSite_lastN__API.getProcessedReport_day.xml
@@ -4,6 +4,7 @@
3 Jan 10 - 9 Jan 10
Actions
+ Pages
Page URLs
Actions
getPageUrls
diff --git a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Referrers.getWebsites_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Referrers.getWebsites_firstSite_lastN__API.getProcessedReport_day.xml
index f77187ebe6..de7b01af17 100644
--- a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Referrers.getWebsites_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_Referrers.getWebsites_firstSite_lastN__API.getProcessedReport_day.xml
@@ -4,6 +4,7 @@
3 Jan 10 - 9 Jan 10
Referrers
+ Websites & Social
Websites
Referrers
getWebsites
diff --git a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_VisitFrequency.get_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_VisitFrequency.get_firstSite_lastN__API.getProcessedReport_day.xml
index 5d889eb52f..3659833662 100644
--- a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_VisitFrequency.get_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_VisitFrequency.get_firstSite_lastN__API.getProcessedReport_day.xml
@@ -4,6 +4,7 @@
3 Jan 10 - 9 Jan 10
Visitors
+ Engagement
Returning Visits
VisitFrequency
get
diff --git a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_VisitorInterest.getNumberOfVisitsByDaysSinceLast_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_VisitorInterest.getNumberOfVisitsByDaysSinceLast_firstSite_lastN__API.getProcessedReport_day.xml
index b7072a3a35..a75f643726 100644
--- a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_VisitorInterest.getNumberOfVisitsByDaysSinceLast_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_VisitorInterest.getNumberOfVisitsByDaysSinceLast_firstSite_lastN__API.getProcessedReport_day.xml
@@ -4,6 +4,7 @@
3 Jan 10 - 9 Jan 10
Visitors
+ Engagement
Visits by days since last visit
VisitorInterest
getNumberOfVisitsByDaysSinceLast
diff --git a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_VisitsSummary.get_firstSite_lastN__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_VisitsSummary.get_firstSite_lastN__API.getProcessedReport_day.xml
index 63b6741a62..b5ba74182e 100644
--- a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_VisitsSummary.get_firstSite_lastN__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_VisitsSummary.get_firstSite_lastN__API.getProcessedReport_day.xml
@@ -3,7 +3,8 @@
Site 1
3 Jan 10 - 9 Jan 10
- Visits Summary
+ Visitors
+ Overview
Visits Summary
VisitsSummary
get
diff --git a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_csv__ScheduledReports.generateReport_month.original.csv b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_csv__ScheduledReports.generateReport_month.original.csv
index 98bd55b5b0..8d071ce80a 100644
--- a/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_csv__ScheduledReports.generateReport_month.original.csv
+++ b/tests/PHPUnit/System/expected/test_TwoVisitors_twoWebsites_differentDays_scheduled_report_in_csv__ScheduledReports.generateReport_month.original.csv
@@ -7,34 +7,172 @@ Visits Summary
nb_uniq_visitors,nb_visits,nb_actions,max_actions,nb_actions_per_visit,avg_time_on_site,bounce_rate
2,11,43,5,3.9,00:10:55,27%
-Visits by Server Time
+Custom Variables
+No data available
+
+Device type
+label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
+Unknown,8,40,0%,5,00:15:01,0%
+Desktop,3,3,0%,1,00:00:00,100%
+
+Device model
+label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
+Unknown,11,43,0%,3.9,00:10:55,27%
+
+Device brand
+label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
+Unknown,11,43,0%,3.9,00:10:55,27%
+
+Screen Resolution
+label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
+800x300,9,41,0%,4.6,00:13:21,11%
+1024x768,2,2,0%,1,00:00:00,100%
+
+Operating System versions
+label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
+Unknown,8,40,0%,5,00:15:01,0%
+Windows XP,3,3,0%,1,00:00:00,100%
+
+Browsers
+label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
+Unknown,8,40,0%,5,00:15:01,0%
+Firefox,2,2,0%,1,00:00:00,100%
+Opera,1,1,0%,1,00:00:00,100%
+
+Browser version
+label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
+Unknown,8,40,0%,5,00:15:01,0%
+Firefox 3.6,2,2,0%,1,00:00:00,100%
+Opera 9.63,1,1,0%,1,00:00:00,100%
+
+Configurations
+label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
+Unknown / Unknown / 800x300,8,40,0%,5,00:15:01,0%
+Windows / Firefox / 1024x768,2,2,0%,1,00:00:00,100%
+Windows / Opera / 800x300,1,1,0%,1,00:00:00,100%
+
+Operating System families
+label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
+Unknown,8,40,0%,5,00:15:01,0%
+Windows,3,3,0%,1,00:00:00,100%
+
+Browser engines
+label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
+Unknown,8,40,0%,5,00:15:01,0%
+Gecko (Firefox),2,2,0%,1,00:00:00,100%
+Presto (Opera),1,1,0%,1,00:00:00,100%
+
+Browser Plugins
+label,nb_visits,nb_visits_percentage
+Cookie,11,100%
+Flash,11,100%
+Java,11,100%
+Director,0,0%
+Gears,0,0%
+Pdf,0,0%
+Quicktime,0,0%
+Realplayer,0,0%
+Silverlight,0,0%
+Windowsmedia,0,0%
+
+Country
label,nb_visits,nb_actions,nb_actions_per_visit,avg_time_on_site,bounce_rate,revenue
-0h,1,1,1,00:00:00,100%,$ 0
-1h,0,0,0,00:00:00,0%,$ 0
-2h,0,0,0,00:00:00,0%,$ 0
-3h,0,0,0,00:00:00,0%,$ 0
-4h,0,0,0,00:00:00,0%,$ 0
-5h,0,0,0,00:00:00,0%,$ 0
-6h,0,0,0,00:00:00,0%,$ 0
-7h,0,0,0,00:00:00,0%,$ 0
-8h,0,0,0,00:00:00,0%,$ 0
-9h,0,0,0,00:00:00,0%,$ 0
-10h,0,0,0,00:00:00,0%,$ 0
-11h,8,40,5,00:15:01,0%,$ 0
-12h,1,1,1,00:00:00,100%,$ 0
-13h,0,0,0,00:00:00,0%,$ 0
-14h,0,0,0,00:00:00,0%,$ 0
-15h,0,0,0,00:00:00,0%,$ 0
-16h,0,0,0,00:00:00,0%,$ 0
-17h,0,0,0,00:00:00,0%,$ 0
-18h,0,0,0,00:00:00,0%,$ 0
-19h,0,0,0,00:00:00,0%,$ 0
-20h,0,0,0,00:00:00,0%,$ 0
-21h,0,0,0,00:00:00,0%,$ 0
-22h,0,0,0,00:00:00,0%,$ 0
-23h,1,1,1,00:00:00,100%,$ 0
+Unknown,8,40,5,00:15:01,0%,$ 0
+France,3,3,1,00:00:00,100%,$ 0
+
+Continent
+label,nb_visits,nb_actions,nb_actions_per_visit,avg_time_on_site,bounce_rate,revenue
+Unknown,8,40,5,00:15:01,0%,$ 0
+Europe,3,3,1,00:00:00,100%,$ 0
+
+Region
+label,nb_visits,nb_actions,nb_actions_per_visit,avg_time_on_site,bounce_rate,revenue
+Unknown,11,43,3.9,00:10:55,27%,$ 0
+
+Browser language
+label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
+Unknown,8,40,0%,5,00:15:01,0%
+French,3,3,0%,1,00:00:00,100%
+
+City
+label,nb_visits,nb_actions,nb_actions_per_visit,avg_time_on_site,bounce_rate,revenue
+Unknown,11,43,3.9,00:10:55,27%,$ 0
+
+Language code
+label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
+Unknown (xx),8,40,0%,5,00:15:01,0%
+French (fr),3,3,0%,1,00:00:00,100%
+
+Provider
+label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
+Unknown,11,43,0%,3.9,00:10:55,27%
+
+Length of Visits
+label,nb_visits
+0-10s,3
+11-30s,0
+31-60s,0
+1-2 min,0
+2-4 min,0
+4-7 min,0
+7-10 min,0
+10-15 min,0
+15-30 min,8
+30+ min,0
+
+Pages per Visit
+label,nb_visits
+1 page,3
+2 pages,0
+3 pages,0
+4 pages,0
+5 pages,8
+6-7 pages,0
+8-10 pages,0
+11-14 pages,0
+15-20 pages,0
+21+ pages,0
+
+Visits by Visit Number
+label,nb_visits,nb_visits_percentage
+1 visit,3,27%
+2 visits,8,73%
+3 visits,0,0%
+4 visits,0,0%
+5 visits,0,0%
+6 visits,0,0%
+7 visits,0,0%
+8 visits,0,0%
+9-14 visits,0,0%
+15-25 visits,0,0%
+26-50 visits,0,0%
+51-100 visits,0,0%
+101-200 visits,0,0%
+201+ visits,0,0%
-Visits by Local Time
+Visits by days since last visit
+label,nb_visits
+New visits,2
+0 days,1
+1 day,0
+2 days,1
+3 days,1
+4 days,1
+5 days,1
+6 days,1
+7 days,1
+8-14 days,2
+15-30 days,0
+31-60 days,0
+61-120 days,0
+121-364 days,0
+365+ days,0
+
+Returning Visits
+nb_uniq_visitors_returning,nb_users_returning,nb_visits_returning,nb_actions_returning,max_actions_returning,bounce_rate_returning,nb_actions_per_visit_returning,avg_time_on_site_returning
+2,0,9,41,5,11%,4.6,00:13:21
+
+Visits per local time
label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
0h,0,0,0%,0,00:00:00,0%
1h,0,0,0%,0,00:00:00,0%
@@ -61,6 +199,33 @@ label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site
22h,0,0,0%,0,00:00:00,0%
23h,0,0,0%,0,00:00:00,0%
+Visits per server time
+label,nb_visits,nb_actions,nb_actions_per_visit,avg_time_on_site,bounce_rate,revenue
+0h,1,1,1,00:00:00,100%,$ 0
+1h,0,0,0,00:00:00,0%,$ 0
+2h,0,0,0,00:00:00,0%,$ 0
+3h,0,0,0,00:00:00,0%,$ 0
+4h,0,0,0,00:00:00,0%,$ 0
+5h,0,0,0,00:00:00,0%,$ 0
+6h,0,0,0,00:00:00,0%,$ 0
+7h,0,0,0,00:00:00,0%,$ 0
+8h,0,0,0,00:00:00,0%,$ 0
+9h,0,0,0,00:00:00,0%,$ 0
+10h,0,0,0,00:00:00,0%,$ 0
+11h,8,40,5,00:15:01,0%,$ 0
+12h,1,1,1,00:00:00,100%,$ 0
+13h,0,0,0,00:00:00,0%,$ 0
+14h,0,0,0,00:00:00,0%,$ 0
+15h,0,0,0,00:00:00,0%,$ 0
+16h,0,0,0,00:00:00,0%,$ 0
+17h,0,0,0,00:00:00,0%,$ 0
+18h,0,0,0,00:00:00,0%,$ 0
+19h,0,0,0,00:00:00,0%,$ 0
+20h,0,0,0,00:00:00,0%,$ 0
+21h,0,0,0,00:00:00,0%,$ 0
+22h,0,0,0,00:00:00,0%,$ 0
+23h,1,1,1,00:00:00,100%,$ 0
+
Visits by Day of Week
label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
Monday,2,6,0%,3,00:07:31,50%
@@ -71,44 +236,6 @@ Friday,1,5,0%,5,00:15:01,0%
Saturday,1,5,0%,5,00:15:01,0%
Sunday,3,7,0%,2.3,00:05:00,67%
-Screen Resolution
-label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
-800x300,9,41,0%,4.6,00:13:21,11%
-1024x768,2,2,0%,1,00:00:00,100%
-
-Browser Plugins
-label,nb_visits,nb_visits_percentage
-Cookie,11,100%
-Flash,11,100%
-Java,11,100%
-Director,0,0%
-Gears,0,0%
-Pdf,0,0%
-Quicktime,0,0%
-Realplayer,0,0%
-Silverlight,0,0%
-Windowsmedia,0,0%
-
-Visitor Configuration
-label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
-Unknown / Unknown / 800x300,8,40,0%,5,00:15:01,0%
-Windows / Firefox / 1024x768,2,2,0%,1,00:00:00,100%
-Windows / Opera / 800x300,1,1,0%,1,00:00:00,100%
-
-Browser language
-label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
-Unknown,8,40,0%,5,00:15:01,0%
-French,3,3,0%,1,00:00:00,100%
-
-Language code
-label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
-Unknown (xx),8,40,0%,5,00:15:01,0%
-French (fr),3,3,0%,1,00:00:00,100%
-
-Actions - Main metrics
-nb_pageviews,nb_uniq_pageviews,nb_downloads,nb_uniq_downloads,nb_outlinks,nb_uniq_outlinks,nb_searches,nb_keywords,avg_time_generation
-43,27,0,0,0,0,0,0,0.3s
-
Page URLs
label,nb_visits,nb_hits,bounce_rate,avg_time_on_page,exit_rate,avg_time_generation
/index.htm,9,9,11%,00:05:20,11%,0.3s
@@ -134,26 +261,25 @@ Checkout,8,8,0%,00:00:00,100%,0.45s
first page view,2,2,100%,00:00:00,100%,0.14s
Page Name not defined,1,1,0%,00:00:00,0%,0.22s
-Entry page titles
-label,entry_nb_visits,entry_bounce_count,bounce_rate,avg_time_generation
-second visitor,8,0,0%,0.25s
- first page view,2,2,100%,0.14s
+Site Search Keywords
+No data available
-Exit page titles
-label,nb_visits,exit_nb_visits,exit_rate,avg_time_generation
-Checkout,8,8,100%,0.45s
- first page view,2,2,100%,0.14s
+Pages Following a Site Search
+No data available
-Outlinks
+Search Keywords with No Results
No data available
-Downloads
+Page Titles Following a Site Search
No data available
-Content Name
+Search Categories
No data available
-Content Piece
+Outlinks
+No data available
+
+Downloads
No data available
Event Categories
@@ -165,20 +291,25 @@ No data available
Event Names
No data available
-Site Search Keywords
+Content Name
No data available
-Search Keywords with No Results
+Content Piece
No data available
-Search Categories
-No data available
+Actions - Main metrics
+nb_pageviews,nb_uniq_pageviews,nb_downloads,nb_uniq_downloads,nb_outlinks,nb_uniq_outlinks,nb_searches,nb_keywords,avg_time_generation
+43,27,0,0,0,0,0,0,0.3s
-Pages Following a Site Search
-No data available
+Entry page titles
+label,entry_nb_visits,entry_bounce_count,bounce_rate,avg_time_generation
+second visitor,8,0,0%,0.25s
+ first page view,2,2,100%,0.14s
-Page Titles Following a Site Search
-No data available
+Exit page titles
+label,nb_visits,exit_nb_visits,exit_rate,avg_time_generation
+Checkout,8,8,100%,0.45s
+ first page view,2,2,100%,0.14s
Referrer Type
label,nb_visits,nb_actions,nb_actions_per_visit,avg_time_on_site,bounce_rate,revenue
@@ -194,20 +325,20 @@ goal-matching-url-parameter,4,20,0%,5,00:15:01,0%
Keywords
No data available
+Search Engines
+No data available
+
Websites
label,nb_visits,nb_actions,nb_actions_per_visit,avg_time_on_site,bounce_rate,revenue
referrer.com,6,22,3.7,00:10:01,33%,$ 0
-Search Engines
+Social Networks
No data available
Campaigns
label,nb_visits,nb_actions,nb_actions_per_visit,avg_time_on_site,bounce_rate,revenue
goal-matching-url-parameter,4,20,5,00:15:01,0%,$ 0
-Social Networks
-No data available
-
Goals
nb_conversions,nb_visits_converted,revenue,conversion_rate
0,0,$ 0,0%
@@ -218,130 +349,15 @@ No data available
Days to Conversion
No data available
-Country
-label,nb_visits,nb_actions,nb_actions_per_visit,avg_time_on_site,bounce_rate,revenue
-Unknown,8,40,5,00:15:01,0%,$ 0
-France,3,3,1,00:00:00,100%,$ 0
-
-Continent
-label,nb_visits,nb_actions,nb_actions_per_visit,avg_time_on_site,bounce_rate,revenue
-Unknown,8,40,5,00:15:01,0%,$ 0
-Europe,3,3,1,00:00:00,100%,$ 0
-
-Region
-label,nb_visits,nb_actions,nb_actions_per_visit,avg_time_on_site,bounce_rate,revenue
-Unknown,11,43,3.9,00:10:55,27%,$ 0
-
-City
-label,nb_visits,nb_actions,nb_actions_per_visit,avg_time_on_site,bounce_rate,revenue
-Unknown,11,43,3.9,00:10:55,27%,$ 0
-
-Custom Variables
+Data tables
No data available
-Length of Visits
-label,nb_visits
-0-10s,3
-11-30s,0
-31-60s,0
-1-2 min,0
-2-4 min,0
-4-7 min,0
-7-10 min,0
-10-15 min,0
-15-30 min,8
-30+ min,0
-
-Pages per Visit
-label,nb_visits
-1 page,3
-2 pages,0
-3 pages,0
-4 pages,0
-5 pages,8
-6-7 pages,0
-8-10 pages,0
-11-14 pages,0
-15-20 pages,0
-21+ pages,0
-
-Visits by Visit Number
-label,nb_visits,nb_visits_percentage
-1 visit,3,27%
-2 visits,8,73%
-3 visits,0,0%
-4 visits,0,0%
-5 visits,0,0%
-6 visits,0,0%
-7 visits,0,0%
-8 visits,0,0%
-9-14 visits,0,0%
-15-25 visits,0,0%
-26-50 visits,0,0%
-51-100 visits,0,0%
-101-200 visits,0,0%
-201+ visits,0,0%
-
-Visits by days since last visit
-label,nb_visits
-New visits,2
-0 days,1
-1 day,0
-2 days,1
-3 days,1
-4 days,1
-5 days,1
-6 days,1
-7 days,1
-8-14 days,2
-15-30 days,0
-31-60 days,0
-61-120 days,0
-121-364 days,0
-365+ days,0
-
-Returning Visits
-nb_uniq_visitors_returning,nb_users_returning,nb_visits_returning,nb_actions_returning,max_actions_returning,bounce_rate_returning,nb_actions_per_visit_returning,avg_time_on_site_returning
-2,0,9,41,5,11%,4.6,00:13:21
-
-Device type
-label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
-Unknown,8,40,0%,5,00:15:01,0%
-Desktop,3,3,0%,1,00:00:00,100%
-
-Visitor Browser
-label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
-Unknown,8,40,0%,5,00:15:01,0%
-Firefox,2,2,0%,1,00:00:00,100%
-Opera,1,1,0%,1,00:00:00,100%
-
-Device brand
-label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
-Unknown,11,43,0%,3.9,00:10:55,27%
-
-Device model
-label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
-Unknown,11,43,0%,3.9,00:10:55,27%
-
-Browser version
-label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
-Unknown,8,40,0%,5,00:15:01,0%
-Firefox 3.6,2,2,0%,1,00:00:00,100%
-Opera 9.63,1,1,0%,1,00:00:00,100%
-
-Operating System families
-label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
-Unknown,8,40,0%,5,00:15:01,0%
-Windows,3,3,0%,1,00:00:00,100%
+Temperatures evolution over time
+No data available
-Operating System versions
-label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
-Unknown,8,40,0%,5,00:15:01,0%
-Windows XP,3,3,0%,1,00:00:00,100%
+Pie graph
+No data available
-Browser engines
-label,nb_visits,nb_actions,conversion_rate,nb_actions_per_visit,avg_time_on_site,bounce_rate
-Unknown,8,40,0%,5,00:15:01,0%
-Gecko (Firefox),2,2,0%,1,00:00:00,100%
-Presto (Opera),1,1,0%,1,00:00:00,100%
+Advanced tag cloud: with logos and links
+No data available
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 5f6c7d172c..b0a975f7d7 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
@@ -31,18 +31,23 @@
-
- Visits by Server Time
+
+ Custom Variables
-
- Visits by Local Time
+
+ Device type
-
- Visits by Day of Week
+
+ Device model
+
+
+
+
+ Device brand
@@ -51,258 +56,278 @@
-
- Browser Plugins
+
+ Operating System versions
+
+
+
+
+ Browsers
+
+
+
+
+ Browser version
- Visitor Configuration
+ Configurations
-
- Browser language
+
+ Operating System families
-
- Language code
+
+ Browser engines
-
- Actions - Main metrics
+
+ Browser Plugins
-
- Page URLs
+
+ Country
-
- Entry pages
+
+ Continent
-
- Exit pages
+
+ Region
-
- Page titles
+
+ Browser language
-
- Entry page titles
+
+ City
-
- Exit page titles
+
+ Language code
-
- Outlinks
+
+ Provider
-
- Downloads
+
+ Length of Visits
-
- Content Name
+
+ Pages per Visit
-
- Content Piece
+
+ Visits by Visit Number
-
- Event Categories
+
+ Visits by days since last visit
-
- Event Actions
+
+ Returning Visits
-
- Event Names
+
+ Visits per local time
-
- Site Search Keywords
+
+ Visits per server time
-
- Search Keywords with No Results
+
+ Visits by Day of Week
-
- Search Categories
+
+ Page URLs
-
- Pages Following a Site Search
+
+ Entry pages
-
- Page Titles Following a Site Search
+
+ Exit pages
-
- Referrer Type
+
+ Page titles
-
- All Referrers
+
+ Site Search Keywords
-
- Keywords
+
+ Pages Following a Site Search
-
- Websites
+
+ Search Keywords with No Results
-
- Search Engines
+
+ Page Titles Following a Site Search
-
- Campaigns
+
+ Search Categories
-
- Social Networks
+
+ Outlinks
-
- Goals
+
+ Downloads
-
- Visits to Conversion
+
+ Event Categories
-
- Days to Conversion
+
+ Event Actions
-
- Country
+
+ Event Names
-
- Continent
+
+ Content Name
-
- Region
+
+ Content Piece
-
- City
+
+ Actions - Main metrics
-
- Custom Variables
+
+ Entry page titles
-
- Length of Visits
+
+ Exit page titles
-
- Pages per Visit
+
+ Referrer Type
-
- Visits by Visit Number
+
+ All Referrers
-
- Visits by days since last visit
+
+ Keywords
-
- Returning Visits
+
+ Search Engines
-
- Device type
+
+ Websites
-
- Visitor Browser
+
+ Social Networks
-
- Device brand
+
+ Campaigns
-
- Device model
+
+ Goals
-
- Browser version
+
+ Visits to Conversion
-
- Operating System families
+
+ Days to Conversion
-
- Operating System versions
+
+ Data tables
-
- Browser engines
+
+ Temperatures evolution over time
+
+
+
+
+ Pie graph
+
+
+
+
+ Advanced tag cloud: with logos and links
@@ -476,8 +501,13 @@
Back to top
-
- Server time
+ Device type
Visits
@@ -503,267 +533,507 @@
Bounce Rate
- Revenue
+ Conversion Rate
- 0h
+
+
+ Unknown
- 1
+ 8
- 1
+ 40
- 1
+ 5
- 00:00:00
+ 00:15:01
- 100%
+ 0%
- $ 0
+ 0%
- 1h
+
+
+ Desktop
- 0
+ 3
- 0
+ 3
- 0
+ 1
00:00:00
- 0%
+ 100%
- $ 0
+ 0%
-
+
+
+
+
+ Back to top
+
+
+ Device model
+
+
+
+
+
+
+
+ Device model
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Conversion Rate
+
+
+
+
- 2h
+ Unknown
- 0
+ 11
- 0
+ 43
- 0
+ 3.9
- 00:00:00
+ 00:10:55
- 0%
+ 27%
- $ 0
+ 0%
-
-
+
+
+
+
+ Back to top
+
+
+ Device brand
+
+
+
+
+
+
+
+ Device brand
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Conversion Rate
+
+
+
+
+
- 3h
+
+
+ Unknown
- 0
+ 11
- 0
+ 43
- 0
+ 3.9
- 00:00:00
+ 00:10:55
- 0%
+ 27%
- $ 0
+ 0%
-
+
+
+
+
+ Back to top
+
+
+ Screen Resolution
+
+
+
+
+
+
+
+ Resolution
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Conversion Rate
+
+
+
+
- 4h
+ 800x300
- 0
+ 9
- 0
+ 41
- 0
+ 4.6
- 00:00:00
+ 00:13:21
- 0%
+ 11%
- $ 0
+ 0%
- 5h
+ 1024x768
- 0
+ 2
- 0
+ 2
- 0
+ 1
00:00:00
- 0%
+ 100%
- $ 0
+ 0%
-
+
+
+
+
+ Back to top
+
+
+ Operating System versions
+
+
+
+
+
+
+
+ Operating System versions
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Conversion Rate
+
+
+
+
- 6h
+
+
+ Unknown
- 0
+ 8
- 0
+ 40
- 0
+ 5
- 00:00:00
+ 00:15:01
0%
- $ 0
+ 0%
- 7h
+
+
+ Windows XP
- 0
+ 3
- 0
+ 3
- 0
+ 1
00:00:00
- 0%
+ 100%
- $ 0
+ 0%
-
-
-
- 8h
-
- 0
-
+
+
+
+
+ Back to top
+
+
+ Browsers
+
+
+
+
+
+
+
+ Browser
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Conversion Rate
+
+
+
+
+
+
+
+
+ Unknown
- 0
+ 8
- 0
+ 40
- 00:00:00
+ 5
+
+
+ 00:15:01
0%
- $ 0
+ 0%
- 9h
+
+
+ Firefox
- 0
+ 2
- 0
+ 2
- 0
+ 1
00:00:00
- 0%
+ 100%
- $ 0
+ 0%
- 10h
+
+
+ Opera
- 0
+ 1
- 0
+ 1
- 0
+ 1
00:00:00
- 0%
+ 100%
- $ 0
+ 0%
-
-
+
+
+
+
+ Back to top
+
+
+ Browser version
+
+
+
+
+
+
+
+ Browser version
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Conversion Rate
+
+
+
+
+
- 11h
+
+
+ Unknown
8
@@ -780,18 +1050,20 @@
0%
- $ 0
+ 0%
-
+
- 12h
+
+
+ Firefox 3.6
- 1
+ 2
- 1
+ 2
1
@@ -803,243 +1075,314 @@
100%
- $ 0
+ 0%
-
+
- 13h
+
+
+ Opera 9.63
- 0
+ 1
- 0
+ 1
- 0
+ 1
00:00:00
- 0%
+ 100%
- $ 0
+ 0%
-
+
+
+
+
+ Back to top
+
+
+ Configurations
+
+
+
+
+
+
+
+ Configuration
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Conversion Rate
+
+
+
+
- 14h
+ Unknown / Unknown / 800x300
- 0
+ 8
- 0
+ 40
- 0
+ 5
- 00:00:00
+ 00:15:01
0%
- $ 0
+ 0%
- 15h
+ Windows / Firefox / 1024x768
- 0
+ 2
- 0
+ 2
- 0
+ 1
00:00:00
- 0%
+ 100%
- $ 0
+ 0%
- 16h
+ Windows / Opera / 800x300
- 0
+ 1
- 0
+ 1
- 0
+ 1
00:00:00
- 0%
-
-
- $ 0
-
-
-
-
-
- 17h
-
- 0
-
-
- 0
-
-
- 0
-
-
- 00:00:00
+ 100%
0%
-
- $ 0
-
-
-
-
- 18h
-
- 0
+
+
+
+
+ Back to top
+
+
+ Operating System families
+
+
+
+
+
+
+
+ Operating system family
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Conversion Rate
+
+
+
+
+
+
+
+
+ Unknown
+
+ 8
- 0
+ 40
- 0
+ 5
- 00:00:00
+ 00:15:01
0%
- $ 0
+ 0%
- 19h
+
+
+ Windows
- 0
+ 3
- 0
+ 3
- 0
+ 1
00:00:00
- 0%
+ 100%
- $ 0
+ 0%
-
+
+
+
+
+ Back to top
+
+
+ Browser engines
+
+
+
+
+
+
+
+ Browser engine
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Conversion Rate
+
+
+
+
- 20h
+ Unknown
- 0
+ 8
- 0
+ 40
- 0
+ 5
- 00:00:00
+ 00:15:01
0%
- $ 0
+ 0%
- 21h
+ Gecko (Firefox)
- 0
+ 2
- 0
+ 2
- 0
+ 1
00:00:00
- 0%
-
-
- $ 0
-
-
-
-
-
- 22h
-
- 0
-
-
- 0
-
-
- 0
-
-
- 00:00:00
+ 100%
0%
-
- $ 0
-
-
+
- 23h
+ Presto (Opera)
1
@@ -1056,7 +1399,7 @@
100%
- $ 0
+ 0%
@@ -1065,8 +1408,8 @@
Back to top
-
- Visits by Local Time
+
+ Browser Plugins
@@ -1074,69 +1417,89 @@
- Local time
+ Plugin
Visits
- Actions
-
-
- Actions per Visit
-
-
- Avg. Time on Website
-
-
- Bounce Rate
-
-
- Conversion Rate
+ % Visits
- 0h
+
+
+ Cookie
- 0
+ 11
- 0
+ 100%
+
+
+
+
+
+
+ Flash
- 0
+ 11
- 00:00:00
+ 100%
+
+
+
+
+
+
+ Java
- 0%
+ 11
- 0%
+ 100%
- 1h
+
+
+ Director
0
- 0
+ 0%
+
+
+
+
+
+
+ Gears
0
- 00:00:00
+ 0%
+
+
+
+
+
+
+ Pdf
- 0%
+ 0
0%
@@ -1145,21 +1508,37 @@
- 2h
+
+
+ Quicktime
0
- 0
+ 0%
+
+
+
+
+
+
+ Realplayer
0
- 00:00:00
+ 0%
+
+
+
+
+
+
+ Silverlight
- 0%
+ 0
0%
@@ -1168,156 +1547,300 @@
- 3h
-
- 0
-
-
- 0
-
+
+
+ Windowsmedia
0
-
- 00:00:00
-
-
- 0%
-
0%
-
+
+
+
+
+ Back to top
+
+
+ Country
+
+
+
+
+
+
+
+ Country
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Revenue
+
+
+
+
- 4h
+
+
+ Unknown
- 0
+ 8
- 0
+ 40
- 0
+ 5
- 00:00:00
+ 00:15:01
0%
- 0%
+ $ 0
- 5h
+
+
+ France
- 0
+ 3
- 0
+ 3
- 0
+ 1
00:00:00
- 0%
+ 100%
- 0%
+ $ 0
-
+
+
+
+
+ Back to top
+
+
+ Continent
+
+
+
+
+
+
+
+ Continent
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Revenue
+
+
+
+
- 6h
+ Unknown
- 0
+ 8
- 0
+ 40
- 0
+ 5
- 00:00:00
+ 00:15:01
0%
- 0%
+ $ 0
- 7h
+ Europe
- 0
+ 3
- 0
+ 3
- 0
+ 1
00:00:00
- 0%
+ 100%
- 0%
+ $ 0
-
+
+
+
+
+ Back to top
+
+
+ Region
+
+
+
+
+
+
+
+ Region
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Revenue
+
+
+
+
- 8h
+
+
+ Unknown
- 0
+ 11
- 0
+ 43
- 0
+ 3.9
- 00:00:00
+ 00:10:55
- 0%
+ 27%
- 0%
+ $ 0
-
-
+
+
+
+
+ Back to top
+
+
+ Browser language
+
+
+
+
+
+
+
+ Language
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Conversion Rate
+
+
+
+
+
- 9h
+ Unknown
- 0
+ 8
- 0
+ 40
- 0
+ 5
- 00:00:00
+ 00:15:01
0%
@@ -1327,69 +1850,145 @@
-
+
- 10h
+ French
- 0
+ 3
- 0
+ 3
- 0
+ 1
00:00:00
- 0%
+ 100%
0%
-
-
+
+
+
+
+ Back to top
+
+
+ City
+
+
+
+
+
+
+
+ City
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Revenue
+
+
+
+
+
- 11h
+
+
+ Unknown
- 0
+ 11
- 0
+ 43
- 0
+ 3.9
- 00:00:00
+ 00:10:55
- 0%
+ 27%
- 0%
+ $ 0
-
+
+
+
+
+ Back to top
+
+
+ Language code
+
+
+
+
+
+
+
+ Language
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Conversion Rate
+
+
+
+
- 12h
+ Unknown (xx)
- 11
+ 8
- 43
+ 40
- 3.9
+ 5
- 00:10:55
+ 00:15:01
- 27%
+ 0%
0%
@@ -1398,182 +1997,367 @@
- 13h
+ French (fr)
- 0
+ 3
- 0
+ 3
- 0
+ 1
00:00:00
- 0%
+ 100%
0%
-
+
+
+
+
+ Back to top
+
+
+ Provider
+
+
+
+
+
+
+
+ Provider
+
+
+ Visits
+
+
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
+
+
+ Bounce Rate
+
+
+ Conversion Rate
+
+
+
+
- 14h
+
+ Unknown
+
- 0
+ 11
- 0
+ 43
- 0
+ 3.9
- 00:00:00
+ 00:10:55
- 0%
+ 27%
0%
+
+
+
+
+
+ Back to top
+
+
+ Length of Visits
+
+
+
+
+
+
+
+ Visit duration
+
+
+ Visits
+
+
+
+
+
+
+ 0-10s
+
+ 3
+
- 15h
+ 11-30s
0
+
+
+
+
+ 31-60s
0
+
+
+
+
+ 1-2 min
0
-
- 00:00:00
-
-
- 0%
-
-
- 0%
-
- 16h
+ 2-4 min
0
+
+
+
+
+ 4-7 min
0
+
+
+
+
+ 7-10 min
0
+
+
+
+
+ 10-15 min
- 00:00:00
-
-
- 0%
+ 0
+
+
+
+
+ 15-30 min
- 0%
+ 8
- 17h
+ 30+ min
0
+
+
+
+
+
+ Back to top
+
+
+ Pages per Visit
+
+
+
+
+
+
+
+ Pages per visit
+
+
+ Visits
+
+
+
+
+
+
+ 1 page
- 0
+ 3
+
+
+
+
+ 2 pages
0
-
- 00:00:00
-
-
- 0%
-
-
- 0%
-
- 18h
-
- 0
-
+ 3 pages
0
+
+
+
+
+ 4 pages
0
+
+
+
+
+ 5 pages
- 00:00:00
+ 8
+
+
+
+
+ 6-7 pages
- 0%
+ 0
+
+
+
+
+ 8-10 pages
- 0%
+ 0
- 19h
+ 11-14 pages
0
+
+
+
+
+ 15-20 pages
0
+
+
+
+
+ 21+ pages
0
+
+
+
+
+
+ Back to top
+
+
+ Visits by Visit Number
+
+
+
+
+
+
+
+ Visits by Visit Number
+
+
+ Visits
+
+
+ % Visits
+
+
+
+
+
+
+ 1 visit
- 00:00:00
+ 3
- 0%
+ 27%
+
+
+
+
+ 2 visits
- 0%
+ 8
+
+
+ 73%
- 20h
+ 3 visits
0
- 0
+ 0%
+
+
+
+
+ 4 visits
0
- 00:00:00
+ 0%
+
+
+
+
+ 5 visits
- 0%
+ 0
0%
@@ -1582,21 +2366,31 @@
- 21h
+ 6 visits
0
- 0
+ 0%
+
+
+
+
+ 7 visits
0
- 00:00:00
+ 0%
+
+
+
+
+ 8 visits
- 0%
+ 0
0%
@@ -1605,21 +2399,31 @@
- 22h
+ 9-14 visits
0
- 0
+ 0%
+
+
+
+
+ 15-25 visits
0
- 00:00:00
+ 0%
+
+
+
+
+ 26-50 visits
- 0%
+ 0
0%
@@ -1628,21 +2432,31 @@
- 23h
+ 51-100 visits
0
- 0
+ 0%
+
+
+
+
+ 101-200 visits
0
- 00:00:00
+ 0%
+
+
+
+
+ 201+ visits
- 0%
+ 0
0%
@@ -1654,8 +2468,8 @@
Back to top
-
- Visits by Day of Week
+
+ Visits by days since last visit
@@ -1663,187 +2477,217 @@
- Day of the week
+ Visits by days since last visit
Visits
-
-
- Actions
-
-
- Actions per Visit
-
-
- Avg. Time on Website
-
-
- Bounce Rate
-
-
- Conversion Rate
- Monday
+ New visits
2
+
+
+
+
+ 0 days
- 6
-
-
- 3
+ 1
+
+
+
+
+ 1 day
- 00:07:31
+ 0
+
+
+
+
+ 2 days
- 50%
+ 1
+
+
+
+
+ 3 days
- 0%
+ 1
- Tuesday
-
- 2
-
-
- 10
-
-
- 5
-
-
- 00:15:01
-
-
- 0%
-
+ 4 days
- 0%
+ 1
- Wednesday
+ 5 days
1
-
- 5
-
-
- 5
-
-
- 00:15:01
-
-
- 0%
-
-
- 0%
-
- Thursday
+ 6 days
1
-
- 5
-
-
- 5
-
-
- 00:15:01
-
-
- 0%
-
-
- 0%
-
- Friday
+ 7 days
1
+
+
+
+
+ 8-14 days
- 5
-
-
- 5
-
-
- 00:15:01
-
-
- 0%
+ 2
+
+
+
+
+ 15-30 days
- 0%
+ 0
- Saturday
+ 31-60 days
- 1
+ 0
+
+
+
+
+ 61-120 days
- 5
+ 0
+
+
+
+
+ 121-364 days
- 5
+ 0
+
+
+
+
+ 365+ days
- 00:15:01
+ 0
+
+
+
+
+
+ Back to top
+
+
+ Returning Visits
+
+
+
+
+
+
+
+ Name
+
+
+ Value
+
+
+
+
+
+
+ Unique returning visitors
- 0%
+ 2
+
+
+
+
+ Returning Users
- 0%
+ 0
- Sunday
+ Returning Visits
- 3
+ 9
+
+
+
+
+ Actions by Returning Visits
- 7
+ 41
+
+
+
+
+ Maximum actions in one returning visit
- 2.3
+ 5
+
+
+
+
+ Bounce Rate for Returning Visits
- 00:05:00
+ 11%
+
+
+
+
+ Avg. Actions per Returning Visit
- 67%
+ 4.6
+
+
+
+
+ Avg. Duration of a Returning Visit (in sec)
- 0%
+ 00:13:21
@@ -1852,8 +2696,8 @@
Back to top
-
- Screen Resolution
+
+ Visits per local time
@@ -1861,7 +2705,7 @@
- Resolution
+ Local time
Visits
@@ -1886,21 +2730,21 @@
- 800x300
+ 0h
- 9
+ 0
- 41
+ 0
- 4.6
+ 0
- 00:13:21
+ 00:00:00
- 11%
+ 0%
0%
@@ -1909,98 +2753,67 @@
- 1024x768
+ 1h
- 2
+ 0
- 2
+ 0
- 1
+ 0
00:00:00
- 100%
+ 0%
0%
-
-
-
-
- Back to top
-
-
- Browser Plugins
-
-
-
-
-
-
-
- Plugin
-
-
- Visits
-
-
- % Visits
-
-
-
-
+
-
-
- Cookie
+ 2h
- 11
+ 0
- 100%
+ 0
+
+
+ 0
+
+
+ 00:00:00
+
+
+ 0%
+
+
+ 0%
-
-
- Flash
+ 3h
- 11
+ 0
- 100%
+ 0
-
-
-
-
-
-
- Java
- 11
+ 0
- 100%
+ 00:00:00
-
-
-
-
-
-
- Director
- 0
+ 0%
0%
@@ -2009,12 +2822,22 @@
-
-
- Gears
+ 4h
+
+ 0
+
+
+ 0
+
0
+
+ 00:00:00
+
+
+ 0%
+
0%
@@ -2022,12 +2845,22 @@
-
-
- Pdf
+ 5h
+
+ 0
+
+
+ 0
+
0
+
+ 00:00:00
+
+
+ 0%
+
0%
@@ -2035,12 +2868,22 @@
-
-
- Quicktime
+ 6h
+
+ 0
+
+
+ 0
+
0
+
+ 00:00:00
+
+
+ 0%
+
0%
@@ -2048,12 +2891,22 @@
-
-
- Realplayer
+ 7h
+
+ 0
+
+
+ 0
+
0
+
+ 00:00:00
+
+
+ 0%
+
0%
@@ -2061,12 +2914,22 @@
-
-
- Silverlight
+ 8h
+
+ 0
+
+
+ 0
+
0
+
+ 00:00:00
+
+
+ 0%
+
0%
@@ -2074,68 +2937,41 @@
-
-
- Windowsmedia
+ 9h
+
+ 0
+
+
+ 0
+
0
+
+ 00:00:00
+
+
+ 0%
+
0%
-
-
-
-
- Back to top
-
-
- Visitor Configuration
-
-
-
-
-
-
-
- Configuration
-
-
- Visits
-
-
- Actions
-
-
- Actions per Visit
-
-
- Avg. Time on Website
-
-
- Bounce Rate
-
-
- Conversion Rate
-
-
-
-
+
- Unknown / Unknown / 800x300
+ 10h
- 8
+ 0
- 40
+ 0
- 5
+ 0
- 00:15:01
+ 00:00:00
0%
@@ -2147,21 +2983,21 @@
- Windows / Firefox / 1024x768
+ 11h
- 2
+ 0
- 2
+ 0
- 1
+ 0
00:00:00
- 100%
+ 0%
0%
@@ -2170,78 +3006,41 @@
- Windows / Opera / 800x300
+ 12h
- 1
+ 11
- 1
+ 43
- 1
+ 3.9
- 00:00:00
+ 00:10:55
- 100%
+ 27%
0%
-
-
-
-
- Back to top
-
-
- Browser language
-
-
-
-
-
-
-
- Language
-
-
- Visits
-
-
- Actions
-
-
- Actions per Visit
-
-
- Avg. Time on Website
-
-
- Bounce Rate
-
-
- Conversion Rate
-
-
-
-
-
+
+
- Unknown
+ 13h
- 8
+ 0
- 40
+ 0
- 5
+ 0
- 00:15:01
+ 00:00:00
0%
@@ -2251,80 +3050,43 @@
-
+
- French
+ 14h
- 3
+ 0
- 3
+ 0
- 1
+ 0
00:00:00
- 100%
+ 0%
0%
-
-
-
-
- Back to top
-
-
- Language code
-
-
-
-
-
-
-
- Language
-
-
- Visits
-
-
- Actions
-
-
- Actions per Visit
-
-
- Avg. Time on Website
-
-
- Bounce Rate
-
-
- Conversion Rate
-
-
-
-
-
+
+
- Unknown (xx)
+ 15h
- 8
+ 0
- 40
+ 0
- 5
+ 0
- 00:15:01
+ 00:00:00
0%
@@ -2334,257 +3096,187 @@
-
+
- French (fr)
+ 16h
- 3
+ 0
- 3
+ 0
- 1
+ 0
00:00:00
-
- 100%
-
0%
-
-
-
-
-
- Back to top
-
-
- Actions - Main metrics
-
-
-
-
-
-
-
- Name
-
-
- Value
-
-
-
-
-
-
- Pageviews
- 43
+ 0%
- Unique Pageviews
+ 17h
- 27
+ 0
-
-
-
-
- Downloads
0
-
-
-
-
- Unique Downloads
0
+
+ 00:00:00
+
+
+ 0%
+
+
+ 0%
+
- Outlinks
+ 18h
0
-
-
-
-
- Unique Outlinks
0
-
-
-
-
- Searches
0
+
+ 00:00:00
+
+
+ 0%
+
+
+ 0%
+
- Unique Keywords
+ 19h
0
-
-
-
-
- Avg. generation time
- 0.3s
+ 0
+
+
+ 0
+
+
+ 00:00:00
+
+
+ 0%
+
+
+ 0%
-
-
-
-
- Back to top
-
-
- Page URLs
-
-
-
-
-
-
-
- Page URL
-
-
- Pageviews
-
-
- Unique Pageviews
-
-
- Avg. time on page
-
-
- Bounce Rate
-
-
- Exit rate
-
-
- Avg. generation time
-
-
-
-
+
-
- /index.htm
-
+ 20h
- 9
+ 0
- 9
+ 0
- 00:05:20
+ 0
- 11%
+ 00:00:00
- 11%
+ 0%
- 0.3s
+ 0%
-
- Page URL not defined
-
+ 21h
- 17
+ 0
- 9
+ 0
- 00:00:00
+ 0
- 0%
+ 00:00:00
0%
- 0.22s
+ 0%
-
- /thankyou
-
+ 22h
- 16
+ 0
- 8
+ 0
- 00:06:00
+ 0
- 0%
+ 00:00:00
- 100%
+ 0%
- 0.31s
+ 0%
-
- /products
-
+ 23h
- 1
+ 0
- 1
+ 0
- 00:00:00
+ 0
- 100%
+ 00:00:00
- 100%
+ 0%
- 0.15s
+ 0%
@@ -2593,8 +3285,8 @@
Back to top
-
- Entry pages
+
+ Visits per server time
@@ -2602,217 +3294,178 @@
- Entry Page URL
+ Server time
- Entrances
+ Visits
- Bounces
+ Actions
+
+
+ Actions per Visit
+
+
+ Avg. Time on Website
Bounce Rate
- Avg. generation time
+ Revenue
-
- /index.htm
-
+ 0h
- 9
+ 1
1
- 11%
+ 1
- 0.3s
+ 00:00:00
+
+
+ 100%
+
+
+ $ 0
-
- /products
-
+ 1h
- 1
+ 0
- 1
+ 0
- 100%
+ 0
- 0.15s
+ 00:00:00
+
+
+ 0%
+
+
+ $ 0
-
-
-
-
- Back to top
-
-
- Exit pages
-
-
-
-
-
-
-
- Exit Page URL
-
-
- Exits
-
-
- Unique Pageviews
-
-
- Exit rate
-
-
- Avg. generation time
-
-
-
-
+
-
- /index.htm
-
+ 2h
- 1
+ 0
- 9
+ 0
- 11%
+ 0
- 0.3s
+ 00:00:00
+
+
+ 0%
+
+
+ $ 0
-
- /thankyou
-
+ 3h
- 8
+ 0
- 8
+ 0
- 100%
+ 0
- 0.31s
+ 00:00:00
+
+
+ 0%
+
+
+ $ 0
-
- /products
-
+ 4h
- 1
+ 0
- 1
+ 0
- 100%
+ 0
- 0.15s
+ 00:00:00
+
+
+ 0%
+
+
+ $ 0
-
-
-
-
- Back to top
-
-
- Page titles
-
-
-
-
-
-
-
- Page Name
-
-
- Pageviews
-
-
- Unique Pageviews
-
-
- Avg. time on page
-
-
- Bounce Rate
-
-
- Exit rate
-
-
- Avg. generation time
-
-
-
-
-
+
+
- second visitor
+ 5h
- 16
+ 0
- 16
+ 0
- 00:07:30
+ 0
- 0%
+ 00:00:00
0%
- 0.25s
+ $ 0
-
+
- Checkout
+ 6h
- 8
+ 0
- 8
+ 0
+
+
+ 0
00:00:00
@@ -2821,303 +3474,188 @@
0%
- 100%
+ $ 0
+
+
+
+
+
+ 7h
+
+ 0
- 0.45s
+ 0
+
+
+ 0
+
+
+ 00:00:00
+
+
+ 0%
+
+
+ $ 0
- first page view
+ 8h
- 2
+ 0
- 2
+ 0
- 00:00:00
+ 0
- 100%
+ 00:00:00
- 100%
+ 0%
- 0.14s
+ $ 0
- Page Name not defined
+ 9h
- 1
+ 0
- 1
+ 0
- 00:00:00
+ 0
- 0%
+ 00:00:00
0%
- 0.22s
+ $ 0
-
-
-
-
- Back to top
-
-
- Entry page titles
-
-
-
-
-
-
-
- Entry Page title
-
-
- Entrances
-
-
- Bounces
-
-
- Bounce Rate
-
-
- Avg. generation time
-
-
-
-
+
- second visitor
+ 10h
- 8
+ 0
+
+
+ 0
0
+
+ 00:00:00
+
0%
- 0.25s
+ $ 0
- first page view
+ 11h
- 2
+ 8
- 2
+ 40
- 100%
+ 5
- 0.14s
+ 00:15:01
+
+
+ 0%
+
+
+ $ 0
-
-
-
-
- Back to top
-
-
- Exit page titles
-
-
-
-
-
-
-
- Exit Page Title
-
-
- Exits
-
-
- Unique Pageviews
-
-
- Exit rate
-
-
- Avg. generation time
-
-
-
-
+
- Checkout
+ 12h
- 8
+ 1
- 8
+ 1
+
+
+ 1
+
+
+ 00:00:00
100%
- 0.45s
+ $ 0
- first page view
+ 13h
- 2
+ 0
- 2
+ 0
- 100%
+ 0
- 0.14s
+ 00:00:00
+
+
+ 0%
+
+
+ $ 0
-
-
-
-
- Back to top
-
-
- Outlinks
-
-
- There is no data for this report.
-
- Downloads
-
-
- There is no data for this report.
-
- Content Name
-
-
- There is no data for this report.
-
- Content Piece
-
-
- There is no data for this report.
-
- Event Categories
-
-
- There is no data for this report.
-
- Event Actions
-
-
- There is no data for this report.
-
- Event Names
-
-
- There is no data for this report.
-
- Site Search Keywords
-
-
- There is no data for this report.
-
- Search Keywords with No Results
-
-
- There is no data for this report.
-
- Search Categories
-
-
- There is no data for this report.
-
- Pages Following a Site Search
-
-
- There is no data for this report.
-
- Page Titles Following a Site Search
-
-
- There is no data for this report.
-
- Referrer Type
-
-
-
-
-
-
-
- Referrer Type
-
-
- Visits
-
-
- Actions
-
-
- Actions per Visit
-
-
- Avg. Time on Website
-
-
- Bounce Rate
-
-
- Revenue
-
-
-
-
+
- Websites
+ 14h
- 6
+ 0
- 22
+ 0
- 3.7
+ 0
- 00:10:01
+ 00:00:00
- 33%
+ 0%
$ 0
@@ -3126,18 +3664,18 @@
- Campaigns
+ 15h
- 4
+ 0
- 20
+ 0
- 5
+ 0
- 00:15:01
+ 00:00:00
0%
@@ -3149,169 +3687,182 @@
- Direct Entry
+ 16h
- 1
+ 0
- 1
+ 0
- 1
+ 0
00:00:00
- 100%
+ 0%
$ 0
-
-
-
-
- Back to top
-
-
- All Referrers
-
-
-
-
-
-
-
- Referrer
-
-
- Visits
-
-
- Actions
-
-
- Actions per Visit
-
-
- Avg. Time on Website
-
-
- Bounce Rate
-
-
- Conversion Rate
-
-
-
-
-
+
+
- referrer.com
+ 17h
- 6
+ 0
- 22
+ 0
- 3.7
+ 0
- 00:10:01
+ 00:00:00
- 33%
+ 0%
+
+
+ $ 0
+
+
+
+
+
+ 18h
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 00:00:00
0%
+
+ $ 0
+
- goal-matching-url-parameter
+ 19h
- 4
+ 0
- 20
+ 0
- 5
+ 0
- 00:15:01
+ 00:00:00
0%
+
+ $ 0
+
+
+
+
+
+ 20h
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 00:00:00
+
0%
+
+ $ 0
+
-
-
-
-
- Back to top
-
-
- Keywords
-
-
- There is no data for this report.
-
- Websites
-
-
-
-
-
-
-
- Website
-
-
- Visits
-
-
- Actions
-
-
- Actions per Visit
-
-
- Avg. Time on Website
-
-
- Bounce Rate
-
-
- Revenue
-
-
-
-
+
+
+
+ 21h
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 00:00:00
+
+
+ 0%
+
+
+ $ 0
+
+
+
- referrer.com
+ 22h
- 6
+ 0
- 22
+ 0
- 3.7
+ 0
- 00:10:01
+ 00:00:00
- 33%
+ 0%
+
+
+ $ 0
+
+
+
+
+
+ 23h
+
+ 1
+
+
+ 1
+
+
+ 1
+
+
+ 00:00:00
+
+
+ 100%
$ 0
@@ -3323,13 +3874,8 @@
Back to top
-
- Search Engines
-
-
- There is no data for this report.
-
- Campaigns
+
+ Visits by Day of Week
@@ -3337,7 +3883,7 @@
- Campaign
+ Day of the week
Visits
@@ -3355,19 +3901,42 @@
Bounce Rate
- Revenue
+ Conversion Rate
- goal-matching-url-parameter
+ Monday
- 4
+ 2
- 20
+ 6
+
+
+ 3
+
+
+ 00:07:31
+
+
+ 50%
+
+
+ 0%
+
+
+
+
+
+ Tuesday
+
+ 2
+
+
+ 10
5
@@ -3379,126 +3948,87 @@
0%
- $ 0
+ 0%
-
-
-
-
- Back to top
-
-
- Social Networks
-
-
- There is no data for this report.
-
- Goals
-
-
-
-
-
-
-
- Name
-
-
- Value
-
-
-
-
+
- Conversions
+ Wednesday
- 0
+ 1
+
+
+ 5
+
+
+ 5
+
+
+ 00:15:01
+
+
+ 0%
+
+
+ 0%
- Visits with Conversions
+ Thursday
- 0
+ 1
+
+
+ 5
+
+
+ 5
+
+
+ 00:15:01
+
+
+ 0%
+
+
+ 0%
- Revenue
+ Friday
- $ 0
+ 1
+
+
+ 5
+
+
+ 5
+
+
+ 00:15:01
+
+
+ 0%
-
-
-
-
- Conversion Rate
0%
-
-
-
-
- Back to top
-
-
- Visits to Conversion
-
-
- There is no data for this report.
-
- Days to Conversion
-
-
- There is no data for this report.
-
- Country
-
-
-
-
-
-
-
- Country
-
-
- Visits
-
-
- Actions
-
-
- Actions per Visit
-
-
- Avg. Time on Website
-
-
- Bounce Rate
-
-
- Revenue
-
-
-
-
-
+
+
-
-
- Unknown
+ Saturday
- 8
+ 1
- 40
+ 5
5
@@ -3510,32 +4040,30 @@
0%
- $ 0
+ 0%
-
+
-
-
- France
+ Sunday
3
- 3
+ 7
- 1
+ 2.3
- 00:00:00
+ 00:05:00
- 100%
+ 67%
- $ 0
+ 0%
@@ -3544,8 +4072,8 @@
Back to top
-
- Continent
+
+ Page URLs
@@ -3553,60 +4081,111 @@
- Continent
+ Page URL
- Visits
+ Pageviews
- Actions
+ Unique Pageviews
- Actions per Visit
+ Avg. time on page
- Avg. Time on Website
+ Bounce Rate
- Bounce Rate
+ Exit rate
- Revenue
+ Avg. generation time
- Unknown
+
+ /index.htm
+
- 8
+ 9
- 40
+ 9
- 5
+ 00:05:20
- 00:15:01
+ 11%
- 0%
+ 11%
- $ 0
+ 0.3s
- Europe
+
+ Page URL not defined
+
- 3
+ 17
- 3
+ 9
+
+
+ 00:00:00
+
+
+ 0%
+
+
+ 0%
+
+
+ 0.22s
+
+
+
+
+
+
+ /thankyou
+
+
+ 16
+
+
+ 8
+
+
+ 00:06:00
+
+
+ 0%
+
+
+ 100%
+
+
+ 0.31s
+
+
+
+
+
+
+ /products
+
+
+ 1
1
@@ -3618,7 +4197,10 @@
100%
- $ 0
+ 100%
+
+
+ 0.15s
@@ -3627,60 +4209,67 @@
Back to top
-
- Region
-
+
+ Entry pages
+
- Region
-
-
- Visits
-
-
- Actions
+ Entry Page URL
- Actions per Visit
+ Entrances
- Avg. Time on Website
+ Bounces
Bounce Rate
- Revenue
+ Avg. generation time
-
-
- Unknown
+
+ /index.htm
+
- 11
+ 9
- 43
+ 1
- 3.9
+ 11%
- 00:10:55
+ 0.3s
+
+
+
+
+
+
+ /products
+
+
+ 1
- 27%
+ 1
- $ 0
+ 100%
+
+
+ 0.15s
@@ -3689,8 +4278,8 @@
Back to top
-
- City
+
+ Exit pages
@@ -3698,51 +4287,77 @@
- City
-
-
- Visits
-
-
- Actions
+ Exit Page URL
- Actions per Visit
+ Exits
- Avg. Time on Website
+ Unique Pageviews
- Bounce Rate
+ Exit rate
- Revenue
+ Avg. generation time
-
-
- Unknown
+
+ /index.htm
+
- 11
+ 1
- 43
+ 9
- 3.9
+ 11%
- 00:10:55
+ 0.3s
+
+
+
+
+
+
+ /thankyou
+
+
+ 8
- 27%
+ 8
- $ 0
+ 100%
+
+
+ 0.31s
+
+
+
+
+
+
+ /products
+
+
+ 1
+
+
+ 1
+
+
+ 100%
+
+
+ 0.15s
@@ -3751,13 +4366,8 @@
Back to top
-
- Custom Variables
-
-
- There is no data for this report.
-
- Length of Visits
+
+ Page titles
@@ -3765,91 +4375,118 @@
- Visit duration
+ Page Name
- Visits
+ Pageviews
+
+
+ Unique Pageviews
+
+
+ Avg. time on page
+
+
+ Bounce Rate
+
+
+ Exit rate
+
+
+ Avg. generation time
- 0-10s
+ second visitor
- 3
+ 16
-
-
-
-
- 11-30s
- 0
+ 16
-
-
-
-
- 31-60s
- 0
+ 00:07:30
-
-
-
-
- 1-2 min
- 0
+ 0%
-
-
-
-
- 2-4 min
- 0
+ 0%
+
+
+ 0.25s
- 4-7 min
+ Checkout
- 0
+ 8
-
-
-
-
- 7-10 min
- 0
+ 8
-
-
-
-
- 10-15 min
- 0
+ 00:00:00
+
+
+ 0%
+
+
+ 100%
+
+
+ 0.45s
- 15-30 min
+ first page view
- 8
+ 2
+
+
+ 2
+
+
+ 00:00:00
+
+
+ 100%
+
+
+ 100%
+
+
+ 0.14s
- 30+ min
+ Page Name not defined
- 0
+ 1
+
+
+ 1
+
+
+ 00:00:00
+
+
+ 0%
+
+
+ 0%
+
+
+ 0.22s
@@ -3858,517 +4495,68 @@
Back to top
-
- Pages per Visit
+
+ Site Search Keywords
-
-
-
-
-
- Pages per visit
-
-
- Visits
-
-
-
-
-
-
- 1 page
-
- 3
-
-
-
-
-
- 2 pages
-
- 0
-
-
-
-
-
- 3 pages
-
- 0
-
-
-
-
-
- 4 pages
-
- 0
-
-
-
-
-
- 5 pages
-
- 8
-
-
-
-
-
- 6-7 pages
-
- 0
-
-
-
-
-
- 8-10 pages
-
- 0
-
-
-
-
-
- 11-14 pages
-
- 0
-
-
-
-
-
- 15-20 pages
-
- 0
-
-
-
-
-
- 21+ pages
-
- 0
-
-
-
-
-
-
- Back to top
-
-
- Visits by Visit Number
+ There is no data for this report.
+
+ Pages Following a Site Search
-
-
-
-
-
- Visits by Visit Number
-
-
- Visits
-
-
- % Visits
-
-
-
-
-
-
- 1 visit
-
- 3
-
-
- 27%
-
-
-
-
-
- 2 visits
-
- 8
-
-
- 73%
-
-
-
-
-
- 3 visits
-
- 0
-
-
- 0%
-
-
-
-
-
- 4 visits
-
- 0
-
-
- 0%
-
-
-
-
-
- 5 visits
-
- 0
-
-
- 0%
-
-
-
-
-
- 6 visits
-
- 0
-
-
- 0%
-
-
-
-
-
- 7 visits
-
- 0
-
-
- 0%
-
-
-
-
-
- 8 visits
-
- 0
-
-
- 0%
-
-
-
-
-
- 9-14 visits
-
- 0
-
-
- 0%
-
-
-
-
-
- 15-25 visits
-
- 0
-
-
- 0%
-
-
-
-
-
- 26-50 visits
-
- 0
-
-
- 0%
-
-
-
-
-
- 51-100 visits
-
- 0
-
-
- 0%
-
-
-
-
-
- 101-200 visits
-
- 0
-
-
- 0%
-
-
-
-
-
- 201+ visits
-
- 0
-
-
- 0%
-
-
-
-
-
-
- Back to top
-
-
- Visits by days since last visit
+ There is no data for this report.
+
+ Search Keywords with No Results
+
+
+ There is no data for this report.
+
+ Page Titles Following a Site Search
+
+
+ There is no data for this report.
+
+ Search Categories
-
-
-
-
-
- Visits by days since last visit
-
-
- Visits
-
-
-
-
-
-
- New visits
-
- 2
-
-
-
-
-
- 0 days
-
- 1
-
-
-
-
-
- 1 day
-
- 0
-
-
-
-
-
- 2 days
-
- 1
-
-
-
-
-
- 3 days
-
- 1
-
-
-
-
-
- 4 days
-
- 1
-
-
-
-
-
- 5 days
-
- 1
-
-
-
-
-
- 6 days
-
- 1
-
-
-
-
-
- 7 days
-
- 1
-
-
-
-
-
- 8-14 days
-
- 2
-
-
-
-
-
- 15-30 days
-
- 0
-
-
-
-
-
- 31-60 days
-
- 0
-
-
-
-
-
- 61-120 days
-
- 0
-
-
-
-
-
- 121-364 days
-
- 0
-
-
-
-
-
- 365+ days
-
- 0
-
-
-
-
-
-
- Back to top
-
-
- Returning Visits
+ There is no data for this report.
+
+ Outlinks
-
-
-
-
-
- Name
-
-
- Value
-
-
-
-
-
-
- Unique returning visitors
-
- 2
-
-
-
-
-
- Returning Users
-
- 0
-
-
-
-
-
- Returning Visits
-
- 9
-
-
-
-
-
- Actions by Returning Visits
-
- 41
-
-
-
-
-
- Maximum actions in one returning visit
-
- 5
-
-
-
-
-
- Bounce Rate for Returning Visits
-
- 11%
-
-
-
-
-
- Avg. Actions per Returning Visit
-
- 4.6
-
-
-
-
-
- Avg. Duration of a Returning Visit (in sec)
-
- 00:13:21
-
-
-
-
-
-
- Back to top
-
-
- Device type
+ There is no data for this report.
+
+ Downloads
+
+
+ There is no data for this report.
+
+ Event Categories
+
+
+ There is no data for this report.
+
+ Event Actions
+
+
+ There is no data for this report.
+
+ Event Names
+
+
+ There is no data for this report.
+
+ Content Name
+
+
+ There is no data for this report.
+
+ Content Piece
+
+
+ There is no data for this report.
+
+ Actions - Main metrics
@@ -4376,76 +4564,83 @@
- Device type
-
-
- Visits
-
-
- Actions
-
-
- Actions per Visit
-
-
- Avg. Time on Website
-
-
- Bounce Rate
+ Name
- Conversion Rate
+ Value
-
-
- Unknown
-
- 8
-
-
- 40
-
-
- 5
-
+ Pageviews
- 00:15:01
+ 43
+
+
+
+
+ Unique Pageviews
- 0%
+ 27
+
+
+
+
+ Downloads
- 0%
+ 0
-
-
- Desktop
+ Unique Downloads
- 3
+ 0
+
+
+
+
+ Outlinks
- 3
+ 0
+
+
+
+
+ Unique Outlinks
- 1
+ 0
+
+
+
+
+ Searches
- 00:00:00
+ 0
+
+
+
+
+ Unique Keywords
- 100%
+ 0
+
+
+
+
+ Avg. generation time
- 0%
+ 0.3s
@@ -4454,8 +4649,8 @@
Back to top
-
- Visitor Browser
+
+ Entry page titles
@@ -4463,101 +4658,54 @@
- Browser
-
-
- Visits
-
-
- Actions
+ Entry Page title
- Actions per Visit
+ Entrances
- Avg. Time on Website
+ Bounces
Bounce Rate
- Conversion Rate
+ Avg. generation time
-
-
- Unknown
+ second visitor
8
- 40
-
-
- 5
-
-
- 00:15:01
+ 0
0%
- 0%
+ 0.25s
-
-
- Firefox
+ first page view
2
2
-
- 1
-
-
- 00:00:00
-
-
- 100%
-
-
- 0%
-
-
-
-
-
-
-
- Opera
-
- 1
-
-
- 1
-
-
- 1
-
-
- 00:00:00
-
100%
- 0%
+ 0.14s
@@ -4566,8 +4714,8 @@
Back to top
-
- Device brand
+
+ Exit page titles
@@ -4575,111 +4723,54 @@
- Device brand
-
-
- Visits
-
-
- Actions
+ Exit Page Title
- Actions per Visit
+ Exits
- Avg. Time on Website
+ Unique Pageviews
- Bounce Rate
+ Exit rate
- Conversion Rate
+ Avg. generation time
-
-
- Unknown
-
- 11
-
-
- 43
-
+ Checkout
- 3.9
+ 8
- 00:10:55
+ 8
- 27%
+ 100%
- 0%
+ 0.45s
-
-
-
-
- Back to top
-
-
- Device model
-
-
-
-
-
-
-
- Device model
-
-
- Visits
-
-
- Actions
-
-
- Actions per Visit
-
-
- Avg. Time on Website
-
-
- Bounce Rate
-
-
- Conversion Rate
-
-
-
-
-
+
+
- Unknown
-
- 11
-
-
- 43
-
+ first page view
- 3.9
+ 2
- 00:10:55
+ 2
- 27%
+ 100%
- 0%
+ 0.14s
@@ -4688,8 +4779,8 @@
Back to top
-
- Browser version
+
+ Referrer Type
@@ -4697,7 +4788,7 @@
- Browser version
+ Referrer Type
Visits
@@ -4715,66 +4806,60 @@
Bounce Rate
- Conversion Rate
+ Revenue
-
-
- Unknown
+ Websites
- 8
+ 6
- 40
+ 22
- 5
+ 3.7
- 00:15:01
+ 00:10:01
- 0%
+ 33%
- 0%
+ $ 0
-
-
- Firefox 3.6
+ Campaigns
- 2
+ 4
- 2
+ 20
- 1
+ 5
- 00:00:00
+ 00:15:01
- 100%
+ 0%
- 0%
+ $ 0
-
-
- Opera 9.63
+ Direct Entry
1
@@ -4791,7 +4876,7 @@
100%
- 0%
+ $ 0
@@ -4800,8 +4885,8 @@
Back to top
-
- Operating System families
+
+ All Referrers
@@ -4809,7 +4894,7 @@
- Operating system family
+ Referrer
Visits
@@ -4834,23 +4919,21 @@
-
-
- Unknown
+ referrer.com
- 8
+ 6
- 40
+ 22
- 5
+ 3.7
- 00:15:01
+ 00:10:01
- 0%
+ 33%
0%
@@ -4859,23 +4942,21 @@
-
-
- Windows
+ goal-matching-url-parameter
- 3
+ 4
- 3
+ 20
- 1
+ 5
- 00:00:00
+ 00:15:01
- 100%
+ 0%
0%
@@ -4887,8 +4968,18 @@
Back to top
-
- Operating System versions
+
+ Keywords
+
+
+ There is no data for this report.
+
+ Search Engines
+
+
+ There is no data for this report.
+
+ Websites
@@ -4896,7 +4987,7 @@
- Operating System versions
+ Website
Visits
@@ -4914,58 +5005,31 @@
Bounce Rate
- Conversion Rate
+ Revenue
-
-
- Unknown
-
- 8
-
-
- 40
-
-
- 5
-
-
- 00:15:01
-
-
- 0%
-
-
- 0%
-
-
-
-
-
-
-
- Windows XP
+ referrer.com
- 3
+ 6
- 3
+ 22
- 1
+ 3.7
- 00:00:00
+ 00:10:01
- 100%
+ 33%
- 0%
+ $ 0
@@ -4974,8 +5038,13 @@
Back to top
-
- Browser engines
+
+ Social Networks
+
+
+ There is no data for this report.
+
+ Campaigns
@@ -4983,7 +5052,7 @@
- Browser engine
+ Campaign
Visits
@@ -5001,19 +5070,19 @@
Bounce Rate
- Conversion Rate
+ Revenue
- Unknown
+ goal-matching-url-parameter
- 8
+ 4
- 40
+ 20
5
@@ -5025,51 +5094,59 @@
0%
- 0%
+ $ 0
-
-
+
+
+
+
+ Back to top
+
+
+ Goals
+
+
+
+
+
+
+
+ Name
+
+
+ Value
+
+
+
+
+
- Gecko (Firefox)
-
- 2
-
-
- 2
-
-
- 1
-
-
- 00:00:00
-
+ Conversions
- 100%
+ 0
+
+
+
+
+ Visits with Conversions
- 0%
+ 0
- Presto (Opera)
-
- 1
-
-
- 1
-
-
- 1
-
-
- 00:00:00
-
+ Revenue
- 100%
+ $ 0
+
+
+
+
+ Conversion Rate
0%
@@ -5080,5 +5157,35 @@
Back to top
+
+ Visits to Conversion
+
+
+ There is no data for this report.
+
+ Days to Conversion
+
+
+ There is no data for this report.
+
+ Data tables
+
+
+ There is no data for this report.
+
+ Temperatures evolution over time
+
+
+ There is no data for this report.
+
+ Pie graph
+
+
+ There is no data for this report.
+
+ Advanced tag cloud: with logos and links
+
+
+ There is no data for this report.